gnss-js 0.1.2 → 1.0.1
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/LICENSE +23 -0
- package/README.md +171 -5
- package/dist/analysis.cjs +1488 -0
- package/dist/analysis.d.cts +211 -0
- package/dist/analysis.d.ts +211 -0
- package/dist/analysis.js +14 -0
- package/dist/antex.cjs +239 -0
- package/dist/antex.d.cts +77 -0
- package/dist/antex.d.ts +77 -0
- package/dist/antex.js +8 -0
- package/dist/chunk-37QNKGTC.js +65 -0
- package/dist/chunk-4YN353Q7.js +440 -0
- package/dist/chunk-5CMSYIYM.js +214 -0
- package/dist/chunk-5S5MONFK.js +0 -0
- package/dist/chunk-6FAL6P4G.js +12 -0
- package/dist/chunk-7NOFXKET.js +433 -0
- package/dist/chunk-BJHTBYNG.js +154 -0
- package/dist/chunk-HBLU2EJ4.js +802 -0
- package/dist/chunk-HKN3PUGN.js +800 -0
- package/dist/chunk-LEEU5OIO.js +75 -0
- package/dist/chunk-LWNTWBHB.js +268 -0
- package/dist/chunk-MIIM4LDY.js +239 -0
- package/dist/chunk-SO3POWWR.js +502 -0
- package/dist/chunk-W4YMQKWH.js +622 -0
- package/dist/chunk-WP2JFDLA.js +1475 -0
- package/dist/chunk-YDMYO3YK.js +220 -0
- package/dist/constants.cjs +423 -0
- package/dist/constants.d.cts +14 -0
- package/dist/constants.d.ts +14 -0
- package/dist/constants.js +103 -0
- package/dist/coordinates.cjs +337 -0
- package/dist/coordinates.d.cts +81 -0
- package/dist/coordinates.d.ts +81 -0
- package/dist/coordinates.js +39 -0
- package/dist/ecef-CF0uAysr.d.cts +40 -0
- package/dist/ecef-CF0uAysr.d.ts +40 -0
- package/dist/ephemeris-C10stHhM.d.cts +114 -0
- package/dist/ephemeris-C10stHhM.d.ts +114 -0
- package/dist/gnss-BT6ulR17.d.cts +56 -0
- package/dist/gnss-C-tgoYNa.d.ts +56 -0
- package/dist/index.cjs +5940 -78
- package/dist/index.d.cts +17 -96
- package/dist/index.d.ts +17 -96
- package/dist/index.js +352 -415
- package/dist/nav-BAI1a9vK.d.cts +108 -0
- package/dist/nav-BAI1a9vK.d.ts +108 -0
- package/dist/nmea.cjs +247 -0
- package/dist/nmea.d.cts +37 -0
- package/dist/nmea.d.ts +37 -0
- package/dist/nmea.js +12 -0
- package/dist/ntrip.cjs +180 -0
- package/dist/ntrip.d.cts +98 -0
- package/dist/ntrip.d.ts +98 -0
- package/dist/ntrip.js +10 -0
- package/dist/orbit.cjs +511 -0
- package/dist/orbit.d.cts +117 -0
- package/dist/orbit.d.ts +117 -0
- package/dist/orbit.js +29 -0
- package/dist/parser-JPjjFgeP.d.cts +73 -0
- package/dist/parser-JPjjFgeP.d.ts +73 -0
- package/dist/rinex.cjs +1469 -0
- package/dist/rinex.d.cts +116 -0
- package/dist/rinex.d.ts +116 -0
- package/dist/rinex.js +37 -0
- package/dist/rtcm3.cjs +1512 -0
- package/dist/rtcm3.d.cts +143 -0
- package/dist/rtcm3.d.ts +143 -0
- package/dist/rtcm3.js +31 -0
- package/dist/signals.cjs +858 -0
- package/dist/signals.d.cts +93 -0
- package/dist/signals.d.ts +93 -0
- package/dist/signals.js +70 -0
- package/dist/time-DnI1VpE8.d.cts +33 -0
- package/dist/time-DnI1VpE8.d.ts +33 -0
- package/dist/time.cjs +585 -0
- package/dist/time.d.cts +307 -0
- package/dist/time.d.ts +307 -0
- package/dist/time.js +154 -0
- package/package.json +112 -14
- package/.prettierrc +0 -5
- package/jest.config.ts +0 -9
- package/test/functions.test.ts +0 -63
- package/test/gnss-scales.test.ts +0 -95
- package/test/julian.test.ts +0 -145
- package/test/rinex.test.ts +0 -11
- package/test/test.ts +0 -10
- package/test/utc.test.ts +0 -25
package/dist/orbit.cjs
ADDED
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/orbit/index.ts
|
|
21
|
+
var orbit_exports = {};
|
|
22
|
+
__export(orbit_exports, {
|
|
23
|
+
computeAllPositions: () => computeAllPositions,
|
|
24
|
+
computeDop: () => computeDop,
|
|
25
|
+
computeLiveSkyPositions: () => computeLiveSkyPositions,
|
|
26
|
+
computeSatPosition: () => computeSatPosition,
|
|
27
|
+
ecefToAzEl: () => ecefToAzEl,
|
|
28
|
+
ecefToGeodetic: () => ecefToGeodetic,
|
|
29
|
+
geodeticToEcef: () => geodeticToEcef,
|
|
30
|
+
glonassPosition: () => glonassPosition,
|
|
31
|
+
keplerPosition: () => keplerPosition,
|
|
32
|
+
navTimesFromEph: () => navTimesFromEph,
|
|
33
|
+
selectEphemeris: () => selectEphemeris
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(orbit_exports);
|
|
36
|
+
|
|
37
|
+
// src/constants/wgs84.ts
|
|
38
|
+
var WGS84_SEMI_MAJOR_AXIS = 6378137;
|
|
39
|
+
var WGS84_FLATTENING = 1 / 298.257223563;
|
|
40
|
+
var WGS84_ECCENTRICITY_SQUARED = 0.006694379990197;
|
|
41
|
+
|
|
42
|
+
// src/coordinates/ecef.ts
|
|
43
|
+
function geodeticToEcef(lat, lon, h) {
|
|
44
|
+
const N = WGS84_SEMI_MAJOR_AXIS / Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * Math.sin(lat) ** 2);
|
|
45
|
+
const x = (N + h) * Math.cos(lat) * Math.cos(lon);
|
|
46
|
+
const y = (N + h) * Math.cos(lat) * Math.sin(lon);
|
|
47
|
+
const z = ((1 - WGS84_ECCENTRICITY_SQUARED) * N + h) * Math.sin(lat);
|
|
48
|
+
return [x, y, z];
|
|
49
|
+
}
|
|
50
|
+
function ecefToGeodetic(x, y, z) {
|
|
51
|
+
const lon = Math.atan2(y, x);
|
|
52
|
+
const p = Math.sqrt(x * x + y * y);
|
|
53
|
+
if (p < 1e-10) {
|
|
54
|
+
const b = WGS84_SEMI_MAJOR_AXIS * Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED);
|
|
55
|
+
const lat2 = z >= 0 ? Math.PI / 2 : -Math.PI / 2;
|
|
56
|
+
const alt2 = Math.abs(z) - b;
|
|
57
|
+
return [lat2, lon, alt2];
|
|
58
|
+
}
|
|
59
|
+
let lat = Math.atan2(z, p * (1 - WGS84_ECCENTRICITY_SQUARED));
|
|
60
|
+
let N;
|
|
61
|
+
for (let i = 0; i < 10; i++) {
|
|
62
|
+
const sinLat2 = Math.sin(lat);
|
|
63
|
+
N = WGS84_SEMI_MAJOR_AXIS / Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * sinLat2 * sinLat2);
|
|
64
|
+
const prevLat = lat;
|
|
65
|
+
lat = Math.atan2(z + WGS84_ECCENTRICITY_SQUARED * N * sinLat2, p);
|
|
66
|
+
if (Math.abs(lat - prevLat) < 1e-15) break;
|
|
67
|
+
}
|
|
68
|
+
const sinLat = Math.sin(lat);
|
|
69
|
+
N = WGS84_SEMI_MAJOR_AXIS / Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * sinLat * sinLat);
|
|
70
|
+
const alt = p / Math.cos(lat) - N;
|
|
71
|
+
return [lat, lon, alt];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/orbit/index.ts
|
|
75
|
+
var GM_GPS = 3986005e8;
|
|
76
|
+
var GM_GAL = 3986004418e5;
|
|
77
|
+
var GM_BDS = 3986004418e5;
|
|
78
|
+
var GM_GLO = 39860044e7;
|
|
79
|
+
var OMEGA_E = 72921151467e-15;
|
|
80
|
+
var AE_GLO = 6378136;
|
|
81
|
+
var J2_GLO = 108263e-8;
|
|
82
|
+
var TWO_PI = 2 * Math.PI;
|
|
83
|
+
function gmForSystem(sys) {
|
|
84
|
+
if (sys === "E") return GM_GAL;
|
|
85
|
+
if (sys === "C") return GM_BDS;
|
|
86
|
+
return GM_GPS;
|
|
87
|
+
}
|
|
88
|
+
function keplerPosition(eph, t) {
|
|
89
|
+
const GM = gmForSystem(eph.system);
|
|
90
|
+
const a = eph.sqrtA * eph.sqrtA;
|
|
91
|
+
const n0 = Math.sqrt(GM / (a * a * a));
|
|
92
|
+
const n = n0 + eph.deltaN;
|
|
93
|
+
let tk = t - eph.toe;
|
|
94
|
+
if (tk > 302400) tk -= 604800;
|
|
95
|
+
if (tk < -302400) tk += 604800;
|
|
96
|
+
const Mk = eph.m0 + n * tk;
|
|
97
|
+
let Ek = Mk;
|
|
98
|
+
for (let i = 0; i < 10; i++) {
|
|
99
|
+
const dE = (Mk - (Ek - eph.e * Math.sin(Ek))) / (1 - eph.e * Math.cos(Ek));
|
|
100
|
+
Ek += dE;
|
|
101
|
+
if (Math.abs(dE) < 1e-12) break;
|
|
102
|
+
}
|
|
103
|
+
const sinE = Math.sin(Ek);
|
|
104
|
+
const cosE = Math.cos(Ek);
|
|
105
|
+
const vk = Math.atan2(Math.sqrt(1 - eph.e * eph.e) * sinE, cosE - eph.e);
|
|
106
|
+
const phik = vk + eph.omega;
|
|
107
|
+
const sin2phi = Math.sin(2 * phik);
|
|
108
|
+
const cos2phi = Math.cos(2 * phik);
|
|
109
|
+
const duk = eph.cus * sin2phi + eph.cuc * cos2phi;
|
|
110
|
+
const drk = eph.crs * sin2phi + eph.crc * cos2phi;
|
|
111
|
+
const dik = eph.cis * sin2phi + eph.cic * cos2phi;
|
|
112
|
+
const uk = phik + duk;
|
|
113
|
+
const rk = a * (1 - eph.e * cosE) + drk;
|
|
114
|
+
const ik = eph.i0 + dik + eph.idot * tk;
|
|
115
|
+
const xp = rk * Math.cos(uk);
|
|
116
|
+
const yp = rk * Math.sin(uk);
|
|
117
|
+
const isBdsGeo = eph.system === "C" && Math.abs(eph.i0) < 0.1 && a > 4e7;
|
|
118
|
+
if (isBdsGeo) {
|
|
119
|
+
const omegak2 = eph.omega0 + eph.omegaDot * tk - OMEGA_E * eph.toe;
|
|
120
|
+
const cosO2 = Math.cos(omegak2);
|
|
121
|
+
const sinO2 = Math.sin(omegak2);
|
|
122
|
+
const cosI2 = Math.cos(ik);
|
|
123
|
+
const sinI2 = Math.sin(ik);
|
|
124
|
+
const xg = xp * cosO2 - yp * cosI2 * sinO2;
|
|
125
|
+
const yg = xp * sinO2 + yp * cosI2 * cosO2;
|
|
126
|
+
const zg = yp * sinI2;
|
|
127
|
+
const phi = OMEGA_E * tk;
|
|
128
|
+
const cosPhi = Math.cos(phi);
|
|
129
|
+
const sinPhi = Math.sin(phi);
|
|
130
|
+
const COS5 = Math.cos(-5 * Math.PI / 180);
|
|
131
|
+
const SIN5 = Math.sin(-5 * Math.PI / 180);
|
|
132
|
+
return {
|
|
133
|
+
prn: eph.prn,
|
|
134
|
+
x: xg * cosPhi + yg * sinPhi * COS5 + zg * sinPhi * SIN5,
|
|
135
|
+
y: -xg * sinPhi + yg * cosPhi * COS5 + zg * cosPhi * SIN5,
|
|
136
|
+
z: -yg * SIN5 + zg * COS5
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const omegak = eph.omega0 + (eph.omegaDot - OMEGA_E) * tk - OMEGA_E * eph.toe;
|
|
140
|
+
const cosO = Math.cos(omegak);
|
|
141
|
+
const sinO = Math.sin(omegak);
|
|
142
|
+
const cosI = Math.cos(ik);
|
|
143
|
+
const sinI = Math.sin(ik);
|
|
144
|
+
return {
|
|
145
|
+
prn: eph.prn,
|
|
146
|
+
x: xp * cosO - yp * cosI * sinO,
|
|
147
|
+
y: xp * sinO + yp * cosI * cosO,
|
|
148
|
+
z: yp * sinI
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function gloDerivatives(state, acc) {
|
|
152
|
+
const [x, y, z, vx, vy, vz] = state;
|
|
153
|
+
const [ax, ay, az] = acc;
|
|
154
|
+
const r = Math.sqrt(x * x + y * y + z * z);
|
|
155
|
+
const r2 = r * r;
|
|
156
|
+
const r3 = r2 * r;
|
|
157
|
+
const r5 = r2 * r3;
|
|
158
|
+
const mu_r3 = GM_GLO / r3;
|
|
159
|
+
const j2_term = 1.5 * J2_GLO * AE_GLO * AE_GLO / r5;
|
|
160
|
+
const dvx = -mu_r3 * x + j2_term * x * (5 * z * z / r2 - 1) + OMEGA_E * OMEGA_E * x + 2 * OMEGA_E * vy + ax;
|
|
161
|
+
const dvy = -mu_r3 * y + j2_term * y * (5 * z * z / r2 - 1) + OMEGA_E * OMEGA_E * y - 2 * OMEGA_E * vx + ay;
|
|
162
|
+
const dvz = -mu_r3 * z + j2_term * z * (5 * z * z / r2 - 3) + az;
|
|
163
|
+
return [vx, vy, vz, dvx, dvy, dvz];
|
|
164
|
+
}
|
|
165
|
+
function rk4Step(state, acc, dt) {
|
|
166
|
+
const k1 = gloDerivatives(state, acc);
|
|
167
|
+
const s2 = state.map((v, i) => v + k1[i] * dt / 2);
|
|
168
|
+
const k2 = gloDerivatives(s2, acc);
|
|
169
|
+
const s3 = state.map((v, i) => v + k2[i] * dt / 2);
|
|
170
|
+
const k3 = gloDerivatives(s3, acc);
|
|
171
|
+
const s4 = state.map((v, i) => v + k3[i] * dt);
|
|
172
|
+
const k4 = gloDerivatives(s4, acc);
|
|
173
|
+
return state.map(
|
|
174
|
+
(v, i) => v + dt / 6 * (k1[i] + 2 * k2[i] + 2 * k3[i] + k4[i])
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
function glonassPosition(eph, tUtc) {
|
|
178
|
+
const ephTime = eph.tocDate.getTime() / 1e3;
|
|
179
|
+
const dt = tUtc - ephTime;
|
|
180
|
+
let state = [
|
|
181
|
+
eph.x * 1e3,
|
|
182
|
+
eph.y * 1e3,
|
|
183
|
+
eph.z * 1e3,
|
|
184
|
+
eph.xDot * 1e3,
|
|
185
|
+
eph.yDot * 1e3,
|
|
186
|
+
eph.zDot * 1e3
|
|
187
|
+
];
|
|
188
|
+
const acc = [
|
|
189
|
+
eph.xAcc * 1e3,
|
|
190
|
+
eph.yAcc * 1e3,
|
|
191
|
+
eph.zAcc * 1e3
|
|
192
|
+
];
|
|
193
|
+
const step = dt > 0 ? 60 : -60;
|
|
194
|
+
const nSteps = Math.floor(Math.abs(dt) / 60);
|
|
195
|
+
const remainder = dt - nSteps * step;
|
|
196
|
+
for (let i = 0; i < nSteps; i++) {
|
|
197
|
+
state = rk4Step(state, acc, step);
|
|
198
|
+
}
|
|
199
|
+
if (Math.abs(remainder) > 1e-3) {
|
|
200
|
+
state = rk4Step(state, acc, remainder);
|
|
201
|
+
}
|
|
202
|
+
return { prn: eph.prn, x: state[0], y: state[1], z: state[2] };
|
|
203
|
+
}
|
|
204
|
+
function ecefToAzEl(rxX, rxY, rxZ, satX, satY, satZ) {
|
|
205
|
+
const [lat, lon] = ecefToGeodetic(rxX, rxY, rxZ);
|
|
206
|
+
const dx = satX - rxX;
|
|
207
|
+
const dy = satY - rxY;
|
|
208
|
+
const dz = satZ - rxZ;
|
|
209
|
+
const sinLat = Math.sin(lat);
|
|
210
|
+
const cosLat = Math.cos(lat);
|
|
211
|
+
const sinLon = Math.sin(lon);
|
|
212
|
+
const cosLon = Math.cos(lon);
|
|
213
|
+
const east = -sinLon * dx + cosLon * dy;
|
|
214
|
+
const north = -sinLat * cosLon * dx - sinLat * sinLon * dy + cosLat * dz;
|
|
215
|
+
const up = cosLat * cosLon * dx + cosLat * sinLon * dy + sinLat * dz;
|
|
216
|
+
const az = Math.atan2(east, north);
|
|
217
|
+
const el = Math.atan2(up, Math.sqrt(east * east + north * north));
|
|
218
|
+
return { az: (az + TWO_PI) % TWO_PI, el };
|
|
219
|
+
}
|
|
220
|
+
function computeDop(satAzEls) {
|
|
221
|
+
const n = satAzEls.length;
|
|
222
|
+
if (n < 4) return null;
|
|
223
|
+
const H = [];
|
|
224
|
+
for (const { az, el } of satAzEls) {
|
|
225
|
+
const cosEl = Math.cos(el);
|
|
226
|
+
H.push([cosEl * Math.sin(az), cosEl * Math.cos(az), Math.sin(el), 1]);
|
|
227
|
+
}
|
|
228
|
+
const HtH = Array.from({ length: 4 }, () => new Array(4).fill(0));
|
|
229
|
+
for (let i = 0; i < 4; i++) {
|
|
230
|
+
for (let j = 0; j < 4; j++) {
|
|
231
|
+
let sum = 0;
|
|
232
|
+
for (let k = 0; k < n; k++) {
|
|
233
|
+
sum += H[k][i] * H[k][j];
|
|
234
|
+
}
|
|
235
|
+
HtH[i][j] = sum;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const Q = invert4x4(HtH);
|
|
239
|
+
if (!Q) return null;
|
|
240
|
+
const gdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2] + Q[3][3]);
|
|
241
|
+
const pdop = Math.sqrt(Q[0][0] + Q[1][1] + Q[2][2]);
|
|
242
|
+
const hdop = Math.sqrt(Q[0][0] + Q[1][1]);
|
|
243
|
+
const vdop = Math.sqrt(Q[2][2]);
|
|
244
|
+
return { gdop, pdop, hdop, vdop };
|
|
245
|
+
}
|
|
246
|
+
function invert4x4(m) {
|
|
247
|
+
const a = m.flat();
|
|
248
|
+
const inv = new Array(16).fill(0);
|
|
249
|
+
inv[0] = a[5] * a[10] * a[15] - a[5] * a[11] * a[14] - a[9] * a[6] * a[15] + a[9] * a[7] * a[14] + a[13] * a[6] * a[11] - a[13] * a[7] * a[10];
|
|
250
|
+
inv[4] = -a[4] * a[10] * a[15] + a[4] * a[11] * a[14] + a[8] * a[6] * a[15] - a[8] * a[7] * a[14] - a[12] * a[6] * a[11] + a[12] * a[7] * a[10];
|
|
251
|
+
inv[8] = a[4] * a[9] * a[15] - a[4] * a[11] * a[13] - a[8] * a[5] * a[15] + a[8] * a[7] * a[13] + a[12] * a[5] * a[11] - a[12] * a[7] * a[9];
|
|
252
|
+
inv[12] = -a[4] * a[9] * a[14] + a[4] * a[10] * a[13] + a[8] * a[5] * a[14] - a[8] * a[6] * a[13] - a[12] * a[5] * a[10] + a[12] * a[6] * a[9];
|
|
253
|
+
inv[1] = -a[1] * a[10] * a[15] + a[1] * a[11] * a[14] + a[9] * a[2] * a[15] - a[9] * a[3] * a[14] - a[13] * a[2] * a[11] + a[13] * a[3] * a[10];
|
|
254
|
+
inv[5] = a[0] * a[10] * a[15] - a[0] * a[11] * a[14] - a[8] * a[2] * a[15] + a[8] * a[3] * a[14] + a[12] * a[2] * a[11] - a[12] * a[3] * a[10];
|
|
255
|
+
inv[9] = -a[0] * a[9] * a[15] + a[0] * a[11] * a[13] + a[8] * a[1] * a[15] - a[8] * a[3] * a[13] - a[12] * a[1] * a[11] + a[12] * a[3] * a[9];
|
|
256
|
+
inv[13] = a[0] * a[9] * a[14] - a[0] * a[10] * a[13] - a[8] * a[1] * a[14] + a[8] * a[2] * a[13] + a[12] * a[1] * a[10] - a[12] * a[2] * a[9];
|
|
257
|
+
inv[2] = a[1] * a[6] * a[15] - a[1] * a[7] * a[14] - a[5] * a[2] * a[15] + a[5] * a[3] * a[14] + a[13] * a[2] * a[7] - a[13] * a[3] * a[6];
|
|
258
|
+
inv[6] = -a[0] * a[6] * a[15] + a[0] * a[7] * a[14] + a[4] * a[2] * a[15] - a[4] * a[3] * a[14] - a[12] * a[2] * a[7] + a[12] * a[3] * a[6];
|
|
259
|
+
inv[10] = a[0] * a[5] * a[15] - a[0] * a[7] * a[13] - a[4] * a[1] * a[15] + a[4] * a[3] * a[13] + a[12] * a[1] * a[7] - a[12] * a[3] * a[5];
|
|
260
|
+
inv[14] = -a[0] * a[5] * a[14] + a[0] * a[6] * a[13] + a[4] * a[1] * a[14] - a[4] * a[2] * a[13] - a[12] * a[1] * a[6] + a[12] * a[2] * a[5];
|
|
261
|
+
inv[3] = -a[1] * a[6] * a[11] + a[1] * a[7] * a[10] + a[5] * a[2] * a[11] - a[5] * a[3] * a[10] - a[9] * a[2] * a[7] + a[9] * a[3] * a[6];
|
|
262
|
+
inv[7] = a[0] * a[6] * a[11] - a[0] * a[7] * a[10] - a[4] * a[2] * a[11] + a[4] * a[3] * a[10] + a[8] * a[2] * a[7] - a[8] * a[3] * a[6];
|
|
263
|
+
inv[11] = -a[0] * a[5] * a[11] + a[0] * a[7] * a[9] + a[4] * a[1] * a[11] - a[4] * a[3] * a[9] - a[8] * a[1] * a[7] + a[8] * a[3] * a[5];
|
|
264
|
+
inv[15] = a[0] * a[5] * a[10] - a[0] * a[6] * a[9] - a[4] * a[1] * a[10] + a[4] * a[2] * a[9] + a[8] * a[1] * a[6] - a[8] * a[2] * a[5];
|
|
265
|
+
let det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
|
|
266
|
+
if (Math.abs(det) < 1e-20) return null;
|
|
267
|
+
det = 1 / det;
|
|
268
|
+
const result = Array.from(
|
|
269
|
+
{ length: 4 },
|
|
270
|
+
() => new Array(4).fill(0)
|
|
271
|
+
);
|
|
272
|
+
for (let i = 0; i < 4; i++) {
|
|
273
|
+
for (let j = 0; j < 4; j++) {
|
|
274
|
+
result[i][j] = inv[i * 4 + j] * det;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
function selectEphemeris(ephemerides, prn, timeMs) {
|
|
280
|
+
let best = null;
|
|
281
|
+
let bestDt = Infinity;
|
|
282
|
+
for (const eph of ephemerides) {
|
|
283
|
+
if (eph.prn !== prn) continue;
|
|
284
|
+
const ephTime = eph.tocDate.getTime();
|
|
285
|
+
const dt = Math.abs(timeMs - ephTime);
|
|
286
|
+
if (dt > 4 * 3600 * 1e3) continue;
|
|
287
|
+
if (dt < bestDt) {
|
|
288
|
+
bestDt = dt;
|
|
289
|
+
best = eph;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return best;
|
|
293
|
+
}
|
|
294
|
+
function computeSatPosition(eph, timeMs) {
|
|
295
|
+
if (eph.system === "R" || eph.system === "S") {
|
|
296
|
+
return glonassPosition(eph, timeMs / 1e3);
|
|
297
|
+
}
|
|
298
|
+
const GPS_EPOCH = Date.UTC(1980, 0, 6);
|
|
299
|
+
const gpsSeconds = (timeMs - GPS_EPOCH) / 1e3;
|
|
300
|
+
const tow = gpsSeconds % (7 * 86400);
|
|
301
|
+
return keplerPosition(eph, tow);
|
|
302
|
+
}
|
|
303
|
+
function navTimesFromEph(ephs) {
|
|
304
|
+
let minT = Infinity, maxT = -Infinity;
|
|
305
|
+
for (const eph of ephs) {
|
|
306
|
+
const t = eph.tocDate.getTime();
|
|
307
|
+
if (t < minT) minT = t;
|
|
308
|
+
if (t > maxT) maxT = t;
|
|
309
|
+
}
|
|
310
|
+
const span = maxT - minT;
|
|
311
|
+
const step = Math.max(span / 500, 3e4);
|
|
312
|
+
const times = [];
|
|
313
|
+
for (let t = minT; t <= maxT; t += step) times.push(t);
|
|
314
|
+
if (times.length === 0 && isFinite(minT)) times.push(minT);
|
|
315
|
+
return times;
|
|
316
|
+
}
|
|
317
|
+
function computeAllPositions(ephemerides, times, rxPos) {
|
|
318
|
+
const ephByPrn = /* @__PURE__ */ new Map();
|
|
319
|
+
for (const eph of ephemerides) {
|
|
320
|
+
let arr = ephByPrn.get(eph.prn);
|
|
321
|
+
if (!arr) {
|
|
322
|
+
arr = [];
|
|
323
|
+
ephByPrn.set(eph.prn, arr);
|
|
324
|
+
}
|
|
325
|
+
arr.push(eph);
|
|
326
|
+
}
|
|
327
|
+
const prns = [...ephByPrn.keys()].sort();
|
|
328
|
+
const positions = {};
|
|
329
|
+
for (const prn of prns) positions[prn] = [];
|
|
330
|
+
let sinLat = 0, cosLat = 1, sinLon = 0, cosLon = 1;
|
|
331
|
+
let hasRx = false;
|
|
332
|
+
if (rxPos) {
|
|
333
|
+
const [rxX, rxY, rxZ] = rxPos;
|
|
334
|
+
if (rxX !== 0 || rxY !== 0 || rxZ !== 0) {
|
|
335
|
+
hasRx = true;
|
|
336
|
+
const [gLat, gLon] = ecefToGeodetic(rxX, rxY, rxZ);
|
|
337
|
+
sinLat = Math.sin(gLat);
|
|
338
|
+
cosLat = Math.cos(gLat);
|
|
339
|
+
sinLon = Math.sin(gLon);
|
|
340
|
+
cosLon = Math.cos(gLon);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
for (const t of times) {
|
|
344
|
+
for (const prn of prns) {
|
|
345
|
+
const eph = selectBest(ephByPrn.get(prn), t);
|
|
346
|
+
if (!eph) {
|
|
347
|
+
positions[prn].push(null);
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
const pos = computeSatPosition(eph, t);
|
|
351
|
+
const [geoLat, geoLon] = ecefToGeodetic(pos.x, pos.y, pos.z);
|
|
352
|
+
let az = 0, el = 0;
|
|
353
|
+
if (hasRx) {
|
|
354
|
+
const dx = pos.x - rxPos[0];
|
|
355
|
+
const dy = pos.y - rxPos[1];
|
|
356
|
+
const dz = pos.z - rxPos[2];
|
|
357
|
+
const east = -sinLon * dx + cosLon * dy;
|
|
358
|
+
const north = -sinLat * cosLon * dx - sinLat * sinLon * dy + cosLat * dz;
|
|
359
|
+
const up = cosLat * cosLon * dx + cosLat * sinLon * dy + sinLat * dz;
|
|
360
|
+
az = (Math.atan2(east, north) + TWO_PI) % TWO_PI;
|
|
361
|
+
el = Math.atan2(up, Math.sqrt(east * east + north * north));
|
|
362
|
+
}
|
|
363
|
+
positions[prn].push({ lat: geoLat, lon: geoLon, az, el });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return { prns, times, positions };
|
|
367
|
+
}
|
|
368
|
+
function selectBest(ephs, timeMs) {
|
|
369
|
+
let best = null;
|
|
370
|
+
let bestDt = Infinity;
|
|
371
|
+
for (const eph of ephs) {
|
|
372
|
+
const dt = Math.abs(timeMs - eph.tocDate.getTime());
|
|
373
|
+
const maxAge = eph.system === "R" || eph.system === "S" ? 18e5 : 4 * 36e5;
|
|
374
|
+
if (dt > maxAge) continue;
|
|
375
|
+
if (dt < bestDt) {
|
|
376
|
+
bestDt = dt;
|
|
377
|
+
best = eph;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return best;
|
|
381
|
+
}
|
|
382
|
+
var GPS_EPOCH_MS = Date.UTC(1980, 0, 6);
|
|
383
|
+
var BDS_EPOCH_MS = Date.UTC(2006, 0, 1);
|
|
384
|
+
var GAL_EPOCH_MS = GPS_EPOCH_MS;
|
|
385
|
+
function ephInfoToEphemeris(info) {
|
|
386
|
+
const sys = info.prn.charAt(0);
|
|
387
|
+
if (sys === "R") {
|
|
388
|
+
if (info.x === void 0 || info.y === void 0 || info.z === void 0)
|
|
389
|
+
return null;
|
|
390
|
+
if (info.vx === void 0 || info.vy === void 0 || info.vz === void 0)
|
|
391
|
+
return null;
|
|
392
|
+
const now = new Date(info.lastReceived);
|
|
393
|
+
const utcMidnight = Date.UTC(
|
|
394
|
+
now.getUTCFullYear(),
|
|
395
|
+
now.getUTCMonth(),
|
|
396
|
+
now.getUTCDate()
|
|
397
|
+
);
|
|
398
|
+
const tbSec = (info.tb ?? 0) * 900;
|
|
399
|
+
const moscowOffset = 3 * 3600;
|
|
400
|
+
const tocMs = utcMidnight + (tbSec - moscowOffset) * 1e3;
|
|
401
|
+
return {
|
|
402
|
+
system: "R",
|
|
403
|
+
prn: info.prn,
|
|
404
|
+
tocDate: new Date(tocMs),
|
|
405
|
+
tauN: -(info.af0 ?? 0),
|
|
406
|
+
// RTCM stores -tauN
|
|
407
|
+
gammaN: info.gammaN ?? 0,
|
|
408
|
+
messageFrameTime: 0,
|
|
409
|
+
x: info.x,
|
|
410
|
+
xDot: info.vx,
|
|
411
|
+
xAcc: info.ax ?? 0,
|
|
412
|
+
y: info.y,
|
|
413
|
+
yDot: info.vy,
|
|
414
|
+
yAcc: info.ay ?? 0,
|
|
415
|
+
z: info.z,
|
|
416
|
+
zDot: info.vz,
|
|
417
|
+
zAcc: info.az ?? 0,
|
|
418
|
+
health: info.health,
|
|
419
|
+
freqNum: info.freqChannel ?? 0
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
if (info.sqrtA === void 0 || info.eccentricity === void 0 || info.inclination === void 0 || info.omega0 === void 0 || info.argPerigee === void 0 || info.meanAnomaly === void 0 || info.toe === void 0 || info.week === void 0)
|
|
423
|
+
return null;
|
|
424
|
+
let epochMs;
|
|
425
|
+
const tocSec = info.toc ?? info.toe;
|
|
426
|
+
if (sys === "C") {
|
|
427
|
+
epochMs = BDS_EPOCH_MS;
|
|
428
|
+
} else if (sys === "E") {
|
|
429
|
+
epochMs = GAL_EPOCH_MS;
|
|
430
|
+
} else {
|
|
431
|
+
epochMs = GPS_EPOCH_MS;
|
|
432
|
+
}
|
|
433
|
+
const tocDate = new Date(epochMs + info.week * 7 * 864e5 + tocSec * 1e3);
|
|
434
|
+
return {
|
|
435
|
+
system: sys,
|
|
436
|
+
prn: info.prn,
|
|
437
|
+
toc: tocSec,
|
|
438
|
+
tocDate,
|
|
439
|
+
af0: info.af0 ?? 0,
|
|
440
|
+
af1: info.af1 ?? 0,
|
|
441
|
+
af2: info.af2 ?? 0,
|
|
442
|
+
iode: info.iode ?? 0,
|
|
443
|
+
crs: info.crs ?? 0,
|
|
444
|
+
deltaN: info.deltaN ?? 0,
|
|
445
|
+
m0: info.meanAnomaly,
|
|
446
|
+
cuc: info.cuc ?? 0,
|
|
447
|
+
e: info.eccentricity,
|
|
448
|
+
cus: info.cus ?? 0,
|
|
449
|
+
sqrtA: info.sqrtA,
|
|
450
|
+
toe: info.toe,
|
|
451
|
+
cic: info.cic ?? 0,
|
|
452
|
+
omega0: info.omega0,
|
|
453
|
+
cis: info.cis ?? 0,
|
|
454
|
+
i0: info.inclination,
|
|
455
|
+
crc: info.crc ?? 0,
|
|
456
|
+
omega: info.argPerigee,
|
|
457
|
+
omegaDot: info.omegaDot ?? 0,
|
|
458
|
+
idot: info.idot ?? 0,
|
|
459
|
+
week: info.week,
|
|
460
|
+
svHealth: info.health,
|
|
461
|
+
tgd: 0
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
function computeLiveSkyPositions(ephemerides, rxPos, cn0Map) {
|
|
465
|
+
const now = Date.now();
|
|
466
|
+
const result = [];
|
|
467
|
+
const [rxX, rxY, rxZ] = rxPos;
|
|
468
|
+
const [rxLat, rxLon] = ecefToGeodetic(rxX, rxY, rxZ);
|
|
469
|
+
const sinRxLat = Math.sin(rxLat), cosRxLat = Math.cos(rxLat);
|
|
470
|
+
const sinRxLon = Math.sin(rxLon), cosRxLon = Math.cos(rxLon);
|
|
471
|
+
for (const info of ephemerides.values()) {
|
|
472
|
+
const eph = ephInfoToEphemeris(info);
|
|
473
|
+
if (!eph) continue;
|
|
474
|
+
try {
|
|
475
|
+
const pos = computeSatPosition(eph, now);
|
|
476
|
+
const [satLat, satLon] = ecefToGeodetic(pos.x, pos.y, pos.z);
|
|
477
|
+
const dx = pos.x - rxX, dy = pos.y - rxY, dz = pos.z - rxZ;
|
|
478
|
+
const east = -sinRxLon * dx + cosRxLon * dy;
|
|
479
|
+
const north = -sinRxLat * cosRxLon * dx - sinRxLat * sinRxLon * dy + cosRxLat * dz;
|
|
480
|
+
const up = cosRxLat * cosRxLon * dx + cosRxLat * sinRxLon * dy + sinRxLat * dz;
|
|
481
|
+
const az = (Math.atan2(east, north) + TWO_PI) % TWO_PI;
|
|
482
|
+
const el = Math.atan2(up, Math.sqrt(east * east + north * north));
|
|
483
|
+
if (el > -0.05) {
|
|
484
|
+
result.push({
|
|
485
|
+
prn: info.prn,
|
|
486
|
+
az,
|
|
487
|
+
el,
|
|
488
|
+
lat: satLat,
|
|
489
|
+
lon: satLon,
|
|
490
|
+
cn0: cn0Map?.get(info.prn)
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
} catch {
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return result;
|
|
497
|
+
}
|
|
498
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
499
|
+
0 && (module.exports = {
|
|
500
|
+
computeAllPositions,
|
|
501
|
+
computeDop,
|
|
502
|
+
computeLiveSkyPositions,
|
|
503
|
+
computeSatPosition,
|
|
504
|
+
ecefToAzEl,
|
|
505
|
+
ecefToGeodetic,
|
|
506
|
+
geodeticToEcef,
|
|
507
|
+
glonassPosition,
|
|
508
|
+
keplerPosition,
|
|
509
|
+
navTimesFromEph,
|
|
510
|
+
selectEphemeris
|
|
511
|
+
});
|
package/dist/orbit.d.cts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { E as Ephemeris, G as GlonassEphemeris, K as KeplerEphemeris } from './nav-BAI1a9vK.cjs';
|
|
2
|
+
import { E as EphemerisInfo } from './ephemeris-C10stHhM.cjs';
|
|
3
|
+
export { e as ecefToGeodetic, g as geodeticToEcef } from './ecef-CF0uAysr.cjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Satellite orbit computation from broadcast ephemerides.
|
|
7
|
+
* Supports GPS, Galileo, BeiDou (Keplerian) and GLONASS (Runge-Kutta).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface SatPosition {
|
|
11
|
+
prn: string;
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
z: number;
|
|
15
|
+
}
|
|
16
|
+
interface SatAzEl {
|
|
17
|
+
prn: string;
|
|
18
|
+
az: number;
|
|
19
|
+
el: number;
|
|
20
|
+
lat: number;
|
|
21
|
+
lon: number;
|
|
22
|
+
cn0?: number;
|
|
23
|
+
}
|
|
24
|
+
interface DopValues {
|
|
25
|
+
gdop: number;
|
|
26
|
+
pdop: number;
|
|
27
|
+
hdop: number;
|
|
28
|
+
vdop: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Compute satellite ECEF position from Keplerian ephemeris.
|
|
32
|
+
* @param eph Broadcast ephemeris
|
|
33
|
+
* @param t GPS time of week in seconds
|
|
34
|
+
*/
|
|
35
|
+
declare function keplerPosition(eph: KeplerEphemeris, t: number): SatPosition;
|
|
36
|
+
/**
|
|
37
|
+
* Compute GLONASS satellite ECEF position.
|
|
38
|
+
* @param eph GLONASS ephemeris
|
|
39
|
+
* @param tUtc Target time as UTC seconds since Unix epoch
|
|
40
|
+
*/
|
|
41
|
+
declare function glonassPosition(eph: GlonassEphemeris, tUtc: number): SatPosition;
|
|
42
|
+
/**
|
|
43
|
+
* Compute azimuth and elevation from receiver ECEF to satellite ECEF.
|
|
44
|
+
* @param rxX Receiver X in meters (ECEF)
|
|
45
|
+
* @param rxY Receiver Y in meters (ECEF)
|
|
46
|
+
* @param rxZ Receiver Z in meters (ECEF)
|
|
47
|
+
* @param satX Satellite X in meters (ECEF)
|
|
48
|
+
* @param satY Satellite Y in meters (ECEF)
|
|
49
|
+
* @param satZ Satellite Z in meters (ECEF)
|
|
50
|
+
* @returns {az: radians, el: radians}
|
|
51
|
+
*/
|
|
52
|
+
declare function ecefToAzEl(rxX: number, rxY: number, rxZ: number, satX: number, satY: number, satZ: number): {
|
|
53
|
+
az: number;
|
|
54
|
+
el: number;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Compute DOP values from a set of satellite az/el.
|
|
58
|
+
* @param satAzEls Array of satellite azimuth/elevation pairs (radians)
|
|
59
|
+
* @returns DOP values (GDOP, PDOP, HDOP, VDOP), or null if fewer than 4 satellites
|
|
60
|
+
*/
|
|
61
|
+
declare function computeDop(satAzEls: {
|
|
62
|
+
az: number;
|
|
63
|
+
el: number;
|
|
64
|
+
}[]): DopValues | null;
|
|
65
|
+
/**
|
|
66
|
+
* Find the best ephemeris for a given PRN at a given time.
|
|
67
|
+
* Selects the one with the closest epoch (toe/toc) to the target time.
|
|
68
|
+
* @param ephemerides Array of candidate ephemerides
|
|
69
|
+
* @param prn Satellite PRN identifier (e.g. "G01")
|
|
70
|
+
* @param timeMs Target time in Unix milliseconds
|
|
71
|
+
*/
|
|
72
|
+
declare function selectEphemeris(ephemerides: Ephemeris[], prn: string, timeMs: number): Ephemeris | null;
|
|
73
|
+
/**
|
|
74
|
+
* Compute satellite position from ephemeris.
|
|
75
|
+
* @param eph Ephemeris record
|
|
76
|
+
* @param timeMs Target time in Unix milliseconds
|
|
77
|
+
* @returns Satellite position with ECEF coordinates in meters
|
|
78
|
+
*/
|
|
79
|
+
declare function computeSatPosition(eph: Ephemeris, timeMs: number): SatPosition;
|
|
80
|
+
interface EpochSkyData {
|
|
81
|
+
time: number;
|
|
82
|
+
satellites: SatAzEl[];
|
|
83
|
+
dop: DopValues | null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Derive evenly-spaced epoch times from an ephemeris set's toc range.
|
|
87
|
+
* @param ephs Array of ephemerides to derive the time span from
|
|
88
|
+
* @returns Array of epoch timestamps in Unix milliseconds
|
|
89
|
+
*/
|
|
90
|
+
declare function navTimesFromEph(ephs: Ephemeris[]): number[];
|
|
91
|
+
interface SatPoint {
|
|
92
|
+
lat: number;
|
|
93
|
+
lon: number;
|
|
94
|
+
az: number;
|
|
95
|
+
el: number;
|
|
96
|
+
}
|
|
97
|
+
interface AllPositionsData {
|
|
98
|
+
/** All unique PRNs sorted. */
|
|
99
|
+
prns: string[];
|
|
100
|
+
/** Epoch timestamps in Unix ms. */
|
|
101
|
+
times: number[];
|
|
102
|
+
/** positions[prn][epochIdx] — null if no valid ephemeris. */
|
|
103
|
+
positions: Record<string, (SatPoint | null)[]>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Compute satellite positions for ALL PRNs at all times in a single pass.
|
|
107
|
+
* Returns geodetic (lat/lon) for ground tracks and az/el for sky plot.
|
|
108
|
+
* When rxPos is provided, az/el are computed; otherwise they are 0.
|
|
109
|
+
*/
|
|
110
|
+
declare function computeAllPositions(ephemerides: Ephemeris[], times: number[], rxPos?: [number, number, number]): AllPositionsData;
|
|
111
|
+
/**
|
|
112
|
+
* Compute current satellite az/el from live RTCM3 ephemeris data.
|
|
113
|
+
* Returns array of SatAzEl for all satellites with valid ephemeris.
|
|
114
|
+
*/
|
|
115
|
+
declare function computeLiveSkyPositions(ephemerides: Map<string, EphemerisInfo>, rxPos: [number, number, number], cn0Map?: Map<string, number>): SatAzEl[];
|
|
116
|
+
|
|
117
|
+
export { type AllPositionsData, type DopValues, type EpochSkyData, type SatAzEl, type SatPoint, type SatPosition, computeAllPositions, computeDop, computeLiveSkyPositions, computeSatPosition, ecefToAzEl, glonassPosition, keplerPosition, navTimesFromEph, selectEphemeris };
|