gnss-js 1.66.0 → 2.0.0
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 +144 -132
- package/dist/{chunk-REULRNJ5.js → chunk-WVTDVPKA.js} +151 -188
- package/dist/{ephemeris-Ohl6NAfw.d.cts → ephemeris-BAnBLLB9.d.cts} +0 -1
- package/dist/{ephemeris-Ohl6NAfw.d.ts → ephemeris-BAnBLLB9.d.ts} +0 -1
- package/dist/index.cjs +152 -188
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -3
- package/dist/orbit.d.cts +1 -1
- package/dist/orbit.d.ts +1 -1
- package/dist/positioning.d.cts +1 -1
- package/dist/positioning.d.ts +1 -1
- package/dist/rtcm3.cjs +181 -217
- package/dist/rtcm3.d.cts +28 -15
- package/dist/rtcm3.d.ts +28 -15
- package/dist/rtcm3.js +5 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# gnss-js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A comprehensive, dependency-free GNSS toolkit for JavaScript and TypeScript — from raw receiver bytes to a positioning solution, and everything between. Runs in Node.js, browsers, Deno, Bun, and Workers.
|
|
4
|
+
|
|
5
|
+
It decodes the formats a GNSS workflow actually meets in the wild (RINEX, RTCM 2/3, and the Septentrio/u-blox/NovAtel/Trimble/BINEX binary logs), reconstructs broadcast navigation down to the raw subframe, and solves positions from single-point all the way to PPP with integer ambiguity resolution — with consistent units and explicit JSDoc throughout.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -8,169 +10,192 @@ Comprehensive GNSS library for JavaScript and TypeScript. Zero dependencies. Wor
|
|
|
8
10
|
npm install gnss-js
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
Everything is exposed through subpath exports (`gnss-js/rinex`, `gnss-js/positioning`, …), so bundlers only pull in what you import.
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## What's inside
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
- **Formats in** — RINEX 2/3/4 obs + nav (plain, Hatanaka `.crx`, `.gz`), SP3, RINEX CLK, IONEX, SINEX bias; RTCM3 (MSM + legacy obs, ephemeris, SSR/IGS-SSR, station); RTCM 2.x; and the receiver binaries SBF, UBX, NovAtel OEM4, Trimble, BINEX.
|
|
18
|
+
- **Formats out** — RINEX 2/3/4 observation + navigation writers and Hatanaka (CRINEX) compression.
|
|
19
|
+
- **Navigation** — Keplerian/GLONASS ephemeris handling plus raw-frame decoding of GPS LNAV/CNAV, Galileo I/NAV + F/NAV, BeiDou D1/D2, GLONASS strings, and Galileo HAS (E6 C/NAV) corrections.
|
|
20
|
+
- **Orbits** — satellite position/velocity, Doppler, DOP, azimuth/elevation, pass prediction and almanac propagation.
|
|
21
|
+
- **Positioning** — SPP, static and kinematic float PPP, PPP-AR (LAMBDA + wide-/narrow-lane FCB estimation), post-processed RTK, DGNSS, and SBAS/DFMC augmentation.
|
|
22
|
+
- **Quality** — multipath, cycle slips, completeness and slant-TEC ionosphere analysis, with SINEX-DCB calibration.
|
|
23
|
+
- **Live** — NTRIP client and a self-detecting RTCM/receiver stream stats accumulator.
|
|
24
|
+
- **Foundations** — GNSS time scales, ECEF/geodetic coordinates, 14-parameter reference-frame transforms, signal/frequency definitions and physical constants.
|
|
16
25
|
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
getGpsTime,
|
|
20
|
-
getTimeOfWeek,
|
|
21
|
-
getWeekNumber,
|
|
22
|
-
getDateFromGpsData,
|
|
23
|
-
} from 'gnss-js/time';
|
|
24
|
-
import { getGalTime, getBdsTime, getUnixTime } from 'gnss-js/time';
|
|
25
|
-
import { getJulianDate, getMJD } from 'gnss-js/time';
|
|
26
|
-
import { getLeap, getGpsLeap, getUtcDate } from 'gnss-js/time';
|
|
27
|
-
|
|
28
|
-
const date = new Date('2024-01-15T12:00:00Z');
|
|
29
|
-
const gpsTime = getGpsTime(date); // milliseconds since GPS epoch
|
|
30
|
-
const tow = getTimeOfWeek(date); // milliseconds into current GPS week
|
|
31
|
-
const week = getWeekNumber(date); // GPS week number
|
|
32
|
-
```
|
|
26
|
+
## Usage
|
|
33
27
|
|
|
34
|
-
###
|
|
28
|
+
### Time, coordinates & reference frames
|
|
35
29
|
|
|
36
30
|
```ts
|
|
31
|
+
import { getGpsTime, getTimeOfWeek, getWeekNumber } from 'gnss-js/time';
|
|
37
32
|
import {
|
|
38
33
|
geodeticToEcef,
|
|
39
34
|
ecefToGeodetic,
|
|
40
35
|
vincenty,
|
|
41
36
|
deg2rad,
|
|
42
37
|
} from 'gnss-js/coordinates';
|
|
38
|
+
import { transformFrame } from 'gnss-js/frames';
|
|
43
39
|
|
|
40
|
+
const gpsTime = getGpsTime(new Date()); // ms since GPS epoch
|
|
44
41
|
const [x, y, z] = geodeticToEcef(deg2rad(48.8566), deg2rad(2.3522), 35);
|
|
45
|
-
const { distance } = vincenty(
|
|
46
|
-
deg2rad(51.5),
|
|
47
|
-
deg2rad(-0.1),
|
|
48
|
-
deg2rad(48.9),
|
|
49
|
-
deg2rad(2.4)
|
|
50
|
-
);
|
|
51
|
-
// distance in meters, angles in radians
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Visibility / pass prediction
|
|
55
42
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const vis = computeVisibility(ephemerides, rxEcef, startMs, endMs, 300, 10);
|
|
60
|
-
// vis.passes: rise/peak/set per satellite; vis.pdop / vis.visibleCount
|
|
61
|
-
// per epoch; vis.elevation[prn]: elevation timeline for plotting.
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### Single-point positioning
|
|
65
|
-
|
|
66
|
-
```ts
|
|
67
|
-
import { solveSpp, ionoFree } from 'gnss-js/positioning';
|
|
68
|
-
|
|
69
|
-
const sol = solveSpp(pseudoranges, ephemerides, epochMs);
|
|
70
|
-
// sol.position (ECEF m), per-system clock biases, DOP, residuals,
|
|
71
|
-
// rejected outliers. ~10 m single-frequency; use ionoFree(p1, p2, f1, f2)
|
|
72
|
-
// pseudoranges for metre-level.
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Reference frame transformations
|
|
76
|
-
|
|
77
|
-
```ts
|
|
78
|
-
import { transformFrame, dateToEpoch } from 'gnss-js/frames';
|
|
79
|
-
|
|
80
|
-
// ETRS89 (ETRF2000) station coordinates → ITRF2020 at epoch 2010.5
|
|
43
|
+
// Time-dependent 14-parameter Helmert (IERS/EPSG): ITRF88–ITRF2020,
|
|
44
|
+
// ETRF2000/2014/2020, NAD83(2011). 'WGS84' aliases ITRF2020.
|
|
81
45
|
const itrf = transformFrame(
|
|
82
|
-
[3924687.
|
|
46
|
+
[3924687.7, 301132.8, 5001910.8],
|
|
83
47
|
'ETRF2000',
|
|
84
48
|
'ITRF2020',
|
|
85
49
|
2010.5
|
|
86
50
|
);
|
|
87
|
-
// Time-dependent 14-parameter Helmert (IERS/EPSG parameters);
|
|
88
|
-
// supports ITRF88–ITRF2020, ETRF2000/2014/2020, NAD83(2011).
|
|
89
|
-
// 'WGS84' is treated as an ITRF2020 alias (standard GNSS practice).
|
|
90
51
|
```
|
|
91
52
|
|
|
92
|
-
### RINEX
|
|
53
|
+
### RINEX — read, write & products
|
|
93
54
|
|
|
94
55
|
```ts
|
|
95
|
-
import {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
56
|
+
import {
|
|
57
|
+
parseRinexStream,
|
|
58
|
+
parseNavFile,
|
|
59
|
+
parseSp3,
|
|
60
|
+
parseClk,
|
|
61
|
+
parseIonex,
|
|
62
|
+
writeRinexObsBlob,
|
|
63
|
+
writeRinexNav,
|
|
64
|
+
writeCrx,
|
|
65
|
+
} from 'gnss-js/rinex';
|
|
66
|
+
|
|
67
|
+
// Streaming observation parser — plain, Hatanaka .crx, or .gz
|
|
68
|
+
const { header } = await parseRinexStream(
|
|
69
|
+
file,
|
|
70
|
+
(pct) => {}, // optional progress
|
|
101
71
|
undefined, // optional AbortSignal
|
|
102
72
|
(time, prn, codes, values) => {
|
|
103
|
-
|
|
73
|
+
/* per-satellite obs callback */
|
|
104
74
|
}
|
|
105
75
|
);
|
|
106
|
-
// result.header: RinexHeader
|
|
107
76
|
|
|
108
|
-
//
|
|
109
|
-
const
|
|
110
|
-
|
|
77
|
+
const nav = parseNavFile(navText); // KeplerEphemeris[] | GlonassEphemeris[]
|
|
78
|
+
const sp3 = parseSp3(sp3Text); // precise orbits
|
|
79
|
+
const clk = parseClk(clkText); // precise clocks
|
|
80
|
+
|
|
81
|
+
// Writers: RINEX 2/3/4 obs + nav, and Hatanaka compression
|
|
82
|
+
const rnx = writeRinexObsBlob(header, epochs);
|
|
83
|
+
const crx = writeCrx(rnx); // → CRINEX
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Receiver binary formats
|
|
87
|
+
|
|
88
|
+
One decoder per receiver family; each returns measurement epochs, and a companion nav parser reconstructs broadcast ephemeris from the raw frames.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { parseSbfMeas, parseSbfNav, parseSbfHas } from 'gnss-js/sbf'; // Septentrio
|
|
92
|
+
import { parseUbxRawx, parseUbxRawNav } from 'gnss-js/ubx'; // u-blox
|
|
93
|
+
import { parseNovatelRange, parseNovatelNav } from 'gnss-js/novatel'; // NovAtel OEM4
|
|
94
|
+
import { parseTrimble, parseTrimbleNav } from 'gnss-js/trimble'; // Trimble
|
|
95
|
+
import { parseBinex } from 'gnss-js/binex'; // BINEX
|
|
96
|
+
|
|
97
|
+
const sbf = parseSbfMeas(bytes); // measurement epochs + counts
|
|
98
|
+
const nav = parseSbfNav(bytes); // GPS/GAL/BDS/GLO ephemeris from raw blocks
|
|
99
|
+
const has = parseSbfHas(bytes); // Galileo HAS (E6 C/NAV) SSR corrections
|
|
111
100
|
```
|
|
112
101
|
|
|
113
|
-
### RTCM3
|
|
102
|
+
### RTCM (RTCM3, RTCM2, SSR)
|
|
114
103
|
|
|
115
104
|
```ts
|
|
116
|
-
import {
|
|
105
|
+
import {
|
|
106
|
+
Rtcm3Decoder,
|
|
107
|
+
decodeObs,
|
|
108
|
+
decodeEphemeris,
|
|
109
|
+
decodeSsr,
|
|
110
|
+
decodeIgsSsr,
|
|
111
|
+
updateStreamStats,
|
|
112
|
+
createStreamStats,
|
|
113
|
+
} from 'gnss-js/rtcm3';
|
|
117
114
|
|
|
118
115
|
const decoder = new Rtcm3Decoder();
|
|
119
|
-
decoder.addData(chunk); // Uint8Array from NTRIP stream
|
|
116
|
+
decoder.addData(chunk); // Uint8Array from an NTRIP stream
|
|
120
117
|
for (const frame of decoder) {
|
|
121
|
-
const
|
|
122
|
-
const eph = decodeEphemeris(frame); // broadcast ephemeris
|
|
118
|
+
const obs = decodeObs(frame); // ObsEpoch | null — MSM4-7 or legacy 1001-1012
|
|
119
|
+
const eph = decodeEphemeris(frame); // broadcast ephemeris (all constellations)
|
|
120
|
+
const ssr = decodeSsr(frame); // orbit/clock/bias corrections (1057-1068)
|
|
121
|
+
const igs = decodeIgsSsr(frame); // IGS-SSR 4076 (multi-GNSS + phase bias + iono)
|
|
123
122
|
}
|
|
124
123
|
```
|
|
125
124
|
|
|
126
|
-
### Orbit computation
|
|
127
|
-
|
|
128
125
|
```ts
|
|
129
|
-
import {
|
|
126
|
+
import { Rtcm2Decoder, rtcm2Observation, rtcm2Station } from 'gnss-js/rtcm2';
|
|
130
127
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// positions.positions[prn][epochIdx]: { lat, lon, az, el } | null
|
|
128
|
+
// Legacy RTCM 2.x — a wholly different wire format (30-bit words, 6-of-8)
|
|
129
|
+
const frames = new Rtcm2Decoder().decode(bytes);
|
|
134
130
|
```
|
|
135
131
|
|
|
136
|
-
###
|
|
132
|
+
### Orbits, visibility & Doppler
|
|
137
133
|
|
|
138
134
|
```ts
|
|
139
|
-
import {
|
|
135
|
+
import {
|
|
136
|
+
computeAllPositions,
|
|
137
|
+
computeVisibility,
|
|
138
|
+
computeDop,
|
|
139
|
+
dopplerHz,
|
|
140
|
+
} from 'gnss-js/orbit';
|
|
140
141
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
//
|
|
142
|
+
const positions = computeAllPositions(ephemerides, times, rxEcef);
|
|
143
|
+
const vis = computeVisibility(ephemerides, rxEcef, startMs, endMs, 300, 10);
|
|
144
|
+
// vis.passes: rise/peak/set; vis.pdop / vis.visibleCount per epoch;
|
|
145
|
+
// vis.elevation[prn]: elevation timeline for plotting.
|
|
146
|
+
```
|
|
145
147
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
### Positioning — SPP → PPP → PPP-AR / RTK
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import {
|
|
152
|
+
solveSpp,
|
|
153
|
+
ionoFree, // single-point
|
|
154
|
+
solvePpp, // static/kinematic float PPP + optional AR
|
|
155
|
+
postProcessRtk, // rnx2rtkp-style relative positioning
|
|
156
|
+
solveDgnss, // code-differential
|
|
157
|
+
SbasProcessor, // SBAS / DFMC augmentation
|
|
158
|
+
} from 'gnss-js/positioning';
|
|
159
|
+
|
|
160
|
+
// Single-point: ~10 m single-frequency; ionoFree(p1,p2,f1,f2) for metre-level.
|
|
161
|
+
const spp = solveSpp(pseudoranges, ephemerides, epochMs);
|
|
162
|
+
|
|
163
|
+
// Precise point positioning against SP3/CLK products (dm→cm; AR to cm).
|
|
164
|
+
const ppp = solvePpp(pppEpochs, sp3, pppOptions);
|
|
165
|
+
|
|
166
|
+
// Post-processed RTK: base + rover obs, ephemeris, base ECEF → fixed track.
|
|
167
|
+
const rtk = postProcessRtk(baseObs, roverObs, ephemerides, baseEcef);
|
|
149
168
|
```
|
|
150
169
|
|
|
151
|
-
###
|
|
170
|
+
### Signal quality & ionosphere
|
|
152
171
|
|
|
153
172
|
```ts
|
|
154
|
-
import {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
//
|
|
161
|
-
//
|
|
173
|
+
import {
|
|
174
|
+
analyzeQuality,
|
|
175
|
+
parseSinexBiasDcb,
|
|
176
|
+
applyIonoDcb,
|
|
177
|
+
} from 'gnss-js/analysis';
|
|
178
|
+
|
|
179
|
+
// One re-parse pass: multipath (per band, Anubis-style), cycle slips,
|
|
180
|
+
// completeness, and slant-TEC series.
|
|
181
|
+
const q = await analyzeQuality(file, header);
|
|
182
|
+
|
|
183
|
+
// Calibrate STEC with satellite DCBs (ESA .BIA / CAS .BSX); receiver bias
|
|
184
|
+
// from the night-time floor.
|
|
185
|
+
const { result, receiverDcbTecu } = applyIonoDcb(
|
|
186
|
+
q.iono,
|
|
187
|
+
parseSinexBiasDcb(biasText, epochMs)
|
|
188
|
+
);
|
|
162
189
|
```
|
|
163
190
|
|
|
164
|
-
|
|
191
|
+
The individual accumulators (`MultipathAccumulator`, `CycleSlipAccumulator`, `CompletenessAccumulator`, `IonoAccumulator`) are exported too, for a custom `parseRinexStream` pass.
|
|
192
|
+
|
|
193
|
+
### NTRIP, NMEA & ANTEX
|
|
165
194
|
|
|
166
195
|
```ts
|
|
167
196
|
import { fetchSourcetable, connectToMountpoint } from 'gnss-js/ntrip';
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
host: 'caster.example.com',
|
|
171
|
-
port: 2101,
|
|
172
|
-
version: '2.0',
|
|
173
|
-
});
|
|
197
|
+
import { parseNmeaFile, computeStats } from 'gnss-js/nmea';
|
|
198
|
+
import { parseAntex } from 'gnss-js/antex';
|
|
174
199
|
|
|
175
200
|
const { reader, abort } = await connectToMountpoint(
|
|
176
201
|
'https://proxy.example.com',
|
|
@@ -183,28 +208,16 @@ const { reader, abort } = await connectToMountpoint(
|
|
|
183
208
|
version: '2.0',
|
|
184
209
|
}
|
|
185
210
|
);
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### NMEA and ANTEX
|
|
189
|
-
|
|
190
|
-
```ts
|
|
191
|
-
import { parseNmeaFile, computeStats } from 'gnss-js/nmea';
|
|
192
|
-
import { parseAntex } from 'gnss-js/antex';
|
|
193
211
|
|
|
194
212
|
const track = parseNmeaFile(nmeaText); // fixes, satellites, per-sentence records
|
|
195
|
-
const stats = computeStats(track.fixes); // position/HDOP/speed statistics
|
|
196
213
|
const antex = parseAntex(antexText); // antenna PCO/PCV calibrations
|
|
197
214
|
```
|
|
198
215
|
|
|
199
|
-
### Constants
|
|
216
|
+
### Constants & signals
|
|
200
217
|
|
|
201
218
|
```ts
|
|
202
|
-
import {
|
|
203
|
-
|
|
204
|
-
FREQ,
|
|
205
|
-
SYSTEM_NAMES,
|
|
206
|
-
DUAL_FREQ_PAIRS,
|
|
207
|
-
} from 'gnss-js/constants';
|
|
219
|
+
import { C_LIGHT, FREQ, SYSTEM_NAMES } from 'gnss-js/constants';
|
|
220
|
+
import { CONSTELLATIONS, computePsdDb } from 'gnss-js/signals';
|
|
208
221
|
|
|
209
222
|
FREQ['G']['1']; // 1575.42e6 Hz (GPS L1)
|
|
210
223
|
SYSTEM_NAMES['E']; // "Galileo"
|
|
@@ -212,7 +225,7 @@ SYSTEM_NAMES['E']; // "Galileo"
|
|
|
212
225
|
|
|
213
226
|
## Unit conventions
|
|
214
227
|
|
|
215
|
-
All functions use consistent units:
|
|
228
|
+
All functions use consistent units, with explicit `@param`/`@returns` JSDoc:
|
|
216
229
|
|
|
217
230
|
| Quantity | Unit |
|
|
218
231
|
| ----------- | ---------------- |
|
|
@@ -221,12 +234,11 @@ All functions use consistent units:
|
|
|
221
234
|
| Distances | **meters** |
|
|
222
235
|
| Frequencies | **Hz** |
|
|
223
236
|
|
|
224
|
-
Every exported function has JSDoc with `@param` and `@returns` annotations including explicit units.
|
|
225
|
-
|
|
226
237
|
## Roadmap
|
|
227
238
|
|
|
228
|
-
- **Class-based API** —
|
|
229
|
-
- **
|
|
239
|
+
- **Class-based API** — ergonomic wrappers (`GnssTime.fromUtc(date).gps`, `Position.fromGeodetic(...).utm`) over the functional core.
|
|
240
|
+
- **RTCM3 MSM1–3** and NavIC 1041 ephemeris decoding.
|
|
241
|
+
- **Real-time SSR/HAS application** — feeding decoded corrections into a live PPP solve.
|
|
230
242
|
|
|
231
243
|
## Development
|
|
232
244
|
|
|
@@ -238,9 +250,9 @@ pnpm run lint
|
|
|
238
250
|
pnpm run build
|
|
239
251
|
```
|
|
240
252
|
|
|
241
|
-
Without the fixtures, the
|
|
253
|
+
Without the fixtures, the data-backed suites skip silently (`describe.skipIf`), so run the fetch script before trusting a green run.
|
|
242
254
|
|
|
243
|
-
**Releasing**: bump the version and push the tag — CI publishes to npm via Trusted Publishing
|
|
255
|
+
**Releasing**: bump the version and push the tag — CI publishes to npm via Trusted Publishing and creates the GitHub Release:
|
|
244
256
|
|
|
245
257
|
```bash
|
|
246
258
|
npm version patch # or minor/major; updates package.json + creates the tag
|
|
@@ -258,5 +270,5 @@ cd ../gnsscalc && pnpm link ../gnss-js # then `pnpm run build --watch` here
|
|
|
258
270
|
|
|
259
271
|
This project is dual-licensed:
|
|
260
272
|
|
|
261
|
-
- **AGPL-3.0** —
|
|
262
|
-
- **Commercial license** —
|
|
273
|
+
- **AGPL-3.0** — free for open-source projects. Any software that uses gnss-js must also be open-sourced under a compatible license.
|
|
274
|
+
- **Commercial license** — for proprietary/closed-source use. Contact [work@miguel.es](mailto:work@miguel.es) for terms.
|