@neilberkman/sidereon 0.8.1 → 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 +123 -76
- package/package.json +18 -2
- package/pkg/sidereon.d.ts +5060 -1415
- package/pkg/sidereon.js +8715 -850
- package/pkg/sidereon_bg.wasm +0 -0
- package/pkg/sidereon_bg.wasm.d.ts +1012 -521
- package/pkg-node/sidereon.d.ts +3286 -132
- package/pkg-node/sidereon.js +8880 -852
- package/pkg-node/sidereon_bg.wasm +0 -0
- package/pkg-node/sidereon_bg.wasm.d.ts +1012 -521
- package/types/sidereon-extra.d.ts +1417 -7
package/README.md
CHANGED
|
@@ -1,105 +1,152 @@
|
|
|
1
1
|
# @neilberkman/sidereon
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
`sidereon-core`; this package only marshals across the wasm boundary.
|
|
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
|
-
The
|
|
10
|
-
|
|
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.
|
|
11
14
|
|
|
12
|
-
## Install
|
|
13
|
-
|
|
14
|
-
The published package ships the prebuilt wasm. To build from source:
|
|
15
|
+
## Install
|
|
15
16
|
|
|
16
17
|
```sh
|
|
17
|
-
|
|
18
|
-
wasm-pack build --target nodejs --out-dir pkg-node --out-name sidereon
|
|
18
|
+
npm install @neilberkman/sidereon
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
The `web` build follows the standard wasm-pack pattern: import the default
|
|
24
|
-
`init`, await it once, then call the API. In the browser `init()` fetches the
|
|
25
|
-
`.wasm` for you; in Node, hand it the bytes.
|
|
21
|
+
The package is dual ESM/CJS and ships prebuilt wasm with bundled TypeScript
|
|
22
|
+
declarations. The two entry points initialize differently:
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
- **Browser / bundler (ESM):** import the default `init`, `await` it once, then
|
|
25
|
+
call the API. `init()` fetches the `.wasm` for you.
|
|
26
|
+
- **Node (CommonJS):** `require(...)` loads and initializes the wasm at require
|
|
27
|
+
time, so there is no init step and everything is ready synchronously.
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
await init({ module_or_path: await readFile(new URL("./pkg/sidereon_bg.wasm", import.meta.url)) });
|
|
29
|
+
## Example: propagate a TLE
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
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.
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const { positionM, clockS } = sp3.interpolate("G01", epochs.slice(0, 1));
|
|
34
|
+
```js
|
|
35
|
+
import init, { Tle, GroundStation } from "@neilberkman/sidereon";
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
const solution = sp3.solveSpp({
|
|
43
|
-
observations: [{ satelliteId: "G01", pseudorangeM: 22853110.4 }, /* ... */],
|
|
44
|
-
tRxJ2000S: 649728000,
|
|
45
|
-
tRxSecondOfDayS: 86400,
|
|
46
|
-
dayOfYear: 176,
|
|
47
|
-
corrections: { ionosphere: false, troposphere: false },
|
|
48
|
-
});
|
|
49
|
-
console.log(solution.positionM, solution.rxClockS, solution.usedSats);
|
|
50
|
-
```
|
|
37
|
+
await init();
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
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",
|
|
42
|
+
);
|
|
43
|
+
const station = new GroundStation(51.5, -0.1, 10.0);
|
|
54
44
|
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
const
|
|
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]);
|
|
58
49
|
```
|
|
59
50
|
|
|
60
|
-
|
|
51
|
+
`Tle` also gives you `propagate(epochs)` (TEME position/velocity over a
|
|
52
|
+
`BigInt64Array` of unix-microsecond epochs) and `findPasses(station, start, end,
|
|
53
|
+
minElevationDeg)` for visibility windows.
|
|
61
54
|
|
|
62
|
-
|
|
63
|
-
const ionex = loadIonex(new Uint8Array(await readFile("igsg.20i")));
|
|
64
|
-
const epochs = ionex.mapEpochsJ2000S; // Float64Array, seconds since J2000
|
|
65
|
-
// receiver lat/lon deg, satellite az/el deg, epoch, carrier Hz -> metres
|
|
66
|
-
const delayM = ionex.slantDelay(37.4, -122.1, 135, 30, epochs[0], 1575.42e6);
|
|
67
|
-
```
|
|
55
|
+
### Node (CommonJS)
|
|
68
56
|
|
|
69
|
-
|
|
57
|
+
`require` resolves to the Node build, which initializes the wasm at require time:
|
|
70
58
|
|
|
71
59
|
```js
|
|
72
|
-
const
|
|
73
|
-
const t0 = BigInt(Date.now()) * 1000n; // unix microseconds
|
|
74
|
-
const epochs = new BigInt64Array([t0, t0 + 600n * 1_000_000n]);
|
|
75
|
-
|
|
76
|
-
const { positionKm, velocityKmS } = tle.propagate(epochs); // TEME, flat [x,y,z,...]
|
|
60
|
+
const { Tle, GroundStation } = require("@neilberkman/sidereon");
|
|
77
61
|
|
|
78
|
-
const
|
|
79
|
-
const
|
|
62
|
+
const tle = new Tle(line1, line2);
|
|
63
|
+
const look = tle.lookAngles(new GroundStation(51.5, -0.1, 10.0), epochs);
|
|
80
64
|
```
|
|
81
65
|
|
|
82
|
-
##
|
|
66
|
+
## Example: precise positioning
|
|
83
67
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
`TypeError` (wrong shape or an unparseable satellite token) or `RangeError`
|
|
87
|
-
(non-finite or out-of-domain numbers). Nothing traps or panics across the
|
|
88
|
-
boundary.
|
|
68
|
+
Load a precise SP3 ephemeris, hand it pseudoranges, and get a least-squares fix
|
|
69
|
+
back.
|
|
89
70
|
|
|
90
|
-
|
|
71
|
+
```js
|
|
72
|
+
import init, { loadSp3 } from "@neilberkman/sidereon";
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
row-major: `[x0, y0, z0, x1, y1, z1, ...]`, length `3 * epochCount`.
|
|
94
|
-
- Epochs are plain numbers (seconds since J2000) except SGP4 epoch grids, which
|
|
95
|
-
are `BigInt64Array` of unix microseconds.
|
|
96
|
-
- SPP positions are ECEF metres; geodetic output is `[latRad, lonRad, heightM]`.
|
|
97
|
-
- SGP4 states are TEME, kilometres and km/s.
|
|
74
|
+
await init();
|
|
98
75
|
|
|
99
|
-
|
|
76
|
+
// SP3-c precise orbits, as bytes (read from a file, fetch, or string).
|
|
77
|
+
const sp3 = loadSp3(new TextEncoder().encode(sp3Text));
|
|
78
|
+
|
|
79
|
+
// GPS L1 pseudoranges (m) for the satellites in view at the epoch.
|
|
80
|
+
const solution = sp3.solveSpp({
|
|
81
|
+
observations: [
|
|
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 },
|
|
88
|
+
],
|
|
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 },
|
|
94
|
+
withGeodetic: true,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
console.log(solution.positionM); // ECEF m ~ [4484128, 550582, 4487561]
|
|
98
|
+
console.log(solution.rxClockS); // receiver clock bias, seconds
|
|
99
|
+
```
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
`solveRtkFloat` / `solveRtkFixed` and `solvePppFloat` / `solvePppFixed` follow
|
|
102
|
+
the same shape: an options object in, a result object with `Float64Array`
|
|
103
|
+
positions and scalar attributes out.
|
|
104
|
+
|
|
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.
|
|
124
|
+
|
|
125
|
+
The binding adds no modeling of its own: every result is exactly what the engine
|
|
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`.
|
|
131
|
+
|
|
132
|
+
A few conventions worth knowing: positions and state arrays cross as
|
|
133
|
+
`Float64Array` (multi-epoch arrays are flat row-major, `3 * epochCount`); SGP4
|
|
134
|
+
epoch grids are `BigInt64Array` of unix microseconds; SP3 query epochs are plain
|
|
135
|
+
numbers in seconds since J2000.
|
|
136
|
+
|
|
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.
|
|
142
|
+
|
|
143
|
+
## Links
|
|
144
|
+
|
|
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.
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neilberkman/sidereon",
|
|
3
|
-
"version": "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",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/neilberkman/sidereon-wasm.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/neilberkman/sidereon-wasm#readme",
|
|
12
|
+
"bugs": "https://github.com/neilberkman/sidereon-wasm/issues",
|
|
7
13
|
"exports": {
|
|
8
14
|
".": {
|
|
9
15
|
"types": "./pkg/sidereon.d.ts",
|
|
@@ -28,6 +34,16 @@
|
|
|
28
34
|
],
|
|
29
35
|
"scripts": {
|
|
30
36
|
"build": "wasm-pack build --target web --out-dir pkg --out-name sidereon && wasm-pack build --target nodejs --out-dir pkg-node --out-name sidereon && node scripts/postbuild.mjs",
|
|
31
|
-
"test": "node --test"
|
|
37
|
+
"test": "node --test",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"lint": "eslint . && prettier --check \"test/**/*.mjs\" \"scripts/**/*.mjs\" \"*.json\" \"eslint.config.js\"",
|
|
40
|
+
"format": "prettier --write \"test/**/*.mjs\" \"scripts/**/*.mjs\" \"*.json\" \"eslint.config.js\""
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@eslint/js": "^10.0.1",
|
|
44
|
+
"eslint": "^10.6.0",
|
|
45
|
+
"globals": "^17.7.0",
|
|
46
|
+
"prettier": "^3.9.1",
|
|
47
|
+
"typescript": "^6.0.3"
|
|
32
48
|
}
|
|
33
49
|
}
|