@saber-usa/node-common 1.7.16 → 1.7.17
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 +22 -1
- package/package.json +9 -9
- package/src/FrameConverter.js +116 -139
- package/src/LLA.js +1 -1
- package/src/LaunchNominalClass.js +10 -24
- package/src/NodeVector3D.js +2 -2
- package/src/OrbitUtils.js +23 -42
- package/src/ShadowGEOCalculator.js +2 -2
- package/src/TimeConverter.js +11 -9
- package/src/astro.js +170 -164
- package/src/ballisticPropagator.js +34 -40
- package/src/checkNetwork.cjs +21 -21
- package/src/constants.js +2 -2
- package/src/fixDate.js +1 -1
- package/src/index.js +1 -1
- package/src/launchNominal.js +13 -13
- package/src/loggerFactory.cjs +122 -99
- package/src/s3.js +3 -3
- package/src/udl.js +1 -1
- package/src/utils.js +2 -2
package/src/astro.js
CHANGED
|
@@ -72,6 +72,7 @@ const checkTle = (line1, line2) => {
|
|
|
72
72
|
return satrec;
|
|
73
73
|
}
|
|
74
74
|
} catch (e) {
|
|
75
|
+
console.error(e);
|
|
75
76
|
return false; // TLE invalid
|
|
76
77
|
}
|
|
77
78
|
};
|
|
@@ -188,6 +189,7 @@ const getRaanPrecession = (line1, line2) => {
|
|
|
188
189
|
// result is in degrees per day
|
|
189
190
|
return satrec.nodedot * 1440 * RAD2DEG;
|
|
190
191
|
} catch (e) {
|
|
192
|
+
console.error(e);
|
|
191
193
|
return 0;
|
|
192
194
|
}
|
|
193
195
|
};
|
|
@@ -244,6 +246,7 @@ const getLonAndDrift = (line1, line2, datetime) => {
|
|
|
244
246
|
lonDriftDegreesPerDay: diff,
|
|
245
247
|
};
|
|
246
248
|
} catch (e) {
|
|
249
|
+
console.error(e);
|
|
247
250
|
return {
|
|
248
251
|
latitude: 0,
|
|
249
252
|
longitude: 0,
|
|
@@ -382,7 +385,7 @@ const angleBetween3DCoords = (coord1, coord2, coord3) => {
|
|
|
382
385
|
// angle between them (it's scaled by the product of their magnitudes).
|
|
383
386
|
|
|
384
387
|
// Normalize v1
|
|
385
|
-
const v1mag = Math.
|
|
388
|
+
const v1mag = Math.hypot(v1.x, v1.y, v1.z);
|
|
386
389
|
const v1norm = {
|
|
387
390
|
x: v1.x / v1mag,
|
|
388
391
|
y: v1.y / v1mag,
|
|
@@ -390,7 +393,7 @@ const angleBetween3DCoords = (coord1, coord2, coord3) => {
|
|
|
390
393
|
};
|
|
391
394
|
|
|
392
395
|
// Normalize v2
|
|
393
|
-
const v2mag = Math.
|
|
396
|
+
const v2mag = Math.hypot(v2.x, v2.y, v2.z);
|
|
394
397
|
const v2norm = {
|
|
395
398
|
x: v2.x / v2mag,
|
|
396
399
|
y: v2.y / v2mag,
|
|
@@ -402,7 +405,7 @@ const angleBetween3DCoords = (coord1, coord2, coord3) => {
|
|
|
402
405
|
= v1norm.x * v2norm.x + v1norm.y * v2norm.y + v1norm.z * v2norm.z;
|
|
403
406
|
|
|
404
407
|
// Extract the angle from the dot products
|
|
405
|
-
const angle = (Math.acos(dotProducts) * 180
|
|
408
|
+
const angle = (Math.acos(dotProducts) * 180) / Math.PI;
|
|
406
409
|
|
|
407
410
|
// Round result to 3 decimal points and return
|
|
408
411
|
return Math.round(angle * 1000) / 1000;
|
|
@@ -463,17 +466,17 @@ const planeChangeDeltaV = (pv1, pv2) => {
|
|
|
463
466
|
|
|
464
467
|
// 1. Compute the angular momentum of each orbit from the pos vel vectors
|
|
465
468
|
const r = multiply([pv1.position.x, pv1.position.y, pv1.position.z],
|
|
466
|
-
1000
|
|
469
|
+
1000);
|
|
467
470
|
const v = multiply([pv1.velocity.x, pv1.velocity.y, pv1.velocity.z],
|
|
468
|
-
1000
|
|
471
|
+
1000);
|
|
469
472
|
const h1 = cross(r, v);
|
|
470
473
|
|
|
471
474
|
const vMag = norm(v);
|
|
472
475
|
|
|
473
476
|
const r2 = multiply([pv2.position.x, pv2.position.y, pv2.position.z],
|
|
474
|
-
1000
|
|
477
|
+
1000);
|
|
475
478
|
const v2 = multiply([pv2.velocity.x, pv2.velocity.y, pv2.velocity.z],
|
|
476
|
-
1000
|
|
479
|
+
1000);
|
|
477
480
|
const h2 = cross(r2, v2);
|
|
478
481
|
|
|
479
482
|
// 2. Compute the mutual line of nodes vector
|
|
@@ -543,7 +546,7 @@ const planeChangeDeltaV = (pv1, pv2) => {
|
|
|
543
546
|
r: 0,
|
|
544
547
|
i: dvI,
|
|
545
548
|
c: dvC,
|
|
546
|
-
mag: (dvAsc
|
|
549
|
+
mag: Math.min(dvAsc, dvDesc),
|
|
547
550
|
node: (dvAsc <= dvDesc) ? "asc": "desc",
|
|
548
551
|
};
|
|
549
552
|
};
|
|
@@ -581,14 +584,14 @@ const planeChangePureInclinationDeltaV = (pv1, pv2)=>{
|
|
|
581
584
|
// 1. Get position and velocity vectors and magnitudes.
|
|
582
585
|
// Note that the magnitude of the final velocity for sat1 will be EQUAL
|
|
583
586
|
// to this initial velocity magnitude of sat1!
|
|
584
|
-
const r = multiply([pv1.position.x, pv1.position.y, pv1.position.z], 1000
|
|
585
|
-
const v = multiply([pv1.velocity.x, pv1.velocity.y, pv1.velocity.z], 1000
|
|
587
|
+
const r = multiply([pv1.position.x, pv1.position.y, pv1.position.z], 1000);
|
|
588
|
+
const v = multiply([pv1.velocity.x, pv1.velocity.y, pv1.velocity.z], 1000);
|
|
586
589
|
|
|
587
590
|
// Velocity Magnitude in m/s
|
|
588
591
|
const vMag = norm(v);
|
|
589
592
|
|
|
590
|
-
const r2 = multiply([pv2.position.x, pv2.position.y, pv2.position.z], 1000
|
|
591
|
-
const v2 = multiply([pv2.velocity.x, pv2.velocity.y, pv2.velocity.z], 1000
|
|
593
|
+
const r2 = multiply([pv2.position.x, pv2.position.y, pv2.position.z], 1000);
|
|
594
|
+
const v2 = multiply([pv2.velocity.x, pv2.velocity.y, pv2.velocity.z], 1000);
|
|
592
595
|
|
|
593
596
|
// 2. Compute the Keplerian Elements of both satellites.
|
|
594
597
|
const el = cartesianToKeplerian(r, v);
|
|
@@ -613,7 +616,7 @@ const planeChangePureInclinationDeltaV = (pv1, pv2)=>{
|
|
|
613
616
|
const dvAsc = 2* vMag * Math.cos(gammaAscending) * Math.sin(incRad/2);
|
|
614
617
|
|
|
615
618
|
// 4b. Compute the flight path angle and DV at descending node
|
|
616
|
-
const fAtDesc = fAtAsc + 180
|
|
619
|
+
const fAtDesc = fAtAsc + 180;
|
|
617
620
|
const gammaDescending = Math.atan(
|
|
618
621
|
el.e* Math.sin(fAtDesc*DEG2RAD)/(1+ el.e* Math.cos(fAtDesc*DEG2RAD)));
|
|
619
622
|
const dvDesc = 2* vMag * Math.cos(gammaDescending) * Math.sin(incRad/2);
|
|
@@ -644,16 +647,14 @@ const planeChangePureInclinationDeltaV = (pv1, pv2)=>{
|
|
|
644
647
|
dvC = (+1) * vMag * Math.cos(gammaDescending) * Math.sin(incRad);
|
|
645
648
|
dvI = (-1) * vMag * Math.cos(gammaDescending) * (1 - Math.cos(incRad));
|
|
646
649
|
}
|
|
650
|
+
} else if (Math.abs(dvAsc) <= Math.abs(dvDesc)) {
|
|
651
|
+
// Burn at ASC node is aligned with sat1 cross-track
|
|
652
|
+
dvC = (+1) * vMag * Math.cos(gammaAscending) * Math.sin(incRad);
|
|
653
|
+
dvI = (-1) * vMag * Math.cos(gammaAscending) * (1 - Math.cos(incRad));
|
|
647
654
|
} else {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
dvI = (-1) * vMag * Math.cos(gammaAscending) * (1 - Math.cos(incRad));
|
|
652
|
-
} else {
|
|
653
|
-
// Burn at DESC node is opposite of the sat1 cross-track axis
|
|
654
|
-
dvC = (-1) * vMag * Math.cos(gammaDescending) * Math.sin(incRad);
|
|
655
|
-
dvI = (-1) * vMag * Math.cos(gammaDescending) * (1 - Math.cos(incRad));
|
|
656
|
-
}
|
|
655
|
+
// Burn at DESC node is opposite of the sat1 cross-track axis
|
|
656
|
+
dvC = (-1) * vMag * Math.cos(gammaDescending) * Math.sin(incRad);
|
|
657
|
+
dvI = (-1) * vMag * Math.cos(gammaDescending) * (1 - Math.cos(incRad));
|
|
657
658
|
}
|
|
658
659
|
|
|
659
660
|
// 5. Return the RSW components of the required burn!
|
|
@@ -662,7 +663,7 @@ const planeChangePureInclinationDeltaV = (pv1, pv2)=>{
|
|
|
662
663
|
r: 0,
|
|
663
664
|
i: dvI,
|
|
664
665
|
c: dvC,
|
|
665
|
-
mag: (dvAsc
|
|
666
|
+
mag: Math.min(dvAsc, dvDesc),
|
|
666
667
|
node: (dvAsc <= dvDesc) ? "asc": "desc",
|
|
667
668
|
};
|
|
668
669
|
};
|
|
@@ -686,10 +687,7 @@ const angleBetweenPlanes = (pv1, pv2) => {
|
|
|
686
687
|
[pv1.position.x, pv1.position.y, pv1.position.z],
|
|
687
688
|
[pv1.velocity.x, pv1.velocity.y, pv1.velocity.z],
|
|
688
689
|
);
|
|
689
|
-
const cross1mag = Math.
|
|
690
|
-
cross1[0] * cross1[0]
|
|
691
|
-
+ cross1[1] * cross1[1]
|
|
692
|
-
+ cross1[2] * cross1[2]);
|
|
690
|
+
const cross1mag = Math.hypot(cross1[0], cross1[1], cross1[2]);
|
|
693
691
|
|
|
694
692
|
const cross1Norm = {
|
|
695
693
|
x: cross1[0] / cross1mag,
|
|
@@ -701,10 +699,7 @@ const angleBetweenPlanes = (pv1, pv2) => {
|
|
|
701
699
|
[pv2.position.x, pv2.position.y, pv2.position.z],
|
|
702
700
|
[pv2.velocity.x, pv2.velocity.y, pv2.velocity.z],
|
|
703
701
|
);
|
|
704
|
-
const cross2Mag = Math.
|
|
705
|
-
cross2[0] * cross2[0]
|
|
706
|
-
+ cross2[1] * cross2[1]
|
|
707
|
-
+ cross2[2] * cross2[2]);
|
|
702
|
+
const cross2Mag = Math.hypot(cross2[0], cross2[1], cross2[2]);
|
|
708
703
|
|
|
709
704
|
const cross2Norm = {
|
|
710
705
|
x: cross2[0] / cross2Mag,
|
|
@@ -721,7 +716,7 @@ const angleBetweenPlanes = (pv1, pv2) => {
|
|
|
721
716
|
const clamped = Math.max(-1, Math.min(1, dotProducts));
|
|
722
717
|
|
|
723
718
|
// Extract the angle from the dot products in degrees
|
|
724
|
-
const angle = Math.acos(clamped) * (180
|
|
719
|
+
const angle = Math.acos(clamped) * (180 / Math.PI);
|
|
725
720
|
|
|
726
721
|
// Round result to 3 decimal points and return
|
|
727
722
|
return Math.round(angle * 1000) / 1000;
|
|
@@ -829,7 +824,7 @@ const doesLineSegmentSphereIntersect = (linePoint0, linePoint1, circleCenter, ci
|
|
|
829
824
|
return Math.abs(distanceSquared - circleRadius * circleRadius) < Number.EPSILON;
|
|
830
825
|
}
|
|
831
826
|
|
|
832
|
-
const b = 2
|
|
827
|
+
const b = 2 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
|
|
833
828
|
const c = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy
|
|
834
829
|
+ pz * pz - 2 * pz * cz + cz * cz - circleRadius * circleRadius;
|
|
835
830
|
|
|
@@ -841,8 +836,8 @@ const doesLineSegmentSphereIntersect = (linePoint0, linePoint1, circleCenter, ci
|
|
|
841
836
|
}
|
|
842
837
|
|
|
843
838
|
const sqrtD = Math.sqrt(d);
|
|
844
|
-
const t1 = (-b - sqrtD) / (2
|
|
845
|
-
const t2 = (-b + sqrtD) / (2
|
|
839
|
+
const t1 = (-b - sqrtD) / (2 * a);
|
|
840
|
+
const t2 = (-b + sqrtD) / (2 * a);
|
|
846
841
|
|
|
847
842
|
// Check if either intersection point lies within the line segment bounds [0,1]
|
|
848
843
|
return (t1 >= 0 && t1 <= 1) || (t2 >= 0 && t2 <= 1);
|
|
@@ -946,13 +941,13 @@ const getSunDirection = (time) => {
|
|
|
946
941
|
const seconds = time.getUTCSeconds();
|
|
947
942
|
const JD
|
|
948
943
|
= 367 * year
|
|
949
|
-
- Math.floor((7
|
|
950
|
-
+ Math.floor((275
|
|
944
|
+
- Math.floor((7 * (year + Math.floor((month + 9) / 12))) / 4)
|
|
945
|
+
+ Math.floor((275 * month) / 9)
|
|
951
946
|
+ day
|
|
952
947
|
+ 1721013.5
|
|
953
|
-
+ hour / 24
|
|
954
|
-
+ minute / 1440
|
|
955
|
-
+ seconds / 86400
|
|
948
|
+
+ hour / 24
|
|
949
|
+
+ minute / 1440
|
|
950
|
+
+ seconds / 86400;
|
|
956
951
|
const UT1 = (JD - 2451545) / 36525;
|
|
957
952
|
const longMSUN = 280.4606184 + 36000.77005361 * UT1;
|
|
958
953
|
const mSUN = 357.5277233 + 35999.05034 * UT1;
|
|
@@ -1111,8 +1106,21 @@ const estimateSlantRange = (obTime, ra, dec, senLat, senLon, senAltKm) => {
|
|
|
1111
1106
|
(Math.pow(a, 2) + Math.pow(b, 2) - Math.pow(delta, 2)), (-2*a*b), Math.pow(delta, 2));
|
|
1112
1107
|
|
|
1113
1108
|
// The two solutions, only one is valid. Validity check is based on the fact that the sum of triangle angles is 180.
|
|
1114
|
-
const
|
|
1115
|
-
|
|
1109
|
+
const toRealRoot = (root) => {
|
|
1110
|
+
if (typeof root === "number") {
|
|
1111
|
+
return root;
|
|
1112
|
+
}
|
|
1113
|
+
if (root && typeof root.re === "number" && typeof root.im === "number") {
|
|
1114
|
+
// Accept numerically real roots produced as Complex with negligible imaginary part.
|
|
1115
|
+
if (Math.abs(root.im) <= 1e-12) {
|
|
1116
|
+
return root.re;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
throw new Error("Unable to compute GEO range from non-real polynomial root.");
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
const gamma1 = Math.acos(toRealRoot(roots[0]));
|
|
1123
|
+
const gamma2 = Math.acos(toRealRoot(roots[1]));
|
|
1116
1124
|
|
|
1117
1125
|
// Compute the sensor-satellite-earth angle
|
|
1118
1126
|
const alpha = Math.sin(delta/a);
|
|
@@ -1244,8 +1252,7 @@ const GetResiduals = (obs, tle) => {
|
|
|
1244
1252
|
return residuals; // Can't propegate TLE
|
|
1245
1253
|
}
|
|
1246
1254
|
|
|
1247
|
-
for (
|
|
1248
|
-
const ob = obs[i];
|
|
1255
|
+
for (const ob of obs) {
|
|
1249
1256
|
const obTimeUtc = dtStrtoJsDt(ob.ObTime);
|
|
1250
1257
|
const dt
|
|
1251
1258
|
= (obTimeUtc.getTime() - dtStrtoJsDt(epochDate).getTime())
|
|
@@ -1321,7 +1328,7 @@ const GetElsetUdlFromTle = (
|
|
|
1321
1328
|
// and we may be unsure of entering true or false for any reason.
|
|
1322
1329
|
if (isBoolean(isUct)) {
|
|
1323
1330
|
elset.uct = isUct;
|
|
1324
|
-
} else if (isUct === null ||
|
|
1331
|
+
} else if (isUct === null || isUct === undefined) {
|
|
1325
1332
|
// Do nothing, do not populate nor put null.
|
|
1326
1333
|
} else {
|
|
1327
1334
|
throw new Error("Input uct flag is not of type boolean.");
|
|
@@ -1353,7 +1360,7 @@ const GetElsetUdlFromTle = (
|
|
|
1353
1360
|
const date = julianToGregorian(satrec.jdsatepoch);
|
|
1354
1361
|
elset.epoch = date.toISOString();
|
|
1355
1362
|
|
|
1356
|
-
elset.satNo = parseInt(satrec.satnum);
|
|
1363
|
+
elset.satNo = Number.parseInt(satrec.satnum);
|
|
1357
1364
|
|
|
1358
1365
|
// Eccentricity
|
|
1359
1366
|
elset.eccentricity = satrec.ecco;
|
|
@@ -1361,16 +1368,16 @@ const GetElsetUdlFromTle = (
|
|
|
1361
1368
|
// Mean motion conversion from radians per minute to revs per day does not match
|
|
1362
1369
|
// the value of mean motion on line 2 exactly! This is an issue of the satellite.js library
|
|
1363
1370
|
// So we directly extract this number from line 2.
|
|
1364
|
-
elset.meanMotion = parseFloat(l2.slice(52, 63));
|
|
1371
|
+
elset.meanMotion = Number.parseFloat(l2.slice(52, 63));
|
|
1365
1372
|
|
|
1366
1373
|
// According to UDL, period field is the inverse of mean motion, in minutes!
|
|
1367
1374
|
// This is added for convenience of the consumer. It is not needed for a successful POST operation.
|
|
1368
|
-
elset.period = (1 / elset.meanMotion) * 24
|
|
1375
|
+
elset.period = (1 / elset.meanMotion) * 24 * 60;
|
|
1369
1376
|
|
|
1370
1377
|
// For convenience, we report the ephemeris Type, which is NOT automatically populated by UDL.
|
|
1371
1378
|
// Again, this field is not mandatory and POST will succeed if it is ommitted. It is offered for convenience!
|
|
1372
1379
|
// UDL suggests to use "SGP4" if orbital period < 225 minutes, and SDP4 otherwise (see ephemType field description in UDL Elset schema)
|
|
1373
|
-
if (elset.period < 225
|
|
1380
|
+
if (elset.period < 225) {
|
|
1374
1381
|
elset.ephemType = 0;
|
|
1375
1382
|
} else {
|
|
1376
1383
|
elset.ephemType = 3;
|
|
@@ -1401,7 +1408,7 @@ const GetElsetUdlFromTle = (
|
|
|
1401
1408
|
elset.meanMotionDDot = satrec.nddot * (constants.xpdotp * 1440 * 1440);
|
|
1402
1409
|
|
|
1403
1410
|
// Revolution number, parsed directly from line 2
|
|
1404
|
-
elset.revNo = parseInt(l2.slice(63, 68));
|
|
1411
|
+
elset.revNo = Number.parseInt(l2.slice(63, 68));
|
|
1405
1412
|
|
|
1406
1413
|
// sma
|
|
1407
1414
|
elset.semiMajorAxis = (
|
|
@@ -1418,6 +1425,7 @@ const GetElsetUdlFromTle = (
|
|
|
1418
1425
|
removeNullUndefined(elset);
|
|
1419
1426
|
return elset;
|
|
1420
1427
|
} catch (e) {
|
|
1428
|
+
console.error(e);
|
|
1421
1429
|
// Return an empty object, which is guaranteed to be invalid
|
|
1422
1430
|
// against UDL Elset_Ingested schema.
|
|
1423
1431
|
return {};
|
|
@@ -1434,7 +1442,7 @@ const GetElsetUdlFromTle = (
|
|
|
1434
1442
|
* Source: https://www.movable-type.co.uk/scripts/latlong.html
|
|
1435
1443
|
*/
|
|
1436
1444
|
const distGeodetic = (lat1, lon1, lat2, lon2) => {
|
|
1437
|
-
const R = WGS84_EARTH_EQUATORIAL_RADIUS_KM * 1000
|
|
1445
|
+
const R = WGS84_EARTH_EQUATORIAL_RADIUS_KM * 1000; // metres
|
|
1438
1446
|
const phi1 = lat1 * DEG2RAD; // φ1 in formula
|
|
1439
1447
|
const phi2 = lat2 * DEG2RAD; // φ2 in formula
|
|
1440
1448
|
const deltaPhi = (lat2 - lat1) * DEG2RAD; // Δφ in formula
|
|
@@ -1486,7 +1494,7 @@ const cartesianToKeplerian = (r, v) => {
|
|
|
1486
1494
|
const zeta = 0.5 * Math.pow(norm(v), 2) - (mu / norm(r));
|
|
1487
1495
|
|
|
1488
1496
|
if (zeta === 0) {throw new Error("Zeta cannot be zero.");}
|
|
1489
|
-
if (Math.abs(1
|
|
1497
|
+
if (Math.abs(1 - norm(e)) <= tol) {
|
|
1490
1498
|
throw new Error("Parabolic orbit conversion is not supported.");
|
|
1491
1499
|
}
|
|
1492
1500
|
|
|
@@ -1509,7 +1517,7 @@ const cartesianToKeplerian = (r, v) => {
|
|
|
1509
1517
|
|
|
1510
1518
|
// CASE 1: Non-circular, Inclined Orbit
|
|
1511
1519
|
if (norm(e) >= 1E-11 && i >= 1E-11 && i <= Math.PI - 1E-11) {
|
|
1512
|
-
if (norm(n) === 0
|
|
1520
|
+
if (norm(n) === 0) {
|
|
1513
1521
|
throw new Error(`Cannot convert from Cartesian to Keplerian,
|
|
1514
1522
|
line-of-nodes vector is a zero vector.`);
|
|
1515
1523
|
}
|
|
@@ -1525,7 +1533,7 @@ const cartesianToKeplerian = (r, v) => {
|
|
|
1525
1533
|
}
|
|
1526
1534
|
// CASE 2: Non-circular, Equatorial Orbit
|
|
1527
1535
|
if (norm(e) >= 1E-11 && (i < 1E-11 || i > Math.PI - 1E-11)) {
|
|
1528
|
-
if (norm(e) === 0
|
|
1536
|
+
if (norm(e) === 0) {
|
|
1529
1537
|
throw new Error(`Cannot convert from Cartesian to Keplerian,
|
|
1530
1538
|
eccentricity is zero.`);
|
|
1531
1539
|
}
|
|
@@ -1537,9 +1545,9 @@ const cartesianToKeplerian = (r, v) => {
|
|
|
1537
1545
|
|
|
1538
1546
|
// For GMT-4446 fix (LOJ: 2014.03.21)
|
|
1539
1547
|
if (i > Math.PI - 1E-11) {
|
|
1540
|
-
w *= -1
|
|
1548
|
+
w *= -1;
|
|
1541
1549
|
}
|
|
1542
|
-
if (w < 0
|
|
1550
|
+
if (w < 0) {
|
|
1543
1551
|
w += 2 * Math.PI;
|
|
1544
1552
|
}
|
|
1545
1553
|
|
|
@@ -1550,7 +1558,7 @@ const cartesianToKeplerian = (r, v) => {
|
|
|
1550
1558
|
}
|
|
1551
1559
|
// CASE 3: Circular, Inclined Orbit
|
|
1552
1560
|
if (norm(e) < 1E-11 && i >= 1E-11 && i <= Math.PI - 1E-11) {
|
|
1553
|
-
if (norm(n) === 0
|
|
1561
|
+
if (norm(n) === 0) {
|
|
1554
1562
|
throw new Error(`Cannot convert from Cartesian to Keplerian,
|
|
1555
1563
|
line-of-nodes vector is a zero vector.`);
|
|
1556
1564
|
}
|
|
@@ -1573,14 +1581,14 @@ const cartesianToKeplerian = (r, v) => {
|
|
|
1573
1581
|
if (r[1] < 0) {f = 2 * Math.PI - f;}
|
|
1574
1582
|
|
|
1575
1583
|
// For GMT-4446 fix (LOJ: 2014.03.21)
|
|
1576
|
-
if (i > Math.PI - 1E-11) {f *= -1
|
|
1577
|
-
if (f < 0
|
|
1584
|
+
if (i > Math.PI - 1E-11) {f *= -1;}
|
|
1585
|
+
if (f < 0) {
|
|
1578
1586
|
f += 2 * Math.PI;
|
|
1579
1587
|
}
|
|
1580
1588
|
}
|
|
1581
1589
|
|
|
1582
1590
|
return {
|
|
1583
|
-
a: a /1000
|
|
1591
|
+
a: a /1000, // km
|
|
1584
1592
|
e: norm(e),
|
|
1585
1593
|
i: i * RAD2DEG, // deg
|
|
1586
1594
|
raan: raan * RAD2DEG, // deg
|
|
@@ -1641,21 +1649,22 @@ const keplerianToCartesian = (elset, mu = 398600.4418) => {
|
|
|
1641
1649
|
const sinPer = Math.sin(w);
|
|
1642
1650
|
|
|
1643
1651
|
const r = {
|
|
1644
|
-
x: 1000
|
|
1645
|
-
y: 1000
|
|
1646
|
-
z: 1000
|
|
1652
|
+
x: 1000 * rad * (cosPerAnom * cosRaan - cosInc * sinPerAnom * sinRaan),
|
|
1653
|
+
y: 1000 * rad * (cosPerAnom * sinRaan + cosInc * sinPerAnom * cosRaan),
|
|
1654
|
+
z: 1000 * rad * sinPerAnom * sinInc,
|
|
1647
1655
|
};
|
|
1648
1656
|
|
|
1649
1657
|
const v = {
|
|
1650
|
-
x: 1000
|
|
1658
|
+
x: 1000 * (sqrtGravP * cosAnomPlusE * (-sinPer * cosRaan - cosInc * sinRaan * cosPer)
|
|
1651
1659
|
- sqrtGravP * sinAnom * (cosPer * cosRaan - cosInc * sinRaan * sinPer)),
|
|
1652
|
-
y: 1000
|
|
1660
|
+
y: 1000 * (sqrtGravP * cosAnomPlusE * (-sinPer * sinRaan + cosInc * cosRaan * cosPer)
|
|
1653
1661
|
- sqrtGravP * sinAnom * (cosPer * sinRaan + cosInc * cosRaan * sinPer)),
|
|
1654
|
-
z: 1000
|
|
1662
|
+
z: 1000 * (sqrtGravP * (cosAnomPlusE * sinInc * cosPer - sinAnom * sinInc * sinPer)),
|
|
1655
1663
|
};
|
|
1656
1664
|
|
|
1657
1665
|
return {r, v};
|
|
1658
1666
|
} catch (err) {
|
|
1667
|
+
console.error(err);
|
|
1659
1668
|
return {};
|
|
1660
1669
|
}
|
|
1661
1670
|
};
|
|
@@ -1663,8 +1672,8 @@ const keplerianToCartesian = (elset, mu = 398600.4418) => {
|
|
|
1663
1672
|
const cartesianToElsetElements = (pv, epoch) => {
|
|
1664
1673
|
// sma, eccentricity, inclination, raan, argp, trueAnomaly
|
|
1665
1674
|
const kepl = cartesianToKeplerian(
|
|
1666
|
-
multiply(posToArray(pv.position), 1000
|
|
1667
|
-
multiply(posToArray(pv.velocity), 1000
|
|
1675
|
+
multiply(posToArray(pv.position), 1000), // km to m
|
|
1676
|
+
multiply(posToArray(pv.velocity), 1000), // km/s to m/s
|
|
1668
1677
|
);
|
|
1669
1678
|
|
|
1670
1679
|
const elset = {
|
|
@@ -1677,7 +1686,7 @@ const cartesianToElsetElements = (pv, epoch) => {
|
|
|
1677
1686
|
|
|
1678
1687
|
// Mean motion in radians per second
|
|
1679
1688
|
const mu = 3.986004418e14; // (m^3)/(s^2) WGS-84 Earth Mu
|
|
1680
|
-
const meanMotion = Math.sqrt(mu/Math.pow((elset.SemiMajorAxis*1000
|
|
1689
|
+
const meanMotion = Math.sqrt(mu/Math.pow((elset.SemiMajorAxis*1000), 3));
|
|
1681
1690
|
|
|
1682
1691
|
elset.MeanMotion = meanMotion / (2*Math.PI) * 60 * 60 * 24; // rads/s to revs per day
|
|
1683
1692
|
|
|
@@ -1867,8 +1876,8 @@ const getGeoRpoData = (line1, line2, sats, startTime, endTime, lonTime) => {
|
|
|
1867
1876
|
aResult.longitude = (sLonAndDrift.longitude + 360) % 360; // Normalize to 0-360
|
|
1868
1877
|
|
|
1869
1878
|
const lonDiff = Math.abs(
|
|
1870
|
-
(pLonAndDrift.longitude + 360
|
|
1871
|
-
- (sLonAndDrift.longitude + 360
|
|
1879
|
+
(pLonAndDrift.longitude + 360) % 360
|
|
1880
|
+
- (sLonAndDrift.longitude + 360) % 360,
|
|
1872
1881
|
);
|
|
1873
1882
|
// To ensure that the smallest "short-way" lon diff is considered
|
|
1874
1883
|
aResult.lonDiff = Math.min(lonDiff, 360 - lonDiff);
|
|
@@ -1948,7 +1957,7 @@ const getGeoShadowZones = (time, accuracySecondsDeg=0.00416*100) => {
|
|
|
1948
1957
|
// Compare with the previous eclipse state
|
|
1949
1958
|
if (res[i].ecl!==res[i-1].ecl) {
|
|
1950
1959
|
// Close the previous zone
|
|
1951
|
-
ints
|
|
1960
|
+
ints.at(-1).stopDeg = res[i-1].geolon;
|
|
1952
1961
|
|
|
1953
1962
|
// Open a new zone
|
|
1954
1963
|
ints.push({
|
|
@@ -1971,16 +1980,16 @@ const getGeoShadowZones = (time, accuracySecondsDeg=0.00416*100) => {
|
|
|
1971
1980
|
};
|
|
1972
1981
|
}
|
|
1973
1982
|
|
|
1974
|
-
ints[0].startDeg = ints
|
|
1983
|
+
ints[0].startDeg = ints.at(-1).startDeg;
|
|
1975
1984
|
ints.pop();
|
|
1976
1985
|
|
|
1977
1986
|
if (
|
|
1978
1987
|
ints.filter((x)=>x.ecl === "UMBRA").length===0
|
|
1979
|
-
&& ints.
|
|
1988
|
+
&& ints.some((x)=>x.ecl === "PENUMBRA")) {
|
|
1980
1989
|
// If there's no umbra but penumbra exists, return the perumbra values
|
|
1981
1990
|
|
|
1982
1991
|
// Extract the penumbra interval. In the absence of umbra, a single penumbra interval should exist.
|
|
1983
|
-
const penumbraInt = ints.
|
|
1992
|
+
const penumbraInt = ints.find((x)=>x.ecl === "PENUMBRA");
|
|
1984
1993
|
return {
|
|
1985
1994
|
penStartWestLon: wrapToRange(penumbraInt.startDeg, 0, 360),
|
|
1986
1995
|
penStartEastLon: wrapToRange(penumbraInt.stopDeg, 0, 360),
|
|
@@ -1988,7 +1997,7 @@ const getGeoShadowZones = (time, accuracySecondsDeg=0.00416*100) => {
|
|
|
1988
1997
|
} else {
|
|
1989
1998
|
// If sun, umbra, penumbra intervals exist
|
|
1990
1999
|
|
|
1991
|
-
const umbra = ints.
|
|
2000
|
+
const umbra = ints.find((x)=>x.ecl === "UMBRA");
|
|
1992
2001
|
|
|
1993
2002
|
const umbraStart360 = wrapToRange(umbra.startDeg, 0, 360);
|
|
1994
2003
|
const umbraStop360 = wrapToRange(umbra.stopDeg, 0, 360);
|
|
@@ -2019,8 +2028,8 @@ const getGeoShadowZones = (time, accuracySecondsDeg=0.00416*100) => {
|
|
|
2019
2028
|
* @param {Number} accuracySeconds The accuracy of the calulations, defaults to 1 sec. The higher, the faster.
|
|
2020
2029
|
* @return {Array} An array of objects, each of which represents a contiguous interval of sunlight, penumbra, or umbra.
|
|
2021
2030
|
*/
|
|
2022
|
-
const getGeoLightIntervals = (time, durationSeconds=86400, accuracySeconds=10
|
|
2023
|
-
const endTime = new Date(time.getTime() + 1000
|
|
2031
|
+
const getGeoLightIntervals = (time, durationSeconds=86400, accuracySeconds=10) => {
|
|
2032
|
+
const endTime = new Date(time.getTime() + 1000*durationSeconds);
|
|
2024
2033
|
// Create an artificial satellite on GEO
|
|
2025
2034
|
const satrec = twoline2satrec(
|
|
2026
2035
|
"1 00000U 00000A 24079.98445361 -.00000000 00000-0 -00000-0 0 00000",
|
|
@@ -2048,7 +2057,7 @@ const getGeoLightIntervals = (time, durationSeconds=86400, accuracySeconds=10.0)
|
|
|
2048
2057
|
for (let i=1; i<=res.length-1; i++) {
|
|
2049
2058
|
if (res[i].ecl!==res[i-1].ecl) {
|
|
2050
2059
|
// Close the previous interval
|
|
2051
|
-
ints
|
|
2060
|
+
ints.at(-1).end = res[i-1].t;
|
|
2052
2061
|
// Create a new open interval
|
|
2053
2062
|
ints.push({
|
|
2054
2063
|
ecl: res[i].ecl,
|
|
@@ -2057,7 +2066,7 @@ const getGeoLightIntervals = (time, durationSeconds=86400, accuracySeconds=10.0)
|
|
|
2057
2066
|
}
|
|
2058
2067
|
}
|
|
2059
2068
|
// Close the last interval
|
|
2060
|
-
ints
|
|
2069
|
+
ints.at(-1).end = res.at(-1).t;
|
|
2061
2070
|
|
|
2062
2071
|
return {
|
|
2063
2072
|
ints: ints,
|
|
@@ -2174,9 +2183,9 @@ const calculateGeoCrossingTimes = async (propagateBetween, start, end, stepMs =
|
|
|
2174
2183
|
const calculateNextApogeePerigeeTimesWithPropagation
|
|
2175
2184
|
= async (pv, propagateTo, time, findApogee=true, findPerigee=true) => {
|
|
2176
2185
|
const r = multiply([pv.position.x, pv.position.y, pv.position.z],
|
|
2177
|
-
1000
|
|
2186
|
+
1000);
|
|
2178
2187
|
const v = multiply([pv.velocity.x, pv.velocity.y, pv.velocity.z],
|
|
2179
|
-
1000
|
|
2188
|
+
1000);
|
|
2180
2189
|
const el = cartesianToKeplerian(r, v);
|
|
2181
2190
|
|
|
2182
2191
|
// Compute Eccentric Anomaly from True Anomaly and Eccentricity
|
|
@@ -2187,7 +2196,7 @@ const calculateNextApogeePerigeeTimesWithPropagation
|
|
|
2187
2196
|
|
|
2188
2197
|
// Mean motion in radians per second
|
|
2189
2198
|
const mu = 3.986004418e14; // (m^3)/(s^2) WGS-84 Earth Mu
|
|
2190
|
-
const n = Math.sqrt(mu/Math.pow((el.a*1000
|
|
2199
|
+
const n = Math.sqrt(mu/Math.pow((el.a*1000), 3));
|
|
2191
2200
|
|
|
2192
2201
|
// Orbit Period
|
|
2193
2202
|
const periodSecs = 2*Math.PI/n;
|
|
@@ -2204,7 +2213,7 @@ const calculateNextApogeePerigeeTimesWithPropagation
|
|
|
2204
2213
|
|
|
2205
2214
|
// For next apoapsis, we check the true anomaly of the reference date
|
|
2206
2215
|
let timeToNextApoapsisSecs;
|
|
2207
|
-
if (el.f >= 180
|
|
2216
|
+
if (el.f >= 180) {
|
|
2208
2217
|
// Satellite is past the apoapsis and before the next periapsis
|
|
2209
2218
|
timeToNextApoapsisSecs = timeToNextPeriapsisSecs + periodSecs/2;
|
|
2210
2219
|
} else {
|
|
@@ -2274,7 +2283,7 @@ const calculateNextApogeePerigeeTimes
|
|
|
2274
2283
|
const satrec = twoline2satrec(line1, line2);
|
|
2275
2284
|
const propagateTo = (t) => propagate(satrec, new Date(t));
|
|
2276
2285
|
const pv = propagateTo(time);
|
|
2277
|
-
return
|
|
2286
|
+
return calculateNextApogeePerigeeTimesWithPropagation(
|
|
2278
2287
|
pv, propagateTo, time, findApogee, findPerigee,
|
|
2279
2288
|
);
|
|
2280
2289
|
};
|
|
@@ -2336,8 +2345,8 @@ const computePhaseDiff = (pSatRec, tSatRec, time) => {
|
|
|
2336
2345
|
*/
|
|
2337
2346
|
const getOrbitalPeriod = (line2) => {
|
|
2338
2347
|
// apparently mean motion should be obtained through TLE instead of satellite.js SatRec (see getElsetUdlFromTle)
|
|
2339
|
-
const meanMotion = parseFloat(line2.slice(52, 63)); // revs per day
|
|
2340
|
-
const period = (1 / meanMotion) * 24
|
|
2348
|
+
const meanMotion = Number.parseFloat(line2.slice(52, 63)); // revs per day
|
|
2349
|
+
const period = (1 / meanMotion) * 24 * 60; // period in minutes
|
|
2341
2350
|
return period;
|
|
2342
2351
|
};
|
|
2343
2352
|
|
|
@@ -2444,8 +2453,8 @@ const getLeoWaterfallData = (elsets, startTime, endTime, stepMs = 10000) => {
|
|
|
2444
2453
|
const ephem = prop(elset, segmentStart, segmentEnd, stepMs);
|
|
2445
2454
|
satEphems[satIndex].push(...ephem.map((point, pointInd) => {
|
|
2446
2455
|
const osculatingElements = cartesianToKeplerian(
|
|
2447
|
-
multiply(posToArray(point.p), 1000
|
|
2448
|
-
multiply(posToArray(point.v), 1000
|
|
2456
|
+
multiply(posToArray(point.p), 1000), // km to m
|
|
2457
|
+
multiply(posToArray(point.v), 1000), // km/s to m/s
|
|
2449
2458
|
);
|
|
2450
2459
|
return {
|
|
2451
2460
|
...point,
|
|
@@ -2539,9 +2548,9 @@ const getInterceptRendezvousMinDv = (sat1Tle, sat2Tle, startTime) =>{
|
|
|
2539
2548
|
const sat1Pv = propagate(sat1Tle, new Date(startTime));
|
|
2540
2549
|
|
|
2541
2550
|
// Sweep across 1 orbit period
|
|
2542
|
-
const sat1Period = parseInt((120*Math.PI)/(sat1Tle.no));
|
|
2543
|
-
const sat2Period = parseInt((120*Math.PI)/(sat2Tle.no));
|
|
2544
|
-
const periodS = sat1Period
|
|
2551
|
+
const sat1Period = Number.parseInt((120*Math.PI)/(sat1Tle.no));
|
|
2552
|
+
const sat2Period = Number.parseInt((120*Math.PI)/(sat2Tle.no));
|
|
2553
|
+
const periodS = Math.max(sat1Period, sat2Period);
|
|
2545
2554
|
|
|
2546
2555
|
for (let rev=0; rev < 10; rev++) {
|
|
2547
2556
|
for (let dt=100; dt<=periodS; dt+=100) { // Seconds
|
|
@@ -2565,7 +2574,7 @@ const getInterceptRendezvousMinDv = (sat1Tle, sat2Tle, startTime) =>{
|
|
|
2565
2574
|
}
|
|
2566
2575
|
|
|
2567
2576
|
const currentVector = v1Mag <= vH1Mag ? deltaV1 : deltaVH1;
|
|
2568
|
-
const currentNorm = v1Mag
|
|
2577
|
+
const currentNorm = Math.min(v1Mag, vH1Mag);
|
|
2569
2578
|
|
|
2570
2579
|
if (currentNorm < minDv) {
|
|
2571
2580
|
revNum = rev;
|
|
@@ -2590,7 +2599,7 @@ const getInterceptRendezvousMinDv = (sat1Tle, sat2Tle, startTime) =>{
|
|
|
2590
2599
|
}
|
|
2591
2600
|
// As N increments, at some point either both or one of the two energy solutions will be NaN.
|
|
2592
2601
|
// At that point, the N sweep can safely stop.
|
|
2593
|
-
if (isNaN(v1Mag) || isNaN(vH1Mag)) {
|
|
2602
|
+
if (Number.isNaN(v1Mag) || Number.isNaN(vH1Mag)) {
|
|
2594
2603
|
break;
|
|
2595
2604
|
}
|
|
2596
2605
|
}
|
|
@@ -2718,7 +2727,6 @@ const detectManeuverMinDv = (initialTLE, finalTLE) => {
|
|
|
2718
2727
|
|
|
2719
2728
|
// Loop for N until NaN
|
|
2720
2729
|
let minNorm = Infinity;
|
|
2721
|
-
// let minIndex = -1;
|
|
2722
2730
|
let minVector = [0, 0, 0];
|
|
2723
2731
|
for (let i=0; i<10; i++) { // loop up to 10 revolutions, it is a safe upper limit to capture impulsive maneuvers
|
|
2724
2732
|
const {v1, vH1} = lambertThomsonAlgorithm(r1, r2, dt, i, 0, v1Before, mu);
|
|
@@ -2735,16 +2743,15 @@ const detectManeuverMinDv = (initialTLE, finalTLE) => {
|
|
|
2735
2743
|
}
|
|
2736
2744
|
|
|
2737
2745
|
const currentVector = v1Mag <= vH1Mag ? deltaV1 : deltaVH1;
|
|
2738
|
-
const currentNorm = v1Mag
|
|
2746
|
+
const currentNorm = Math.min(v1Mag, vH1Mag);
|
|
2739
2747
|
|
|
2740
2748
|
if (currentNorm < minNorm) {
|
|
2741
2749
|
minNorm = currentNorm;
|
|
2742
|
-
// minIndex = i;
|
|
2743
2750
|
minVector = currentVector;
|
|
2744
2751
|
}
|
|
2745
2752
|
// As N increments, at some point either both or one of the two energy solutions will be NaN.
|
|
2746
2753
|
// At that point, the N sweep can safely stop.
|
|
2747
|
-
if (isNaN(v1Mag) || isNaN(vH1Mag)) {
|
|
2754
|
+
if (Number.isNaN(v1Mag) || Number.isNaN(vH1Mag)) {
|
|
2748
2755
|
break;
|
|
2749
2756
|
}
|
|
2750
2757
|
}
|
|
@@ -2799,8 +2806,8 @@ const lambertThomsonAlgorithm
|
|
|
2799
2806
|
|
|
2800
2807
|
// ----- Lines 2-5 -----
|
|
2801
2808
|
let A = dot(r1, r2) / (r1Mag * r2Mag);
|
|
2802
|
-
if (A > 1
|
|
2803
|
-
if (A < -1
|
|
2809
|
+
if (A > 1) {A = 1;}
|
|
2810
|
+
if (A < -1) {A = -1;}
|
|
2804
2811
|
let theta = Math.acos(A); // transfer angle
|
|
2805
2812
|
|
|
2806
2813
|
// ----- Lines 6-8: Determine transfer direction -----
|
|
@@ -2813,26 +2820,26 @@ const lambertThomsonAlgorithm
|
|
|
2813
2820
|
// ----- Lines 9-13 -----
|
|
2814
2821
|
const c = norm(subtract(r2, r1)); // chord length
|
|
2815
2822
|
const s = 0.5 * (r1Mag + r2Mag + c); // semiperimeter
|
|
2816
|
-
const lambda = (1
|
|
2817
|
-
const L = Math.pow((1 - lambda) / (1 + lambda), 2
|
|
2818
|
-
const mVal = 8 * mu * t * t / (Math.pow(s, 3
|
|
2823
|
+
const lambda = (1 / s) * Math.sqrt(r1Mag * r2Mag) * Math.cos(theta / 2);
|
|
2824
|
+
const L = Math.pow((1 - lambda) / (1 + lambda), 2);
|
|
2825
|
+
const mVal = 8 * mu * t * t / (Math.pow(s, 3) * Math.pow(1 + lambda, 6));
|
|
2819
2826
|
|
|
2820
2827
|
// ----- Line 14: Initial x -----
|
|
2821
2828
|
let x = (N > 0) ? (1 + 4 * L) : L;
|
|
2822
|
-
let xdiff = 1
|
|
2823
|
-
let y = 0
|
|
2829
|
+
let xdiff = 1;
|
|
2830
|
+
let y = 0;
|
|
2824
2831
|
|
|
2825
2832
|
// ----- Lines 15-54: Iteration for x -----
|
|
2826
2833
|
while (xdiff > 1e-6) {
|
|
2827
2834
|
let h1; let h2;
|
|
2828
2835
|
if (N > 0) {
|
|
2829
2836
|
// ----- Lines 17-19: Multi-rev (N > 0) branch using Loechler's method -----
|
|
2830
|
-
const numerator = Math.pow(L + x, 2
|
|
2837
|
+
const numerator = Math.pow(L + x, 2);
|
|
2831
2838
|
const denominator = 4 * x * x * (1 + 2 * x + L);
|
|
2832
2839
|
const sqrtx = Math.sqrt(x);
|
|
2833
2840
|
const commonTerm = ((N * Math.PI / 2 + Math.atan(sqrtx)) / sqrtx);
|
|
2834
2841
|
// The bracketed term (Loechler p.30 (4.3))
|
|
2835
|
-
const term1 = 3 * Math.pow(1 + x, 2
|
|
2842
|
+
const term1 = 3 * Math.pow(1 + x, 2) * commonTerm - (3 + 5 * x);
|
|
2836
2843
|
h1 = (numerator / denominator) * term1;
|
|
2837
2844
|
|
|
2838
2845
|
// ----- Line 19: h2 (Loechler p.31 (4.4)) -----
|
|
@@ -2840,7 +2847,7 @@ const lambertThomsonAlgorithm
|
|
|
2840
2847
|
h2 = mVal / denominator * term2;
|
|
2841
2848
|
} else {
|
|
2842
2849
|
// ----- Lines 20-36: Single-rev branch -----
|
|
2843
|
-
const eta = x / Math.pow(Math.sqrt(1 + x) + 1, 2
|
|
2850
|
+
const eta = x / Math.pow(Math.sqrt(1 + x) + 1, 2);
|
|
2844
2851
|
let bn = 3; let dn = 1; let un = 8 * (Math.sqrt(1 + x) + 1) / bn;
|
|
2845
2852
|
let xi = un;
|
|
2846
2853
|
let i = 1;
|
|
@@ -2862,16 +2869,16 @@ const lambertThomsonAlgorithm
|
|
|
2862
2869
|
un = up * (dn - 1);
|
|
2863
2870
|
xi += un;
|
|
2864
2871
|
}
|
|
2865
|
-
h1 = (Math.pow(L + x, 2
|
|
2872
|
+
h1 = (Math.pow(L + x, 2) * (1 + 3 * x + xi))
|
|
2866
2873
|
/ ((1 + 2 * x + L) * (4 * x + xi * (3 + x)));
|
|
2867
2874
|
h2 = mVal * (x - L + xi) / ((1 + 2 * x + L) * (4 * x + xi * (3 + x)));
|
|
2868
2875
|
}
|
|
2869
2876
|
|
|
2870
2877
|
// ----- Lines 38-39: Compute B and solve for K -----
|
|
2871
|
-
const BVal = 27
|
|
2872
|
-
const uVal = BVal / (2
|
|
2878
|
+
const BVal = 27 * h2 / (4 * Math.pow(1 + h1, 3));
|
|
2879
|
+
const uVal = BVal / (2 * (Math.sqrt(1 + BVal) + 1));
|
|
2873
2880
|
|
|
2874
|
-
const bn2 = 1
|
|
2881
|
+
const bn2 = 1; let dn2 = 1; let un2 = 1 / 3;
|
|
2875
2882
|
let K = un2;
|
|
2876
2883
|
let n = 0;
|
|
2877
2884
|
let evenflag = 1;
|
|
@@ -2879,22 +2886,22 @@ const lambertThomsonAlgorithm
|
|
|
2879
2886
|
const bp = bn2; const dp = dn2; const up = un2;
|
|
2880
2887
|
let an;
|
|
2881
2888
|
if (evenflag === 1) {
|
|
2882
|
-
an = -2
|
|
2889
|
+
an = -2 * uVal * (3 * n + 2) * (6 * n + 1) / (9 * (4 * n + 1) * (4 * n + 3));
|
|
2883
2890
|
evenflag = 0;
|
|
2884
2891
|
n++;
|
|
2885
2892
|
} else {
|
|
2886
|
-
an = -2
|
|
2893
|
+
an = -2 * uVal * (3 * n + 1) * (6 * n - 1) / 9 / (4 * n - 1) / (4 * n + 1);
|
|
2887
2894
|
evenflag = 1;
|
|
2888
2895
|
}
|
|
2889
|
-
dn2 = 1
|
|
2890
|
-
un2 = up * (dn2 - 1
|
|
2896
|
+
dn2 = 1 / (1 - an * dp / bp / bn2);
|
|
2897
|
+
un2 = up * (dn2 - 1);
|
|
2891
2898
|
K += un2;
|
|
2892
2899
|
}
|
|
2893
2900
|
|
|
2894
2901
|
// ----- Line 50: Compute y -----
|
|
2895
|
-
y = (1 + h1) / 3
|
|
2902
|
+
y = (1 + h1) / 3 * (2 + Math.sqrt(1 + BVal) / (1 + 2 * uVal * K * K));
|
|
2896
2903
|
// ----- Line 51: Update xnew (Battin p.335 (7.113))
|
|
2897
|
-
const xnew = Math.sqrt(Math.pow((1
|
|
2904
|
+
const xnew = Math.sqrt(Math.pow((1 - L)/2, 2) + mVal/(y*y)) - (1 + L)/2;
|
|
2898
2905
|
// ----- Line 52: Compute difference -----
|
|
2899
2906
|
xdiff = Math.abs(xnew - x);
|
|
2900
2907
|
// ----- Line 53: Update x -----
|
|
@@ -2902,14 +2909,14 @@ const lambertThomsonAlgorithm
|
|
|
2902
2909
|
}
|
|
2903
2910
|
|
|
2904
2911
|
// ----- Lines 55-57: Compute p and eccentricity -----
|
|
2905
|
-
const pVal = 2 * r1Mag * r2Mag * y * y * Math.pow(1 + x, 2
|
|
2906
|
-
* Math.pow(Math.sin(theta / 2), 2
|
|
2907
|
-
/ (mVal * s * Math.pow(1 + lambda, 2
|
|
2912
|
+
const pVal = 2 * r1Mag * r2Mag * y * y * Math.pow(1 + x, 2)
|
|
2913
|
+
* Math.pow(Math.sin(theta / 2), 2)
|
|
2914
|
+
/ (mVal * s * Math.pow(1 + lambda, 2));
|
|
2908
2915
|
const eps = (r2Mag - r1Mag) / r1Mag;
|
|
2909
2916
|
const eVal = Math.sqrt(
|
|
2910
|
-
(Math.pow(eps, 2
|
|
2911
|
-
* Math.pow(Math.sin(theta / 2
|
|
2912
|
-
/ (Math.pow(eps, 2
|
|
2917
|
+
(Math.pow(eps, 2) + 4 * r2Mag / r1Mag
|
|
2918
|
+
* Math.pow(Math.sin(theta / 2), 2) * Math.pow((L - x) / (L + x), 2))
|
|
2919
|
+
/ (Math.pow(eps, 2) + (4 * r2Mag / r1Mag) * Math.pow(Math.sin(theta / 2), 2)),
|
|
2913
2920
|
);
|
|
2914
2921
|
|
|
2915
2922
|
// ----- Line 58: Call hodograph velocity algorithm -----
|
|
@@ -2922,7 +2929,7 @@ const lambertThomsonAlgorithm
|
|
|
2922
2929
|
if (N > 0) {
|
|
2923
2930
|
// Line 61:
|
|
2924
2931
|
x = 1e-20;
|
|
2925
|
-
xdiff = 1
|
|
2932
|
+
xdiff = 1;
|
|
2926
2933
|
while (xdiff > 1e-6) {
|
|
2927
2934
|
// ----- Line 64: Compute h1 for high-energy solution (Loechler p.35 (5.5)) -----
|
|
2928
2935
|
const h1He = (L + x) * (1 + 2 * x + L) / (2 * (L - x * x));
|
|
@@ -2931,18 +2938,18 @@ const lambertThomsonAlgorithm
|
|
|
2931
2938
|
* ((L - x * x) * ((N * Math.PI / 2 + Math.atan(Math.sqrt(x)))
|
|
2932
2939
|
/ Math.sqrt(x)) - (L + x));
|
|
2933
2940
|
// ----- Line 66: Compute B for high-energy solution (Loechler p.37 (5.9)) -----
|
|
2934
|
-
const BHe = 27 * h2He / (4 * Math.pow(Math.sqrt(x) * (1 + h1He), 3
|
|
2941
|
+
const BHe = 27 * h2He / (4 * Math.pow(Math.sqrt(x) * (1 + h1He), 3));
|
|
2935
2942
|
let F;
|
|
2936
2943
|
if (BHe < 0) {
|
|
2937
2944
|
// ----- Line 68: Compute F for negative B -----
|
|
2938
|
-
F = 2 * Math.cos(1
|
|
2945
|
+
F = 2 * Math.cos(1 / 3 * Math.acos(Math.sqrt(BHe + 1)));
|
|
2939
2946
|
} else {
|
|
2940
2947
|
// ----- Lines 70-71: Compute F for nonnegative B -----
|
|
2941
|
-
const AHe = Math.pow(Math.sqrt(BHe) + Math.sqrt(BHe + 1), 1
|
|
2948
|
+
const AHe = Math.pow(Math.sqrt(BHe) + Math.sqrt(BHe + 1), 1 / 3);
|
|
2942
2949
|
F = AHe + 1 / AHe;
|
|
2943
2950
|
}
|
|
2944
|
-
// ----- Line 73: Compute y for high-energy solution (Loechler p.37 (5.
|
|
2945
|
-
const yHe = (2
|
|
2951
|
+
// ----- Line 73: Compute y for high-energy solution (Loechler p.37 (5.1)) -----
|
|
2952
|
+
const yHe = (2 / 3) * Math.sqrt(x) * (1 + h1He) * (Math.sqrt(BHe+1) / F + 1);
|
|
2946
2953
|
// ----- Line 74: Update xnew for high-energy solution (Loechler p.34 (5.3)) -----
|
|
2947
2954
|
const temp = (mVal / (yHe * yHe)) - (1 + L);
|
|
2948
2955
|
const xnewHe = 0.5 * (temp - Math.sqrt(temp * temp - 4 * L));
|
|
@@ -2950,10 +2957,10 @@ const lambertThomsonAlgorithm
|
|
|
2950
2957
|
x = xnewHe;
|
|
2951
2958
|
}
|
|
2952
2959
|
// ----- Line 78: Compute semi-major axis a -----
|
|
2953
|
-
const eVal = s * Math.pow(1 + lambda, 2
|
|
2960
|
+
const eVal = s * Math.pow(1 + lambda, 2) * (1 + x) * (L + x) / (8 * x);
|
|
2954
2961
|
// ----- Line 79: Compute p for high-energy solution -----
|
|
2955
|
-
const pHe = 2 * r1Mag * r2Mag * Math.pow(Math.sin(theta / 2), 2
|
|
2956
|
-
/ (s * Math.pow(1 + lambda, 2
|
|
2962
|
+
const pHe = 2 * r1Mag * r2Mag * Math.pow(Math.sin(theta / 2), 2) * (1 + x)
|
|
2963
|
+
/ (s * Math.pow(1 + lambda, 2) * (L + x));
|
|
2957
2964
|
// ----- Line 80: Compute eccentricity for high-energy solution -----
|
|
2958
2965
|
const eHe = Math.sqrt(1 - pHe / eVal);
|
|
2959
2966
|
// ----- Line 81: Compute high-energy velocities -----
|
|
@@ -2979,19 +2986,19 @@ const lambertThomsonAlgorithm
|
|
|
2979
2986
|
*/
|
|
2980
2987
|
const hodographVelocityAlgorithm = (r1, r2, t, v1Minus, theta, p, e, mu) => {
|
|
2981
2988
|
// Line 2: Define L180 (in meters)
|
|
2982
|
-
const L180 = 1
|
|
2989
|
+
const L180 = 1;
|
|
2983
2990
|
|
|
2984
2991
|
const r1Mag = norm(r1);
|
|
2985
2992
|
const r2Mag = norm(r2);
|
|
2986
2993
|
|
|
2987
2994
|
// Line 3: A = mu*(1/r1 - 1/p)
|
|
2988
|
-
const eVal = mu * (1
|
|
2995
|
+
const eVal = mu * (1 / r1Mag - 1 / p);
|
|
2989
2996
|
|
|
2990
2997
|
// Line 4: B = (mu*e/p)^2 - A^2
|
|
2991
|
-
const BVal = Math.pow(mu * e / p, 2
|
|
2998
|
+
const BVal = Math.pow(mu * e / p, 2) - (eVal * eVal);
|
|
2992
2999
|
|
|
2993
3000
|
// Line 5: if B <= 0 then x1 = 0 else x1 = -sqrt(B)
|
|
2994
|
-
let x1 = (BVal <= 0) ? 0
|
|
3001
|
+
let x1 = (BVal <= 0) ? 0 : -Math.sqrt(BVal);
|
|
2995
3002
|
|
|
2996
3003
|
let nHat; // unit normal vector
|
|
2997
3004
|
|
|
@@ -3003,9 +3010,9 @@ const hodographVelocityAlgorithm = (r1, r2, t, v1Minus, theta, p, e, mu) => {
|
|
|
3003
3010
|
nHat = [nHat.x, nHat.y, nHat.z];
|
|
3004
3011
|
|
|
3005
3012
|
// Line 8: If the orbit is elliptical (e < 1)
|
|
3006
|
-
if (e < 1
|
|
3013
|
+
if (e < 1) {
|
|
3007
3014
|
// Line 9: P = 2π * sqrt( p^3 / [ mu * (1-e^2)^3 ] )
|
|
3008
|
-
const P = 2 * Math.PI * Math.sqrt(Math.pow(p, 3
|
|
3015
|
+
const P = 2 * Math.PI * Math.sqrt(Math.pow(p, 3) / (mu * Math.pow(1 - e * e, 3)));
|
|
3009
3016
|
// Line 10: If (t mod P) > (P/2) then reverse x1
|
|
3010
3017
|
const tMod = t % P;
|
|
3011
3018
|
if (tMod > P / 2) {
|
|
@@ -3182,7 +3189,7 @@ const isSatInShadow = (epoch, satPos) => {
|
|
|
3182
3189
|
const century = _century(epoch); // Convert date to Julian centuries since J2000 || equal to var tut1 in GetPosition()
|
|
3183
3190
|
|
|
3184
3191
|
// Variables to solve for Sun Pos in ___ frame
|
|
3185
|
-
const lamM = (280.
|
|
3192
|
+
const lamM = (280.46 + 36000.771*century % 360) * DEG2RAD;
|
|
3186
3193
|
const M = (357.5291092 + 35999.05034*century % 360) * DEG2RAD;
|
|
3187
3194
|
const lamEc = (((lamM * RAD2DEG)
|
|
3188
3195
|
+ 1.914666471 * Math.sin(M)
|
|
@@ -3229,14 +3236,14 @@ const isSatInShadow = (epoch, satPos) => {
|
|
|
3229
3236
|
};
|
|
3230
3237
|
|
|
3231
3238
|
// Convert Sun Pos from GCRF to J2000
|
|
3232
|
-
const xi0 = -0.
|
|
3239
|
+
const xi0 = -0.016617 * arcsec2rad;
|
|
3233
3240
|
const eta0 = -0.0068192 * arcsec2rad;
|
|
3234
|
-
const da0 = -0.
|
|
3241
|
+
const da0 = -0.0146 * arcsec2rad;
|
|
3235
3242
|
|
|
3236
3243
|
const gcrf2j2000 = [
|
|
3237
|
-
[1
|
|
3238
|
-
[-da0, 1
|
|
3239
|
-
[xi0, eta0, 1
|
|
3244
|
+
[1 - 0.5*(-da0*-da0 + xi0*xi0), da0, -xi0],
|
|
3245
|
+
[-da0, 1 - 0.5*(-da0*-da0 + eta0*eta0), -eta0],
|
|
3246
|
+
[xi0, eta0, 1 - 0.5*(eta0*eta0 + xi0*xi0)],
|
|
3240
3247
|
];
|
|
3241
3248
|
|
|
3242
3249
|
const sunVecJ2000 = multiply(gcrf2j2000, posToArray(sunVecGCRF)); // Sun Position in J2000
|
|
@@ -3277,15 +3284,26 @@ const isSatInShadow = (epoch, satPos) => {
|
|
|
3277
3284
|
return shadowType; // return the shadow type
|
|
3278
3285
|
};
|
|
3279
3286
|
|
|
3280
|
-
|
|
3281
|
-
export {
|
|
3282
|
-
export {
|
|
3283
|
-
|
|
3287
|
+
export * as CONSTANTS from "./constants.js";
|
|
3288
|
+
export {REGIMES} from "./constants.js";
|
|
3289
|
+
export {julianToGregorian, multiplyVector, dist} from "./utils.js";
|
|
3290
|
+
export {
|
|
3291
|
+
twoline2satrec,
|
|
3292
|
+
sgp4,
|
|
3293
|
+
gstime,
|
|
3294
|
+
eciToGeodetic,
|
|
3295
|
+
eciToEcf,
|
|
3296
|
+
ecfToLookAngles,
|
|
3297
|
+
degreesToRadians,
|
|
3298
|
+
radiansToDegrees,
|
|
3299
|
+
degreesLong,
|
|
3300
|
+
degreesLat,
|
|
3301
|
+
propagate,
|
|
3302
|
+
} from "satellite.js";
|
|
3303
|
+
export {
|
|
3284
3304
|
calcRegime,
|
|
3285
3305
|
altToRegime,
|
|
3286
3306
|
cartesianToRIC,
|
|
3287
|
-
multiplyVector,
|
|
3288
|
-
dist,
|
|
3289
3307
|
angleBetween3DCoords,
|
|
3290
3308
|
prop,
|
|
3291
3309
|
propGeodetic,
|
|
@@ -3298,7 +3316,6 @@ export {REGIMES,
|
|
|
3298
3316
|
distGeodetic,
|
|
3299
3317
|
getSemiMajorAxis,
|
|
3300
3318
|
angleBetweenPlanes,
|
|
3301
|
-
propagate,
|
|
3302
3319
|
planeChangeDeltaV,
|
|
3303
3320
|
planeChangePureInclinationDeltaV,
|
|
3304
3321
|
cartesianToKeplerian,
|
|
@@ -3330,21 +3347,10 @@ export {REGIMES,
|
|
|
3330
3347
|
isSatInShadow,
|
|
3331
3348
|
calculateGeoCrossingTimes,
|
|
3332
3349
|
tryPropagateSatrec,
|
|
3333
|
-
twoline2satrec,
|
|
3334
|
-
sgp4,
|
|
3335
|
-
gstime,
|
|
3336
|
-
eciToGeodetic,
|
|
3337
|
-
eciToEcf,
|
|
3338
|
-
ecfToLookAngles,
|
|
3339
|
-
degreesToRadians,
|
|
3340
|
-
radiansToDegrees,
|
|
3341
|
-
degreesLong,
|
|
3342
|
-
degreesLat,
|
|
3343
3350
|
};
|
|
3344
3351
|
export const raDecToGeodetic = RaDecToGeodetic;
|
|
3345
3352
|
export const getResiduals = GetResiduals;
|
|
3346
3353
|
export const raDecToAzEl = RaDecToAzEl;
|
|
3347
3354
|
export const azElToRaDec = AzElToRaDec;
|
|
3348
3355
|
export const getElsetUdlFromTle = GetElsetUdlFromTle;
|
|
3349
|
-
|
|
3350
|
-
export {satjs};
|
|
3356
|
+
export * as satjs from "satellite.js";
|