MCPcopy
hub / github.com/cpaczek/skylight / predictAim

Function predictAim

tracker/src/pointing/predict.ts:131–201  ·  view source on GitHub ↗
(
  ac: Aircraft,
  site: GeoPoint,
  now: number,
  history: TrackHistory,
  params: PredictParams,
  mount: MountModel,
  limits: CameraLimits,
  currentPanTilt: PanTilt | null,
)

Source from the content-addressed store, hash-verified

129 * recompute slew from the resulting move, predict again.
130 */
131export function predictAim(
132 ac: Aircraft,
133 site: GeoPoint,
134 now: number,
135 history: TrackHistory,
136 params: PredictParams,
137 mount: MountModel,
138 limits: CameraLimits,
139 currentPanTilt: PanTilt | null,
140): Prediction | null {
141 // Denoised position when smoothing is on (kills ~1 Hz aim jitter from
142 // ADS-B position noise); raw fix otherwise.
143 const geo =
144 params.posSmoothing && params.posSmoothing > 0
145 ? history.smoothGeo(ac, now, 1 - params.posSmoothing * 0.85) ?? aircraftGeoPoint(ac)
146 : aircraftGeoPoint(ac);
147 if (!geo) return null;
148
149 const fixAgeSec = Math.max(0, (now - (ac.ts ?? now)) / 1000) + (ac.seen ?? 0);
150 const turnRateDps = history.turnRateDps(ac.hex);
151 const kin = {
152 lat: geo.lat,
153 lon: geo.lon,
154 altM: geo.altM,
155 gsKt: ac.gs,
156 trackDeg: ac.track,
157 vRateFpm: ac.baroRate,
158 turnRateDps,
159 };
160
161 const predictAt = (leadSec: number): AzEl =>
162 azElFromSite(site, predictGeo(kin, leadSec));
163
164 // The camera doesn't move the instant we command it — aim where the plane
165 // will be when the command BITES, not when it's sent. While continuously
166 // tracking, this is the term that converts a steady trailing error into a
167 // centered lock (the rate feedforward picks up the matching value because
168 // the analytic rate is evaluated at the same shifted epoch).
169 const motorLag = params.motorLatencySec ?? 0;
170
171 // Pass 1: assume a small slew.
172 let lead = fixAgeSec + params.adsbLatencySec + motorLag + 0.3;
173 let clamped = lead > params.maxLeadSec + fixAgeSec;
174 lead = Math.min(lead, params.maxLeadSec + fixAgeSec);
175 let azEl = predictAt(lead);
176
177 // Pass 2: refine with the actual slew time to the pass-1 aim point.
178 if (currentPanTilt) {
179 const goal = mountFromWorld(azEl.azDeg, azEl.elDeg, mount);
180 const slew = slewSeconds(currentPanTilt, goal, limits);
181 let refined = fixAgeSec + params.adsbLatencySec + motorLag + slew;
182 if (refined > params.maxLeadSec + fixAgeSec) {
183 refined = params.maxLeadSec + fixAgeSec;
184 clamped = true;
185 }
186 azEl = predictAt(refined);
187 lead = refined;
188 }

Callers 4

pipeline.test.tsFile · 0.85
planner.test.tsFile · 0.85
tickMethod · 0.85

Calls 7

aircraftGeoPointFunction · 0.85
predictAtFunction · 0.85
mountFromWorldFunction · 0.85
slewSecondsFunction · 0.85
norm180Function · 0.85
smoothGeoMethod · 0.80
turnRateDpsMethod · 0.80

Tested by

no test coverage detected