gnss-js 1.39.0 → 1.40.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.
@@ -40,68 +40,106 @@ function* trimbleFrames(data, stats) {
40
40
 
41
41
  // src/trimble/nav.ts
42
42
  var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
43
+ var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
43
44
  var SEC_PER_WEEK = 7 * 86400;
44
45
  var MS_PER_WEEK = SEC_PER_WEEK * 1e3;
45
46
  var GPS_PI = 3.1415926535898;
47
+ var BDS_WEEK_OFFSET = 1356;
48
+ var BDS_LEAP_SEC = 14;
46
49
  var gpsMs = (week, sec) => GPS_EPOCH_MS + week * MS_PER_WEEK + sec * 1e3;
47
50
  var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
48
51
  var SUB_GPS_EPHEMERIS = 1;
49
52
  var SUB_ION_UTC = 3;
50
- function decodeGpsEphemeris(data, view, base) {
51
- const prn = data[base + 5];
52
- if (prn < 1 || prn > 32) return null;
53
- const week = view.getUint16(base + 6, false);
54
- const iode = data[base + 11];
55
- const toc = view.getInt32(base + 16, false);
56
- const toe = view.getUint32(base + 20, false);
57
- const tgd = view.getFloat64(base + 24, false);
58
- const af2 = view.getFloat64(base + 32, false);
59
- const af1 = view.getFloat64(base + 40, false);
60
- const af0 = view.getFloat64(base + 48, false);
61
- const crs = view.getFloat64(base + 56, false);
62
- const deltaN = view.getFloat64(base + 64, false) * GPS_PI;
63
- const m0 = view.getFloat64(base + 72, false) * GPS_PI;
64
- const cuc = view.getFloat64(base + 80, false) * GPS_PI;
65
- const e = view.getFloat64(base + 88, false);
66
- const cus = view.getFloat64(base + 96, false) * GPS_PI;
67
- const sqrtA = view.getFloat64(base + 104, false);
68
- const cic = view.getFloat64(base + 112, false) * GPS_PI;
69
- const omega0 = view.getFloat64(base + 120, false) * GPS_PI;
70
- const cis = view.getFloat64(base + 128, false) * GPS_PI;
71
- const i0 = view.getFloat64(base + 136, false) * GPS_PI;
72
- const crc = view.getFloat64(base + 144, false);
73
- const omega = view.getFloat64(base + 152, false) * GPS_PI;
74
- const omegaDot = view.getFloat64(base + 160, false) * GPS_PI;
75
- const idot = view.getFloat64(base + 168, false) * GPS_PI;
76
- const flags = view.getUint32(base + 176, false);
53
+ var SUB_BDS_EPHEMERIS = 21;
54
+ function readKepler(view, base) {
55
+ return {
56
+ svid: view.getUint8(base + 5),
57
+ week: view.getUint16(base + 6, false),
58
+ iode: view.getUint8(base + 11),
59
+ tocSec: view.getInt32(base + 16, false),
60
+ toeSec: view.getUint32(base + 20, false),
61
+ tgd: view.getFloat64(base + 24, false),
62
+ af2: view.getFloat64(base + 32, false),
63
+ af1: view.getFloat64(base + 40, false),
64
+ af0: view.getFloat64(base + 48, false),
65
+ crs: view.getFloat64(base + 56, false),
66
+ deltaN: view.getFloat64(base + 64, false) * GPS_PI,
67
+ m0: view.getFloat64(base + 72, false) * GPS_PI,
68
+ cuc: view.getFloat64(base + 80, false) * GPS_PI,
69
+ e: view.getFloat64(base + 88, false),
70
+ cus: view.getFloat64(base + 96, false) * GPS_PI,
71
+ sqrtA: view.getFloat64(base + 104, false),
72
+ cic: view.getFloat64(base + 112, false) * GPS_PI,
73
+ omega0: view.getFloat64(base + 120, false) * GPS_PI,
74
+ cis: view.getFloat64(base + 128, false) * GPS_PI,
75
+ i0: view.getFloat64(base + 136, false) * GPS_PI,
76
+ crc: view.getFloat64(base + 144, false),
77
+ omega: view.getFloat64(base + 152, false) * GPS_PI,
78
+ omegaDot: view.getFloat64(base + 160, false) * GPS_PI,
79
+ idot: view.getFloat64(base + 168, false) * GPS_PI,
80
+ flags: view.getUint32(base + 176, false)
81
+ };
82
+ }
83
+ function keplerCommon(r) {
84
+ return {
85
+ af0: r.af0,
86
+ af1: r.af1,
87
+ af2: r.af2,
88
+ iode: r.iode,
89
+ crs: r.crs,
90
+ deltaN: r.deltaN,
91
+ m0: r.m0,
92
+ cuc: r.cuc,
93
+ e: r.e,
94
+ cus: r.cus,
95
+ sqrtA: r.sqrtA,
96
+ cic: r.cic,
97
+ omega0: r.omega0,
98
+ cis: r.cis,
99
+ i0: r.i0,
100
+ crc: r.crc,
101
+ omega: r.omega,
102
+ omegaDot: r.omegaDot,
103
+ idot: r.idot,
104
+ tgd: r.tgd
105
+ };
106
+ }
107
+ function decodeGpsEphemeris(view, base) {
108
+ const r = readKepler(view, base);
109
+ if (r.svid < 1 || r.svid > 32) return null;
110
+ const tocMs = gpsMs(r.week, r.tocSec);
77
111
  return {
78
112
  system: "G",
79
- prn: `G${String(prn).padStart(2, "0")}`,
80
- toc: sowOf(gpsMs(week, toc)),
81
- tocDate: new Date(gpsMs(week, toc)),
82
- af0,
83
- af1,
84
- af2,
85
- iode,
86
- crs,
87
- deltaN,
88
- m0,
89
- cuc,
90
- e,
91
- cus,
92
- sqrtA,
93
- toe,
94
- cic,
95
- omega0,
96
- cis,
97
- i0,
98
- crc,
99
- omega,
100
- omegaDot,
101
- idot,
102
- week,
103
- svHealth: flags >>> 4 & 127,
104
- tgd
113
+ prn: `G${String(r.svid).padStart(2, "0")}`,
114
+ toc: sowOf(tocMs),
115
+ tocDate: new Date(tocMs),
116
+ toe: r.toeSec,
117
+ week: r.week,
118
+ svHealth: r.flags >>> 4 & 127,
119
+ ...keplerCommon(r)
120
+ };
121
+ }
122
+ function decodeBdsEphemeris(view, base) {
123
+ const r = readKepler(view, base);
124
+ if (r.svid < 1 || r.svid > 63) return null;
125
+ const bdsWeek = r.week - BDS_WEEK_OFFSET;
126
+ const tocSec = r.tocSec - BDS_LEAP_SEC;
127
+ const toeSec = r.toeSec - BDS_LEAP_SEC;
128
+ const tocDate = new Date(
129
+ BDT_EPOCH_MS + bdsWeek * MS_PER_WEEK + tocSec * 1e3
130
+ );
131
+ return {
132
+ system: "C",
133
+ prn: `C${String(r.svid).padStart(2, "0")}`,
134
+ toc: sowOf(tocDate.getTime()),
135
+ tocDate,
136
+ toe: toeSec,
137
+ week: bdsWeek,
138
+ // Health from the same flags nibble as GPS (0 = healthy for the observed
139
+ // operational BeiDou sats); flags bit 0 is a set-on-all format marker,
140
+ // not SatH1, so it must not be used here.
141
+ svHealth: r.flags >>> 4 & 127,
142
+ ...keplerCommon(r)
105
143
  };
106
144
  }
107
145
  function parseTrimbleNav(data) {
@@ -116,8 +154,8 @@ function parseTrimbleNav(data) {
116
154
  if (f.type !== RETSVDATA) continue;
117
155
  const sub = data[f.payload];
118
156
  retsvCounts[sub] = (retsvCounts[sub] ?? 0) + 1;
119
- if (sub === SUB_GPS_EPHEMERIS && f.len >= 176) {
120
- const eph = decodeGpsEphemeris(data, view, f.start);
157
+ if ((sub === SUB_GPS_EPHEMERIS || sub === SUB_BDS_EPHEMERIS) && f.len >= 176) {
158
+ const eph = sub === SUB_GPS_EPHEMERIS ? decodeGpsEphemeris(view, f.start) : decodeBdsEphemeris(view, f.start);
121
159
  if (!eph) continue;
122
160
  if (lastIode.get(eph.prn) === eph.iode) continue;
123
161
  lastIode.set(eph.prn, eph.iode);
package/dist/index.cjs CHANGED
@@ -9590,68 +9590,106 @@ function* trimbleFrames(data, stats) {
9590
9590
 
9591
9591
  // src/trimble/nav.ts
9592
9592
  var GPS_EPOCH_MS14 = Date.UTC(1980, 0, 6);
9593
+ var BDT_EPOCH_MS4 = Date.UTC(2006, 0, 1);
9593
9594
  var SEC_PER_WEEK10 = 7 * 86400;
9594
9595
  var MS_PER_WEEK9 = SEC_PER_WEEK10 * 1e3;
9595
9596
  var GPS_PI2 = 3.1415926535898;
9597
+ var BDS_WEEK_OFFSET = 1356;
9598
+ var BDS_LEAP_SEC = 14;
9596
9599
  var gpsMs3 = (week, sec) => GPS_EPOCH_MS14 + week * MS_PER_WEEK9 + sec * 1e3;
9597
9600
  var sowOf4 = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK10;
9598
9601
  var SUB_GPS_EPHEMERIS = 1;
9599
9602
  var SUB_ION_UTC = 3;
9600
- function decodeGpsEphemeris2(data, view, base) {
9601
- const prn = data[base + 5];
9602
- if (prn < 1 || prn > 32) return null;
9603
- const week = view.getUint16(base + 6, false);
9604
- const iode = data[base + 11];
9605
- const toc = view.getInt32(base + 16, false);
9606
- const toe = view.getUint32(base + 20, false);
9607
- const tgd = view.getFloat64(base + 24, false);
9608
- const af2 = view.getFloat64(base + 32, false);
9609
- const af1 = view.getFloat64(base + 40, false);
9610
- const af0 = view.getFloat64(base + 48, false);
9611
- const crs = view.getFloat64(base + 56, false);
9612
- const deltaN = view.getFloat64(base + 64, false) * GPS_PI2;
9613
- const m0 = view.getFloat64(base + 72, false) * GPS_PI2;
9614
- const cuc = view.getFloat64(base + 80, false) * GPS_PI2;
9615
- const e = view.getFloat64(base + 88, false);
9616
- const cus = view.getFloat64(base + 96, false) * GPS_PI2;
9617
- const sqrtA = view.getFloat64(base + 104, false);
9618
- const cic = view.getFloat64(base + 112, false) * GPS_PI2;
9619
- const omega0 = view.getFloat64(base + 120, false) * GPS_PI2;
9620
- const cis = view.getFloat64(base + 128, false) * GPS_PI2;
9621
- const i0 = view.getFloat64(base + 136, false) * GPS_PI2;
9622
- const crc = view.getFloat64(base + 144, false);
9623
- const omega = view.getFloat64(base + 152, false) * GPS_PI2;
9624
- const omegaDot = view.getFloat64(base + 160, false) * GPS_PI2;
9625
- const idot = view.getFloat64(base + 168, false) * GPS_PI2;
9626
- const flags = view.getUint32(base + 176, false);
9603
+ var SUB_BDS_EPHEMERIS = 21;
9604
+ function readKepler(view, base) {
9605
+ return {
9606
+ svid: view.getUint8(base + 5),
9607
+ week: view.getUint16(base + 6, false),
9608
+ iode: view.getUint8(base + 11),
9609
+ tocSec: view.getInt32(base + 16, false),
9610
+ toeSec: view.getUint32(base + 20, false),
9611
+ tgd: view.getFloat64(base + 24, false),
9612
+ af2: view.getFloat64(base + 32, false),
9613
+ af1: view.getFloat64(base + 40, false),
9614
+ af0: view.getFloat64(base + 48, false),
9615
+ crs: view.getFloat64(base + 56, false),
9616
+ deltaN: view.getFloat64(base + 64, false) * GPS_PI2,
9617
+ m0: view.getFloat64(base + 72, false) * GPS_PI2,
9618
+ cuc: view.getFloat64(base + 80, false) * GPS_PI2,
9619
+ e: view.getFloat64(base + 88, false),
9620
+ cus: view.getFloat64(base + 96, false) * GPS_PI2,
9621
+ sqrtA: view.getFloat64(base + 104, false),
9622
+ cic: view.getFloat64(base + 112, false) * GPS_PI2,
9623
+ omega0: view.getFloat64(base + 120, false) * GPS_PI2,
9624
+ cis: view.getFloat64(base + 128, false) * GPS_PI2,
9625
+ i0: view.getFloat64(base + 136, false) * GPS_PI2,
9626
+ crc: view.getFloat64(base + 144, false),
9627
+ omega: view.getFloat64(base + 152, false) * GPS_PI2,
9628
+ omegaDot: view.getFloat64(base + 160, false) * GPS_PI2,
9629
+ idot: view.getFloat64(base + 168, false) * GPS_PI2,
9630
+ flags: view.getUint32(base + 176, false)
9631
+ };
9632
+ }
9633
+ function keplerCommon(r) {
9634
+ return {
9635
+ af0: r.af0,
9636
+ af1: r.af1,
9637
+ af2: r.af2,
9638
+ iode: r.iode,
9639
+ crs: r.crs,
9640
+ deltaN: r.deltaN,
9641
+ m0: r.m0,
9642
+ cuc: r.cuc,
9643
+ e: r.e,
9644
+ cus: r.cus,
9645
+ sqrtA: r.sqrtA,
9646
+ cic: r.cic,
9647
+ omega0: r.omega0,
9648
+ cis: r.cis,
9649
+ i0: r.i0,
9650
+ crc: r.crc,
9651
+ omega: r.omega,
9652
+ omegaDot: r.omegaDot,
9653
+ idot: r.idot,
9654
+ tgd: r.tgd
9655
+ };
9656
+ }
9657
+ function decodeGpsEphemeris2(view, base) {
9658
+ const r = readKepler(view, base);
9659
+ if (r.svid < 1 || r.svid > 32) return null;
9660
+ const tocMs = gpsMs3(r.week, r.tocSec);
9627
9661
  return {
9628
9662
  system: "G",
9629
- prn: `G${String(prn).padStart(2, "0")}`,
9630
- toc: sowOf4(gpsMs3(week, toc)),
9631
- tocDate: new Date(gpsMs3(week, toc)),
9632
- af0,
9633
- af1,
9634
- af2,
9635
- iode,
9636
- crs,
9637
- deltaN,
9638
- m0,
9639
- cuc,
9640
- e,
9641
- cus,
9642
- sqrtA,
9643
- toe,
9644
- cic,
9645
- omega0,
9646
- cis,
9647
- i0,
9648
- crc,
9649
- omega,
9650
- omegaDot,
9651
- idot,
9652
- week,
9653
- svHealth: flags >>> 4 & 127,
9654
- tgd
9663
+ prn: `G${String(r.svid).padStart(2, "0")}`,
9664
+ toc: sowOf4(tocMs),
9665
+ tocDate: new Date(tocMs),
9666
+ toe: r.toeSec,
9667
+ week: r.week,
9668
+ svHealth: r.flags >>> 4 & 127,
9669
+ ...keplerCommon(r)
9670
+ };
9671
+ }
9672
+ function decodeBdsEphemeris3(view, base) {
9673
+ const r = readKepler(view, base);
9674
+ if (r.svid < 1 || r.svid > 63) return null;
9675
+ const bdsWeek = r.week - BDS_WEEK_OFFSET;
9676
+ const tocSec = r.tocSec - BDS_LEAP_SEC;
9677
+ const toeSec = r.toeSec - BDS_LEAP_SEC;
9678
+ const tocDate = new Date(
9679
+ BDT_EPOCH_MS4 + bdsWeek * MS_PER_WEEK9 + tocSec * 1e3
9680
+ );
9681
+ return {
9682
+ system: "C",
9683
+ prn: `C${String(r.svid).padStart(2, "0")}`,
9684
+ toc: sowOf4(tocDate.getTime()),
9685
+ tocDate,
9686
+ toe: toeSec,
9687
+ week: bdsWeek,
9688
+ // Health from the same flags nibble as GPS (0 = healthy for the observed
9689
+ // operational BeiDou sats); flags bit 0 is a set-on-all format marker,
9690
+ // not SatH1, so it must not be used here.
9691
+ svHealth: r.flags >>> 4 & 127,
9692
+ ...keplerCommon(r)
9655
9693
  };
9656
9694
  }
9657
9695
  function parseTrimbleNav(data) {
@@ -9666,8 +9704,8 @@ function parseTrimbleNav(data) {
9666
9704
  if (f.type !== RETSVDATA) continue;
9667
9705
  const sub = data[f.payload];
9668
9706
  retsvCounts[sub] = (retsvCounts[sub] ?? 0) + 1;
9669
- if (sub === SUB_GPS_EPHEMERIS && f.len >= 176) {
9670
- const eph = decodeGpsEphemeris2(data, view, f.start);
9707
+ if ((sub === SUB_GPS_EPHEMERIS || sub === SUB_BDS_EPHEMERIS) && f.len >= 176) {
9708
+ const eph = sub === SUB_GPS_EPHEMERIS ? decodeGpsEphemeris2(view, f.start) : decodeBdsEphemeris3(view, f.start);
9671
9709
  if (!eph) continue;
9672
9710
  if (lastIode.get(eph.prn) === eph.iode) continue;
9673
9711
  lastIode.set(eph.prn, eph.iode);
@@ -10076,7 +10114,7 @@ function* binexRecords(data, view, stats) {
10076
10114
  // src/binex/nav.ts
10077
10115
  var GPS_EPOCH_MS16 = Date.UTC(1980, 0, 6);
10078
10116
  var GLO_DAY0_MS = Date.UTC(1980, 0, 1);
10079
- var BDT_EPOCH_MS4 = Date.UTC(2006, 0, 1);
10117
+ var BDT_EPOCH_MS5 = Date.UTC(2006, 0, 1);
10080
10118
  var SEC_PER_WEEK11 = 7 * 86400;
10081
10119
  var MS_PER_WEEK11 = SEC_PER_WEEK11 * 1e3;
10082
10120
  var SC2RAD = GPS_PI;
@@ -10245,7 +10283,7 @@ function decodeBeidou(view, p, le) {
10245
10283
  const iode = flag1 >> 6 & 31;
10246
10284
  const svHealth = flag1 & 1;
10247
10285
  const tgd = bdsTgd(flag2 >> 4);
10248
- const tocDate = new Date(BDT_EPOCH_MS4 + week * MS_PER_WEEK11 + toes * 1e3);
10286
+ const tocDate = new Date(BDT_EPOCH_MS5 + week * MS_PER_WEEK11 + toes * 1e3);
10249
10287
  return {
10250
10288
  system: "C",
10251
10289
  prn: `C${two5(prn)}`,
@@ -10713,13 +10751,13 @@ var GM_GPS = 3986005e8;
10713
10751
  var GM_GAL = 3986004418e5;
10714
10752
  var GM_BDS = 3986004418e5;
10715
10753
  var GPS_EPOCH_MS18 = Date.UTC(1980, 0, 6);
10716
- var BDT_EPOCH_MS5 = Date.UTC(2006, 0, 1);
10754
+ var BDT_EPOCH_MS6 = Date.UTC(2006, 0, 1);
10717
10755
  var WEEK_MS = 6048e5;
10718
10756
  var BDT_MINUS_GPST_MS = 14e3;
10719
10757
  var BDS_GEO_PRNS = /* @__PURE__ */ new Set([1, 2, 3, 4, 5, 59, 60, 61, 62, 63]);
10720
10758
  function almanacEpochMs(alm) {
10721
10759
  if (alm.system === "C") {
10722
- return BDT_EPOCH_MS5 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3 + BDT_MINUS_GPST_MS;
10760
+ return BDT_EPOCH_MS6 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3 + BDT_MINUS_GPST_MS;
10723
10761
  }
10724
10762
  return GPS_EPOCH_MS18 + alm.weekAlm * WEEK_MS + alm.toaSec * 1e3;
10725
10763
  }
package/dist/index.js CHANGED
@@ -79,7 +79,7 @@ import {
79
79
  parseTrimble,
80
80
  parseTrimbleNav,
81
81
  trimbleFrames
82
- } from "./chunk-U47XUQAT.js";
82
+ } from "./chunk-J2E2IKJR.js";
83
83
  import {
84
84
  binexCrc16,
85
85
  binexCrc32,
package/dist/trimble.cjs CHANGED
@@ -68,68 +68,106 @@ function* trimbleFrames(data, stats) {
68
68
 
69
69
  // src/trimble/nav.ts
70
70
  var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
71
+ var BDT_EPOCH_MS = Date.UTC(2006, 0, 1);
71
72
  var SEC_PER_WEEK = 7 * 86400;
72
73
  var MS_PER_WEEK = SEC_PER_WEEK * 1e3;
73
74
  var GPS_PI = 3.1415926535898;
75
+ var BDS_WEEK_OFFSET = 1356;
76
+ var BDS_LEAP_SEC = 14;
74
77
  var gpsMs = (week, sec) => GPS_EPOCH_MS + week * MS_PER_WEEK + sec * 1e3;
75
78
  var sowOf = (dateMs) => dateMs / 1e3 % SEC_PER_WEEK;
76
79
  var SUB_GPS_EPHEMERIS = 1;
77
80
  var SUB_ION_UTC = 3;
78
- function decodeGpsEphemeris(data, view, base) {
79
- const prn = data[base + 5];
80
- if (prn < 1 || prn > 32) return null;
81
- const week = view.getUint16(base + 6, false);
82
- const iode = data[base + 11];
83
- const toc = view.getInt32(base + 16, false);
84
- const toe = view.getUint32(base + 20, false);
85
- const tgd = view.getFloat64(base + 24, false);
86
- const af2 = view.getFloat64(base + 32, false);
87
- const af1 = view.getFloat64(base + 40, false);
88
- const af0 = view.getFloat64(base + 48, false);
89
- const crs = view.getFloat64(base + 56, false);
90
- const deltaN = view.getFloat64(base + 64, false) * GPS_PI;
91
- const m0 = view.getFloat64(base + 72, false) * GPS_PI;
92
- const cuc = view.getFloat64(base + 80, false) * GPS_PI;
93
- const e = view.getFloat64(base + 88, false);
94
- const cus = view.getFloat64(base + 96, false) * GPS_PI;
95
- const sqrtA = view.getFloat64(base + 104, false);
96
- const cic = view.getFloat64(base + 112, false) * GPS_PI;
97
- const omega0 = view.getFloat64(base + 120, false) * GPS_PI;
98
- const cis = view.getFloat64(base + 128, false) * GPS_PI;
99
- const i0 = view.getFloat64(base + 136, false) * GPS_PI;
100
- const crc = view.getFloat64(base + 144, false);
101
- const omega = view.getFloat64(base + 152, false) * GPS_PI;
102
- const omegaDot = view.getFloat64(base + 160, false) * GPS_PI;
103
- const idot = view.getFloat64(base + 168, false) * GPS_PI;
104
- const flags = view.getUint32(base + 176, false);
81
+ var SUB_BDS_EPHEMERIS = 21;
82
+ function readKepler(view, base) {
83
+ return {
84
+ svid: view.getUint8(base + 5),
85
+ week: view.getUint16(base + 6, false),
86
+ iode: view.getUint8(base + 11),
87
+ tocSec: view.getInt32(base + 16, false),
88
+ toeSec: view.getUint32(base + 20, false),
89
+ tgd: view.getFloat64(base + 24, false),
90
+ af2: view.getFloat64(base + 32, false),
91
+ af1: view.getFloat64(base + 40, false),
92
+ af0: view.getFloat64(base + 48, false),
93
+ crs: view.getFloat64(base + 56, false),
94
+ deltaN: view.getFloat64(base + 64, false) * GPS_PI,
95
+ m0: view.getFloat64(base + 72, false) * GPS_PI,
96
+ cuc: view.getFloat64(base + 80, false) * GPS_PI,
97
+ e: view.getFloat64(base + 88, false),
98
+ cus: view.getFloat64(base + 96, false) * GPS_PI,
99
+ sqrtA: view.getFloat64(base + 104, false),
100
+ cic: view.getFloat64(base + 112, false) * GPS_PI,
101
+ omega0: view.getFloat64(base + 120, false) * GPS_PI,
102
+ cis: view.getFloat64(base + 128, false) * GPS_PI,
103
+ i0: view.getFloat64(base + 136, false) * GPS_PI,
104
+ crc: view.getFloat64(base + 144, false),
105
+ omega: view.getFloat64(base + 152, false) * GPS_PI,
106
+ omegaDot: view.getFloat64(base + 160, false) * GPS_PI,
107
+ idot: view.getFloat64(base + 168, false) * GPS_PI,
108
+ flags: view.getUint32(base + 176, false)
109
+ };
110
+ }
111
+ function keplerCommon(r) {
112
+ return {
113
+ af0: r.af0,
114
+ af1: r.af1,
115
+ af2: r.af2,
116
+ iode: r.iode,
117
+ crs: r.crs,
118
+ deltaN: r.deltaN,
119
+ m0: r.m0,
120
+ cuc: r.cuc,
121
+ e: r.e,
122
+ cus: r.cus,
123
+ sqrtA: r.sqrtA,
124
+ cic: r.cic,
125
+ omega0: r.omega0,
126
+ cis: r.cis,
127
+ i0: r.i0,
128
+ crc: r.crc,
129
+ omega: r.omega,
130
+ omegaDot: r.omegaDot,
131
+ idot: r.idot,
132
+ tgd: r.tgd
133
+ };
134
+ }
135
+ function decodeGpsEphemeris(view, base) {
136
+ const r = readKepler(view, base);
137
+ if (r.svid < 1 || r.svid > 32) return null;
138
+ const tocMs = gpsMs(r.week, r.tocSec);
105
139
  return {
106
140
  system: "G",
107
- prn: `G${String(prn).padStart(2, "0")}`,
108
- toc: sowOf(gpsMs(week, toc)),
109
- tocDate: new Date(gpsMs(week, toc)),
110
- af0,
111
- af1,
112
- af2,
113
- iode,
114
- crs,
115
- deltaN,
116
- m0,
117
- cuc,
118
- e,
119
- cus,
120
- sqrtA,
121
- toe,
122
- cic,
123
- omega0,
124
- cis,
125
- i0,
126
- crc,
127
- omega,
128
- omegaDot,
129
- idot,
130
- week,
131
- svHealth: flags >>> 4 & 127,
132
- tgd
141
+ prn: `G${String(r.svid).padStart(2, "0")}`,
142
+ toc: sowOf(tocMs),
143
+ tocDate: new Date(tocMs),
144
+ toe: r.toeSec,
145
+ week: r.week,
146
+ svHealth: r.flags >>> 4 & 127,
147
+ ...keplerCommon(r)
148
+ };
149
+ }
150
+ function decodeBdsEphemeris(view, base) {
151
+ const r = readKepler(view, base);
152
+ if (r.svid < 1 || r.svid > 63) return null;
153
+ const bdsWeek = r.week - BDS_WEEK_OFFSET;
154
+ const tocSec = r.tocSec - BDS_LEAP_SEC;
155
+ const toeSec = r.toeSec - BDS_LEAP_SEC;
156
+ const tocDate = new Date(
157
+ BDT_EPOCH_MS + bdsWeek * MS_PER_WEEK + tocSec * 1e3
158
+ );
159
+ return {
160
+ system: "C",
161
+ prn: `C${String(r.svid).padStart(2, "0")}`,
162
+ toc: sowOf(tocDate.getTime()),
163
+ tocDate,
164
+ toe: toeSec,
165
+ week: bdsWeek,
166
+ // Health from the same flags nibble as GPS (0 = healthy for the observed
167
+ // operational BeiDou sats); flags bit 0 is a set-on-all format marker,
168
+ // not SatH1, so it must not be used here.
169
+ svHealth: r.flags >>> 4 & 127,
170
+ ...keplerCommon(r)
133
171
  };
134
172
  }
135
173
  function parseTrimbleNav(data) {
@@ -144,8 +182,8 @@ function parseTrimbleNav(data) {
144
182
  if (f.type !== RETSVDATA) continue;
145
183
  const sub = data[f.payload];
146
184
  retsvCounts[sub] = (retsvCounts[sub] ?? 0) + 1;
147
- if (sub === SUB_GPS_EPHEMERIS && f.len >= 176) {
148
- const eph = decodeGpsEphemeris(data, view, f.start);
185
+ if ((sub === SUB_GPS_EPHEMERIS || sub === SUB_BDS_EPHEMERIS) && f.len >= 176) {
186
+ const eph = sub === SUB_GPS_EPHEMERIS ? decodeGpsEphemeris(view, f.start) : decodeBdsEphemeris(view, f.start);
149
187
  if (!eph) continue;
150
188
  if (lastIode.get(eph.prn) === eph.iode) continue;
151
189
  lastIode.set(eph.prn, eph.iode);
@@ -25,24 +25,25 @@ declare function trimbleFrames(data: Uint8Array, stats: {
25
25
 
26
26
  /**
27
27
  * Trimble RT17/RT27 navigation-message decoding: RETSVDATA (0x55)
28
- * GPS ephemeris (subtype 1) and ION / UTC data (subtype 3).
28
+ * GPS ephemeris (subtype 1), BeiDou ephemeris (subtype 21) and ION / UTC
29
+ * data (subtype 3).
29
30
  *
30
- * Ported field-for-field from RTKLIB's `src/rcv/rt17.c`
31
+ * The GPS path is ported field-for-field from RTKLIB's `src/rcv/rt17.c`
31
32
  * (`decode_gps_ephemeris` / `decode_ion_utc_data`, tomojitakasu/RTKLIB,
32
33
  * Copyright (C) 2014 D. A. Cook / T. Takasu, BSD-2-Clause). All scalar
33
34
  * fields are big-endian; the semicircle angular/harmonic fields are
34
- * scaled by π to radians, exactly as RTKLIB does.
35
+ * scaled by π to radians, exactly as RTKLIB does. BeiDou (subtype 21)
36
+ * reuses the identical 176-byte Keplerian struct — established from a
37
+ * real DLF100NLD1 capture (√a ≈ 5282.6 m^½ ⇒ BeiDou MEO, i₀ ≈ 55°) — with
38
+ * the GPS→BDT time-scale conversion (week − 1356, SOW − 14 s) applied so
39
+ * the record matches `parseSbfNav`'s BDSNav output.
35
40
  *
36
41
  * Output records mirror what `parseNavFile` produces for the equivalent
37
- * RINEX 3 GPS navigation record and match the NovAtel/SBF ephemeris
38
- * paths (Keplerian `tocDate` a GPS-scale Date, angles in radians).
42
+ * RINEX 3 navigation record and match the NovAtel/SBF ephemeris paths
43
+ * (GPS: GPS-scale `tocDate`; BeiDou: naive-BDT `tocDate`; angles in rad).
39
44
  *
40
- * RTKLIB decodes only these two RETSVDATA subtypes; the GLONASS,
41
- * Galileo, BeiDou and QZSS ephemeris/almanac subtypes (RETSVDATA
42
- * subtypes 9/11/13/21/23/27, seen on the DLF100NLD1 Trimble stream) are
43
- * not handled here. Validated against a real DLF100NLD1 capture: the
44
- * subtype-1 GPS ephemerides decode to physical Keplerian elements
45
- * (√a ≈ 5153.6 m^½, i₀ ≈ 55°, week 2429).
45
+ * Still not decoded: the GLONASS/Galileo/QZSS ephemeris and almanac
46
+ * subtypes (RETSVDATA 9/11/13/23/27 on this stream).
46
47
  */
47
48
 
48
49
  interface TrimbleNavResult {
package/dist/trimble.d.ts CHANGED
@@ -25,24 +25,25 @@ declare function trimbleFrames(data: Uint8Array, stats: {
25
25
 
26
26
  /**
27
27
  * Trimble RT17/RT27 navigation-message decoding: RETSVDATA (0x55)
28
- * GPS ephemeris (subtype 1) and ION / UTC data (subtype 3).
28
+ * GPS ephemeris (subtype 1), BeiDou ephemeris (subtype 21) and ION / UTC
29
+ * data (subtype 3).
29
30
  *
30
- * Ported field-for-field from RTKLIB's `src/rcv/rt17.c`
31
+ * The GPS path is ported field-for-field from RTKLIB's `src/rcv/rt17.c`
31
32
  * (`decode_gps_ephemeris` / `decode_ion_utc_data`, tomojitakasu/RTKLIB,
32
33
  * Copyright (C) 2014 D. A. Cook / T. Takasu, BSD-2-Clause). All scalar
33
34
  * fields are big-endian; the semicircle angular/harmonic fields are
34
- * scaled by π to radians, exactly as RTKLIB does.
35
+ * scaled by π to radians, exactly as RTKLIB does. BeiDou (subtype 21)
36
+ * reuses the identical 176-byte Keplerian struct — established from a
37
+ * real DLF100NLD1 capture (√a ≈ 5282.6 m^½ ⇒ BeiDou MEO, i₀ ≈ 55°) — with
38
+ * the GPS→BDT time-scale conversion (week − 1356, SOW − 14 s) applied so
39
+ * the record matches `parseSbfNav`'s BDSNav output.
35
40
  *
36
41
  * Output records mirror what `parseNavFile` produces for the equivalent
37
- * RINEX 3 GPS navigation record and match the NovAtel/SBF ephemeris
38
- * paths (Keplerian `tocDate` a GPS-scale Date, angles in radians).
42
+ * RINEX 3 navigation record and match the NovAtel/SBF ephemeris paths
43
+ * (GPS: GPS-scale `tocDate`; BeiDou: naive-BDT `tocDate`; angles in rad).
39
44
  *
40
- * RTKLIB decodes only these two RETSVDATA subtypes; the GLONASS,
41
- * Galileo, BeiDou and QZSS ephemeris/almanac subtypes (RETSVDATA
42
- * subtypes 9/11/13/21/23/27, seen on the DLF100NLD1 Trimble stream) are
43
- * not handled here. Validated against a real DLF100NLD1 capture: the
44
- * subtype-1 GPS ephemerides decode to physical Keplerian elements
45
- * (√a ≈ 5153.6 m^½, i₀ ≈ 55°, week 2429).
45
+ * Still not decoded: the GLONASS/Galileo/QZSS ephemeris and almanac
46
+ * subtypes (RETSVDATA 9/11/13/23/27 on this stream).
46
47
  */
47
48
 
48
49
  interface TrimbleNavResult {
package/dist/trimble.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  parseTrimble,
3
3
  parseTrimbleNav,
4
4
  trimbleFrames
5
- } from "./chunk-U47XUQAT.js";
5
+ } from "./chunk-J2E2IKJR.js";
6
6
  export {
7
7
  parseTrimble,
8
8
  parseTrimbleNav,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnss-js",
3
- "version": "1.39.0",
3
+ "version": "1.40.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,