Sun position
Sun position algorithm
star.c
1 /*==============================================================================
2  * star.c - Astronomical conversion routines for stars and other objects
3  * beyond the Solar System
4  *
5  * Author: David Hoadley
6  *
7  * Description: (see star.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  * Notes:
37  * Character set: UTF-8. (Non-ASCII characters appear in this file)
38  *----------------------------------------------------------------------------*/
39 
40 /* ANSI includes etc. */
41 #include <ctype.h>
42 #include <errno.h>
43 #include "instead-of-math.h" /* for sincos() & normalize() */
44 #include <limits.h> /* for INT_MAX */
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 
49 /* Local and project includes */
50 #include "star.h"
51 
52 #include "sky1.h"
53 #include "astron.h"
54 #include "general.h"
55 #include "sky.h"
56 #include "skyio.h"
57 #include "vectors3d.h"
58 
59 /*
60  * Local #defines and typedefs
61  */
62 DEFINE_THIS_FILE; /* For use by REQUIRE() - assertions. */
63 
64 #define UNUSED_EPOCH_cy (-99.9) /* Set this when epoch is meaningless */
65 #define HALFPI_PLUS_SFA (HALFPI + SFA)
66 #define TROP_YEAR (TROP_CENT / 100.0) /* Length of Tropical year */
67 #define STRINGEMPTY (-2)
68 
69 /*
70  * Prototypes for local functions (not called from other modules)
71  */
72 LOCAL int extractObjectName(const char coordStr[],
73  char nameStr[],
74  size_t nameStrSize,
75  const char **endPtr);
76 LOCAL int parseEpochStr(const char epochStr[],
77  Star_CoordSys *coordSys,
78  double *epochT_cy,
79  const char **endPtr);
80 LOCAL double myStrtod(const char str[], const char **endPtr, int *error);
81 
82 
83 #ifdef PREDEF_STANDARD_C_1999
84 /* Compiler supports inline functions */
85 static inline char *saferStrncpy(char *dest, const char *src, size_t len)
86 /* The standard strncpy() function does not guarantee that the destination
87  string will be NUL-terminated. This one does. So use it instead. */
88 {
89  dest[0] = '\0';
90  if (len > 0) {
91  strncat(dest, src, len - 1);
92  }
93  return dest;
94 }
95 
96 #else
97 /* C89/C90 compiler only - no inline functions. Need macros instead */
98 /* I swore I would never use the horrid comma operator. Here it is. */
99  #define saferStrncpy(dest__, src__, len__) \
100  ( (dest__)[0] = '\0', \
101  ((len__) > 0) ? strncat((dest__), (src__), (len__) - 1) : (dest__) )
102 #endif
103 
104 
105 /*
106  * Global variables accessible by other modules
107  */
108 
109 
110 /*
111  * Local variables (not accessed by other modules)
112  */
113 /* Constants found in the 2007 Astronomical Almanac, pages K6 & K7 */
114 LOCAL const double lightTime_s = 499.0047863852; // time to travel 1 AU(seconds)
115 LOCAL const double au_km = 1.49597871464e8; // one Astronomical Unit (km)
116 
117 /* Derived constants */
118 LOCAL const double invC_dpau = lightTime_s / 86400.0;// 1/c (light speed) (d/AU)
119 LOCAL const double auFactor = 86400.0 / au_km; // Convert km/s to AU/d
120 
121 
122 LOCAL Star_CatalogPosn currentObject; /* For use by star_getApparent() */
123 
124 /*
125  *==============================================================================
126  *
127  * Implementation
128  *
129  *==============================================================================
130  *
131  * Global functions callable by other modules
132  *
133  *------------------------------------------------------------------------------
134  */
136 /*! Copies the catalogue information in \a c to internal storage for later use
137  by star_getApparent()
138  \param[in] c The catalogue information for the star, as set by one of the
139  three functions star_parseCoordString(), star_setCatalogPosn()
140  or star_setCatalogOldStyle().
141 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
142 {
143  currentObject = *c;
144 }
145 
146 
147 
149  double j2kTT_cy,
150  const Sky1_Nut1980 *nut,
151  V3D_Vector *appV,
152  double *dist_au)
153 /*! Convert the catalogue coordinates for a star (or other object outside the
154  Solar System) to apparent coordinates at time \a j2kTT_cy.
155  \param[in] c Catalogue position and motion of object, as set by one
156  of the three functions star_parseCoordString(),
157  star_setCatalogPosn() or star_setCatalogOldStyle().
158  \param[in] j2kTT_cy Julian centuries since J2000.0, TT timescale
159  \param[in] nut Nutation angles and obliquity of the ecliptic at
160  time \a j2kTT_cy, as returned by functions
161  sky1_nutationIAU1980() and sky1_epsilon1980()
162  \param[out] appV Unit vector of geocentric apparent direction of
163  object, referred to the true equator and equinox at
164  time (\a j2kTT_cy)
165  \param[out] dist_au Distance to object (AU) as derived from
166  \a c->parallax_rad.
167 
168  This routine is simplified, in that:
169  - It uses an approximate position for the Earth (calling star_earth())
170  - It does not apply relativistic corrections for gravitational light
171  deflection; this effect is ignored.
172  - Neither does it apply a relativistic annual aberration correction - it
173  uses a simple vector sum of the earth's velocity to calculate annual
174  aberration (calling star_annAberr()).
175 
176  \par References:
177  _Astronomical Almanac_ 1987 pages B39-40, or\n
178  _Astronomical Almanac_ 2007 pages B66-67
179  \note
180  If the field \a c->cSys is equal to #FK4, this routine will return zero for
181  the vector and distance. FK4 stellar positions are not supported (yet).
182 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
183 {
184  Sky1_Prec1976 prec; /* Precession angles */
185  V3D_Matrix cM, bM, aM; /* Used to obtain precession-nutation rotation*/
186  V3D_Matrix npM; /* Precession/nutation combined matrix */
187  V3D_Vector ebV_au; /* Barycentric position of Earth (AU) */
188  V3D_Vector ebdotV_aupd;/* Velocity of the Earth (AU/day) */
189  V3D_Vector sbV_au; /* Barycentric position of Sun (AU) */
190  double tau; /* Elapsed time for proper motion */
191  V3D_Vector qV; /* Barycentric direction of object, (unit vector
192  \b q in _Astronomical Almanac_) */
193  V3D_Vector mV_radpcy; /* Space motion vector, (radian/Julian century)
194  (vector \b m in _Astronomical Almanac_) */
195  V3D_Vector p0V; /* Barycentric direction of celestial object */
196  V3D_Vector pV; /* Geocentric geometric direction of object */
197  V3D_Vector p2V; /* Geocentric "proper" direction of object */
198  double scale;
199 
200  REQUIRE_NOT_NULL(c);
201  REQUIRE_NOT_NULL(appV);
202  REQUIRE_NOT_NULL(dist_au);
203 
204  /* Obtain combined precession/nutation matrix from catalogue position to
205  apparent coordinates. */
206  switch (c->cSys) {
207  case APPARENT:
208  case INTERMEDIATE:
209  /* Coordinates are already apparent. All we need to do is convert to
210  a vector and return. Assume no diurnal parallax. */
211  v3d_polarToRect(appV, c->ra_rad, c->dec_rad);
212  *dist_au = 0.0;
213  return;
214  break;
215 
216  case FK4:
217  /* Not supported yet. */
218  appV->a[0] = appV->a[1] = appV->a[2] = 0.0;
219  *dist_au = 0.0;
220  return;
221  break;
222 
223  case FK5:
224  /* Create combined precession and nutation matrix */
225  sky1_precessionIAU1976(c->eqnxT_cy, j2kTT_cy, &prec);
226  sky1_createPrec1976Matrix(&prec, &bM);
227 
228  sky1_createNut1980Matrix(nut, &aM);
229  v3d_multMxM(&npM, &aM, &bM);
230  break;
231 
232  case ICRS:
233  /* Create combined precession, nutation and frame bias matrix */
234  sky1_frameBiasFK5(&aM);
235  sky1_precessionIAU1976(c->eqnxT_cy, j2kTT_cy, &prec);
236  sky1_createPrec1976Matrix(&prec, &bM);
237  v3d_multMxM(&cM, &bM, &aM);
238 
239  sky1_createNut1980Matrix(nut, &aM);
240  v3d_multMxM(&npM, &aM, &cM);
241  break;
242 
243  default:
244  break;
245  }
246 
247  /* Obtain the position and velocity of the Earth, referred to the catalogue
248  equator and equinox of our object of interest. */
249  star_earth(j2kTT_cy, &npM, &ebV_au, &ebdotV_aupd, &sbV_au);
250 
251  /* Get catalogue position and space motion as vectors */
252  star_catalogToVectors(c, &qV, &mV_radpcy);
253 
254  /* Calculate elapsed time from initial to final epoch in Julian centuries
255  and apply space motion over that elapsed time */
256  tau = j2kTT_cy - c->epochT_cy;
257  p0V.a[0] = (qV.a[0] + tau * mV_radpcy.a[0]);
258  p0V.a[1] = (qV.a[1] + tau * mV_radpcy.a[1]);
259  p0V.a[2] = (qV.a[2] + tau * mV_radpcy.a[2]);
260 
261  /* Apply the correction for annual parallax to obtain the (geometric)
262  geocentric position from the barycentric position. This is given
263  rigorously by the vector sum of its barycentric position and the
264  (negative of the) barycentric position of the earth.
265  [objGeoV] = [objBarV] - annParallax_rad * [ebV_au] */
266  pV.a[0] = (p0V.a[0] - c->parallax_rad * ebV_au.a[0]);
267  pV.a[1] = (p0V.a[1] - c->parallax_rad * ebV_au.a[1]);
268  pV.a[2] = (p0V.a[2] - c->parallax_rad * ebV_au.a[2]);
269 #if 0
270  printVector("Eb", &ebV_au);
271  printVector("Eb'", &ebdotV_aupd);
272  printVector("Sb", &sbV_au);
273  printMatrix("NP", &npM);
274  printVector("q", &qV);
275  printVector("m", &mV_radpcy);
276  printf("tau = %.9f\n", tau);
277  printVector("p0", &p0V);
278  printVector("P", &pV);
279 #endif
280 
281  /* Re-scale vector pV back to unity magnitude */
282  scale = 1.0 / v3d_magV(&pV);
283  pV.a[0] *= scale;
284  pV.a[1] *= scale;
285  pV.a[2] *= scale;
286 
287 #ifdef RUN_RIDICULOUSLY_RIGOROUS_RELATIVISTIC_ROUTINE
288  star_lightDeflection(&pV, &ebV_au, &ebdotV_aupd, &sbV_au, &p2V);
289 #else
290  /* This version does not calculate vector p1 (light deflection) */
291 
292  /* Apply simple annual aberration correction to convert geometric position
293  to "proper" position. */
294  star_annAberr(&pV, &ebdotV_aupd, &p2V);
295 #endif
296 #if 0
297  printVector("p", &pV);
298  printVector("p2", &p2V);
299 #endif
300 
301  /* Calculate distance to object */
302  if (c->parallax_rad <= 0.0) {
303  *dist_au = 0.0;
304  } else {
305  *dist_au = RAD2ARCSEC / c->parallax_rad;
306  }
307 
308  /* Convert to geocentric apparent coordinates by multiplying
309  by matrices for precession and nutation */
310  v3d_multMxV(appV, &npM, &p2V);
311 }
312 
313 
314 
315 GLOBAL void star_getApparent(double j2kTT_cy, Sky_TrueEquatorial *pos)
316 /*! Calculate the position of the currently selected star (or other object
317  outside the solar system) as a unit vector and a distance, in apparent
318  coordinates. It calls star_catalogToApp() to obtain the star's position,
319  after having called sky1_nutationIAU1980() and sky1_epsilon1980() to obtain
320  the necessary nutation terms.
321 
322  The star whose coordinates are obtained with this function is the star most
323  recently specified with star_setCurrentObject()
324  \param[in] j2kTT_cy Julian centuries since J2000.0, TT timescale
325  \param[out] pos Timestamped structure containing position data in
326  apparent coordinates and the equation of the equinoxes.
327 
328  This function is designed to be callable by the skyfast_init() and
329  skyfast_backgroundUpdate() functions in a tracking application.
330 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
331 {
332  Sky1_Nut1980 nut;
333 
334  REQUIRE_NOT_NULL(pos);
335 
336  if (currentObject.cSys == INTERMEDIATE) {
337  pos->eqEq_rad = 0.0;
338 
339  } else {
340  /* Calculate nutation, the mean obliquity of the ecliptic and
341  the equation of the equinoxes */
342  sky1_nutationIAU1980(j2kTT_cy, 0, &nut);
343  sky1_epsilon1980(j2kTT_cy, &nut);
344  pos->eqEq_rad = nut.eqEq_rad;
345  }
346 
347  star_catalogToApp(&currentObject, j2kTT_cy, &nut,
348  &pos->appCirsV, &pos->distance_au);
349 
350  /* Now set the timestamp*/
351  pos->timestamp_cy = j2kTT_cy;
352 }
353 
354 
355 
356 GLOBAL void star_getTopocentric(double j2kUtc_d,
357  const Sky_DeltaTs *deltas,
358  const Sky_SiteProp *site,
359  Sky_SiteHorizon *topo)
360 /*! Calls star_getApparent() to calculate the star's position in apparent
361  coordinates, and then converts this to topocentric horizon coordinates at
362  the specified site.
363 
364  The star whose coordinates are obtained with this function is the star most
365  recently specified with star_setCurrentObject()
366  \param[in] j2kUtc_d UTC time in "J2KD" form - i.e days since J2000.0
367  (= JD - 2 451 545.0)
368  \param[in] deltas Delta T values, as set by the sky_initTime() (or
369  sky_initTimeSimple() or sky_initTimeDetailed()) routines
370  \param[in] site Properties of the observing site, particularly its
371  geometric location on the surface of the Earth, as set by
372  the sky_setSiteLocation() function (or sky_setSiteLoc2())
373  \param[out] topo Topocentric position, in both rectangular (unit vector)
374  form, and as Azimuth and Elevation (altitude) angles
375 
376  \par When to call this function
377  Use this function if you are calculating the star topocentric position once,
378  for a single site. But if you are going to be calculating it repeatedly, or
379  for multiple sites, use of this function will cause you to perform a great
380  many needless recalculations. Use skyfast_getApprox(), followed by
381  sky1_appToTirs() and sky_siteTirsToTopo() instead.
382 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
383 {
384  Sky_Times atime; // time, in various timescales
385  Sky_TrueEquatorial pos; // geocentric position of the Sun and distance
386  V3D_Vector terInterV; // unit vector in Terrestrial Intermed Ref Sys
387 
388  REQUIRE_NOT_NULL(deltas);
389  REQUIRE_NOT_NULL(site);
390  REQUIRE_NOT_NULL(topo);
391 
392  sky_updateTimes(j2kUtc_d, deltas, &atime);
393 
394  star_getApparent(atime.j2kTT_cy, &pos);
395 
396  /* Convert apparent position to topocentric Azimuth/Elevation coords */
397  sky1_appToTirs(&pos.appCirsV, atime.j2kUT1_d, pos.eqEq_rad, &terInterV);
398  sky_siteTirsToTopo(&terInterV, pos.distance_au, site, topo);
399 }
400 
401 
402 
404  V3D_Vector *pV,
405  V3D_Vector *vV_radpcy)
406 /*! This routine takes the mean equatorial polar coordinate of an object and its
407  proper motions, annual parallax (distance) and radial velocity, and computes
408  the rectangular position and velocity vectors with respect to the same
409  coordinate system.
410  \param[in] c Catalog position and motion of object
411 
412  \param[out] pV Unit position vector (direction cosines)
413  \param[out] vV_radpcy Space velocity vector (radian/Julian century)
414 
415  \par Reference:
416  _Astronomical Almanac_ 2007, pages B66 and B67
417 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
418 {
419  double sinRa, cosRa, sinDec, cosDec;
420 
421  REQUIRE_NOT_NULL(c);
422  REQUIRE_NOT_NULL(pV);
423  REQUIRE_NOT_NULL(vV_radpcy);
424 
425  sincos(c->ra_rad, &sinRa, &cosRa);
426  sincos(c->dec_rad, &sinDec, &cosDec);
427 
428  /* Form the position vector */
429  pV->a[0] = cosRa * cosDec;
430  pV->a[1] = sinRa * cosDec;
431  pV->a[2] = sinDec;
432 
433  /* Form the velocity vector */
434  /* vV = muRA * ∂([pV])/∂(RA) + muDec * ∂([pV])/∂(Dec)
435  * + parallax * radVel * [pV] */
436  if (c->muRaInclCosDec) {
437  // The cos(dec) term is already included in the figure for proper
438  // motion in RA
439  vV_radpcy->a[0] = -c->muRA_radpcy * sinRa
440  - c->muDec_radpcy * cosRa * sinDec
441  + c->parallax_rad * c->radVel_aupcy * pV->a[0];
442  vV_radpcy->a[1] = c->muRA_radpcy * cosRa
443  - c->muDec_radpcy * sinRa * sinDec
444  + c->parallax_rad * c->radVel_aupcy * pV->a[1];
445  } else {
446  // cos(dec) term was not included. Needs to be factored in here
447  vV_radpcy->a[0] = -c->muRA_radpcy * pV->a[1]
448  - c->muDec_radpcy * cosRa * sinDec
449  + c->parallax_rad * c->radVel_aupcy * pV->a[0];
450  vV_radpcy->a[1] = c->muRA_radpcy * pV->a[0]
451  - c->muDec_radpcy * sinRa * sinDec
452  + c->parallax_rad * c->radVel_aupcy * pV->a[1];
453  }
454 
455  vV_radpcy->a[2] = c->muDec_radpcy * cosDec
456  + c->parallax_rad * c->radVel_aupcy * pV->a[2];
457 }
458 
459 
460 
461 GLOBAL void star_earth(double t1_cy,
462  const V3D_Matrix *npM,
463  V3D_Vector *ebV_au,
464  V3D_Vector *ebdotV_aupd,
465  V3D_Vector *sbV_au)
466 /*! Calculate the position and velocity of the Earth, for use in making annual
467  * parallax and aberration corrections.
468  \param[in] t1_cy Epoch of time of observation (Julian centuries since
469  J2000.0, TT timescale)
470  \param[in] npM Combined precession and nutation matrix \b NP that
471  will be used to convert the coordinates of the star
472  of interest from its catalogue mean place to
473  apparent coordinates. It will be used in this
474  routine to do the reverse - convert the apparent
475  coordinates of the Earth's position back into
476  mean coordinates at the catalogue epoch of the star.
477  \param[out] ebV_au Barycentric position of the Earth (AU) (vector \b Eb
478  in _Astronomical Almanac_)
479  \param[out] ebdotV_aupd Velocity of the Earth (AU/day) (vector \b Ėb
480  in _Astronomical Almanac_)
481  \param[out] sbV_au Barycentric position of the Sun (AU) (vector \b Sb
482  in _Astronomical Almanac_)
483 
484  * In theory, this is the barycentric
485  * position, and the values are referred to the same system as is used for the
486  * catalogue position of the star (such as J2000, or
487  * the Barycentric Celestial Reference System/ICRS). This can be obtained from
488  * the IAU SOFA routine \c iauEpv00(), calculated in fabulously complicated
489  * detail. But the parallax and aberration corrections are small, which means
490  * that the accuracy required of this routine is not actually that high.
491  *
492  * So to save on computing resources in a tracking application, this routine
493  * uses instead the simple algorithm in the _Astronomical Almanac_
494  * for the Sun's position, entitled "Low-precision formulas for the Sun's
495  * coordinates..." It differentiates the position vector to obtain the Earth's
496  * velocity. So there are a few approximations here:
497  * 1. It calculates a heliocentric position, rather than a barycentric position
498  * 2. The algorithm itself is approximate - accurate to 0.01° between 1950 and
499  * 2050.
500  * 3. It does not calculate the barycentric position of the Sun at all. So
501  * \a sbV_au will be returned as a zero vector.
502  *
503  * But nonetheless, the effect on the final star position is very small. For
504  * example, the _Almanac_ gives an example of stellar reduction calculations
505  * for a fictitious star, (which appears to be very close to Alpha Centauri).
506  * This star has about as much parallax as one is ever likely to see.
507  *
508  * In the 2007 _Almanac_, page B68, the apparent position of this star at
509  * 2007 January 1, 0h TT , after performing a rigorous reduction, is
510  * - RA = 14 40 03.4343 (hms), Dec = -60°51′37.770″
511  *
512  * The position obtained by using the approximations of star_catalogToApp()
513  * (which calls this routine) and converts to apparent coordinates is
514  * - RA = 14:40:03.4276 (hms), Dec = -60°51'37.782"
515  *
516  * which is clearly good enough for tracking.
517 
518  * In the 1987 _Almanac_, page B41, the apparent position of a (very slightly
519  * different) fictitious star at 1987 January 1, 0h TT is
520  * - RA = 14 38 40.164 (hms), Dec = -60°46′44.82″
521  *
522  * The position obtained using the routines here (as above) is
523  * - RA = 14:38:40.157 (hms), Dec = -60°46'44.84"
524  *
525  * For a more detailed look at this, see \ref page-stellar-reduction-accuracy
526 
527  \par References:
528  _Astronomical Almanac_ 1987 pages
529  B39 (method), B41 (example) and C24 (Sun position) or\n
530  _Astronomical Almanac_ 2007 pages
531  B66 (method), B68 (example) and C24 (Sun position).
532 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
533 {
534  static const double g1_rad = 0.9856003 * DEG2RAD;
535  static const double l1_rad = 0.9856474 * DEG2RAD;
536  static const double lam1_rad = 1.915 * DEG2RAD;
537  static const double lam2_rad = 0.020 * DEG2RAD;
538  static const double r1 = 0.01671;
539  static const double r2 = 0.00014;
540 
541  double L_rad; // Mean longitude, corrected for aberration (radian)
542  double g_rad; // Mean anomaly (radian)
543  double lambda_rad; // Ecliptic longitude (radian)
544  double epsilon_rad; // Obliquity of ecliptic (radian)
545  double sing, cosg; // sine and cosine of g
546  double sin2g, cos2g; // sine and cosine of 2g
547  double r_au; // Sun-Earth distance (AU)
548  double n; // days since J2000.0
549  double lambdadot; // differentiation of lambda_rad
550  double rdot; // differentiation of r_au
551 
552  V3D_Vector posV;
553  V3D_Vector velV;
554 
555  REQUIRE_NOT_NULL(npM);
556  REQUIRE_NOT_NULL(ebV_au);
557  REQUIRE_NOT_NULL(ebdotV_aupd);
558  REQUIRE_NOT_NULL(sbV_au);
559 
560  n = t1_cy * JUL_CENT;
561  // The following lines are taken from the Astronomical Almanac 2007 p C24
562  // "Low precision formulas for the Sun's coordinates and the equat. of time"
563  L_rad = normalize(degToRad(280.461) + (l1_rad * n), TWOPI);
564  g_rad = normalize(degToRad(357.529) + (g1_rad * n), TWOPI);
565 
566  sincos(g_rad, &sing, &cosg);
567  sincos(g_rad + g_rad, &sin2g, &cos2g);
568 
569  lambda_rad = L_rad + lam1_rad * sing + lam2_rad * sin2g;
570  epsilon_rad = degToRad(23.439 - (0.0000004 * n));
571  r_au = 1.00014 - r1 * cosg - r2 * cos2g;
572 
573  // Earth position vector is negative of vector to sun, which is why each of
574  // the following terms is negated
575  posV.a[0] = -r_au * cos(lambda_rad);
576  posV.a[1] = -r_au * cos(epsilon_rad) * sin(lambda_rad);
577  posV.a[2] = -r_au * sin(epsilon_rad) * sin(lambda_rad);
578  // Rotate back to mean coordinates - using inverse of NP matrix
579  v3d_multMtransxV(ebV_au, npM, &posV);
580 
581  // from the above data, calculate a velocity vector
582  lambdadot = l1_rad + lam1_rad * g1_rad * cosg
583  + 2.0 * lam2_rad * g1_rad * cos2g;
584  rdot = -r1 * g1_rad * sing - 2.0 * r2 * sin2g;
585 
586  velV.a[0] = r_au * sin(lambda_rad) * lambdadot - cos(lambda_rad) * rdot;
587  velV.a[1] = -cos(epsilon_rad) * (r_au * cos(lambda_rad) * lambdadot
588  + sin(lambda_rad) * rdot);
589  velV.a[2] = -sin(epsilon_rad) * (r_au * cos(lambda_rad) * lambdadot
590  + sin(lambda_rad) * rdot);
591  // Rotate back to mean coordinates - using inverse of NP matrix
592  v3d_multMtransxV(ebdotV_aupd, npM, &velV);
593 
594  // Barycentric Sun position.
595  // Of course with this simple algorithm we are not actually calculating this
596  sbV_au->a[0] = 0.0;
597  sbV_au->a[1] = 0.0;
598  sbV_au->a[2] = 0.0;
599 }
600 
601 
602 
604  const V3D_Vector *earthVelV_aupd,
605  V3D_Vector *p2V)
606 /*! Apply the annual aberration correction, to convert an object's coordinates
607  from geocentric geometric direction to "proper" direction (still geocentric)
608  \param[in] p1V Unit vector pointing to object's geometric direction
609  as viewed from the centre of the earth.
610  \param[in] earthVelV_aupd Earth velocity vector, referred to the J2000 mean
611  equator and equinox, as returned by routine
612  star_earth() (AU/day - not a unit vector)
613  \param[out] p2V Unit vector pointing to object's proper
614  direction
615 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
616 {
617  V3D_Vector aberrV;
618 
619  REQUIRE_NOT_NULL(earthVelV_aupd);
620  REQUIRE_NOT_NULL(p2V);
621 
622  aberrV.a[0] = earthVelV_aupd->a[0] * invC_dpau;
623  aberrV.a[1] = earthVelV_aupd->a[1] * invC_dpau;
624  aberrV.a[2] = earthVelV_aupd->a[2] * invC_dpau;
625 
626  *p2V = *p1V; // copy vector
627  v3d_addToUVfast(p2V, &aberrV);
628 }
629 
630 
631 
632 GLOBAL char *star_equinoxToStr(const Star_CatalogPosn *coordBlock,
633  char equinoxStr[],
634  size_t eqnxStrSize)
635 /*! Write the coordinate system and equinox out in a standard form.
636  \param[in] coordBlock Coordinate block describing the celestial object
637  \param[out] equinoxStr String to contain the coordinate system and equinox
638  \param[in] eqnxStrSize Size of equinoxStr (i.e. maximum number of characters
639  available for the output
640 
641  * The output form depends upon the value of \a coordBlock->cSys:
642  * |\a coordBlock->cSys| Output |
643  * |-------------------|-----------------------|
644  * | #APPARENT | Apparent |
645  * | #INTERMEDIATE | Intermediate |
646  * | #FK4 | Bnnnn.n (n is numeric)|
647  * | #FK5 | Jnnnn.n (n is numeric)|
648  * | #ICRS | ICRS |
649 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
650 {
651  int epoch_ax10; /* Integer form of year, multiplied by 10 */
652 
653  REQUIRE_NOT_NULL(coordBlock);
654  REQUIRE_NOT_NULL(equinoxStr);
655  REQUIRE(eqnxStrSize < INT_MAX); /* Was a -ve value passed to this param? */
656 
657  switch (coordBlock->cSys) {
658  case APPARENT:
659  saferStrncpy(equinoxStr, "Apparent", eqnxStrSize);
660  break;
661 
662  case INTERMEDIATE:
663  saferStrncpy(equinoxStr, "Intermediate", eqnxStrSize);
664  break;
665 
666  case FK4:
667  /* Write out year to 1 decimal place without using the %f formatting
668  specifier to snprintf(). (Many embedded systems don't support it) */
669  epoch_ax10 = (int)( ((coordBlock->eqnxT_cy * JUL_CENT
670  + MJD_J2000 - MJD_B1900) * 10.0 / TROP_YEAR)
671  + 19000.5);
672  snprintf(equinoxStr, eqnxStrSize, "B%05d ", epoch_ax10);
673  if (eqnxStrSize > 7) { equinoxStr[6] = equinoxStr[5]; }
674  if (eqnxStrSize > 6) { equinoxStr[5] = '.'; }
675  break;
676 
677  case FK5:
678  /* As above, don't use %f */
679  epoch_ax10 = (int)(coordBlock->eqnxT_cy * 1000.0 + 20000.5);
680  snprintf(equinoxStr, eqnxStrSize, "J%05d ", epoch_ax10);
681  if (eqnxStrSize > 7) { equinoxStr[6] = equinoxStr[5]; }
682  if (eqnxStrSize > 6) { equinoxStr[5] = '.'; }
683  break;
684 
685  case ICRS:
686  saferStrncpy(equinoxStr, "ICRS", eqnxStrSize);
687  break;
688 
689  default:
690  equinoxStr[0] = '\0';
691  break;
692  }
693  return equinoxStr;
694 }
695 
696 
697 
698 GLOBAL char *star_epochToStr(const Star_CatalogPosn *coordBlock,
699  char epochStr[],
700  size_t epochStrSize)
701 /*! Write the epoch used for proper motion calculations out in a standard form.
702  \param[in] coordBlock Coordinate block describing the celestial object
703  \param[out] epochStr String to contain the coordinate system and epoch
704  \param[in] epochStrSize Size of epochStr (i.e. maximum number of characters
705  available for the output
706 
707  * The output form depends upon the value of \a coordBlock->cSys:
708  * |\a coordBlock->cSys| Output |
709  * |-------------------|-------------------------------------|
710  * | #APPARENT | (empty string - epoch is irrelevant)|
711  * | #INTERMEDIATE | (empty string - epoch is irrelevant)|
712  * | #FK4 | Bnnnn.n (n is numeric) |
713  * | #FK5 | Jnnnn.n (n is numeric) |
714  * | #ICRS | Jnnnn.n (n is numeric) |
715 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
716 {
717  double epochT_cy;
718  int epoch_ax10; /* Integer form of year, multiplied by 10 */
719 
720  REQUIRE_NOT_NULL(coordBlock);
721  REQUIRE_NOT_NULL(epochStr);
722  REQUIRE(epochStrSize < INT_MAX); /* Was a -ve value passed to this param? */
723 
724  if (coordBlock->epochSpecified) {
725  epochT_cy = coordBlock->epochT_cy;
726  } else {
727  epochT_cy = coordBlock->eqnxT_cy;
728  }
729  switch (coordBlock->cSys) {
730  case APPARENT:
731  case INTERMEDIATE:
732  epochStr[0] = '\0';
733  break;
734 
735  case FK4:
736  /* Write out year to 1 decimal place without using the %f formatting
737  specifier to snprintf(). (Many embedded systems don't support it) */
738  epoch_ax10 = (int)(( (epochT_cy * JUL_CENT + MJD_J2000 - MJD_B1900)
739  * 10.0 / TROP_YEAR) + 19000.5);
740  snprintf(epochStr, epochStrSize, "B%05d ", epoch_ax10);
741  if (epochStrSize > 7) { epochStr[6] = epochStr[5]; }
742  if (epochStrSize > 6) { epochStr[5] = '.'; }
743  break;
744 
745  case FK5:
746  case ICRS:
747  /* As above, don't use %f */
748  epoch_ax10 = (int)(epochT_cy * 1000.0 + 20000.5);
749  snprintf(epochStr, epochStrSize, "J%05d ", epoch_ax10);
750  if (epochStrSize > 7) { epochStr[6] = epochStr[5]; }
751  if (epochStrSize > 6) { epochStr[5] = '.'; }
752  break;
753 
754  default:
755  epochStr[0] = '\0';
756  break;
757  }
758  return epochStr;
759 }
760 
761 
762 
763 GLOBAL int star_setCatalogPosn(const char objectName[],
764  double ra_h,
765  double dec_deg,
766  Star_CoordSys coordSys,
767  double equinoxYear,
768  double epochYear,
769  double muRaSplat_maspa,
770  double muDec_maspa,
771  double annParallax_as,
772  double radVel_kmps,
773  Star_CatalogPosn *coordBlock)
774 /*! This is one of three alternative routines for loading the information for a
775  celestial object into a block of form Star_CatalogPosn. (The other two
776  routines are star_setCatalogOldStyle() and star_parseCoordString().)
777  \returns An error code containing one of the values in
778  Star_CoordErrors
779  \param[in] objectName Name of the object (optional)
780  \param[in] ra_h Right ascension (hours)
781  \param[in] dec_deg Declination (degrees)
782  \param[in] coordSys Coordinate system for RA and Dec
783  \param[in] equinoxYear Year of epoch of the mean equinox (if coordSys
784  is #FK4 or #FK5)
785  \param[in] epochYear Year of epoch for proper motion (if coordSys is
786  #ICRS, #FK4 or #FK5)
787  \param[in] muRaSplat_maspa μα* - proper motion in RA, including the cos(δ)
788  factor (milliarcseconds/year)
789  \param[in] muDec_maspa μδ - proper motion in declination
790  (milliarcseconds/year)
791  \param[in] annParallax_as π - annual parallax (arcseconds)
792  \param[in] radVel_kmps ν - radial velocity (km/s), positive away from
793  the Earth
794  \param[out] coordBlock The block containing the data from all the input
795  parameters, basically scaled into radians
796 
797  The difference between this routine and star_setCatalogOldStyle() is in the
798  way the proper motions in RA and Dec are specified. In this routine, μα* is
799  specified in milliarcseconds per year, after scaling to absolute
800  displacement on the sky by multiplying by cos(δ). μδ is also specified in
801  milliarcseconds per year.
802 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
803 {
804  REQUIRE_NOT_NULL(coordBlock);
805 
806  saferStrncpy(coordBlock->objectName,
807  objectName,
808  sizeof(coordBlock->objectName));
809  coordBlock->ra_rad = hrsToRad(ra_h);
810  coordBlock->dec_rad = degToRad(dec_deg);
811 
812  coordBlock->cSys = coordSys;
813  switch (coordBlock->cSys) {
814  case APPARENT:
815  case INTERMEDIATE:
816  coordBlock->eqnxT_cy = UNUSED_EPOCH_cy;
817  coordBlock->epochT_cy = UNUSED_EPOCH_cy;
818  break;
819  case FK4:
820  coordBlock->eqnxT_cy = ((equinoxYear - 1900.0) * TROP_YEAR
821  + (MJD_B1900 - MJD_J2000)) / JUL_CENT;
822  coordBlock->epochT_cy = ((epochYear - 1900.0) * TROP_YEAR
823  + (MJD_B1900 - MJD_J2000)) / JUL_CENT;
824  break;
825  case FK5:
826  coordBlock->eqnxT_cy = (equinoxYear - 2000.0) / 100.0;
827  coordBlock->epochT_cy = (epochYear - 2000.0) / 100.0;
828  break;
829  case ICRS:
830  coordBlock->eqnxT_cy = 0.0;
831  coordBlock->epochT_cy = (epochYear - 2000.0) / 100.0;
832  break;
833  default:
834  break;
835  }
836 
837  coordBlock->muRA_radpcy = arcsecToRad(muRaSplat_maspa * 0.1);
838  coordBlock->muDec_radpcy = arcsecToRad(muDec_maspa * 0.1);
839  coordBlock->parallax_rad = arcsecToRad(annParallax_as);
840  coordBlock->radVel_aupcy = radVel_kmps * auFactor * JUL_CENT;
841  coordBlock->muRaInclCosDec = true;
842  coordBlock->epochSpecified = (fabs(coordBlock->eqnxT_cy
843  - coordBlock->epochT_cy) > SFA);
844 
845  /* A bit of basic sanity checking */
846  if ((coordBlock->ra_rad < 0.0) || (coordBlock->ra_rad >= TWOPI)) {
847  return STAR_RARANERR;
848  }
849  if (fabs(coordBlock->dec_rad) > HALFPI_PLUS_SFA) {
850  return STAR_DECRANERR;
851  }
852  if ( ((coordBlock->cSys == FK4) || (coordBlock->cSys == FK5))
853  && ((equinoxYear < 0.0) || (equinoxYear > 9999.9)))
854  {
855  return STAR_IVEPOCH;
856  }
857  if ( ( (coordBlock->cSys == ICRS) || (coordBlock->cSys == FK4)
858  || (coordBlock->cSys == FK5))
859  && ((equinoxYear < 0.0) || (equinoxYear > 9999.9)))
860  {
861  return STAR_IVEPOCH;
862  }
863  return STAR_NORMAL;
864 }
865 
866 
867 
868 GLOBAL int star_setCatalogOldStyle(const char objectName[],
869  double ra_h,
870  double dec_deg,
871  Star_CoordSys coordSys,
872  double equinoxYear,
873  double epochYear,
874  double muRa_spcy,
875  double muDec_aspcy,
876  double annParallax_as,
877  double radVel_kmps,
878  Star_CatalogPosn *coordBlock)
879 /*! This is one of three alternative routines for loading the information for a
880  celestial object into a block of form Star_CatalogPosn. (The other two
881  routines are star_setCatalogPosn() and star_parseCoordString().)
882  \returns An error code containing one of the values in
883  Star_CoordErrors
884  \param[in] objectName Name of the object (optional)
885  \param[in] ra_h Right ascension (hours)
886  \param[in] dec_deg Declination (degrees)
887  \param[in] coordSys Coordinate system for RA and Dec
888  \param[in] equinoxYear Year of epoch of the mean equinox (if coordSys
889  is #FK4 or #FK5)
890  \param[in] epochYear Year of epoch for proper motion (if coordSys is
891  #ICRS, #FK4 or #FK5)
892  \param[in] muRa_spcy μα - proper motion in RA, \b not including the
893  cos(δ) factor (seconds/century)
894  \param[in] muDec_aspcy μδ - proper motion in declination
895  (arcseconds/century)
896  \param[in] annParallax_as π - annual parallax (arcseconds)
897  \param[in] radVel_kmps ν - radial velocity (km/s), positive away from
898  the Earth
899  \param[out] coordBlock The block containing the data from all the input
900  parameters, basically scaled into radians
901 
902  The difference between this routine and star_setCatalogPosn() is in the way
903  the proper motions in RA and Dec are specified. In this routine, μα is
904  specified in seconds (of time) per century, and is not scaled to absolute
905  displacement on the sky. μδ is specified in arcseconds per century.
906 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
907 {
908  REQUIRE_NOT_NULL(coordBlock);
909 
910  saferStrncpy(coordBlock->objectName,
911  objectName,
912  sizeof(coordBlock->objectName));
913  coordBlock->ra_rad = hrsToRad(ra_h);
914  coordBlock->dec_rad = degToRad(dec_deg);
915 
916  coordBlock->cSys = coordSys;
917  switch (coordBlock->cSys) {
918  case APPARENT:
919  case INTERMEDIATE:
920  coordBlock->eqnxT_cy = UNUSED_EPOCH_cy;
921  coordBlock->epochT_cy = UNUSED_EPOCH_cy;
922  break;
923  case FK4:
924  coordBlock->eqnxT_cy = ((equinoxYear - 1900.0) * TROP_YEAR
925  + (MJD_B1900 - MJD_J2000)) / JUL_CENT;
926  coordBlock->epochT_cy = ((epochYear - 1900.0) * TROP_YEAR
927  + (MJD_B1900 - MJD_J2000)) / JUL_CENT;
928  break;
929  case FK5:
930  coordBlock->eqnxT_cy = (equinoxYear - 2000.0) / 100.0;
931  coordBlock->epochT_cy = (epochYear - 2000.0) / 100.0;
932  break;
933  case ICRS:
934  coordBlock->eqnxT_cy = 0.0;
935  coordBlock->epochT_cy = (epochYear - 2000.0) / 100.0;
936  break;
937  default:
938  break;
939  }
940 
941  coordBlock->muRA_radpcy = secToRad(muRa_spcy);
942  coordBlock->muDec_radpcy = arcsecToRad(muDec_aspcy);
943  coordBlock->parallax_rad = arcsecToRad(annParallax_as);
944  coordBlock->radVel_aupcy = radVel_kmps * auFactor * JUL_CENT;
945  coordBlock->muRaInclCosDec = false;
946  coordBlock->epochSpecified = (fabs(coordBlock->eqnxT_cy
947  - coordBlock->epochT_cy) > SFA);
948 
949  /* A bit of basic sanity checking */
950  if ((coordBlock->ra_rad < 0.0) || (coordBlock->ra_rad >= TWOPI)) {
951  return STAR_RARANERR;
952  }
953  if (fabs(coordBlock->dec_rad) > HALFPI_PLUS_SFA) {
954  return STAR_DECRANERR;
955  }
956  if ( ((coordBlock->cSys == FK4) || (coordBlock->cSys == FK5))
957  && ((equinoxYear < 0.0) || (equinoxYear > 9999.9)))
958  {
959  return STAR_IVEPOCH;
960  }
961  if ( ( (coordBlock->cSys == ICRS) || (coordBlock->cSys == FK4)
962  || (coordBlock->cSys == FK5))
963  && ((equinoxYear < 0.0) || (equinoxYear > 9999.9)))
964  {
965  return STAR_IVEPOCH;
966  }
967  return STAR_NORMAL;
968 }
969 
970 
971 
972 GLOBAL int star_parseCoordString(const char inStr[],
973  Star_CatalogPosn *coordBlock)
974 /*! This is one of three alternative routines for loading the information for a
975  celestial object into a block of form Star_CatalogPosn. (The other two
976  routines are star_setCatalogPosn() and star_setCatalogOldStyle().)
977  \returns An error code containing one of the values in
978  Star_CoordErrors
979  \param[in] inStr The input string.
980  \param[out] coordBlock The block containing the data from the input
981  string, basically scaled into radians
982 
983  This routine decodes an input string which is of the form
984  \verbatim
985  [name], RA, DEC[, Coords,[Epoch][, Mu_ra*, Mu_dec[, Pi[, Vr]]]]
986  \endverbatim
987  where the items shown inside square brackets are optional. This is basically
988  a comma separated value (CSV) format. The text fields
989  are as follows:
990  - \b name - Object name. If the name is longer than the field
991  Star_CatalogPosn.objectName, the extra characters will be
992  lost.
993  - \b RA - Right Ascension. This has one to three numeric subfields,
994  depending upon where the decimal point (if any) is placed. So
995  it may be in the form of
996  + decimal hours (e.g. 12.582) (one subfield only)
997  + hours and decimal minutes (e.g. 12:34.933 or 12 34.933) (two
998  subfields)
999  + hours, minutes and seconds (e.g. 12:34:56 or 12 34 56, or
1000  12:34:56.78 or 12:34:56:78)
1001  - \b DEC - Declination. This also has one to three numeric subfields, as
1002  above. it may be in the form of
1003  + decimal degrees (e.g. ±21.625 or ±21.625°)
1004  + degrees and decimal arcminutes (e.g. ±21 37.5 or ±21°37.5′)
1005  + degrees, arcminutes and arcseconds (e.g. ±21 37 30 or
1006  ±21°37′30″, or ±21 37 30.0 or ±21°37′30.0″)
1007 
1008  In both of the above cases, the subfields may be separated by spaces,
1009  colons, degree, minute and seconds symbols, single and double quotes,
1010  or anything really. The parser is not fussy, and does not check.
1011  - \b Coords - the coordinate system reference for the coordinates. Valid
1012  values for this field are
1013  + \b Apparent - referred to true equinox and equator of date
1014  + \b Jnnnn.n - FK5 position referred to mean equator and equinox
1015  of the specified Julian epoch (e.g. J2000)
1016  + \b Bnnnn.n - FK4 position referred to mean equator and equinox
1017  of the specified Besselian epoch (e.g. B1950)
1018  + \b nnnn.n - If not specified with a B or a J, values here
1019  before 1984.0 will be assumed to be FK4 (Besselian) and those
1020  after to be FK5 (Julian)
1021  + \b Intermediate - Celestial Intermediate Reference System.
1022  This is referred to the true equator of date and the Celestial
1023  Intermediate Origin (CIO).
1024  + \b ICRS - International Celestial Reference System
1025  .
1026  If this field is not specified, it will be assumed to be
1027  Apparent, and all following fields will be ignored.
1028  - \b Epoch - Initial epoch for proper motion. If this field is omitted, the
1029  epoch is assumed to be the same as the coordinate system
1030  specification that precedes it. If that coordinate system is
1031  ICRS, then the epoch is assumed to be J2000.0 if is not
1032  specified. If the coordinate system is Apparent or
1033  Intermediate, the Epoch is ignored.
1034  - \b Mu_ra* - proper motion in right ascension (milliarcseconds/year),
1035  including the cos(DEC) factor.
1036  This field will be ignored if Coords is set to Apparent or
1037  Intermediate
1038  - \b Mu_dec - proper motion in declination (milliarcseconds/year).
1039  This field will be ignored if Coords is set to Apparent or
1040  Intermediate
1041  - \b Pi - annual parallax (arcseconds)
1042  This field will be ignored if Coords is set to Apparent or
1043  Intermediate
1044  - \b Vr - radial velocity (km/s), positive for objects moving away.
1045  This field will be ignored if Coords is set to Apparent or
1046  Intermediate
1047 
1048  The string format is fairly flexible. For example, fields may be separated
1049  with spaces rather than commas, so long as any omitted fields are at the end
1050  of the string. But if any fields not at the end are omitted, commas must be
1051  used to indicate the empty fields.
1052  \note
1053  The facilities provided by the C language for text handling are rudimentary,
1054  and as a result, this routine is slightly fragile. In trying to be flexible
1055  but not too complex, the following limitation applies: Do not ever have
1056  white space immediately preceding a comma. If you do, the routine may go
1057  wrong. So, in particular this means that omitted fields (not at the end of
1058  the string) must be denoted by two successive commas with no space in
1059  between.
1060  \verbatim
1061  Dummy, 12:34:56.789, +89°56′43.210″, J2000.0,, 23.455, 12.766
1062  Barnard's star, 17 57 48.500 +04 41 36.111 ICRS,, -802.803, 10362.542, 0.5474506, -110.353
1063  \endverbatim
1064 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1065 {
1066  int decodeError;
1067  double tempVal; /* temporary value, decoded from string */
1068  const char *startPtr;
1069  const char *endPtr;
1070  bool muRAsupplied = false;
1071  bool muDecSupplied = false;
1072  bool piSupplied = false;
1073  Star_CoordSys dummyCoordSys;
1074 
1075  REQUIRE_NOT_NULL(inStr);
1076  REQUIRE_NOT_NULL(coordBlock);
1077 
1078  /* Start by assuming that we will be given a minimal string - just RA and
1079  Dec in Apparent coordinates. */
1080  memset(coordBlock, 0, sizeof(*coordBlock));
1081  coordBlock->eqnxT_cy = UNUSED_EPOCH_cy;
1082  coordBlock->epochT_cy = UNUSED_EPOCH_cy;
1083 
1084  startPtr = inStr;
1085 
1086  /* Extract the object name */
1087  decodeError = extractObjectName(startPtr,
1088  coordBlock->objectName,
1089  sizeof(coordBlock->objectName),
1090  &endPtr);
1091  if (decodeError != STAR_NORMAL) {
1092  return decodeError;
1093  }
1094 
1095  if (*endPtr == ',') { endPtr++; }
1096  startPtr = endPtr;
1097 
1098  /* Decode the right ascension */
1099  tempVal = skyio_sxStrToAng(startPtr, &endPtr, &decodeError);
1100  if (decodeError == 0) {
1101  /* RA is specified in hours. Convert to radians */
1102  coordBlock->ra_rad = hrsToRad(tempVal);
1103  } else if (decodeError == INVALID_ANGLE) {
1104  return STAR_ERRINRA;
1105  } else {
1106  return STAR_NORA;
1107  }
1108  if ((coordBlock->ra_rad < 0.0) || (coordBlock->ra_rad >= TWOPI)) {
1109  return STAR_RARANERR;
1110  }
1111 
1112  /* Decode the declination */
1113  startPtr = endPtr;
1114  tempVal = skyio_sxStrToAng(startPtr, &endPtr, &decodeError);
1115  if (decodeError == 0) {
1116  /* Dec is specified in degrees. Convert to radians */
1117  coordBlock->dec_rad = degToRad(tempVal);
1118  } else if (decodeError == INVALID_ANGLE) {
1119  return STAR_ERRINDEC;
1120  } else {
1121  return STAR_NODEC;
1122  }
1123  if (fabs(coordBlock->dec_rad) > HALFPI_PLUS_SFA) {
1124  return STAR_DECRANERR;
1125  }
1126 
1127  /* Decode the equinox/coordinate system specification */
1128  startPtr = endPtr;
1129  decodeError = parseEpochStr(startPtr,
1130  &coordBlock->cSys,
1131  &coordBlock->eqnxT_cy,
1132  &endPtr);
1133  if (decodeError == STRINGEMPTY) {
1134  coordBlock->cSys = APPARENT;
1135  coordBlock->eqnxT_cy = UNUSED_EPOCH_cy;
1136  coordBlock->epochT_cy = UNUSED_EPOCH_cy;
1137 
1138  } else if (decodeError != 0) {
1139  return decodeError;
1140  } else {
1141  ;
1142  }
1143  if ((coordBlock->cSys == APPARENT) || (coordBlock->cSys == INTERMEDIATE)) {
1144  return STAR_NORMAL;
1145  }
1146 
1147  /* Decode the epoch specification (for proper motion) if it is present */
1148  if (*endPtr == ',') { endPtr++; }
1149  startPtr = endPtr;
1150  /* Is there a left parenthesis at this point? If so, look for a right
1151  parenthesis and decode the contents in between as an epoch specification
1152  */
1153  decodeError = parseEpochStr(startPtr,
1154  &dummyCoordSys,
1155  &coordBlock->epochT_cy,
1156  &endPtr);
1157  if (decodeError == STRINGEMPTY) {
1158  coordBlock->epochSpecified = false;
1159  coordBlock->epochT_cy = coordBlock->eqnxT_cy;
1160 
1161  } else if (decodeError != 0) {
1162  return decodeError;
1163  } else {
1164  coordBlock->epochSpecified = true;
1165  }
1166 
1167  /* Are there any fields left? If so, we expect at least two items i.e.
1168  proper motion in RA and Dec. */
1169  if (*endPtr == ',') { endPtr++; }
1170  startPtr = endPtr;
1171  if (startPtr[0] != '\0') {
1172  tempVal = myStrtod(startPtr, &endPtr, &decodeError);
1173  if (decodeError == STRINGEMPTY) {
1174  ;
1175  } else if (decodeError != 0) {
1176  return STAR_ERRINMURA;
1177  } else {
1178  /* Convert from milliarcseconds per year to radians per century */
1179  coordBlock->muRA_radpcy = arcsecToRad(tempVal * 0.1);
1180  coordBlock->muRaInclCosDec = true;
1181  muRAsupplied = true;
1182  }
1183 
1184  if (*endPtr == ',') { endPtr++; }
1185  startPtr = endPtr;
1186  tempVal = myStrtod(startPtr, &endPtr, &decodeError);
1187  if (decodeError == STRINGEMPTY) {
1188  ;
1189  } else if (decodeError != 0) {
1190  return STAR_ERRINMUDEC;
1191  } else {
1192  /* Convert from milliarcseconds per year to radians per century */
1193  coordBlock->muDec_radpcy = arcsecToRad(tempVal * 0.1);
1194  muDecSupplied = true;
1195  }
1196  if (muRAsupplied && !muDecSupplied) {
1197  return STAR_NOMUDEC;
1198  }
1199  if (muDecSupplied && !muRAsupplied) {
1200  return STAR_NOMURA;
1201  }
1202 
1203  } else {
1204  return STAR_NORMAL;
1205  }
1206 
1207  /* Anything left? It will be annual parallax */
1208  if (*endPtr == ',') { endPtr++; }
1209  startPtr = endPtr;
1210  if (startPtr[0] != '\0') {
1211  tempVal = myStrtod(startPtr, &endPtr, &decodeError);
1212  if (decodeError == STRINGEMPTY) {
1213  ;
1214  } else if (decodeError != 0) {
1215  return STAR_ERRINPARAL;
1216  } else {
1217  /* Pi is specified in arcseconds. Convert to radians */
1218  coordBlock->parallax_rad = arcsecToRad(tempVal);
1219  piSupplied = true;
1220  }
1221  if (piSupplied && !muRAsupplied && !muDecSupplied) {
1222  return STAR_PINEEDSPM;
1223  }
1224 
1225  } else {
1226  return STAR_NORMAL;
1227  }
1228 
1229  /* Anything left? This is radial velocity */
1230  if (*endPtr == ',') { endPtr++; }
1231  startPtr = endPtr;
1232  if (startPtr[0] != '\0') {
1233  tempVal = myStrtod(startPtr, &endPtr, &decodeError);
1234  if (decodeError == STRINGEMPTY) {
1235  return STAR_NORMAL;
1236  } else if (decodeError != 0) {
1237  return STAR_ERRINVR;
1238  } else {
1239  /* Vr is specified in kilometres/second. Convert to AU per century*/
1240  coordBlock->radVel_aupcy = tempVal * auFactor * JUL_CENT;
1241  }
1242  if (muRAsupplied && muDecSupplied && piSupplied) {
1243  return STAR_NORMAL;
1244  } else {
1245  return STAR_VRNEEDSPM;
1246  }
1247 
1248  } else {
1249  return STAR_NORMAL;
1250  }
1251 }
1252 
1253 
1254 
1255 #if 0
1256 GLOBAL void star_lightDeflection(const V3D_Vector *pV,
1257  const V3D_Vector *earthBV_au,
1258  const V3D_Vector *earthVelV_aupd,
1259  const V3D_Vector *sunBV_au,
1260  V3D_Vector *p2V)
1261 /*! Applies the full relativistic corrections to the star's position for the
1262  effects of (a) light deflection (by the Sun's gravity) and (b) annual
1263  aberration. For tracking a star, this routine is basically unnecessary.
1264  The magnitude of light deflection when viewing a star at night is for all
1265  practical purposes negligible, and the very simple non-relativistic
1266  correction for annual aberration performed by the alternative routine
1267  star_annAberr() is within about 10 milliarcseconds (i.e. quite good enough).
1268  But here it is for completeness.
1269  \param[in] pV Unit vector pointing to object's geometric direction
1270  as viewed from the centre of the earth.
1271  \param[in] earthBV_au Barycentric position of the Earth, referred to the
1272  J2000 mean equator and equinox, as returned by
1273  routine star_earth()
1274  \param[in] earthVelV_aupd Earth velocity vector, referred to the J2000 mean
1275  equator and equinox, as returned by routine
1276  star_earth() (AU/day - not a unit vector)
1277  \param[in] sunBV_au Barycentric position of the Sun, referred to the
1278  J2000 mean equator and equinox, as returned by
1279  routine star_earth()
1280  \param[out] p2V Unit vector pointing to object's proper
1281  direction
1282 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1283 {
1284  V3D_Vector p1V; /* pV, corrected for light deflection */
1285  V3D_Vector dpV;
1286  V3D_Vector e0V_au, eV; /* Heliocentric Earth direction */
1287  double eMag_au; /* Sun-Earth distance, |e0V_au| */
1288  double ep; /* dot product of eV and pV */
1289  V3D_Vector vV; /* Velocity vector, as fraction of light speed*/
1290  double vMag; /* Magnitude of velocity |vV| */
1291  double p1dotV; /* dot product of p1V & vV */
1292  double invbeta, p1vbeta;
1293  double scale;
1294 
1295  REQUIRE_NOT_NULL(pV);
1296  REQUIRE_NOT_NULL(earthBV_au);
1297  REQUIRE_NOT_NULL(earthVelV_aupd);
1298  REQUIRE_NOT_NULL(sunBV_au);
1299  REQUIRE_NOT_NULL(p2V);
1300 
1301  v3d_subtractV(&e0V_au, earthBV_au, sunBV_au);
1302  eMag_au = v3d_magV(&e0V_au);
1303  scale = 1.0 / eMag_au;
1304  eV.a[0] = e0V_au.a[0] * scale;
1305  eV.a[1] = e0V_au.a[1] * scale;
1306  eV.a[2] = e0V_au.a[2] * scale;
1307 
1308  ep = v3d_dotProductV(pV, &eV);
1309 
1310  dpV.a[0] = (2.0 * 9.87e-9 / eMag_au) * (eV.a[0] - ep * pV->a[0]) / (1 + ep);
1311  dpV.a[1] = (2.0 * 9.87e-9 / eMag_au) * (eV.a[1] - ep * pV->a[1]) / (1 + ep);
1312  dpV.a[2] = (2.0 * 9.87e-9 / eMag_au) * (eV.a[2] - ep * pV->a[2]) / (1 + ep);
1313 
1314  p1V.a[0] = pV->a[0] + dpV.a[0];
1315  p1V.a[1] = pV->a[1] + dpV.a[1];
1316  p1V.a[2] = pV->a[2] + dpV.a[2];
1317 #if 1
1318  printVector("E", &e0V_au);
1319  printf("|E| = %.9f\n", eMag_au);
1320  printVector("e", &eV);
1321  printf("e.p = %.9f\n", ep);
1322  printVector("dp", &dpV);
1323  printVector("p1", &p1V);
1324 #endif
1325 
1326  vV.a[0] = invC_dpau * earthVelV_aupd->a[0];
1327  vV.a[1] = invC_dpau * earthVelV_aupd->a[1];
1328  vV.a[2] = invC_dpau * earthVelV_aupd->a[2];
1329  vMag = v3d_magV(&vV);
1330  invbeta = sqrt(1 - vMag * vMag);
1331  p1dotV = v3d_dotProductV(&p1V, &vV);
1332  p1vbeta = 1.0 + p1dotV / (1.0 + invbeta);
1333 #if 1
1334  printVector("V", &vV);
1335  printf("|V| = %.9f\n", vMag);
1336  printf("invbeta = %.9f\n", invbeta);
1337  printf("p1.V = %.9f\n", p1dotV);
1338  printf("1 + (p1.V)/(1 + invbeta) = %.9f\n", p1vbeta);
1339 #endif
1340  p2V->a[0] = (invbeta * p1V.a[0] + p1vbeta * vV.a[0]) / (1.0 + p1dotV);
1341  p2V->a[1] = (invbeta * p1V.a[1] + p1vbeta * vV.a[1]) / (1.0 + p1dotV);
1342  p2V->a[2] = (invbeta * p1V.a[2] + p1vbeta * vV.a[2]) / (1.0 + p1dotV);
1343 }
1344 #endif
1345 
1346 
1347 /*
1348  *------------------------------------------------------------------------------
1349  *
1350  * Local functions (not called from other modules)
1351  *
1352  *------------------------------------------------------------------------------
1353  */
1354 
1355 LOCAL int extractObjectName(const char coordStr[],
1356  char nameStr[],
1357  size_t nameStrSize,
1358  const char **endPtr)
1359 /*! Extract the celestial object's name from the coordinate string. If the name
1360  is present, it will be the first field in the string. It must be separated
1361  from the fields that follow by a comma. It may be enclosed in double quote
1362  characters, in which case a comma must immediately follow the closing double
1363  quote character. The double quotes will be removed from the output string.
1364  [in] coordStr The input coordinate string
1365  [out] nameStr The object name
1366  [in] nameStrSize The maximum number chars that can be written to nameStr
1367  [out] endPtr Points to first char in coordStr after the object name
1368 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1369 {
1370  size_t n;
1371  const char *sc, *ec; /* Point to start char, end char of name */
1372 
1373  sc = coordStr;
1374  if (coordStr[0] == '"') {
1375  sc++;
1376  ec = strchr(sc, '"');
1377  *endPtr = ec;
1378  /* Move endPtr to char immediately after that second double-quote */
1379  if (*endPtr != NULL) { (*endPtr)++; }
1380  } else {
1381  ec = strchr(coordStr, ',');
1382  *endPtr = ec;
1383  }
1384  if (ec == NULL) {
1385  return STAR_ERRINOBJ;
1386  }
1387 
1388  n = (size_t)(ec - sc) + 1; /* Include room for the '\0' at the end */
1389  if (n > nameStrSize) {
1390  n = nameStrSize;
1391  }
1392  saferStrncpy(nameStr, sc, n);
1393 
1394  return STAR_NORMAL;
1395 }
1396 
1397 
1398 
1399 LOCAL int parseEpochStr(const char epochStr[],
1400  Star_CoordSys *coordSys,
1401  double *epochT_cy,
1402  const char **endPtr)
1403 /*! Decodes a character string representing an Epoch into its component data;
1404  the coordinate system specification (APPARENT, FK4 or FK5 etc.), and the
1405  year. The string \a epochStr must be without spaces or tabs and be like
1406  one of the following format examples:
1407  - Besselian: B1950 B1950.0 B1967.4 1975 1976.5
1408  - Julian: J2000 J2000.0 J1985.1 1986 1987.5
1409  - Apparent Place: APPARENT (or abbreviation of at least 3 letters)
1410  - Intermediate Place: INTERMEDIATE (or abbreviation of at least 3 letters)
1411  - ICRS Place: ICRS (or abbreviation of at least 3 letters)
1412  \returns An error code. STAR_NORMAL if OK, STAR_IVEPOCH if not, or
1413  STRINGEMPTY if \a epochStr was empty
1414  \param[in] epochStr The input string containing the epoch or equinox
1415  specification
1416  \param[out] coordSys Coordinate system that has been implied by the equinox
1417  specification
1418  \param[out] epochT_cy Time of epoch (Julian centuries since J2000.0)
1419  \param[out] endPtr A pointer to the end of that part of \a epochStr that was
1420  read to obtain the results. This may be pointing to some
1421  white space between the results just decoded and further
1422  data, or it may be the end of the string. A NULL
1423  pointer may be passed to this parameter if you do not
1424  need this value.
1425 
1426  If the year is specified without a preceding 'B' or 'J' character, it will
1427  be interpreted as a Besselian epoch if it is less than 1984.0, and as a
1428  Julian epoch if it is from 1984.0 onwards.
1429 
1430  If a Julian epoch is specified, \a coordSys will be set to #FK5.
1431  If a Besselian epoch is specified, \a coordSys will be set to #FK4.
1432  If APPARENT or INTERMEDIATE is specified, the epochT_cy value is meaningless
1433  so it is set to -99.9.
1434  If ICRS is specified, epochT_cy will be set to 0.0 (i.e. J2000.0)
1435 
1436  \note
1437  There are two independent reasons for specifying an epoch string. One is to
1438  identify the coordinate system in use and the equinox that applies to the
1439  celestial object's right ascension and declination, if that coordinate
1440  system is FK4 or FK5. The other is to specify the time zero for a celestial
1441  object's proper motion. In this case the value of \a coordSys returned by
1442  this function is irrelevant, and should be ignored.
1443 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1444 {
1445  static const char *apparent = "APPARENT";
1446  static const char *intermediate = "INTERMEDIATE";
1447  static const char *icrs = "ICRS";
1448  double tempEpoch; /* Read into this variable and check range
1449  before inserting into epochT_cy */
1450  int sError = 0; /* Error status of myStrtod() calls */
1451  char upEpStr[16]; /* Upper case version of epochStr[] */
1452  size_t i, j;
1453 
1454  /* Trim leading space and convert epochStr to upper case before doing
1455  comparisons */
1456  i = 0;
1457  j = 0;
1458  while (isspace(epochStr[i])) {
1459  i++;
1460  }
1461  while( (j < sizeof(upEpStr) - 1) && (epochStr[i] != '\0')
1462  && !isspace(epochStr[i]) && (epochStr[i] != ','))
1463  {
1464  upEpStr[j] = (char)toupper(epochStr[i]);
1465  i++;
1466  j++;
1467  }
1468  upEpStr[j] = '\0';
1469  if (endPtr != NULL) {
1470  *endPtr = &epochStr[i];
1471  }
1472 
1473  /* Was supplied string completely empty or blank? */
1474  if (upEpStr[0] == '\0') {
1475  *epochT_cy = UNUSED_EPOCH_cy;
1476  return STRINGEMPTY;
1477  }
1478 
1479  /* Does the string specify "apparent" or "intermediate" or "ICRS" with
1480  at least 3 characters? */
1481  if ((j >= 3) && (strstr(apparent, upEpStr) == apparent)) {
1482  *coordSys = APPARENT;
1483  *epochT_cy = UNUSED_EPOCH_cy;
1484 
1485  } else if ((j >= 3) && (strstr(intermediate, upEpStr) == intermediate)) {
1486  *coordSys = INTERMEDIATE;
1487  *epochT_cy = UNUSED_EPOCH_cy;
1488 
1489  } else if ((j >= 3) && (strstr(icrs, upEpStr) == icrs)) {
1490  *coordSys = ICRS;
1491  *epochT_cy = 0.0;
1492 
1493  } else {
1494  /* Must be a numeric epoch spec. Work out if it is Besselian or Julian*/
1495  if (upEpStr[0] == 'B') {
1496  *coordSys = FK4;
1497  tempEpoch = myStrtod(&epochStr[i - j + 1], NULL, &sError);
1498 
1499  } else if (upEpStr[0] == 'J') {
1500  *coordSys = FK5;
1501  tempEpoch = myStrtod(&epochStr[i - j + 1], NULL, &sError);
1502 
1503  } else if ((epochStr[i - j] >= '0') && (epochStr[i - j] <= '9')) {
1504  tempEpoch = myStrtod(&epochStr[0], NULL, &sError);
1505  if (tempEpoch < 1984.0) {
1506  *coordSys = FK4;
1507  } else {
1508  *coordSys = FK5;
1509  }
1510 
1511  } else {
1512  sError = STRINGEMPTY; // String must be invalid.
1513  }
1514 
1515  /* Was a sensible epoch specified? */
1516  if ((sError != 0) || (tempEpoch < 0.0) || (tempEpoch > 9999.9)) {
1517  return STAR_IVEPOCH;
1518 
1519  } else {
1520  if (*coordSys == FK4) {
1521  *epochT_cy = ((tempEpoch - 1900.0) * TROP_YEAR
1522  + (MJD_B1900 - MJD_J2000)) / JUL_CENT;
1523  } else if (*coordSys == FK5) {
1524  *epochT_cy = (tempEpoch - 2000.0) / 100.0;
1525  } else {
1526  return STAR_IVEPOCH;
1527  }
1528  }
1529  }
1530  return STAR_NORMAL;
1531 }
1532 
1533 
1534 
1535 LOCAL double myStrtod(const char str[], const char **endPtr, int *error)
1536 /* Convert string to double-precision float.
1537 Returns - the result of the conversion as a floating point number
1538  Inputs
1539  str - the input text string, to be passed to strtod()
1540  Output (optional)
1541  endPtr - pointer to the first character after the last character that was
1542  read to obtain the number.
1543  A NULL pointer can be passed to this parameter if you don't care
1544  about this.
1545  error - conversion status. One of
1546  0 : success
1547  STRINGEMPTY : No characters could be converted into a number. There was
1548  no valid data in the string
1549  ERANGE : the strtod() function detected overflow or underflow and
1550  set errno to ERANGE. The returned result will be plus or
1551  minus HUGE_VAL or zero.
1552  A NULL pointer can be passed to this parameter if you don't care
1553  about the conversion status.
1554 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1555 {
1556  char *endPtr2;
1557  double num;
1558  int status;
1559  int savedErrno;
1560 
1561  savedErrno = errno;
1562  errno = 0;
1563 
1564  num = strtod(str, &endPtr2);
1565  if (endPtr2 == str) {
1566  status = STRINGEMPTY; /* No conversion done; nothing valid found */
1567  } else {
1568  status = errno; /* Might be ERANGE */
1569  }
1570 
1571  errno = savedErrno;
1572  if (endPtr != NULL) { *endPtr = endPtr2; }
1573  if (error != NULL) { *error = status; }
1574  return num;
1575 }
1576 
1577 
1578 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1579 
1580 
1581 
1582 /*! \page page-stellar-reduction-accuracy Stellar reduction accuracy
1583  *
1584  * Several tests were performed to compare the calculations of the routines in
1585  * star.h / star.c with those given in examples in section B of the
1586  * _Astronomical Almanac_ (various years).
1587  *
1588  * The 1987 _Astronomical Almanac_ uses a fictitious star for its example
1589  * calculations of stellar reduction. The catalog position of the star (given
1590  * on page B40 of the _Almanac_) is
1591  *
1592  * 14 39 36.087, -60°50′07.14″, J2000,, -49.486 s/cy, +69.60″/cy, 0.752″, -22.2 km/s
1593  *
1594  * The table below shows the results of converting this position to apparent
1595  * coordinates (at the date shown in the table heading), using various
1596  * approximations, and compared with the results from the _Almanac_ itself.
1597  *
1598  * |Apparent coords at 1987-01-01 0h TT |RA |Dec |
1599  * |------------------------------------|---------------|----------------|
1600  * |Almanac page B41 | 14:38:40.164 | -60°46′44.82″ |
1601  * |Confirmation (see 1. below) | 14:38:40.1641 | -60°46′44.822″ |
1602  * |No light deflection (see 2.) | 14:38:40.1652 | -60°46′44.820″ |
1603  * |Simpler nutation (see 3.) | 14:38:40.1639 | -60°46′44.820″ |
1604  * |IAU 2000 prec&nut | 14:38:40.1632 | -60°46′44.829″ |
1605  * |Approx Earth (see 4.) | 14:38:40.1564 | -60°46′44.843″ |
1606  * |All of the above (see 5.) | 14:38:40.1572 | -60°46′44.841″ |
1607  *
1608  * The simplest calculation (see 5. below) gives a position error in RA of
1609  * 0.0069 s and in Dec of 0.019″, giving a total angular position error on the
1610  * sky of 0.054″.
1611  *
1612  * The same star appears on page B40 of the 1990 _Astronomical Almanac_.
1613  * Likewise, the table below compares the calculations here with the results
1614  * from the _Almanac_ itself.
1615  *
1616  * |Apparent coords at 1990-01-01 0h TT |RA |Dec |
1617  * |------------------------------------|---------------|----------------|
1618  * |Almanac page B41 | 14:38:54.112 | -60°47′32.78″ |
1619  * |Confirmation (see 1. below) | 14:38:54.1120 | -60°47′32.780″ |
1620  * |No light deflection (see 2.) | 14:38:54.1131 | -60°47′32.778″ |
1621  * |Simpler nutation (see 3.) | 14:38:54.1119 | -60°47′32.779″ |
1622  * |IAU2000 prec&nut | 14:39:54.1109 | -60°47′32.784″ |
1623  * |Approx Earth (see 4.) | 14:38:54.1046 | -60°47′32.790″ |
1624  * |All of the above (see 5.) | 14:38:54.1056 | -60°47′32.789″ |
1625  *
1626  * The simple calculation (see 5. below) gives a position error in RA of
1627  * 0.0064 s and in Dec of 0.010″, giving a total angular position error on the
1628  * sky of 0.048″.
1629  *
1630  * The 2007 _Astronomical Almanac_ introduced a very slightly different
1631  * fictitious star for its example calculations on page B68. This one has a
1632  * catalog position of
1633  *
1634  * 14 39 36.4958, -60 50 02.309 ICRS, J2000.0, -3678.06 mas/yr, 482.87 mas/yr, 0.742″, -21.6 km/s
1635  *
1636  * Again, the table below compares the calculations here with the results
1637  * from the _Almanac_ itself.
1638  *
1639  * |Apparent coords at 2007-01-01 0h TT |RA |Dec |
1640  * |------------------------------------|---------------|----------------|
1641  * |Almanac page B68 | 14:40:03.4343 | -60°51′37.770″ |
1642  * |Nutation IAU2000B (see 6. below) | 14:40:03.4342 | -60°51′37.770″ |
1643  * |No light deflection (see 7.) | 14:40:03.4353 | -60°51′37.769″ |
1644  * |Approx Earth (see 8.) | 14:40:03.4265 | -60°51′37.784″ |
1645  * |All of the above (see 9.) | 14:40:03.4276 | -60°51′37.782″ |
1646  * |Nutation IAU1980 with precision 0 | 14:40:03.4400 | -60°51′37.784″ |
1647  * |All above and Nutation 1980 | 14:40:03.4334 | -60°51′37.796″ |
1648  * |All above and simpler Nutation 1980 | 14:40:03.4336 | -60°51′37.796″ |
1649  *
1650  * The simple calculation (see 9. below) gives a position error in RA of
1651  * 0.0067 s and in Dec of 0.012″, giving a total angular position error on the
1652  * sky of 0.051″. Or if we use 1980 nutation, 0.0009s and 0.026″,
1653  *
1654  * These three results show very small position errors.
1655  *
1656  * Test conditions
1657  * 1. Confirmation: sky1_nutationIAU1980() called with \a precision set to
1658  * 0, vectors \b Eb, \b Ėb, and \b Sb taken from the relevant _Almanac_,
1659  * page B39, and star_lightDeflection() used to calculate aberration
1660  * etc.
1661  * 2. Ignore light deflection. That is, conditions as for 1, but the much
1662  * simpler routine star_annAberr() called instead of
1663  * star_lightDeflection().
1664  * 3. Simpler nutation. Conditions as for 1, but sky1_nutationIAU1980()
1665  * called with \a precision set to 4.
1666  * 4. Approx Earth. Conditions as for 1, but the vectors \b Eb, \b Ėb, and
1667  * \b Sb are calculated by the very approximate routine star_earth()
1668  * 5. All simplifications: sky1_nutationIAU1980() called with \a precision
1669  * set to 4, vectors \b Eb, \b Ėb, and \b Sb obtained approximately from
1670  * star_earth(), and star_annAberr() used to calculate annual aberration
1671  * 6. Approximate confirmation: astc2_nutationIAU2000B() called to
1672  * calculate nutation, vectors \b Eb, \b Ėb, and \b Sb taken from the
1673  * 2007 _Almanac_ page B68, and star_lightDeflection() used to calculate
1674  * aberration etc.
1675  * 7. Ignore light deflection. That is, conditions as for 6, but the much
1676  * simpler routine star_annAberr() called instead of
1677  * star_lightDeflection().
1678  * 8. Approx Earth. Conditions as for 6, but the vectors \b Eb, \b Ėb, and
1679  * \b Sb are calculated by the very approximate routine star_earth()
1680  * 9. All simplifications: astc2_nutationIAU2000B() called, vectors \b Eb,
1681  * \b Ėb, and \b Sb obtained approximately from star_earth(), and
1682  * star_annAberr() used to calculate annual aberration
1683  *
1684  * */
1685 
1686 
sky1.h
sky1.h - astronomical coordinate conversion routines, IAU 1980
Star_CatalogPosn::muDec_radpcy
double muDec_radpcy
μδ - Proper motion in Dec (radian/Julian century)
Definition: star.h:90
star_catalogToVectors
void star_catalogToVectors(const Star_CatalogPosn *c, V3D_Vector *pV, V3D_Vector *vV_radpcy)
This routine takes the mean equatorial polar coordinate of an object and its proper motions,...
Definition: star.c:403
v3d_multMxM
V3D_Matrix * v3d_multMxM(V3D_Matrix *destM, const V3D_Matrix *srcM1, const V3D_Matrix *srcM2)
Multiply 3x3 matrix by 3x3 matrix to give a new 3x3 matrix, as per [destM] = [srcM1]*[srcM2].
Definition: vectors3d.c:517
general.h
general.h - definitions of general use to (standard) C programs
STAR_ERRINMUDEC
@ STAR_ERRINMUDEC
Proper motion in Declination can't be decoded.
Definition: star.h:112
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
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
REQUIRE
#define REQUIRE(test_)
Check preconditions.
Definition: general.h:273
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
Sky1_Nut1980
Nutation angles and obliquity.
Definition: sky1.h:75
v3d_magV
double v3d_magV(const V3D_Vector *srcV)
Return the magnitude of the specified vector.
Definition: vectors3d.c:271
astron.h
astron.h - assorted definitions useful for astronomy
STAR_ERRINDEC
@ STAR_ERRINDEC
Declination can't be decoded.
Definition: star.h:109
STAR_ERRINMURA
@ STAR_ERRINMURA
Proper motion in RA can't be decoded.
Definition: star.h:111
Star_CoordSys
Star_CoordSys
Coordinate system equator and origin.
Definition: star.h:69
star_setCatalogPosn
int star_setCatalogPosn(const char objectName[], double ra_h, double dec_deg, Star_CoordSys coordSys, double equinoxYear, double epochYear, double muRaSplat_maspa, double muDec_maspa, double annParallax_as, double radVel_kmps, Star_CatalogPosn *coordBlock)
This is one of three alternative routines for loading the information for a celestial object into a b...
Definition: star.c:763
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
sky1_epsilon1980
void sky1_epsilon1980(double t_cy, Sky1_Nut1980 *nut)
Calculate the obliquity of the ecliptic and the equation of the equinoxes.
Definition: sky1.c:631
Star_CatalogPosn::epochSpecified
bool epochSpecified
equinox and epoch were separately specified
Definition: star.h:88
MJD_J2000
#define MJD_J2000
MJD of Fundamental Epoch J2000.0.
Definition: astron.h:25
Sky_TrueEquatorial
Struct used for holding an object's coordinates in equatorial apparent or Intermediate coordinates.
Definition: sky.h:106
STAR_NORA
@ STAR_NORA
Specification of Right Ascension is missing.
Definition: star.h:105
STAR_NOMURA
@ STAR_NOMURA
Specification of proper motion in RA is missing when mu_dec was provided.
Definition: star.h:115
STAR_ERRINRA
@ STAR_ERRINRA
Right Ascension can't be decoded.
Definition: star.h:106
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
skyio_sxStrToAng
double skyio_sxStrToAng(const char angleStr[], const char **endPtr, int *error)
Convert a string containing an angle (or a time) in sexagesimal format to the angle's value.
Definition: skyio.c:288
REQUIRE_NOT_NULL
#define REQUIRE_NOT_NULL(pointer_)
,
Definition: general.h:308
Star_CatalogPosn::ra_rad
double ra_rad
α - Right Ascension (radian)
Definition: star.h:81
SFA
#define SFA
A very small number, used to avoid divide by 0 errors.
Definition: general.h:169
Star_CatalogPosn::parallax_rad
double parallax_rad
π - annual parallax (radian)
Definition: star.h:91
Sky_Times
This structure contains the continuously varying time (and earth rotation) data, in various forms tha...
Definition: sky.h:184
STAR_DECRANERR
@ STAR_DECRANERR
Declination out of range.
Definition: star.h:110
star_epochToStr
char * star_epochToStr(const Star_CatalogPosn *coordBlock, char epochStr[], size_t epochStrSize)
Write the epoch used for proper motion calculations out in a standard form.
Definition: star.c:698
RAD2ARCSEC
#define RAD2ARCSEC
radians to arcseconds
Definition: astron.h:30
Sky_TrueEquatorial::eqEq_rad
double eqEq_rad
Equation of the Equinoxes (radian).
Definition: sky.h:115
v3d_multMtransxV
V3D_Vector * v3d_multMtransxV(V3D_Vector *destV, const V3D_Matrix *srcM, const V3D_Vector *srcV)
Multiply 3x1 vector by the transpose of the 3x3 matrix to give a new 3x1 vector, as per equation [des...
Definition: vectors3d.c:475
secToRad
static double secToRad(double angle_s)
Returns angle_s converted from seconds to radians.
Definition: astron.h:50
Sky_SiteProp
Site properties.
Definition: sky.h:315
star_getTopocentric
void star_getTopocentric(double j2kUtc_d, const Sky_DeltaTs *deltas, const Sky_SiteProp *site, Sky_SiteHorizon *topo)
Calls star_getApparent() to calculate the star's position in apparent coordinates,...
Definition: star.c:356
sky1_frameBiasFK5
void sky1_frameBiasFK5(V3D_Matrix *biasM)
Create the frame bias matrix from the IAU 2000 precession-nutation model.
Definition: sky1.c:339
star_annAberr
void star_annAberr(const V3D_Vector *p1V, const V3D_Vector *earthVelV_aupd, V3D_Vector *p2V)
Apply the annual aberration correction, to convert an object's coordinates from geocentric geometric ...
Definition: star.c:603
Star_CatalogPosn::muRaInclCosDec
bool muRaInclCosDec
μα term already includes the cosδ factor.
Definition: star.h:93
Star_CatalogPosn::objectName
char objectName[80]
Name of the celestial object (optional)
Definition: star.h:80
TWOPI
#define TWOPI
,
Definition: general.h:160
MJD_B1900
#define MJD_B1900
MJD of Besselian std epoch B1900.0.
Definition: astron.h:23
Star_CatalogPosn::cSys
Star_CoordSys cSys
equator and origin used for α & δ
Definition: star.h:83
APPARENT
@ APPARENT
True equator and equinox of date.
Definition: star.h:70
sky1_createNut1980Matrix
void sky1_createNut1980Matrix(const Sky1_Nut1980 *nut, V3D_Matrix *nutM)
This routine calculates the Nutation matrix, using the nutation in longitude, the nutation in obliqui...
Definition: sky1.c:676
DEG2RAD
#define DEG2RAD
degrees to radians
Definition: general.h:165
ICRS
@ ICRS
International Celestial Reference System.
Definition: star.h:74
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
sky1_appToTirs
void sky1_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: sky1.c:810
star_getApparent
void star_getApparent(double j2kTT_cy, Sky_TrueEquatorial *pos)
Calculate the position of the currently selected star (or other object outside the solar system) as a...
Definition: star.c:315
star_earth
void star_earth(double t1_cy, const V3D_Matrix *npM, V3D_Vector *ebV_au, V3D_Vector *ebdotV_aupd, V3D_Vector *sbV_au)
Calculate the position and velocity of the Earth, for use in making annual parallax and aberration co...
Definition: star.c:461
STAR_ERRINVR
@ STAR_ERRINVR
Radial Velocity can't be decoded.
Definition: star.h:120
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
Star_CatalogPosn::muRA_radpcy
double muRA_radpcy
μα - Proper motion in RA (radian/Julian century)
Definition: star.h:89
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_subtractV
V3D_Vector * v3d_subtractV(V3D_Vector *destV, const V3D_Vector *srcV1, const V3D_Vector *srcV2)
Vector subtraction: [destV] = [srcV1] - [srcV2].
Definition: vectors3d.c:106
V3D_Vector
3x1 vector.
Definition: vectors3d.h:57
v3d_dotProductV
double v3d_dotProductV(const V3D_Vector *srcV1, const V3D_Vector *srcV2)
Return the dot product of the two vectors: res = [srcV1] · [srcV2] or res = ||srcV1|| * ||srcV2|| * c...
Definition: vectors3d.c:148
degToRad
static double degToRad(double angle_deg)
Returns angle_deg converted from degrees to radians.
Definition: general.h:180
Star_CatalogPosn::epochT_cy
double epochT_cy
time zero for proper motion (Julian centuries since J2000, TT timescale)
Definition: star.h:86
FK4
@ FK4
Mean equator and equinox of Besselian epoch.
Definition: star.h:72
star_setCatalogOldStyle
int star_setCatalogOldStyle(const char objectName[], double ra_h, double dec_deg, Star_CoordSys coordSys, double equinoxYear, double epochYear, double muRa_spcy, double muDec_aspcy, double annParallax_as, double radVel_kmps, Star_CatalogPosn *coordBlock)
This is one of three alternative routines for loading the information for a celestial object into a b...
Definition: star.c:868
star_parseCoordString
int star_parseCoordString(const char inStr[], Star_CatalogPosn *coordBlock)
This is one of three alternative routines for loading the information for a celestial object into a b...
Definition: star.c:972
sky1_precessionIAU1976
void sky1_precessionIAU1976(double t0, double t1, Sky1_Prec1976 *terms)
This procedure calculates the equatorial precession parameters ζ, z, and θ which represent the rotati...
Definition: sky1.c:375
DEFINE_THIS_FILE
#define DEFINE_THIS_FILE
Leave DEFINE_THIS_FILE undefined.
Definition: general.h:252
star.h
star.h - Astronomical conversion routines for stars and other objects beyond the Solar System
FK5
@ FK5
Mean equator and equinox of Julian epoch.
Definition: star.h:73
INTERMEDIATE
@ INTERMEDIATE
True equator and CIO of date.
Definition: star.h:71
sky.h
sky.h - structures and routines for astronomical observing & tracking
STAR_NOMUDEC
@ STAR_NOMUDEC
Specification of proper motion in Dec is missing when mu_ra was provided.
Definition: star.h:113
JUL_CENT
#define JUL_CENT
Length of Julian Century in days.
Definition: astron.h:27
vectors3d.h
vectors3d.h - Three dimensional geometry, vectors and matrices
STAR_ERRINPARAL
@ STAR_ERRINPARAL
Parallax can't be decoded.
Definition: star.h:117
sky1_createPrec1976Matrix
void sky1_createPrec1976Matrix(const Sky1_Prec1976 *terms, V3D_Matrix *precM)
This routine calculates the precession matrix, based on angles ζ, z, and θ.
Definition: sky1.c:437
Sky_Times::j2kUT1_d
double j2kUT1_d
days since J2000.0, UT1 timescale [Du]
Definition: sky.h:186
Sky1_Nut1980::eqEq_rad
double eqEq_rad
Equation of the Equinoxes (radian)
Definition: sky1.h:79
STAR_ERRINOBJ
@ STAR_ERRINOBJ
Error extracting object name from the string.
Definition: star.h:101
Star_CatalogPosn
Catalogue position of a celestial object.
Definition: star.h:79
sky1_nutationIAU1980
void sky1_nutationIAU1980(double t_cy, int precision, Sky1_Nut1980 *nut)
Calculates the nutation in longitude and obliquity, according to the IAU 1980 Nutation Theory.
Definition: sky1.c:481
v3d_addToUVfast
V3D_Vector * v3d_addToUVfast(V3D_Vector *modV1, const V3D_Vector *srcV2)
Modify a unit length rectangular position vector modV1 by adding a small correction vector srcV2 to i...
Definition: vectors3d.c:223
hrsToRad
static double hrsToRad(double angle_h)
Returns angle_h converted from hours to radians.
Definition: astron.h:55
sincos
static void sincos(double angle_rad, double *sinA, double *cosA)
Calculates sine and cosine of an angle.
Definition: instead-of-math.h:114
STAR_RARANERR
@ STAR_RARANERR
Right Ascension out of range.
Definition: star.h:107
star_catalogToApp
void star_catalogToApp(const Star_CatalogPosn *c, double j2kTT_cy, const Sky1_Nut1980 *nut, V3D_Vector *appV, double *dist_au)
Convert the catalogue coordinates for a star (or other object outside the Solar System) to apparent c...
Definition: star.c:148
Sky1_Prec1976
Precession angles.
Definition: sky1.h:68
star_setCurrentObject
void star_setCurrentObject(const Star_CatalogPosn *c)
Copies the catalogue information in c to internal storage for later use by star_getApparent()
Definition: star.c:135
STAR_PINEEDSPM
@ STAR_PINEEDSPM
Proper motion must be specified if specifying parallax.
Definition: star.h:118
STAR_VRNEEDSPM
@ STAR_VRNEEDSPM
Proper motion and parallax must be specified if specifying Radial Velocity.
Definition: star.h:121
Star_CatalogPosn::radVel_aupcy
double radVel_aupcy
ν - radial velocity (AU/Julian century)
Definition: star.h:92
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
Star_CatalogPosn::dec_rad
double dec_rad
δ - Declination (radian)
Definition: star.h:82
STAR_NORMAL
@ STAR_NORMAL
Normal successful completion.
Definition: star.h:99
STAR_NODEC
@ STAR_NODEC
Specification of Declination is missing.
Definition: star.h:108
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
STAR_IVEPOCH
@ STAR_IVEPOCH
Invalid epoch or equinox specification.
Definition: star.h:100
Sky_SiteHorizon
Coordinates of a celestial object in the horizon frame, in both rectangular and polar forms.
Definition: sky.h:129
star_equinoxToStr
char * star_equinoxToStr(const Star_CatalogPosn *coordBlock, char equinoxStr[], size_t eqnxStrSize)
Write the coordinate system and equinox out in a standard form.
Definition: star.c:632
Star_CatalogPosn::eqnxT_cy
double eqnxT_cy
time of equinox for FK4 or FK5 positions (Julian centuries since J2000, TT timescale)
Definition: star.h:84