Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3631367
Spec 001: implementation plan for Speed Gauge
Flazzarious Sep 27, 2026
dcf919c
Spec 001: record feature branch in plan
Flazzarious Sep 27, 2026
4a89bf8
Document Gitflow branching in README and CLAUDE.md
Flazzarious Sep 27, 2026
0d55eaa
Spec 001: add visual design reference for the gauge
claude Sep 27, 2026
5d2c494
Target the DS-24 II screen for the gauge; add screen probe
claude Sep 27, 2026
e562e5b
Record DS-24 II window sizes; add full-screen gauge layout
claude Sep 27, 2026
6f3be4a
Record DS-24 II full-screen size (320x260)
claude Sep 27, 2026
ea3f1ce
Spec 001: point to local gauge reference image
claude Sep 27, 2026
a0dda5f
Spec 001: task list for Speed Gauge
Flazzarious Sep 27, 2026
2d7fc01
Merge branch 'feature/001-speed-gauge' of https://github.com/Flazzari…
Flazzarious Sep 27, 2026
8936ea6
Add emulator sensor config for Speed Gauge testing
claude Sep 27, 2026
92474d2
Emulator sensors send m/s; note sensor-function caching in plan
claude Sep 27, 2026
049b4a7
Spec 001: update plan and tasks for the gauge design reference
Flazzarious Sep 27, 2026
7a8cbbf
Speed Gauge 0.1.0: implement callouts, warnings, gauge and density
Flazzarious Sep 27, 2026
02d94fc
Speed Gauge: emulator fixes and gauge refinements
Flazzarious Sep 28, 2026
01d01ff
Speed Gauge: full-screen panel rework and spec catch-up
Flazzarious Sep 28, 2026
d060594
Speed Gauge: drawing performance and SC-007 revision
Flazzarious Sep 28, 2026
c7abb7f
Spec 001: one app voice (Piper "Amy") for all callouts
claude Sep 29, 2026
85389e6
Spec 001: limit elevation to -300..10,000 ft, temperature to -20..130 F
claude Sep 30, 2026
0dc88be
Spec 001: temperature from a selectable sensor (e.g. MSpeed)
claude Sep 30, 2026
e7b18cf
Spec 001: temperature sensor fallback/resume; show it on full screen
claude Sep 30, 2026
5747cc5
Enforce main-only-from-develop branch rule
Flazzarious Oct 2, 2026
81adc7a
Speed Gauge 0.2.0: temperature source, app voice, transmitter layouts
Flazzarious Oct 4, 2026
0b1a5b9
Speed Gauge: smooth dial rings, glow bands, emulator workaround
Flazzarious Oct 4, 2026
a281879
Speed Gauge: lighter emulator glow so full screen stays in budget
Flazzarious Oct 4, 2026
914bc11
Speed Gauge: image glow, arming speed, landing callouts toggle, 1.5x …
Flazzarious Oct 4, 2026
85c2aa4
Speed Gauge: commit the Amy voice set; stall limit, continuous cutoff
Flazzarious Oct 4, 2026
517f115
Speed Gauge: max-speed callout, 5 mph floor, longest-time range
Flazzarious Oct 5, 2026
8586dbd
Speed Gauge 0.3.0: release prep
Flazzarious Oct 5, 2026
0a60d5b
Merge develop into feature/001-speed-gauge
Flazzarious Oct 5, 2026
ad817ae
Speed Gauge 0.3.0: record test results; release candidate
Flazzarious Oct 5, 2026
f8fcd7f
Merge pull request #1 from Flazzarious/feature/001-speed-gauge
Flazzarious Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
27 changes: 27 additions & 0 deletions .github/workflows/main-pr-source.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Enforces the branch rule: main is only updated by a pull request from
# develop. The main ruleset requires this check, so a PR into main from any
# other branch (or from a fork) cannot be merged.
name: Main PR source

on:
pull_request:
branches: [main]

permissions: {}

jobs:
check-source:
name: PR source is develop
runs-on: ubuntu-latest
steps:
- name: Require develop as the source branch
env:
HEAD_REF: ${{ github.head_ref }}
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
BASE_REPO: ${{ github.repository }}
run: |
if [ "$HEAD_REF" != "develop" ] || [ "$HEAD_REPO" != "$BASE_REPO" ]; then
echo "::error::main only accepts pull requests from develop in this repository (got $HEAD_REPO:$HEAD_REF)."
exit 1
fi
echo "Source is develop. OK."
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ docs/vendor/
.DS_Store
Thumbs.db
*.swp

