The BioCro C++ Library
solar_position_michalsky.h
Go to the documentation of this file.
1#ifndef SOLAR_POSITION_MICHALSKY_H
2#define SOLAR_POSITION_MICHALSKY_H
3
4#include <cmath> // for floor, fmod
5#include "../framework/degree_trigonometry.h" // for atan2_deg, cos_deg, sin_deg, acos_deg
6#include "../framework/module.h"
7#include "../framework/state_map.h"
8
9namespace standardBML
10{
96class solar_position_michalsky : public direct_module
97{
98 public:
100 state_map const& input_quantities,
101 state_map* output_quantities)
102 : direct_module{},
103
104 // Get references to input quantities
105 lat{get_input(input_quantities, "lat")},
106 longitude{get_input(input_quantities, "longitude")},
107 fractional_doy{get_input(input_quantities, "fractional_doy")},
108 time_zone_offset{get_input(input_quantities, "time_zone_offset")},
109 year{get_input(input_quantities, "year")},
110
111 // Get pointers to output quantities
112 cosine_zenith_angle_op{get_op(output_quantities, "cosine_zenith_angle")},
113 julian_date_op{get_op(output_quantities, "julian_date")},
114 solar_L_op{get_op(output_quantities, "solar_L")},
115 solar_g_op{get_op(output_quantities, "solar_g")},
116 solar_ell_op{get_op(output_quantities, "solar_ell")},
117 solar_ep_op{get_op(output_quantities, "solar_ep")},
118 solar_ra_op{get_op(output_quantities, "solar_ra")},
119 solar_dec_op{get_op(output_quantities, "solar_dec")},
120 gmst_op{get_op(output_quantities, "gmst")},
121 lmst_op{get_op(output_quantities, "lmst")},
122 lha_op{get_op(output_quantities, "lha")},
123 solar_zenith_angle_op{get_op(output_quantities, "solar_zenith_angle")},
124 solar_azimuth_angle_op{get_op(output_quantities, "solar_azimuth_angle")}
125 {
126 }
127 static string_vector get_inputs();
128 static string_vector get_outputs();
129 static std::string get_name() { return "solar_position_michalsky"; }
130
131 private:
132 // References to input quantities
133 double const& lat;
134 double const& longitude;
135 double const& fractional_doy;
136 double const& time_zone_offset;
137 double const& year;
138
139 // Pointers to output quantities
140 double* cosine_zenith_angle_op;
141 double* julian_date_op;
142 double* solar_L_op;
143 double* solar_g_op;
144 double* solar_ell_op;
145 double* solar_ep_op;
146 double* solar_ra_op;
147 double* solar_dec_op;
148 double* gmst_op;
149 double* lmst_op;
150 double* lha_op;
151 double* solar_zenith_angle_op;
152 double* solar_azimuth_angle_op;
153
154 // Main operation
155 void do_operation() const;
156};
157
159{
160 return {
161 "lat", // degrees (North is positive)
162 "longitude", // degrees (East is positive)
163 "fractional_doy", // days
164 "time_zone_offset", // the offset of the time zone relative to UTC
165 "year" // a year between 1950 and 2050
166 };
167}
168
170{
171 return {
172 "cosine_zenith_angle", // dimensionless
173 "julian_date", // days
174 "solar_L", // degrees
175 "solar_g", // degrees
176 "solar_ell", // degrees
177 "solar_ep", // degrees
178 "solar_ra", // degrees
179 "solar_dec", // degrees
180 "gmst", // hr
181 "lmst", // hr
182 "lha", // degrees
183 "solar_zenith_angle", // degrees
184 "solar_azimuth_angle" // degrees
185 };
186}
187
188void solar_position_michalsky::do_operation() const
189{
190 // Define some constants
191 double constexpr deg_to_hr = 1.0 / 15.0;
192 double constexpr hr_to_deg = 15.0;
193 double constexpr hr_per_day = 24.0;
194 double constexpr deg_per_rev = 360.0;
195
196 double constexpr jd_ref_1948 = 2432916.5; // Julian date at midnight on 31 December 1948 (UTC)
197 double constexpr jd_ref_2000 = 2451545.0; // Julian date at noon on 1 January 2000 (UTC)
198
199 // Unpack the doy and hour in UTC
200 double const fractional_doy_utc = fractional_doy - time_zone_offset / hr_per_day; // days
201 double const doy_utc = std::floor(fractional_doy_utc / hr_per_day); // days
202 double const hour_utc = hr_per_day * (fractional_doy_utc - doy_utc); // hr
203
204 // Calculate the Julian date
205 double const delta = year - 1949.0;
206 double const leap = floor(0.25 * delta);
207 double const jd = jd_ref_1948 + delta * 365.0 + leap + doy_utc + hour_utc / hr_per_day; // days
208
209 // Calculate the "n" date
210 double const n = jd - jd_ref_2000; // days
211
212 // Calculate the ecliptic coordinates of the sun
213 double const L = fmod(280.460 + 0.9856474 * n, deg_per_rev); // degrees
214 double const g = fmod(357.528 + 0.9856003 * n, deg_per_rev); // degrees
215 double const ell = fmod(L + 1.915 * sin_deg(g) + 0.020 * sin_deg(2 * g), deg_per_rev); // degrees
216 double const ep = 23.439 - 0.0000004 * n; // degrees
217
218 // Calculate the equatorial celestial coordinates of the sun
219 double const ra = atan2_deg(cos_deg(ep) * sin_deg(ell), cos_deg(ell)); // degrees
220 double const dec = asin_deg(sin_deg(ep) * sin_deg(ell)); // degrees
221
222 // Calculate the sidereal time
223 double const gmst = fmod(6.697375 + 0.0657098242 * n + hour_utc, hr_per_day); // hours
224 double const lmst = fmod(gmst + longitude * deg_to_hr, hr_per_day); // hours
225
226 // Convert to local coordinates
227 double const lha = fmod(lmst * hr_to_deg - ra, deg_per_rev); // degrees
228
229 double const zen = acos_deg(sin_deg(dec) * sin_deg(lat) +
230 cos_deg(dec) * cos_deg(lat) * cos_deg(lha)); // degrees
231
232 double az = asin_deg(-cos_deg(dec) * sin_deg(lha) / cos_deg(90.0 - zen)); // degrees
233
234 // Make sure azimuth is in the correct quadrant
235 double const el = 90.0 - zen; // degrees
236 double const el_critical = asin_deg(sin_deg(dec) / sin_deg(lat)); // degrees
237
238 if (el >= el_critical) {
239 az = 180.0 - az; // degrees
240 } else if (lha > 0) {
241 az = 360.0 + az; // degrees
242 }
243
244 // Determine the cosine of the zenith angle
245 double const cos_zen = cos_deg(zen); // dimensionless
246
247 // Update the output pointers
248 update(cosine_zenith_angle_op, cos_zen);
249 update(julian_date_op, jd);
250 update(solar_L_op, L);
251 update(solar_g_op, g);
252 update(solar_ell_op, ell);
253 update(solar_ep_op, ep);
254 update(solar_ra_op, ra);
255 update(solar_dec_op, dec);
256 update(gmst_op, gmst);
257 update(lmst_op, lmst);
258 update(lha_op, lha);
259 update(solar_zenith_angle_op, zen);
260 update(solar_azimuth_angle_op, az);
261}
262
263} // namespace standardBML
264#endif
Calculates the solar position using the model described in Michalsky, J. J. "The Astronomical Almanac...
solar_position_michalsky(state_map const &input_quantities, state_map *output_quantities)
This is the standard BioCro module library; it includes the essential modules used in typical BioCro ...
Definition: aba_decay.h:8