sidereon (C)

July 24, 2026 ยท View on GitHub

GNSS and astrodynamics for C. Propagate satellites, predict passes, solve precise positions (SPP / RTK / PPP), and work with coordinate frames, time, and the orbital-data formats the field actually exchanges.

This repository is the C ABI interface to sidereon. The engine is a Rust core, compiled here into one self-contained library plus one generated header. There is no runtime to install and nothing to link but libsidereon and libm. The binding adds no modeling of its own: a solve returns exactly the numbers the engine computes, in the C idiom you already use (opaque handles, integer status codes, caller-owned output buffers). The numbers are reference-validated: the SGP4 propagator is a port of David Vallado's reference implementation, bit-exact to it, and the positioning stack is checked against IGS products.

Build

cargo build --release builds the library. The C header is generated from the Rust source with cbindgen and is committed alongside it, so you do not need cbindgen to consume the binding.

The committed header exposes SIDEREON_VERSION_STRING and the matching SIDEREON_VERSION_MAJOR, SIDEREON_VERSION_MINOR, and SIDEREON_VERSION_PATCH macros.

cargo build --release
# -> target/release/libsidereon.a      (static archive)
#    target/release/libsidereon.dylib  (or .so on Linux)
# header: bindings/c/include/sidereon.h (already committed)

This crate is a workspace member, so the library lands in the workspace root's target/release. If your layout differs, cargo metadata --format-version 1 reports the exact target_directory.

Regenerate the header only after changing the C surface:

cargo install cbindgen   # once
cbindgen --config bindings/c/cbindgen.toml --crate sidereon-c \
    --output bindings/c/include/sidereon.h

Compile and link a C program against the header and the library:

cc -std=c11 -I bindings/c/include my_program.c \
    -L target/release -lsidereon -Wl,-rpath,target/release -lm \
    -o my_program

Link target/release/libsidereon.a directly instead of -lsidereon if you want the solver baked into your binary with no shared object to ship.

Exact GNSS product sources

Product identity and distribution are separate. The pure C functions derive a catalog identity or one explicitly selected public location without performing network or file IO:

SidereonProductIdentity identity;
SidereonStatus status = sidereon_data_product_identity(
    "cod",
    SIDEREON_PRODUCT_FAMILY_SP3,
    2026, 7, 12,
    NULL,  /* catalog default cadence */
    NULL,  /* no ultra-rapid issue */
    &identity
);

SidereonDistributionLocation location;
status = sidereon_data_distribution_location(
    "cod",
    SIDEREON_PRODUCT_FAMILY_SP3,
    2026, 7, 12,
    NULL,
    NULL,
    SIDEREON_DISTRIBUTION_SOURCE_NASA_CDDIS,
    &location
);

const SidereonProductIdentity expected[] = {identity};
const SidereonProductIdentity available[] = {identity};
status = sidereon_data_validate_exact_product_set(expected, 1, available, 1);

SidereonSolutionClass solution_class;
status = sidereon_data_product_solution_class(
    "igs", SIDEREON_PRODUCT_FAMILY_SP3, &solution_class
);  /* FINAL; IGS RINEX_NAVIGATION is BROADCAST */

SidereonSp3ContentStartConvention start_convention;
int64_t start_offset_s;
status = sidereon_data_sp3_content_start_convention(
    "gfz_ult", 2022, 9, 7, "0300", &start_convention, &start_offset_s
);  /* FILENAME_EPOCH_MINUS_ONE_DAY; -86400 */

size_t sample_count;
size_t sample_written;
status = sidereon_data_supported_samples(
    "gfz_ult", SIDEREON_PRODUCT_FAMILY_SP3, 2021, 5, 15, "0000",
    NULL, 0, &sample_written, &sample_count
);  /* sample_count == 2; allocate exactly that many records, then call again */

