gnss-js 1.10.0 → 1.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.
package/dist/index.d.cts CHANGED
@@ -4,14 +4,14 @@ export { A as ARC_GAP_FACTOR, B as BAND_LABELS, a as BDS_SATELLITES, b as BdsSat
4
4
  export { Scale, getBdsTime, getDateFromBdsTime, getDateFromDayOfWeek, getDateFromDayOfYear, getDateFromGalTime, getDateFromGloN, getDateFromGpsData, getDateFromGpsTime, getDateFromHourCode, getDateFromJulianDate, getDateFromMJD, getDateFromMJD2000, getDateFromRINEX, getDateFromTai, getDateFromTimeOfDay, getDateFromTt, getDateFromUnixTime, getDateFromUtc, getDayOfWeek, getDayOfYear, getGalTime, getGloN4, getGloNA, getGpsLeap, getGpsTime, getHourCode, getHoursFromTimeDifference, getJulianDate, getLeap, getMJD, getMJD2000, getMinutesFromTimeDifference, getNtpTime, getRINEX, getSecondsFromTimeDifference, getTaiDate, getTimeDifference, getTimeDifferenceFromDays, getTimeDifferenceFromHours, getTimeDifferenceFromMinutes, getTimeDifferenceFromObject, getTimeDifferenceFromSeconds, getTimeOfDay, getTimeOfWeek, getTotalDaysFromTimeDifference, getTtDate, getUnixTime, getUtcDate, getWeekNumber, getWeekOfYear } from './time.cjs';
5
5
  export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty } from './coordinates.cjs';
6
6
  export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer, b as getEnuDifference } from './ecef-CF0uAysr.cjs';
7
- export { CrxField, DiffState, EMPTY_WARNINGS, IonexGrid, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, parseIonex, writeRinexNav } from './rinex.cjs';
7
+ export { CompactEpoch, CrxField, DiffState, EMPTY_WARNINGS, IonexGrid, RinexWarning, RinexWarnings, Sp3File, Sp3Sample, WarningAccumulator, WarningSeverity, createGzipLineSink, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, parseIonex, parseSp3, sp3Position, stationHeaderLines, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexObsBlob } from './rinex.cjs';
8
8
  export { E as EpochSummary, P as ParseOptions, R as RinexHeader, 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';
9
9
  export { E as Ephemeris, G as GlonassEphemeris, K as KeplerEphemeris, N as NavHeader, a as NavResult, p as parseNavFile } from './nav-BAI1a9vK.cjs';
10
10
  export { B as BitReader, E as EphemerisInfo, R as Rtcm3DebugHandler, a as Rtcm3Decoder, b as Rtcm3Frame, d as decodeEphemeris, r as readString, c as reportDecodeError, s as setRtcm3DebugHandler } from './ephemeris-Ohl6NAfw.cjs';
11
11
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.cjs';
12
- export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, selectEphemeris } from './orbit.cjs';
12
+ export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris } from './orbit.cjs';
13
13
  export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.cjs';
14
- export { SppOptions, SppSolution, ionoFree, satClockCorrection, solveSpp } from './positioning.cjs';
14
+ export { KlobucharCoeffs, SppOptions, SppSolution, ionoFree, klobucharDelay, satClockCorrection, solveSpp } from './positioning.cjs';
15
15
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.cjs';
16
16
  export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoDcbResult, IonoPoint, IonoRatePoint, IonoRateSeries, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, SatDcbMap, analyzeQuality, applyIonoDcb, computeIonoRate, detrendIonoArcs, parseSinexBiasDcb } from './analysis.cjs';
17
17
  export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.cjs';
