Measurement Pipeline¶
Components¶
flowchart LR
UI[React UI] <-->|WebSocket| S[Flask server]
S --> RB[RollingBufferMonitor]
RB --> OPS[OPS243Radar]
RB --> ST[SoundTrigger<br/>SEN-14262 → HOST_INT]
S --> TI[IWR6843Runtime]
S --> KLD[KLD7Tracker<br/>deprecated]
S --> BAL[Ballistics simulator]
S --> SIM[Sim connectors]
S --> CS[Cloud sync]
S --> SL[SessionLogger]
| Module | Responsibility |
|---|---|
ops243.py |
OPS243 driver, rolling-buffer capture, I/Q transfer |
rolling_buffer/processor.py |
FFT, mode-based speed extraction, spin detection |
rolling_buffer/ |
Trigger strategies |
iwr6843/ |
TI driver, L3 raw dump parser, LCMF-v1 angle and club path |
launch_monitor.py |
Shot dataclass, ClubType, carry estimation |
ballistics.py |
RK4 trajectory with drag and Magnus |
club_data.py |
Canonical club physics parameters |
inclinometer.py |
LIS3DH tilt compensation |
sim/ |
Simulator connectors and network transports |
cloud/ |
Telemetry, config, session upload |
server.py |
Flask server, AppState, staged shot processing |
session_logger.py |
JSONL session logs |
The sequence¶
-
Impact. The SEN-14262 detects the strike and drives its
GATEoutput high. -
Trigger, twice. That single edge goes to the OPS243's
HOST_INTpin (hardware, ~10 µs) and to the Pi's BCM17. There is no software in the OPS path — that is what makes the capture window reliable. -
OPS dump. The radar freezes its rolling buffer and returns 4,096 I and 4,096 Q samples — 40,556 bytes on the wire.
-
IWR dump. The Pi asks the IWR6843 firmware to finish its current frame and dump the rolling frame ring.
-
Speed extraction.
RollingBufferProcessorruns a 128-sample FFT zero-padded to 4,096 across the capture, building a timeline. Overlapping regions separate club, impact, and ball; the club region is the pre-impact window, the ball region is post-impact. -
Angle extraction. LCMF-v1 processes the IWR6843 radar cube for vertical launch angle, horizontal launch direction, and club path from pre-impact frames.
-
Correlation. The two captures are matched on the OPS impact timestamp.
-
Carry. The ballistic simulator integrates the trajectory.
-
Emit. The Flask server sends a
shotWebSocket event to the UI, forwards to any connected simulator, and appends to the session log.
Why the trigger is hardware¶
A software trigger would have to poll or interrupt, then command the radar to freeze — tens of milliseconds at best. At 150 mph a golf ball travels about 2.7 metres in 40 ms, which is well outside the useful capture window.
Wiring GATE directly to HOST_INT removes software from the decision. The
cost is the one-time flash-persist step: the
OPS243 firmware changes HOST_INT behaviour when rolling-buffer mode is entered
at runtime, so the mode has to already be in flash at power-on.
Timing constraints¶
| Quantity | Value |
|---|---|
| Sample rate | 30,000 samples/s |
| Buffer | 4,096 I + 4,096 Q samples (~136 ms) |
| Dump size | 40,556 bytes |
| Dump time at 230,400 baud | ~1.8 s |
| Dump time at 19,200 baud (factory default) | ~21 s — every capture truncated |
The baud rate is why the UART migration negotiates up to 230,400 rather than accepting the factory default.