identity.official_filename is the exact decompressed standard-product name. location records the selected source, public URL, archive filename, and transport compression. Switching from CDDIS to the direct location cannot change publisher, solution class, campaign, issue, date, cadence, family, or format. Unsupported combinations return SIDEREON_STATUS_INVALID_ARGUMENT and the typed core detail through sidereon_last_error_message.

IGS combined-final SP3 identity is date-aware: historical CDDIS locations use igs<week><day>.sp3.Z with SIDEREON_ARCHIVE_COMPRESSION_UNIX_COMPRESS, while current CDDIS and direct-BKG locations use the long filename and gzip. Historical direct-BKG layout is not modeled and is rejected rather than guessed. sidereon_data_default_sample_for_date returns the published cadence for a specific date, including GFZ rapid SP3's 2021 change from 15M to 05M. sidereon_data_supported_samples reports the complete date- and issue-aware set via a two-call caller-buffer/count contract; the GFZ ultra-rapid overlap above returns 15M and 05M, while its 2100 issue returns only 15M. Product constructors reject any cadence outside that set. sidereon_data_product_solution_class distinguishes the IGS final-SP3 and broadcast-navigation product families. sidereon_data_sp3_content_start_convention reports the cataloged relationship between an SP3 filename epoch and its required first content epoch. It validates ultra-rapid issues strictly and returns both the typed convention and signed seconds offset; exact requests built from identities apply the same catalog fact without allowing a caller override.

Product derivation is bounded by the publicly evidenced eras: ESA final SP3/clock starts 2014-01-05, GFZ rapid SP3/clock starts 2020-05-13, GFZ ultra-rapid starts 2020-10-06, ESA ultra-rapid starts 2022-10-04, and IGS and CODE ultra-rapid long names start with GPS week 2238. GFZ ultra-rapid defaults change from 15M to 05M on 2021-05-16. ESA ultra-rapid defaults are 15M through the 2025-02-02 0600 issue and 05M from the 1200 issue; the date-only query reports the start-of-day (0000) default. CDDIS rejects unmodeled pre-week-2238 long-name SP3/IONEX locations; the modeled legacy IGS final .sp3.Z family remains available. ESA ESA0MGNFIN final SP3 is direct-only because the catalog does not substitute a different CDDIS family.

The exact-set gate rejects an empty declaration, duplicates, missing products, and undeclared products. It compares all identity fields, so same-filename prediction tiers remain distinct. Start dependent processing only after it returns SIDEREON_STATUS_OK. For SP3 observed/predicted timing, use sidereon_sp3_prediction_summary; do not infer the boundary from issue times or catalog fields.

After acquiring and validating every SP3 input, describe each contributor with SidereonSp3ArtifactIdentity: requested and resolved identities, distributor, official filename, decompressed and archive SHA-256/length pairs, and archive compression. sidereon_sp3_merge_input_identity validates those records with the complete SidereonSp3MergeOptions and returns a schema version plus a stable ID. Contributor enumeration order does not affect mean or median IDs. Precedence IDs bind the original order because it determines source priority. Changing an artifact or any effective merge control changes the ID. The record intentionally has no URL, credential, cache path, retrieval timestamp, or HTTP metadata fields. Use the usual NULL/zero first call to query the stable-ID buffer length.

The C interface deliberately leaves HTTP, Earthdata credentials, retries, and transport decompression to the caller. It exposes exact SP3 validation below; validation of other product formats remains caller-managed. After validation, sidereon_exact_cache_open acquires the bounded native writer lock; sidereon_exact_cache_publish commits product, archive, and provenance bytes as one immutable transaction. Locked and unlocked reads return only a complete digest-verified entry, and sidereon_exact_cache_cleanup removes abandoned entries only under the held lock. Entry accessors copy its authenticated bytes, paths, and immutable transaction identifier. Use sidereon_data_product_identity_cache_key when constructing a portable cache layout.

HTTP and credentials remain caller-managed, but exact SP3 content validation is available before publication to a cache or downstream use:

