Skip to main content

Collar telemetry

Source: src/sim/herd.ts (generation), src/sim/types.ts (schema), src/sim/features.ts (feature derivation). Every cow emits the data stream a real collar reports — and the detection layer is only allowed to see this stream, never the simulation's internal state.

The pipeline at a glance​

Everything in the "on the collar" box is derived on-device; everything to the right is what this demo implements. The collar reports derived telemetry — a compact 5-minute summary — not raw sensor data, which is what keeps the data volume tiny.

The telemetry sample​

Every 5 sim-minutes each collar records a sample:

FieldTypeWhat it isHow a real collar gets it
ttimestampSample timeRTC / GPS time
x, ymetresPositionGNSS fix (duty-cycled to save power)
speedm/s5-minute average speedSuccessive GNSS fixes + accelerometer
behaviourenumgrazing / walking / resting / ruminatingOn-collar activity classifier over IMU data — head position and jaw/ear movement patterns
temperature°CBody temperatureContact thermistor (or ear-tag partner device)
rumination0–1Fraction of recent time ruminatingAccumulated from the activity classifier

As JSON (the demo's in-memory form — a real feed would pack this far smaller):

{
"t": 1834,
"x": 287.4, "y": 141.2,
"speed": 0.11,
"behaviour": "grazing",
"temperature": 38.7,
"rumination": 0.31
}

Compact by design​

Packed as binary, this is ~20 bytes per sample: position quantised to the paddock (2 × 2 bytes), speed (1), behaviour class (2 bits), temperature offset (1), rumination (1), timestamp delta (2), plus header. At one sample per 5 minutes that is ~6 KB per cow per day, because the collar sends derived telemetry, not raw sensor data. The heavy lifting — activity classification, fence logic — runs on-collar; only the compact summary and alerts leave the device.

How each signal behaves in the sim​

  • Speed — 5-minute EWMA of true movement. Grazing ≈ 0.12 m/s, walking ≈ 1 m/s, resting/ruminating ≈ 0. Each cow has a persistent ±15% pace factor, so healthy cows genuinely differ — the detector has to cope with that.
  • Behaviour — the state machine's current state, i.e. a perfect activity classifier. (A real classifier is ~90–95% accurate; adding label noise here would be a straightforward extension.)
  • Temperature — relaxes towards 38.6 °C + a ±0.25 °C diurnal cycle + condition offsets (illness adds +1.4 °C) + a herd-wide heat-stress term (weather). Fevers develop over ~1.5 h, as in practice.
  • Rumination — EWMA (~2 h) of time-in-rumination, shown as min/h in the UI. A healthy cow runs ~20 min/h averaged over the day. Illness collapses the underlying behaviour, so the reported rate decays over an hour or two — a leading indicator, and exactly the signal commercial rumination collars are sold on.

The ring buffer​

Each cow keeps 24 hours of samples (288). Everything downstream reads this buffer:

  • the sidebar's 24-hour sparklines (cow vs. herd mean),
  • the detector's 2-hour feature window (recent behaviour),
  • the detector's own-baseline window (2–24 h ago) — used to compute self-relative deltas, which separate "naturally slow cow" from "cow that just went slow" (see detection features).

Why synthetic telemetry is the right demo choice​

The generator and the detector are strictly separated: conditions are injected into the behaviour model, and the detector must recover them from the telemetry alone. That makes the demo falsifiable — inject a condition, watch whether and how fast the detector finds it — which is a far stronger portfolio claim than a dashboard drawn over canned data. The same separation is what made training a real model possible: the simulator generates unlimited labelled telemetry.