@aeroswarm/client 0.1.12 → 0.1.13

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 CHANGED
@@ -449,7 +449,41 @@ Shared live-track state; feed it from your subscription callbacks and read it im
449
449
  | `setStatus` / `setSourceStatus` / `setError` / `setDegraded` / `clear` | Connection metadata |
450
450
  | Selectors | `selectAllTracks`, `selectRenderableTracks`, `selectTrack(id)`, `selectTrackCount`, `selectStatus`, `selectIsDegraded`, `selectLastUpdateAt`, `selectError` |
451
451
 
452
- ### `useTrackHistory` — smooth map animation
452
+ ### `useTrackMotion` / `createTrackMotion` — smooth live tracks
453
+
454
+ Feeds update a track once a second (MLAT, ADS-B) or once per antenna sweep (radar, ~7.5 s). Drawn as they arrive, icons jump. `createTrackMotion` (no React needed) shows where the aircraft *probably is right now* and eases corrections in, like a flight tracker:
455
+
456
+ ```ts
457
+ import { createTrackMotion } from "@aeroswarm/client";
458
+
459
+ const motion = createTrackMotion(); // all options optional
460
+
461
+ client.tracks.subscribe(opts, {
462
+ onSnapshot: (tracks, meta) => tracks.forEach((t) => motion.update(t, meta)),
463
+ onTrackUpdate: (t, meta) => motion.update(t, meta), // meta.serverTime keeps the clocks aligned
464
+ onTrackRemoved: ({ trackId }) => motion.remove(trackId),
465
+ });
466
+
467
+ const stop = motion.start(({ changed }) => { // ~30 frames/s, only tracks that moved
468
+ for (const [id, s] of changed) layer.put(id, s.lat, s.lon, s.headingDeg);
469
+ });
470
+ ```
471
+
472
+ React: `const motion = useTrackMotion({ onFrame })`, then call `motion.update(...)` from the subscription. Nothing here renders React.
473
+
474
+ What it does:
475
+
476
+ | | |
477
+ |---|---|
478
+ | **Dead reckoning** | From the last fix, speed and heading carry the position forward, up to `maxExtrapolateMs` (20 s in the air, 8 s on the surface). `lost` tracks freeze. |
479
+ | **Eased corrections** | A new fix does not snap the icon. The gap between where it was shown and where the fix says it should be becomes an offset that fades out (smoothstep over `blendMs`, 1.5 s). The icon keeps moving at its own speed meanwhile, so there is no stop-and-go at every update. A correction larger than `snapDistanceM` (5 km) is a glitch and jumps. |
480
+ | **Heading** | Eases along the shortest arc (`headingSmoothMs`, 0.8 s); keeps the last heading when the track stops. |
481
+ | **Server clock** | `lastSeenAt` is the server's clock. `meta.serverTime` of each message lets the engine learn the offset to the browser clock (the estimate with the least network delay wins), so a wrong browser clock does not stretch or shrink the prediction. |
482
+ | **Cheap frames** | Tracks that stand still, or whose picture changed by less than `epsilonM` (0.5 m) / 0.5°, are not reported; no timer runs while nothing moves; `frameHz` caps the rate (30). |
483
+
484
+ Options (`MotionOptions`): `blendMs`, `headingSmoothMs`, `maxExtrapolateMs`, `minMovingKt` (5), `snapDistanceM`, `frameHz`, `epsilonM`, `extrapolate` (`false` shows raw positions, for comparing sources), `isSurface`, `clock` (inject the replay clock to play back at another speed), `scheduler` (tests). `motion.sample(id)` gives the shown position at any time without touching frame state, e.g. to attach a trail's head to the icon.
485
+
486
+ ### `useTrackHistory` — smooth map animation (deprecated: use `useTrackMotion`)
453
487
 
454
488
  Maintains per-track position interpolation (tweening), dead-reckoning between updates, and bounded trail history, emitting animation frames outside React render:
455
489