SidereonExactSp3Request *request = NULL;
SidereonSp3 *sp3 = NULL;
SidereonExactSp3Coverage coverage;

status = sidereon_sp3_exact_request_from_identity(&identity, &request);
if (status == SIDEREON_STATUS_OK) {
    status = sidereon_sp3_load_exact(bytes, byte_count, request, &sp3, &coverage);
}
sidereon_sp3_exact_request_free(request);
/* ... use sp3 only after SIDEREON_STATUS_OK ... */
sidereon_sp3_free(sp3);

The exact gate checks the requested cadence and span against the SP3 header and regular parsed epoch grid, accepts the specified half-open and inclusive boundary forms, binds catalog identities to their official producing agency, and treats malformed or mismatched bytes as a terminal error. The existing sidereon_sp3_load remains the permissive general reader. Its raw sidereon_sp3_declared_epoch_count and sidereon_sp3_declared_start_j2000_seconds accessors expose the independent line-1 evidence used by exact validation.

For CDDIS, send caller-managed credentials only to NASA's documented hosts, remove URL queries from logs/provenance, reject HTML success bodies, validate content length and the advertised transport compression (gzip or historical Unix-compress), and parse the result with sidereon_sp3_load_exact (or the general sidereon_sp3_load) or sidereon_ionex_load. NASA's public access documentation is at CDDIS archive access and Earthdata Login data access.

Example: a single-point positioning solve

A complete program with no external data files. It loads a trimmed SP3 precise orbit product from an in-memory buffer, feeds in six GPS L1 pseudoranges, and prints the receiver position in ECEF metres. Every fallible call returns SIDEREON_STATUS_OK; on anything else, sidereon_last_error_message gives the reason. Each handle gets a matching _free.

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "sidereon.h"

