@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/src/utils.js CHANGED
@@ -1,406 +1,406 @@
1
- import {cross, dot, norm} from "mathjs";
2
-
3
- /**
4
- * Wrap angle to [0, 2π] range (0 to 360 degrees)
5
- * @param {number} angleRad - Angle in radians
6
- * @return {number} Wrapped angle in radians
7
- */
8
- function wrapOneRevUnsigned(angleRad) {
9
- let wrapped = angleRad % (2 * Math.PI);
10
- if (wrapped < 0) {
11
- wrapped += 2 * Math.PI;
12
- }
13
- return wrapped;
14
- }
15
-
16
- /**
17
- * Wrap angle to [0, π] range (0 to 180 degrees)
18
- * @param {number} angleRad - Angle in radians
19
- * @return {number} Wrapped angle in radians
20
- */
21
- function wrapHalfRevUnsigned(angleRad) {
22
- let wrapped = angleRad % Math.PI;
23
- if (wrapped < 0) {
24
- wrapped += Math.PI;
25
- }
26
- return wrapped;
27
- }
28
-
29
- /**
30
- * Wrap angle to [-π, π] range (-180 to 180 degrees)
31
- * @param {number} angleRad - Angle in radians
32
- * @return {number} Wrapped angle in radians
33
- */
34
- function wrapHalfRev(angleRad) {
35
- let wrapped = angleRad % (2 * Math.PI);
36
- if (wrapped > Math.PI) {
37
- wrapped -= 2 * Math.PI;
38
- } else if (wrapped < -Math.PI) {
39
- wrapped += 2 * Math.PI;
40
- }
41
- return wrapped;
42
- }
43
-
44
- /**
45
- * Wrap angle to [-π/2, π/2] range (-90 to 90 degrees)
46
- * @param {number} angleRad - Angle in radians
47
- * @return {number} Wrapped angle in radians
48
- */
49
- function wrapQuarterRev(angleRad) {
50
- let wrapped = angleRad % Math.PI;
51
- if (wrapped > Math.PI / 2) {
52
- wrapped -= Math.PI;
53
- } else if (wrapped < -Math.PI / 2) {
54
- wrapped += Math.PI;
55
- }
56
- return wrapped;
57
- }
58
-
59
- /**
60
- * Will convert a julian date to a gregorian calendar date.
61
- * @param {Double} julianDay e.g 2460153.253704
62
- * @return {Date} javascript date object e.g 2023-07-27 18:05:20
63
- */
64
- function julianToGregorian(julianDay) {
65
- const j = julianDay + 0.5;
66
- const z = Math.floor(j);
67
- const f = j - z - 0.5; // subtract 0.5 to account for the day starting at noon
68
- const dayFraction = f + 0.5; // add 0.5 to account for the day starting at midnight
69
-
70
- let a;
71
-
72
- if (z < 2299161) {
73
- a = z;
74
- } else {
75
- const alpha = Math.floor((z - 1867216.25) / 36524.25);
76
- a = z + 1 + alpha - Math.floor(alpha / 4);
77
- }
78
-
79
- const b = a + 1524;
80
- const c = Math.floor((b - 122.1) / 365.25);
81
- const d = Math.floor(365.25 * c);
82
- const e = Math.floor((b - d) / 30.6001);
83
-
84
- const day = b - d - Math.floor(30.6001 * e);
85
- const month = e < 14 ? e - 1 : e - 13;
86
- const year = month > 2 ? c - 4716 : c - 4715;
87
-
88
- // Calculate hours, minutes, and seconds
89
- const hour = Math.floor(dayFraction * 24);
90
- const minute = Math.floor((dayFraction * 24 - hour) * 60);
91
- const second = Math.floor(((dayFraction * 24 - hour) * 60 - minute) * 60);
92
-
93
- const ms = Math.floor((((dayFraction * 24 - hour) * 60 - minute) * 60 - second)*1000);
94
-
95
- return new Date(Date.UTC(year, month - 1, day, hour, minute, second, ms));
96
- }
97
-
98
- /**
99
- * Converts an epoch (days and year) to a date string
100
- * @param {Number} days, number of days since the beginning of the year
101
- * @param {Number} year, the year
102
- * @return {String} a date string in the format YYYY-MM-DDTHH:MM:SSZ
103
- */
104
- function epochToDate(days, year) {
105
- const millisecondsPerDay = 24 * 60 * 60 * 1000; // Number of milliseconds in a day
106
-
107
- // Create a Date object representing January 1st of the given year, in UTC
108
- const startDate = new Date(Date.UTC(year, 0, 1));
109
-
110
- // Calculate the total milliseconds from the start of the year
111
- const totalMilliseconds = startDate.getTime() + (days - 1) * millisecondsPerDay;
112
-
113
- // Create a new Date object using the total milliseconds, ensuring it's treated as UTC
114
- const date = new Date(totalMilliseconds);
115
-
116
- // Extract the individual components from the Date object, all in UTC
117
- const yearString = date.getUTCFullYear().toString().padStart(4, "0");
118
- const monthString = (date.getUTCMonth() + 1).toString().padStart(2, "0");
119
- const dayString = date.getUTCDate().toString().padStart(2, "0");
120
- const hoursString = date.getUTCHours().toString().padStart(2, "0");
121
- const minutesString = date.getUTCMinutes().toString().padStart(2, "0");
122
- const secondsString = date.getUTCSeconds().toString().padStart(2, "0");
123
-
124
- // Construct the date string in the desired format
125
- const dateString = `${yearString}-${monthString}-${dayString}T${hoursString}:${minutesString}:${secondsString}Z`;
126
-
127
- return dateString;
128
- }
129
-
130
- /**
131
- * Calculates the magnitude given a vector object
132
- *
133
- * @param {Object} v Expects a vector object: v{x,y,z}
134
- *
135
- * @return {Object} Returns magnitude object
136
- */
137
- const getMagnitude = function(v) {
138
- return Math.sqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z));
139
- };
140
-
141
- /**
142
- * The code normalizes the values of a vector by dividing them by their magnitude
143
- *
144
- * @param {Object} v Expects a vector object: v{x,y,z}
145
- *
146
- * @return {Object} Returns normalized vector v{x,y,z}
147
- */
148
- const normalize = (v) => {
149
- const mag = getMagnitude(v);
150
- return {
151
- x: v.x / mag,
152
- y: v.y / mag,
153
- z: v.z / mag,
154
- };
155
- };
156
-
157
- /**
158
- * [
159
- [1,2,3],
160
- [1,2,3],
161
- [1,2,3],
162
- ]
163
- Converts to:
164
- [
165
- [1,1,1],
166
- [2,2,2],
167
- [3,3,3],
168
- ]
169
- * @param {*} Array2D
170
-
171
- * @return {array} The transposed array
172
- */
173
- const transpose = (Array2D) => {
174
- return Array2D[0].map((_, colIndex) => Array2D.map((row) => row[colIndex]));
175
- };
176
-
177
- /**
178
- * Multiply a Vector by a Rotation Matrix and calculate a final 3D vector
179
- *
180
- * @param {Object} vector Vector object with x, y, z properties
181
- * @param {Array} matrix Rotation Matrix
182
- *
183
- * @return {Object} Final 3D vector
184
- */
185
- const multiplyVector = (vector, matrix) => {
186
- const X = matrix[0][0] * vector.x
187
- + matrix[0][1] * vector.y
188
- + matrix[0][2] * vector.z;
189
-
190
- const Y = matrix[1][0] * vector.x
191
- + matrix[1][1] * vector.y
192
- + matrix[1][2] * vector.z;
193
-
194
- const Z = matrix[2][0] * vector.x
195
- + matrix[2][1] * vector.y
196
- + matrix[2][2] * vector.z;
197
-
198
- return {
199
- x: X,
200
- y: Y,
201
- z: Z,
202
- };
203
- };
204
-
205
- /**
206
- * Calculates the distance between two points
207
- *
208
- * @param {Object} p1 {x,y,z} coordinates
209
- * @param {Object} p2 {x,y,z} coordinates
210
- *
211
- * @return {Number} Distance between the two points
212
- */
213
- const dist = (p1, p2) => {
214
- const a = p2.x - p1.x;
215
- const b = p2.y - p1.y;
216
- const c = p2.z - p1.z;
217
- return Math.sqrt(a * a + b * b + c * c);
218
- };
219
-
220
- /** Checks if the variable is a valid dataMode string.
221
- *
222
- * @param {String} dataMode The dataMode to evaluate
223
- * @return {Boolean} True if input is a valid dataMode, false otherwise
224
- */
225
- const isValidDataMode = (dataMode) => {
226
- const validDataModes = ["REAL", "EXERCISE", "TEST", "SIMULATED"];
227
- if (isNonEmptyString(dataMode)) {
228
- return validDataModes.includes(dataMode);
229
- }
230
- return false;
231
- };
232
-
233
- /** Check if a variable is of boolean type
234
- *
235
- * @param {*} variable The variable to be tested
236
- * @return {Boolean} True is the variable is boolean, false otherwise
237
- */
238
- const isBoolean = (variable) => {
239
- return typeof variable === "boolean";
240
- };
241
-
242
- /** Check if a variable is a valid AND non-empty string.
243
- *
244
- * @param {*} variable The variable to test
245
- * @return {Boolean} True if it is valid and non-empty string, false otherwise.
246
- */
247
- const isNonEmptyString = (variable) => {
248
- return typeof variable === "string" && variable.trim() !== "";
249
- };
250
-
251
- /** A function that removes object properties that are null or undefined.
252
- * It is not recursive, it only removes first level properties and not nested objects.
253
- * It is ES6 compatible.
254
- * https://stackoverflow.com/questions/286141/remove-blank-attributes-from-an-object-in-javascript
255
- *
256
- * @param {Object} obj The object to clean up from null and undefined properties.
257
- * @return {Object} The mutated object with null and undefined properties removed.
258
- */
259
- const removeNullUndefined = (obj) => {
260
- Object.keys(obj).forEach((k) => obj[k] === null && delete obj[k]);
261
- return obj;
262
- };
263
-
264
- const getTimeDifference = (datetimeString)=>{
265
- // Parse the provided datetime string to a Date object
266
- const providedDate = new Date(datetimeString).getTime();
267
-
268
- // Get the current UTC date and time
269
- const currentUTCDate = Date.now(); // use Date.now() for jest mock
270
-
271
- // Calculate the time difference in milliseconds
272
- const timeDifferenceInMilliseconds = (providedDate > currentUTCDate)
273
- ? providedDate - currentUTCDate
274
- : currentUTCDate - providedDate;
275
-
276
- // Calculate the time difference in hours, minutes, and seconds
277
- let timeDifferenceInSeconds = Math.floor(timeDifferenceInMilliseconds / 1000);
278
- const hours = Math.floor(timeDifferenceInSeconds / 3600);
279
- timeDifferenceInSeconds %= 3600;
280
- const minutes = Math.floor(timeDifferenceInSeconds / 60);
281
- const seconds = timeDifferenceInSeconds % 60;
282
-
283
- // Return the time difference
284
- return ((hours > 0) ? hours + "h " : "")
285
- + ((minutes >0) ? minutes + "m " : "")
286
- + seconds+"s";
287
- };
288
-
289
- /** A function that properly checks if a variable is defined.
290
- *
291
- * @param {Object} v The variable to check
292
- * @return {Boolean} true if the number is even, false otherwise.
293
- */
294
- const isDefined = (v) => {
295
- return v!== undefined && v!== null;
296
- };
297
-
298
- /** Compute the angular separation between two vectors.
299
- *
300
- * This method computes the angular separation between two vectors using the dot product
301
- * for well separated vectors and the cross product for almost aligned vectors.
302
- * This allows to have a good accuracy in all cases, even for vectors very close to each other.
303
- *
304
- * The function is a JavaScript adaptation of Java Hipparchus Math library function Vector3D.angle(...),
305
- * which is frequently used in all of Orekit.
306
- * @param {Array} v1 First vector
307
- * @param {Array} v2 Second vector
308
- * @return {Number} The angle between the two vectors, in radians
309
- */
310
- const getAngle = (v1, v2) => {
311
- const normProduct = norm(v1) * norm(v2);
312
- if (normProduct === 0) {
313
- throw new Error("One of the two vectors has zero norm!");
314
- }
315
-
316
- const dotPr = dot(v1, v2);
317
- const threshold = normProduct * 0.9999;
318
- if ((dotPr < -threshold) || (dotPr > threshold)) {
319
- // the vectors are almost aligned, compute using the sine
320
- const v3 = cross(v1, v2);
321
- if (dotPr >= 0) {
322
- return Math.asin(norm(v3) / normProduct);
323
- }
324
- return Math.PI - Math.asin(norm(v3) / normProduct);
325
- }
326
- // the vectors are sufficiently separated to use the cosine
327
- return Math.acos(dotPr / normProduct);
328
- };
329
-
330
- /**
331
- * Wraps a number to the specified range [min, max).
332
- * @param {number} value - The number to wrap.
333
- * @param {number} min - The minimum value of the range.
334
- * @param {number} max - The maximum value of the range (exclusive).
335
- * @return {number} The wrapped number within the specified range.
336
- */
337
- const wrapToRange = (value, min, max) => {
338
- const range = max - min;
339
- return ((((value - min) % range) + range) % range) + min;
340
- };
341
-
342
- /**
343
- * Converts XYZ position object to array
344
- * @param {Object} pos - Position object with x, y, and z values
345
- * @return {Array} position array [x, y, z]
346
- */
347
- const posToArray = (pos) => {
348
- return [pos.x, pos.y, pos.z];
349
- };
350
-
351
- /** Returns the CW signed minimum difference between two angles on a ciscular buffer of 0 to 360.
352
- *
353
- * This function is commonly used to correctly compute longitude or azimuth differences.
354
- *
355
- * A positive value means that the minimum angle formed from the initial to the final position is CW.
356
- * A negative value means that the minimum angle formed from the initial to the final position is CCW.
357
- *
358
- * @example
359
- * // returns 20
360
- * getAngleDiffSigned(350, 10);
361
- * @example
362
- * // returns -20
363
- * globalNS.method(10, 350);
364
- *
365
- * @param {Number} initial The initial angle value in 0 to 360
366
- * @param {Number} final The final angle value in 0 to 360
367
- * @return {Number} The clock-wise signed minimum angle difference
368
- */
369
- const getAngleDiffSigned = (initial, final) => {
370
- const diff = final - initial;
371
- const angle = Math.abs(diff);
372
- const minAngle = Math.min(angle, 360 - angle);
373
- let sign;
374
-
375
- if (angle <= 180) {
376
- sign = diff >= 0 ? 1 : -1;
377
- } else {
378
- sign = (diff - 360 * Math.sign(diff)) >= 0 ? 1 : -1;
379
- }
380
-
381
- return sign * minAngle;
382
- };
383
-
384
- export {
385
- wrapOneRevUnsigned,
386
- wrapHalfRevUnsigned,
387
- wrapHalfRev,
388
- wrapQuarterRev,
389
- julianToGregorian,
390
- epochToDate,
391
- getMagnitude,
392
- normalize,
393
- transpose,
394
- multiplyVector,
395
- dist,
396
- isValidDataMode,
397
- isBoolean,
398
- isNonEmptyString,
399
- removeNullUndefined,
400
- getTimeDifference,
401
- isDefined,
402
- wrapToRange,
403
- getAngle,
404
- getAngleDiffSigned,
405
- posToArray,
406
- };
1
+ import {cross, dot, norm} from "mathjs";
2
+
3
+ /**
4
+ * Wrap angle to [0, 2π] range (0 to 360 degrees)
5
+ * @param {number} angleRad - Angle in radians
6
+ * @return {number} Wrapped angle in radians
7
+ */
8
+ function wrapOneRevUnsigned(angleRad) {
9
+ let wrapped = angleRad % (2 * Math.PI);
10
+ if (wrapped < 0) {
11
+ wrapped += 2 * Math.PI;
12
+ }
13
+ return wrapped;
14
+ }
15
+
16
+ /**
17
+ * Wrap angle to [0, π] range (0 to 180 degrees)
18
+ * @param {number} angleRad - Angle in radians
19
+ * @return {number} Wrapped angle in radians
20
+ */
21
+ function wrapHalfRevUnsigned(angleRad) {
22
+ let wrapped = angleRad % Math.PI;
23
+ if (wrapped < 0) {
24
+ wrapped += Math.PI;
25
+ }
26
+ return wrapped;
27
+ }
28
+
29
+ /**
30
+ * Wrap angle to [-π, π] range (-180 to 180 degrees)
31
+ * @param {number} angleRad - Angle in radians
32
+ * @return {number} Wrapped angle in radians
33
+ */
34
+ function wrapHalfRev(angleRad) {
35
+ let wrapped = angleRad % (2 * Math.PI);
36
+ if (wrapped > Math.PI) {
37
+ wrapped -= 2 * Math.PI;
38
+ } else if (wrapped < -Math.PI) {
39
+ wrapped += 2 * Math.PI;
40
+ }
41
+ return wrapped;
42
+ }
43
+
44
+ /**
45
+ * Wrap angle to [-π/2, π/2] range (-90 to 90 degrees)
46
+ * @param {number} angleRad - Angle in radians
47
+ * @return {number} Wrapped angle in radians
48
+ */
49
+ function wrapQuarterRev(angleRad) {
50
+ let wrapped = angleRad % Math.PI;
51
+ if (wrapped > Math.PI / 2) {
52
+ wrapped -= Math.PI;
53
+ } else if (wrapped < -Math.PI / 2) {
54
+ wrapped += Math.PI;
55
+ }
56
+ return wrapped;
57
+ }
58
+
59
+ /**
60
+ * Will convert a julian date to a gregorian calendar date.
61
+ * @param {Double} julianDay e.g 2460153.253704
62
+ * @return {Date} javascript date object e.g 2023-07-27 18:05:20
63
+ */
64
+ function julianToGregorian(julianDay) {
65
+ const j = julianDay + 0.5;
66
+ const z = Math.floor(j);
67
+ const f = j - z - 0.5; // subtract 0.5 to account for the day starting at noon
68
+ const dayFraction = f + 0.5; // add 0.5 to account for the day starting at midnight
69
+
70
+ let a;
71
+
72
+ if (z < 2299161) {
73
+ a = z;
74
+ } else {
75
+ const alpha = Math.floor((z - 1867216.25) / 36524.25);
76
+ a = z + 1 + alpha - Math.floor(alpha / 4);
77
+ }
78
+
79
+ const b = a + 1524;
80
+ const c = Math.floor((b - 122.1) / 365.25);
81
+ const d = Math.floor(365.25 * c);
82
+ const e = Math.floor((b - d) / 30.6001);
83
+
84
+ const day = b - d - Math.floor(30.6001 * e);
85
+ const month = e < 14 ? e - 1 : e - 13;
86
+ const year = month > 2 ? c - 4716 : c - 4715;
87
+
88
+ // Calculate hours, minutes, and seconds
89
+ const hour = Math.floor(dayFraction * 24);
90
+ const minute = Math.floor((dayFraction * 24 - hour) * 60);
91
+ const second = Math.floor(((dayFraction * 24 - hour) * 60 - minute) * 60);
92
+
93
+ const ms = Math.floor((((dayFraction * 24 - hour) * 60 - minute) * 60 - second)*1000);
94
+
95
+ return new Date(Date.UTC(year, month - 1, day, hour, minute, second, ms));
96
+ }
97
+
98
+ /**
99
+ * Converts an epoch (days and year) to a date string
100
+ * @param {Number} days, number of days since the beginning of the year
101
+ * @param {Number} year, the year
102
+ * @return {String} a date string in the format YYYY-MM-DDTHH:MM:SSZ
103
+ */
104
+ function epochToDate(days, year) {
105
+ const millisecondsPerDay = 24 * 60 * 60 * 1000; // Number of milliseconds in a day
106
+
107
+ // Create a Date object representing January 1st of the given year, in UTC
108
+ const startDate = new Date(Date.UTC(year, 0, 1));
109
+
110
+ // Calculate the total milliseconds from the start of the year
111
+ const totalMilliseconds = startDate.getTime() + (days - 1) * millisecondsPerDay;
112
+
113
+ // Create a new Date object using the total milliseconds, ensuring it's treated as UTC
114
+ const date = new Date(totalMilliseconds);
115
+
116
+ // Extract the individual components from the Date object, all in UTC
117
+ const yearString = date.getUTCFullYear().toString().padStart(4, "0");
118
+ const monthString = (date.getUTCMonth() + 1).toString().padStart(2, "0");
119
+ const dayString = date.getUTCDate().toString().padStart(2, "0");
120
+ const hoursString = date.getUTCHours().toString().padStart(2, "0");
121
+ const minutesString = date.getUTCMinutes().toString().padStart(2, "0");
122
+ const secondsString = date.getUTCSeconds().toString().padStart(2, "0");
123
+
124
+ // Construct the date string in the desired format
125
+ const dateString = `${yearString}-${monthString}-${dayString}T${hoursString}:${minutesString}:${secondsString}Z`;
126
+
127
+ return dateString;
128
+ }
129
+
130
+ /**
131
+ * Calculates the magnitude given a vector object
132
+ *
133
+ * @param {Object} v Expects a vector object: v{x,y,z}
134
+ *
135
+ * @return {Object} Returns magnitude object
136
+ */
137
+ const getMagnitude = function(v) {
138
+ return Math.sqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z));
139
+ };
140
+
141
+ /**
142
+ * The code normalizes the values of a vector by dividing them by their magnitude
143
+ *
144
+ * @param {Object} v Expects a vector object: v{x,y,z}
145
+ *
146
+ * @return {Object} Returns normalized vector v{x,y,z}
147
+ */
148
+ const normalize = (v) => {
149
+ const mag = getMagnitude(v);
150
+ return {
151
+ x: v.x / mag,
152
+ y: v.y / mag,
153
+ z: v.z / mag,
154
+ };
155
+ };
156
+
157
+ /**
158
+ * [
159
+ [1,2,3],
160
+ [1,2,3],
161
+ [1,2,3],
162
+ ]
163
+ Converts to:
164
+ [
165
+ [1,1,1],
166
+ [2,2,2],
167
+ [3,3,3],
168
+ ]
169
+ * @param {*} Array2D
170
+
171
+ * @return {array} The transposed array
172
+ */
173
+ const transpose = (Array2D) => {
174
+ return Array2D[0].map((_, colIndex) => Array2D.map((row) => row[colIndex]));
175
+ };
176
+
177
+ /**
178
+ * Multiply a Vector by a Rotation Matrix and calculate a final 3D vector
179
+ *
180
+ * @param {Object} vector Vector object with x, y, z properties
181
+ * @param {Array} matrix Rotation Matrix
182
+ *
183
+ * @return {Object} Final 3D vector
184
+ */
185
+ const multiplyVector = (vector, matrix) => {
186
+ const X = matrix[0][0] * vector.x
187
+ + matrix[0][1] * vector.y
188
+ + matrix[0][2] * vector.z;
189
+
190
+ const Y = matrix[1][0] * vector.x
191
+ + matrix[1][1] * vector.y
192
+ + matrix[1][2] * vector.z;
193
+
194
+ const Z = matrix[2][0] * vector.x
195
+ + matrix[2][1] * vector.y
196
+ + matrix[2][2] * vector.z;
197
+
198
+ return {
199
+ x: X,
200
+ y: Y,
201
+ z: Z,
202
+ };
203
+ };
204
+
205
+ /**
206
+ * Calculates the distance between two points
207
+ *
208
+ * @param {Object} p1 {x,y,z} coordinates
209
+ * @param {Object} p2 {x,y,z} coordinates
210
+ *
211
+ * @return {Number} Distance between the two points
212
+ */
213
+ const dist = (p1, p2) => {
214
+ const a = p2.x - p1.x;
215
+ const b = p2.y - p1.y;
216
+ const c = p2.z - p1.z;
217
+ return Math.sqrt(a * a + b * b + c * c);
218
+ };
219
+
220
+ /** Checks if the variable is a valid dataMode string.
221
+ *
222
+ * @param {String} dataMode The dataMode to evaluate
223
+ * @return {Boolean} True if input is a valid dataMode, false otherwise
224
+ */
225
+ const isValidDataMode = (dataMode) => {
226
+ const validDataModes = ["REAL", "EXERCISE", "TEST", "SIMULATED"];
227
+ if (isNonEmptyString(dataMode)) {
228
+ return validDataModes.includes(dataMode);
229
+ }
230
+ return false;
231
+ };
232
+
233
+ /** Check if a variable is of boolean type
234
+ *
235
+ * @param {*} variable The variable to be tested
236
+ * @return {Boolean} True is the variable is boolean, false otherwise
237
+ */
238
+ const isBoolean = (variable) => {
239
+ return typeof variable === "boolean";
240
+ };
241
+
242
+ /** Check if a variable is a valid AND non-empty string.
243
+ *
244
+ * @param {*} variable The variable to test
245
+ * @return {Boolean} True if it is valid and non-empty string, false otherwise.
246
+ */
247
+ const isNonEmptyString = (variable) => {
248
+ return typeof variable === "string" && variable.trim() !== "";
249
+ };
250
+
251
+ /** A function that removes object properties that are null or undefined.
252
+ * It is not recursive, it only removes first level properties and not nested objects.
253
+ * It is ES6 compatible.
254
+ * https://stackoverflow.com/questions/286141/remove-blank-attributes-from-an-object-in-javascript
255
+ *
256
+ * @param {Object} obj The object to clean up from null and undefined properties.
257
+ * @return {Object} The mutated object with null and undefined properties removed.
258
+ */
259
+ const removeNullUndefined = (obj) => {
260
+ Object.keys(obj).forEach((k) => obj[k] === null && delete obj[k]);
261
+ return obj;
262
+ };
263
+
264
+ const getTimeDifference = (datetimeString)=>{
265
+ // Parse the provided datetime string to a Date object
266
+ const providedDate = new Date(datetimeString).getTime();
267
+
268
+ // Get the current UTC date and time
269
+ const currentUTCDate = Date.now(); // use Date.now() for jest mock
270
+
271
+ // Calculate the time difference in milliseconds
272
+ const timeDifferenceInMilliseconds = (providedDate > currentUTCDate)
273
+ ? providedDate - currentUTCDate
274
+ : currentUTCDate - providedDate;
275
+
276
+ // Calculate the time difference in hours, minutes, and seconds
277
+ let timeDifferenceInSeconds = Math.floor(timeDifferenceInMilliseconds / 1000);
278
+ const hours = Math.floor(timeDifferenceInSeconds / 3600);
279
+ timeDifferenceInSeconds %= 3600;
280
+ const minutes = Math.floor(timeDifferenceInSeconds / 60);
281
+ const seconds = timeDifferenceInSeconds % 60;
282
+
283
+ // Return the time difference
284
+ return ((hours > 0) ? hours + "h " : "")
285
+ + ((minutes >0) ? minutes + "m " : "")
286
+ + seconds+"s";
287
+ };
288
+
289
+ /** A function that properly checks if a variable is defined.
290
+ *
291
+ * @param {Object} v The variable to check
292
+ * @return {Boolean} true if the number is even, false otherwise.
293
+ */
294
+ const isDefined = (v) => {
295
+ return v!== undefined && v!== null;
296
+ };
297
+
298
+ /** Compute the angular separation between two vectors.
299
+ *
300
+ * This method computes the angular separation between two vectors using the dot product
301
+ * for well separated vectors and the cross product for almost aligned vectors.
302
+ * This allows to have a good accuracy in all cases, even for vectors very close to each other.
303
+ *
304
+ * The function is a JavaScript adaptation of Java Hipparchus Math library function Vector3D.angle(...),
305
+ * which is frequently used in all of Orekit.
306
+ * @param {Array} v1 First vector
307
+ * @param {Array} v2 Second vector
308
+ * @return {Number} The angle between the two vectors, in radians
309
+ */
310
+ const getAngle = (v1, v2) => {
311
+ const normProduct = norm(v1) * norm(v2);
312
+ if (normProduct === 0) {
313
+ throw new Error("One of the two vectors has zero norm!");
314
+ }
315
+
316
+ const dotPr = dot(v1, v2);
317
+ const threshold = normProduct * 0.9999;
318
+ if ((dotPr < -threshold) || (dotPr > threshold)) {
319
+ // the vectors are almost aligned, compute using the sine
320
+ const v3 = cross(v1, v2);
321
+ if (dotPr >= 0) {
322
+ return Math.asin(norm(v3) / normProduct);
323
+ }
324
+ return Math.PI - Math.asin(norm(v3) / normProduct);
325
+ }
326
+ // the vectors are sufficiently separated to use the cosine
327
+ return Math.acos(dotPr / normProduct);
328
+ };
329
+
330
+ /**
331
+ * Wraps a number to the specified range [min, max).
332
+ * @param {number} value - The number to wrap.
333
+ * @param {number} min - The minimum value of the range.
334
+ * @param {number} max - The maximum value of the range (exclusive).
335
+ * @return {number} The wrapped number within the specified range.
336
+ */
337
+ const wrapToRange = (value, min, max) => {
338
+ const range = max - min;
339
+ return ((((value - min) % range) + range) % range) + min;
340
+ };
341
+
342
+ /**
343
+ * Converts XYZ position object to array
344
+ * @param {Object} pos - Position object with x, y, and z values
345
+ * @return {Array} position array [x, y, z]
346
+ */
347
+ const posToArray = (pos) => {
348
+ return [pos.x, pos.y, pos.z];
349
+ };
350
+
351
+ /** Returns the CW signed minimum difference between two angles on a ciscular buffer of 0 to 360.
352
+ *
353
+ * This function is commonly used to correctly compute longitude or azimuth differences.
354
+ *
355
+ * A positive value means that the minimum angle formed from the initial to the final position is CW.
356
+ * A negative value means that the minimum angle formed from the initial to the final position is CCW.
357
+ *
358
+ * @example
359
+ * // returns 20
360
+ * getAngleDiffSigned(350, 10);
361
+ * @example
362
+ * // returns -20
363
+ * globalNS.method(10, 350);
364
+ *
365
+ * @param {Number} initial The initial angle value in 0 to 360
366
+ * @param {Number} final The final angle value in 0 to 360
367
+ * @return {Number} The clock-wise signed minimum angle difference
368
+ */
369
+ const getAngleDiffSigned = (initial, final) => {
370
+ const diff = final - initial;
371
+ const angle = Math.abs(diff);
372
+ const minAngle = Math.min(angle, 360 - angle);
373
+ let sign;
374
+
375
+ if (angle <= 180) {
376
+ sign = diff >= 0 ? 1 : -1;
377
+ } else {
378
+ sign = (diff - 360 * Math.sign(diff)) >= 0 ? 1 : -1;
379
+ }
380
+
381
+ return sign * minAngle;
382
+ };
383
+
384
+ export {
385
+ wrapOneRevUnsigned,
386
+ wrapHalfRevUnsigned,
387
+ wrapHalfRev,
388
+ wrapQuarterRev,
389
+ julianToGregorian,
390
+ epochToDate,
391
+ getMagnitude,
392
+ normalize,
393
+ transpose,
394
+ multiplyVector,
395
+ dist,
396
+ isValidDataMode,
397
+ isBoolean,
398
+ isNonEmptyString,
399
+ removeNullUndefined,
400
+ getTimeDifference,
401
+ isDefined,
402
+ wrapToRange,
403
+ getAngle,
404
+ getAngleDiffSigned,
405
+ posToArray,
406
+ };