![]() |
SuperNOVAS C API v1.6
High-precision C/C++ astrometry library
|
This guide is specifically for using SuperNOVAS as a C99 library. There is a separate guide for using the C++11 API. The links below let you jump to the relevant sections:
SuperNOVAS is a C99 library at its core. If you are looking for maximum speed, or want to use SuperNOVAS on older platforms, the C99 API is there for you.
The following links provide further useful documentation resources for you:
There are a number of ways you can build your application with SuperNOVAS. See which of the options suits your needs best:
Provided you have installed the SuperNOVAS headers and (static or shared) libraries into a standard location, you can build your application against it easily. For example, to build myastroapp.c against SuperNOVAS, you might have a Makefile with contents like:
If you have a legacy NOVAS C 3.1 application, it is possible that the compilation will give you errors due to missing includes for stdio.h, stdlib.h, ctype.h or string.h, because these headers were implicitly included with novas.h in NOVAS C 3.1, but not in SuperNOVAS (at least not by default), as a matter of best practice. If this is a problem for you can 'fix' it in one of two ways: (1) Add the missing #include directives to your application source explicitly, or if that's not an option for you, then (2) set the -DCOMPAT compiler flag when compiling your application:
If your application uses optional planet or ephemeris calculator modules, you may need to specify the additional shared libraries also:
Add the appropriate bits from below to the CMakeLists.txt file of your application (my-application):
SuperNOVAS began deprecating some NOVAS C functions, either because they are no longer needed; or are not easy to use and have better alternatives around; or are internals that should never have been exposed to end-users. The deprecations are marked in the inline and HTML API documentations, suggesting also alternatives.
That said, the deprecated parts of the API are NOT removed, nor we plan on removing them for the foreseeable future. Instead, they serve as a gentle reminder to users that perhaps they should stay away from these features for their own good.
However, you have the option to force yourself to avoid using the deprecated API if you choose to do so, by compiling your application with -D_EXCLUDE_DEPRECATED, or equivalently, by defining _EXCLUDE_DEPRECATED in your source code before including novas.h. E.g.:
After that, your compiler will complain if your source code references any of the deprecated entities, so you may change that part of your code to use the recommended alternatives instead.
The NOVAS C way to handle planet or other ephemeris functions was to link particular modules to provide the solarsystem() / solarsystem_hp() and readeph() functions. This approach is discouraged in SuperNOVAS, with preference for selecting implementations at runtime. The old, deprecated way, of incorporating Solar-system data is supported, nevertheless, for legacy applications with some caveats.
To use your own existing default solarsystem() implementation in the NOVAS C way, you will have to build SuperNOVAS with SOLSYS_SOURCE set to the source file(s) of the implementation (config.mk or the environment).
The same principle applies to using your specific legacy readeph() implementation, except that you must set READEPH_SOURCE to the source file(s) of the chosen implementation when building SuperNOVAS).
(You might have to also add additional include directories to CPPFLAGS, e.g. -I/my-path/include for you custom sources for their associated headers).
A better way to recycle your old planet and ephemeris calculator modules may be to rename solarsystem() / solarsystem_hp() functions therein to e.g. my_planet_calculator() / my_planet_calculator_hp() and then in your application can specify these functions as the provider at runtime.
E.g.:
(You might also change the short type parameters to the SuperNOVAS enum types while at it, to conform to the SuperNOVAS novas_planet_provider / novas_planet_provider_hp types.)
For readeph() implementations, it is recommended that you change both the name and the footprint to e.g.:
and then then apply it in your application as:
While all of that requires some minimal changes to your old code, the advantage of this preferred approach is that you do not need to re-build the library with USER_SOLSYS and/or USER_READEPH defined.
SuperNOVAS v1.1 has introduced a new, more intuitive, more elegant, and more efficient approach for calculating astrometric positions of celestial objects. The guide below is geared towards this new method. However, the original NOVAS C approach remains viable also (albeit often less efficient).
A sidereal source may be anything beyond the Solar system with 'fixed' catalog coordinates. It may be a star, or a galactic molecular cloud, or a distant quasar.
First, you must provide the astrometric parameters (coordinates, and optionally radial velocity or redshift, proper motion, and/or parallax or distance also). Let's assume we pick a star for which we have B1950 (i.e. FK4) coordinates. We begin with the assigned name and the R.A. / Dec coordinates.
If you have coordinates as strings in decimal or HMS / DMS format, you might use novas_str_hours() and/or novas_str_degrees() to convert them to hours/degrees for novas_init_cat_entry(), with a fair bit of flexibility on the particulars of the representation, e.g.:
Next, if it's a star or some other source within our own Galaxy, you'll want to specify its proper motion (in the same reference system as the above coordinates), so we can calculate its position for the epoch of observation.
For Galactic sources you will also want to set the parallax using novas_set_parallax() or equivalently the distance (in parsecs) using novas_set_distance(), e.g.:
Finally, for spectroscopic applications you will also want to set the radial velocity. You can use novas_set_ssb_vel() if you have standard radial velocities defined with respect to the Solar System Barycenter; or novas_set_lsr_vel() if the velocity is relative to the Local Standard of Rest (LSR); or else novas_set_redshift() if you have a redshift measure (as is typical for distant galaxies and quasars). E.g.:
Alternatively, if you prefer, you may use the original NOVAS C make_cat_entry() to set the astrometric parameters above all at once.
E.g.:
Next, we wrap that catalog source into a generic celestial object structure. (An object handles various Solar-system sources also, as you'll see further below). Whereas the catalog source may have been defined in any epoch / catalog system, the object structure shall define ICRS coordinates always (no exceptions):
Alternatively, for high-_z_ sources you might simply use the 1-step make_redshifted_object_sys() e.g.:
Next, we define the location where we observe from. Let's assume we have a GPS location:
Again you might use novas_str_degrees() for typical string representations of the longitude and latitude coordinates here, such as:
Alternatively, you can also specify airborne observers, or observers in Earth orbit, in heliocentric orbit, at the geocenter, or at the Solar-system barycenter. The above also sets default, mean annual weather parameters based on the location and a global model based on Feulner et al. (2013).
You can, of course, set actual weather values after, as appropriate, if you need them for the refraction models, e.g.:
Next, we set the time of observation. For a ground-based observer, you will need to provide SuperNOVAS with the UT1 - UTC time difference (a.k.a. DUT1), and the current leap seconds. You can obtain suitable values for DUT1 from IERS, and for the highest precision, interpolate for the time of observations. For the example, let's assume 37 leap seconds, and DUT1 = 0.042,
Then we can set the time of observation, for example, using the current UNIX time:
Alternatively, you may set the time as a Julian date in the time measure of choice (UTC, UT1, TT, TDB, GPS, TAI, TCG, or TCB):
or, for the best precision we may do the same with an integer / fractional split:
Or, you might use string dates, such as an ISO timestamp:
Note, that the likes of novas_set_time() will automatically apply diurnal corrections to the supplied UT1-UTC time difference for libration and ocean tides. Thus, the supplied values should not include these. Rather you should pass dut1 directly (or interpolated) from the IERS Bulletin values for the time of observation.
Next, we set up an observing frame, which is defined for a unique combination of the observer location and the time of observation:
Here xp and yp are small (sub-arcsec level) corrections to Earth orientation. Values for these are are published in the IERS Bulletins. These values should be interpolated for the time of observation, but should NOT be corrected for libration and ocean tides (novas_make_frame() will apply such corrections as appropriate for full accuracy frames). The Earth orientation parameters (EOP) are needed only when converting positions from the celestial CIRS (or TOD) frame to the Earth-fixed ITRS (or PEF) frames. You may ignore these and set zeroes if not interested in Earth-fixed calculations or if sub-arcsecond precision is not required.
The advantage of using the observing frame, is that it enables very fast position calculations for multiple objects in that frame (see the benchmarks), since all sources in a frame have well-defined, fixed, topological positions on the celestial sphere. It is only a matter of expressing these positions as coordinates (and velocities) in a particular coordinate system. So, if you need to calculate positions for thousands of sources for the same observer and time, it will be significantly faster than using the low-level NOVAS C routines instead. You can create derivative frames for different observer locations, if need be, via novas_change_observer().
Now we can calculate the apparent R.A. and declination for our source, which includes proper motion (for sidereal sources) or light-time correction (for Solar-system bodies), and also aberration corrections for the moving observer and gravitational deflection around the major Solar System bodies (in full accuracy mode). You can calculate an apparent location in the coordinate system of choice (ICRS/GCRS, CIRS, J2000, MOD, TOD, TIRS, or ITRS) using novas_sky_pos(). E.g.:
Apart from providing precise apparent R.A. and declination coordinates, the sky_pos structure also provides the x,y,z unit vector pointing in the observed direction of the source (in the designated coordinate system). We also get radial velocity (for spectroscopy), and apparent distance for Solar-system bodies (e.g. for apparent-to-physical size conversion).
If your ultimate goal is to calculate the azimuth and elevation angles of the source at the specified observing location, you can proceed from the sky_pos data you obtained above (in whichever coordinate system!):
Above we converted the apparent coordinates, that were calculated in CIRS, to refracted azimuth and elevation coordinates at the observing location, using the novas_standard_refraction() function to provide a suitable refraction correction. We could have used novas_optical_refraction() instead to use the weather data embedded in the frame's observer structure, or some user-defined refraction model, or else NULL to calculate unrefracted elevation angles.
Solar-system sources work similarly to the above with a few important differences at the start.
Historically, NOVAS divided Solar-system objects into two categories: (1) major planets (including also the Sun, the Moon, and the Solar-system Barycenter); and (2) 'ephemeris' type objects, which are all other Solar-system objects. The main difference is the numbering convention. NOVAS major planets have definitive ID numbers (see enum novas_planet), whereas 'ephemeris' objects have user-defined IDs. They are also handled by two separate adapter functions (although SuperNOVAS has the option of using the same ephemeris provider for both types of objects also).
Thus, instead of make_cat_object() you define your source as a planet or ephemeris type object with a name or ID number that is used by the ephemeris service you provided. For major planets you might want to use make_planet(), if they use a novas_planet_provider function to access ephemeris data with their NOVAS IDs, or else make_ephem_object() for more generic ephemeris handling via a user-provided novas_ephem_provider. E.g.:
And then, it's the same spiel as before, e.g.:
As of version 1.2 you can also define solar system sources with Keplerian orbital elements (such as the most up-to-date ones provided by the Minor Planet Center for asteroids, comets, etc.):
Finally, as of version 1.4, you might generate approximate (arcmin-level) orbitals for the major planets (but not Earth!), the Moon, and the Earth-Moon Barycenter (EMB) also. E.g.:
While the planet and Moon orbitals are not suitable for precision applications, they can be useful for determining approximate positions (e.g. via the novas_approx_heliocentric() and novas_approx_sky_pos() functions), and for rise/set time calculations.
SuperNOVAS can calculate positions and velocities for the Moon to arcsecond (or km) level, or better, accuracy using the ELP2000 / MPP02 semi-analytical model by Chapront & Francou (2003). This means that you can calculate astrometric quantities for the Moon with reasonable accuracy even without an ephemeris provider configured.
For example, you can calculate the apparent place of the Moon in an observing frame as:
Alternatively, you can obtain geometric positions and velocities of the Moon, relative to the observer using novas_moon_elp_posvel() instead.
You can also obtain the current phase of the Moon, for the time of observation:
or, calculate when the Moon will reach a particular phase next:
Of course, SuperNOVAS allows you to go in reverse, for example from an observed Az/El position all the way to proper ICRS R.A./Dec coordinates.
E.g.:
Voila! And, of course you might want the coordinates in some other reference systems, such as B1950. For that you can simply add a transformation before vector2radec() above, e.g. as:
You may be interested to know when sources rise above or set below some specific elevation angle, or at what time they appear to transit at the observer location. SuperNOVAS has routines to help you with that too.
Given that rise, set, or transit times are dependent on the day of observation, and observer location, they are effectively tied to an observer frame.
Note, that in the current implementation these calls are not well suited sources that are at or within the geostationary orbit, such as such as Low Earth Orbit satellites (LEOs), geostationary satellites (which never really rise, set, or transit), or some Near Earth Objects (NEOs), which will rise set multiple times per day. For the latter, the above calls may still return a valid time, only without the guarantee that it is the time of the first such event after the specified frame instant. A future implementation may address near-Earth orbits better, so stay tuned for updates.
SuperNOVAS introduces matrix transforms (correctly since version 1.4), which can take a position or velocity vector (geometric or apparent), obtained for an observer frame, from one coordinate system to another efficiently. E.g.:
Transformations support all SupeNOVAS reference systems, that is ICRS/GCRS, J2000, TOD, MOD, CIRS, TIRS, and ITRS. The same transform can also be used to convert apparent positions in a sky_pos structure also, e.g.:
Some of the calculations involved can be expensive from a computational perspective. For the most typical use case however, NOVAS (and SuperNOVAS) has a trick up its sleeve: it caches the last result of intensive calculations so they may be re-used if the call is made with the same environmental parameters again (such as JD time and accuracy).
A direct consequence of the caching of results is that calculations are generally not thread-safe as implemented by the original NOVAS C 3.1 library. One thread may be in the process of returning cached values for one set of input parameters while, at the same time, another thread is saving cached values for a different set of parameters. Thus, when running calculations in more than one thread, the NOVAS C results returned may at times be incorrect, or more precisely they may not correspond to the requested input parameters.
While you should never call the original NOVAS C library from multiple threads simultaneously, SuperNOVAS caches the results in thread local variables (provided your compiler supports it), and is therefore generally safe to use in multi-threaded applications. Just make sure that you:
The NOVAS API has been using conventional units (e.g. AU, km, day, deg, h) typically for its parameters and return values alike. Hence, SuperNOVAS follows the same conventions for its added functions and data structures also. However, when interfacing SuperNOVAS with other programs, libraries, or data files, it is often necessary to use quantities that are expressed in different units, such as SI or CGS. To facilitate such conversions, novas.h provides a set of unit constants, which can be used for converting to/from SI units (and radians).
For example, novas.h contains the following definitions:
You can use these, for example, to convert quantities expressed in conventional units for NOVAS to standard (SI) values, by multiplying NOVAS quantities with the corresponding unit definition. E.g.:
And vice-versa: to convert values expressed in standard (SI) units, you can divide by the appropriate constant to 'cast' an SI value into the particular physical unit, e.g.:
Finally, you can combine them to convert between two different conventional units, e.g.:
SuperNOVAS functions typically input and output times and angles as decimal values (hours and degrees, but also as days and hour-angles), but that is not how these are represented in many cases. Time and right-ascention are often given as string values indicating hours, minutes, and seconds (e.g. "11:32:31.595", or "11h 32m 31.595s"). Similarly angles, are commonly represented as degrees, arc-minutes, and arc-seconds (e.g. "+53 60 19.9"). For that reason, SuperNOVAS provides a set of functions to convert string values expressed in decimal or broken-down format to floating point representation. E.g.,
The conversions have a lot of flexibility. Components can be separated by spaces (as above), by colons, commas, or underscores, by the letters 'h'/'d', 'm', and 's', by single (minutes) and double quotes (seconds), or any combination thereof. Decimal values may be followed by 'h' or 'd' unit markers. Additionally, angles can end with a compass direction, such as 'N', 'E', 'S' or 'W'. So the above could also have been:
or as decimals:
Dates are typically represented broken down into year, month, and day (e.g. "2025-02-16", or "16.02.2025", or "2/16/2025"), with or without a time marker, which itself may or may not include a time zone specification. In astronomy, the most commonly used string representation of dates is with ISO 8601 timestamps. The following are all valid ISO date specifications:
SuperNOVAS provides functions to convert between ISO dates/times and their string representation for convenience. E.g.,
Other SuperNOVAS string date functions will process dates in the astronomical calendar of date by default, that is in the Gregorian calendar after the Gregorian calendar reform of 1582, or the Julian/Roman calendar for dates prior, and support timescales other than UTC also. E.g.:
Or, parse an astronomical date:
Or, parse an astronomical date, including the timescale specification:
Sometimes your input dates are represented in various other formats. You can have additional flexibility for parsing dates using the novas_parse_date_format() and novas_timescale_for_string() functions.
E.g.,
Copyright (C) 2026 Attila Kovács