gnss-js 1.23.0 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-CUWF56ST.js → chunk-4XGKCVE3.js} +384 -13
- package/dist/index.cjs +389 -13
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +11 -1
- package/dist/ionex-DaW5x9nq.d.cts +23 -0
- package/dist/ionex-DaW5x9nq.d.ts +23 -0
- package/dist/positioning.cjs +389 -13
- package/dist/positioning.d.cts +225 -7
- package/dist/positioning.d.ts +225 -7
- package/dist/positioning.js +11 -1
- package/dist/rinex.d.cts +2 -23
- package/dist/rinex.d.ts +2 -23
- package/package.json +1 -1
package/dist/positioning.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { E as Ephemeris } from './nav-DImXUdbp.cjs';
|
|
2
2
|
import { DopValues } from './orbit.cjs';
|
|
3
|
+
import { I as IonexGrid } from './ionex-DaW5x9nq.cjs';
|
|
3
4
|
import './ephemeris-Ohl6NAfw.cjs';
|
|
4
5
|
import './ecef-CF0uAysr.cjs';
|
|
5
6
|
import './nav-BYmYoh9A.cjs';
|
|
@@ -34,6 +35,52 @@ interface KlobucharCoeffs {
|
|
|
34
35
|
*/
|
|
35
36
|
declare function klobucharDelay(coeffs: KlobucharCoeffs, latRad: number, lonRad: number, azRad: number, elRad: number, gpsTowSec: number): number;
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Slant ionospheric delay from a Global Ionosphere Map (IONEX/GIM).
|
|
40
|
+
*
|
|
41
|
+
* A GIM gives vertical TEC on an hourly lat/lon grid (see
|
|
42
|
+
* {@link parseIonex}). For single-frequency positioning this is a much
|
|
43
|
+
* stronger correction than the broadcast Klobuchar model — GIMs capture
|
|
44
|
+
* ~80–90% of the true ionosphere versus Klobuchar's ~50% — so feeding
|
|
45
|
+
* `solveSpp` a GIM (the `gim` option) collapses most of the residual
|
|
46
|
+
* height bias a single-frequency solution otherwise carries.
|
|
47
|
+
*
|
|
48
|
+
* The evaluation is the standard thin-shell recipe: pierce the shell at
|
|
49
|
+
* {@link H_ION}, interpolate vertical TEC there (bilinear in space,
|
|
50
|
+
* linear in time), map to slant with the obliquity factor, and convert
|
|
51
|
+
* TEC → L1 group delay. The result is the delay on GPS L1; `solveSpp`
|
|
52
|
+
* scales it to each system's primary frequency by (f_L1/f)².
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* L1 group delay per unit slant TEC: 40.3 · 10¹⁶ / f_L1² (m per TECU).
|
|
57
|
+
* 40.3 m·Hz²/(el/m²), one TECU = 10¹⁶ el/m². ≈ 0.1624 m/TECU.
|
|
58
|
+
*/
|
|
59
|
+
declare const IONO_L1_M_PER_TECU: number;
|
|
60
|
+
/**
|
|
61
|
+
* Space- and time-interpolated vertical TEC (TECU) at `timeMs`, or null
|
|
62
|
+
* outside the map's time span or where the grid marks no value.
|
|
63
|
+
*
|
|
64
|
+
* IGS GIMs are global, so spatial coverage is total; a pierce point is
|
|
65
|
+
* bilinearly interpolated and clamped to the nearest edge cell rather
|
|
66
|
+
* than rejected. Null therefore signals a time gap or a no-value cell,
|
|
67
|
+
* not an out-of-area position.
|
|
68
|
+
*/
|
|
69
|
+
declare function gimVerticalTec(grid: IonexGrid, timeMs: number, latDeg: number, lonDeg: number): number | null;
|
|
70
|
+
/**
|
|
71
|
+
* Slant ionospheric group delay on GPS L1 (m) from a GIM, or null when
|
|
72
|
+
* the pierce point falls outside the map's coverage in space or time.
|
|
73
|
+
*
|
|
74
|
+
* @param grid Parsed IONEX grid ({@link parseIonex}).
|
|
75
|
+
* @param latRad Receiver geodetic latitude (radians).
|
|
76
|
+
* @param lonRad Receiver geodetic longitude (radians).
|
|
77
|
+
* @param azRad Satellite azimuth (radians).
|
|
78
|
+
* @param elRad Satellite elevation (radians).
|
|
79
|
+
* @param timeMs Epoch (ms). GIM epochs are UTC; a GPS-scale time differs
|
|
80
|
+
* by leap seconds (~18 s), negligible against hourly maps.
|
|
81
|
+
*/
|
|
82
|
+
declare function gimSlantIonoDelayL1(grid: IonexGrid, latRad: number, lonRad: number, azRad: number, elRad: number, timeMs: number): number | null;
|
|
83
|
+
|
|
37
84
|
/**
|
|
38
85
|
* RTK positioning, stage 1: epoch-wise double-differenced (DD)
|
|
39
86
|
* processing of a base/rover receiver pair.
|
|
@@ -54,7 +101,29 @@ declare function klobucharDelay(coeffs: KlobucharCoeffs, latRad: number, lonRad:
|
|
|
54
101
|
* (LAMBDA) needs the joint float ambiguity covariance — which the
|
|
55
102
|
* EKF maintains natively. This mirrors RTKLIB's float-filter
|
|
56
103
|
* structure (rtkpos.c), which the implementation was validated
|
|
57
|
-
* against
|
|
104
|
+
* against.
|
|
105
|
+
*
|
|
106
|
+
* Stage 2 — integer ambiguity resolution (opt-in via
|
|
107
|
+
* `ambiguityResolution: 'instant'`): after each float update the
|
|
108
|
+
* engine extracts the DD ambiguity subvector and its joint covariance
|
|
109
|
+
* from the filter, runs MLAMBDA (`lambdaSearch`, a port of RTKLIB's
|
|
110
|
+
* lambda.c) and validates the best candidate with the ratio test
|
|
111
|
+
* (s₂/s₁ ≥ `ratioThreshold`, default 3.0 — RTKLIB's default). On
|
|
112
|
+
* acceptance the rover position is conditioned on the integer
|
|
113
|
+
* ambiguities (xa = x − P_xb·Q_b⁻¹·(b − ǎ), RTKLIB rtkpos.c
|
|
114
|
+
* `resamb_LAMBDA`) and reported as a FIXED solution; the filter state
|
|
115
|
+
* itself is *not* modified (instant, per-epoch fixing — RTKLIB's
|
|
116
|
+
* `armode=instantaneous`; continuous AR/fix-and-hold, which feed the
|
|
117
|
+
* fix back into the filter, are stage-3 continuity options). When the
|
|
118
|
+
* full set fails the ratio test, partial fixing retries after
|
|
119
|
+
* dropping the lowest-elevation ambiguity, one at a time, down to
|
|
120
|
+
* half the candidate set (and never below `minFixAmbiguities`) — an
|
|
121
|
+
* elevation-ordered subset walk in the spirit of RTKLIB demo5's
|
|
122
|
+
* satellite exclusion, floored so an unconverged float cannot buy a
|
|
123
|
+
* lucky ratio on a tiny subset. GLONASS ambiguities are excluded from
|
|
124
|
+
* fixing by
|
|
125
|
+
* default (DD IFB is only integer-safe across same-model receivers;
|
|
126
|
+
* enable with `glonassAr`).
|
|
58
127
|
*
|
|
59
128
|
* Differencing model: for satellites s and reference r observed by
|
|
60
129
|
* rover R and base B, the DD pseudorange/phase cancels satellite
|
|
@@ -208,18 +277,65 @@ interface RtkFloatOptions {
|
|
|
208
277
|
slipGateM?: number;
|
|
209
278
|
/** Measurement-update relinearisations (IEKF). Default 2. */
|
|
210
279
|
updateIterations?: number;
|
|
280
|
+
/**
|
|
281
|
+
* Integer ambiguity resolution: 'off' keeps the stage-1 float-only
|
|
282
|
+
* behaviour; 'instant' attempts a per-epoch LAMBDA fix after each
|
|
283
|
+
* float update (no state feedback — RTKLIB `armode=instantaneous`).
|
|
284
|
+
* Default 'off'.
|
|
285
|
+
*/
|
|
286
|
+
ambiguityResolution?: 'off' | 'instant';
|
|
287
|
+
/** Ratio-test acceptance threshold (s₂/s₁). Default 3.0 (RTKLIB). */
|
|
288
|
+
ratioThreshold?: number;
|
|
289
|
+
/**
|
|
290
|
+
* Additional elevation mask (deg) for ambiguities entering the fix
|
|
291
|
+
* (RTKLIB `elmaskar`); 0 = use every measured ambiguity. Default 0.
|
|
292
|
+
*/
|
|
293
|
+
arElevationMaskDeg?: number;
|
|
294
|
+
/**
|
|
295
|
+
* Include GLONASS FDMA ambiguities in the integer fix. Only sound
|
|
296
|
+
* across same-model receivers (differential IFB). Default false.
|
|
297
|
+
*/
|
|
298
|
+
glonassAr?: boolean;
|
|
299
|
+
/**
|
|
300
|
+
* Partial fixing: when the full ambiguity set fails the ratio test,
|
|
301
|
+
* drop the lowest-elevation ambiguity and retry (elevation-ordered
|
|
302
|
+
* subset walk), never below half the candidates. Default true.
|
|
303
|
+
*/
|
|
304
|
+
partialFixing?: boolean;
|
|
305
|
+
/** Minimum DD ambiguities to attempt/accept a fix. Default 4. */
|
|
306
|
+
minFixAmbiguities?: number;
|
|
211
307
|
}
|
|
212
308
|
interface RtkFloatSolution {
|
|
213
309
|
/** Epoch, GPS-scale milliseconds. */
|
|
214
310
|
timeMs: number;
|
|
215
|
-
/**
|
|
311
|
+
/**
|
|
312
|
+
* Rover position, ECEF metres — the ambiguity-fixed position when
|
|
313
|
+
* `status === 'fixed'`, the float filter position otherwise.
|
|
314
|
+
*/
|
|
216
315
|
position: [number, number, number];
|
|
217
|
-
/** Float baseline rover − base, ECEF metres. */
|
|
316
|
+
/** Float baseline rover − base, ECEF metres (always the float). */
|
|
218
317
|
floatBaseline: [number, number, number];
|
|
318
|
+
/**
|
|
319
|
+
* Solution quality: 'fixed' when LAMBDA passed the ratio test,
|
|
320
|
+
* 'float' when carrier ambiguities contributed but no (accepted)
|
|
321
|
+
* fix, 'dgnss' when the epoch was updated from code DDs only.
|
|
322
|
+
*/
|
|
323
|
+
status: 'fixed' | 'float' | 'dgnss';
|
|
219
324
|
/** Satellites contributing DD rows this epoch (incl. references). */
|
|
220
325
|
nSats: number;
|
|
221
|
-
/**
|
|
326
|
+
/**
|
|
327
|
+
* Ambiguity-validation ratio (s₂/s₁ of the LAMBDA candidates,
|
|
328
|
+
* capped at 999.9 as RTKLIB): the accepted subset's ratio when
|
|
329
|
+
* fixed, the full-set ratio otherwise. Undefined when no fix was
|
|
330
|
+
* attempted (`ambiguityResolution: 'off'` or no eligible set).
|
|
331
|
+
*/
|
|
222
332
|
ratio?: number;
|
|
333
|
+
/** Number of integer-fixed DD ambiguities (when `status==='fixed'`). */
|
|
334
|
+
nFixed?: number;
|
|
335
|
+
/** Fixed baseline rover − base, ECEF metres (when fixed). */
|
|
336
|
+
fixedBaseline?: [number, number, number];
|
|
337
|
+
/** Integer DD ambiguity (cycles) per fixed PRN (when fixed). */
|
|
338
|
+
fixedAmbiguities?: Record<string, number>;
|
|
223
339
|
/** Formal position sigmas (filter covariance), ECEF metres. */
|
|
224
340
|
sigmas: [number, number, number];
|
|
225
341
|
/** Float DD ambiguity (cycles) per non-reference PRN. */
|
|
@@ -262,6 +378,17 @@ declare class RtkFloatEngine {
|
|
|
262
378
|
* transform of state and covariance (P' = T P Tᵀ).
|
|
263
379
|
*/
|
|
264
380
|
private retarget;
|
|
381
|
+
/**
|
|
382
|
+
* Try to fix the ambiguity subset `idxs` (indices into `this.amb`):
|
|
383
|
+
* extract the float subvector b and covariance Q_b from the filter,
|
|
384
|
+
* run MLAMBDA and, without touching the filter state, condition the
|
|
385
|
+
* position on the best integer candidate,
|
|
386
|
+
* xa = x − P_xb·Q_b⁻¹·(b − ǎ)
|
|
387
|
+
* (RTKLIB rtkpos.c `resamb_LAMBDA`). Returns null when Q_b is not
|
|
388
|
+
* positive definite / invertible or the search fails; ratio
|
|
389
|
+
* validation is the caller's job.
|
|
390
|
+
*/
|
|
391
|
+
private tryFix;
|
|
265
392
|
/**
|
|
266
393
|
* Process one synchronized epoch (same nominal `timeMs` for both
|
|
267
394
|
* receivers). Returns null until a first position can be
|
|
@@ -271,6 +398,88 @@ declare class RtkFloatEngine {
|
|
|
271
398
|
process(rover: RtkEpochMeasurements, base: RtkEpochMeasurements, timeMs: number): RtkFloatSolution | null;
|
|
272
399
|
}
|
|
273
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Integer ambiguity resolution: LAMBDA decorrelation + MLAMBDA
|
|
403
|
+
* integer least-squares search.
|
|
404
|
+
*
|
|
405
|
+
* Port of RTKLIB's `lambda.c` (demo5 / rtklibexplorer fork,
|
|
406
|
+
* src/lambda.c, Copyright (c) 2007-2008 T. Takasu, BSD-2-Clause),
|
|
407
|
+
* cross-checked against the reference papers where the C comments are
|
|
408
|
+
* thin:
|
|
409
|
+
*
|
|
410
|
+
* - [1] P.J.G. Teunissen, "The least-squares ambiguity decorrelation
|
|
411
|
+
* adjustment: a method for fast GPS ambiguity estimation",
|
|
412
|
+
* J. Geodesy 70, 65-82, 1995.
|
|
413
|
+
* - [2] X.-W. Chang, X. Yang, T. Zhou, "MLAMBDA: A modified LAMBDA
|
|
414
|
+
* method for integer least-squares estimation", J. Geodesy 79,
|
|
415
|
+
* 552-565, 2005.
|
|
416
|
+
*
|
|
417
|
+
* Pipeline (identical to RTKLIB's `lambda()`):
|
|
418
|
+
* 1. LᵀDL factorization of the float covariance, Q = Lᵀ·diag(D)·L
|
|
419
|
+
* with L unit lower triangular (RTKLIB's `LD`).
|
|
420
|
+
* 2. LAMBDA reduction [1]: integer Gauss transformations interleaved
|
|
421
|
+
* with symmetric permutations until the transformed D is
|
|
422
|
+
* (approximately) descending — producing a unimodular Z with
|
|
423
|
+
* Qz = ZᵀQZ = Lᵀ·diag(D)·L far better conditioned than Q.
|
|
424
|
+
* 3. MLAMBDA search [2] in the decorrelated space: depth-first
|
|
425
|
+
* enumeration over a shrinking ellipsoid, keeping the `m` smallest
|
|
426
|
+
* residual quadratic forms (z−ž)ᵀQz⁻¹(z−ž).
|
|
427
|
+
* 4. Back-transform the candidates to the original space, a = Z⁻ᵀ·ž
|
|
428
|
+
* (RTKLIB solves ZᵀF = E; Z is unimodular so F is exactly integer).
|
|
429
|
+
*
|
|
430
|
+
* Deviations from lambda.c:
|
|
431
|
+
* - matrices are `Float64Array` in the same column-major layout, with
|
|
432
|
+
* allocation-failure paths dropped (JS throws instead),
|
|
433
|
+
* - failures (non-positive-definite Q, search loop overflow) return
|
|
434
|
+
* `null` instead of a status code + stderr message,
|
|
435
|
+
* - back-transformed candidates are rounded to the nearest integer:
|
|
436
|
+
* F = Z⁻ᵀ·ž is exactly integer in ℤ arithmetic and the rounding only
|
|
437
|
+
* removes the ~1e-12 fuzz of the floating-point triangular solve,
|
|
438
|
+
* - the ratio s₂/s₁ used by the RTKLIB caller (`rtkpos.c`) is returned
|
|
439
|
+
* alongside the candidates (∞ when s₁ = 0, i.e. noise-free input).
|
|
440
|
+
*/
|
|
441
|
+
/** Result of an integer least-squares search. */
|
|
442
|
+
interface LambdaResult {
|
|
443
|
+
/**
|
|
444
|
+
* The `m` best integer candidate vectors (length n each), sorted by
|
|
445
|
+
* ascending residual quadratic form; `candidates[0]` is the integer
|
|
446
|
+
* least-squares minimizer.
|
|
447
|
+
*/
|
|
448
|
+
candidates: Float64Array[];
|
|
449
|
+
/**
|
|
450
|
+
* Residual quadratic forms sᵢ = (a−ǎᵢ)ᵀQ⁻¹(a−ǎᵢ) matching
|
|
451
|
+
* `candidates`, ascending.
|
|
452
|
+
*/
|
|
453
|
+
residuals: Float64Array;
|
|
454
|
+
/**
|
|
455
|
+
* Ratio-test statistic s₂/s₁ (second-best over best); `Infinity`
|
|
456
|
+
* when the best residual is exactly 0 (noise-free input). Callers
|
|
457
|
+
* typically cap it (RTKLIB uses 999.9) before thresholding.
|
|
458
|
+
*/
|
|
459
|
+
ratio: number;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* LAMBDA/MLAMBDA integer least-squares estimation (RTKLIB `lambda`):
|
|
463
|
+
* decorrelate the float ambiguities, search the `m` best integer
|
|
464
|
+
* vectors and back-transform them to the original space.
|
|
465
|
+
*
|
|
466
|
+
* @param a Float ambiguities (length ≥ n).
|
|
467
|
+
* @param Q Their covariance, n×n (symmetric — row/column-major agree).
|
|
468
|
+
* @param n Number of ambiguities.
|
|
469
|
+
* @param m Number of candidates to return (default 2: best +
|
|
470
|
+
* second-best, what the ratio test needs).
|
|
471
|
+
* @returns Candidates + residuals + ratio, or null when Q is not
|
|
472
|
+
* positive definite or the search does not terminate.
|
|
473
|
+
*/
|
|
474
|
+
declare function lambdaSearch(a: Float64Array, Q: Float64Array, n: number, m?: number): LambdaResult | null;
|
|
475
|
+
/**
|
|
476
|
+
* LAMBDA reduction only (RTKLIB `lambda_reduction`): the unimodular
|
|
477
|
+
* decorrelation matrix Z (n×n, column-major) such that Qz = ZᵀQZ is
|
|
478
|
+
* (near-)diagonally dominant. Exposed for diagnostics/tests; returns
|
|
479
|
+
* null when Q is not positive definite.
|
|
480
|
+
*/
|
|
481
|
+
declare function lambdaReduction(Q: Float64Array, n: number): Float64Array | null;
|
|
482
|
+
|
|
274
483
|
/**
|
|
275
484
|
* Single-point positioning (SPP) from pseudoranges and broadcast
|
|
276
485
|
* ephemerides.
|
|
@@ -279,7 +488,7 @@ declare class RtkFloatEngine {
|
|
|
279
488
|
* constellation (absorbing inter-system time offsets), satellite clock
|
|
280
489
|
* polynomial + relativistic correction, broadcast group delay
|
|
281
490
|
* (TGD/BGD), Sagnac (Earth-rotation) correction, elevation
|
|
282
|
-
* masking/weighting, and a
|
|
491
|
+
* masking/weighting, and a Saastamoinen tropospheric model. Ionospheric
|
|
283
492
|
* delay can be modelled from the broadcast Klobuchar coefficients
|
|
284
493
|
* (`iono` option) for single-frequency measurements; for metre-level
|
|
285
494
|
* results prefer the iono-free combination (ionoFree helper) with
|
|
@@ -289,7 +498,7 @@ declare class RtkFloatEngine {
|
|
|
289
498
|
interface SppOptions {
|
|
290
499
|
/** Elevation mask in degrees (applied after the first pass). Default 10. */
|
|
291
500
|
elevationMaskDeg?: number;
|
|
292
|
-
/** Apply the
|
|
501
|
+
/** Apply the Saastamoinen tropospheric model. Default true. */
|
|
293
502
|
troposphere?: boolean;
|
|
294
503
|
/** Maximum Gauss-Newton iterations. Default 15. */
|
|
295
504
|
maxIterations?: number;
|
|
@@ -303,6 +512,15 @@ interface SppOptions {
|
|
|
303
512
|
* iono-free combinations.
|
|
304
513
|
*/
|
|
305
514
|
iono?: KlobucharCoeffs;
|
|
515
|
+
/**
|
|
516
|
+
* Global Ionosphere Map (IONEX/GIM, from `parseIonex`). When given it
|
|
517
|
+
* takes precedence over `iono`: the slant delay is read from the map
|
|
518
|
+
* (~80–90% of the true ionosphere vs Klobuchar's ~50%), falling back
|
|
519
|
+
* to `iono` — if also supplied — only where the map has no value for
|
|
520
|
+
* an epoch/cell (a time gap; global maps always cover space). Omit for
|
|
521
|
+
* iono-free input.
|
|
522
|
+
*/
|
|
523
|
+
gim?: IonexGrid;
|
|
306
524
|
/**
|
|
307
525
|
* Apply the broadcast group-delay correction (GPS TGD, Galileo
|
|
308
526
|
* BGD E5a/E1, BeiDou TGD1) to the satellite clock. Correct for
|
|
@@ -351,4 +569,4 @@ declare function ionoFree(p1: number, p2: number, f1: number, f2: number): numbe
|
|
|
351
569
|
*/
|
|
352
570
|
declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
|
|
353
571
|
|
|
354
|
-
export { type DgnssOptions, type DgnssSolution, type EphemerisSource, type KlobucharCoeffs, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type SppOptions, type SppSolution, ionoFree, klobucharDelay, satClockCorrection, solveDgnss, solveSpp, toRtkEpoch };
|
|
572
|
+
export { type DgnssOptions, type DgnssSolution, type EphemerisSource, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type SppOptions, type SppSolution, gimSlantIonoDelayL1, gimVerticalTec, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, satClockCorrection, solveDgnss, solveSpp, toRtkEpoch };
|
package/dist/positioning.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { E as Ephemeris } from './nav-DImXUdbp.js';
|
|
2
2
|
import { DopValues } from './orbit.js';
|
|
3
|
+
import { I as IonexGrid } from './ionex-DaW5x9nq.js';
|
|
3
4
|
import './ephemeris-Ohl6NAfw.js';
|
|
4
5
|
import './ecef-CF0uAysr.js';
|
|
5
6
|
import './nav-CN8rxL4h.js';
|
|
@@ -34,6 +35,52 @@ interface KlobucharCoeffs {
|
|
|
34
35
|
*/
|
|
35
36
|
declare function klobucharDelay(coeffs: KlobucharCoeffs, latRad: number, lonRad: number, azRad: number, elRad: number, gpsTowSec: number): number;
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Slant ionospheric delay from a Global Ionosphere Map (IONEX/GIM).
|
|
40
|
+
*
|
|
41
|
+
* A GIM gives vertical TEC on an hourly lat/lon grid (see
|
|
42
|
+
* {@link parseIonex}). For single-frequency positioning this is a much
|
|
43
|
+
* stronger correction than the broadcast Klobuchar model — GIMs capture
|
|
44
|
+
* ~80–90% of the true ionosphere versus Klobuchar's ~50% — so feeding
|
|
45
|
+
* `solveSpp` a GIM (the `gim` option) collapses most of the residual
|
|
46
|
+
* height bias a single-frequency solution otherwise carries.
|
|
47
|
+
*
|
|
48
|
+
* The evaluation is the standard thin-shell recipe: pierce the shell at
|
|
49
|
+
* {@link H_ION}, interpolate vertical TEC there (bilinear in space,
|
|
50
|
+
* linear in time), map to slant with the obliquity factor, and convert
|
|
51
|
+
* TEC → L1 group delay. The result is the delay on GPS L1; `solveSpp`
|
|
52
|
+
* scales it to each system's primary frequency by (f_L1/f)².
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* L1 group delay per unit slant TEC: 40.3 · 10¹⁶ / f_L1² (m per TECU).
|
|
57
|
+
* 40.3 m·Hz²/(el/m²), one TECU = 10¹⁶ el/m². ≈ 0.1624 m/TECU.
|
|
58
|
+
*/
|
|
59
|
+
declare const IONO_L1_M_PER_TECU: number;
|
|
60
|
+
/**
|
|
61
|
+
* Space- and time-interpolated vertical TEC (TECU) at `timeMs`, or null
|
|
62
|
+
* outside the map's time span or where the grid marks no value.
|
|
63
|
+
*
|
|
64
|
+
* IGS GIMs are global, so spatial coverage is total; a pierce point is
|
|
65
|
+
* bilinearly interpolated and clamped to the nearest edge cell rather
|
|
66
|
+
* than rejected. Null therefore signals a time gap or a no-value cell,
|
|
67
|
+
* not an out-of-area position.
|
|
68
|
+
*/
|
|
69
|
+
declare function gimVerticalTec(grid: IonexGrid, timeMs: number, latDeg: number, lonDeg: number): number | null;
|
|
70
|
+
/**
|
|
71
|
+
* Slant ionospheric group delay on GPS L1 (m) from a GIM, or null when
|
|
72
|
+
* the pierce point falls outside the map's coverage in space or time.
|
|
73
|
+
*
|
|
74
|
+
* @param grid Parsed IONEX grid ({@link parseIonex}).
|
|
75
|
+
* @param latRad Receiver geodetic latitude (radians).
|
|
76
|
+
* @param lonRad Receiver geodetic longitude (radians).
|
|
77
|
+
* @param azRad Satellite azimuth (radians).
|
|
78
|
+
* @param elRad Satellite elevation (radians).
|
|
79
|
+
* @param timeMs Epoch (ms). GIM epochs are UTC; a GPS-scale time differs
|
|
80
|
+
* by leap seconds (~18 s), negligible against hourly maps.
|
|
81
|
+
*/
|
|
82
|
+
declare function gimSlantIonoDelayL1(grid: IonexGrid, latRad: number, lonRad: number, azRad: number, elRad: number, timeMs: number): number | null;
|
|
83
|
+
|
|
37
84
|
/**
|
|
38
85
|
* RTK positioning, stage 1: epoch-wise double-differenced (DD)
|
|
39
86
|
* processing of a base/rover receiver pair.
|
|
@@ -54,7 +101,29 @@ declare function klobucharDelay(coeffs: KlobucharCoeffs, latRad: number, lonRad:
|
|
|
54
101
|
* (LAMBDA) needs the joint float ambiguity covariance — which the
|
|
55
102
|
* EKF maintains natively. This mirrors RTKLIB's float-filter
|
|
56
103
|
* structure (rtkpos.c), which the implementation was validated
|
|
57
|
-
* against
|
|
104
|
+
* against.
|
|
105
|
+
*
|
|
106
|
+
* Stage 2 — integer ambiguity resolution (opt-in via
|
|
107
|
+
* `ambiguityResolution: 'instant'`): after each float update the
|
|
108
|
+
* engine extracts the DD ambiguity subvector and its joint covariance
|
|
109
|
+
* from the filter, runs MLAMBDA (`lambdaSearch`, a port of RTKLIB's
|
|
110
|
+
* lambda.c) and validates the best candidate with the ratio test
|
|
111
|
+
* (s₂/s₁ ≥ `ratioThreshold`, default 3.0 — RTKLIB's default). On
|
|
112
|
+
* acceptance the rover position is conditioned on the integer
|
|
113
|
+
* ambiguities (xa = x − P_xb·Q_b⁻¹·(b − ǎ), RTKLIB rtkpos.c
|
|
114
|
+
* `resamb_LAMBDA`) and reported as a FIXED solution; the filter state
|
|
115
|
+
* itself is *not* modified (instant, per-epoch fixing — RTKLIB's
|
|
116
|
+
* `armode=instantaneous`; continuous AR/fix-and-hold, which feed the
|
|
117
|
+
* fix back into the filter, are stage-3 continuity options). When the
|
|
118
|
+
* full set fails the ratio test, partial fixing retries after
|
|
119
|
+
* dropping the lowest-elevation ambiguity, one at a time, down to
|
|
120
|
+
* half the candidate set (and never below `minFixAmbiguities`) — an
|
|
121
|
+
* elevation-ordered subset walk in the spirit of RTKLIB demo5's
|
|
122
|
+
* satellite exclusion, floored so an unconverged float cannot buy a
|
|
123
|
+
* lucky ratio on a tiny subset. GLONASS ambiguities are excluded from
|
|
124
|
+
* fixing by
|
|
125
|
+
* default (DD IFB is only integer-safe across same-model receivers;
|
|
126
|
+
* enable with `glonassAr`).
|
|
58
127
|
*
|
|
59
128
|
* Differencing model: for satellites s and reference r observed by
|
|
60
129
|
* rover R and base B, the DD pseudorange/phase cancels satellite
|
|
@@ -208,18 +277,65 @@ interface RtkFloatOptions {
|
|
|
208
277
|
slipGateM?: number;
|
|
209
278
|
/** Measurement-update relinearisations (IEKF). Default 2. */
|
|
210
279
|
updateIterations?: number;
|
|
280
|
+
/**
|
|
281
|
+
* Integer ambiguity resolution: 'off' keeps the stage-1 float-only
|
|
282
|
+
* behaviour; 'instant' attempts a per-epoch LAMBDA fix after each
|
|
283
|
+
* float update (no state feedback — RTKLIB `armode=instantaneous`).
|
|
284
|
+
* Default 'off'.
|
|
285
|
+
*/
|
|
286
|
+
ambiguityResolution?: 'off' | 'instant';
|
|
287
|
+
/** Ratio-test acceptance threshold (s₂/s₁). Default 3.0 (RTKLIB). */
|
|
288
|
+
ratioThreshold?: number;
|
|
289
|
+
/**
|
|
290
|
+
* Additional elevation mask (deg) for ambiguities entering the fix
|
|
291
|
+
* (RTKLIB `elmaskar`); 0 = use every measured ambiguity. Default 0.
|
|
292
|
+
*/
|
|
293
|
+
arElevationMaskDeg?: number;
|
|
294
|
+
/**
|
|
295
|
+
* Include GLONASS FDMA ambiguities in the integer fix. Only sound
|
|
296
|
+
* across same-model receivers (differential IFB). Default false.
|
|
297
|
+
*/
|
|
298
|
+
glonassAr?: boolean;
|
|
299
|
+
/**
|
|
300
|
+
* Partial fixing: when the full ambiguity set fails the ratio test,
|
|
301
|
+
* drop the lowest-elevation ambiguity and retry (elevation-ordered
|
|
302
|
+
* subset walk), never below half the candidates. Default true.
|
|
303
|
+
*/
|
|
304
|
+
partialFixing?: boolean;
|
|
305
|
+
/** Minimum DD ambiguities to attempt/accept a fix. Default 4. */
|
|
306
|
+
minFixAmbiguities?: number;
|
|
211
307
|
}
|
|
212
308
|
interface RtkFloatSolution {
|
|
213
309
|
/** Epoch, GPS-scale milliseconds. */
|
|
214
310
|
timeMs: number;
|
|
215
|
-
/**
|
|
311
|
+
/**
|
|
312
|
+
* Rover position, ECEF metres — the ambiguity-fixed position when
|
|
313
|
+
* `status === 'fixed'`, the float filter position otherwise.
|
|
314
|
+
*/
|
|
216
315
|
position: [number, number, number];
|
|
217
|
-
/** Float baseline rover − base, ECEF metres. */
|
|
316
|
+
/** Float baseline rover − base, ECEF metres (always the float). */
|
|
218
317
|
floatBaseline: [number, number, number];
|
|
318
|
+
/**
|
|
319
|
+
* Solution quality: 'fixed' when LAMBDA passed the ratio test,
|
|
320
|
+
* 'float' when carrier ambiguities contributed but no (accepted)
|
|
321
|
+
* fix, 'dgnss' when the epoch was updated from code DDs only.
|
|
322
|
+
*/
|
|
323
|
+
status: 'fixed' | 'float' | 'dgnss';
|
|
219
324
|
/** Satellites contributing DD rows this epoch (incl. references). */
|
|
220
325
|
nSats: number;
|
|
221
|
-
/**
|
|
326
|
+
/**
|
|
327
|
+
* Ambiguity-validation ratio (s₂/s₁ of the LAMBDA candidates,
|
|
328
|
+
* capped at 999.9 as RTKLIB): the accepted subset's ratio when
|
|
329
|
+
* fixed, the full-set ratio otherwise. Undefined when no fix was
|
|
330
|
+
* attempted (`ambiguityResolution: 'off'` or no eligible set).
|
|
331
|
+
*/
|
|
222
332
|
ratio?: number;
|
|
333
|
+
/** Number of integer-fixed DD ambiguities (when `status==='fixed'`). */
|
|
334
|
+
nFixed?: number;
|
|
335
|
+
/** Fixed baseline rover − base, ECEF metres (when fixed). */
|
|
336
|
+
fixedBaseline?: [number, number, number];
|
|
337
|
+
/** Integer DD ambiguity (cycles) per fixed PRN (when fixed). */
|
|
338
|
+
fixedAmbiguities?: Record<string, number>;
|
|
223
339
|
/** Formal position sigmas (filter covariance), ECEF metres. */
|
|
224
340
|
sigmas: [number, number, number];
|
|
225
341
|
/** Float DD ambiguity (cycles) per non-reference PRN. */
|
|
@@ -262,6 +378,17 @@ declare class RtkFloatEngine {
|
|
|
262
378
|
* transform of state and covariance (P' = T P Tᵀ).
|
|
263
379
|
*/
|
|
264
380
|
private retarget;
|
|
381
|
+
/**
|
|
382
|
+
* Try to fix the ambiguity subset `idxs` (indices into `this.amb`):
|
|
383
|
+
* extract the float subvector b and covariance Q_b from the filter,
|
|
384
|
+
* run MLAMBDA and, without touching the filter state, condition the
|
|
385
|
+
* position on the best integer candidate,
|
|
386
|
+
* xa = x − P_xb·Q_b⁻¹·(b − ǎ)
|
|
387
|
+
* (RTKLIB rtkpos.c `resamb_LAMBDA`). Returns null when Q_b is not
|
|
388
|
+
* positive definite / invertible or the search fails; ratio
|
|
389
|
+
* validation is the caller's job.
|
|
390
|
+
*/
|
|
391
|
+
private tryFix;
|
|
265
392
|
/**
|
|
266
393
|
* Process one synchronized epoch (same nominal `timeMs` for both
|
|
267
394
|
* receivers). Returns null until a first position can be
|
|
@@ -271,6 +398,88 @@ declare class RtkFloatEngine {
|
|
|
271
398
|
process(rover: RtkEpochMeasurements, base: RtkEpochMeasurements, timeMs: number): RtkFloatSolution | null;
|
|
272
399
|
}
|
|
273
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Integer ambiguity resolution: LAMBDA decorrelation + MLAMBDA
|
|
403
|
+
* integer least-squares search.
|
|
404
|
+
*
|
|
405
|
+
* Port of RTKLIB's `lambda.c` (demo5 / rtklibexplorer fork,
|
|
406
|
+
* src/lambda.c, Copyright (c) 2007-2008 T. Takasu, BSD-2-Clause),
|
|
407
|
+
* cross-checked against the reference papers where the C comments are
|
|
408
|
+
* thin:
|
|
409
|
+
*
|
|
410
|
+
* - [1] P.J.G. Teunissen, "The least-squares ambiguity decorrelation
|
|
411
|
+
* adjustment: a method for fast GPS ambiguity estimation",
|
|
412
|
+
* J. Geodesy 70, 65-82, 1995.
|
|
413
|
+
* - [2] X.-W. Chang, X. Yang, T. Zhou, "MLAMBDA: A modified LAMBDA
|
|
414
|
+
* method for integer least-squares estimation", J. Geodesy 79,
|
|
415
|
+
* 552-565, 2005.
|
|
416
|
+
*
|
|
417
|
+
* Pipeline (identical to RTKLIB's `lambda()`):
|
|
418
|
+
* 1. LᵀDL factorization of the float covariance, Q = Lᵀ·diag(D)·L
|
|
419
|
+
* with L unit lower triangular (RTKLIB's `LD`).
|
|
420
|
+
* 2. LAMBDA reduction [1]: integer Gauss transformations interleaved
|
|
421
|
+
* with symmetric permutations until the transformed D is
|
|
422
|
+
* (approximately) descending — producing a unimodular Z with
|
|
423
|
+
* Qz = ZᵀQZ = Lᵀ·diag(D)·L far better conditioned than Q.
|
|
424
|
+
* 3. MLAMBDA search [2] in the decorrelated space: depth-first
|
|
425
|
+
* enumeration over a shrinking ellipsoid, keeping the `m` smallest
|
|
426
|
+
* residual quadratic forms (z−ž)ᵀQz⁻¹(z−ž).
|
|
427
|
+
* 4. Back-transform the candidates to the original space, a = Z⁻ᵀ·ž
|
|
428
|
+
* (RTKLIB solves ZᵀF = E; Z is unimodular so F is exactly integer).
|
|
429
|
+
*
|
|
430
|
+
* Deviations from lambda.c:
|
|
431
|
+
* - matrices are `Float64Array` in the same column-major layout, with
|
|
432
|
+
* allocation-failure paths dropped (JS throws instead),
|
|
433
|
+
* - failures (non-positive-definite Q, search loop overflow) return
|
|
434
|
+
* `null` instead of a status code + stderr message,
|
|
435
|
+
* - back-transformed candidates are rounded to the nearest integer:
|
|
436
|
+
* F = Z⁻ᵀ·ž is exactly integer in ℤ arithmetic and the rounding only
|
|
437
|
+
* removes the ~1e-12 fuzz of the floating-point triangular solve,
|
|
438
|
+
* - the ratio s₂/s₁ used by the RTKLIB caller (`rtkpos.c`) is returned
|
|
439
|
+
* alongside the candidates (∞ when s₁ = 0, i.e. noise-free input).
|
|
440
|
+
*/
|
|
441
|
+
/** Result of an integer least-squares search. */
|
|
442
|
+
interface LambdaResult {
|
|
443
|
+
/**
|
|
444
|
+
* The `m` best integer candidate vectors (length n each), sorted by
|
|
445
|
+
* ascending residual quadratic form; `candidates[0]` is the integer
|
|
446
|
+
* least-squares minimizer.
|
|
447
|
+
*/
|
|
448
|
+
candidates: Float64Array[];
|
|
449
|
+
/**
|
|
450
|
+
* Residual quadratic forms sᵢ = (a−ǎᵢ)ᵀQ⁻¹(a−ǎᵢ) matching
|
|
451
|
+
* `candidates`, ascending.
|
|
452
|
+
*/
|
|
453
|
+
residuals: Float64Array;
|
|
454
|
+
/**
|
|
455
|
+
* Ratio-test statistic s₂/s₁ (second-best over best); `Infinity`
|
|
456
|
+
* when the best residual is exactly 0 (noise-free input). Callers
|
|
457
|
+
* typically cap it (RTKLIB uses 999.9) before thresholding.
|
|
458
|
+
*/
|
|
459
|
+
ratio: number;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* LAMBDA/MLAMBDA integer least-squares estimation (RTKLIB `lambda`):
|
|
463
|
+
* decorrelate the float ambiguities, search the `m` best integer
|
|
464
|
+
* vectors and back-transform them to the original space.
|
|
465
|
+
*
|
|
466
|
+
* @param a Float ambiguities (length ≥ n).
|
|
467
|
+
* @param Q Their covariance, n×n (symmetric — row/column-major agree).
|
|
468
|
+
* @param n Number of ambiguities.
|
|
469
|
+
* @param m Number of candidates to return (default 2: best +
|
|
470
|
+
* second-best, what the ratio test needs).
|
|
471
|
+
* @returns Candidates + residuals + ratio, or null when Q is not
|
|
472
|
+
* positive definite or the search does not terminate.
|
|
473
|
+
*/
|
|
474
|
+
declare function lambdaSearch(a: Float64Array, Q: Float64Array, n: number, m?: number): LambdaResult | null;
|
|
475
|
+
/**
|
|
476
|
+
* LAMBDA reduction only (RTKLIB `lambda_reduction`): the unimodular
|
|
477
|
+
* decorrelation matrix Z (n×n, column-major) such that Qz = ZᵀQZ is
|
|
478
|
+
* (near-)diagonally dominant. Exposed for diagnostics/tests; returns
|
|
479
|
+
* null when Q is not positive definite.
|
|
480
|
+
*/
|
|
481
|
+
declare function lambdaReduction(Q: Float64Array, n: number): Float64Array | null;
|
|
482
|
+
|
|
274
483
|
/**
|
|
275
484
|
* Single-point positioning (SPP) from pseudoranges and broadcast
|
|
276
485
|
* ephemerides.
|
|
@@ -279,7 +488,7 @@ declare class RtkFloatEngine {
|
|
|
279
488
|
* constellation (absorbing inter-system time offsets), satellite clock
|
|
280
489
|
* polynomial + relativistic correction, broadcast group delay
|
|
281
490
|
* (TGD/BGD), Sagnac (Earth-rotation) correction, elevation
|
|
282
|
-
* masking/weighting, and a
|
|
491
|
+
* masking/weighting, and a Saastamoinen tropospheric model. Ionospheric
|
|
283
492
|
* delay can be modelled from the broadcast Klobuchar coefficients
|
|
284
493
|
* (`iono` option) for single-frequency measurements; for metre-level
|
|
285
494
|
* results prefer the iono-free combination (ionoFree helper) with
|
|
@@ -289,7 +498,7 @@ declare class RtkFloatEngine {
|
|
|
289
498
|
interface SppOptions {
|
|
290
499
|
/** Elevation mask in degrees (applied after the first pass). Default 10. */
|
|
291
500
|
elevationMaskDeg?: number;
|
|
292
|
-
/** Apply the
|
|
501
|
+
/** Apply the Saastamoinen tropospheric model. Default true. */
|
|
293
502
|
troposphere?: boolean;
|
|
294
503
|
/** Maximum Gauss-Newton iterations. Default 15. */
|
|
295
504
|
maxIterations?: number;
|
|
@@ -303,6 +512,15 @@ interface SppOptions {
|
|
|
303
512
|
* iono-free combinations.
|
|
304
513
|
*/
|
|
305
514
|
iono?: KlobucharCoeffs;
|
|
515
|
+
/**
|
|
516
|
+
* Global Ionosphere Map (IONEX/GIM, from `parseIonex`). When given it
|
|
517
|
+
* takes precedence over `iono`: the slant delay is read from the map
|
|
518
|
+
* (~80–90% of the true ionosphere vs Klobuchar's ~50%), falling back
|
|
519
|
+
* to `iono` — if also supplied — only where the map has no value for
|
|
520
|
+
* an epoch/cell (a time gap; global maps always cover space). Omit for
|
|
521
|
+
* iono-free input.
|
|
522
|
+
*/
|
|
523
|
+
gim?: IonexGrid;
|
|
306
524
|
/**
|
|
307
525
|
* Apply the broadcast group-delay correction (GPS TGD, Galileo
|
|
308
526
|
* BGD E5a/E1, BeiDou TGD1) to the satellite clock. Correct for
|
|
@@ -351,4 +569,4 @@ declare function ionoFree(p1: number, p2: number, f1: number, f2: number): numbe
|
|
|
351
569
|
*/
|
|
352
570
|
declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
|
|
353
571
|
|
|
354
|
-
export { type DgnssOptions, type DgnssSolution, type EphemerisSource, type KlobucharCoeffs, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type SppOptions, type SppSolution, ionoFree, klobucharDelay, satClockCorrection, solveDgnss, solveSpp, toRtkEpoch };
|
|
572
|
+
export { type DgnssOptions, type DgnssSolution, type EphemerisSource, IONO_L1_M_PER_TECU, type KlobucharCoeffs, type LambdaResult, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type SppOptions, type SppSolution, gimSlantIonoDelayL1, gimVerticalTec, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, satClockCorrection, solveDgnss, solveSpp, toRtkEpoch };
|
package/dist/positioning.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
+
IONO_L1_M_PER_TECU,
|
|
2
3
|
RtkFloatEngine,
|
|
4
|
+
gimSlantIonoDelayL1,
|
|
5
|
+
gimVerticalTec,
|
|
3
6
|
ionoFree,
|
|
4
7
|
klobucharDelay,
|
|
8
|
+
lambdaReduction,
|
|
9
|
+
lambdaSearch,
|
|
5
10
|
satClockCorrection,
|
|
6
11
|
solveDgnss,
|
|
7
12
|
solveSpp,
|
|
8
13
|
toRtkEpoch
|
|
9
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-4XGKCVE3.js";
|
|
10
15
|
import "./chunk-MHGNKSSH.js";
|
|
11
16
|
import "./chunk-37QNKGTC.js";
|
|
12
17
|
import "./chunk-6FAL6P4G.js";
|
|
@@ -14,9 +19,14 @@ import "./chunk-HVXYFUCB.js";
|
|
|
14
19
|
import "./chunk-LEEU5OIO.js";
|
|
15
20
|
import "./chunk-FIEWO4J4.js";
|
|
16
21
|
export {
|
|
22
|
+
IONO_L1_M_PER_TECU,
|
|
17
23
|
RtkFloatEngine,
|
|
24
|
+
gimSlantIonoDelayL1,
|
|
25
|
+
gimVerticalTec,
|
|
18
26
|
ionoFree,
|
|
19
27
|
klobucharDelay,
|
|
28
|
+
lambdaReduction,
|
|
29
|
+
lambdaSearch,
|
|
20
30
|
satClockCorrection,
|
|
21
31
|
solveDgnss,
|
|
22
32
|
solveSpp,
|