Commit Graph

294 Commits

Author SHA1 Message Date
itsrubberduck
22f50aa403 feat(atis): clock-locked ATIS loop tied to tuned frequency
Real ATIS broadcasts run continuously; tuning in mid-broadcast should
drop the pilot into the current spoken position, then loop. The frontend
now generates the full announcement once via TTS, plays it as a looping
HTMLAudioElement, and seeks to ((Date.now() - lastUpdated) / duration)
on metadata-load so all clients tuned to the same ATIS hear it phase-
synced. The loop starts/stops automatically with frequency tuning and
restarts on info-letter change. say.post.ts now caches tag=atis like
tag=flightlab to avoid re-synthesizing identical announcements.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 14:43:48 +02:00
itsrubberduck
4ac8c1104a Make callsign STT matching far more tolerant
Whisper routinely garbles the airline portion of a callsign while
nailing the flight number ("Loftansa three five niner", "Speed bird
27", "Lufthana 359"). Previously these slipped past the matcher.

Two changes:

1) Whole-string fuzzy distance for callsigns bumped to allowedDistance
   + 3 (was +1), which covers ~1–2 character substitutions in the
   airline name.

2) New `callsignMatches()` splits each candidate into its alphabetic
   airline prefix and trailing digit run and matches each part
   independently:
   - The digits (e.g. "359") are the strong anchor and must appear.
   - The airline portion is matched both verbatim and with whitespace
     stripped ("Speed bird" → "speedbird"), with a generous ~25%
     character-distance allowance.
   - Bare flight number without any airline trigger does NOT match —
     verified by a dedicated false-positive test.

7 new test cases cover the realistic Whisper error modes (misspell,
split words, ICAO letter readout, reordered words, telephony glue).
All 69 tests green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 21:26:54 +02:00
itsrubberduck
60c53c73e4 Make classroom STT production-ready
Last pass fixed the crashes but the UX wasn't trustworthy — you'd hit
the mic, something happened, fields silently changed, and there was no
clear way to see what Whisper actually heard or which fields it touched.
This rebuilds that flow:

UI
- Dedicated transcription panel below the controls row replaces the
  single-line "Heard:" hint. Has explicit states: recording (red,
  pulsing dot, live MM:SS timer), transcribing (spinner), result
  (editable textarea + summary chip), or error (red body text).
- Mic button label shows the elapsed recording time so the pilot knows
  recording is actually running.
- Per-field mic icon appears on every blank that was filled by the
  current transcription, so it's obvious what came from speech vs.
  what was typed.
- Result panel exposes three explicit actions: Apply to fields (re-runs
  the mapping after edits), Record again, Dismiss.
- Hard auto-stop at 45s (well under the server's 2 MB / ~60s cap).
- 503/unreachable responses from the PTT endpoint now flip
  `sttServerAvailable` so the mic button gracefully hides itself.

Matching reliability (shared/utils/sttMatch.ts)
- Process fields longest-expected-first so a 6-char callsign claims its
  substring before a 1-char digit field grabs an overlapping character.
- Short candidates (<3 chars) now require a whole-word boundary match,
  so the digit "5" in callsign "359" no longer auto-fills an unrelated
  readability field.
- Two new test cases cover both false-positive guards.

62 / 62 tests green, vue-tsc clean, dev server starts and serves the
classroom page without TDZ / hydration warnings in the log.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 21:24:41 +02:00
itsrubberduck
a12da5e15c Fix STT readback — TDZ crash, spoken-form mismatch, hydration
Three bugs in yesterday's STT addition:

1) **TDZ crash** — `sttSupported` referenced `isClient` before its const
   declaration, throwing on setup and breaking the whole classroom page.
   `sttSupported` is now a ref that's populated in `onMounted`.

2) **Spoken vs. written mismatch** — Whisper returns natural ATC speech
   ("runway two five right", "lufthansa three five niner"), but the
   lesson fields hold the canonical written form ("25R", "DLH359"). The
   old `normalized.includes(...)` check never matched. Matching now lives
   in `shared/utils/sttMatch.ts` and searches both the raw normalized
   transcription *and* a denormalized projection that folds spoken
   digits/letters back to written tokens (incl. SID suffix `7S`, runway
   `25R`, scale words `five thousand → 5000`, frequency `decimal` as a
   digit-run boundary).

3) **SSR hydration mismatch** — `sttSupported` evaluated differently on
   server vs. client, causing visible-vs-hidden button divergence on
   hydration. The ref-set-on-mount approach resolves it.

