gnss-js 1.22.0 → 1.24.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-JNFV5BYB.js → chunk-MHGNKSSH.js} +2 -3
- package/dist/chunk-T2ZMNRCY.js +1274 -0
- package/dist/index.cjs +1022 -7
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +13 -3
- package/dist/orbit.cjs +2 -3
- package/dist/orbit.js +1 -1
- package/dist/positioning.cjs +1044 -9
- package/dist/positioning.d.cts +400 -1
- package/dist/positioning.d.ts +400 -1
- package/dist/positioning.js +14 -4
- package/package.json +1 -1
- package/dist/chunk-B5LU4746.js +0 -262
package/dist/positioning.d.ts
CHANGED
|
@@ -34,6 +34,405 @@ interface KlobucharCoeffs {
|
|
|
34
34
|
*/
|
|
35
35
|
declare function klobucharDelay(coeffs: KlobucharCoeffs, latRad: number, lonRad: number, azRad: number, elRad: number, gpsTowSec: number): number;
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* RTK positioning, stage 1: epoch-wise double-differenced (DD)
|
|
39
|
+
* processing of a base/rover receiver pair.
|
|
40
|
+
*
|
|
41
|
+
* Two solvers share the same DD construction:
|
|
42
|
+
*
|
|
43
|
+
* - `solveDgnss` — code-based DGNSS: between-receiver single
|
|
44
|
+
* differences on pseudoranges, double-differenced against a
|
|
45
|
+
* reference satellite (highest elevation per signal group), iterated
|
|
46
|
+
* weighted least squares for the rover position with the full DD
|
|
47
|
+
* covariance (DDs sharing a reference satellite are correlated).
|
|
48
|
+
*
|
|
49
|
+
* - `RtkFloatEngine` — a stateful extended Kalman filter over DD
|
|
50
|
+
* carrier phase + code with one float DD ambiguity state per
|
|
51
|
+
* satellite/signal. An EKF (rather than epoch-wise iterated least
|
|
52
|
+
* squares with appended ambiguity columns) was chosen because the
|
|
53
|
+
* float ambiguities must persist across epochs anyway, and stage 2
|
|
54
|
+
* (LAMBDA) needs the joint float ambiguity covariance — which the
|
|
55
|
+
* EKF maintains natively. This mirrors RTKLIB's float-filter
|
|
56
|
+
* structure (rtkpos.c), which the implementation was validated
|
|
57
|
+
* against.
|
|
58
|
+
*
|
|
59
|
+
* Stage 2 — integer ambiguity resolution (opt-in via
|
|
60
|
+
* `ambiguityResolution: 'instant'`): after each float update the
|
|
61
|
+
* engine extracts the DD ambiguity subvector and its joint covariance
|
|
62
|
+
* from the filter, runs MLAMBDA (`lambdaSearch`, a port of RTKLIB's
|
|
63
|
+
* lambda.c) and validates the best candidate with the ratio test
|
|
64
|
+
* (s₂/s₁ ≥ `ratioThreshold`, default 3.0 — RTKLIB's default). On
|
|
65
|
+
* acceptance the rover position is conditioned on the integer
|
|
66
|
+
* ambiguities (xa = x − P_xb·Q_b⁻¹·(b − ǎ), RTKLIB rtkpos.c
|
|
67
|
+
* `resamb_LAMBDA`) and reported as a FIXED solution; the filter state
|
|
68
|
+
* itself is *not* modified (instant, per-epoch fixing — RTKLIB's
|
|
69
|
+
* `armode=instantaneous`; continuous AR/fix-and-hold, which feed the
|
|
70
|
+
* fix back into the filter, are stage-3 continuity options). When the
|
|
71
|
+
* full set fails the ratio test, partial fixing retries after
|
|
72
|
+
* dropping the lowest-elevation ambiguity, one at a time, down to
|
|
73
|
+
* half the candidate set (and never below `minFixAmbiguities`) — an
|
|
74
|
+
* elevation-ordered subset walk in the spirit of RTKLIB demo5's
|
|
75
|
+
* satellite exclusion, floored so an unconverged float cannot buy a
|
|
76
|
+
* lucky ratio on a tiny subset. GLONASS ambiguities are excluded from
|
|
77
|
+
* fixing by
|
|
78
|
+
* default (DD IFB is only integer-safe across same-model receivers;
|
|
79
|
+
* enable with `glonassAr`).
|
|
80
|
+
*
|
|
81
|
+
* Differencing model: for satellites s and reference r observed by
|
|
82
|
+
* rover R and base B, the DD pseudorange/phase cancels satellite
|
|
83
|
+
* clocks (single difference) and receiver clocks (double difference).
|
|
84
|
+
* On short baselines the ionospheric and most of the tropospheric
|
|
85
|
+
* delay cancel too; the residual troposphere is modelled by the same
|
|
86
|
+
* simple elevation-mapped zenith model `solveSpp` uses, applied
|
|
87
|
+
* differentially. DDs are formed per *signal group* (constellation
|
|
88
|
+
* letter + RINEX code, e.g. `G1C`, `C2I`) so that only like signals
|
|
89
|
+
* are differenced.
|
|
90
|
+
*
|
|
91
|
+
* GLONASS (FDMA): DD phase is formed in metres with per-satellite
|
|
92
|
+
* wavelengths (λ_s·SDφ_s − λ_r·SDφ_r). The common receiver clock
|
|
93
|
+
* term cancels exactly (λ·f = c on every channel); the per-channel
|
|
94
|
+
* receiver phase bias (inter-frequency bias, IFB) does not, but it is
|
|
95
|
+
* constant while lock holds and is absorbed into the float DD
|
|
96
|
+
* ambiguity — the standard reason GLONASS float works while GLONASS
|
|
97
|
+
* integer fixing is hard. DD *code* retains the differential
|
|
98
|
+
* inter-channel code bias between the two receivers, which is small
|
|
99
|
+
* (well below the code noise) for same-model receivers — IFB-safe
|
|
100
|
+
* enough for stage 1, and documented as such.
|
|
101
|
+
*
|
|
102
|
+
* Time convention: `timeMs` is GPS-scale epoch milliseconds, exactly
|
|
103
|
+
* as produced by `parseNovatelRange` / the RINEX and RTCM parsers —
|
|
104
|
+
* the same convention `solveSpp` uses. No system clock is read.
|
|
105
|
+
*
|
|
106
|
+
* Adapting decoder output (see `toRtkEpoch`):
|
|
107
|
+
* - NovAtel: `parseNovatelRange(bytes).epochs[i]` gives
|
|
108
|
+
* `{ timeMs, meas }`; `toRtkEpoch(meas)` picks each satellite's
|
|
109
|
+
* L1-band measurement. When a log carries both RANGE and RANGECMP
|
|
110
|
+
* the same epoch appears twice — keep one epoch per `timeMs`.
|
|
111
|
+
* - RTCM MSM: for each satellite/signal cell build
|
|
112
|
+
* `{ code, pr: pseudorangeM, cp: phaserangeM / lambda, lockTimeMs }`
|
|
113
|
+
* (MSM phaseranges are metres; divide by the carrier wavelength to
|
|
114
|
+
* get cycles) and pass a `prn → measurement` map for the epoch.
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
/** One satellite's measurement on one signal, for one epoch. */
|
|
118
|
+
interface RtkMeasurement {
|
|
119
|
+
/** RINEX band+attribute, e.g. "1C", "2I". */
|
|
120
|
+
code: string;
|
|
121
|
+
/** Pseudorange (m). */
|
|
122
|
+
pr: number | null;
|
|
123
|
+
/** Carrier phase (cycles, RINEX sign), optional — DGNSS needs none. */
|
|
124
|
+
cp?: number | null;
|
|
125
|
+
/** Continuous lock time (ms) for cycle-slip detection, optional. */
|
|
126
|
+
lockTimeMs?: number;
|
|
127
|
+
/** GLONASS frequency channel k (−7…+6), for FDMA wavelengths. */
|
|
128
|
+
gloChannel?: number | null;
|
|
129
|
+
}
|
|
130
|
+
/** One epoch of measurements: PRN → measurement (one signal per PRN). */
|
|
131
|
+
type RtkEpochMeasurements = ReadonlyMap<string, RtkMeasurement>;
|
|
132
|
+
/** Decoder-shaped observation (structurally `NovatelMeasurement`). */
|
|
133
|
+
interface RawObservation {
|
|
134
|
+
prn: string;
|
|
135
|
+
code: string;
|
|
136
|
+
pr: number | null;
|
|
137
|
+
cp?: number | null;
|
|
138
|
+
lockTimeS?: number;
|
|
139
|
+
gloChannel?: number | null;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Adapt one epoch of decoder observations (e.g.
|
|
143
|
+
* `parseNovatelRange(...).epochs[i].meas`) to the RTK input map:
|
|
144
|
+
* keeps each satellite's preferred L1-band code measurement with a
|
|
145
|
+
* valid pseudorange, drops unsupported systems (SBAS, NavIC).
|
|
146
|
+
*/
|
|
147
|
+
declare function toRtkEpoch(meas: readonly RawObservation[]): Map<string, RtkMeasurement>;
|
|
148
|
+
/** Ephemeris source: a per-PRN map, or a list searched per epoch. */
|
|
149
|
+
type EphemerisSource = ReadonlyMap<string, Ephemeris> | readonly Ephemeris[];
|
|
150
|
+
interface DgnssOptions {
|
|
151
|
+
/** Elevation mask at the base, degrees. Default 10. */
|
|
152
|
+
elevationMaskDeg?: number;
|
|
153
|
+
/** Model the differential troposphere. Default true. */
|
|
154
|
+
troposphere?: boolean;
|
|
155
|
+
/** Maximum Gauss-Newton iterations. Default 15. */
|
|
156
|
+
maxIterations?: number;
|
|
157
|
+
/** Convergence threshold on the position update (m). Default 1e-4. */
|
|
158
|
+
convergenceM?: number;
|
|
159
|
+
/** Zenith code sigma per receiver (m). Default 0.3. */
|
|
160
|
+
codeSigmaM?: number;
|
|
161
|
+
/** Worst-first DD residual rejection threshold (m). Default 20. */
|
|
162
|
+
rejectThresholdM?: number;
|
|
163
|
+
/** Initial rover position; defaults to the base position. */
|
|
164
|
+
initialPosition?: [number, number, number];
|
|
165
|
+
}
|
|
166
|
+
interface DgnssSolution {
|
|
167
|
+
/** Rover position, ECEF metres. */
|
|
168
|
+
position: [number, number, number];
|
|
169
|
+
/** Baseline rover − base, ECEF metres. */
|
|
170
|
+
baseline: [number, number, number];
|
|
171
|
+
/** Formal position sigmas from the LSQ covariance, ECEF metres. */
|
|
172
|
+
sigmas: [number, number, number];
|
|
173
|
+
/** Satellites used (including references). */
|
|
174
|
+
usedSatellites: string[];
|
|
175
|
+
/** Reference satellite per signal group. */
|
|
176
|
+
refSatellites: Record<string, string>;
|
|
177
|
+
/** Post-fit DD residual (m) per non-reference PRN. */
|
|
178
|
+
residuals: Record<string, number>;
|
|
179
|
+
/** Satellites rejected by the residual screen. */
|
|
180
|
+
rejectedSatellites: string[];
|
|
181
|
+
nSats: number;
|
|
182
|
+
iterations: number;
|
|
183
|
+
converged: boolean;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Solve the rover position from one epoch of DD pseudoranges.
|
|
187
|
+
*
|
|
188
|
+
* @param rover Rover measurements (PRN → measurement).
|
|
189
|
+
* @param base Base measurements (PRN → measurement, same codes).
|
|
190
|
+
* @param basePos Known base position, ECEF metres.
|
|
191
|
+
* @param ephemerides Broadcast ephemerides (per-PRN map, or a list
|
|
192
|
+
* searched by closest epoch — pass `parseNovatelNav(...).ephemerides`
|
|
193
|
+
* or `parseNavFile(...).ephemerides` directly).
|
|
194
|
+
* @param timeMs Receiver epoch, GPS-scale milliseconds.
|
|
195
|
+
*/
|
|
196
|
+
declare function solveDgnss(rover: RtkEpochMeasurements, base: RtkEpochMeasurements, basePos: readonly [number, number, number], ephemerides: EphemerisSource, timeMs: number, opts?: DgnssOptions): DgnssSolution | null;
|
|
197
|
+
interface RtkFloatOptions {
|
|
198
|
+
/**
|
|
199
|
+
* 'static': the rover position is a constant state (random walk
|
|
200
|
+
* `processNoisePosM`·√s, default 0). 'kinematic': the position is
|
|
201
|
+
* re-initialised each epoch with `kinematicSigmaM` uncertainty
|
|
202
|
+
* (no velocity states at stage 1 — RTKLIB's default kinematic
|
|
203
|
+
* handling without doppler). Default 'kinematic'.
|
|
204
|
+
*/
|
|
205
|
+
mode?: 'static' | 'kinematic';
|
|
206
|
+
/** Elevation mask at the base, degrees. Default 10. */
|
|
207
|
+
elevationMaskDeg?: number;
|
|
208
|
+
/** Model the differential troposphere. Default true. */
|
|
209
|
+
troposphere?: boolean;
|
|
210
|
+
/** Zenith code sigma per receiver (m). Default 0.3. */
|
|
211
|
+
codeSigmaM?: number;
|
|
212
|
+
/** Zenith phase sigma per receiver (m). Default 0.003. */
|
|
213
|
+
phaseSigmaM?: number;
|
|
214
|
+
/** Static-mode position random walk (m/√s). Default 0. */
|
|
215
|
+
processNoisePosM?: number;
|
|
216
|
+
/** Kinematic per-epoch position reset sigma (m). Default 30. */
|
|
217
|
+
kinematicSigmaM?: number;
|
|
218
|
+
/** Ambiguity random walk (cycles/√s). Default 1e-4. */
|
|
219
|
+
ambProcessNoiseCycles?: number;
|
|
220
|
+
/** New float ambiguity sigma (cycles). Default 30. */
|
|
221
|
+
ambInitSigmaCycles?: number;
|
|
222
|
+
/** Drop ambiguity states unseen for this long (ms). Default 10000. */
|
|
223
|
+
maxGapMs?: number;
|
|
224
|
+
/** DD code innovation gate (m): drop the satellite. Default 30. */
|
|
225
|
+
codeGateM?: number;
|
|
226
|
+
/**
|
|
227
|
+
* Phase-minus-code innovation divergence gate (m): treat as an
|
|
228
|
+
* undetected cycle slip and re-initialise the ambiguity. Default 5.
|
|
229
|
+
*/
|
|
230
|
+
slipGateM?: number;
|
|
231
|
+
/** Measurement-update relinearisations (IEKF). Default 2. */
|
|
232
|
+
updateIterations?: number;
|
|
233
|
+
/**
|
|
234
|
+
* Integer ambiguity resolution: 'off' keeps the stage-1 float-only
|
|
235
|
+
* behaviour; 'instant' attempts a per-epoch LAMBDA fix after each
|
|
236
|
+
* float update (no state feedback — RTKLIB `armode=instantaneous`).
|
|
237
|
+
* Default 'off'.
|
|
238
|
+
*/
|
|
239
|
+
ambiguityResolution?: 'off' | 'instant';
|
|
240
|
+
/** Ratio-test acceptance threshold (s₂/s₁). Default 3.0 (RTKLIB). */
|
|
241
|
+
ratioThreshold?: number;
|
|
242
|
+
/**
|
|
243
|
+
* Additional elevation mask (deg) for ambiguities entering the fix
|
|
244
|
+
* (RTKLIB `elmaskar`); 0 = use every measured ambiguity. Default 0.
|
|
245
|
+
*/
|
|
246
|
+
arElevationMaskDeg?: number;
|
|
247
|
+
/**
|
|
248
|
+
* Include GLONASS FDMA ambiguities in the integer fix. Only sound
|
|
249
|
+
* across same-model receivers (differential IFB). Default false.
|
|
250
|
+
*/
|
|
251
|
+
glonassAr?: boolean;
|
|
252
|
+
/**
|
|
253
|
+
* Partial fixing: when the full ambiguity set fails the ratio test,
|
|
254
|
+
* drop the lowest-elevation ambiguity and retry (elevation-ordered
|
|
255
|
+
* subset walk), never below half the candidates. Default true.
|
|
256
|
+
*/
|
|
257
|
+
partialFixing?: boolean;
|
|
258
|
+
/** Minimum DD ambiguities to attempt/accept a fix. Default 4. */
|
|
259
|
+
minFixAmbiguities?: number;
|
|
260
|
+
}
|
|
261
|
+
interface RtkFloatSolution {
|
|
262
|
+
/** Epoch, GPS-scale milliseconds. */
|
|
263
|
+
timeMs: number;
|
|
264
|
+
/**
|
|
265
|
+
* Rover position, ECEF metres — the ambiguity-fixed position when
|
|
266
|
+
* `status === 'fixed'`, the float filter position otherwise.
|
|
267
|
+
*/
|
|
268
|
+
position: [number, number, number];
|
|
269
|
+
/** Float baseline rover − base, ECEF metres (always the float). */
|
|
270
|
+
floatBaseline: [number, number, number];
|
|
271
|
+
/**
|
|
272
|
+
* Solution quality: 'fixed' when LAMBDA passed the ratio test,
|
|
273
|
+
* 'float' when carrier ambiguities contributed but no (accepted)
|
|
274
|
+
* fix, 'dgnss' when the epoch was updated from code DDs only.
|
|
275
|
+
*/
|
|
276
|
+
status: 'fixed' | 'float' | 'dgnss';
|
|
277
|
+
/** Satellites contributing DD rows this epoch (incl. references). */
|
|
278
|
+
nSats: number;
|
|
279
|
+
/**
|
|
280
|
+
* Ambiguity-validation ratio (s₂/s₁ of the LAMBDA candidates,
|
|
281
|
+
* capped at 999.9 as RTKLIB): the accepted subset's ratio when
|
|
282
|
+
* fixed, the full-set ratio otherwise. Undefined when no fix was
|
|
283
|
+
* attempted (`ambiguityResolution: 'off'` or no eligible set).
|
|
284
|
+
*/
|
|
285
|
+
ratio?: number;
|
|
286
|
+
/** Number of integer-fixed DD ambiguities (when `status==='fixed'`). */
|
|
287
|
+
nFixed?: number;
|
|
288
|
+
/** Fixed baseline rover − base, ECEF metres (when fixed). */
|
|
289
|
+
fixedBaseline?: [number, number, number];
|
|
290
|
+
/** Integer DD ambiguity (cycles) per fixed PRN (when fixed). */
|
|
291
|
+
fixedAmbiguities?: Record<string, number>;
|
|
292
|
+
/** Formal position sigmas (filter covariance), ECEF metres. */
|
|
293
|
+
sigmas: [number, number, number];
|
|
294
|
+
/** Float DD ambiguity (cycles) per non-reference PRN. */
|
|
295
|
+
ambiguities: Record<string, number>;
|
|
296
|
+
/** Reference satellite per signal group. */
|
|
297
|
+
refSatellites: Record<string, string>;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Epoch-by-epoch float RTK filter: EKF states = rover position
|
|
301
|
+
* (static or kinematic) + one float DD ambiguity (cycles) per
|
|
302
|
+
* satellite/signal. Cycle slips are handled via lock-time regressions
|
|
303
|
+
* (LLI-style resets) and a phase/code divergence gate; reference-
|
|
304
|
+
* satellite switches re-map the DD ambiguity states algebraically
|
|
305
|
+
* (N_i' = N_i − N_r', old reference gains −N_r') so no information is
|
|
306
|
+
* lost. Feed epochs in time order via `process`.
|
|
307
|
+
*/
|
|
308
|
+
declare class RtkFloatEngine {
|
|
309
|
+
private readonly basePos;
|
|
310
|
+
private readonly ephemerides;
|
|
311
|
+
private readonly o;
|
|
312
|
+
/** State vector [x, y, z, N₁…] and covariance; null before init. */
|
|
313
|
+
private x;
|
|
314
|
+
private P;
|
|
315
|
+
private amb;
|
|
316
|
+
private refs;
|
|
317
|
+
private track;
|
|
318
|
+
private lastMs;
|
|
319
|
+
constructor(basePos: readonly [number, number, number], ephemerides: EphemerisSource, opts?: RtkFloatOptions);
|
|
320
|
+
/** Clear all filter state (position, ambiguities, lock history). */
|
|
321
|
+
reset(): void;
|
|
322
|
+
private ambIndex;
|
|
323
|
+
/** Remove one ambiguity state (row/col) from x and P. */
|
|
324
|
+
private dropAmb;
|
|
325
|
+
/** Append an ambiguity state with the given value and variance. */
|
|
326
|
+
private addAmb;
|
|
327
|
+
/**
|
|
328
|
+
* Re-map a group's DD ambiguities from the old reference r to the
|
|
329
|
+
* new reference r' (which must hold a state): N_i' = N_i − N_r' and
|
|
330
|
+
* the old reference's slot becomes N_r = −N_r'. Exact linear
|
|
331
|
+
* transform of state and covariance (P' = T P Tᵀ).
|
|
332
|
+
*/
|
|
333
|
+
private retarget;
|
|
334
|
+
/**
|
|
335
|
+
* Try to fix the ambiguity subset `idxs` (indices into `this.amb`):
|
|
336
|
+
* extract the float subvector b and covariance Q_b from the filter,
|
|
337
|
+
* run MLAMBDA and, without touching the filter state, condition the
|
|
338
|
+
* position on the best integer candidate,
|
|
339
|
+
* xa = x − P_xb·Q_b⁻¹·(b − ǎ)
|
|
340
|
+
* (RTKLIB rtkpos.c `resamb_LAMBDA`). Returns null when Q_b is not
|
|
341
|
+
* positive definite / invertible or the search fails; ratio
|
|
342
|
+
* validation is the caller's job.
|
|
343
|
+
*/
|
|
344
|
+
private tryFix;
|
|
345
|
+
/**
|
|
346
|
+
* Process one synchronized epoch (same nominal `timeMs` for both
|
|
347
|
+
* receivers). Returns null until a first position can be
|
|
348
|
+
* initialised (via an internal DGNSS solve) or when the epoch has
|
|
349
|
+
* fewer than 3 usable DD rows.
|
|
350
|
+
*/
|
|
351
|
+
process(rover: RtkEpochMeasurements, base: RtkEpochMeasurements, timeMs: number): RtkFloatSolution | null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Integer ambiguity resolution: LAMBDA decorrelation + MLAMBDA
|
|
356
|
+
* integer least-squares search.
|
|
357
|
+
*
|
|
358
|
+
* Port of RTKLIB's `lambda.c` (demo5 / rtklibexplorer fork,
|
|
359
|
+
* src/lambda.c, Copyright (c) 2007-2008 T. Takasu, BSD-2-Clause),
|
|
360
|
+
* cross-checked against the reference papers where the C comments are
|
|
361
|
+
* thin:
|
|
362
|
+
*
|
|
363
|
+
* - [1] P.J.G. Teunissen, "The least-squares ambiguity decorrelation
|
|
364
|
+
* adjustment: a method for fast GPS ambiguity estimation",
|
|
365
|
+
* J. Geodesy 70, 65-82, 1995.
|
|
366
|
+
* - [2] X.-W. Chang, X. Yang, T. Zhou, "MLAMBDA: A modified LAMBDA
|
|
367
|
+
* method for integer least-squares estimation", J. Geodesy 79,
|
|
368
|
+
* 552-565, 2005.
|
|
369
|
+
*
|
|
370
|
+
* Pipeline (identical to RTKLIB's `lambda()`):
|
|
371
|
+
* 1. LᵀDL factorization of the float covariance, Q = Lᵀ·diag(D)·L
|
|
372
|
+
* with L unit lower triangular (RTKLIB's `LD`).
|
|
373
|
+
* 2. LAMBDA reduction [1]: integer Gauss transformations interleaved
|
|
374
|
+
* with symmetric permutations until the transformed D is
|
|
375
|
+
* (approximately) descending — producing a unimodular Z with
|
|
376
|
+
* Qz = ZᵀQZ = Lᵀ·diag(D)·L far better conditioned than Q.
|
|
377
|
+
* 3. MLAMBDA search [2] in the decorrelated space: depth-first
|
|
378
|
+
* enumeration over a shrinking ellipsoid, keeping the `m` smallest
|
|
379
|
+
* residual quadratic forms (z−ž)ᵀQz⁻¹(z−ž).
|
|
380
|
+
* 4. Back-transform the candidates to the original space, a = Z⁻ᵀ·ž
|
|
381
|
+
* (RTKLIB solves ZᵀF = E; Z is unimodular so F is exactly integer).
|
|
382
|
+
*
|
|
383
|
+
* Deviations from lambda.c:
|
|
384
|
+
* - matrices are `Float64Array` in the same column-major layout, with
|
|
385
|
+
* allocation-failure paths dropped (JS throws instead),
|
|
386
|
+
* - failures (non-positive-definite Q, search loop overflow) return
|
|
387
|
+
* `null` instead of a status code + stderr message,
|
|
388
|
+
* - back-transformed candidates are rounded to the nearest integer:
|
|
389
|
+
* F = Z⁻ᵀ·ž is exactly integer in ℤ arithmetic and the rounding only
|
|
390
|
+
* removes the ~1e-12 fuzz of the floating-point triangular solve,
|
|
391
|
+
* - the ratio s₂/s₁ used by the RTKLIB caller (`rtkpos.c`) is returned
|
|
392
|
+
* alongside the candidates (∞ when s₁ = 0, i.e. noise-free input).
|
|
393
|
+
*/
|
|
394
|
+
/** Result of an integer least-squares search. */
|
|
395
|
+
interface LambdaResult {
|
|
396
|
+
/**
|
|
397
|
+
* The `m` best integer candidate vectors (length n each), sorted by
|
|
398
|
+
* ascending residual quadratic form; `candidates[0]` is the integer
|
|
399
|
+
* least-squares minimizer.
|
|
400
|
+
*/
|
|
401
|
+
candidates: Float64Array[];
|
|
402
|
+
/**
|
|
403
|
+
* Residual quadratic forms sᵢ = (a−ǎᵢ)ᵀQ⁻¹(a−ǎᵢ) matching
|
|
404
|
+
* `candidates`, ascending.
|
|
405
|
+
*/
|
|
406
|
+
residuals: Float64Array;
|
|
407
|
+
/**
|
|
408
|
+
* Ratio-test statistic s₂/s₁ (second-best over best); `Infinity`
|
|
409
|
+
* when the best residual is exactly 0 (noise-free input). Callers
|
|
410
|
+
* typically cap it (RTKLIB uses 999.9) before thresholding.
|
|
411
|
+
*/
|
|
412
|
+
ratio: number;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* LAMBDA/MLAMBDA integer least-squares estimation (RTKLIB `lambda`):
|
|
416
|
+
* decorrelate the float ambiguities, search the `m` best integer
|
|
417
|
+
* vectors and back-transform them to the original space.
|
|
418
|
+
*
|
|
419
|
+
* @param a Float ambiguities (length ≥ n).
|
|
420
|
+
* @param Q Their covariance, n×n (symmetric — row/column-major agree).
|
|
421
|
+
* @param n Number of ambiguities.
|
|
422
|
+
* @param m Number of candidates to return (default 2: best +
|
|
423
|
+
* second-best, what the ratio test needs).
|
|
424
|
+
* @returns Candidates + residuals + ratio, or null when Q is not
|
|
425
|
+
* positive definite or the search does not terminate.
|
|
426
|
+
*/
|
|
427
|
+
declare function lambdaSearch(a: Float64Array, Q: Float64Array, n: number, m?: number): LambdaResult | null;
|
|
428
|
+
/**
|
|
429
|
+
* LAMBDA reduction only (RTKLIB `lambda_reduction`): the unimodular
|
|
430
|
+
* decorrelation matrix Z (n×n, column-major) such that Qz = ZᵀQZ is
|
|
431
|
+
* (near-)diagonally dominant. Exposed for diagnostics/tests; returns
|
|
432
|
+
* null when Q is not positive definite.
|
|
433
|
+
*/
|
|
434
|
+
declare function lambdaReduction(Q: Float64Array, n: number): Float64Array | null;
|
|
435
|
+
|
|
37
436
|
/**
|
|
38
437
|
* Single-point positioning (SPP) from pseudoranges and broadcast
|
|
39
438
|
* ephemerides.
|
|
@@ -114,4 +513,4 @@ declare function ionoFree(p1: number, p2: number, f1: number, f2: number): numbe
|
|
|
114
513
|
*/
|
|
115
514
|
declare function solveSpp(pseudoranges: Map<string, number>, ephemerides: Map<string, Ephemeris>, timeMs: number, opts?: SppOptions): SppSolution | null;
|
|
116
515
|
|
|
117
|
-
export { type KlobucharCoeffs, type SppOptions, type SppSolution, ionoFree, klobucharDelay, satClockCorrection, solveSpp };
|
|
516
|
+
export { type DgnssOptions, type DgnssSolution, type EphemerisSource, type KlobucharCoeffs, type LambdaResult, type RawObservation, type RtkEpochMeasurements, RtkFloatEngine, type RtkFloatOptions, type RtkFloatSolution, type RtkMeasurement, type SppOptions, type SppSolution, ionoFree, klobucharDelay, lambdaReduction, lambdaSearch, satClockCorrection, solveDgnss, solveSpp, toRtkEpoch };
|
package/dist/positioning.js
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
|
+
RtkFloatEngine,
|
|
2
3
|
ionoFree,
|
|
3
4
|
klobucharDelay,
|
|
5
|
+
lambdaReduction,
|
|
6
|
+
lambdaSearch,
|
|
4
7
|
satClockCorrection,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
solveDgnss,
|
|
9
|
+
solveSpp,
|
|
10
|
+
toRtkEpoch
|
|
11
|
+
} from "./chunk-T2ZMNRCY.js";
|
|
12
|
+
import "./chunk-MHGNKSSH.js";
|
|
8
13
|
import "./chunk-37QNKGTC.js";
|
|
9
14
|
import "./chunk-6FAL6P4G.js";
|
|
10
15
|
import "./chunk-HVXYFUCB.js";
|
|
11
16
|
import "./chunk-LEEU5OIO.js";
|
|
12
17
|
import "./chunk-FIEWO4J4.js";
|
|
13
18
|
export {
|
|
19
|
+
RtkFloatEngine,
|
|
14
20
|
ionoFree,
|
|
15
21
|
klobucharDelay,
|
|
22
|
+
lambdaReduction,
|
|
23
|
+
lambdaSearch,
|
|
16
24
|
satClockCorrection,
|
|
17
|
-
|
|
25
|
+
solveDgnss,
|
|
26
|
+
solveSpp,
|
|
27
|
+
toRtkEpoch
|
|
18
28
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnss-js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.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,
|