/* Precise orbits (SP3-c), trimmed to the satellites and epoch below. */
static const char SP3[] =
    "#cP2020  6 24  9 45  0.00000000      19 TRACK IGb14 FIT GRGS\n"
    "## 2111 259200.00000000   900.00000000 59024 0.0000000000000\n"
    "+    6   G08G10G16G18G20G21  0  0  0  0  0  0  0  0  0  0  0\n"
    "+          0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"
    "+          0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"
    "+          0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"
    "+          0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"
    "++         4  4  4  4  4  4  0  0  0  0  0  0  0  0  0  0  0\n"
    "++         0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"
    "++         0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"
    "++         0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"
    "++         0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0\n"
    "%c M  cc GPS ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc\n"
    "%c cc cc ccc ccc cccc cccc cccc cccc ccccc ccccc ccccc ccccc\n"
    "%f  0.0000000  0.000000000  0.00000000000  0.000000000000000\n"
    "%f  0.0000000  0.000000000  0.00000000000  0.000000000000000\n"
    "%i    0    0    0    0      0      0      0      0         0\n"
    "%i    0    0    0    0      0      0      0      0         0\n"
    "/* CNES/CLS/GRGS - TOULOUSE,FRANCE - Contact : igs-ac@cls.fr\n"
    "/* PCV:IGS14_2108 OL/AL:FES2012  NONE     NN ORB:CoN CLK:CoN\n"
    "/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n"
    "/* CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n"
    "*  2020  6 24  9 45  0.00000000\n"
    "PG08   4643.904912 -24162.558516  -9650.049610    -38.635183\n"
    "PG10  17886.671789   3039.006353 -19391.393793   -380.478330\n"
    "PG16   3673.608258 -18933.969815  17839.814705   -174.352223\n"
    "PG18  23700.127530   6507.753313  10093.688366    228.808891\n"
    "PG20  21015.602926  12381.822675 -10856.524893    527.448122\n"
    "PG21  26299.269767  -2460.995007    716.844563     15.508264\n"
    "*  2020  6 24 10  0  0.00000000\n"
    "PG08   5254.320976 -24937.345008  -6980.386897    -38.636691\n"
    "PG10  19010.269319   4866.851618 -17921.084337   -380.488198\n"
    "PG16   4860.112779 -17100.303698  19364.987730   -174.356395\n"
    "PG18  22418.061092   6785.447511  12538.206018    228.818132\n"
    "PG20  21540.808052  13373.630254  -8364.466576    527.447968\n"
    "PG21  26177.802655  -2146.941378   3560.974533     15.512752\n"
    "*  2020  6 24 10 15  0.00000000\n"
    "PG08   5710.564170 -25453.105280  -4188.721104    -38.638443\n"
    "PG10  20115.189466   6485.476927 -16142.894217   -380.498119\n"
    "PG16   6226.321564 -15204.115225  20546.504507   -174.360500\n"
    "PG18  20889.343380   7160.242478  14767.111628    228.827365\n"
    "PG20  21908.611100  14137.388789  -5730.669927    527.447926\n"
    "PG21  25760.514626  -1769.431822   6343.514786     15.516981\n"
    "*  2020  6 24 10 30  0.00000000\n"
    "PG08   6042.284075 -25684.525952  -1323.814227    -38.640412\n"
    "PG10  21159.408012   7879.460815 -14087.964262   -380.507982\n"
    "PG16   7759.098529 -13294.312785  21364.232984   -174.364502\n"
    "PG18  19148.071571   7657.452624  16742.023312    228.836711\n"
    "PG20  22085.133601  14686.270178  -2999.737823    527.447449\n"
    "PG21  25067.475736  -1296.107146   9017.301047     15.521531\n"
    "*  2020  6 24 10 45  0.00000000\n"
    "PG08   6283.596097 -25614.361768   1564.242656    -38.641756\n"
    "PG10  22100.268611   9041.937175 -11792.020438   -380.517890\n"
    "PG16   9436.967444 -11417.672716  21804.746560   -174.368582\n"
    "PG18  17234.002467   8295.952772  18428.910848    228.845896\n"
    "PG20  22041.002117  15040.256156   -217.934016    527.447614\n"
    "PG21  24126.033756   -698.817607  11537.888708     15.525474\n"
    "*  2020  6 24 11  0  0.00000000\n"
    "PG08   6471.529324 -25234.155183   4424.947554    -38.642177\n"
    "PG10  22896.131923   9974.623387  -9294.735744   -380.527755\n"
    "PG16  11230.846890  -9617.105625  21861.506015   -174.372582\n"
    "PG18  15191.136796   9087.282030  19798.684014    228.855179\n"
    "PG20  21752.562074  15225.214120   2567.564373    527.447459\n"
    "PG21  22969.651026     45.186775  13864.197409     15.529722\n"
    "*  2020  6 24 11 15  0.00000000\n"
    "PG08   6644.360327 -24544.616731   7208.313715    -38.643219\n"
    "PG10  23507.988002  10687.523653  -6639.037631   -380.537732\n"
    "PG16  13105.095965  -7930.108701  21534.895433   -174.377072\n"
    "PG18  13066.149231  10035.038521  20827.696867    228.864441\n"
    "PG20  21202.891348  15271.756854   5309.432624    527.447520\n"
    "PG21  21636.563260    952.305394  15959.053118     15.534059\n"
    "*  2020  6 24 11 30  0.00000000\n"
    "PG08   6839.902243 -23555.650675   9865.783600    -38.644720\n"
    "PG10  23900.971736  11198.322998  -3870.373323   -380.547626\n"
    "PG16  15018.822256  -6387.464812  20832.117593   -174.381097\n"
    "PG18  10906.724082  11134.589308  21498.159124    228.873724\n"
    "PG20  20382.574992  15213.927788   7960.972253    527.447536\n"
    "PG21  20168.317061   2031.899012  17789.631146     15.538108\n"
    "*  2020  6 24 11 45  0.00000000\n"
    "PG08   7093.820244 -22286.025531  12351.111011    -38.645281\n"
    "PG10  24045.725638  11531.498375  -1035.945281   -380.557547\n"
    "PG16  16927.393599  -5012.236439  19766.958053   -174.385107\n"
    "PG18   8759.860313  12373.107746  21798.447374    228.883076\n"
    "PG20  19290.210666  15087.759974  10476.882192    527.447233\n"
    "PG21  18608.242309   3285.876753  19327.806335     15.542220\n"
    "*  2020  6 24 12  0  0.00000000\n"
    "PG08   7438.042916 -20762.704119  14621.192800    -38.647002\n"
    "PG10  23919.560682  11717.183156   1816.071269   -380.567404\n"
    "PG16  18784.088401  -3819.088286  18359.430195   -174.389524\n"
    "PG18   6670.210753  13729.937789  21723.310519    228.892428\n"
    "PG20  17932.623680  14929.762015  12814.016152    527.447354\n"
    "PG21  16999.913180   4708.560403  20550.418405     15.546299\n"
    "*  2020  6 24 12 15  0.00000000\n"
    "PG08   7899.334503 -19019.862023  16636.833344    -38.647763\n"
    "PG10  23507.373783  11789.829543   4637.317275   -380.577286\n"
    "PG16  20541.815806  -2813.957336  16635.315598   -174.393761\n"
    "PG18   4678.519090  15177.272097  21273.965621    228.901739\n"
    "PG20  16324.780780  14775.387488  14932.114622    527.446948\n"
    "PG21  15385.647811   6286.819595  21439.461249     15.550671\n"
    "*  2020  6 24 12 30  0.00000000\n"
    "PG08   8498.085463 -17097.635989  18363.427105    -38.649562\n"
    "PG10  22802.289648  11786.720063   7380.034276   -380.587160\n"
    "PG16  22154.836702  -1994.074300  14625.615492   -174.397842\n"
    "PG18   2820.214554  16681.118608  20458.082166    228.911022\n"
    "PG20  14489.402694  14657.545765  16794.499670    527.447051\n"
    "PG21  13805.092069   8000.463976  21982.204956     15.554459\n"
    "*  2020  6 24 12 45  0.00000000\n"
    "PG08   9247.369015 -15040.654467  19771.547156    -38.650484\n"
    "PG10  21806.004803  11746.384064   9997.833858   -380.596890\n"
    "PG16  23580.419142  -1348.326156  12365.929519   -174.402246\n"
    "PG18   1124.218120  18202.518914  19289.654667    228.920337\n"
    "PG20  12456.286563  14605.211454  18368.720568    527.447006\n"
    "PG21  12293.927824   9822.872282  22171.258698     15.558791\n"
    "*  2020  6 24 13  0  0.00000000\n"
    "PG08  10152.299669 -12896.410588  20837.430270    -38.651742\n"
    "PG10  20528.822194  11706.977946  12446.442268   -380.606741\n"
    "PG16  24780.366877   -857.936737   9895.777935   -174.406704\n"
    "PG18   -387.994051  19698.971935  17788.765433    228.929616\n"
    "PG20  10261.360393  14642.186916  19627.138333    527.447150\n"
    "PG21  10882.740798  11721.833118  22004.581485     15.563408\n"
    "*  2020  6 24 13 15  0.00000000\n"
    "PG08  11209.716830 -10713.543221  21543.351971    -38.652897\n"
    "PG10  18989.375629  11704.688739  14684.410767   -380.616599\n"
    "PG16  25722.366833   -497.431201   7257.882649   -174.411115\n"
    "PG18  -1703.034135  21126.008215  15981.241263    228.938925\n"
    "PG20   7945.502154  14786.067388  20547.437547    527.446927\n"
    "PG21   9596.077666  13660.568119  21485.446350     15.567441\n"
    "*  2020  6 24 13 30  0.00000000\n"
    "PG08  12408.203090  -8540.094231  21877.887705    -38.653740\n"
    "PG10  17214.054103  11772.219589  16673.782838   -380.626446\n"
    "PG16  26381.110092   -235.841125   4497.421430   -174.415665\n"
    "PG18  -2815.928687  22438.854161  13898.209721    228.948171\n"
    "PG20   5553.165699  15047.451338  21113.054383    527.446839\n"
    "PG21   8451.716682  15598.904800  20622.361836     15.572148\n"
    "*  2020  6 24 13 45  0.00000000\n"
    "PG08  13728.433306  -6421.809657  21836.058871    -38.653666\n"
    "PG10  15236.146433  11937.412720  18380.709769   -380.636302\n"
    "PG16  26739.150669    -38.100050   1661.268176   -174.419882\n"
    "PG18  -3730.115104  23594.121838  11575.562442    228.957546\n"
    "PG20   3130.863904  15429.429881  21313.510668    527.447015\n"
    "PG21   7460.170730  17494.563735  19428.952908     15.577088\n"
    "*  2020  6 24 14  0  0.00000000\n"
    "PG08  15143.837640  -4400.549449  21419.364822    -38.654444\n"
    "PG10  13094.736367  12222.060725  19776.006518   -380.646060\n"
    "PG16  26787.476793    133.425531  -1202.769329   -174.424084\n"
    "PG18  -4457.109220  24551.458779   9053.334699    228.966797\n"
    "PG20    725.565911  15927.378707  21144.644989    527.446993\n"
    "PG21   6624.436243  19304.522499  17923.801627     15.580392\n"
    "*  2020  6 24 14 15  0.00000000\n"
    "PG08  16621.549904  -2512.864913  20635.703989    -38.655549\n"
    "PG10  10833.387063  12640.950613  20835.640171   -380.655943\n"
    "PG16  26525.779980    317.327470  -4046.704331   -174.428345\n"
    "PG18  -5015.857646  25275.093632   6375.012002    228.975990\n"
    "PG20  -1616.930248  16529.064324  20608.733434    527.446900\n"
    "PG21   5939.995958  20986.417129  16130.246267     15.584692\n"
    "EOF\n";

