|
2 | 2 | * Compute azimuth and elevation from a ground observer to a satellite. |
3 | 3 | * Pure math — no THREE.js or DOM dependencies. Usable in Web Workers. |
4 | 4 | * |
5 | | - * Uses spherical Earth model (R = 6371 km). |
| 5 | + * Uses WGS-84 ellipsoid for observer positioning. |
6 | 6 | * |
7 | 7 | * @param eciX Standard ECI X position (km) — toward vernal equinox |
8 | 8 | * @param eciY Standard ECI Y position (km) — completes right-hand system |
9 | 9 | * @param eciZ Standard ECI Z position (km) — toward north pole |
10 | 10 | * @param gmstRad Greenwich Mean Sidereal Time in radians |
11 | | - * @param obsLatDeg Observer latitude in degrees (-90 to 90) |
12 | | - * @param obsLonDeg Observer longitude in degrees (-180 to 180) |
13 | | - * @param obsAltM Observer altitude in meters above sea level |
| 11 | + * @param obsLatDeg Observer geodetic latitude in degrees (-90 to 90) |
| 12 | + * @param obsLonDeg Observer geodetic longitude in degrees (-180 to 180) |
| 13 | + * @param obsAltM Observer altitude in meters above WGS-84 ellipsoid |
14 | 14 | * @returns { az, el } in degrees. Az: 0=N, 90=E, 180=S, 270=W. El: 0=horizon, 90=zenith. |
15 | 15 | */ |
| 16 | +import { geodeticToEcef } from './geodetic'; |
| 17 | + |
16 | 18 | export function getAzEl( |
17 | 19 | eciX: number, eciY: number, eciZ: number, |
18 | 20 | gmstRad: number, |
19 | 21 | obsLatDeg: number, obsLonDeg: number, obsAltM: number, |
20 | 22 | ): { az: number; el: number } { |
21 | 23 | const DEG2RAD = Math.PI / 180; |
22 | | - const EARTH_R = 6371.0; // km |
23 | 24 |
|
24 | 25 | const satR = Math.sqrt(eciX * eciX + eciY * eciY + eciZ * eciZ); |
25 | 26 | if (satR === 0) return { az: 0, el: -90 }; |
26 | 27 |
|
27 | | - // Satellite geodetic from ECI |
| 28 | + // Satellite geocentric lat/lon from ECI, then to ECEF |
28 | 29 | const satLat = Math.asin(eciZ / satR); |
29 | 30 | const satLonEci = Math.atan2(eciY, eciX); |
30 | 31 | const satLonEcef = satLonEci - gmstRad; |
31 | 32 |
|
32 | | - // Satellite ECEF position |
33 | 33 | const sx = satR * Math.cos(satLat) * Math.cos(satLonEcef); |
34 | 34 | const sy = satR * Math.cos(satLat) * Math.sin(satLonEcef); |
35 | 35 | const sz = satR * Math.sin(satLat); |
36 | 36 |
|
37 | | - // Observer ECEF position |
38 | | - const latRad = obsLatDeg * DEG2RAD; |
39 | | - const lonRad = obsLonDeg * DEG2RAD; |
40 | | - const obsR = EARTH_R + obsAltM / 1000; |
41 | | - const ox = obsR * Math.cos(latRad) * Math.cos(lonRad); |
42 | | - const oy = obsR * Math.cos(latRad) * Math.sin(lonRad); |
43 | | - const oz = obsR * Math.sin(latRad); |
| 37 | + // Observer ECEF position (WGS-84 ellipsoid) |
| 38 | + const obs = geodeticToEcef(obsLatDeg, obsLonDeg, obsAltM); |
44 | 39 |
|
45 | 40 | // Range vector (ECEF) |
46 | | - const dx = sx - ox; |
47 | | - const dy = sy - oy; |
48 | | - const dz = sz - oz; |
| 41 | + const dx = sx - obs.x; |
| 42 | + const dy = sy - obs.y; |
| 43 | + const dz = sz - obs.z; |
49 | 44 |
|
50 | | - // Rotate to topocentric East-North-Up |
| 45 | + // Rotate to topocentric East-North-Up (geodetic lat/lon defines the local frame) |
| 46 | + const latRad = obsLatDeg * DEG2RAD; |
| 47 | + const lonRad = obsLonDeg * DEG2RAD; |
51 | 48 | const clat = Math.cos(latRad), slat = Math.sin(latRad); |
52 | 49 | const clon = Math.cos(lonRad), slon = Math.sin(lonRad); |
53 | 50 |
|
|
0 commit comments