From 36313670f86b142b32dba342f480410fa5e1cc84 Mon Sep 17 00:00:00 2001 From: Aaron George Date: Sun, 27 Sep 2026 12:15:38 -0600 Subject: [PATCH 01/29] Spec 001: implementation plan for Speed Gauge Add plan, research, data model, contracts (settings form, telemetry window, lib modules, audio) and quickstart validation guide. Sensor speed is taken as m/s, as in DFM Speed Announcer v2.1. Co-Authored-By: Claude Opus 5.5 --- .../001-speed-gauge/contracts/audio-events.md | 23 ++ .../001-speed-gauge/contracts/lib-modules.md | 39 ++++ .../contracts/settings-form.md | 65 ++++++ .../contracts/telemetry-window.md | 68 ++++++ specs/001-speed-gauge/data-model.md | 127 ++++++++++ specs/001-speed-gauge/plan.md | 147 ++++++++++++ specs/001-speed-gauge/quickstart.md | 120 ++++++++++ specs/001-speed-gauge/research.md | 216 ++++++++++++++++++ 8 files changed, 805 insertions(+) create mode 100644 specs/001-speed-gauge/contracts/audio-events.md create mode 100644 specs/001-speed-gauge/contracts/lib-modules.md create mode 100644 specs/001-speed-gauge/contracts/settings-form.md create mode 100644 specs/001-speed-gauge/contracts/telemetry-window.md create mode 100644 specs/001-speed-gauge/data-model.md create mode 100644 specs/001-speed-gauge/plan.md create mode 100644 specs/001-speed-gauge/quickstart.md create mode 100644 specs/001-speed-gauge/research.md diff --git a/specs/001-speed-gauge/contracts/audio-events.md b/specs/001-speed-gauge/contracts/audio-events.md new file mode 100644 index 0000000..f03c1ec --- /dev/null +++ b/specs/001-speed-gauge/contracts/audio-events.md @@ -0,0 +1,23 @@ +# Contract: Audio and vibration + +Assets are copied unmodified from `docs/examples/dfm-speed-announce/DFM-SpdA/` +to `src/Apps/AG-SpdGa/`, with a `CREDITS.txt` next to them naming DFM +(Dave McQueeney) and the MIT license (FR-029, constitution VIII). + +| Event | Condition (data-model.md) | Output | +| --- | --- | --- | +| Startup | `init()`, `startAnn = 1`, `cal ≠ 100` | `playFile("/Apps/AG-SpdGa/airspeed_cal_factor.wav", AUDIO_QUEUE)`, `playNumber(cal, 0, "%")` | +| Startup | `init()`, `startAnn = 1` | `playFile(".../stall_speed_warning_at.wav", AUDIO_QUEUE)`, `playNumber(vStall, 0, unitSpoken)` | +| Airspeed alive | first `everAboveHalf` with a switch on | `playFile(".../airspeed_alive.wav", AUDIO_IMMEDIATE)` | +| Stall | stall armed → fired | `playFile(".../stall_warning.wav", AUDIO_IMMEDIATE)`, `vibration(true, 4)` | +| Overspeed | overspeed armed → fired | `playFile(".../overspeed.wav", AUDIO_IMMEDIATE)`, `vibration(true, 3)` | +| Callout, full | callout due, not short form | `playNumber(round(shownSpd), 0, unitSpoken, "Speed")` | +| Callout, short | callout due, short form | `playNumber(round(shownSpd), 0)` | + +- `unitSpoken` uses v2.1's strings, which match the voice pack's + `numbers.jsn`: `mph`, `km/h`, `kt.`, `m/s`, `ft./s`. +- Callouts are skipped while `system.isPlayback()` is true (FR-007); warnings + use `AUDIO_IMMEDIATE` and are not delayed, as in v2.1. +- Vibration profiles 3 and 4 are v2.1's. The API doc names them "two short" + and "three short"; v2.1's comments say 2 and 4 pulses. Keep the profile + numbers; don't rely on either description. diff --git a/specs/001-speed-gauge/contracts/lib-modules.md b/specs/001-speed-gauge/contracts/lib-modules.md new file mode 100644 index 0000000..048736c --- /dev/null +++ b/specs/001-speed-gauge/contracts/lib-modules.md @@ -0,0 +1,39 @@ +# Contract: Shared modules + +Both modules are new; Speed Gauge is their only user. Per constitution VII +they return a table of functions, hold no mutable module-level state, register +nothing, and follow principles II–VI. Constant lookup tables are allowed. +Any later change must list and re-verify every app that `require`s them. + +## `ag_dens` (`src/Apps/lib/ag_dens.lua`) + +Pure math. Safe to call anywhere; the app calls it only from `init()` and +form callbacks. + +| Function | Returns | Notes | +| --- | --- | --- | +| `factor(elevM, tempC)` | number k ≥ ~0.9 | True airspeed = sensor speed · k. `tempC` nil → standard temperature. Formula in research R1 | +| `stdTempC(elevM)` | number °C | 15 − 0.0065 · elevM | +| `ftToM(ft)` / `mToFt(m)` | number | 0.3048 | +| `fToC(f)` / `cToF(c)` | number | | + +Reference values (must hold within 0.001): `factor(0, nil)` = 1.0000, +`factor(1524, nil)` = 1.0773, `factor(1524, 35)` = 1.1337, +`factor(4572, 50)` = 1.4097. + +## `ag_gauge` (`src/Apps/lib/ag_gauge.lua`) + +Drawing helpers. The draw functions use `lcd` and must only be called from a +registered print function. + +| Function | Returns / effect | +| --- | --- | +| `newDial(steps, startDeg, sweepDeg)` | Table `{n, cx[], sy[]}` of unit cos/sin for `steps + 1` points; defaults 54, 225, 270. Owned by the caller. Call once in `init()` | +| `arc(r, dial, cx, cy, radius, f, width)` | Draws an arc from 0 to fraction `f` (clamped 0..1) with renderer `r` (reset first). Nothing drawn if `f <= 0` | +| `tick(dial, cx, cy, r1, r2, f)` | Radial line at fraction `f` between radii `r1` and `r2` | +| `needle(dial, cx, cy, radius, f)` | Line from center to `f` at `radius` | +| `point(dial, f)` | `cos, sin` at fraction `f`, interpolated. Used by the others; public for labels | + +Colors are set by the caller with `lcd.setColor` before each call. The +renderer `r` comes from the caller (`lcd.renderer()`), so reuse is the app's +decision (research R6). diff --git a/specs/001-speed-gauge/contracts/settings-form.md b/specs/001-speed-gauge/contracts/settings-form.md new file mode 100644 index 0000000..afbeee3 --- /dev/null +++ b/specs/001-speed-gauge/contracts/settings-form.md @@ -0,0 +1,65 @@ +# Contract: Settings form + +Registered as `system.registerForm(1, MENU_APPS, "Speed Gauge", initForm, +keyForm)`. One form, no subforms except a `form.reinit()` redraw after a +units change. Rows appear in this order (FR-025, US5 #1). Label width 180–220 +px as in v2.1; hints are `FONT_MINI` rows. + +| # | Row | Component | Notes | +| --- | --- | --- | --- | +| | **Sensor and switches** | label, `FONT_BOLD` | | +| 1 | Speed sensor | selectbox (dialog) | "(none)", sensors, optional "