gnss-js 1.0.0 → 1.1.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 +26 -0
- package/dist/analysis.cjs +143 -36
- package/dist/analysis.js +3 -3
- package/dist/{chunk-HKN3PUGN.js → chunk-5SPJH4MG.js} +35 -26
- package/dist/{chunk-WR7LCB52.js → chunk-BJHTBYNG.js} +15 -4
- package/dist/{chunk-Y3R57B5P.js → chunk-G3N4S3DM.js} +37 -8
- package/dist/{chunk-PRSZIWKM.js → chunk-JDO3LEPC.js} +100 -132
- package/dist/{chunk-K7WZQFBV.js → chunk-MIIM4LDY.js} +4 -1
- package/dist/{chunk-CB6EOOLA.js → chunk-OZCYOM5D.js} +70 -21
- package/dist/{chunk-SDRRAJT5.js → chunk-PGHDJFQK.js} +31 -14
- package/dist/{chunk-IS4UUDBV.js → chunk-W4YMQKWH.js} +54 -9
- package/dist/{chunk-354IRDOG.js → chunk-W5WKEV7U.js} +39 -9
- package/dist/constants.cjs +39 -9
- package/dist/constants.js +26 -26
- package/dist/coordinates.cjs +4 -1
- package/dist/coordinates.js +1 -1
- package/dist/{ephemeris-BUWzfmBy.d.cts → ephemeris-Ohl6NAfw.d.cts} +12 -2
- package/dist/{ephemeris-BUWzfmBy.d.ts → ephemeris-Ohl6NAfw.d.ts} +12 -2
- package/dist/index.cjs +366 -220
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +40 -34
- package/dist/ntrip.cjs +15 -4
- package/dist/ntrip.js +1 -1
- package/dist/orbit.cjs +31 -14
- package/dist/orbit.d.cts +1 -1
- package/dist/orbit.d.ts +1 -1
- package/dist/orbit.js +2 -1
- package/dist/rinex.cjs +123 -29
- package/dist/rinex.js +3 -3
- package/dist/rtcm3.cjs +123 -131
- package/dist/rtcm3.d.cts +9 -3
- package/dist/rtcm3.d.ts +9 -3
- package/dist/rtcm3.js +9 -2
- package/dist/signals.cjs +55 -26
- package/dist/signals.d.cts +26 -26
- package/dist/signals.d.ts +26 -26
- package/dist/signals.js +2 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -167,6 +167,32 @@ Every exported function has JSDoc with `@param` and `@returns` annotations inclu
|
|
|
167
167
|
- **Class-based API** — Ergonomic wrappers like `GnssTime.fromUtc(date).gps` and `Position.fromGeodetic(lat, lon, h).utm` on top of the existing functional core
|
|
168
168
|
- **RINEX observation writers** (RINEX 2/3/4 with gzip compression)
|
|
169
169
|
|
|
170
|
+
## Development
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pnpm install
|
|
174
|
+
bash scripts/fetch-test-data.sh # RINEX test fixtures (~5 MB, checksummed; gitignored)
|
|
175
|
+
pnpm test
|
|
176
|
+
pnpm run lint
|
|
177
|
+
pnpm run build
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Without the fixtures, the nav-file test suites are skipped silently (`describe.skipIf`), so run the fetch script before trusting a green test run.
|
|
181
|
+
|
|
182
|
+
**Releasing**: bump the version and push the tag — CI publishes to npm via Trusted Publishing (one-time setup: npm package settings → Trusted Publisher → this repo + publish.yml) and creates the GitHub Release:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npm version patch # or minor/major; updates package.json + creates the tag
|
|
186
|
+
git push --follow-tags
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Developing against [gnsscalc](https://github.com/MiguelPuntoEs/gnsscalc)** without publishing:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
cd ../gnsscalc && pnpm link ../gnss-js # then `pnpm run build --watch` here
|
|
193
|
+
# undo with: pnpm unlink gnss-js && pnpm install
|
|
194
|
+
```
|
|
195
|
+
|
|
170
196
|
## License
|
|
171
197
|
|
|
172
198
|
This project is dual-licensed:
|
package/dist/analysis.cjs
CHANGED
|
@@ -40,9 +40,22 @@ var SYSTEM_NAMES = {
|
|
|
40
40
|
var C_LIGHT = 299792458;
|
|
41
41
|
var FREQ = {
|
|
42
42
|
G: { "1": 157542e4, "2": 12276e5, "5": 117645e4 },
|
|
43
|
-
R
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
// R bands 4/6 are the CDMA L1OC/L2OC center frequencies
|
|
44
|
+
R: { "3": 1202025e3, "4": 1600995e3, "6": 124806e4 },
|
|
45
|
+
E: {
|
|
46
|
+
"1": 157542e4,
|
|
47
|
+
"5": 117645e4,
|
|
48
|
+
"6": 127875e4,
|
|
49
|
+
"7": 120714e4,
|
|
50
|
+
"8": 1191795e3
|
|
51
|
+
},
|
|
52
|
+
C: {
|
|
53
|
+
"1": 157542e4,
|
|
54
|
+
"2": 1561098e3,
|
|
55
|
+
"5": 117645e4,
|
|
56
|
+
"6": 126852e4,
|
|
57
|
+
"7": 120714e4
|
|
58
|
+
},
|
|
46
59
|
J: { "1": 157542e4, "2": 12276e5, "5": 117645e4, "6": 127875e4 },
|
|
47
60
|
I: { "5": 117645e4, "9": 2492028e3 },
|
|
48
61
|
S: { "1": 157542e4, "5": 117645e4 }
|
|
@@ -57,11 +70,28 @@ var BAND_LABELS = {
|
|
|
57
70
|
S: { "1": "L1", "5": "L5" }
|
|
58
71
|
};
|
|
59
72
|
var DUAL_FREQ_PAIRS = {
|
|
60
|
-
G: [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
G: [
|
|
74
|
+
["1", "2"],
|
|
75
|
+
["1", "5"]
|
|
76
|
+
],
|
|
77
|
+
R: [
|
|
78
|
+
["1", "2"],
|
|
79
|
+
["1", "3"]
|
|
80
|
+
],
|
|
81
|
+
E: [
|
|
82
|
+
["1", "5"],
|
|
83
|
+
["1", "7"],
|
|
84
|
+
["1", "6"]
|
|
85
|
+
],
|
|
86
|
+
C: [
|
|
87
|
+
["2", "7"],
|
|
88
|
+
["2", "6"],
|
|
89
|
+
["1", "5"]
|
|
90
|
+
],
|
|
91
|
+
J: [
|
|
92
|
+
["1", "2"],
|
|
93
|
+
["1", "5"]
|
|
94
|
+
],
|
|
65
95
|
I: [["5", "9"]],
|
|
66
96
|
S: [["1", "5"]]
|
|
67
97
|
};
|
|
@@ -69,7 +99,6 @@ var GLO_F1_BASE = 1602e6;
|
|
|
69
99
|
var GLO_F1_STEP = 562500;
|
|
70
100
|
var GLO_F2_BASE = 1246e6;
|
|
71
101
|
var GLO_F2_STEP = 437500;
|
|
72
|
-
var GLO_F3 = 1202025e3;
|
|
73
102
|
var GLO_CHANNEL_FALLBACK = {
|
|
74
103
|
R01: 1,
|
|
75
104
|
R02: -4,
|
|
@@ -105,11 +134,11 @@ function buildGloChannelMap(slots) {
|
|
|
105
134
|
return map;
|
|
106
135
|
}
|
|
107
136
|
function gloFreq(gloChannels, prn, band) {
|
|
137
|
+
if (band === "3" || band === "4" || band === "6") return FREQ.R[band];
|
|
108
138
|
const k = gloChannels[prn];
|
|
109
139
|
if (k === void 0) return void 0;
|
|
110
140
|
if (band === "1") return GLO_F1_BASE + k * GLO_F1_STEP;
|
|
111
141
|
if (band === "2") return GLO_F2_BASE + k * GLO_F2_STEP;
|
|
112
|
-
if (band === "3") return GLO_F3;
|
|
113
142
|
return void 0;
|
|
114
143
|
}
|
|
115
144
|
function getFreq(gloChannels, prn, band) {
|
|
@@ -270,8 +299,18 @@ var MultipathAccumulator = class {
|
|
|
270
299
|
time: t,
|
|
271
300
|
mp: arc.rawMp[i] - mean
|
|
272
301
|
}));
|
|
273
|
-
const rms = Math.sqrt(
|
|
274
|
-
|
|
302
|
+
const rms = Math.sqrt(
|
|
303
|
+
points.reduce((s, p) => s + p.mp * p.mp, 0) / points.length
|
|
304
|
+
);
|
|
305
|
+
this.results.push({
|
|
306
|
+
prn,
|
|
307
|
+
system: sys,
|
|
308
|
+
band,
|
|
309
|
+
refBand,
|
|
310
|
+
label,
|
|
311
|
+
points,
|
|
312
|
+
rms
|
|
313
|
+
});
|
|
275
314
|
}
|
|
276
315
|
state.arc = { times: [], rawMp: [] };
|
|
277
316
|
}
|
|
@@ -425,10 +464,20 @@ var CycleSlipAccumulator = class {
|
|
|
425
464
|
const sigKey = `${sys}:${signal}`;
|
|
426
465
|
this.countEpoch(sigKey);
|
|
427
466
|
const stddev = mwSmoothStddev(prevSmooth);
|
|
428
|
-
const threshold = Math.max(
|
|
467
|
+
const threshold = Math.max(
|
|
468
|
+
MW_MIN_THRESHOLD,
|
|
469
|
+
MW_SIGMA_FACTOR * stddev
|
|
470
|
+
);
|
|
429
471
|
const deviation = Math.abs(mw - prevSmooth.mean);
|
|
430
472
|
if (deviation > threshold) {
|
|
431
|
-
this.recordSlip(
|
|
473
|
+
this.recordSlip(
|
|
474
|
+
sigKey,
|
|
475
|
+
time,
|
|
476
|
+
prn,
|
|
477
|
+
signal,
|
|
478
|
+
deviation,
|
|
479
|
+
/* @__PURE__ */ new Set([b1, b2])
|
|
480
|
+
);
|
|
432
481
|
mwSmooth.set(pairKey, mwSmoothInit(mw));
|
|
433
482
|
} else {
|
|
434
483
|
mwSmooth.set(pairKey, mwSmoothUpdate(prevSmooth, mw));
|
|
@@ -483,7 +532,13 @@ var CycleSlipAccumulator = class {
|
|
|
483
532
|
}
|
|
484
533
|
}
|
|
485
534
|
}
|
|
486
|
-
this.prev.set(prn, {
|
|
535
|
+
this.prev.set(prn, {
|
|
536
|
+
time,
|
|
537
|
+
mwSmooth,
|
|
538
|
+
gf: gfMap,
|
|
539
|
+
phase: phaseM,
|
|
540
|
+
code: codeM
|
|
541
|
+
});
|
|
487
542
|
};
|
|
488
543
|
finalize() {
|
|
489
544
|
const satSlipCounts = {};
|
|
@@ -491,7 +546,10 @@ var CycleSlipAccumulator = class {
|
|
|
491
546
|
satSlipCounts[ev.prn] = (satSlipCounts[ev.prn] ?? 0) + 1;
|
|
492
547
|
}
|
|
493
548
|
const signalStats = [];
|
|
494
|
-
const allKeys = /* @__PURE__ */ new Set([
|
|
549
|
+
const allKeys = /* @__PURE__ */ new Set([
|
|
550
|
+
...this.signalEpochs.keys(),
|
|
551
|
+
...this.signalSlips.keys()
|
|
552
|
+
]);
|
|
495
553
|
for (const sigKey of allKeys) {
|
|
496
554
|
const [sys, ...rest] = sigKey.split(":");
|
|
497
555
|
const signal = rest.join(":");
|
|
@@ -650,10 +708,20 @@ function parseCrxDataLine(line, ntype) {
|
|
|
650
708
|
if (token.length >= 3 && token[1] === "&") {
|
|
651
709
|
const arcOrder = parseInt(token[0]);
|
|
652
710
|
const value = parseInt(token.substring(2));
|
|
653
|
-
fields.push({
|
|
711
|
+
fields.push({
|
|
712
|
+
empty: false,
|
|
713
|
+
init: true,
|
|
714
|
+
arcOrder,
|
|
715
|
+
value: isNaN(value) ? 0 : value
|
|
716
|
+
});
|
|
654
717
|
} else {
|
|
655
718
|
const value = parseInt(token);
|
|
656
|
-
fields.push({
|
|
719
|
+
fields.push({
|
|
720
|
+
empty: false,
|
|
721
|
+
init: false,
|
|
722
|
+
arcOrder: -1,
|
|
723
|
+
value: isNaN(value) ? 0 : value
|
|
724
|
+
});
|
|
657
725
|
}
|
|
658
726
|
}
|
|
659
727
|
const flagStr = pos < line.length ? line.substring(pos) : "";
|
|
@@ -663,7 +731,10 @@ function crxDecompress(prev, field) {
|
|
|
663
731
|
if (field.init) {
|
|
664
732
|
const a = new Array(Math.max(field.arcOrder + 1, 1)).fill(0);
|
|
665
733
|
a[0] = field.value;
|
|
666
|
-
return {
|
|
734
|
+
return {
|
|
735
|
+
state: { accum: a, order: 0, arcOrder: field.arcOrder },
|
|
736
|
+
result: field.value
|
|
737
|
+
};
|
|
667
738
|
}
|
|
668
739
|
if (!prev) {
|
|
669
740
|
return { state: { accum: [0], order: 0, arcOrder: 0 }, result: 0 };
|
|
@@ -676,7 +747,10 @@ function crxDecompress(prev, field) {
|
|
|
676
747
|
for (let k = order; k >= 1; k--) {
|
|
677
748
|
accum[k - 1] += accum[k];
|
|
678
749
|
}
|
|
679
|
-
return {
|
|
750
|
+
return {
|
|
751
|
+
state: { accum, order, arcOrder: prev.arcOrder },
|
|
752
|
+
result: accum[0]
|
|
753
|
+
};
|
|
680
754
|
}
|
|
681
755
|
|
|
682
756
|
// src/rinex/parser.ts
|
|
@@ -684,7 +758,9 @@ var CHUNK_SIZE = 2 * 1024 * 1024;
|
|
|
684
758
|
var yieldToMain = () => new Promise((r) => setTimeout(r, 0));
|
|
685
759
|
var noYield = () => Promise.resolve();
|
|
686
760
|
var SYSTEM_ORDER = ["G", "R", "E", "C", "J", "I", "S"];
|
|
687
|
-
var SYSTEM_RANK = Object.fromEntries(
|
|
761
|
+
var SYSTEM_RANK = Object.fromEntries(
|
|
762
|
+
SYSTEM_ORDER.map((s, i) => [s, i])
|
|
763
|
+
);
|
|
688
764
|
function systemCmp(a, b) {
|
|
689
765
|
const ra = SYSTEM_RANK[a.charAt(0)] ?? 99;
|
|
690
766
|
const rb = SYSTEM_RANK[b.charAt(0)] ?? 99;
|
|
@@ -867,7 +943,11 @@ function parseEpochLine3(line) {
|
|
|
867
943
|
const wholeSec = Math.floor(sc);
|
|
868
944
|
const ms = Math.round((sc - wholeSec) * 1e3);
|
|
869
945
|
const time = Date.UTC(yr, mo - 1, dy, hr, mn, wholeSec, ms);
|
|
870
|
-
return {
|
|
946
|
+
return {
|
|
947
|
+
time,
|
|
948
|
+
flag: isNaN(flag) ? 0 : flag,
|
|
949
|
+
numSats: isNaN(numSats) ? 0 : numSats
|
|
950
|
+
};
|
|
871
951
|
}
|
|
872
952
|
function parseEpochLine2(line) {
|
|
873
953
|
const yr2 = parseInt(line.substring(1, 3));
|
|
@@ -889,7 +969,12 @@ function parseEpochLine2(line) {
|
|
|
889
969
|
const id = satPart.substring(i, i + 3).trim();
|
|
890
970
|
if (id) satIds.push(id);
|
|
891
971
|
}
|
|
892
|
-
return {
|
|
972
|
+
return {
|
|
973
|
+
time,
|
|
974
|
+
flag: isNaN(flag) ? 0 : flag,
|
|
975
|
+
numSats: isNaN(numSats) ? 0 : numSats,
|
|
976
|
+
satIds
|
|
977
|
+
};
|
|
893
978
|
}
|
|
894
979
|
async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode) {
|
|
895
980
|
const skipEpochs = !!workerMode;
|
|
@@ -942,16 +1027,19 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
942
1027
|
if (!skipEpochs) {
|
|
943
1028
|
const snrBySys = {};
|
|
944
1029
|
for (const [sys, vals] of Object.entries(snrPerSystemAccum)) {
|
|
945
|
-
if (vals.length > 0)
|
|
1030
|
+
if (vals.length > 0)
|
|
1031
|
+
snrBySys[sys] = vals.reduce((a, b) => a + b, 0) / vals.length;
|
|
946
1032
|
}
|
|
947
1033
|
const meanSnr = snrValues.length > 0 ? snrValues.reduce((a, b) => a + b, 0) / snrValues.length : null;
|
|
948
1034
|
const snrPerSat = {};
|
|
949
1035
|
for (const [prn, vals] of Object.entries(snrPerSatAccum)) {
|
|
950
|
-
if (vals.length > 0)
|
|
1036
|
+
if (vals.length > 0)
|
|
1037
|
+
snrPerSat[prn] = vals.reduce((a, b) => a + b, 0) / vals.length;
|
|
951
1038
|
}
|
|
952
1039
|
const snrPerSatBand = {};
|
|
953
1040
|
for (const [key, vals] of Object.entries(snrPerSatBandAccum)) {
|
|
954
|
-
if (vals.length > 0)
|
|
1041
|
+
if (vals.length > 0)
|
|
1042
|
+
snrPerSatBand[key] = vals.reduce((a, b) => a + b, 0) / vals.length;
|
|
955
1043
|
}
|
|
956
1044
|
epochs.push({
|
|
957
1045
|
time: epochInfo.time,
|
|
@@ -996,7 +1084,8 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
996
1084
|
const codes = header.obsTypes[sys] ?? [];
|
|
997
1085
|
if (onSatObs && epochInfo) {
|
|
998
1086
|
const values = new Array(codes.length);
|
|
999
|
-
for (let i = 0; i < codes.length; i++)
|
|
1087
|
+
for (let i = 0; i < codes.length; i++)
|
|
1088
|
+
values[i] = readObsValue(obsLine, i);
|
|
1000
1089
|
if (!skipEpochs) {
|
|
1001
1090
|
for (const { idx, band } of getSnrIndices(sys)) {
|
|
1002
1091
|
const val = values[idx] ?? null;
|
|
@@ -1233,7 +1322,10 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1233
1322
|
v2SnrBandInfo = snrIndicesWithBandV2(header.obsTypes);
|
|
1234
1323
|
v2SatIndex = 0;
|
|
1235
1324
|
v2CurrentSatLine = 0;
|
|
1236
|
-
const continuationLines = Math.max(
|
|
1325
|
+
const continuationLines = Math.max(
|
|
1326
|
+
0,
|
|
1327
|
+
Math.ceil((info.numSats - 12) / 12)
|
|
1328
|
+
);
|
|
1237
1329
|
if (continuationLines > 0) {
|
|
1238
1330
|
v2ContinuationSatsRemaining = continuationLines;
|
|
1239
1331
|
} else {
|
|
@@ -1273,7 +1365,9 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1273
1365
|
const magic = new Uint8Array(await file.slice(0, 2).arrayBuffer());
|
|
1274
1366
|
const isGz = magic[0] === 31 && magic[1] === 139;
|
|
1275
1367
|
if (magic[0] === 31 && magic[1] === 157) {
|
|
1276
|
-
throw new Error(
|
|
1368
|
+
throw new Error(
|
|
1369
|
+
"Unix compress (.Z) files are not supported. Please decompress first (e.g. uncompress or gzip -d)."
|
|
1370
|
+
);
|
|
1277
1371
|
}
|
|
1278
1372
|
if (isGz) {
|
|
1279
1373
|
let bytesRead = 0;
|
|
@@ -1289,7 +1383,9 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1289
1383
|
if (done) break;
|
|
1290
1384
|
bytesRead += value.byteLength;
|
|
1291
1385
|
processChunkText(decoder.decode(value, { stream: true }), false);
|
|
1292
|
-
onProgress?.(
|
|
1386
|
+
onProgress?.(
|
|
1387
|
+
Math.min(99, Math.round(bytesRead / (file.size * 4) * 100))
|
|
1388
|
+
);
|
|
1293
1389
|
await yield_();
|
|
1294
1390
|
}
|
|
1295
1391
|
processChunkText(decoder.decode(), true);
|
|
@@ -1299,7 +1395,10 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1299
1395
|
const end = Math.min(offset + CHUNK_SIZE, file.size);
|
|
1300
1396
|
const slice = file.slice(offset, end);
|
|
1301
1397
|
const arrayBuf = await slice.arrayBuffer();
|
|
1302
|
-
processChunkText(
|
|
1398
|
+
processChunkText(
|
|
1399
|
+
decoder.decode(arrayBuf, { stream: end < file.size }),
|
|
1400
|
+
false
|
|
1401
|
+
);
|
|
1303
1402
|
onProgress?.(Math.min(99, Math.round(end / file.size * 100)));
|
|
1304
1403
|
await yield_();
|
|
1305
1404
|
}
|
|
@@ -1307,19 +1406,27 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
1307
1406
|
processChunkText("", true);
|
|
1308
1407
|
}
|
|
1309
1408
|
}
|
|
1310
|
-
if (epochInfo && satLinesRemaining === 0 && crxPhase === "epoch")
|
|
1311
|
-
|
|
1409
|
+
if (epochInfo && satLinesRemaining === 0 && crxPhase === "epoch")
|
|
1410
|
+
finishEpoch();
|
|
1411
|
+
if (!header)
|
|
1412
|
+
throw new Error("No valid RINEX header found (missing END OF HEADER).");
|
|
1312
1413
|
onProgress?.(100);
|
|
1313
|
-
return {
|
|
1414
|
+
return {
|
|
1415
|
+
header,
|
|
1416
|
+
epochs,
|
|
1417
|
+
stats: computeStats(header, epochs, satellitesSeen)
|
|
1418
|
+
};
|
|
1314
1419
|
}
|
|
1315
1420
|
function computeStats(header, epochs, satellitesSeen) {
|
|
1316
1421
|
const n = epochs.length;
|
|
1317
1422
|
const startTime = n > 0 ? new Date(epochs[0].time) : header.timeOfFirstObs;
|
|
1318
1423
|
const endTime = n > 0 ? new Date(epochs[n - 1].time) : header.timeOfLastObs;
|
|
1319
1424
|
let duration = null;
|
|
1320
|
-
if (startTime && endTime)
|
|
1425
|
+
if (startTime && endTime)
|
|
1426
|
+
duration = (endTime.getTime() - startTime.getTime()) / 1e3;
|
|
1321
1427
|
let interval = header.interval;
|
|
1322
|
-
if (interval === null && n >= 2)
|
|
1428
|
+
if (interval === null && n >= 2)
|
|
1429
|
+
interval = (epochs[1].time - epochs[0].time) / 1e3;
|
|
1323
1430
|
const uniqueSatsPerSystem = {};
|
|
1324
1431
|
let totalUnique = 0;
|
|
1325
1432
|
const systems = [];
|
package/dist/analysis.js
CHANGED
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
CycleSlipAccumulator,
|
|
4
4
|
MultipathAccumulator,
|
|
5
5
|
analyzeQuality
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-G3N4S3DM.js";
|
|
7
|
+
import "./chunk-OZCYOM5D.js";
|
|
8
|
+
import "./chunk-W5WKEV7U.js";
|
|
9
9
|
export {
|
|
10
10
|
CompletenessAccumulator,
|
|
11
11
|
CycleSlipAccumulator,
|
|
@@ -1,30 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FREQ,
|
|
3
|
+
GLO_F1_BASE,
|
|
4
|
+
GLO_F1_STEP,
|
|
5
|
+
GLO_F2_BASE,
|
|
6
|
+
GLO_F2_STEP
|
|
7
|
+
} from "./chunk-W5WKEV7U.js";
|
|
8
|
+
|
|
1
9
|
// src/signals/definitions.ts
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
var
|
|
15
|
-
var
|
|
16
|
-
var
|
|
17
|
-
var
|
|
18
|
-
var
|
|
19
|
-
var
|
|
20
|
-
var
|
|
21
|
-
var
|
|
22
|
-
var
|
|
23
|
-
var
|
|
24
|
-
var
|
|
25
|
-
var
|
|
26
|
-
var
|
|
27
|
-
var
|
|
10
|
+
var MHz = 1e6;
|
|
11
|
+
var FREQ_GPS_L1 = FREQ.G["1"] / MHz;
|
|
12
|
+
var FREQ_GPS_L2 = FREQ.G["2"] / MHz;
|
|
13
|
+
var FREQ_GPS_L5 = FREQ.G["5"] / MHz;
|
|
14
|
+
var FREQ_GLO_L1 = GLO_F1_BASE / MHz;
|
|
15
|
+
var FREQ_GLO_L2 = GLO_F2_BASE / MHz;
|
|
16
|
+
var DELTA_GLO_L1 = GLO_F1_STEP / MHz;
|
|
17
|
+
var DELTA_GLO_L2 = GLO_F2_STEP / MHz;
|
|
18
|
+
var FREQ_GLO_L1OC = FREQ.R["4"] / MHz;
|
|
19
|
+
var FREQ_GLO_L2OC = FREQ.R["6"] / MHz;
|
|
20
|
+
var FREQ_GLO_L3OC = FREQ.R["3"] / MHz;
|
|
21
|
+
var FREQ_GAL_E1 = FREQ.E["1"] / MHz;
|
|
22
|
+
var FREQ_GAL_E5 = FREQ.E["8"] / MHz;
|
|
23
|
+
var FREQ_GAL_E5a = FREQ.E["5"] / MHz;
|
|
24
|
+
var FREQ_GAL_E5b = FREQ.E["7"] / MHz;
|
|
25
|
+
var FREQ_GAL_E6 = FREQ.E["6"] / MHz;
|
|
26
|
+
var FREQ_BDS_B1I = FREQ.C["2"] / MHz;
|
|
27
|
+
var FREQ_BDS_B2I = FREQ.C["7"] / MHz;
|
|
28
|
+
var FREQ_BDS_B3I = FREQ.C["6"] / MHz;
|
|
29
|
+
var FREQ_BDS_B1A = FREQ.C["1"] / MHz;
|
|
30
|
+
var FREQ_BDS_B1C = FREQ.C["1"] / MHz;
|
|
31
|
+
var FREQ_BDS_B3A = FREQ.C["6"] / MHz;
|
|
32
|
+
var FREQ_QZS_L1 = FREQ.J["1"] / MHz;
|
|
33
|
+
var FREQ_QZS_L2 = FREQ.J["2"] / MHz;
|
|
34
|
+
var FREQ_QZS_L5 = FREQ.J["5"] / MHz;
|
|
35
|
+
var FREQ_QZS_L6 = FREQ.J["6"] / MHz;
|
|
36
|
+
var FREQ_NAVIC_L5 = FREQ.I["5"] / MHz;
|
|
28
37
|
var GPS_COLOR = "#4ade80";
|
|
29
38
|
var GPS_M_COLOR = "#ef4444";
|
|
30
39
|
var GPS_P_COLOR = "#dbc51f";
|
|
@@ -98,14 +98,18 @@ async function ntripFetch(proxyUrl, path, headers, signal) {
|
|
|
98
98
|
} catch (err) {
|
|
99
99
|
if (err instanceof Error && err.name === "AbortError") throw err;
|
|
100
100
|
const message = err instanceof Error ? err.message : "The service may be temporarily unavailable.";
|
|
101
|
-
throw new Error(`Could not reach the NTRIP proxy: ${message}`, {
|
|
101
|
+
throw new Error(`Could not reach the NTRIP proxy: ${message}`, {
|
|
102
|
+
cause: err
|
|
103
|
+
});
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
async function fetchSourcetable(proxyUrl, info, signal) {
|
|
105
107
|
const headers = ntripHeaders(info);
|
|
106
108
|
const res = await ntripFetch(proxyUrl, "/", headers, signal);
|
|
107
109
|
if (res.status === 401) {
|
|
108
|
-
throw new Error(
|
|
110
|
+
throw new Error(
|
|
111
|
+
"Authentication required. Please provide valid credentials."
|
|
112
|
+
);
|
|
109
113
|
}
|
|
110
114
|
if (!res.ok) {
|
|
111
115
|
throw new Error(`Caster returned ${res.status} ${res.statusText}`);
|
|
@@ -117,7 +121,12 @@ async function connectToMountpoint(proxyUrl, info, signal) {
|
|
|
117
121
|
const headers = ntripHeaders(info);
|
|
118
122
|
const controller = new AbortController();
|
|
119
123
|
const combinedSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
120
|
-
const res = await ntripFetch(
|
|
124
|
+
const res = await ntripFetch(
|
|
125
|
+
proxyUrl,
|
|
126
|
+
`/${info.mountpoint}`,
|
|
127
|
+
headers,
|
|
128
|
+
combinedSignal
|
|
129
|
+
);
|
|
121
130
|
if (res.status === 401) {
|
|
122
131
|
throw new Error("Authentication required for this mountpoint.");
|
|
123
132
|
}
|
|
@@ -128,7 +137,9 @@ async function connectToMountpoint(proxyUrl, info, signal) {
|
|
|
128
137
|
throw new Error(`Caster returned ${res.status} ${res.statusText}`);
|
|
129
138
|
}
|
|
130
139
|
if (!res.body) {
|
|
131
|
-
throw new Error(
|
|
140
|
+
throw new Error(
|
|
141
|
+
"No response body \u2014 streaming not supported by this environment."
|
|
142
|
+
);
|
|
132
143
|
}
|
|
133
144
|
return {
|
|
134
145
|
reader: res.body.getReader(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseRinexStream
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OZCYOM5D.js";
|
|
4
4
|
import {
|
|
5
5
|
ARC_GAP_FACTOR,
|
|
6
6
|
BAND_LABELS,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
buildGloChannelMap,
|
|
12
12
|
buildObsIndices,
|
|
13
13
|
getFreq
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-W5WKEV7U.js";
|
|
15
15
|
|
|
16
16
|
// src/analysis/multipath.ts
|
|
17
17
|
var MIN_ARC_LENGTH = 10;
|
|
@@ -99,8 +99,18 @@ var MultipathAccumulator = class {
|
|
|
99
99
|
time: t,
|
|
100
100
|
mp: arc.rawMp[i] - mean
|
|
101
101
|
}));
|
|
102
|
-
const rms = Math.sqrt(
|
|
103
|
-
|
|
102
|
+
const rms = Math.sqrt(
|
|
103
|
+
points.reduce((s, p) => s + p.mp * p.mp, 0) / points.length
|
|
104
|
+
);
|
|
105
|
+
this.results.push({
|
|
106
|
+
prn,
|
|
107
|
+
system: sys,
|
|
108
|
+
band,
|
|
109
|
+
refBand,
|
|
110
|
+
label,
|
|
111
|
+
points,
|
|
112
|
+
rms
|
|
113
|
+
});
|
|
104
114
|
}
|
|
105
115
|
state.arc = { times: [], rawMp: [] };
|
|
106
116
|
}
|
|
@@ -254,10 +264,20 @@ var CycleSlipAccumulator = class {
|
|
|
254
264
|
const sigKey = `${sys}:${signal}`;
|
|
255
265
|
this.countEpoch(sigKey);
|
|
256
266
|
const stddev = mwSmoothStddev(prevSmooth);
|
|
257
|
-
const threshold = Math.max(
|
|
267
|
+
const threshold = Math.max(
|
|
268
|
+
MW_MIN_THRESHOLD,
|
|
269
|
+
MW_SIGMA_FACTOR * stddev
|
|
270
|
+
);
|
|
258
271
|
const deviation = Math.abs(mw - prevSmooth.mean);
|
|
259
272
|
if (deviation > threshold) {
|
|
260
|
-
this.recordSlip(
|
|
273
|
+
this.recordSlip(
|
|
274
|
+
sigKey,
|
|
275
|
+
time,
|
|
276
|
+
prn,
|
|
277
|
+
signal,
|
|
278
|
+
deviation,
|
|
279
|
+
/* @__PURE__ */ new Set([b1, b2])
|
|
280
|
+
);
|
|
261
281
|
mwSmooth.set(pairKey, mwSmoothInit(mw));
|
|
262
282
|
} else {
|
|
263
283
|
mwSmooth.set(pairKey, mwSmoothUpdate(prevSmooth, mw));
|
|
@@ -312,7 +332,13 @@ var CycleSlipAccumulator = class {
|
|
|
312
332
|
}
|
|
313
333
|
}
|
|
314
334
|
}
|
|
315
|
-
this.prev.set(prn, {
|
|
335
|
+
this.prev.set(prn, {
|
|
336
|
+
time,
|
|
337
|
+
mwSmooth,
|
|
338
|
+
gf: gfMap,
|
|
339
|
+
phase: phaseM,
|
|
340
|
+
code: codeM
|
|
341
|
+
});
|
|
316
342
|
};
|
|
317
343
|
finalize() {
|
|
318
344
|
const satSlipCounts = {};
|
|
@@ -320,7 +346,10 @@ var CycleSlipAccumulator = class {
|
|
|
320
346
|
satSlipCounts[ev.prn] = (satSlipCounts[ev.prn] ?? 0) + 1;
|
|
321
347
|
}
|
|
322
348
|
const signalStats = [];
|
|
323
|
-
const allKeys = /* @__PURE__ */ new Set([
|
|
349
|
+
const allKeys = /* @__PURE__ */ new Set([
|
|
350
|
+
...this.signalEpochs.keys(),
|
|
351
|
+
...this.signalSlips.keys()
|
|
352
|
+
]);
|
|
324
353
|
for (const sigKey of allKeys) {
|
|
325
354
|
const [sys, ...rest] = sigKey.split(":");
|
|
326
355
|
const signal = rest.join(":");
|