package/dist/index.d.ts CHANGED
@@ -4,14 +4,14 @@ export { A as ARC_GAP_FACTOR, B as BAND_LABELS, a as BDS_SATELLITES, b as BdsSat
4
4
  export { Scale, getBdsTime, getDateFromBdsTime, getDateFromDayOfWeek, getDateFromDayOfYear, getDateFromGalTime, getDateFromGloN, getDateFromGpsData, getDateFromGpsTime, getDateFromHourCode, getDateFromJulianDate, getDateFromMJD, getDateFromMJD2000, getDateFromRINEX, getDateFromTai, getDateFromTimeOfDay, getDateFromTt, getDateFromUnixTime, getDateFromUtc, getDayOfWeek, getDayOfYear, getGalTime, getGloN4, getGloNA, getGpsLeap, getGpsTime, getHourCode, getHoursFromTimeDifference, getJulianDate, getLeap, getMJD, getMJD2000, getMinutesFromTimeDifference, getNtpTime, getRINEX, getSecondsFromTimeDifference, getTaiDate, getTimeDifference, getTimeDifferenceFromDays, getTimeDifferenceFromHours, getTimeDifferenceFromMinutes, getTimeDifferenceFromObject, getTimeDifferenceFromSeconds, getTimeOfDay, getTimeOfWeek, getTotalDaysFromTimeDifference, getTtDate, getUnixTime, getUtcDate, getWeekNumber, getWeekOfYear } from './time.js';
5
5
  export { deg2dms, deg2rad, euclidean3D, geodeticToGeohash, geodeticToMaidenhead, geodeticToUtm, greatCircleMidpoint, horizonDistance, rad2deg, rhumbLine, vincenty } from './coordinates.js';
6
6
  export { c as clampUnit, e as ecefToGeodetic, g as geodeticToEcef, a as getAer, b as getEnuDifference } from './ecef-CF0uAysr.js';
7
- export { CrxField, DiffState, EMPTY_WARNINGS, IonexGrid, RinexWarning, RinexWarnings, WarningAccumulator, WarningSeverity, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, parseIonex, writeRinexNav } from './rinex.js';
7
+ export { CompactEpoch, CrxField, DiffState, EMPTY_WARNINGS, IonexGrid, RinexWarning, RinexWarnings, Sp3File, Sp3Sample, WarningAccumulator, WarningSeverity, createGzipLineSink, crxDecompress, crxRepair, fmtF, hdrLine, padL, padR, parseCrxDataLine, parseIonex, parseSp3, sp3Position, stationHeaderLines, writeModernObsBlob, writeRinex2ObsBlob, writeRinex4ObsBlob, writeRinexNav, writeRinexObsBlob } from './rinex.js';
8
8
  export { E as EpochSummary, P as ParseOptions, R as RinexHeader, 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.js';
9
9
  export { E as Ephemeris, G as GlonassEphemeris, K as KeplerEphemeris, N as NavHeader, a as NavResult, p as parseNavFile } from './nav-BAI1a9vK.js';
10
10
  export { B as BitReader, E as EphemerisInfo, R as Rtcm3DebugHandler, a as Rtcm3Decoder, b as Rtcm3Frame, d as decodeEphemeris, r as readString, c as reportDecodeError, s as setRtcm3DebugHandler } from './ephemeris-Ohl6NAfw.js';
11
11
  export { MessageTypeStats, MsmEpoch, MsmSatObs, MsmSignal, RTCM3_MESSAGE_NAMES, SatCn0, SignalCn0, StationMeta, StreamStats, createStationMeta, createStreamStats, decodeMsmFull, msmEpochToDate, resetGloFreqCache, rtcm3Constellation, setGloFreqNumber, updateStationMeta, updateStreamStats } from './rtcm3.js';
12
- export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, selectEphemeris } from './orbit.js';
12
+ export { AllPositionsData, DopValues, EpochSkyData, SatAzEl, SatPoint, SatPosition, VisibilityPass, VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris } from './orbit.js';
13
13
  export { Helmert14, REFERENCE_FRAMES, ReferenceFrame, applyHelmert, dateToEpoch, transformFrame } from './frames.js';
14
- export { SppOptions, SppSolution, ionoFree, satClockCorrection, solveSpp } from './positioning.js';
14
+ export { KlobucharCoeffs, SppOptions, SppSolution, ionoFree, klobucharDelay, satClockCorrection, solveSpp } from './positioning.js';
15
15
  export { NtripCaster, NtripConnectionInfo, NtripNetwork, NtripStream, NtripStreamConnection, NtripVersion, Sourcetable, SourcetableEntry, connectToMountpoint, fetchSourcetable, parseSourcetable } from './ntrip.js';
16
16
  export { CompleteSatSignal, CompleteSignalStats, CompletenessAccumulator, CompletenessResult, CycleSlipAccumulator, CycleSlipEvent, CycleSlipResult, CycleSlipSignalStats, IonoAccumulator, IonoDcbResult, IonoPoint, IonoRatePoint, IonoRateSeries, IonoResult, IonoSeries, MultipathAccumulator, MultipathPoint, MultipathResult, MultipathSeries, MultipathSignalStat, QualityResult, SatDcbMap, analyzeQuality, applyIonoDcb, computeIonoRate, detrendIonoArcs, parseSinexBiasDcb } from './analysis.js';
17
17
  export { AntennaEntry, AntexFile, FrequencyData, frequencyLabel, parseAntex } from './antex.js';
package/dist/index.js CHANGED
@@ -100,23 +100,26 @@ import {
100
100
  } from "./chunk-4KOXMHUW.js";
101
101
  import {
102
102
  ionoFree,
103
+ klobucharDelay,
103
104
  satClockCorrection,
104
105
  solveSpp
105
- } from "./chunk-ZTFKRZMM.js";
106
+ } from "./chunk-LKFSXVIZ.js";
106
107
  import {
107
108
  computeAllPositions,
108
109
  computeDop,
109
110
  computeLiveSkyPositions,
110
111
  computeSatPosition,
111
112
  computeVisibility,
113
+ dopplerHz,
112
114
  ecefToAzEl,
113
115
  ephInfoToEphemeris,
114
116
  glonassPosition,
115
117
  keplerPosition,
116
118
  maskRadForAzimuth,
117
119
  navTimesFromEph,
120
+ rangeRate,
118
121
  selectEphemeris
119
- } from "./chunk-AK6LNWZX.js";
122
+ } from "./chunk-DMNFB7Q6.js";
120
123
  import {
121
124
  getBdsTime,
122
125
  getDateFromBdsTime,
@@ -169,14 +172,22 @@ import {
169
172
  import {
170
173
  EMPTY_WARNINGS,
171
174
  WarningAccumulator,
175
+ createGzipLineSink,
172
176
  fmtF,
173
177
  hdrLine,
174
178
  padL,
175
179
  padR,
176
180
  parseIonex,
177
181
  parseNavFile,
178
- writeRinexNav
179
- } from "./chunk-77VUSDNI.js";
182
+ parseSp3,
183
+ sp3Position,
184
+ stationHeaderLines,
185
+ writeModernObsBlob,
186
+ writeRinex2ObsBlob,
187
+ writeRinex4ObsBlob,
188
+ writeRinexNav,
189
+ writeRinexObsBlob
190
+ } from "./chunk-HXF7XVW7.js";
180
191
  import {
181
192
  SYSTEM_ORDER,
182
193
  crxDecompress,
@@ -351,6 +362,7 @@ export {
351
362
  computeStats,
352
363
  computeVisibility,
353
364
  connectToMountpoint,
365
+ createGzipLineSink,
354
366
  createStationMeta,
355
367
  createStreamStats,
356
368
  crxDecompress,
@@ -361,6 +373,7 @@ export {
361
373
  deg2dms,
362
374
  deg2rad,
363
375
  detrendIonoArcs,
376
+ dopplerHz,
364
377
  ecefToAzEl,
365
378
  ecefToGeodetic,
366
379
  ephInfoToEphemeris,
@@ -433,6 +446,7 @@ export {
433
446
  horizonDistance,
434
447
  ionoFree,
435
448
  keplerPosition,
449
+ klobucharDelay,
436
450
  maskRadForAzimuth,
437
451
  msmEpochToDate,
438
452
  navTimesFromEph,
@@ -447,11 +461,13 @@ export {
447
461
  parseRinexStream,
448
462
  parseSinexBiasDcb,
449
463
  parseSourcetable,
464
+ parseSp3,
450
465
  phiAltBOC,
451
466
  phiBOCc,
452
467
  phiBOCs,
453
468
  phiBPSK,
454
469
  rad2deg,
470
+ rangeRate,
455
471
  readString,
456
472
  reportDecodeError,
457
473
  resetGloFreqCache,
@@ -462,6 +478,8 @@ export {
462
478
  setGloFreqNumber,
463
479
  setRtcm3DebugHandler,
464
480
  solveSpp,
481
+ sp3Position,
482
+ stationHeaderLines,
465
483
  systemCmp,
466
484
  systemName,
467
485
  transformFrame,
@@ -469,5 +487,9 @@ export {
469
487
  updateStreamStats,
470
488
  verifyChecksum,
471
489
  vincenty,
472
- writeRinexNav
490
+ writeModernObsBlob,
491
+ writeRinex2ObsBlob,
492
+ writeRinex4ObsBlob,
493
+ writeRinexNav,
494
+ writeRinexObsBlob
473
495
  };
package/dist/orbit.cjs CHANGED
@@ -25,6 +25,7 @@ __export(orbit_exports, {
25
25
  computeLiveSkyPositions: () => computeLiveSkyPositions,
26
26
  computeSatPosition: () => computeSatPosition,
27
27
  computeVisibility: () => computeVisibility,
28
+ dopplerHz: () => dopplerHz,
28
29
  ecefToAzEl: () => ecefToAzEl,
29
30
  ecefToGeodetic: () => ecefToGeodetic,
30
31
  ephInfoToEphemeris: () => ephInfoToEphemeris,
@@ -33,6 +34,7 @@ __export(orbit_exports, {
33
34
  keplerPosition: () => keplerPosition,
34
35
  maskRadForAzimuth: () => maskRadForAzimuth,
35
36
  navTimesFromEph: () => navTimesFromEph,
37
+ rangeRate: () => rangeRate,
36
38
  selectEphemeris: () => selectEphemeris
37
39
  });
38
40
  module.exports = __toCommonJS(orbit_exports);
@@ -82,6 +84,7 @@ var START_BDS_TIME = /* @__PURE__ */ new Date("2006-01-01T00:00:14Z");
82
84
  var START_NTP_TIME = /* @__PURE__ */ new Date("1900-01-01T00:00:00Z");
83
85
 
84
86
  // src/constants/gnss.ts
87
+ var C_LIGHT = 299792458;
85
88
  var OMEGA_E = 72921151467e-15;
86
89
 
87
90
  // src/time/gnss.ts
@@ -181,6 +184,7 @@ function gmForSystem(sys) {
181
184
  if (sys === "C") return GM_BDS;
182
185
  return GM_GPS;
183
186
  }
187
+ var geoVelocityPass = false;
184
188
  function keplerPosition(eph, t) {
185
189
  const GM = gmForSystem(eph.system);
186
190
  const a = eph.sqrtA * eph.sqrtA;
@@ -208,8 +212,16 @@ function keplerPosition(eph, t) {
208
212
  const uk = phik + duk;
209
213
  const rk = a * (1 - eph.e * cosE) + drk;
210
214
  const ik = eph.i0 + dik + eph.idot * tk;
215
+ const EkDot = n / (1 - eph.e * cosE);
216
+ const vkDot = EkDot * Math.sqrt(1 - eph.e * eph.e) / (1 - eph.e * cosE);
217
+ const dukDot = 2 * vkDot * (eph.cus * cos2phi - eph.cuc * sin2phi);
218
+ const drkDot = a * eph.e * sinE * EkDot + 2 * vkDot * (eph.crs * cos2phi - eph.crc * sin2phi);
219
+ const dikDot = eph.idot + 2 * vkDot * (eph.cis * cos2phi - eph.cic * sin2phi);
220
+ const ukDot = vkDot + dukDot;
211
221
  const xp = rk * Math.cos(uk);
212
222
  const yp = rk * Math.sin(uk);
223
+ const xpDot = drkDot * Math.cos(uk) - rk * ukDot * Math.sin(uk);
224
+ const ypDot = drkDot * Math.sin(uk) + rk * ukDot * Math.cos(uk);
213
225
  const isBdsGeo = eph.system === "C" && Math.abs(eph.i0) < BDS_GEO_MAX_INCLINATION_RAD && a > BDS_GEO_MIN_SEMIMAJOR_AXIS_M;
214
226
  if (isBdsGeo) {
215
227
  const omegak2 = eph.omega0 + eph.omegaDot * tk - OMEGA_E * eph.toe;
@@ -225,23 +237,43 @@ function keplerPosition(eph, t) {
225
237
  const sinPhi = Math.sin(phi);
226
238
  const COS5 = Math.cos(-5 * Math.PI / 180);
227
239
  const SIN5 = Math.sin(-5 * Math.PI / 180);
240
+ const px = xg * cosPhi + yg * sinPhi * COS5 + zg * sinPhi * SIN5;
241
+ const py = -xg * sinPhi + yg * cosPhi * COS5 + zg * cosPhi * SIN5;
242
+ const pz = -yg * SIN5 + zg * COS5;
243
+ if (geoVelocityPass) {
244
+ return { prn: eph.prn, x: px, y: py, z: pz, vx: 0, vy: 0, vz: 0 };
245
+ }
246
+ geoVelocityPass = true;
247
+ const h = 0.5;
248
+ const pm = keplerPosition(eph, t - h);
249
+ const pp = keplerPosition(eph, t + h);
250
+ geoVelocityPass = false;
228
251
  return {
229
252
  prn: eph.prn,
230
- x: xg * cosPhi + yg * sinPhi * COS5 + zg * sinPhi * SIN5,
231
- y: -xg * sinPhi + yg * cosPhi * COS5 + zg * cosPhi * SIN5,
232
- z: -yg * SIN5 + zg * COS5
253
+ x: px,
254
+ y: py,
255
+ z: pz,
256
+ vx: (pp.x - pm.x) / (2 * h),
257
+ vy: (pp.y - pm.y) / (2 * h),
258
+ vz: (pp.z - pm.z) / (2 * h)
233
259
  };
234
260
  }
235
261
  const omegak = eph.omega0 + (eph.omegaDot - OMEGA_E) * tk - OMEGA_E * eph.toe;
262
+ const omegakDot = eph.omegaDot - OMEGA_E;
236
263
  const cosO = Math.cos(omegak);
237
264
  const sinO = Math.sin(omegak);
238
265
  const cosI = Math.cos(ik);
239
266
  const sinI = Math.sin(ik);
267
+ const x = xp * cosO - yp * cosI * sinO;
268
+ const y = xp * sinO + yp * cosI * cosO;
240
269
  return {
241
270
  prn: eph.prn,
242
- x: xp * cosO - yp * cosI * sinO,
243
- y: xp * sinO + yp * cosI * cosO,
244
- z: yp * sinI
271
+ x,
272
+ y,
273
+ z: yp * sinI,
274
+ vx: xpDot * cosO - ypDot * cosI * sinO + yp * sinI * sinO * dikDot - omegakDot * y,
275
+ vy: xpDot * sinO + ypDot * cosI * cosO - yp * sinI * cosO * dikDot + omegakDot * x,
276
+ vz: ypDot * sinI + yp * cosI * dikDot
245
277
  };
246
278
  }
247
279
  function gloDerivatives(state, acc) {
@@ -295,7 +327,25 @@ function glonassPosition(eph, tUtc) {
295
327
  if (Math.abs(remainder) > 1e-3) {
296
328
  state = rk4Step(state, acc, remainder);
297
329
  }
298
- return { prn: eph.prn, x: state[0], y: state[1], z: state[2] };
330
+ return {
331
+ prn: eph.prn,
332
+ x: state[0],
333
+ y: state[1],
334
+ z: state[2],
335
+ vx: state[3],
336
+ vy: state[4],
337
+ vz: state[5]
338
+ };
339
+ }
340
+ function rangeRate(sat, rx, rxVel = [0, 0, 0]) {
341
+ const dx = sat.x - rx[0];
342
+ const dy = sat.y - rx[1];
343
+ const dz = sat.z - rx[2];
344
+ const rho = Math.hypot(dx, dy, dz);
345
+ return ((sat.vx - rxVel[0]) * dx + (sat.vy - rxVel[1]) * dy + (sat.vz - rxVel[2]) * dz) / rho;
346
+ }
347
+ function dopplerHz(rangeRateMs, freqHz) {
348
+ return -rangeRateMs * freqHz / C_LIGHT;
299
349
  }
300
350
  function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
301
351
  const [lat, lon] = ecefToGeodetic(rxX, rxY, rxZ);
@@ -703,6 +753,7 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
703
753
  computeLiveSkyPositions,
704
754
  computeSatPosition,
705
755
  computeVisibility,
756
+ dopplerHz,
706
757
  ecefToAzEl,
707
758
  ecefToGeodetic,
708
759
  ephInfoToEphemeris,
@@ -711,5 +762,6 @@ function computeVisibility(ephemerides, rxPos, startMs, endMs, stepSec = 300, el
711
762
  keplerPosition,
712
763
  maskRadForAzimuth,
713
764
  navTimesFromEph,
765
+ rangeRate,
714
766
  selectEphemeris
715
767
  });
package/dist/orbit.d.cts CHANGED
@@ -12,6 +12,9 @@ interface SatPosition {
12
12
  x: number;
13
13
  y: number;
14
14
  z: number;
15
+ vx: number;
16
+ vy: number;
17
+ vz: number;
15
18
  }
16
19
  interface SatAzEl {
17
20
  prn: string;
@@ -28,7 +31,7 @@ interface DopValues {
28
31
  vdop: number;
29
32
  }
30
33
  /**
31
- * Compute satellite ECEF position from Keplerian ephemeris.
34
+ * Compute satellite ECEF position and velocity from Keplerian ephemeris.
32
35
  * @param eph Broadcast ephemeris
33
36
  * @param t GPS time of week in seconds
34
37
  */
@@ -39,6 +42,13 @@ declare function keplerPosition(eph: KeplerEphemeris, t: number): SatPosition;
39
42
  * @param tUtc Target time as UTC seconds since Unix epoch
40
43
  */
41
44
  declare function glonassPosition(eph: GlonassEphemeris, tUtc: number): SatPosition;
45
+ /**
46
+ * Geometric range rate (m/s, positive = receding) between a satellite
47
+ * with ECEF velocity and a receiver (static by default).
48
+ */
49
+ declare function rangeRate(sat: SatPosition, rx: [number, number, number], rxVel?: [number, number, number]): number;
50
+ /** Predicted carrier Doppler (Hz) from a range rate at frequency f. */
51
+ declare function dopplerHz(rangeRateMs: number, freqHz: number): number;
42
52
  /**
43
53
  * Compute azimuth and elevation from receiver ECEF to satellite ECEF.
44
54
  * @param rxX Receiver X in meters (ECEF)
@@ -176,4 +186,4 @@ declare function maskRadForAzimuth(maskDeg: number | number[]): (azRad: number)
176
186
  */
177
187
  declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number | number[]): VisibilityResult;
178
188
 
179
- export { type AllPositionsData, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, selectEphemeris };
189
+ export { type AllPositionsData, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris };
package/dist/orbit.d.ts CHANGED
@@ -12,6 +12,9 @@ interface SatPosition {
12
12
  x: number;
13
13
  y: number;
14
14
  z: number;
15
+ vx: number;
16
+ vy: number;
17
+ vz: number;
15
18
  }
16
19
  interface SatAzEl {
17
20
  prn: string;
@@ -28,7 +31,7 @@ interface DopValues {
28
31
  vdop: number;
29
32
  }
30
33
  /**
31
- * Compute satellite ECEF position from Keplerian ephemeris.
34
+ * Compute satellite ECEF position and velocity from Keplerian ephemeris.
32
35
  * @param eph Broadcast ephemeris
33
36
  * @param t GPS time of week in seconds
34
37
  */
@@ -39,6 +42,13 @@ declare function keplerPosition(eph: KeplerEphemeris, t: number): SatPosition;
39
42
  * @param tUtc Target time as UTC seconds since Unix epoch
40
43
  */
41
44
  declare function glonassPosition(eph: GlonassEphemeris, tUtc: number): SatPosition;
45
+ /**
46
+ * Geometric range rate (m/s, positive = receding) between a satellite
47
+ * with ECEF velocity and a receiver (static by default).
48
+ */
49
+ declare function rangeRate(sat: SatPosition, rx: [number, number, number], rxVel?: [number, number, number]): number;
50
+ /** Predicted carrier Doppler (Hz) from a range rate at frequency f. */
51
+ declare function dopplerHz(rangeRateMs: number, freqHz: number): number;
42
52
  /**
43
53
  * Compute azimuth and elevation from receiver ECEF to satellite ECEF.
44
54
  * @param rxX Receiver X in meters (ECEF)
@@ -176,4 +186,4 @@ declare function maskRadForAzimuth(maskDeg: number | number[]): (azRad: number)
176
186
  */
177
187
  declare function computeVisibility(ephemerides: Ephemeris[], rxPos: [number, number, number], startMs: number, endMs: number, stepSec?: number, elevationMaskDeg?: number | number[]): VisibilityResult;
178
188
 
179
- export { type AllPositionsData, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, selectEphemeris };
189
+ export { type AllPositionsData, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, type VisibilityPass, type VisibilityResult, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, computeVisibility, dopplerHz, ecefToAzEl, ephInfoToEphemeris, glonassPosition, keplerPosition, maskRadForAzimuth, navTimesFromEph, rangeRate, selectEphemeris };
package/dist/orbit.js CHANGED
@@ -4,14 +4,16 @@ import {
4
4
  computeLiveSkyPositions,
5
5
  computeSatPosition,
6
6
  computeVisibility,
7
+ dopplerHz,
7
8
  ecefToAzEl,
8
9
  ephInfoToEphemeris,
9
10
  glonassPosition,
10
11
  keplerPosition,
11
12
  maskRadForAzimuth,
12
13
  navTimesFromEph,
14
+ rangeRate,
13
15
  selectEphemeris
14
- } from "./chunk-AK6LNWZX.js";
16
+ } from "./chunk-DMNFB7Q6.js";
15
17
  import "./chunk-HVXYFUCB.js";
16
18
  import {
17
19
  ecefToGeodetic,
@@ -26,6 +28,7 @@ export {
26
28
  computeLiveSkyPositions,
27
29
  computeSatPosition,
28
30
  computeVisibility,
31
+ dopplerHz,
29
32
  ecefToAzEl,
30
33
  ecefToGeodetic,
31
34
  ephInfoToEphemeris,
@@ -34,5 +37,6 @@ export {
34
37
  keplerPosition,
35
38
  maskRadForAzimuth,
36
39
  navTimesFromEph,
40
+ rangeRate,
37
41
  selectEphemeris
38
42
  };
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var positioning_exports = {};
22
22
  __export(positioning_exports, {
23
23
  ionoFree: () => ionoFree,
24
+ klobucharDelay: () => klobucharDelay,
24
25
  satClockCorrection: () => satClockCorrection,
25
26
  solveSpp: () => solveSpp
26
27
  });
@@ -164,6 +165,7 @@ function gmForSystem(sys) {
164
165
  if (sys === "C") return GM_BDS;
165
166
  return GM_GPS;
166
167
  }
168
+ var geoVelocityPass = false;
167
169
  function keplerPosition(eph, t) {
168
170
  const GM = gmForSystem(eph.system);
169
171
  const a = eph.sqrtA * eph.sqrtA;
@@ -191,8 +193,16 @@ function keplerPosition(eph, t) {
191
193
  const uk = phik + duk;
192
194
  const rk = a * (1 - eph.e * cosE) + drk;
193
195
  const ik = eph.i0 + dik + eph.idot * tk;
196
+ const EkDot = n / (1 - eph.e * cosE);
197
+ const vkDot = EkDot * Math.sqrt(1 - eph.e * eph.e) / (1 - eph.e * cosE);
198
+ const dukDot = 2 * vkDot * (eph.cus * cos2phi - eph.cuc * sin2phi);
199
+ const drkDot = a * eph.e * sinE * EkDot + 2 * vkDot * (eph.crs * cos2phi - eph.crc * sin2phi);
200
+ const dikDot = eph.idot + 2 * vkDot * (eph.cis * cos2phi - eph.cic * sin2phi);
201
+ const ukDot = vkDot + dukDot;
194
202
  const xp = rk * Math.cos(uk);
195
203
  const yp = rk * Math.sin(uk);
204
+ const xpDot = drkDot * Math.cos(uk) - rk * ukDot * Math.sin(uk);
205
+ const ypDot = drkDot * Math.sin(uk) + rk * ukDot * Math.cos(uk);
196
206
  const isBdsGeo = eph.system === "C" && Math.abs(eph.i0) < BDS_GEO_MAX_INCLINATION_RAD && a > BDS_GEO_MIN_SEMIMAJOR_AXIS_M;
197
207
  if (isBdsGeo) {
198
208
  const omegak2 = eph.omega0 + eph.omegaDot * tk - OMEGA_E * eph.toe;
@@ -208,23 +218,43 @@ function keplerPosition(eph, t) {
208
218
  const sinPhi = Math.sin(phi);
209
219
  const COS5 = Math.cos(-5 * Math.PI / 180);
210
220
  const SIN5 = Math.sin(-5 * Math.PI / 180);
221
+ const px = xg * cosPhi + yg * sinPhi * COS5 + zg * sinPhi * SIN5;
222
+ const py = -xg * sinPhi + yg * cosPhi * COS5 + zg * cosPhi * SIN5;
223
+ const pz = -yg * SIN5 + zg * COS5;
224
+ if (geoVelocityPass) {
225
+ return { prn: eph.prn, x: px, y: py, z: pz, vx: 0, vy: 0, vz: 0 };
226
+ }
227
+ geoVelocityPass = true;
228
+ const h = 0.5;
229
+ const pm = keplerPosition(eph, t - h);
230
+ const pp = keplerPosition(eph, t + h);
231
+ geoVelocityPass = false;
211
232
  return {
212
233
  prn: eph.prn,
213
- x: xg * cosPhi + yg * sinPhi * COS5 + zg * sinPhi * SIN5,
214
- y: -xg * sinPhi + yg * cosPhi * COS5 + zg * cosPhi * SIN5,
215
- z: -yg * SIN5 + zg * COS5
234
+ x: px,
235
+ y: py,
236
+ z: pz,
237
+ vx: (pp.x - pm.x) / (2 * h),
238
+ vy: (pp.y - pm.y) / (2 * h),
239
+ vz: (pp.z - pm.z) / (2 * h)
216
240
  };
217
241
  }
218
242
  const omegak = eph.omega0 + (eph.omegaDot - OMEGA_E) * tk - OMEGA_E * eph.toe;
243
+ const omegakDot = eph.omegaDot - OMEGA_E;
219
244
  const cosO = Math.cos(omegak);
220
245
  const sinO = Math.sin(omegak);
221
246
  const cosI = Math.cos(ik);
222
247
  const sinI = Math.sin(ik);
248
+ const x = xp * cosO - yp * cosI * sinO;
249
+ const y = xp * sinO + yp * cosI * cosO;
223
250
  return {
224
251
  prn: eph.prn,
225
- x: xp * cosO - yp * cosI * sinO,
226
- y: xp * sinO + yp * cosI * cosO,
227
- z: yp * sinI
252
+ x,
253
+ y,
254
+ z: yp * sinI,
255
+ vx: xpDot * cosO - ypDot * cosI * sinO + yp * sinI * sinO * dikDot - omegakDot * y,
256
+ vy: xpDot * sinO + ypDot * cosI * cosO - yp * sinI * cosO * dikDot + omegakDot * x,
257
+ vz: ypDot * sinI + yp * cosI * dikDot
228
258
  };
229
259
  }
230
260
  function gloDerivatives(state, acc) {
@@ -278,7 +308,15 @@ function glonassPosition(eph, tUtc) {
278
308
  if (Math.abs(remainder) > 1e-3) {
279
309
  state = rk4Step(state, acc, remainder);
280
310
  }
281
- return { prn: eph.prn, x: state[0], y: state[1], z: state[2] };
311
+ return {
312
+ prn: eph.prn,
313
+ x: state[0],
314
+ y: state[1],
315
+ z: state[2],
316
+ vx: state[3],
317
+ vy: state[4],
318
+ vz: state[5]
319
+ };
282
320
  }
283
321
  function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
284
322
  const [lat, lon] = ecefToGeodetic(rxX, rxY, rxZ);
@@ -371,6 +409,37 @@ var MS_PER_WEEK = 7 * 864e5;
371
409
  var BDS_EPOCH_MS = START_BDS_TIME.getTime();
372
410
  var GAL_EPOCH_MS = GPS_EPOCH_MS + 1024 * MS_PER_WEEK;
373
411
 
412
+ // src/positioning/klobuchar.ts
413
+ function klobucharDelay(coeffs, latRad, lonRad, azRad, elRad, gpsTowSec) {
414
+ const { alpha, beta } = coeffs;
415
+ if (alpha.length < 4 || beta.length < 4) return 0;
416
+ const E = elRad / Math.PI;
417
+ const phiU = latRad / Math.PI;
418
+ const lambdaU = lonRad / Math.PI;
419
+ const psi = 0.0137 / (E + 0.11) - 0.022;
420
+ let phiI = phiU + psi * Math.cos(azRad);
421
+ if (phiI > 0.416) phiI = 0.416;
422
+ else if (phiI < -0.416) phiI = -0.416;
423
+ const lambdaI = lambdaU + psi * Math.sin(azRad) / Math.cos(phiI * Math.PI);
424
+ const phiM = phiI + 0.064 * Math.cos((lambdaI - 1.617) * Math.PI);
425
+ let t = 43200 * lambdaI + gpsTowSec;
426
+ t %= 86400;
427
+ if (t < 0) t += 86400;
428
+ let amp = 0;
429
+ let per = 0;
430
+ for (let n = 3; n >= 0; n--) {
431
+ amp = amp * phiM + alpha[n];
432
+ per = per * phiM + beta[n];
433
+ }
434
+ if (amp < 0) amp = 0;
435
+ if (per < 72e3) per = 72e3;
436
+ const F = 1 + 16 * Math.pow(0.53 - E, 3);
437
+ const x = 2 * Math.PI * (t - 50400) / per;
438
+ if (Math.abs(x) >= 1.57) return F * 5e-9;
439
+ const x2 = x * x;
440
+ return F * (5e-9 + amp * (1 - x2 / 2 + x2 * x2 / 24));
441
+ }
442
+
374
443
  // src/positioning/index.ts
375
444
  var GM_GPS2 = 3986005e8;
376
445
  var F_REL = -4442807633e-19;
@@ -397,6 +466,16 @@ function ionoFree(p1, p2, f1, f2) {
397
466
  const g = f1 * f1 / (f1 * f1 - f2 * f2);
398
467
  return g * p1 - (g - 1) * p2;
399
468
  }
469
+ var GPS_EPOCH_MS_SPP = Date.UTC(1980, 0, 6);
470
+ var PRIMARY_FREQ_HZ = {
471
+ G: 157542e4,
472
+ E: 157542e4,
473
+ J: 157542e4,
474
+ S: 157542e4,
475
+ C: 1561098e3,
476
+ R: 1602e6
477
+ };
478
+ var F_L1 = 157542e4;
400
479
  function tropoDelay(elevationRad) {
401
480
  const sinEl = Math.sin(elevationRad);
402
481
  return 2.47 / (sinEl + 0.0121);
@@ -431,8 +510,10 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
431
510
  troposphere = true,
432
511
  maxIterations = 15,
433
512
  convergenceM = 1e-4,
434
- tgd = true
513
+ tgd = true,
514
+ iono
435
515
  } = opts;
516
+ const gpsTow = (timeMs - GPS_EPOCH_MS_SPP) / 1e3 % 604800;
436
517
  const all = [...pseudoranges.keys()].filter((p) => ephemerides.has(p));
437
518
  const minSats = (list) => 3 + new Set(list.map((p) => p[0])).size;
438
519
  if (all.length < minSats(all)) return null;
@@ -456,6 +537,8 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
456
537
  const Htv = new Array(dim).fill(0);
457
538
  let rows = 0;
458
539
  for (const k of Object.keys(residuals2)) delete residuals2[k];
540
+ const positionSaneIter = x2 * x2 + y2 * y2 + z2 * z2 > 1e12;
541
+ const [rxLat, rxLon] = positionSaneIter ? ecefToGeodetic(x2, y2, z2) : [0, 0];
459
542
  for (const prn of prns) {
460
543
  const psr = pseudoranges.get(prn);
461
544
  const eph = ephemerides.get(prn);
@@ -474,17 +557,24 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
474
557
  const uy = (y2 - sy) / rho;
475
558
  const uz = (z2 - sz) / rho;
476
559
  let elev = Math.PI / 2;
560
+ let azim = 0;
477
561
  let weight = 1;
478
- const positionSane = x2 * x2 + y2 * y2 + z2 * z2 > 1e12;
562
+ const positionSane = positionSaneIter;
479
563
  if (positionSane) {
480
564
  const azel = ecefToAzEl(x2, y2, z2, sx, sy, sz);
481
565
  elev = azel.el;
566
+ azim = azel.az;
482
567
  if (iter >= 2 && elev < elevationMaskDeg * Math.PI / 180) continue;
483
568
  const sinEl = Math.max(Math.sin(elev), 0.1);
484
569
  weight = sinEl * sinEl;
485
570
  }
486
571
  const tropo = troposphere && positionSane ? tropoDelay(elev) : 0;
487
- const predicted = rho + clk - C_LIGHT * dts + tropo;
572
+ let ionoM = 0;
573
+ if (iono && positionSane) {
574
+ const f = PRIMARY_FREQ_HZ[sys] ?? F_L1;
575
+ ionoM = C_LIGHT * klobucharDelay(iono, rxLat, rxLon, azim, elev, gpsTow) * (F_L1 / f * (F_L1 / f));
576
+ }
577
+ const predicted = rho + clk - C_LIGHT * dts + tropo + ionoM;
488
578
  const v = psr - predicted;
489
579
  const h = new Array(dim).fill(0);
490
580
  h[0] = ux;
@@ -564,6 +654,7 @@ function solveSpp(pseudoranges, ephemerides, timeMs, opts = {}) {
564
654
  // Annotate the CommonJS export names for ESM import in node:
565
655
  0 && (module.exports = {
566
656
  ionoFree,
657
+ klobucharDelay,
567
658
  satClockCorrection,
568
659
  solveSpp
569
660
  });