@neilberkman/sidereon 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,16 @@
1
- # sidereon
1
+ # @neilberkman/sidereon
2
2
 
3
- GNSS and astrodynamics for JavaScript and TypeScript: propagate satellites,
4
- predict passes, solve precise positions (SPP / RTK / PPP), and convert between
5
- coordinate frames and time the real engine, compiled to WebAssembly, running
6
- in the browser or Node — checked against the references the field trusts
7
- (Vallado, Skyfield, IGS, IERS).
3
+ GNSS and astrodynamics in the browser and in Node: propagate satellites, predict
4
+ passes, solve precise positions (SPP / RTK / PPP), and convert between coordinate
5
+ frames and time scales. This is the JavaScript and TypeScript interface to the
6
+ sidereon engine.
8
7
 
9
- This isn't a reimplementation in JavaScript. It's the same Rust engine that
10
- backs the Python, C, and Elixir interfaces, compiled to WebAssembly so a
11
- browser tab gets bit-for-bit the same numbers as the native builds, with no
12
- server round-trip and no native add-on to install.
8
+ The engine is written in Rust and compiled to WebAssembly, so a browser tab or a
9
+ Node process gets the same numbers the native builds produce, with no server
10
+ round-trip and no native add-on to install. Results are reference-validated: the
11
+ SGP4 propagator is a bit-exact port of Vallado's reference implementation, frames
12
+ and time are checked against Skyfield and IERS, and the positioning stack is
13
+ checked against IGS products.
13
14
 
14
15
  ## Install
15
16
 
@@ -17,145 +18,135 @@ server round-trip and no native add-on to install.
17
18
  npm install @neilberkman/sidereon
18
19
  ```
19
20
 
20
- It's a dual ESM/CJS package shipping prebuilt wasm works in bundlers,
21
- browsers, and Node. TypeScript declarations are bundled. There are two entry
22
- points and they init differently:
21
+ The package is dual ESM/CJS and ships prebuilt wasm with bundled TypeScript
22
+ declarations. The two entry points initialize differently:
23
23
 
24
24
  - **Browser / bundler (ESM):** import the default `init`, `await` it once, then
25
25
  call the API. `init()` fetches the `.wasm` for you.
26
26
  - **Node (CommonJS):** `require(...)` loads and initializes the wasm at require
27
- time there is no init step.
27
+ time, so there is no init step and everything is ready synchronously.
28
28
 
29
- ## Quickstart: when does the ISS fly over you?
29
+ ## Example: propagate a TLE
30
30
 
31
- No data files, no setup — give it a two-line element set and a ground station,
32
- and ask when the satellite is above the horizon.
31
+ Parse a two-line element set, run SGP4, and take look angles (azimuth, elevation,
32
+ range) from a ground station. No data files, no setup.
33
33
 
34
34
  ```js
35
35
  import init, { Tle, GroundStation } from "@neilberkman/sidereon";
36
36
 
37
- await init(); // browser/bundler: fetches the wasm. (Node CJS: see below — no init.)
37
+ await init();
38
38
 
