Sun position
Sun position algorithm
sky0.c
1 /*==============================================================================
2  * sky0.c - astronomical coordinate conversion for NREL Sun Position Algorithm
3  *
4  * Author: David Hoadley
5  *
6  * Description: (see sky0.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 "instead-of-math.h"
41 
42 /* Local and project includes */
43 #include "sky0.h"
44 
45 #include "astron.h"
46 #include "general.h"
47 
48 /*
49  * Local #defines and typedefs
50  */
51 DEFINE_THIS_FILE; // For use by REQUIRE() - assertions.
52 
53 /* Convert from units of 0.1 milliarcsec to radians */
54 #define MILLIARCSECx10_TO_RAD (PI / (180.0 * 3600.0 * 10000.0))
55 
56 /* Nutation constants from NREL SPA algorithm */
57 #define Y_COUNT 63
58 enum {TERM_X0, TERM_X1, TERM_X2, TERM_X3, TERM_X4, TERM_X_COUNT};
59 enum {TERM_PSI_A, TERM_PSI_B, TERM_EPS_C, TERM_EPS_D, TERM_PE_COUNT};
60 #define TERM_Y_COUNT TERM_X_COUNT
61 
62 
63 /*
64  * Prototypes for local functions (not called from other modules)
65  */
66 
67 /*
68  * Global variables accessible by other modules
69  */
70 
71 
72 /*
73  * Local variables (not accessed by other modules)
74  */
75 /* Data tables from NREL SPA algorithm */
76 /* Periodic Terms for the nutation in longitude and obliquity */
77 LOCAL const int Y_TERMS[Y_COUNT][TERM_Y_COUNT]=
78 {
79  {0,0,0,0,1},
80  {-2,0,0,2,2},
81  {0,0,0,2,2},
82  {0,0,0,0,2},
83  {0,1,0,0,0},
84  {0,0,1,0,0},
85  {-2,1,0,2,2},
86  {0,0,0,2,1},
87  {0,0,1,2,2},
88  {-2,-1,0,2,2},
89  {-2,0,1,0,0},
90  {-2,0,0,2,1},
91  {0,0,-1,2,2},
92  {2,0,0,0,0},
93  {0,0,1,0,1},
94  {2,0,-1,2,2},
95  {0,0,-1,0,1},
96  {0,0,1,2,1},
97  {-2,0,2,0,0},
98  {0,0,-2,2,1},
99  {2,0,0,2,2},
100  {0,0,2,2,2},
101  {0,0,2,0,0},
102  {-2,0,1,2,2},
103  {0,0,0,2,0},
104  {-2,0,0,2,0},
105  {0,0,-1,2,1},
106  {0,2,0,0,0},
107  {2,0,-1,0,1},
108  {-2,2,0,2,2},
109  {0,1,0,0,1},
110  {-2,0,1,0,1},
111  {0,-1,0,0,1},
112  {0,0,2,-2,0},
113  {2,0,-1,2,1},
114  {2,0,1,2,2},
115  {0,1,0,2,2},
116  {-2,1,1,0,0},
117  {0,-1,0,2,2},
118  {2,0,0,2,1},
119  {2,0,1,0,0},
120  {-2,0,2,2,2},
121  {-2,0,1,2,1},
122  {2,0,-2,0,1},
123  {2,0,0,0,1},
124  {0,-1,1,0,0},
125  {-2,-1,0,2,1},
126  {-2,0,0,0,1},
127  {0,0,2,2,1},
128  {-2,0,2,0,1},
129  {-2,1,0,2,1},
130  {0,0,1,-2,0},
131  {-1,0,1,0,0},
132  {-2,1,0,0,0},
133  {1,0,0,0,0},
134  {0,0,1,2,0},
135  {0,0,-2,2,2},
136  {-1,-1,1,0,0},
137  {0,1,1,0,0},
138  {0,-1,1,2,2},
139  {2,-1,-1,2,2},
140  {0,0,3,2,2},
141  {2,-1,0,2,2},
142 };
143 
144 LOCAL const double PE_TERMS[Y_COUNT][TERM_PE_COUNT]={
145  {-171996,-174.2,92025,8.9},
146  {-13187,-1.6,5736,-3.1},
147  {-2274,-0.2,977,-0.5},
148  {2062,0.2,-895,0.5},
149  {1426,-3.4,54,-0.1},
150  {712,0.1,-7,0},
151  {-517,1.2,224,-0.6},
152  {-386,-0.4,200,0},
153  {-301,0,129,-0.1},
154  {217,-0.5,-95,0.3},
155  {-158,0,0,0},
156  {129,0.1,-70,0},
157  {123,0,-53,0},
158  {63,0,0,0},
159  {63,0.1,-33,0},
160  {-59,0,26,0},
161  {-58,-0.1,32,0},
162  {-51,0,27,0},
163  {48,0,0,0},
164  {46,0,-24,0},
165  {-38,0,16,0},
166  {-31,0,13,0},
167  {29,0,0,0},
168  {29,0,-12,0},
169  {26,0,0,0},
170  {-22,0,0,0},
171  {21,0,-10,0},
172  {17,-0.1,0,0},
173  {16,0,-8,0},
174  {-16,0.1,7,0},
175  {-15,0,9,0},
176  {-13,0,7,0},
177  {-12,0,6,0},
178  {11,0,0,0},
179  {-10,0,5,0},
180  {-8,0,3,0},
181  {7,0,-3,0},
182  {-7,0,0,0},
183  {-7,0,3,0},
184  {-7,0,3,0},
185  {6,0,0,0},
186  {6,0,-3,0},
187  {6,0,-3,0},
188  {-6,0,3,0},
189  {-6,0,3,0},
190  {5,0,0,0},
191  {-5,0,3,0},
192  {-5,0,3,0},
193  {-5,0,3,0},
194  {4,0,0,0},
195  {4,0,0,0},
196  {4,0,0,0},
197  {-4,0,0,0},
198  {-4,0,0,0},
199  {-4,0,0,0},
200  {3,0,0,0},
201  {-3,0,0,0},
202  {-3,0,0,0},
203  {-3,0,0,0},
204  {-3,0,0,0},
205  {-3,0,0,0},
206  {-3,0,0,0},
207  {-3,0,0,0},
208 };
209 
210 
211 /*
212  *==============================================================================
213  *
214  * Implementation
215  *
216  *==============================================================================
217  *
218  * Global functions callable by other modules
219  *
220  *------------------------------------------------------------------------------
221  */
222 GLOBAL void sky0_nutationSpa(double t_cy, Sky0_Nut1980 *nut)
223 /*! Calculates the nutation in longitude and obliquity, according to the
224  algorithm set out in the NREL SPA document. This is a simplified version
225  of the IAU 1980 Nutation Theory. Calculates first the fundamental nutation
226  arguments, and then a series of terms. There are 106 terms in the full
227  series, but this routine uses only the largest 63 of those terms.
228  \param[in] t_cy centuries since J2000.0, TT timescale
229  \param[out] nut field \a nut->dPsi_rad - Nutation in longitude Δψ (radian)\n
230  field \a nut->dEps_rad - Nutation in obliquity Δε (radian)
231 
232  \par References:
233  Reda, I. and Andreas, A. (2003), "Solar Position Algorithm
234  for Solar Radiation Applications", National Renewable Energy Laboratory,
235  NREL publication no. NREL/TP-560-34302, section 3.4.
236  However, the theory on which it is based, and the full sequence of terms,
237  can be found in:\n
238  Final report of the IAU Working Group on Nutation,
239  chairman P.K.Seidelmann, 1980,
240  published in _Celestial Mechanics_, Vol 27, pp79-106, 1982.\n
241  also
242  Kaplan,G.H. _USNO circular no. 163_, pp A3-6, 1981.\n
243  _Supplement to the Astronomical Almanac_ 1984.
244 
245  \par When to call this function
246  It is quite likely that you will not need to call this function directly. It
247  is used in the Solar Position Algorithm and the Moon Position Algorithm, so
248  if you call sun_nrelApparent() or sun_nrelTopocentric(), or
249  call moon_nrelApparent() or moon_nrelTopocentric(), those routines will call
250  this routine for you. Likewise, if you are tracking the Sun or Moon using
251  the skyfast module, the call to skyfast_init() will call either
252  sun_nrelApparent() or moon_nrelApparent(), and therefore call this routine
253  for you.
254  \par
255  The values calculated by this routine change only slowly. So if you are
256  calling it yourself, you can call it infrequently. Intervals of up to an
257  hour between calls will not introduce much error.
258 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
259 {
260  /* Fundamental Nutation arguments at date */
261  double l; // l - mean anomaly of the Moon (radian) (usually called M?)
262  double lp; // l' - mean anomaly of the Sun (Earth) (radian) (M'?)
263  double om; /* Ω - longitude of the ascending node of the Moon's mean orbit
264  on the ecliptic, measured from the mean equinox of date
265  (radian) */
266  double d; // D - mean elongation of the Moon from the Sun (radian)
267  double f; // F - Mean longitude of the Moon (L) minus mean longitude of
268  // the Moon's node (radian), = L - Ω
269 
270  double psiSum_masx10; // Nutation in longitude (units - 0.1 mas)
271  double epsSum_masx10; // Nutation in obliquity (units - 0.1 mas)
272  int i;
273  double a_rad; // angle - summation of args (radian)
274 
275  REQUIRE_NOT_NULL(nut);
276 
277  // Calculate FUNDAMENTAL ARGUMENTS in the FK5 reference system
278 
279  // Mean elongation of the moon from the sun (called X0 in NREL SPA)
280  d = degToRad(297.85036 + t_cy * (445267.11148
281  + t_cy * (-0.0019142
282  + t_cy * (1.0/189474.0))));
283 
284  // Solar Mean Anomaly = Mean longitude of the sun minus mean longitude of
285  // the sun's perigee (called X1 in NREL SPA)
286  lp = degToRad(357.52772 + t_cy * (35999.05034
287  + t_cy * (-0.0001603
288  + t_cy * (-1.0/300000.0))));
289 
290  // Lunar Mean Anomaly = Mean longitude of the moon minus mean longitude of
291  // the moon 's perigee (called X2 in NREL SPA)
292  l = degToRad(134.96298 + t_cy * (477198.867398
293  + t_cy * (0.0086972
294  + t_cy * (1.0/56250.0))));
295 
296  // Mean longitude of the moon minus mean longitude of the moon's node
297  // (called X3 in NREL SPA and (mistakenly, I think) called the Moon's
298  // Argument of Latitude)
299  f = degToRad(93.27191 + t_cy * (483202.017538
300  + t_cy * (-0.0036825
301  + t_cy * (1.0/327270.0))));
302 
303  // Longitude of the mean ascending node of the lunar orbit on the
304  // ecliptic, measured from the mean equinox of date (X4 in NREL SPA)
305  om = degToRad(125.04452 + t_cy * (-1934.136261
306  + t_cy * (0.0020708
307  + t_cy * (1.0/450000.0))));
308 #if 0
309  printf("Nutation Args: d (X0) = %f°, lp (X1) = %f°, l (X2) = %f°\n"
310  " f (X3) = %f°, om (X4) = %f°\n",
311  radToDeg(d), radToDeg(lp), radToDeg(l), radToDeg(f), radToDeg(om));
312 #endif
313 
314  // Multiply through the table of nutation co-efficients and add up all the
315  // terms.
316  psiSum_masx10 = 0.0;
317  epsSum_masx10 = 0.0;
318  for (i = 0; i < Y_COUNT; i++) {
319  a_rad = d * Y_TERMS[i][0] + lp * Y_TERMS[i][1] + l * Y_TERMS[i][2]
320  + f * Y_TERMS[i][3] + om * Y_TERMS[i][4];
321  psiSum_masx10 += sin(a_rad) * (PE_TERMS[i][TERM_PSI_A]
322  + t_cy * PE_TERMS[i][TERM_PSI_B]);
323  epsSum_masx10 += cos(a_rad) * (PE_TERMS[i][TERM_EPS_C]
324  + t_cy * PE_TERMS[i][TERM_EPS_D]);
325  }
326 
327  // Convert results to radians
328  nut->dPsi_rad = psiSum_masx10 * MILLIARCSECx10_TO_RAD;
329  nut->dEps_rad = epsSum_masx10 * MILLIARCSECx10_TO_RAD;
330 }
331 
332 
333 
334 GLOBAL void sky0_epsilonSpa(double t_cy, Sky0_Nut1980 *nut)
335 /*! Calculate the obliquity of the ecliptic and the equation of the equinoxes
336  \param[in] t_cy centuries since J2000.0, TT timescale
337  \param[in,out] nut [in] field \a nut->dPsi_rad - Nutation in longitude Δψ,
338  as returned by function sky0_nutationSpa()
339  (radian)\n
340  [in] field \a nut->dEps_rad - Nutation in obliquity Δε,
341  as returned by function sky0_nutationSpa()
342  (radian)\n
343  [out] field \a nut->eps0_rad - Mean obliquity of the
344  ecliptic ε0 (radian)\n
345  [out] field \a nut->eqEq_rad - Equation of the equinoxes
346  = Δψ * cos(ε0 + Δε) (radian) Note: not seconds
347 
348  \par Reference
349  Reda, I. and Andreas, A. (2003), "Solar Position Algorithm
350  for Solar Radiation Applications", National Renewable Energy Laboratory,
351  NREL publication no. NREL/TP-560-34302, section 3.5.1
352 
353  \par When to call this function
354  It is quite likely that you will not need to call this function directly. It
355  is used in the Solar Position Algorithm and the Moon Position Algorithm, so
356  if you call sun_nrelApparent() or sun_nrelTopocentric(), or
357  call moon_nrelApparent() or moon_nrelTopocentric(), those routines will call
358  this routine for you. Likewise, if you are tracking the Sun or Moon using
359  the skyfast module, the call to skyfast_init() will call either
360  sun_nrelApparent() or moon_nrelApparent(), and therefore call this routine
361  for you.
362  \par
363  The values calculated by this routine change only slowly. So if you are
364  calling it yourself, you can call it infrequently. Intervals of up to an
365  hour between calls will not introduce much error.
366 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
367 {
368  double eps0_as;
369  double u = t_cy/100.0;
370 
371  REQUIRE_NOT_NULL(nut);
372 
373  eps0_as = 84381.448
374  + u * (-4680.93
375  + u * (-1.55
376  + u * (1999.25
377  + u * (-51.38
378  + u * (-249.67
379  + u * (-39.05
380  + u * (7.12
381  + u * (27.87
382  + u * (5.79
383  + u * 2.45)))))))));
384  nut->eps0_rad = arcsecToRad(eps0_as);
385  // Calculate the Equation of the Equinoxes in radian (not in seconds, which
386  // is more usually done)
387  nut->eqEq_rad = nut->dPsi_rad * cos(nut->eps0_rad + nut->dEps_rad);
388 }
389 
390 
391 
393 /*! Calculate the Greenwich mean sidereal time using the algorithm from the NREL
394  SPA document. This is basically the IAU 1982 algorithm, but the various
395  constants have been scaled in degrees.
396  \returns Greenwich Mean Sidereal Time (radian)
397  \param[in] du days since J2000.0, UT1 timescale
398 
399  \par Reference
400  Reda, I. and Andreas, A. (2003), "Solar Position Algorithm
401  for Solar Radiation Applications", National Renewable Energy Laboratory,
402  NREL publication no. NREL/TP-560-34302, section 3.8.1 & 3.8.2
403 
404  \par When to call this function
405  If you are tracking a celestial object, you need not call this function
406  directly. So long as you call sky0_appToTirs() every time around your
407  control loop, it will call this function for you.
408 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
409 {
410  static const double B0 = 280.46061837 * DEG2RAD; // 67310.54841 s
411  static const double B1 = 360.98564736629 * DEG2RAD; // 86636.55536791 s
412  static const double B2 = 0.000387933 * DEG2RAD; // 0.093104 s
413  static const double B3 = (-1.0 / 38710000.0) * DEG2RAD; // -6.2e-6 s
414 #if 0
415  static const double B0d = 4.89496121274; // 3579;
416  static const double B1d = 6.300388098984957;
417  static const double B2d = 6.77070812713916E-06;
418  static const double B3d = -4.50872966157151E-10;
419 #endif
420 
421  double gmst_rad; // Greenwich Mean Sidereal Time (radian)
422  double tu; // Julian centuries since J2000.0, UT1 timescale
423 
424  tu = du / JUL_CENT;
425  gmst_rad = B0 + B1 * du + (tu * tu * (B2 + (tu * B3)));
426 
427  return normalize(gmst_rad, TWOPI);
428 }
429 
430 
431 
433  double j2kUT1_d,
434  double eqEq_rad,
435  V3D_Vector *terInterV)
436 /*! Convert a position in geocentric apparent coordinates to geocentric
437  coordinates in the Terrestrial Intermediate Reference System. This is the
438  first stage of converting apparent coordinates to topocentric coordinates.
439  The resulting vector depends upon the current rotational position of the
440  Earth. (For the second stage, to obtain topocentric coordinates, call
441  routine sky_siteTirsToTopo()).
442  \param[in] appV Position vector of apparent place
443  (unit vector in equatorial coordinates)
444  \param[in] j2kUT1_d days since J2000.0, UT1 timescale, as returned by function
445  sky_updateTimes() in the \a j2kUT1_d field of the
446  Sky_Times struct.
447  \param[in] eqEq_rad Equation of the equinoxes (radian), as returned by function
448  sky0_epsilonSpa() in the \a eqEq_rad field of the
449  Sky0_Nut1980 struct.
450 
451  \param[out] terInterV Position vector in Terrestrial Intermediate Ref System
452 
453  \par When to call this function
454  When you have the position of a celestial object expressed in Apparent
455  coordinates, use this function to convert it to Terrestrial coordinates at
456  the relevant rotational position of the earth. (Effectively you are
457  converting from Right Ascension and Declination to the negative of the
458  Greenwich Hour Angle and Declination.)
459  If you are running a control loop to enable continuous tracking
460  of this object, you will need to call this function (once) every time around
461  your control loop.
462  \par
463  Follow this function with a call to sky_siteTirsToTopo() to obtain the
464  object's position in topocentric coordinates at the observing site.
465 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
466 {
467  double gast_rad; // Greenwich Apparent Sidereal Time (GAST)
468  V3D_Matrix earthRotM; // rotation matrix for current GAST
469 
470  REQUIRE_NOT_NULL(appV);
471  REQUIRE_NOT_NULL(terInterV);
472 
473  /* Get sidereal times */
474  gast_rad = sky0_gmSiderealTimeSpa(j2kUT1_d) + eqEq_rad;
475 
476  /* Create earth rotation matrix from the current Apparent Sidereal Time */
477  v3d_createRotationMatrix(&earthRotM, Zaxis, gast_rad);
478 
479  /* Convert apparent posn to posn in Terrestrial Intermediate Ref System */
480  v3d_multMxV(terInterV, &earthRotM, appV);
481 }
482 
483 
484 /*
485  *------------------------------------------------------------------------------
486  *
487  * Local functions (not called from other modules)
488  *
489  *------------------------------------------------------------------------------
490  */
491 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
492 
493 
general.h
general.h - definitions of general use to (standard) C programs
sky0_gmSiderealTimeSpa
double sky0_gmSiderealTimeSpa(double du)
Calculate the Greenwich mean sidereal time using the algorithm from the NREL SPA document.
Definition: sky0.c:392
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
astron.h
astron.h - assorted definitions useful for astronomy
LOCAL
#define LOCAL
C has some very silly default behaviours.
Definition: general.h:133
GLOBAL
#define GLOBAL
See above.
Definition: general.h:134
REQUIRE_NOT_NULL
#define REQUIRE_NOT_NULL(pointer_)
,
Definition: general.h:308
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
DEG2RAD
#define DEG2RAD
degrees to radians
Definition: general.h:165
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
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
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
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
JUL_CENT
#define JUL_CENT
Length of Julian Century in days.
Definition: astron.h:27
Sky0_Nut1980::eqEq_rad
double eqEq_rad
Equation of the Equinoxes (radian)
Definition: sky0.h:77
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
radToDeg
static double radToDeg(double angle_rad)
Returns angle_rad converted from radians to degrees.
Definition: general.h:182
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
sky0.h
sky0.h - astronomical coordinate conversion for NREL Sun Position Algorithm