mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-03 09:39:38 +00:00
The reconciliation body of applyManeuverClassificationsToWindTrack was designed for the graph path: NewSpotsUpdate feeds it a whole-race view produced by IncrementalMstManeuverGraphGenerator.parseGraph, which aggregates all competitors' spots into a single MST/HMM graph and returns wind fixes derived from all of them together. Reconciling the estimator's wind track down to that whole-race view is correct because each successive graph invocation contains everyone's contributions. PreClassifiedUpdate, in contrast, is fired *per competitor* by TrackedRaceImpl.feedAlreadyKnownManeuversToWindEstimation (for the DB-load path). Each call carries only one competitor's maneuvers, so the resulting newWindTrack is a per-competitor slice. Sending that slice through the full reconciliation would remove every other competitor's fixes on every call. The last competitor to arrive would win and the estimated wind track would end up with just their fixes. Fix: introduce an additive-only helper, addManeuverClassificationsToWindTrack, that inserts any missing wind fixes for the current batch and does not remove any pre-existing ones. PreClassifiedUpdate now uses this helper. NewSpotsUpdate continues to use the full reconciliation via applyManeuverClassificationsToWindTrack. Additive semantics are correct for the DB-load path because the union of the per-competitor wind fixes across all successive PreClassifiedUpdate invocations is exactly the intended track content. The DB-loaded maneuvers are stable inputs -- there is no need for the estimator to consolidate them via cross-competitor aggregation the way the graph path does for live detection, because the persistent cache already contains the results of a prior consolidation. As with the reconciling helper, the mutation of the internal map happens under the wind track's write lock and the corresponding trackedRace.recordWind notifications are fired outside the lock.