The new helper is fully unit-tested (15 cases covering radio check,
departure clearance, SIDs, squawks, Speedbird telephony, decimal
frequencies and edge cases).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 21:18:39 +02:00
itsrubberduck
bc23afc040 Add STT readback input to classroom
Detlef's original "Rückmeldungen sollten gesprochen werden können"
request was about speaking *into* the readback rather than typing every
field — this wires that up using the existing /api/atc/ptt Whisper
endpoint.

- Mic button in the readback control row records via MediaRecorder,
  posts base64 audio to /api/atc/ptt, then maps the transcription onto
  the lesson's input fields via normalized substring matching against
  each field's expected value plus alternatives.
- Callsign-flagged fields additionally allow a sliding-window
  Levenshtein match because Whisper routinely garbles airline names.
- The transcription is shown back to the user so they can verify what
  was heard before pressing Check.
- The button is hidden when the Speaches speech server is configured
  but unreachable — a small "Speech server unavailable" hint takes its
  place. Browsers without MediaRecorder/getUserMedia never see it.
- Recording state animates the button; transcription state shows a
  spinner. Stops cleanly on Reset and on component unmount.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 10:58:38 +02:00
itsrubberduck
d16ce90386 Group readback label and field so they wrap together
Detlef reported that the prompt for a blank (e.g. "runway") often ended
up on the line above the input itself, forcing him to look back up to
remember what he was typing.

Each preceding text segment is now paired with its following input field
into a `.cloze-group`. The group uses `inline-flex; flex-wrap: nowrap`
so its label and input stay on one line, while the outer `.cloze`
container still wraps groups normally. On narrow viewports (<640px) the
group falls back to wrapping internally so it never overflows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 10:37:33 +02:00
itsrubberduck
3e560b3e23 Address classroom tester feedback (Detlef / FSC e.V.)
Fixes the most impactful issues from ~1.5h of testing:

- TTS: spell SID prefixes phonetically (ANEKI → Alfa November Echo Kilo
  India) so unfamiliar waypoint names are intelligible without prior
  briefing context.
- TTS: expand standalone uppercase waypoints after via/direct (with a
  skip list for common ATC English tokens like MAYDAY, CLEARED, …).
- TTS: join taxi route tokens with ", " so pauses land between
  taxiways (C5, Z5, U10, …) instead of running together.
- TTS: handle "ILS Z 25C" variant before the runway → "ILS Zulu runway
  two five center" (was previously read as "Zee twentyfive cee").
- Scenarios: derive arrivalRunway from the chosen approach so the
  controller no longer clears a flight for ILS 25C onto runway 18.
- Radio check: accept any readability 1–5 (numeric or spoken), shorten
  placeholder so it fits the sm-width field.
- Line-up readback: clearer hint about the runway-first ICAO order.
- Classroom UI: disable browser autocomplete/autocorrect on readback
  inputs (Edge autofill was injecting unrelated values).
- Classroom UI: "Speak answer" button replays the expected readback as
  TTS so students can hear the correct phrasing.

Tests adjusted for the new SID and taxi-route phonetics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 10:24:48 +02:00
itsrubberduck
d2c8348bf7 Refine PM frequency overview cards 2026-05-21 10:35:44 +02:00
itsrubberduck
282887192f Simplify PM frequency panel 2026-05-21 10:30:23 +02:00
itsrubberduck
fc6956d5ae Unify PM topbar radio controls 2026-05-21 10:24:29 +02:00
itsrubberduck
408544641a Translate PM tab labels 2026-05-21 10:22:06 +02:00
itsrubberduck
d08e76d5a7 Polish PM radio settings copy 2026-05-21 10:20:44 +02:00
itsrubberduck
fe5d7d46b8 intercom rausgefactored und ui improvements 2026-05-21 10:18:25 +02:00
itsrubberduck
db825d63e0 neues pm ui 2026-05-21 10:10:53 +02:00
leubeem
d50e0e67b3 fix: correct OpenAIP v2 airport frequency parsing (3 bugs)
The frequency panel was always empty when using OpenAIP because of three
compounding bugs in frequencies.get.ts:

1. Wrong query parameter — ?icao=EDDF performs an unfiltered full-text
   search and returns all 46 000+ airports paginated; EDDF wasn't even
   on the first page.  The correct parameter is ?search=EDDF, which
   returns exactly the one matching airport.

2. Wrong ICAO field name — the code checked airport.icao but the real
   field in the v2 API response is icaoCode.  Even on a correctly
   filtered response the match would always fail.