int main(void) {
    char err[256];

    SidereonSp3 *sp3 = NULL;
    if (sidereon_sp3_load((const uint8_t *)SP3, strlen(SP3), &sp3)
        != SIDEREON_STATUS_OK) {
        sidereon_last_error_message(err, sizeof err);
        fprintf(stderr, "sp3 load: %s\n", err);
        return 1;
    }

    /* GPS L1 pseudoranges (m) for the satellites in view at the epoch. */
    SidereonObservation obs[] = {
        { "G08", 23825519.8 }, { "G10", 22717690.1 }, { "G16", 20478653.4 },
        { "G18", 21768335.2 }, { "G20", 21248327.7 }, { "G21", 20808709.8 },
    };
    SidereonSppInputs inputs = {
        .observations         = obs,
        .observation_count    = 6,
        .t_rx_j2000_s         = 646272000.0,
        .t_rx_second_of_day_s = 43200.0,
        .day_of_year          = 176.5,
        .initial_guess        = { 4.5e6, 0.5e6, 4.5e6, 0.0 },
        .ionosphere           = false,   /* geometry-only L1 solve */
        .troposphere          = false,
        .with_geodetic        = true,
    };

    SidereonSppSolution *sol = NULL;
    if (sidereon_solve_spp(sp3, &inputs, &sol) != SIDEREON_STATUS_OK) {
        sidereon_last_error_message(err, sizeof err);
        fprintf(stderr, "solve spp: %s\n", err);
        sidereon_sp3_free(sp3);
        return 1;
    }

    double xyz[3] = { 0 };
    if (sidereon_spp_solution_position(sol, xyz, 3) != SIDEREON_STATUS_OK) {
        sidereon_last_error_message(err, sizeof err);
        fprintf(stderr, "spp position: %s\n", err);
        return 1;
    }
    printf("ECEF m  %.3f %.3f %.3f\n", xyz[0], xyz[1], xyz[2]);
    /* ~4484128 550582 4487561 */

    sidereon_spp_solution_free(sol);
    sidereon_sp3_free(sp3);
    return 0;
}

