Spaces:
Configuration error
Configuration error
| // The shape of a satellite's sampled-position window, as arithmetic on plain | |
| // numbers. | |
| // | |
| // It lives here rather than inside SampledTrajectory because two things have to | |
| // agree on it exactly: the trajectory, which fills the window, and the | |
| // propagation prefetch, which computes the same samples ahead of time in a | |
| // worker. A prefetch whose sample times are half a step out of phase with the | |
| // window is not a cache, it is dead weight — so there is one function and both | |
| // callers use it. | |
| /** | |
| * Samples per revolution. 120 is a compromise between accuracy and both of the | |
| * costs it drives: propagation time, and the memory the samples occupy (see | |
| * SampledTrajectory). | |
| */ | |
| export const SAMPLES_PER_ORBIT = 120; | |
| /** Kept behind the satellite, as a fraction of one revolution. */ | |
| export const WINDOW_ORBITS_BACK = 0.5; | |
| /** Kept ahead of it. The Orbit component needs a full revolution of it. */ | |
| export const WINDOW_ORBITS_FORWARD = 1.5; | |
| export interface TrajectoryWindow { | |
| /** Seconds from the reference time to the first sample. Negative. */ | |
| offsetSeconds: number; | |
| stepSeconds: number; | |
| /** How many samples span the whole window, first and last inclusive. */ | |
| sampleCount: number; | |
| /** For the caller that wants an interval rather than a count. */ | |
| spanSeconds: number; | |
| } | |
| /** | |
| * The window for one satellite, from its orbital period in minutes. | |
| * | |
| * `sampleCount` is deliberately derived the way the filling loop counts rather | |
| * than by dividing the span: the loop steps from the start while | |
| * `stop >= time`, so it lands on the stop boundary and takes it, giving one more | |
| * sample than the number of steps. At 120 samples an orbit over two orbits that | |
| * is 241, not 240 — measured against the running app, which reported exactly 241 | |
| * samples per satellite. | |
| */ | |
| export function trajectoryWindow(orbitalPeriodMinutes: number): TrajectoryWindow { | |
| // A satrec that failed to parse reports no mean motion, and the period derived | |
| // from it is zero or infinite. Left alone that divides to NaN and reaches the | |
| // worker as a NaN-sized buffer, so it is answered with an empty window instead: | |
| // no samples to prefetch, and the caller propagates for itself as it would for | |
| // any other miss. | |
| if (!Number.isFinite(orbitalPeriodMinutes) || orbitalPeriodMinutes <= 0) { | |
| return { offsetSeconds: 0, stepSeconds: 0, sampleCount: 0, spanSeconds: 0 }; | |
| } | |
| const orbitalPeriodSeconds = orbitalPeriodMinutes * 60; | |
| const stepSeconds = orbitalPeriodSeconds / SAMPLES_PER_ORBIT; | |
| const spanSeconds = orbitalPeriodSeconds * (WINDOW_ORBITS_BACK + WINDOW_ORBITS_FORWARD); | |
| return { | |
| offsetSeconds: -orbitalPeriodSeconds * WINDOW_ORBITS_BACK, | |
| stepSeconds, | |
| sampleCount: Math.floor(spanSeconds / stepSeconds) + 1, | |
| spanSeconds, | |
| }; | |
| } | |