@saber-usa/node-common 1.6.207
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 +42 -0
- package/package.json +51 -0
- package/src/FrameConverter.js +1071 -0
- package/src/LLA.js +181 -0
- package/src/LaunchNominalClass.js +822 -0
- package/src/NodeVector3D.js +71 -0
- package/src/OrbitUtils.js +491 -0
- package/src/ShadowGEOCalculator.js +201 -0
- package/src/TimeConverter.js +311 -0
- package/src/astro.js +3169 -0
- package/src/ballisticPropagator/ballisticPropagator.js +1047 -0
- package/src/checkNetwork.js +19 -0
- package/src/constants.js +33 -0
- package/src/fixDate.js +69 -0
- package/src/index.js +11 -0
- package/src/launchNominal.js +215 -0
- package/src/loggerFactory.js +98 -0
- package/src/s3.js +59 -0
- package/src/transform.js +40 -0
- package/src/udl.js +115 -0
- package/src/utils.js +406 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
const constants = require("./constants.js");
|
|
2
|
+
const solar = require("solar-calculator");
|
|
3
|
+
const THREE = require("three");
|
|
4
|
+
const {wrapToRange} = require("./utils.js");
|
|
5
|
+
const {sunPosAt} = require("./astro.js");
|
|
6
|
+
|
|
7
|
+
class ShadowGEOCalculator {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.constants = constants;
|
|
10
|
+
this.solarCalculator = solar;
|
|
11
|
+
this.sunRadiusKm = 695700; // Radius of the Sun in kilometers, (source: orekit, through resolution B3 from IAU 2015)
|
|
12
|
+
// 695701 in astro library
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static _calculateUmbraGeometry(deltaPS) {
|
|
16
|
+
const Dp = 2 * constants.WGS84_EARTH_EQUATORIAL_RADIUS_KM;
|
|
17
|
+
const Ds = 2 * 695700; // sunRadiusKm
|
|
18
|
+
|
|
19
|
+
// Distance from Earth center to umbra apex
|
|
20
|
+
const Xu = (Dp * deltaPS) / (Ds - Dp);
|
|
21
|
+
|
|
22
|
+
// Umbra subtended angle
|
|
23
|
+
const alphaU = Math.asin(Dp / (2 * Xu));
|
|
24
|
+
|
|
25
|
+
return {Xu, alphaU};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static _calculatePenumbraGeometry(deltaPS) {
|
|
29
|
+
const Dp = 2 * constants.WGS84_EARTH_EQUATORIAL_RADIUS_KM;
|
|
30
|
+
const Ds = 2 * 695700; // sunRadiusKm
|
|
31
|
+
|
|
32
|
+
// Distance from Earth center to penumbra apex
|
|
33
|
+
const Xp = (Dp * deltaPS) / (Ds + Dp);
|
|
34
|
+
|
|
35
|
+
// Penumbra subtended angle
|
|
36
|
+
const alphaP = Math.asin(Dp / (2 * Xp));
|
|
37
|
+
|
|
38
|
+
return {Xp, alphaP};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static _umbraRadiusDistance(distance, Xu, alphaU) {
|
|
42
|
+
if (distance > Xu) {
|
|
43
|
+
throw new Error("Distance must be less than or equal to the umbra apex distance.");
|
|
44
|
+
}
|
|
45
|
+
// Calculate the radius of the umbra at a given distance from the apex
|
|
46
|
+
return (Xu - distance) * Math.tan(alphaU);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static _penumbraRadiusDistance(distance, Xp, alphaP) {
|
|
50
|
+
// Calculate the radius of the penumbra at a given distance from the apex
|
|
51
|
+
return (distance + Math.abs(Xp)) * Math.tan(alphaP);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static _sunUnitVector(sunLongitude, sunDeclination) {
|
|
55
|
+
const sunVector = new THREE.Vector3();
|
|
56
|
+
sunVector.x = Math.cos(sunDeclination * constants.DEG2RAD)
|
|
57
|
+
* Math.cos(sunLongitude * constants.DEG2RAD);
|
|
58
|
+
sunVector.y = Math.cos(sunDeclination * constants.DEG2RAD)
|
|
59
|
+
* Math.sin(sunLongitude * constants.DEG2RAD);
|
|
60
|
+
sunVector.z = Math.sin(sunDeclination * constants.DEG2RAD);
|
|
61
|
+
return sunVector; // returns the unit vector of the Sun's position
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static _calculateSunDistance(sunLongitude, sunDeclination) {
|
|
65
|
+
// Longitude and declination (latitude) describe direction, not distance.
|
|
66
|
+
// To get the Earth-Sun distance in km, use astronomical data (mean ≈ 149,597,870.7 km).
|
|
67
|
+
// If you want the position vector in km, use the distance and direction:
|
|
68
|
+
const AU_KM = constants.AU_KM; // Astronomical Unit in kilometers
|
|
69
|
+
// AstroLibrary uses 149597900
|
|
70
|
+
const r = AU_KM; // Use actual Earth-Sun distance if available, otherwise mean AU
|
|
71
|
+
|
|
72
|
+
// Convert longitude and declination to a position vector in km
|
|
73
|
+
const x = r * Math.cos(sunDeclination * constants.DEG2RAD)
|
|
74
|
+
* Math.cos(sunLongitude * constants.DEG2RAD);
|
|
75
|
+
const y = r * Math.cos(sunDeclination * constants.DEG2RAD)
|
|
76
|
+
* Math.sin(sunLongitude * constants.DEG2RAD);
|
|
77
|
+
const z = r * Math.sin(sunDeclination * constants.DEG2RAD);
|
|
78
|
+
|
|
79
|
+
return {x, y, z, distance: r};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static geoBeltShadowIntersection(date) {
|
|
83
|
+
const [sunDeclination, sunLongitude] = sunPosAt(date);
|
|
84
|
+
// Calculate sun position
|
|
85
|
+
let deltaPS;
|
|
86
|
+
if (sunLongitude !== null && sunDeclination !== null) {
|
|
87
|
+
// convert sunLongitude and sunDeclination to distance in km
|
|
88
|
+
deltaPS = ShadowGEOCalculator._calculateSunDistance(
|
|
89
|
+
sunLongitude, sunDeclination)["distance"];
|
|
90
|
+
} else {
|
|
91
|
+
const time = new Date();
|
|
92
|
+
const [newSunDeclination, newSunLongitude] = sunPosAt(time);
|
|
93
|
+
const sunPositionData = ShadowGEOCalculator._calculateSunDistance(
|
|
94
|
+
newSunLongitude, newSunDeclination);
|
|
95
|
+
deltaPS = sunPositionData.distance;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Calculate shadow geometry
|
|
99
|
+
const {Xu, alphaU} = ShadowGEOCalculator._calculateUmbraGeometry(deltaPS);
|
|
100
|
+
const {Xp, alphaP} = ShadowGEOCalculator._calculatePenumbraGeometry(deltaPS);
|
|
101
|
+
|
|
102
|
+
const geoRadius = constants.WGS84_EARTH_EQUATORIAL_RADIUS_KM + constants.GEO_ALTITUDE_KM;
|
|
103
|
+
|
|
104
|
+
// Calculate Shadow radii at GEO altitude
|
|
105
|
+
const umbraRadiusGEO = ShadowGEOCalculator._umbraRadiusDistance(geoRadius, Xu, alphaU);
|
|
106
|
+
const penumbraRadiusGEO = ShadowGEOCalculator._penumbraRadiusDistance(
|
|
107
|
+
geoRadius, Xp, alphaP);
|
|
108
|
+
|
|
109
|
+
// Calculate sun unit vector
|
|
110
|
+
const sunVector = ShadowGEOCalculator._sunUnitVector(sunLongitude, sunDeclination);
|
|
111
|
+
|
|
112
|
+
// Calculate shadow axis
|
|
113
|
+
const shadowAxis = [-sunVector.x, -sunVector.y, -sunVector.z];
|
|
114
|
+
|
|
115
|
+
// Finding shadow axis center in x, y, z coordinates
|
|
116
|
+
|
|
117
|
+
const shadowCenter = [
|
|
118
|
+
shadowAxis[0] * geoRadius,
|
|
119
|
+
shadowAxis[1] * geoRadius,
|
|
120
|
+
shadowAxis[2] * geoRadius,
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
// Converting shadow axis to latitude and longitude
|
|
124
|
+
const shadowCenterLat = Math.asin(shadowCenter[2] / geoRadius) * constants.RAD2DEG;
|
|
125
|
+
const shadowCenterLon = Math.atan2(shadowCenter[1], shadowCenter[0]) * constants.RAD2DEG;
|
|
126
|
+
|
|
127
|
+
// Check if GEO belt intersects the shadow
|
|
128
|
+
const equatorialDistance = Math.abs(shadowCenter[2]); // Distance from the equator to the shadow center
|
|
129
|
+
const intersectsUmbra = umbraRadiusGEO > equatorialDistance;
|
|
130
|
+
const intersectsPenumbra = penumbraRadiusGEO > equatorialDistance;
|
|
131
|
+
|
|
132
|
+
const intersectionPoints = [];
|
|
133
|
+
|
|
134
|
+
if (intersectsPenumbra) {
|
|
135
|
+
// angle from the shadow center to the intersection point
|
|
136
|
+
const alpha = Math.sqrt(penumbraRadiusGEO**2 - equatorialDistance**2);
|
|
137
|
+
const theta = Math.atan2(alpha, geoRadius);
|
|
138
|
+
|
|
139
|
+
// Project shadow center onto the GEO belt
|
|
140
|
+
const shadowAxisEq = [shadowAxis[0], shadowAxis[1], 0];
|
|
141
|
+
const shadowAxisNorm = Math.sqrt(shadowAxisEq[0]**2 + shadowAxisEq[1]**2);
|
|
142
|
+
shadowAxisEq[0] /= shadowAxisNorm;
|
|
143
|
+
shadowAxisEq[1] /= shadowAxisNorm;
|
|
144
|
+
|
|
145
|
+
// Intersection points in the GEO belt
|
|
146
|
+
const centerLon = shadowCenterLon; // You already have this!
|
|
147
|
+
|
|
148
|
+
let lon1 = centerLon + theta * constants.RAD2DEG;
|
|
149
|
+
let lon2 = centerLon - theta * constants.RAD2DEG;
|
|
150
|
+
|
|
151
|
+
// Ensure longitudes are within -180 to 180 degrees
|
|
152
|
+
lon1 = wrapToRange(lon1, -180, 180);
|
|
153
|
+
lon2 = wrapToRange(lon2, -180, 180);
|
|
154
|
+
|
|
155
|
+
intersectionPoints.push({
|
|
156
|
+
type: "penumbra",
|
|
157
|
+
longitudeRange: [lon1, lon2],
|
|
158
|
+
latitude: 0, // Equatorial belt
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (intersectsUmbra) {
|
|
163
|
+
// angle from the shadow center to the intersection point
|
|
164
|
+
const alpha = Math.sqrt(umbraRadiusGEO**2 - equatorialDistance**2);
|
|
165
|
+
const theta = Math.atan2(alpha, geoRadius);
|
|
166
|
+
|
|
167
|
+
// Project shadow center onto the GEO belt
|
|
168
|
+
const shadowAxisEq = [shadowAxis[0], shadowAxis[1], 0];
|
|
169
|
+
const shadowAxisNorm = Math.sqrt(shadowAxisEq[0]**2 + shadowAxisEq[1]**2);
|
|
170
|
+
shadowAxisEq[0] /= shadowAxisNorm;
|
|
171
|
+
shadowAxisEq[1] /= shadowAxisNorm;
|
|
172
|
+
|
|
173
|
+
// Intersection points in the GEO belt
|
|
174
|
+
const centerLon = shadowCenterLon; // You already have this!
|
|
175
|
+
|
|
176
|
+
let lon1 = centerLon + theta * constants.RAD2DEG;
|
|
177
|
+
let lon2 = centerLon - theta * constants.RAD2DEG;
|
|
178
|
+
|
|
179
|
+
// Ensure longitudes are within -180 to 180 degrees
|
|
180
|
+
lon1 = ((lon1 + 180) % 360 + 360) % 360 - 180;
|
|
181
|
+
lon2 = ((lon2 + 180) % 360 + 360) % 360 - 180;
|
|
182
|
+
|
|
183
|
+
intersectionPoints.push({
|
|
184
|
+
type: "umbra",
|
|
185
|
+
longitudeRange: [lon1, lon2],
|
|
186
|
+
latitude: 0, // Equatorial belt
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
shadowCenter: shadowCenter,
|
|
192
|
+
shadowCenterLat: shadowCenterLat,
|
|
193
|
+
shadowCenterLon: shadowCenterLon,
|
|
194
|
+
umbraRadiusGEO: umbraRadiusGEO,
|
|
195
|
+
penumbraRadiusGEO: penumbraRadiusGEO,
|
|
196
|
+
intersectionPoints: intersectionPoints,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = ShadowGEOCalculator;
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
// const {gstime} = require("satellite.js");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TimeConverter - Port from C# TimeConverter
|
|
5
|
+
* Handles conversions between different time systems (UTC, UT1, TAI, GPS, TT)
|
|
6
|
+
* and Julian date calculations
|
|
7
|
+
*/
|
|
8
|
+
class TimeConverter {
|
|
9
|
+
// Constants from the C# implementation
|
|
10
|
+
static JULIAN_UTC = 2415018.5; // Julian Date of midnight, December 30, 1899
|
|
11
|
+
static JULIAN_MJD = 2400000.5; // = JD - MJD
|
|
12
|
+
|
|
13
|
+
// These values need to be changed periodically
|
|
14
|
+
// See http://www.stjarnhimlen.se/comp/time.html for bi-annual values (in seconds)
|
|
15
|
+
// See https://hpiers.obspm.fr/eoppc/eop/eopc04/eopc04_IAU2000.62-now for daily updated values
|
|
16
|
+
// As of 6/22/22:
|
|
17
|
+
static TAI_UTC = 37;
|
|
18
|
+
static GPS_UTC = 18;
|
|
19
|
+
static TT_UT1 = 69.29;
|
|
20
|
+
static UT1_UTC = -0.0977075;
|
|
21
|
+
static LOD = 0.0000022; // Length of day correction
|
|
22
|
+
static XP = 0.132949; // Polar motion X
|
|
23
|
+
static YP = 0.486774; // Polar motion Y
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Constructor accepts either a UTC Date or a Julian date number
|
|
27
|
+
* @param {Date|number} input - Either a Date object (UTC) or a Julian date number
|
|
28
|
+
*/
|
|
29
|
+
constructor(input) {
|
|
30
|
+
if (typeof input === "number") {
|
|
31
|
+
// Input is a Julian date
|
|
32
|
+
this._julian = input;
|
|
33
|
+
this._utc = null;
|
|
34
|
+
} else {
|
|
35
|
+
// Input is a Date object
|
|
36
|
+
this._utc = input instanceof Date ? input : new Date(input);
|
|
37
|
+
this._julian = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Instance properties
|
|
42
|
+
get UTC() {
|
|
43
|
+
return this._utc || TimeConverter.julianToDateTime(this.JulianUTC);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get UT1() {
|
|
47
|
+
return TimeConverter.utcToUT1(this.UTC);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get TAI() {
|
|
51
|
+
return TimeConverter.utcToTAI(this.UTC);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get GPS() {
|
|
55
|
+
return TimeConverter.utcToGPS(this.UTC);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get TT() {
|
|
59
|
+
return TimeConverter.ut1ToTT(this.UT1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get GMST() {
|
|
63
|
+
return TimeConverter.getGMST(this.JulianUT1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get JulianUTC() {
|
|
67
|
+
return this._julian !== null ? this._julian : TimeConverter.dateTimeToJulian(this.UTC);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get JulianUT1() {
|
|
71
|
+
return TimeConverter.dateTimeToJulian(this.UT1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get JulianTAI() {
|
|
75
|
+
return TimeConverter.dateTimeToJulian(this.TAI);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get JulianGPS() {
|
|
79
|
+
return TimeConverter.dateTimeToJulian(this.GPS);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get JulianTT() {
|
|
83
|
+
return TimeConverter.dateTimeToJulian(this.TT);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get JulianCenturiesTT() {
|
|
87
|
+
return TimeConverter.julianToCenturiesTxxx(this.JulianTT);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Backwards compatibility property
|
|
91
|
+
get date() {
|
|
92
|
+
return this.UTC;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Static factory methods
|
|
96
|
+
static FromJulianUTC(julian) {
|
|
97
|
+
return new TimeConverter(julian);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static FromGregorianUTC(date) {
|
|
101
|
+
return new TimeConverter(date);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Convert Julian date to JavaScript Date
|
|
106
|
+
* @param {number} julian - Julian date
|
|
107
|
+
* @return {Date} JavaScript Date object
|
|
108
|
+
*/
|
|
109
|
+
static julianToDateTime(julian) {
|
|
110
|
+
// Using the C# formula: DateTime.FromOADate(julian - JULIAN_UTC)
|
|
111
|
+
// OLE Automation Date is days since Dec 30, 1899
|
|
112
|
+
const daysSince1899 = julian - TimeConverter.JULIAN_UTC;
|
|
113
|
+
const millisSince1899 = daysSince1899 * 86400000; // Convert days to milliseconds
|
|
114
|
+
const date1899 = new Date(Date.UTC(1899, 11, 30, 0, 0, 0, 0)); // Dec 30, 1899
|
|
115
|
+
return new Date(date1899.getTime() + millisSince1899);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Convert Date to Julian date
|
|
120
|
+
* @param {Date} date - JavaScript Date object
|
|
121
|
+
* @return {number} Julian date
|
|
122
|
+
*/
|
|
123
|
+
static dateTimeToJulian(date) {
|
|
124
|
+
// Vallado Fundamentals, Algorithm 14 p. 183
|
|
125
|
+
const year = date.getUTCFullYear();
|
|
126
|
+
const month = date.getUTCMonth() + 1; // JavaScript months are 0-11
|
|
127
|
+
const day = date.getUTCDate();
|
|
128
|
+
const hour = date.getUTCHours();
|
|
129
|
+
const minute = date.getUTCMinutes();
|
|
130
|
+
const second = date.getUTCSeconds();
|
|
131
|
+
const millisecond = date.getUTCMilliseconds();
|
|
132
|
+
|
|
133
|
+
const jd = 367 * year - Math.floor(7 * (year + Math.floor((month + 9) / 12)) / 4)
|
|
134
|
+
+ Math.floor(275 * month / 9) + day + 1721013.5
|
|
135
|
+
+ (((second + millisecond * 1e-3) / 60 + minute) / 60 + hour) / 24;
|
|
136
|
+
|
|
137
|
+
return jd;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Convert Julian date to Modified Julian Date
|
|
142
|
+
* @param {number} julian - Julian date
|
|
143
|
+
* @return {number} Modified Julian Date
|
|
144
|
+
*/
|
|
145
|
+
static JulianToMJD(julian) {
|
|
146
|
+
return julian - TimeConverter.JULIAN_MJD;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Convert Modified Julian Date to Julian date
|
|
151
|
+
* @param {number} mjd - Modified Julian Date
|
|
152
|
+
* @return {number} Julian date
|
|
153
|
+
*/
|
|
154
|
+
static MJDToJulian(mjd) {
|
|
155
|
+
return mjd + TimeConverter.JULIAN_MJD;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Convert TAI to UTC
|
|
160
|
+
* @param {Date} tai - TAI time
|
|
161
|
+
* @return {Date} UTC time
|
|
162
|
+
*/
|
|
163
|
+
static TAIToUTC(tai) {
|
|
164
|
+
const utcMillis = tai.getTime() - (TimeConverter.TAI_UTC * 1000);
|
|
165
|
+
return new Date(utcMillis);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Convert UTC to TAI
|
|
170
|
+
* @param {Date} utc - UTC time
|
|
171
|
+
* @return {Date} TAI time
|
|
172
|
+
*/
|
|
173
|
+
static utcToTAI(utc) {
|
|
174
|
+
const taiMillis = utc.getTime() + (TimeConverter.TAI_UTC * 1000);
|
|
175
|
+
return new Date(taiMillis);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Convert GPS to UTC
|
|
180
|
+
* @param {Date} gps - GPS time
|
|
181
|
+
* @return {Date} UTC time
|
|
182
|
+
*/
|
|
183
|
+
static GPSToUTC(gps) {
|
|
184
|
+
const utcMillis = gps.getTime() - (TimeConverter.GPS_UTC * 1000);
|
|
185
|
+
return new Date(utcMillis);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Convert UTC to GPS
|
|
190
|
+
* @param {Date} utc - UTC time
|
|
191
|
+
* @return {Date} GPS time
|
|
192
|
+
*/
|
|
193
|
+
static utcToGPS(utc) {
|
|
194
|
+
const gpsMillis = utc.getTime() + (TimeConverter.GPS_UTC * 1000);
|
|
195
|
+
return new Date(gpsMillis);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Convert TT to UT1
|
|
200
|
+
* @param {Date} tt - Terrestrial Time
|
|
201
|
+
* @return {Date} UT1 time
|
|
202
|
+
*/
|
|
203
|
+
static TTToUT1(tt) {
|
|
204
|
+
const ut1Millis = tt.getTime() - (TimeConverter.TT_UT1 * 1000);
|
|
205
|
+
return new Date(ut1Millis);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Convert UT1 to TT
|
|
210
|
+
* @param {Date} ut1 - UT1 time
|
|
211
|
+
* @return {Date} Terrestrial Time
|
|
212
|
+
*/
|
|
213
|
+
static ut1ToTT(ut1) {
|
|
214
|
+
const ttMillis = ut1.getTime() + (TimeConverter.TT_UT1 * 1000);
|
|
215
|
+
return new Date(ttMillis);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Convert UT1 to UTC
|
|
220
|
+
* @param {Date} ut1 - UT1 time
|
|
221
|
+
* @return {Date} UTC time
|
|
222
|
+
*/
|
|
223
|
+
static UT1ToUTC(ut1) {
|
|
224
|
+
const utcMillis = ut1.getTime() - (TimeConverter.UT1_UTC * 1000);
|
|
225
|
+
return new Date(utcMillis);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Convert UTC to UT1
|
|
230
|
+
* @param {Date} utc - UTC time
|
|
231
|
+
* @return {Date} UT1 time
|
|
232
|
+
*/
|
|
233
|
+
static utcToUT1(utc) {
|
|
234
|
+
const ut1Millis = utc.getTime() + (TimeConverter.UT1_UTC * 1000);
|
|
235
|
+
return new Date(ut1Millis);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get Greenwich Mean Sidereal Time from UT1 julian
|
|
240
|
+
* @param {number} jdut1 - Julian date UT1
|
|
241
|
+
* @param {boolean} wrap360 - Whether to wrap to 0-360 degrees
|
|
242
|
+
* @return {Object} Angle object with Radians property
|
|
243
|
+
*/
|
|
244
|
+
static getGMST(jdut1, wrap360 = true) {
|
|
245
|
+
const tut1 = TimeConverter.julianToCenturiesTxxx(jdut1);
|
|
246
|
+
let gmst = -6.2e-6 * tut1 * tut1 * tut1 + 0.093104 * tut1 * tut1
|
|
247
|
+
+ (876600.0 * 3600 + 8640184.812866) * tut1 + 67310.54841; // seconds
|
|
248
|
+
gmst = gmst * (Math.PI / 180) / 240.0 % (2.0 * Math.PI); // 360/86400 = 1/240, to deg, to rad
|
|
249
|
+
|
|
250
|
+
// Check quadrants
|
|
251
|
+
if (gmst < 0.0 && wrap360) {
|
|
252
|
+
gmst += 2.0 * Math.PI;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
Radians: gmst,
|
|
257
|
+
Degrees: gmst * 180 / Math.PI,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get Local Mean Sidereal Time from longitude and UT1 julian
|
|
263
|
+
* @param {number} lonDeg - Longitude in degrees (-180 to 180)
|
|
264
|
+
* @param {number} jdut1 - Julian date UT1
|
|
265
|
+
* @param {boolean} wrap360 - Whether to wrap to 0-360 degrees
|
|
266
|
+
* @return {Object} Object containing lmst (radians) and gmst (radians)
|
|
267
|
+
*/
|
|
268
|
+
static GetLMST(lonDeg, jdut1, wrap360 = true) {
|
|
269
|
+
const gmstAngle = TimeConverter.getGMST(jdut1, wrap360);
|
|
270
|
+
const gmst = gmstAngle.Radians;
|
|
271
|
+
let lst = lonDeg * Math.PI / 180 + gmst;
|
|
272
|
+
|
|
273
|
+
// Check quadrants
|
|
274
|
+
lst %= 2.0 * Math.PI;
|
|
275
|
+
if (lst < 0.0 && wrap360) {
|
|
276
|
+
lst += 2.0 * Math.PI;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
lmst: lst,
|
|
281
|
+
gmst: gmst,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Convert Julian date to centuries since J2000
|
|
287
|
+
* @param {number} julian - Julian date
|
|
288
|
+
* @return {number} Centuries since J2000
|
|
289
|
+
*/
|
|
290
|
+
static julianToCenturiesTxxx(julian) {
|
|
291
|
+
return (julian - 2451545.0) / 36525.0;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* RaDec class for Right Ascension and Declination
|
|
297
|
+
*/
|
|
298
|
+
class RaDec {
|
|
299
|
+
constructor(raDeg, decDeg) {
|
|
300
|
+
this.RightAscension = {
|
|
301
|
+
Degrees: raDeg,
|
|
302
|
+
Radians: raDeg * Math.PI / 180,
|
|
303
|
+
};
|
|
304
|
+
this.Declination = {
|
|
305
|
+
Degrees: decDeg,
|
|
306
|
+
Radians: decDeg * Math.PI / 180,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = {TimeConverter, RaDec};
|