gnss-js 1.4.1 → 1.5.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/analysis.cjs +148 -4
- package/dist/analysis.d.cts +52 -2
- package/dist/analysis.d.ts +52 -2
- package/dist/analysis.js +7 -3
- package/dist/{chunk-UQ7D53R2.js → chunk-EJQ6NG5U.js} +144 -2
- package/dist/index.cjs +146 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -2
- package/package.json +1 -1
package/dist/analysis.cjs
CHANGED
|
@@ -24,7 +24,9 @@ __export(analysis_exports, {
|
|
|
24
24
|
CycleSlipAccumulator: () => CycleSlipAccumulator,
|
|
25
25
|
IonoAccumulator: () => IonoAccumulator,
|
|
26
26
|
MultipathAccumulator: () => MultipathAccumulator,
|
|
27
|
-
analyzeQuality: () => analyzeQuality
|
|
27
|
+
analyzeQuality: () => analyzeQuality,
|
|
28
|
+
applyIonoDcb: () => applyIonoDcb,
|
|
29
|
+
parseSinexBiasDcb: () => parseSinexBiasDcb
|
|
28
30
|
});
|
|
29
31
|
module.exports = __toCommonJS(analysis_exports);
|
|
30
32
|
|
|
@@ -717,13 +719,15 @@ var IonoAccumulator = class {
|
|
|
717
719
|
obsIndices;
|
|
718
720
|
gloChannels;
|
|
719
721
|
pairLabel = /* @__PURE__ */ new Map();
|
|
722
|
+
pairCodes = /* @__PURE__ */ new Map();
|
|
723
|
+
pairMeta = /* @__PURE__ */ new Map();
|
|
720
724
|
constructor(header) {
|
|
721
725
|
this.interval = header.interval ?? 30;
|
|
722
726
|
this.obsIndices = buildObsIndices(header);
|
|
723
727
|
this.gloChannels = buildGloChannelMap(header.glonassSlots);
|
|
724
728
|
}
|
|
725
729
|
/** Observation callback — wire this into parseRinexStream. */
|
|
726
|
-
onObservation = (time, prn,
|
|
730
|
+
onObservation = (time, prn, codes, values) => {
|
|
727
731
|
const sys = prn[0];
|
|
728
732
|
const bandMap = this.obsIndices.get(sys) ?? this.obsIndices.get("_v2");
|
|
729
733
|
if (!bandMap) return;
|
|
@@ -734,7 +738,12 @@ var IonoAccumulator = class {
|
|
|
734
738
|
const lVal = values[L];
|
|
735
739
|
const freq = getFreq(this.gloChannels, prn, band);
|
|
736
740
|
if (cVal != null && cVal !== 0 && lVal != null && lVal !== 0 && freq) {
|
|
737
|
-
bandData.set(band, {
|
|
741
|
+
bandData.set(band, {
|
|
742
|
+
C: cVal,
|
|
743
|
+
L: lVal,
|
|
744
|
+
f: freq,
|
|
745
|
+
code: codes[C] ?? `C${band}`
|
|
746
|
+
});
|
|
738
747
|
}
|
|
739
748
|
}
|
|
740
749
|
if (bandData.size < 2) return;
|
|
@@ -753,7 +762,9 @@ var IonoAccumulator = class {
|
|
|
753
762
|
const bLabel = BAND_LABELS[sys]?.[bi] ?? bi;
|
|
754
763
|
const rLabel = BAND_LABELS[sys]?.[bj] ?? bj;
|
|
755
764
|
this.pairLabel.set(`${sys}:${pairKey}`, `${bLabel}-${rLabel}`);
|
|
765
|
+
this.pairCodes.set(`${sys}:${pairKey}`, [di.code, dj.code]);
|
|
756
766
|
}
|
|
767
|
+
this.pairMeta.set(`${prn}:${pairKey}`, { fi: di.f, gamma });
|
|
757
768
|
this.push(prn, pairKey, time, l4, p4, di.f, gamma);
|
|
758
769
|
break;
|
|
759
770
|
}
|
|
@@ -854,10 +865,14 @@ var IonoAccumulator = class {
|
|
|
854
865
|
count++;
|
|
855
866
|
if (p.stec > maxStec) maxStec = p.stec;
|
|
856
867
|
}
|
|
868
|
+
const meta = this.pairMeta.get(`${prn}:${bestKey}`);
|
|
869
|
+
const tecuPerNs = meta ? C_LIGHT * 1e-9 / (meta.gamma - 1) * (meta.fi * meta.fi / TEC_FACTOR) : 0;
|
|
857
870
|
series.push({
|
|
858
871
|
prn,
|
|
859
872
|
system: sys,
|
|
860
873
|
label: this.pairLabel.get(`${sys}:${bestKey}`) ?? bestKey,
|
|
874
|
+
codes: this.pairCodes.get(`${sys}:${bestKey}`) ?? ["", ""],
|
|
875
|
+
tecuPerNs,
|
|
861
876
|
points
|
|
862
877
|
});
|
|
863
878
|
}
|
|
@@ -866,6 +881,133 @@ var IonoAccumulator = class {
|
|
|
866
881
|
}
|
|
867
882
|
};
|
|
868
883
|
|
|
884
|
+
// src/analysis/dcb.ts
|
|
885
|
+
var PRN_RE = /^[A-Z]\d{2}$/;
|
|
886
|
+
var SVN_RE = /^[A-Z]\d{3}$/;
|
|
887
|
+
var OBS_RE = /^[CL]\d[A-Z]$/;
|
|
888
|
+
function sinexEpochMs(s) {
|
|
889
|
+
const [y, d, sec] = s.split(":").map(Number);
|
|
890
|
+
if (!y) return Infinity;
|
|
891
|
+
return Date.UTC(y, 0, 1) + ((d ?? 1) - 1) * 864e5 + (sec ?? 0) * 1e3;
|
|
892
|
+
}
|
|
893
|
+
function parseSinexBiasDcb(text, epochMs) {
|
|
894
|
+
const rows = /* @__PURE__ */ new Map();
|
|
895
|
+
for (const line of text.split("\n")) {
|
|
896
|
+
if (!line.startsWith(" DSB") && !line.startsWith("DSB")) continue;
|
|
897
|
+
const t = line.trim().split(/\s+/);
|
|
898
|
+
if (t.length < 9 || !SVN_RE.test(t[1]) || !PRN_RE.test(t[2]) || !OBS_RE.test(t[3]) || !OBS_RE.test(t[4])) {
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
const unitIdx = t.indexOf("ns");
|
|
902
|
+
if (unitIdx < 0 || unitIdx + 1 >= t.length) continue;
|
|
903
|
+
const value = parseFloat(t[unitIdx + 1]);
|
|
904
|
+
if (!isFinite(value)) continue;
|
|
905
|
+
const start = sinexEpochMs(t[5] ?? "");
|
|
906
|
+
const end = sinexEpochMs(t[6] ?? "");
|
|
907
|
+
const covers = epochMs !== void 0 && epochMs >= start && epochMs < end;
|
|
908
|
+
const prn = t[2];
|
|
909
|
+
const pair = `${t[3]}-${t[4]}`;
|
|
910
|
+
let sat = rows.get(prn);
|
|
911
|
+
if (!sat) {
|
|
912
|
+
sat = /* @__PURE__ */ new Map();
|
|
913
|
+
rows.set(prn, sat);
|
|
914
|
+
}
|
|
915
|
+
const prev = sat.get(pair);
|
|
916
|
+
if (!prev || covers && !prev.covers || covers === prev.covers && end > prev.end) {
|
|
917
|
+
sat.set(pair, { value, end, covers });
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
const out = /* @__PURE__ */ new Map();
|
|
921
|
+
for (const [prn, sat] of rows) {
|
|
922
|
+
const m = /* @__PURE__ */ new Map();
|
|
923
|
+
for (const [pair, row] of sat) m.set(pair, row.value);
|
|
924
|
+
out.set(prn, m);
|
|
925
|
+
}
|
|
926
|
+
return out;
|
|
927
|
+
}
|
|
928
|
+
function percentile(values, p) {
|
|
929
|
+
const s = [...values].sort((a, b) => a - b);
|
|
930
|
+
return s[Math.min(s.length - 1, Math.floor(p * s.length))] ?? 0;
|
|
931
|
+
}
|
|
932
|
+
function applyIonoDcb(iono, satDcb) {
|
|
933
|
+
const missing = [];
|
|
934
|
+
let corrected = 0;
|
|
935
|
+
const dcbFor = (s) => {
|
|
936
|
+
const sat = satDcb.get(s.prn);
|
|
937
|
+
if (!sat) return null;
|
|
938
|
+
const fwd = sat.get(`${s.codes[0]}-${s.codes[1]}`);
|
|
939
|
+
if (fwd !== void 0) return fwd;
|
|
940
|
+
const rev = sat.get(`${s.codes[1]}-${s.codes[0]}`);
|
|
941
|
+
if (rev !== void 0) return -rev;
|
|
942
|
+
return null;
|
|
943
|
+
};
|
|
944
|
+
const series = iono.series.map((s) => {
|
|
945
|
+
const dcbNs = dcbFor(s);
|
|
946
|
+
if (dcbNs === null) {
|
|
947
|
+
missing.push(s.prn);
|
|
948
|
+
return s;
|
|
949
|
+
}
|
|
950
|
+
corrected++;
|
|
951
|
+
const shift = dcbNs * s.tecuPerNs;
|
|
952
|
+
return {
|
|
953
|
+
...s,
|
|
954
|
+
points: s.points.map((p) => ({ time: p.time, stec: p.stec + shift }))
|
|
955
|
+
};
|
|
956
|
+
});
|
|
957
|
+
const missingSet = new Set(missing);
|
|
958
|
+
const receiverDcbTecu = {};
|
|
959
|
+
const groups = /* @__PURE__ */ new Map();
|
|
960
|
+
for (const s of series) {
|
|
961
|
+
const key = `${s.system} ${s.label}`;
|
|
962
|
+
let g = groups.get(key);
|
|
963
|
+
if (!g) {
|
|
964
|
+
g = [];
|
|
965
|
+
groups.set(key, g);
|
|
966
|
+
}
|
|
967
|
+
g.push(s);
|
|
968
|
+
}
|
|
969
|
+
const out = [];
|
|
970
|
+
for (const [key, group] of groups) {
|
|
971
|
+
const samples = [];
|
|
972
|
+
for (const s of group) {
|
|
973
|
+
if (missingSet.has(s.prn)) continue;
|
|
974
|
+
for (const p of s.points) samples.push(p.stec);
|
|
975
|
+
}
|
|
976
|
+
if (samples.length >= 100) {
|
|
977
|
+
const rx = percentile(samples, 0.01);
|
|
978
|
+
receiverDcbTecu[key] = rx;
|
|
979
|
+
for (const s of group) {
|
|
980
|
+
out.push({
|
|
981
|
+
...s,
|
|
982
|
+
points: s.points.map((p) => ({
|
|
983
|
+
time: p.time,
|
|
984
|
+
stec: p.stec - rx
|
|
985
|
+
}))
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
} else {
|
|
989
|
+
out.push(...group);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
out.sort((a, b) => a.prn.localeCompare(b.prn));
|
|
993
|
+
let sum = 0;
|
|
994
|
+
let count = 0;
|
|
995
|
+
let maxStec = 0;
|
|
996
|
+
for (const s of out) {
|
|
997
|
+
for (const p of s.points) {
|
|
998
|
+
sum += p.stec;
|
|
999
|
+
count++;
|
|
1000
|
+
if (p.stec > maxStec) maxStec = p.stec;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
return {
|
|
1004
|
+
result: { series: out, maxStec, meanStec: count > 0 ? sum / count : 0 },
|
|
1005
|
+
satellitesCorrected: corrected,
|
|
1006
|
+
satellitesMissing: [...new Set(missing)].sort(),
|
|
1007
|
+
receiverDcbTecu
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
|
|
869
1011
|
// src/rinex/crx.ts
|
|
870
1012
|
function crxRepair(old, diff) {
|
|
871
1013
|
const chars = old.split("");
|
|
@@ -1687,5 +1829,7 @@ async function analyzeQuality(file, header, onProgress, signal) {
|
|
|
1687
1829
|
CycleSlipAccumulator,
|
|
1688
1830
|
IonoAccumulator,
|
|
1689
1831
|
MultipathAccumulator,
|
|
1690
|
-
analyzeQuality
|
|
1832
|
+
analyzeQuality,
|
|
1833
|
+
applyIonoDcb,
|
|
1834
|
+
parseSinexBiasDcb
|
|
1691
1835
|
});
|
package/dist/analysis.d.cts
CHANGED
|
@@ -231,6 +231,12 @@ interface IonoSeries {
|
|
|
231
231
|
system: string;
|
|
232
232
|
/** Band pair used, e.g. "L1-L2". */
|
|
233
233
|
label: string;
|
|
234
|
+
/** Observation codes of the pair, e.g. ["C1C", "C2W"] — the key for
|
|
235
|
+
* matching differential code bias products. */
|
|
236
|
+
codes: [string, string];
|
|
237
|
+
/** TECU shift caused by 1 ns of geometry-free code bias (B_i − B_j);
|
|
238
|
+
* multiply a product DCB in ns by this to correct the series. */
|
|
239
|
+
tecuPerNs: number;
|
|
234
240
|
/** Time series of slant TEC values (TECU, DCB-biased). */
|
|
235
241
|
points: IonoPoint[];
|
|
236
242
|
}
|
|
@@ -249,9 +255,11 @@ declare class IonoAccumulator {
|
|
|
249
255
|
private obsIndices;
|
|
250
256
|
private gloChannels;
|
|
251
257
|
private pairLabel;
|
|
258
|
+
private pairCodes;
|
|
259
|
+
private pairMeta;
|
|
252
260
|
constructor(header: RinexHeader);
|
|
253
261
|
/** Observation callback — wire this into parseRinexStream. */
|
|
254
|
-
onObservation: (time: number, prn: string,
|
|
262
|
+
onObservation: (time: number, prn: string, codes: string[], values: (number | null)[]) => void;
|
|
255
263
|
private push;
|
|
256
264
|
/** External cycle-slip notification — close affected arcs. */
|
|
257
265
|
notifySlip(_time: number, prn: string, bands: Set<string>): void;
|
|
@@ -260,6 +268,48 @@ declare class IonoAccumulator {
|
|
|
260
268
|
finalize(): IonoResult;
|
|
261
269
|
}
|
|
262
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Differential code bias (DCB) products and their application to the
|
|
273
|
+
* slant-TEC series from the ionosphere module.
|
|
274
|
+
*
|
|
275
|
+
* Satellite DCBs come from SINEX_BIAS files published by the IGS
|
|
276
|
+
* analysis centres (e.g. CAS `CAS0MGXRAP_*_DCB.BSX`). The levelled
|
|
277
|
+
* geometry-free combination carries B_i − B_j (satellite) plus the
|
|
278
|
+
* receiver bias; subtracting the product value per satellite and then
|
|
279
|
+
* estimating the receiver bias from the night-time floor (the levelled
|
|
280
|
+
* combination is low-noise and TEC is near zero at night — Hans van
|
|
281
|
+
* der Marel's "no negative values" criterion) yields calibrated STEC.
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
/** PRN → "C1C-C2W" → bias (ns), satellite entries only. */
|
|
285
|
+
type SatDcbMap = Map<string, Map<string, number>>;
|
|
286
|
+
/**
|
|
287
|
+
* Parse satellite DSB entries from a SINEX_BIAS file (ESA .BIA,
|
|
288
|
+
* CAS/GFZ .BSX). Station entries (which carry a site name between PRN
|
|
289
|
+
* and the codes) are skipped. Long-history files publish several
|
|
290
|
+
* validity windows per satellite/pair — with values differing by many
|
|
291
|
+
* ns across SVN swaps — so pass the observation epoch to select the
|
|
292
|
+
* covering window; without it, the latest window wins. Values are
|
|
293
|
+
* returned in ns as published.
|
|
294
|
+
*/
|
|
295
|
+
declare function parseSinexBiasDcb(text: string, epochMs?: number): SatDcbMap;
|
|
296
|
+
interface IonoDcbResult {
|
|
297
|
+
result: IonoResult;
|
|
298
|
+
/** Satellites whose product DCB was found and applied. */
|
|
299
|
+
satellitesCorrected: number;
|
|
300
|
+
/** PRNs with no matching product entry (left sat-DCB-biased). */
|
|
301
|
+
satellitesMissing: string[];
|
|
302
|
+
/** Estimated receiver bias per "system label" group (TECU). */
|
|
303
|
+
receiverDcbTecu: Record<string, number>;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Apply satellite DCBs from a product file to an ionosphere result and
|
|
307
|
+
* estimate the receiver bias per system/pair group from the night-time
|
|
308
|
+
* floor (1st percentile → 0). Series without a product entry get the
|
|
309
|
+
* satellite part left in; they are reported in `satellitesMissing`.
|
|
310
|
+
*/
|
|
311
|
+
declare function applyIonoDcb(iono: IonoResult, satDcb: SatDcbMap): IonoDcbResult;
|
|
312
|
+
|
|
263
313
|
/**
|
|
264
314
|
* Combined quality analysis — runs cycle slip detection, data completeness,
|
|
265
315
|
* multipath and ionosphere analysis in a single file re-parse pass.
|
|
@@ -273,4 +323,4 @@ interface QualityResult {
|
|
|
273
323
|
}
|
|
274
324
|
declare function analyzeQuality(file: File, header: RinexHeader, onProgress?: (percent: number) => void, signal?: AbortSignal): Promise<QualityResult>;
|
|
275
325
|
|
|
276
|
-
export { type CompleteSatSignal, type CompleteSignalStats, CompletenessAccumulator, type CompletenessResult, CycleSlipAccumulator, type CycleSlipEvent, type CycleSlipResult, type CycleSlipSignalStats, IonoAccumulator, type IonoPoint, type IonoResult, type IonoSeries, MultipathAccumulator, type MultipathPoint, type MultipathResult, type MultipathSeries, type MultipathSignalStat, type QualityResult, analyzeQuality };
|
|
326
|
+
export { type CompleteSatSignal, type CompleteSignalStats, CompletenessAccumulator, type CompletenessResult, CycleSlipAccumulator, type CycleSlipEvent, type CycleSlipResult, type CycleSlipSignalStats, IonoAccumulator, type IonoDcbResult, type IonoPoint, type IonoResult, type IonoSeries, MultipathAccumulator, type MultipathPoint, type MultipathResult, type MultipathSeries, type MultipathSignalStat, type QualityResult, type SatDcbMap, analyzeQuality, applyIonoDcb, parseSinexBiasDcb };
|
package/dist/analysis.d.ts
CHANGED
|
@@ -231,6 +231,12 @@ interface IonoSeries {
|
|
|
231
231
|
system: string;
|
|
232
232
|
/** Band pair used, e.g. "L1-L2". */
|
|
233
233
|
label: string;
|
|
234
|
+
/** Observation codes of the pair, e.g. ["C1C", "C2W"] — the key for
|
|
235
|
+
* matching differential code bias products. */
|
|
236
|
+
codes: [string, string];
|
|
237
|
+
/** TECU shift caused by 1 ns of geometry-free code bias (B_i − B_j);
|
|
238
|
+
* multiply a product DCB in ns by this to correct the series. */
|
|
239
|
+
tecuPerNs: number;
|
|
234
240
|
/** Time series of slant TEC values (TECU, DCB-biased). */
|
|
235
241
|
points: IonoPoint[];
|
|
236
242
|
}
|
|
@@ -249,9 +255,11 @@ declare class IonoAccumulator {
|
|
|
249
255
|
private obsIndices;
|
|
250
256
|
private gloChannels;
|
|
251
257
|
private pairLabel;
|
|
258
|
+
private pairCodes;
|
|
259
|
+
private pairMeta;
|
|
252
260
|
constructor(header: RinexHeader);
|
|
253
261
|
/** Observation callback — wire this into parseRinexStream. */
|
|
254
|
-
onObservation: (time: number, prn: string,
|
|
262
|
+
onObservation: (time: number, prn: string, codes: string[], values: (number | null)[]) => void;
|
|
255
263
|
private push;
|
|
256
264
|
/** External cycle-slip notification — close affected arcs. */
|
|
257
265
|
notifySlip(_time: number, prn: string, bands: Set<string>): void;
|
|
@@ -260,6 +268,48 @@ declare class IonoAccumulator {
|
|
|
260
268
|
finalize(): IonoResult;
|
|
261
269
|
}
|
|
262
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Differential code bias (DCB) products and their application to the
|
|
273
|
+
* slant-TEC series from the ionosphere module.
|
|
274
|
+
*
|
|
275
|
+
* Satellite DCBs come from SINEX_BIAS files published by the IGS
|
|
276
|
+
* analysis centres (e.g. CAS `CAS0MGXRAP_*_DCB.BSX`). The levelled
|
|
277
|
+
* geometry-free combination carries B_i − B_j (satellite) plus the
|
|
278
|
+
* receiver bias; subtracting the product value per satellite and then
|
|
279
|
+
* estimating the receiver bias from the night-time floor (the levelled
|
|
280
|
+
* combination is low-noise and TEC is near zero at night — Hans van
|
|
281
|
+
* der Marel's "no negative values" criterion) yields calibrated STEC.
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
/** PRN → "C1C-C2W" → bias (ns), satellite entries only. */
|
|
285
|
+
type SatDcbMap = Map<string, Map<string, number>>;
|
|
286
|
+
/**
|
|
287
|
+
* Parse satellite DSB entries from a SINEX_BIAS file (ESA .BIA,
|
|
288
|
+
* CAS/GFZ .BSX). Station entries (which carry a site name between PRN
|
|
289
|
+
* and the codes) are skipped. Long-history files publish several
|
|
290
|
+
* validity windows per satellite/pair — with values differing by many
|
|
291
|
+
* ns across SVN swaps — so pass the observation epoch to select the
|
|
292
|
+
* covering window; without it, the latest window wins. Values are
|
|
293
|
+
* returned in ns as published.
|
|
294
|
+
*/
|
|
295
|
+
declare function parseSinexBiasDcb(text: string, epochMs?: number): SatDcbMap;
|
|
296
|
+
interface IonoDcbResult {
|
|
297
|
+
result: IonoResult;
|
|
298
|
+
/** Satellites whose product DCB was found and applied. */
|
|
299
|
+
satellitesCorrected: number;
|
|
300
|
+
/** PRNs with no matching product entry (left sat-DCB-biased). */
|
|
301
|
+
satellitesMissing: string[];
|
|
302
|
+
/** Estimated receiver bias per "system label" group (TECU). */
|
|
303
|
+
receiverDcbTecu: Record<string, number>;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Apply satellite DCBs from a product file to an ionosphere result and
|
|
307
|
+
* estimate the receiver bias per system/pair group from the night-time
|
|
308
|
+
* floor (1st percentile → 0). Series without a product entry get the
|
|
309
|
+
* satellite part left in; they are reported in `satellitesMissing`.
|
|
310
|
+
*/
|
|
311
|
+
declare function applyIonoDcb(iono: IonoResult, satDcb: SatDcbMap): IonoDcbResult;
|
|
312
|
+
|
|
263
313
|
/**
|
|
264
314
|
* Combined quality analysis — runs cycle slip detection, data completeness,
|
|
265
315
|
* multipath and ionosphere analysis in a single file re-parse pass.
|
|
@@ -273,4 +323,4 @@ interface QualityResult {
|
|
|
273
323
|
}
|
|
274
324
|
declare function analyzeQuality(file: File, header: RinexHeader, onProgress?: (percent: number) => void, signal?: AbortSignal): Promise<QualityResult>;
|
|
275
325
|
|
|
276
|
-
export { type CompleteSatSignal, type CompleteSignalStats, CompletenessAccumulator, type CompletenessResult, CycleSlipAccumulator, type CycleSlipEvent, type CycleSlipResult, type CycleSlipSignalStats, IonoAccumulator, type IonoPoint, type IonoResult, type IonoSeries, MultipathAccumulator, type MultipathPoint, type MultipathResult, type MultipathSeries, type MultipathSignalStat, type QualityResult, analyzeQuality };
|
|
326
|
+
export { type CompleteSatSignal, type CompleteSignalStats, CompletenessAccumulator, type CompletenessResult, CycleSlipAccumulator, type CycleSlipEvent, type CycleSlipResult, type CycleSlipSignalStats, IonoAccumulator, type IonoDcbResult, type IonoPoint, type IonoResult, type IonoSeries, MultipathAccumulator, type MultipathPoint, type MultipathResult, type MultipathSeries, type MultipathSignalStat, type QualityResult, type SatDcbMap, analyzeQuality, applyIonoDcb, parseSinexBiasDcb };
|
package/dist/analysis.js
CHANGED
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
CycleSlipAccumulator,
|
|
4
4
|
IonoAccumulator,
|
|
5
5
|
MultipathAccumulator,
|
|
6
|
-
analyzeQuality
|
|
7
|
-
|
|
6
|
+
analyzeQuality,
|
|
7
|
+
applyIonoDcb,
|
|
8
|
+
parseSinexBiasDcb
|
|
9
|
+
} from "./chunk-EJQ6NG5U.js";
|
|
8
10
|
import "./chunk-3U5AX7PY.js";
|
|
9
11
|
import "./chunk-FIEWO4J4.js";
|
|
10
12
|
export {
|
|
@@ -12,5 +14,7 @@ export {
|
|
|
12
14
|
CycleSlipAccumulator,
|
|
13
15
|
IonoAccumulator,
|
|
14
16
|
MultipathAccumulator,
|
|
15
|
-
analyzeQuality
|
|
17
|
+
analyzeQuality,
|
|
18
|
+
applyIonoDcb,
|
|
19
|
+
parseSinexBiasDcb
|
|
16
20
|
};
|
|
@@ -516,13 +516,15 @@ var IonoAccumulator = class {
|
|
|
516
516
|
obsIndices;
|
|
517
517
|
gloChannels;
|
|
518
518
|
pairLabel = /* @__PURE__ */ new Map();
|
|
519
|
+
pairCodes = /* @__PURE__ */ new Map();
|
|
520
|
+
pairMeta = /* @__PURE__ */ new Map();
|
|
519
521
|
constructor(header) {
|
|
520
522
|
this.interval = header.interval ?? 30;
|
|
521
523
|
this.obsIndices = buildObsIndices(header);
|
|
522
524
|
this.gloChannels = buildGloChannelMap(header.glonassSlots);
|
|
523
525
|
}
|
|
524
526
|
/** Observation callback — wire this into parseRinexStream. */
|
|
525
|
-
onObservation = (time, prn,
|
|
527
|
+
onObservation = (time, prn, codes, values) => {
|
|
526
528
|
const sys = prn[0];
|
|
527
529
|
const bandMap = this.obsIndices.get(sys) ?? this.obsIndices.get("_v2");
|
|
528
530
|
if (!bandMap) return;
|
|
@@ -533,7 +535,12 @@ var IonoAccumulator = class {
|
|
|
533
535
|
const lVal = values[L];
|
|
534
536
|
const freq = getFreq(this.gloChannels, prn, band);
|
|
535
537
|
if (cVal != null && cVal !== 0 && lVal != null && lVal !== 0 && freq) {
|
|
536
|
-
bandData.set(band, {
|
|
538
|
+
bandData.set(band, {
|
|
539
|
+
C: cVal,
|
|
540
|
+
L: lVal,
|
|
541
|
+
f: freq,
|
|
542
|
+
code: codes[C] ?? `C${band}`
|
|
543
|
+
});
|
|
537
544
|
}
|
|
538
545
|
}
|
|
539
546
|
if (bandData.size < 2) return;
|
|
@@ -552,7 +559,9 @@ var IonoAccumulator = class {
|
|
|
552
559
|
const bLabel = BAND_LABELS[sys]?.[bi] ?? bi;
|
|
553
560
|
const rLabel = BAND_LABELS[sys]?.[bj] ?? bj;
|
|
554
561
|
this.pairLabel.set(`${sys}:${pairKey}`, `${bLabel}-${rLabel}`);
|
|
562
|
+
this.pairCodes.set(`${sys}:${pairKey}`, [di.code, dj.code]);
|
|
555
563
|
}
|
|
564
|
+
this.pairMeta.set(`${prn}:${pairKey}`, { fi: di.f, gamma });
|
|
556
565
|
this.push(prn, pairKey, time, l4, p4, di.f, gamma);
|
|
557
566
|
break;
|
|
558
567
|
}
|
|
@@ -653,10 +662,14 @@ var IonoAccumulator = class {
|
|
|
653
662
|
count++;
|
|
654
663
|
if (p.stec > maxStec) maxStec = p.stec;
|
|
655
664
|
}
|
|
665
|
+
const meta = this.pairMeta.get(`${prn}:${bestKey}`);
|
|
666
|
+
const tecuPerNs = meta ? C_LIGHT * 1e-9 / (meta.gamma - 1) * (meta.fi * meta.fi / TEC_FACTOR) : 0;
|
|
656
667
|
series.push({
|
|
657
668
|
prn,
|
|
658
669
|
system: sys,
|
|
659
670
|
label: this.pairLabel.get(`${sys}:${bestKey}`) ?? bestKey,
|
|
671
|
+
codes: this.pairCodes.get(`${sys}:${bestKey}`) ?? ["", ""],
|
|
672
|
+
tecuPerNs,
|
|
660
673
|
points
|
|
661
674
|
});
|
|
662
675
|
}
|
|
@@ -665,6 +678,133 @@ var IonoAccumulator = class {
|
|
|
665
678
|
}
|
|
666
679
|
};
|
|
667
680
|
|
|
681
|
+
// src/analysis/dcb.ts
|
|
682
|
+
var PRN_RE = /^[A-Z]\d{2}$/;
|
|
683
|
+
var SVN_RE = /^[A-Z]\d{3}$/;
|
|
684
|
+
var OBS_RE = /^[CL]\d[A-Z]$/;
|
|
685
|
+
function sinexEpochMs(s) {
|
|
686
|
+
const [y, d, sec] = s.split(":").map(Number);
|
|
687
|
+
if (!y) return Infinity;
|
|
688
|
+
return Date.UTC(y, 0, 1) + ((d ?? 1) - 1) * 864e5 + (sec ?? 0) * 1e3;
|
|
689
|
+
}
|
|
690
|
+
function parseSinexBiasDcb(text, epochMs) {
|
|
691
|
+
const rows = /* @__PURE__ */ new Map();
|
|
692
|
+
for (const line of text.split("\n")) {
|
|
693
|
+
if (!line.startsWith(" DSB") && !line.startsWith("DSB")) continue;
|
|
694
|
+
const t = line.trim().split(/\s+/);
|
|
695
|
+
if (t.length < 9 || !SVN_RE.test(t[1]) || !PRN_RE.test(t[2]) || !OBS_RE.test(t[3]) || !OBS_RE.test(t[4])) {
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
const unitIdx = t.indexOf("ns");
|
|
699
|
+
if (unitIdx < 0 || unitIdx + 1 >= t.length) continue;
|
|
700
|
+
const value = parseFloat(t[unitIdx + 1]);
|
|
701
|
+
if (!isFinite(value)) continue;
|
|
702
|
+
const start = sinexEpochMs(t[5] ?? "");
|
|
703
|
+
const end = sinexEpochMs(t[6] ?? "");
|
|
704
|
+
const covers = epochMs !== void 0 && epochMs >= start && epochMs < end;
|
|
705
|
+
const prn = t[2];
|
|
706
|
+
const pair = `${t[3]}-${t[4]}`;
|
|
707
|
+
let sat = rows.get(prn);
|
|
708
|
+
if (!sat) {
|
|
709
|
+
sat = /* @__PURE__ */ new Map();
|
|
710
|
+
rows.set(prn, sat);
|
|
711
|
+
}
|
|
712
|
+
const prev = sat.get(pair);
|
|
713
|
+
if (!prev || covers && !prev.covers || covers === prev.covers && end > prev.end) {
|
|
714
|
+
sat.set(pair, { value, end, covers });
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
const out = /* @__PURE__ */ new Map();
|
|
718
|
+
for (const [prn, sat] of rows) {
|
|
719
|
+
const m = /* @__PURE__ */ new Map();
|
|
720
|
+
for (const [pair, row] of sat) m.set(pair, row.value);
|
|
721
|
+
out.set(prn, m);
|
|
722
|
+
}
|
|
723
|
+
return out;
|
|
724
|
+
}
|
|
725
|
+
function percentile(values, p) {
|
|
726
|
+
const s = [...values].sort((a, b) => a - b);
|
|
727
|
+
return s[Math.min(s.length - 1, Math.floor(p * s.length))] ?? 0;
|
|
728
|
+
}
|
|
729
|
+
function applyIonoDcb(iono, satDcb) {
|
|
730
|
+
const missing = [];
|
|
731
|
+
let corrected = 0;
|
|
732
|
+
const dcbFor = (s) => {
|
|
733
|
+
const sat = satDcb.get(s.prn);
|
|
734
|
+
if (!sat) return null;
|
|
735
|
+
const fwd = sat.get(`${s.codes[0]}-${s.codes[1]}`);
|
|
736
|
+
if (fwd !== void 0) return fwd;
|
|
737
|
+
const rev = sat.get(`${s.codes[1]}-${s.codes[0]}`);
|
|
738
|
+
if (rev !== void 0) return -rev;
|
|
739
|
+
return null;
|
|
740
|
+
};
|
|
741
|
+
const series = iono.series.map((s) => {
|
|
742
|
+
const dcbNs = dcbFor(s);
|
|
743
|
+
if (dcbNs === null) {
|
|
744
|
+
missing.push(s.prn);
|
|
745
|
+
return s;
|
|
746
|
+
}
|
|
747
|
+
corrected++;
|
|
748
|
+
const shift = dcbNs * s.tecuPerNs;
|
|
749
|
+
return {
|
|
750
|
+
...s,
|
|
751
|
+
points: s.points.map((p) => ({ time: p.time, stec: p.stec + shift }))
|
|
752
|
+
};
|
|
753
|
+
});
|
|
754
|
+
const missingSet = new Set(missing);
|
|
755
|
+
const receiverDcbTecu = {};
|
|
756
|
+
const groups = /* @__PURE__ */ new Map();
|
|
757
|
+
for (const s of series) {
|
|
758
|
+
const key = `${s.system} ${s.label}`;
|
|
759
|
+
let g = groups.get(key);
|
|
760
|
+
if (!g) {
|
|
761
|
+
g = [];
|
|
762
|
+
groups.set(key, g);
|
|
763
|
+
}
|
|
764
|
+
g.push(s);
|
|
765
|
+
}
|
|
766
|
+
const out = [];
|
|
767
|
+
for (const [key, group] of groups) {
|
|
768
|
+
const samples = [];
|
|
769
|
+
for (const s of group) {
|
|
770
|
+
if (missingSet.has(s.prn)) continue;
|
|
771
|
+
for (const p of s.points) samples.push(p.stec);
|
|
772
|
+
}
|
|
773
|
+
if (samples.length >= 100) {
|
|
774
|
+
const rx = percentile(samples, 0.01);
|
|
775
|
+
receiverDcbTecu[key] = rx;
|
|
776
|
+
for (const s of group) {
|
|
777
|
+
out.push({
|
|
778
|
+
...s,
|
|
779
|
+
points: s.points.map((p) => ({
|
|
780
|
+
time: p.time,
|
|
781
|
+
stec: p.stec - rx
|
|
782
|
+
}))
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
} else {
|
|
786
|
+
out.push(...group);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
out.sort((a, b) => a.prn.localeCompare(b.prn));
|
|
790
|
+
let sum = 0;
|
|
791
|
+
let count = 0;
|
|
792
|
+
let maxStec = 0;
|
|
793
|
+
for (const s of out) {
|
|
794
|
+
for (const p of s.points) {
|
|
795
|
+
sum += p.stec;
|
|
796
|
+
count++;
|
|
797
|
+
if (p.stec > maxStec) maxStec = p.stec;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return {
|
|
801
|
+
result: { series: out, maxStec, meanStec: count > 0 ? sum / count : 0 },
|
|
802
|
+
satellitesCorrected: corrected,
|
|
803
|
+
satellitesMissing: [...new Set(missing)].sort(),
|
|
804
|
+
receiverDcbTecu
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
|
|
668
808
|
// src/analysis/quality-analysis.ts
|
|
669
809
|
async function analyzeQuality(file, header, onProgress, signal) {
|
|
670
810
|
const mpAccum = new MultipathAccumulator(header);
|
|
@@ -700,5 +840,7 @@ export {
|
|
|
700
840
|
CycleSlipAccumulator,
|
|
701
841
|
CompletenessAccumulator,
|
|
702
842
|
IonoAccumulator,
|
|
843
|
+
parseSinexBiasDcb,
|
|
844
|
+
applyIonoDcb,
|
|
703
845
|
analyzeQuality
|
|
704
846
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -105,6 +105,7 @@ __export(src_exports, {
|
|
|
105
105
|
WarningAccumulator: () => WarningAccumulator,
|
|
106
106
|
analyzeQuality: () => analyzeQuality,
|
|
107
107
|
applyHelmert: () => applyHelmert,
|
|
108
|
+
applyIonoDcb: () => applyIonoDcb,
|
|
108
109
|
buildGloChannelMap: () => buildGloChannelMap,
|
|
109
110
|
buildObsIndices: () => buildObsIndices,
|
|
110
111
|
clampUnit: () => clampUnit,
|
|
@@ -207,6 +208,7 @@ __export(src_exports, {
|
|
|
207
208
|
parseNavFile: () => parseNavFile,
|
|
208
209
|
parseNmeaFile: () => parseNmeaFile,
|
|
209
210
|
parseRinexStream: () => parseRinexStream,
|
|
211
|
+
parseSinexBiasDcb: () => parseSinexBiasDcb,
|
|
210
212
|
parseSourcetable: () => parseSourcetable,
|
|
211
213
|
phiAltBOC: () => phiAltBOC,
|
|
212
214
|
phiBOCc: () => phiBOCc,
|
|
@@ -5545,13 +5547,15 @@ var IonoAccumulator = class {
|
|
|
5545
5547
|
obsIndices;
|
|
5546
5548
|
gloChannels;
|
|
5547
5549
|
pairLabel = /* @__PURE__ */ new Map();
|
|
5550
|
+
pairCodes = /* @__PURE__ */ new Map();
|
|
5551
|
+
pairMeta = /* @__PURE__ */ new Map();
|
|
5548
5552
|
constructor(header) {
|
|
5549
5553
|
this.interval = header.interval ?? 30;
|
|
5550
5554
|
this.obsIndices = buildObsIndices(header);
|
|
5551
5555
|
this.gloChannels = buildGloChannelMap(header.glonassSlots);
|
|
5552
5556
|
}
|
|
5553
5557
|
/** Observation callback — wire this into parseRinexStream. */
|
|
5554
|
-
onObservation = (time, prn,
|
|
5558
|
+
onObservation = (time, prn, codes, values) => {
|
|
5555
5559
|
const sys = prn[0];
|
|
5556
5560
|
const bandMap = this.obsIndices.get(sys) ?? this.obsIndices.get("_v2");
|
|
5557
5561
|
if (!bandMap) return;
|
|
@@ -5562,7 +5566,12 @@ var IonoAccumulator = class {
|
|
|
5562
5566
|
const lVal = values[L];
|
|
5563
5567
|
const freq = getFreq(this.gloChannels, prn, band);
|
|
5564
5568
|
if (cVal != null && cVal !== 0 && lVal != null && lVal !== 0 && freq) {
|
|
5565
|
-
bandData.set(band, {
|
|
5569
|
+
bandData.set(band, {
|
|
5570
|
+
C: cVal,
|
|
5571
|
+
L: lVal,
|
|
5572
|
+
f: freq,
|
|
5573
|
+
code: codes[C] ?? `C${band}`
|
|
5574
|
+
});
|
|
5566
5575
|
}
|
|
5567
5576
|
}
|
|
5568
5577
|
if (bandData.size < 2) return;
|
|
@@ -5581,7 +5590,9 @@ var IonoAccumulator = class {
|
|
|
5581
5590
|
const bLabel = BAND_LABELS[sys]?.[bi] ?? bi;
|
|
5582
5591
|
const rLabel = BAND_LABELS[sys]?.[bj] ?? bj;
|
|
5583
5592
|
this.pairLabel.set(`${sys}:${pairKey}`, `${bLabel}-${rLabel}`);
|
|
5593
|
+
this.pairCodes.set(`${sys}:${pairKey}`, [di.code, dj.code]);
|
|
5584
5594
|
}
|
|
5595
|
+
this.pairMeta.set(`${prn}:${pairKey}`, { fi: di.f, gamma });
|
|
5585
5596
|
this.push(prn, pairKey, time, l4, p4, di.f, gamma);
|
|
5586
5597
|
break;
|
|
5587
5598
|
}
|
|
@@ -5682,10 +5693,14 @@ var IonoAccumulator = class {
|
|
|
5682
5693
|
count++;
|
|
5683
5694
|
if (p.stec > maxStec) maxStec = p.stec;
|
|
5684
5695
|
}
|
|
5696
|
+
const meta = this.pairMeta.get(`${prn}:${bestKey}`);
|
|
5697
|
+
const tecuPerNs = meta ? C_LIGHT * 1e-9 / (meta.gamma - 1) * (meta.fi * meta.fi / TEC_FACTOR) : 0;
|
|
5685
5698
|
series.push({
|
|
5686
5699
|
prn,
|
|
5687
5700
|
system: sys,
|
|
5688
5701
|
label: this.pairLabel.get(`${sys}:${bestKey}`) ?? bestKey,
|
|
5702
|
+
codes: this.pairCodes.get(`${sys}:${bestKey}`) ?? ["", ""],
|
|
5703
|
+
tecuPerNs,
|
|
5689
5704
|
points
|
|
5690
5705
|
});
|
|
5691
5706
|
}
|
|
@@ -5694,6 +5709,133 @@ var IonoAccumulator = class {
|
|
|
5694
5709
|
}
|
|
5695
5710
|
};
|
|
5696
5711
|
|
|
5712
|
+
// src/analysis/dcb.ts
|
|
5713
|
+
var PRN_RE = /^[A-Z]\d{2}$/;
|
|
5714
|
+
var SVN_RE = /^[A-Z]\d{3}$/;
|
|
5715
|
+
var OBS_RE = /^[CL]\d[A-Z]$/;
|
|
5716
|
+
function sinexEpochMs(s) {
|
|
5717
|
+
const [y, d, sec] = s.split(":").map(Number);
|
|
5718
|
+
if (!y) return Infinity;
|
|
5719
|
+
return Date.UTC(y, 0, 1) + ((d ?? 1) - 1) * 864e5 + (sec ?? 0) * 1e3;
|
|
5720
|
+
}
|
|
5721
|
+
function parseSinexBiasDcb(text, epochMs) {
|
|
5722
|
+
const rows = /* @__PURE__ */ new Map();
|
|
5723
|
+
for (const line of text.split("\n")) {
|
|
5724
|
+
if (!line.startsWith(" DSB") && !line.startsWith("DSB")) continue;
|
|
5725
|
+
const t = line.trim().split(/\s+/);
|
|
5726
|
+
if (t.length < 9 || !SVN_RE.test(t[1]) || !PRN_RE.test(t[2]) || !OBS_RE.test(t[3]) || !OBS_RE.test(t[4])) {
|
|
5727
|
+
continue;
|
|
5728
|
+
}
|
|
5729
|
+
const unitIdx = t.indexOf("ns");
|
|
5730
|
+
if (unitIdx < 0 || unitIdx + 1 >= t.length) continue;
|
|
5731
|
+
const value = parseFloat(t[unitIdx + 1]);
|
|
5732
|
+
if (!isFinite(value)) continue;
|
|
5733
|
+
const start = sinexEpochMs(t[5] ?? "");
|
|
5734
|
+
const end = sinexEpochMs(t[6] ?? "");
|
|
5735
|
+
const covers = epochMs !== void 0 && epochMs >= start && epochMs < end;
|
|
5736
|
+
const prn = t[2];
|
|
5737
|
+
const pair = `${t[3]}-${t[4]}`;
|
|
5738
|
+
let sat = rows.get(prn);
|
|
5739
|
+
if (!sat) {
|
|
5740
|
+
sat = /* @__PURE__ */ new Map();
|
|
5741
|
+
rows.set(prn, sat);
|
|
5742
|
+
}
|
|
5743
|
+
const prev = sat.get(pair);
|
|
5744
|
+
if (!prev || covers && !prev.covers || covers === prev.covers && end > prev.end) {
|
|
5745
|
+
sat.set(pair, { value, end, covers });
|
|
5746
|
+
}
|
|
5747
|
+
}
|
|
5748
|
+
const out = /* @__PURE__ */ new Map();
|
|
5749
|
+
for (const [prn, sat] of rows) {
|
|
5750
|
+
const m = /* @__PURE__ */ new Map();
|
|
5751
|
+
for (const [pair, row] of sat) m.set(pair, row.value);
|
|
5752
|
+
out.set(prn, m);
|
|
5753
|
+
}
|
|
5754
|
+
return out;
|
|
5755
|
+
}
|
|
5756
|
+
function percentile(values, p) {
|
|
5757
|
+
const s = [...values].sort((a, b) => a - b);
|
|
5758
|
+
return s[Math.min(s.length - 1, Math.floor(p * s.length))] ?? 0;
|
|
5759
|
+
}
|
|
5760
|
+
function applyIonoDcb(iono, satDcb) {
|
|
5761
|
+
const missing = [];
|
|
5762
|
+
let corrected = 0;
|
|
5763
|
+
const dcbFor = (s) => {
|
|
5764
|
+
const sat = satDcb.get(s.prn);
|
|
5765
|
+
if (!sat) return null;
|
|
5766
|
+
const fwd = sat.get(`${s.codes[0]}-${s.codes[1]}`);
|
|
5767
|
+
if (fwd !== void 0) return fwd;
|
|
5768
|
+
const rev = sat.get(`${s.codes[1]}-${s.codes[0]}`);
|
|
5769
|
+
if (rev !== void 0) return -rev;
|
|
5770
|
+
return null;
|
|
5771
|
+
};
|
|
5772
|
+
const series = iono.series.map((s) => {
|
|
5773
|
+
const dcbNs = dcbFor(s);
|
|
5774
|
+
if (dcbNs === null) {
|
|
5775
|
+
missing.push(s.prn);
|
|
5776
|
+
return s;
|
|
5777
|
+
}
|
|
5778
|
+
corrected++;
|
|
5779
|
+
const shift = dcbNs * s.tecuPerNs;
|
|
5780
|
+
return {
|
|
5781
|
+
...s,
|
|
5782
|
+
points: s.points.map((p) => ({ time: p.time, stec: p.stec + shift }))
|
|
5783
|
+
};
|
|
5784
|
+
});
|
|
5785
|
+
const missingSet = new Set(missing);
|
|
5786
|
+
const receiverDcbTecu = {};
|
|
5787
|
+
const groups = /* @__PURE__ */ new Map();
|
|
5788
|
+
for (const s of series) {
|
|
5789
|
+
const key = `${s.system} ${s.label}`;
|
|
5790
|
+
let g = groups.get(key);
|
|
5791
|
+
if (!g) {
|
|
5792
|
+
g = [];
|
|
5793
|
+
groups.set(key, g);
|
|
5794
|
+
}
|
|
5795
|
+
g.push(s);
|
|
5796
|
+
}
|
|
5797
|
+
const out = [];
|
|
5798
|
+
for (const [key, group] of groups) {
|
|
5799
|
+
const samples = [];
|
|
5800
|
+
for (const s of group) {
|
|
5801
|
+
if (missingSet.has(s.prn)) continue;
|
|
5802
|
+
for (const p of s.points) samples.push(p.stec);
|
|
5803
|
+
}
|
|
5804
|
+
if (samples.length >= 100) {
|
|
5805
|
+
const rx = percentile(samples, 0.01);
|
|
5806
|
+
receiverDcbTecu[key] = rx;
|
|
5807
|
+
for (const s of group) {
|
|
5808
|
+
out.push({
|
|
5809
|
+
...s,
|
|
5810
|
+
points: s.points.map((p) => ({
|
|
5811
|
+
time: p.time,
|
|
5812
|
+
stec: p.stec - rx
|
|
5813
|
+
}))
|
|
5814
|
+
});
|
|
5815
|
+
}
|
|
5816
|
+
} else {
|
|
5817
|
+
out.push(...group);
|
|
5818
|
+
}
|
|
5819
|
+
}
|
|
5820
|
+
out.sort((a, b) => a.prn.localeCompare(b.prn));
|
|
5821
|
+
let sum = 0;
|
|
5822
|
+
let count = 0;
|
|
5823
|
+
let maxStec = 0;
|
|
5824
|
+
for (const s of out) {
|
|
5825
|
+
for (const p of s.points) {
|
|
5826
|
+
sum += p.stec;
|
|
5827
|
+
count++;
|
|
5828
|
+
if (p.stec > maxStec) maxStec = p.stec;
|
|
5829
|
+
}
|
|
5830
|
+
}
|
|
5831
|
+
return {
|
|
5832
|
+
result: { series: out, maxStec, meanStec: count > 0 ? sum / count : 0 },
|
|
5833
|
+
satellitesCorrected: corrected,
|
|
5834
|
+
satellitesMissing: [...new Set(missing)].sort(),
|
|
5835
|
+
receiverDcbTecu
|
|
5836
|
+
};
|
|
5837
|
+
}
|
|
5838
|
+
|
|
5697
5839
|
// src/analysis/quality-analysis.ts
|
|
5698
5840
|
async function analyzeQuality(file, header, onProgress, signal) {
|
|
5699
5841
|
const mpAccum = new MultipathAccumulator(header);
|
|
@@ -7000,6 +7142,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
7000
7142
|
WarningAccumulator,
|
|
7001
7143
|
analyzeQuality,
|
|
7002
7144
|
applyHelmert,
|
|
7145
|
+
applyIonoDcb,
|
|
7003
7146
|
buildGloChannelMap,
|
|
7004
7147
|
buildObsIndices,
|
|
7005
7148
|
clampUnit,
|
|
@@ -7102,6 +7245,7 @@ function computePsdDb(centerMHz, halfSpanChips, numPoints, psdFn, f0 = 1023e3, f
|
|
|
7102
7245
|
parseNavFile,
|
|
7103
7246
|
parseNmeaFile,
|
|
7104
7247
|
parseRinexStream,
|
|
7248
|
+
parseSinexBiasDcb,
|
|
7105
7249
|
parseSourcetable,
|
|
7106
7250
|
phiAltBOC,
|
|
7107
7251
|
phiBOCc,
|
package/dist/index.d.cts
CHANGED
|
@@ -13,7 +13,7 @@ export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPositi
|
|
|
13
13
|
export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.cjs';
|
|
14
14
|
export { SppOptions, SppSolution, ionoFree, satClockCorrection, solveSpp } from './positioning.cjs';
|
|
15
15
|
export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.cjs';
|
|
16
|
-
export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoPoint, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, analyzeQuality } from './analysis.cjs';
|
|
16
|
+
export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoDcbResult, IonoPoint, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, SatDcbMap, analyzeQuality, applyIonoDcb, parseSinexBiasDcb } from './analysis.cjs';
|
|
17
17
|
export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.cjs';
|
|
18
18
|
export { NmeaFix, NmeaStats, NmeaTrack, computeStats, nmeaCoordToDecimal, parseNmeaFile, verifyChecksum } from './nmea.cjs';
|
|
19
19
|
export { BAND_PRESETS, BandPreset, CONSTELLATIONS, ConstellationRow, DELTA_GLO_L1, DELTA_GLO_L2, FREQ_BDS_B1A, FREQ_BDS_B1C, FREQ_BDS_B1I, FREQ_BDS_B2I, FREQ_BDS_B3A, FREQ_BDS_B3I, FREQ_GAL_E1, FREQ_GAL_E5, FREQ_GAL_E5a, FREQ_GAL_E5b, FREQ_GAL_E6, FREQ_GLO_L1, FREQ_GLO_L1OC, FREQ_GLO_L2, FREQ_GLO_L2OC, FREQ_GLO_L3OC, FREQ_GPS_L1, FREQ_GPS_L2, FREQ_GPS_L5, FREQ_NAVIC_L5, FREQ_QZS_L1, FREQ_QZS_L2, FREQ_QZS_L5, FREQ_QZS_L6, Modulation, SignalDef, computePsdDb, phiAltBOC, phiBOCc, phiBOCs, phiBPSK } from './signals.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPositi
|
|
|
13
13
|
export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.js';
|
|
14
14
|
export { SppOptions, SppSolution, ionoFree, satClockCorrection, solveSpp } from './positioning.js';
|
|
15
15
|
export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.js';
|
|
16
|
-
export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoPoint, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, analyzeQuality } from './analysis.js';
|
|
16
|
+
export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoDcbResult, IonoPoint, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, SatDcbMap, analyzeQuality, applyIonoDcb, parseSinexBiasDcb } from './analysis.js';
|
|
17
17
|
export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.js';
|
|
18
18
|
export { NmeaFix, NmeaStats, NmeaTrack, computeStats, nmeaCoordToDecimal, parseNmeaFile, verifyChecksum } from './nmea.js';
|
|
19
19
|
export { BAND_PRESETS, BandPreset, CONSTELLATIONS, ConstellationRow, DELTA_GLO_L1, DELTA_GLO_L2, FREQ_BDS_B1A, FREQ_BDS_B1C, FREQ_BDS_B1I, FREQ_BDS_B2I, FREQ_BDS_B3A, FREQ_BDS_B3I, FREQ_GAL_E1, FREQ_GAL_E5, FREQ_GAL_E5a, FREQ_GAL_E5b, FREQ_GAL_E6, FREQ_GLO_L1, FREQ_GLO_L1OC, FREQ_GLO_L2, FREQ_GLO_L2OC, FREQ_GLO_L3OC, FREQ_GPS_L1, FREQ_GPS_L2, FREQ_GPS_L5, FREQ_NAVIC_L5, FREQ_QZS_L1, FREQ_QZS_L2, FREQ_QZS_L5, FREQ_QZS_L6, Modulation, SignalDef, computePsdDb, phiAltBOC, phiBOCc, phiBOCs, phiBPSK } from './signals.js';
|
package/dist/index.js
CHANGED
|
@@ -8,8 +8,10 @@ import {
|
|
|
8
8
|
CycleSlipAccumulator,
|
|
9
9
|
IonoAccumulator,
|
|
10
10
|
MultipathAccumulator,
|
|
11
|
-
analyzeQuality
|
|
12
|
-
|
|
11
|
+
analyzeQuality,
|
|
12
|
+
applyIonoDcb,
|
|
13
|
+
parseSinexBiasDcb
|
|
14
|
+
} from "./chunk-EJQ6NG5U.js";
|
|
13
15
|
import {
|
|
14
16
|
frequencyLabel,
|
|
15
17
|
parseAntex
|
|
@@ -332,6 +334,7 @@ export {
|
|
|
332
334
|
WarningAccumulator,
|
|
333
335
|
analyzeQuality,
|
|
334
336
|
applyHelmert,
|
|
337
|
+
applyIonoDcb,
|
|
335
338
|
buildGloChannelMap,
|
|
336
339
|
buildObsIndices,
|
|
337
340
|
clampUnit,
|
|
@@ -434,6 +437,7 @@ export {
|
|
|
434
437
|
parseNavFile,
|
|
435
438
|
parseNmeaFile,
|
|
436
439
|
parseRinexStream,
|
|
440
|
+
parseSinexBiasDcb,
|
|
437
441
|
parseSourcetable,
|
|
438
442
|
phiAltBOC,
|
|
439
443
|
phiBOCc,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnss-js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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,
|