gnss-js 2.11.0 → 2.12.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.
@@ -1,6 +1,6 @@
1
1
  import { E as Ephemeris } from './nav-COp0Vmlw.cjs';
2
2
  import { DopValues } from './orbit.cjs';
3
- import { I as IonexGrid, C as ClkFile, S as Sp3File } from './clk-DGVBGRzA.cjs';
3
+ import { I as IonexGrid, C as ClkFile, S as SatOsbTable, b as Sp3File } from './bias-sinex-OGMw9xEZ.cjs';
4
4
  import { AntexFile } from './antex.cjs';
5
5
  import { a as ObsEpoch } from './msm-DWtoTvx2.cjs';
6
6
  import { a as HasOrbitCorrection, b as HasClockCorrection } from './has-BaXnQarR.cjs';
@@ -690,6 +690,14 @@ interface LambdaResult {
690
690
  * typically cap it (RTKLIB uses 999.9) before thresholding.
691
691
  */
692
692
  ratio: number;
693
+ /**
694
+ * Bootstrapped success rate of the decorrelated ambiguities,
695
+ * Π (2Φ(1/(2σᵢ)) − 1) over the conditional standard deviations σᵢ —
696
+ * a lower bound on the probability that the integer solution is right
697
+ * (Teunissen 1998). Unlike the ratio, it depends only on the model's
698
+ * precision, so it refuses fixes the float solution can't yet support.
699
+ */
700
+ successRate: number;
693
701
  }
694
702
  /**
695
703
  * LAMBDA/MLAMBDA integer least-squares estimation (RTKLIB `lambda`):
@@ -1068,6 +1076,11 @@ interface PppSatObs {
1068
1076
  * up satellite code (OSB) biases from an SSR/HAS source; optional. */
1069
1077
  code1?: string;
1070
1078
  code2?: string;
1079
+ /** RINEX-3 tracking codes of the two *carriers* (e.g. '1C','2W'), for
1080
+ * satellite phase-bias (OSB) lookup when they differ from the code's
1081
+ * (GPS: C1W code with L1C phase). Default: `code1` / `code2`. */
1082
+ phase1?: string;
1083
+ phase2?: string;
1071
1084
  /** True if a cycle slip is flagged on either band (LLI or detected). */
1072
1085
  slip: boolean;
1073
1086
  }
@@ -1110,6 +1123,37 @@ interface PppOptions {
1110
1123
  * instead of the SP3 5-minute clocks — the dominant PPP accuracy term.
1111
1124
  * Satellites/epochs absent from the CLK fall back to the SP3 clock. */
1112
1125
  clk?: ClkFile;
1126
+ /** Satellite observable-specific biases (code + phase OSB) consistent with
1127
+ * the orbit/clock product — e.g. the CNES "ABS" biases that accompany GFZ
1128
+ * rapid products. With phase OSBs the float ambiguities keep their integer
1129
+ * nature (between satellites), which is what PPP-AR (`fixPppPosition`)
1130
+ * needs. Used with an Sp3File; a custom source supplies its own biases. */
1131
+ osb?: SatOsbTable;
1132
+ /**
1133
+ * Integer ambiguity resolution (PPP-AR). Needs satellite phase biases
1134
+ * (`osb`, or a source with `phaseBias`) — without them the ambiguities
1135
+ * aren't integers and this is ignored. Every `everyS` seconds the
1136
+ * converged ambiguities are resolved (wide-lane by rounding, narrow-lane
1137
+ * by LAMBDA, between satellites of a constellation) and, if the fix is
1138
+ * accepted, the epoch gets an ambiguity-fixed position next to the float
1139
+ * one. `true` uses the defaults.
1140
+ */
1141
+ ar?: boolean | {
1142
+ /** LAMBDA ratio test threshold. Default 3. */
1143
+ ratio?: number;
1144
+ /** Max wide-lane fractional part (cycles). Default 0.15. */
1145
+ wlThreshold?: number;
1146
+ /** Min arc age before an ambiguity is a candidate (s). Default 600. */
1147
+ minArcS?: number;
1148
+ /** Attempt interval (s). Default 120. */
1149
+ everyS?: number;
1150
+ /** Min bootstrapped success rate. Default 0.999. */
1151
+ successRate?: number;
1152
+ /** Min between-satellite ambiguities in a fix. Default 5. */
1153
+ minFixed?: number;
1154
+ /** Max χ²/dof of the float ambiguities vs the integers. Default 20. */
1155
+ maxChi2?: number;
1156
+ };
1113
1157
  }
