Sun position
Sun position algorithm
sun.c
1 /*==============================================================================
2  * sun.c - routines to calculate the Sun's position
3  *
4  * Author: David Hoadley
5  *
6  * Description: (see sun.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 normalize() */
42 
43 /* Local and project includes */
44 #include "sun.h"
45 
46 #include "astron.h"
47 #include "general.h"
48 #include "vectors3d.h"
49 
50 /*
51  * Local #defines and typedefs
52  */
53 DEFINE_THIS_FILE; // For use by REQUIRE() - assertions.
54 
55 /* Constants from NREL SPA algorithm */
56 #define L_COUNT 6
57 #define B_COUNT 2
58 #define R_COUNT 5
59 
60 typedef struct {
61  double a; // term A in (A * cos(B + C * t)). (called TERM_A in SPA)
62  double cb; // term B in (A * cos(B + C * t)). (called TERM_B in SPA)
63  double cct; // term C in (A * cos(B + C * t)). (called TERM_C in SPA)
64 } SunSeries;
65 
66 
67 /*
68  * Prototypes for local functions (not called from other modules).
69  */
70 LOCAL double sunLongitude(double t_ka);
71 LOCAL double sunLatitude(double t_ka);
72 LOCAL double sunDistance(double t_ka);
73 LOCAL double solarNoonApprox(double noonGuess_d,
74  const Sky_DeltaTs *deltas,
75  const Sky_SiteProp *site,
76  Sky_SiteHorizon *topo);
77 LOCAL double riseSetApprox(double risesetGuess_d,
78  bool getSunrise,
79  const Sky_DeltaTs *deltas,
80  const Sky_SiteProp *site,
81  Sky_SiteHorizon *topo);
82 
83 /*
84  * Global variables accessible by other modules
85  */
86 /* (none) */
87 
88 /*
89  * Local variables (not accessed by other modules)
90  */
91 /* (none) */
92 
93 
94 /*
95  *==============================================================================
96  *
97  * Implementation
98  *
99  *==============================================================================
100  *
101  * Global functions callable by other modules
102  *
103  *------------------------------------------------------------------------------
104  */
106  V3D_Vector *appV,
107  double *dist_au)
108 /*! This function calculates an approximate Sun position in apparent coordinates
109  using the algorithm given in the _Astronomical Almanac_. The quoted
110  accuracy is a precision of 0.01 degrees between 1950 and 2050.
111  \param[in] n Days since J2000.0, UT1 timescale
112  \param[out] appV Position vector of Sun in apparent coordinates (unit
113  vector i.e. direction cosines)
114  \param[out] dist_au Geocentric distance of the Sun (Astronomical Units)
115 
116  \par When to call this routine
117  Use this routine for a quick rough calculation of the Sun's position. If you
118  need an accurate position, don't call this routine, call sun_nrelApparent()
119  instead.
120 
121  \par Reference:
122  The _Astronomical Almanac_, 2007, page C24
123 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
124 {
125  double L_deg; // Mean longitude, corrected for aberr (degrees)
126  double g_deg; // Mean anomaly (degrees)
127  double lambda_deg; // Ecliptic longitude (degrees)
128  double epsilon_deg; // Obliquity of ecliptic (degrees)
129 
130  REQUIRE_NOT_NULL(appV);
131  REQUIRE_NOT_NULL(dist_au);
132 
133  L_deg = normalize(280.461 + (0.9856474 * n), 360.0);
134  g_deg = normalize(357.529 + (0.9856003 * n), 360.0);
135  lambda_deg = L_deg + 1.915 * sin(degToRad(g_deg))
136  + 0.02 * sin(degToRad(g_deg + g_deg));
137  epsilon_deg = 23.439 - (0.0000004 * n);
138  appV->a[0] = cos(degToRad(lambda_deg));
139  appV->a[1] = cos(degToRad(epsilon_deg)) * sin(degToRad(lambda_deg));
140  appV->a[2] = sin(degToRad(epsilon_deg)) * sin(degToRad(lambda_deg));
141 
142  *dist_au = 1.00014 - 0.01671 * cos(degToRad(g_deg))
143  - 0.00014 * cos(degToRad(g_deg + g_deg));
144 }
145 
146 
147 
148 GLOBAL void sun_nrelApp2(double t_cy,
149  const Sky0_Nut1980 *nut,
150  V3D_Vector *appV,
151  double *dist_au)
152 /*! This function calculates the Sun position in apparent coordinates, using the
153  NREL SPA algorithm (see reference). The quoted accuracy of this algorithm
154  os 0.0003° (or about 1 arcsecond). It is much more computationally intensive
155  than the approximate algorithm from the _Astronomical Almanac_ implemented
156  by the routine sun_aaApparentApprox().
157  \param[in] t_cy Julian centuries since J2000.0, TT timescale
158  \param[in] nut Nutation terms and obliquity of the ecliptic, as returned
159  by functions sky0_nutationSpa() and sky0_epsilonSpa().
160 
161  \param[out] appV Position vector of Sun in apparent coordinates (unit
162  vector i.e. direction cosines)
163  \param[out] dist_au Geocentric distance of the Sun (Astronomical Units)
164 
165  \par When to call this function
166  In most cases, you will want to call sun_nrelApparent() or
167  sun_nrelTopocentric() instead, and those functions call this function for
168  you.
169 
170  \par Reference:
171  Ibrahim Reda and Afshin Andreas,
172  _Solar Position Algorithm for Solar Radiation Applications_
173  National Renewable Energy Laboratory publication no. NREL/TP-560-34302,
174  Revised January 2008
175 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
176 {
177  double t_ka; // Millennia since J2000.0, TT timescale (J2KM)
178  double lambda_rad; // Sun longitude
179  double beta_rad; // Sun latitude
180  V3D_Matrix epsM; // Rotation by obliquity of ecliptic
181  V3D_Vector eclipV; // Sun position in rectangular ecliptic coordinates
182 
183  REQUIRE_NOT_NULL(appV);
184  REQUIRE_NOT_NULL(dist_au);
185 
186  t_ka = t_cy / 10.0;
187 
188  /* Calculate Sun longitude, latitude and distance from tables (steps 3.2 and
189  3.3 of the algorithm in the SPA document). */
190  *dist_au = sunDistance(t_ka);
191  beta_rad = sunLatitude(t_ka);
192  lambda_rad = sunLongitude(t_ka);
193 
194  /* Calculate the apparent longitude (in ecliptic coordinates), using the
195  nutation in longitude and the light-time aberration correction. (The
196  apparent longitude is the Sun's position 499 seconds earlier than the
197  geometric position, because it took light that much time to reach Earth.)
198  This is steps 3.6 and 3.7 of the SPA algorithm. */
199  lambda_rad += nut->dPsi_rad - arcsecToRad(20.4898) / *dist_au;
200 
201  /* Convert to rectangular coordinates */
202  v3d_polarToRect(&eclipV, lambda_rad, beta_rad);
203 
204  /* Rotate from ecliptic to equatorial coordinates */
205  v3d_createRotationMatrix(&epsM, Xaxis, -(nut->eps0_rad + nut->dEps_rad));
206  v3d_multMxV(appV, &epsM, &eclipV);
207 }
208 
209 
210 
211 GLOBAL void sun_nrelApparent(double j2kTT_cy, Sky_TrueEquatorial *pos)
212 /*! Calculate the Sun's position as a unit vector and a distance, in apparent
213  coordinates. It calls sun_nrelApp2() to obtain the Sun's position, after
214  having called sky0_nutationSpa() to obtain the necessary nutation terms.
215  \param[in] j2kTT_cy Julian centuries since J2000.0, TT timescale
216  \param[out] pos Timestamped structure containing position data and the
217  equation of the equinoxes
218 
219  \par When to call this function
220  * Because this function is computationally intensive, you may wish to limit
221  * your use of this function.
222  * - if you want the Sun's position at multiple sites simultaneously at a
223  * single time, call this function, then follow it with a call to routine
224  * sky0_appToTirs(), and then make a separate call to
225  * sky_siteTirsToTopo() for each of one those sites.
226  * - if you want the Sun's position at one or more sites at closely spaced
227  * times (e.g. for tracking the Sun), pass this function to the
228  * skyfast_init() function. skyfast_init() will
229  * call it for you several times, to fully calculate positions that will
230  * be saved and used later for interpolation by the skyfast_getApprox()
231  * function for tracking.
232  * \par
233  * \em Alternatives:
234  * - If you want the Sun's position at a single site only at a single time,
235  * call sun_nrelTopocentric() instead, and it will call this function for
236  * you.
237  * - If you want the Sun's position at a single site for more than one time
238  * but the times are spaced more than a few hours apart, once again call
239  * sun_nrelTopocentric() instead.
240  * - If you want to track the sun, call skyfast_getApprox() instead.
241  * But this requires you to set up interpolation first with function
242  * skyfast_init() (and this function), as described above.
243 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
244 {
245  Sky0_Nut1980 nut;
246 
247  REQUIRE_NOT_NULL(pos);
248 
249  /* Calculate nutation (steps 3.4 of the algorithm in the SPA document) */
250  sky0_nutationSpa(j2kTT_cy, &nut);
251 
252  /* Calculate the mean obliquity of the ecliptic (step 3.5) and the equation
253  of the equinoxes */
254  sky0_epsilonSpa(j2kTT_cy, &nut);
255  pos->eqEq_rad = nut.eqEq_rad;
256 
257  /* Calculate sun apparent position */
258  sun_nrelApp2(j2kTT_cy, &nut, &pos->appCirsV, &pos->distance_au);
259 
260  /* Now set the timestamp*/
261  pos->timestamp_cy = j2kTT_cy;
262 }
263 
264 
265 
266 GLOBAL void sun_nrelTopocentric(double j2kUtc_d,
267  const Sky_DeltaTs *deltas,
268  const Sky_SiteProp *site,
269  Sky_SiteHorizon *topo)
270 /*! Calls sun_nrelApparent() to calculate the Sun's position in apparent
271  coordinates using the NREL Sun Position Algorithm, and then converts this
272  to topocentric horizon coordinates at the specified site.
273  \param[in] j2kUtc_d UTC time in "J2KD" form - i.e days since J2000.0
274  (= JD - 2 451 545.0)
275  \param[in] deltas Delta T values, as set by the sky_initTime() (or
276  sky_initTimeSimple() or sky_initTimeDetailed()) routines
277  \param[in] site Properties of the observing site, particularly its
278  geometric location on the surface of the Earth, as set by
279  the sky_setSiteLocation() function (or sky_setSiteLoc2())
280  \param[out] topo Topocentric position, in both rectangular (unit vector)
281  form, and as Azimuth and Elevation (altitude) angles
282 
283  \par When to call this function
284  Use this function if you are calculating the Sun topocentric position once,
285  for a single site. But if you are going to be calculating it repeatedly, or
286  for multiple sites, use of this function will cause you to perform a great
287  many needless recalculations. Use skyfast_getApprox(), followed by
288  sky0_appToTirs() and sky_siteTirsToTopo() instead.
289 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
290 {
291  Sky_Times atime; // time, in various timescales
292  Sky_TrueEquatorial pos; // geocentric position of the Sun and distance
293  V3D_Vector terInterV; // unit vector in Terrestrial Intermed Ref Sys
294 
295  REQUIRE_NOT_NULL(deltas);
296  REQUIRE_NOT_NULL(site);
297  REQUIRE_NOT_NULL(topo);
298 
299  sky_updateTimes(j2kUtc_d, deltas, &atime);
300 
301  sun_nrelApparent(atime.j2kTT_cy, &pos);
302 
303  /* Convert apparent position to topocentric Azimuth/Elevation coords */
304  sky0_appToTirs(&pos.appCirsV, atime.j2kUT1_d, pos.eqEq_rad, &terInterV);
305  sky_siteTirsToTopo(&terInterV, pos.distance_au, site, topo);
306 }
307 
308 
309 
310 GLOBAL double sun_solarNoon(int year,
311  int month,
312  int day,
313  const Sky_DeltaTs *deltas,
314  const Sky_SiteProp *site,
315  Sky_SiteHorizon *topo)
316 /*! Routine to calculate the time of solar noon (Sun transit) for the day
317  specified by \a year, \a month and \a day. This function uses the NREL SPA
318  algorithm of sun_nrelTopocentric() to calculate the Sun's position.
319  \returns Solar noon for the day given in \a year, \a month and
320  \a day (returned as a J2KD date (= JD - 2 451 545.0),
321  UTC timescale). To view this as a local date and time,
322  add this value to \a site->timezone_d and pass the result
323  to function sky_j2kdToCalTime().
324  \param[in] year, month, day
325  Date for which solar noon is desired
326  \param[in] deltas Delta T values, as set by the sky_initTime() (or
327  sky_initTimeSimple() or sky_initTimeDetailed()) routines
328  \param[in] site Properties of the observing site, particularly its
329  geometric location on the surface of the Earth and its
330  time zone, as set by the sky_setSiteLocation() function
331  (or sky_setSiteLoc2())
332  \param[out] topo \b Optional. Topocentric position of the Sun at
333  transit, in both rectangular (unit vector) form, and as
334  Azimuth and Elevation (altitude) angles. If you are not
335  interested in these values, you can pass NULL to this
336  parameter.
337 
338  This routine uses an iterative approach. Two iterations is all that is needed
339  to get a result within about 0.05 seconds.
340 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
341 {
342  Sky_SiteHorizon topo1; // Sun apparent position
343  double estimate_d; // estimate of the J2KD of solar noon
344 
345  REQUIRE_NOT_NULL(deltas);
346  REQUIRE_NOT_NULL(site);
347 
348  /* Make an initial guess of civil noon on the specified day */
349  estimate_d = sky_calTimeToJ2kd(year, month, day,
350  12, 0, 0.0, site->timeZone_d * 24.0);
351 
352  estimate_d = solarNoonApprox(estimate_d, deltas, site, &topo1);
353  estimate_d = solarNoonApprox(estimate_d, deltas, site, &topo1);
354 
355  if (topo != NULL) {
356  *topo = topo1;
357  }
358  return estimate_d;
359 }
360 
361 
362 
363 GLOBAL double sun_riseSet(int year,
364  int month,
365  int day,
366  bool getSunrise,
367  const Sky_DeltaTs *deltas,
368  const Sky_SiteProp *site,
369  Sky_SiteHorizon *topo)
370 /*! Routine to calculate the time of sunrise or sunset for the day specified by
371  \a year, \a month and \a day. This function uses the NREL SPA algorithm of
372  sun_nrelTopocentric() to calculate the Sun's position.
373  \returns Sunrise (or sunset) time for the day given in \a year,
374  \a month and \a day (returned as a J2KD date
375  (= JD - 2 451 545.0), UTC timescale).
376  To view this as a local date and time, add this
377  value to \a site->timezone_d and pass the result to
378  function sky_j2kdToCalTime().
379  \param[in] year, month, day
380  Date for which sunrise or sunset time is desired
381  \param[in] getSunrise If true, get sunrise time. If false, get sunset time
382  \param[in] deltas Delta T values, as set by the sky_initTime() (or
383  sky_initTimeSimple() or sky_initTimeDetailed())
384  routines
385  \param[in] site Properties of the observing site, particularly its
386  geometric location on the surface of the Earth and its
387  time zone, as set by the sky_setSiteLocation() function
388  (or sky_setSiteLoc2()) and sky_setSiteTimeZone().
389  \param[out] topo \b Optional. Topocentric position of the Sun at rise or
390  set, in both rectangular (unit vector) form, and as
391  Azimuth and Elevation (altitude) angles. If you are not
392  interested in these values, you can pass NULL to this
393  parameter.
394 
395  This routine uses an iterative approach. It does two iterations.
396 
397  \note
398  If the Sun does not rise (or set) on the specified day at the specified
399  latitude, this function will return 0.0
400  \note
401  * This routine assumes a standard refraction at the horizon of 34 arcminutes.
402  * Although this is typical of sunrise/sunset calculations, it is not
403  * necessarily the actual refraction that will occur at any given place or
404  * time. So the results should be considered approximate. Don't expect it to be
405  * be any better than the nearest minute to the time at which the sun appears
406  * or disappears.
407 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
408 {
409  Sky_SiteHorizon topo1; // Sun apparent position
410  double estimate_d; // estimate of the MJD of sunrise or sunset time
411  Sky_SiteProp st; // copy of site details, without refraction
412 
413  REQUIRE_NOT_NULL(deltas);
414  REQUIRE_NOT_NULL(site);
415 
416  /* Sunrise and set calculations assume a standard refraction at the horizon
417  * of 34 arcminutes, and a Sun semi-diameter of 16 arcminutes. So the
418  * calculation is based on the time at which the UNREFRACTED Sun position is
419  * at -50 arcmin. So copy site information, and set refraction to zero for
420  * that copy, which will be passed to riseSetApprox() */
421  st = *site;
422  st.refracPT = 0.0;
423 
424  /* Make an initial guess of 6 AM (or 6 PM) on the specified day */
425  if (getSunrise) {
426  estimate_d = sky_calTimeToJ2kd(year, month, day,
427  6, 0, 0.0, site->timeZone_d * 24.0);
428  } else {
429  estimate_d = sky_calTimeToJ2kd(year, month, day,
430  18, 0, 0.0, site->timeZone_d * 24.0);
431  }
432 
433  estimate_d = riseSetApprox(estimate_d, getSunrise, deltas, &st, &topo1);
434  if (estimate_d == 0.0) {
435  /* Sun does not rise (or set) at the specified latitude on the requested
436  day. Don't try another iteration. */
437  return estimate_d;
438  }
439  estimate_d = riseSetApprox(estimate_d, getSunrise, deltas, &st, &topo1);
440 
441  if (topo != NULL) {
442  *topo = topo1;
443  }
444  return estimate_d;
445 }
446 
447 
448 /*
449  *------------------------------------------------------------------------------
450  *
451  * Local functions (not called from other modules).
452  *
453  *------------------------------------------------------------------------------
454  */
455 LOCAL double sunLongitude(double t_ka)
456 /* This routine performs steps 3.2.1 to 3.2.4, 3.2.6, 3.3.1 and 3.3.2 of the
457  algorithm outlined in the SPA document
458  Returns - Sun geocentric longitude (radian) (geometric)
459  Inputs
460  t_ka - Julian ephemeris millennium, millennia since J2000.0, TT timescale
461 
462  Earth heliocentric longitude terms are stored in array L_TERMS.
463  There are 6 intermediate terms to obtain (L0 -- L5), and each
464  one is obtained by calculating
465  L[i] = sum_over_rows_j( A[j] * cos(B[j] + C[j]*j_ka) )
466  For L0, there are 64 rows j, for L1 there are 34 (etc - value
467  is stored in lSubcount[i])
468  Having obtained L0 -- L5, the longitude is obtained from
469  (L0 + L1*j_ka + L2*j_ka^2 + ... + L5*j_ka^5) / 10^8
470 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
471 {
472  /* Periodic terms for the Sun's ecliptic longitude */
473  static const SunSeries l0[] = {
474  {175347046.0, 0, 0},
475  { 3341656.0, 4.6692568, 6283.07585},
476  { 34894.0, 4.6261, 12566.1517 },
477  {3497.0, 2.7441, 5753.3849},
478  {3418.0, 2.8289, 3.5231},
479  {3136.0, 3.6277, 77713.7715},
480  {2676.0, 4.4181, 7860.4194},
481  {2343.0, 6.1352, 3930.2097},
482  {1324.0, 0.7425, 11506.7698},
483  {1273.0, 2.0371, 529.691 },
484  {1199.0, 1.1096, 1577.3435},
485  {990, 5.233, 5884.927},
486  {902, 2.045, 26.298},
487  {857, 3.508, 398.149},
488  {780, 1.179, 5223.694},
489  {753, 2.533, 5507.553},
490  {505, 4.583, 18849.228},
491  {492, 4.205, 775.523},
492  {357, 2.92, 0.067},
493  {317, 5.849, 11790.629},
494  {284, 1.899, 796.298},
495  {271, 0.315, 10977.079},
496  {243, 0.345, 5486.778},
497  {206, 4.806, 2544.314},
498  {205, 1.869, 5573.143},
499  {202, 2.458, 6069.777},
500  {156, 0.833, 213.299},
501  {132, 3.411, 2942.463},
502  {126, 1.083, 20.775},
503  {115, 0.645, 0.98 },
504  {103, 0.636, 4694.003},
505  {102, 0.976, 15720.839},
506  {102, 4.267, 7.114},
507  { 99, 6.21, 2146.17},
508  { 98, 0.68, 155.42},
509  { 86, 5.98, 161000.69},
510  { 85, 1.3, 6275.96},
511  { 85, 3.67, 71430.7 },
512  { 80, 1.81, 17260.15},
513  { 79, 3.04, 12036.46},
514  { 75, 1.76, 5088.63},
515  { 74, 3.5, 3154.69},
516  { 74, 4.68, 801.82},
517  { 70, 0.83, 9437.76},
518  { 62, 3.98, 8827.39},
519  { 61, 1.82, 7084.9 },
520  { 57, 2.78, 6286.6 },
521  { 56, 4.39, 14143.5 },
522  { 56, 3.47, 6279.55},
523  { 52, 0.19, 12139.55},
524  { 52, 1.33, 1748.02},
525  { 51, 0.28, 5856.48},
526  { 49, 0.49, 1194.45},
527  { 41, 5.37, 8429.24},
528  { 41, 2.4, 19651.05},
529  { 39, 6.17, 10447.39},
530  { 37, 6.04, 10213.29},
531  { 37, 2.57, 1059.38},
532  { 36, 1.71, 2352.87},
533  { 36, 1.78, 6812.77},
534  { 33, 0.59, 17789.85},
535  { 30, 0.44, 83996.85},
536  { 30, 2.74, 1349.87},
537  { 25, 3.16, 4690.48}
538  };
539  static const SunSeries l1[] = {
540  {628331966747.0, 0, 0},
541  {206059.0, 2.678235, 6283.07585},
542  {4303.0, 2.6351, 12566.1517},
543  {425.0, 1.59, 3.523},
544  {119.0, 5.796, 26.298},
545  {109.0, 2.966, 1577.344},
546  {93, 2.59, 18849.23},
547  {72, 1.14, 529.69},
548  {68, 1.87, 398.15},
549  {67, 4.41, 5507.55},
550  {59, 2.89, 5223.69},
551  {56, 2.17, 155.42},
552  {45, 0.4, 796.3 },
553  {36, 0.47, 775.52},
554  {29, 2.65, 7.11},
555  {21, 5.34, 0.98},
556  {19, 1.85, 5486.78},
557  {19, 4.97, 213.3 },
558  {17, 2.99, 6275.96},
559  {16, 0.03, 2544.31},
560  {16, 1.43, 2146.17},
561  {15, 1.21, 10977.08},
562  {12, 2.83, 1748.02},
563  {12, 3.26, 5088.63},
564  {12, 5.27, 1194.45},
565  {12, 2.08, 4694 },
566  {11, 0.77, 553.57},
567  {10, 1.3, 6286.6 },
568  {10, 4.24, 1349.87},
569  { 9, 2.7, 242.73},
570  { 9, 5.64, 951.72},
571  { 8, 5.3, 2352.87},
572  { 6, 2.65, 9437.76},
573  { 6, 4.67, 4690.48}
574  };
575  static const SunSeries l2[] = {
576  {52919.0, 0, 0},
577  { 8720.0, 1.0721, 6283.0758},
578  { 309.0, 0.867, 12566.152},
579  {27, 0.05, 3.52 },
580  {16, 5.19, 26.3 },
581  {16, 3.68, 155.42},
582  {10, 0.76, 18849.23},
583  { 9, 2.06, 77713.77},
584  { 7, 0.83, 775.52},
585  { 5, 4.66, 1577.34},
586  { 4, 1.03, 7.11},
587  { 4, 3.44, 5573.14},
588  { 3, 5.14, 796.3 },
589  { 3, 6.05, 5507.55},
590  { 3, 1.19, 242.73},
591  { 3, 6.12, 529.69},
592  { 3, 0.31, 398.15},
593  { 3, 2.28, 553.57},
594  { 2, 4.38, 5223.69},
595  { 2, 3.75, 0.98}
596  };
597  static const SunSeries l3[] = {
598  {289.0, 5.844, 6283.076},
599  {35, 0, 0},
600  {17, 5.49, 12566.15},
601  { 3, 5.2, 155.42},
602  { 1, 4.72, 3.52},
603  { 1, 5.3, 18849.23},
604  { 1, 5.97, 242.73}
605  };
606  static const SunSeries l4[] = {
607  {114.0, 3.142, 0},
608  {8, 4.13, 6283.08},
609  {1, 3.84, 12566.15}
610  };
611  static const SunSeries l5[] = {
612  {1, 3.14, 0}
613  };
614  /* Allow access to l0..l5 as if they form a single array lt[][] */
615  static const SunSeries *lt[L_COUNT] = { l0, l1, l2, l3, l4, l5 };
616  static const int lSubcount[L_COUNT] = { ARRAY_SIZE(l0),
617  ARRAY_SIZE(l1),
618  ARRAY_SIZE(l2),
619  ARRAY_SIZE(l3),
620  ARRAY_SIZE(l4),
621  ARRAY_SIZE(l5)};
622 
623  double sum[L_COUNT];
624  int i, j;
625  double earthSum;
626  double tPower;
627 
628  /* Calculate the Earth heliocentric longitude (radian) */
629  for (i = 0; i < L_COUNT; i++) {
630  sum[i] = 0.0;
631  for (j = 0; j < lSubcount[i]; j++) {
632  sum[i] += lt[i][j].a * cos(lt[i][j].cb + lt[i][j].cct * t_ka);
633  }
634  }
635 
636  earthSum = 0.0;
637  tPower = 1.0;
638  for (i = 0; i < L_COUNT; i++) {
639  /* replace earthSum += sum[i] * pow(t_ka, i); with faster code */
640  earthSum += sum[i] * tPower;
641  tPower *= t_ka;
642  }
643  earthSum /= 1.0e8;
644 
645  /* Convert Earth heliocentric longitude to Sun geocentric longitude (radian)
646  and force into the range 0 to TwoPi */
647  earthSum += PI;
648 
649  return normalize(earthSum, TWOPI);
650 }
651 
652 
653 
654 LOCAL double sunLatitude(double t_ka)
655 /* This routine performs steps 3.2.7 and 3.3.3 of the algorithm outlined
656  in the SPA document
657  Returns - Sun geocentric latitude (radian)
658  Inputs
659  t_ka - Julian ephemeris millennium, millennia since J2000.0, TT timescale
660 
661  Earth heliocentric latitude terms are stored in array B_TERMS.
662  There are 2 intermediate terms to obtain (B0 -- B1), and each
663  one is obtained by calculating
664  B[i] = sum_over_rows_j( A[j] * cos(B[j] + C[j]*j_ka) )
665  For B0, there are 5 rows j, for B1 there are 2 (value
666  is stored in bSubcount(i))
667  Having obtained B0 -- B1, the latitude is obtained from
668  (B0 + L1*j_ka) / 10^8
669 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
670 {
671  /* Periodic terms for the Sun's ecliptic latitude */
672  static const SunSeries b0[] = {
673  {280.0, 3.199, 84334.662},
674  {102.0, 5.422, 5507.553},
675  { 80.0, 3.88, 5223.69},
676  { 44.0, 3.7, 2352.87},
677  { 32.0, 4.0, 1577.34}
678  };
679  static const SunSeries b1[] = {
680  {9.0, 3.9, 5507.55},
681  {6.0, 1.73, 5223.69}
682  };
683  /* Allow access to b0..b1 as if they form a single array bt[][] */
684  static const SunSeries *bt[B_COUNT] = { b0, b1 };
685  static const int bSubcount[B_COUNT] = { ARRAY_SIZE(b0),
686  ARRAY_SIZE(b1) };
687 
688  double sum[B_COUNT];
689  int i, j;
690  double earthSum;
691 
692  /* Calculate the Earth heliocentric latitude (radian) */
693  for (i = 0; i < B_COUNT; i++) {
694  sum[i] = 0.0;
695  for (j = 0; j < bSubcount[i]; j++) {
696  sum[i] += bt[i][j].a * cos(bt[i][j].cb + bt[i][j].cct * t_ka);
697  }
698  }
699 
700  earthSum = sum[0] + sum[1] * t_ka;
701  earthSum /= 1.0e8;
702 
703  /* Convert Earth heliocentric latitude to Sun geocentric latitude (radian)*/
704  earthSum = -earthSum;
705 
706  return earthSum;
707 }
708 
709 
710 
711 LOCAL double sunDistance(double t_ka)
712 /* This routine performs step 3.2.8 of the algorithm outlined in the SPA
713  document.
714  Returns - distance to the sun (astronomical units)
715  Inputs
716  t_ka - Julian ephemeris millennium, millennia since J2000.0, TT timescale
717 
718  Earth heliocentric distance terms are stored in array R_TERMS.
719  There are 5 intermediate terms to obtain (R0 -- R4), and each
720  one is obtained by calculating
721  R[i] = sum_over_rows_j( A[j] * cos(B[j] + C[j]*j_ka) )
722  For R0, there are 40 rows j, for R1 there are 10 (etc - value
723  is stored in rSubcount(i))
724  Having obtained R0 -- R4, the distance is obtained from
725  (R0 + R1*j_ka + R2*j_ka^2 + ... + R4*j_ka^4) / 10^8
726 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
727 {
728  /* Periodic terms for the Earth - Sun distance */
729  static const SunSeries r0[] = {
730  {100013989.0, 0.0, 0.0},
731  {1670700.0, 3.0984635, 6283.07585},
732  {13956.0, 3.05525, 12566.1517},
733  {3084.0, 5.1985, 77713.7715},
734  {1628.0, 1.1739, 5753.3849},
735  {1576.0, 2.8469, 7860.4194},
736  { 925.0, 5.453, 11506.77},
737  { 542.0, 4.564, 3930.21},
738  { 472.0, 3.661, 5884.927},
739  { 346.0, 0.964, 5507.553},
740  { 329.0, 5.9, 5223.694},
741  { 307.0, 0.299, 5573.143},
742  { 243.0, 4.273, 11790.629},
743  { 212.0, 5.847, 1577.344},
744  { 186.0, 5.022, 10977.079},
745  { 175.0, 3.012, 18849.228},
746  { 110.0, 5.055, 5486.778},
747  { 98.0, 0.89, 6069.78},
748  { 86.0, 5.69, 15720.84},
749  { 86.0, 1.27, 161000.69},
750  { 65.0, 0.27, 17260.15},
751  { 63.0, 0.92, 529.69},
752  { 57.0, 2.01, 83996.85},
753  { 56.0, 5.24, 71430.7},
754  { 49.0, 3.25, 2544.31},
755  { 47.0, 2.58, 775.52},
756  { 45.0, 5.54, 9437.76},
757  { 43.0, 6.01, 6275.96},
758  { 39.0, 5.36, 4694.0},
759  { 38.0, 2.39, 8827.39},
760  { 37.0, 0.83, 19651.05},
761  { 37.0, 4.9, 12139.55},
762  { 36.0, 1.67, 12036.46},
763  { 35.0, 1.84, 2942.46},
764  { 33.0, 0.24, 7084.9},
765  { 32.0, 0.18, 5088.63},
766  { 32.0, 1.78, 398.15},
767  { 28.0, 1.21, 6286.6},
768  { 28.0, 1.9, 6279.55},
769  { 26.0, 4.59, 10447.39}
770  };
771  static const SunSeries r1[] = {
772  {103019.0, 1.10749, 6283.07585},
773  { 1721.0, 1.0644, 12566.1517},
774  {702.0, 3.142, 0.0 },
775  { 32.0, 1.02, 18849.23},
776  { 31.0, 2.84, 5507.55},
777  { 25.0, 1.32, 5223.69},
778  { 18.0, 1.42, 1577.34},
779  { 10.0, 5.91, 10977.08},
780  { 9.0, 1.42, 6275.96},
781  { 9.0, 0.27, 5486.78}
782  };
783  static const SunSeries r2[] = {
784  {4359.0, 5.7846, 6283.0758},
785  { 124.0, 5.579, 12566.152},
786  { 12.0, 3.14, 0.0 },
787  { 9.0, 3.63, 77713.77},
788  { 6.0, 1.87, 5573.14},
789  { 3.0, 5.47, 18849.23}
790  };
791  static const SunSeries r3[] = {
792  {145.0, 4.273, 6283.076},
793  { 7.0, 3.92, 12566.15 }
794  };
795  static const SunSeries r4[] = {
796  {4.0, 2.56, 6283.08}
797  };
798  /* Allow access to r0..r4 as if they form a single array rt[][] */
799  static const SunSeries *rt[R_COUNT] = { r0, r1, r2, r3, r4 };
800  static const int rSubcount[R_COUNT] = { ARRAY_SIZE(r0),
801  ARRAY_SIZE(r1),
802  ARRAY_SIZE(r2),
803  ARRAY_SIZE(r3),
804  ARRAY_SIZE(r4)};
805 
806  double sum[R_COUNT];
807  int i, j;
808  double earthSum;
809  double tPower;
810 
811  for (i = 0; i < R_COUNT; i++) {
812  sum[i] = 0.0;
813  for (j = 0; j < rSubcount[i]; j++) {
814  sum[i] += rt[i][j].a * cos(rt[i][j].cb + rt[i][j].cct * t_ka);
815  }
816  }
817 
818  earthSum = 0.0;
819  tPower = 1.0;
820  for (i = 0; i < R_COUNT; i++) {
821  /* replace earthSum += sum[i] * pow(t_ka, i); with faster code */
822  earthSum += sum[i] * tPower;
823  tPower *= t_ka;
824  }
825  earthSum /= 1.0e8;
826 
827  return earthSum;
828 }
829 
830 
831 
832 LOCAL double solarNoonApprox(double noonGuess_d,
833  const Sky_DeltaTs *deltas,
834  const Sky_SiteProp *site,
835  Sky_SiteHorizon *topo)
836 /* Routine to calculate the time of solar noon for the day specified by
837  noonGuess_d. The result returned is an approximate value, whose accuracy
838  depends upon how close noonGuess_d is to true solar noon.
839  If noonGuess_d is equal to civil noon in local time, the result can be
840  expected to be within a few seconds of the true solar noon. Of course, if
841  this routine is called again with the previous result used as the new guess,
842  the new result will be more accurate. Generally, only one or two calls would
843  ever be required.
844  Returns - Improved estimate of time of solar noon for the day & time
845  given in noonGuess_d, in the form J2KD (= JD - 2 451 545.0)
846  Inputs
847  noonGuess_d - J2KD (= JD - 2 451 545.0) of the date at which solar noon is
848  desired, and the time of day of a guess of when solar noon
849  might be
850  deltas - delta times, to convert from UTC to TT
851  site - site properties
852  Outputs
853  topo - Topocentric position of Sun at transit
854 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
855 {
856  double dec; // ignored
857  double ha_rad; // hour angle of the Sun (radian)
858 
859  sun_nrelTopocentric(noonGuess_d, deltas, site, topo);
860  sky_siteAzElToHaDec(&topo->rectV, site, &ha_rad, &dec);
861 
862  /* HA gives time since celestial object passed meridian (so -ve HA gives
863  time until object will pass meridian) in units of Sidereal time. But
864  the sun moves against the fixed stars in that time, so, as an
865  approximation, subtract HA from Solar time (UT1) instead. */
866  return noonGuess_d - ha_rad / TWOPI;
867 }
868 
869 
870 
871 LOCAL double riseSetApprox(double risesetGuess_d,
872  bool getSunrise,
873  const Sky_DeltaTs *deltas,
874  const Sky_SiteProp *site,
875  Sky_SiteHorizon *topo)
876 /* Routine to calculate the time of Sun rise or set for the day specified by
877  risesetGuess_d. The result returned is an approximate value, whose accuracy
878  depends upon how close risesetGuess_d is to true Sun rise or set time.
879  6 AM local time would be a good starting sunrise guess, and 6PM a good sunset
880  guess.
881  Of course, if this routine is called again with the previous result used as
882  the new guess, the new result will be more accurate. Convergence is not
883  quite as fast as the SolarNoonApprox() algorithm above.
884  Returns - Improved estimate of time of sunrise or sunset, (or zero
885  if the sun does not rise or does not set on this date)
886  Inputs
887  risesetGuess_d - J2KD (= JD - 2 451 545.0) of date at which sunrise or
888  sunset time is desired, and time of day of a guess of when
889  sunrise or sunset might be
890  getSunrise - If true, get sunrise time. If false, get sunset time
891  deltas - delta times, to convert from UTC to TT
892  site - properties of the site. Note: it is assumed that the field
893  site->refracPT will be set to 0.0 before calling this
894  routine, in order to calculate an unrefracted position of
895  the Sun (as per note below).
896  Output
897  topo - Topocentric position of Sun at rise or set
898 
899  Sunrise and set calculations assume a standard refraction at the horizon of
900  34 arcminutes, and a sun semi-diameter of 16 arcminutes. So the calculation
901  is based on the time at which the UNREFRACTED Sun position is at -50 arcmin.
902  *
903  * TODO
904  * This routine uses the equation
905  * cos(HA) = (sin(-50′) - sin(ϕA) sin(δ)) / (cos(ϕA) cos(δ))
906  * which means it will fail if astronomical latitude ϕA = π/2 (i.e. at the
907  * poles), and will presumably get less accurate the closer to the poles we
908  * get. Find a better expression.
909 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
910 {
911  double ha1_rad; // Hour angle of Sun at time riseSetGuess_d(rad)
912  double dec_rad; // Declination of Sun (radian)
913  double ha2_rad; // Hour angle of Sun at horizon (radian)
914  double cosHa2; // Cos(Hour Angle) at horizon
915  double riseSetApprox_d;// Improved estimate of rise or set time
916 
917  sun_nrelTopocentric(risesetGuess_d, deltas, site, topo);
918  sky_siteAzElToHaDec(&topo->rectV, site, &ha1_rad, &dec_rad);
919 
920  /* assuming Dec remains constant over the period, find out where dec circle
921  intersects the Elevation = -50 arcminute circle */
922  cosHa2 = (sin(degToRad(-50.0/60.0)) - sin(site->astLat_rad) * sin(dec_rad))
923  / (cos(site->astLat_rad) * cos(dec_rad));
924  /* If there is no intersection, the Sun either doesn't rise or doesn't set
925  on this day at this latitude. */
926  if (cosHa2 > 1.0) {
927  /* Sun does not rise */
928  riseSetApprox_d = 0.0;
929  } else if (cosHa2 < -1.0) {
930  /* Sun does not set */
931  riseSetApprox_d = 0.0;
932  } else {
933  if (getSunrise) {
934  ha2_rad = -acos(cosHa2);
935  } else {
936  ha2_rad = acos(cosHa2);
937  }
938  /* Subtract difference in HA from Solar time (not sidereal time)
939  i.e. this is the same approximation used in routine
940  solarNoonApprox() above. */
941  riseSetApprox_d = risesetGuess_d - (ha1_rad - ha2_rad) / TWOPI;
942  }
943  return riseSetApprox_d;
944 }
945 
946 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
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
Sky_DeltaTs
This structure contains relatively constant data, and is set up by one of the three functions sky_ini...
Definition: sky.h:166
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
sun_nrelApp2
void sun_nrelApp2(double t_cy, const Sky0_Nut1980 *nut, V3D_Vector *appV, double *dist_au)
This function calculates the Sun position in apparent coordinates, using the NREL SPA algorithm (see ...
Definition: sun.c:148
astron.h
astron.h - assorted definitions useful for astronomy
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
sun_nrelTopocentric
void sun_nrelTopocentric(double j2kUtc_d, const Sky_DeltaTs *deltas, const Sky_SiteProp *site, Sky_SiteHorizon *topo)
Calls sun_nrelApparent() to calculate the Sun's position in apparent coordinates using the NREL Sun P...
Definition: sun.c:266
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
sun_aaApparentApprox
void sun_aaApparentApprox(double n, V3D_Vector *appV, double *dist_au)
This function calculates an approximate Sun position in apparent coordinates using the algorithm give...
Definition: sun.c:105
Sky0_Nut1980::dEps_rad
double dEps_rad
Nutation in obliquity (Δε) (radian)
Definition: sky0.h:75
TWOPI
#define TWOPI
,
Definition: general.h:160
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
arcsecToRad
static double arcsecToRad(double angle_as)
Returns angle_as converted from arcseconds to radians.
Definition: astron.h:43
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
sun_riseSet
double sun_riseSet(int year, int month, int day, bool getSunrise, const Sky_DeltaTs *deltas, const Sky_SiteProp *site, Sky_SiteHorizon *topo)
Routine to calculate the time of sunrise or sunset for the day specified by year, month and day.
Definition: sun.c:363
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
vectors3d.h
vectors3d.h - Three dimensional geometry, vectors and matrices
sun_nrelApparent
void sun_nrelApparent(double j2kTT_cy, Sky_TrueEquatorial *pos)
Calculate the Sun's position as a unit vector and a distance, in apparent coordinates.
Definition: sun.c:211
Sky_Times::j2kUT1_d
double j2kUT1_d
days since J2000.0, UT1 timescale [Du]
Definition: sky.h:186
sun_solarNoon
double sun_solarNoon(int year, int month, int day, const Sky_DeltaTs *deltas, const Sky_SiteProp *site, Sky_SiteHorizon *topo)
Routine to calculate the time of solar noon (Sun transit) for the day specified by year,...
Definition: sun.c:310
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
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
sun.h
sun.h - routines to calculate the Sun's position
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