Sun position
Sun position algorithm
skyfast.c
1 /*==============================================================================
2  * skyfast.c - set up and use interpolation for rapid calculation of a celestial
3  * object's apparent coordinates.
4  *
5  * Author: David Hoadley
6  *
7  * Description: (see skyfast.h)
8  *
9  * Copyright (c) 2020, David Hoadley <vcrumble@westnet.com.au>
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  * * Redistributions of source code must retain the above copyright notice, this
16  * list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright notice,
18  * this list of conditions and the following disclaimer in the documentation
19  * and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  *==============================================================================
34  */
35 
36 /* ANSI includes etc. */
37 #include <math.h>
38 
39 /* Local and project includes */
40 #include "skyfast.h"
41 
42 #include "sky0.h"
43 #include "astron.h"
44 #include "general.h"
45 #include "sun.h"
46 
47 /*
48  * Local #defines and typedefs
49  */
50 DEFINE_THIS_FILE; // For use by REQUIRE() - assertions.
51 
52 /* --- Definitions that you may need to or wish to modify --- */
53 //#define BARE_METAL_THREADS
54 //#define POSIX_THREADS
55 //#define NO_THREADS
56 #if defined(BARE_METAL_THREADS)
57 #define startCriticalSection() disableInterrupts()
58 #define endCriticalSection() enableInterrupts()
59 
60 #elif defined(POSIX_THREADS)
61 #include <pthread.h>
62 
63 #define startCriticalSection() pthread_mutex_lock(&mutex)
64 #define endCriticalSection() pthread_mutex_unlock(&mutex)
65 
66 #else /* Must be NO_THREADS */
67 #define startCriticalSection() ((void)0)
68 #define endCriticalSection() ((void)0)
69 
70 #endif
71 
72 
73 
74 /*
75  * Prototypes for local functions (not called from other modules)
76  */
77 
78 
79 /*
80  * Global variables accessible by other modules
81  */
82 
83 
84 /*
85  * Local variables (not accessed by other modules)
86  */
87 LOCAL Sky_TrueEquatorial lfiA, lfiB, lfiC;
88 LOCAL Sky_TrueEquatorial *last = &lfiA; // items calculated for time in past
89 LOCAL Sky_TrueEquatorial *next = &lfiB; // items calculated for time ahead
90 LOCAL Sky_TrueEquatorial *oneAfter = &lfiC;// ditto for time after next
91 LOCAL volatile bool oneAfterIsValid = false;
92 
93 LOCAL void (*callback)(double j2kTT_cy, Sky_TrueEquatorial *pos);
94 LOCAL double recalcInterval_cy = 0.0; // time between full recalculations
95 
96 #ifdef POSIX_THREADS
97 LOCAL pthread_mutex_t mutex;
98 #endif
99 
100 /*
101  *==============================================================================
102  *
103  * Implementation
104  *
105  *==============================================================================
106  *
107  * Global functions callable by other modules
108  *
109  *------------------------------------------------------------------------------
110  */
111 GLOBAL void skyfast_init(double tStartUtc_d,
112  int fullRecalcInterval_mins,
113  const Sky_DeltaTs *deltas,
114  void (*getApparent)(double j2kTT_cy,
115  Sky_TrueEquatorial *pos)
116  )
117 /*! Initialise those items that take a long time to calculate, but which do not
118  need to be recalculated frequently. This routine calls a function that you
119  supply to calculate the apparent coordinates of a celestial object, its
120  distance, and the Equation of the Equinoxes, as derived from nutation
121  calculations. This routine calls that function
122  1. for the time specified by \a tStartUtc_d,
123  2. for time \a tStartUtc_d + \a fullRecalcalcInterval_mins, and
124  3. for time \a tStartUtc_d + 2 x \a fullRecalcalcInterval_mins.
125  .
126  For example, to track the Sun, specify the function sun_nrelApparent() when
127  calling this routine. To track the Moon, specify the function
128  moon_nrelApparent().
129 
130  The routine skyfast_getApprox() can then be called
131  (at a high frequency if required) to calculate the current position of the
132  object, using these values to do it.
133  \param[in] tStartUtc_d Time for first full calculation using function
134  \a getApparent(). UTC time in "J2KD" form - i.e days
135  since J2000.0 (= JD - 2 451 545.0)
136  \param[in] fullRecalcInterval_mins
137  Interval of time between full recalculation of the
138  object's position using the function supplied to
139  \a getApparent (minutes). This value must be greater
140  than zero. (Otherwise you will get a precondition
141  failure.)
142  \param[in] deltas Delta T values, as set by the sky_initTime() (or
143  sky_initTimeSimple() or sky_initTimeDetailed())
144  routines
145  \param getApparent Function to get the position of a celestial object in
146  apparent coordinates (i.e. referred to the true
147  equinox and equator at the time), in rectangular form.
148 
149  \par When to call this function
150  At program initialisation time.
151  \note
152  Although the parameter name \a getApparent suggests that you must supply a
153  function which calculates an object's position in apparent coordinates, it
154  is also possible to supply a function which returns the position in
155  Celestial Intermediate coordinates (i.e. referred to the true equator and
156  the Celestial Intermediate Origin (CIO) at time \a t_cy) instead. If so,
157  the function does not need to fill in the \a eqEq_rad field of struct
158  Sky_TrueEquatorial.
159 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
160 {
161  Sky_Times atime; // time, in various timescales
162  double calcTimeTT_cy;
163 #ifdef POSIX_THREADS
164  int ret;
165 #endif
166 
167  REQUIRE_NOT_NULL(deltas);
168  REQUIRE_NOT_NULL(getApparent);
169  REQUIRE(fullRecalcInterval_mins > 0);
170 
171 
172 #ifdef POSIX_THREADS
173  ret = pthread_mutex_init(&mutex, NULL);
174  ASSERT(ret == 0); // There is no possible recovery from an error here.
175 #endif
176  /* Save the function address for later call by skyfast_backgroundUpdate() */
177  callback = getApparent;
178 
179  sky_updateTimes(tStartUtc_d, deltas, &atime);
180 
181  /* Save the recalculation rate, converted from minutes to centuries. */
182  recalcInterval_cy = fullRecalcInterval_mins / (1440.0 * JUL_CENT);
183 
184  calcTimeTT_cy = atime.j2kTT_cy;
185  getApparent(calcTimeTT_cy, last);
186 
187  /* Now do the same for the next time (e.g. next hour) */
188  calcTimeTT_cy += recalcInterval_cy;
189  getApparent(calcTimeTT_cy, next);
190 
191  /* And again for the time after */
192  calcTimeTT_cy += recalcInterval_cy;
193  getApparent(calcTimeTT_cy, oneAfter);
194  oneAfterIsValid = true;
195 }
196 
197 
198 
200 /*! Recalculation of the low frequency quantities.
201  Checks whether the calculations of the celestial object's apparent
202  coordinates and the equation of the equinoxes have been performed for the
203  time called "oneAfter" (i.e. the time after the time called "next"). If they
204  have not, this function performs those calculations.
205 
206  This function calls the function that you previously supplied to function
207  skyfast_init() (parameter \a getApparent).
208 
209  \par When to call this function
210  This function is designed to be called in a low priority loop, or at
211  background level, using any available leftover processor time to slowly
212  precalculate values for the "time after next". It needs to have been called
213  before the high frequency routine skyfast_getApprox() (which is
214  interpolating between time "last" and time "next") arrives at time "next"
215  and therefore needs to access the data for time "oneAfter".
216 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
217 {
218  double t_cy;
219 
220  REQUIRE(recalcInterval_cy > 0.0); // skyfast_init() not called before now?
221 
222  if (!oneAfterIsValid) {
223  t_cy = next->timestamp_cy + recalcInterval_cy;
224  callback(t_cy, oneAfter);
225 
226  startCriticalSection();
227  oneAfterIsValid = true;
228  endCriticalSection();
229  }
230 }
231 
232 
233 
234 GLOBAL void skyfast_getApprox(double t_cy,
235  Sky_TrueEquatorial *approx)
236 /*! Get the best approximation to the celestial object's apparent coordinates
237  and distance, and the equation of the equinoxes, based on an interpolation
238  between two sets of such data that we have previously calculated.
239  \param[in] t_cy Julian centuries since J2000.0, TT timescale. This must
240  specify a time no earlier than the time specified in
241  argument \a tStartUtc_d in the call to skyfast_init().
242  \param[out] approx position vector, distance, etc, obtained by interpolation
243 
244  Although the position is described as approximate, the position returned can
245  be very accurate, as shown in \ref page-interpolation, depending on the
246  interpolation interval that was specified to routine skyfast_init().
247 
248  \note
249  This function will return an approximate position in apparent coordinates
250  (i.e. referred to the true equator and equinox at time \a t_cy) if the
251  function that you passed to the skyfast_init() function returned apparent
252  coordinates. This function will return an approximate position in
253  Celestial Intermediate coordinates (i.e. referred to the true equator and
254  the Celestial Intermediate Origin (CIO)) at time \a t_cy) if the
255  function that you passed to the skyfast_init() function returned CIRS
256  coordinates.
257 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
258 {
259  Sky_TrueEquatorial *temp;
260  double a;
261  double b;
262 
263  if (t_cy > next->timestamp_cy) {
264  /* Time t_cy is no longer between last and next, so we need to make next
265  and oneAfter become the new last and next respectively. But this
266  requires that our low frequency/low priority routine has completed
267  filling in all the data for oneAfter. */
268  REQUIRE(oneAfterIsValid);
269 
270  startCriticalSection();
271  temp = last;
272  last = next;
273  next = oneAfter;
274  oneAfter = temp;
275  oneAfterIsValid = false;
276  endCriticalSection();
277  }
278 
279  /* It is a programming error if time t_cy is not between last and next */
280  REQUIRE(t_cy >= last->timestamp_cy);
281  REQUIRE(next->timestamp_cy >= t_cy);
282 
283  if ((next->timestamp_cy - last->timestamp_cy) < SFA) {
284  a = 0.0;
285  b = 1.0;
286  } else {
287  a = (t_cy - last->timestamp_cy)
288  / (next->timestamp_cy - last->timestamp_cy);
289  b = 1.0 - a;
290  }
291  /* Do a simple linear interpolation between the two appCirsV position
292  * vectors last and next. Unlike the two appCirsV vectors, the resulting
293  * vector will not be exactly of unit magnitude. But if the two appCirsV
294  * vectors are less than one degree apart, the resulting position error is
295  * very small (< 0.3′). If the two appCirsV are a few arcminutes
296  * apart, the magnitude error of the resulting vector is negligible. */
297  approx->appCirsV.a[0] = a * next->appCirsV.a[0] + b * last->appCirsV.a[0];
298  approx->appCirsV.a[1] = a * next->appCirsV.a[1] + b * last->appCirsV.a[1];
299  approx->appCirsV.a[2] = a * next->appCirsV.a[2] + b * last->appCirsV.a[2];
300  /* And a linear interpolation of the other two quantities also. */
301  approx->distance_au = a * next->distance_au + b * last->distance_au;
302  approx->eqEq_rad = a * next->eqEq_rad + b * last->eqEq_rad;
303 }
304 
305 
306 /*
307  *------------------------------------------------------------------------------
308  *
309  * Local functions (not called from other modules)
310  *
311  *------------------------------------------------------------------------------
312  */
313 
314 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
315 
316 /*! \page page-skyfast-c Edits you may want to make to skyfast.c
317  *
318  * There are four different ways that you might want to use the skyfast
319  * module. It can be configured to run in two threads or one. To
320  * control this, define one or none of the following three macros. (Don't
321  * define more than one.) They are BARE_METAL_THREADS, POSIX_THREADS or
322  * NO_THREADS.
323  *
324  * The four different approaches are:
325  * 1. The simplest approach is to define NO_THREADS (or not to define any
326  * macro at all), and not to call the skyfast_backgroundUpdate()
327  * routine at all. The limitation is that you will only be able to
328  * track your object for a period that is twice as long as the number
329  * that you pass to the \a fullRecalcInterval_mins parameter of routine
330  * skyfast_init(), in minutes. If you try to track longer than this,
331  * the program will abort.
332  * 2. The next simplest is once again to define NO_THREADS (or not to
333  * define any macro at all), and to put calls to
334  * skyfast_backgroundUpdate() in the same loop as your calls to
335  * skyfast_getApprox(). You will benefit from the faster execution of
336  * skyfast_getApprox(), but every \a fullRecalcInterval_mins minutes,
337  * a full recalculation will take place. That is, the loop will take
338  * much longer to execute than it does all the rest of the time.
339  * 3. If the occasional long loop execution time is not acceptable to you,
340  * (say, it might upset your tracking control loop), consider using two
341  * threads. In this approach, you set up a high-frequency,
342  * high-priority task to drive the control calculations. This task
343  * calls skyfast_getApprox(). A background task calls
344  * skyfast_backgroundUpdate() to perform the slow calculations at very
345  * low priority. To use this approach, define either BARE_METAL_THREADS
346  * or POSIX_THREADS.
347  *
348  * BARE_METAL_THREADS is intended for small embedded processors without
349  * an operating system. You set your high-priority task to be triggered
350  * by a timer interrupt. To control access to shared data, this makes
351  * use of two other macros, which you will need to define for your
352  * processor. They are called \c disableInterrupts() and
353  * \c enableInterrupts(), and it should be obvious from those names
354  * what it is that you need to make them do.
355  * 4. The fourth approach is basically the same as the third, but it
356  * enables implementation on processors running a POSIX-compliant
357  * operating system (such as Linux). You will need to define the
358  * POSIX_THREADS macro, and then create the posix
359  * threads yourself; this macro simply causes this module to use the
360  * pthreads Mutex mechanism to control access to shared data.
361  */
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
REQUIRE
#define REQUIRE(test_)
Check preconditions.
Definition: general.h:273
astron.h
astron.h - assorted definitions useful for astronomy
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
skyfast_getApprox
void skyfast_getApprox(double t_cy, Sky_TrueEquatorial *approx)
Get the best approximation to the celestial object's apparent coordinates and distance,...
Definition: skyfast.c:234
LOCAL
#define LOCAL
C has some very silly default behaviours.
Definition: general.h:133
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
SFA
#define SFA
A very small number, used to avoid divide by 0 errors.
Definition: general.h:169
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
skyfast_backgroundUpdate
void skyfast_backgroundUpdate(void)
Recalculation of the low frequency quantities.
Definition: skyfast.c:199
skyfast_init
void skyfast_init(double tStartUtc_d, int fullRecalcInterval_mins, const Sky_DeltaTs *deltas, void(*getApparent)(double j2kTT_cy, Sky_TrueEquatorial *pos))
Initialise those items that take a long time to calculate, but which do not need to be recalculated f...
Definition: skyfast.c:111
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
ASSERT
#define ASSERT(test_)
Uses standard assert.
Definition: general.h:253
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
DEFINE_THIS_FILE
#define DEFINE_THIS_FILE
Leave DEFINE_THIS_FILE undefined.
Definition: general.h:252
JUL_CENT
#define JUL_CENT
Length of Julian Century in days.
Definition: astron.h:27
skyfast.h
skyfast.h - set up and use interpolation for rapid calculation of a celestial object's apparent coord...
sun.h
sun.h - routines to calculate the Sun's position
sky0.h
sky0.h - astronomical coordinate conversion for NREL Sun Position Algorithm