# Python bytecode and virtual environments from tools/
__pycache__/
*.pyc
.venv/
venv/

28 changes: 27 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ built spec-first with GitHub Spec Kit (`/speckit-specify`, `/speckit-plan`,

| App (menu name) | Script | Spec | Based on |
| --- | --- | --- | --- |
| Speed Gauge | `src/Apps/AG-SpdGa.lua` (planned) | `specs/001-speed-gauge/` | DFM Speed Announcer (Dave McQueeney, MIT) |
| Speed Gauge | `src/Apps/AG-SpdGa.lua` | `specs/001-speed-gauge/` | DFM Speed Announcer (Dave McQueeney, MIT) |

Keep this table current when an app is added.

Expand Down Expand Up @@ -76,6 +76,22 @@ src/Apps/lib/ag_xxxxx.lua shared module, loaded with require("ag_xxxxx")
- `loop()` runs every 20-30 ms: rate-limit with `system.getTimeCounter()`, and
don't format strings or build tables per call.

## Branches

- `main` is updated **only** by a pull request from `develop`. Never commit
or push to `main` directly, and never open a PR into `main` from any other
branch. GitHub enforces this (a ruleset on `main` plus the
`PR source is develop` check in `.github/workflows/main-pr-source.yml`).
- Feature work goes on `feature/NNN-name`, branched from `develop`, with the
same `NNN-name` as the spec folder, and is merged back into `develop` by
pull request. Never commit directly to `develop` either.
- When `develop` is ready to release, open a PR from `develop` into `main`.
After it merges, tag the release `<script>-v<version>` (e.g.
`AG-SpdGa-v0.3.0`).
- Spec Kit finds the feature via `.specify/feature.json`, not the branch
name, so the `feature/` prefix is fine.
- Ask before pushing, opening or merging pull requests, or tagging.

## Checking work

- `python tools/check.py` must pass (syntax, forbidden APIs, AG- naming, UTF-8,
Expand All @@ -86,6 +102,16 @@ src/Apps/lib/ag_xxxxx.lua shared module, loaded with require("ag_xxxxx")

## Environment

- **Target:** DS-24 II / DC-24 II only for anything drawn on screen. Design
for the Lua window sizes measured **on the transmitter**, not the panel's
480 × 480 or the emulator's: small 150 × 23, large 150 × 68, full screen
316 × 159, all visible, with the title drawn above the window. The JETI
Studio emulator (firmware 6.04) reports 157 × 60 / 157 × 127 / 320 × 260
with a title bar inside. Details, scaling and drawing limits are in
`docs/jeti-api-notes.md`; measure more with `tools/probe/PROBE.lua`. The
emulator plays no Lua audio, so voice can only be tested on the
transmitter.

- Windows, PowerShell. Spec Kit scripts are the PowerShell variants.
- The deploy target is `/Apps` on the transmitter's SD card; `src/Apps/` mirrors
it. The emulator's copy is `%LOCALAPPDATA%\JETI-Studio\Emulator\Apps`.
26 changes: 26 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ comes directly from that app:
Speed Gauge exists because that work made it possible. The original is kept
unmodified in
[`docs/examples/dfm-speed-announce/`](docs/examples/dfm-speed-announce/).
The WAV files Speed Gauge reuses carry their own credit note in
[`src/Apps/AG-SpdGa/CREDITS.txt`](src/Apps/AG-SpdGa/CREDITS.txt).

DFM Speed Announcer was itself inspired by Tero's Altitude Announcer
from [RC-Thoughts.com](https://www.rc-thoughts.com), whose style it followed.
Expand Down Expand Up @@ -54,6 +56,30 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

## Speed Gauge voice — Piper and the "Amy" voice

Speed Gauge's app voice (callouts, warnings and startup announcement) is a
set of WAV files generated with **Piper**, a local neural text-to-speech
engine, using the voice model **"Amy"** (`en_US-amy-medium`) by
**Mycroft / Rhasspy**.

- **Piper:** created as [rhasspy/piper](https://github.com/rhasspy/piper)
(MIT), now continued as the `piper-tts` package from
[OHF-Voice/piper1-gpl](https://github.com/OHF-Voice/piper1-gpl)
(GPL-3.0-or-later). It is only installed locally to run the generator;
no Piper code is part of this repository or of Speed Gauge.
- **Amy voice model:** [rhasspy/piper-voices](https://huggingface.co/rhasspy/piper-voices),
licensed Creative Commons Attribution-ShareAlike 4.0 International
([CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)).

The generated files are in this repository, in
[`src/Apps/AG-SpdGa/voice/`](src/Apps/AG-SpdGa/voice/), and are licensed
**CC BY-SA 4.0**, not MIT like the rest of the repository; that folder's
`CREDITS.txt` carries the attribution. The license of the recordings the
Amy model was trained on is undocumented. They were generated with
[`tools/voice/`](tools/voice/README.md), which can regenerate them (the
user chose to commit them, 2026-10-03; spec FR-035).

## JETI model official demos

[`docs/examples/jeti-demos/`](docs/examples/jeti-demos/) holds unmodified demos
Expand Down
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ spec-driven workflow (GitHub Spec Kit + Claude Code) in VS Code.

| App | Script | Status | Spec | Based on |
| --- | --- | --- | --- | --- |
| Speed Gauge | `AG-SpdGa.lua` | Spec written | [001](specs/001-speed-gauge/spec.md) | DFM Speed Announcer by Dave McQueeney (MIT) |
| Speed Gauge | [`AG-SpdGa.lua`](src/Apps/AG-SpdGa.lua) | Release candidate (0.3.0) | [001](specs/001-speed-gauge/spec.md) | DFM Speed Announcer by Dave McQueeney (MIT) |

Speed Gauge speaks in its own voice (Piper "Amy"), in
`src/Apps/AG-SpdGa/voice/`. Those files are licensed CC BY-SA 4.0, not MIT
(see [CREDITS.md](CREDITS.md)); [`tools/voice/`](tools/voice/README.md)
regenerates them. Without them the app works in the transmitter's voice.

Apps here are for telemetry, timers, announcements and displays only. Nothing
in this repo may control surfaces, throttle or any flight function. See the
Expand All @@ -31,6 +36,8 @@ src/Apps/ Mirrors /Apps on the transmitter SD card
lib/ag_xxxxx.lua Shared modules, require("ag_xxxxx")
tools/check.py Syntax, forbidden-API, naming, encoding checks
tools/pdf2md.py Converts Jeti's API PDF for local reference
tests/test_ag_dens.lua Optional desktop test for the density module (any Lua 5.3)
tools/probe/PROBE.lua Dev tool: prints the screen/window sizes in the emulator
```

### Multiple apps, one repo
Expand Down Expand Up @@ -84,6 +91,28 @@ tools/pdf2md.py Converts Jeti's API PDF for local reference
firmware/emulator version; `.lc` files are never committed.
6. First real run on a dedicated test model.

## Branching

| Branch | From | Merges into | Holds |
| --- | --- | --- | --- |
| `main` | | | Released versions only. Every commit is a release, tagged |
| `develop` | `main` | `main`, by pull request only | Finished features waiting for a release |
| `feature/NNN-name` | `develop` | `develop`, by pull request | One spec's work, e.g. `feature/001-speed-gauge` |

`main` is updated **only** by a pull request from `develop`; GitHub enforces
this (a ruleset on `main` and the `PR source is develop` check in
`.github/workflows/main-pr-source.yml`). Version bumps and last fixes happen
on the feature branch or on `develop` before that pull request; there are no
release or hotfix branches.

- A feature branch uses the same `NNN-name` as its `specs/` folder, and
carries that spec from `/speckit-specify` through `/speckit-implement`.
- Merge a feature into `develop` only after `tools/check.py` passes and the
quickstart's emulator scenarios have been run.
- Tag releases on `main` as `<script>-v<version>`, e.g. `AG-SpdGa-v1.0.0`,
matching the app's `version` field. Each app has its own version.
- Never commit directly to `main` or `develop`.

## Style reference: HELLO.lua

`docs/examples/style/HELLO.lua` is a small flight timer started by a
Expand Down
140 changes: 140 additions & 0 deletions docs/jeti-api-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,146 @@ Official PDF: https://github.com/JETImodel/Lua-Apps/tree/master/Doc
`mobdebug`, so it will not catch use of those.
- DC/DS-24 runs up to 10 apps per model. The hard 50 kB memory cap applies to
DC/DS-14/16, not the 24 series, but memory is still shared by all apps.
- Lua allows at most 200 local variables in one function, and the file's main
chunk counts as one: an app with ~200 file-level `local`s won't compile
("too many local variables"; LuaLS reports `local-limit`). Group related
constants and state into tables. Speed Gauge 0.3.0 is at 191 of 200.

## Target hardware (DS-24 II)

- **Screen:** JETI lists the DC/DS-24 II display as 4", 480 × 480 px, color,
with the JUi2 interface. The original DC/DS-24 was 320 × 240; the v1.5 API
document and most community apps assume that older screen.
- **Window sizes:** measured in the emulator with `tools/probe/PROBE.lua`
(firmware 6.04, 2026-09-27). They match the original DS-24's layout
(157 px ≈ half of a 320-px-wide screen), and full screen is 320 × 260,
not a 480-px canvas. On the II, full-screen sizes 3 and 4 give the same
area. Lua on the II
apparently keeps the older coordinate space, and the II may scale it up on
its larger display. Design with these numbers, not with 480 × 480.
- **Firmware:** the emulator in use runs 6.04; the transmitter runs 6.03 or
newer.
- **Device string:** `system.getDeviceType()` returns "JETI DS-24 II" in the
emulator (firmware 6.04, 2026-09-27). Not yet checked on the transmitter.

| Area | Size on DS-24 II (px) |
| --- | --- |
| Small telemetry window | 157 × 60 (measured) |
| Large telemetry window | 157 × 127 (measured) |
| Full screen, status bar kept (size 3) | 320 × 260 (measured) |
| Full screen (size 4+) | 320 × 260 (measured, same as size 3) |
| Size 0 ("auto") | pilot chooses: 157 × 60 or 157 × 127 (measured 2026-09-27, PROBE MODE 3) |
| App form canvas | *not yet measured* |

**What is actually visible** (emulator screenshots with the probe's ruler,
2026-09-27). On the II the desktop draws each window's title bar inside the
reported canvas, and the bottom of the canvas is clipped, so about 25 px of
the reported height never shows. Lua coordinates are scaled about 1.44x to
fill the 480-px panel (320 × 1.44 ≈ 460 px plus the frame).

| Window | Reported | Visible |
| --- | --- | --- |
| Small | 157 × 60 | 157 × ~34 |
| Large | 157 × 127 | 157 × ~101 |
| Full screen (size 3) | 320 × 260 | 320 × ~236 |

- **Font heights:** `FONT_NORMAL` 18, `FONT_BIG` 22, `FONT_MINI` 13,
`FONT_MAXI` 40 (probe, emulator 6.04).

**The real transmitter differs from the emulator** (DS-24 II, PROBE MODE 1,
transmitter screenshot `Screen001.png`, 2026-10-03). Design for these, not
the emulator's numbers above:

| Window | Emulator reports | Transmitter reports |
| --- | --- | --- |
| Small (size 1) | 157 × 60 (~34 visible) | **150 × 23**, all visible |
| Large (size 2) | 157 × 127 (~101 visible) | **150 × 68**, all visible |
| Full screen (size 3/4) | 320 × 260 (~236 visible) | **316 × 159**, all visible (PROBE MODE 2, `Screen004/005.png`) |

- The window title is drawn **above** the reported area, not inside it, so
nothing is hidden: don't subtract a title height on the transmitter.
- "Full screen" is not the whole panel on the transmitter: sizes 3 and 4
both get a titled 316 × 159 area across the top two thirds. With size 3
the desktop's model tile still covers its lower-left corner; size 4 has
no overlap.
- Widths tell the two apart: the emulator reports 157 / 320, the
transmitter 150 / 316.
- Font heights on the transmitter: `FONT_NORMAL` 17, `FONT_BIG` 20–21,
`FONT_MINI` 12, `FONT_MAXI` 38–39 (the two probes read 20/38 in the
small/large windows and 21/39 full screen).
- Lua coordinates are enlarged about 1.45× on the 480 × 480 panel (the
316 × 159 window covers 454 × 228 screen pixels). Lines are drawn at Lua
resolution and then enlarged, so curves built from 5° segments look
stepped.
- **Text is drawn at the panel's own resolution** (crisp, smooth), but
lines, polygons and renderer shapes are drawn at Lua resolution and
enlarged without smoothing: edges show hard stair-steps about 1.5 screen
px each. Adding points to a curve doesn't help. Renderer alpha does work
on the device (semi-transparent glow bands blend), so a wider translucent
pass under an edge can soften it.
- No app gets more than this area: DFM-InsP (MIT; studied, not copied) also
registers size-4 windows, clears `0, 0, 319, 158`, and ships its panel
images at 318 × 159.
- **`lcd.setClipping(x, y, w, h)` also moves the origin** to (x, y): after
it, `lcd.drawImage(0, 0, img)` puts the image's corner at the clip
rectangle's corner, not the window's. Draw at `(-x, -y)` to keep window
coordinates. Clipping itself works on images (transmitter, 2026-10-03,
`Screen017/018.png`). `lcd.resetClipping()` restores the window.
- **Verified on the transmitter with Speed Gauge 0.3.0 (2026-10-03/04):**
- `system.getDeviceType()` contains "24 II" (the gauge draws, not the
"needs DS-24 II" notice); the emulator returns "JETI DS-24 II".
- Size-0 windows let the pilot place them at single or double size.
- Mono 16-bit 22.05 kHz WAVs play (Speed Gauge's app voice).
- `°` renders in `lcd.drawText` ("°F" on the gauge panel).
- One `lcd.renderer()` reused across frames works.
- A temperature value's unit ends in "C" or "F" and is 2–3 bytes (the
MSpeed temperature appears in Speed Gauge's filtered sensor list).
- Worst single-call CPU with images for the dial and arcs: 14% at speed,
full screen.
- The transmitter can save screenshots to the SD card root
(`Screen001.png`, 480 × 480): the quickest way to check a layout.
- **Model tile:** the desktop's model tile (model name, image, page "n/3")
stays in the bottom-left slot on every desktop page and is drawn **over** a
full-screen Lua window, covering its lower-left 157 × ~110. No Lua API hides
it; whether a desktop setting or size 4 avoids it is still being checked.

## CPU figure and off-screen images (emulator, 2026-09-27)

- **The "CPU" figure in Applications → User Applications is the highest
per-call budget use seen, not a running average.** `system.getCPU()`
returns how much of the current call's instruction/time budget has been
used (0–100); at 100 the transmitter kills the script. The list keeps the
maximum, so it stays high after one heavy call (start-up, or a heavy
window draw) until the app restarts.
- To see live per-call numbers, call `system.getCPU()` at the end of `loop()`
and of the print function and log them (Speed Gauge was measured this way:
init 24, loop 0–1, small/double draw 12–18, full-screen draw 27–43).
- Cost follows semi-transparent anti-aliased drawing (renderer polylines with
alpha, e.g. glow bands) far more than plain fills: removing a full-window
`drawFilledRectangle` and the filled face polygon changed nothing, while
removing 16 alpha glow bands halved the figure.
- **Off-screen images didn't work:** `lcd.createImage(w, h)` plus
`lcd.renderer(image.data)` and `lcd.drawImage` produced nothing visible,
and the figure did not drop. Don't rely on off-screen rendering.

## Emulator telemetry (LeonAirRC's Emulated Telemetry)

- Installed in the emulator's Apps folder as `emutelem.lua` with its config in
`Apps/EmulatedTelemetry/sensors.json` (this repo's copy:
`tools/emulator/sensors.json`). Add "Emulated Telemetry" in Applications →
User Applications alongside the app under test.
- It replaces `system.getSensors`, `getSensorByID` and `getSensorValueByID`,
so apps must call them through `system` each time, never a saved copy.
- Each sensor's value follows its control (`input`), mapped from −1..1 to
`lowerBound..upperBound`. **The control fully down (exactly −1) makes the
sensor invalid**, i.e. "sensor lost".
- It also replaces `system.playFile`, `playNumber`, `playBeep`,
`playSystemSound` and `vibration` with `print`: in the emulator, audio and
vibration appear as lines in the Lua console instead of sound.
- **The emulator plays no Lua audio even without that app** (JETI Studio,
firmware 6.04, observed 2026-10-03: `playFile` with the telemetry app
removed was silent). Anything that must be heard, such as WAV formats and
the pause between queued files, can only be checked on the transmitter.

## Files and names

Expand Down
Loading
Loading