gnss-js 1.40.0 → 1.42.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/{chunk-J2E2IKJR.js → chunk-4ER3NBP3.js} +91 -0
- package/dist/index.cjs +87 -0
- package/dist/index.js +1 -1
- package/dist/trimble.cjs +174 -0
- package/dist/trimble.d.cts +12 -6
- package/dist/trimble.d.ts +12 -6
- package/dist/trimble.js +3 -1
- package/package.json +1 -1
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getGpsLeap
|
|
3
|
+
} from "./chunk-HVXYFUCB.js";
|
|
4
|
+
|
|
1
5
|
// src/trimble/frame.ts
|
|
2
6
|
var STX = 2;
|
|
3
7
|
var ETX = 3;
|
|
@@ -50,6 +54,8 @@ var gpsMs = (week, sec) => GPS_EPOCH_MS + week * MS_PER_WEEK + sec * 1e3;
|
|
|
50
54
|
var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
|
|
51
55
|
var SUB_GPS_EPHEMERIS = 1;
|
|
52
56
|
var SUB_ION_UTC = 3;
|
|
57
|
+
var SUB_GLO_EPHEMERIS = 9;
|
|
58
|
+
var SUB_GAL_EPHEMERIS = 11;
|
|
53
59
|
var SUB_BDS_EPHEMERIS = 21;
|
|
54
60
|
function readKepler(view, base) {
|
|
55
61
|
return {
|
|
@@ -142,6 +148,78 @@ function decodeBdsEphemeris(view, base) {
|
|
|
142
148
|
...keplerCommon(r)
|
|
143
149
|
};
|
|
144
150
|
}
|
|
151
|
+
function decodeGalEphemeris(view, base) {
|
|
152
|
+
const svid = view.getUint8(base + 5);
|
|
153
|
+
if (svid < 1 || svid > 36) return null;
|
|
154
|
+
const g = (o) => view.getFloat64(base + o, false);
|
|
155
|
+
const week = view.getUint16(base + 7, false);
|
|
156
|
+
const toe = view.getUint32(base + 9, false);
|
|
157
|
+
const tocMs = gpsMs(week, toe);
|
|
158
|
+
return {
|
|
159
|
+
system: "E",
|
|
160
|
+
prn: `E${String(svid).padStart(2, "0")}`,
|
|
161
|
+
toc: sowOf(tocMs),
|
|
162
|
+
tocDate: new Date(tocMs),
|
|
163
|
+
toe,
|
|
164
|
+
week,
|
|
165
|
+
iode: view.getUint16(base + 13, false),
|
|
166
|
+
// IODnav
|
|
167
|
+
af0: g(146),
|
|
168
|
+
af1: g(154),
|
|
169
|
+
af2: g(162),
|
|
170
|
+
crs: g(19),
|
|
171
|
+
deltaN: g(27) * GPS_PI,
|
|
172
|
+
m0: g(35) * GPS_PI,
|
|
173
|
+
cuc: g(43) * GPS_PI,
|
|
174
|
+
e: g(51),
|
|
175
|
+
cus: g(59) * GPS_PI,
|
|
176
|
+
sqrtA: g(67),
|
|
177
|
+
cic: g(75) * GPS_PI,
|
|
178
|
+
omega0: g(83) * GPS_PI,
|
|
179
|
+
cis: g(91) * GPS_PI,
|
|
180
|
+
i0: g(99) * GPS_PI,
|
|
181
|
+
crc: g(107),
|
|
182
|
+
omega: g(115) * GPS_PI,
|
|
183
|
+
omegaDot: g(123) * GPS_PI,
|
|
184
|
+
idot: g(131) * GPS_PI,
|
|
185
|
+
svHealth: 0,
|
|
186
|
+
// Galileo SISA/health word not located; observed sats healthy
|
|
187
|
+
tgd: g(179)
|
|
188
|
+
// BGD E5a/E1 (the RINEX Galileo tgd slot)
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function decodeGloEphemeris(view, base) {
|
|
192
|
+
const slot = view.getUint8(base + 5);
|
|
193
|
+
if (slot < 1 || slot > 24) return null;
|
|
194
|
+
const week = view.getUint16(base + 6, false);
|
|
195
|
+
const tow = view.getUint32(base + 8, false);
|
|
196
|
+
const gpsTimeMs = gpsMs(week, tow);
|
|
197
|
+
const leapMs = getGpsLeap(new Date(gpsTimeMs)) * 1e3;
|
|
198
|
+
const tocMs = gpsTimeMs - leapMs;
|
|
199
|
+
return {
|
|
200
|
+
system: "R",
|
|
201
|
+
prn: `R${String(slot).padStart(2, "0")}`,
|
|
202
|
+
tocDate: new Date(tocMs),
|
|
203
|
+
// RINEX stores −τn as the clock bias; the record carries τn (ICD sign).
|
|
204
|
+
tauN: -view.getFloat64(base + 113, false),
|
|
205
|
+
gammaN: view.getFloat64(base + 121, false),
|
|
206
|
+
messageFrameTime: (tocMs - GPS_EPOCH_MS) / 1e3 % SEC_PER_WEEK,
|
|
207
|
+
// State vector: metres / m·s⁻¹ / m·s⁻² → km / km·s⁻¹ / km·s⁻².
|
|
208
|
+
x: view.getFloat64(base + 33, false) / 1e3,
|
|
209
|
+
xDot: view.getFloat64(base + 41, false) / 1e3,
|
|
210
|
+
xAcc: view.getFloat64(base + 49, false) / 1e3,
|
|
211
|
+
y: view.getFloat64(base + 57, false) / 1e3,
|
|
212
|
+
yDot: view.getFloat64(base + 65, false) / 1e3,
|
|
213
|
+
yAcc: view.getFloat64(base + 73, false) / 1e3,
|
|
214
|
+
z: view.getFloat64(base + 81, false) / 1e3,
|
|
215
|
+
zDot: view.getFloat64(base + 89, false) / 1e3,
|
|
216
|
+
zAcc: view.getFloat64(base + 97, false) / 1e3,
|
|
217
|
+
health: 0,
|
|
218
|
+
// Bn health bit not located; observed sats are operational
|
|
219
|
+
freqNum: view.getInt8(base + 29)
|
|
220
|
+
// FCN, signed byte (−7…+6)
|
|
221
|
+
};
|
|
222
|
+
}
|
|
145
223
|
function parseTrimbleNav(data) {
|
|
146
224
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
147
225
|
const stats = { badChecksum: 0 };
|
|
@@ -160,6 +238,12 @@ function parseTrimbleNav(data) {
|
|
|
160
238
|
if (lastIode.get(eph.prn) === eph.iode) continue;
|
|
161
239
|
lastIode.set(eph.prn, eph.iode);
|
|
162
240
|
ephemerides.push(eph);
|
|
241
|
+
} else if (sub === SUB_GAL_EPHEMERIS && f.len >= 183) {
|
|
242
|
+
const eph = decodeGalEphemeris(view, f.start);
|
|
243
|
+
if (!eph) continue;
|
|
244
|
+
if (lastIode.get(eph.prn) === eph.iode) continue;
|
|
245
|
+
lastIode.set(eph.prn, eph.iode);
|
|
246
|
+
ephemerides.push(eph);
|
|
163
247
|
} else if (sub === SUB_ION_UTC && f.len >= 102) {
|
|
164
248
|
const p = f.start;
|
|
165
249
|
const alpha = [];
|
|
@@ -171,6 +255,13 @@ function parseTrimbleNav(data) {
|
|
|
171
255
|
ionoCorrections["GPSA"] = alpha;
|
|
172
256
|
ionoCorrections["GPSB"] = beta;
|
|
173
257
|
leapSeconds = Math.trunc(view.getFloat64(p + 94, false));
|
|
258
|
+
} else if (sub === SUB_GLO_EPHEMERIS && f.len >= 125) {
|
|
259
|
+
const eph = decodeGloEphemeris(view, f.start);
|
|
260
|
+
if (!eph) continue;
|
|
261
|
+
const key = eph.tocDate.getTime();
|
|
262
|
+
if (lastIode.get(eph.prn) === key) continue;
|
|
263
|
+
lastIode.set(eph.prn, key);
|
|
264
|
+
ephemerides.push(eph);
|
|
174
265
|
}
|
|
175
266
|
}
|
|
176
267
|
return {
|
package/dist/index.cjs
CHANGED
|
@@ -9600,6 +9600,8 @@ var gpsMs3 = (week, sec) => GPS_EPOCH_MS14 + week * MS_PER_WEEK9 + sec * 1e3;
|
|
|
9600
9600
|
var sowOf4 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK10;
|
|
9601
9601
|
var SUB_GPS_EPHEMERIS = 1;
|
|
9602
9602
|
var SUB_ION_UTC = 3;
|
|
9603
|
+
var SUB_GLO_EPHEMERIS = 9;
|
|
9604
|
+
var SUB_GAL_EPHEMERIS = 11;
|
|
9603
9605
|
var SUB_BDS_EPHEMERIS = 21;
|
|
9604
9606
|
function readKepler(view, base) {
|
|
9605
9607
|
return {
|
|
@@ -9692,6 +9694,78 @@ function decodeBdsEphemeris3(view, base) {
|
|
|
9692
9694
|
...keplerCommon(r)
|
|
9693
9695
|
};
|
|
9694
9696
|
}
|
|
9697
|
+
function decodeGalEphemeris2(view, base) {
|
|
9698
|
+
const svid = view.getUint8(base + 5);
|
|
9699
|
+
if (svid < 1 || svid > 36) return null;
|
|
9700
|
+
const g = (o) => view.getFloat64(base + o, false);
|
|
9701
|
+
const week = view.getUint16(base + 7, false);
|
|
9702
|
+
const toe = view.getUint32(base + 9, false);
|
|
9703
|
+
const tocMs = gpsMs3(week, toe);
|
|
9704
|
+
return {
|
|
9705
|
+
system: "E",
|
|
9706
|
+
prn: `E${String(svid).padStart(2, "0")}`,
|
|
9707
|
+
toc: sowOf4(tocMs),
|
|
9708
|
+
tocDate: new Date(tocMs),
|
|
9709
|
+
toe,
|
|
9710
|
+
week,
|
|
9711
|
+
iode: view.getUint16(base + 13, false),
|
|
9712
|
+
// IODnav
|
|
9713
|
+
af0: g(146),
|
|
9714
|
+
af1: g(154),
|
|
9715
|
+
af2: g(162),
|
|
9716
|
+
crs: g(19),
|
|
9717
|
+
deltaN: g(27) * GPS_PI2,
|
|
9718
|
+
m0: g(35) * GPS_PI2,
|
|
9719
|
+
cuc: g(43) * GPS_PI2,
|
|
9720
|
+
e: g(51),
|
|
9721
|
+
cus: g(59) * GPS_PI2,
|
|
9722
|
+
sqrtA: g(67),
|
|
9723
|
+
cic: g(75) * GPS_PI2,
|
|
9724
|
+
omega0: g(83) * GPS_PI2,
|
|
9725
|
+
cis: g(91) * GPS_PI2,
|
|
9726
|
+
i0: g(99) * GPS_PI2,
|
|
9727
|
+
crc: g(107),
|
|
9728
|
+
omega: g(115) * GPS_PI2,
|
|
9729
|
+
omegaDot: g(123) * GPS_PI2,
|
|
9730
|
+
idot: g(131) * GPS_PI2,
|
|
9731
|
+
svHealth: 0,
|
|
9732
|
+
// Galileo SISA/health word not located; observed sats healthy
|
|
9733
|
+
tgd: g(179)
|
|
9734
|
+
// BGD E5a/E1 (the RINEX Galileo tgd slot)
|
|
9735
|
+
};
|
|
9736
|
+
}
|
|
9737
|
+
function decodeGloEphemeris2(view, base) {
|
|
9738
|
+
const slot = view.getUint8(base + 5);
|
|
9739
|
+
if (slot < 1 || slot > 24) return null;
|
|
9740
|
+
const week = view.getUint16(base + 6, false);
|
|
9741
|
+
const tow = view.getUint32(base + 8, false);
|
|
9742
|
+
const gpsTimeMs = gpsMs3(week, tow);
|
|
9743
|
+
const leapMs = getGpsLeap(new Date(gpsTimeMs)) * 1e3;
|
|
9744
|
+
const tocMs = gpsTimeMs - leapMs;
|
|
9745
|
+
return {
|
|
9746
|
+
system: "R",
|
|
9747
|
+
prn: `R${String(slot).padStart(2, "0")}`,
|
|
9748
|
+
tocDate: new Date(tocMs),
|
|
9749
|
+
// RINEX stores −τn as the clock bias; the record carries τn (ICD sign).
|
|
9750
|
+
tauN: -view.getFloat64(base + 113, false),
|
|
9751
|
+
gammaN: view.getFloat64(base + 121, false),
|
|
9752
|
+
messageFrameTime: (tocMs - GPS_EPOCH_MS14) / 1e3 % SEC_PER_WEEK10,
|
|
9753
|
+
// State vector: metres / m·s⁻¹ / m·s⁻² → km / km·s⁻¹ / km·s⁻².
|
|
9754
|
+
x: view.getFloat64(base + 33, false) / 1e3,
|
|
9755
|
+
xDot: view.getFloat64(base + 41, false) / 1e3,
|
|
9756
|
+
xAcc: view.getFloat64(base + 49, false) / 1e3,
|
|
9757
|
+
y: view.getFloat64(base + 57, false) / 1e3,
|
|
9758
|
+
yDot: view.getFloat64(base + 65, false) / 1e3,
|
|
9759
|
+
yAcc: view.getFloat64(base + 73, false) / 1e3,
|
|
9760
|
+
z: view.getFloat64(base + 81, false) / 1e3,
|
|
9761
|
+
zDot: view.getFloat64(base + 89, false) / 1e3,
|
|
9762
|
+
zAcc: view.getFloat64(base + 97, false) / 1e3,
|
|
9763
|
+
health: 0,
|
|
9764
|
+
// Bn health bit not located; observed sats are operational
|
|
9765
|
+
freqNum: view.getInt8(base + 29)
|
|
9766
|
+
// FCN, signed byte (−7…+6)
|
|
9767
|
+
};
|
|
9768
|
+
}
|
|
9695
9769
|
function parseTrimbleNav(data) {
|
|
9696
9770
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
9697
9771
|
const stats = { badChecksum: 0 };
|
|
@@ -9710,6 +9784,12 @@ function parseTrimbleNav(data) {
|
|
|
9710
9784
|
if (lastIode.get(eph.prn) === eph.iode) continue;
|
|
9711
9785
|
lastIode.set(eph.prn, eph.iode);
|
|
9712
9786
|
ephemerides.push(eph);
|
|
9787
|
+
} else if (sub === SUB_GAL_EPHEMERIS && f.len >= 183) {
|
|
9788
|
+
const eph = decodeGalEphemeris2(view, f.start);
|
|
9789
|
+
if (!eph) continue;
|
|
9790
|
+
if (lastIode.get(eph.prn) === eph.iode) continue;
|
|
9791
|
+
lastIode.set(eph.prn, eph.iode);
|
|
9792
|
+
ephemerides.push(eph);
|
|
9713
9793
|
} else if (sub === SUB_ION_UTC && f.len >= 102) {
|
|
9714
9794
|
const p = f.start;
|
|
9715
9795
|
const alpha = [];
|
|
@@ -9721,6 +9801,13 @@ function parseTrimbleNav(data) {
|
|
|
9721
9801
|
ionoCorrections["GPSA"] = alpha;
|
|
9722
9802
|
ionoCorrections["GPSB"] = beta;
|
|
9723
9803
|
leapSeconds = Math.trunc(view.getFloat64(p + 94, false));
|
|
9804
|
+
} else if (sub === SUB_GLO_EPHEMERIS && f.len >= 125) {
|
|
9805
|
+
const eph = decodeGloEphemeris2(view, f.start);
|
|
9806
|
+
if (!eph) continue;
|
|
9807
|
+
const key = eph.tocDate.getTime();
|
|
9808
|
+
if (lastIode.get(eph.prn) === key) continue;
|
|
9809
|
+
lastIode.set(eph.prn, key);
|
|
9810
|
+
ephemerides.push(eph);
|
|
9724
9811
|
}
|
|
9725
9812
|
}
|
|
9726
9813
|
return {
|
package/dist/index.js
CHANGED
package/dist/trimble.cjs
CHANGED
|
@@ -66,6 +66,93 @@ function* trimbleFrames(data, stats) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// src/constants/time.ts
|
|
70
|
+
var MILLISECONDS_IN_SECOND = 1e3;
|
|
71
|
+
var MILLISECONDS_GPS_TAI = 19e3;
|
|
72
|
+
var START_NTP_TIME = /* @__PURE__ */ new Date("1900-01-01T00:00:00Z");
|
|
73
|
+
|
|
74
|
+
// src/time/gnss.ts
|
|
75
|
+
function getTaiDate(date) {
|
|
76
|
+
return new Date(date.getTime() + MILLISECONDS_GPS_TAI);
|
|
77
|
+
}
|
|
78
|
+
function getNtpTime(date) {
|
|
79
|
+
return getTaiDate(date).getTime() - START_NTP_TIME.getTime();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/time/utc.ts
|
|
83
|
+
var LEAP_SECONDS_TABLE = [
|
|
84
|
+
[3692217600, 37],
|
|
85
|
+
// 1 Jan 2017
|
|
86
|
+
[3644697600, 36],
|
|
87
|
+
// 1 Jul 2015
|
|
88
|
+
[3550089600, 35],
|
|
89
|
+
// 1 Jul 2012
|
|
90
|
+
[3439756800, 34],
|
|
91
|
+
// 1 Jan 2009
|
|
92
|
+
[3345062400, 33],
|
|
93
|
+
// 1 Jan 2006
|
|
94
|
+
[3124137600, 32],
|
|
95
|
+
// 1 Jan 1999
|
|
96
|
+
[3076704e3, 31],
|
|
97
|
+
// 1 Jul 1997
|
|
98
|
+
[3029443200, 30],
|
|
99
|
+
// 1 Jan 1996
|
|
100
|
+
[2982009600, 29],
|
|
101
|
+
// 1 Jul 1994
|
|
102
|
+
[2950473600, 28],
|
|
103
|
+
// 1 Jul 1993
|
|
104
|
+
[2918937600, 27],
|
|
105
|
+
// 1 Jul 1992
|
|
106
|
+
[2871676800, 26],
|
|
107
|
+
// 1 Jan 1991
|
|
108
|
+
[2840140800, 25],
|
|
109
|
+
// 1 Jan 1990
|
|
110
|
+
[2776982400, 24],
|
|
111
|
+
// 1 Jan 1988
|
|
112
|
+
[2698012800, 23],
|
|
113
|
+
// 1 Jul 1985
|
|
114
|
+
[2634854400, 22],
|
|
115
|
+
// 1 Jul 1983
|
|
116
|
+
[2603318400, 21],
|
|
117
|
+
// 1 Jul 1982
|
|
118
|
+
[2571782400, 20],
|
|
119
|
+
// 1 Jul 1981
|
|
120
|
+
[2524521600, 19],
|
|
121
|
+
// 1 Jan 1980
|
|
122
|
+
[2492985600, 18],
|
|
123
|
+
// 1 Jan 1979
|
|
124
|
+
[2461449600, 17],
|
|
125
|
+
// 1 Jan 1978
|
|
126
|
+
[2429913600, 16],
|
|
127
|
+
// 1 Jan 1977
|
|
128
|
+
[2398291200, 15],
|
|
129
|
+
// 1 Jan 1976
|
|
130
|
+
[2366755200, 14],
|
|
131
|
+
// 1 Jan 1975
|
|
132
|
+
[2335219200, 13],
|
|
133
|
+
// 1 Jan 1974
|
|
134
|
+
[2303683200, 12],
|
|
135
|
+
// 1 Jan 1973
|
|
136
|
+
[2287785600, 11],
|
|
137
|
+
// 1 Jul 1972
|
|
138
|
+
[2272060800, 10]
|
|
139
|
+
// 1 Jan 1972
|
|
140
|
+
];
|
|
141
|
+
function getLeap(date) {
|
|
142
|
+
const ntp_time = getNtpTime(date);
|
|
143
|
+
for (const [timestamp, leapSeconds] of LEAP_SECONDS_TABLE) {
|
|
144
|
+
if (ntp_time / MILLISECONDS_IN_SECOND - leapSeconds >= timestamp) {
|
|
145
|
+
return leapSeconds;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return 8;
|
|
149
|
+
}
|
|
150
|
+
function getGpsLeap(date) {
|
|
151
|
+
const leap_seconds = getLeap(date);
|
|
152
|
+
if (leap_seconds < 0) return 0;
|
|
153
|
+
return leap_seconds - 19;
|
|
154
|
+
}
|
|
155
|
+
|
|
69
156
|
// src/trimble/nav.ts
|
|
70
157
|
var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
|
|
71
158
|
var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
@@ -78,6 +165,8 @@ var gpsMs = (week, sec) => GPS_EPOCH_MS + week * MS_PER_WEEK + sec * 1e3;
|
|
|
78
165
|
var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
|
|
79
166
|
var SUB_GPS_EPHEMERIS = 1;
|
|
80
167
|
var SUB_ION_UTC = 3;
|
|
168
|
+
var SUB_GLO_EPHEMERIS = 9;
|
|
169
|
+
var SUB_GAL_EPHEMERIS = 11;
|
|
81
170
|
var SUB_BDS_EPHEMERIS = 21;
|
|
82
171
|
function readKepler(view, base) {
|
|
83
172
|
return {
|
|
@@ -170,6 +259,78 @@ function decodeBdsEphemeris(view, base) {
|
|
|
170
259
|
...keplerCommon(r)
|
|
171
260
|
};
|
|
172
261
|
}
|
|
262
|
+
function decodeGalEphemeris(view, base) {
|
|
263
|
+
const svid = view.getUint8(base + 5);
|
|
264
|
+
if (svid < 1 || svid > 36) return null;
|
|
265
|
+
const g = (o) => view.getFloat64(base + o, false);
|
|
266
|
+
const week = view.getUint16(base + 7, false);
|
|
267
|
+
const toe = view.getUint32(base + 9, false);
|
|
268
|
+
const tocMs = gpsMs(week, toe);
|
|
269
|
+
return {
|
|
270
|
+
system: "E",
|
|
271
|
+
prn: `E${String(svid).padStart(2, "0")}`,
|
|
272
|
+
toc: sowOf(tocMs),
|
|
273
|
+
tocDate: new Date(tocMs),
|
|
274
|
+
toe,
|
|
275
|
+
week,
|
|
276
|
+
iode: view.getUint16(base + 13, false),
|
|
277
|
+
// IODnav
|
|
278
|
+
af0: g(146),
|
|
279
|
+
af1: g(154),
|
|
280
|
+
af2: g(162),
|
|
281
|
+
crs: g(19),
|
|
282
|
+
deltaN: g(27) * GPS_PI,
|
|
283
|
+
m0: g(35) * GPS_PI,
|
|
284
|
+
cuc: g(43) * GPS_PI,
|
|
285
|
+
e: g(51),
|
|
286
|
+
cus: g(59) * GPS_PI,
|
|
287
|
+
sqrtA: g(67),
|
|
288
|
+
cic: g(75) * GPS_PI,
|
|
289
|
+
omega0: g(83) * GPS_PI,
|
|
290
|
+
cis: g(91) * GPS_PI,
|
|
291
|
+
i0: g(99) * GPS_PI,
|
|
292
|
+
crc: g(107),
|
|
293
|
+
omega: g(115) * GPS_PI,
|
|
294
|
+
omegaDot: g(123) * GPS_PI,
|
|
295
|
+
idot: g(131) * GPS_PI,
|
|
296
|
+
svHealth: 0,
|
|
297
|
+
// Galileo SISA/health word not located; observed sats healthy
|
|
298
|
+
tgd: g(179)
|
|
299
|
+
// BGD E5a/E1 (the RINEX Galileo tgd slot)
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function decodeGloEphemeris(view, base) {
|
|
303
|
+
const slot = view.getUint8(base + 5);
|
|
304
|
+
if (slot < 1 || slot > 24) return null;
|
|
305
|
+
const week = view.getUint16(base + 6, false);
|
|
306
|
+
const tow = view.getUint32(base + 8, false);
|
|
307
|
+
const gpsTimeMs = gpsMs(week, tow);
|
|
308
|
+
const leapMs = getGpsLeap(new Date(gpsTimeMs)) * 1e3;
|
|
309
|
+
const tocMs = gpsTimeMs - leapMs;
|
|
310
|
+
return {
|
|
311
|
+
system: "R",
|
|
312
|
+
prn: `R${String(slot).padStart(2, "0")}`,
|
|
313
|
+
tocDate: new Date(tocMs),
|
|
314
|
+
// RINEX stores −τn as the clock bias; the record carries τn (ICD sign).
|
|
315
|
+
tauN: -view.getFloat64(base + 113, false),
|
|
316
|
+
gammaN: view.getFloat64(base + 121, false),
|
|
317
|
+
messageFrameTime: (tocMs - GPS_EPOCH_MS) / 1e3 % SEC_PER_WEEK,
|
|
318
|
+
// State vector: metres / m·s⁻¹ / m·s⁻² → km / km·s⁻¹ / km·s⁻².
|
|
319
|
+
x: view.getFloat64(base + 33, false) / 1e3,
|
|
320
|
+
xDot: view.getFloat64(base + 41, false) / 1e3,
|
|
321
|
+
xAcc: view.getFloat64(base + 49, false) / 1e3,
|
|
322
|
+
y: view.getFloat64(base + 57, false) / 1e3,
|
|
323
|
+
yDot: view.getFloat64(base + 65, false) / 1e3,
|
|
324
|
+
yAcc: view.getFloat64(base + 73, false) / 1e3,
|
|
325
|
+
z: view.getFloat64(base + 81, false) / 1e3,
|
|
326
|
+
zDot: view.getFloat64(base + 89, false) / 1e3,
|
|
327
|
+
zAcc: view.getFloat64(base + 97, false) / 1e3,
|
|
328
|
+
health: 0,
|
|
329
|
+
// Bn health bit not located; observed sats are operational
|
|
330
|
+
freqNum: view.getInt8(base + 29)
|
|
331
|
+
// FCN, signed byte (−7…+6)
|
|
332
|
+
};
|
|
333
|
+
}
|
|
173
334
|
function parseTrimbleNav(data) {
|
|
174
335
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
175
336
|
const stats = { badChecksum: 0 };
|
|
@@ -188,6 +349,12 @@ function parseTrimbleNav(data) {
|
|
|
188
349
|
if (lastIode.get(eph.prn) === eph.iode) continue;
|
|
189
350
|
lastIode.set(eph.prn, eph.iode);
|
|
190
351
|
ephemerides.push(eph);
|
|
352
|
+
} else if (sub === SUB_GAL_EPHEMERIS && f.len >= 183) {
|
|
353
|
+
const eph = decodeGalEphemeris(view, f.start);
|
|
354
|
+
if (!eph) continue;
|
|
355
|
+
if (lastIode.get(eph.prn) === eph.iode) continue;
|
|
356
|
+
lastIode.set(eph.prn, eph.iode);
|
|
357
|
+
ephemerides.push(eph);
|
|
191
358
|
} else if (sub === SUB_ION_UTC && f.len >= 102) {
|
|
192
359
|
const p = f.start;
|
|
193
360
|
const alpha = [];
|
|
@@ -199,6 +366,13 @@ function parseTrimbleNav(data) {
|
|
|
199
366
|
ionoCorrections["GPSA"] = alpha;
|
|
200
367
|
ionoCorrections["GPSB"] = beta;
|
|
201
368
|
leapSeconds = Math.trunc(view.getFloat64(p + 94, false));
|
|
369
|
+
} else if (sub === SUB_GLO_EPHEMERIS && f.len >= 125) {
|
|
370
|
+
const eph = decodeGloEphemeris(view, f.start);
|
|
371
|
+
if (!eph) continue;
|
|
372
|
+
const key = eph.tocDate.getTime();
|
|
373
|
+
if (lastIode.get(eph.prn) === key) continue;
|
|
374
|
+
lastIode.set(eph.prn, key);
|
|
375
|
+
ephemerides.push(eph);
|
|
202
376
|
}
|
|
203
377
|
}
|
|
204
378
|
return {
|
package/dist/trimble.d.cts
CHANGED
|
@@ -25,7 +25,8 @@ declare function trimbleFrames(data: Uint8Array, stats: {
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Trimble RT17/RT27 navigation-message decoding: RETSVDATA (0x55)
|
|
28
|
-
* GPS ephemeris (subtype 1),
|
|
28
|
+
* GPS ephemeris (subtype 1), GLONASS ephemeris (subtype 9), Galileo
|
|
29
|
+
* ephemeris (subtype 11), BeiDou ephemeris (subtype 21) and ION / UTC
|
|
29
30
|
* data (subtype 3).
|
|
30
31
|
*
|
|
31
32
|
* The GPS path is ported field-for-field from RTKLIB's `src/rcv/rt17.c`
|
|
@@ -42,8 +43,12 @@ declare function trimbleFrames(data: Uint8Array, stats: {
|
|
|
42
43
|
* RINEX 3 navigation record and match the NovAtel/SBF ephemeris paths
|
|
43
44
|
* (GPS: GPS-scale `tocDate`; BeiDou: naive-BDT `tocDate`; angles in rad).
|
|
44
45
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
46
|
+
* The subtype→constellation map is confirmed by RTKLIB's rt17.c
|
|
47
|
+
* RETSVDATA table (st[]: 1 = GPS Eph, 9 = GLONASS Eph, 11 = Galileo Eph,
|
|
48
|
+
* 21 = BeiDou Eph) — but RTKLIB only *decodes* subtypes 1 and 3, so the
|
|
49
|
+
* GLONASS/BeiDou field layouts here were reverse-engineered and validated
|
|
50
|
+
* against physics + the 2026-07-27 BRDC. Still not decoded: the
|
|
51
|
+
* QZSS / almanac subtypes (13/23/27 on this stream).
|
|
47
52
|
*/
|
|
48
53
|
|
|
49
54
|
interface TrimbleNavResult {
|
|
@@ -62,9 +67,10 @@ interface TrimbleNavResult {
|
|
|
62
67
|
badChecksum: number;
|
|
63
68
|
}
|
|
64
69
|
/**
|
|
65
|
-
* Decode every RETSVDATA navigation message (GPS
|
|
66
|
-
* a Trimble binary byte stream. Repeated
|
|
67
|
-
* ephemeris are suppressed by issue of data
|
|
70
|
+
* Decode every RETSVDATA navigation message (GPS/BeiDou/GLONASS
|
|
71
|
+
* ephemeris, ION/UTC) in a Trimble binary byte stream. Repeated
|
|
72
|
+
* broadcasts of an unchanged ephemeris are suppressed by issue of data
|
|
73
|
+
* (Keplerian) / reference epoch (GLONASS), as RTKLIB does.
|
|
68
74
|
*/
|
|
69
75
|
declare function parseTrimbleNav(data: Uint8Array): TrimbleNavResult;
|
|
70
76
|
|
package/dist/trimble.d.ts
CHANGED
|
@@ -25,7 +25,8 @@ declare function trimbleFrames(data: Uint8Array, stats: {
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Trimble RT17/RT27 navigation-message decoding: RETSVDATA (0x55)
|
|
28
|
-
* GPS ephemeris (subtype 1),
|
|
28
|
+
* GPS ephemeris (subtype 1), GLONASS ephemeris (subtype 9), Galileo
|
|
29
|
+
* ephemeris (subtype 11), BeiDou ephemeris (subtype 21) and ION / UTC
|
|
29
30
|
* data (subtype 3).
|
|
30
31
|
*
|
|
31
32
|
* The GPS path is ported field-for-field from RTKLIB's `src/rcv/rt17.c`
|
|
@@ -42,8 +43,12 @@ declare function trimbleFrames(data: Uint8Array, stats: {
|
|
|
42
43
|
* RINEX 3 navigation record and match the NovAtel/SBF ephemeris paths
|
|
43
44
|
* (GPS: GPS-scale `tocDate`; BeiDou: naive-BDT `tocDate`; angles in rad).
|
|
44
45
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
46
|
+
* The subtype→constellation map is confirmed by RTKLIB's rt17.c
|
|
47
|
+
* RETSVDATA table (st[]: 1 = GPS Eph, 9 = GLONASS Eph, 11 = Galileo Eph,
|
|
48
|
+
* 21 = BeiDou Eph) — but RTKLIB only *decodes* subtypes 1 and 3, so the
|
|
49
|
+
* GLONASS/BeiDou field layouts here were reverse-engineered and validated
|
|
50
|
+
* against physics + the 2026-07-27 BRDC. Still not decoded: the
|
|
51
|
+
* QZSS / almanac subtypes (13/23/27 on this stream).
|
|
47
52
|
*/
|
|
48
53
|
|
|
49
54
|
interface TrimbleNavResult {
|
|
@@ -62,9 +67,10 @@ interface TrimbleNavResult {
|
|
|
62
67
|
badChecksum: number;
|
|
63
68
|
}
|
|
64
69
|
/**
|
|
65
|
-
* Decode every RETSVDATA navigation message (GPS
|
|
66
|
-
* a Trimble binary byte stream. Repeated
|
|
67
|
-
* ephemeris are suppressed by issue of data
|
|
70
|
+
* Decode every RETSVDATA navigation message (GPS/BeiDou/GLONASS
|
|
71
|
+
* ephemeris, ION/UTC) in a Trimble binary byte stream. Repeated
|
|
72
|
+
* broadcasts of an unchanged ephemeris are suppressed by issue of data
|
|
73
|
+
* (Keplerian) / reference epoch (GLONASS), as RTKLIB does.
|
|
68
74
|
*/
|
|
69
75
|
declare function parseTrimbleNav(data: Uint8Array): TrimbleNavResult;
|
|
70
76
|
|
package/dist/trimble.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnss-js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.0",
|
|
4
4
|
"description": "Comprehensive GNSS library for JavaScript — time scales, coordinates, RINEX parsing, RTCM3 decoding, orbit computation, and signal analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|