gnss-js 2.10.1 → 2.12.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 +16 -1
- package/dist/analysis.cjs +24 -25
- package/dist/analysis.js +2 -2
- package/dist/{clk-DGVBGRzA.d.cts → bias-sinex-OGMw9xEZ.d.cts} +66 -1
- package/dist/{clk-DGVBGRzA.d.ts → bias-sinex-OGMw9xEZ.d.ts} +66 -1
- package/dist/binex.d.cts +1 -1
- package/dist/binex.d.ts +1 -1
- package/dist/{chunk-56HM5B6T.js → chunk-2DJ4XENO.js} +177 -9
- package/dist/{chunk-GH7MYXON.js → chunk-2KQMCJ5Z.js} +7 -5
- package/dist/chunk-E3Y6YKQM.js +65 -0
- package/dist/{chunk-3U5AX7PY.js → chunk-FEABDV5P.js} +24 -25
- package/dist/{chunk-WQX354KD.js → chunk-H3AKFD25.js} +1 -1
- package/dist/{chunk-YUW33VXK.js → chunk-HUXQABJI.js} +2 -0
- package/dist/{chunk-74IVYOQV.js → chunk-HWHD5E6V.js} +128 -78
- package/dist/{chunk-KUSUN7DP.js → chunk-MFCPYUEI.js} +1 -1
- package/dist/{chunk-3KSDL3CO.js → chunk-NIJ6WFQJ.js} +6 -321
- package/dist/chunk-NL2ORX75.js +319 -0
- package/dist/{chunk-K3GLUWZI.js → chunk-RWFJEVLJ.js} +590 -296
- package/dist/{chunk-SEYH4BEN.js → chunk-SZP4TLYR.js} +2 -2
- package/dist/chunk-TWGZTRBL.js +323 -0
- package/dist/index.cjs +1315 -460
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +20 -12
- package/dist/{nav-DImXUdbp.d.cts → nav-COp0Vmlw.d.cts} +83 -2
- package/dist/{nav-DImXUdbp.d.ts → nav-COp0Vmlw.d.ts} +83 -2
- package/dist/{nav-BYmYoh9A.d.cts → nav-CvZXmumD.d.cts} +1 -1
- package/dist/{nav-CN8rxL4h.d.ts → nav-D2DFlVpa.d.ts} +1 -1
- package/dist/novatel.d.cts +1 -1
- package/dist/novatel.d.ts +1 -1
- package/dist/novatel.js +3 -3
- package/dist/orbit.d.cts +2 -2
- package/dist/orbit.d.ts +2 -2
- package/dist/osnma.cjs +1336 -0
- package/dist/osnma.d.cts +273 -0
- package/dist/osnma.d.ts +273 -0
- package/dist/osnma.js +1216 -0
- package/dist/positioning.cjs +585 -291
- package/dist/positioning.d.cts +116 -6
- package/dist/positioning.d.ts +116 -6
- package/dist/positioning.js +3 -3
- package/dist/rinex.cjs +210 -36
- package/dist/rinex.d.cts +10 -55
- package/dist/rinex.d.ts +10 -55
- package/dist/rinex.js +6 -3
- package/dist/sbf.cjs +420 -24
- package/dist/sbf.d.cts +46 -5
- package/dist/sbf.d.ts +46 -5
- package/dist/sbf.js +12 -6
- package/dist/trimble.d.cts +1 -1
- package/dist/trimble.d.ts +1 -1
- package/dist/ubx.d.cts +1 -1
- package/dist/ubx.d.ts +1 -1
- package/dist/ubx.js +5 -4
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -16,7 +16,8 @@ Everything is exposed through subpath exports (`gnss-js/rinex`, `gnss-js/positio
|
|
|
16
16
|
|
|
17
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
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.
|
|
19
|
+
- **Navigation** — Keplerian/GLONASS ephemeris handling plus raw-frame decoding of GPS LNAV/CNAV, Galileo I/NAV + F/NAV, BeiDou D1/D2 and B-CNAV1/2/3 (B1C/B2a/B2b), GLONASS strings, and Galileo HAS (E6 C/NAV) corrections.
|
|
20
|
+
- **Authentication** — Galileo OSNMA: the full chain of trust from the Merkle root through ECDSA-signed TESLA keys to per-satellite MAC tags, flagging navigation data Galileo did not sign.
|
|
20
21
|
- **Orbits** — satellite position/velocity, Doppler, DOP, azimuth/elevation, pass prediction and almanac propagation.
|
|
21
22
|
- **Positioning** — SPP, static and kinematic float PPP, PPP-AR (LAMBDA + wide-/narrow-lane FCB estimation), post-processed RTK, DGNSS, and SBAS/DFMC augmentation.
|
|
22
23
|
- **Quality** — multipath, cycle slips, completeness and slant-TEC ionosphere analysis, with SINEX-DCB calibration.
|
|
@@ -99,6 +100,20 @@ const nav = parseSbfNav(bytes); // GPS/GAL/BDS/GLO ephemeris from raw blocks
|
|
|
99
100
|
const has = parseSbfHas(bytes); // Galileo HAS (E6 C/NAV) SSR corrections
|
|
100
101
|
```
|
|
101
102
|
|
|
103
|
+
### Galileo OSNMA (navigation message authentication)
|
|
104
|
+
|
|
105
|
+
`OsnmaReceiver` takes Galileo E1-B I/NAV pages and verifies them the way the OSNMA SIS ICD v1.1 prescribes: public key against the Merkle root (built in, or broadcast in DSM-PKR), the DSM-KROOT's ECDSA signature, every TESLA chain key back to the root key, and the MAC tags over each satellite's ephemeris/clock (ADKD 0/12) and timing parameters (ADKD 4). A tag that fails means the data differ from what Galileo signed. Validated against the official ICD test vectors and live receivers.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { OsnmaReceiver, verifySbfOsnma } from 'gnss-js/osnma';
|
|
109
|
+
|
|
110
|
+
const s = verifySbfOsnma(sbfBytes); // whole capture
|
|
111
|
+
s.chains; // verified TESLA chains; s.tags → { ok, failed, ... }
|
|
112
|
+
s.satellites; // per PRN: ced.authenticatedIodNav, timing.lastAuthenticated, failures
|
|
113
|
+
|
|
114
|
+
const rx = new OsnmaReceiver(); // live: rx.pushPage(svid, wn, tow, page); rx.status()
|
|
115
|
+
```
|
|
116
|
+
|
|
102
117
|
### RTCM (RTCM3, RTCM2, SSR)
|
|
103
118
|
|
|
104
119
|
```ts
|
package/dist/analysis.cjs
CHANGED
|
@@ -1113,20 +1113,14 @@ function applyIonoDcb(iono, satDcb) {
|
|
|
1113
1113
|
// src/rinex/crx.ts
|
|
1114
1114
|
function crxRepair(old, diff) {
|
|
1115
1115
|
const chars = old.split("");
|
|
1116
|
-
|
|
1116
|
+
const n = Math.min(old.length, diff.length);
|
|
1117
|
+
for (let i = 0; i < n; i++) {
|
|
1117
1118
|
const dc = diff[i];
|
|
1118
|
-
if (dc === " ")
|
|
1119
|
-
|
|
1120
|
-
chars[i] = " ";
|
|
1121
|
-
} else {
|
|
1122
|
-
chars[i] = dc;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
if (diff.length > chars.length) {
|
|
1126
|
-
for (let i = chars.length; i < diff.length; i++) {
|
|
1127
|
-
chars[i] = diff[i] === "&" ? " " : diff[i];
|
|
1128
|
-
}
|
|
1119
|
+
if (dc === " ") continue;
|
|
1120
|
+
chars[i] = dc === "&" ? " " : dc;
|
|
1129
1121
|
}
|
|
1122
|
+
for (let i = old.length; i < diff.length; i++)
|
|
1123
|
+
chars[i] = diff[i] === "&" ? " " : diff[i];
|
|
1130
1124
|
return chars.join("");
|
|
1131
1125
|
}
|
|
1132
1126
|
function parseCrxDataLine(line, ntype) {
|
|
@@ -1371,6 +1365,15 @@ function readObsValue(line, index) {
|
|
|
1371
1365
|
const v = parseFloat(raw);
|
|
1372
1366
|
return isNaN(v) ? null : v;
|
|
1373
1367
|
}
|
|
1368
|
+
function normalizePrn(id) {
|
|
1369
|
+
let sys = id[0];
|
|
1370
|
+
let num = id.substring(1);
|
|
1371
|
+
if (sys === " " || /\d/.test(sys)) {
|
|
1372
|
+
num = id;
|
|
1373
|
+
sys = "G";
|
|
1374
|
+
}
|
|
1375
|
+
return sys + num.replace(/\s/g, "").padStart(2, "0");
|
|
1376
|
+
}
|
|
1374
1377
|
function parseEpochLine3(line) {
|
|
1375
1378
|
if (line[0] !== ">") return null;
|
|
1376
1379
|
const yr = parseInt(line.substring(2, 6));
|
|
@@ -1520,7 +1523,7 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1520
1523
|
function processSatLineV3(line) {
|
|
1521
1524
|
if (!header || line.length < 3) return;
|
|
1522
1525
|
const sys = line[0];
|
|
1523
|
-
const prn = line.substring(0, 3);
|
|
1526
|
+
const prn = normalizePrn(line.substring(0, 3));
|
|
1524
1527
|
trackSat(sys, prn);
|
|
1525
1528
|
const obsLine = line.substring(3);
|
|
1526
1529
|
const codes = header.obsTypes[sys] ?? [];
|
|
@@ -1545,10 +1548,8 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1545
1548
|
let v2ObsAccum = [];
|
|
1546
1549
|
function processSatLineV2(line) {
|
|
1547
1550
|
if (!header || v2SatIndex >= v2SatIds.length) return;
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1550
|
-
if (sys === " " || /\d/.test(sys)) sys = "G";
|
|
1551
|
-
const prn = sys + satId.substring(1).padStart(2, "0");
|
|
1551
|
+
const prn = normalizePrn(v2SatIds[v2SatIndex]);
|
|
1552
|
+
const sys = prn[0];
|
|
1552
1553
|
if (v2CurrentSatLine === 0) {
|
|
1553
1554
|
trackSat(sys, prn);
|
|
1554
1555
|
if (onSatObs) v2ObsAccum = [];
|
|
@@ -1639,6 +1640,7 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1639
1640
|
} else {
|
|
1640
1641
|
epochLine = crxRepair(crxPrevEpochLine, line);
|
|
1641
1642
|
}
|
|
1643
|
+
epochLine = epochLine.replace(/ +$/, "");
|
|
1642
1644
|
if (isV3) {
|
|
1643
1645
|
if (epochLine[0] !== ">") return;
|
|
1644
1646
|
const info = parseEpochLine3(epochLine);
|
|
@@ -1656,10 +1658,11 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1656
1658
|
return;
|
|
1657
1659
|
}
|
|
1658
1660
|
resetEpochAccum(info);
|
|
1659
|
-
const satPart = epochLine.substring(41).replace(/\s+$/, "");
|
|
1660
1661
|
crxSatList = [];
|
|
1661
|
-
for (let
|
|
1662
|
-
crxSatList.push(
|
|
1662
|
+
for (let k = 0; k < info.numSats; k++) {
|
|
1663
|
+
crxSatList.push(
|
|
1664
|
+
normalizePrn(epochLine.substring(41 + 3 * k, 44 + 3 * k))
|
|
1665
|
+
);
|
|
1663
1666
|
}
|
|
1664
1667
|
crxSatIndex = 0;
|
|
1665
1668
|
crxPrevEpochLine = epochLine;
|
|
@@ -1680,11 +1683,7 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1680
1683
|
return;
|
|
1681
1684
|
}
|
|
1682
1685
|
resetEpochAccum(info);
|
|
1683
|
-
crxSatList = (info.satIds ?? []).map(
|
|
1684
|
-
let sys = id[0];
|
|
1685
|
-
if (sys === " " || /\d/.test(sys)) sys = "G";
|
|
1686
|
-
return sys + id.substring(1).padStart(2, "0");
|
|
1687
|
-
});
|
|
1686
|
+
crxSatList = (info.satIds ?? []).slice(0, info.numSats).map(normalizePrn);
|
|
1688
1687
|
crxSatIndex = 0;
|
|
1689
1688
|
crxPrevEpochLine = epochLine;
|
|
1690
1689
|
crxPhase = "clock";
|
package/dist/analysis.js
CHANGED
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
computeIonoRate,
|
|
9
9
|
detrendIonoArcs,
|
|
10
10
|
parseSinexBiasDcb
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-MFCPYUEI.js";
|
|
12
|
+
import "./chunk-FEABDV5P.js";
|
|
13
13
|
import "./chunk-FIEWO4J4.js";
|
|
14
14
|
export {
|
|
15
15
|
CompletenessAccumulator,
|
|
@@ -93,4 +93,69 @@ declare function parseClk(text: string): ClkFile;
|
|
|
93
93
|
*/
|
|
94
94
|
declare function clkBias(clk: ClkFile, prn: string, tMs: number): number | null;
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Bias-SINEX (`.BIA`) parser — GNSS code/phase biases published by the IGS
|
|
98
|
+
* analysis centres. Two record kinds matter here:
|
|
99
|
+
*
|
|
100
|
+
* - **DSB** differential signal bias (OBS1−OBS2), the classic inter-code
|
|
101
|
+
* bias used for ionosphere/DCB corrections in SPP.
|
|
102
|
+
* - **OSB** observable-specific (absolute) bias for a single OBS, the
|
|
103
|
+
* ingredient PPP-AR needs: the *phase* OSBs are the satellite
|
|
104
|
+
* fractional-cycle biases that make the ambiguities integer-recoverable.
|
|
105
|
+
*
|
|
106
|
+
* Values keep their file unit (`ns` for code/most phase biases, `cyc` for
|
|
107
|
+
* cycle-based phase biases); `biasMetres` converts ns → m via c.
|
|
108
|
+
*/
|
|
109
|
+
interface BiasRecord {
|
|
110
|
+
/** 'DSB' | 'OSB' | 'ISB' | … */
|
|
111
|
+
type: string;
|
|
112
|
+
/** Satellite PRN (e.g. 'G01', 'E11'); '' for a pure station bias. */
|
|
113
|
+
prn: string;
|
|
114
|
+
/** Station (9-char), '' for satellite biases. */
|
|
115
|
+
station: string;
|
|
116
|
+
/** First observable code (e.g. 'C1W', 'L1C'). */
|
|
117
|
+
obs1: string;
|
|
118
|
+
/** Second observable (DSB only; '' for OSB). */
|
|
119
|
+
obs2: string;
|
|
120
|
+
/** Validity start / end (GPS-scale ms); ±Infinity when open. */
|
|
121
|
+
startMs: number;
|
|
122
|
+
endMs: number;
|
|
123
|
+
/** Estimated value in the file unit. */
|
|
124
|
+
value: number;
|
|
125
|
+
/** File unit ('ns' | 'cyc' | …). */
|
|
126
|
+
unit: string;
|
|
127
|
+
}
|
|
128
|
+
interface BiasSinex {
|
|
129
|
+
records: BiasRecord[];
|
|
130
|
+
/** All satellite records grouped by PRN (station === ''). */
|
|
131
|
+
bySat: Map<string, BiasRecord[]>;
|
|
132
|
+
}
|
|
133
|
+
/** Parse a Bias-SINEX file. */
|
|
134
|
+
declare function parseBiasSinex(text: string): BiasSinex;
|
|
135
|
+
/** A satellite bias record's value in metres (ns → m via c; cyc left as-is). */
|
|
136
|
+
declare function biasMetres(r: BiasRecord): number;
|
|
137
|
+
/**
|
|
138
|
+
* Look up a satellite bias: a DSB by its (obs1,obs2) pair, or an OSB by obs1
|
|
139
|
+
* alone (pass obs2 = undefined), valid at `tMs` (default: any). Returns the
|
|
140
|
+
* record or null.
|
|
141
|
+
*/
|
|
142
|
+
declare function findSatBias(bias: BiasSinex, prn: string, obs1: string, obs2?: string, tMs?: number): BiasRecord | null;
|
|
143
|
+
/**
|
|
144
|
+
* Fast time-aware lookup of satellite OSBs (metres) — the form PPP needs.
|
|
145
|
+
* Products with integer-recoverable clocks (CNES/GFZ "ABS", CODE) publish
|
|
146
|
+
* phase OSBs that change every 30 s–15 min, so one PRN/observable can carry
|
|
147
|
+
* thousands of records; they're sorted once and found by binary search.
|
|
148
|
+
* Values are as published (observed = true + bias), so the corrected
|
|
149
|
+
* observable is `observed − osb(...)`.
|
|
150
|
+
*/
|
|
151
|
+
declare class SatOsbTable {
|
|
152
|
+
private readonly byKey;
|
|
153
|
+
constructor(bias: BiasSinex | BiasRecord[]);
|
|
154
|
+
/** Whether any phase (L…) OSB is present — i.e. the product supports AR. */
|
|
155
|
+
get hasPhase(): boolean;
|
|
156
|
+
/** OSB (m) of `prn`'s observable `obs` (e.g. 'C1W', 'L2W') at `tMs`;
|
|
157
|
+
* null when the product has none valid then. */
|
|
158
|
+
get(prn: string, obs: string, tMs: number): number | null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export { type BiasRecord as B, type ClkFile as C, type IonexGrid as I, SatOsbTable as S, type BiasSinex as a, type Sp3File as b, type Sp3Sample as c, biasMetres as d, clkBias as e, findSatBias as f, parseClk as g, parseIonex as h, parseSp3 as i, parseBiasSinex as p, sp3Position as s };
|
|
@@ -93,4 +93,69 @@ declare function parseClk(text: string): ClkFile;
|
|
|
93
93
|
*/
|
|
94
94
|
declare function clkBias(clk: ClkFile, prn: string, tMs: number): number | null;
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Bias-SINEX (`.BIA`) parser — GNSS code/phase biases published by the IGS
|
|
98
|
+
* analysis centres. Two record kinds matter here:
|
|
99
|
+
*
|
|
100
|
+
* - **DSB** differential signal bias (OBS1−OBS2), the classic inter-code
|
|
101
|
+
* bias used for ionosphere/DCB corrections in SPP.
|
|
102
|
+
* - **OSB** observable-specific (absolute) bias for a single OBS, the
|
|
103
|
+
* ingredient PPP-AR needs: the *phase* OSBs are the satellite
|
|
104
|
+
* fractional-cycle biases that make the ambiguities integer-recoverable.
|
|
105
|
+
*
|
|
106
|
+
* Values keep their file unit (`ns` for code/most phase biases, `cyc` for
|
|
107
|
+
* cycle-based phase biases); `biasMetres` converts ns → m via c.
|
|
108
|
+
*/
|
|
109
|
+
interface BiasRecord {
|
|
110
|
+
/** 'DSB' | 'OSB' | 'ISB' | … */
|
|
111
|
+
type: string;
|
|
112
|
+
/** Satellite PRN (e.g. 'G01', 'E11'); '' for a pure station bias. */
|
|
113
|
+
prn: string;
|
|
114
|
+
/** Station (9-char), '' for satellite biases. */
|
|
115
|
+
station: string;
|
|
116
|
+
/** First observable code (e.g. 'C1W', 'L1C'). */
|
|
117
|
+
obs1: string;
|
|
118
|
+
/** Second observable (DSB only; '' for OSB). */
|
|
119
|
+
obs2: string;
|
|
120
|
+
/** Validity start / end (GPS-scale ms); ±Infinity when open. */
|
|
121
|
+
startMs: number;
|
|
122
|
+
endMs: number;
|
|
123
|
+
/** Estimated value in the file unit. */
|
|
124
|
+
value: number;
|
|
125
|
+
/** File unit ('ns' | 'cyc' | …). */
|
|
126
|
+
unit: string;
|
|
127
|
+
}
|
|
128
|
+
interface BiasSinex {
|
|
129
|
+
records: BiasRecord[];
|
|
130
|
+
/** All satellite records grouped by PRN (station === ''). */
|
|
131
|
+
bySat: Map<string, BiasRecord[]>;
|
|
132
|
+
}
|
|
133
|
+
/** Parse a Bias-SINEX file. */
|
|
134
|
+
declare function parseBiasSinex(text: string): BiasSinex;
|
|
135
|
+
/** A satellite bias record's value in metres (ns → m via c; cyc left as-is). */
|
|
136
|
+
declare function biasMetres(r: BiasRecord): number;
|
|
137
|
+
/**
|
|
138
|
+
* Look up a satellite bias: a DSB by its (obs1,obs2) pair, or an OSB by obs1
|
|
139
|
+
* alone (pass obs2 = undefined), valid at `tMs` (default: any). Returns the
|
|
140
|
+
* record or null.
|
|
141
|
+
*/
|
|
142
|
+
declare function findSatBias(bias: BiasSinex, prn: string, obs1: string, obs2?: string, tMs?: number): BiasRecord | null;
|
|
143
|
+
/**
|
|
144
|
+
* Fast time-aware lookup of satellite OSBs (metres) — the form PPP needs.
|
|
145
|
+
* Products with integer-recoverable clocks (CNES/GFZ "ABS", CODE) publish
|
|
146
|
+
* phase OSBs that change every 30 s–15 min, so one PRN/observable can carry
|
|
147
|
+
* thousands of records; they're sorted once and found by binary search.
|
|
148
|
+
* Values are as published (observed = true + bias), so the corrected
|
|
149
|
+
* observable is `observed − osb(...)`.
|
|
150
|
+
*/
|
|
151
|
+
declare class SatOsbTable {
|
|
152
|
+
private readonly byKey;
|
|
153
|
+
constructor(bias: BiasSinex | BiasRecord[]);
|
|
154
|
+
/** Whether any phase (L…) OSB is present — i.e. the product supports AR. */
|
|
155
|
+
get hasPhase(): boolean;
|
|
156
|
+
/** OSB (m) of `prn`'s observable `obs` (e.g. 'C1W', 'L2W') at `tMs`;
|
|
157
|
+
* null when the product has none valid then. */
|
|
158
|
+
get(prn: string, obs: string, tMs: number): number | null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export { type BiasRecord as B, type ClkFile as C, type IonexGrid as I, SatOsbTable as S, type BiasSinex as a, type Sp3File as b, type Sp3Sample as c, biasMetres as d, clkBias as e, findSatBias as f, parseClk as g, parseIonex as h, parseSp3 as i, parseBiasSinex as p, sp3Position as s };
|
package/dist/binex.d.cts
CHANGED
package/dist/binex.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bdsCnavAref
|
|
3
|
+
} from "./chunk-NL2ORX75.js";
|
|
1
4
|
import {
|
|
2
5
|
CNAV_A_REF,
|
|
3
6
|
CNAV_OMEGA_DOT_REF
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HUXQABJI.js";
|
|
5
8
|
import {
|
|
6
9
|
GPS_PI
|
|
7
10
|
} from "./chunk-V2D5KUKH.js";
|
|
@@ -661,10 +664,11 @@ var R4_MSG_LINES = {
|
|
|
661
664
|
// RINEX 4 adds orbit-4 (status flags) vs 3 in RINEX 3
|
|
662
665
|
// EPH type parsed into RinexCnavEphemeris for G/J (skipped otherwise)
|
|
663
666
|
CNAV: 9,
|
|
664
|
-
//
|
|
667
|
+
// BeiDou-3 B-CNAV, parsed into BdsCnavEphemeris
|
|
665
668
|
CNV1: 10,
|
|
666
669
|
CNV2: 10,
|
|
667
670
|
CNV3: 9,
|
|
671
|
+
// EPH types we skip (different field layout)
|
|
668
672
|
L1NV: 8,
|
|
669
673
|
L1OC: 9,
|
|
670
674
|
L3OC: 9
|
|
@@ -873,6 +877,68 @@ function buildCnavEphemeris(sys, prn, date, epochVals, d) {
|
|
|
873
877
|
tow: num(7, 0)
|
|
874
878
|
};
|
|
875
879
|
}
|
|
880
|
+
var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
881
|
+
function buildBdsCnavEphemeris(message, prn, date, epochVals, d) {
|
|
882
|
+
const num = (r, c) => d[r]?.[c] ?? 0;
|
|
883
|
+
const opt = (r, c) => d[r]?.[c] ?? null;
|
|
884
|
+
const cnv3 = message === "CNV3";
|
|
885
|
+
const t = (date.getTime() - BDT_EPOCH_MS) / 1e3;
|
|
886
|
+
const weekToc = Math.floor(t / SEC_PER_WEEK);
|
|
887
|
+
const toc = t - weekToc * SEC_PER_WEEK;
|
|
888
|
+
const toe = num(2, 0);
|
|
889
|
+
const week = toe - toc > SEC_PER_WEEK / 2 ? weekToc - 1 : toc - toe > SEC_PER_WEEK / 2 ? weekToc + 1 : weekToc;
|
|
890
|
+
const sqrtA = num(1, 3);
|
|
891
|
+
const a = sqrtA * sqrtA;
|
|
892
|
+
const satType = num(4, 2);
|
|
893
|
+
return {
|
|
894
|
+
system: "C",
|
|
895
|
+
prn,
|
|
896
|
+
message,
|
|
897
|
+
week,
|
|
898
|
+
toe,
|
|
899
|
+
toeDate: new Date(BDT_EPOCH_MS + (week * SEC_PER_WEEK + toe) * 1e3),
|
|
900
|
+
toc,
|
|
901
|
+
tocDate: date,
|
|
902
|
+
satType,
|
|
903
|
+
a,
|
|
904
|
+
deltaA: a - bdsCnavAref(satType),
|
|
905
|
+
aDot: num(0, 0),
|
|
906
|
+
deltaN0: num(0, 2),
|
|
907
|
+
deltaN0Dot: num(4, 1),
|
|
908
|
+
m0: num(0, 3),
|
|
909
|
+
e: num(1, 1),
|
|
910
|
+
omega: num(3, 2),
|
|
911
|
+
omega0: num(2, 2),
|
|
912
|
+
i0: num(3, 0),
|
|
913
|
+
omegaDot: num(3, 3),
|
|
914
|
+
i0Dot: num(4, 0),
|
|
915
|
+
cis: num(2, 3),
|
|
916
|
+
cic: num(2, 1),
|
|
917
|
+
crs: num(0, 1),
|
|
918
|
+
crc: num(3, 1),
|
|
919
|
+
cus: num(1, 2),
|
|
920
|
+
cuc: num(1, 0),
|
|
921
|
+
af0: epochVals[0] ?? 0,
|
|
922
|
+
af1: epochVals[1] ?? 0,
|
|
923
|
+
af2: epochVals[2] ?? 0,
|
|
924
|
+
iode: cnv3 ? null : opt(8, 3),
|
|
925
|
+
iodc: cnv3 ? null : opt(7, 3),
|
|
926
|
+
tgdB1Cp: cnv3 ? null : opt(6, 2),
|
|
927
|
+
tgdB2ap: cnv3 ? null : opt(6, 3),
|
|
928
|
+
iscB1Cd: cnv3 ? null : opt(6, 0),
|
|
929
|
+
iscB2ad: cnv3 ? null : opt(6, 1),
|
|
930
|
+
tgdB2bI: cnv3 ? opt(6, 3) : null,
|
|
931
|
+
top: opt(4, 3),
|
|
932
|
+
sisaiOe: opt(5, 0),
|
|
933
|
+
sisaiOcb: opt(5, 1),
|
|
934
|
+
sisaiOc1: opt(5, 2),
|
|
935
|
+
sisaiOc2: opt(5, 3),
|
|
936
|
+
sismai: cnv3 ? opt(6, 0) : opt(7, 0),
|
|
937
|
+
health: cnv3 ? opt(6, 1) : opt(7, 1),
|
|
938
|
+
integrityFlags: cnv3 ? opt(6, 2) : opt(7, 2),
|
|
939
|
+
tow: cnv3 ? num(7, 0) : num(8, 0)
|
|
940
|
+
};
|
|
941
|
+
}
|
|
876
942
|
function parseNavFile(text) {
|
|
877
943
|
const lines = text.split("\n");
|
|
878
944
|
const header = {
|
|
@@ -883,6 +949,7 @@ function parseNavFile(text) {
|
|
|
883
949
|
};
|
|
884
950
|
const ephemerides = [];
|
|
885
951
|
const cnav = [];
|
|
952
|
+
const bdsCnav = [];
|
|
886
953
|
let inHeader = true;
|
|
887
954
|
let i = 0;
|
|
888
955
|
while (i < lines.length) {
|
|
@@ -951,7 +1018,8 @@ function parseNavFile(text) {
|
|
|
951
1018
|
continue;
|
|
952
1019
|
}
|
|
953
1020
|
const isCnav = msgType === "CNAV" && (svSys === "G" || svSys === "J");
|
|
954
|
-
|
|
1021
|
+
const isBdsCnav = svSys === "C" && (msgType === "CNV1" || msgType === "CNV2" || msgType === "CNV3");
|
|
1022
|
+
if (!SUPPORTED_EPH_MSGS.has(msgType) && !isCnav && !isBdsCnav) {
|
|
955
1023
|
const totalLines = R4_MSG_LINES[msgType] ?? 2;
|
|
956
1024
|
i += totalLines + 1;
|
|
957
1025
|
continue;
|
|
@@ -963,17 +1031,34 @@ function parseNavFile(text) {
|
|
|
963
1031
|
const sys2 = epochLine.charAt(0);
|
|
964
1032
|
const parsed = parseNavEpochV3(epochLine);
|
|
965
1033
|
const prn2 = `${sys2}${parsed.prn.substring(1)}`;
|
|
966
|
-
if (isCnav) {
|
|
1034
|
+
if (isCnav || isBdsCnav) {
|
|
967
1035
|
const slotLines = [];
|
|
968
1036
|
for (let j = 0; j < numDataLines2; j++) {
|
|
969
1037
|
i++;
|
|
970
1038
|
if (i >= lines.length) break;
|
|
971
1039
|
slotLines.push(parseDataLineSlots(lines[i]));
|
|
972
1040
|
}
|
|
973
|
-
if (slotLines.length === numDataLines2
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1041
|
+
if (slotLines.length === numDataLines2) {
|
|
1042
|
+
if (isBdsCnav && sys2 === "C")
|
|
1043
|
+
bdsCnav.push(
|
|
1044
|
+
buildBdsCnavEphemeris(
|
|
1045
|
+
msgType,
|
|
1046
|
+
prn2,
|
|
1047
|
+
parsed.date,
|
|
1048
|
+
parsed.values,
|
|
1049
|
+
slotLines
|
|
1050
|
+
)
|
|
1051
|
+
);
|
|
1052
|
+
else if (sys2 === "G" || sys2 === "J")
|
|
1053
|
+
cnav.push(
|
|
1054
|
+
buildCnavEphemeris(
|
|
1055
|
+
sys2,
|
|
1056
|
+
prn2,
|
|
1057
|
+
parsed.date,
|
|
1058
|
+
parsed.values,
|
|
1059
|
+
slotLines
|
|
1060
|
+
)
|
|
1061
|
+
);
|
|
977
1062
|
}
|
|
978
1063
|
i++;
|
|
979
1064
|
continue;
|
|
@@ -1051,7 +1136,12 @@ function parseNavFile(text) {
|
|
|
1051
1136
|
}
|
|
1052
1137
|
i++;
|
|
1053
1138
|
}
|
|
1054
|
-
return
|
|
1139
|
+
return {
|
|
1140
|
+
header,
|
|
1141
|
+
ephemerides,
|
|
1142
|
+
...cnav.length > 0 && { cnav },
|
|
1143
|
+
...bdsCnav.length > 0 && { bdsCnav }
|
|
1144
|
+
};
|
|
1055
1145
|
}
|
|
1056
1146
|
|
|
1057
1147
|
// src/rinex/nav-writer.ts
|
|
@@ -1247,6 +1337,30 @@ function cnavRecord(eph) {
|
|
|
1247
1337
|
orbitLine4([eph.tow, resolveWnOp(eph.week, eph.wnOp)])
|
|
1248
1338
|
].join("\n");
|
|
1249
1339
|
}
|
|
1340
|
+
function bdsCnavRecord(eph) {
|
|
1341
|
+
const lines = [
|
|
1342
|
+
epochLine4(eph.prn, eph.tocDate, [eph.af0, eph.af1, eph.af2]),
|
|
1343
|
+
orbitLine4([eph.aDot, eph.crs, eph.deltaN0, eph.m0]),
|
|
1344
|
+
orbitLine4([eph.cuc, eph.e, eph.cus, Math.sqrt(eph.a)]),
|
|
1345
|
+
orbitLine4([eph.toe, eph.cic, eph.omega0, eph.cis]),
|
|
1346
|
+
orbitLine4([eph.i0, eph.crc, eph.omega, eph.omegaDot]),
|
|
1347
|
+
orbitLine4([eph.i0Dot, eph.deltaN0Dot, eph.satType, eph.top]),
|
|
1348
|
+
orbitLine4([eph.sisaiOe, eph.sisaiOcb, eph.sisaiOc1, eph.sisaiOc2])
|
|
1349
|
+
];
|
|
1350
|
+
if (eph.message === "CNV3") {
|
|
1351
|
+
lines.push(
|
|
1352
|
+
orbitLine4([eph.sismai, eph.health, eph.integrityFlags, eph.tgdB2bI]),
|
|
1353
|
+
orbitLine4([eph.tow])
|
|
1354
|
+
);
|
|
1355
|
+
} else {
|
|
1356
|
+
lines.push(
|
|
1357
|
+
orbitLine4([eph.iscB1Cd, eph.iscB2ad, eph.tgdB1Cp, eph.tgdB2ap]),
|
|
1358
|
+
orbitLine4([eph.sismai, eph.health, eph.integrityFlags, eph.iodc]),
|
|
1359
|
+
orbitLine4([eph.tow, null, null, eph.iode])
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
return lines.join("\n");
|
|
1363
|
+
}
|
|
1250
1364
|
var KLOBUCHAR_SOURCES = [
|
|
1251
1365
|
["GPSA", "GPSB", "G", "LNAV"],
|
|
1252
1366
|
["QZSA", "QZSB", "J", "LNAV"],
|
|
@@ -1324,6 +1438,15 @@ function writeRinexNav4(nav) {
|
|
|
1324
1438
|
` + cnavRecord(eph)
|
|
1325
1439
|
});
|
|
1326
1440
|
}
|
|
1441
|
+
for (const eph of nav.bdsCnav ?? []) {
|
|
1442
|
+
records.push({
|
|
1443
|
+
prn: eph.prn,
|
|
1444
|
+
time: eph.tocDate.getTime(),
|
|
1445
|
+
label: eph.message,
|
|
1446
|
+
body: `> EPH ${padL(eph.prn, 3)} ${eph.message}
|
|
1447
|
+
` + bdsCnavRecord(eph)
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1327
1450
|
records.sort(
|
|
1328
1451
|
(a, b) => a.prn.localeCompare(b.prn) || a.time - b.time || a.label.localeCompare(b.label)
|
|
1329
1452
|
);
|
|
@@ -1494,6 +1617,50 @@ function findSatBias(bias, prn, obs1, obs2, tMs) {
|
|
|
1494
1617
|
}
|
|
1495
1618
|
return null;
|
|
1496
1619
|
}
|
|
1620
|
+
var SatOsbTable = class {
|
|
1621
|
+
byKey = /* @__PURE__ */ new Map();
|
|
1622
|
+
constructor(bias) {
|
|
1623
|
+
const recs = Array.isArray(bias) ? bias : bias.records;
|
|
1624
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1625
|
+
for (const r of recs) {
|
|
1626
|
+
if (r.type !== "OSB" || !r.prn || r.station) continue;
|
|
1627
|
+
if (r.unit !== "ns") continue;
|
|
1628
|
+
const k = `${r.prn} ${r.obs1}`;
|
|
1629
|
+
let g = groups.get(k);
|
|
1630
|
+
if (!g) groups.set(k, g = []);
|
|
1631
|
+
g.push(r);
|
|
1632
|
+
}
|
|
1633
|
+
for (const [k, g] of groups) {
|
|
1634
|
+
g.sort((a, b) => a.startMs - b.startMs);
|
|
1635
|
+
this.byKey.set(k, {
|
|
1636
|
+
start: Float64Array.from(g, (r) => r.startMs),
|
|
1637
|
+
end: Float64Array.from(g, (r) => r.endMs),
|
|
1638
|
+
m: Float64Array.from(g, biasMetres)
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
/** Whether any phase (L…) OSB is present — i.e. the product supports AR. */
|
|
1643
|
+
get hasPhase() {
|
|
1644
|
+
for (const k of this.byKey.keys())
|
|
1645
|
+
if (k.split(" ")[1][0] === "L") return true;
|
|
1646
|
+
return false;
|
|
1647
|
+
}
|
|
1648
|
+
/** OSB (m) of `prn`'s observable `obs` (e.g. 'C1W', 'L2W') at `tMs`;
|
|
1649
|
+
* null when the product has none valid then. */
|
|
1650
|
+
get(prn, obs, tMs) {
|
|
1651
|
+
const e = this.byKey.get(`${prn} ${obs}`) ?? this.byKey.get(`${prn} ${obs.slice(0, 2)}X`);
|
|
1652
|
+
if (!e) return null;
|
|
1653
|
+
let lo = 0;
|
|
1654
|
+
let hi = e.start.length - 1;
|
|
1655
|
+
if (tMs < e.start[0]) return null;
|
|
1656
|
+
while (lo < hi) {
|
|
1657
|
+
const mid = lo + hi + 1 >> 1;
|
|
1658
|
+
if (e.start[mid] <= tMs) lo = mid;
|
|
1659
|
+
else hi = mid - 1;
|
|
1660
|
+
}
|
|
1661
|
+
return tMs <= e.end[lo] ? e.m[lo] : null;
|
|
1662
|
+
}
|
|
1663
|
+
};
|
|
1497
1664
|
|
|
1498
1665
|
// src/rinex/obs-writer-common.ts
|
|
1499
1666
|
function createGzipLineSink() {
|
|
@@ -1898,6 +2065,7 @@ export {
|
|
|
1898
2065
|
parseBiasSinex,
|
|
1899
2066
|
biasMetres,
|
|
1900
2067
|
findSatBias,
|
|
2068
|
+
SatOsbTable,
|
|
1901
2069
|
createGzipLineSink,
|
|
1902
2070
|
stationHeaderLines,
|
|
1903
2071
|
writeRinexObsBlob,
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BdsAssembler,
|
|
3
|
-
GalInavAssembler,
|
|
4
3
|
GloStringAssembler,
|
|
5
4
|
bdsSubframeParityOk,
|
|
6
|
-
galInavPageCrcOk,
|
|
7
5
|
testGloString
|
|
8
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-NIJ6WFQJ.js";
|
|
7
|
+
import {
|
|
8
|
+
GalInavAssembler,
|
|
9
|
+
galInavPageCrcOk
|
|
10
|
+
} from "./chunk-TWGZTRBL.js";
|
|
9
11
|
import {
|
|
10
12
|
decodeSbasGeoNav,
|
|
11
13
|
sbasCrcOk
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-H3AKFD25.js";
|
|
13
15
|
import {
|
|
14
16
|
CnavAssembler,
|
|
15
17
|
cnavCrcOk
|
|
16
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-HUXQABJI.js";
|
|
17
19
|
import {
|
|
18
20
|
GpsLnavAssembler,
|
|
19
21
|
getBitS,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/sbf/frame.ts
|
|
2
|
+
var CRC_TABLE = (() => {
|
|
3
|
+
const t = new Uint16Array(256);
|
|
4
|
+
for (let n = 0; n < 256; n++) {
|
|
5
|
+
let c = n << 8;
|
|
6
|
+
for (let k = 0; k < 8; k++)
|
|
7
|
+
c = c & 32768 ? (c << 1 ^ 4129) & 65535 : c << 1 & 65535;
|
|
8
|
+
t[n] = c;
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
})();
|
|
12
|
+
function crc16(data, start, end) {
|
|
13
|
+
let crc = 0;
|
|
14
|
+
for (let i = start; i < end; i++)
|
|
15
|
+
crc = (crc << 8 ^ CRC_TABLE[(crc >> 8 ^ data[i]) & 255]) & 65535;
|
|
16
|
+
return crc;
|
|
17
|
+
}
|
|
18
|
+
function scanSbfFrames(data, view, onBlock) {
|
|
19
|
+
let badCrc = 0;
|
|
20
|
+
let i = 0;
|
|
21
|
+
while (i + 8 <= data.length) {
|
|
22
|
+
if (data[i] !== 36 || data[i + 1] !== 64) {
|
|
23
|
+
i++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const len = view.getUint16(i + 6, true);
|
|
27
|
+
if (len < 8 || len % 4 !== 0) {
|
|
28
|
+
i++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (i + len > data.length) break;
|
|
32
|
+
if (crc16(data, i + 4, i + len) !== view.getUint16(i + 2, true)) {
|
|
33
|
+
badCrc++;
|
|
34
|
+
i++;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
onBlock(view.getUint16(i + 4, true) & 8191, i, len);
|
|
38
|
+
i += len;
|
|
39
|
+
}
|
|
40
|
+
return badCrc;
|
|
41
|
+
}
|
|
42
|
+
var two = (n) => String(n).padStart(2, "0");
|
|
43
|
+
function svidToPrn(svid) {
|
|
44
|
+
if (svid >= 1 && svid <= 37) return svid <= 32 ? `G${two(svid)}` : null;
|
|
45
|
+
if (svid <= 61) return svid - 37 <= 27 ? `R${two(svid - 37)}` : null;
|
|
46
|
+
if (svid <= 62) return null;
|
|
47
|
+
if (svid <= 68) return svid - 38 <= 27 ? `R${two(svid - 38)}` : null;
|
|
48
|
+
if (svid <= 70) return null;
|
|
49
|
+
if (svid <= 106) return svid - 70 <= 36 ? `E${two(svid - 70)}` : null;
|
|
50
|
+
if (svid <= 119) return null;
|
|
51
|
+
if (svid <= 140) return `S${two(svid - 100)}`;
|
|
52
|
+
if (svid <= 180) return svid - 140 <= 50 ? `C${two(svid - 140)}` : null;
|
|
53
|
+
if (svid <= 190) return `J${two(svid - 180)}`;
|
|
54
|
+
if (svid <= 197) return svid - 190 <= 14 ? `I${two(svid - 190)}` : null;
|
|
55
|
+
if (svid <= 215) return `S${two(svid - 157)}`;
|
|
56
|
+
if (svid <= 222) return svid - 208 <= 14 ? `I${two(svid - 208)}` : null;
|
|
57
|
+
if (svid <= 245) return svid - 182 <= 50 ? `C${two(svid - 182)}` : null;
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
crc16,
|
|
63
|
+
scanSbfFrames,
|
|
64
|
+
svidToPrn
|
|
65
|
+
};
|