gnss-js 1.0.0 → 1.0.1
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/analysis.cjs +140 -33
- package/dist/analysis.js +3 -3
- package/dist/{chunk-SDRRAJT5.js → chunk-4YN353Q7.js} +16 -9
- package/dist/{chunk-WR7LCB52.js → chunk-BJHTBYNG.js} +15 -4
- package/dist/{chunk-CB6EOOLA.js → chunk-HBLU2EJ4.js} +70 -21
- package/dist/{chunk-354IRDOG.js → chunk-LWNTWBHB.js} +36 -7
- package/dist/{chunk-K7WZQFBV.js → chunk-MIIM4LDY.js} +4 -1
- package/dist/{chunk-Y3R57B5P.js → chunk-SO3POWWR.js} +37 -8
- package/dist/{chunk-IS4UUDBV.js → chunk-W4YMQKWH.js} +54 -9
- package/dist/{chunk-PRSZIWKM.js → chunk-WP2JFDLA.js} +21 -28
- package/dist/constants.cjs +36 -7
- package/dist/constants.js +1 -1
- package/dist/coordinates.cjs +4 -1
- package/dist/coordinates.js +1 -1
- package/dist/{ephemeris-BUWzfmBy.d.cts → ephemeris-C10stHhM.d.cts} +1 -1
- package/dist/{ephemeris-BUWzfmBy.d.ts → ephemeris-C10stHhM.d.ts} +1 -1
- package/dist/index.cjs +249 -83
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -8
- package/dist/ntrip.cjs +15 -4
- package/dist/ntrip.js +1 -1
- package/dist/orbit.cjs +16 -9
- package/dist/orbit.d.cts +1 -1
- package/dist/orbit.d.ts +1 -1
- package/dist/orbit.js +1 -1
- package/dist/rinex.cjs +123 -29
- package/dist/rinex.js +3 -3
- package/dist/rtcm3.cjs +20 -27
- package/dist/rtcm3.d.cts +2 -2
- package/dist/rtcm3.d.ts +2 -2
- package/dist/rtcm3.js +2 -2
- package/package.json +1 -1
package/dist/analysis.cjs
CHANGED
|
@@ -41,8 +41,20 @@ var C_LIGHT = 299792458;
|
|
|
41
41
|
var FREQ = {
|
|
42
42
|
G: { "1": 157542e4, "2": 12276e5, "5": 117645e4 },
|
|
43
43
|
R: { "3": 1202025e3 },
|
|
44
|
-
E: {
|
|
45
|
-
|
|
44
|
+
E: {
|
|
45
|
+
"1": 157542e4,
|
|
46
|
+
"5": 117645e4,
|
|
47
|
+
"6": 127875e4,
|
|
48
|
+
"7": 120714e4,
|
|
49
|
+
"8": 1191795e3
|
|
50
|
+
},
|
|
51
|
+
C: {
|
|
52
|
+
"1": 157542e4,
|
|
53
|
+
"2": 1561098e3,
|
|
54
|
+
"5": 117645e4,
|
|
55
|
+
"6": 126852e4,
|
|
56
|
+
"7": 120714e4
|
|
57
|
+
},
|
|
46
58
|
J: { "1": 157542e4, "2": 12276e5, "5": 117645e4, "6": 127875e4 },
|
|
47
59
|
I: { "5": 117645e4, "9": 2492028e3 },
|
|
48
60
|
S: { "1": 157542e4, "5": 117645e4 }
|
|
@@ -57,11 +69,28 @@ var BAND_LABELS = {
|
|
|
57
69
|
S: { "1": "L1", "5": "L5" }
|
|
58
70
|
};
|
|
59
71
|
var DUAL_FREQ_PAIRS = {
|
|
60
|
-
G: [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
G: [
|
|
73
|
+
["1", "2"],
|
|
74
|
+
["1", "5"]
|
|
75
|
+
],
|
|
76
|
+
R: [
|
|
77
|
+
["1", "2"],
|
|
78
|
+
["1", "3"]
|
|
79
|
+
],
|
|
80
|
+
E: [
|
|
81
|
+
["1", "5"],
|
|
82
|
+
["1", "7"],
|
|
83
|
+
["1", "6"]
|
|
84
|
+
],
|
|
85
|
+
C: [
|
|
86
|
+
["2", "7"],
|
|
87
|
+
["2", "6"],
|
|
88
|
+
["1", "5"]
|
|
89
|
+
],
|
|
90
|
+
J: [
|
|
91
|
+
["1", "2"],
|
|
92
|
+
["1", "5"]
|
|
93
|
+
],
|
|
65
94
|
I: [["5", "9"]],
|
|
66
95
|
S: [["1", "5"]]
|
|
67
96
|
};
|
|
@@ -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-SO3POWWR.js";
|
|
7
|
+
import "./chunk-HBLU2EJ4.js";
|
|
8
|
+
import "./chunk-LWNTWBHB.js";
|
|
9
9
|
export {
|
|
10
10
|
CompletenessAccumulator,
|
|
11
11
|
CycleSlipAccumulator,
|
|
@@ -33,10 +33,7 @@ function keplerPosition(eph, t) {
|
|
|
33
33
|
}
|
|
34
34
|
const sinE = Math.sin(Ek);
|
|
35
35
|
const cosE = Math.cos(Ek);
|
|
36
|
-
const vk = Math.atan2(
|
|
37
|
-
Math.sqrt(1 - eph.e * eph.e) * sinE,
|
|
38
|
-
cosE - eph.e
|
|
39
|
-
);
|
|
36
|
+
const vk = Math.atan2(Math.sqrt(1 - eph.e * eph.e) * sinE, cosE - eph.e);
|
|
40
37
|
const phik = vk + eph.omega;
|
|
41
38
|
const sin2phi = Math.sin(2 * phik);
|
|
42
39
|
const cos2phi = Math.cos(2 * phik);
|
|
@@ -199,7 +196,10 @@ function invert4x4(m) {
|
|
|
199
196
|
let det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
|
|
200
197
|
if (Math.abs(det) < 1e-20) return null;
|
|
201
198
|
det = 1 / det;
|
|
202
|
-
const result = Array.from(
|
|
199
|
+
const result = Array.from(
|
|
200
|
+
{ length: 4 },
|
|
201
|
+
() => new Array(4).fill(0)
|
|
202
|
+
);
|
|
203
203
|
for (let i = 0; i < 4; i++) {
|
|
204
204
|
for (let j = 0; j < 4; j++) {
|
|
205
205
|
result[i][j] = inv[i * 4 + j] * det;
|
|
@@ -316,10 +316,16 @@ var GAL_EPOCH_MS = GPS_EPOCH_MS;
|
|
|
316
316
|
function ephInfoToEphemeris(info) {
|
|
317
317
|
const sys = info.prn.charAt(0);
|
|
318
318
|
if (sys === "R") {
|
|
319
|
-
if (info.x === void 0 || info.y === void 0 || info.z === void 0)
|
|
320
|
-
|
|
319
|
+
if (info.x === void 0 || info.y === void 0 || info.z === void 0)
|
|
320
|
+
return null;
|
|
321
|
+
if (info.vx === void 0 || info.vy === void 0 || info.vz === void 0)
|
|
322
|
+
return null;
|
|
321
323
|
const now = new Date(info.lastReceived);
|
|
322
|
-
const utcMidnight = Date.UTC(
|
|
324
|
+
const utcMidnight = Date.UTC(
|
|
325
|
+
now.getUTCFullYear(),
|
|
326
|
+
now.getUTCMonth(),
|
|
327
|
+
now.getUTCDate()
|
|
328
|
+
);
|
|
323
329
|
const tbSec = (info.tb ?? 0) * 900;
|
|
324
330
|
const moscowOffset = 3 * 3600;
|
|
325
331
|
const tocMs = utcMidnight + (tbSec - moscowOffset) * 1e3;
|
|
@@ -344,7 +350,8 @@ function ephInfoToEphemeris(info) {
|
|
|
344
350
|
freqNum: info.freqChannel ?? 0
|
|
345
351
|
};
|
|
346
352
|
}
|
|
347
|
-
if (info.sqrtA === void 0 || info.eccentricity === void 0 || info.inclination === void 0 || info.omega0 === void 0 || info.argPerigee === void 0 || info.meanAnomaly === void 0 || info.toe === void 0 || info.week === void 0)
|
|
353
|
+
if (info.sqrtA === void 0 || info.eccentricity === void 0 || info.inclination === void 0 || info.omega0 === void 0 || info.argPerigee === void 0 || info.meanAnomaly === void 0 || info.toe === void 0 || info.week === void 0)
|
|
354
|
+
return null;
|
|
348
355
|
let epochMs;
|
|
349
356
|
const tocSec = info.toc ?? info.toe;
|
|
350
357
|
if (sys === "C") {
|
|
@@ -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
|
SYSTEM_NAMES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LWNTWBHB.js";
|
|
4
4
|
|
|
5
5
|
// src/rinex/crx.ts
|
|
6
6
|
function crxRepair(old, diff) {
|
|
@@ -42,10 +42,20 @@ function parseCrxDataLine(line, ntype) {
|
|
|
42
42
|
if (token.length >= 3 && token[1] === "&") {
|
|
43
43
|
const arcOrder = parseInt(token[0]);
|
|
44
44
|
const value = parseInt(token.substring(2));
|
|
45
|
-
fields.push({
|
|
45
|
+
fields.push({
|
|
46
|
+
empty: false,
|
|
47
|
+
init: true,
|
|
48
|
+
arcOrder,
|
|
49
|
+
value: isNaN(value) ? 0 : value
|
|
50
|
+
});
|
|
46
51
|
} else {
|
|
47
52
|
const value = parseInt(token);
|
|
48
|
-
fields.push({
|
|
53
|
+
fields.push({
|
|
54
|
+
empty: false,
|
|
55
|
+
init: false,
|
|
56
|
+
arcOrder: -1,
|
|
57
|
+
value: isNaN(value) ? 0 : value
|
|
58
|
+
});
|
|
49
59
|
}
|
|
50
60
|
}
|
|
51
61
|
const flagStr = pos < line.length ? line.substring(pos) : "";
|
|
@@ -55,7 +65,10 @@ function crxDecompress(prev, field) {
|
|
|
55
65
|
if (field.init) {
|
|
56
66
|
const a = new Array(Math.max(field.arcOrder + 1, 1)).fill(0);
|
|
57
67
|
a[0] = field.value;
|
|
58
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
state: { accum: a, order: 0, arcOrder: field.arcOrder },
|
|
70
|
+
result: field.value
|
|
71
|
+
};
|
|
59
72
|
}
|
|
60
73
|
if (!prev) {
|
|
61
74
|
return { state: { accum: [0], order: 0, arcOrder: 0 }, result: 0 };
|
|
@@ -68,7 +81,10 @@ function crxDecompress(prev, field) {
|
|
|
68
81
|
for (let k = order; k >= 1; k--) {
|
|
69
82
|
accum[k - 1] += accum[k];
|
|
70
83
|
}
|
|
71
|
-
return {
|
|
84
|
+
return {
|
|
85
|
+
state: { accum, order, arcOrder: prev.arcOrder },
|
|
86
|
+
result: accum[0]
|
|
87
|
+
};
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
// src/rinex/parser.ts
|
|
@@ -76,7 +92,9 @@ var CHUNK_SIZE = 2 * 1024 * 1024;
|
|
|
76
92
|
var yieldToMain = () => new Promise((r) => setTimeout(r, 0));
|
|
77
93
|
var noYield = () => Promise.resolve();
|
|
78
94
|
var SYSTEM_ORDER = ["G", "R", "E", "C", "J", "I", "S"];
|
|
79
|
-
var SYSTEM_RANK = Object.fromEntries(
|
|
95
|
+
var SYSTEM_RANK = Object.fromEntries(
|
|
96
|
+
SYSTEM_ORDER.map((s, i) => [s, i])
|
|
97
|
+
);
|
|
80
98
|
function systemCmp(a, b) {
|
|
81
99
|
const ra = SYSTEM_RANK[a.charAt(0)] ?? 99;
|
|
82
100
|
const rb = SYSTEM_RANK[b.charAt(0)] ?? 99;
|
|
@@ -262,7 +280,11 @@ function parseEpochLine3(line) {
|
|
|
262
280
|
const wholeSec = Math.floor(sc);
|
|
263
281
|
const ms = Math.round((sc - wholeSec) * 1e3);
|
|
264
282
|
const time = Date.UTC(yr, mo - 1, dy, hr, mn, wholeSec, ms);
|
|
265
|
-
return {
|
|
283
|
+
return {
|
|
284
|
+
time,
|
|
285
|
+
flag: isNaN(flag) ? 0 : flag,
|
|
286
|
+
numSats: isNaN(numSats) ? 0 : numSats
|
|
287
|
+
};
|
|
266
288
|
}
|
|
267
289
|
function parseEpochLine2(line) {
|
|
268
290
|
const yr2 = parseInt(line.substring(1, 3));
|
|
@@ -284,7 +306,12 @@ function parseEpochLine2(line) {
|
|
|
284
306
|
const id = satPart.substring(i, i + 3).trim();
|
|
285
307
|
if (id) satIds.push(id);
|
|
286
308
|
}
|
|
287
|
-
return {
|
|
309
|
+
return {
|
|
310
|
+
time,
|
|
311
|
+
flag: isNaN(flag) ? 0 : flag,
|
|
312
|
+
numSats: isNaN(numSats) ? 0 : numSats,
|
|
313
|
+
satIds
|
|
314
|
+
};
|
|
288
315
|
}
|
|
289
316
|
async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode) {
|
|
290
317
|
const skipEpochs = !!workerMode;
|
|
@@ -337,16 +364,19 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
337
364
|
if (!skipEpochs) {
|
|
338
365
|
const snrBySys = {};
|
|
339
366
|
for (const [sys, vals] of Object.entries(snrPerSystemAccum)) {
|
|
340
|
-
if (vals.length > 0)
|
|
367
|
+
if (vals.length > 0)
|
|
368
|
+
snrBySys[sys] = vals.reduce((a, b) => a + b, 0) / vals.length;
|
|
341
369
|
}
|
|
342
370
|
const meanSnr = snrValues.length > 0 ? snrValues.reduce((a, b) => a + b, 0) / snrValues.length : null;
|
|
343
371
|
const snrPerSat = {};
|
|
344
372
|
for (const [prn, vals] of Object.entries(snrPerSatAccum)) {
|
|
345
|
-
if (vals.length > 0)
|
|
373
|
+
if (vals.length > 0)
|
|
374
|
+
snrPerSat[prn] = vals.reduce((a, b) => a + b, 0) / vals.length;
|
|
346
375
|
}
|
|
347
376
|
const snrPerSatBand = {};
|
|
348
377
|
for (const [key, vals] of Object.entries(snrPerSatBandAccum)) {
|
|
349
|
-
if (vals.length > 0)
|
|
378
|
+
if (vals.length > 0)
|
|
379
|
+
snrPerSatBand[key] = vals.reduce((a, b) => a + b, 0) / vals.length;
|
|
350
380
|
}
|
|
351
381
|
epochs.push({
|
|
352
382
|
time: epochInfo.time,
|
|
@@ -391,7 +421,8 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
391
421
|
const codes = header.obsTypes[sys] ?? [];
|
|
392
422
|
if (onSatObs && epochInfo) {
|
|
393
423
|
const values = new Array(codes.length);
|
|
394
|
-
for (let i = 0; i < codes.length; i++)
|
|
424
|
+
for (let i = 0; i < codes.length; i++)
|
|
425
|
+
values[i] = readObsValue(obsLine, i);
|
|
395
426
|
if (!skipEpochs) {
|
|
396
427
|
for (const { idx, band } of getSnrIndices(sys)) {
|
|
397
428
|
const val = values[idx] ?? null;
|
|
@@ -628,7 +659,10 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
628
659
|
v2SnrBandInfo = snrIndicesWithBandV2(header.obsTypes);
|
|
629
660
|
v2SatIndex = 0;
|
|
630
661
|
v2CurrentSatLine = 0;
|
|
631
|
-
const continuationLines = Math.max(
|
|
662
|
+
const continuationLines = Math.max(
|
|
663
|
+
0,
|
|
664
|
+
Math.ceil((info.numSats - 12) / 12)
|
|
665
|
+
);
|
|
632
666
|
if (continuationLines > 0) {
|
|
633
667
|
v2ContinuationSatsRemaining = continuationLines;
|
|
634
668
|
} else {
|
|
@@ -668,7 +702,9 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
668
702
|
const magic = new Uint8Array(await file.slice(0, 2).arrayBuffer());
|
|
669
703
|
const isGz = magic[0] === 31 && magic[1] === 139;
|
|
670
704
|
if (magic[0] === 31 && magic[1] === 157) {
|
|
671
|
-
throw new Error(
|
|
705
|
+
throw new Error(
|
|
706
|
+
"Unix compress (.Z) files are not supported. Please decompress first (e.g. uncompress or gzip -d)."
|
|
707
|
+
);
|
|
672
708
|
}
|
|
673
709
|
if (isGz) {
|
|
674
710
|
let bytesRead = 0;
|
|
@@ -684,7 +720,9 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
684
720
|
if (done) break;
|
|
685
721
|
bytesRead += value.byteLength;
|
|
686
722
|
processChunkText(decoder.decode(value, { stream: true }), false);
|
|
687
|
-
onProgress?.(
|
|
723
|
+
onProgress?.(
|
|
724
|
+
Math.min(99, Math.round(bytesRead / (file.size * 4) * 100))
|
|
725
|
+
);
|
|
688
726
|
await yield_();
|
|
689
727
|
}
|
|
690
728
|
processChunkText(decoder.decode(), true);
|
|
@@ -694,7 +732,10 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
694
732
|
const end = Math.min(offset + CHUNK_SIZE, file.size);
|
|
695
733
|
const slice = file.slice(offset, end);
|
|
696
734
|
const arrayBuf = await slice.arrayBuffer();
|
|
697
|
-
processChunkText(
|
|
735
|
+
processChunkText(
|
|
736
|
+
decoder.decode(arrayBuf, { stream: end < file.size }),
|
|
737
|
+
false
|
|
738
|
+
);
|
|
698
739
|
onProgress?.(Math.min(99, Math.round(end / file.size * 100)));
|
|
699
740
|
await yield_();
|
|
700
741
|
}
|
|
@@ -702,19 +743,27 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
|
|
|
702
743
|
processChunkText("", true);
|
|
703
744
|
}
|
|
704
745
|
}
|
|
705
|
-
if (epochInfo && satLinesRemaining === 0 && crxPhase === "epoch")
|
|
706
|
-
|
|
746
|
+
if (epochInfo && satLinesRemaining === 0 && crxPhase === "epoch")
|
|
747
|
+
finishEpoch();
|
|
748
|
+
if (!header)
|
|
749
|
+
throw new Error("No valid RINEX header found (missing END OF HEADER).");
|
|
707
750
|
onProgress?.(100);
|
|
708
|
-
return {
|
|
751
|
+
return {
|
|
752
|
+
header,
|
|
753
|
+
epochs,
|
|
754
|
+
stats: computeStats(header, epochs, satellitesSeen)
|
|
755
|
+
};
|
|
709
756
|
}
|
|
710
757
|
function computeStats(header, epochs, satellitesSeen) {
|
|
711
758
|
const n = epochs.length;
|
|
712
759
|
const startTime = n > 0 ? new Date(epochs[0].time) : header.timeOfFirstObs;
|
|
713
760
|
const endTime = n > 0 ? new Date(epochs[n - 1].time) : header.timeOfLastObs;
|
|
714
761
|
let duration = null;
|
|
715
|
-
if (startTime && endTime)
|
|
762
|
+
if (startTime && endTime)
|
|
763
|
+
duration = (endTime.getTime() - startTime.getTime()) / 1e3;
|
|
716
764
|
let interval = header.interval;
|
|
717
|
-
if (interval === null && n >= 2)
|
|
765
|
+
if (interval === null && n >= 2)
|
|
766
|
+
interval = (epochs[1].time - epochs[0].time) / 1e3;
|
|
718
767
|
const uniqueSatsPerSystem = {};
|
|
719
768
|
let totalUnique = 0;
|
|
720
769
|
const systems = [];
|