39
- // Real orbital elements (grab fresh ones from CelesTrak any time).
40
- const iss = new Tle(
41
- "1 25544U 98067A 26178.50947090 .00006280 00000+0 12016-3 0 9996",
42
- "2 25544 51.6322 248.9966 0004278 238.4942 121.5629 15.49454046573359",
39
+ const tle = new Tle(
40
+ "1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9009",
41
+ "2 25544 51.6400 208.8657 0002644 250.3037 109.7782 15.49560812999990",
43
42
  );
43
+ const station = new GroundStation(51.5, -0.1, 10.0);
44
44
 
45
- // A ground station: latitude, longitude in degrees (altitude in metres, optional).
46
- const berkeley = new GroundStation(37.87, -122.27);
47
-
48
- // Epochs are UTC unix microseconds and cross as BigInt.
49
- const us = (ms) => BigInt(ms) * 1000n;
50
- const now = Date.now();
51
-
52
- // Every pass above 10° over the next 24 hours.
53
- const passes = iss.findPasses(berkeley, us(now), us(now + 24 * 3600 * 1000), 10.0);
54
-
55
- const at = (uus) => new Date(Number(uus / 1000n)).toISOString().slice(11, 16);
56
- for (const p of passes) {
57
- console.log(`${at(p.aosUnixUs)} UTC · ${(p.durationS / 60).toFixed(1)} min · peak ${p.maxElevationDeg.toFixed(0)}°`);
58
- }
59
- ```
60
-
61
- ```
62
- 08:30 UTC · 6.8 min · peak 88°
63
- 10:09 UTC · 4.3 min · peak 16°
64
- 13:25 UTC · 3.2 min · peak 13°
65
- 15:01 UTC · 6.6 min · peak 56°
66
- 16:39 UTC · 3.5 min · peak 14°
45
+ // Epochs are unix microseconds as BigInt64Array.
46
+ const t = BigInt(Date.UTC(2024, 0, 1, 12)) * 1000n;
47
+ const look = tle.lookAngles(station, new BigInt64Array([t]));
48
+ console.log(look.azimuthDeg[0], look.elevationDeg[0], look.rangeKm[0]);
67
49
  ```
68
50
 
69
51
  `Tle` also gives you `propagate(epochs)` (TEME position/velocity over a
70
- `BigInt64Array` of unix-microsecond epochs) and `lookAngles(station, epochs)`
71
- (azimuth / elevation / range over a time grid).
52
+ `BigInt64Array` of unix-microsecond epochs) and `findPasses(station, start, end,
53
+ minElevationDeg)` for visibility windows.
72
54
 
73
55
  ### Node (CommonJS)
74
56
 
75
- `require` resolves to the Node build, which loads the wasm at require time — no
76
- init call, everything is ready synchronously:
57
+ `require` resolves to the Node build, which initializes the wasm at require time:
77
58
 
78
59
  ```js
79
60
  const { Tle, GroundStation } = require("@neilberkman/sidereon");
80
61
 
81
- const iss = new Tle(line1, line2);
82
- const passes = iss.findPasses(new GroundStation(37.87, -122.27), startUs, endUs, 10.0);
62
+ const tle = new Tle(line1, line2);
63
+ const look = tle.lookAngles(new GroundStation(51.5, -0.1, 10.0), epochs);
83
64
  ```
84
65
 
85
- (Node ESM resolves the same default-`init` build as the browser; `await init()`
86
- needs the wasm bytes there — `await init({ module_or_path: await readFile(new
87
- URL("...sidereon_bg.wasm", import.meta.url)) })`. If you're not sure, `require`
88
- is the simplest path in Node.)
66
+ ## Example: precise positioning
89
67
 
90
- ## Precise positioning
91
-
92
- The positioning engine is the other half of the library: load a precise
93
- ephemeris, hand it pseudoranges, and get a least-squares fix back.
68
+ Load a precise SP3 ephemeris, hand it pseudoranges, and get a least-squares fix
69
+ back.
94
70
 
95
71
  ```js
96
- import { loadSp3 } from "@neilberkman/sidereon"; // (after init)
97
- import type { SppRequest } from "@neilberkman/sidereon/types";
72
+ import init, { loadSp3 } from "@neilberkman/sidereon";
73
+
74
+ await init();
98
75
 
99
- const sp3 = loadSp3(new Uint8Array(sp3FileBytes));
76
+ // SP3-c precise orbits, as bytes (read from a file, fetch, or string).
77
+ const sp3 = loadSp3(new TextEncoder().encode(sp3Text));
100
78
 
101
- const fix = sp3.solveSpp({
79
+ // GPS L1 pseudoranges (m) for the satellites in view at the epoch.
80
+ const solution = sp3.solveSpp({
102
81
  observations: [
103
- { satelliteId: "G01", pseudorangeM: 21_000_123.4 },
104
- { satelliteId: "G08", pseudorangeM: 22_517_889.1 },
105
- // ...more satellites
82
+ { satelliteId: "G08", pseudorangeM: 23825519.8 },
83
+ { satelliteId: "G10", pseudorangeM: 22717690.1 },
84
+ { satelliteId: "G16", pseudorangeM: 20478653.4 },
85
+ { satelliteId: "G18", pseudorangeM: 21768335.2 },
86
+ { satelliteId: "G20", pseudorangeM: 21248327.7 },
87
+ { satelliteId: "G21", pseudorangeM: 20808709.8 },
106
88
  ],
107
- tRxJ2000S: 649_728_000, // receive epoch, seconds since J2000
108
- tRxSecondOfDayS: 86_400,
109
- dayOfYear: 176,
110
- corrections: { ionosphere: true, troposphere: true },
89
+ tRxJ2000S: 646272000.0,
90
+ tRxSecondOfDayS: 43200.0,
91
+ dayOfYear: 176.5,
92
+ initialGuess: [4.5e6, 0.5e6, 4.5e6, 0],
93
+ corrections: { ionosphere: false, troposphere: false },
111
94
  withGeodetic: true,
112
95
  });
113
96
 
114
- console.log(fix.positionM); // Float64Array [x, y, z] ECEF metres
115
- console.log(fix.geodetic); // Float64Array [latRad, lonRad, heightM]
116
- console.log(fix.usedSats); // satellites that contributed
97
+ console.log(solution.positionM); // ECEF m ~ [4484128, 550582, 4487561]
98
+ console.log(solution.rxClockS); // receiver clock bias, seconds
117
99
  ```
118
100
 
119
101
  `solveRtkFloat` / `solveRtkFixed` and `solvePppFloat` / `solvePppFixed` follow
120
- the same shape an options object in, a result object with `Float64Array`
102
+ the same shape: an options object in, a result object with `Float64Array`
121
103
  positions and scalar attributes out.
122
104
 
123
- ## What's in the box
124
-
125
- The wasm surface is broad the same domains the native engine exposes:
126
-
127
- - **Orbits** SGP4/TLE and OMM, numerical propagation, passes, look angles, visibility
128
- - **Frames & time** — TEME ↔ GCRS ↔ ITRS, geodetic ↔ ECEF, `Instant` with GMST/GAST and the resolved TT/UT1/TDB scales
129
- - **Bodies** Sun/Moon positions, eclipse / shadow geometry, plus JPL SPK (DAF/.bsp) kernels
130
- - **Positioning** — SPP, RTK (float/fixed), PPP (float/fixed), DGNSS, DOP, velocity, RAIM/FDE
131
- - **GNSS data** SP3 (load/interpolate/merge), RINEX (obs/nav/clock), CRINEX (Hatanaka), ANTEX, IONEX slant delay, broadcast ephemeris
132
- - **Space situational awareness** conjunction/TCA screening, collision probability, CDM, covariance transforms
133
- - **RF / signal** — link budget (FSPL, EIRP, C/N0, antenna gain), C/A code, acquisition/correlation
105
+ ## Capabilities
106
+
107
+ The wasm surface mirrors the full breadth of the engine:
108
+
109
+ - **Orbit propagation:** SGP4 from TLE and OMM, numerical propagation, batch
110
+ constellation propagation, pass prediction, look angles, coverage.
111
+ - **GNSS positioning:** SPP, RTK (float/fixed), PPP (float/fixed), DGNSS, RAIM/FDE,
112
+ DOP, velocity.
113
+ - **Ephemeris and time:** broadcast ephemeris and SP3 (load/interpolate/merge),
114
+ JPL SPK (DAF/.bsp) kernels, scale-aware time (`Instant` with GMST/GAST and
115
+ resolved TT/UT1/TDB), Earth orientation parameters.
116
+ - **Geometry and events:** reference frames (TEME, GCRS, ITRS, geodetic, ECEF),
117
+ look angles, eclipse and shadow geometry, conjunction screening with collision
118
+ probability, initial orbit determination, orbital elements.
119
+ - **Atmosphere:** Klobuchar and NeQuick-G ionosphere, IONEX slant delay,
120
+ troposphere models.
121
+ - **RF link budget:** free-space path loss, EIRP, C/N0, antenna gain.
122
+ - **Format parsing and serialization:** TLE/OMM, CCSDS, RINEX, CRINEX (Hatanaka),
123
+ SP3, IONEX, ANTEX, RTCM.
134
124
 
135
125
  The binding adds no modeling of its own: every result is exactly what the engine
136
- computes. Failures surface as the JS exception you'd expect `Error` for engine
137
- rejections (parse failures, non-converging solves, SGP4 error codes), `TypeError`
138
- for malformed input, `RangeError` for out-of-domain numbers. Full signatures
139
- live in the bundled TypeScript declarations (`sidereon.d.ts`), with the
140
- plain-object request shapes in `@neilberkman/sidereon/types`.
126
+ computes. Failures surface as the JS exception you would expect (`Error` for
127
+ engine rejections such as parse failures, non-converging solves, and SGP4 error
128
+ codes; `TypeError` for malformed input; `RangeError` for out-of-domain numbers).
129
+ Full signatures live in the bundled TypeScript declarations (`sidereon.d.ts`),
130
+ with the plain-object request shapes in `@neilberkman/sidereon/types`.
141
131
 
142
132
  A few conventions worth knowing: positions and state arrays cross as
143
133
  `Float64Array` (multi-epoch arrays are flat row-major, `3 * epochCount`); SGP4
144
134
  epoch grids are `BigInt64Array` of unix microseconds; SP3 query epochs are plain
145
135
  numbers in seconds since J2000.
146
136
 
147
- ## Other languages
137
+ ## Live demo
138
+
139
+ The interactive demo at [sidereon.dev](https://sidereon.dev) runs on this exact
140
+ package: every computation happens client-side in your browser via this wasm
141
+ build.
148
142
 
149
- sidereon is one validated engine with first-class interfaces in **Rust**,
150
- **Python**, **C**, **Elixir**, and **WebAssembly** — same numbers everywhere.
151
- See the live demo and docs at [sidereon.dev](https://sidereon.dev).
143
+ ## Links
152
144
 
153
- ## How it's validated
145
+ - **Engine and core repo:** https://github.com/neilberkman/sidereon
146
+ - **Live demo:** https://sidereon.dev
147
+ - **Sibling interfaces:** sidereon-python (PyPI), sidereon-c, sidereon-ex (Hex).
148
+ One validated engine, the same numbers in every language.
154
149
 
155
- The SGP4 propagator is a Rust port of David Vallado's reference implementation,
156
- bit-exact to it. Frames and time are checked against Skyfield and IERS; the
157
- positioning stack is checked against IGS products. The published package ships
158
- the prebuilt wasm, so there's nothing to compile to use it.
150
+ ## License
159
151
 
160
- *Building from source (for contributors): `npm run build` (runs `wasm-pack
161
- build` for the `web` and `nodejs` targets). Tests: `npm test`.*
152
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neilberkman/sidereon",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "WebAssembly / JavaScript interface over the sidereon GNSS + astrodynamics engine: SP3 loading and multi-product merge, SPP / RTK (float + fixed) / PPP (float + fixed) positioning, IONEX slant delay, SGP4 and numerical orbit propagation, RINEX observation/navigation/clock parsing, CRINEX (Hatanaka) decoding, observable-domain math (carrier combinations, cycle slips, Hatch smoothing, weighting, velocity, GPS C/A signal), and the astro domain (scale-tagged time, frame transforms, Sun/Moon ephemerides, eclipse / body angles, DOP, passes / visibility, conjunction + collision probability, CDM / OMM / ANTEX parsing, RF link budget)",
5
5
  "type": "module",
6
6
  "license": "MIT",