@saber-usa/node-common 1.7.7 → 1.7.9-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -42
- package/package.json +51 -52
- package/src/FrameConverter.js +1120 -1121
- package/src/LLA.js +179 -179
- package/src/LaunchNominalClass.js +772 -753
- package/src/NodeVector3D.js +71 -71
- package/src/OrbitUtils.js +490 -309
- package/src/PropagateUtils.js +100 -100
- package/src/ShadowGEOCalculator.js +203 -203
- package/src/TimeConverter.js +309 -309
- package/src/astro.js +3315 -3301
- package/src/ballisticPropagator.js +1037 -1037
- package/src/constants.js +35 -37
- package/src/fixDate.js +62 -62
- package/src/index.js +47 -47
- package/src/launchNominal.js +208 -208
- package/src/s3.js +59 -59
- package/src/transform.js +35 -35
- package/src/udl.js +231 -116
- package/src/utils.js +406 -406
package/src/launchNominal.js
CHANGED
|
@@ -1,208 +1,208 @@
|
|
|
1
|
-
import {twoline2satrec,
|
|
2
|
-
propagate,
|
|
3
|
-
gstime,
|
|
4
|
-
eciToGeodetic,
|
|
5
|
-
degreesToRadians,
|
|
6
|
-
radiansToDegrees,
|
|
7
|
-
eciToEcf,
|
|
8
|
-
ecfToLookAngles,
|
|
9
|
-
degreesLong,
|
|
10
|
-
degreesLat} from "satellite.js";
|
|
11
|
-
import {distGeodetic} from "./astro.js";
|
|
12
|
-
import {wrapToRange} from "./utils.js";
|
|
13
|
-
|
|
14
|
-
const notamToCoordArray = (notam) => {
|
|
15
|
-
const regexPatterns = [
|
|
16
|
-
/(\d{4,6}[NS]|[N|S]\d{6})(\d{4,7}[EW]|[E|W]\d{7})/g,
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
// clean up the NOTAM. Remove all spaces and unwanted characters and convert to upper case
|
|
20
|
-
notam = notam
|
|
21
|
-
.replace(/\s/g, "")
|
|
22
|
-
.replace(/-/g, "")
|
|
23
|
-
.replace(/\./g, "")
|
|
24
|
-
.toUpperCase();
|
|
25
|
-
|
|
26
|
-
const coordArray = [];
|
|
27
|
-
let matchFound = false;
|
|
28
|
-
let match;
|
|
29
|
-
for (const regex of regexPatterns) {
|
|
30
|
-
while ((match = regex.exec(notam)) !== null) {
|
|
31
|
-
matchFound = true;
|
|
32
|
-
// Depending on the pattern, you might need to adjust the indexing in convertToDecimal calls
|
|
33
|
-
const lat = match[1];
|
|
34
|
-
const lon = match[2];
|
|
35
|
-
const decimalLat = convertToDecimal(lat);
|
|
36
|
-
const decimalLon = convertToDecimal(lon);
|
|
37
|
-
coordArray.push([decimalLat, decimalLon]);
|
|
38
|
-
}
|
|
39
|
-
if (matchFound) break; // Stop iterating through patterns if a match is found
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return coordArray;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Helper function to convert coordinate string to decimal
|
|
46
|
-
const convertToDecimal = (coord) => {
|
|
47
|
-
// Determine if the direction is at the beginning or the end
|
|
48
|
-
const direction = coord.charAt(0).match(/[NSWE]/) ? coord.charAt(0) : coord.charAt(coord.length - 1);
|
|
49
|
-
let degrees;
|
|
50
|
-
let minutes;
|
|
51
|
-
let seconds = 0; // Default to 0 for formats without seconds
|
|
52
|
-
|
|
53
|
-
if (direction === coord.charAt(0)) { // Direction at the beginning
|
|
54
|
-
coord = coord.substr(1); // Remove direction from coord for processing
|
|
55
|
-
} else { // Direction at the end
|
|
56
|
-
coord = coord.substr(0, coord.length - 1); // Remove direction from coord for processing
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Now, coord contains only the numeric part
|
|
60
|
-
if (coord.length === 4) { // Format DDMM
|
|
61
|
-
degrees = parseInt(coord.substr(0, 2), 10);
|
|
62
|
-
minutes = parseInt(coord.substr(2, 2), 10);
|
|
63
|
-
} else if (coord.length === 5) { // Format DDDMM
|
|
64
|
-
degrees = parseInt(coord.substr(0, 3), 10);
|
|
65
|
-
minutes = parseInt(coord.substr(3, 2), 10);
|
|
66
|
-
} else if (coord.length === 6) { // Format DDMMSS
|
|
67
|
-
degrees = parseInt(coord.substr(0, 2), 10);
|
|
68
|
-
minutes = parseInt(coord.substr(2, 2), 10);
|
|
69
|
-
seconds = parseInt(coord.substr(4, 2), 10);
|
|
70
|
-
} else if (coord.length === 7) { // Format DDDMMSS
|
|
71
|
-
degrees = parseInt(coord.substr(0, 3), 10);
|
|
72
|
-
minutes = parseInt(coord.substr(3, 2), 10);
|
|
73
|
-
seconds = parseInt(coord.substr(5, 2), 10);
|
|
74
|
-
} else {
|
|
75
|
-
return 0; // Invalid format
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Convert to decimal format
|
|
79
|
-
let decimal = degrees + (minutes / 60) + (seconds / 3600);
|
|
80
|
-
decimal = (direction === "S" || direction === "W") ? -decimal : decimal;
|
|
81
|
-
return decimal;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/** Gets the bearing of an orbiting object relative to it's launch pad.
|
|
85
|
-
* @param {String} line1 The first line of the TLE
|
|
86
|
-
* @param {String} line2 The second line of the TLE
|
|
87
|
-
* @param {Date} otop The OTOP time (object time over pad) in UTC
|
|
88
|
-
* @param {Object} opop The OPOP object with the following properties:
|
|
89
|
-
* - latitude: The geocentric latitude of the object in degrees
|
|
90
|
-
* - longitude: The geocentric longitude of the object in degrees
|
|
91
|
-
* @return {Number} The bearing of the object in degrees
|
|
92
|
-
*/
|
|
93
|
-
const getBearingFromTle = (line1, line2, otop, opop) => {
|
|
94
|
-
const satrec = twoline2satrec(line1, line2);
|
|
95
|
-
const propTime = new Date(otop.getTime() + 10000);
|
|
96
|
-
const propPos = eciToGeodetic(
|
|
97
|
-
propagate(satrec, propTime).position,
|
|
98
|
-
gstime(propTime),
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
const ascending = degreesToRadians(opop.latitude) < propPos.latitude;
|
|
102
|
-
|
|
103
|
-
return getBearing(
|
|
104
|
-
radiansToDegrees(satrec.inclo),
|
|
105
|
-
opop.latitude,
|
|
106
|
-
ascending,
|
|
107
|
-
);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Calculates the bearing angle given inclination, latitude, and whether the orbit is ascending.
|
|
112
|
-
* @param {number} i - Inclination angle in degrees.
|
|
113
|
-
* @param {number} lat - Latitude angle in degrees.
|
|
114
|
-
* @param {boolean} ascending - Whether the orbit is ascending. Defaults to true.
|
|
115
|
-
* @return {number} The azimuth angle in degrees, wrapped to [0, 360).
|
|
116
|
-
*/
|
|
117
|
-
const getBearing = (i, lat, ascending = true) => {
|
|
118
|
-
i = degreesToRadians(i);
|
|
119
|
-
lat = degreesToRadians(lat);
|
|
120
|
-
const azimuthRadians = Math.asin(Math.cos(i) / Math.cos(lat));
|
|
121
|
-
let azimuthDegrees = radiansToDegrees(azimuthRadians);
|
|
122
|
-
|
|
123
|
-
if (!ascending) {
|
|
124
|
-
azimuthDegrees = 180 - azimuthDegrees;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return wrapToRange(azimuthDegrees, 0, 360);
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
/** Gets the OPOP (Object Position Over Pad) and OTOP (Object Time Over Pad) of an orbiting object relative to it's launch pad.
|
|
131
|
-
* @param {String} line1 The first line of the TLE
|
|
132
|
-
* @param {String} line2 The second line of the TLE
|
|
133
|
-
* @param {Number} padLat The latitude of the launch pad in degrees
|
|
134
|
-
* @param {Number} padLon The longitude of the launch pad in degrees
|
|
135
|
-
* @param {Number} padAlt The altitude of the launch pad in km
|
|
136
|
-
* @param {Date} launchTime The launch time of the object
|
|
137
|
-
* @param {Number} flyoutTimeThresholdMin The flyout time threshold in minutes, defaults to 20 min
|
|
138
|
-
* @param {Number} opopDistThresholdKm The OPOP distance threshold in km, defaults to 250 km
|
|
139
|
-
* @param {Number} maxIterations The maximum number of iterations, defaults to 100
|
|
140
|
-
* @return {Object} The OPOP object with the following properties:
|
|
141
|
-
* - opopLat: The geocentric latitude of the object in degrees
|
|
142
|
-
* - opopLon: The geocentric longitude of the object in degrees
|
|
143
|
-
* - otop: The time of the object over the pad in UTC
|
|
144
|
-
*/
|
|
145
|
-
const getOpopOtop = (
|
|
146
|
-
line1,
|
|
147
|
-
line2,
|
|
148
|
-
padLat,
|
|
149
|
-
padLon,
|
|
150
|
-
padAlt,
|
|
151
|
-
launchTime,
|
|
152
|
-
flyoutTimeThresholdMin = 20,
|
|
153
|
-
opopDistThresholdKm = 250,
|
|
154
|
-
maxIterations = 100,
|
|
155
|
-
) => {
|
|
156
|
-
const satrec = twoline2satrec(line1, line2);
|
|
157
|
-
|
|
158
|
-
// iterate time to find the time with closest range
|
|
159
|
-
let time = launchTime;
|
|
160
|
-
let stepSize = 60000; // 1 minute
|
|
161
|
-
let lastRange;
|
|
162
|
-
const timeTreshold = 500; // half second
|
|
163
|
-
for (let i = 0; i < maxIterations; i++) {
|
|
164
|
-
const sv = propagate(satrec, time);
|
|
165
|
-
const gmst = gstime(time);
|
|
166
|
-
const satEcf = eciToEcf(sv.position, gmst);
|
|
167
|
-
const padGd = {
|
|
168
|
-
longitude: degreesToRadians(padLon),
|
|
169
|
-
latitude: degreesToRadians(padLat),
|
|
170
|
-
height: padAlt,
|
|
171
|
-
};
|
|
172
|
-
const range = ecfToLookAngles(padGd, satEcf).rangeSat;
|
|
173
|
-
if (lastRange && lastRange < range) {
|
|
174
|
-
// we went too far, half the step size and invert the sign
|
|
175
|
-
stepSize = (-1 * stepSize) / 2;
|
|
176
|
-
}
|
|
177
|
-
time = new Date(time.getTime() + stepSize);
|
|
178
|
-
lastRange = range;
|
|
179
|
-
|
|
180
|
-
if (Math.abs(stepSize) < timeTreshold) {
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (Math.abs(stepSize) >= timeTreshold) {
|
|
186
|
-
throw new Error(`Failed to converge on OPOP after ${maxIterations} iterations`);
|
|
187
|
-
}
|
|
188
|
-
if (Math.abs(time.getTime() - launchTime.getTime()) > flyoutTimeThresholdMin * 60000) {
|
|
189
|
-
throw new Error(`OTOP is more than ${flyoutTimeThresholdMin} minute(s) from launch`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const sv = propagate(satrec, time);
|
|
193
|
-
const opop = eciToGeodetic(sv.position, gstime(time));
|
|
194
|
-
const opopLat = degreesLat(opop.latitude);
|
|
195
|
-
const opopLon = degreesLong(opop.longitude);
|
|
196
|
-
|
|
197
|
-
if (distGeodetic(opopLat, opopLon, padLat, padLon) > opopDistThresholdKm) {
|
|
198
|
-
throw new Error(`OPOP is more than ${opopDistThresholdKm}km from launch pad`);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
opopLat: opopLat,
|
|
203
|
-
opopLon: opopLon,
|
|
204
|
-
otop: time,
|
|
205
|
-
};
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
export {notamToCoordArray, getBearingFromTle, getBearing, getOpopOtop};
|
|
1
|
+
import {twoline2satrec,
|
|
2
|
+
propagate,
|
|
3
|
+
gstime,
|
|
4
|
+
eciToGeodetic,
|
|
5
|
+
degreesToRadians,
|
|
6
|
+
radiansToDegrees,
|
|
7
|
+
eciToEcf,
|
|
8
|
+
ecfToLookAngles,
|
|
9
|
+
degreesLong,
|
|
10
|
+
degreesLat} from "satellite.js";
|
|
11
|
+
import {distGeodetic} from "./astro.js";
|
|
12
|
+
import {wrapToRange} from "./utils.js";
|
|
13
|
+
|
|
14
|
+
const notamToCoordArray = (notam) => {
|
|
15
|
+
const regexPatterns = [
|
|
16
|
+
/(\d{4,6}[NS]|[N|S]\d{6})(\d{4,7}[EW]|[E|W]\d{7})/g,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// clean up the NOTAM. Remove all spaces and unwanted characters and convert to upper case
|
|
20
|
+
notam = notam
|
|
21
|
+
.replace(/\s/g, "")
|
|
22
|
+
.replace(/-/g, "")
|
|
23
|
+
.replace(/\./g, "")
|
|
24
|
+
.toUpperCase();
|
|
25
|
+
|
|
26
|
+
const coordArray = [];
|
|
27
|
+
let matchFound = false;
|
|
28
|
+
let match;
|
|
29
|
+
for (const regex of regexPatterns) {
|
|
30
|
+
while ((match = regex.exec(notam)) !== null) {
|
|
31
|
+
matchFound = true;
|
|
32
|
+
// Depending on the pattern, you might need to adjust the indexing in convertToDecimal calls
|
|
33
|
+
const lat = match[1];
|
|
34
|
+
const lon = match[2];
|
|
35
|
+
const decimalLat = convertToDecimal(lat);
|
|
36
|
+
const decimalLon = convertToDecimal(lon);
|
|
37
|
+
coordArray.push([decimalLat, decimalLon]);
|
|
38
|
+
}
|
|
39
|
+
if (matchFound) break; // Stop iterating through patterns if a match is found
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return coordArray;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Helper function to convert coordinate string to decimal
|
|
46
|
+
const convertToDecimal = (coord) => {
|
|
47
|
+
// Determine if the direction is at the beginning or the end
|
|
48
|
+
const direction = coord.charAt(0).match(/[NSWE]/) ? coord.charAt(0) : coord.charAt(coord.length - 1);
|
|
49
|
+
let degrees;
|
|
50
|
+
let minutes;
|
|
51
|
+
let seconds = 0; // Default to 0 for formats without seconds
|
|
52
|
+
|
|
53
|
+
if (direction === coord.charAt(0)) { // Direction at the beginning
|
|
54
|
+
coord = coord.substr(1); // Remove direction from coord for processing
|
|
55
|
+
} else { // Direction at the end
|
|
56
|
+
coord = coord.substr(0, coord.length - 1); // Remove direction from coord for processing
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Now, coord contains only the numeric part
|
|
60
|
+
if (coord.length === 4) { // Format DDMM
|
|
61
|
+
degrees = parseInt(coord.substr(0, 2), 10);
|
|
62
|
+
minutes = parseInt(coord.substr(2, 2), 10);
|
|
63
|
+
} else if (coord.length === 5) { // Format DDDMM
|
|
64
|
+
degrees = parseInt(coord.substr(0, 3), 10);
|
|
65
|
+
minutes = parseInt(coord.substr(3, 2), 10);
|
|
66
|
+
} else if (coord.length === 6) { // Format DDMMSS
|
|
67
|
+
degrees = parseInt(coord.substr(0, 2), 10);
|
|
68
|
+
minutes = parseInt(coord.substr(2, 2), 10);
|
|
69
|
+
seconds = parseInt(coord.substr(4, 2), 10);
|
|
70
|
+
} else if (coord.length === 7) { // Format DDDMMSS
|
|
71
|
+
degrees = parseInt(coord.substr(0, 3), 10);
|
|
72
|
+
minutes = parseInt(coord.substr(3, 2), 10);
|
|
73
|
+
seconds = parseInt(coord.substr(5, 2), 10);
|
|
74
|
+
} else {
|
|
75
|
+
return 0; // Invalid format
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Convert to decimal format
|
|
79
|
+
let decimal = degrees + (minutes / 60) + (seconds / 3600);
|
|
80
|
+
decimal = (direction === "S" || direction === "W") ? -decimal : decimal;
|
|
81
|
+
return decimal;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/** Gets the bearing of an orbiting object relative to it's launch pad.
|
|
85
|
+
* @param {String} line1 The first line of the TLE
|
|
86
|
+
* @param {String} line2 The second line of the TLE
|
|
87
|
+
* @param {Date} otop The OTOP time (object time over pad) in UTC
|
|
88
|
+
* @param {Object} opop The OPOP object with the following properties:
|
|
89
|
+
* - latitude: The geocentric latitude of the object in degrees
|
|
90
|
+
* - longitude: The geocentric longitude of the object in degrees
|
|
91
|
+
* @return {Number} The bearing of the object in degrees
|
|
92
|
+
*/
|
|
93
|
+
const getBearingFromTle = (line1, line2, otop, opop) => {
|
|
94
|
+
const satrec = twoline2satrec(line1, line2);
|
|
95
|
+
const propTime = new Date(otop.getTime() + 10000);
|
|
96
|
+
const propPos = eciToGeodetic(
|
|
97
|
+
propagate(satrec, propTime).position,
|
|
98
|
+
gstime(propTime),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const ascending = degreesToRadians(opop.latitude) < propPos.latitude;
|
|
102
|
+
|
|
103
|
+
return getBearing(
|
|
104
|
+
radiansToDegrees(satrec.inclo),
|
|
105
|
+
opop.latitude,
|
|
106
|
+
ascending,
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Calculates the bearing angle given inclination, latitude, and whether the orbit is ascending.
|
|
112
|
+
* @param {number} i - Inclination angle in degrees.
|
|
113
|
+
* @param {number} lat - Latitude angle in degrees.
|
|
114
|
+
* @param {boolean} ascending - Whether the orbit is ascending. Defaults to true.
|
|
115
|
+
* @return {number} The azimuth angle in degrees, wrapped to [0, 360).
|
|
116
|
+
*/
|
|
117
|
+
const getBearing = (i, lat, ascending = true) => {
|
|
118
|
+
i = degreesToRadians(i);
|
|
119
|
+
lat = degreesToRadians(lat);
|
|
120
|
+
const azimuthRadians = Math.asin(Math.cos(i) / Math.cos(lat));
|
|
121
|
+
let azimuthDegrees = radiansToDegrees(azimuthRadians);
|
|
122
|
+
|
|
123
|
+
if (!ascending) {
|
|
124
|
+
azimuthDegrees = 180 - azimuthDegrees;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return wrapToRange(azimuthDegrees, 0, 360);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/** Gets the OPOP (Object Position Over Pad) and OTOP (Object Time Over Pad) of an orbiting object relative to it's launch pad.
|
|
131
|
+
* @param {String} line1 The first line of the TLE
|
|
132
|
+
* @param {String} line2 The second line of the TLE
|
|
133
|
+
* @param {Number} padLat The latitude of the launch pad in degrees
|
|
134
|
+
* @param {Number} padLon The longitude of the launch pad in degrees
|
|
135
|
+
* @param {Number} padAlt The altitude of the launch pad in km
|
|
136
|
+
* @param {Date} launchTime The launch time of the object
|
|
137
|
+
* @param {Number} flyoutTimeThresholdMin The flyout time threshold in minutes, defaults to 20 min
|
|
138
|
+
* @param {Number} opopDistThresholdKm The OPOP distance threshold in km, defaults to 250 km
|
|
139
|
+
* @param {Number} maxIterations The maximum number of iterations, defaults to 100
|
|
140
|
+
* @return {Object} The OPOP object with the following properties:
|
|
141
|
+
* - opopLat: The geocentric latitude of the object in degrees
|
|
142
|
+
* - opopLon: The geocentric longitude of the object in degrees
|
|
143
|
+
* - otop: The time of the object over the pad in UTC
|
|
144
|
+
*/
|
|
145
|
+
const getOpopOtop = (
|
|
146
|
+
line1,
|
|
147
|
+
line2,
|
|
148
|
+
padLat,
|
|
149
|
+
padLon,
|
|
150
|
+
padAlt,
|
|
151
|
+
launchTime,
|
|
152
|
+
flyoutTimeThresholdMin = 20,
|
|
153
|
+
opopDistThresholdKm = 250,
|
|
154
|
+
maxIterations = 100,
|
|
155
|
+
) => {
|
|
156
|
+
const satrec = twoline2satrec(line1, line2);
|
|
157
|
+
|
|
158
|
+
// iterate time to find the time with closest range
|
|
159
|
+
let time = launchTime;
|
|
160
|
+
let stepSize = 60000; // 1 minute
|
|
161
|
+
let lastRange;
|
|
162
|
+
const timeTreshold = 500; // half second
|
|
163
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
164
|
+
const sv = propagate(satrec, time);
|
|
165
|
+
const gmst = gstime(time);
|
|
166
|
+
const satEcf = eciToEcf(sv.position, gmst);
|
|
167
|
+
const padGd = {
|
|
168
|
+
longitude: degreesToRadians(padLon),
|
|
169
|
+
latitude: degreesToRadians(padLat),
|
|
170
|
+
height: padAlt,
|
|
171
|
+
};
|
|
172
|
+
const range = ecfToLookAngles(padGd, satEcf).rangeSat;
|
|
173
|
+
if (lastRange && lastRange < range) {
|
|
174
|
+
// we went too far, half the step size and invert the sign
|
|
175
|
+
stepSize = (-1 * stepSize) / 2;
|
|
176
|
+
}
|
|
177
|
+
time = new Date(time.getTime() + stepSize);
|
|
178
|
+
lastRange = range;
|
|
179
|
+
|
|
180
|
+
if (Math.abs(stepSize) < timeTreshold) {
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (Math.abs(stepSize) >= timeTreshold) {
|
|
186
|
+
throw new Error(`Failed to converge on OPOP after ${maxIterations} iterations`);
|
|
187
|
+
}
|
|
188
|
+
if (Math.abs(time.getTime() - launchTime.getTime()) > flyoutTimeThresholdMin * 60000) {
|
|
189
|
+
throw new Error(`OTOP is more than ${flyoutTimeThresholdMin} minute(s) from launch`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const sv = propagate(satrec, time);
|
|
193
|
+
const opop = eciToGeodetic(sv.position, gstime(time));
|
|
194
|
+
const opopLat = degreesLat(opop.latitude);
|
|
195
|
+
const opopLon = degreesLong(opop.longitude);
|
|
196
|
+
|
|
197
|
+
if (distGeodetic(opopLat, opopLon, padLat, padLon) > opopDistThresholdKm * 1000) {
|
|
198
|
+
throw new Error(`OPOP is more than ${opopDistThresholdKm}km from launch pad`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
opopLat: opopLat,
|
|
203
|
+
opopLon: opopLon,
|
|
204
|
+
otop: time,
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export {notamToCoordArray, getBearingFromTle, getBearing, getOpopOtop};
|
package/src/s3.js
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
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
|
-
|
|
7
|
-
const s3Client = new S3Client({
|
|
8
|
-
region: "us-west-1", // Replace with your AWS region
|
|
9
|
-
credentials: {
|
|
10
|
-
accessKeyId: process.env.S3_ACCESS_KEY,
|
|
11
|
-
secretAccessKey: process.env.S3_SECRET,
|
|
12
|
-
},
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Takes either a filename, or a buffer. If array buffer is supplied, then filename is just used to name the file.
|
|
18
|
-
* If no array buffer is supplied, it will attempt to read the file at the fileName path.
|
|
19
|
-
* Example Usage:
|
|
20
|
-
* await s3Upload("imageName.png", "image/png", imageBuffer);
|
|
21
|
-
* await s3Upload("/path/to/local/image.png");
|
|
22
|
-
* @param {string} fileName
|
|
23
|
-
* @param {string} type
|
|
24
|
-
* @param {Array|Buffer|null} buffer
|
|
25
|
-
* @return {Promise<Object>}
|
|
26
|
-
*/
|
|
27
|
-
export async function s3Upload(fileName, type = "image/png", buffer = null) {
|
|
28
|
-
try {
|
|
29
|
-
const fileContent = buffer ? buffer : readFileSync(fileName);
|
|
30
|
-
return await s3Client.send(new PutObjectCommand({
|
|
31
|
-
Bucket: "saber-probe-images", // S3 Bucket name
|
|
32
|
-
Key: buffer ? fileName : basename(fileName), // File name to save as in S3
|
|
33
|
-
Body: fileContent, // File content (either buffer or file read)
|
|
34
|
-
ContentType: type, // File content type
|
|
35
|
-
}));
|
|
36
|
-
} catch (err) {
|
|
37
|
-
console.error("Error uploading file or buffer to S3: ", err);
|
|
38
|
-
throw err; // Re-throw the error so it can be caught by the calling function if needed
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function doesS3ObjectExist(fileName) {
|
|
43
|
-
try {
|
|
44
|
-
// Try to fetch the metadata of the object to check if it exists
|
|
45
|
-
await s3Client.send(new HeadObjectCommand({
|
|
46
|
-
Bucket: "saber-probe-images",
|
|
47
|
-
Key: fileName, // The key (file name) to check in the S3 bucket
|
|
48
|
-
}));
|
|
49
|
-
return true;
|
|
50
|
-
} catch (err) {
|
|
51
|
-
// If a 404 error is thrown, the object does not exist
|
|
52
|
-
if (err.name === "NotFound") {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
// Other errors (such as permission errors) should be thrown
|
|
56
|
-
console.error("Error checking if file exists in S3: ", err);
|
|
57
|
-
throw err;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
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
|
+
|
|
7
|
+
const s3Client = new S3Client({
|
|
8
|
+
region: "us-west-1", // Replace with your AWS region
|
|
9
|
+
credentials: {
|
|
10
|
+
accessKeyId: process.env.S3_ACCESS_KEY,
|
|
11
|
+
secretAccessKey: process.env.S3_SECRET,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Takes either a filename, or a buffer. If array buffer is supplied, then filename is just used to name the file.
|
|
18
|
+
* If no array buffer is supplied, it will attempt to read the file at the fileName path.
|
|
19
|
+
* Example Usage:
|
|
20
|
+
* await s3Upload("imageName.png", "image/png", imageBuffer);
|
|
21
|
+
* await s3Upload("/path/to/local/image.png");
|
|
22
|
+
* @param {string} fileName
|
|
23
|
+
* @param {string} type
|
|
24
|
+
* @param {Array|Buffer|null} buffer
|
|
25
|
+
* @return {Promise<Object>}
|
|
26
|
+
*/
|
|
27
|
+
export async function s3Upload(fileName, type = "image/png", buffer = null) {
|
|
28
|
+
try {
|
|
29
|
+
const fileContent = buffer ? buffer : readFileSync(fileName);
|
|
30
|
+
return await s3Client.send(new PutObjectCommand({
|
|
31
|
+
Bucket: "saber-probe-images", // S3 Bucket name
|
|
32
|
+
Key: buffer ? fileName : basename(fileName), // File name to save as in S3
|
|
33
|
+
Body: fileContent, // File content (either buffer or file read)
|
|
34
|
+
ContentType: type, // File content type
|
|
35
|
+
}));
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error("Error uploading file or buffer to S3: ", err);
|
|
38
|
+
throw err; // Re-throw the error so it can be caught by the calling function if needed
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function doesS3ObjectExist(fileName) {
|
|
43
|
+
try {
|
|
44
|
+
// Try to fetch the metadata of the object to check if it exists
|
|
45
|
+
await s3Client.send(new HeadObjectCommand({
|
|
46
|
+
Bucket: "saber-probe-images",
|
|
47
|
+
Key: fileName, // The key (file name) to check in the S3 bucket
|
|
48
|
+
}));
|
|
49
|
+
return true;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
// If a 404 error is thrown, the object does not exist
|
|
52
|
+
if (err.name === "NotFound") {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
// Other errors (such as permission errors) should be thrown
|
|
56
|
+
console.error("Error checking if file exists in S3: ", err);
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/transform.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import _ from "lodash";
|
|
2
|
-
|
|
3
|
-
const transformObjectKeys = (transform, object, deep = true) => _.cond([
|
|
4
|
-
[
|
|
5
|
-
(transformItem) => _.isPlainObject(transformItem),
|
|
6
|
-
(transformItem) => _.fromPairs(
|
|
7
|
-
_.map(
|
|
8
|
-
_.toPairs(transformItem),
|
|
9
|
-
([key, value]) => [
|
|
10
|
-
transform(key),
|
|
11
|
-
deep && _.isPlainObject(value)
|
|
12
|
-
? transformObjectKeys(transform, value)
|
|
13
|
-
: value,
|
|
14
|
-
],
|
|
15
|
-
),
|
|
16
|
-
),
|
|
17
|
-
],
|
|
18
|
-
[_.stubTrue, _.identity],
|
|
19
|
-
])(object);
|
|
20
|
-
|
|
21
|
-
const pascalCase = (string) => _.upperFirst(_.camelCase(string));
|
|
22
|
-
|
|
23
|
-
const lowerCaseObjectKeys = (transformItem, deep) => transformObjectKeys(
|
|
24
|
-
_.toLower,
|
|
25
|
-
transformItem,
|
|
26
|
-
deep,
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const pascalCaseObjectKeys = (transformItem, deep) => transformObjectKeys(
|
|
30
|
-
pascalCase,
|
|
31
|
-
transformItem,
|
|
32
|
-
deep,
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
export {transformObjectKeys, lowerCaseObjectKeys, pascalCaseObjectKeys, pascalCase};
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
|
|
3
|
+
const transformObjectKeys = (transform, object, deep = true) => _.cond([
|
|
4
|
+
[
|
|
5
|
+
(transformItem) => _.isPlainObject(transformItem),
|
|
6
|
+
(transformItem) => _.fromPairs(
|
|
7
|
+
_.map(
|
|
8
|
+
_.toPairs(transformItem),
|
|
9
|
+
([key, value]) => [
|
|
10
|
+
transform(key),
|
|
11
|
+
deep && _.isPlainObject(value)
|
|
12
|
+
? transformObjectKeys(transform, value)
|
|
13
|
+
: value,
|
|
14
|
+
],
|
|
15
|
+
),
|
|
16
|
+
),
|
|
17
|
+
],
|
|
18
|
+
[_.stubTrue, _.identity],
|
|
19
|
+
])(object);
|
|
20
|
+
|
|
21
|
+
const pascalCase = (string) => _.upperFirst(_.camelCase(string));
|
|
22
|
+
|
|
23
|
+
const lowerCaseObjectKeys = (transformItem, deep) => transformObjectKeys(
|
|
24
|
+
_.toLower,
|
|
25
|
+
transformItem,
|
|
26
|
+
deep,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const pascalCaseObjectKeys = (transformItem, deep) => transformObjectKeys(
|
|
30
|
+
pascalCase,
|
|
31
|
+
transformItem,
|
|
32
|
+
deep,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export {transformObjectKeys, lowerCaseObjectKeys, pascalCaseObjectKeys, pascalCase};
|