Skip to content

Commit

Permalink
Merge pull request #89 from Smithsonian/further-cspice-support
Browse files Browse the repository at this point in the history
Further CSPICE support, improved error handling
  • Loading branch information
attipaci authored Nov 11, 2024
2 parents 51fadb0 + bda1586 commit 0e7ef7c
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 48 deletions.
13 changes: 7 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

Changes expected for the next feature release, expected around 1 February 2025.

## Added
### Added

- #57: New `novas_make_redshifted_object()` to simplify the creation of distant catalog sources that are characterized
with a redshift measure rather than a radial velocity value.
Expand All @@ -34,11 +34,12 @@ Changes expected for the next feature release, expected around 1 February 2025.

- #86: NAIF CSPICE integration: `novas_use_cspice()`, `novas_use_cspice_planets()`, `novas_use_cspice_ephem()`
to use the NAIF CSPICE library for all Solar-system sources, major planets only, or for other bodies only.
`NOVAS_EPHEM_OBJECTS` should use NAIF IDs with CSPICE (or else -1 for name-based lookup). These functions are
provided by the `libsolsys-cspice.so[.1]` and/or `.a` plugin libraries, which are built contingent on the
`CSPICE_SUPPORT` variable being set to 1 prior to the build. The build of the plugin module requires an accessible
installation of the CSPICE development files (C headers and unversioned static or shared libraries depending on the
needs of the build).
`NOVAS_EPHEM_OBJECTS` should use NAIF IDs with CSPICE (or else -1 for name-based lookup).
Also provides `novas_cspice_add_kernel()` and `novas_cspice_remove_kernel()` for convenience to manage the set of
active kernels (#89). These functions are provided by the `libsolsys-cspice.so[.1]` and/or `.a` plugin libraries,
which are built contingent on the `CSPICE_SUPPORT` variable being set to 1 prior to the build. The build of the
plugin module requires an accessible installation of the CSPICE development files (C headers and unversioned static
or shared libraries depending on the needs of the build).

- #87: Added `novas_planet_for_name()` function to return the NOVAS planet ID for a given (case insensitive) name.

Expand Down
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,8 @@ before that level of accuracy is reached.
- NAIF CSPICE integration: `novas_use_cspice()`, `novas_use_cspice_planets()`, `novas_use_cspice_ephem()` to use the
NAIF CSPICE library for all Solar-system sources, major planets only, or for other bodies only.
`NOVAS_EPHEM_OBJECTS` should use NAIF IDs with CSPICE (or else -1 for name-based lookup).
`NOVAS_EPHEM_OBJECTS` should use NAIF IDs with CSPICE (or else -1 for name-based lookup). Also provides
`novas_cspice_add_kernel()` and `novas_cspice_remove_kernel()`.
- NAIF/NOVAS ID conversions for major planets (and Sun, Moon, SSB): `novas_to_naif_planet()`,
`novas_to_dexxx_planet()`, and `naif_to_novas_planet()`.
Expand Down Expand Up @@ -1000,18 +1001,21 @@ repository to help you build CSPICE with shared libraries and dynamically linked
Here is an example on how you might use CSPICE with SuperNOVAS in your application code:

```c
// You can load the desired kernels for CSPICE, using the CSPICE API.
// You can load the desired kernels for CSPICE
// E.g. load DE440s and the Mars satellites from /data/ephem:
furnsh_c("/data/ephem/de440s.bsp");
furnsh_c("/data/ephem/mar097.bsp");
...
int status;

// check for CSPICE errors
if(return_c()) {
// oops, one of the kernels must not have loaded...
status = novas_cspice_add_kernel("/data/ephem/de440s.bsp");
if(status < 0) {
// oops, the kernels must not have loaded...
...
}

// Load additional kernels as needed...
status = novas_cspice_add_kernel("/data/ephem/mar097.bsp");
...


// Then use CSPICE as your SuperNOVAS ephemeris provider
novas_use_cspice();
```
Expand Down
6 changes: 5 additions & 1 deletion include/solarsystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,11 @@ int novas_use_cspice_ephem();

int novas_use_cspice_planets();

#endif /* USE_CALCEPH */
int novas_cspice_add_kernel(const char *filename);

int novas_cspice_remove_kernel(const char *filename);

#endif /* USE_CSPICE */


/// \cond PRIVATE
Expand Down
12 changes: 8 additions & 4 deletions src/solsys-calceph.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,10 @@ static short planet_calceph_hp(const double jd_tdb[2], enum novas_planet body, e
return novas_error(3, EAGAIN, fn, "calceph_compute() failure (NOVAS ID=%d)", body);

for(i = 3; --i >= 0;) {
if(position) position[i] = pv[i] * NORM_POS;
if(velocity) velocity[i] = pv[3 + i] * NORM_VEL;
if(position)
position[i] = pv[i] * NORM_POS;
if(velocity)
velocity[i] = pv[3 + i] * NORM_VEL;
}

return 0;
Expand Down Expand Up @@ -334,8 +336,10 @@ static int novas_calceph(const char *name, long id, double jd_tdb_high, double j
return novas_error(3, EAGAIN, fn, "calceph_compute() failure (name='%s', NAIF=%ld)", name ? name : "<null>", id);

for(i = 3; --i >= 0;) {
if(pos) pos[i] = pv[i] * NORM_POS;
if(vel) vel[i] = pv[3 + i] * NORM_VEL;
if(pos)
pos[i] = pv[i] * NORM_POS;
if(vel)
vel[i] = pv[3 + i] * NORM_VEL;
}

return 0;
Expand Down
173 changes: 153 additions & 20 deletions src/solsys-cspice.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,131 @@ static int mutex_unlock() {
return 0;
}

/**
* Supresses CSPICE error output and disables exit on error behavior, so we can check and process
* CSPICE errors gracefully ourselves.
*
* REFERENCES:
* <ol>
* <li>https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/erract_c.html</li>
* <li>https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/errprt_c.html</li>
* </ol>
*
* @return 0
*/
static int suppress_cspice_errors() {
erract_c("SET", 0, "RETURN"); // Do not exit in case of CSPICE errors.
errprt_c("SET", 0, "NONE"); // Suppress CSPICE error messages
return 0;
}

/**
* Returns a short description of the CSPICE error in the supplied buffer, and resets the
* CSPICE error state.
*
* @param[out] msg the buffer in which to return the message.
* @param len (bytes) maximum length of the message to return including termination
* @return the CSPICE error code.
*/
static int get_cspice_error(char *msg, int len) {
int err = return_c();
getmsg_c("SHORT", len, msg);
reset_c();
return err;
}

/**
* Adds a SPICE kernel to the currently managed open kernels. Subsequent ephemeris lookups through
* CSPICE will use the added kernel. It's simply a wrapper around the CSPICE `furnsh_c()` routine,
* with graceful error handling. You can of course add kernels using `furnsh_c()` directly to the
* same effect.
*
* REFERENCES:
* <ol>
* <li>https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/furnsh_c.html</li>
* </ol>
*
* @param filename The fully qualified path to the ephemeris kernel data (e.g.
* "/data/ephem/de440s.bsp")
* @return 0 if successful, or else -1 if there was an error (errno will be set to
* EINVAL).
*
* @sa novas_cspice_remove_kernel()
*
* @author Attila Kovacs
* @since 1.2
*/
int novas_cspice_add_kernel(const char *filename) {
static const char *fn = "novas_cspice_add_kernel";

char msg[100];
int err;

if(!filename)
return novas_error(-1, EINVAL, fn, "input filename is NULL");
if(!filename[0])
return novas_error(-1, EINVAL, fn, "input filename is empty");

suppress_cspice_errors();

prop_error(fn, mutex_lock(), 0);
reset_c();
furnsh_c(filename);
err = get_cspice_error(msg, sizeof(msg));
mutex_unlock();

if(err)
return novas_error(-1, EINVAL, fn, "furnsh_c(%s): %s", filename, msg);

return 0;
}

/**
* Removes a SPICE kernel from the currently managed open kernels. Subsequent ephemeris lookups
* through CSPICE will not use the removed kernel data. It's simply a wrapper around the CSPICE
* `unload_c()` routine, with graceful error handling. You can of course remove kernels using
* `unload_c()` directly to the same effect.
*
* REFERENCES:
* <ol>
* <li>https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/unload_c.html</li>
* </ol>
*
* @param filename The fully qualified path to the ephemeris kernel data (e.g.
* "/data/ephem/de440s.bsp")
* @return 0 if successful, or else -1 if there was an error (errno will be set to
* EINVAL).
*
* @sa novas_cspice_add_kernel()
*
* @author Attila Kovacs
* @since 1.2
*/
int novas_cspice_remove_kernel(const char *filename) {
static const char *fn = "novas_cspice_remove_kernel";

char msg[100];
int err;

if(!filename)
return novas_error(-1, EINVAL, fn, "input filename is NULL");
if(!filename[0])
return novas_error(-1, EINVAL, fn, "input filename is empty");

suppress_cspice_errors();

prop_error(fn, mutex_lock(), 0);
reset_c();
unload_c(filename);
err = get_cspice_error(msg, sizeof(msg));
mutex_unlock();

if(err)
return novas_error(-1, EINVAL, fn, "unload_c(%s): %s", filename, msg);

return 0;
}

/**
* Provides an interface between the NAIF CSPICE C library and NOVAS-C for regular (reduced)
* precision applications. The user must set the cspice ephemeris binary data to use using the
Expand All @@ -71,6 +196,7 @@ static int mutex_unlock() {
* REFERENCES:
* <ol>
* <li>NAIF CSPICE: https://naif.jpl.nasa.gov/naif/toolkit.html</li>
* <li>https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkez_c.html</li>
* <li>Kaplan, G. H. "NOVAS: Naval Observatory Vector Astrometry
* Subroutines"; USNO internal document dated 20 Oct 1988;
* revised 15 Mar 1990.</li>
Expand Down Expand Up @@ -102,6 +228,7 @@ static short planet_cspice_hp(const double jd_tdb[2], enum novas_planet body, en
double *velocity) {
static const char *fn = "planet_cspice_hp";

char msg[100];
SpiceDouble pv[6];
SpiceDouble lt;
SpiceInt target, center;
Expand All @@ -112,7 +239,8 @@ static short planet_cspice_hp(const double jd_tdb[2], enum novas_planet body, en
return novas_error(-1, EINVAL, fn, "jd_tdb input time array is NULL.");

target = novas_to_naif_planet(body);
if(target < 0) return novas_trace(fn, 1, 0);
if(target < 0)
return novas_trace(fn, 1, 0);

switch(origin) {
case NOVAS_BARYCENTER:
Expand All @@ -135,27 +263,27 @@ static short planet_cspice_hp(const double jd_tdb[2], enum novas_planet body, en
// "J2000" and "ICRF" are treated the same, with "J2000" being the compatibility label.
reset_c();
spkez_c(target, tdb2000, "J2000", "NONE", center, pv, &lt);
err = return_c();
reset_c();
err = get_cspice_error(msg, sizeof(msg));

if(err) {
SpiceInt alt = novas_to_dexxx_planet(body);
if(alt != target) {
// Try with DExxx ID (barycenter vs planet center)
spkez_c(alt, tdb2000, "J2000", "NONE", center, pv, &lt);
err = return_c();
reset_c();
err = get_cspice_error(msg, sizeof(msg));
}
}

mutex_unlock(sem);

if(err)
return novas_error(3, EAGAIN, fn, "spkez_c() error (NOVAS ID=%d)", body);
return novas_error(3, EAGAIN, fn, "spkez_c(NOVAS ID=%d, JD=%.1f): %s", body, (jd_tdb[0] + jd_tdb[1]), msg);

for(i = 3; --i >= 0;) {
if(position) position[i] = pv[i] * NORM_POS;
if(velocity) velocity[i] = pv[3 + i] * NORM_VEL;
if(position)
position[i] = pv[i] * NORM_POS;
if(velocity)
velocity[i] = pv[3 + i] * NORM_VEL;
}

return 0;
Expand Down Expand Up @@ -218,6 +346,11 @@ static short planet_cspice(double jd_tdb, enum novas_planet body, enum novas_ori
* The call will use whatever ephemeris (SPK) files were loaded by the CSPICE library prior
* to the call (see furnsh_c() function)
*
* REFERENCES:
* <ol>
* <li>https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkez_c.html</li>
* </ol>
*
* @param name The name of the solar-system body. It is important only if the 'id' is
* -1.
* @param id The NAIF ID number of the solar-system body for which the position in
Expand Down Expand Up @@ -253,6 +386,7 @@ static short planet_cspice(double jd_tdb, enum novas_planet body, enum novas_ori
static int novas_cspice(const char *name, long id, double jd_tdb_high, double jd_tdb_low, enum novas_origin *origin, double *pos, double *vel) {
static const char *fn = "novas_cspice";

char msg[100];
SpiceDouble pv[6];
SpiceDouble lt;
SpiceInt target, center;
Expand All @@ -276,14 +410,13 @@ static int novas_cspice(const char *name, long id, double jd_tdb_high, double jd

reset_c();
bodn2c_c(spiceName, &spiceCode, &spiceFound);
err = return_c();
reset_c();
err = get_cspice_error(msg, sizeof(msg));

if(!spiceFound)
return novas_error(1, EINVAL, fn, "CSPICE could not find a NAIF ID for '%s'", name);

if(err)
return novas_error(1, EINVAL, fn, "CSPICE name lookup error for '%s'", name);
return novas_error(1, EINVAL, fn, "CSPICE name lookup error for '%s': %s", name, msg);

id = spiceCode;
}
Expand All @@ -304,17 +437,19 @@ static int novas_cspice(const char *name, long id, double jd_tdb_high, double jd
// "J2000" and "ICRF" are treated the same, with "J2000" being the compatibility label.
reset_c();
spkez_c(target, tdb2000, "J2000", "NONE", center, pv, &lt);
err = return_c();
reset_c();
err = get_cspice_error(msg, sizeof(msg));

mutex_unlock();

if(err)
return novas_error(3, EAGAIN, fn, "spkez_c() failure (name='%s', NAIF=%ld)", name ? name : "<null>", id);
return novas_error(3, EAGAIN, fn, "spkez_c(name='%s', NAIF=%ld, JD=%.1f): %s",
name ? name : "<null>", id, (jd_tdb_high + jd_tdb_low), msg);

for(i = 3; --i >= 0;) {
if(pos) pos[i] = pv[i] * NORM_POS;
if(vel) vel[i] = pv[3 + i] * NORM_VEL;
if(pos)
pos[i] = pv[i] * NORM_POS;
if(vel)
vel[i] = pv[3 + i] * NORM_VEL;
}

return 0;
Expand All @@ -337,8 +472,7 @@ static int novas_cspice(const char *name, long id, double jd_tdb_high, double jd
* @since 1.2
*/
int novas_use_cspice_ephem() {
errprt_c("SET", 100, "NONE"); // Suppress CSPICE error messages
erract_c("SET", 0, "RETURN"); // Do not exit in case of CSPICE errors.
suppress_cspice_errors();
set_ephem_provider(novas_cspice);
return 0;
}
Expand All @@ -362,8 +496,7 @@ int novas_use_cspice_ephem() {
* @since 1.2
*/
int novas_use_cspice_planets() {
errprt_c("SET", 100, "NONE"); // Suppress CSPICE error messages
erract_c("SET", 0, "RETURN"); // Do not exit in case of CSPICE errors.
suppress_cspice_errors();
set_planet_provider_hp(planet_cspice_hp);
set_planet_provider(planet_cspice);
return 0;
Expand Down
Loading

0 comments on commit 0e7ef7c

Please sign in to comment.