3. Wrong frequency field names and numeric types — each frequency item
   exposes the MHz value in a value field (not frequency / frequencyMHz),
   and the service type is a numeric code (5=Delivery, 9=Ground,
   14=Tower, 15=ATIS) rather than a string.  Added OPENAIP_TYPE_MAP to
   translate these numeric codes to the internal DEL/GND/TWR/ATIS codes
   the rest of the pipeline expects.

With these fixes EDDF now returns all 8 frequencies (Delivery 122.035,
Ground 121.805, three Tower variants, two ATIS) which are then stored in
delivery_freq / ground_freq / tower_freq / atis_freq and used for both
the frequency overview panel and the per-state frequency validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 18:16:32 +02:00
leubeem
62721ced4c feat: live expected communication from backend, radio pronunciation toggle, and frequency validation
- Expected Communication card now driven entirely by backend responses:
  controller speech from controller_say_rendered, expected pilot phrase
  from expected_pilot_template — both update after every state transition
  instead of relying on the static COMMUNICATION_STEPS array
- Initial expected pilot phrase seeded on session creation via the new
  expected_pilot_template field in CreateSessionResponse, so the card
  shows the correct text before any transmission
- Radio pronunciation toggle (mdi-radio / mdi-text) on the card applies
  normalizeRadioPhrase() (ICAO alphabet, wun/too/tree, callsign expansion)
  to both ATC speech and expected pilot phrase
- Frequency validation at transmit time: if the pilot's active frequency
  does not match the state's expected frequency (resolved from
  frequency_name → flight-plan variable) a canned ATC reply is played
  and the backend is not called
- Flight-plan variables (callsign, squawk, destination, ATIS, SID, stand,
  initial altitude) now passed to createSession() as variable_overrides
  so backend sessions use real flight-plan data from the first state
- Backend session variables synced into local vars after each response
  so frontend and backend stay consistent
- Removed hardcoded frequency chip from Expected Communication card

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 17:51:44 +02:00
leubeem
c9f51f757f feat: wire Python backend session to PTT, add pm.vue debug logger
- Pass  as top-level field in PTT requests so Whisper STT
  results are linked to the correct Python backend session
- Add namespaced  helper in pm.vue (info/warn/error/debug/group)
  controlled by localStorage PM_DEBUG flag; logs transmit/response
  cycles, TTS calls, flag/variable syncs, and fallback warnings
- Log backend session creation context (flow, start state, vars, flags)
  in startMonitoring
- Fix typo in text input hint: STT fails not PTT fails

and

fix: sync backend variables to frontend after each transmission

The ATC say template was rendered using the frontend engine's local
variable defaults (squawk '1234', hardcoded SID, etc.) instead of
the authoritative values from the Python backend session. This caused
the spoken clearance and the readback prompt to show different squawk
codes.

- After each backend transmission response, sync all response.variables
  into vars.value (same pattern already used for flags)
- Prefer controller_say_rendered (pre-rendered by backend) over the raw
  template for TTS scheduling, eliminating any remaining dependency on
  local variable state for the ATC speech text
2026-05-20 16:44:01 +02:00
leubeem
76ab765069 Cleanup old unused code and add id sessionId to /api/atc/ptt 2026-05-20 14:13:26 +02:00
itsrubberduck
dc45de6c94 copilot 2026-05-19 12:46:31 +02:00
itsrubberduck
2f96102879 copilot ui improvements 2026-05-10 17:15:41 +02:00
Emanuel Leube
063b327090 Merge pull request #263 from OpenSquawk/feature/pm-python-backend-integration
Wire /pm to Python backend for stateful ATC training sessions
2026-05-09 17:50:38 +02:00
leubeem
e807e483ce Wire /pm to Python backend for stateful ATC training sessions
Replace the LLM-per-request flow in /pm with a stateful Python backend
(OpenSquawk-LiveATC-api). The backend owns session state, does regex-first
routing with readback evaluation, and returns the next state + ATC speech.
The frontend keeps its local cursor (communicationsEngine) for TTS and
monitoring UI, but no longer calls /api/llm/decide.

Changes:

app/composables/useRadioBackend.ts (new)
  Typed Nuxt composable wrapping the Python REST API:
  createSession, transmit, deleteSession, fetchFlows.
  Base URL read from NUXT_PUBLIC_RADIO_BACKEND_URL (default 127.0.0.1:8000).

nuxt.config.ts
  Expose radioBackendUrl as a public runtime config key so the composable
  and communicationsEngine can both reach the Python backend.