1114
1158
  interface PppEpochResult {
1115
1159
  timeMs: number;
@@ -1125,6 +1169,10 @@ interface PppEpochResult {
1125
1169
  ztdHydrostatic: number;
1126
1170
  /** RMS of post-fit phase residuals this epoch (m). */
1127
1171
  phaseResRms: number;
1172
+ /** PPP-AR: between-satellite ambiguities fixed (0 = float only). */
1173
+ nFixed: number;
1174
+ /** PPP-AR: the ambiguity-fixed position, when the latest attempt fixed. */
1175
+ fixedPosition: [number, number, number] | null;
1128
1176
  }
1129
1177
  interface PppSolution {
1130
1178
  /** Final estimated ECEF position (m). */
@@ -1144,6 +1192,17 @@ interface PppSolution {
1144
1192
  convergenceSec: number | null;
1145
1193
  /** Final 3D error vs ground truth (m), if given. */
1146
1194
  finalError3d: number | null;
1195
+ /** PPP-AR summary, when `ar` was requested with a phase-bias product. */
1196
+ ar?: {
1197
+ /** Seconds from the first epoch to the first accepted fix (null: none). */
1198
+ firstFixSec: number | null;
1199
+ /** Ambiguities fixed at the end (0: the session ends float). */
1200
+ nFixedEnd: number;
1201
+ /** The ambiguity-fixed position at the end, or null. */
1202
+ position: [number, number, number] | null;
1203
+ /** Share of epochs with at least one ambiguity fixed. */
1204
+ fixedEpochShare: number;
1205
+ };
1147
1206
  /** Per-arc converged float ambiguities — one per continuous satellite
1148
1207
  * tracking arc — the raw material for ambiguity resolution (PPP-AR). */
1149
1208
  arcs: PppArc[];
@@ -1212,6 +1271,10 @@ interface PppEphemerisSource {
1212
1271
  * corrected code is `observed − codeBias`. SP3/CLK sources omit this (the
1213
1272
  * float ambiguity + broadcast clock reference absorb it). */
1214
1273
  codeBias?(prn: string, signalCode: string, tEmitMs: number): number | null;
1274
+ /** Optional satellite *phase* bias (m) for a carrier tracking code
1275
+ * ('1C','2W'…): the corrected carrier is `observed − phaseBias`. Only
1276
+ * integer-clock products (for PPP-AR) provide these. */
1277
+ phaseBias?(prn: string, signalCode: string, tEmitMs: number): number | null;
1215
1278
  }
1216
1279
  /** Default source: SP3 orbits + clocks, with optional high-rate CLK. Velocity
1217
1280
  * is a central finite difference (±0.5 s); the clock prefers the high-rate CLK
@@ -1219,7 +1282,10 @@ interface PppEphemerisSource {
1219
1282
  declare class Sp3EphemerisSource implements PppEphemerisSource {
1220
1283
  private readonly sp3;
1221
1284
  private readonly clk?;
1222
- constructor(sp3: Sp3File, clk?: ClkFile | undefined);
1285
+ private readonly osb?;
1286
+ constructor(sp3: Sp3File, clk?: ClkFile | undefined, osb?: SatOsbTable | undefined);
1287
+ codeBias?: PppEphemerisSource['codeBias'];
1288
+ phaseBias?: PppEphemerisSource['phaseBias'];
1223
1289
  satState(prn: string, tEmitMs: number): SatSample | null;
1224
1290
  }
1225
1291
  /**
@@ -1248,6 +1314,16 @@ declare class PppEngine {
1248
1314
  private arcs;
1249
1315
  private arcSnap;
1250
1316
  private mwState;
1317
+ /** Previous epoch's phase terms per satellite, for the time-differenced
1318
+ * (clock-free) cycle-slip test. */
1319
+ private tdPrev;
1320
+ /** Consecutive gated-out phase updates per satellite. */
1321
+ private phaseRejects;
1322
+ /** PPP-AR: the latest accepted fix (null: float). */
1323
+ private lastFix;
1324
+ private lastArMs;
1325
+ private firstFixMs;
1326
+ private fixedEpochs;
1251
1327
  private series;
1252
1328
  private ei;
1253
1329
  private t0;
@@ -1265,6 +1341,16 @@ declare class PppEngine {
1265
1341
  private clockOf;
1266
1342
  /** Process one epoch, carrying the filter forward; returns its result. */
1267
1343
  process(epoch: PppEpoch): PppEpochResult;
1344
+ /**
1345
+ * PPP-AR. Every `everyS`, resolve the converged ambiguities of the
1346
+ * satellites in view (between satellites of one constellation) and, when
1347
+ * the fix passes the ratio, success-rate and χ² tests, condition the
1348
+ * float position on it ({@link fixPppPosition}). The float filter itself
1349
+ * is never constrained — a wrong fix can't contaminate later epochs, and
1350
+ * the next attempt starts clean. Between attempts the last fix carries
1351
+ * over (static) or is dropped (kinematic).
1352
+ */
1353
+ private tryFix;
1268
1354
  /** Finalise the session: capture the AR state, flush open arcs, assemble. */
1269
1355
  solution(): PppSolution;
1270
1356
  }
@@ -1371,6 +1457,18 @@ interface ArOptions {
1371
1457
  wlThreshold?: number;
1372
1458
  /** Min LAMBDA ratio to accept the narrow-lane fix. Default 3. */
1373
1459
  ratioThreshold?: number;
1460
+ /** Partial fixing: when the full set fails the ratio test, drop the
1461
+ * lowest-elevation satellite and retry while more than this many
1462
+ * between-satellite ambiguities remain. Default 4. */
1463
+ minPartial?: number;
1464
+ /** Min bootstrapped success rate (0–1) to accept a fix, besides the
1465
+ * ratio test. Default 0 (ratio test only). */
1466
+ minSuccessRate?: number;
1467
+ /** `fixPppPosition`: min between-satellite ambiguities to accept. Default 1. */
1468
+ minFixed?: number;
1469
+ /** `fixPppPosition`: max χ²/dof of the float ambiguities against the fixed
1470
+ * integers (a wrong fix sits many σ away). Default Infinity. */
1471
+ maxChi2?: number;
1374
1472
  }
1375
1473
  interface ArFixedSat {
1376
1474
  prn: string;
@@ -1378,15 +1476,22 @@ interface ArFixedSat {
1378
1476
  n1: number;
1379
1477
  /** Fixed IF ambiguity (m). */
1380
1478
  aIF: number;
1479
+ /** PRN of this satellite's single-difference reference (itself for a
1480
+ * reference satellite). */
1481
+ ref: string;
1381
1482
  }
1382
1483
  interface ArResult {
1383
1484
  /** Whether the narrow-lane fix passed the ratio test. */
1384
1485
  fixed: boolean;
1385
1486
  /** LAMBDA ratio statistic (second-best / best). */
1386
1487
  ratio: number;
1387
- /** Reference PRN whose absolute N1 stays float (SD datum). */
1488
+ /** Reference PRN of the first constellation (whose absolute N1 stays
1489
+ * float); see `refPrns` for all. */
1388
1490
  refPrn: string | null;
1389
- /** Fixed satellites (includes the reference with its float-rounded N1). */
1491
+ /** One reference per constellation — differences are taken within a
1492
+ * constellation, where the receiver phase bias is common. */
1493
+ refPrns: string[];
1494
+ /** Satellites in the fix (references included, with a float-rounded N1). */
1390
1495
  sats: ArFixedSat[];
1391
1496
  /** PRNs whose wide-lane did not round confidently (excluded). */
1392
1497
  rejectedWl: string[];
@@ -1397,6 +1502,11 @@ interface ArResult {
1397
1502
  * `sats`. Returns the fixed narrow-lane / wide-lane integers and the ratio
1398
1503
  * test result. A `fixed: false` result means the data did not support a
1399
1504
  * confident fix (keep the float solution).
1505
+ *
1506
+ * Everything is differenced between satellites of the same constellation
1507
+ * (one reference each, the highest), which removes the receiver's
1508
+ * wide-lane and narrow-lane phase biases — those differ per constellation
1509
+ * and per receiver, so an undifferenced ambiguity is never an integer.
1400
1510
  */
1401
1511
  declare function resolvePppAmbiguities(sats: ArSat[], Qaif: Float64Array, opts?: ArOptions): ArResult;
1402
1512
  interface FixedPositionResult {
@@ -1,6 +1,6 @@
1
1
  import { E as Ephemeris } from './nav-COp0Vmlw.js';
2
2
  import { DopValues } from './orbit.js';
3
- import { I as IonexGrid, C as ClkFile, S as Sp3File } from './clk-DGVBGRzA.js';
3
+ import { I as IonexGrid, C as ClkFile, S as SatOsbTable, b as Sp3File } from './bias-sinex-OGMw9xEZ.js';
4
4
  import { AntexFile } from './antex.js';
5
5
  import { a as ObsEpoch } from './msm-CcQJV3PC.js';
6
6
  import { a as HasOrbitCorrection, b as HasClockCorrection } from './has-BaXnQarR.js';
@@ -690,6 +690,14 @@ interface LambdaResult {
690
690
  * typically cap it (RTKLIB uses 999.9) before thresholding.
691
691
  */
692
692
  ratio: number;
693
+ /**
694
+ * Bootstrapped success rate of the decorrelated ambiguities,
695
+ * Π (2Φ(1/(2σᵢ)) − 1) over the conditional standard deviations σᵢ —
696
+ * a lower bound on the probability that the integer solution is right
697
+ * (Teunissen 1998). Unlike the ratio, it depends only on the model's
698
+ * precision, so it refuses fixes the float solution can't yet support.
699
+ */
700
+ successRate: number;
693
701
  }
694
702
  /**
695
703
  * LAMBDA/MLAMBDA integer least-squares estimation (RTKLIB `lambda`):
@@ -1068,6 +1076,11 @@ interface PppSatObs {
1068
1076
  * up satellite code (OSB) biases from an SSR/HAS source; optional. */
1069
1077
  code1?: string;
1070
1078
  code2?: string;
1079
+ /** RINEX-3 tracking codes of the two *carriers* (e.g. '1C','2W'), for
1080
+ * satellite phase-bias (OSB) lookup when they differ from the code's
1081
+ * (GPS: C1W code with L1C phase). Default: `code1` / `code2`. */
1082
+ phase1?: string;
1083
+ phase2?: string;
1071
1084
  /** True if a cycle slip is flagged on either band (LLI or detected). */
1072
1085
  slip: boolean;
1073
1086
  }
@@ -1110,6 +1123,37 @@ interface PppOptions {
1110
1123
  * instead of the SP3 5-minute clocks — the dominant PPP accuracy term.
1111
1124
  * Satellites/epochs absent from the CLK fall back to the SP3 clock. */
1112
1125
  clk?: ClkFile;
1126
+ /** Satellite observable-specific biases (code + phase OSB) consistent with
1127
+ * the orbit/clock product — e.g. the CNES "ABS" biases that accompany GFZ
1128
+ * rapid products. With phase OSBs the float ambiguities keep their integer
1129
+ * nature (between satellites), which is what PPP-AR (`fixPppPosition`)
1130
+ * needs. Used with an Sp3File; a custom source supplies its own biases. */
1131
+ osb?: SatOsbTable;
1132
+ /**
1133
+ * Integer ambiguity resolution (PPP-AR). Needs satellite phase biases
1134
+ * (`osb`, or a source with `phaseBias`) — without them the ambiguities
1135
+ * aren't integers and this is ignored. Every `everyS` seconds the
1136
+ * converged ambiguities are resolved (wide-lane by rounding, narrow-lane
1137
+ * by LAMBDA, between satellites of a constellation) and, if the fix is
1138
+ * accepted, the epoch gets an ambiguity-fixed position next to the float
1139
+ * one. `true` uses the defaults.
1140
+ */
1141
+ ar?: boolean | {
1142
+ /** LAMBDA ratio test threshold. Default 3. */
1143
+ ratio?: number;
1144
+ /** Max wide-lane fractional part (cycles). Default 0.15. */
1145
+ wlThreshold?: number;
1146
+ /** Min arc age before an ambiguity is a candidate (s). Default 600. */
1147
+ minArcS?: number;
1148
+ /** Attempt interval (s). Default 120. */
1149
+ everyS?: number;
1150
+ /** Min bootstrapped success rate. Default 0.999. */
1151
+ successRate?: number;
1152
+ /** Min between-satellite ambiguities in a fix. Default 5. */
1153
+ minFixed?: number;
1154
+ /** Max χ²/dof of the float ambiguities vs the integers. Default 20. */
1155
+ maxChi2?: number;
1156
+ };
1113
1157
  }
1114
1158
  interface PppEpochResult {
1115
1159
  timeMs: number;
@@ -1125,6 +1169,10 @@ interface PppEpochResult {
1125
1169
  ztdHydrostatic: number;
1126
1170
  /** RMS of post-fit phase residuals this epoch (m). */
1127
1171
  phaseResRms: number;
1172
+ /** PPP-AR: between-satellite ambiguities fixed (0 = float only). */
1173
+ nFixed: number;
1174
+ /** PPP-AR: the ambiguity-fixed position, when the latest attempt fixed. */
1175
+ fixedPosition: [number, number, number] | null;
1128
1176
  }
1129
1177
  interface PppSolution {
1130
1178
  /** Final estimated ECEF position (m). */
@@ -1144,6 +1192,17 @@ interface PppSolution {
1144
1192
  convergenceSec: number | null;
1145
1193
  /** Final 3D error vs ground truth (m), if given. */
1146
1194
  finalError3d: number | null;
1195
+ /** PPP-AR summary, when `ar` was requested with a phase-bias product. */
1196
+ ar?: {
1197
+ /** Seconds from the first epoch to the first accepted fix (null: none). */
1198
+ firstFixSec: number | null;
1199
+ /** Ambiguities fixed at the end (0: the session ends float). */
1200
+ nFixedEnd: number;
1201
+ /** The ambiguity-fixed position at the end, or null. */
1202
+ position: [number, number, number] | null;
1203
+ /** Share of epochs with at least one ambiguity fixed. */
1204
+ fixedEpochShare: number;
1205
+ };
1147
1206
  /** Per-arc converged float ambiguities — one per continuous satellite
1148
1207
  * tracking arc — the raw material for ambiguity resolution (PPP-AR). */
1149
1208
  arcs: PppArc[];
@@ -1212,6 +1271,10 @@ interface PppEphemerisSource {
1212
1271
  * corrected code is `observed − codeBias`. SP3/CLK sources omit this (the
1213
1272
  * float ambiguity + broadcast clock reference absorb it). */
1214
1273
  codeBias?(prn: string, signalCode: string, tEmitMs: number): number | null;
1274
+ /** Optional satellite *phase* bias (m) for a carrier tracking code
1275
+ * ('1C','2W'…): the corrected carrier is `observed − phaseBias`. Only
1276
+ * integer-clock products (for PPP-AR) provide these. */
1277
+ phaseBias?(prn: string, signalCode: string, tEmitMs: number): number | null;
1215
1278
  }
1216
1279
  /** Default source: SP3 orbits + clocks, with optional high-rate CLK. Velocity
1217
1280
  * is a central finite difference (±0.5 s); the clock prefers the high-rate CLK
@@ -1219,7 +1282,10 @@ interface PppEphemerisSource {
1219
1282
  declare class Sp3EphemerisSource implements PppEphemerisSource {
1220
1283
  private readonly sp3;
1221
1284
  private readonly clk?;
1222
- constructor(sp3: Sp3File, clk?: ClkFile | undefined);
1285
+ private readonly osb?;
1286
+ constructor(sp3: Sp3File, clk?: ClkFile | undefined, osb?: SatOsbTable | undefined);
1287
+ codeBias?: PppEphemerisSource['codeBias'];
1288
+ phaseBias?: PppEphemerisSource['phaseBias'];
1223
1289
  satState(prn: string, tEmitMs: number): SatSample | null;
1224
1290
  }
1225
1291
  /**
@@ -1248,6 +1314,16 @@ declare class PppEngine {
1248
1314
  private arcs;
1249
1315
  private arcSnap;
1250
1316
  private mwState;
1317
+ /** Previous epoch's phase terms per satellite, for the time-differenced
1318
+ * (clock-free) cycle-slip test. */
1319
+ private tdPrev;
1320
+ /** Consecutive gated-out phase updates per satellite. */
1321
+ private phaseRejects;
1322
+ /** PPP-AR: the latest accepted fix (null: float). */
1323
+ private lastFix;
1324
+ private lastArMs;
1325
+ private firstFixMs;
1326
+ private fixedEpochs;
1251
1327
  private series;
1252
1328
  private ei;
1253
1329
  private t0;
@@ -1265,6 +1341,16 @@ declare class PppEngine {
1265
1341
  private clockOf;
1266
1342
  /** Process one epoch, carrying the filter forward; returns its result. */
1267
1343
  process(epoch: PppEpoch): PppEpochResult;
1344
+ /**
1345
+ * PPP-AR. Every `everyS`, resolve the converged ambiguities of the
1346
+ * satellites in view (between satellites of one constellation) and, when
1347
+ * the fix passes the ratio, success-rate and χ² tests, condition the
1348
+ * float position on it ({@link fixPppPosition}). The float filter itself
1349
+ * is never constrained — a wrong fix can't contaminate later epochs, and
1350
+ * the next attempt starts clean. Between attempts the last fix carries
1351
+ * over (static) or is dropped (kinematic).
1352
+ */
1353
+ private tryFix;
1268
1354
  /** Finalise the session: capture the AR state, flush open arcs, assemble. */
1269
1355
  solution(): PppSolution;
1270
1356
  }
@@ -1371,6 +1457,18 @@ interface ArOptions {
1371
1457
  wlThreshold?: number;
1372
1458
  /** Min LAMBDA ratio to accept the narrow-lane fix. Default 3. */
1373
1459
  ratioThreshold?: number;
1460
+ /** Partial fixing: when the full set fails the ratio test, drop the
1461
+ * lowest-elevation satellite and retry while more than this many
1462
+ * between-satellite ambiguities remain. Default 4. */
1463
+ minPartial?: number;
1464
+ /** Min bootstrapped success rate (0–1) to accept a fix, besides the
1465
+ * ratio test. Default 0 (ratio test only). */
1466
+ minSuccessRate?: number;
1467
+ /** `fixPppPosition`: min between-satellite ambiguities to accept. Default 1. */
1468
+ minFixed?: number;
1469
+ /** `fixPppPosition`: max χ²/dof of the float ambiguities against the fixed
1470
+ * integers (a wrong fix sits many σ away). Default Infinity. */
1471
+ maxChi2?: number;
1374
1472
  }
1375
1473
  interface ArFixedSat {
1376
1474
  prn: string;
@@ -1378,15 +1476,22 @@ interface ArFixedSat {
1378
1476
  n1: number;
1379
1477
  /** Fixed IF ambiguity (m). */
1380
1478
  aIF: number;
1479
+ /** PRN of this satellite's single-difference reference (itself for a
1480
+ * reference satellite). */
1481
+ ref: string;
1381
1482
  }
1382
1483
  interface ArResult {
1383
1484
  /** Whether the narrow-lane fix passed the ratio test. */
1384
1485
  fixed: boolean;
1385
1486
  /** LAMBDA ratio statistic (second-best / best). */
1386
1487
  ratio: number;
1387
- /** Reference PRN whose absolute N1 stays float (SD datum). */
1488
+ /** Reference PRN of the first constellation (whose absolute N1 stays
1489
+ * float); see `refPrns` for all. */
1388
1490
  refPrn: string | null;
1389
- /** Fixed satellites (includes the reference with its float-rounded N1). */
1491
+ /** One reference per constellation — differences are taken within a
1492
+ * constellation, where the receiver phase bias is common. */
1493
+ refPrns: string[];
1494
+ /** Satellites in the fix (references included, with a float-rounded N1). */
1390
1495
  sats: ArFixedSat[];
1391
1496
  /** PRNs whose wide-lane did not round confidently (excluded). */
1392
1497
  rejectedWl: string[];
@@ -1397,6 +1502,11 @@ interface ArResult {
1397
1502
  * `sats`. Returns the fixed narrow-lane / wide-lane integers and the ratio
1398
1503
  * test result. A `fixed: false` result means the data did not support a
1399
1504
  * confident fix (keep the float solution).
1505
+ *
1506
+ * Everything is differenced between satellites of the same constellation
1507
+ * (one reference each, the highest), which removes the receiver's
1508
+ * wide-lane and narrow-lane phase biases — those differ per constellation
1509
+ * and per receiver, so an undifferenced ambiguity is never an integer.
1400
1510
  */
1401
1511
  declare function resolvePppAmbiguities(sats: ArSat[], Qaif: Float64Array, opts?: ArOptions): ArResult;
1402
1512
  interface FixedPositionResult {
@@ -41,7 +41,7 @@ import {
41
41
  sunEcef,
42
42
  toRtkEpoch,
43
43
  wlWavelength
44
- } from "./chunk-UMINQTMK.js";
44
+ } from "./chunk-RWFJEVLJ.js";
45
45
  import "./chunk-H3AKFD25.js";
46
46
  import "./chunk-7T5DLVZL.js";
47
47
  import "./chunk-HVXYFUCB.js";
package/dist/rinex.cjs CHANGED
@@ -22,6 +22,7 @@ var rinex_exports = {};
22
22
  __export(rinex_exports, {
23
23
  EMPTY_WARNINGS: () => EMPTY_WARNINGS,
24
24
  SYSTEM_ORDER: () => SYSTEM_ORDER,
25
+ SatOsbTable: () => SatOsbTable,
25
26
  WarningAccumulator: () => WarningAccumulator,
26
27
  biasMetres: () => biasMetres,
27
28
  clkBias: () => clkBias,
@@ -59,20 +60,14 @@ module.exports = __toCommonJS(rinex_exports);
59
60
  // src/rinex/crx.ts
60
61
  function crxRepair(old, diff) {
61
62
  const chars = old.split("");
62
- for (let i = 0; i < diff.length; i++) {
63
+ const n = Math.min(old.length, diff.length);
64
+ for (let i = 0; i < n; i++) {
63
65
  const dc = diff[i];
64
- if (dc === " ") {
65
- } else if (dc === "&") {
66
- chars[i] = " ";
67
- } else {
68
- chars[i] = dc;
69
- }
70
- }
71
- if (diff.length > chars.length) {
72
- for (let i = chars.length; i < diff.length; i++) {
73
- chars[i] = diff[i] === "&" ? " " : diff[i];
74
- }
66
+ if (dc === " ") continue;
67
+ chars[i] = dc === "&" ? " " : dc;
75
68
  }
69
+ for (let i = old.length; i < diff.length; i++)
70
+ chars[i] = diff[i] === "&" ? " " : diff[i];
76
71
  return chars.join("");
77
72
  }
78
73
  function parseCrxDataLine(line, ntype) {
@@ -782,6 +777,15 @@ function readObsValue(line, index) {
782
777
  const v = parseFloat(raw);
783
778
  return isNaN(v) ? null : v;
784
779
  }
780
+ function normalizePrn(id) {
781
+ let sys = id[0];
782
+ let num = id.substring(1);
783
+ if (sys === " " || /\d/.test(sys)) {
784
+ num = id;
785
+ sys = "G";
786
+ }
787
+ return sys + num.replace(/\s/g, "").padStart(2, "0");
788
+ }
785
789
  function parseEpochLine3(line) {
786
790
  if (line[0] !== ">") return null;
787
791
  const yr = parseInt(line.substring(2, 6));
@@ -931,7 +935,7 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
931
935
  function processSatLineV3(line) {
932
936
  if (!header || line.length < 3) return;
933
937
  const sys = line[0];
934
- const prn = line.substring(0, 3);
938
+ const prn = normalizePrn(line.substring(0, 3));
935
939
  trackSat(sys, prn);
936
940
  const obsLine = line.substring(3);
937
941
  const codes = header.obsTypes[sys] ?? [];
@@ -956,10 +960,8 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
956
960
  let v2ObsAccum = [];
957
961
  function processSatLineV2(line) {
958
962
  if (!header || v2SatIndex >= v2SatIds.length) return;
959
- const satId = v2SatIds[v2SatIndex];
960
- let sys = satId[0];
961
- if (sys === " " || /\d/.test(sys)) sys = "G";
962
- const prn = sys + satId.substring(1).padStart(2, "0");
963
+ const prn = normalizePrn(v2SatIds[v2SatIndex]);
964
+ const sys = prn[0];
963
965
  if (v2CurrentSatLine === 0) {
964
966
  trackSat(sys, prn);
965
967
  if (onSatObs) v2ObsAccum = [];
@@ -1050,6 +1052,7 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
1050
1052
  } else {
1051
1053
  epochLine = crxRepair(crxPrevEpochLine, line);
1052
1054
  }
1055
+ epochLine = epochLine.replace(/ +$/, "");
1053
1056
  if (isV3) {
1054
1057
  if (epochLine[0] !== ">") return;
1055
1058
  const info = parseEpochLine3(epochLine);
@@ -1067,10 +1070,11 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
1067
1070
  return;
1068
1071
  }
1069
1072
  resetEpochAccum(info);
1070
- const satPart = epochLine.substring(41).replace(/\s+$/, "");
1071
1073
  crxSatList = [];
1072
- for (let i = 0; i + 3 <= satPart.length; i += 3) {
1073
- crxSatList.push(satPart.substring(i, i + 3));
1074
+ for (let k = 0; k < info.numSats; k++) {
1075
+ crxSatList.push(
1076
+ normalizePrn(epochLine.substring(41 + 3 * k, 44 + 3 * k))
1077
+ );
1074
1078
  }
1075
1079
  crxSatIndex = 0;
1076
1080
  crxPrevEpochLine = epochLine;
@@ -1091,11 +1095,7 @@ async function parseRinexStream(file, onProgress, signal, onSatObs, workerMode)
1091
1095
  return;
1092
1096
  }
1093
1097
  resetEpochAccum(info);
1094
- crxSatList = (info.satIds ?? []).map((id) => {
1095
- let sys = id[0];
1096
- if (sys === " " || /\d/.test(sys)) sys = "G";
1097
- return sys + id.substring(1).padStart(2, "0");
1098
- });
1098
+ crxSatList = (info.satIds ?? []).slice(0, info.numSats).map(normalizePrn);
1099
1099
  crxSatIndex = 0;
1100
1100
  crxPrevEpochLine = epochLine;
1101
1101
  crxPhase = "clock";
@@ -2658,6 +2658,50 @@ function findSatBias(bias, prn, obs1, obs2, tMs) {
2658
2658
  }
2659
2659
  return null;
2660
2660
  }
2661
+ var SatOsbTable = class {
2662
+ byKey = /* @__PURE__ */ new Map();
2663
+ constructor(bias) {
2664
+ const recs = Array.isArray(bias) ? bias : bias.records;
2665
+ const groups = /* @__PURE__ */ new Map();
2666
+ for (const r of recs) {
2667
+ if (r.type !== "OSB" || !r.prn || r.station) continue;
2668
+ if (r.unit !== "ns") continue;
2669
+ const k = `${r.prn} ${r.obs1}`;
2670
+ let g = groups.get(k);
2671
+ if (!g) groups.set(k, g = []);
2672
+ g.push(r);
2673
+ }
2674
+ for (const [k, g] of groups) {
2675
+ g.sort((a, b) => a.startMs - b.startMs);
2676
+ this.byKey.set(k, {
2677
+ start: Float64Array.from(g, (r) => r.startMs),
2678
+ end: Float64Array.from(g, (r) => r.endMs),
2679
+ m: Float64Array.from(g, biasMetres)
2680
+ });
2681
+ }
2682
+ }
2683
+ /** Whether any phase (L…) OSB is present — i.e. the product supports AR. */
2684
+ get hasPhase() {
2685
+ for (const k of this.byKey.keys())
2686
+ if (k.split(" ")[1][0] === "L") return true;
2687
+ return false;
2688
+ }
2689
+ /** OSB (m) of `prn`'s observable `obs` (e.g. 'C1W', 'L2W') at `tMs`;
2690
+ * null when the product has none valid then. */
2691
+ get(prn, obs, tMs) {
2692
+ const e = this.byKey.get(`${prn} ${obs}`) ?? this.byKey.get(`${prn} ${obs.slice(0, 2)}X`);
2693
+ if (!e) return null;
2694
+ let lo = 0;
2695
+ let hi = e.start.length - 1;
2696
+ if (tMs < e.start[0]) return null;
2697
+ while (lo < hi) {
2698
+ const mid = lo + hi + 1 >> 1;
2699
+ if (e.start[mid] <= tMs) lo = mid;
2700
+ else hi = mid - 1;
2701
+ }
2702
+ return tMs <= e.end[lo] ? e.m[lo] : null;
2703
+ }
2704
+ };
2661
2705
 
2662
2706
  // src/rinex/obs-writer-common.ts
2663
2707
  function createGzipLineSink() {
@@ -3049,6 +3093,7 @@ function writeRinex4ObsBlob(header, epochs, obsTypes) {
3049
3093
  0 && (module.exports = {
3050
3094
  EMPTY_WARNINGS,
3051
3095
  SYSTEM_ORDER,
3096
+ SatOsbTable,
3052
3097
  WarningAccumulator,
3053
3098
  biasMetres,
3054
3099
  clkBias,
package/dist/rinex.d.cts CHANGED
@@ -2,7 +2,7 @@ import { R as RinexHeader } from './parser-JPjjFgeP.cjs';
2
2
  export { E as EpochSummary, P as ParseOptions, a as RinexResult, b as RinexStats, S as SYSTEM_ORDER, c as SatObsCallback, p as parseRinexStream, s as systemCmp, d as systemName } from './parser-JPjjFgeP.cjs';
3
3
  import { b as NavResult, N as NavHeader, E as Ephemeris, R as RinexCnavEphemeris, B as BdsCnavEphemeris } from './nav-COp0Vmlw.cjs';
4
4
  export { a as BdsCnavMessage, G as GlonassEphemeris, K as KeplerEphemeris, p as parseNavFile } from './nav-COp0Vmlw.cjs';
5
- export { C as ClkFile, I as IonexGrid, S as Sp3File, a as Sp3Sample, c as clkBias, p as parseClk, b as parseIonex, d as parseSp3, s as sp3Position } from './clk-DGVBGRzA.cjs';
5
+ export { B as BiasRecord, a as BiasSinex, C as ClkFile, I as IonexGrid, S as SatOsbTable, b as Sp3File, c as Sp3Sample, d as biasMetres, e as clkBias, f as findSatBias, p as parseBiasSinex, g as parseClk, h as parseIonex, i as parseSp3, s as sp3Position } from './bias-sinex-OGMw9xEZ.cjs';
6
6
 
7
7
  /**
8
8
  * Compact RINEX (Hatanaka / CRX) decompression utilities.
@@ -185,54 +185,6 @@ interface Nav4Input {
185
185
  */
186
186
  declare function writeRinexNav4(nav: Nav4Input): string;
187
187
 
188
- /**
189
- * Bias-SINEX (`.BIA`) parser — GNSS code/phase biases published by the IGS
190
- * analysis centres. Two record kinds matter here:
191
- *
192
- * - **DSB** differential signal bias (OBS1−OBS2), the classic inter-code
193
- * bias used for ionosphere/DCB corrections in SPP.
194
- * - **OSB** observable-specific (absolute) bias for a single OBS, the
195
- * ingredient PPP-AR needs: the *phase* OSBs are the satellite
196
- * fractional-cycle biases that make the ambiguities integer-recoverable.
197
- *
198
- * Values keep their file unit (`ns` for code/most phase biases, `cyc` for
199
- * cycle-based phase biases); `biasMetres` converts ns → m via c.
200
- */
201
- interface BiasRecord {
202
- /** 'DSB' | 'OSB' | 'ISB' | … */
203
- type: string;
204
- /** Satellite PRN (e.g. 'G01', 'E11'); '' for a pure station bias. */
205
- prn: string;
206
- /** Station (9-char), '' for satellite biases. */
207
- station: string;
208
- /** First observable code (e.g. 'C1W', 'L1C'). */
209
- obs1: string;
210
- /** Second observable (DSB only; '' for OSB). */
211
- obs2: string;
212
- /** Validity start / end (GPS-scale ms); ±Infinity when open. */
213
- startMs: number;
214
- endMs: number;
215
- /** Estimated value in the file unit. */
216
- value: number;
217
- /** File unit ('ns' | 'cyc' | …). */
218
- unit: string;
219
- }
220
- interface BiasSinex {
221
- records: BiasRecord[];
222
- /** All satellite records grouped by PRN (station === ''). */
223
- bySat: Map<string, BiasRecord[]>;
224
- }
225
- /** Parse a Bias-SINEX file. */
226
- declare function parseBiasSinex(text: string): BiasSinex;
227
- /** A satellite bias record's value in metres (ns → m via c; cyc left as-is). */
228
- declare function biasMetres(r: BiasRecord): number;
229
- /**
230
- * Look up a satellite bias: a DSB by its (obs1,obs2) pair, or an OSB by obs1
231
- * alone (pass obs2 = undefined), valid at `tMs` (default: any). Returns the
232
- * record or null.
233
- */
234
- declare function findSatBias(bias: BiasSinex, prn: string, obs1: string, obs2?: string, tMs?: number): BiasRecord | null;
235
-
236
188
  /**
237
189
  * RINEX 3.04 / 4.01 observation file writer.
238
190
  *
@@ -323,4 +275,4 @@ declare function createGzipLineSink(): {
323
275
  */
324
276
  declare function stationHeaderLines(header: RinexHeader): string[];
325
277
 
326
- export { BdsCnavEphemeris, type BiasRecord, type BiasSinex, type CompactEpoch, type CrxField, type DiffState, EMPTY_WARNINGS, Ephemeris, type Nav4Input, NavHeader, NavResult, RinexCnavEphemeris, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, type WriteCrxOptions, biasMetres, createGzipLineSink, crxDecompress, crxRepair, findSatBias, fmtD, fmtF, hdrLine, modalIntervalS, padL, padR, parseBiasSinex, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob };
278
+ export { BdsCnavEphemeris, type CompactEpoch, type CrxField, type DiffState, EMPTY_WARNINGS, Ephemeris, type Nav4Input, NavHeader, NavResult, RinexCnavEphemeris, RinexHeader, type RinexWarning, type RinexWarnings, WarningAccumulator, type WarningSeverity, type WriteCrxOptions, createGzipLineSink, crxDecompress, crxRepair, fmtD, fmtF, hdrLine, modalIntervalS, padL, padR, parseCrxDataLine, stationHeaderLines, writeCrx, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexNav4, writeRinexObsBlob };