()
| 339 | } |
| 340 | |
| 341 | private tick(): void { |
| 342 | const now = Date.now(); |
| 343 | const cfg = this.cfg(); |
| 344 | const d = this.driver(); |
| 345 | const pose = d.getPose(); |
| 346 | // Dead-reckoned current pose: position replies stall around drives, so |
| 347 | // the raw pose can lag by seconds mid-pursuit — closing the loop on it |
| 348 | // injects phase lag that reads as trailing/hunting. All error terms and |
| 349 | // slew estimates use this; the raw pose is kept for state/recording. |
| 350 | const poseEst = d.getPoseEstimate() ?? pose; |
| 351 | |
| 352 | let prediction: Prediction | null = null; |
| 353 | let targetAc: Aircraft | undefined; |
| 354 | let commandedPanTilt: PanTilt | null = null; |
| 355 | let hfovDeg: number | null = null; |
| 356 | let angular: number | null = null; |
| 357 | |
| 358 | // Target selection runs in auto mode, or pinned in manual-target mode. |
| 359 | const tracking = |
| 360 | this.mode === "auto" || (this.mode === "manual" && this.manualHex != null); |
| 361 | |
| 362 | const selection = selectTarget( |
| 363 | this.upstream.getAircraft(), |
| 364 | cfg.site, |
| 365 | now, |
| 366 | this.current, |
| 367 | cfg.targetMode, |
| 368 | cfg.target, |
| 369 | ); |
| 370 | |
| 371 | if (tracking) { |
| 372 | const hex = this.manualHex ?? selection.hex; |
| 373 | if (hex !== this.current?.hex) { |
| 374 | this.current = hex ? { hex, sinceMs: now } : null; |
| 375 | this.resetSetpoint(); |
| 376 | this.recorder.write("target", { hex }); |
| 377 | } |
| 378 | targetAc = hex ? this.upstream.find(hex) : undefined; |
| 379 | // A manually-pinned target may be outside the candidate filter — allow it. |
| 380 | if (this.manualHex && !targetAc) this.current = null; |
| 381 | } |
| 382 | |
| 383 | if (tracking && targetAc) { |
| 384 | prediction = predictAim( |
| 385 | targetAc, |
| 386 | cfg.site, |
| 387 | now, |
| 388 | this.history, |
| 389 | cfg.predict, |
| 390 | cfg.mount, |
| 391 | cfg.limits, |
| 392 | poseEst ? { panDeg: poseEst.panDeg, tiltDeg: poseEst.tiltDeg } : null, |
| 393 | ); |
| 394 | } |
| 395 | |
| 396 | if (prediction && targetAc) { |
| 397 | // Trajectory plan: detects near-zenith passes where chasing azimuth |
| 398 | // would exceed the pan rate — pre-rotates to the exit side instead. |
no test coverage detected