shared/utils/communicationsEngine.ts
  - fetchRuntimeTree now accepts an optional baseUrl so it fetches from the
    Python backend instead of the Nuxt server when a URL is provided.
  - renderTpl handles both {var} (old MongoDB schema) and {{var}} (new YAML
    schema) — double-brace matched first to avoid partial matches.
  - stateSayTpl / stateUtteranceTpl helpers unify say_tpl|say_template and
    utterance_tpl|expected_pilot_template across both schema versions.
  - auto_transitions from the new YAML schema are included when collecting
    eligible transitions in collectAtcStatesUntilPilotTurn.

shared/types/decision.ts
  RuntimeDecisionState extended with say_template and expected_pilot_template
  fields (new YAML schema field names alongside the existing legacy names).

app/pages/pm.vue
  - startMonitoring: loads tree from Python backend, then creates a backend
    session (backendSessionId). Cursor synced to session.current_state.
  - handlePilotTransmission: calls radioBackend.transmit instead of
    /api/llm/decide. Applies auto_advanced_states via moveToSilent, then
    the final state. Speaks controller_say_template via TTS.
  - Both fetchRuntimeTree calls now pass radioBackendUrl so they hit the
    Python backend, not the Nuxt flow-from-MongoDB path.

AGENTS.md (new)
  Project guide updated to document the new two-backend architecture,
  the Python backend session lifecycle, and the dual template schema.

docs/plans/2026-05-06-pm-python-runtime-contract.md (new)
  Implementation plan and API contract written before the work started.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 17:49:28 +02:00
itsrubberduck
d309b712f1 Wochenreport 2026-05-06 17:38:36 +02:00
itsrubberduck
fadc5a9cb3 fix popover 2026-05-06 16:08:45 +02:00
itsrubberduck
b6ca2091a0 komplette 320 sops 2026-05-06 15:51:58 +02:00
itsrubberduck
971fff388c simbrief gefixt 2026-05-06 15:43:05 +02:00
itsrubberduck
50fe1e34ca final fixes for copilot 2026-05-06 15:37:50 +02:00
itsrubberduck
2bacfdaa02 sehr gute design fixes 2026-05-06 15:35:38 +02:00
itsrubberduck
8a660ae628 ui fixes 2026-05-06 15:28:55 +02:00
itsrubberduck
fbbea8dc6c bessers ui aber mit fehlern 2026-05-06 14:57:25 +02:00
itsrubberduck
fb00e1f58f clenaeres design verbesserte views 2026-04-26 09:59:34 +02:00
itsrubberduck
7a863f0678 redeisng to match osq 2026-04-25 20:52:25 +02:00
itsrubberduck
d3e6787836 first version looks ok 2026-04-25 20:29:09 +02:00
itsrubberduck
125dca28c5 fix: refine classroom tts popups 2026-04-23 10:20:06 +02:00
itsrubberduck
5a09f3820e feat: warn when classroom speech server is unavailable 2026-04-23 09:21:23 +02:00
itsrubberduck
837c1ef623 feat: update classroom feedback round 2 content 2026-02-27 13:03:04 +01:00
itsrubberduck
46c39fe40b feat(flightlab): add moving cloud field for speed heading and altitude cues 2026-02-21 00:13:47 +01:00
itsrubberduck
5dae46cea5 feat(learn-pfd): add pitch-based speed hold step and target speed zones 2026-02-21 00:08:16 +01:00
itsrubberduck
63babb3788 feat(flightlab): add custom airplane glb asset 2026-02-21 00:01:01 +01:00
itsrubberduck
cee840b52e einfache verison 2026-02-20 23:47:48 +01:00
itsrubberduck
6cf5a8f39e feat(types): add LearnPfdPhase, PFD element types, and stick-input WS event
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:29:01 +01:00
itsrubberduck
6dab26403c chore: add three.js dependency for 3D aircraft model
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:28:32 +01:00
itsrubberduck
7862f1ffd4 Bridge dome light mode mapping and flightlab seat belt flow 2026-02-18 22:43:56 +01:00
itsrubberduck
36ad4677dc add the lidl dome light temporarily 2026-02-18 21:49:03 +01:00
itsrubberduck
6b96b295e1 change default settings 2026-02-18 18:05:12 +01:00
itsrubberduck
ea9cc476bb besser error message 2026-02-17 19:10:50 +01:00
itsrubberduck
db7b147e9c test: expand coverage for core backend features 2026-02-17 18:36:13 +01:00
itsrubberduck
d35930565e smoketests 2026-02-17 18:26:20 +01:00
itsrubberduck
bf4ce3abdf typescript 2026-02-17 18:19:55 +01:00
itsrubberduck
4a01593612 fix typescript errors and update dependencies 2026-02-17 18:13:04 +01:00