Sun position
Sun position algorithm
moon.c
1 /*==============================================================================
2  * moon.c - routines to calculate the Moon's position
3  *
4  * Author: David Hoadley
5  *
6  * Description: (see moon.h)
7  *
8  * Copyright (c) 2020, David Hoadley <vcrumble@westnet.com.au>
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions are met:
13  *
14  * * Redistributions of source code must retain the above copyright notice, this
15  * list of conditions and the following disclaimer.
16  * * Redistributions in binary form must reproduce the above copyright notice,
17  * this list of conditions and the following disclaimer in the documentation
18  * and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  *==============================================================================
33  */
34 /*------------------------------------------------------------------------------
35  * Notes:
36  * Character set: UTF-8. (Non ASCII characters appear in this file)
37  *----------------------------------------------------------------------------*/
38 
39 /* ANSI includes etc. */
40 #include <float.h>
41 #include "instead-of-math.h" /* for sincos() & normalize() */
42 #include <math.h>
43 #include <stdlib.h>
44 
45 /* Local and project includes */
46 #include "moon.h"
47 
48 #include "general.h"
49 ///+
50 #include <stdio.h>
51 #include "skyio.h"
52 ///-
53 
54 /*
55  * Local #defines and typedefs
56  */
57 DEFINE_THIS_FILE; /* For use by REQUIRE() - assertions. */
58 
59 typedef struct {
60  double lp_rad; // L' - Mean Longitude of the Moon (radian)
61  double d_rad; // D - Mean Elongation of the Moon from the Sun (radian)
62  double m_rad; // M - Mean Anomaly of the Sun (radian)
63  double mp_rad; // M' - Mean Anomaly of the Moon (radian)
64  double f_rad; // F - so-called Argument of Latitude of the Moon (radian)
65  double e; // E - Eccentricity of the Earth's orbit around the Sun
66 } OrbTerms;
67 
68 /* Note there is some inconsistency in the use of terms here, when compared
69  * with the fundamental arguments of the 1980 nutation theory.
70  * NREL Moon Nutation1980 Term description
71  * L' (L) Mean Longitude of the Moon
72  * D D Mean Elongation of the Moon from the Sun
73  * M l' Mean Anomaly of the Sun
74  * M' l Mean Anomaly of the Moon
75  * Ω Longitude of ascending node of Moon
76  * F F = L - Ω, Mean Longitude of Moon minus Longitude
77  * of ascending node.
78  *
79  * In the NREL SAMPA document, sections 3.2.5 and 3.2.4, the term F is referred
80  * to as the "Argument of Latitude". I believe this is wrong. F is actually
81  * the MEAN Longitude of Moon minus the Longitude of the Moon's ascending node
82  * (F = L - Ω), or alternatively the MEAN anomaly plus the argument of periapsis
83  * (F = M' + ω). But the "Argument of Latitude" is defined as the TRUE anomaly
84  * plus the argument of periapsis - i.e. u = ν + ω.
85  *
86  * In the code that follows, I have kept this label, to make it easier for
87  * someone reading the NREL SAMPA document to follow the code, but I have marked
88  * it as "so-called" each time.
89  */
90 
91 typedef struct {
92  double cd;
93  int cm;
94  double cmp;
95  double cf;
96  double cl;
97  double cr;
98 } LonDistTerms;
99 
100 
101 typedef struct {
102  double cd;
103  int cm;
104  double cmp;
105  double cf;
106  double cb;
107 } LatTerms;
108 
109 /*
110  * Prototypes for local functions (not called from other modules)
111  */
112 LOCAL void moonOrbitals(double t_cy, OrbTerms *orb);
113 LOCAL void moonLongDist(const OrbTerms *orb, double *long_udeg, double *dist_m);
114 LOCAL double moonLatitude(const OrbTerms *orb);
115 LOCAL double riseSetApprox(double risesetGuess_d,
116  bool getMoonrise,
117  const Sky_DeltaTs *deltas,
118  const Sky_SiteProp *site,
119  Sky_SiteHorizon *topo);
120 
121 /*
122  * Global variables accessible by other modules
123  */
124 
125 
126 /*
127  * Local variables (not accessed by other modules)
128  */
129 /* Constants found in the 2007 Astronomical Almanac, pages K6 & K7 */
130 LOCAL const double au_km = 1.49597871464e8; // one Astronomical Unit (km)
131 
132 /*
133  *==============================================================================
134  *
135  * Implementation
136  *
137  *==============================================================================
138  *
139  * Global functions callable by other modules
140  *
141  *------------------------------------------------------------------------------
142  */
143 GLOBAL void moon_nrelApp2(double t_cy,
144  const Sky0_Nut1980 *nut,
145  V3D_Vector *appV,
146  double *dist_au)
147 /*! Calculates the Moon's position in geocentric apparent coordinates, using the
148  NREL Moon Position Algorithm.
149  \param[in] t_cy Julian centuries since J2000.0, TT timescale
150  \param[in] nut Nutation terms and obliquity of the ecliptic
151  \param[out] appV Position vector of Moon in apparent coordinates (unit
152  vector i.e. direction cosines)
153  \param[out] dist_au Geocentric distance of the Moon (Astronomical Units)
154 
155  \par Reference
156  Reda, I., "Solar Eclipse Monitoring for Solar Energy Applications Using the
157  Solar and Moon Position Algorithms". National Renewable Energy Laboratory
158  Technical Report NREL/TP-3B0-47681, March 2010
159 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
160 {
161  OrbTerms orbt; // Orbital terms for the Moon
162  double a1_rad, a2_rad, a3_rad; // mysterious factors (radian)
163  double l_udeg, b_udeg; // Moon long & lat terms (micro-degrees)
164  double r_m; // Moon distance term (metres)
165  double dl_udeg, db_udeg; // Δl & Δb (micro-degrees)
166  double lamdap_rad; // λ' - Moon uncorrected longitude (radian)
167  double lamda_rad; // λ - Moon apparent longitude (radian)
168  double beta_rad; // β - Moon latitude (radian)
169  double mDelta_km; // Δ - Moon geocentric distance (km)
170  V3D_Matrix epsM; // Rotation by obliquity of ecliptic
171  V3D_Vector eclV; // Rectangular coordinates in ecliptic frame
172 
173  REQUIRE_NOT_NULL(appV);
174  REQUIRE_NOT_NULL(dist_au);
175 
176  /* Steps 3.2.1 tp 3.2.5, and equation (15) of 3.2.6 */
177  moonOrbitals(t_cy, &orbt);
178 
179  /* Steps 3.2.6 to 3.2.8 (the first!) */
180  moonLongDist(&orbt, &l_udeg, &r_m);
181  b_udeg = moonLatitude(&orbt);
182 
183  /* Steps 3.2.8 (the second!) 3.2.9 and 3.2.10 */
184  a1_rad = degToRad(119.75 + 131.849 * t_cy);
185  a2_rad = degToRad(53.09 + 479264.29 * t_cy);
186  a3_rad = degToRad(313.45 + 481266.484 * t_cy);
187 
188  /* Steps 3.2.11 & 3.2.12 */
189  dl_udeg = 3958.0 * sin(a1_rad) + 1962.0 * sin(orbt.lp_rad - orbt.f_rad)
190  + 318.0 * sin(a2_rad);
191  db_udeg = -2235.0 * sin(orbt.lp_rad) + 382.0 * sin(a3_rad)
192  + 175.0 * sin(a1_rad - orbt.f_rad)
193  + 175.0 * sin(a1_rad + orbt.f_rad)
194  + 127.0 * sin(orbt.lp_rad - orbt.mp_rad)
195  - 115.0 * sin(orbt.lp_rad + orbt.mp_rad);
196 
197  /* Steps 3.2.13 to 3.2.16 */
198  lamdap_rad = normalize(orbt.lp_rad + degToRad((l_udeg + dl_udeg) / 1e6),
199  TWOPI);
200  beta_rad = degToRad((b_udeg + db_udeg) / 1e6);
201  mDelta_km = 385000.56 + r_m / 1000.0;
202  /* Convert distance to the Moon from km to Astronomical Units, because all
203  other celestial objects are specified that way, so our routines
204  converting geocentric to topocentric place expect it to be that way. */
205  *dist_au = mDelta_km / au_km;
206 
207  /* Step 3.6 */
208  lamda_rad = lamdap_rad + nut->dPsi_rad;
209 
210  /* Convert to rectangular coordinates in ecliptic plane and rotate to
211  equatorial plane (i.e to RA/Dec frame). */
212  v3d_polarToRect(&eclV, lamda_rad, beta_rad);
213  v3d_createRotationMatrix(&epsM, Xaxis, -(nut->eps0_rad + nut->dEps_rad));
214  v3d_multMxV(appV, &epsM, &eclV);
215 
216  /* Now have vector of apparent coords. Could convert to RA and Dec using
217  v3d_rectToPolar(&RA, &Dec, appV) but there isn't really any need.
218  Had we done so we would have performed steps 3.8 and 3.9 of NREL SAMPA */
219 }
220 
221 
222 
223 GLOBAL void moon_nrelApparent(double j2kTT_cy, Sky_TrueEquatorial *pos)
224 /*! Calculate the Moon's position as a unit vector and a distance, in apparent
225  coordinates. It calls moon_nrelApp2() to obtain the Moon's position, after
226  having called sky0_nutationSpa() to obtain the necessary nutation terms.
227  \param[in] j2kTT_cy Julian centuries since J2000.0, TT timescale
228  \param[out] pos Timestamped structure containing position data and the
229  equation of the equinoxes
230 
231  \par When to call this function
232  Because this function is computationally intensive, you may wish to limit
233  your use of this function.
234  - if you want the Moon's position at multiple sites simultaneously at a
235  single time, call this function, then follow it with a call to routine
236  sky0_appToTirs(), and then make a separate call to
237  sky_siteTirsToTopo() for each of one those sites.
238  - if you want the Moon's position at one or more sites at closely spaced
239  times (e.g. for tracking the Moon), pass this function to the
240  skyfast_init() function. skyfast_init() will
241  call it for you several times, to fully calculate positions that will
242  be saved and used later for interpolation by the skyfast_getApprox()
243  function for tracking.
244  \par
245  \em Alternatives:
246  - If you want the Moon's position at a single site only at a single
247  time, call moon_nrelTopocentric() instead, and it will call this
248  function for you.
249  - If you want the Moon's position at a single site for more than one
250  time but the times are spaced more than an hour or so apart, once
251  again call moon_nrelTopocentric() instead.
252  - If you want to track the Moon, call skyfast_getApprox() instead.
253  But this requires you to set up interpolation first with function
254  skyfast_init() (and this function), as described above.
255 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
256 {
257  Sky0_Nut1980 nut;
258 
259  REQUIRE_NOT_NULL(pos);
260 
261  /* Calculate nutation (steps 3.4 of the algorithm in the SPA document) */
262  sky0_nutationSpa(j2kTT_cy, &nut);
263 
264  /* Calculate the mean obliquity of the ecliptic (step 3.5) and the equation
265  of the equinoxes */
266  sky0_epsilonSpa(j2kTT_cy, &nut);
267  pos->eqEq_rad = nut.eqEq_rad;
268 
269  /* Calculate Moon apparent position */
270  moon_nrelApp2(j2kTT_cy, &nut, &pos->appCirsV, &pos->distance_au);
271 
272  /* Now set the timestamp*/
273  pos->timestamp_cy = j2kTT_cy;
274 }
275 
276 
277 
278 GLOBAL void moon_nrelTopocentric(double j2kdUtc,
279  const Sky_DeltaTs *deltas,
280  const Sky_SiteProp *site,
281  Sky_SiteHorizon *topo)
282 /*! Calls moon_nrelApparent() to calculate the Moon's position in apparent
283  * coordinates using the NREL Moon Position Algorithm, and then converts this
284  * to topocentric horizon coordinates at the specified site.
285  \param[in] j2kdUtc UTC time in "J2KD" form - i.e days since J2000.0
286  (= JD - 2 451 545.0)
287  \param[in] deltas Delta T values, as set by the sky_initTime() (or
288  sky_initTimeSimple() or sky_initTimeDetailed()) routines
289  \param[in] site Properties of the observing site, particularly its
290  geometric location on the surface of the Earth, as set by
291  the sky_setSiteLocation() function (or sky_setSiteLoc2())
292  \param[out] topo Topocentric position, in both rectangular (unit vector)
293  form, and as Azimuth and Elevation (altitude) angles
294 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
295 {
296  Sky_Times atime; // time, in various timescales
297  Sky_TrueEquatorial pos; // geocentric position of the Moon and dist.
298  V3D_Vector terInterV; // unit vector in Terrestrial Intermed Ref Sys
299 
300  REQUIRE_NOT_NULL(deltas);
301  REQUIRE_NOT_NULL(site);
302  REQUIRE_NOT_NULL(topo);
303 
304  sky_updateTimes(j2kdUtc, deltas, &atime);
305 
306  /* Calculate Moon apparent position */
307  moon_nrelApparent(atime.j2kTT_cy, &pos);
308 
309  /* Convert apparent position to topocentric Azimuth/Elevation coords */
310  sky0_appToTirs(&pos.appCirsV, atime.j2kUT1_d, pos.eqEq_rad, &terInterV);
311  sky_siteTirsToTopo(&terInterV, pos.distance_au, site, topo);
312 }
313 
314 
315 
316 GLOBAL double moon_riseSet(int year,
317  int month,
318  int day,
319  bool getMoonrise,
320  const Sky_DeltaTs *deltas,
321  const Sky_SiteProp *site,
322  Sky_SiteHorizon *topo)
323 /*! Routine to calculate the time of moonrise or moonset for the day specified
324  by \a year, \a month and \a day. This function uses the NREL MPA algorithm
325  of moon_nrelTopocentric() to calculate the Moon's position.
326  \returns Moonrise (or moonset) time for the day given in
327  \a year, \a month and \a day (returned as a J2KD date
328  (= JD - 2 451 545.0), UTC timescale).
329  To view this as a local date and time, add this
330  value to \a site->timezone_d and pass the result to
331  function sky_j2kdToCalTime().
332  \param[in] year, month, day
333  Date for which moonrise or moonset time is desired
334  \param[in] getMoonrise If true, get moonrise time. If false, get moonset time
335  \param[in] deltas Delta T values, as set by the sky_initTime() (or
336  sky_initTimeSimple() or sky_initTimeDetailed())
337  routines
338  \param[in] site Properties of the observing site, particularly its
339  geometric location on the surface of the Earth and its
340  time zone, as set by the sky_setSiteLocation() function
341  (or sky_setSiteLoc2()) and sky_setSiteTimeZone().
342  \param[out] topo \b Optional. Topocentric position of the Moon at rise
343  or set, in both rectangular (unit vector) form, and as
344  Azimuth and Elevation (altitude) angles. If you are not
345  interested in these values, you can pass NULL to this
346  parameter.
347 
348  This routine uses an iterative approach. Three iterations seems to be enough.
349 
350  \note
351  If the Moon does not rise (or set) on the specified day, this routine will
352  return the time just before midnight on the previous day, or just after
353  midnight on the next day, when the rise (or set) actually occurs.
354  If the routine encounters an error, it will return 0.0
355 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
356 {
357  Sky_SiteHorizon topo1; // Moon apparent position
358  double estimate_d; // estimate of the MJD of Moon rise or set time
359  Sky_SiteProp st; // copy of site details, without refraction
360  int i; // iteration counter
361 
362  REQUIRE_NOT_NULL(deltas);
363  REQUIRE_NOT_NULL(site);
364 
365  /* Moonrise and set calculations assume a standard refraction at the horizon
366  * of 34 arcminutes, and a Moon semi-diameter of 16 arcminutes. So the
367  * calculation is based on the time at which the UNREFRACTED Moon position is
368  * at -50 arcmin. So copy site information, and set refraction to zero for
369  * that copy, which will be passed to riseSetApprox() */
370  st = *site;
371  st.refracPT = 0.0;
372 
373  /* Make an initial guess of civil noon on the specified day */
374  estimate_d = sky_calTimeToJ2kd(year, month, day,
375  12, 0, 0.0, site->timeZone_d * 24.0);
376 
377  /* Iterate to settle on a time. */
378  for (i = 0; i < 3; i++) {
379  estimate_d = riseSetApprox(estimate_d, getMoonrise, deltas,&st, &topo1);
380  if (estimate_d == 0.0) {
381  /* Error in calculations on the requested day.
382  Don't try another iteration. */
383  break;
384  }
385  }
386 
387  if (topo != NULL) {
388  *topo = topo1;
389  }
390  return estimate_d;
391 }
392 
393 /*
394  *------------------------------------------------------------------------------
395  *
396  * Local functions (not called from other modules)
397  *
398  *------------------------------------------------------------------------------
399  */
400 LOCAL void moonOrbitals(double t_cy, OrbTerms *orb)
401 /* Calculate the fundamental orbital parameters required to get the moon
402  position. This is steps 3.2.1 to 3.2.5 of the NREL SAMPA document.
403  Inputs
404  t_cy - julian centuries since J2000.0, TT timescale
405  Outputs
406  orb - orbital terms for the moon, and eccentricity of Earth's orbit
407 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
408 {
409  double temp_deg;
410 
411  /* Moon's Mean Longitude L' */
412  temp_deg = 218.3164477
413  + t_cy * (481267.88123421
414  + t_cy * (-0.0015786
415  + t_cy * (1.0 / 538841.0
416  + t_cy * (-1.0 / 65194000.0))));
417  orb->lp_rad = normalize(degToRad(temp_deg), TWOPI);
418 
419  /* Mean Elongation of the Moon D */
420  temp_deg = 297.8501921
421  + t_cy * (445267.1114034
422  + t_cy * (-0.0018819
423  + t_cy * (1.0 / 545868.0
424  + t_cy * (-1.0 / 113065000.0))));
425  orb->d_rad = normalize(degToRad(temp_deg), TWOPI);
426 
427  /* Sun's Mean Anomaly M */
428  temp_deg = 357.5291092
429  + t_cy * (35999.0502909
430  + t_cy * (-0.0001536
431  + t_cy * (1.0 / 24490000.0)));
432  orb->m_rad = normalize(degToRad(temp_deg), TWOPI);
433 
434  /* Moon's Mean Anomaly M' */
435  temp_deg = 134.9633964
436  + t_cy * (477198.8675055
437  + t_cy * (0.0087414
438  + t_cy * (1.0 / 69699.0
439  + t_cy * (-1.0 / 14712000.0))));
440  orb->mp_rad = normalize(degToRad(temp_deg), TWOPI);
441 
442  /* Moon's so-called Argument of Latitude F */
443  temp_deg = 93.2720950
444  + t_cy * (483202.0175233
445  + t_cy * (-0.0036539
446  + t_cy * (-1.0 / 3526000.0
447  + t_cy * (1.0 / 863310000.0))));
448  orb->f_rad = normalize(degToRad(temp_deg), TWOPI);
449 
450  /* Eccentricity of the Earth's orbit around the Sun */
451  orb->e = 1.0 - 0.002516 * t_cy - 0.0000074 * t_cy * t_cy;
452 }
453 
454 
455 
456 LOCAL void moonLongDist(const OrbTerms *orb, double *long_udeg, double *dist_m)
457 /* Accumulate longitude and distance terms from table lonD[]. This is steps
458  3.2.6 and 3.2.7 of the NREL SAMPA document.
459  Inputs
460  orb - orbital terms
461  Outputs
462  long_udeg - accumulator of terms in longitude (micro-degrees)
463  dist_m - accumulator of terms in distance (metres)
464 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
465 {
466  static const LonDistTerms lonD[] = {
467  { 0, 0, 1, 0, 6288774,-20905355 },
468  { 2, 0,-1, 0, 1274027,-3699111 },
469  { 2, 0, 0, 0, 658314,-2955968 },
470  { 0, 0, 2, 0, 213618,-569925 },
471  { 0, 1, 0, 0,-185116, 48888 },
472  { 0, 0, 0, 2,-114332,-3149 },
473  { 2, 0,-2, 0, 58793, 246158 },
474  { 2,-1,-1, 0, 57066,-152138 },
475  { 2, 0, 1, 0, 53322,-170733 },
476  { 2,-1, 0, 0, 45758,-204586 },
477  { 0, 1,-1, 0,-40923,-129620 },
478  { 1, 0, 0, 0,-34720, 108743 },
479  { 0, 1, 1, 0,-30383, 104755 },
480  { 2, 0, 0,-2, 15327, 10321 },
481  { 0, 0, 1, 2,-12528, 0 },
482  { 0, 0, 1,-2, 10980, 79661 },
483  { 4, 0,-1, 0, 10675,-34782 },
484  { 0, 0, 3, 0, 10034,-23210 },
485  { 4, 0,-2, 0, 8548,-21636 },
486  { 2, 1,-1, 0,-7888, 24208 },
487  { 2, 1, 0, 0,-6766, 30824 },
488  { 1, 0,-1, 0,-5163,-8379 },
489  { 1, 1, 0, 0, 4987,-16675 },
490  { 2,-1, 1, 0, 4036,-12831 },
491  { 2, 0, 2, 0, 3994,-10445 },
492  { 4, 0, 0, 0, 3861,-11650 },
493  { 2, 0,-3, 0, 3665, 14403 },
494  { 0, 1,-2, 0,-2689,-7003 },
495  { 2, 0,-1, 2,-2602, 0 },
496  { 2,-1,-2, 0, 2390, 10056 },
497  { 1, 0, 1, 0,-2348, 6322 },
498  { 2,-2, 0, 0, 2236,-9884 },
499  { 0, 1, 2, 0,-2120, 5751 },
500  { 0, 2, 0, 0,-2069, 0 },
501  { 2,-2,-1, 0, 2048,-4950 },
502  { 2, 0, 1,-2,-1773, 4130 },
503  { 2, 0, 0, 2,-1595, 0 },
504  { 4,-1,-1, 0, 1215,-3958 },
505  { 0, 0, 2, 2,-1110, 0 },
506  { 3, 0,-1, 0,-892, 3258 },
507  { 2, 1, 1, 0,-810, 2616 },
508  { 4,-1,-2, 0, 759,-1897 },
509  { 0, 2,-1, 0,-713,-2117 },
510  { 2, 2,-1, 0,-700, 2354 },
511  { 2, 1,-2, 0, 691, 0 },
512  { 2,-1, 0,-2, 596, 0 },
513  { 4, 0, 1, 0, 549,-1423 },
514  { 0, 0, 4, 0, 537,-1117 },
515  { 4,-1, 0, 0, 520,-1571 },
516  { 1, 0,-2, 0,-487,-1739 },
517  { 2, 1, 0,-2,-399, 0 },
518  { 0, 0, 2,-2,-381,-4421 },
519  { 1, 1, 1, 0, 351, 0 },
520  { 3, 0,-2, 0,-340, 0 },
521  { 4, 0,-3, 0, 330, 0 },
522  { 2,-1, 2, 0, 327, 0 },
523  { 0, 2, 1, 0,-323, 1165 },
524  { 1, 1,-1, 0, 299, 0 },
525  { 2, 0, 3, 0, 294, 0 },
526  { 2, 0,-1,-2, 0, 8752 },
527  };
528  static const int lonDistSize = ARRAY_SIZE(lonD);
529 
530  int i;
531  double li; // lonD[i].cl, maybe scaled for eccentricity
532  double ri; // lonD[i].cr, maybe scaled for eccentricity
533  double a_rad, sinA, cosA; // angle - summation of args (radian)
534 
535 
536  *long_udeg = 0.0;
537  *dist_m = 0.0;
538  for (i = lonDistSize - 1; i >= 0; i--) {
539  li = lonD[i].cl;
540  ri = lonD[i].cr;
541  if (lonD[i].cm != 0) {
542  // Multiply terms by E or E^2 as appropriate
543  li *= orb->e;
544  ri *= orb->e;
545  if (abs(lonD[i].cm) == 2) {
546  li *= orb->e;
547  ri *= orb->e;
548  }
549  }
550 
551  a_rad = lonD[i].cd * orb->d_rad + lonD[i].cm * orb->m_rad
552  + lonD[i].cmp * orb->mp_rad + lonD[i].cf * orb->f_rad;
553  sincos(a_rad, &sinA, &cosA);
554 
555  *long_udeg += li * sinA;
556  *dist_m += ri * cosA;
557  }
558 }
559 
560 
561 
562 LOCAL double moonLatitude(const OrbTerms *orb)
563 /* Accumulate latitude terms from table lat[]. This is steps 3.2.8 (the first of
564  the two sections marked 3.2.8!) of of the NREL SAMPA document.
565  Returns - accumulator of terms in latitude (micro-degrees)
566  Input
567  orb - orbital terms
568 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
569 {
570  static const LatTerms lat[] = {
571  { 0, 0, 0, 1, 5128122 },
572  { 0, 0, 1, 1, 280602 },
573  { 0, 0, 1,-1, 277693 },
574  { 2, 0, 0,-1, 173237 },
575  { 2, 0,-1, 1, 55413 },
576  { 2, 0,-1,-1, 46271 },
577  { 2, 0, 0, 1, 32573 },
578  { 0, 0, 2, 1, 17198 },
579  { 2, 0, 1,-1, 9266 },
580  { 0, 0, 2,-1, 8822 },
581  { 2,-1, 0,-1, 8216 },
582  { 2, 0,-2,-1, 4324 },
583  { 2, 0, 1, 1, 4200 },
584  { 2, 1, 0,-1,-3359 },
585  { 2,-1,-1, 1, 2463 },
586  { 2,-1, 0, 1, 2211 },
587  { 2,-1,-1,-1, 2065 },
588  { 0, 1,-1,-1,-1870 },
589  { 4, 0,-1,-1, 1828 },
590  { 0, 1, 0, 1,-1794 },
591  { 0, 0, 0, 3,-1749 },
592  { 0, 1,-1, 1,-1565 },
593  { 1, 0, 0, 1,-1491 },
594  { 0, 1, 1, 1,-1475 },
595  { 0, 1, 1,-1,-1410 },
596  { 0, 1, 0,-1,-1344 },
597  { 1, 0, 0,-1,-1335 },
598  { 0, 0, 3, 1, 1107 },
599  { 4, 0, 0,-1, 1021 },
600  { 4, 0,-1, 1, 833 },
601  { 0, 0, 1,-3, 777 },
602  { 4, 0,-2, 1, 671 },
603  { 2, 0, 0,-3, 607 },
604  { 2, 0, 2,-1, 596 },
605  { 2,-1, 1,-1, 491 },
606  { 2, 0,-2, 1,-451 },
607  { 0, 0, 3,-1, 439 },
608  { 2, 0, 2, 1, 422 },
609  { 2, 0,-3,-1, 421 },
610  { 2, 1,-1, 1,-366 },
611  { 2, 1, 0, 1,-351 },
612  { 4, 0, 0, 1, 331 },
613  { 2,-1, 1, 1, 315 },
614  { 2,-2, 0,-1, 302 },
615  { 0, 0, 1, 3,-283 },
616  { 2, 1, 1,-1,-229 },
617  { 1, 1, 0,-1, 223 },
618  { 1, 1, 0, 1, 223 },
619  { 0, 1,-2,-1,-220 },
620  { 2, 1,-1,-1,-220 },
621  { 1, 0, 1, 1,-185 },
622  { 2,-1,-2,-1, 181 },
623  { 0, 1, 2, 1,-177 },
624  { 4, 0,-2,-1, 176 },
625  { 4,-1,-1,-1, 166 },
626  { 1, 0, 1,-1,-164 },
627  { 4, 0, 1,-1, 132 },
628  { 1, 0,-1,-1,-119 },
629  { 4,-1, 0,-1, 115 },
630  { 2,-2, 0, 1, 107 },
631  };
632  static const int latSize = ARRAY_SIZE(lat);
633 
634  int i;
635  double bi; // lat[i].cb, maybe scaled for eccentricity
636  double lat_udeg; // accumulator of terms in latitude (micro-degrees)
637 
638  lat_udeg = 0.0;
639  for (i = latSize - 1; i >= 0; i--) {
640  bi = lat[i].cb;
641  if (lat[i].cm != 0) {
642  // Multiply terms by E or E^2 as appropriate
643  bi *= orb->e;
644  if (abs(lat[i].cm) == 2) {
645  bi *= orb->e;
646  }
647  }
648  lat_udeg += bi * sin(lat[i].cd * orb->d_rad
649  + lat[i].cm * orb->m_rad
650  + lat[i].cmp * orb->mp_rad
651  + lat[i].cf * orb->f_rad);
652  }
653 
654  return lat_udeg;
655 }
656 
657 
658 
659 LOCAL double riseSetApprox(double risesetGuess_d,
660  bool getMoonrise,
661  const Sky_DeltaTs *deltas,
662  const Sky_SiteProp *site,
663  Sky_SiteHorizon *topo)
664 /* Routine to calculate the time of Moon rise or set for the day specified by
665  MJDrisesetGuess. The result returned is an approximate value, whose accuracy
666  depends upon how close MJDrisesetGuess is to true Moon rise or set time.
667 
668  Of course, if this routine is called again with the previous result used as
669  the new guess, the new result will be more accurate.
670  Returns - Improved estimate of time of moonrise or moonset, (or zero
671  if the moon does not rise or does not set on this date)
672  Inputs
673  risesetGuess_d - MJD of date at which moonrise or moonset time is desired,
674  and time of day of a guess of when moonrise or moonset
675  might be
676  getMoonrise - If true, get moonrise time. If false, get moonset time
677  deltas - delta times, to convert from UTC to TT
678  site - properties of the site. Note: it is assumed that the field
679  site->refracPT will be set to 0.0 before calling this
680  routine, in order to calculate an unrefracted position of
681  the Moon (as per note below).
682  Output
683  topo - Topocentric position of Moon at approx. rise or set time
684 
685  Moonrise and set calculations assume a standard refraction at the horizon of
686  34 arcminutes, and a moon semi-diameter of 16 arcminutes. So the calculation
687  is based on the time at which the UNREFRACTED Moon position is at -50 arcmin.
688 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
689 {
690  double ha1_rad; // Hour angle of Moon at time riseSetGuess_d(rad)
691  double dec_rad; // Declination of Moon (radian)
692  double ha2_rad; // Hour angle of Moon at horizon (radian)
693  double cosHa2; // Cos(Hour Angle) at horizon
694  double riseSetApprox_d;// Improved estimate of rise or set time
695 
696  moon_nrelTopocentric(risesetGuess_d, deltas, site, topo);
697  sky_siteAzElToHaDec(&topo->rectV, site, &ha1_rad, &dec_rad);
698 
699  /* Assuming the Moon's declination remains constant over the period, find
700  * out where dec circle intersects the Elevation = -50 arcminutes circle.
701  * Of course the declination does not remain constant, so this estimate of
702  * Hour Angle is approximate, but it will be a better estimate than
703  * ha1_rad. */
704  cosHa2 = (sin(degToRad(-50.0/60.0)) - sin(site->astLat_rad) * sin(dec_rad))
705  / (cos(site->astLat_rad) * cos(dec_rad));
706  /* If there is no intersection, the Moon either doesn't rise or doesn't set
707  on this day at this latitude. (In practice, does this ever happen?) */
708  if (fabs(cosHa2) > 1.0) {
709  riseSetApprox_d = 0.0;
710  } else {
711  if (getMoonrise) {
712  ha2_rad = -acos(cosHa2);
713  } else {
714  ha2_rad = acos(cosHa2);
715  }
716  /* Subtract difference in HA from Solar time (not sidereal time). If
717  the difference is large, there is a risk we will return the rise or
718  set time for the day before or the day after the one requested. Make
719  sure that doesn't happen first. The magic number of 1.03 is a little
720  fudge factor that seems to help this routine converge a bit faster.*/
721  if ((ha1_rad - ha2_rad) > PI) { risesetGuess_d += 1.0; }
722  if ((ha1_rad - ha2_rad) < -PI) { risesetGuess_d -= 1.0; }
723  riseSetApprox_d = risesetGuess_d - (ha1_rad - ha2_rad) / TWOPI * 1.03;
724  }
725  return riseSetApprox_d;
726 }
727 
728 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
general.h
general.h - definitions of general use to (standard) C programs
Sky_TrueEquatorial::appCirsV
V3D_Vector appCirsV
Direction of object in apparent or CIRS coordinates (effectively a unit vector).
Definition: sky.h:110
moon_nrelApparent
void moon_nrelApparent(double j2kTT_cy, Sky_TrueEquatorial *pos)
Calculate the Moon's position as a unit vector and a distance, in apparent coordinates.
Definition: moon.c:223
Sky_DeltaTs
This structure contains relatively constant data, and is set up by one of the three functions sky_ini...
Definition: sky.h:166
skyio.h
skyio.h - output and formatting routines and a read routine
v3d_polarToRect
V3D_Vector * v3d_polarToRect(V3D_Vector *destV, double alpha_rad, double delta_rad)
Converts polar (curvilinear) coordinates to equivalent rectangular (Cartesian) coordinates.
Definition: vectors3d.c:280
sky_siteTirsToTopo
void sky_siteTirsToTopo(const V3D_Vector *terInterV, double dist_au, const Sky_SiteProp *site, Sky_SiteHorizon *topo)
Transform a coordinate vector from the Terrestrial Intermediate Reference System to topocentric Az/El...
Definition: sky-site.c:378
sky0_nutationSpa
void sky0_nutationSpa(double t_cy, Sky0_Nut1980 *nut)
Calculates the nutation in longitude and obliquity, according to the algorithm set out in the NREL SP...
Definition: sky0.c:222
ARRAY_SIZE
#define ARRAY_SIZE(x__)
Because C passes arrays to functions by passing only a pointer to the zero'th element of the array,...
Definition: general.h:194
Sky_SiteProp::astLat_rad
double astLat_rad
Astronomical latitude of site (ϕA) (radian)
Definition: sky.h:316
sky_updateTimes
void sky_updateTimes(double j2kUtc_d, const Sky_DeltaTs *d, Sky_Times *t)
Convert the given "J2KD" in the UTC timescale to the other timescales, and pre-calculate some other q...
Definition: sky-time.c:206
Sky_TrueEquatorial
Struct used for holding an object's coordinates in equatorial apparent or Intermediate coordinates.
Definition: sky.h:106
moon_riseSet
double moon_riseSet(int year, int month, int day, bool getMoonrise, const Sky_DeltaTs *deltas, const Sky_SiteProp *site, Sky_SiteHorizon *topo)
Routine to calculate the time of moonrise or moonset for the day specified by year,...
Definition: moon.c:316
LOCAL
#define LOCAL
C has some very silly default behaviours.
Definition: general.h:133
PI
#define PI
,
Definition: general.h:158
GLOBAL
#define GLOBAL
See above.
Definition: general.h:134
Sky_Times::j2kTT_cy
double j2kTT_cy
Julian centuries since J2000.0, TT timescale [T].
Definition: sky.h:188
REQUIRE_NOT_NULL
#define REQUIRE_NOT_NULL(pointer_)
,
Definition: general.h:308
Sky_Times
This structure contains the continuously varying time (and earth rotation) data, in various forms tha...
Definition: sky.h:184
Sky_TrueEquatorial::eqEq_rad
double eqEq_rad
Equation of the Equinoxes (radian).
Definition: sky.h:115
Sky_SiteProp
Site properties.
Definition: sky.h:315
sky0_appToTirs
void sky0_appToTirs(const V3D_Vector *appV, double j2kUT1_d, double eqEq_rad, V3D_Vector *terInterV)
Convert a position in geocentric apparent coordinates to geocentric coordinates in the Terrestrial In...
Definition: sky0.c:432
Sky0_Nut1980::dPsi_rad
double dPsi_rad
Nutation in longitude (Δψ) (radian)
Definition: sky0.h:74
Sky0_Nut1980::dEps_rad
double dEps_rad
Nutation in obliquity (Δε) (radian)
Definition: sky0.h:75
TWOPI
#define TWOPI
,
Definition: general.h:160
moon_nrelApp2
void moon_nrelApp2(double t_cy, const Sky0_Nut1980 *nut, V3D_Vector *appV, double *dist_au)
Calculates the Moon's position in geocentric apparent coordinates, using the NREL Moon Position Algor...
Definition: moon.c:143
Sky_TrueEquatorial::timestamp_cy
double timestamp_cy
Time applying to the other figures in this struct (centuries since J2000.0, TT timescale)
Definition: sky.h:107
sky0_epsilonSpa
void sky0_epsilonSpa(double t_cy, Sky0_Nut1980 *nut)
Calculate the obliquity of the ecliptic and the equation of the equinoxes.
Definition: sky0.c:334
Sky_SiteProp::timeZone_d
double timeZone_d
time zone offset from UTC (fraction of a day)
Definition: sky.h:323
V3D_Matrix
3x3 matrix.
Definition: vectors3d.h:51
Sky_TrueEquatorial::distance_au
double distance_au
Distance to object (Astronomical Units) or 0.0 for far distant objects (that is, those with negligibl...
Definition: sky.h:112
instead-of-math.h
instead-of-math.h - header to be included instead of math.h
V3D_Vector
3x1 vector.
Definition: vectors3d.h:57
degToRad
static double degToRad(double angle_deg)
Returns angle_deg converted from degrees to radians.
Definition: general.h:180
moon_nrelTopocentric
void moon_nrelTopocentric(double j2kdUtc, const Sky_DeltaTs *deltas, const Sky_SiteProp *site, Sky_SiteHorizon *topo)
Calls moon_nrelApparent() to calculate the Moon's position in apparent coordinates using the NREL Moo...
Definition: moon.c:278
DEFINE_THIS_FILE
#define DEFINE_THIS_FILE
Leave DEFINE_THIS_FILE undefined.
Definition: general.h:252
Sky0_Nut1980::eps0_rad
double eps0_rad
Mean obliquity of ecliptic at date (ε0)(radian)
Definition: sky0.h:76
Sky0_Nut1980
Nutation angles and obliquity.
Definition: sky0.h:73
v3d_createRotationMatrix
V3D_Matrix * v3d_createRotationMatrix(V3D_Matrix *destM, V3D_AxisNames axis, double angle_rad)
Creates a matrix to rotate a coordinate system about an axis.
Definition: vectors3d.c:377
Sky_Times::j2kUT1_d
double j2kUT1_d
days since J2000.0, UT1 timescale [Du]
Definition: sky.h:186
moon.h
moon.h - routines to calculate the Moon's position
sky_siteAzElToHaDec
void sky_siteAzElToHaDec(const V3D_Vector *topoV, const Sky_SiteProp *site, double *hourAngle_rad, double *declination_rad)
Take a topocentric position vector in Azimuth/Elevation frame and use it to calculate the observed Ho...
Definition: sky-site.c:511
Sky0_Nut1980::eqEq_rad
double eqEq_rad
Equation of the Equinoxes (radian)
Definition: sky0.h:77
Sky_SiteProp::refracPT
double refracPT
Refraction correction: pressure & temperature.
Definition: sky.h:322
sincos
static void sincos(double angle_rad, double *sinA, double *cosA)
Calculates sine and cosine of an angle.
Definition: instead-of-math.h:114
sky_calTimeToJ2kd
double sky_calTimeToJ2kd(int year, int month, int day, int hour, int minute, double second, double tz_h)
Return the number of days (and fraction of a day) since noon 2000 Jan 1 (UTC) of the given calendar d...
Definition: sky-time.c:241
v3d_multMxV
V3D_Vector * v3d_multMxV(V3D_Vector *destV, const V3D_Matrix *srcM, const V3D_Vector *srcV)
Multiply 3x3 matrix by 3x1 vector to give a new 3x1 vector, as per equation [destV] = [srcM] * [srcV]...
Definition: vectors3d.c:439
normalize
static double normalize(double x, double range)
Normalizes a cyclic double precision floating point variable x to the interval [0,...
Definition: instead-of-math.h:144
Sky_SiteHorizon
Coordinates of a celestial object in the horizon frame, in both rectangular and polar forms.
Definition: sky.h:129
Sky_SiteHorizon::rectV
V3D_Vector rectV
unit vector in horizon coordinates
Definition: sky.h:130