The other solvers follow the same pattern: a typed config in, an opaque solution out, reader functions that copy scalars and positions into memory you own. Every variable-length result uses one contract: call with a NULL buffer and length 0 to learn the required count, then call again with storage you own. Anything that takes time takes UTC unix microseconds. The full surface (every struct, status code, and per-function ownership note) lives in bindings/c/include/sidereon.h.

Example: PROJ EGM96 vertical-grid interpolation

Load the public OSGeo egm96_15.gtx bytes with sidereon_geoid_grid_from_proj_egm96_gtx, then call sidereon_geoid_grid_undulation_proj_rad. The arithmetic argument is required: choose SIDEREON_PROJ_VGRIDSHIFT_ARITHMETIC_SEPARATE_MULTIPLY_ADD for a PROJ build without floating-point contraction, or SIDEREON_PROJ_VGRIDSHIFT_ARITHMETIC_FUSED_MULTIPLY_ADD for a contracted build. The two valid recipes can differ by one ULP.

Coordinate failures return SIDEREON_STATUS_INVALID_ARGUMENT plus a SidereonProjVgridshiftError containing both the error category and the offending coordinate. The function never panics, silently extrapolates, or selects arithmetic from the host platform.

Capabilities

  • Orbit propagation. SGP4 from TLE/OMM, numerical state propagation with a composable force model (spherical-harmonic geopotential to selectable degree and order, Sun/Moon third-body, solar radiation pressure, relativistic correction, atmospheric drag) and orbital decay estimation with a post-decay validity latch, Kepler two-body propagation, batch and constellation propagation, pass prediction, look angles, ground tracks, coverage grids, and batch least-squares orbit fitting against precise ephemerides (including terrestrial-frame SP3 through the Earth-orientation chain) with a per-satellite residual ledger.
  • GNSS positioning. Single-point positioning (SPP), public static-position solves from SP3 or broadcast ephemeris with covariance, leave-one-out redundancy diagnostics, and robust weighting, RINEX NAV/OBS path loaders with RINEX observation to SPP assembly and solve helpers, RTK float and fixed (static, kinematic, and moving baseline), PPP float and fixed, static PPP temporal-correlation covariance with calibrated day-length bounds, optional elevation cutoff, optional tropospheric-gradient estimation, DGNSS, velocity solving, RAIM / FDE fault detection and exclusion, Huber reweighting, and DOP.
  • Integrity and error bounds. Multi-constellation ARAIM protection levels, SBAS protection levels (DO-229), per-observation reliability (minimal detectable bias, internal and external), observability classification of every solution (rank, redundancy, conditioning), and covariance-derived error metrics (CEP, R95, SEP, error ellipse) that report wide or flagged bounds for weak geometry rather than fabricated confidence. For C callers, direct post-solve integrity entry points are sidereon_raim for residual fault detection and sidereon_araim for HPL/VPL protection levels.
  • Timing, estimation, and geodesy. Allan-family clock stability with power-law noise identification (IEEE 1139), scalar Kalman and alpha-beta trackers with innovation gating, NIS/MAD/EWMA helpers, and CFAR thresholds, source localization (ToA/TDOA), station velocity (MIDAS) with trajectory fitting, step detection, and network motion fields, repeating-geometry (sidereal) filtering, geodesic direct and inverse problems (Karney), an epoch-aware terrestrial frame catalog (ITRF/ETRF Helmert sets), and EGM2008 geoid grids alongside EGM96.
  • GNSS corrections and biases. SBAS message decode and corrected solving, SSR orbit / clock / bias corrections from RTCM SSR or Galileo HAS, RTCM 3 broadcast ephemeris decode for GPS (1019), GLONASS (1020), Galileo (1045/1046), BeiDou (1042), and QZSS (1044), each real-data validated, Bias-SINEX DCB and OSB products, and DGNSS pseudorange corrections.
  • GNSS measurements. Carrier-phase combinations (wide-lane, narrow-lane, Melbourne-Wubbena, ionosphere-free), cycle-slip detection, carrier smoothing, Doppler, and C/A-code generation, correlation, and acquisition search.
  • Ephemeris and time. Broadcast ephemeris with fallback selection, SP3 precise products, JPL SPK (DAF/.bsp) sampling, one sampling contract across broadcast, precise, and SSR-corrected sources, solving with precise-to-broadcast fallback and staleness reporting, scale-aware time conversions, and Earth-orientation handling.
  • Orbital mechanics. Classical, equinoctial, and modified-equinoctial element conversions, anomaly conversions, Lambert transfer solutions, initial orbit determination (Gauss, Gibbs, Herrick-Gibbs), and relative motion in RSW / RTN / RIC / LVLH frames with Clohessy-Wiltshire propagation.
  • Geometry and events. Coordinate-frame transforms, look angles, eclipse, conjunction screening with collision probability, and angular measures: separation, position angle, phase angle, beta angle, parallactic angle.
  • Observation and almanac. Astrometric and apparent places (RA/Dec, azimuth/elevation with optional refraction, aberration, and light deflection) for the Sun, Moon, and any SPK body, sub-solar and sub-observer points, Moon rise/set and transit finding, satellite visual magnitude, and almanac events: seasons, moon phases, meridian transits, lunar and solar eclipses, and planetary events.
  • Atmosphere. Klobuchar and NeQuick-G ionosphere, IONEX maps, troposphere delay models, and NRLMSISE-00 density.
  • Terrain and geoid. DTED elevation lookup on tiles you supply, batch queries, memory-mappable terrain stores, EGM96 and EGM2008 geoid grids, PROJ-compatible EGM96 GTX interpolation with explicit fused/separate arithmetic and typed coordinate errors, typed terrain/geoid labels, and orthometric / ellipsoidal height conversion.
  • RF. Link-budget computation.
  • GNSS/INS fusion. Strapdown mechanization with an error-state EKF (UKF option), loose and tight coupling, IGG-III loose updates, an RTS smoother, a serializable filter state, and field mode (zero-velocity and zero-angular-rate updates, non-holonomic constraints, per-fix-status weighting, IMU-to-body mounting matrix), all off by default.
  • Reference-station static solve. Rover and reference observations in, one station coordinate with covariance and typed per-mode errors out.
  • Scenario simulation. Deterministic synthetic observables and a ground-truth error ledger from a versioned scenario; identical bytes for the same scenario and seed.
  • Signal analysis. Canonical sidereon_signal_* helpers for closed-form BPSK/BOC spectra, spectral separation coefficients, DLL jitter, and multipath error envelopes against published constants.
  • Formats. Parsing and serialization for TLE/OMM, CCSDS (OEM/OPM/CDM/TDM), RINEX (observation, navigation, clock), CRINEX, SP3, IONEX, ANTEX, Bias-SINEX, RTCM 3, and SBAS messages.

Every result is exactly what the engine computes; the binding adds no modeling of its own, and no data acquisition either: every product it consumes (orbit files, bias products, ionosphere maps, terrain tiles) arrives as a buffer or file you supply.

How it's validated

bindings/c/tests/run_smoke.sh builds the library, regenerates the header, compiles a suite of C programs against it, and runs them on committed reference fixtures, asserting the binding reproduces the engine's reference numbers bit-exact. CI also runs bindings/c/tests/run_ci_smoke.sh on Linux and macOS; that network-free gate checks the committed header byte-for-byte and executes the focused data-distribution, exact-SP3, and RINEX observation-QC ABI programs.

Other interfaces

sidereon is one validated engine with first-class interfaces in several languages, all returning the same numbers:

See the live demo and docs at sidereon.dev.

License

MIT. See LICENSE.