@saber-usa/node-common 1.6.207 → 1.7.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/README.md +1 -1
- package/package.json +4 -4
- package/src/FrameConverter.js +6 -9
- package/src/LLA.js +1 -3
- package/src/LaunchNominalClass.js +20 -18
- package/src/NodeVector3D.js +2 -2
- package/src/OrbitUtils.js +43 -44
- package/src/PropagateUtils.js +100 -0
- package/src/ShadowGEOCalculator.js +29 -27
- package/src/TimeConverter.js +1 -3
- package/src/astro.js +93 -96
- package/src/{ballisticPropagator/ballisticPropagator.js → ballisticPropagator.js} +3 -13
- package/src/{checkNetwork.js → checkNetwork.cjs} +1 -0
- package/src/constants.js +15 -18
- package/src/fixDate.js +9 -16
- package/src/index.js +46 -10
- package/src/launchNominal.js +5 -12
- package/src/{loggerFactory.js → loggerFactory.cjs} +1 -1
- package/src/s3.js +9 -9
- package/src/transform.js +2 -7
- package/src/udl.js +8 -9
- package/src/utils.js +2 -2
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ A transformer for object keys is provided. This will take an object and transfor
|
|
|
36
36
|
## Publishing changes
|
|
37
37
|
|
|
38
38
|
1. Change the version in package.json
|
|
39
|
-
2. Run `npm publish
|
|
39
|
+
2. Run `npm publish && npm install`
|
|
40
40
|
3. Enable GPG in GIT: `git config --global commit.gpgsign true`
|
|
41
41
|
4. Run `gpg --list-keys` to get your <id>
|
|
42
42
|
5. Tell the GIT to use your key `git config user.signingkey <id>`
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saber-usa/node-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Common node functions for Saber",
|
|
5
5
|
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"lint": "eslint . --ext js",
|
|
8
9
|
"lint:fix": "eslint . --ext js --fix",
|
|
@@ -17,10 +18,9 @@
|
|
|
17
18
|
"license": "ISC",
|
|
18
19
|
"dependencies": {
|
|
19
20
|
"@aws-sdk/client-s3": "^3.666.0",
|
|
20
|
-
"date-fns": "^
|
|
21
|
-
"local-sidereal-time": "^1.0.4",
|
|
21
|
+
"date-fns": "^4.1.0",
|
|
22
22
|
"lodash": "4.17.21",
|
|
23
|
-
"mathjs": "^
|
|
23
|
+
"mathjs": "^14.7.0",
|
|
24
24
|
"pious-squid": "^2.3.0",
|
|
25
25
|
"plotly": "^1.0.6",
|
|
26
26
|
"satellite.js": "^6.0.1",
|
package/src/FrameConverter.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
// FrameConverter.js - Port from C# FrameConverter.cs
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
const {TimeConverter, RaDec} = require("./TimeConverter.js");
|
|
7
|
-
const {norm, cross} = require("mathjs");
|
|
2
|
+
import {Matrix3D, Vector3D} from "pious-squid";
|
|
3
|
+
import {LLA} from "./LLA.js";
|
|
4
|
+
import {TimeConverter, RaDec} from "./TimeConverter.js";
|
|
5
|
+
import {norm, cross} from "mathjs";
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
// Enums (assuming these exist elsewhere)
|
|
@@ -1066,6 +1064,5 @@ class FrameConverter {
|
|
|
1066
1064
|
];
|
|
1067
1065
|
}
|
|
1068
1066
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
module.exports.EarthParams = Earth;
|
|
1067
|
+
export {FrameConverter, ReferenceFrame};
|
|
1068
|
+
export const EarthParams = Earth;
|
package/src/LLA.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import {degreesToRadians, radiansToDegrees} from "satellite.js";
|
|
2
|
+
import {Vector3D, J2000, EpochUTC} from "pious-squid";
|
|
3
|
+
import {NodeVector3D} from "./NodeVector3D.js";
|
|
4
|
+
import {WGS84_EARTH_EQUATORIAL_RADIUS_KM,
|
|
5
|
+
GRAV_CONST,
|
|
6
|
+
EARTH_MASS,
|
|
7
|
+
MU,
|
|
8
|
+
EARTH_RADIUS_KM} from "./constants.js";
|
|
9
|
+
import {FrameConverter} from "./FrameConverter.js";
|
|
10
|
+
import {OrbitUtils} from "./OrbitUtils.js";
|
|
11
|
+
import {wrapOneRevUnsigned} from "./utils.js";
|
|
12
|
+
import {BallisticPropagator} from "./ballisticPropagator.js";
|
|
13
|
+
import {norm, cross, dot} from "mathjs";
|
|
10
14
|
|
|
11
15
|
// Reference frames enum
|
|
12
16
|
const ReferenceFrame = {
|
|
@@ -290,7 +294,7 @@ class LaunchNominalClass {
|
|
|
290
294
|
initialNominal.states[0].elements.raan,
|
|
291
295
|
targetEcc,
|
|
292
296
|
degreesToRadians(targetArgOfPeriapsisDeg),
|
|
293
|
-
targetPerigeeKm +
|
|
297
|
+
targetPerigeeKm + WGS84_EARTH_EQUATORIAL_RADIUS_KM,
|
|
294
298
|
);
|
|
295
299
|
|
|
296
300
|
// Convert elliptical elements to cartesian state
|
|
@@ -406,8 +410,8 @@ class LaunchNominalClass {
|
|
|
406
410
|
}
|
|
407
411
|
|
|
408
412
|
const azimuthRad = degreesToRadians(azimuthDeg);
|
|
409
|
-
const earthRadius =
|
|
410
|
-
const mu =
|
|
413
|
+
const earthRadius = WGS84_EARTH_EQUATORIAL_RADIUS_KM;
|
|
414
|
+
const mu = GRAV_CONST * EARTH_MASS / 1e9; // km^3/s^2
|
|
411
415
|
|
|
412
416
|
// Get transformation from ITRF to J2000
|
|
413
417
|
const itrf2j2000 = FrameConverter.itrfToJ2000(orbitInsertionTime);
|
|
@@ -454,7 +458,7 @@ class LaunchNominalClass {
|
|
|
454
458
|
}
|
|
455
459
|
|
|
456
460
|
static hohmannTransferWithIncZeroing(state0, targetSMA,
|
|
457
|
-
burnAtNodes = 1, mu =
|
|
461
|
+
burnAtNodes = 1, mu = MU / 1e9) {
|
|
458
462
|
let t0 = state0.epochUtc;
|
|
459
463
|
let i = null;
|
|
460
464
|
|
|
@@ -587,7 +591,7 @@ class LaunchNominalClass {
|
|
|
587
591
|
|
|
588
592
|
static #getNextNodeCrossingGeoOrbit(sv) {
|
|
589
593
|
const geoElements = {
|
|
590
|
-
semiMajorAxis: 35786.0 +
|
|
594
|
+
semiMajorAxis: 35786.0 + EARTH_RADIUS_KM,
|
|
591
595
|
eccentricity: 0,
|
|
592
596
|
inclination: 0,
|
|
593
597
|
raan: 0,
|
|
@@ -602,7 +606,7 @@ class LaunchNominalClass {
|
|
|
602
606
|
return nextNode.nextNodeTime;
|
|
603
607
|
}
|
|
604
608
|
|
|
605
|
-
static #findMutualNodeTimes(sv1, sv2, muEarth =
|
|
609
|
+
static #findMutualNodeTimes(sv1, sv2, muEarth = MU / 1e9) {
|
|
606
610
|
// Implementation for finding mutual node times between two state vectors
|
|
607
611
|
|
|
608
612
|
if (!(sv1.epochUtc === sv2.epochUtc)) {
|
|
@@ -817,6 +821,4 @@ class LaunchNominalOutput {
|
|
|
817
821
|
}
|
|
818
822
|
}
|
|
819
823
|
|
|
820
|
-
|
|
821
|
-
LaunchNominalClass: LaunchNominalClass,
|
|
822
|
-
};
|
|
824
|
+
export {LaunchNominalClass};
|
package/src/NodeVector3D.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import {Vector3D} from "pious-squid";
|
|
2
2
|
|
|
3
3
|
class NodeVector3D {
|
|
4
4
|
constructor(x, y, z) {
|
|
@@ -68,4 +68,4 @@ class NodeVector3D {
|
|
|
68
68
|
// TODO: add angle and join operation methods
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
export {NodeVector3D};
|
package/src/OrbitUtils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {MU} from "./constants.js";
|
|
2
|
+
import {cross, norm} from "mathjs";
|
|
3
|
+
import {wrapHalfRevUnsigned, wrapOneRevUnsigned} from "./utils.js";
|
|
4
4
|
|
|
5
5
|
const EARTH_MU_KM = MU / 1e9;
|
|
6
6
|
|
|
@@ -280,11 +280,11 @@ class OrbitUtils {
|
|
|
280
280
|
*/
|
|
281
281
|
static stateVectorToElements(r, v, mu = EARTH_MU_KM) {
|
|
282
282
|
const tol = 1e-9;
|
|
283
|
-
|
|
283
|
+
|
|
284
284
|
if (mu < 1e-30) {
|
|
285
285
|
throw new Error("Mu must be greater than 1e-30.");
|
|
286
286
|
}
|
|
287
|
-
|
|
287
|
+
|
|
288
288
|
// Helper functions
|
|
289
289
|
function cross(a, b) {
|
|
290
290
|
return [
|
|
@@ -293,100 +293,102 @@ class OrbitUtils {
|
|
|
293
293
|
a[0] * b[1] - a[1] * b[0],
|
|
294
294
|
];
|
|
295
295
|
}
|
|
296
|
-
|
|
296
|
+
|
|
297
297
|
function dot(a, b) {
|
|
298
298
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
299
299
|
}
|
|
300
|
-
|
|
300
|
+
|
|
301
301
|
function norm(v) {
|
|
302
302
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
303
303
|
}
|
|
304
|
-
|
|
304
|
+
|
|
305
305
|
function clamp(value, min, max) {
|
|
306
306
|
return Math.min(Math.max(value, min), max);
|
|
307
307
|
}
|
|
308
|
-
|
|
308
|
+
|
|
309
309
|
// Calculate basic vectors
|
|
310
310
|
const h = cross(r, v);
|
|
311
311
|
const n = cross([0, 0, 1], h);
|
|
312
|
-
|
|
312
|
+
|
|
313
313
|
const rLength = norm(r);
|
|
314
314
|
const vLength = norm(v);
|
|
315
|
-
|
|
315
|
+
|
|
316
316
|
if (rLength === 0) throw new Error("Position vector must not be zero.");
|
|
317
317
|
if (vLength === 0) throw new Error("Velocity vector must not be zero.");
|
|
318
|
-
|
|
318
|
+
|
|
319
319
|
// Eccentricity vector calculation (corrected formula)
|
|
320
320
|
const vLengthSq = vLength * vLength;
|
|
321
321
|
const muOverR = mu / rLength;
|
|
322
322
|
const rvDot = dot(r, v);
|
|
323
|
-
|
|
323
|
+
|
|
324
324
|
const e = [
|
|
325
325
|
(1 / mu) * ((vLengthSq - muOverR) * r[0] - rvDot * v[0]),
|
|
326
326
|
(1 / mu) * ((vLengthSq - muOverR) * r[1] - rvDot * v[1]),
|
|
327
327
|
(1 / mu) * ((vLengthSq - muOverR) * r[2] - rvDot * v[2]),
|
|
328
328
|
];
|
|
329
|
-
|
|
329
|
+
|
|
330
330
|
const zeta = 0.5 * vLengthSq - muOverR;
|
|
331
|
-
|
|
331
|
+
|
|
332
332
|
if (zeta === 0) throw new Error("Zeta cannot be zero.");
|
|
333
|
-
|
|
333
|
+
|
|
334
334
|
const eLength = norm(e);
|
|
335
335
|
if (Math.abs(1.0 - eLength) <= tol) {
|
|
336
336
|
throw new Error("Parabolic orbit conversion is not supported.");
|
|
337
337
|
}
|
|
338
|
-
|
|
338
|
+
|
|
339
339
|
const a = -mu / (zeta * 2);
|
|
340
|
-
|
|
340
|
+
|
|
341
341
|
if (Math.abs(a * (1 - eLength)) < 1e-3) {
|
|
342
342
|
throw new Error("The state results in a singular conic section "
|
|
343
343
|
+ "with radius of periapsis less than 1 m.");
|
|
344
344
|
}
|
|
345
|
-
|
|
345
|
+
|
|
346
346
|
const hLength = norm(h);
|
|
347
347
|
if (hLength === 0) {
|
|
348
|
-
throw new Error(
|
|
348
|
+
throw new Error(`Cannot convert from Cartesian to Keplerian
|
|
349
|
+
- angular momentum is zero.`);
|
|
349
350
|
}
|
|
350
|
-
|
|
351
|
+
|
|
351
352
|
const i = Math.acos(h[2] / hLength);
|
|
352
|
-
|
|
353
|
+
|
|
353
354
|
if (i >= Math.PI - tol) {
|
|
354
355
|
throw new Error("Cannot convert orbit with inclination of 180 degrees.");
|
|
355
356
|
}
|
|
356
|
-
|
|
357
|
+
|
|
357
358
|
let raan = 0;
|
|
358
359
|
let w = 0;
|
|
359
360
|
let f = 0;
|
|
360
|
-
|
|
361
|
+
|
|
361
362
|
const nLength = norm(n);
|
|
362
|
-
|
|
363
|
+
|
|
363
364
|
// CASE 1: Non-circular, Inclined Orbit
|
|
364
365
|
if (eLength >= 1e-11 && i >= 1e-11 && i <= Math.PI - 1e-11) {
|
|
365
366
|
if (nLength === 0.0) {
|
|
366
367
|
throw new Error("Cannot convert from Cartesian to Keplerian "
|
|
367
368
|
+ "- line-of-nodes vector is a zero vector.");
|
|
368
369
|
}
|
|
369
|
-
|
|
370
|
+
|
|
370
371
|
raan = Math.acos(n[0] / nLength);
|
|
371
372
|
if (n[1] < 0) raan = 2 * Math.PI - raan;
|
|
372
|
-
|
|
373
|
+
|
|
373
374
|
w = Math.acos(dot(n, e) / (nLength * eLength));
|
|
374
375
|
if (e[2] < 0) w = 2 * Math.PI - w;
|
|
375
|
-
|
|
376
|
+
|
|
376
377
|
f = Math.acos(clamp(dot(e, r) / (eLength * rLength), -1, 1));
|
|
377
378
|
if (rvDot < 0) f = 2 * Math.PI - f;
|
|
378
379
|
} else if (eLength >= 1e-11 && (i < 1e-11 || i > Math.PI - 1e-11)) { // CASE 2: Non-circular, Equatorial Orbit
|
|
379
380
|
if (eLength === 0.0) {
|
|
380
|
-
throw new Error(
|
|
381
|
+
throw new Error(`Cannot convert from Cartesian to Keplerian
|
|
382
|
+
- eccentricity is zero.`);
|
|
381
383
|
}
|
|
382
384
|
raan = 0;
|
|
383
385
|
w = Math.acos(e[0] / eLength);
|
|
384
386
|
if (e[1] < 0) w = 2 * Math.PI - w;
|
|
385
|
-
|
|
387
|
+
|
|
386
388
|
// For GMT-4446 fix (LOJ: 2014.03.21)
|
|
387
389
|
if (i > Math.PI - 1e-11) w *= -1.0;
|
|
388
390
|
if (w < 0.0) w += 2 * Math.PI;
|
|
389
|
-
|
|
391
|
+
|
|
390
392
|
f = Math.acos(clamp(dot(e, r) / (eLength * rLength), -1, 1));
|
|
391
393
|
if (rvDot < 0) f = 2 * Math.PI - f;
|
|
392
394
|
} else if (eLength < 1e-11 && i >= 1e-11 && i <= Math.PI - 1e-11) { // CASE 3: Circular, Inclined Orbit
|
|
@@ -396,9 +398,9 @@ class OrbitUtils {
|
|
|
396
398
|
}
|
|
397
399
|
raan = Math.acos(n[0] / nLength);
|
|
398
400
|
if (n[1] < 0) raan = 2 * Math.PI - raan;
|
|
399
|
-
|
|
401
|
+
|
|
400
402
|
w = 0;
|
|
401
|
-
|
|
403
|
+
|
|
402
404
|
f = Math.acos(clamp(dot(n, r) / (nLength * rLength), -1, 1));
|
|
403
405
|
if (r[2] < 0) f = 2 * Math.PI - f;
|
|
404
406
|
} else if (eLength < 1e-11 && (i < 1e-11 || i > Math.PI - 1e-11)) { // CASE 4: Circular, Equatorial Orbit
|
|
@@ -406,16 +408,16 @@ class OrbitUtils {
|
|
|
406
408
|
w = 0;
|
|
407
409
|
f = Math.acos(clamp(r[0] / rLength, -1, 1));
|
|
408
410
|
if (r[1] < 0) f = 2 * Math.PI - f;
|
|
409
|
-
|
|
411
|
+
|
|
410
412
|
// For GMT-4446 fix (LOJ: 2014.03.21)
|
|
411
413
|
if (i > Math.PI - 1e-11) f *= -1.0;
|
|
412
414
|
if (f < 0.0) f += 2 * Math.PI;
|
|
413
415
|
}
|
|
414
|
-
|
|
416
|
+
|
|
415
417
|
// Calculate additional orbital parameters
|
|
416
418
|
const period = 2 * Math.PI * Math.sqrt(Math.pow(Math.abs(a), 3) / mu);
|
|
417
419
|
const meanMotion = 2 * Math.PI / period;
|
|
418
|
-
|
|
420
|
+
|
|
419
421
|
// Convert true anomaly to mean anomaly properly
|
|
420
422
|
const eccentricAnomaly = this.trueAnomalyToEccentricAnomaly(f, eLength);
|
|
421
423
|
const meanAnomaly = this.eccentricAnomalyToMeanAnomaly(eccentricAnomaly, eLength);
|
|
@@ -432,7 +434,7 @@ class OrbitUtils {
|
|
|
432
434
|
meanMotion: meanMotion, // rad/s
|
|
433
435
|
};
|
|
434
436
|
}
|
|
435
|
-
|
|
437
|
+
|
|
436
438
|
/**
|
|
437
439
|
* Convert true anomaly to eccentric anomaly
|
|
438
440
|
* @param {number} nu - true anomaly
|
|
@@ -444,7 +446,7 @@ class OrbitUtils {
|
|
|
444
446
|
const sinE = Math.sqrt(1 - e * e) * Math.sin(nu) / (1 + e * Math.cos(nu));
|
|
445
447
|
return Math.atan2(sinE, cosE);
|
|
446
448
|
}
|
|
447
|
-
|
|
449
|
+
|
|
448
450
|
/**
|
|
449
451
|
* Convert eccentric anomaly to mean anomaly
|
|
450
452
|
* @param {number} E - Eccentric anomaly
|
|
@@ -454,7 +456,7 @@ class OrbitUtils {
|
|
|
454
456
|
static eccentricAnomalyToMeanAnomaly(E, e) {
|
|
455
457
|
return E - e * Math.sin(E);
|
|
456
458
|
}
|
|
457
|
-
|
|
459
|
+
|
|
458
460
|
/**
|
|
459
461
|
* Convert mean anomaly to eccentric anomaly (Kepler's equation)
|
|
460
462
|
* @param {number} M - Mean anomaly
|
|
@@ -471,7 +473,7 @@ class OrbitUtils {
|
|
|
471
473
|
}
|
|
472
474
|
return E;
|
|
473
475
|
}
|
|
474
|
-
|
|
476
|
+
|
|
475
477
|
/**
|
|
476
478
|
* Convert eccentric anomaly to true anomaly
|
|
477
479
|
* @param {number} E - Eccentric anomaly
|
|
@@ -483,9 +485,6 @@ class OrbitUtils {
|
|
|
483
485
|
const sinNu = Math.sqrt(1 - e * e) * Math.sin(E) / (1 - e * Math.cos(E));
|
|
484
486
|
return Math.atan2(sinNu, cosNu);
|
|
485
487
|
}
|
|
486
|
-
|
|
487
488
|
}
|
|
488
489
|
|
|
489
|
-
|
|
490
|
-
OrbitUtils: OrbitUtils,
|
|
491
|
-
};
|
|
490
|
+
export {OrbitUtils};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {BallisticPropagator} from "./ballisticPropagator.js";
|
|
2
|
+
import {RungeKutta4Propagator,
|
|
3
|
+
J2000,
|
|
4
|
+
KeplerPropagator,
|
|
5
|
+
EpochUTC,
|
|
6
|
+
ClassicalElements} from "pious-squid";
|
|
7
|
+
import {sgp4} from "satellite.js";
|
|
8
|
+
import {NodeVector3D} from "./NodeVector3D.js";
|
|
9
|
+
import {checkTle} from "./astro.js";
|
|
10
|
+
import {OrbitUtils} from "./OrbitUtils.js";
|
|
11
|
+
|
|
12
|
+
const ReferenceFrame = {
|
|
13
|
+
ITRF: "ITRF",
|
|
14
|
+
J2000: "J2000",
|
|
15
|
+
TEME: "TEME",
|
|
16
|
+
GCRF: "GCRF",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
class PropagateUtils {
|
|
20
|
+
static propagatorSelection(initialState, propagatorType, endTime) {
|
|
21
|
+
let propVal = null;
|
|
22
|
+
switch (propagatorType) {
|
|
23
|
+
case "NUMERICAL":
|
|
24
|
+
propVal = PropagateUtils.numericalPropagator(initialState, endTime);
|
|
25
|
+
break;
|
|
26
|
+
case "KEPLERIAN":
|
|
27
|
+
propVal = PropagateUtils.keplerianPropagator(initialState, endTime);
|
|
28
|
+
break;
|
|
29
|
+
case "ANALYTICAL":
|
|
30
|
+
propVal = PropagateUtils.analyticalPropagator(
|
|
31
|
+
initialState, ReferenceFrame.J2000, endTime);
|
|
32
|
+
break;
|
|
33
|
+
case "TLE":
|
|
34
|
+
propVal = PropagateUtils.tlePropagator(initialState, endTime);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
return propVal;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static numericalPropagator(initialState, time) {
|
|
41
|
+
const state0 = new J2000(
|
|
42
|
+
new EpochUTC(initialState.epochUtc), initialState.position, initialState.velocity);
|
|
43
|
+
const prop = new RungeKutta4Propagator(state0);
|
|
44
|
+
const endTime = new EpochUTC(time);
|
|
45
|
+
const result = prop.propagate(endTime);
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static analyticalPropagator(initialState, frame, time) {
|
|
50
|
+
const state = {
|
|
51
|
+
position: new NodeVector3D(
|
|
52
|
+
initialState.position.x, initialState.position.y, initialState.position.z), // km
|
|
53
|
+
velocity: new NodeVector3D(
|
|
54
|
+
initialState.velocity.x, initialState.velocity.y, initialState.velocity.z), // km/s
|
|
55
|
+
epochUtc: initialState.epochUtc,
|
|
56
|
+
referenceFrame: frame,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const propagator = new BallisticPropagator(state);
|
|
60
|
+
const result = propagator.propagate(time);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static keplerianPropagator(initialState, time) {
|
|
65
|
+
const elementsObj = OrbitUtils.stateVectorToElements(
|
|
66
|
+
initialState.position, initialState.velocity);
|
|
67
|
+
|
|
68
|
+
const elements = new ClassicalElements(
|
|
69
|
+
new EpochUTC(initialState.epoch),
|
|
70
|
+
elementsObj.semiMajorAxis,
|
|
71
|
+
elementsObj.eccentricity,
|
|
72
|
+
elementsObj.inclination,
|
|
73
|
+
elementsObj.raan,
|
|
74
|
+
elementsObj.argOfPeriapsis,
|
|
75
|
+
elementsObj.trueAnomaly,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Now pass to KeplerPropagator:
|
|
79
|
+
const prop = new KeplerPropagator(elements);
|
|
80
|
+
|
|
81
|
+
const result = prop.propagate(new EpochUTC(time));
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static tlePropagator(tle, time) {
|
|
88
|
+
const line1 = tle[0];
|
|
89
|
+
const line2 = tle[1];
|
|
90
|
+
const satrec = checkTle(line1, line2);
|
|
91
|
+
if (!satrec) {
|
|
92
|
+
throw new Error("Invalid TLE data");
|
|
93
|
+
}
|
|
94
|
+
const result = sgp4(satrec, time);
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export {PropagateUtils};
|
|
100
|
+
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import constants, {WGS84_EARTH_EQUATORIAL_RADIUS_KM,
|
|
2
|
+
DEG2RAD, AU_KM as _AU_KM,
|
|
3
|
+
GEO_ALTITUDE_KM, RAD2DEG} from "./constants.js";
|
|
4
|
+
import solar from "solar-calculator";
|
|
5
|
+
import {Vector3} from "three";
|
|
6
|
+
import {wrapToRange} from "./utils.js";
|
|
7
|
+
import {sunPosAt} from "./astro.js";
|
|
6
8
|
|
|
7
9
|
class ShadowGEOCalculator {
|
|
8
10
|
constructor() {
|
|
@@ -13,7 +15,7 @@ class ShadowGEOCalculator {
|
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
static _calculateUmbraGeometry(deltaPS) {
|
|
16
|
-
const Dp = 2 *
|
|
18
|
+
const Dp = 2 * WGS84_EARTH_EQUATORIAL_RADIUS_KM;
|
|
17
19
|
const Ds = 2 * 695700; // sunRadiusKm
|
|
18
20
|
|
|
19
21
|
// Distance from Earth center to umbra apex
|
|
@@ -26,7 +28,7 @@ class ShadowGEOCalculator {
|
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
static _calculatePenumbraGeometry(deltaPS) {
|
|
29
|
-
const Dp = 2 *
|
|
31
|
+
const Dp = 2 * WGS84_EARTH_EQUATORIAL_RADIUS_KM;
|
|
30
32
|
const Ds = 2 * 695700; // sunRadiusKm
|
|
31
33
|
|
|
32
34
|
// Distance from Earth center to penumbra apex
|
|
@@ -52,12 +54,12 @@ class ShadowGEOCalculator {
|
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
static _sunUnitVector(sunLongitude, sunDeclination) {
|
|
55
|
-
const sunVector = new
|
|
56
|
-
sunVector.x = Math.cos(sunDeclination *
|
|
57
|
-
* Math.cos(sunLongitude *
|
|
58
|
-
sunVector.y = Math.cos(sunDeclination *
|
|
59
|
-
* Math.sin(sunLongitude *
|
|
60
|
-
sunVector.z = Math.sin(sunDeclination *
|
|
57
|
+
const sunVector = new Vector3();
|
|
58
|
+
sunVector.x = Math.cos(sunDeclination * DEG2RAD)
|
|
59
|
+
* Math.cos(sunLongitude * DEG2RAD);
|
|
60
|
+
sunVector.y = Math.cos(sunDeclination * DEG2RAD)
|
|
61
|
+
* Math.sin(sunLongitude * DEG2RAD);
|
|
62
|
+
sunVector.z = Math.sin(sunDeclination * DEG2RAD);
|
|
61
63
|
return sunVector; // returns the unit vector of the Sun's position
|
|
62
64
|
}
|
|
63
65
|
|
|
@@ -65,16 +67,16 @@ class ShadowGEOCalculator {
|
|
|
65
67
|
// Longitude and declination (latitude) describe direction, not distance.
|
|
66
68
|
// To get the Earth-Sun distance in km, use astronomical data (mean ≈ 149,597,870.7 km).
|
|
67
69
|
// If you want the position vector in km, use the distance and direction:
|
|
68
|
-
const AU_KM =
|
|
70
|
+
const AU_KM = _AU_KM; // Astronomical Unit in kilometers
|
|
69
71
|
// AstroLibrary uses 149597900
|
|
70
72
|
const r = AU_KM; // Use actual Earth-Sun distance if available, otherwise mean AU
|
|
71
73
|
|
|
72
74
|
// Convert longitude and declination to a position vector in km
|
|
73
|
-
const x = r * Math.cos(sunDeclination *
|
|
74
|
-
* Math.cos(sunLongitude *
|
|
75
|
-
const y = r * Math.cos(sunDeclination *
|
|
76
|
-
* Math.sin(sunLongitude *
|
|
77
|
-
const z = r * Math.sin(sunDeclination *
|
|
75
|
+
const x = r * Math.cos(sunDeclination * DEG2RAD)
|
|
76
|
+
* Math.cos(sunLongitude * DEG2RAD);
|
|
77
|
+
const y = r * Math.cos(sunDeclination * DEG2RAD)
|
|
78
|
+
* Math.sin(sunLongitude * DEG2RAD);
|
|
79
|
+
const z = r * Math.sin(sunDeclination * DEG2RAD);
|
|
78
80
|
|
|
79
81
|
return {x, y, z, distance: r};
|
|
80
82
|
}
|
|
@@ -99,7 +101,7 @@ class ShadowGEOCalculator {
|
|
|
99
101
|
const {Xu, alphaU} = ShadowGEOCalculator._calculateUmbraGeometry(deltaPS);
|
|
100
102
|
const {Xp, alphaP} = ShadowGEOCalculator._calculatePenumbraGeometry(deltaPS);
|
|
101
103
|
|
|
102
|
-
const geoRadius =
|
|
104
|
+
const geoRadius = WGS84_EARTH_EQUATORIAL_RADIUS_KM + GEO_ALTITUDE_KM;
|
|
103
105
|
|
|
104
106
|
// Calculate Shadow radii at GEO altitude
|
|
105
107
|
const umbraRadiusGEO = ShadowGEOCalculator._umbraRadiusDistance(geoRadius, Xu, alphaU);
|
|
@@ -121,8 +123,8 @@ class ShadowGEOCalculator {
|
|
|
121
123
|
];
|
|
122
124
|
|
|
123
125
|
// Converting shadow axis to latitude and longitude
|
|
124
|
-
const shadowCenterLat = Math.asin(shadowCenter[2] / geoRadius) *
|
|
125
|
-
const shadowCenterLon = Math.atan2(shadowCenter[1], shadowCenter[0]) *
|
|
126
|
+
const shadowCenterLat = Math.asin(shadowCenter[2] / geoRadius) * RAD2DEG;
|
|
127
|
+
const shadowCenterLon = Math.atan2(shadowCenter[1], shadowCenter[0]) * RAD2DEG;
|
|
126
128
|
|
|
127
129
|
// Check if GEO belt intersects the shadow
|
|
128
130
|
const equatorialDistance = Math.abs(shadowCenter[2]); // Distance from the equator to the shadow center
|
|
@@ -145,8 +147,8 @@ class ShadowGEOCalculator {
|
|
|
145
147
|
// Intersection points in the GEO belt
|
|
146
148
|
const centerLon = shadowCenterLon; // You already have this!
|
|
147
149
|
|
|
148
|
-
let lon1 = centerLon + theta *
|
|
149
|
-
let lon2 = centerLon - theta *
|
|
150
|
+
let lon1 = centerLon + theta * RAD2DEG;
|
|
151
|
+
let lon2 = centerLon - theta * RAD2DEG;
|
|
150
152
|
|
|
151
153
|
// Ensure longitudes are within -180 to 180 degrees
|
|
152
154
|
lon1 = wrapToRange(lon1, -180, 180);
|
|
@@ -173,8 +175,8 @@ class ShadowGEOCalculator {
|
|
|
173
175
|
// Intersection points in the GEO belt
|
|
174
176
|
const centerLon = shadowCenterLon; // You already have this!
|
|
175
177
|
|
|
176
|
-
let lon1 = centerLon + theta *
|
|
177
|
-
let lon2 = centerLon - theta *
|
|
178
|
+
let lon1 = centerLon + theta * RAD2DEG;
|
|
179
|
+
let lon2 = centerLon - theta * RAD2DEG;
|
|
178
180
|
|
|
179
181
|
// Ensure longitudes are within -180 to 180 degrees
|
|
180
182
|
lon1 = ((lon1 + 180) % 360 + 360) % 360 - 180;
|
|
@@ -198,4 +200,4 @@ class ShadowGEOCalculator {
|
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
202
|
|
|
201
|
-
|
|
203
|
+
export {ShadowGEOCalculator};
|
package/src/TimeConverter.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// const {gstime} = require("satellite.js");
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* TimeConverter - Port from C# TimeConverter
|
|
5
3
|
* Handles conversions between different time systems (UTC, UT1, TAI, GPS, TT)
|
|
@@ -308,4 +306,4 @@ class RaDec {
|
|
|
308
306
|
}
|
|
309
307
|
}
|
|
310
308
|
|
|
311
|
-
|
|
309
|
+
export {TimeConverter, RaDec};
|
package/src/astro.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
twoline2satrec,
|
|
1
|
+
import {cross, dot, norm, multiply, subtract, inv, polynomialRoot, add} from "mathjs";
|
|
2
|
+
import {dateToMySqlDate, dtStrtoJsDt, parseDate} from "./fixDate.js";
|
|
3
|
+
import {century as _century, declination, equationOfTime} from "solar-calculator";
|
|
4
|
+
import {twoline2satrec,
|
|
6
5
|
propagate,
|
|
7
6
|
sgp4,
|
|
8
7
|
gstime,
|
|
@@ -12,12 +11,10 @@ const {
|
|
|
12
11
|
eciToEcf,
|
|
13
12
|
ecfToLookAngles,
|
|
14
13
|
degreesLong,
|
|
15
|
-
degreesLat
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const {
|
|
20
|
-
normalize,
|
|
14
|
+
degreesLat} from "satellite.js";
|
|
15
|
+
import {lowerCaseObjectKeys} from "./transform.js";
|
|
16
|
+
import {EpochUTC, Geodetic, J2000, Vector3D} from "pious-squid";
|
|
17
|
+
import {normalize,
|
|
21
18
|
transpose,
|
|
22
19
|
multiplyVector,
|
|
23
20
|
dist,
|
|
@@ -32,10 +29,8 @@ const {
|
|
|
32
29
|
getAngle,
|
|
33
30
|
wrapToRange,
|
|
34
31
|
getAngleDiffSigned,
|
|
35
|
-
posToArray
|
|
36
|
-
|
|
37
|
-
const {
|
|
38
|
-
DEG2RAD,
|
|
32
|
+
posToArray} from "./utils.js";
|
|
33
|
+
import {DEG2RAD,
|
|
39
34
|
RAD2DEG,
|
|
40
35
|
SEC2RAD,
|
|
41
36
|
ARCSEC2RAD,
|
|
@@ -47,18 +42,17 @@ const {
|
|
|
47
42
|
WGS72_EARTH_EQUATORIAL_RADIUS_KM,
|
|
48
43
|
WGS84_EARTH_EQUATORIAL_RADIUS_KM,
|
|
49
44
|
MILLIS_PER_DAY,
|
|
50
|
-
GEO_ALTITUDE_KM
|
|
51
|
-
} = require("./constants.js");
|
|
45
|
+
GEO_ALTITUDE_KM} from "./constants.js";
|
|
52
46
|
|
|
53
47
|
// Solar Terminator
|
|
54
48
|
// Returns sun latitude and longitude as -180/180
|
|
55
49
|
const sunPosAt = (dt) => {
|
|
56
50
|
const day = new Date(+dt).setUTCHours(0, 0, 0, 0);
|
|
57
|
-
const t =
|
|
51
|
+
const t = _century(dt);
|
|
58
52
|
const longitude = ((day - dt) / 864e5) * 360 - 180;
|
|
59
53
|
return [
|
|
60
|
-
|
|
61
|
-
(longitude -
|
|
54
|
+
declination(t),
|
|
55
|
+
(longitude - equationOfTime(t) / 4) % 360,
|
|
62
56
|
];
|
|
63
57
|
};
|
|
64
58
|
|
|
@@ -68,7 +62,9 @@ const checkTle = (line1, line2) => {
|
|
|
68
62
|
const satrec = twoline2satrec(line1, line2);
|
|
69
63
|
satrec.epoch = dateToMySqlDate(julianToGregorian(satrec.jdsatepoch)); // Add the epoch
|
|
70
64
|
const pv = sgp4(satrec, 0);
|
|
71
|
-
if (Number.isNaN(pv.position.x)
|
|
65
|
+
if (Number.isNaN(pv.position.x)
|
|
66
|
+
|| Number.isNaN(pv.position.y)
|
|
67
|
+
|| Number.isNaN(pv.position.z)) {
|
|
72
68
|
return false;
|
|
73
69
|
} else {
|
|
74
70
|
return satrec;
|
|
@@ -646,8 +642,9 @@ const angleBetweenPlanes = (pv1, pv2) => {
|
|
|
646
642
|
*/
|
|
647
643
|
const propTo = (satRec, time) => {
|
|
648
644
|
const pv = propagate(satRec, new Date(time));
|
|
649
|
-
if ( !isDefined(pv))
|
|
645
|
+
if ( !isDefined(pv)) {
|
|
650
646
|
return null;
|
|
647
|
+
}
|
|
651
648
|
return {
|
|
652
649
|
p: pv.position,
|
|
653
650
|
v: pv.velocity,
|
|
@@ -660,7 +657,7 @@ const propTo = (satRec, time) => {
|
|
|
660
657
|
argp: pv.meanElements.om,
|
|
661
658
|
meanmotion: pv.meanElements.nm,
|
|
662
659
|
meananomaly: pv.meanElements.mm,
|
|
663
|
-
}
|
|
660
|
+
},
|
|
664
661
|
};
|
|
665
662
|
};
|
|
666
663
|
|
|
@@ -916,25 +913,25 @@ const RaDecToGeodetic = (
|
|
|
916
913
|
return {Altitude: null, Latitude: null, Longitude: null};
|
|
917
914
|
}
|
|
918
915
|
|
|
919
|
-
const time =
|
|
916
|
+
const time = EpochUTC.fromDateString(
|
|
920
917
|
dtStrtoJsDt(ObTimeUtc).toISOString(),
|
|
921
918
|
);
|
|
922
|
-
const sensorGeo = new
|
|
919
|
+
const sensorGeo = new Geodetic(
|
|
923
920
|
senLat * DEG2RAD,
|
|
924
921
|
senLon * DEG2RAD,
|
|
925
922
|
senAltKm,
|
|
926
923
|
);
|
|
927
924
|
const sensorItrf = sensorGeo.toITRF(time);
|
|
928
925
|
const sensorGcrf = sensorItrf.toJ2000();
|
|
929
|
-
const sat = new
|
|
926
|
+
const sat = new J2000(
|
|
930
927
|
time,
|
|
931
|
-
new
|
|
928
|
+
new Vector3D(
|
|
932
929
|
rangeKm * Math.cos(Ra * DEG2RAD) * Math.cos(Dec * DEG2RAD),
|
|
933
930
|
rangeKm * Math.sin(Ra * DEG2RAD) * Math.cos(Dec * DEG2RAD),
|
|
934
931
|
rangeKm * Math.sin(Dec * DEG2RAD),
|
|
935
932
|
),
|
|
936
933
|
);
|
|
937
|
-
const satGcrf = new
|
|
934
|
+
const satGcrf = new J2000(
|
|
938
935
|
time,
|
|
939
936
|
sensorGcrf.position.add(sat.position),
|
|
940
937
|
);
|
|
@@ -980,12 +977,12 @@ const estimateSlantRange = (obTime, ra, dec, senLat, senLon, senAltKm) => {
|
|
|
980
977
|
const slantU = [-x, -y, -z];
|
|
981
978
|
|
|
982
979
|
// Sensor coordinates
|
|
983
|
-
const sensorGeo = new
|
|
980
|
+
const sensorGeo = new Geodetic(
|
|
984
981
|
senLat * DEG2RAD,
|
|
985
982
|
senLon * DEG2RAD,
|
|
986
983
|
senAltKm,
|
|
987
984
|
);
|
|
988
|
-
const time =
|
|
985
|
+
const time = EpochUTC.fromDateString(obTime);
|
|
989
986
|
const sensorItrf = sensorGeo.toITRF(time);
|
|
990
987
|
const sensorGcrf = sensorItrf.toJ2000();
|
|
991
988
|
|
|
@@ -1043,7 +1040,7 @@ const AzElToRaDec = (ObTimeUtc, az, el, lat, lon) => {
|
|
|
1043
1040
|
return {ra: null, dec: null};
|
|
1044
1041
|
}
|
|
1045
1042
|
|
|
1046
|
-
const time =
|
|
1043
|
+
const time = EpochUTC.fromDateString(ObTimeUtc);
|
|
1047
1044
|
|
|
1048
1045
|
// Convert all angles to radians
|
|
1049
1046
|
az = az * DEG2RAD;
|
|
@@ -1095,23 +1092,23 @@ const RaDecToAzEl = (ObTime, Ra, Dec, SenLat, SenLon) => {
|
|
|
1095
1092
|
const SenAlt = 0;
|
|
1096
1093
|
const SlantRangeKm = 1;
|
|
1097
1094
|
|
|
1098
|
-
const time =
|
|
1099
|
-
const sensorGeo = new
|
|
1095
|
+
const time = EpochUTC.fromDateString(ObTime);
|
|
1096
|
+
const sensorGeo = new Geodetic(
|
|
1100
1097
|
SenLat * DEG2RAD,
|
|
1101
1098
|
SenLon * DEG2RAD,
|
|
1102
1099
|
SenAlt,
|
|
1103
1100
|
);
|
|
1104
1101
|
const sensorITRF = sensorGeo.toITRF(time);
|
|
1105
1102
|
const sensorGCRF = sensorITRF.toJ2000();
|
|
1106
|
-
const satellite = new
|
|
1103
|
+
const satellite = new J2000(
|
|
1107
1104
|
time,
|
|
1108
|
-
new
|
|
1105
|
+
new Vector3D(
|
|
1109
1106
|
SlantRangeKm * Math.cos(Ra * DEG2RAD) * Math.cos(Dec * DEG2RAD),
|
|
1110
1107
|
SlantRangeKm * Math.sin(Ra * DEG2RAD) * Math.cos(Dec * DEG2RAD),
|
|
1111
1108
|
SlantRangeKm * Math.sin(Dec * DEG2RAD),
|
|
1112
1109
|
),
|
|
1113
1110
|
);
|
|
1114
|
-
const satGCRF = new
|
|
1111
|
+
const satGCRF = new J2000(
|
|
1115
1112
|
time,
|
|
1116
1113
|
sensorGCRF.position.add(satellite.position),
|
|
1117
1114
|
);
|
|
@@ -3010,7 +3007,7 @@ const isSatInShadow = (epoch, satPos) => {
|
|
|
3010
3007
|
let shadowType = "None";
|
|
3011
3008
|
|
|
3012
3009
|
// Getting Sun's apperent longitude and declination at the current epoch
|
|
3013
|
-
const century =
|
|
3010
|
+
const century = _century(epoch); // Convert date to Julian centuries since J2000 || equal to var tut1 in GetPosition()
|
|
3014
3011
|
|
|
3015
3012
|
// Variables to solve for Sun Pos in ___ frame
|
|
3016
3013
|
const lamM = (280.460 + 36000.771*century % 360) * DEG2RAD;
|
|
@@ -3108,62 +3105,62 @@ const isSatInShadow = (epoch, satPos) => {
|
|
|
3108
3105
|
return shadowType; // return the shadow type
|
|
3109
3106
|
};
|
|
3110
3107
|
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3108
|
+
import * as CONSTANTS from "./constants.js";
|
|
3109
|
+
export {CONSTANTS};
|
|
3110
|
+
export {REGIMES,
|
|
3111
|
+
julianToGregorian,
|
|
3112
|
+
calcRegime,
|
|
3113
|
+
altToRegime,
|
|
3114
|
+
cartesianToRIC,
|
|
3115
|
+
multiplyVector,
|
|
3116
|
+
dist,
|
|
3117
|
+
angleBetween3DCoords,
|
|
3118
|
+
prop,
|
|
3119
|
+
propGeodetic,
|
|
3120
|
+
getTRIC,
|
|
3121
|
+
getSunDirection,
|
|
3122
|
+
getLonAndDrift,
|
|
3123
|
+
getRaanPrecession,
|
|
3124
|
+
checkTle,
|
|
3125
|
+
sunPosAt,
|
|
3126
|
+
distGeodetic,
|
|
3127
|
+
getSemiMajorAxis,
|
|
3128
|
+
angleBetweenPlanes,
|
|
3129
|
+
propagate,
|
|
3130
|
+
planeChangeDeltaV,
|
|
3131
|
+
planeChangePureInclinationDeltaV,
|
|
3132
|
+
cartesianToKeplerian,
|
|
3133
|
+
keplerianToCartesian,
|
|
3134
|
+
getLeoRpoData,
|
|
3135
|
+
getGeoRpoData,
|
|
3136
|
+
getGeoShadowZones,
|
|
3137
|
+
getGeoLightIntervals,
|
|
3138
|
+
getEclipseStatus,
|
|
3139
|
+
estimateSlantRange,
|
|
3140
|
+
calculateNextApogeePerigeeTimes,
|
|
3141
|
+
calculateNextApogeePerigeeTimesWithPropagation,
|
|
3142
|
+
calculateLeoPhaseDifference,
|
|
3143
|
+
getLeoWaterfallData,
|
|
3144
|
+
lambertThomsonAlgorithm,
|
|
3145
|
+
detectManeuverMinDv,
|
|
3146
|
+
getInterceptRendezvousMinDv,
|
|
3147
|
+
wrap360,
|
|
3148
|
+
inWrappedRange,
|
|
3149
|
+
subtractWrapped,
|
|
3150
|
+
propagateRaan,
|
|
3151
|
+
getIncDiff,
|
|
3152
|
+
getRaanDiff,
|
|
3153
|
+
getRaanClosureRate,
|
|
3154
|
+
getRaanAlignTime,
|
|
3155
|
+
getRaanAlignTimeStr,
|
|
3156
|
+
getRaanDetails,
|
|
3157
|
+
isSatInShadow,
|
|
3158
|
+
calculateGeoCrossingTimes,
|
|
3159
|
+
};
|
|
3160
|
+
export const raDecToGeodetic = RaDecToGeodetic;
|
|
3161
|
+
export const getResiduals = GetResiduals;
|
|
3162
|
+
export const raDecToAzEl = RaDecToAzEl;
|
|
3163
|
+
export const azElToRaDec = AzElToRaDec;
|
|
3164
|
+
export const getElsetUdlFromTle = GetElsetUdlFromTle;
|
|
3165
|
+
import * as satjs from "satellite.js";
|
|
3166
|
+
export {satjs};
|
|
@@ -7,11 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
// Import existing libraries instead of defining custom ones
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
WGS84_EARTH_EQUATORIAL_RADIUS_KM,
|
|
13
|
-
} = require("../constants");
|
|
14
|
-
const {NodeVector3D} = require("../NodeVector3D.js");
|
|
10
|
+
import {MU, WGS84_EARTH_EQUATORIAL_RADIUS_KM} from "./constants.js";
|
|
11
|
+
import {NodeVector3D} from "./NodeVector3D.js";
|
|
15
12
|
|
|
16
13
|
// Earth constants using existing constants.js values
|
|
17
14
|
const EarthConstants = {
|
|
@@ -988,14 +985,7 @@ class BallisticPropagator {
|
|
|
988
985
|
}
|
|
989
986
|
|
|
990
987
|
// Export for Node.js
|
|
991
|
-
|
|
992
|
-
module.exports = {
|
|
993
|
-
BallisticPropagator,
|
|
994
|
-
BallisticPropagatorUtils,
|
|
995
|
-
PosVelVec,
|
|
996
|
-
EarthConstants,
|
|
997
|
-
};
|
|
998
|
-
}
|
|
988
|
+
export {BallisticPropagator, BallisticPropagatorUtils, PosVelVec, EarthConstants};
|
|
999
989
|
|
|
1000
990
|
/**
|
|
1001
991
|
* REFACTORING SUMMARY:
|
package/src/constants.js
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
module.exports.MILLIS_PER_DAY = 24 * 60 * 60 * 1000; // Number of milliseconds in a day
|
|
1
|
+
export const DEG2RAD = Math.PI / 180;
|
|
2
|
+
export const RAD2DEG = 180 / Math.PI;
|
|
3
|
+
export const SEC2RAD = Math.PI / (180.0 * 3600.0); // seconds to radians conversion
|
|
4
|
+
export const ARCSEC2RAD = 1 / (3600 * 180 / Math.PI); // arcseconds to radians conversion (1 arcsec = 1/3600 deg = 1/3600 * π/180 rad)
|
|
5
|
+
export const MILLIS_PER_DAY = 24 * 60 * 60 * 1000; // Number of milliseconds in a day
|
|
7
6
|
|
|
8
|
-
// Sun
|
|
9
|
-
|
|
10
|
-
module.exports.AU_KM = 149597870.7; // Astronomical Unit in kilometers
|
|
7
|
+
export const SUN_RADIUS_KM = 695701.0; // Sun radius in kilometers
|
|
8
|
+
export const AU_KM = 149597870.7; // Astronomical Unit in kilometers
|
|
11
9
|
|
|
12
|
-
// Earth
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
module.exports.GEO_ALTITUDE_KM = 35786; // Km
|
|
10
|
+
export const MU = 3.986004418e14; // (m^3)/(s^2) WGS-84 Earth Mu
|
|
11
|
+
export const GRAV_CONST = 6.6743e-11; // N⋅m2⋅kg−2
|
|
12
|
+
export const EARTH_MASS = 5.97219e24; // kg
|
|
13
|
+
export const WGS72_EARTH_EQUATORIAL_RADIUS_KM = 6378.135; // in km. Use this when calculations are done with SGP4 which uses WGS72 assumptions.
|
|
14
|
+
export const WGS84_EARTH_EQUATORIAL_RADIUS_KM = 6378.137; // in km. Use this for general calculations.
|
|
15
|
+
export const EARTH_RADIUS_KM = 6378.135; // still in use for backwards compatibility with old code. Has been removed from nps, ingest, node-common. TODO: refactor from all other js projects using node-common.
|
|
16
|
+
export const GEO_ALTITUDE_KM = 35786; // Km
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
export const REGIMES = {
|
|
22
19
|
Undetermined: 1,
|
|
23
20
|
Leo: 2,
|
|
24
21
|
Heo: 4,
|
package/src/fixDate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import {isValid, parseISO, format} from "date-fns";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Formats a date how MySQL wants it.
|
|
@@ -6,8 +6,8 @@ const {isValid, parseISO, format} = require("date-fns");
|
|
|
6
6
|
* @param {string} date in ISO format used by the UDL
|
|
7
7
|
* @return {string} date formatted for MySQL
|
|
8
8
|
*/
|
|
9
|
-
const fixDate = (date) => isValid(parseISO(date))
|
|
10
|
-
? format(parseISO(date.replace("Z", "")), "yyyy-MM-dd HH:mm:ss")
|
|
9
|
+
export const fixDate = (date) => date && isValid(parseISO(date.toString()))
|
|
10
|
+
? format(parseISO(date.toString().replace("Z", "")), "yyyy-MM-dd HH:mm:ss")
|
|
11
11
|
: null;
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -15,14 +15,15 @@ const fixDate = (date) => isValid(parseISO(date))
|
|
|
15
15
|
* @param {Date} date
|
|
16
16
|
* @return {string} Mysql formatted date string
|
|
17
17
|
*/
|
|
18
|
-
const dateToMySqlDate
|
|
18
|
+
export const dateToMySqlDate
|
|
19
|
+
= (date) => new Date(date).toISOString().slice(0, 19).replace("T", " ");
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Converts string, int, or Date input into a Date. Adds Z to end of string if not present
|
|
22
23
|
* @param {*} date
|
|
23
24
|
* @return {Date} date
|
|
24
25
|
*/
|
|
25
|
-
const parseDate = (date) => {
|
|
26
|
+
export const parseDate = (date) => {
|
|
26
27
|
const corrected = (typeof date !== "string" || date.endsWith("Z")) ? date : (date+"Z");
|
|
27
28
|
return new Date(corrected);
|
|
28
29
|
};
|
|
@@ -33,7 +34,7 @@ const parseDate = (date) => {
|
|
|
33
34
|
* @param {String} dt date time string
|
|
34
35
|
* @return {Date} Date object in UTC
|
|
35
36
|
*/
|
|
36
|
-
const dtStrtoJsDt = (dt) => {
|
|
37
|
+
export const dtStrtoJsDt = (dt) => {
|
|
37
38
|
if (isSqlDate(dt)) {
|
|
38
39
|
// MySql date-time format
|
|
39
40
|
return mySqlDateToJs(dt);
|
|
@@ -45,7 +46,7 @@ const dtStrtoJsDt = (dt) => {
|
|
|
45
46
|
* @param {String} dt MySql date-time format must be yyyy-mm-dd hh:mm:ss
|
|
46
47
|
* @return {Date} JS date-time
|
|
47
48
|
*/
|
|
48
|
-
const mySqlDateToJs = (dt) => {
|
|
49
|
+
export const mySqlDateToJs = (dt) => {
|
|
49
50
|
// check the date is in MySQL format
|
|
50
51
|
if (isSqlDate(dt)) {
|
|
51
52
|
return new Date(dt+"Z");
|
|
@@ -58,12 +59,4 @@ const mySqlDateToJs = (dt) => {
|
|
|
58
59
|
* @param {String} dt The date string to test
|
|
59
60
|
* @return {Boolean} True if is SQL date, false otherwise
|
|
60
61
|
*/
|
|
61
|
-
const isSqlDate = (dt) => dt.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{1,6})?$/);
|
|
62
|
-
|
|
63
|
-
module.exports = {
|
|
64
|
-
fixDate: fixDate,
|
|
65
|
-
parseDate: parseDate,
|
|
66
|
-
dateToMySqlDate: dateToMySqlDate,
|
|
67
|
-
mySqlDateToJs: mySqlDateToJs,
|
|
68
|
-
dtStrtoJsDt: dtStrtoJsDt,
|
|
69
|
-
};
|
|
62
|
+
export const isSqlDate = (dt) => dt.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{1,6})?$/);
|
package/src/index.js
CHANGED
|
@@ -1,11 +1,47 @@
|
|
|
1
|
-
module
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
// Proper ES module barrel file
|
|
2
|
+
export * from "./loggerFactory.cjs";
|
|
3
|
+
export * from "./transform.js";
|
|
4
|
+
export * from "./checkNetwork.cjs";
|
|
5
|
+
export * from "./fixDate.js";
|
|
6
|
+
export * from "./astro.js";
|
|
7
|
+
export * from "./launchNominal.js";
|
|
8
|
+
export * from "./LaunchNominalClass.js";
|
|
9
|
+
export * from "./OrbitUtils.js";
|
|
10
|
+
export * from "./PropagateUtils.js";
|
|
11
|
+
export * from "./ballisticPropagator.js";
|
|
12
|
+
export * from "./NodeVector3D.js";
|
|
13
|
+
|
|
14
|
+
// UDL exports are grouped; re-export individually and as namespace if needed
|
|
15
|
+
import * as udl from "./udl.js";
|
|
16
|
+
export * from "./udl.js";
|
|
17
|
+
export {udl};
|
|
18
|
+
|
|
19
|
+
// Optional default aggregate similar to old CommonJS shape
|
|
20
|
+
import * as loggerFactoryNS from "./loggerFactory.cjs";
|
|
21
|
+
import * as transformNS from "./transform.js";
|
|
22
|
+
import * as checkNetworkNS from "./checkNetwork.cjs";
|
|
23
|
+
import * as fixDateNS from "./fixDate.js";
|
|
24
|
+
import * as astroNS from "./astro.js";
|
|
25
|
+
import * as launchNominalNS from "./launchNominal.js";
|
|
26
|
+
import * as LaunchNominalClassNS from "./LaunchNominalClass.js";
|
|
27
|
+
import * as OrbitUtilsNS from "./OrbitUtils.js";
|
|
28
|
+
import * as PropagateUtilsNS from "./PropagateUtils.js";
|
|
29
|
+
import * as ballisticPropagatorNS from "./ballisticPropagator.js";
|
|
30
|
+
import * as NodeVector3DNS from "./NodeVector3D.js";
|
|
31
|
+
|
|
32
|
+
const aggregate = {
|
|
33
|
+
...loggerFactoryNS,
|
|
34
|
+
...transformNS,
|
|
35
|
+
...checkNetworkNS,
|
|
36
|
+
...fixDateNS,
|
|
37
|
+
...astroNS,
|
|
38
|
+
...launchNominalNS,
|
|
39
|
+
...LaunchNominalClassNS,
|
|
40
|
+
...OrbitUtilsNS,
|
|
41
|
+
...PropagateUtilsNS,
|
|
42
|
+
...ballisticPropagatorNS,
|
|
43
|
+
...NodeVector3DNS,
|
|
44
|
+
udl,
|
|
11
45
|
};
|
|
46
|
+
|
|
47
|
+
export default aggregate;
|
package/src/launchNominal.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
twoline2satrec,
|
|
1
|
+
import {twoline2satrec,
|
|
3
2
|
propagate,
|
|
4
3
|
gstime,
|
|
5
4
|
eciToGeodetic,
|
|
@@ -8,12 +7,9 @@ const {
|
|
|
8
7
|
eciToEcf,
|
|
9
8
|
ecfToLookAngles,
|
|
10
9
|
degreesLong,
|
|
11
|
-
degreesLat
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const {
|
|
15
|
-
wrapToRange,
|
|
16
|
-
} = require("./utils.js");
|
|
10
|
+
degreesLat} from "satellite.js";
|
|
11
|
+
import {distGeodetic} from "./astro.js";
|
|
12
|
+
import {wrapToRange} from "./utils.js";
|
|
17
13
|
|
|
18
14
|
const notamToCoordArray = (notam) => {
|
|
19
15
|
const regexPatterns = [
|
|
@@ -209,7 +205,4 @@ const getOpopOtop = (
|
|
|
209
205
|
};
|
|
210
206
|
};
|
|
211
207
|
|
|
212
|
-
|
|
213
|
-
module.exports.getBearingFromTle = getBearingFromTle;
|
|
214
|
-
module.exports.getBearing = getBearing;
|
|
215
|
-
module.exports.getOpopOtop = getOpopOtop;
|
|
208
|
+
export {notamToCoordArray, getBearingFromTle, getBearing, getOpopOtop};
|
package/src/s3.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {S3Client, PutObjectCommand, HeadObjectCommand} from "@aws-sdk/client-s3";
|
|
2
|
+
import {readFileSync} from "fs";
|
|
3
|
+
import {basename} from "path";
|
|
4
|
+
import {config} from "dotenv";
|
|
5
|
+
config();
|
|
6
6
|
|
|
7
7
|
const s3Client = new S3Client({
|
|
8
8
|
region: "us-west-1", // Replace with your AWS region
|
|
@@ -24,7 +24,7 @@ const s3Client = new S3Client({
|
|
|
24
24
|
* @param {Array|Buffer|null} buffer
|
|
25
25
|
* @return {Promise<Object>}
|
|
26
26
|
*/
|
|
27
|
-
|
|
27
|
+
export async function s3Upload(fileName, type = "image/png", buffer = null) {
|
|
28
28
|
try {
|
|
29
29
|
const fileContent = buffer ? buffer : readFileSync(fileName);
|
|
30
30
|
return await s3Client.send(new PutObjectCommand({
|
|
@@ -37,9 +37,9 @@ module.exports.s3Upload = async (fileName, type = "image/png", buffer = null) =>
|
|
|
37
37
|
console.error("Error uploading file or buffer to S3: ", err);
|
|
38
38
|
throw err; // Re-throw the error so it can be caught by the calling function if needed
|
|
39
39
|
}
|
|
40
|
-
}
|
|
40
|
+
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
export async function doesS3ObjectExist(fileName) {
|
|
43
43
|
try {
|
|
44
44
|
// Try to fetch the metadata of the object to check if it exists
|
|
45
45
|
await s3Client.send(new HeadObjectCommand({
|
|
@@ -56,4 +56,4 @@ module.exports.doesS3ObjectExist = async (fileName) => {
|
|
|
56
56
|
console.error("Error checking if file exists in S3: ", err);
|
|
57
57
|
throw err;
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|
package/src/transform.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import _ from "lodash";
|
|
2
2
|
|
|
3
3
|
const transformObjectKeys = (transform, object, deep = true) => _.cond([
|
|
4
4
|
[
|
|
@@ -32,9 +32,4 @@ const pascalCaseObjectKeys = (transformItem, deep) => transformObjectKeys(
|
|
|
32
32
|
deep,
|
|
33
33
|
);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
transformObjectKeys,
|
|
37
|
-
lowerCaseObjectKeys,
|
|
38
|
-
pascalCaseObjectKeys,
|
|
39
|
-
pascalCase,
|
|
40
|
-
};
|
|
35
|
+
export {transformObjectKeys, lowerCaseObjectKeys, pascalCaseObjectKeys, pascalCase};
|
package/src/udl.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {fixDate} from "./fixDate.js";
|
|
2
|
+
import {calcRegime, getElsetUdlFromTle, getLonAndDrift, getRaanPrecession} from "./astro.js";
|
|
3
|
+
import {lowerCaseObjectKeys} from "./transform.js";
|
|
4
|
+
import {isDefined} from "./utils.js";
|
|
5
|
+
import _ from "lodash";
|
|
6
6
|
|
|
7
7
|
const udlToNpsElset = (udlRow) => {
|
|
8
8
|
let derivedElset;
|
|
@@ -98,7 +98,7 @@ const udlToNpsGroundSite = (udlRow) => ({
|
|
|
98
98
|
* @param {Object[]} udlData
|
|
99
99
|
* @return {Object[]}
|
|
100
100
|
*/
|
|
101
|
-
|
|
101
|
+
export function formatUdlData(topic, udlData) {
|
|
102
102
|
switch (topic) {
|
|
103
103
|
case "Elsets":
|
|
104
104
|
return _.map(udlData, udlToNpsElset);
|
|
@@ -109,7 +109,6 @@ module.exports.formatUdlData = (topic, udlData) => {
|
|
|
109
109
|
default:
|
|
110
110
|
return udlData;
|
|
111
111
|
}
|
|
112
|
-
}
|
|
112
|
+
}
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
module.exports.udlToNpsGroundSite = udlToNpsGroundSite;
|
|
114
|
+
export {udlToNpsElset, udlToNpsGroundSite};
|
package/src/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import {cross, dot, norm} from "mathjs";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Wrap angle to [0, 2π] range (0 to 360 degrees)
|
|
@@ -381,7 +381,7 @@ const getAngleDiffSigned = (initial, final) => {
|
|
|
381
381
|
return sign * minAngle;
|
|
382
382
|
};
|
|
383
383
|
|
|
384
|
-
|
|
384
|
+
export {
|
|
385
385
|
wrapOneRevUnsigned,
|
|
386
386
|
wrapHalfRevUnsigned,
|
|
387
387
|
wrapHalfRev,
|