gnss-js 1.24.0 → 1.26.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/dist/binex.cjs +799 -0
- package/dist/binex.d.cts +189 -0
- package/dist/binex.d.ts +189 -0
- package/dist/binex.js +19 -0
- package/dist/{chunk-T2ZMNRCY.js → chunk-4XGKCVE3.js} +73 -10
- package/dist/{chunk-VTX3VCL4.js → chunk-5AUK42AL.js} +4 -2
- package/dist/{chunk-WZDFZ76K.js → chunk-726LQBGM.js} +8 -148
- package/dist/{chunk-PZNVFCKJ.js → chunk-AFEDNWXI.js} +5 -3
- package/dist/chunk-CKRNXZHT.js +424 -0
- package/dist/{chunk-7RKLWE6R.js → chunk-F4CM6Q4F.js} +4 -2
- package/dist/{chunk-MCAVU3XL.js → chunk-FEOMYZEU.js} +5 -3
- package/dist/chunk-IQ5OIMQD.js +148 -0
- package/dist/chunk-OK4GGNOO.js +756 -0
- package/dist/{chunk-NOQAAUGY.js → chunk-WDUN4BWL.js} +4 -2
- package/dist/index.cjs +1273 -27
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +48 -17
- package/dist/ionex-DaW5x9nq.d.cts +23 -0
- package/dist/ionex-DaW5x9nq.d.ts +23 -0
- package/dist/novatel.js +3 -2
- package/dist/positioning.cjs +76 -10
- package/dist/positioning.d.cts +59 -3
- package/dist/positioning.d.ts +59 -3
- package/dist/positioning.js +7 -1
- package/dist/rinex.d.cts +2 -23
- package/dist/rinex.d.ts +2 -23
- package/dist/rinex.js +3 -2
- package/dist/sbf.js +4 -3
- package/dist/trimble.cjs +452 -0
- package/dist/trimble.d.cts +147 -0
- package/dist/trimble.d.ts +147 -0
- package/dist/trimble.js +10 -0
- package/dist/ubx.js +4 -3
- package/package.json +11 -1
package/dist/binex.d.cts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { E as Ephemeris } from './nav-DImXUdbp.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BINEX (BINary EXchange) framing, shared by the observation and
|
|
5
|
+
* navigation decoders.
|
|
6
|
+
*
|
|
7
|
+
* A forward-readable record is: a 1-byte sync that encodes byte order and
|
|
8
|
+
* checksum model, a record ID (ubnxi), a message length (ubnxi), the
|
|
9
|
+
* message body, then a trailing checksum whose width depends on the
|
|
10
|
+
* covered-byte count. Sync bytes (ref [1] §"Format Details"):
|
|
11
|
+
*
|
|
12
|
+
* 0xC2 little-endian, regular CRC (forward)
|
|
13
|
+
* 0xE2 big-endian, regular CRC (forward)
|
|
14
|
+
* 0xC8 little-endian, enhanced CRC (forward)
|
|
15
|
+
* 0xE8 big-endian, enhanced CRC (forward)
|
|
16
|
+
*
|
|
17
|
+
* The reverse-readable variants (0xD2/0xF2/0xD8/0xF8 leading and
|
|
18
|
+
* 0xB4/0xB0/0xE4/0xE0 terminating) are NOT decoded here (deferred).
|
|
19
|
+
*
|
|
20
|
+
* Checksum widths over the covered bytes N = recordID + lengthField +
|
|
21
|
+
* body (regular model): N ≤ 127 → 1-byte XOR; 128–4095 → CRC16
|
|
22
|
+
* (x^16+x^12+x^5+1, i.e. poly 0x1021 MSB-first, init 0); 4096–1048575 →
|
|
23
|
+
* CRC32 (poly 0x04C11DB7 MSB-first); ≥ 1048576 → 16-byte MD5. Enhanced
|
|
24
|
+
* model shifts every threshold up one step (CRC16 from 0 bytes, no XOR).
|
|
25
|
+
* The 16-byte MD5 case is not implemented (records that large do not
|
|
26
|
+
* occur in GNSS obs/eph streams); such a record is treated as bad.
|
|
27
|
+
*
|
|
28
|
+
* ubnxi (1–4 byte unsigned) and the field layouts are ported from RTKLIB
|
|
29
|
+
* demo5/2.4.3 src/rcv/binex.c (getbnxi, decode_bnx, Copyright (c)
|
|
30
|
+
* 2013-2018 T. Takasu, BSD-2-Clause) and cross-checked against the
|
|
31
|
+
* EarthScope/UNAVCO BINEX definition [1].
|
|
32
|
+
*
|
|
33
|
+
* [1] UNAVCO/EarthScope, BINEX: Binary exchange format
|
|
34
|
+
* (http://binex.unavco.org/binex.html).
|
|
35
|
+
*/
|
|
36
|
+
/** BINEX 2-byte CRC (x^16+x^12+x^5+1), init 0, MSB-first. */
|
|
37
|
+
declare function binexCrc16(data: Uint8Array, start: number, end: number): number;
|
|
38
|
+
/**
|
|
39
|
+
* BINEX 4-byte CRC (poly 0x04C11DB7), init 0, MSB-first. Not exercised
|
|
40
|
+
* by any known fixture (obs/eph records use XOR-8 or CRC16); implemented
|
|
41
|
+
* to spec but fixture-unverified.
|
|
42
|
+
*/
|
|
43
|
+
declare function binexCrc32(data: Uint8Array, start: number, end: number): number;
|
|
44
|
+
/** 8-bit XOR checksum of `data[start, end)`. */
|
|
45
|
+
declare function binexCsum8(data: Uint8Array, start: number, end: number): number;
|
|
46
|
+
/**
|
|
47
|
+
* Decode a BINEX ubnxi (1–4 byte unsigned integer, MSB-first) at
|
|
48
|
+
* `data[pos]`. The first up-to-three bytes contribute their low 7 bits
|
|
49
|
+
* with the high bit as a continuation flag; a fourth byte (reached only
|
|
50
|
+
* when the first three all continue) contributes all 8 bits. Returns the
|
|
51
|
+
* value and the number of bytes consumed (1–4). Ported from RTKLIB
|
|
52
|
+
* getbnxi.
|
|
53
|
+
*/
|
|
54
|
+
declare function getBnxi(data: Uint8Array, pos: number): {
|
|
55
|
+
value: number;
|
|
56
|
+
size: number;
|
|
57
|
+
};
|
|
58
|
+
/** One checksum-valid forward BINEX record located in a byte stream. */
|
|
59
|
+
interface BinexRecord {
|
|
60
|
+
/** Record ID (e.g. 0x01, 0x7f). */
|
|
61
|
+
id: number;
|
|
62
|
+
/** Offset of the sync byte. */
|
|
63
|
+
start: number;
|
|
64
|
+
/** Offset of the message body (first byte after the length field). */
|
|
65
|
+
body: number;
|
|
66
|
+
/** Message body length in bytes. */
|
|
67
|
+
len: number;
|
|
68
|
+
/** True when the sync selects little-endian field byte order. */
|
|
69
|
+
littleEndian: boolean;
|
|
70
|
+
/** True when the sync selects the enhanced-CRC model. */
|
|
71
|
+
enhanced: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Iterate every checksum-valid forward BINEX record in `data`. Corrupt
|
|
75
|
+
* or truncated candidates resync at the next byte; checksum failures
|
|
76
|
+
* additionally increment `stats.badCrc`. Reverse-readable records are
|
|
77
|
+
* not recognised.
|
|
78
|
+
*/
|
|
79
|
+
declare function binexRecords(data: Uint8Array, view: DataView, stats: {
|
|
80
|
+
badCrc: number;
|
|
81
|
+
}): Generator<BinexRecord>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* BINEX record 0x01 — decoded GNSS ephemeris — with the per-constellation
|
|
85
|
+
* subrecords laid out in RTKLIB src/rcv/binex.c:
|
|
86
|
+
*
|
|
87
|
+
* 0x01-01 GPS LNAV (decode_bnx_01_01)
|
|
88
|
+
* 0x01-02 GLONASS (decode_bnx_01_02)
|
|
89
|
+
* 0x01-03 SBAS (decode_bnx_01_03)
|
|
90
|
+
* 0x01-04 Galileo (decode_bnx_01_04)
|
|
91
|
+
* 0x01-05 BeiDou-2/3 (decode_bnx_01_05)
|
|
92
|
+
* 0x01-06 QZSS (decode_bnx_01_06)
|
|
93
|
+
*
|
|
94
|
+
* Ported from RTKLIB demo5/2.4.3 src/rcv/binex.c (Copyright (c) 2013-2018
|
|
95
|
+
* T. Takasu, BSD-2-Clause) and cross-checked against the EarthScope/UNAVCO
|
|
96
|
+
* BINEX definition and its reference RINEX fixtures.
|
|
97
|
+
*
|
|
98
|
+
* Output records mirror what `parseNavFile` produces for the equivalent
|
|
99
|
+
* RINEX navigation file, exactly like the SBF/NovAtel decoders:
|
|
100
|
+
* - the Keplerian angles M0/Ω0/ω/i0 arrive as radians on the wire (R8),
|
|
101
|
+
* while the rate terms Δn/Ω̇/İ arrive as semicircles (R4) and are
|
|
102
|
+
* scaled by π (IS-GPS-200 SC2RAD);
|
|
103
|
+
* - BeiDou epochs and weeks stay on the BDT scale (no BDT→GPST shift),
|
|
104
|
+
* matching a RINEX BDS record and the NovAtel decoder;
|
|
105
|
+
* - GLONASS state vectors are in km (PZ-90), the clock is −τ_n (the
|
|
106
|
+
* RINEX sign), the reference epoch is UTC, and `messageFrameTime` is
|
|
107
|
+
* the frame time `tof` as broadcast (seconds of day);
|
|
108
|
+
* - deferred subrecords: 0x01-00 (raw-byte ephemeris) and 0x01-14 (a
|
|
109
|
+
* newer Galileo encoding not covered by RTKLIB).
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Decode a record-0x01 ephemeris body. `body` points at the subrecord-ID
|
|
114
|
+
* byte. Returns the decoded `Ephemeris`, or null for an unsupported or
|
|
115
|
+
* malformed subrecord. Minimum body lengths mirror RTKLIB's guards.
|
|
116
|
+
*/
|
|
117
|
+
declare function decodeBinexEph(view: DataView, body: number, len: number, le: boolean): Ephemeris | null;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* BINEX (BINary EXchange) decoding — the path from a receiver BINEX log
|
|
121
|
+
* to RINEX-grade observables and broadcast ephemerides, mirroring the
|
|
122
|
+
* SBF and NovAtel decoders in this package.
|
|
123
|
+
*
|
|
124
|
+
* `parseBinex` runs a single framing pass over the byte stream and emits
|
|
125
|
+
* both observation epochs (record 0x7f-05, the obs record `convbin`
|
|
126
|
+
* reads) and decoded `Ephemeris` records (record 0x01, per-constellation
|
|
127
|
+
* subrecords). Frame walking, the ubnxi variable-length integer, the
|
|
128
|
+
* XOR-8/CRC16/CRC32 checksum models and the field layouts are ported
|
|
129
|
+
* from RTKLIB demo5/2.4.3 src/rcv/binex.c (Copyright (c) 2013-2018
|
|
130
|
+
* T. Takasu, BSD-2-Clause) and cross-checked against the
|
|
131
|
+
* EarthScope/UNAVCO BINEX definition and its reference RINEX fixtures.
|
|
132
|
+
*
|
|
133
|
+
* Framing (forward records only): a sync byte (0xE2 big-endian /
|
|
134
|
+
* 0xC2 little-endian, regular CRC; 0xE8/0xC8 enhanced CRC), the record
|
|
135
|
+
* ID, the message length (ubnxi), the body, then the checksum. The
|
|
136
|
+
* subrecord ID is the first body byte. Reverse-readable records
|
|
137
|
+
* (0xD2/0xF2/0xD8/0xF8 and terminating variants) are NOT decoded.
|
|
138
|
+
*
|
|
139
|
+
* Implemented: 0x7f-05 observations (multi-GNSS pseudorange, carrier
|
|
140
|
+
* phase, Doppler, C/N0, slip) and 0x01-01…06 ephemeris (GPS, GLONASS,
|
|
141
|
+
* SBAS, Galileo, BeiDou, QZSS). Deferred (counted, skipped): 0x00 site
|
|
142
|
+
* metadata, 0x02/0x03 generalized data, 0x7d/0x7e prototyping, the
|
|
143
|
+
* 0x7f-00…04 obs prototypes, 0x01-00 raw-byte ephemeris and 0x01-14.
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
interface BinexMeasurement {
|
|
147
|
+
/** RINEX PRN, e.g. "G04", "R11", "S23", "J01". */
|
|
148
|
+
prn: string;
|
|
149
|
+
/** RINEX band+attribute, e.g. "1C", "2W", "5Q", "2I". */
|
|
150
|
+
code: string;
|
|
151
|
+
/** Pseudorange (m). */
|
|
152
|
+
pr: number | null;
|
|
153
|
+
/** Carrier phase (cycles, RINEX sign), null when the frequency is unknown. */
|
|
154
|
+
cp: number | null;
|
|
155
|
+
/** Doppler (Hz), null when the record carries none for the signal. */
|
|
156
|
+
doppler: number | null;
|
|
157
|
+
/** C/N0 (dB-Hz). */
|
|
158
|
+
cn0: number | null;
|
|
159
|
+
/** Loss-of-lock / cycle-slip flag. */
|
|
160
|
+
slip: boolean;
|
|
161
|
+
}
|
|
162
|
+
interface BinexEpoch {
|
|
163
|
+
/** Receiver epoch (GPS-scale ms — same convention as the RINEX parser). */
|
|
164
|
+
timeMs: number;
|
|
165
|
+
meas: BinexMeasurement[];
|
|
166
|
+
}
|
|
167
|
+
interface BinexParseResult {
|
|
168
|
+
/** Observation epochs (record 0x7f-05), one per record. */
|
|
169
|
+
epochs: BinexEpoch[];
|
|
170
|
+
/** Broadcast ephemerides (record 0x01), duplicates suppressed. */
|
|
171
|
+
ephemerides: Ephemeris[];
|
|
172
|
+
/** Count per record/subrecord, keyed "0x7f-05" / "0x01-01" style. */
|
|
173
|
+
messageCounts: Record<string, number>;
|
|
174
|
+
/** Observation codes seen per system letter, in first-seen order. */
|
|
175
|
+
obsCodes: Record<string, string[]>;
|
|
176
|
+
/** Records whose checksum failed (corruption indicator). */
|
|
177
|
+
badCrc: number;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Decode every supported BINEX record in a byte stream: 0x7f-05
|
|
181
|
+
* observation epochs and 0x01-01…06 broadcast ephemerides. Repeated
|
|
182
|
+
* broadcasts of an unchanged ephemeris are suppressed the way RTKLIB
|
|
183
|
+
* does: GPS/Galileo/QZSS/BeiDou by issue of data (iode) plus toe and toc,
|
|
184
|
+
* GLONASS/SBAS by reference epoch and health. Other records are counted
|
|
185
|
+
* and skipped; checksum failures resync at the next byte.
|
|
186
|
+
*/
|
|
187
|
+
declare function parseBinex(data: Uint8Array): BinexParseResult;
|
|
188
|
+
|
|
189
|
+
export { type BinexEpoch, type BinexMeasurement, type BinexParseResult, type BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex };
|
package/dist/binex.d.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { E as Ephemeris } from './nav-DImXUdbp.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BINEX (BINary EXchange) framing, shared by the observation and
|
|
5
|
+
* navigation decoders.
|
|
6
|
+
*
|
|
7
|
+
* A forward-readable record is: a 1-byte sync that encodes byte order and
|
|
8
|
+
* checksum model, a record ID (ubnxi), a message length (ubnxi), the
|
|
9
|
+
* message body, then a trailing checksum whose width depends on the
|
|
10
|
+
* covered-byte count. Sync bytes (ref [1] §"Format Details"):
|
|
11
|
+
*
|
|
12
|
+
* 0xC2 little-endian, regular CRC (forward)
|
|
13
|
+
* 0xE2 big-endian, regular CRC (forward)
|
|
14
|
+
* 0xC8 little-endian, enhanced CRC (forward)
|
|
15
|
+
* 0xE8 big-endian, enhanced CRC (forward)
|
|
16
|
+
*
|
|
17
|
+
* The reverse-readable variants (0xD2/0xF2/0xD8/0xF8 leading and
|
|
18
|
+
* 0xB4/0xB0/0xE4/0xE0 terminating) are NOT decoded here (deferred).
|
|
19
|
+
*
|
|
20
|
+
* Checksum widths over the covered bytes N = recordID + lengthField +
|
|
21
|
+
* body (regular model): N ≤ 127 → 1-byte XOR; 128–4095 → CRC16
|
|
22
|
+
* (x^16+x^12+x^5+1, i.e. poly 0x1021 MSB-first, init 0); 4096–1048575 →
|
|
23
|
+
* CRC32 (poly 0x04C11DB7 MSB-first); ≥ 1048576 → 16-byte MD5. Enhanced
|
|
24
|
+
* model shifts every threshold up one step (CRC16 from 0 bytes, no XOR).
|
|
25
|
+
* The 16-byte MD5 case is not implemented (records that large do not
|
|
26
|
+
* occur in GNSS obs/eph streams); such a record is treated as bad.
|
|
27
|
+
*
|
|
28
|
+
* ubnxi (1–4 byte unsigned) and the field layouts are ported from RTKLIB
|
|
29
|
+
* demo5/2.4.3 src/rcv/binex.c (getbnxi, decode_bnx, Copyright (c)
|
|
30
|
+
* 2013-2018 T. Takasu, BSD-2-Clause) and cross-checked against the
|
|
31
|
+
* EarthScope/UNAVCO BINEX definition [1].
|
|
32
|
+
*
|
|
33
|
+
* [1] UNAVCO/EarthScope, BINEX: Binary exchange format
|
|
34
|
+
* (http://binex.unavco.org/binex.html).
|
|
35
|
+
*/
|
|
36
|
+
/** BINEX 2-byte CRC (x^16+x^12+x^5+1), init 0, MSB-first. */
|
|
37
|
+
declare function binexCrc16(data: Uint8Array, start: number, end: number): number;
|
|
38
|
+
/**
|
|
39
|
+
* BINEX 4-byte CRC (poly 0x04C11DB7), init 0, MSB-first. Not exercised
|
|
40
|
+
* by any known fixture (obs/eph records use XOR-8 or CRC16); implemented
|
|
41
|
+
* to spec but fixture-unverified.
|
|
42
|
+
*/
|
|
43
|
+
declare function binexCrc32(data: Uint8Array, start: number, end: number): number;
|
|
44
|
+
/** 8-bit XOR checksum of `data[start, end)`. */
|
|
45
|
+
declare function binexCsum8(data: Uint8Array, start: number, end: number): number;
|
|
46
|
+
/**
|
|
47
|
+
* Decode a BINEX ubnxi (1–4 byte unsigned integer, MSB-first) at
|
|
48
|
+
* `data[pos]`. The first up-to-three bytes contribute their low 7 bits
|
|
49
|
+
* with the high bit as a continuation flag; a fourth byte (reached only
|
|
50
|
+
* when the first three all continue) contributes all 8 bits. Returns the
|
|
51
|
+
* value and the number of bytes consumed (1–4). Ported from RTKLIB
|
|
52
|
+
* getbnxi.
|
|
53
|
+
*/
|
|
54
|
+
declare function getBnxi(data: Uint8Array, pos: number): {
|
|
55
|
+
value: number;
|
|
56
|
+
size: number;
|
|
57
|
+
};
|
|
58
|
+
/** One checksum-valid forward BINEX record located in a byte stream. */
|
|
59
|
+
interface BinexRecord {
|
|
60
|
+
/** Record ID (e.g. 0x01, 0x7f). */
|
|
61
|
+
id: number;
|
|
62
|
+
/** Offset of the sync byte. */
|
|
63
|
+
start: number;
|
|
64
|
+
/** Offset of the message body (first byte after the length field). */
|
|
65
|
+
body: number;
|
|
66
|
+
/** Message body length in bytes. */
|
|
67
|
+
len: number;
|
|
68
|
+
/** True when the sync selects little-endian field byte order. */
|
|
69
|
+
littleEndian: boolean;
|
|
70
|
+
/** True when the sync selects the enhanced-CRC model. */
|
|
71
|
+
enhanced: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Iterate every checksum-valid forward BINEX record in `data`. Corrupt
|
|
75
|
+
* or truncated candidates resync at the next byte; checksum failures
|
|
76
|
+
* additionally increment `stats.badCrc`. Reverse-readable records are
|
|
77
|
+
* not recognised.
|
|
78
|
+
*/
|
|
79
|
+
declare function binexRecords(data: Uint8Array, view: DataView, stats: {
|
|
80
|
+
badCrc: number;
|
|
81
|
+
}): Generator<BinexRecord>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* BINEX record 0x01 — decoded GNSS ephemeris — with the per-constellation
|
|
85
|
+
* subrecords laid out in RTKLIB src/rcv/binex.c:
|
|
86
|
+
*
|
|
87
|
+
* 0x01-01 GPS LNAV (decode_bnx_01_01)
|
|
88
|
+
* 0x01-02 GLONASS (decode_bnx_01_02)
|
|
89
|
+
* 0x01-03 SBAS (decode_bnx_01_03)
|
|
90
|
+
* 0x01-04 Galileo (decode_bnx_01_04)
|
|
91
|
+
* 0x01-05 BeiDou-2/3 (decode_bnx_01_05)
|
|
92
|
+
* 0x01-06 QZSS (decode_bnx_01_06)
|
|
93
|
+
*
|
|
94
|
+
* Ported from RTKLIB demo5/2.4.3 src/rcv/binex.c (Copyright (c) 2013-2018
|
|
95
|
+
* T. Takasu, BSD-2-Clause) and cross-checked against the EarthScope/UNAVCO
|
|
96
|
+
* BINEX definition and its reference RINEX fixtures.
|
|
97
|
+
*
|
|
98
|
+
* Output records mirror what `parseNavFile` produces for the equivalent
|
|
99
|
+
* RINEX navigation file, exactly like the SBF/NovAtel decoders:
|
|
100
|
+
* - the Keplerian angles M0/Ω0/ω/i0 arrive as radians on the wire (R8),
|
|
101
|
+
* while the rate terms Δn/Ω̇/İ arrive as semicircles (R4) and are
|
|
102
|
+
* scaled by π (IS-GPS-200 SC2RAD);
|
|
103
|
+
* - BeiDou epochs and weeks stay on the BDT scale (no BDT→GPST shift),
|
|
104
|
+
* matching a RINEX BDS record and the NovAtel decoder;
|
|
105
|
+
* - GLONASS state vectors are in km (PZ-90), the clock is −τ_n (the
|
|
106
|
+
* RINEX sign), the reference epoch is UTC, and `messageFrameTime` is
|
|
107
|
+
* the frame time `tof` as broadcast (seconds of day);
|
|
108
|
+
* - deferred subrecords: 0x01-00 (raw-byte ephemeris) and 0x01-14 (a
|
|
109
|
+
* newer Galileo encoding not covered by RTKLIB).
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Decode a record-0x01 ephemeris body. `body` points at the subrecord-ID
|
|
114
|
+
* byte. Returns the decoded `Ephemeris`, or null for an unsupported or
|
|
115
|
+
* malformed subrecord. Minimum body lengths mirror RTKLIB's guards.
|
|
116
|
+
*/
|
|
117
|
+
declare function decodeBinexEph(view: DataView, body: number, len: number, le: boolean): Ephemeris | null;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* BINEX (BINary EXchange) decoding — the path from a receiver BINEX log
|
|
121
|
+
* to RINEX-grade observables and broadcast ephemerides, mirroring the
|
|
122
|
+
* SBF and NovAtel decoders in this package.
|
|
123
|
+
*
|
|
124
|
+
* `parseBinex` runs a single framing pass over the byte stream and emits
|
|
125
|
+
* both observation epochs (record 0x7f-05, the obs record `convbin`
|
|
126
|
+
* reads) and decoded `Ephemeris` records (record 0x01, per-constellation
|
|
127
|
+
* subrecords). Frame walking, the ubnxi variable-length integer, the
|
|
128
|
+
* XOR-8/CRC16/CRC32 checksum models and the field layouts are ported
|
|
129
|
+
* from RTKLIB demo5/2.4.3 src/rcv/binex.c (Copyright (c) 2013-2018
|
|
130
|
+
* T. Takasu, BSD-2-Clause) and cross-checked against the
|
|
131
|
+
* EarthScope/UNAVCO BINEX definition and its reference RINEX fixtures.
|
|
132
|
+
*
|
|
133
|
+
* Framing (forward records only): a sync byte (0xE2 big-endian /
|
|
134
|
+
* 0xC2 little-endian, regular CRC; 0xE8/0xC8 enhanced CRC), the record
|
|
135
|
+
* ID, the message length (ubnxi), the body, then the checksum. The
|
|
136
|
+
* subrecord ID is the first body byte. Reverse-readable records
|
|
137
|
+
* (0xD2/0xF2/0xD8/0xF8 and terminating variants) are NOT decoded.
|
|
138
|
+
*
|
|
139
|
+
* Implemented: 0x7f-05 observations (multi-GNSS pseudorange, carrier
|
|
140
|
+
* phase, Doppler, C/N0, slip) and 0x01-01…06 ephemeris (GPS, GLONASS,
|
|
141
|
+
* SBAS, Galileo, BeiDou, QZSS). Deferred (counted, skipped): 0x00 site
|
|
142
|
+
* metadata, 0x02/0x03 generalized data, 0x7d/0x7e prototyping, the
|
|
143
|
+
* 0x7f-00…04 obs prototypes, 0x01-00 raw-byte ephemeris and 0x01-14.
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
interface BinexMeasurement {
|
|
147
|
+
/** RINEX PRN, e.g. "G04", "R11", "S23", "J01". */
|
|
148
|
+
prn: string;
|
|
149
|
+
/** RINEX band+attribute, e.g. "1C", "2W", "5Q", "2I". */
|
|
150
|
+
code: string;
|
|
151
|
+
/** Pseudorange (m). */
|
|
152
|
+
pr: number | null;
|
|
153
|
+
/** Carrier phase (cycles, RINEX sign), null when the frequency is unknown. */
|
|
154
|
+
cp: number | null;
|
|
155
|
+
/** Doppler (Hz), null when the record carries none for the signal. */
|
|
156
|
+
doppler: number | null;
|
|
157
|
+
/** C/N0 (dB-Hz). */
|
|
158
|
+
cn0: number | null;
|
|
159
|
+
/** Loss-of-lock / cycle-slip flag. */
|
|
160
|
+
slip: boolean;
|
|
161
|
+
}
|
|
162
|
+
interface BinexEpoch {
|
|
163
|
+
/** Receiver epoch (GPS-scale ms — same convention as the RINEX parser). */
|
|
164
|
+
timeMs: number;
|
|
165
|
+
meas: BinexMeasurement[];
|
|
166
|
+
}
|
|
167
|
+
interface BinexParseResult {
|
|
168
|
+
/** Observation epochs (record 0x7f-05), one per record. */
|
|
169
|
+
epochs: BinexEpoch[];
|
|
170
|
+
/** Broadcast ephemerides (record 0x01), duplicates suppressed. */
|
|
171
|
+
ephemerides: Ephemeris[];
|
|
172
|
+
/** Count per record/subrecord, keyed "0x7f-05" / "0x01-01" style. */
|
|
173
|
+
messageCounts: Record<string, number>;
|
|
174
|
+
/** Observation codes seen per system letter, in first-seen order. */
|
|
175
|
+
obsCodes: Record<string, string[]>;
|
|
176
|
+
/** Records whose checksum failed (corruption indicator). */
|
|
177
|
+
badCrc: number;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Decode every supported BINEX record in a byte stream: 0x7f-05
|
|
181
|
+
* observation epochs and 0x01-01…06 broadcast ephemerides. Repeated
|
|
182
|
+
* broadcasts of an unchanged ephemeris are suppressed the way RTKLIB
|
|
183
|
+
* does: GPS/Galileo/QZSS/BeiDou by issue of data (iode) plus toe and toc,
|
|
184
|
+
* GLONASS/SBAS by reference epoch and health. Other records are counted
|
|
185
|
+
* and skipped; checksum failures resync at the next byte.
|
|
186
|
+
*/
|
|
187
|
+
declare function parseBinex(data: Uint8Array): BinexParseResult;
|
|
188
|
+
|
|
189
|
+
export { type BinexEpoch, type BinexMeasurement, type BinexParseResult, type BinexRecord, binexCrc16, binexCrc32, binexCsum8, binexRecords, decodeBinexEph, getBnxi, parseBinex };
|
package/dist/binex.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
binexCrc16,
|
|
3
|
+
binexCrc32,
|
|
4
|
+
binexCsum8,
|
|
5
|
+
binexRecords,
|
|
6
|
+
decodeBinexEph,
|
|
7
|
+
getBnxi,
|
|
8
|
+
parseBinex
|
|
9
|
+
} from "./chunk-OK4GGNOO.js";
|
|
10
|
+
import "./chunk-IQ5OIMQD.js";
|
|
11
|
+
export {
|
|
12
|
+
binexCrc16,
|
|
13
|
+
binexCrc32,
|
|
14
|
+
binexCsum8,
|
|
15
|
+
binexRecords,
|
|
16
|
+
decodeBinexEph,
|
|
17
|
+
getBnxi,
|
|
18
|
+
parseBinex
|
|
19
|
+
};
|
|
@@ -43,6 +43,51 @@ function klobucharDelay(coeffs, latRad, lonRad, azRad, elRad, gpsTowSec) {
|
|
|
43
43
|
return F * (5e-9 + amp * (1 - x2 / 2 + x2 * x2 / 24));
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// src/positioning/gim.ts
|
|
47
|
+
var R_E = 6371e3;
|
|
48
|
+
var H_ION = 45e4;
|
|
49
|
+
var F_L1 = 157542e4;
|
|
50
|
+
var IONO_L1_M_PER_TECU = 403e15 / (F_L1 * F_L1);
|
|
51
|
+
function tecAtMap(grid, mapIdx, latDeg, lonDeg) {
|
|
52
|
+
const m = grid.maps[mapIdx];
|
|
53
|
+
const { lats, lons } = grid;
|
|
54
|
+
const w = lons.length;
|
|
55
|
+
const fi = (latDeg - lats[0]) / (lats[lats.length - 1] - lats[0]) * (lats.length - 1);
|
|
56
|
+
const fj = (lonDeg - lons[0]) / (lons[w - 1] - lons[0]) * (w - 1);
|
|
57
|
+
const i0 = Math.max(0, Math.min(lats.length - 2, Math.floor(fi)));
|
|
58
|
+
const j0 = Math.max(0, Math.min(w - 2, Math.floor(fj)));
|
|
59
|
+
const di = Math.max(0, Math.min(1, fi - i0));
|
|
60
|
+
const dj = Math.max(0, Math.min(1, fj - j0));
|
|
61
|
+
return m[i0 * w + j0] * (1 - di) * (1 - dj) + m[i0 * w + j0 + 1] * (1 - di) * dj + m[(i0 + 1) * w + j0] * di * (1 - dj) + m[(i0 + 1) * w + j0 + 1] * di * dj;
|
|
62
|
+
}
|
|
63
|
+
function gimVerticalTec(grid, timeMs, latDeg, lonDeg) {
|
|
64
|
+
const e = grid.epochs;
|
|
65
|
+
if (e.length === 0 || timeMs < e[0] || timeMs > e[e.length - 1]) return null;
|
|
66
|
+
let i = 0;
|
|
67
|
+
while (i < e.length - 2 && e[i + 1] <= timeMs) i++;
|
|
68
|
+
const span = e[i + 1] - e[i];
|
|
69
|
+
const f = span > 0 ? (timeMs - e[i]) / span : 0;
|
|
70
|
+
const a = tecAtMap(grid, i, latDeg, lonDeg);
|
|
71
|
+
const b = tecAtMap(grid, i + 1, latDeg, lonDeg);
|
|
72
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return null;
|
|
73
|
+
return a * (1 - f) + b * f;
|
|
74
|
+
}
|
|
75
|
+
function gimSlantIonoDelayL1(grid, latRad, lonRad, azRad, elRad, timeMs) {
|
|
76
|
+
if (elRad <= 0) return null;
|
|
77
|
+
const sinZp = R_E / (R_E + H_ION) * Math.cos(elRad);
|
|
78
|
+
const mapping = 1 / Math.sqrt(1 - sinZp * sinZp);
|
|
79
|
+
const psi = Math.PI / 2 - elRad - Math.asin(sinZp);
|
|
80
|
+
const latI = Math.asin(
|
|
81
|
+
Math.sin(latRad) * Math.cos(psi) + Math.cos(latRad) * Math.sin(psi) * Math.cos(azRad)
|
|
82
|
+
);
|
|
83
|
+
const lonI = lonRad + Math.asin(Math.sin(psi) * Math.sin(azRad) / Math.cos(latI));
|
|
84
|
+
const latDeg = latI * 180 / Math.PI;
|
|
85
|
+
const lonDeg = ((lonI * 180 / Math.PI + 540) % 360 + 360) % 360 - 180;
|
|
86
|
+
const vtec = gimVerticalTec(grid, timeMs, latDeg, lonDeg);
|
|
87
|
+
if (vtec === null) return null;
|
|
88
|
+
return IONO_L1_M_PER_TECU * vtec * mapping;
|
|
89
|
+
}
|
|
90
|
+
|
|
46
91
|
// src/positioning/lambda.ts
|
|
47
92
|
var LOOPMAX = 1e4;
|
|
48
93
|
var ROUND = (x) => Math.floor(x + 0.5);
|
|
@@ -1084,10 +1129,17 @@ var PRIMARY_FREQ_HZ = {
|
|
|
1084
1129
|
C: 1561098e3,
|
|
1085
1130
|
R: 1602e6
|
|
1086
1131
|
};
|
|
1087
|
-
var
|
|
1088
|
-
function tropoDelay2(elevationRad) {
|
|
1089
|
-
|
|
1090
|
-
|
|
1132
|
+
var F_L12 = 157542e4;
|
|
1133
|
+
function tropoDelay2(elevationRad, latRad, heightM) {
|
|
1134
|
+
if (elevationRad <= 0) return 0;
|
|
1135
|
+
const h = heightM < 0 ? 0 : heightM > 1e4 ? 1e4 : heightM;
|
|
1136
|
+
const humi = 0.7;
|
|
1137
|
+
const pres = 1013.25 * Math.pow(1 - 22557e-9 * h, 5.2568);
|
|
1138
|
+
const temp = 15 - 65e-4 * h + 273.16;
|
|
1139
|
+
const e = 6.108 * humi * Math.exp((17.15 * temp - 4684) / (temp - 38.45));
|
|
1140
|
+
const zhd = 22768e-7 * pres / (1 - 266e-5 * Math.cos(2 * latRad) - 28e-5 * (h / 1e3));
|
|
1141
|
+
const zwd = 2277e-6 * (1255 / temp + 0.05) * e;
|
|
1142
|
+
return (zhd + zwd) / Math.sin(elevationRad);
|
|
1091
1143
|
}
|
|
1092
1144
|
function sagnac2(pos, travelTimeS) {
|
|
1093
1145
|
const theta = OMEGA_E * travelTimeS;
|
|
@@ -1120,7 +1172,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
1120
1172
|
maxIterations = 15,
|
|
1121
1173
|
convergenceM = 1e-4,
|
|
1122
1174
|
tgd = true,
|
|
1123
|
-
iono
|
|
1175
|
+
iono,
|
|
1176
|
+
gim
|
|
1124
1177
|
} = opts;
|
|
1125
1178
|
const gpsTow = (timeMs - GPS_EPOCH_MS_SPP) / 1e3 % 604800;
|
|
1126
1179
|
const all = [...pseudoranges.keys()].filter((p) => ephemerides.has(p));
|
|
@@ -1147,7 +1200,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
1147
1200
|
let rows = 0;
|
|
1148
1201
|
for (const k of Object.keys(residuals2)) delete residuals2[k];
|
|
1149
1202
|
const positionSaneIter = x2 * x2 + y2 * y2 + z2 * z2 > 1e12;
|
|
1150
|
-
const [rxLat, rxLon] = positionSaneIter ? ecefToGeodetic(x2, y2, z2) : [0, 0];
|
|
1203
|
+
const [rxLat, rxLon, rxHgt] = positionSaneIter ? ecefToGeodetic(x2, y2, z2) : [0, 0, 0];
|
|
1151
1204
|
for (const prn of prns) {
|
|
1152
1205
|
const psr = pseudoranges.get(prn);
|
|
1153
1206
|
const eph = ephemerides.get(prn);
|
|
@@ -1177,11 +1230,18 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
1177
1230
|
const sinEl = Math.max(Math.sin(elev), 0.1);
|
|
1178
1231
|
weight = sinEl * sinEl;
|
|
1179
1232
|
}
|
|
1180
|
-
const tropo = troposphere && positionSane ? tropoDelay2(elev) : 0;
|
|
1233
|
+
const tropo = troposphere && positionSane ? tropoDelay2(elev, rxLat, rxHgt) : 0;
|
|
1181
1234
|
let ionoM = 0;
|
|
1182
|
-
if (iono && positionSane) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1235
|
+
if ((gim || iono) && positionSane) {
|
|
1236
|
+
let l1M = null;
|
|
1237
|
+
if (gim) l1M = gimSlantIonoDelayL1(gim, rxLat, rxLon, azim, elev, timeMs);
|
|
1238
|
+
if (l1M === null && iono) {
|
|
1239
|
+
l1M = C_LIGHT * klobucharDelay(iono, rxLat, rxLon, azim, elev, gpsTow);
|
|
1240
|
+
}
|
|
1241
|
+
if (l1M !== null) {
|
|
1242
|
+
const f = PRIMARY_FREQ_HZ[sys] ?? F_L12;
|
|
1243
|
+
ionoM = l1M * (F_L12 / f * (F_L12 / f));
|
|
1244
|
+
}
|
|
1185
1245
|
}
|
|
1186
1246
|
const predicted = rho + clk - C_LIGHT * dts + tropo + ionoM;
|
|
1187
1247
|
const v = psr - predicted;
|
|
@@ -1263,6 +1323,9 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
|
|
|
1263
1323
|
|
|
1264
1324
|
export {
|
|
1265
1325
|
klobucharDelay,
|
|
1326
|
+
IONO_L1_M_PER_TECU,
|
|
1327
|
+
gimVerticalTec,
|
|
1328
|
+
gimSlantIonoDelayL1,
|
|
1266
1329
|
lambdaSearch,
|
|
1267
1330
|
lambdaReduction,
|
|
1268
1331
|
toRtkEpoch,
|