{"id":15049090,"url":"https://github.com/smithsonian/supernovas","last_synced_at":"2025-04-10T01:41:19.818Z","repository":{"id":223140770,"uuid":"748170057","full_name":"Smithsonian/SuperNOVAS","owner":"Smithsonian","description":"The Naval Observatory NOVAS C astrometry library, made better","archived":false,"fork":false,"pushed_at":"2024-10-29T10:17:55.000Z","size":8016,"stargazers_count":13,"open_issues_count":1,"forks_count":3,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-10-29T12:18:22.726Z","etag":null,"topics":["astrometry","astronomy","astronomy-library","astronomy-software","c-language","c-programming-language","observatory","open-source","positional-astronomy"],"latest_commit_sha":null,"homepage":"https://smithsonian.github.io/SuperNOVAS/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Smithsonian.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"attipaci","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2024-01-25T12:24:52.000Z","updated_at":"2024-10-29T10:16:56.000Z","dependencies_parsed_at":"2024-02-18T15:44:29.955Z","dependency_job_id":"afdacaf4-1876-4f42-b5c1-610ea8ef4da4","html_url":"https://github.com/Smithsonian/SuperNOVAS","commit_stats":{"total_commits":215,"total_committers":4,"mean_commits":53.75,"dds":"0.023255813953488413","last_synced_commit":"76a0a83bf02bc70ae217f87426dbedfbb315e243"},"previous_names":["smithsonian/supernovas"],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smithsonian%2FSuperNOVAS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smithsonian%2FSuperNOVAS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smithsonian%2FSuperNOVAS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smithsonian%2FSuperNOVAS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Smithsonian","download_url":"https://codeload.github.com/Smithsonian/SuperNOVAS/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248142101,"owners_count":21054592,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astrometry","astronomy","astronomy-library","astronomy-software","c-language","c-programming-language","observatory","open-source","positional-astronomy"],"created_at":"2024-09-24T21:18:01.438Z","updated_at":"2025-04-10T01:41:19.803Z","avatar_url":"https://github.com/Smithsonian.png","language":"C","readme":"![Build Status](https://github.com/Smithsonian/SuperNOVAS/actions/workflows/build.yml/badge.svg)\n![Test](https://github.com/Smithsonian/SuperNOVAS/actions/workflows/test.yml/badge.svg)\n![Static Analysis](https://github.com/Smithsonian/SuperNOVAS/actions/workflows/analyze.yml/badge.svg)\n\u003ca href=\"https://smithsonian.github.io/SuperNOVAS/apidoc/html/files.html\"\u003e\n ![API documentation](https://github.com/Smithsonian/SuperNOVAS/actions/workflows/dox.yml/badge.svg)\n\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/Smithsonian/SuperNOVAS\"\u003e\n ![Coverage Status](https://codecov.io/gh/Smithsonian/SuperNOVAS/graph/badge.svg?token=E11OFOISMW)\n\u003c/a\u003e\n\n\u003cpicture\u003e\n  \u003csource srcset=\"resources/CfA-logo-dark.png\" alt=\"CfA logo\" media=\"(prefers-color-scheme: dark)\"/\u003e\n  \u003csource srcset=\"resources/CfA-logo.png\" alt=\"CfA logo\" media=\"(prefers-color-scheme: light)\"/\u003e\n  \u003cimg src=\"resources/CfA-logo.png\" alt=\"CfA logo\" width=\"400\" height=\"67\" align=\"right\"/\u003e\n\u003c/picture\u003e\n\u003cbr clear=\"all\"\u003e\n\n# SuperNOVAS \n\n[![DOI](resources/748170057.svg)](https://doi.org/10.5281/zenodo.14584983)\n\nThe NOVAS C astrometry library, made better.\n\n - [API documentation](https://smithsonian.github.io/SuperNOVAS/apidoc/html/files.html)\n - [SuperNOVAS pages](https://smithsonian.github.io/SuperNOVAS) on github.io \n\n[SuperNOVAS](https://github.com/Smithsonian/SuperNOVAS/) is a C/C++ astronomy software library, providing \nhigh-precision astrometry such as one might need for running an observatory or a precise planetarium program. It is a \nfork of the Naval Observatory Vector Astrometry Software ([NOVAS](https://aa.usno.navy.mil/software/novas_info)) \nC version 3.1, providing bug fixes, tons of extra features, while making it easier (and safer) to use also.\n\nSuperNOVAS is fast, providing 3--5 orders of magnitude faster position calculations than \n[astropy](https://www.astropy.org/) 7.0.0 in a single thread (see the [benchmarks](#benchmarks)), and its performance \nwill scales further with the number of CPUs when calculations are performed in parallel threads.\n\nSuperNOVAS is entirely free to use without licensing restrictions.  Its source code is compatible with the C99 \nstandard, and hence should be suitable for old and new platforms alike. It is light-weight and easy to use, with full \nsupport for the IAU 2000/2006 standards for sub-microarcsecond position calculations. \n\nThis document has been updated for the `v1.3` and later releases.\n\n\n## Table of Contents\n\n - [Introduction](#introduction)\n - [Fixed NOVAS C 3.1 issues](#fixed-issues)\n - [Compatibility with NOVAS C 3.1](#compatibility)\n - [Building and installation](#installation)\n - [Building your application with SuperNOVAS](#integration)\n - [Example usage](#examples)\n - [Tips and tricks](#tips)\n - [Notes on precision](#precision)\n - [Representative benchmarks](#benchmarks)\n - [SuperNOVAS specific features](#supernovas-features)\n - [Incorporating Solar-system ephemeris data or services](#solarsystem)\n - [Runtime debug support](#debug-support)\n - [Release schedule](#release-schedule)\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"introduction\"\u003e\u003c/a\u003e\n## Introduction\n\nSuperNOVAS is a fork of the The Naval Observatory Vector Astrometry Software \n([NOVAS](https://aa.usno.navy.mil/software/novas_info)). (It is not related to the separate NOVA / libnova library.)\n\nThe primary goal of SuperNOVAS is to improve on the stock NOVAS C library via:\n\n - Fixing [outstanding issues](#fixed-issues).\n - Improved [API documentation](https://smithsonian.github.io/SuperNOVAS/apidoc/html/files.html).\n - [Faster calculations](#benchmarks).\n - [New features](#added-functionality).\n - [Refining the API](#api-changes) to promote best programming practices.\n - [Thread-safe calculations](#multi-threading).\n - [Debug mode](#debug-support) with informative error tracing.\n - [Regression testing](https://codecov.io/gh/Smithsonian/SuperNOVAS) and continuous integration on GitHub.\n\nAt the same time, SuperNOVAS aims to be fully backward compatible with the intended functionality of the upstream \nNOVAS C library, such that it can be used as a _build-time_ replacement for NOVAS in your application without having \nto change existing (functional) code you may have written for NOVAS C. \n \nSuperNOVAS is currently based on NOVAS C version 3.1. We plan to rebase SuperNOVAS to the latest upstream release of \nthe NOVAS C library, if new releases become available.\n \nSuperNOVAS is maintained by [Attila Kovács](https://github.com/attipaci) at the Center for Astrophysics \\| Harvard \n\u0026amp; Smithsonian, and it is available through the [Smithsonian/SuperNOVAS](https://github.com/Smithsonian/SuperNOVAS) \nrepository on GitHub.\n\nOutside contributions are very welcome. See\n[how you can contribute](https://github.com/Smithsonian/SuperNOVAS/CONTRIBUTING.md) to make SuperNOVAS even better.\n\n### Related links\n\n - [NOVAS](https://aa.usno.navy.mil/software/novas_info) home page at the US Naval Observatory.\n - [CALCEPH C library](https://calceph.imcce.fr/docs/4.0.0/html/c/index.html) for integrating Solar-system ephemerides\n   from JPL and/or in INPOP 2.0/3.0 format.\n - [NAIF SPICE toolkit](https://naif.jpl.nasa.gov/naif/toolkit.html) for integrating Solar-system ephemerides\n   from JPL.\n - [Smithsonian/cspice-sharedlib](https://github.com/Smithsonian/cspice-sharedlib) for building CSPICE as a shared\n   library for dynamic linking.\n - [IAU Minor Planet Center](https://www.minorplanetcenter.net/iau/mpc.html) provides up-to-date orbital elements\n   for asteroids, comets, and near-Earth objects (NEOs), including newly discovered objects.\n\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"fixed-issues\"\u003e\u003c/a\u003e\n## Fixed NOVAS C 3.1 issues\n\nThe SuperNOVAS library fixes a number of outstanding issues with NOVAS C 3.1. Here is a list of issues and fixes \nprovided by SuperNOVAS over the upstream NOVAS C 3.1 code:\n\n - Fixes the [sidereal_time bug](https://aa.usno.navy.mil/software/novas_faq), whereby the `sidereal_time()` function \n   had an incorrect unit cast. This was a documented issue of NOVAS C 3.1.\n   \n - Fixes the [ephem_close bug](https://aa.usno.navy.mil/software/novas_faq), whereby `ephem_close()` in \n   `eph_manager.c` did not reset the `EPHFILE` pointer to NULL. This was a documented issue of NOVAS C 3.1.\n     \n - Fixes antedating velocities and distances for light travel time in `ephemeris()`. When getting positions and \n   velocities for Solar-system sources, it is important to use the values from the time light originated from the \n   observed body rather than at the time that light arrives to the observer. This correction was done properly for \n   positions, but not for velocities or distances, resulting in incorrect observed radial velocities or apparent \n   distances being reported for spectroscopic observations or for angular-physical size conversions. \n   \n - Fixes bug in `ira_equinox()` which may return the result for the wrong type of equinox (mean vs. true) if the \n   `equinox` argument was changing from 1 to 0, and back to 1 again with the date being held the same. This affected \n   routines downstream also, such as `sidereal_time()`.\n   \n - Fixes accuracy switching bug in `cio_basis()`, `cio_location()`, `ecl2equ()`, `equ2ecl_vec()`, `ecl2equ_vec()`, \n   `geo_posvel()`, `place()`, and `sidereal_time()`. All these functions returned a cached value for the other \n   accuracy if the other input parameters are the same as a prior call, except the accuracy. \n   \n - Fixes multiple bugs related to using cached values in `cio_basis()` with alternating CIO location reference \n   systems. This affected many CIRS-based position calculations downstream.\n   \n - Fixes bug in `equ2ecl_vec()` and `ecl2equ_vec()` whereby a query with `coord_sys = 2` (GCRS) has overwritten the\n   cached mean obliquity value for `coord_sys = 0` (mean equinox of date). As a result, a subsequent call with\n   `coord_sys = 0` and the same date as before would return the results in GCRS coordinates instead of the requested \n   mean equinox of date coordinates.\n \n - Some remainder calculations in NOVAS C 3.1 used the result from `fmod()` unchecked, which resulted in angles outside\n   of the expected [0:2\u0026pi;] range and was also the reason why `cal_date()` did not work for negative JD values.\n \n - Fixes `aberration()` returning NaN vectors if the `ve` argument is 0. It now returns the unmodified input vector \n   appropriately instead.\n   \n - Fixes unpopulated `az` output value in `equ2hor()` at zenith. While any azimuth is acceptable really, it results in \n   unpredictable behavior. Hence, we set `az` to 0.0 for zenith to be consistent.\n   \n - Fixes potential string overflows and eliminates associated compiler warnings.\n \n - [__v1.1__] Fixes division by zero bug in `d_light()` if the first position argument is the ephemeris reference\n   position (e.g. the Sun for `solsys3.c`). The bug affects for example `grav_def()`, where it effectively results in\n   the gravitational deflection due to the Sun being skipped.\n    \n - [__v1.1__] The NOVAS C 3.1 implementation of `rad_vel()` has a number of issues that produce inaccurate results. \n   The errors are typically at or below the tens of m/s level for objects not moving at relativistic speeds.\n   \n - [__v1.3__] `transform_cat()` to update parallax to the recalculated value when precessing or changing epochs.\n \n   \n-----------------------------------------------------------------------------\n\n\u003ca name=\"compatibility\"\u003e\u003c/a\u003e\n## Compatibility with NOVAS C 3.1\n\nSuperNOVAS strives to maintain API compatibility with the upstream NOVAS C 3.1 library, but not binary (ABI) \ncompatibility. \n\nIf you have code that was written for NOVAS C 3.1, it should work with SuperNOVAS as is, without modifications. Simply \n(re)build your application against SuperNOVAS, and you are good to go. \n\nThe lack of binary compatibility just means that you cannot drop-in replace the libraries (e.g. the static \n`libnovas.a`, or the shared `libnovas.so`), from NOVAS C 3.1 with those from SuperNOVAS. Instead, you will have to \nbuild (compile) your application referencing the SuperNOVAS headers and/or libraries from the start.\n\nThis is because some function signatures have changed, e.g. to use an `enum` argument instead of the nondescript \n`short int` argument of NOVAS C 3.1, or because we added a return value to a function that was declared `void` in \nNOVAS C 3.1. We also changed the `object` structure to contain a `long` ID number instead of `short` to accommodate \nJPL NAIF codes, for which 16-bit storage is insufficient. \n\n\n-----------------------------------------------------------------------------\n\n\n\u003ca name=\"installation\"\u003e\u003c/a\u003e\n## Building and installation\n\n\nThe SuperNOVAS distribution contains a GNU `Makefile`, which is suitable for compiling the library (as well as local \ndocumentation, and tests, etc.) on POSIX systems such as Linux, BSD, MacOS X, or Cygwin or WSL. (At this point we do \nnot provide a similar native build setup for Windows, but speak up if you would like to add it yourself!)\n\nBefore compiling the library take a look a `config.mk` and edit it as necessary for your needs, or else define\nthe necessary variables in the shell prior to invoking `make`. For example:\n\n - Choose which planet calculator function routines are built into the library (for example to provide \n   `earth_sun_calc()` set `BUILTIN_SOLSYS3 = 1`  and/or for `planet_ephem_provider()` set `BUILTIN_SOLSYS_EPHEM = 1`. \n   You can then specify these functions (or others) as your planet calculator of choice for `ephemeris()` in your \n   application dynamically via `set_planet_provider()`.\n   \n - [CALCEPH](https://www.imcce.fr/recherche/equipes/asd/calceph/) C library integration is automatic if `ldconfig` can \n   locate the `libcalceph` shared library. You can also control CALCEPH integration manually, e.g. by setting \n   `CALCEPH_SUPPORT = 1` in `config.mk` or in the shell prior to the build. CALCEPH integration will require an \n   accessible installation of the CALCEPH development files (C headers and unversioned static or shared libraries \n   depending on the needs of the build).\n   \n - [NAIF CSPICE Toolkit](https://naif.jpl.nasa.gov/naif/toolkit.html) integration automatic, if `ldconfig` can locate \n   the `libcspice` shared library. You can also control CSPICE integration manually, e.g. by setting \n   `CSPICE_SUPPORT = 1` in `config.mk` or in the shell prior to the build. CSPICE integration will require an \n   accessible installation of the CSPICE development files (C headers, under a `cspice/` sub-folder in the header \n   search path, and unversioned static or shared libraries depending on the needs of the build). You might want to \n   check out the [Smithsonian/cspice-sharedlib](https://github.com/Smithsonian/cspice-sharedlib) repository for \n   building CSPICE as a shared library.\n   \n - Choose which stock planetary calculator module (if any) should provide a default `solarsystem()` implementation for \n   `ephemeris()` calls by setting `DEFAULT_SOLSYS` to 1 -- 3 for `solsys1.c` trough `solsys3.c`, respectively. If you \n   want to link your own `solarsystem()` implementation(s) against the library, you should not set `DEFAULT_SOLSYS` \n   (i.e. delete or comment out the corresponding line or else set `DEFAULT_SOLSYS` to 0).\n   \n - You may also specify the source file that will provide a `readeph()` implementation, by setting `DEFAULT_READEPH`. \n   (The default setting uses the dummy `readeph0.c` which simply returns an error). Note, that a `readeph()` \n   implementation is a relic of NOVAS C and not generally needed. You can provide a superior ephemeris reader \n   implementation at runtime via the `set_ephem_provider()` call or equivalent (e.g. `novas_use_calceph()` or \n   `novas_use_cspice()`, if they are available).\n\n - If you want to use a CIO locator file for `cio_location()`, you can specify the path to the CIO locator file (e.g. \n   `/usr/local/share/supernovas/CIO_RA.TXT`) on your system e.g. by setting the `CIO_LOCATOR_FILE` shell variable \n   prior to calling `make`. (The CIO locator file is not necessary for the functioning of the library, unless you \n   specifically require CIO positions relative to GCRS.)\n   \n - If your compiler does not support the C11 standard and it is not GCC \u0026gt;=3.3, but provides some non-standard\n   support for declaring thread-local variables, you may want to pass the keyword to use to declare variables as\n   thread local via `-DTHREAD_LOCAL=...` added to `CFLAGS`. (Don't forget to enclose the string value in escaped\n   quotes in `config.mk`, or unescaped if defining the `THREAD_LOCAL` shell variable prior to invoking `make`.)\n\n\nAdditionally, you may set number of environment variables to futher customize the build, such as:\n\n - `CC`: The C compiler to use (default: `gcc`).\n\n - `CPPFLAGS`: C preprocessor flags, such as externally defined compiler constants.\n \n - `CFLAGS`: Flags to pass onto the C compiler (default: `-g -Os -Wall`). Note, `-Iinclude` will be added \n   automatically.\n   \n - `CSTANDARD`: Optionally, specify the C standard to compile for, e.g. `c99` to compile for the C99 standard. If\n   defined then `-std=$(CSTANDARD)` is added to `CFLAGS` automatically.\n   \n - `WEXTRA`: If set to 1, `-Wextra` is added to `CFLAGS` automatically.\n   \n - `FORTIFY`: If set it will set the `_FORTIFY_SOURCE` macro to the specified value (`gcc` supports values 1 \n   through 3). It affords varying levels of extra compile time / runtime checks.\n   \n - `LDFLAGS`: Extra linker flags (default is _not set_). Note, `-lm -lxchange` will be added automatically.\n\n - `CHECKEXTRA`: Extra options to pass to `cppcheck` for the `make check` target\n \n - `DOXYGEN`: Specify the `doxygen` executable to use for generating documentation. If not set (default), `make` will\n   use `doxygen` in your `PATH` (if any). You can also set it to `none` to disable document generation and the\n   checking for a usable `doxygen` version entirely.\n\nNow you are ready to build the library:\n\n```bash\n  $ make\n```\n\nwill compile the shared (e.g. `lib/libsupernovas.so`) libraries, produce a CIO locator data file (e.g. \n`tools/data/cio_ra.bin`), and compile the API documentation (into `apidoc/`) using `doxygen` (if available). \nAlternatively, you can build select components of the above with the `make` targets `shared`, and `local-dox` \nrespectively. And, if unsure, you can always call `make help` to see what build targets are available.\n\nAfter building the library you can install the above components to the desired locations on your system. For a \nsystem-wide install you may simply run:\n\n```bash\n  $ sudo make install\n```\n\nOr, to install in some other locations, you may set a prefix and/or `DESTDIR`. For example, to install under `/opt` \ninstead, you can:\n\n```bash\n  $ sudo make prefix=\"/opt\" install\n```\n\nOr, to stage the installation (to `/usr`) under a 'build root':\n\n```bash\n  $ make DESTDIR=\"/tmp/stage\" install\n```\n\n\n-----------------------------------------------------------------------------\n\n\n\u003ca name=\"integration\"\u003e\u003c/a\u003e\n## Building your application with SuperNOVAS\n\nProvided you have installed the SuperNOVAS headers into a standard location, you can build your application against it \neasily. For example, to build `myastroapp.c` against SuperNOVAS, you might have a `Makefile` with contents like:\n\n```make\n  myastroapp: myastroapp.c \n  \t$(CC) -o $@ $(CFLAGS) $^ -lm -lsupernovas\n```\n\nIf you have a legacy NOVAS C 3.1 application, it is possible that the compilation will give you errors due to missing \nincludes for `stdio.h`, `stdlib.h`, `ctype.h` or `string.h`. This is because these headers were explicitly included by \n`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 \nproblem for you can 'fix' it in one of two ways: (1) Add the missing `#include` directives to your application source \nexplicitly, or if that's not an option for you, then (2) set the `-DCOMPAT=1` compiler flag when compiling your \napplication:\n\n```make\n  myastroapp: myastroapp.c \n  \t$(CC) -o $@ $(CFLAGS) -DCOMPAT=1 $^ -lm -lsupernovas\n```\n\nIf your application uses optional planet or ephemeris calculator modules, you may need to specify the appropriate \noptional shared library also:\n\n```make\n  myastroapp: myastroapp.c \n  \t$(CC) -o $@ $(CFLAGS) $^ -lm -lsupernovas -lsolsys-calceph\n```\n\n### Legacy linking `solarsystem()` and `readeph()` modules\n\nThe NOVAS C way to handle planet or other ephemeris functions was to link particular modules to provide the\n`solarsystem()` / `solarsystem_hp()` and `readeph()` functions. This approach is discouraged in SuperNOVAS, since we \nnow allow selecting different implementations at runtime, but the old way is supported for legacy applications,\nnevertheless.\n\nTo use your own existing default `solarsystem()` implementation in this way, you must build the library with \n`DEFAULT_SOLSYS` unset (or else set to 0) in `config.mk` (see section above), and your applications `Makefile` may \ncontain something like:\n\n```make\n  myastroapp: myastroapp.c my_solsys.c \n  \t$(CC) -o $@ $(CFLAGS) $^ -lm -lsupernovas\n```\n\nThe same principle applies to using your specific `readeph()` implementation (only with `DEFAULT_READEPH` being unset \nin `config.mk`).\n\n### Legacy modules: a better way...\n\nNote, a better way to recycle your old planet and ephemeris calculator modules may be to rename `solarsystem()` / \n`solarsystem_hp()` functions therein to e.g. `my_planet_calculator()` / `my_planet_calculator_hp()` and then in your \napplication can specify these functions as the provider at runtime:\n\n```c\n  set_planet_calculator(my_planet_calculator);\n  set_planet_calculator(my_planet_calculator_hp);\n```\n\nFor `readeph()` implementations, it is recommended that you change both the name and the footprint to e.g.:\n\n```c\n  int my_ephem_provider(const char *name, long id, double jd_tdb_high, double jd_tdb_low, \n                        enum novas_origin *origin, double *pos, double *vel);\n```\n\nand then then apply it in your application as:\n\n```c\n  set_ephem_provider(my_ephem_provider);\n```\n\nWhile it requires some minimal changes to the old code, the advantage of this preferred approach is (a) that you do \nnot need to re-build the library with the `DEFAULT_SOLSYS` and `DEFAULT_READEPH` options disabled, and (b) you can \nswitch between different planet and ephemeris calculator functions at will, during runtime.\n\n\n-----------------------------------------------------------------------------\n\n\n\u003ca name=\"examples\"\u003e\u003c/a\u003e\n## Example usage\n\n - [Note on alternative methodologies](#methodologies)\n - [Calculating positions for a sidereal source](#sidereal-example)\n - [Calculating positions for a Solar-system source](#solsys-example)\n\n\nSuperNOVAS __v1.1__ has introduced a new, more intuitive, more elegant, and more efficient approach for calculating\nastrometric positions of celestial objects. The guide below is geared towards this new method. However, the original\nNOVAS C approach remains viable also (albeit often less efficient). You may find an equivalent example usage \nshowcasing the original NOVAS method in [LEGACY.md](LEGACY.html).\n\n\u003ca name=\"methodologies\"\u003e\u003c/a\u003e\n### Note on alternative methodologies\n\nThe IAU 2000 and 2006 resolutions have completely overhauled the system of astronomical coordinate transformations\nto enable higher precision astrometry. (Super)NOVAS supports coordinate calculations both in the old (pre IAU 2000) \nways, and in the new IAU standard method. Here is an overview of how the old and new methods define some of the\nterms differently:\n\n\n | Concept                    | Old standard                  | New IAU standard                                  |\n | -------------------------- | ----------------------------- | ------------------------------------------------- |\n | Catalog coordinate system  | FK4, FK5, HIP...              | International Celestial Reference System (ICRS)   |\n | Dynamical system           | True of Date (TOD)            | Celestial Intermediate Reference System (CIRS)    |\n | Dynamical R.A. origin      | equinox of date               | Celestial Intermediate Origin (CIO)               |\n | Precession, nutation, bias | separate, no tidal terms      | IAU 2000 precession/nutation model                |\n | Celestial Pole offsets     | d\u0026psi;, d\u0026epsilon;            | _dx_, _dy_                                        |\n | Earth rotation measure     | Greenwich Sidereal Time (GST) | Earth Rotation Angle (ERA)                        |\n | Fixed Earth System         | WGS84                         | International Terrestrial Reference System (ITRS) |\n \n \nSee the various enums and constants defined in `novas.h`, as well as the descriptions on the various NOVAS routines\non how they are appropriate for the old and new methodologies respectively. Figure 1 also shows the relation of the\nvarious old and new coordinate systems and the (Super)NOVAS functions for converting position / velocity vectors among \nthem.\n\nIn NOVAS, the barycentric BCRS and the geocentric GCRS systems are effectively synonymous to ICRS, since the origin \nfor positions and for velocities, in any reference system, is determined by the `observer` location, while aberration\nand gravitational deflection is included for apparent places only (as seen from the observer location). \n\nOlder catalogs, such as B1950 (FK4) or B1900 are just special cases of MOD (mean-of-date) coordinates for the B1950\nand B1900 epochs, respectively.\n\nTIRS (Terrestrial Intermediate Reference System) and its older equivalent PEF (Pseudo-Earth-Fixed) are not explicitly \nreferenced in SuperNOVAS. But they can be thought of as a special case of ITRS (International Terrestrial Reference \nSystem) with zero polar offsets (_dx_, _dy_).\n\nWGS84 has been superseded by ITRS for higher accuracy definitions of Earth-based locations. WGS84 matches ITRS to the \n10m level globally, but it does not account for continental drifts and crustal motion. In (Super)NOVAS all Earth-fixed \ncoordinates are effectively assumed as ITRS, whether explicitly or implicitly. There is nothing WGS84-specific in the \nimplementation.\n\n| ![SuperNOVAS coordinate systems and conversions](resources/SuperNOVAS-systems.png) |\n|:--:| \n| __Figure 1.__ SuperNOVAS Coordinate Systems and Conversions. Functions indicated in bold face are available in NOVAS C also. All other functions are available in SuperNOVAS only. Dotted arrows indicate possible loss of precision due to the inadequacy of the old precession-nutation (Lieske et al. 1977) model. |\n\n\n\u003ca name=\"sidereal-example\"\u003e\u003c/a\u003e\n### Calculating positions for a sidereal source\n\nA sidereal source may be anything beyond the solar-system with 'fixed' catalog coordinates. It may be a star, or a \ngalactic molecular cloud, or a distant quasar. \n\n - [Specify the object of interest](#specify-object)\n - [Specify the observer location](#specify-observer)\n - [Specify the time of observation](#specify-time)\n - [Set up the observing frame](#observing-frame)\n - [Calculate an apparent place on sky](#apparent-place)\n - [Calculate azimuth and elevation angles at the observing location](#horizontal-place)\n - [Calculate rise, set, and transit times](#rise-set-transit)\n\n\u003ca name=\"specify-object\"\u003e\u003c/a\u003e\n#### Specify the object of interest\n\nFirst, you must provide the coordinates (which may include proper motion and parallax). Let's assume we pick a star \nfor which we have B1950 (i.e. FK4) coordinates:\n\n```c\n cat_entry star; // Structure to contain information on sidereal source \n\n // Let's assume we have B1950 (FK4) coordinates...\n // 16h26m20.1918s, -26d19m23.138s (B1950), proper motion -12.11, -23.30 mas/year, \n // parallax 5.89 mas, radial velocity -3.4 km/s.\n make_cat_entry(\"Antares\", \"FK4\", 1, 16.43894213, -26.323094, -12.11, -23.30, 5.89, -3.4, \u0026star);\n```\n\nOr, if you have coordinates as strings in decimal or HMS / DMS format, you might use `novas_str_hours()` and/or \n`novas_str_degrees()` to convert them to hours/degrees for `make_cat_entry()`, with a fair bit of flexibility on the \nseparators used between the components and more, e.g.:\n\n```c\n make_cat_entry(\"Antares\", \"FK4\", 1, \n   novas_str_hours(\"16h 26m 20.1918s\"),   // e.g. using h,m,s and spaces as separators \n   novas_str_degrees(\"-26:19:23.138\"),    // e.g. using colons to separate components\n   -12.11, -23.30, 5.89, -3.4, \u0026star);\n```\n\nAnd, if you have LSR-based radial velocities instead of Solar-system Barycentric radial velocities, you may convert \nthese to SSB-based velocities for use in `make_cat_entry()` with `novas_lsr_to_ssb_vel()`.\n\nNext, we create a generic celestial `object` from the catalog source. (The `object` structure handles various \nSolar-system sources also, as you'll see further below). Whereas the catalog source may have been defined in any \nepoch / catalog system, the `object` structure shall contain ICRS coordinates always:\n\n```c\n object source;   // Common structure for a sidereal or an Solar-system source\n  \n // Use the B1950 coodinates for generic source data structure in ICRS...\n make_cat_object_sys(\u0026star, \"B1950\", \u0026source);\n```\n\nAlternatively, for high-_z_ sources you might use `make_redshifted_cat_entry()` or `make_redshifted_object_sys()` \ne.g.:\n\n```c\n object quasar;\n  \n // 12h29m6.6997s +2d3m8.598s (ICRS) z=0.158339\n make_redshifted_object_sys(\"3c273\", 12.4851944, 2.0523883, \"ICRS\", 0.158339, \u0026quasar);\n```\n\n\u003ca name=\"specify-observer\"\u003e\u003c/a\u003e\n#### Specify the observer location\n\nNext, we define the location where we observe from. Here we can (but don't have to) specify local weather parameters\n(temperature and pressure) also for refraction correction later (in this example, we'll skip the weather):\n\n```c\n observer obs;    // Structure to contain observer location \n\n // Specify the location we are observing from\n // 50.7374 deg N, 7.0982 deg E, 60m elevation\n make_observer_on_surface(50.7374, 7.0982, 60.0, 0.0, 0.0, \u0026obs);\n```\n\nAgain you might use `novas_str_degrees()` for typical string representations of the longitude and latitude coordinates \nhere, such as:\n\n```c\n make_observer_on_surface(\n   novas_str_degrees(\"50.7374N\"),\n   novas_str_degrees(\"7.0982 deg E\"),\n   60.0, 0.0, 0.0, \u0026obs);\n```\n\nAlternatively, you can also specify airborne observers, or observers in Earth orbit, in heliocentric orbit, at the \ngeocenter, or at the Solar-system barycenter.\n\n\n\u003ca name=\"specify-time\"\u003e\u003c/a\u003e\n#### Specify the time of observation\n\nNext, we set the time of observation. For a ground-based observer, you will need to provide SuperNOVAS with the\nUT1 - UTC time difference (a.k.a. DUT1), and the current leap seconds. Let's assume 37 leap seconds, and DUT1 = 0.042,\nthen we can set the time of observation, for example, using the current UNIX time:\n\n```c\n novas_timespec obs_time;       // Structure that will define astrometric time\n struct timespec unix_time;     // Standard precision UNIX time structure\n\n // Get the current system time, with up to nanosecond resolution...\n clock_gettime(CLOCK_REALTIME, \u0026unix_time);\n \n // Set the time of observation to the precise UTC-based UNIX time\n novas_set_unix_time(unix_time.tv_sec, unix_time.tv_nsec, 37, 0.042, \u0026obs_time);\n```\n\nAlternatively, you may set the time as a Julian date in the time measure of choice (UTC, UT1, TT, TDB, GPS, TAI, TCG, \nor TCB):\n\n```c\n double jd_tai = ...     // TAI-based Julian Date \n\n novas_set_time(NOVAS_TAI, jd_tai, leap_seconds, dut1, \u0026obs_time);\n```\n\nor, for the best precision we may do the same with an integer / fractional split:\n\n```c\n long ijd_tai = ...     // Integer part of the TAI-based Julian Date\n double fjd_tai = ...   // Fractional part of the TAI-based Julian Date \n  \n novas_set_split_time(NOVAS_TAI, ijd_tai, fjd_tai, leap_seconds, dut1, \u0026obs_time);\n```\n\nor, you can use a string date, such as an ISO timestamp:\n\n```c\n novas_set_time(NOVAS_UTC, novas_date(\"2025-01-26T22:05:14.234+0200\"), 37, 0.042, \u0026obs_time);\n```\n\n\u003ca name=\"observing-frame\"\u003e\u003c/a\u003e\n#### Set up the observing frame\n\nNext, we set up an observing frame, which is defined for a unique combination of the observer location and the time of\nobservation:\n\n```c\n novas_frame obs_frame;  // Structure that will define the observing frame\n double dx = ...         // [mas] Earth polar offset x, e.g. from IERS Bulletin A.\n double dy = ...         // [mas] Earth polar offset y, from same source as above.\n  \n // Initialize the observing frame with the given observing parameters\n novas_make_frame(NOVAS_REDUCED_ACCURACY, \u0026obs, \u0026obs_time, dx, dy, \u0026obs_frame);\n```\n\nHere `dx` and `dy` are small diurnal (sub-arcsec level) corrections to Earth orientation, which are published\nin the [IERS Bulletins](https://www.iers.org/IERS/EN/Publications/Bulletins/bulletins.html). They are needed when \nconverting positions from the celestial CIRS frame to the Earth-fixed ITRS frame. You may ignore these and set zeroes \nif sub-arcsecond precision is not required.\n\nThe advantage of using the observing frame, is that it enables very fast position calculations for multiple objects\nin that frame (see the [benchmarks](#benchmarks)). So, if you need to calculate positions for thousands of sources for \nthe same observer and time, it will be significantly faster than using the low-level NOVAS C routines instead. You can \ncreate derivative frames for different observer locations, if need be, via `novas_change_observer()`.\n\nNote that without a proper ephemeris provider for the major planets, you are invariably restricted to working with \n`NOVAS_REDUCED_ACCURACY` frames, providing milliarcsecond precision only. To create `NOVAS_FULL_ACCURACY` frames, with \nsub-\u0026mu;as precision, you will you will need a high-precision ephemeris provider for the major planets (beyond the \nlow-precision Earth and Sun calculator included by default), both for precise Earth-based observer locations and to \naccount for gravitational bending around the Sun and massive planets. Without them, \u0026mu;as accuracy cannot be ensured, \nin general. Therefore, attempting to construct high-accuracy frames without an appropriate high-precision ephemeris \nprovider will result in an error from the requisite `ephemeris()` calls. \n\n\u003ca name=\"apparent-place\"\u003e\u003c/a\u003e\n#### Calculate an apparent place on sky\n\nNow we can calculate the apparent R.A. and declination for our source, which includes proper motion (for sidereal\nsources) or light-time correction (for Solar-system bodies), and also aberration corrections for the moving observer \nand gravitational deflection around the major Solar System bodies. You can calculate an apparent location in the \ncoordinate system of choice (ICRS/GCRS, CIRS, J2000, MOD, or TOD):\n\n```c\n sky_pos apparent;    // Structure containing the precise observed position\n  \n novas_sky_pos(\u0026source, \u0026obs_frame, NOVAS_CIRS, \u0026apparent);\n```\n\nApart from providing precise apparent R.A. and declination coordinates, the `sky_pos` structure also provides the \n_x,y,z_ unit vector pointing in the observed direction of the source (in the designated coordinate system). We also \nget radial velocity (for spectroscopy), and apparent distance for Solar-system bodies (e.g. for apparent-to-physical \nsize conversion).\n\nNote, that if you want geometric positions (and/or velocities) instead, without aberration and gravitational \ndeflection, you might use `novas_geom_posvel()` instead. And regardless, which function you use you can always easily \nand efficiently change the coordinate system in which your results are expressed by creating an appropriate transform \nvia `novas_make_transform()` and then using `novas_transform_vector()` or `novas_transform_skypos()`.\n\n\u003ca name=\"horizontal-place\"\u003e\u003c/a\u003e\n#### Calculate azimuth and elevation angles at the observing location\n\nIf your ultimate goal is to calculate the azimuth and elevation angles of the source at the specified observing \nlocation, you can proceed from the `sky_pos` data you obtained above (in whichever coordinate system!) as:\n\n```c\n double az, el;   // [deg] local azimuth and elevation angles to populate\n  \n // Convert the apparent position in CIRS on sky to horizontal coordinates\n novas_app_to_hor(\u0026obs_frame, NOVAS_CIRS, apparent.ra, apparent.dec, novas_standard_refraction, \n   \u0026az, \u0026el);\n```\n\nAbove we converted the apparent coordinates, that were calculated in CIRS, to refracted azimuth and elevation \ncoordinates at the observing location, using the `novas_standard_refraction()` function to provide a suitable \nrefraction correction. We could have used `novas_optical_refraction()` instead to use the weather data embedded in the \nframe's `observer` structure, or some user-defined refraction model, or else `NULL` to calculate unrefracted elevation \nangles.\n\n\u003ca name=\"rise-set-transit\"\u003e\u003c/a\u003e\n#### Calculate rise, set, and transit times\n\nYou may be interested to know when sources rise above or set below some specific elevation angle, or at what time they \nappear to transit at the observer location. SuperNOVAS has routines to help you with that too. Given that rise, set, \nor transit times are dependent on the day of observation, and observer location, they are effectively tied to an \nobserver frame.\n\n```c\n novas_frame frame = ...;  // Earth-based observer location and lower-bound time of interest.\n object source = ...;      // Source of interest\n\n // UTC-based Julian day *after* observer frame, when source rises above 30 degrees of elevation \n // next, given a standard optical refraction model.\n double jd_rise = novas_rises_above(30.0, \u0026source, \u0026frame, novas_standard_refraction);\n\n // UTC-based Julian day *after* observer frame, of next source transit\n double jd_transit = novas_transit_time(\u0026source, \u0026frame);\n \n // UTC-based Julian day *after* observer frame, when source sets below 30 degrees of elevation \n // next, not accounting for refraction.\n double jd_rise = novas_sets_below(30.0, \u0026source, \u0026frame, NULL);\n```\n\nNote, that in the current implementation these calls are not well-suited sources that are at or within the \ngeostationary orbit, such as such as low-Earth orbit satellites (LEOs), geostationary satellites (which never really \nrise, set, or transit), or some Near Earth Objects (NEOs), which will rise set multiple times per day. For the latter, \nthe above calls may still return a valid time, only without the guarantee that it is the time of the first such event \nafter the specified frame instant. A future implementation may address near-Earth orbits better, so stay tuned for \nupdates.\n\n\n\u003ca name=\"solsys-example\"\u003e\u003c/a\u003e\n### Calculating positions for a Solar-system source\n\nSolar-system sources work similarly to the above with a few important differences.\n\nFirst, you will have to provide one or more functions to obtain the barycentric ICRS positions for your Solar-system \nsource(s) of interest for the specific Barycentric Dynamical Time (TDB) of observation. See section on integrating \n[External Solar-system ephemeris data or services](#solarsystem) with SuperNOVAS. You can specify the functions that \nwill handle the respective ephemeris data at runtime before making the NOVAS calls that need them, e.g.:\n\n```c\n // Set the function to use for regular precision planet position calculations\n set_planet_provider(my_planet_function);\n  \n // Set the function for high-precision planet position calculations\n set_planet_provider_hp(my_very_precise_planet_function);\n  \n // Set the function to use for calculating all other solar-system bodies\n set_ephem_provider(my_ephemeris_provider_function);\n```\n\nOr, if you have the CALCEPH library installed on your system, and you have built SuperNOVAS with `CALCEPH_SUPPORT = 1`, \nthen you might call:\n\n```c\n #include \u003cnovas-calceph.h\u003e\n  \n // Use calceph to open se set of ephemeris files...\n t_calcephbin *ephem_data = calceph_open_array(...);\n  \n // Use CALCEPH with the specified data for all Solar-system objects.\n novas_use_calceph(ephem_data);\n```\n\nNext, instead of `make_cat_object()` you define your source as an `object` with an name or ID number that is used by \nthe ephemeris service you provided. For major planets you might want to use `make_planet()`, if they use a \n`novas_planet_provider` function to access ephemeris data with their NOVAS IDs, or else `make_ephem_object()` for \nmore generic ephemeris handling via a user-provided `novas_ephem_provider`. E.g.:\n\n```c\n object mars, ceres; // Hold data on solar-system bodies.\n  \n // Mars will be handled by the planet provider function\n make_planet(NOVAS_MARS, \u0026mars);\n  \n // Ceres will be handled by the generic ephemeris provider function, which let's say \n // uses the NAIF ID of 2000001\n make_ephem_object(\"Ceres\", 2000001, \u0026ceres);\n```\n\nAs of version 1.2 you can also define solar system sources with orbital elements (such as the most up-to-date ones \nprovided by the [Minor Planet Center](https://minorplanetcenter.net/data) for asteroids, comets, etc.):\n\n```c\n object NEA;\t\t// e.g. a Near-Earth Asteroid\n  \n // Fill in the orbital parameters (pay attention to units!)\n novas_orbital orbit = NOVAS_ORBIT_INIT;\n orbit.a = ...;\t        // Major axis in AU...\n ...                    // ... and the rest of the orbital elements\n  \n // Create an object for that orbit\n make_orbital_object(\"NEAxxx\", -1, \u0026orbit, \u0026NEA);\n```\n\nNote, that even with orbital elements, you will, in general, require a planet calculator, to provide precise\npositions for the Sun or planet, around which the orbit is defined.\n\nOther than that, it's the same spiel as before, e.g.:\n\n```c\n int status = novas_sky_pos(\u0026mars, \u0026obs_frame, NOVAS_TOD, \u0026apparent);\n if(status) {\n   // Oops, something went wrong...\n   ...\n }\n```\n\n------------------------------------------------------------------------------\n\n\u003ca name=\"tips\"\u003e\u003c/a\u003e\n## Tips and tricks\n\n - [Reduced accuracy shortcuts](#accuracy-notes)\n - [Multi-threaded calculations](#multi-threading)\n - [Physical units](#physical-units)\n - [String times and angles](#string-times-and-angles)\n - [String dates](#string-dates)\n\n\n\u003ca name=\"accuracy-notes\"\u003e\u003c/a\u003e\n### Reduced accuracy shortcuts\n\nWhen one does not need positions at the microarcsecond level, some shortcuts can be made to the recipe above:\n\n - You can use `NOVAS_REDUCED_ACCURACY` instead of `NOVAS_FULL_ACCURACY` for the calculations. This typically has an \n   effect at or below the milliarcsecond level only, but may be much faster to calculate.\n - You might skip the pole offsets _dx_, _dy_. These are tenths of arcsec, typically.\n \n \n\u003ca name=\"multi-threading\"\u003e\u003c/a\u003e\n### Multi-threaded calculations\n\nSome of the calculations involved can be expensive from a computational perspective. For the most typical use case\nhowever, NOVAS (and SuperNOVAS) has a trick up its sleeve: it caches the last result of intensive calculations so they \nmay be re-used if the call is made with the same environmental parameters again (such as JD time and accuracy).\n \nA direct consequence of the caching of results in NOVAS is that calculations are generally not thread-safe as \nimplemented by the original NOVAS C 3.1 library. One thread may be in the process of returning cached values for one \nset of input parameters while, at the same time, another thread is saving cached values for a different set of \nparameters. Thus, when running calculations in more than one thread, the results returned may at times be incorrect, \nor more precisely they may not correspond to the requested input parameters.\n \nWhile you should never call the original NOVAS C library from multiple threads simultaneously, SuperNOVAS caches the \nresults in thread local variables (provided your compiler supports it), and is therefore generally safe to use in \nmulti-threaded applications. Just make sure that you:\n\n - use a compiler which supports the C11 language standard;\n - or, compile with GCC \u0026gt;= 3.3;\n - or else, set the appropriate non-standard keyword to use for declaring thread-local variables for your compiler in \n   `config.mk` or in your equivalent build setup.\n \n \n\u003ca name=\"physical-units\"\u003e\u003c/a\u003e\n### Physical units\n\nThe NOVAS API has been using conventional units (e.g. AU, km, day, deg, h) typically for its parameters and return \nvalues alike. Hence, SuperNOVAS follows the same conventions for its added functions and data structures also. \nHowever, when interfacing SuperNOVAS with other programs, libraries, or data files, it is often necessary to use\nquantities that are expressed in different units, such as SI or CGS. To facilitate such conversions, `novas.h` \nprovides a set of unit constants, which can be used for converting to/from SI units (and radians). For example, \n`novas.h` contains the following definitions:\n\n```c\n /// [s] The length of a synodic day, that is 24 hours exactly. @since 1.2\n #define NOVAS_DAY                 86400.0\n\n /// [rad] A degree expressed in radians. @since 1.2\n #define NOVAS_DEGREE              (M_PI / 180.0)\n\n /// [rad] An hour of angle expressed in radians. @since 1.2\n #define NOVAS_HOURANGLE           (M_PI / 12.0)\n```\n\nYou can use these, for example, to convert quantities expressed in conventional units for NOVAS to standard (SI) \nvalues, by multiplying NOVAS quantities with the corresponding unit definition. E.g.:\n\n```c\n // A difference in Julian Dates [day] in seconds.\n double delta_t = (tjd - tjd0) * NOVAS_DAY;\n  \n // R.A. [h] / declination [deg] converted radians (e.g. for trigonometric functions).\n double ra_rad = ra_h * NOVAS_HOURANGLE;\n double dec_rad = dec_d * NOVAS_DEGREE; \n```\n\nAnd vice-versa: to convert values expressed in standard (SI) units, you can divide by the appropriate constant to\n'cast' an SI value into the particular physical unit, e.g.:\n\n```c\n // Increment a Julian Date [day] with some time differential [s].\n double tjd = tjd0 + delta_t / NOVAS_DAY;\n  \n // convert R.A. / declination in radians to hours and degrees\n double ra_h = ra_rad / NOVAS_HOURANGLE;\n double dec_d = dec_rad / NOVAS_DEGREE;\n```\n\nFinally, you can combine them to convert between two different conventional units, e.g.:\n\n```c\n // Convert angle from [h] -\u003e [rad] -\u003e [deg]\n double lst_d = lst_h * HOURANGLE / DEGREE; \n  \n // Convert [AU/day] -\u003e [m/s] (SI) -\u003e [km/s]\n double v_kms = v_auday * (NOVAS_AU / NOVAS_DAY) / NOVAS_KMS\n```\n\n\u003ca name=\"string-times-and-angles\"\u003e\u003c/a\u003e\n### String times and angles\n\n__SuperNOVAS__ functions typically input and output times and angles as decimal values (hours and degrees, but also as \ndays and hour-angles), but that is not how these are represented in many cases. Time and right-ascention are often \ngiven as string values indicating hours, minutes, and seconds (e.g. \"11:32:31.595\", or \"11h 32m 31.595s\"). Similarly \nangles, are commonly represented as degrees, arc-minutes, and arc-seconds (e.g. \"+53 60 19.9\"). For that reason, \n__SuperNOVAS__ provides a set of functions to convert string values expressed in decimal or broken-down format to floating\npoint representation. E.g.,\n\n```c\n // Right ascention from string\n double ra_h = novas_str_hours(\"9 18 49.068\");\n\n // Declination from string\n double dec_d = novas_str_degrees(\"-53 10 07.33\");\n```\n\nThe conversions have a lot of flexibility. Components can be separated by spaces (as above), by colons, commas, or\nunderscores, by the letters 'h'/'d', 'm', and 's', by single (minutes) and double quotes (seconds), or any combination \nthereof. Decimal values may be followed by 'h' or 'd' unit markers. Additionally, angles can end with a compass \ndirection, such as 'N', 'E', 'S' or 'W'. So the above could also have been:\n\n```c\n double ra_h = novas_str_hours(\"9h_18:49.068\\\"\");\n double dec_d = novas_str_degrees(\"53d 10'_07.33S\");\n```\n\nor as decimals:\n\n```c\n double ra_h = novas_str_hours(\"9.31363\");\n double dec_d = novas_str_degrees(\"53.16870278d South\");\n```\n\n\n\u003ca name=\"string-dates\"\u003e\u003c/a\u003e\n### String dates\n\nDates are typically represented broken down into year, month, and day (e.g. \"2025-02-16\", or \"16.02.2025\", or \n\"2/16/2025\"), with or without a time marker, which itself may or may not include a time-zone specification. In \nastronomy, the most commonly used string representation of dates is with ISO 8601 timestamps. The following are\nall valid ISO date specifications:\n\n```\n 2025-02-16\t\t\t# Date only (0 UTC)\n 2025-02-16T19:35:21Z\t\t# UTC date/time\n 2025-02-16T19:35:21.832Z\t# UTC date/time with decimals\n 2025-02-16T14:35:21+0500\t# date in time-zone (e.g. EST)\n 2025-02-16T14:35:21.832+05:00  # alternative time-zone specification\n```\n\nSuperNOVAS provides functions to convert between ISO dates/times and their string representation for convenience. \nE.g.,\n\n```c\n novas_timespec time;           // Astronomical time specification\n char timestamp[40];            // A string to contain an ISO representation\n\n // Parse an ISO timestamp into a Julian day\n double jd = novas_date(\"2025-02-16T19:35:21Z\");\n if(isnan(jd)) {\n   // Ooops could not parse date.\n   ...\n }\n   \n // Use the parsed JD date in any timescale, e.g. in TAI\n // with the appropriate leap seconds and UT1-UTC time difference\n novas_set_time(NOVAS_TAI, jd, leap_seconds, dut1, \u0026time);\n  \n // Print an ISO timestamp, with millisecond precision, into the \n // designated string buffer.\n novas_iso_timestamp(\u0026time, timestamp, sizeof(timestamp));\n```\n\nYou can also parse a string date/time which includes a timescale specification also:\n\n```c\n enum novas_timescale scale;    // Timescale to be parsed\n novas_timespec time;           // Astronomical time specification\n char timestamp[40];            // A string to contain an ISO representation\n\n // Parse a timestamp into a Julian day and corresponding timescale\n double jd = novas_date_scale(\"2025-02-16T19:35:21 TAI\", \u0026scale);\n if(isnan(jd)) {\n   // Ooops could not parse date.\n   ...\n }\n  \n // Use the parsed JD date and timescale, rogether with the appropriate \n // leap seconds and UT1-UTC time difference\n novas_set_time(scale, jd, leap_seconds, dut1, \u0026time);\n```\n\nThe SuperNOVAS string date funtions always interpret dates in the astronomical calendar of date by default, that is in \nthe Gregorian calendar after the Gregorian calendar reform of 1582, or the Julian/Roman calendar for dates prior. As\nsuch, SuperNOVAS string timestamps are also always in the (conventional) astronomical calendar of date also. And while\n`novas_iso_timestamp()` will always express dates as UTC dates, you can use the somewhat more generic \n`novas_timestamp()` function instead to timestamp in other timescales, e.g.:\n\n```c\n // Print a TDB timestamp instead\n novas_timestamp(\u0026time, NOVAS_TDB, timestamp, sizeof(timestamp));\n```\n\nISO timestamps are best, but sometimes your input dates are represented in other formats. You can have additional \nflexibility for parsing dates using the `novas_parse_date_format()` and `novas_timescale_for_string()` functions. E.g.,\n\n```c\n char *pos = NULL;            // We'll keep track of the string parse position here\n enum novas_timescale scale;  // We'll parse the timescale here (if we can)\n\n // We'll parse the M/D/Y date up to the 'TAI' timescale specification...\n double jd = novas_parse_date_format(NOVAS_GREGORIAN_CALENDAR, NOVAS_MDY, \n   \"2/16/2025 20:08:49.082 TAI\", \u0026pos);\n  \n // We'll parse the 'TAI' timescale marker, after the date/time specification\n scale = novas_timescale_for_string(pos);\n if(scale \u003c 0) {\n   // Ooops, not a valid timescale marker. Perhaps assume UTC...\n   scale = NOVAS_UTC;\n }\n\n // Now set the time for the given calendar, date format, and timescale of the \n // string representation.\n novas_set_time(scale, jd, leap_seconds, dut1, \u0026time);\n``` \n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"precision\"\u003e\u003c/a\u003e\n## Notes on precision\n\nMany of the (Super)NOVAS functions take an accuracy argument, which determines to what precision quantities are \ncalculated. The argument can have one of two values, which correspond to typical precisions around:\n\n | `enum novas_accuracy` value  | Typical precision                |\n | ---------------------------- | -------------------------------- |\n | `NOVAS_REDUCED_ACCURACY`     | ~ 1 milli-arcsecond (mas)        |\n | `NOVAS_FULL_ACCURACY`        | below 1 micro-arcsecond (\u0026mu;as) |\n\nNote, that some functions will not support full accuracy calculations, unless you have provided a high-precision\nephemeris provider for the major planets (and any Solar-system bodies of interest), which does not come with \nSuperNOVAS out of the box. In the absense of a suitable high-precision ephemeris provider, some functions might return \nan error if called with `NOVAS_FULL_ACCURACY`.\n\n### Prerequisites to precise results\n\nThe SuperNOVAS library is in principle capable of calculating positions to sub-microarcsecond, and velocities to mm/s, \nprecision for all types of celestial sources. However, there are certain prerequisites and practical considerations \nbefore that level of accuracy is reached.\n\n    \n 1. __IAU 2000/2006 conventions__: High precision calculations will generally require that you use SuperNOVAS with the\n    new IAU standard quantities and methods. The old ways were simply not suited for precision much below the \n    milliarcsecond level.\n    \n 2. __Gravitational bending__: Calculations much below the milliarcsecond level will require to account for \n    gravitational bending around massive Solar-system bodies, and hence will require you to provide a high-precision \n    ephemeris provider for the major planets. Without it, there is no guarantee of achieving the desired \u0026mu;as-level \n    precision in general, especially when observing near massive planets (e.g. observing Jupiter's or Saturn's moons, \n    near conjuction with the host planet). Therefore some functions will return with an error, if used with \n    `NOVAS_FULL_ACCURACY` in the absense of a suitable high-precision planetary ephemeris provider.\n\n 3. __Solar-system sources__: Precise calculations for Solar-system sources requires precise ephemeris data for both\n    the target object as well as for Earth, and the Sun. For the highest precision calculations you also need \n    positions for all major planets to calculate gravitational deflection precisely. By default SuperNOVAS can only \n    provide approximate positions for the Earth and Sun (see `earth_sun_calc()` in `solsys3.c`), but certainly not at \n    the sub-microarcsecond level, and not for other Solar-system sources. You will need to provide a way to interface \n    SuperNOVAS with a suitable ephemeris source (such as the CSPICE toolkit from JPL or CALCEPH) if you want to use it \n    to obtain precise positions for Solar-system bodies. See the [section further below](#solarsystem) for more \n    information how you can do that.\n\n 4. __Earth's polar motion__: Calculating precise positions for any Earth-based observations requires precise \n    knowledge of Earth orientation at the time of observation. The pole is subject to predictable precession and \n    nutation, but also small irregular variations in the orientation of the rotational axis and the rotation period \n    (a.k.a polar wobble). The [IERS Bulletins](https://www.iers.org/IERS/EN/Publications/Bulletins/bulletins.html) \n    provide up-to-date measurements, historical data, and near-term projections for the polar offsets and the UT1-UTC \n    time difference and leap-seconds (UTC-TAI). In SuperNOVAS you can use `cel_pole()` and `get_ut1_to_tt()` functions \n    to apply / use the published values from these to improve the astrometric precision of Earth-orientation based \n    coordinate calculations. Without setting and using the actual polar offset values for the time of observation, \n    positions for Earth-based observations will be accurate at the tenths of arcsecond level only.\n   \n  5. __Refraction__: Ground based observations are also subject to atmospheric refraction. SuperNOVAS offers the \n    option to include approximate _optical_ refraction corrections either for a standard atmosphere or more precisely \n    using the weather parameters defined in the `on_surface` data structure that specifies the observer locations.\n    Note, that refraction at radio wavelengths is notably different from the included optical model, and a standard\n    radio refraction model is included as of version 1.1 also. In any case you may want to skip the refraction \n    corrections offered in this library, and instead implement your own as appropriate (or not at all).\n  \n\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"benchmarks\"\u003e\u003c/a\u003e\n## Representative benchmarks\n\nTo get an idea of the speed of __SuperNOVAS__, you can use the `make benchmark` on your machine. The table below \nsummarizes the single-threaded results obtained on an AMD Ryzen 5 PRO 6650U laptop. While this is clearly not the \nstate of the art for today's server class machines, it nevertheless gives you a ballpark idea for how a typical, not \nso new, run-of-the-mill PC might perform.\n\n| ![SuperNOVAS benchmarks](resources/SuperNOVAS-benchmark.png) |\n|:--:| \n| __Figure 2.__ SuperNOVAS apparent position calculation benchmarks, including proper motion, the IAU 2000 precession-nutation model, polar wobble, aberration, and gravitational deflection corrections, and precise spectroscopic redhift calculations. |\n\nThe tests calculate apparent positions (in CIRS) for a set of sidereal sources with random parameters, using either \nthe __SuperNOVAS__ `novas_sky_pos()` or the legacy NOVAS C `place()`, both in full accuracy and reduced accuracy \nmodes. The two methods are equivalent, and both include calculating a precise geometric position, as well as \naberration and gravitational deflection corrections from the observer's point of view.\n\n\n | Description                         | accuracy  | positions / sec |\n |-------------------------------------|:---------:|----------------:|\n | `novas_sky_pos()`, same frame       | reduced   |         2713879 |\n |                                     |   full    |         2708014 |\n | `place()`, same time, same observer | reduced   |          898609 |\n |                                     |   full    |          833300 |\n | `novas_sky_pos()`, individual       | reduced   |           69568 |\n |                                     |   full    |           30728 |\n | `place()`, individual               | reduced   |           61844 |\n |                                     |   full    |           28586 |\n\nFor reference, we also provide the reduced accuracy benchmarks from NOVAS C 3.1.\n\n | Description                         | accuracy  | positions / sec |\n |-------------------------------------|:---------:|----------------:|\n | NOVAS C 3.1 `place()`, same         | reduced   |          371164 |\n | NOVAS C 3.1 `place()`, individual   | reduced   |           55484 |\n \nFor comparison, a very similar benchmark with [astropy](https://www.astropy.org/) (v7.0.0 on Python v3.13.1) on the \nsame machine, provides ~70 positions / second both for a fixed frame and for individual frames. As such, \n__SuperNOVAS__ is a whopping ~40000 times faster than __astropy__ for calculations in the same observing frame, and \n~450 times faster than __astropy__ for individual frames. (The __astropy__ benchmarking code is also provided under \nthe `benchmark/` folder in the __SuperNOVAS__ GitHub repository).\n \n | Description                                     | positions / sec |\n |-------------------------------------------------|----------------:|\n | __astropy__ 7.0.0 (python 3.13.1), same frame   |              71 |\n | __astropy__ 7.0.0 (python 3.13.1), individual   |              70 |\n \n\nFigure 2 offers a visual comparison for the above mentioned performance measures.\n \nAs one may observe, the __SuperNOVAS__ `novas_sky_pos()` significantly outperforms the legacy `place()` function, when \nrepeatedly calculating positions for sources for the same instant of time and same observer location, providing up to \n2 orders of magnitude faster performance than for individual observing times and/or observer locations. Also, when \nobserving frames are reused, the performance is essentially independent of the accuracy. By contrast, calculations for \nindividual observing times or observer locations are generally around 2x faster if reduced accuracy is sufficient.\n\nThe above benchmarks are all for single-threaded performance. Since __SuperNOVAS__ is generally thread-safe, you can \nexpect that performance shall scale with the number of concurrent CPUs used. So, on a 16-core PC, with similar single \ncore performance, you could calculate up to 32 million precise positions per second, if you wanted to. To put that into \nperspective, you could calculate precise apparent positions for the entire Gaia dataset (1.7 billion stars) in under \none minute.\n\n\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"supernovas-features\"\u003e\u003c/a\u003e\n## SuperNOVAS specific features\n\n- [Newly functionality highlights](#added-functionality)\n- [Refinements to the NOVAS C API](#api-changes)\n\n\n\u003ca name=\"added-functionality\"\u003e\u003c/a\u003e\n### New functionality highlights\n\n Below is a non-exhaustive overview new features added by SuperNOVAS on top of the existing NOVAS C API. See \n `CHANGELOG.md` for more details.\n \n \n#### New in v1.0\n    \n - New runtime configuration:\n\n   * The planet position, and generic Solar-system position calculator functions can be set at runtime, and users\n     can provide their own custom implementations, e.g. to read ephemeris data, such as from a JPL `.bsp` file.\n \n   * If CIO locations vs GCRS are important to the user, the user may call `set_cio_locator_file()` at runtime to\n     specify the location of the binary CIO interpolation table (e.g. `CIO_RA.TXT` or `cio_ra.bin`) to use, even if \n     the library was compiled with the different default CIO locator path. \n \n   * The default low-precision nutation calculator `nu2000k()` can be replaced by another suitable IAU 2006 nutation\n     approximation. For example, the user may want to use the `iau2000b()` model or some custom algorithm instead.\n \n - New constants, and enums -- adding more specificity and transparency to option switches and physical units.\n \n - Many new functions to provide more coordinate transformations, inverse calculations, and more intuitive usage.\n \n\n#### New in v1.1\n\n - New observing-frame based approach for calculations (`frames.c`). A `novas_frame` object uniquely defines both the \n   place and time of observation, with a set of pre-calculated transformations and constants. Once the frame is \n   defined it can be used very efficiently to calculate positions for multiple celestial objects with minimum \n   additional computational cost. The frames API is also more elegant and more versatile than the low-level NOVAS C \n   approach for performing the same kind of calculations. And, frames are inherently thread-safe since post-creation \n   their internal state is never modified during the calculations.\n   \n - New `novas_timespec` structure for the self-contained definition of precise astronomical time (`timescale.c`). You \n   can set the time to a JD date in the timescale of choice (UTC, UT1, GPS, TAI, TT, TCG, TDB, or TCB), or to a UNIX \n   time. Once set, you can obtain an expression of that time in any timescale of choice. And, you can create a new \n   time specification by incrementing an existing one, or measure precise time differences.\n   \n - New coordinate reference systems `NOVAS_MOD` (Mean of Date) which includes precession by not nutation and\n   `NOVAS_J2000` for the J2000 dynamical reference system.\n\n - New observer locations `NOVAS_AIRBORNE_OBSERVER` and `NOVAS_SOLAR_SYSTEM_OBSERVER`, and corresponding\n   `make_airborne_observer()` and `make_solar_system_observer()` functions. Airborne observers have an Earth-fixed\n   momentary location, defined by longitude, latitude, and altitude, the same way as for a stationary observer on\n   Earth, but are moving relative to the surface, such as in an aircraft or balloon based observatory. Solar-system\n   observers are similar to observers in Earth-orbit but their momentary position and velocity is defined relative\n   to the Solar System Barycenter (SSB), instead of the geocenter.\n   \n - New set of built-in refraction models to use with the frame-based functions, inclusing a radio refraction model\n   based on the formulae by Berman \u0026amp; Rockwell 1976. Users may supply their own custom refraction model also, and \n   may make use of the generic reversal function `novas_inv_refract()` to calculate refraction in the reverse \n   direction (observed vs astrometric elevations as the input) as needed.\n\n\n#### New in v1.2\n\n - New functions to calculate and apply additional gravitational redshift corrections for light that originates\n   near massive gravitating bodies (other than major planets, or Sun or Moon), or for observers located near massive\n   gravitating bodies (other than the Sun and Earth).\n   \n - [CALCEPH integration](#calceph-integration) to specify and use ephemeris data via the CALCEPH library for \n   Solar-system sources in general, and for major planets specifically.\n   \n - [NAIF CSPICE integration](#cspice-integration) to use the NAIF CSPICE library for all Solar-system sources, or for\n   major planets or other bodies only. \n   \n - Added support for using orbital elements. `object.type` can now be set to `NOVAS_ORBITAL_OBJECT`, whose orbit can \n   be defined by `novas_orbital`, relative to a `novas_orbital_system`. While orbital elements do not always yield \n   precise positions, they can for shorter periods, provided that the orbital elements are up-to-date. For example, \n   the [Minor Planer Center](https://www.minorplanetcenter.net/iau/mpc.html) (MPC) publishes accurate orbital elements \n   for all known asteroids and comets regularly. For newly discovered objects, this may be the only and/or most \n   accurate information available anywhere.\n\n - Added `NOVAS_EMB` (Earth-Moon Barycenter) and `NOVAS_PLUTO_BARYCENTER` to `enum novas_planets` to distinguish\n   from the corresponding planet centers in calculations.\n\n - Added more physical unit constants to `novas.h`.\n \n \n#### New in v1.3\n\n - New functions to aid the conversion of LSR velocities to SSB-based velocities, and vice-versa. (Super)NOVAS always \n   defines catalog sources with SSB-based radial velocities, but some catalogs provide LSR velocities.\n\n - New functions to convert dates/times and angles to/from their string representations.\n \n - New functions to convert between Julian Days and calendar dates in the calendar of choice (astronomical, Gregorian, \n   or Roman/Julian).\n\n - New convenience functions for oft-used astronomical calculations, such as rise/set times, LST, parallactic angle \n   (a.k.a. Vertical Position Angle), heliocentric distance, illumination fraction, or incident solar power, Sun and \n   Moon angles, and much more. \n \n - New functions and data structures provide second order Taylor series expansion of the apparent horizontal or \n   equatorial positions, distances, and redshifts for sources. These values, including rates and accelerations, can be \n   directly useful for controlling telescope drives in horizontal or equatorial mounts to track sources. You can also \n   use them to obtain instantaneous projected (extrapolated) positions at low computational cost.\n\n\n\n\u003ca name=\"api-changes\"\u003e\u003c/a\u003e\n### Refinements to the NOVAS C API\n\n - Changed to [support for calculations in parallel threads](#multi-threading) by making cached results thread-local.\n   This works using the C11 standard `_Thread_local`, or the C23 `thread_local`, or else the earlier GNU C \u0026gt;= 3.3 \n   standard `__thread` modifier. You can also set the preferred thread-local keyword for your compiler by passing it \n   via `-DTHREAD_LOCAL=...` in `config.mk` to ensure that your build is thread-safe. And, if your compiler has no \n   support whatsoever for thread_local variables, then SuperNOVAS will not be thread-safe, just as NOVAS C isn't.\n\n - SuperNOVAS functions take `enum`s as their option arguments instead of raw integers. The enums allow for some \n   compiler checking (e.g. using the wrong enum), and make for more readable code that is easier to debug. They also \n   make it easy to see what choices are available for each function argument, without having to consult the \n   documentation each and every time.\n\n - All SuperNOVAS functions check for the basic validity of the supplied arguments (Such as NULL pointers or illegal \n   duplicate arguments) and will return -1 (with `errno` set, usually to `EINVAL`) if the arguments supplied are\n   invalid (unless the NOVAS C API already defined a different return value for specific cases. If so, the NOVAS C\n   error code is returned for compatibility).\n   \n - All erroneous returns now set `errno` so that users can track the source of the error in the standard C way and\n   use functions such as `perror()` and `strerror()` to print human-readable error messages.\n\n - SuperNOVAS prototypes declare function pointer arguments as `const` whenever the function does not modify the\n   data content being pointed at. This supports better programming practices that generally aim to avoid unintended \n   data modifications.\n\n - Many SuperNOVAS functions allow `NULL` arguments, both for optional input values as well as outputs that are not \n   required (see the [API Documentation](https://smithsonian.github.io/SuperNOVAS/apidoc/html/) for specifics).\n   This eliminates the need to declare dummy variables in your application code.\n  \n - Many output values supplied via pointers are set to clearly invalid values in case of erroneous returns, such as\n   `NAN` so that even if the caller forgets to check the error code, it becomes obvious that the values returned\n   should not be used as if they were valid. (No more sneaky silent failures.)\n\n - All SuperNOVAS functions that take an input vector to produce an output vector allow the output vector argument\n   be the same as the input vector argument. For example, `frame_tie(pos, J2000_TO_ICRS, pos)` using the same \n   `pos` vector both as the input and the output. In this case the `pos` vector is modified in place by the call. \n   This can greatly simplify usage, and eliminate extraneous declarations, when intermediates are not required.\n\n - Catalog names can be up to 6 bytes (including termination), up from 4 in NOVAS C, while keeping `struct` layouts \n   the same as NOVAS C thanks to alignment, thus allowing cross-compatible binary exchange of `cat_entry` records\n   with NOVAS C 3.1.\n\n - Changed `make_object()` to retain the specified number argument (which can be different from the `starnumber` \n   value in the supplied `cat_entry` structure).\n   \n - `cio_location()` will always return a valid value as long as neither output pointer argument is NULL. (NOVAS C\n   3.1 would return an error if a CIO locator file was previously opened but cannot provide the data for whatever\n   reason). \n\n - `cel2ter()` and `ter2cel()` can now process 'option'/'class' = 1 (`NOVAS_REFERENCE_CLASS`) regardless of the\n   methodology (`EROT_ERA` or `EROT_GST`) used to input or output coordinates in GCRS.\n   \n - More efficient paging (cache management) for `cio_array()`, including I/O error checking.\n \n - IAU 2000A nutation model uses higher-order Delaunay arguments provided by `fund_args()`, instead of the linear\n   model in NOVAS C 3.1.\n   \n - IAU 2000 nutation made a bit faster, reducing the the number of floating-point multiplications necessary by \n   skipping terms that do not contribute. Its coefficients are also packed more frugally in memory, resulting in a\n   smaller footprint.\n   \n - Changed the standard atmospheric model for (optical) refraction calculation to include a simple model for the \n   annual average temperature at the site (based on latitude and elevation). This results is a slightly more educated \n   guess of the actual refraction than the global fixed temperature of 10 \u0026deg;C assumed by NOVAC C 3.1 regardless of \n   observing location.\n   \n - [__v1.1__] Improved precision of some calculations, like `era()`, `fund_args()`, and `planet_lon()` by being more \n   careful about the order in which terms are accumulated and combined, resulting in a small improvement on the few \n   uas (micro-arcsecond) level.\n   \n - [__v1.1__] `place()` now returns an error 3 if and only if the observer is at (or very close, within ~1.5m) of the \n   observed Solar-system object.\n\n - [__v1.1__] `grav_def()` is simplified. It no longer uses the location type argument. Instead it will skip \n   deflections due to a body if the observer is within ~1500 km of its center (which is below the surface for all\n   major Solar system bodies).\n\n - [__v1.1.1__] For major planets (and Sun and Moon) `rad_vel()` and `place()` will include gravitational corrections \n   to radial velocity for light originating at the surface, and observed near Earth or at a large distance away from \n   the source.\n   \n - [__v1.3__] In reduced accuracy mode apply gravitational deflection for the Sun only. In prior versions, deflection \n   corrections were applied for Earth too. However, these are below the mas-level accuracy promised in reduced \n   accuracy mode, and without it, the calculations for `place()` and `novas_sky_pos()` are significantly faster.\n\n - [__v1.3__] `julian_date()` and `cal_date()` now use astronomical calendar dates instead of the fixed Gregorian \n   dates of before. Astronomical dates are Julian/Roman calendar dates prior to the Gregorian calendar reform of 1582.\n\n - [__v1.3__] Use C99 `restrict` keyword to prevent pointer argument aliasing as appropriate.\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"solarsystem\"\u003e\u003c/a\u003e\n## Incorporating Solar-system ephemeris data or services\n\nIf you want to use SuperNOVAS to calculate positions for a range of Solar-system objects, and/or to do it with \nprecision, you will have to interface it to a suitable provider of ephemeris data. Given the NOVAS C heritage, and \nsome added SuperNOVAS flexibility in this area, you have several options for going about it. These are listed from the \nmost practical (and preferred) to the least so (the old ways). \n\nNASA/JPL provides [generic ephemerides](https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/) for the major \nplanets, satellites thereof, the 300 largest asteroids, the Lagrange points, and some Earth orbiting stations. For \nexample, [DE440](https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440.bsp) covers the major planets, \nand the Sun, Moon, and barycenters for times between 1550 AD and 2650 AD. Or, you can use the \n[JPL HORIZONS](https://ssd.jpl.nasa.gov/horizons/) system (via the commnad-line / telnet or API interfaces) to \ngenerate custom ephemerides (SPK/BSP) for just about all known solar systems bodies, down to the tiniest rocks.\n\n - [Optional CALCEPH integration](#calceph-integration)\n - [Optional NAIF CSPICE toolkit integration](#cspice-integration)\n - [Universal ephemeris data / service integration](#universal-ephemerides)\n - [Legacy support for (older) JPL major planet ephemerides](#builtin-ephem-readers)\n - [Legacy linking of custom ephemeris functions](#explicit-ephem-linking)\n\n\n\u003ca name=\"calceph-integration\"\u003e\u003c/a\u003e\n### Optional CALCEPH integration\n\nThe [CALCEPH](https://www.imcce.fr/recherche/equipes/asd/calceph/) library provides easy-to-use access to JPL and \nINPOP ephemeris files from C/C++. As of version 1.2, we provide optional support for interfacing SuperNOVAS with the \nthe CALCEPH C library for handling Solar-system objects.\n\nPrior to building SuperNOVAS simply set `CALCEPH_SUPPORT` to 1 in `config.mk` or in your environment. Depending on the \nbuild target, it will build `libsolsys-calceph.so[.1]` (target `shared`) or `libsolsys-calceph.a` (target `static`) \nlibraries or `solsys-calceph.o` (target `solsys`), which provide the `novas_use_calceph()` and \n`novas_use_calceph_planets()`, and `novas_calceph_use_ids()` functions.\n\nOf course, you will need access to the CALCEPH C development files (C headers and unversioned `libcalceph.so` or `.a` \nlibrary) for the build to succeed. Here is an example on how you'd use CALCEPH with SuperNOVAS in your application \ncode:\n\n```c\n #include \u003cnovas.h\u003e\n #include \u003cnovas-calceph.h\u003e\n  \n // You can open a set of JPL/INPOP ephemeris files with CALCEPH...\n t_calcephbin *eph = calceph_open_array(...);\n  \n // Then use them as your generic SuperNOVAS ephemeris provider\n int status = novas_use_calceph(eph);\n if(status \u003c 0) {\n   // Ooops something went wrong...\n }\n  \n // -----------------------------------------------------------------------\n // Optionally you may use a separate ephemeris dataset for major planets\n // (or if planet ephemeris was included in 'eph' above, you don't have to) \n t_calcephbin *pleph = calceph_open(...);\n status = novas_use_calceph_planets(pleph);\n if(status \u003c 0) {\n   // Ooops something went wrong...\n }\n```\n\nAll modern JPL (SPK) ephemeris files should work with the `solsys-calceph` plugin. When linking your application, \nadd `-lsolsys-calceph` to your link flags (or else link with `solsys-calceph.o`), and of course the CSPICE library\ntoo. That's all there is to it.\n\n\n\u003ca name=\"cspice-integration\"\u003e\u003c/a\u003e\n### Optional NAIF CSPICE toolkit integration\n\nThe [NAIF CSPICE Toolkit](https://naif.jpl.nasa.gov/naif/toolkit.html) is the canonical standard library for JPL \nephemeris files from C/C++. As of version 1.2, we provide optional support for interfacing SuperNOVAS with CSPICE for \nhandling Solar-system objects.\n\nPrior to building SuperNOVAS simply set `CSPICE_SUPPORT` to 1 in `config.mk` or in your environment. Depending on the \nbuild target, it will build `libsolsys-cspice.so[.1]` (target `shared`) or `libsolsys-cspice.a` (target `static`) \nlibraries or `solsys-cspice.o` (target `solsys`), which provide the `novas_use_cspice()`, \n`novas_use_cspice_planets()`, and `novas_use_cspice_ephem()` functions to enable CSPICE for providing data for all \nSolar-system sources, or for major planets only, or for other bodies only, respectively. You can also manage the \nactive kernels with the `cspice_add_kernel()` and `cspice_remove_kernel()` functions.\n\nOf course, you will need access to the CSPICE development files (C headers, installed under a `cspice/` directory \nof an header search location, and the unversioned `libcspice.so` or `.a` library) for the build to succeed. You may \nwant to check out the [Smithsonian/cspice-sharedlib](https://github.com/Smithsonian/cspice-sharedlib) GitHub \nrepository to help you build CSPICE with shared libraries and dynamically linked tools.\n\nHere is an example on how you might use CSPICE with SuperNOVAS in your application code:\n\n```c\n #include \u003cnovas.h\u003e\n #include \u003cnovas-cspice.h\u003e\n\n // You can load the desired kernels for CSPICE\n // E.g. load DE440s and the Mars satellites:\n int status;\n  \n status = cspice_add_kernel(\"/path/to/de440s.bsp\");\n if(status \u003c 0) {\n   // oops, the kernels must not have loaded...\n   ...\n }\n  \n // Load additional kernels as needed...\n status = cspice_add_kernel(\"/path/to/mar097.bsp\");\n ...\n  \n // Then use CSPICE as your SuperNOVAS ephemeris provider\n novas_use_cspice();\n```\n\nAll JPL ephemeris data will work with the `solsys-cspice` plugin. When linking your application, add `-lsolsys-cspice` \nto your link flags (or else link with `solsys-cspice.o`), and link against te calceph library also (`-lcalceph`). \nThat's all there is to it.\n\n\n\u003ca name=\"universal-ephemerides\"\u003e\u003c/a\u003e\n### Universal ephemeris data / service integration \n\nPossibly the most universal way to integrate ephemeris data with SuperNOVAS is to write your own \n`novas_ephem_provider` function, e.g.:\n\n```c\n int my_ephem_reader(const char *name, long id, double jd_tdb_high, double jd_tdb_low, \n                     enum novas_origin *restrict origin, \n                     double *restric pos, double *restrict vel) {\n   // Your custom ephemeris reader implementation here\n   ...\n }\n```\n\nwhich takes an object ID number (such as a NAIF) an object name, and a split TDB date (for precision) as it inputs, \nand returns the type of origin with corresponding ICRS position and velocity vectors in the supplied pointer \nlocations. The function can use either the ID number or the name to identify the object or file (whatever is the most \nappropriate for the implementation and for the supplied parameters). The positions and velocities may be returned \neither relative to the SSB or relative to the  heliocenter, and accordingly, your function should set the value \npointed at by origin to `NOVAS_BARYCENTER` or `NOVAS_HELIOCENTER` accordingly. Positions and velocities are \nrectangular ICRS _x,y,z_ vectors in units of AU and AU/day respectively. \n\nThis way you can easily integrate current ephemeris data, e.g. for the Minor Planet Center (MPC), or whatever other \nephemeris service you prefer.\n\nOnce you have your adapter function, you can set it as your ephemeris service via `set_ephem_provider()`:\n\n```c\n set_ephem_provider(my_ephem_reader);\n```\n\nBy default, your custom `my_ephem_reader` function will be used for 'minor planets' only (i.e. anything other than the \nmajor planets, the Sun, Moon, Solar-system Barycenter...). But, you can use the same function for the mentioned \n'major planets' also via:\n\n```c\n set_planet_provider(planet_ephem_provider);\n set_planet_provider_hp(planet_ephem_provider_hp);\n```\n\nprovided you compiled SuperNOVAS with `BUILTIN_SOLSYS_EPHEM = 1` (in `config.mk`), or else you link your code against\n`solsys-ephem.c` explicitly. Easy-peasy.\n\n\n\n\u003ca name=\"builtin-ephem-readers\"\u003e\u003c/a\u003e\n### Legacy support for (older) JPL major planet ephemerides\n\nIf you only need support for major planets, you may be able to use one of the modules included with the distribution. \nThe legacy NOVAS modules `solsys1.c` and `solsys2.c` provide built-in support to older JPL ephemerides (DE200 to DE421), \neither via the `eph_manager` interface of `solsys1.c` or via the FORTRAN `pleph` interface with `solsys2.c`.\n\n#### Planets via `eph_manager`\n\nTo use the `eph_manager` interface for planet 1997 JPL planet ephemeris (DE200 through DE421), you must either build \nSuperNOVAS with `BUILTIN_SOLSYS1 = 1` in `config.mk`, or else link your application with `solsys1.c` and \n`eph_manager.c` from SuperNOVAS explicitly. If you want `eph_manager` to be your default ephemeris provider (the old \nway) you might also want to set `DEFAULT_SOLSYS = 1` in `config.mk`. Otherwise, your application should set \n`eph_manager` as your planetary ephemeris provider at runtime via:\n\n```c\n set_planet_provider(planet_eph_manager);\n set_planet_provider_hp(planet_eph_manager_hp);\n```\n\nEither way, before you can use the ephemeris, you must also open the relevant ephemeris data file with `ephem_open()`:\n\n```c\n double from_jd, to_jd;  // [day] Julian date range of the ephemeris data\n int de_number;\t         // Will be set to the DE number, e.g. 405 for DE405\n  \n ephem_open(\"path/to/de405.bsp\", \u0026from_jd, \u0026to_jd, \u0026de_number);\n```\n\nAnd, when you are done using the ephemeris file, you should close it with\n\n```c\n ephem_close();\n```\n \nNote, that at any given time `eph_manager` can have only one ephemeris data file opened. You cannot use it to \nretrieve data from multiple ephemeris input files at the same time. (But you can with CALCEPH or the CSPICE toolkit, \neither of which you can interface as discussed further above!)\n\nThat's all, except the warning that this method will not work with newer JPL ephemeris data, beyond DE421.\n\n\n#### Planets via JPL's `pleph` FORTRAN interface\n\nTo interface eith the venerable JPL PLEPH library (FORTRAN) for planet ephemerides, you must either build SuperNOVAS \nwith `BUILTIN_SOLSYS2 = 1` in `config.mk`, or else link your application with `solsys2.c` and your appropriately \nmodified `jplint.f` (from the `examples` sub-directory) explicitly, together with the JPL PLEPH library. If you want \nthis to be your default ephemeris provider (the old way) you might also want to set `DEFAULT_SOLSYS = 2` in \n`config.mk`. Otherwise, your application should set your planetary ephemeris provider at runtime via:\n\n```c\n set_planet_provider(planet_jplint);\n set_planet_provider_hp(planet_jplint_hp);\n```\n\nIntegrating JPL ephemeris data this way can be arduous. You will need to compile and link FORTRAN with C (not the end\nof the world), but you may also have to modify `jplint.f` (providing the intermediate FORTRAN `jplint_()` / \n`jplihp_()` interfaces to `pleph_()`) to work with the version of `pleph.f` that you will be using. Unless you already \nhave code that relies on this method, you are probably better off choosing one of the other ways for integrating \nplanetary ephemeris data with SuperNOVAS.\n\n\n\u003ca name=\"explicit-ephem-linking\"\u003e\u003c/a\u003e\n### Legacy linking of custom ephemeris functions\n\nFinally, if none of the above is appealing, and you are fond of the old ways, you may compile SuperNOVAS with the \n`DEFAULT_SOLSYS` option disabled (commented, removed, or else set to 0), and then link your own implementation of\n`solarsystem()` and `solarsystem_hp()` calls with your application. \n\nFor Solar-system objects other than the major planets, you may also provide your own `readeph()` implementation. (In\nthis case you will want to set `DEFAULT_READEPH` in `config.mk` to specify your source code for that function before\nbuilding the SuperNOVAS library, or else disable that option entirely (e.g. by commenting or removing it), and link\nyour application explicitly with your `readeph()` implementation.\n\nThe downside of this approach is that your SuperNOVAS library will not be usable without invariably providing a\n`solarsystem()` / `solarsystem_hp()` and/or `readeph()` implementations for _every_ application that you will want\nto use SuperNOVAS with. This is why the runtime configuration of the ephemeris provider functions is the best and\nmost generic way to add your preferred implementations while also providing some minimum default implementations for\n_other users_ of the library, who may not need _your_ ephemeris service, or have no need for planet data beyond the \napproximate positions for the Earth and Sun.\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"debug-support\"\u003e\u003c/a\u003e\n## Runtime debug support\n\nYou can enable or disable debugging output to `stderr` with `novas_debug(enum novas_debug_mode)`, where the argument \nis one of the defined constants from `novas.h`:\n\n | `novas_debug_mode` value   | Description                                        |\n | -------------------------- | -------------------------------------------------- |\n | `NOVAS_DEBUG_OFF`          | No debugging output (_default_)                    |\n | `NOVAS_DEBUG_ON`           | Prints error messages and traces to `stderr`       |\n | `NOVAS_DEBUG_EXTRA`        | Same as above but with stricter error checking     |\n \nThe main difference between `NOVAS_DEBUG_ON` and `NOVAS_DEBUG_EXTRA` is that the latter will treat minor issues as \nerrors also, while the former may ignore them. For example, `place()` will return normally by default if it cannot \ncalculate gravitational bending around massive planets in full accuracy mode. It is unlikely that this omission would \nsignificantly alter the result in most cases, except for some very specific ones when observing in a direction close \nto a major planet. Thus, with `NOVAS_DEBUG_ON`, `place()` go about as usual even if the Jupiter's position is not \nknown. However, `NOVAS_DEBUG_EXTRA` will not give it a free pass, and will make `place()` return an error (and print \nthe trace) if it cannot properly account for gravitational bending around the major planets as it is expected to.\n\nWhen debug mode is enabled, any error condition (such as NULL pointer arguments, or invalid input values etc.) will\nbe reported to the standard error, complete with call tracing within the SuperNOVAS library, s.t. users can havev a \nbetter idea of what exactly did not go to plan (and where). The debug messages can be disabled by passing \n`NOVAS_DEBUF_OFF` (0) as the argument to the same call. Here is an example error trace when your application calls \n`grav_def()` with `NOVAS_FULL_ACCURACY` while `solsys3` provides Earth and Sun positions only and when debug mode is \n`NOVAS_DEBUG_EXTRA` (otherwise we'll ignore that we skipped the almost always negligible deflection due to planets):\n\n```\n  ERROR! earth_sun_calc: invalid or unsupported planet number: 5 [=\u003e 2]\n       @ earth_sun_calc_hp [=\u003e 2]\n       @ solarsystem_hp [=\u003e 2]\n       @ ephemeris:planet [=\u003e 12]\n       @ grav_def:Jupiter [=\u003e 12]\n```\n\n-----------------------------------------------------------------------------\n\n\n\u003ca name=\"release-schedule\"\u003e\u003c/a\u003e\n## Release schedule\n\nA predictable release schedule and process can help manage expectations and reduce stress on adopters and developers \nalike.\n\nReleases of the library shall follow a quarterly release schedule. You may expect upcoming releases \nto be published around __February 1__, __May 1__, __August 1__, and/or __November 1__ each year, on an as-needed\nbasis. That means that if there are outstanding bugs, or new pull requests (PRs), you may expect a release that \naddresses these in the upcoming quarter. The dates are placeholders only, with no guarantee that a new release will \nactually be available every quarter. If nothing of note comes up, a potential release date may pass without a release \nbeing published.\n\nNew features are generally reserved for the feature releases (e.g. __1.x.0__ version bumps), although they may also be \nrolled out in bug-fix releases as long as they do not affect the existing API -- in line with the desire to keep \nbug-fix releases fully backwards compatible with their parent versions.\n\nIn the weeks and month(s) preceding releases one or more _release candidates_ (e.g. `1.0.1-rc3`) will be published \ntemporarily on GitHub, under [Releases](https://github.com/Smithsonian/SuperNOVAS/releases), so that changes can be \ntested by adopters before the releases are finalized. Please use due diligence to test such release candidates with \nyour code when they become available to avoid unexpected surprises when the finalized release is published. Release \ncandidates are typically available for one week only before they are superseded either by another, or by the finalized \nrelease.\n\n-----------------------------------------------------------------------------\nCopyright (C) 2025 Attila Kovács\n","funding_links":["https://github.com/sponsors/attipaci"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmithsonian%2Fsupernovas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmithsonian%2Fsupernovas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmithsonian%2Fsupernovas/lists"}