From 6bc67a1cc67e937c3660484daefb69a96947bba3 Mon Sep 17 00:00:00 2001 From: Patrick Burns Date: Mon, 13 Jul 2026 14:34:48 -0500 Subject: [PATCH 1/5] iOS parity: pre-stage settings fields for logging, highlights, tune Adds SettingsState fields + persistence for Cloudlog/QRZ-logbook/PSK-Reporter, decode highlight toggles, continent filter, distance unit, and tune timeout, ahead of the feature wiring. Co-Authored-By: Claude Fable 5 --- .claude/tasks/session-current.md | 52 +++++++++++++++++++++++++ ios/FT8AF/FT8AF/AppState.swift | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 .claude/tasks/session-current.md diff --git a/.claude/tasks/session-current.md b/.claude/tasks/session-current.md new file mode 100644 index 00000000..3001c5f4 --- /dev/null +++ b/.claude/tasks/session-current.md @@ -0,0 +1,52 @@ +# Session — iOS parity with Android (features + style) + +## Session Overview + +**User Request**: "go ahead and get the ios app in parity in both style and features as the android app" +**Workflow Mode**: Development +**Success Criteria**: iOS app matches Android feature set and visual style for everything feasible without rig hardware; app builds for simulator; `swift test` green; every new code path unit-tested; PR to `dev`. +**Branch**: `feat/ios-parity` (worktree `/Users/patrickburns/Projects/FT8AF-ios-parity`) + +## Strategic Analysis + +**Complexity**: Complex, multi-phase. +**Research**: Full Android + iOS inventories completed (Explore agents). Key gaps: + +- Settings persisted but inert on iOS: spectrum width, decode filters (show-only-CQ, DX-only), TX watchdog / stop-after-N, auto-sequence toggles, PTT/TX delay + late-start tolerance, TX power. +- Missing operating features: caller queue, ActiveQsoPanel TX-message selector chips + LOG/clear actions + header states, TUNE button, CQ options (free text / modifiers), waterfall UTC period timestamps, input-level indicator, DecodeRow status pills / distance / ago / location styling. +- Missing integrations: Cloudlog upload, QRZ logbook upload, PSKReporter sender, POTA real spot feed + activation QSO counting. +- Style: iOS uses SF fonts (Android: Inter + Geist Mono, bundled in `ft8af/app/src/main/res/font/`), app icon missing (placeholder Contents.json), color palette already matches Android dark palette hex-for-hex. + +**Deferred (out of scope this PR, documented in PR body)**: rig CAT transport (no serial on iOS; BLE bridge is its own project — ALC/SWR/tune-via-ATU depend on it), FT4/FT2 mode switching, Hound/Field Day modes, GPS grid + time discipline, needed-DX local notifications, light theme, localization catalogs (16 languages), CarPlay, PSK Reporter map overlay, POTA OAuth self-spot/upload. + +**Build facts**: XcodeGen (`ios/FT8AF/project.yml`, sources glob → run `xcodegen generate` after adding files). Baseline green: app builds (iPhone 16 Pro sim), 129 kit tests pass. Fonts require Info.plist `UIAppFonts` → use project.yml `info:` properties. + +## Task Breakdown + +### Phase 0: Shared plumbing (Central AI) +- [x] Worktree + session file +- [ ] Pre-stage `SettingsState` fields + persistence for all new settings (Cloudlog, QRZ key, PSKReporter, distance unit, etc.) so parallel agents don't collide on AppState.swift + +### Batch 1 (parallel, disjoint files) +- [ ] **Agent D — Logging integrations**: Cloudlog + QRZ logbook upload + PSKReporter sender (pure request-builders in FT8AFKit/FT8Engine + tests; thin app service; LoggingSettings screen; sync chips + catch-up sync in Logbook) +- [ ] **Agent W — Waterfall parity**: spectrum width wired end-to-end, UTC timestamps at period boundaries, input-level indicator (kit geometry + tests; owns LiveEngine waterfall loop + Waterfall screen files) + +### Batch 2 (parallel, disjoint files) +- [ ] **Agent B — Engine/TX parity**: watchdog, stop-after-N, auto-seq toggles wired, late-start clip rule `max(0, msIntoCycle - slack)`, TUNE carrier, caller queue, stage-selector plumbing; ActiveQsoPanel + TxStrip UI upgrades (owns LiveEngine, QsoEngine, Components/) +- [ ] **Agent R — Decode UI parity**: DecodeRow Android-style (accent bar, pills, SNR bar, distance, ago, location), filters wired (show-only-CQ, DX-only, continent), highlights vs logbook (new grid/band/worked, DXCC prefix heuristic) — extracted pure logic + tests (owns Screens/Decode, Util/) + +### Batch 3 (parallel) +- [ ] **Agent E — POTA real data**: live spots from api.pota.app, activation QSO count wired to log, per-park ADIF export +- [ ] **Agent S — Style**: bundle Inter + Geist Mono, typography adoption (UI=Inter, data=Geist Mono), app icon from play_store_icon.png, project.yml info properties + +### Phase 4: Verification & ship (Central AI) +- [ ] `swift test` (kit) green; xcodebuild simulator build green; app smoke-run in simulator +- [ ] Code review pass; commit; PR to `dev` + +## Agent Work Sections + +(appended by agents as they complete) + +## Session Metrics + +Tasks total: 10 · Completed: 1 diff --git a/ios/FT8AF/FT8AF/AppState.swift b/ios/FT8AF/FT8AF/AppState.swift index 3809c341..08e82ac8 100644 --- a/ios/FT8AF/FT8AF/AppState.swift +++ b/ios/FT8AF/FT8AF/AppState.swift @@ -199,6 +199,23 @@ final class SettingsState { var udpHost: String = "127.0.0.1" var udpPort: Int = 2237 var udpAcceptRequests: Bool = false + // Online logging (Cloudlog/Wavelog family + QRZ.com logbook + PSK Reporter) + var cloudlogEnabled: Bool = false + var cloudlogUrl: String = "" + var cloudlogApiKey: String = "" + var cloudlogStationId: String = "" + var qrzLogbookEnabled: Bool = false + var qrzLogbookApiKey: String = "" + var pskReporterEnabled: Bool = false + // Decode highlights & filters + var highlightNewDxcc: Bool = true + var highlightNewGrid: Bool = true + var highlightNewBand: Bool = true + var highlightWorked: Bool = true + var continentFilter: String = "All" // All / NA / SA / EU / AF / AS / OC / AN + var distanceInMiles: Bool = false + // Tune + var tuneTimeoutSec: Int = 30 } // MARK: - Rig @@ -289,6 +306,20 @@ enum SettingsPersistence { d.set(s.udpHost, forKey: key("udpHost")) d.set(s.udpPort, forKey: key("udpPort")) d.set(s.udpAcceptRequests, forKey: key("udpAcceptRequests")) + d.set(s.cloudlogEnabled, forKey: key("cloudlogEnabled")) + d.set(s.cloudlogUrl, forKey: key("cloudlogUrl")) + d.set(s.cloudlogApiKey, forKey: key("cloudlogApiKey")) + d.set(s.cloudlogStationId, forKey: key("cloudlogStationId")) + d.set(s.qrzLogbookEnabled, forKey: key("qrzLogbookEnabled")) + d.set(s.qrzLogbookApiKey, forKey: key("qrzLogbookApiKey")) + d.set(s.pskReporterEnabled, forKey: key("pskReporterEnabled")) + d.set(s.highlightNewDxcc, forKey: key("highlightNewDxcc")) + d.set(s.highlightNewGrid, forKey: key("highlightNewGrid")) + d.set(s.highlightNewBand, forKey: key("highlightNewBand")) + d.set(s.highlightWorked, forKey: key("highlightWorked")) + d.set(s.continentFilter, forKey: key("continentFilter")) + d.set(s.distanceInMiles, forKey: key("distanceInMiles")) + d.set(s.tuneTimeoutSec, forKey: key("tuneTimeoutSec")) } @MainActor static func load(into s: SettingsState) { @@ -362,6 +393,40 @@ enum SettingsPersistence { if d.object(forKey: key("udpAcceptRequests")) != nil { s.udpAcceptRequests = d.bool(forKey: key("udpAcceptRequests")) } + if d.object(forKey: key("cloudlogEnabled")) != nil { + s.cloudlogEnabled = d.bool(forKey: key("cloudlogEnabled")) + } + if let v = d.string(forKey: key("cloudlogUrl")) { s.cloudlogUrl = v } + if let v = d.string(forKey: key("cloudlogApiKey")) { s.cloudlogApiKey = v } + if let v = d.string(forKey: key("cloudlogStationId")) { s.cloudlogStationId = v } + if d.object(forKey: key("qrzLogbookEnabled")) != nil { + s.qrzLogbookEnabled = d.bool(forKey: key("qrzLogbookEnabled")) + } + if let v = d.string(forKey: key("qrzLogbookApiKey")) { s.qrzLogbookApiKey = v } + if d.object(forKey: key("pskReporterEnabled")) != nil { + s.pskReporterEnabled = d.bool(forKey: key("pskReporterEnabled")) + } + if d.object(forKey: key("highlightNewDxcc")) != nil { + s.highlightNewDxcc = d.bool(forKey: key("highlightNewDxcc")) + } + if d.object(forKey: key("highlightNewGrid")) != nil { + s.highlightNewGrid = d.bool(forKey: key("highlightNewGrid")) + } + if d.object(forKey: key("highlightNewBand")) != nil { + s.highlightNewBand = d.bool(forKey: key("highlightNewBand")) + } + if d.object(forKey: key("highlightWorked")) != nil { + s.highlightWorked = d.bool(forKey: key("highlightWorked")) + } + if let v = d.string(forKey: key("continentFilter")), !v.isEmpty { + s.continentFilter = v + } + if d.object(forKey: key("distanceInMiles")) != nil { + s.distanceInMiles = d.bool(forKey: key("distanceInMiles")) + } + if d.object(forKey: key("tuneTimeoutSec")) != nil { + s.tuneTimeoutSec = d.integer(forKey: key("tuneTimeoutSec")) + } } private static func key(_ name: String) -> String { prefix + name } From ffde6588f8dd0f0cbf8f4ccac977b926c08f99cc Mon Sep 17 00:00:00 2001 From: Patrick Burns Date: Mon, 13 Jul 2026 14:52:47 -0500 Subject: [PATCH 2/5] iOS parity batch 1: online logging + waterfall parity - Cloudlog/Wavelog and QRZ.com logbook upload (pure request builders in FT8Engine + OnlineLogService, upload-on-log, catch-up sync, sync chips, LoggingSettings screen, test-connection actions) - PSK Reporter reception reports (IPFIX datagram port of the Android sender, 5-min per-call/band dedup, UDP flush) - QsoRecord syncedCloudlog/syncedQrz flags with legacy-JSON back-compat - Spectrum width wired end-to-end (parametric WaterfallAxis/RowBuilder, live application, ruler math mirroring 647b12e8) - UTC period timestamps drawn on the waterfall (WaterfallTimestampGate port) - RX input level meter (AudioInputLevel port, info-bar meter) - App icon (1024 opaque from play_store_icon.png) Kit tests 209/209; simulator build green. Co-Authored-By: Claude Fable 5 --- .claude/tasks/session-current.md | 56 +++ ios/FT8AF/FT8AF.xcodeproj/project.pbxproj | 10 +- ios/FT8AF/FT8AF/AppState.swift | 15 + .../AppIcon.appiconset/AppIcon.png | Bin 0 -> 13883 bytes .../AppIcon.appiconset/Contents.json | 1 + ios/FT8AF/FT8AF/Engine/LiveEngine.swift | 93 ++++- ios/FT8AF/FT8AF/Engine/OnlineLogService.swift | 271 +++++++++++++++ .../FT8AF/Screens/Logbook/LogbookScreen.swift | 83 ++++- .../Screens/Settings/LoggingSettings.swift | 180 ++++++++++ .../Screens/Settings/SettingsScreen.swift | 28 ++ .../Screens/Waterfall/FrequencyRuler.swift | 19 +- .../Screens/Waterfall/SpectrumStrip.swift | 22 +- .../Screens/Waterfall/WaterfallCanvas.swift | 53 ++- .../Screens/Waterfall/WaterfallScreen.swift | 70 ++++ .../Sources/FT8Audio/AudioInputLevel.swift | 119 +++++++ .../Sources/FT8Audio/WaterfallAxis.swift | 121 +++++-- .../FT8Audio/WaterfallRowBuilder.swift | 11 +- .../FT8Audio/WaterfallTimestampGate.swift | 83 +++++ .../Sources/FT8Engine/CloudlogClient.swift | 88 +++++ .../Sources/FT8Engine/OnlineLogAdif.swift | 34 ++ .../Sources/FT8Engine/OnlineLogSync.swift | 26 ++ .../Sources/FT8Engine/PskReporter.swift | 327 ++++++++++++++++++ .../Sources/FT8Engine/QrzLogbookClient.swift | 73 ++++ .../Sources/FT8Engine/QsoRecord.swift | 37 +- .../FT8AudioTests/AudioInputLevelTests.swift | 108 ++++++ .../FT8AudioTests/WaterfallAxisTests.swift | 192 +++++++--- .../WaterfallTimestampGateTests.swift | 104 ++++++ .../FT8EngineTests/CloudlogClientTests.swift | 112 ++++++ .../FT8EngineTests/OnlineLogSyncTests.swift | 119 +++++++ .../FT8EngineTests/PskReporterTests.swift | 233 +++++++++++++ .../QrzLogbookClientTests.swift | 79 +++++ 31 files changed, 2652 insertions(+), 115 deletions(-) create mode 100644 ios/FT8AF/FT8AF/Assets.xcassets/AppIcon.appiconset/AppIcon.png create mode 100644 ios/FT8AF/FT8AF/Engine/OnlineLogService.swift create mode 100644 ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift create mode 100644 ios/FT8AFKit/Sources/FT8Audio/AudioInputLevel.swift create mode 100644 ios/FT8AFKit/Sources/FT8Audio/WaterfallTimestampGate.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/CloudlogClient.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/OnlineLogAdif.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/OnlineLogSync.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/PskReporter.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/QrzLogbookClient.swift create mode 100644 ios/FT8AFKit/Tests/FT8AudioTests/AudioInputLevelTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8AudioTests/WaterfallTimestampGateTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/CloudlogClientTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/OnlineLogSyncTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/PskReporterTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/QrzLogbookClientTests.swift diff --git a/.claude/tasks/session-current.md b/.claude/tasks/session-current.md index 3001c5f4..3e1ed599 100644 --- a/.claude/tasks/session-current.md +++ b/.claude/tasks/session-current.md @@ -47,6 +47,62 @@ (appended by agents as they complete) +### Agent W — Waterfall parity + +**Status: COMPLETE** — kit tests green (209/209), simulator build green. + +**1. Configurable spectrum width, end-to-end (display-only; decoder untouched)** +- `FT8Audio/WaterfallAxis.swift`: fully parametric — every mapping (`fraction`, `clampedFraction`, `hz(forFraction:)`, `tunedTxHz`) now takes `displayMaxHz`. New `maxTxHz(displayMaxHz:)` implements the Android TX clamp `100...min(3000, width-100)` (per task directive "mirror Android"; note this lowers the old 200 Hz floor to Android's 100 — flag if the coordinator wants 200 kept). New `rulerTicks(displayMaxHz:)` + `RulerTick` mirror Android commit 647b12e8 (labels at true `hz/width` fractions, top tick below the far edge for non-500-multiples, degenerate width → single 0 tick). +- `FT8Audio/WaterfallRowBuilder.swift`: `maxHz` → `defaultMaxHz` (3500); `columns(sampleRate:maxHz:)` takes the width (defaulted, so existing callers/tests unchanged). +- `LiveEngine.runWaterfallLoop` reads `settings.spectrumWidthHz` on the main actor **every tick** (new `readSpectrumWidthHz` closure) → live application; column count recomputed per tick. On a width change the `applyWaterfall` closure clears `rows`/`rowTimestamps` (old-width history can't be drawn against the new axis) and updates `WaterfallState.displayMaxHz`. +- Views (`FrequencyRuler`, `SpectrumStrip`, `WaterfallCanvas`) map overlays/tap-to-tune against `Float(appState.settings.spectrumWidthHz)` so the setting applies instantly. + +**2. UTC timestamps at FT8 period boundaries** +- New `FT8Audio/WaterfallTimestampGate.swift`: pure port of Android's `WaterfallTimestampGate` — `shouldDraw(utcMs:slotMs:)` once-per-slot gate (incl. mode-change re-baseline rule), `slotPeriod`, `slotStartMs`, and `utcLabel(forUtcMs:)` ("HH:mm:ss", day-independent, Euclidean-safe). +- Waterfall loop stamps the **period-start** label on the first row of each 15 s slot; `WaterfallState.rowTimestamps: [String?]` kept in lockstep with `rows` (append/trim/clear together). +- `WaterfallCanvas.drawPeriodTimestamps`: subtle full-width divider (`textFaint` 40 %, 0.5 pt) + HH:mm:ss monospaced 9 pt `textFaint` label at the left edge. + +**3. Input level indicator** +- New `FT8Audio/AudioInputLevel.swift`: pure port of Android's `AudioInputLevel` classifier — identical thresholds (clip ≥0.985 linear, high ≥-3 dBFS peak, silent <-75 dBFS RMS, low <-45 dBFS RMS, -120 dBFS floor, NaN/∞ sanitized), `measure(_:)`, `fromPeakRms(peak:rms:)`, `meterFraction(_:)`. +- Waterfall loop meters the most recent ~250 ms window each tick → `WaterfallState.inputPeak/.inputRms` (plain Floats — AppState doesn't import FT8Audio). +- `WaterfallScreen`: new `InputLevelMeter` in the bottom info bar — "RX" label, 44 pt bar (RMS fill + peak tick) and status word; colors silent/low→`textFaint`, good→`statusConfirmed`, high→`statusWarn`, clipping→`statusBad`. + +**Files touched** (all within ownership): `ios/FT8AFKit/Sources/FT8Audio/{WaterfallAxis,WaterfallRowBuilder,WaterfallTimestampGate*,AudioInputLevel*}.swift`, `ios/FT8AFKit/Tests/FT8AudioTests/{WaterfallAxisTests,WaterfallTimestampGateTests*,AudioInputLevelTests*}.swift` (* = new), `ios/FT8AF/FT8AF/Engine/LiveEngine.swift` (waterfall loop + closures + new `WaterfallUpdate` struct only), `ios/FT8AF/FT8AF/Screens/Waterfall/*` (all four), `AppState.swift` (WaterfallState body only: `rowTimestamps`, `displayMaxHz`, `inputPeak`, `inputRms`). `AudioCaptureService.swift` unchanged (metering lives in the waterfall loop). + +**Tests**: `WaterfallAxisTests` (rewritten, 17 tests — width-parametric mapping, TX clamp per width, ruler ticks incl. 2750 non-multiple + degenerate cases), `WaterfallTimestampGateTests` (11 — once-per-slot over polled frames, reset, mode-change, label format, period-start-not-frame-time), `AudioInputLevelTests` (13 — all five statuses, threshold edges, NaN sanitizing, meter fraction). `swift test`: 209/209 pass. + +**Coordinator notes**: +- Ran `xcodegen generate` to get a green app build — Agent D's new `LoggingSettings.swift`/`OnlineLogService.swift` existed on disk but weren't in the project yet (build failed on their `SettingsScreen` reference, not my files). Harmless for them; mentioning so nobody is surprised the pbxproj changed. +- TX tune floor changed 200→100 Hz to mirror Android's `100..width-100` clamp per the task brief; trivial to revert to 200 if desired (single constant `WaterfallAxis.minTxHz` + 2 test expectations). + ## Session Metrics Tasks total: 10 · Completed: 1 + +### Agent D — Logging integrations + +**Status: COMPLETE** — kit tests 209/209 green (`swift test`), simulator build green (`xcodebuild` iPhone 16 Pro), `xcodegen generate` run after adding app files. Not committed (coordinator commits). + +**What was built** (parity with Android `ThirdPartyService`, `PskReporterSender`, `ui/settings/LoggingSettings`): + +Kit — pure, network-free protocol builders in `ios/FT8AFKit/Sources/FT8Engine/`: +- `CloudlogClient.swift` — `api/qso` POST request builder (JSON key order byte-identical to Android's JSONStringer; endpoint slash-less to dodge Wavelog/Nextlog 308s), `api/auth/` test URL, whitespace-tolerant `Valid` + `rw` parser, 200/201 success rule. +- `QrzLogbookClient.swift` — logbook.qrz.com form bodies (INSERT/STATUS) with Java-URLEncoder-compatible encoding, `RESULT=` parser, OK/REPLACE=success for INSERT, OK-only for STATUS. +- `PskReporter.swift` — full IPFIX byte-layout port of PskReporterSender: 16-byte header, options template 0x50E2 (5 enterprise-30351 var fields), data template 0x50E3 (6 enterprise fields + standard flowStartSeconds 0x0096), receiver/sender data sets, var-string encoding, 1400-byte MTU splitting, templates on first 3 packets + hourly refresh; `PskDedup` (5-min per call|bandMHz window); `PskReporter.makeSpot` policy (skip self/`<...>`/free-text i3=0&n3=0, strip hash brackets, dial+audio-offset RF freq, ≥4-char locator). Time injected → fully deterministic tests. +- `OnlineLogAdif.swift` — `Adif.singleRecord()`: Android QSLRecordToADIF field order, no ``/QSL flags, freq passed through as MHz. +- `OnlineLogSync.swift` — unsynced-record selection mirroring Android `unsyncedFilter`/`countUnsyncedQSOs`. +- `QsoRecord.swift` — added `syncedCloudlog`/`syncedQrz` Bools; custom `init(from:)` with `decodeIfPresent` defaults so pre-existing `qso_log.json` files still decode (proven by test); memberwise init defaults keep QsoEngine source-compatible. + +Kit tests (45 new) in `Tests/FT8EngineTests/`: `CloudlogClientTests`, `QrzLogbookClientTests`, `PskReporterTests` (golden-byte template/record assertions, MTU split, template cadence, dedup, spot policy), `OnlineLogSyncTests` (incl. legacy-JSON backward-compat decode + single-record ADIF goldens). + +App — `ios/FT8AF/FT8AF/`: +- `Engine/OnlineLogService.swift` — `@Observable @MainActor` singleton; URLSession transport (upload on QSO logged, retry-once, marks synced flags + persists; catch-up `syncAll` with `isSyncing/syncDone/syncTotal` progress; Cloudlog/QRZ test-connection); PSK Reporter NWConnection UDP to report.pskreporter.info:4739 with ~5-min batched flush. +- `Screens/Settings/LoggingSettings.swift` — new screen: Cloudlog (URL/API key/Station ID/enable/Test Connection with spinner + Pass/Fail + toast), QRZ Logbook (key/enable/Test), PSK Reporter toggle; persists via SettingsPersistence. +- `SettingsScreen.swift` — one nav row "Online Logging" (+ enabled-services summary) added to the existing Logging section. +- `Screens/Logbook/LogbookScreen.swift` — CL/QRZ sync chips on rows (signal cyan / confirmed green when synced, shown only when the service is enabled), toolbar cloud-upload button running catch-up sync with progress counter + completion toast. +- `Engine/LiveEngine.swift` — ONLY the two permitted touch points: decode-completion hands decodes to `OnlineLogService.shared.enqueuePskDecodes` (dial = band map + audio offset), QSO-completion calls `handleQsoLogged` after `broadcastUdpQso`. + +**Coordinator notes:** +- AppState.swift untouched (pre-staged fields used as-is). QsoLogStore untouched — Codable back-compat handled inside QsoRecord. +- PSK spots queued while capture runs are flushed by the service's own 5-min timer even if the engine stops; `flushPsk` is also callable on demand if a stop-flush is ever wanted. +- QRZ REPLACE responses count as success (record already on QRZ), matching Android. diff --git a/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj b/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj index 0c1c5cf0..a666c35a 100644 --- a/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj +++ b/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 637BC14B19B728028FD01F07 /* FrequencyRuler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81C53609B2CDC9FB3B71116 /* FrequencyRuler.swift */; }; 65A688B883398E75D00BCD7E /* LogbookStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91DB096C5FC7481CA1B7B49B /* LogbookStats.swift */; }; 666D9271E9FE64C20C29CF47 /* FT8Audio in Frameworks */ = {isa = PBXBuildFile; productRef = 8A825C5161C267D978D895B8 /* FT8Audio */; }; + 6F7F961AB6678F3E6E60A97A /* LoggingSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB0565117E35A6C64BBF519D /* LoggingSettings.swift */; }; 72300AB5D2B37428777C824D /* MapScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE0C13DF4842EC394EFADF /* MapScreen.swift */; }; 771EC057824C1F3698C6DC9B /* FFTProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F287F167508A8E4114F44116 /* FFTProcessor.swift */; }; 7F9F3D670DE6755252D023A3 /* TransmissionSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B08B1AA6951B99CEFAA521F /* TransmissionSettings.swift */; }; @@ -33,6 +34,7 @@ 80F7FFDDEE3AEE3E706C4A2B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 076A186C88F9B5A2FFAAB073 /* Assets.xcassets */; }; 8DA0D562E25925F56B638B00 /* GridDistance.swift in Sources */ = {isa = PBXBuildFile; fileRef = E429D8A3556534F05374F12A /* GridDistance.swift */; }; 8EA75A450404FB948C0A6CDC /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504C9AD4C6667267DFCABEF9 /* SettingsScreen.swift */; }; + 8EF984B5816F012A2DF93FDF /* OnlineLogService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D131B05E27A96EC4BEB28782 /* OnlineLogService.swift */; }; 9F4A1DE79693EF68A7B1A4D2 /* LogbookAwards.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CCA018AEEE169EDCA338FD /* LogbookAwards.swift */; }; A037B00B861024FA2CA4919F /* QsoPathMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0187218903BBFF71931246 /* QsoPathMap.swift */; }; A5C16922F13DEDBDF69DFCF6 /* FT8AFTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = A41EFDE500F865A3B393171D /* FT8AFTab.swift */; }; @@ -96,6 +98,7 @@ AC8DAEDCAE1AD9DE02EB1480 /* QsoCelebration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QsoCelebration.swift; sourceTree = ""; }; B35E490F93FDCACEC4FBF66F /* PotaScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PotaScreen.swift; sourceTree = ""; }; B681B14FC4FED15C628471DF /* WaterfallCanvas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterfallCanvas.swift; sourceTree = ""; }; + BB0565117E35A6C64BBF519D /* LoggingSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingSettings.swift; sourceTree = ""; }; BD0187218903BBFF71931246 /* QsoPathMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QsoPathMap.swift; sourceTree = ""; }; BF21F5D95598018B8A46ED8D /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; C42FB097015693FF14B27553 /* AudioCaptureService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioCaptureService.swift; sourceTree = ""; }; @@ -103,6 +106,7 @@ C81C53609B2CDC9FB3B71116 /* FrequencyRuler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrequencyRuler.swift; sourceTree = ""; }; C96D9C69CFC1480E6A5B64B5 /* SpectrumStrip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpectrumStrip.swift; sourceTree = ""; }; CB943F6B81F9D36BA85504A3 /* AzimuthalMapCanvas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AzimuthalMapCanvas.swift; sourceTree = ""; }; + D131B05E27A96EC4BEB28782 /* OnlineLogService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnlineLogService.swift; sourceTree = ""; }; DE47CF6272CAE2614E6A2383 /* AdvancedSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettings.swift; sourceTree = ""; }; DF04207CDB5BE8A1CCDD5F5B /* TransmitGlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransmitGlow.swift; sourceTree = ""; }; E429D8A3556534F05374F12A /* GridDistance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridDistance.swift; sourceTree = ""; }; @@ -168,6 +172,7 @@ DE47CF6272CAE2614E6A2383 /* AdvancedSettings.swift */, 4A8FFDE0644205A9AE050BC0 /* BlockedCallsignsSettings.swift */, A88688AC6AFBEDC8AC3EF844 /* DecodeFilterSettings.swift */, + BB0565117E35A6C64BBF519D /* LoggingSettings.swift */, 89642B9763C7FA33CFF0EE57 /* RadioAudioSettings.swift */, 504C9AD4C6667267DFCABEF9 /* SettingsScreen.swift */, 4B08B1AA6951B99CEFAA521F /* TransmissionSettings.swift */, @@ -286,6 +291,7 @@ C42FB097015693FF14B27553 /* AudioCaptureService.swift */, F287F167508A8E4114F44116 /* FFTProcessor.swift */, 4E09812700CD93E6187CC4BC /* LiveEngine.swift */, + D131B05E27A96EC4BEB28782 /* OnlineLogService.swift */, 6B9E40312EA203F05764FA56 /* QsoLogStore.swift */, 29B5962B70F4510D76A5BC0A /* TxPlayerService.swift */, ); @@ -408,7 +414,9 @@ 0FD449B6C15FD3407D8E3BC0 /* LogbookCharts.swift in Sources */, 33736A682F52CCBB13E8168A /* LogbookScreen.swift in Sources */, 65A688B883398E75D00BCD7E /* LogbookStats.swift in Sources */, + 6F7F961AB6678F3E6E60A97A /* LoggingSettings.swift in Sources */, 72300AB5D2B37428777C824D /* MapScreen.swift in Sources */, + 8EF984B5816F012A2DF93FDF /* OnlineLogService.swift in Sources */, 595BBF17D95723947D5BFFC4 /* PotaScreen.swift in Sources */, 4D87F21162B4DCEC806D8785 /* QsoCelebration.swift in Sources */, E375A4D50C25F5437C2C9F7F /* QsoLogStore.swift in Sources */, @@ -440,7 +448,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSMicrophoneUsageDescription = "FT8AF needs microphone access to capture radio audio for FT8 decoding"; - INFOPLIST_KEY_NSLocalNetworkUsageDescription = "FT8AF uses the local network to send WSJT-X UDP messages to companion apps like GridTracker and JTAlert"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -520,7 +527,6 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSMicrophoneUsageDescription = "FT8AF needs microphone access to capture radio audio for FT8 decoding"; - INFOPLIST_KEY_NSLocalNetworkUsageDescription = "FT8AF uses the local network to send WSJT-X UDP messages to companion apps like GridTracker and JTAlert"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; diff --git a/ios/FT8AF/FT8AF/AppState.swift b/ios/FT8AF/FT8AF/AppState.swift index 08e82ac8..1c41048e 100644 --- a/ios/FT8AF/FT8AF/AppState.swift +++ b/ios/FT8AF/FT8AF/AppState.swift @@ -75,7 +75,22 @@ struct DecodeMessage: Identifiable, Equatable { @Observable @MainActor final class WaterfallState { var rows: [[UInt8]] = [] + /// Optional UTC boundary label per row, kept in lockstep with `rows`: + /// non-nil on the first row of each 15 s FT8 period ("HH:mm:ss" of the + /// period start), nil elsewhere. `WaterfallCanvas` draws a divider + label + /// at these rows. + var rowTimestamps: [String?] = [] var spectrum: [Float] = [] + /// Top of the audio band the current `rows`/`spectrum` were built to, in + /// Hz. Maintained by the LiveEngine waterfall loop from the operator's + /// spectrum-width setting; on a live width change the loop clears the + /// old-width history and updates this. + var displayMaxHz: Float = 3000 + /// RX input level of the most recent metering window (linear, 0..~1), for + /// the level indicator in the waterfall info bar. Classified via + /// `AudioInputLevel.fromPeakRms` in the view. + var inputPeak: Float = 0 + var inputRms: Float = 0 var txFreqHz: Float = 1500 var isLive: Bool = false var noiseReduction: Bool = false diff --git a/ios/FT8AF/FT8AF/Assets.xcassets/AppIcon.appiconset/AppIcon.png b/ios/FT8AF/FT8AF/Assets.xcassets/AppIcon.appiconset/AppIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..ea053cd70af08b81cddfd8c60387439dc83250a1 GIT binary patch literal 13883 zcmeI(KMO%o7{~GFTsKLsO-XiTvWSt}6?gEbo59FH8B7+7{8{iW?kiAsgG?q2-h@Hf zY;q`By@2mIG%{9qbVUnm-}RU(M1itvMuXl@~WGB zx8?S#RY~g{61C`m4k+Cv^b#?A<2y0M5B&0b^By~-M6w&#DKrP3Q&Lo6rcbFC_n)UP=Epypa2CZK*1jhW~Z47o<*6J=lK1J%PrNMwcVb- E0GyQmx&QzG literal 0 HcmV?d00001 diff --git a/ios/FT8AF/FT8AF/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/FT8AF/FT8AF/Assets.xcassets/AppIcon.appiconset/Contents.json index 13613e3e..cefcc878 100644 --- a/ios/FT8AF/FT8AF/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/FT8AF/FT8AF/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "AppIcon.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/ios/FT8AF/FT8AF/Engine/LiveEngine.swift b/ios/FT8AF/FT8AF/Engine/LiveEngine.swift index 32e6a382..e0bb184e 100644 --- a/ios/FT8AF/FT8AF/Engine/LiveEngine.swift +++ b/ios/FT8AF/FT8AF/Engine/LiveEngine.swift @@ -75,14 +75,33 @@ final class LiveEngine { appState.decode.messages.removeLast(appState.decode.messages.count - 200) } } - let applyWaterfall: @Sendable @MainActor ([UInt8], [Float]) -> Void = { row, spectrum in - appState.waterfall.rows.append(row) - // Keep at most 120 rows for the scrolling waterfall. - if appState.waterfall.rows.count > 120 { - appState.waterfall.rows.removeFirst(appState.waterfall.rows.count - 120) + // Live spectrum-width source for the waterfall loop: read on the main + // actor each tick so a settings change applies without restarting. + let readSpectrumWidthHz: @Sendable @MainActor () -> Int = { + appState.settings.spectrumWidthHz + } + let applyWaterfall: @Sendable @MainActor (WaterfallUpdate) -> Void = { update in + let wf = appState.waterfall + // Width changed live: the new rows span a different band, so drop + // the old-width history rather than draw it against the new axis. + if wf.displayMaxHz != update.displayMaxHz { + wf.displayMaxHz = update.displayMaxHz + wf.rows.removeAll() + wf.rowTimestamps.removeAll() + } + wf.rows.append(update.bins) + wf.rowTimestamps.append(update.timestamp) + // Keep at most 120 rows for the scrolling waterfall (timestamps + // stay in lockstep with the rows they annotate). + if wf.rows.count > 120 { + let overflow = wf.rows.count - 120 + wf.rows.removeFirst(overflow) + wf.rowTimestamps.removeFirst(min(overflow, wf.rowTimestamps.count)) } - appState.waterfall.spectrum = spectrum - appState.waterfall.updateCount += 1 + wf.spectrum = update.spectrum + wf.inputPeak = update.inputPeak + wf.inputRms = update.inputRms + wf.updateCount += 1 } engineTask = Task.detached { [weak self] in @@ -99,6 +118,7 @@ final class LiveEngine { group.addTask { await self?.runWaterfallLoop( accumulator: accumulator, + readSpectrumWidthHz: readSpectrumWidthHz, applyWaterfall: applyWaterfall ) } @@ -251,6 +271,11 @@ final class LiveEngine { // Broadcast this slot's decodes over the WSJT-X UDP interface. broadcastUdpDecodes(decoded) + // Hand this slot's decodes to the PSK Reporter queue (policy-filtered, + // rate-limited per callsign/band, flushed every ~5 min when enabled). + let dialHz = Int64((Double(bandToFreqMhz(appState.settings.band)) ?? 14.074) * 1_000_000) + OnlineLogService.shared.enqueuePskDecodes(decoded, dialFreqHz: dialHz, appState: appState) + // Log incoming RX messages addressed to us for the conversation panel. let myCall = appState.settings.myCall.uppercased() if !myCall.isEmpty { @@ -296,6 +321,9 @@ final class LiveEngine { QsoLogStore.save(appState.logbook.records) // Broadcast the logged QSO over the WSJT-X UDP interface. broadcastUdpQso(record) + // Upload to Cloudlog/QRZ when enabled (fire-and-forget with retry; + // marks the record's synced flags and re-persists on success). + OnlineLogService.shared.handleQsoLogged(record, appState: appState) // Trigger QSO celebration animation. appState.tx.qsoCompletedAt = Date() } @@ -336,23 +364,35 @@ final class LiveEngine { // MARK: - Waterfall loop /// Runs ~4x per second, building FFT rows and updating the waterfall + - /// spectrum state. + /// spectrum state. Reads the operator's spectrum-width setting each tick so + /// a change applies live (display-only — the FT8 decoder's fixed range is + /// untouched), stamps a UTC label on the first row of each 15 s period, and + /// meters the RX input level for the info-bar indicator. private nonisolated func runWaterfallLoop( accumulator: SlotAccumulator, - applyWaterfall: @escaping @Sendable @MainActor ([UInt8], [Float]) -> Void + readSpectrumWidthHz: @escaping @Sendable @MainActor () -> Int, + applyWaterfall: @escaping @Sendable @MainActor (WaterfallUpdate) -> Void ) async { let sampleRate = Int(FT8.sampleRate) let needed = WaterfallRowBuilder.samplesNeeded() - let columns = WaterfallRowBuilder.columns(sampleRate: sampleRate) var builder = WaterfallRowBuilder() + var timestampGate = WaterfallTimestampGate() while !Task.isCancelled { try? await Task.sleep(for: .milliseconds(250)) if Task.isCancelled { break } + let displayMaxHz = Float(await MainActor.run { readSpectrumWidthHz() }) + let columns = WaterfallRowBuilder.columns(sampleRate: sampleRate, maxHz: displayMaxHz) + let samples = accumulator.peekRecent(needed) guard samples.count >= needed else { continue } + // RX input level from the most recent ~250 ms (the window Android + // meters), classified later by the pure AudioInputLevel. + let levelWindow = min(samples.count, sampleRate / 4) + let level = AudioInputLevel.measure(Array(samples.suffix(levelWindow))) + // Compute Welch-averaged power spectrum. let power = FFTProcessor.welchPower( samples: samples, @@ -367,7 +407,25 @@ final class LiveEngine { let scale = maxPower > 0 ? 1.0 / maxPower : 1.0 let spectrum = power.map { min($0 * scale, 1.0) } - await MainActor.run { applyWaterfall(row.bins, spectrum) } + // Stamp the UTC label once on the first row of each FT8 period + // (label shows the period start, matching Android). + let nowMs = Int64(Date().timeIntervalSince1970 * 1000) + var timestamp: String? + if timestampGate.shouldDraw(utcMs: nowMs) { + timestamp = WaterfallTimestampGate.utcLabel( + forUtcMs: WaterfallTimestampGate.slotStartMs(utcMs: nowMs) + ) + } + + let update = WaterfallUpdate( + bins: row.bins, + spectrum: spectrum, + displayMaxHz: displayMaxHz, + timestamp: timestamp, + inputPeak: level.peak, + inputRms: level.rms + ) + await MainActor.run { applyWaterfall(update) } } } @@ -562,6 +620,19 @@ final class LiveEngine { } } +/// One tick of the waterfall pipeline, handed from the background loop to the +/// MainActor state application: the new brightness row, the normalized +/// spectrum, the display width the row was built to, an optional UTC label for +/// a 15 s period boundary, and the RX input level of the metering window. +struct WaterfallUpdate: Sendable { + let bins: [UInt8] + let spectrum: [Float] + let displayMaxHz: Float + let timestamp: String? + let inputPeak: Float + let inputRms: Float +} + /// Initializer extension on DecodedMessage for constructing from individual /// fields (used when mapping UI DecodeMessage back to the DSP type). extension FT8DSP.DecodedMessage { diff --git a/ios/FT8AF/FT8AF/Engine/OnlineLogService.swift b/ios/FT8AF/FT8AF/Engine/OnlineLogService.swift new file mode 100644 index 00000000..d205b91e --- /dev/null +++ b/ios/FT8AF/FT8AF/Engine/OnlineLogService.swift @@ -0,0 +1,271 @@ +import FT8DSP +import FT8Engine +import Foundation +import Network + +/// Thin app-side transport for the online-logging integrations. All request +/// building and response parsing lives in FT8Engine (CloudlogClient, +/// QrzLogbookClient, PskReporterEncoder — pure and unit-tested); this class +/// just moves the bytes: URLSession for the HTTP APIs and an NWConnection UDP +/// socket for PSK Reporter. +/// +/// Mirrors Android's split between `ThirdPartyService` (Cloudlog/QRZ upload + +/// catch-up sync with per-record synced flags) and `PskReporterSender` +/// (batched spots flushed every ~5 minutes with a per-call/band dedup window). +@Observable @MainActor +final class OnlineLogService { + static let shared = OnlineLogService() + + // Catch-up sync progress (drives the Logbook toolbar UI). + private(set) var isSyncing = false + private(set) var syncDone = 0 + private(set) var syncTotal = 0 + + // PSK Reporter batching state. + private let pskEncoder = PskReporterEncoder() + private let pskDedup = PskDedup() + private var pskQueue: [PskSpot] = [] + private var pskFlushTask: Task? + + // MARK: - Upload on QSO logged + + /// Fire-and-forget upload of a freshly-logged QSO to every enabled + /// service (each attempt retried once), then mark the record's synced + /// flags and persist. Safe to call unconditionally — no-op when nothing + /// is enabled/configured. + func handleQsoLogged(_ record: QsoRecord, appState: AppState) { + let s = appState.settings + let cloudlog = s.cloudlogEnabled && !s.cloudlogUrl.isEmpty && !s.cloudlogApiKey.isEmpty + let qrz = s.qrzLogbookEnabled && !s.qrzLogbookApiKey.isEmpty + guard cloudlog || qrz else { return } + + let adif = Adif.singleRecord(record) + let cloudlogReq = cloudlog + ? CloudlogClient.qsoUploadRequest( + serverAddress: s.cloudlogUrl, apiKey: s.cloudlogApiKey, + stationId: s.cloudlogStationId, adif: adif) + : nil + let qrzBody = qrz ? QrzLogbookClient.insertBody(apiKey: s.qrzLogbookApiKey, adif: adif) : nil + + Task { [weak appState] in + var clOk = false + var qrzOk = false + if let cloudlogReq { clOk = await Self.postCloudlogWithRetry(cloudlogReq) } + if let qrzBody { qrzOk = await Self.postQrzWithRetry(qrzBody) } + guard clOk || qrzOk, let appState else { return } + Self.markSynced(id: record.id, cloudlog: clOk, qrz: qrzOk, appState: appState) + QsoLogStore.save(appState.logbook.records) + } + } + + private static func markSynced(id: Int64?, cloudlog: Bool, qrz: Bool, appState: AppState) { + guard let id, + let idx = appState.logbook.records.firstIndex(where: { $0.id == id }) + else { return } + if cloudlog { appState.logbook.records[idx].syncedCloudlog = true } + if qrz { appState.logbook.records[idx].syncedQrz = true } + } + + // MARK: - Catch-up sync ("sync all unsynced") + + /// Re-upload every record still awaiting an enabled service (services + /// dedupe by call+date+time+mode, so repeats are safe). Updates + /// `syncDone`/`syncTotal` as it goes and persists the flags at the end. + /// Returns (attempted, cloudlogOk, qrzOk). + func syncAll(appState: AppState) async -> (attempted: Int, cloudlogOk: Int, qrzOk: Int) { + guard !isSyncing else { return (0, 0, 0) } + let s = appState.settings + let cl = s.cloudlogEnabled && !s.cloudlogUrl.isEmpty && !s.cloudlogApiKey.isEmpty + let qrz = s.qrzLogbookEnabled && !s.qrzLogbookApiKey.isEmpty + let pending = OnlineLogSync.unsynced( + appState.logbook.records, cloudlogEnabled: cl, qrzEnabled: qrz) + guard !pending.isEmpty else { return (0, 0, 0) } + + isSyncing = true + syncTotal = pending.count + syncDone = 0 + var clOk = 0 + var qrzOk = 0 + for record in pending { + let adif = Adif.singleRecord(record) + if cl, !record.syncedCloudlog, + let req = CloudlogClient.qsoUploadRequest( + serverAddress: s.cloudlogUrl, apiKey: s.cloudlogApiKey, + stationId: s.cloudlogStationId, adif: adif), + await Self.postCloudlog(req) { + clOk += 1 + Self.markSynced(id: record.id, cloudlog: true, qrz: false, appState: appState) + } + if qrz, !record.syncedQrz { + let body = QrzLogbookClient.insertBody(apiKey: s.qrzLogbookApiKey, adif: adif) + if await Self.postQrz(body) { + qrzOk += 1 + Self.markSynced(id: record.id, cloudlog: false, qrz: true, appState: appState) + } + } + syncDone += 1 + } + QsoLogStore.save(appState.logbook.records) + isSyncing = false + return (pending.count, clOk, qrzOk) + } + + // MARK: - Test connections + + /// Cloudlog/Wavelog `api/auth/` check. The URL carries the key — + /// never log it. + nonisolated static func testCloudlog(serverAddress: String, apiKey: String) async -> Bool { + guard let urlStr = CloudlogClient.authUrl(serverAddress: serverAddress, apiKey: apiKey), + let url = URL(string: urlStr) + else { return false } + var req = URLRequest(url: url) + req.timeoutInterval = 15 + do { + let (data, resp) = try await URLSession.shared.data(for: req) + guard let http = resp as? HTTPURLResponse, http.statusCode == 200 else { return false } + return CloudlogClient.isAuthValid(String(data: data, encoding: .utf8)) + } catch { + return false + } + } + + /// QRZ logbook `ACTION=STATUS` check (POST keeps the key out of the URL). + nonisolated static func testQrz(apiKey: String) async -> Bool { + guard !apiKey.isEmpty else { return false } + let response = await postForm(QrzLogbookClient.statusBody(apiKey: apiKey)) + return QrzLogbookClient.isStatusOk(response) + } + + // MARK: - PSK Reporter + + /// Hand this slot's decodes to the reporter queue. Applies the Android + /// sender's policy (skip self/free-text/unresolved hashes; one report per + /// callsign per band per 5 minutes) and schedules a batched flush. + func enqueuePskDecodes(_ decoded: [DecodedMessage], dialFreqHz: Int64, appState: AppState) { + let s = appState.settings + guard s.pskReporterEnabled, !s.myCall.isEmpty, !decoded.isEmpty else { return } + let nowMs = Int64(Date().timeIntervalSince1970 * 1000) + for m in decoded { + guard let spot = PskReporter.makeSpot( + callFrom: m.callFrom, i3: m.i3, n3: m.n3, grid: m.grid, snr: m.snr, + audioFreqHz: m.freqHz, dialFreqHz: dialFreqHz, + myCall: s.myCall, utcSeconds: nowMs / 1000) + else { continue } + guard pskDedup.markIfFresh( + call: spot.senderCallsign, dialFreqHz: dialFreqHz, nowMs: nowMs) + else { continue } + pskQueue.append(spot) + } + scheduleFlushIfNeeded(appState: appState) + } + + private func scheduleFlushIfNeeded(appState: AppState) { + guard pskFlushTask == nil, !pskQueue.isEmpty else { return } + pskFlushTask = Task { [weak self, weak appState] in + try? await Task.sleep(for: .milliseconds(Int(PskReporter.sendIntervalMs))) + guard let self, !Task.isCancelled else { return } + self.pskFlushTask = nil + self.flushPsk(appState: appState) + } + } + + /// Build the IPFIX datagrams for the queued spots and send them. + func flushPsk(appState: AppState?) { + guard !pskQueue.isEmpty, let appState else { return } + let myCall = appState.settings.myCall + guard !myCall.isEmpty else { return } + let spots = pskQueue + pskQueue.removeAll() + let version = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "1.0" + let packets = pskEncoder.buildPackets( + rxCall: myCall, + rxGrid: appState.settings.myGrid, + software: PskReporter.buildSoftwareString(version: version, rigName: nil), + spots: spots, + nowMs: Int64(Date().timeIntervalSince1970 * 1000)) + Self.sendUdp(packets.map { Data($0) }) + } + + private nonisolated static func sendUdp(_ packets: [Data]) { + guard !packets.isEmpty, + let port = NWEndpoint.Port(rawValue: PskReporter.port) + else { return } + let conn = NWConnection( + host: NWEndpoint.Host(PskReporter.host), port: port, using: .udp) + conn.stateUpdateHandler = { state in + switch state { + case .ready: + // Sends complete in order on the connection queue; tear the + // connection down once the last one is processed. + for (i, packet) in packets.enumerated() { + conn.send(content: packet, completion: .contentProcessed { _ in + if i == packets.count - 1 { conn.cancel() } + }) + } + case .failed: + conn.cancel() // fire-and-forget: drop the batch + default: + break + } + } + conn.start(queue: .global(qos: .utility)) + } + + // MARK: - HTTP plumbing + + private static func postCloudlogWithRetry(_ req: CloudlogClient.Request) async -> Bool { + if await postCloudlog(req) { return true } + try? await Task.sleep(for: .seconds(2)) + return await postCloudlog(req) + } + + private static func postQrzWithRetry(_ body: String) async -> Bool { + if await postQrz(body) { return true } + try? await Task.sleep(for: .seconds(2)) + return await postQrz(body) + } + + /// POST the JSON upload. URLSession transparently follows the 307/308 + /// redirects Wavelog/Nextlog emit for rewritten paths, preserving the + /// POST body (unlike Android's HttpURLConnection, which needs the manual + /// redirect walk). + private nonisolated static func postCloudlog(_ req: CloudlogClient.Request) async -> Bool { + guard let url = URL(string: req.url) else { return false } + var r = URLRequest(url: url) + r.httpMethod = "POST" + r.setValue("application/json", forHTTPHeaderField: "Content-Type") + r.httpBody = Data(req.body.utf8) + r.timeoutInterval = 20 + do { + let (_, resp) = try await URLSession.shared.data(for: r) + guard let http = resp as? HTTPURLResponse else { return false } + return CloudlogClient.isSuccessStatus(http.statusCode) + } catch { + return false + } + } + + private nonisolated static func postQrz(_ body: String) async -> Bool { + QrzLogbookClient.isInsertSuccess(await postForm(body)) + } + + /// POST a form body to the QRZ endpoint; returns the response text on + /// HTTP 200/201, nil otherwise. + private nonisolated static func postForm(_ body: String) async -> String? { + guard let url = URL(string: QrzLogbookClient.endpoint) else { return nil } + var r = URLRequest(url: url) + r.httpMethod = "POST" + r.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") + r.httpBody = Data(body.utf8) + r.timeoutInterval = 20 + do { + let (data, resp) = try await URLSession.shared.data(for: r) + guard let http = resp as? HTTPURLResponse, + http.statusCode == 200 || http.statusCode == 201 + else { return nil } + return String(data: data, encoding: .utf8) + } catch { + return nil + } + } +} diff --git a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookScreen.swift b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookScreen.swift index 72a1b2f8..fc6bb3da 100644 --- a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookScreen.swift @@ -27,6 +27,32 @@ struct LogbookScreen: View { .foregroundStyle(textPrimary) Spacer() + // Catch-up sync to Cloudlog/QRZ (shown when a service is enabled) + if appState.settings.cloudlogEnabled || appState.settings.qrzLogbookEnabled { + let service = OnlineLogService.shared + Button { + runCatchUpSync() + } label: { + if service.isSyncing { + HStack(spacing: 5) { + ProgressView() + .controlSize(.mini) + .tint(accent) + Text("\(service.syncDone)/\(service.syncTotal)") + .font(.system(size: 11, weight: .medium, design: .monospaced)) + .foregroundStyle(textMuted) + } + } else { + Image(systemName: "icloud.and.arrow.up") + .font(.system(size: 14, weight: .medium)) + .foregroundStyle(textMuted) + } + } + .buttonStyle(.plain) + .disabled(service.isSyncing) + .padding(.trailing, 4) + } + // Import button Button { showImportPicker = true @@ -152,7 +178,11 @@ struct LogbookScreen: View { } else { LazyVStack(spacing: 0) { ForEach(filteredRecords, id: \.id) { record in - LogbookRow(record: record) + LogbookRow( + record: record, + showCloudlogChip: appState.settings.cloudlogEnabled, + showQrzChip: appState.settings.qrzLogbookEnabled + ) .onTapGesture { editingRecord = record } @@ -200,6 +230,24 @@ struct LogbookScreen: View { .padding(.vertical, 60) } + /// Upload every unsynced QSO to the enabled services, then toast the result. + private func runCatchUpSync() { + Task { + let s = appState.settings + let result = await OnlineLogService.shared.syncAll(appState: appState) + if result.attempted == 0 { + appState.toast.show("All QSOs already synced", icon: "checkmark.icloud") + return + } + var parts: [String] = [] + if s.cloudlogEnabled { parts.append("CL \(result.cloudlogOk)") } + if s.qrzLogbookEnabled { parts.append("QRZ \(result.qrzOk)") } + appState.toast.show( + "Synced \(parts.joined(separator: ", ")) of \(result.attempted)", + icon: "icloud.and.arrow.up") + } + } + private func importAdif(result: Result<[URL], Error>) { switch result { case .success(let urls): @@ -256,6 +304,8 @@ struct LogbookScreen: View { private struct LogbookRow: View { let record: QsoRecord + var showCloudlogChip = false + var showQrzChip = false var body: some View { HStack(spacing: 0) { @@ -265,15 +315,23 @@ private struct LogbookRow: View { .frame(width: 3, height: 36) .padding(.trailing, 10) - // Call + grid + // Call + grid + sync chips VStack(alignment: .leading, spacing: 2) { Text(record.call) .font(.system(size: 14, weight: .bold, design: .monospaced)) .foregroundStyle(textPrimary) - if !record.gridsquare.isEmpty { - Text(record.gridsquare) - .font(.system(size: 10, weight: .medium, design: .monospaced)) - .foregroundStyle(textFaint) + HStack(spacing: 4) { + if !record.gridsquare.isEmpty { + Text(record.gridsquare) + .font(.system(size: 10, weight: .medium, design: .monospaced)) + .foregroundStyle(textFaint) + } + if showCloudlogChip { + syncChip("CL", synced: record.syncedCloudlog, color: signal) + } + if showQrzChip { + syncChip("QRZ", synced: record.syncedQrz, color: statusConfirmed) + } } } @@ -325,6 +383,19 @@ private struct LogbookRow: View { } } + /// Tiny "uploaded to " badge — colored once the record is synced. + private func syncChip(_ label: String, synced: Bool, color: Color) -> some View { + Text(label) + .font(.system(size: 8, weight: .bold, design: .monospaced)) + .foregroundStyle(synced ? color : textDim) + .padding(.horizontal, 4) + .padding(.vertical, 1) + .background( + RoundedRectangle(cornerRadius: 3) + .fill(synced ? color.opacity(0.14) : bgSurface2) + ) + } + private var snrColor: Color { guard let val = Int(record.rstRcvd.trimmingCharacters(in: CharacterSet(charactersIn: "+-").inverted)) ?? Int(record.rstRcvd) else { return textMuted } diff --git a/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift new file mode 100644 index 00000000..061270e1 --- /dev/null +++ b/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift @@ -0,0 +1,180 @@ +import FT8Engine +import SwiftUI + +/// Online logging integrations: Cloudlog/Wavelog server upload, QRZ.com +/// logbook upload, and PSK Reporter reception reports. Mirrors the Android +/// LoggingSettings groups. +struct LoggingSettings: View { + @Environment(AppState.self) private var appState + + @State private var testingCloudlog = false + @State private var testingQrz = false + @State private var cloudlogResult: Bool? + @State private var qrzResult: Bool? + + var body: some View { + @Bindable var settings = appState.settings + + List { + // MARK: Cloudlog / Wavelog + Section { + Toggle(isOn: $settings.cloudlogEnabled) { + Text("Upload QSOs") + .foregroundStyle(textPrimary) + } + .tint(accent) + + credentialField("Server URL", text: $settings.cloudlogUrl, + placeholder: "https://log.example.com/", + keyboard: .URL) + credentialField("API Key", text: $settings.cloudlogApiKey, + placeholder: "cl64ab…", secure: true) + credentialField("Station ID", text: $settings.cloudlogStationId, + placeholder: "1", keyboard: .numberPad) + + testConnectionRow( + isTesting: testingCloudlog, + result: cloudlogResult, + disabled: settings.cloudlogUrl.isEmpty || settings.cloudlogApiKey.isEmpty + ) { + testingCloudlog = true + cloudlogResult = nil + let addr = settings.cloudlogUrl + let key = settings.cloudlogApiKey + Task { + let ok = await OnlineLogService.testCloudlog( + serverAddress: addr, apiKey: key) + cloudlogResult = ok + testingCloudlog = false + appState.toast.show(ok ? "Cloudlog: Pass" : "Cloudlog: Fail", + icon: ok ? "checkmark.circle" : "xmark.circle") + } + } + } header: { + Text("Cloudlog").foregroundStyle(textMuted) + } footer: { + Text("Upload each logged QSO to a Cloudlog, Wavelog or Nextlog server. The API key needs read/write rights.") + .foregroundStyle(textFaint) + } + .listRowBackground(bgSurface) + + // MARK: QRZ logbook + Section { + Toggle(isOn: $settings.qrzLogbookEnabled) { + Text("Upload QSOs") + .foregroundStyle(textPrimary) + } + .tint(accent) + + credentialField("API Key", text: $settings.qrzLogbookApiKey, + placeholder: "ABCD-1234-…", secure: true) + + testConnectionRow( + isTesting: testingQrz, + result: qrzResult, + disabled: settings.qrzLogbookApiKey.isEmpty + ) { + testingQrz = true + qrzResult = nil + let key = settings.qrzLogbookApiKey + Task { + let ok = await OnlineLogService.testQrz(apiKey: key) + qrzResult = ok + testingQrz = false + appState.toast.show(ok ? "QRZ: Pass" : "QRZ: Fail", + icon: ok ? "checkmark.circle" : "xmark.circle") + } + } + } header: { + Text("QRZ Logbook").foregroundStyle(textMuted) + } footer: { + Text("Upload each logged QSO to your QRZ.com logbook. Use the Logbook API key from QRZ (requires an XML subscription), not your account password.") + .foregroundStyle(textFaint) + } + .listRowBackground(bgSurface) + + // MARK: PSK Reporter + Section { + Toggle(isOn: $settings.pskReporterEnabled) { + Text("Send reception reports") + .foregroundStyle(textPrimary) + } + .tint(accent) + } header: { + Text("PSK Reporter").foregroundStyle(textMuted) + } footer: { + Text("Report decoded stations to pskreporter.info every ~5 minutes so others can see your receive coverage. Requires your callsign and grid to be set.") + .foregroundStyle(textFaint) + } + .listRowBackground(bgSurface) + } + .scrollContentBackground(.hidden) + .background(bgApp) + .navigationTitle("Online Logging") + .navigationBarTitleDisplayMode(.inline) + .toolbarColorScheme(.dark, for: .navigationBar) + .onChange(of: settings.cloudlogEnabled) { _, _ in persist() } + .onChange(of: settings.cloudlogUrl) { _, _ in cloudlogResult = nil; persist() } + .onChange(of: settings.cloudlogApiKey) { _, _ in cloudlogResult = nil; persist() } + .onChange(of: settings.cloudlogStationId) { _, _ in persist() } + .onChange(of: settings.qrzLogbookEnabled) { _, _ in persist() } + .onChange(of: settings.qrzLogbookApiKey) { _, _ in qrzResult = nil; persist() } + .onChange(of: settings.pskReporterEnabled) { _, _ in persist() } + } + + private func persist() { + SettingsPersistence.save(appState.settings) + } + + // MARK: - Rows + + private func credentialField( + _ label: String, text: Binding, placeholder: String, + secure: Bool = false, keyboard: UIKeyboardType = .asciiCapable + ) -> some View { + HStack { + Text(label) + .foregroundStyle(textPrimary) + Spacer() + Group { + if secure { + SecureField(placeholder, text: text) + } else { + TextField(placeholder, text: text) + .keyboardType(keyboard) + } + } + .font(.system(.body, design: .monospaced)) + .multilineTextAlignment(.trailing) + .foregroundStyle(textPrimary) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + } + } + + private func testConnectionRow( + isTesting: Bool, result: Bool?, disabled: Bool, action: @escaping () -> Void + ) -> some View { + HStack { + Button(action: action) { + Text("Test Connection") + .foregroundStyle(disabled || isTesting ? textFaint : signal) + .fontWeight(.semibold) + } + .buttonStyle(.plain) + .disabled(disabled || isTesting) + + Spacer() + + if isTesting { + ProgressView() + .controlSize(.small) + .tint(accent) + } else if let result { + Text(result ? "Pass" : "Fail") + .font(.system(size: 14, weight: .semibold, design: .monospaced)) + .foregroundStyle(result ? statusConfirmed : statusBad) + } + } + } +} diff --git a/ios/FT8AF/FT8AF/Screens/Settings/SettingsScreen.swift b/ios/FT8AF/FT8AF/Screens/Settings/SettingsScreen.swift index b90f1121..e269889b 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/SettingsScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/SettingsScreen.swift @@ -110,6 +110,23 @@ struct SettingsScreen: View { // Logging section Section { + NavigationLink { + LoggingSettings() + } label: { + HStack { + Image(systemName: "icloud.and.arrow.up") + .font(.system(size: 14)) + .foregroundStyle(accent) + .frame(width: 24) + Text("Online Logging") + .foregroundStyle(textPrimary) + Spacer() + Text(onlineLoggingSummary) + .font(.system(size: 14, design: .monospaced)) + .foregroundStyle(textMuted) + } + } + Toggle(isOn: $settings.autoLog) { HStack { Image(systemName: "book") @@ -187,6 +204,17 @@ struct SettingsScreen: View { } } + /// Short summary of enabled online-logging services for the nav row. + private var onlineLoggingSummary: String { + let s = appState.settings + let parts = [ + s.cloudlogEnabled ? "CL" : nil, + s.qrzLogbookEnabled ? "QRZ" : nil, + s.pskReporterEnabled ? "PSK" : nil, + ].compactMap { $0 } + return parts.isEmpty ? "Off" : parts.joined(separator: " ") + } + private var appVersion: String { let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0" let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1" diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift index 9b56bd1c..9a47a011 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift @@ -2,15 +2,21 @@ import SwiftUI import FT8Audio /// Horizontal ruler showing Hz tick marks across the displayed audio band. -/// Ticks span the same range the waterfall/spectrum data covers -/// (`WaterfallAxis.displayMaxHz`) so the labels line up with the trace. +/// Ticks span the operator's spectrum-width setting — the same range the +/// waterfall/spectrum data covers — so the labels line up with the trace. +/// Tick geometry comes from `WaterfallAxis.rulerTicks`, which places each +/// label at its true `hz / width` fraction (mirrors the Android fix for ruler +/// labels drifting when the width isn't a 500 Hz multiple, commit 647b12e8). struct FrequencyRuler: View { - private let ticks = stride(from: 0, through: Int(WaterfallAxis.displayMaxHz), by: 500).map { $0 } + @Environment(AppState.self) private var appState var body: some View { + let ticks = WaterfallAxis.rulerTicks( + displayMaxHz: Float(appState.settings.spectrumWidthHz) + ) Canvas { context, size in - for hz in ticks { - let x = CGFloat(WaterfallAxis.fraction(forHz: Float(hz))) * size.width + for tick in ticks { + let x = CGFloat(tick.fraction) * size.width // Tick mark let tickPath = Path { p in p.move(to: CGPoint(x: x, y: 0)) @@ -18,9 +24,8 @@ struct FrequencyRuler: View { } context.stroke(tickPath, with: .color(textFaint), lineWidth: 0.5) // Label - let label = "\(hz)" context.draw( - Text(label) + Text("\(tick.hz)") .font(.system(size: 8, weight: .medium, design: .monospaced)) .foregroundStyle(textFaint), at: CGPoint(x: x, y: 14), diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift index e45191f1..2707a223 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift @@ -3,11 +3,15 @@ import FT8Audio /// Vertical bar chart of live FFT magnitudes with a TX frequency marker. /// Reads normalized magnitudes (0...1) from `appState.waterfall.spectrum`. -/// Tap or drag to set the TX frequency. +/// Tap or drag to set the TX frequency. All frequency ↔ x mappings use the +/// operator's spectrum-width setting so overlays track the drawn bars. struct SpectrumStrip: View { @Environment(AppState.self) private var appState @State private var touchFreq: Float? + /// Top of the displayed band — the operator's spectrum-width setting. + private var displayMaxHz: Float { Float(appState.settings.spectrumWidthHz) } + var body: some View { GeometryReader { geo in Canvas { context, size in @@ -28,13 +32,17 @@ struct SpectrumStrip: View { } // TX frequency marker line - let txX = CGFloat(WaterfallAxis.clampedFraction(forHz: appState.waterfall.txFreqHz)) * size.width + let txX = CGFloat(WaterfallAxis.clampedFraction( + forHz: appState.waterfall.txFreqHz, displayMaxHz: displayMaxHz + )) * size.width let markerRect = CGRect(x: txX - 0.5, y: 0, width: 1, height: size.height) context.fill(Path(markerRect), with: .color(accent)) // Touch frequency indicator if let tf = touchFreq { - let touchX = CGFloat(WaterfallAxis.fraction(forHz: tf)) * size.width + let touchX = CGFloat(WaterfallAxis.fraction( + forHz: tf, displayMaxHz: displayMaxHz + )) * size.width let touchRect = CGRect(x: touchX - 0.5, y: 0, width: 1, height: size.height) context.fill(Path(touchRect), with: .color(target.opacity(0.7))) @@ -57,11 +65,15 @@ struct SpectrumStrip: View { DragGesture(minimumDistance: 0) .onChanged { value in let fraction = Float(value.location.x / geo.size.width) - touchFreq = WaterfallAxis.tunedTxHz(forFraction: fraction) + touchFreq = WaterfallAxis.tunedTxHz( + forFraction: fraction, displayMaxHz: displayMaxHz + ) } .onEnded { value in let fraction = Float(value.location.x / geo.size.width) - let clamped = WaterfallAxis.tunedTxHz(forFraction: fraction) + let clamped = WaterfallAxis.tunedTxHz( + forFraction: fraction, displayMaxHz: displayMaxHz + ) appState.waterfall.txFreqHz = clamped // Clear touch indicator after short delay DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift index b6ba3e9b..e3112ac6 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift @@ -5,10 +5,16 @@ import FT8Audio /// Each row is a `[UInt8]` of brightness values (0...255) produced by /// `WaterfallRowBuilder`. Renders newest rows at the bottom. /// When message marking is enabled, decoded callsign labels are drawn at -/// their frequency positions. +/// their frequency positions. Rows carrying a UTC boundary label (the first +/// row of each 15 s FT8 period) get a subtle divider line plus the period +/// start time at the left edge, matching Android's slot gridline. struct WaterfallCanvas: View { @Environment(AppState.self) private var appState + /// Top of the displayed band — the operator's spectrum-width setting. All + /// overlays map frequencies against this span so they sit on the trace. + private var displayMaxHz: Float { Float(appState.settings.spectrumWidthHz) } + var body: some View { Canvas { context, size in let rows = appState.waterfall.rows @@ -35,15 +41,22 @@ struct WaterfallCanvas: View { } } + // UTC period-boundary dividers + labels (rows flagged by the + // engine's timestamp gate, one per 15 s slot). + drawPeriodTimestamps(context: context, size: size, numRows: numRows, cellH: cellH) + // Draw message frequency labels when enabled if appState.waterfall.messageMarking { drawMessageLabels(context: context, size: size, numCols: numCols) } // Draw TX frequency marker. Map with the same span the waterfall - // columns cover (WaterfallAxis) so the marker sits on the trace. + // columns cover (the spectrum-width setting) so the marker sits on + // the trace. let txFreq = appState.waterfall.txFreqHz - let txX = CGFloat(WaterfallAxis.clampedFraction(forHz: txFreq)) * size.width + let txX = CGFloat(WaterfallAxis.clampedFraction( + forHz: txFreq, displayMaxHz: displayMaxHz + )) * size.width let txPath = Path { p in p.move(to: CGPoint(x: txX, y: 0)) p.addLine(to: CGPoint(x: txX, y: size.height)) @@ -55,6 +68,36 @@ struct WaterfallCanvas: View { .clipShape(RoundedRectangle(cornerRadius: 4)) } + /// Divider line + "HH:mm:ss" label on each row that starts a new FT8 + /// period. Left edge, faint monospaced caption — Android's look. + private func drawPeriodTimestamps( + context: GraphicsContext, size: CGSize, numRows: Int, cellH: CGFloat + ) { + let stamps = appState.waterfall.rowTimestamps + guard !stamps.isEmpty else { return } + + for r in 0.. 0, x < size.width else { continue } let label = context.resolve(Text(msg.callFrom) diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallScreen.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallScreen.swift index a370dfb0..2eda4453 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallScreen.swift @@ -1,3 +1,4 @@ +import FT8Audio import SwiftUI struct WaterfallScreen: View { @@ -37,6 +38,13 @@ struct WaterfallScreen: View { .fill(accentSoft) ) + // RX input level meter (Android: InputLevelIndicator). + InputLevelMeter( + peak: appState.waterfall.inputPeak, + rms: appState.waterfall.inputRms + ) + .padding(.leading, 4) + Spacer() HStack(spacing: 12) { @@ -76,6 +84,68 @@ struct WaterfallScreen: View { } } +/// Compact live RX input-level meter for the waterfall info bar: an +/// RMS-filled bar with a peak tick and a color-coded status word. Mirrors +/// Android's `InputLevelIndicator`; the level→status classification is the +/// pure, kit-tested `AudioInputLevel` (same thresholds as Android). +private struct InputLevelMeter: View { + let peak: Float + let rms: Float + + private static let barWidth: CGFloat = 44 + + private var status: AudioInputLevel.Status { + AudioInputLevel.fromPeakRms(peak: peak, rms: rms).status + } + + /// Status color: faint = no/too-low input, green = good, yellow = hot, + /// red = clipping. + private var color: Color { + switch status { + case .silent, .low: return textFaint + case .good: return statusConfirmed + case .high: return statusWarn + case .clipping: return statusBad + } + } + + private var statusText: String { + switch status { + case .silent, .low: return "LOW" + case .good: return "GOOD" + case .high: return "HIGH" + case .clipping: return "CLIP" + } + } + + var body: some View { + HStack(spacing: 4) { + Text("RX") + .font(.system(size: 9, weight: .bold, design: .monospaced)) + .foregroundStyle(textMuted) + + // Meter bar: RMS fill + peak tick. + ZStack(alignment: .leading) { + RoundedRectangle(cornerRadius: 3) + .fill(bgSurface3) + RoundedRectangle(cornerRadius: 3) + .fill(color) + .frame(width: Self.barWidth * CGFloat(AudioInputLevel.meterFraction(rms))) + Rectangle() + .fill(color) + .frame(width: 1) + .offset(x: max(0, Self.barWidth * CGFloat(AudioInputLevel.meterFraction(peak)) - 1)) + } + .frame(width: Self.barWidth, height: 6) + .clipShape(RoundedRectangle(cornerRadius: 3)) + + Text(statusText) + .font(.system(size: 9, weight: .bold, design: .monospaced)) + .foregroundStyle(color) + } + } +} + private struct InfoChip: View { let label: String let isOn: Bool diff --git a/ios/FT8AFKit/Sources/FT8Audio/AudioInputLevel.swift b/ios/FT8AFKit/Sources/FT8Audio/AudioInputLevel.swift new file mode 100644 index 00000000..652e7b4f --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Audio/AudioInputLevel.swift @@ -0,0 +1,119 @@ +import Foundation + +/// Pure audio input-level metering for the receive level indicator — the +/// classification logic behind the waterfall's RX meter, kept off the SwiftUI +/// views so it is host-testable. Port of the Android `AudioInputLevel` class +/// (the app's single RX level classifier), same thresholds, so the two +/// platforms judge the same audio identically. +/// +/// A user comparing the app to WSJT-X often has the radio's audio set too +/// quiet (weak signals fall below the decode threshold) or hot enough to clip +/// (intermodulation wrecks the decode). This turns a raw 12 kHz mono float +/// buffer (samples in roughly [-1, 1]) into a peak/RMS reading plus a coarse +/// `Status` the UI can color, so the operator can set gain the way WSJT-X's +/// meter lets them. +/// +/// All thresholds are expressed in dBFS (decibels relative to full scale, so +/// 0 dB is a sample magnitude of 1.0 and every value here is <= 0). Clipping +/// is judged from the peak; "too quiet" from the RMS, which tracks the +/// noise/signal energy rather than a lone spike. +public enum AudioInputLevel { + /// Coarse health of the input level. + public enum Status: Equatable, Sendable { + /// Essentially no input — disconnected cable, wrong source, or muted radio. + case silent + /// Audible but too quiet; weak signals sit below the decode threshold. + case low + /// In the healthy window — what the operator should aim for. + case good + /// Hot: peaks are close to full scale, raise nothing further. + case high + /// Peaks pinned at full scale; clipping distortion will block decodes. + case clipping + } + + /// Immutable result of metering one buffer. + public struct Reading: Equatable, Sendable { + /// Largest sample magnitude in the buffer, linear (0..~1). + public let peak: Float + /// Root-mean-square sample magnitude, linear. + public let rms: Float + /// `peak` in dBFS, clamped at `minDbfs`. + public let peakDbfs: Float + /// `rms` in dBFS, clamped at `minDbfs`. + public let rmsDbfs: Float + /// Coarse health used to color the meter. + public let status: Status + } + + /// Floor we report instead of -infinity for a digitally silent buffer. + public static let minDbfs: Float = -120 + + // Peak at/above this linear magnitude is treated as clipping (~ -0.13 dBFS). + static let clipPeak: Float = 0.985 + // Peak dBFS at/above this is "hot" (clipping risk on transients). + static let highPeakDbfs: Float = -3 + // Below this RMS the buffer carries no usable signal. + static let silentRmsDbfs: Float = -75 + // RMS below this (but above SILENT) is too quiet for reliable weak-signal decode. + static let lowRmsDbfs: Float = -45 + + /// A reading for an empty/absent buffer: digital silence. + public static let silence = Reading( + peak: 0, rms: 0, peakDbfs: minDbfs, rmsDbfs: minDbfs, status: .silent + ) + + /// Meter one buffer of mono float samples in roughly [-1, 1]. An empty + /// buffer yields a `.silent` reading rather than trapping, so the caller + /// can meter unconditionally. + public static func measure(_ samples: [Float]) -> Reading { + guard !samples.isEmpty else { return silence } + var peak: Float = 0 + var sumSquares: Double = 0 + for s in samples { + let a = abs(s) + if a > peak { peak = a } + sumSquares += Double(s) * Double(s) + } + let rms = Float((sumSquares / Double(samples.count)).squareRoot()) + return fromPeakRms(peak: peak, rms: rms) + } + + /// Meter from an already-accumulated linear peak/RMS pair — e.g. the + /// metering window the waterfall loop snapshots. Single classification + /// entry point so the "healthy gain window" lives in exactly one set of + /// thresholds (mirrors Android issue #405). + public static func fromPeakRms(peak: Float, rms: Float) -> Reading { + // Sanitize non-finite inputs to digital silence: comparisons against a + // NaN are all false, which would misreport GOOD with NaN dB values. + let p = peak.isFinite ? max(0, peak) : 0 + let r = rms.isFinite ? max(0, rms) : 0 + let peakDbfs = toDbfs(p) + let rmsDbfs = toDbfs(r) + return Reading( + peak: p, rms: r, peakDbfs: peakDbfs, rmsDbfs: rmsDbfs, + status: classify(peak: p, peakDbfs: peakDbfs, rmsDbfs: rmsDbfs) + ) + } + + /// Meter-bar fill fraction for a linear sample level, clamped to 0...1. + public static func meterFraction(_ level: Float) -> Float { + guard level.isFinite else { return 0 } + return min(max(level, 0), 1) + } + + /// Linear magnitude (>= 0) to dBFS, with a `minDbfs` floor for silence. + static func toDbfs(_ linear: Float) -> Float { + guard linear > 0 else { return minDbfs } + let db = 20 * log10(linear) + return db < minDbfs ? minDbfs : db + } + + static func classify(peak: Float, peakDbfs: Float, rmsDbfs: Float) -> Status { + if peak >= clipPeak { return .clipping } + if peakDbfs >= highPeakDbfs { return .high } + if rmsDbfs < silentRmsDbfs { return .silent } + if rmsDbfs < lowRmsDbfs { return .low } + return .good + } +} diff --git a/ios/FT8AFKit/Sources/FT8Audio/WaterfallAxis.swift b/ios/FT8AFKit/Sources/FT8Audio/WaterfallAxis.swift index 0b792dc9..fed3d7e2 100644 --- a/ios/FT8AFKit/Sources/FT8Audio/WaterfallAxis.swift +++ b/ios/FT8AFKit/Sources/FT8Audio/WaterfallAxis.swift @@ -3,36 +3,58 @@ /// /// The waterfall and spectrum columns produced by `WaterfallRowBuilder` are /// drawn edge-to-edge across the full view width and cover the audio band -/// `0 ... WaterfallRowBuilder.maxHz` (`columns(...) * binHz(...)`, i.e. the top -/// of the displayed band — 3500 Hz, matching the desktop engine's `WF_MAX_HZ`). -/// Any overlay that positions a frequency on that view — the TX marker, decoded -/// message labels, the ruler ticks, tap-to-tune — must map with the **same** -/// span, or it drifts off the trace it annotates. Previously the overlays used a -/// hard-coded 3000 Hz while the data spanned 3500 Hz, so every marker sat ~17% -/// away from its signal and tapping a visible trace keyed up on the wrong audio -/// frequency. +/// `0 ... displayMaxHz` (`columns(...) * binHz(...)`, i.e. the top of the +/// displayed band). Any overlay that positions a frequency on that view — the +/// TX marker, decoded message labels, the ruler ticks, tap-to-tune — must map +/// with the **same** span, or it drifts off the trace it annotates. Previously +/// the overlays used a hard-coded 3000 Hz while the data spanned 3500 Hz, so +/// every marker sat ~17% away from its signal and tapping a visible trace keyed +/// up on the wrong audio frequency. +/// +/// The span is the operator's **spectrum width** setting (Android's +/// `GeneralVariables.spectrumWidth`), so every function takes it as a +/// parameter instead of baking in a constant. This is display-only: the FT8 +/// decoder keeps its fixed range regardless of what's shown. /// /// This is the single source of truth for that mapping, extracted off the /// SwiftUI `Canvas` views so it can be unit-tested (per the project rule of /// putting geometry in a plain, testable type). public enum WaterfallAxis { - /// Top of the displayed audio band, in Hz. Kept equal to the span the - /// waterfall/spectrum data is actually built to so overlays line up with the - /// columns. - public static var displayMaxHz: Float { WaterfallRowBuilder.maxHz } - - /// Lowest / highest audio offset the operator may transmit on. The upper - /// bound keeps TX inside the SSB passband and mirrors the desktop engine's - /// `tx_audio_hz.clamp(200, 3000)`; the display band (`displayMaxHz`) extends - /// above it so higher signals remain visible even though they can't be tuned. - public static let minTxHz: Float = 200 - public static let maxTxHz: Float = 3000 + /// Default top of the displayed audio band, in Hz — used before the + /// operator's spectrum-width setting is known. Kept equal to the span the + /// waterfall/spectrum data is built to by default. + public static var defaultDisplayMaxHz: Float { WaterfallRowBuilder.defaultMaxHz } + + /// Lowest audio offset the operator may transmit on. Mirrors Android, + /// which clamps the audio frequency to `100 ... spectrumWidth - 100` + /// (`WaterfallView` tap-to-tune and the settings input share the clamp). + public static let minTxHz: Float = 100 + + /// Absolute ceiling on the TX audio offset, regardless of how wide the + /// display is. Keeps TX inside the SSB passband (the desktop engine's + /// `tx_audio_hz.clamp(..., 3000)`); the display band can extend above it so + /// higher signals remain visible even though they can't be tuned. + public static let txCeilingHz: Float = 3000 + + /// Margin kept between the tunable range and the display edge, mirroring + /// Android's `100 ... spectrumWidth - 100` clamp. + public static let txEdgeGuardHz: Float = 100 + + /// Hz step between adjacent frequency-ruler labels (Android `RulerStepHz`). + public static let rulerStepHz = 500 + + /// Highest audio offset the operator may transmit on for a given display + /// width: `min(3000, width - 100)` — the Android clamp with the SSB + /// passband ceiling. Never collapses below `minTxHz` even for a degenerate + /// width, so the clamp range stays valid. + public static func maxTxHz(displayMaxHz: Float) -> Float { + return max(minTxHz, min(txCeilingHz, displayMaxHz - txEdgeGuardHz)) + } /// Horizontal position (0...1 of the view width) for a frequency. - public static func fraction(forHz hz: Float) -> Float { - let span = displayMaxHz - guard span > 0 else { return 0 } - return hz / span + public static func fraction(forHz hz: Float, displayMaxHz: Float) -> Float { + guard displayMaxHz > 0 else { return 0 } + return hz / displayMaxHz } /// Horizontal position for a TX-marker overlay, clamped to the drawable @@ -40,21 +62,56 @@ public enum WaterfallAxis { /// — a WSJT-X UDP reply carries a raw `deltaFreq` — so an out-of-band value /// would otherwise push the marker line off-canvas. Clamping keeps it pinned /// to the nearest edge instead of vanishing. - public static func clampedFraction(forHz hz: Float) -> Float { - return min(max(fraction(forHz: hz), 0), 1) + public static func clampedFraction(forHz hz: Float, displayMaxHz: Float) -> Float { + return min(max(fraction(forHz: hz, displayMaxHz: displayMaxHz), 0), 1) } /// Frequency (Hz) at a horizontal position given as a 0...1 fraction of the - /// view width. Inverse of `fraction(forHz:)`. - public static func hz(forFraction fraction: Float) -> Float { + /// view width. Inverse of `fraction(forHz:displayMaxHz:)`. + public static func hz(forFraction fraction: Float, displayMaxHz: Float) -> Float { return fraction * displayMaxHz } /// Tap-to-tune: the frequency under a horizontal fraction of the view, - /// clamped to the valid TX offset range. The fraction is read against the - /// display span so the tap lands on the trace under the finger; the result - /// is then clamped so it never exceeds the SSB passband. - public static func tunedTxHz(forFraction fraction: Float) -> Float { - return min(max(hz(forFraction: fraction), minTxHz), maxTxHz) + /// clamped to the valid TX offset range for this display width. The + /// fraction is read against the display span so the tap lands on the trace + /// under the finger; the result is then clamped to + /// `100 ... min(3000, width - 100)` (the Android clamp) so it never leaves + /// the usable passband. + public static func tunedTxHz(forFraction fraction: Float, displayMaxHz: Float) -> Float { + let hz = hz(forFraction: fraction, displayMaxHz: displayMaxHz) + return min(max(hz, minTxHz), maxTxHz(displayMaxHz: displayMaxHz)) + } + + /// Ruler labels at `rulerStepHz` intervals from 0 up to `displayMaxHz`, + /// each paired with the horizontal fraction it must sit at. + /// + /// A label's fraction is `hz / displayMaxHz` — the same mapping the + /// waterfall columns use — NOT an even spread to the far edge. When the + /// width is a multiple of the step the top tick equals it (fraction 1.0) + /// and the gaps are uniform; otherwise the top tick sits below the far edge + /// instead of being stretched to it. Mirrors the Android fix for ruler + /// labels drifting when `spectrumWidth` isn't a 500 multiple (commit + /// 647b12e8 / PR #575). + public static func rulerTicks(displayMaxHz: Float) -> [RulerTick] { + guard displayMaxHz > 0 else { return [RulerTick(hz: 0, fraction: 0)] } + var ticks: [RulerTick] = [] + var hz = 0 + while Float(hz) <= displayMaxHz { + ticks.append(RulerTick(hz: hz, fraction: Float(hz) / displayMaxHz)) + hz += rulerStepHz + } + return ticks + } +} + +/// A single frequency-ruler label: its frequency and where it sits across the +/// strip (0...1 of the view width). +public struct RulerTick: Equatable, Sendable { + public let hz: Int + public let fraction: Float + public init(hz: Int, fraction: Float) { + self.hz = hz + self.fraction = fraction } } diff --git a/ios/FT8AFKit/Sources/FT8Audio/WaterfallRowBuilder.swift b/ios/FT8AFKit/Sources/FT8Audio/WaterfallRowBuilder.swift index 7741b4f3..d25e3c30 100644 --- a/ios/FT8AFKit/Sources/FT8Audio/WaterfallRowBuilder.swift +++ b/ios/FT8AFKit/Sources/FT8Audio/WaterfallRowBuilder.swift @@ -26,7 +26,11 @@ public struct WaterfallRowBuilder { public static let fftSize = 2048 // ~5.86 Hz/bin at 12 kHz public static let segments = 6 // WF_AVG (Welch segments) public static let step = fftSize / 2 // WF_STEP, 50% overlap - public static let maxHz: Float = 3500 // displayed top of the audio band + /// Default displayed top of the audio band (desktop `WF_MAX_HZ`). The + /// operator's spectrum-width setting overrides it per call via the `maxHz` + /// parameter of `columns(sampleRate:maxHz:)` — display-only; the FT8 + /// decoder's range is unaffected. + public static let defaultMaxHz: Float = 3500 public static let spanDb: Float = 26 // dB above black that maps to full brightness public static let blackOffsetDb: Float = 4 // push black above the noise floor public static let noiseFloorPercentile: Float = 0.30 @@ -40,8 +44,9 @@ public struct WaterfallRowBuilder { /// Hz per FFT bin at a given sample rate. public static func binHz(sampleRate: Int) -> Float { Float(sampleRate) / Float(fftSize) } - /// Number of displayed columns: bins up to `maxHz`, capped at the Nyquist half. - public static func columns(sampleRate: Int) -> Int { + /// Number of displayed columns: bins up to `maxHz` (the operator's spectrum + /// width; defaults to `defaultMaxHz`), capped at the Nyquist half. + public static func columns(sampleRate: Int, maxHz: Float = WaterfallRowBuilder.defaultMaxHz) -> Int { let bh = binHz(sampleRate: sampleRate) guard bh > 0 else { return 1 } return min(fftSize / 2, max(1, Int(maxHz / bh))) diff --git a/ios/FT8AFKit/Sources/FT8Audio/WaterfallTimestampGate.swift b/ios/FT8AFKit/Sources/FT8Audio/WaterfallTimestampGate.swift new file mode 100644 index 00000000..b980c529 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Audio/WaterfallTimestampGate.swift @@ -0,0 +1,83 @@ +import Foundation + +/// Gates the waterfall's UTC-timestamp gridline (the horizontal boundary line +/// plus the time label) to one emission per transmit/decode slot — the pure, +/// unit-testable core behind the labels the waterfall draws at each 15 s FT8 +/// period boundary. Port of the Android `WaterfallTimestampGate`, including +/// its mode-aware slot length (FT8 15 s today; FT4/FT2 would pass a shorter +/// `slotMs`). +/// +/// The line marks where one cycle ends and the next begins on the scrolling +/// waterfall so an operator can line signals up against the slot grid. The +/// waterfall loop ticks ~4x per second, so without the gate the label would be +/// stamped on every row of a slot instead of exactly once at its boundary. +public struct WaterfallTimestampGate { + /// Fallback slot length (FT8, ms) used if the caller has no valid mode slot. + public static let defaultSlotMs: Int64 = 15_000 + + private var lastPeriod: Int64 = -1 + /// Slot length `lastPeriod` was computed under; -1 = none yet. + private var lastSlotMs: Int64 = -1 + + public init() {} + + /// The slot index `utcMs` falls in for a mode of slot length `slotMs`. A + /// non-positive `slotMs` falls back to the 15 s FT8 grid so a mid-init + /// caller can never divide by zero. Euclidean division keeps the index + /// correct even if a clock correction pushes the time negative. + public static func slotPeriod(utcMs: Int64, slotMs: Int64 = defaultSlotMs) -> Int64 { + let slot = slotMs > 0 ? slotMs : defaultSlotMs + return SlotClock.floorDiv(utcMs, slot) + } + + /// Epoch ms of the start of the slot containing `utcMs` — the instant the + /// boundary label should display (Android draws the period-start time, not + /// the wall clock of whichever frame crossed the boundary). + public static func slotStartMs(utcMs: Int64, slotMs: Int64 = defaultSlotMs) -> Int64 { + let slot = slotMs > 0 ? slotMs : defaultSlotMs + return slotPeriod(utcMs: utcMs, slotMs: slot) * slot + } + + /// Whether the boundary line + label should be emitted now: true the first + /// call that `utcMs` crosses into a new slot, false while it stays within + /// the same slot (so the label is stamped exactly once per slot). + /// + /// Slot indices are only comparable within one mode, so when `slotMs` + /// changes mid-stream the gate re-baselines to the new grid silently, + /// emitting only if `utcMs` happens to land exactly on a new-mode boundary + /// (mirrors the Android gate's mode-change rule). + public mutating func shouldDraw(utcMs: Int64, slotMs: Int64 = defaultSlotMs) -> Bool { + let slot = slotMs > 0 ? slotMs : Self.defaultSlotMs + let period = SlotClock.floorDiv(utcMs, slot) + if lastSlotMs > 0 && slot != lastSlotMs { + // Mode change: adopt the new grid without comparing across + // incompatible indices. + lastSlotMs = slot + lastPeriod = period + return SlotClock.floorMod(utcMs, slot) == 0 + } + lastSlotMs = slot + if period == lastPeriod { + return false + } + lastPeriod = period + return true + } + + /// Forget the last emitted slot and mode (e.g. when the waterfall history + /// is cleared), so the next call stamps a fresh label. + public mutating func reset() { + lastPeriod = -1 + lastSlotMs = -1 + } + + /// "HH:mm:ss" UTC label for an epoch-ms instant — the text drawn at the + /// boundary row (matches Android's period-start `HH:mm:ss` stamp). + public static func utcLabel(forUtcMs ms: Int64) -> String { + let secondsOfDay = SlotClock.floorMod(ms, 86_400_000) / 1000 + let h = secondsOfDay / 3600 + let m = (secondsOfDay % 3600) / 60 + let s = secondsOfDay % 60 + return String(format: "%02d:%02d:%02d", h, m, s) + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/CloudlogClient.swift b/ios/FT8AFKit/Sources/FT8Engine/CloudlogClient.swift new file mode 100644 index 00000000..65d5744e --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/CloudlogClient.swift @@ -0,0 +1,88 @@ +import Foundation + +/// Pure request-builders and response-parsers for the Cloudlog/Wavelog/Nextlog +/// HTTP API. No networking here — the app-side `OnlineLogService` performs the +/// actual URLSession calls. Ports the wire shapes from Android +/// `ThirdPartyService` (uploadAdifToCloudlog / CheckCloudlogConnection). +public enum CloudlogClient { + + /// A fully-built HTTP request the app layer just has to send. + public struct Request: Equatable, Sendable { + public let url: String + /// JSON body for POSTs; empty for GETs. + public let body: String + } + + /// Normalize a server address: trim whitespace and ensure a trailing "/". + public static func normalizedAddress(_ address: String) -> String { + let t = address.trimmingCharacters(in: .whitespacesAndNewlines) + guard !t.isEmpty else { return t } + return t.hasSuffix("/") ? t : t + "/" + } + + /// Build the `POST api/qso` request that uploads one ADIF record. The JSON + /// key order matches Android's JSONStringer output so payloads are + /// byte-identical across platforms. Returns nil when the address or API + /// key is missing (nothing to send). + /// + /// Note: the documented endpoint is `api/qso` with NO trailing slash — + /// Wavelog/Nextlog 308-redirect the slashed form on POST. + public static func qsoUploadRequest( + serverAddress: String, apiKey: String, stationId: String, adif: String + ) -> Request? { + let base = normalizedAddress(serverAddress) + guard !base.isEmpty, !apiKey.isEmpty else { return nil } + let body = "{\"key\":\(jsonString(apiKey))" + + ",\"station_profile_id\":\(jsonString(stationId))" + + ",\"type\":\"adif\"" + + ",\"string\":\(jsonString(adif))}" + return Request(url: base + "api/qso", body: body) + } + + /// URL for the test-connection GET (`api/auth/`). The key is a path + /// segment, so the URL is credential-bearing — never log it. Returns nil + /// when address or key is missing. + public static func authUrl(serverAddress: String, apiKey: String) -> String? { + let base = normalizedAddress(serverAddress) + guard !base.isEmpty, !apiKey.isEmpty else { return nil } + return base + "api/auth/" + apiKey + } + + /// Parse the `api/auth` response. Cloudlog, Wavelog and Nextlog all + /// implement it but differ in whitespace/XML-declaration noise, so match + /// the meaningful markers on a whitespace-stripped copy. + public static func isAuthValid(_ response: String?) -> Bool { + guard let response, !response.isEmpty else { return false } + let compact = response.filter { !$0.isWhitespace } + return compact.contains("Valid") + && compact.contains("rw") + } + + /// Whether an HTTP status code counts as upload success. Cloudlog answers + /// 201 Created for a stored QSO; some deployments answer 200. + public static func isSuccessStatus(_ code: Int) -> Bool { + code == 200 || code == 201 + } + + /// Minimal JSON string literal encoder (quote, backslash, control chars) + /// so request bodies are deterministic and unit-testable byte-for-byte. + static func jsonString(_ s: String) -> String { + var out = "\"" + for scalar in s.unicodeScalars { + switch scalar { + case "\"": out += "\\\"" + case "\\": out += "\\\\" + case "\n": out += "\\n" + case "\r": out += "\\r" + case "\t": out += "\\t" + default: + if scalar.value < 0x20 { + out += String(format: "\\u%04x", scalar.value) + } else { + out.unicodeScalars.append(scalar) + } + } + } + return out + "\"" + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/OnlineLogAdif.swift b/ios/FT8AFKit/Sources/FT8Engine/OnlineLogAdif.swift new file mode 100644 index 00000000..56fdf4c2 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/OnlineLogAdif.swift @@ -0,0 +1,34 @@ +import Foundation + +public extension Adif { + /// Single-record ADIF body for online logbook uploads (Cloudlog/Wavelog + /// "api/qso" and the QRZ logbook INSERT). Ports Android + /// `ThirdPartyService.QSLRecordToADIF`: no `` header, no QSL flags, + /// same field order, freq passed through as an MHz string, and the comment + /// always emitted (even when empty). Lengths are UTF-8 byte counts. + static func singleRecord(_ r: QsoRecord) -> String { + var out = "" + append(&out, "call", r.call) + append(&out, "gridsquare", r.gridsquare) + append(&out, "mode", r.mode) + append(&out, "rst_sent", r.rstSent) + append(&out, "rst_rcvd", r.rstRcvd) + append(&out, "qso_date", r.qsoDate) + append(&out, "time_on", r.timeOn) + append(&out, "band", r.band) + append(&out, "qso_date_off", r.qsoDateOff) + append(&out, "time_off", r.timeOff) + append(&out, "freq", r.freq) + append(&out, "station_callsign", r.stationCallsign) + append(&out, "my_gridsquare", r.myGridsquare) + out += "\(r.comment) \n" + return out + } + + /// `value ` — emitted only when the value is non-empty + /// (matches Android's appendAdif / the null-checks in QSLRecordToADIF). + private static func append(_ out: inout String, _ name: String, _ value: String) { + guard !value.isEmpty else { return } + out += "<\(name):\(value.utf8.count)>\(value) " + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/OnlineLogSync.swift b/ios/FT8AFKit/Sources/FT8Engine/OnlineLogSync.swift new file mode 100644 index 00000000..f5409a0e --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/OnlineLogSync.swift @@ -0,0 +1,26 @@ +import Foundation + +/// Pure catch-up-sync planning over the QSO log — which records still need an +/// upload to at least one enabled service. Mirrors Android +/// `ThirdPartyService.unsyncedFilter` / `countUnsyncedQSOs` so the badge count +/// and the actual sync always agree. +public enum OnlineLogSync { + + /// Records still awaiting upload to an enabled service, in log order. + /// Empty when neither service is enabled (nothing to do). + public static func unsynced( + _ records: [QsoRecord], cloudlogEnabled: Bool, qrzEnabled: Bool + ) -> [QsoRecord] { + guard cloudlogEnabled || qrzEnabled else { return [] } + return records.filter { r in + (cloudlogEnabled && !r.syncedCloudlog) || (qrzEnabled && !r.syncedQrz) + } + } + + /// Number of records `unsynced` would return. + public static func unsyncedCount( + _ records: [QsoRecord], cloudlogEnabled: Bool, qrzEnabled: Bool + ) -> Int { + unsynced(records, cloudlogEnabled: cloudlogEnabled, qrzEnabled: qrzEnabled).count + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/PskReporter.swift b/ios/FT8AFKit/Sources/FT8Engine/PskReporter.swift new file mode 100644 index 00000000..c16806c6 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/PskReporter.swift @@ -0,0 +1,327 @@ +import Foundation + +/// One decoded spot destined for PSK Reporter. +public struct PskSpot: Equatable, Sendable { + public let senderCallsign: String + public let frequencyHz: Int64 + public let snr: Int + public let mode: String + public let senderLocator: String? + public let flowStartSeconds: Int64 + + public init(senderCallsign: String, frequencyHz: Int64, snr: Int, mode: String, + senderLocator: String?, flowStartSeconds: Int64) { + self.senderCallsign = senderCallsign + self.frequencyHz = frequencyHz + self.snr = snr + self.mode = mode + self.senderLocator = senderLocator + self.flowStartSeconds = flowStartSeconds + } +} + +/// Spot-policy helpers shared by the encoder and the app-side sender. Pure +/// functions — ports of the filter logic in Android `PskReporterSender`. +public enum PskReporter { + /// Where the datagrams go. + public static let host = "report.pskreporter.info" + public static let port: UInt16 = 4739 + /// Flush cadence and per-callsign/band dedup window (5 minutes, like + /// Android and the WSJT-X convention). + public static let sendIntervalMs: Int64 = 5 * 60 * 1000 + public static let dedupWindowMs: Int64 = 5 * 60 * 1000 + + /// Build the PSKReporter "decodingSoftware" string, optionally including + /// the connected rig name. + public static func buildSoftwareString(version: String, rigName: String?) -> String { + let base = "FT8AF \(version)" + if let rigName, !rigName.trimmingCharacters(in: .whitespaces).isEmpty { + return "\(base) (\(rigName))" + } + return base + } + + /// Convert one decode into a spot, applying the Android sender's policy: + /// drop empty/unresolved-hash calls and free text (i3=0, n3=0), strip + /// angle brackets from hashed callsigns, never report our own callsign, + /// require a >= 4-char locator (else omit it), and report the RF frequency + /// as dial + audio offset. Returns nil when the decode must not be spotted. + public static func makeSpot( + callFrom: String, i3: UInt8, n3: UInt8, grid: String, snr: Int, + audioFreqHz: Float, dialFreqHz: Int64, myCall: String, utcSeconds: Int64 + ) -> PskSpot? { + if callFrom.isEmpty || callFrom == "<...>" { return nil } + let clean = callFrom + .replacingOccurrences(of: "<", with: "") + .replacingOccurrences(of: ">", with: "") + if clean.isEmpty { return nil } + if !myCall.isEmpty, clean.uppercased() == myCall.uppercased() { return nil } + if i3 == 0 && n3 == 0 { return nil } + return PskSpot( + senderCallsign: clean, + frequencyHz: dialFreqHz + Int64(audioFreqHz), + snr: snr, + mode: "FT8", + senderLocator: grid.count >= 4 ? grid : nil, + flowStartSeconds: utcSeconds + ) + } +} + +/// Per-callsign-per-band dedup: a station is reported at most once per +/// `PskReporter.dedupWindowMs` on a given band (keyed by dial-frequency MHz, +/// like Android's "CALL|BAND" key). +public final class PskDedup { + private var lastSeen: [String: Int64] = [:] + + public init() {} + + /// Returns true if the spot should be reported (and marks it seen now); + /// false when it is a duplicate inside the window. + public func markIfFresh(call: String, dialFreqHz: Int64, nowMs: Int64) -> Bool { + let key = "\(call)|\(dialFreqHz / 1_000_000)" + if let t = lastSeen[key], nowMs - t < PskReporter.dedupWindowMs { return false } + lastSeen[key] = nowMs + return true + } +} + +/// Builds the IPFIX-style binary UDP datagrams PSK Reporter's collector +/// expects (report.pskreporter.info:4739). Byte-layout port of Android +/// `PskReporterSender`: 16-byte IPFIX header, options/data template sets on +/// the first sends (and hourly refresh), a receiver record (our station) and +/// N sender records (the spots), each datagram kept under the MTU budget. +/// +/// Pure byte-building — no sockets. Time is injected (`nowMs`) so packet +/// construction is fully deterministic and testable. +public final class PskReporterEncoder { + static let maxDatagram = 1400 + static let enterpriseNumber: UInt32 = 30351 // 0x0000768F + static let ipfixVersion: UInt16 = 0x000A + + // Template set IDs + static let templateSetIdOptions: UInt16 = 0x0003 // options template + static let templateSetIdData: UInt16 = 0x0002 // data template + + // Template IDs (== the data set IDs that reference them) + static let receiverTemplateId: UInt16 = 0x50E2 // 20706 + static let senderTemplateId: UInt16 = 0x50E3 // 20707 + + // PSKReporter's enterprise-specific element IDs under 30351. The collector + // maps element-ID -> column, so these must match exactly. + static let ftRxCallsign: UInt16 = 2 // receiverCallsign (0x8002) + static let ftRxLocator: UInt16 = 4 // receiverLocator (0x8004) + static let ftDecodingSw: UInt16 = 8 // decodingSoftware (0x8008) + static let ftRxAntenna: UInt16 = 9 // antennaInformation (0x8009) + static let ftRxRig: UInt16 = 13 // rigInformation (0x800D) + static let ftTxCallsign: UInt16 = 1 // senderCallsign (0x8001) + static let ftFrequency: UInt16 = 5 // frequency (0x8005) + static let ftSnr: UInt16 = 6 // sNR (0x8006) + static let ftMode: UInt16 = 10 // mode (0x800A) + static let ftTxLocator: UInt16 = 3 // senderLocator (0x8003) + static let ftInfoSource: UInt16 = 11 // informationSource (0x800B) + // flowStartSeconds is the STANDARD IANA IPFIX element 150 (0x0096): no + // enterprise bit, no enterprise number in its template descriptor. + static let ftFlowStart: UInt16 = 150 + + public let observationDomainId: UInt32 + public private(set) var sequenceNumber: UInt32 + private var packetsSent = 0 + private var lastTemplateSentMs: Int64 = 0 + + public init(observationDomainId: UInt32 = UInt32.random(in: 0...UInt32(Int32.max)), + sequenceNumber: UInt32 = 0) { + self.observationDomainId = observationDomainId + self.sequenceNumber = sequenceNumber + } + + /// Build one or more datagrams containing the given spots; each stays + /// under `maxDatagram` bytes. Advances the sequence number per packet and + /// tracks template cadence (first 3 packets and then hourly). + public func buildPackets( + rxCall: String, rxGrid: String, software: String, + antenna: String = "", rig: String = "", + spots: [PskSpot], nowMs: Int64 + ) -> [[UInt8]] { + guard !spots.isEmpty else { return [] } + var packets: [[UInt8]] = [] + let includeTemplates = needTemplates(nowMs: nowMs) + + var remaining = spots[...] + while !remaining.isEmpty { + let templateBytes: [UInt8] = includeTemplates ? Self.encodeTemplates() : [] + let receiverBytes = Self.encodeReceiverDataSet( + call: rxCall, grid: rxGrid, software: software, antenna: antenna, rig: rig) + + // Header is 16 bytes. + let overhead = 16 + templateBytes.count + receiverBytes.count + let budget = Self.maxDatagram - overhead + + // Encode as many sender records as fit. + var senderRecords: [[UInt8]] = [] + var senderSetSize = 4 // set header (setId + length) + var used = 0 + for spot in remaining { + let rec = Self.encodeSenderRecord(spot) + if senderSetSize + rec.count > budget && !senderRecords.isEmpty { break } + senderRecords.append(rec) + senderSetSize += rec.count + used += 1 + } + remaining = remaining.dropFirst(used) + + // Pad sender set to a 4-byte boundary. + let senderPadding = (4 - (senderSetSize % 4)) % 4 + senderSetSize += senderPadding + + let totalLen = 16 + templateBytes.count + receiverBytes.count + senderSetSize + var w = ByteWriter() + // IPFIX header + w.u16(Self.ipfixVersion) + w.u16(UInt16(totalLen)) + w.u32(UInt32(truncatingIfNeeded: nowMs / 1000)) + w.u32(sequenceNumber) + w.u32(observationDomainId) + sequenceNumber &+= 1 + packetsSent += 1 + + w.bytes.append(contentsOf: templateBytes) + w.bytes.append(contentsOf: receiverBytes) + + // Sender data set + w.u16(Self.senderTemplateId) + w.u16(UInt16(senderSetSize)) + for rec in senderRecords { w.bytes.append(contentsOf: rec) } + w.pad(senderPadding) + + packets.append(w.bytes) + if includeTemplates { lastTemplateSentMs = nowMs } + } + return packets + } + + func needTemplates(nowMs: Int64) -> Bool { + if packetsSent < 3 { return true } + if nowMs - lastTemplateSentMs > 3_600_000 { return true } + return false + } + + /// Receiver options template + sender data template, concatenated. + static func encodeTemplates() -> [UInt8] { + encodeReceiverTemplate() + encodeSenderTemplate() + } + + static func encodeReceiverTemplate() -> [UInt8] { + // Options template set (0x0003): template 0x50E2, 5 variable-length + // enterprise fields, scope field count 0. + let fieldCount = 5 + let templateRecordLen = 2 + 2 + 2 + fieldCount * 8 + let setLen = 4 + templateRecordLen + let padding = (4 - (setLen % 4)) % 4 + + var w = ByteWriter() + w.u16(templateSetIdOptions) + w.u16(UInt16(setLen + padding)) + w.u16(receiverTemplateId) + w.u16(UInt16(fieldCount)) + w.u16(0) // scope field count + for field in [ftRxCallsign, ftRxLocator, ftDecodingSw, ftRxAntenna, ftRxRig] { + w.u16(field | 0x8000) // enterprise bit + w.u16(0xFFFF) // variable length + w.u32(enterpriseNumber) + } + w.pad(padding) + return w.bytes + } + + static func encodeSenderTemplate() -> [UInt8] { + // Data template set (0x0002): template 0x50E3, 7 fields. Six are + // enterprise-specific (8-byte descriptor); flowStartSeconds is the + // standard element 0x0096 (4-byte descriptor, no enterprise number). + let templateRecordLen = 2 + 2 + 6 * 8 + 4 + let setLen = 4 + templateRecordLen + let padding = (4 - (setLen % 4)) % 4 + + var w = ByteWriter() + w.u16(templateSetIdData) + w.u16(UInt16(setLen + padding)) + w.u16(senderTemplateId) + w.u16(7) + // senderCallsign — variable length + w.u16(ftTxCallsign | 0x8000); w.u16(0xFFFF); w.u32(enterpriseNumber) + // frequency — uint32 + w.u16(ftFrequency | 0x8000); w.u16(4); w.u32(enterpriseNumber) + // sNR — int8 + w.u16(ftSnr | 0x8000); w.u16(1); w.u32(enterpriseNumber) + // mode — variable length + w.u16(ftMode | 0x8000); w.u16(0xFFFF); w.u32(enterpriseNumber) + // senderLocator — variable length + w.u16(ftTxLocator | 0x8000); w.u16(0xFFFF); w.u32(enterpriseNumber) + // informationSource — uint8 + w.u16(ftInfoSource | 0x8000); w.u16(1); w.u32(enterpriseNumber) + // flowStartSeconds — standard element, uint32, NO enterprise number + w.u16(ftFlowStart); w.u16(4) + w.pad(padding) + return w.bytes + } + + /// Receiver data set (our station info). Set ID = receiver template ID. + static func encodeReceiverDataSet( + call: String, grid: String, software: String, antenna: String, rig: String + ) -> [UInt8] { + let body = encodeVarString(call) + encodeVarString(grid) + + encodeVarString(software) + encodeVarString(antenna) + encodeVarString(rig) + let setLen = 4 + body.count + let padding = (4 - (setLen % 4)) % 4 + + var w = ByteWriter() + w.u16(receiverTemplateId) + w.u16(UInt16(setLen + padding)) + w.bytes.append(contentsOf: body) + w.pad(padding) + return w.bytes + } + + /// One sender record (one spot). No set header — these are concatenated + /// inside the sender data set. + static func encodeSenderRecord(_ spot: PskSpot) -> [UInt8] { + var w = ByteWriter() + w.bytes.append(contentsOf: encodeVarString(spot.senderCallsign)) + w.u32(UInt32(truncatingIfNeeded: spot.frequencyHz)) + w.u8(UInt8(bitPattern: Int8(truncatingIfNeeded: spot.snr))) + w.bytes.append(contentsOf: encodeVarString(spot.mode)) + w.bytes.append(contentsOf: encodeVarString(spot.senderLocator ?? "")) + w.u8(1) // informationSource = 1 (automatic) + w.u32(UInt32(truncatingIfNeeded: spot.flowStartSeconds)) + return w.bytes + } + + /// IPFIX variable-length string: 1-byte length prefix + UTF-8 bytes; a + /// length >= 255 uses a 3-byte prefix (0xFF + 2-byte big-endian length). + static func encodeVarString(_ s: String) -> [UInt8] { + let utf8 = Array(s.utf8) + if utf8.count < 255 { + return [UInt8(utf8.count)] + utf8 + } + return [0xFF, UInt8(utf8.count >> 8), UInt8(utf8.count & 0xFF)] + utf8 + } +} + +/// Tiny big-endian byte assembler (ByteBuffer.BIG_ENDIAN equivalent). +private struct ByteWriter { + var bytes: [UInt8] = [] + mutating func u8(_ v: UInt8) { bytes.append(v) } + mutating func u16(_ v: UInt16) { + bytes.append(UInt8(v >> 8)) + bytes.append(UInt8(v & 0xFF)) + } + mutating func u32(_ v: UInt32) { + bytes.append(UInt8((v >> 24) & 0xFF)) + bytes.append(UInt8((v >> 16) & 0xFF)) + bytes.append(UInt8((v >> 8) & 0xFF)) + bytes.append(UInt8(v & 0xFF)) + } + mutating func pad(_ n: Int) { + bytes.append(contentsOf: [UInt8](repeating: 0, count: n)) + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/QrzLogbookClient.swift b/ios/FT8AFKit/Sources/FT8Engine/QrzLogbookClient.swift new file mode 100644 index 00000000..ef188d55 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/QrzLogbookClient.swift @@ -0,0 +1,73 @@ +import Foundation + +/// Pure request-builders and response-parsers for the QRZ.com logbook API +/// (logbook.qrz.com/api). No networking here — the app-side `OnlineLogService` +/// performs the actual URLSession calls. Ports the wire shapes from Android +/// `ThirdPartyService` (uploadAdifToQrz / CheckQRZConnection / parseQrzResult). +public enum QrzLogbookClient { + + /// All QRZ logbook calls POST to this single endpoint. POST keeps the API + /// key (and ADIF) in the body rather than the URL, where it could leak via + /// proxies or server access logs. + public static let endpoint = "https://logbook.qrz.com/api" + + /// Form body for `ACTION=INSERT` uploading a single ADIF record. + public static func insertBody(apiKey: String, adif: String) -> String { + "KEY=\(formEncode(apiKey))&ACTION=INSERT&ADIF=\(formEncode(adif))" + } + + /// Form body for `ACTION=STATUS` (the test-connection call). + public static func statusBody(apiKey: String) -> String { + "KEY=\(formEncode(apiKey))&ACTION=STATUS" + } + + /// Extract the `RESULT` value from a QRZ response — an `&`-separated body + /// of KEY=VALUE pairs (e.g. `RESULT=OK&COUNT=1&LOGID=...`). Returns nil if + /// the response is nil/empty or carries no RESULT field. + public static func result(of response: String?) -> String? { + guard let response, !response.isEmpty else { return nil } + for pair in response.split(separator: "&") { + let kv = pair.split(separator: "=", maxSplits: 1, + omittingEmptySubsequences: false) + if kv.count > 1, kv[0] == "RESULT" { + return String(kv[1]) + } + } + return nil + } + + /// INSERT success: RESULT=OK, or RESULT=REPLACE (the QSO already existed + /// and was updated — still "the record is now on QRZ"). + public static func isInsertSuccess(_ response: String?) -> Bool { + let r = result(of: response) + return r == "OK" || r == "REPLACE" + } + + /// STATUS success: RESULT=OK only. + public static func isStatusOk(_ response: String?) -> Bool { + result(of: response) == "OK" + } + + /// application/x-www-form-urlencoded encoding matching Java's + /// `URLEncoder.encode(s, UTF-8)` (what the Android app sends): unreserved + /// `[A-Za-z0-9.*_-]` pass through, space becomes `+`, everything else is + /// %XX-escaped per UTF-8 byte. + public static func formEncode(_ s: String) -> String { + var out = "" + for byte in Array(s.utf8) { + switch byte { + case UInt8(ascii: "a")...UInt8(ascii: "z"), + UInt8(ascii: "A")...UInt8(ascii: "Z"), + UInt8(ascii: "0")...UInt8(ascii: "9"), + UInt8(ascii: "."), UInt8(ascii: "-"), + UInt8(ascii: "*"), UInt8(ascii: "_"): + out.append(Character(UnicodeScalar(byte))) + case UInt8(ascii: " "): + out.append("+") + default: + out += String(format: "%%%02X", byte) + } + } + return out + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/QsoRecord.swift b/ios/FT8AFKit/Sources/FT8Engine/QsoRecord.swift index 7ae9bfed..ca11ff67 100644 --- a/ios/FT8AFKit/Sources/FT8Engine/QsoRecord.swift +++ b/ios/FT8AFKit/Sources/FT8Engine/QsoRecord.swift @@ -20,6 +20,13 @@ public struct QsoRecord: Equatable, Codable { public var stationCallsign: String public var myGridsquare: String public var comment: String + /// Whether this QSO has been accepted by the Cloudlog/Wavelog server + /// (Android: QSLTable.synced_cloudlog). Defaults false; absent in logs + /// stored before online logging existed. + public var syncedCloudlog: Bool + /// Whether this QSO has been accepted by the QRZ.com logbook + /// (Android: QSLTable.synced_qrz). + public var syncedQrz: Bool public init( id: Int64? = nil, @@ -36,7 +43,9 @@ public struct QsoRecord: Equatable, Codable { freq: String, stationCallsign: String, myGridsquare: String, - comment: String + comment: String, + syncedCloudlog: Bool = false, + syncedQrz: Bool = false ) { self.id = id self.call = call @@ -53,5 +62,31 @@ public struct QsoRecord: Equatable, Codable { self.stationCallsign = stationCallsign self.myGridsquare = myGridsquare self.comment = comment + self.syncedCloudlog = syncedCloudlog + self.syncedQrz = syncedQrz + } + + /// Custom decode so JSON logs written before the sync flags existed still + /// load (the flags default to false when the keys are absent). Encoding + /// stays synthesized and always writes both flags. + public init(from decoder: Decoder) throws { + let c = try decoder.container(keyedBy: CodingKeys.self) + id = try c.decodeIfPresent(Int64.self, forKey: .id) + call = try c.decode(String.self, forKey: .call) + gridsquare = try c.decode(String.self, forKey: .gridsquare) + mode = try c.decode(String.self, forKey: .mode) + rstSent = try c.decode(String.self, forKey: .rstSent) + rstRcvd = try c.decode(String.self, forKey: .rstRcvd) + qsoDate = try c.decode(String.self, forKey: .qsoDate) + timeOn = try c.decode(String.self, forKey: .timeOn) + qsoDateOff = try c.decode(String.self, forKey: .qsoDateOff) + timeOff = try c.decode(String.self, forKey: .timeOff) + band = try c.decode(String.self, forKey: .band) + freq = try c.decode(String.self, forKey: .freq) + stationCallsign = try c.decode(String.self, forKey: .stationCallsign) + myGridsquare = try c.decode(String.self, forKey: .myGridsquare) + comment = try c.decode(String.self, forKey: .comment) + syncedCloudlog = try c.decodeIfPresent(Bool.self, forKey: .syncedCloudlog) ?? false + syncedQrz = try c.decodeIfPresent(Bool.self, forKey: .syncedQrz) ?? false } } diff --git a/ios/FT8AFKit/Tests/FT8AudioTests/AudioInputLevelTests.swift b/ios/FT8AFKit/Tests/FT8AudioTests/AudioInputLevelTests.swift new file mode 100644 index 00000000..9a30eec7 --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8AudioTests/AudioInputLevelTests.swift @@ -0,0 +1,108 @@ +import XCTest +import FT8Audio + +/// Guards the RX input-level classifier behind the waterfall's level meter +/// (port of the Android `AudioInputLevel` thresholds — the two platforms must +/// judge the same audio identically). +final class AudioInputLevelTests: XCTestCase { + + /// Sine buffer of a given peak amplitude; RMS = amplitude / sqrt(2). + private func sine(amplitude: Float, count: Int = 3000) -> [Float] { + (0..K1ABC \n" + ) + XCTAssertEqual(req?.url, "https://log.example.com/api/qso") + XCTAssertEqual( + req?.body, + "{\"key\":\"cl123\",\"station_profile_id\":\"5\",\"type\":\"adif\"," + + "\"string\":\"K1ABC \\n\"}" + ) + } + + func testQsoUploadRequestEndpointHasNoTrailingSlash() { + // Wavelog/Nextlog 308-redirect "api/qso/" on POST; the endpoint must be + // the documented slash-less form. + let req = CloudlogClient.qsoUploadRequest( + serverAddress: "https://log.example.com/", apiKey: "k", stationId: "1", adif: "x") + XCTAssertEqual(req?.url, "https://log.example.com/api/qso") + } + + func testQsoUploadRequestNilWhenAddressOrKeyMissing() { + XCTAssertNil(CloudlogClient.qsoUploadRequest( + serverAddress: "", apiKey: "k", stationId: "1", adif: "x")) + XCTAssertNil(CloudlogClient.qsoUploadRequest( + serverAddress: "https://log.example.com", apiKey: "", stationId: "1", adif: "x")) + } + + func testQsoUploadRequestBodyIsValidJson() throws { + let req = try XCTUnwrap(CloudlogClient.qsoUploadRequest( + serverAddress: "https://log.example.com", + apiKey: "k\"ey\\", stationId: "2", + adif: "tab\there\n \n")) + let obj = try XCTUnwrap( + JSONSerialization.jsonObject(with: Data(req.body.utf8)) as? [String: String]) + XCTAssertEqual(obj["key"], "k\"ey\\") + XCTAssertEqual(obj["station_profile_id"], "2") + XCTAssertEqual(obj["type"], "adif") + XCTAssertEqual(obj["string"], "tab\there\n \n") + } + + // MARK: authUrl + + func testAuthUrl() { + XCTAssertEqual( + CloudlogClient.authUrl(serverAddress: "https://log.example.com", apiKey: "cl123"), + "https://log.example.com/api/auth/cl123") + XCTAssertNil(CloudlogClient.authUrl(serverAddress: "", apiKey: "cl123")) + XCTAssertNil(CloudlogClient.authUrl(serverAddress: "https://log.example.com", apiKey: "")) + } + + // MARK: isAuthValid + + func testIsAuthValidCloudlogShape() { + XCTAssertTrue(CloudlogClient.isAuthValid( + "Validrw")) + } + + func testIsAuthValidToleratesWhitespaceAndXmlDeclaration() { + // Wavelog/Nextlog return the same markers with extra noise. + let noisy = """ + + + Valid + + rw + + + """ + XCTAssertTrue(CloudlogClient.isAuthValid(noisy)) + } + + func testIsAuthValidRejectsReadOnlyInvalidAndMissing() { + XCTAssertFalse(CloudlogClient.isAuthValid( + "Validr")) + XCTAssertFalse(CloudlogClient.isAuthValid( + "Invalidrw")) + XCTAssertFalse(CloudlogClient.isAuthValid("")) + XCTAssertFalse(CloudlogClient.isAuthValid(nil)) + } + + // MARK: isSuccessStatus + + func testIsSuccessStatus() { + XCTAssertTrue(CloudlogClient.isSuccessStatus(200)) + XCTAssertTrue(CloudlogClient.isSuccessStatus(201)) // Cloudlog answers HTTP_CREATED + XCTAssertFalse(CloudlogClient.isSuccessStatus(308)) + XCTAssertFalse(CloudlogClient.isSuccessStatus(401)) + XCTAssertFalse(CloudlogClient.isSuccessStatus(500)) + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/OnlineLogSyncTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/OnlineLogSyncTests.swift new file mode 100644 index 00000000..e208d03d --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/OnlineLogSyncTests.swift @@ -0,0 +1,119 @@ +import XCTest +import FT8Engine + +final class OnlineLogSyncTests: XCTestCase { + + private func rec( + id: Int64, call: String = "K1ABC", + syncedCloudlog: Bool = false, syncedQrz: Bool = false + ) -> QsoRecord { + QsoRecord(id: id, call: call, gridsquare: "FN42", mode: "FT8", rstSent: "-08", + rstRcvd: "-12", qsoDate: "20260618", timeOn: "123000", + qsoDateOff: "20260618", timeOff: "123115", band: "20M", freq: "14.074", + stationCallsign: "KD2OGR", myGridsquare: "FN20", comment: "", + syncedCloudlog: syncedCloudlog, syncedQrz: syncedQrz) + } + + // MARK: QsoRecord sync flags — persistence compatibility + + func testLegacyJsonWithoutSyncFlagsDecodesWithFalseDefaults() throws { + // Exactly what QsoLogStore wrote before the sync flags existed. + let legacy = """ + [{"id":1,"call":"W1AW","gridsquare":"FN31","mode":"FT8","rstSent":"-05", + "rstRcvd":"-08","qsoDate":"20260618","timeOn":"123000", + "qsoDateOff":"20260618","timeOff":"123115","band":"20M","freq":"14.074", + "stationCallsign":"KD2OGR","myGridsquare":"FN20","comment":""}] + """ + let records = try JSONDecoder().decode([QsoRecord].self, from: Data(legacy.utf8)) + XCTAssertEqual(records.count, 1) + XCTAssertEqual(records[0].call, "W1AW") + XCTAssertFalse(records[0].syncedCloudlog) + XCTAssertFalse(records[0].syncedQrz) + } + + func testSyncFlagsRoundTripThroughJson() throws { + let original = rec(id: 3, syncedCloudlog: true, syncedQrz: true) + let data = try JSONEncoder().encode([original]) + let decoded = try JSONDecoder().decode([QsoRecord].self, from: data) + XCTAssertEqual(decoded, [original]) + XCTAssertTrue(decoded[0].syncedCloudlog) + XCTAssertTrue(decoded[0].syncedQrz) + } + + func testMemberwiseInitDefaultsFlagsToFalse() { + let r = rec(id: 1) + XCTAssertFalse(r.syncedCloudlog) + XCTAssertFalse(r.syncedQrz) + } + + // MARK: unsynced selection (mirror of Android unsyncedFilter) + + func testUnsyncedEmptyWhenNoServiceEnabled() { + let records = [rec(id: 1), rec(id: 2)] + XCTAssertTrue(OnlineLogSync.unsynced(records, cloudlogEnabled: false, + qrzEnabled: false).isEmpty) + XCTAssertEqual(OnlineLogSync.unsyncedCount(records, cloudlogEnabled: false, + qrzEnabled: false), 0) + } + + func testUnsyncedCloudlogOnly() { + let records = [ + rec(id: 1, syncedCloudlog: true), + rec(id: 2, syncedCloudlog: false, syncedQrz: true), + ] + let out = OnlineLogSync.unsynced(records, cloudlogEnabled: true, qrzEnabled: false) + XCTAssertEqual(out.map(\.id), [2]) + } + + func testUnsyncedQrzOnly() { + let records = [ + rec(id: 1, syncedQrz: true), + rec(id: 2), + ] + let out = OnlineLogSync.unsynced(records, cloudlogEnabled: false, qrzEnabled: true) + XCTAssertEqual(out.map(\.id), [2]) + } + + func testUnsyncedBothServicesIsOrOfPending() { + let records = [ + rec(id: 1, syncedCloudlog: true, syncedQrz: true), // fully synced — skipped + rec(id: 2, syncedCloudlog: true, syncedQrz: false), // QRZ pending + rec(id: 3, syncedCloudlog: false, syncedQrz: true), // Cloudlog pending + rec(id: 4), // both pending + ] + let out = OnlineLogSync.unsynced(records, cloudlogEnabled: true, qrzEnabled: true) + XCTAssertEqual(out.map(\.id), [2, 3, 4]) + XCTAssertEqual(OnlineLogSync.unsyncedCount(records, cloudlogEnabled: true, + qrzEnabled: true), 3) + } + + // MARK: single-record ADIF for online uploads + + func testSingleRecordAdifGolden() { + let r = rec(id: 7) + let expected = + "K1ABC " + + "FN42 " + + "FT8 " + + "-08 " + + "-12 " + + "20260618 " + + "123000 " + + "20M " + + "20260618 " + + "123115 " + + "14.074 " + + "KD2OGR " + + "FN20 " + + " \n" + XCTAssertEqual(Adif.singleRecord(r), expected) + } + + func testSingleRecordAdifOmitsEmptyOptionalsAndHasNoHeader() { + let r = QsoRecord(call: "W1AW", gridsquare: "", mode: "", rstSent: "", + rstRcvd: "", qsoDate: "", timeOn: "", qsoDateOff: "", + timeOff: "", band: "", freq: "", stationCallsign: "", + myGridsquare: "", comment: "") + XCTAssertEqual(Adif.singleRecord(r), "W1AW \n") + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/PskReporterTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/PskReporterTests.swift new file mode 100644 index 00000000..dad38b27 --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/PskReporterTests.swift @@ -0,0 +1,233 @@ +import XCTest +@testable import FT8Engine + +final class PskReporterTests: XCTestCase { + + private func spot( + call: String = "K1ABC", freq: Int64 = 14_074_123, snr: Int = -12, + mode: String = "FT8", locator: String? = "FN42", flowStart: Int64 = 1_700_000_000 + ) -> PskSpot { + PskSpot(senderCallsign: call, frequencyHz: freq, snr: snr, mode: mode, + senderLocator: locator, flowStartSeconds: flowStart) + } + + private func u32be(_ v: UInt32) -> [UInt8] { + [UInt8((v >> 24) & 0xFF), UInt8((v >> 16) & 0xFF), + UInt8((v >> 8) & 0xFF), UInt8(v & 0xFF)] + } + + // MARK: encodeVarString + + func testEncodeVarStringShort() { + XCTAssertEqual(PskReporterEncoder.encodeVarString(""), [0]) + XCTAssertEqual(PskReporterEncoder.encodeVarString("AB"), [2, 0x41, 0x42]) + } + + func testEncodeVarStringLongUsesThreeBytePrefix() { + let s = String(repeating: "x", count: 300) + let out = PskReporterEncoder.encodeVarString(s) + XCTAssertEqual(Array(out.prefix(3)), [0xFF, 0x01, 0x2C]) // 300 = 0x012C + XCTAssertEqual(out.count, 303) + } + + // MARK: templates (golden bytes vs the Android layout) + + func testReceiverTemplateGoldenBytes() { + let ent: [UInt8] = [0x00, 0x00, 0x76, 0x8F] // enterprise 30351 + var expected: [UInt8] = [ + 0x00, 0x03, // options template set + 0x00, 0x34, // total length 52 (50 + 2 padding) + 0x50, 0xE2, // template id 20706 + 0x00, 0x05, // field count 5 + 0x00, 0x00, // scope field count 0 + ] + for field: UInt8 in [0x02, 0x04, 0x08, 0x09, 0x0D] { + expected += [0x80, field, 0xFF, 0xFF] + ent + } + expected += [0x00, 0x00] // padding to 4-byte boundary + XCTAssertEqual(PskReporterEncoder.encodeReceiverTemplate(), expected) + } + + func testSenderTemplateGoldenBytes() { + let ent: [UInt8] = [0x00, 0x00, 0x76, 0x8F] + var expected: [UInt8] = [ + 0x00, 0x02, // data template set + 0x00, 0x3C, // total length 60 (no padding needed) + 0x50, 0xE3, // template id 20707 + 0x00, 0x07, // field count 7 + ] + expected += [0x80, 0x01, 0xFF, 0xFF] + ent // senderCallsign, var + expected += [0x80, 0x05, 0x00, 0x04] + ent // frequency, uint32 + expected += [0x80, 0x06, 0x00, 0x01] + ent // sNR, int8 + expected += [0x80, 0x0A, 0xFF, 0xFF] + ent // mode, var + expected += [0x80, 0x03, 0xFF, 0xFF] + ent // senderLocator, var + expected += [0x80, 0x0B, 0x00, 0x01] + ent // informationSource, uint8 + expected += [0x00, 0x96, 0x00, 0x04] // flowStartSeconds: STANDARD element, no enterprise + XCTAssertEqual(PskReporterEncoder.encodeSenderTemplate(), expected) + } + + // MARK: receiver data set + + func testReceiverDataSetGoldenBytes() { + let out = PskReporterEncoder.encodeReceiverDataSet( + call: "K1ABC", grid: "FN20", software: "FT8AF 1.0", antenna: "", rig: "") + // body = 6 + 5 + 10 + 1 + 1 = 23; set = 27; padded to 28 + var expected: [UInt8] = [0x50, 0xE2, 0x00, 0x1C] + expected += [5] + Array("K1ABC".utf8) + expected += [4] + Array("FN20".utf8) + expected += [9] + Array("FT8AF 1.0".utf8) + expected += [0, 0] // empty antenna + rig + expected += [0] // padding + XCTAssertEqual(out, expected) + } + + // MARK: sender record + + func testSenderRecordGoldenBytes() { + let out = PskReporterEncoder.encodeSenderRecord(spot()) + var expected: [UInt8] = [5] + Array("K1ABC".utf8) + expected += u32be(14_074_123) + expected += [0xF4] // snr -12 as int8 + expected += [3] + Array("FT8".utf8) + expected += [4] + Array("FN42".utf8) + expected += [1] // informationSource = automatic + expected += u32be(1_700_000_000) + XCTAssertEqual(out, expected) + } + + func testSenderRecordNilLocatorEncodesEmptyString() { + let out = PskReporterEncoder.encodeSenderRecord(spot(locator: nil)) + // call(6) + freq(4) + snr(1) + mode(4) + locator(1: just the 0 length) + info(1) + flow(4) + XCTAssertEqual(out.count, 21) + XCTAssertEqual(out[15], 0) // empty-locator length prefix + } + + // MARK: buildPackets + + func testBuildPacketsHeaderAndTemplateInclusion() { + let enc = PskReporterEncoder(observationDomainId: 0x1234_5678, sequenceNumber: 0) + let nowMs: Int64 = 1_700_000_000_000 + let packets = enc.buildPackets( + rxCall: "KD2OGR", rxGrid: "FN20", software: "FT8AF 1.0", + spots: [spot()], nowMs: nowMs) + XCTAssertEqual(packets.count, 1) + let p = packets[0] + XCTAssertEqual(Array(p[0..<2]), [0x00, 0x0A]) // IPFIX version + XCTAssertEqual(Int(p[2]) << 8 | Int(p[3]), p.count) // declared length + XCTAssertEqual(Array(p[4..<8]), u32be(1_700_000_000)) // export time (s) + XCTAssertEqual(Array(p[8..<12]), u32be(0)) // sequence number + XCTAssertEqual(Array(p[12..<16]), u32be(0x1234_5678)) // observation domain + XCTAssertEqual(Array(p[16..<18]), [0x00, 0x03]) // templates included + XCTAssertEqual(p.count % 4, 0) // 4-byte aligned + XCTAssertEqual(enc.sequenceNumber, 1) + } + + func testTemplatesSentOnFirstThreePacketsThenOmittedThenHourlyRefresh() { + let enc = PskReporterEncoder(observationDomainId: 1, sequenceNumber: 0) + let nowMs: Int64 = 1_700_000_000_000 + func firstSetId(_ atMs: Int64) -> [UInt8] { + let p = enc.buildPackets(rxCall: "K", rxGrid: "AA00", software: "s", + spots: [spot()], nowMs: atMs)[0] + return Array(p[16..<18]) + } + XCTAssertEqual(firstSetId(nowMs), [0x00, 0x03]) // packet 1: templates + XCTAssertEqual(firstSetId(nowMs), [0x00, 0x03]) // packet 2: templates + XCTAssertEqual(firstSetId(nowMs), [0x00, 0x03]) // packet 3: templates + XCTAssertEqual(firstSetId(nowMs), [0x50, 0xE2]) // packet 4: receiver set directly + XCTAssertEqual(firstSetId(nowMs + 3_600_001), [0x00, 0x03]) // hourly refresh + XCTAssertEqual(firstSetId(nowMs + 3_600_002), [0x50, 0xE2]) // fresh again + } + + func testBuildPacketsSplitsAtMtu() { + let enc = PskReporterEncoder(observationDomainId: 1, sequenceNumber: 0) + let many = (0..<200).map { i in + spot(call: String(format: "K%03dABC", i), freq: 14_074_000 + Int64(i)) + } + let packets = enc.buildPackets(rxCall: "KD2OGR", rxGrid: "FN20", + software: "FT8AF 1.0", spots: many, + nowMs: 1_700_000_000_000) + XCTAssertGreaterThan(packets.count, 1) + for p in packets { + XCTAssertLessThanOrEqual(p.count, 1400) + XCTAssertEqual(p.count % 4, 0) + } + // Sequence advanced once per packet. + XCTAssertEqual(enc.sequenceNumber, UInt32(packets.count)) + } + + func testBuildPacketsEmptySpots() { + let enc = PskReporterEncoder(observationDomainId: 1, sequenceNumber: 0) + XCTAssertTrue(enc.buildPackets(rxCall: "K", rxGrid: "AA00", software: "s", + spots: [], nowMs: 0).isEmpty) + XCTAssertEqual(enc.sequenceNumber, 0) + } + + // MARK: dedup + + func testDedupWindowPerCallPerBand() { + let dedup = PskDedup() + let t0: Int64 = 1_000_000 + XCTAssertTrue(dedup.markIfFresh(call: "K1ABC", dialFreqHz: 14_074_000, nowMs: t0)) + // Same call+band inside the window: suppressed. + XCTAssertFalse(dedup.markIfFresh(call: "K1ABC", dialFreqHz: 14_074_000, nowMs: t0 + 1000)) + // Different band (dial MHz differs): fresh. + XCTAssertTrue(dedup.markIfFresh(call: "K1ABC", dialFreqHz: 7_074_000, nowMs: t0 + 1000)) + // Window expired: fresh again. + XCTAssertTrue(dedup.markIfFresh( + call: "K1ABC", dialFreqHz: 14_074_000, + nowMs: t0 + PskReporter.dedupWindowMs)) + } + + // MARK: makeSpot policy + + func testMakeSpotBuildsRfFrequencyFromDialPlusAudioOffset() { + let s = PskReporter.makeSpot( + callFrom: "K1ABC", i3: 1, n3: 0, grid: "FN42", snr: -7, + audioFreqHz: 1512, dialFreqHz: 14_074_000, myCall: "KD2OGR", + utcSeconds: 1_700_000_000) + XCTAssertEqual(s?.senderCallsign, "K1ABC") + XCTAssertEqual(s?.frequencyHz, 14_075_512) + XCTAssertEqual(s?.snr, -7) + XCTAssertEqual(s?.mode, "FT8") + XCTAssertEqual(s?.senderLocator, "FN42") + XCTAssertEqual(s?.flowStartSeconds, 1_700_000_000) + } + + func testMakeSpotStripsHashedCallsignBrackets() { + let s = PskReporter.makeSpot( + callFrom: "", i3: 1, n3: 0, grid: "", snr: 0, + audioFreqHz: 0, dialFreqHz: 14_074_000, myCall: "KD2OGR", utcSeconds: 0) + XCTAssertEqual(s?.senderCallsign, "K1ABC") + XCTAssertNil(s?.senderLocator) // short grid omitted + } + + func testMakeSpotDropsInvalidSelfAndFreeText() { + // Unresolved hash + XCTAssertNil(PskReporter.makeSpot( + callFrom: "<...>", i3: 1, n3: 0, grid: "", snr: 0, + audioFreqHz: 0, dialFreqHz: 0, myCall: "K", utcSeconds: 0)) + // Empty + XCTAssertNil(PskReporter.makeSpot( + callFrom: "", i3: 1, n3: 0, grid: "", snr: 0, + audioFreqHz: 0, dialFreqHz: 0, myCall: "K", utcSeconds: 0)) + // Our own callsign (case-insensitive) + XCTAssertNil(PskReporter.makeSpot( + callFrom: "kd2ogr", i3: 1, n3: 0, grid: "FN20", snr: 0, + audioFreqHz: 0, dialFreqHz: 0, myCall: "KD2OGR", utcSeconds: 0)) + // Free text (i3 = 0, n3 = 0) + XCTAssertNil(PskReporter.makeSpot( + callFrom: "K1ABC", i3: 0, n3: 0, grid: "FN42", snr: 0, + audioFreqHz: 0, dialFreqHz: 0, myCall: "KD2OGR", utcSeconds: 0)) + } + + // MARK: software string + + func testBuildSoftwareString() { + XCTAssertEqual(PskReporter.buildSoftwareString(version: "1.2", rigName: nil), + "FT8AF 1.2") + XCTAssertEqual(PskReporter.buildSoftwareString(version: "1.2", rigName: " "), + "FT8AF 1.2") + XCTAssertEqual(PskReporter.buildSoftwareString(version: "1.2", rigName: "IC-705"), + "FT8AF 1.2 (IC-705)") + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/QrzLogbookClientTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/QrzLogbookClientTests.swift new file mode 100644 index 00000000..564d733b --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/QrzLogbookClientTests.swift @@ -0,0 +1,79 @@ +import XCTest +import FT8Engine + +final class QrzLogbookClientTests: XCTestCase { + + func testEndpoint() { + XCTAssertEqual(QrzLogbookClient.endpoint, "https://logbook.qrz.com/api") + } + + // MARK: request bodies + + func testInsertBodyGolden() { + let body = QrzLogbookClient.insertBody( + apiKey: "ABCD-1234", + adif: "K1ABC \n" + ) + XCTAssertEqual( + body, + "KEY=ABCD-1234&ACTION=INSERT&ADIF=%3Ccall%3A5%3EK1ABC+%3Ceor%3E%0A") + } + + func testStatusBodyGolden() { + XCTAssertEqual(QrzLogbookClient.statusBody(apiKey: "AB CD"), + "KEY=AB+CD&ACTION=STATUS") + } + + // MARK: form encoding (Java URLEncoder-compatible) + + func testFormEncodePassesUnreservedCharacters() { + XCTAssertEqual(QrzLogbookClient.formEncode("AZaz09.-*_"), "AZaz09.-*_") + } + + func testFormEncodeSpaceIsPlus() { + XCTAssertEqual(QrzLogbookClient.formEncode("a b"), "a+b") + } + + func testFormEncodeEscapesReservedAndUtf8Bytes() { + XCTAssertEqual(QrzLogbookClient.formEncode("<>:&=+\n"), + "%3C%3E%3A%26%3D%2B%0A") + XCTAssertEqual(QrzLogbookClient.formEncode("é"), "%C3%A9") + } + + // MARK: response parsing + + func testResultParsesFirstResultField() { + XCTAssertEqual(QrzLogbookClient.result(of: "RESULT=OK&COUNT=1&LOGID=999"), "OK") + XCTAssertEqual(QrzLogbookClient.result(of: "STATUS=FAIL&RESULT=FAIL&REASON=bad key"), + "FAIL") + XCTAssertEqual(QrzLogbookClient.result(of: "RESULT=REPLACE&COUNT=1"), "REPLACE") + } + + func testResultNilForMissingEmptyOrMalformed() { + XCTAssertNil(QrzLogbookClient.result(of: nil)) + XCTAssertNil(QrzLogbookClient.result(of: "")) + XCTAssertNil(QrzLogbookClient.result(of: "COUNT=1&LOGID=2")) + XCTAssertNil(QrzLogbookClient.result(of: "RESULT")) // no '=' + } + + func testResultKeepsEqualsInsideValue() { + XCTAssertEqual(QrzLogbookClient.result(of: "RESULT=A=B"), "A=B") + } + + // MARK: success predicates + + func testIsInsertSuccessAcceptsOkAndReplace() { + XCTAssertTrue(QrzLogbookClient.isInsertSuccess("RESULT=OK&COUNT=1")) + // REPLACE = the QSO already existed and was updated — still a success. + XCTAssertTrue(QrzLogbookClient.isInsertSuccess("RESULT=REPLACE&COUNT=1")) + XCTAssertFalse(QrzLogbookClient.isInsertSuccess("RESULT=FAIL&REASON=x")) + XCTAssertFalse(QrzLogbookClient.isInsertSuccess(nil)) + } + + func testIsStatusOkAcceptsOnlyOk() { + XCTAssertTrue(QrzLogbookClient.isStatusOk("RESULT=OK&DATA=...")) + XCTAssertFalse(QrzLogbookClient.isStatusOk("RESULT=REPLACE")) + XCTAssertFalse(QrzLogbookClient.isStatusOk("RESULT=FAIL")) + XCTAssertFalse(QrzLogbookClient.isStatusOk(nil)) + } +} From 9422a5eef847a5247870f06f7180692c626509d9 Mon Sep 17 00:00:00 2001 From: Patrick Burns Date: Mon, 13 Jul 2026 15:11:49 -0500 Subject: [PATCH 3/5] iOS parity batch 2: TX/sequencer behavior + decode list parity - Auto-sequence toggles wired (autoCQAfterQSO->autoReturnToCq, real HUNT auto-answer, huntCallsCQ, autoCallFollow); earlyDecode documented inert - TX watchdog + stop-after-N via pure TxSupervisor - Late-start clip rule max(0, msIntoCycle - 2360) via TxTiming (with anti-modulo regression tests); txDelayMs honored, pttDelayMs inert on iOS - TUNE latching carrier with countdown + anti-click ramps (TuneTone) - Caller queue (CallerQueue policy + QUEUE row in ActiveQsoPanel) - ActiveQsoPanel header states, LOG/clear actions, TX stage selector chips - DecodeRow rebuilt to Android layout (accent bar, status pills with resolveQsoStatus priority, SNR bar, distance/age/DXCC line) - DXCC/continent prefix tables + DecodeAnnotator + display formatters - showOnlyCQ / dxOnly / continent filters wired; DecodeFilterSettings real Kit tests 298/298; simulator build green. Co-Authored-By: Claude Fable 5 --- .claude/tasks/session-current.md | 54 +++ ios/FT8AF/FT8AF/AppState.swift | 16 + .../FT8AF/Components/ActiveQsoPanel.swift | 225 ++++++++-- ios/FT8AF/FT8AF/Components/TxStrip.swift | 43 +- ios/FT8AF/FT8AF/Engine/LiveEngine.swift | 425 +++++++++++++++++- ios/FT8AF/FT8AF/Navigation/AppTabView.swift | 5 +- .../FT8AF/Screens/Decode/DecodeRow.swift | 374 +++++++++++---- .../FT8AF/Screens/Decode/DecodeScreen.swift | 190 +++++--- .../Settings/DecodeFilterSettings.swift | 125 ++++-- .../Settings/TransmissionSettings.swift | 22 +- .../Sources/FT8Engine/CallerQueue.swift | 105 +++++ .../Sources/FT8Engine/DecodeAnnotator.swift | 166 +++++++ .../Sources/FT8Engine/DisplayFormatters.swift | 53 +++ .../Sources/FT8Engine/DxccPrefix.swift | 333 ++++++++++++++ .../Sources/FT8Engine/QsoEngine.swift | 43 +- ios/FT8AFKit/Sources/FT8Engine/TuneTone.swift | 54 +++ .../Sources/FT8Engine/TxStageSelector.swift | 83 ++++ .../Sources/FT8Engine/TxSupervisor.swift | 84 ++++ ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift | 29 ++ .../FT8EngineTests/CallerQueueTests.swift | 106 +++++ .../FT8EngineTests/DecodeAnnotatorTests.swift | 166 +++++++ .../DisplayFormattersTests.swift | 90 ++++ .../FT8EngineTests/DxccPrefixTests.swift | 107 +++++ .../Tests/FT8EngineTests/QsoEngineTests.swift | 70 +++ .../Tests/FT8EngineTests/TuneToneTests.swift | 60 +++ .../FT8EngineTests/TxStageSelectorTests.swift | 86 ++++ .../FT8EngineTests/TxSupervisorTests.swift | 101 +++++ .../Tests/FT8EngineTests/TxTimingTests.swift | 50 +++ 28 files changed, 3020 insertions(+), 245 deletions(-) create mode 100644 ios/FT8AFKit/Sources/FT8Engine/CallerQueue.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/DecodeAnnotator.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/DisplayFormatters.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/DxccPrefix.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/TuneTone.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/TxStageSelector.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/TxSupervisor.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/CallerQueueTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/DecodeAnnotatorTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/DisplayFormattersTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/DxccPrefixTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/TuneToneTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/TxStageSelectorTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/TxSupervisorTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/TxTimingTests.swift diff --git a/.claude/tasks/session-current.md b/.claude/tasks/session-current.md index 3e1ed599..46bc969a 100644 --- a/.claude/tasks/session-current.md +++ b/.claude/tasks/session-current.md @@ -106,3 +106,57 @@ App — `ios/FT8AF/FT8AF/`: - AppState.swift untouched (pre-staged fields used as-is). QsoLogStore untouched — Codable back-compat handled inside QsoRecord. - PSK spots queued while capture runs are flushed by the service's own 5-min timer even if the engine stops; `flushPsk` is also callable on demand if a stop-flush is ever wanted. - QRZ REPLACE responses count as success (record already on QRZ), matching Android. + +### Agent R — Decode UI parity + +**Status: COMPLETE** — kit tests 256/256 green (`swift test`; 209 baseline + 47 new), simulator build green (`xcodebuild` iPhone 16 Pro), `xcodegen generate` re-run. Not committed (coordinator commits). + +**What was built** (parity with Android `ui/decode/DecodeRow.kt`, `resolveQsoStatus`, `StatusPill.kt`, `SignalBar.kt`, `ui/settings/DecodeFilterSettings.kt`, `filterMessages` in `DecodeScreen.kt`): + +Kit — pure, host-tested logic in `ios/FT8AFKit/Sources/FT8Engine/`: +- `DxccPrefix.swift` — compact prefix→DXCC-entity heuristic (~200 prefixes / ~130 entities incl. all task spot-checks W/K/N/A→USA, JA→Japan, DL→Germany, G/M/2E→England + GM/GW/GI regions, UA/R→Russia with 8/9/0-area Asiatic split, VK, PY, EA (+EA8 Canary AF), OH, ZL, VE…), longest-prefix match over `/`-separated segments (EA8/W1AW→Canary Is, W1AW/P→USA), continent codes NA/SA/EU/AF/AS/OC/AN. +- `DecodeAnnotator.swift` — `DecodeHighlight` enum, `HighlightToggles`, `LoggedQso`/`LogbookIndex` (O(1) worked calls / grid4s / bands-per-call / DXCC entities), `classifyDecode` with Android's priority PENDING > POTA > NEW DXCC > NEW GRID > NEW BAND > WORKED > CQ, each logbook category gated by its toggle (disabled → falls through). Helpers `isCQMessage`, `isDirectedToMe`, `isPotaCq`, `looksLikeParkRef`. RR73/short grids never count as new grids (reuses `gridToLatLon` validity). +- `DisplayFormatters.swift` — `relativeAge` (now/Ns/Nm/Nh/Nd buckets, clamps negative), `gridDistanceKm` (haversine over `gridToLatLon`), `kilometersToMiles`, `formatQsoDistance` (whole km/mi, empty when it rounds to 0), `gridDistanceText`. +- Tests (47 new): `DxccPrefixTests` (14), `DecodeAnnotatorTests` (21 — priority order, all four toggle fall-throughs, park-ref shape, grid validity, index normalization), `DisplayFormattersTests` (12 — age buckets/edges, FN31→JO31 ≈5900 km, unit conversion, zero suppression). + +App — `ios/FT8AF/FT8AF/`: +- `Screens/Decode/DecodeRow.swift` — full Android-style rewrite: 3pt left accent bar (target pink > CQ amber), CALLING/CQ/TO YOU label chips (stack like Android), bold mono 15pt callsign (cyan to-me / pink target), grid, status pill right; message-text line; meta row = 5-segment SignalBar (Android thresholds −5/−12/−18, fill (snr+25)/30) + SNR dB + freq Hz + distance (km/mi per setting) + relative age; DXCC-entity location line with globe icon (USA/UK shorthand); backgrounds to-me signal 0.08/0.22, target TargetSoft/Border, CQ bgSurface/borderSubtle; third-party-QSO rows dimmed to 0.8; compact variant kept (denser paddings, 22pt bar, no msg/location lines). Status pill = 4pt-corner chip, dot + label, hue 0.12 bg / 0.28 border: pending amber, POTA green, NEW DXCC purple, NEW GRID yellow, NEW BAND/WORKED cyan, CQ amber. +- `Screens/Decode/DecodeScreen.swift` — base filter stage ANDs with chips like Android `filterMessages`: blocklist → showOnlyCQ → dxOnly (my continent from my call prefix; unknown continents excluded, matching Android) → continentFilter ≠ "All". Chips now share kit logic (`isCQMessage`, `isPotaCq`, New DXCC via `LogbookIndex.workedEntities`, Needed via worked calls, For Me via `isDirectedToMe`). Rows get `highlight` from `classifyDecode`, `isTarget` from `tx.targetCall`, live `now` from a 1s `TimelineView` (drives the age labels without per-row timers). +- `Screens/Settings/DecodeFilterSettings.swift` — "Phase 4" placeholder removed; real controls: Show-Only-CQ + DX-Only toggles, continent picker (All/NA/SA/EU/AF/AS/OC/AN), four highlight toggles (NewDxcc/NewGrid/NewBand/Worked) with category-colored dots, distance-unit picker (km/mi), Blocked Callsigns row shows the real count and links to `BlockedCallsignsSettings`. All persist via `SettingsPersistence` onChange. +- `AppState.swift` — DecodeMessage struct ONLY: added `var arrival: Date = Date()` (defaulted, so LiveEngine's existing constructor calls capture arrival at decode time — LiveEngine untouched per ownership). + +**Coordinator notes:** +- NEW BAND uses the current `settings.band` at render time; a decode that survives a band change would be re-classified against the new band. Deriving per-message band would need LiveEngine to stamp it — deliberately avoided per ownership rules; cosmetic only. +- No iOS `highlightPota` setting exists in the pre-staged SettingsState, so POTA pills are always-on (Android gates them). Add the field later if wanted — annotator already isolates the category. +- Android's PENDING pill is textMuted; the task brief specified amber for pending — followed the brief. +- Ran `xcodegen generate`; pbxproj regenerated (kit files need no project entries; no new app-target files were added). + +### Agent B — Engine/TX parity + +**Status: COMPLETE** — kit tests green (298/298, `swift test`), simulator build green (`xcodebuild` iPhone 16 Pro), `xcodegen generate` run. Not committed (coordinator commits). Agents D/W's LiveEngine work (PSK hand-off, online-log hook, waterfall loop) preserved untouched; extended around it. + +**1. Auto-sequence toggles wired** +- `autoCQAfterQSO` → `QsoEngine.autoReturnToCq` (set at engine start + every `syncSettingsToQso`). **Behavior change**: default OFF now means the sequencer *stops* after each completed QSO (it previously always returned to CQ); enable the setting for the old behavior. +- HUNT is now real behavior, not just a UI flag: while armed with no target locked, `autoAnswerIfHunting` answers the strongest decoded CQ (skips blocked callsigns, own call, hashed `<...>` calls, and already-worked stations from the logbook), sets `slotParity` to the opposite of the heard slot, and toasts "Answering X". +- `huntCallsCQ`: hunting with no CQ candidates and the engine idle → calls CQ itself (hunt+CQ hybrid; still interrupts to answer a CQ when one appears). +- `autoCallFollow`: while idle (not hunting-locked, engine inactive), a station calling *us* directly is auto-answered. +- `earlyDecode`: **inert on iOS** — the decode pipeline runs once at slot end (`runDecodeLoop` polls slot boundaries); a mid-slot early pass needs a decode-loop restructure that's out of scope. Setting remains persisted; noted here + nothing pretends to use it. + +**2. TX watchdog + stop-after-N** — pure `TxSupervisor` in kit (`Sources/FT8Engine/TxSupervisor.swift`): watchdog = N minutes since last "progress" event (armed at CQ/answer/new-target/force-log; refreshed on QSO completion when auto-CQ is on and on hunt idle-listening slots, mirroring Android's `launchSupervision`; a *stuck* QSO deliberately ages it); stop-after-N = N consecutive transmissions of the same message with no reply (reply or message change resets). LiveEngine checks `stopReason` before every TX cycle → `stopTx()` + toast ("TX watchdog: stopped after N min" / "No reply after N attempts — TX stopped"). 0 = off for both. + +**3. Late-start clip rule** — pure `TxTiming` in kit: `lateStartClipMs = max(0, msIntoCycle − slack)` (slack = `settings.lateStartToleranceMs`, default 2360), **never** modulo — tests explicitly guard the `% 15000` trap (700 ms in → 0 clipped; 3000 ms → 640; 14900 ms → 12540). Applied at actual playback time in `beginTxPlayback`; hopelessly-late TX (clip ≥ waveform) skips the cycle. `txDelayMs` honored as a pre-play delay (cancellable `txScheduleTask`). `pttDelayMs`: **inert on iOS** — there is no PTT line (AVAudioSession/VOX keying is the audio itself), so a keying settle delay has nothing to wait for; documented here, setting untouched. + +**4. TUNE button** — `TuneTone` in kit: pure sine generator (raised-cosine 10 ms anti-click ramps, amplitude cap) + `chipLabel` ("TUNE 7s"). LiveEngine `toggleTune`/`startTune`/`stopTune`: buffer length = `settings.tuneTimeoutSec` so playback ending enforces the hard max-on even if the ticker dies; 1 Hz countdown into `tx.tuneRemainingSec`; stops on tap, timeout, CALL CQ, answer, or engine stop. TUNE pill in TxStrip info row: red (`statusBad`) + bold + countdown while active, dimmed/disabled while the sequencer is armed/transmitting (mutually exclusive with FT8 TX, as Android). Tune Timeout picker (10/15/30/60/120 s) added to TransmissionSettings' TX Safety section. + +**5. Caller queue** — pure `CallerQueue` in kit: dedup by callsign (re-hear refreshes SNR/freq/time, keeps position), capacity 10 (Android `MAX_QUEUE_SIZE`), stale-prune after 60 s (~4 slots), `shouldEnqueue` policy (never self/target/blocked). LiveEngine enqueues stations calling me that aren't the target while the sequencer is active; auto-dequeues the next caller once the previous QSO fully wraps (incl. after the courtesy-73 slot, and even when auto-CQ is off — dequeue takes precedence over stopping, as Android). `ActiveQsoPanel` shows a wrapping QUEUE row (custom `Layout` flow); tapping a callsign = `forceLogAndMoveOn(nextCallsign:)` — logs the current QSO when it progressed far enough, then works that caller (replying with a report when their grid is known, mirroring Android's `checkFunOrderByExtraInfo + 1`). + +**6. ActiveQsoPanel parity** — header states via pure `QsoPanelHeader` in kit (Hunting… / Calling CQ / QSOing with X / Waiting for X, target SNR in cyan; hunt+CQ hybrid correctly reads "Calling CQ" per the Android rule); LOG action (`QsoEngine.forceLog()` — logs only when reports were exchanged, à la Android `shouldForceLog`); ✕ = `QsoEngine.abandonToCq()` (back to CQ or idle per auto-CQ, nothing logged); TX selector chips CQ/GRID/RPT/R-RPT/RR73/73 wired to `engine.selectTxStage` → `QsoEngine.setStage` (active = amber accent + border, completed = cyan signal; pure `TxStageSelector` rules in kit). Conversation log + stage dots kept. Panel now also shows while HUNT is armed (so the Hunting state has somewhere to live) — one-line visibility condition in AppTabView alongside the `onToggleTune` callback wiring. + +**7. TxStrip parity** — TUNE pill added to the info row (see #4); the mode pill was already a static non-interactive "FT8" chip — kept as-is per the brief (FT4/FT2 deferred). HUNT / CALL CQ / TX1-TX2 layout unchanged. + +**Files** (all within ownership): kit — `Sources/FT8Engine/{TxSupervisor,TxTiming,CallerQueue,TuneTone,TxStageSelector}.swift` (new), `QsoEngine.swift` (added `forceLog`/`abandonToCq`, factored `makeRecord`); kit tests — `Tests/FT8EngineTests/{TxSupervisorTests,TxTimingTests,CallerQueueTests,TuneToneTests,TxStageSelectorTests}.swift` (new, 37 tests) + 6 new cases in `QsoEngineTests`; app — `Engine/LiveEngine.swift`, `Components/{ActiveQsoPanel,TxStrip}.swift`, `Navigation/AppTabView.swift` (callback + panel-visibility line), `Screens/Settings/TransmissionSettings.swift` (tune-timeout row), `AppState.swift` (TxState body only: `qsoStage`, `targetSnr`, `queuedCallers`, `isTuning`, `tuneRemainingSec`). `TxPlayerService.swift`/`SlotTimerBar.swift`/`TransmitGlow.swift` needed no changes. + +**Coordinator notes:** +- Inert-by-platform: `earlyDecode` (slot-end decode pipeline), `pttDelayMs` (no PTT line on iOS). Both documented above; everything else in the TX/sequencer settings group now does something real. +- `autoCQAfterQSO` default-off behavior change called out in #1 — flag in the PR body. +- TUNE plays through the same `TxPlayerService`/AVAudioEngine speaker path as FT8 TX; on-device it keys VOX like any TX audio. Requires RX running (toast prompts if not). diff --git a/ios/FT8AF/FT8AF/AppState.swift b/ios/FT8AF/FT8AF/AppState.swift index 1c41048e..9a7b8433 100644 --- a/ios/FT8AF/FT8AF/AppState.swift +++ b/ios/FT8AF/FT8AF/AppState.swift @@ -53,6 +53,12 @@ struct DecodeMessage: Identifiable, Equatable { var grid: String var extra: String var slotIndex: Int + /// Wall-clock arrival of the decode. Defaulted to construction time so + /// every existing `DecodeMessage(...)` call site (LiveEngine creates + /// messages the moment a slot decodes) gets the correct arrival without + /// changes; the decode list derives the relative "now / 32s / 5m" age + /// from it. + var arrival: Date = Date() static let mock: [DecodeMessage] = [ DecodeMessage(utcTime: "12:30:00", callFrom: "W1AW", callTo: "CQ", snr: -5, freqHz: 1120, grid: "FN31", extra: "FN31", slotIndex: 0), @@ -274,6 +280,16 @@ final class TxState { var conversationLog: [QsoLogEntry] = [] /// Set by the engine when our CQ is answered, to auto-open QsoSheet. var autoOpenMessage: DecodeMessage? + /// The QSO engine's raw sequencer stage, mirrored by LiveEngine for the + /// TX message-selector chips (`TxStageSelector`). + var qsoStage: TxStage = .idle + /// Last SNR heard from the current target (for the Active QSO header). + var targetSnr: Int? + /// Callsigns waiting their turn (mirror of LiveEngine's `CallerQueue`). + var queuedCallers: [String] = [] + /// TUNE carrier state: latched steady tone at the TX audio frequency. + var isTuning: Bool = false + var tuneRemainingSec: Int = 0 } // MARK: - POTA diff --git a/ios/FT8AF/FT8AF/Components/ActiveQsoPanel.swift b/ios/FT8AF/FT8AF/Components/ActiveQsoPanel.swift index 42a4dbc7..8c052ccc 100644 --- a/ios/FT8AF/FT8AF/Components/ActiveQsoPanel.swift +++ b/ios/FT8AF/FT8AF/Components/ActiveQsoPanel.swift @@ -1,6 +1,9 @@ +import FT8Engine import SwiftUI -/// Floating panel showing active QSO progress above the TxStrip. +/// Floating panel showing active QSO progress above the TxStrip (Android +/// parity: header states + SNR, LOG / ✕ actions, conversation log, TX +/// message-selector chips, caller queue, stage dots). struct ActiveQsoPanel: View { @Environment(AppState.self) private var appState @State private var isExpanded = true @@ -15,29 +18,74 @@ struct ActiveQsoPanel: View { } } + private var headerState: QsoPanelHeader.State { + QsoPanelHeader.state( + targetCall: appState.tx.targetCall, + isActivated: appState.tx.isActivated, + huntEnabled: appState.tx.huntEnabled, + huntCallsCQ: appState.settings.huntCallsCQ, + isTransmitting: appState.tx.isTransmitting + ) + } + + private var headerLabel: String { + switch headerState { + case .hunting: return "Hunting…" + case .callingCq: return "Calling CQ" + case .qsoing(let dx): return "QSOing with \(dx)" + case .waiting(let dx): return "Waiting for \(dx)" + case .idle: return "Idle" + } + } + + private var hasTarget: Bool { !appState.tx.targetCall.isEmpty } + private func expandedView(tx: TxState) -> some View { VStack(spacing: 8) { - // Header row with target call and collapse button - HStack { - Image(systemName: "antenna.radiowaves.left.and.right") - .font(.system(size: 12, weight: .semibold)) + // Header row: QSO label + state + SNR · LOG · ✕ · collapse + HStack(spacing: 8) { + Text("QSO") + .font(.system(size: 10, weight: .semibold, design: .monospaced)) + .foregroundStyle(textFaint) + + Text(headerLabel) + .font(.system(size: 13, weight: .bold, design: .monospaced)) .foregroundStyle(accent) + .lineLimit(1) - Text(tx.targetCall.isEmpty ? "CQ" : tx.targetCall) - .font(.system(size: 16, weight: .bold, design: .monospaced)) - .foregroundStyle(textPrimary) + if hasTarget, let snr = tx.targetSnr { + Text(snr >= 0 ? "+\(snr)dB" : "\(snr)dB") + .font(.system(size: 11, weight: .medium, design: .monospaced)) + .foregroundStyle(signal) + } - Spacer() + Spacer(minLength: 4) - Text(stageLabel(tx.stage)) - .font(.system(size: 11, weight: .semibold, design: .monospaced)) - .foregroundStyle(accent) - .padding(.horizontal, 8) - .padding(.vertical, 3) - .background( - RoundedRectangle(cornerRadius: 4) - .fill(accent.opacity(0.14)) - ) + if hasTarget { + // LOG: force-log the current QSO and move on. + Button { + appState.engine?.forceLogAndMoveOn() + } label: { + Text("LOG") + .font(.system(size: 10, weight: .bold, design: .monospaced)) + .foregroundStyle(signal) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background(RoundedRectangle(cornerRadius: 6).fill(signalSoft)) + } + .buttonStyle(.plain) + + // ✕: abandon the target (back to CQ / idle, nothing logged). + Button { + appState.engine?.clearActiveQso() + } label: { + Image(systemName: "xmark") + .font(.system(size: 11, weight: .semibold)) + .foregroundStyle(textMuted) + .frame(width: 22, height: 22) + } + .buttonStyle(.plain) + } Button { withAnimation(.easeInOut(duration: 0.2)) { @@ -47,6 +95,7 @@ struct ActiveQsoPanel: View { Image(systemName: "chevron.down") .font(.system(size: 10, weight: .semibold)) .foregroundStyle(textMuted) + .frame(width: 22, height: 22) } .buttonStyle(.plain) } @@ -85,6 +134,18 @@ struct ActiveQsoPanel: View { ) } + // TX message selector chips (CQ / GRID / RPT / R-RPT / RR73 / 73) + TxSelectorRow(currentStage: tx.qsoStage) { stage in + appState.engine?.selectTxStage(stage) + } + + // Caller queue — tap a callsign to log the current QSO and work them + if !tx.queuedCallers.isEmpty { + CallerQueueBar(callers: tx.queuedCallers) { callsign in + appState.engine?.forceLogAndMoveOn(nextCallsign: callsign) + } + } + // Stage progress dots QsoStageDots(currentStage: tx.stage) } @@ -113,13 +174,15 @@ struct ActiveQsoPanel: View { .fill(accent) .frame(width: 6, height: 6) - Text("QSO: \(tx.targetCall.isEmpty ? "CQ" : tx.targetCall)") + Text(headerLabel) .font(.system(size: 12, weight: .semibold, design: .monospaced)) .foregroundStyle(textPrimary) - Text("— \(stageLabel(tx.stage))") - .font(.system(size: 11, weight: .medium, design: .monospaced)) - .foregroundStyle(textMuted) + if !tx.queuedCallers.isEmpty { + Text("+\(tx.queuedCallers.count)") + .font(.system(size: 10, weight: .bold, design: .monospaced)) + .foregroundStyle(signal) + } Spacer() @@ -142,14 +205,118 @@ struct ActiveQsoPanel: View { } .buttonStyle(.plain) } +} + +// MARK: - TX message selector + +/// Chip row wired to `QsoEngine.setStage` via the engine; the active chip is +/// amber, chips for stages already passed are cyan (Android `TxSelector`). +private struct TxSelectorRow: View { + let currentStage: TxStage + let onSelect: (TxStage) -> Void - private func stageLabel(_ stage: TxUIStage) -> String { - switch stage { - case .idle: return "IDLE" - case .cqSent: return "CQ" - case .reportSent: return "REPORT" - case .rrSent: return "RR73" - case .complete: return "DONE" + var body: some View { + HStack(spacing: 4) { + Text("TX:") + .font(.system(size: 10, weight: .semibold, design: .monospaced)) + .foregroundStyle(textFaint) + + ForEach(TxStageSelector.chips, id: \.rawValue) { chip in + let isActive = TxStageSelector.isActive(chip, current: currentStage) + let isCompleted = TxStageSelector.isCompleted(chip, current: currentStage) + + Button { + onSelect(chip) + } label: { + Text(TxStageSelector.label(for: chip)) + .font(.system(size: 10, weight: isActive ? .bold : .medium, design: .monospaced)) + .foregroundStyle(isActive ? accent : (isCompleted ? signal : textMuted)) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background( + RoundedRectangle(cornerRadius: 6) + .fill(isActive ? accentSoft : (isCompleted ? signalSoft : bgSurface3)) + ) + .overlay( + RoundedRectangle(cornerRadius: 6) + .strokeBorder(isActive ? accent.opacity(0.5) : .clear, lineWidth: 1) + ) + } + .buttonStyle(.plain) + } + + Spacer(minLength: 0) + } + } +} + +// MARK: - Caller queue + +/// Wrapping row of stations waiting their turn; tapping one logs the current +/// QSO (when it progressed far enough) and works that caller next. +private struct CallerQueueBar: View { + let callers: [String] + let onTap: (String) -> Void + + var body: some View { + HStack(alignment: .firstTextBaseline, spacing: 4) { + Text("QUEUE") + .font(.system(size: 9, weight: .semibold, design: .monospaced)) + .foregroundStyle(textFaint) + + WrappingHStack(spacing: 4, lineSpacing: 3) { + ForEach(callers, id: \.self) { callsign in + Button { + onTap(callsign) + } label: { + Text(callsign) + .font(.system(size: 10, weight: .medium, design: .monospaced)) + .foregroundStyle(signal) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(RoundedRectangle(cornerRadius: 4).fill(signalSoft)) + } + .buttonStyle(.plain) + } + } + } + .frame(maxWidth: .infinity, alignment: .leading) + } +} + +/// Minimal flow layout so queued callsigns wrap to new lines (Android FlowRow). +private struct WrappingHStack: Layout { + var spacing: CGFloat = 4 + var lineSpacing: CGFloat = 3 + + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + let maxWidth = proposal.width ?? .infinity + var x: CGFloat = 0, y: CGFloat = 0, lineHeight: CGFloat = 0 + for view in subviews { + let size = view.sizeThatFits(.unspecified) + if x > 0, x + size.width > maxWidth { + x = 0 + y += lineHeight + lineSpacing + lineHeight = 0 + } + x += size.width + spacing + lineHeight = max(lineHeight, size.height) + } + return CGSize(width: maxWidth == .infinity ? x : maxWidth, height: y + lineHeight) + } + + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + var x = bounds.minX, y = bounds.minY, lineHeight: CGFloat = 0 + for view in subviews { + let size = view.sizeThatFits(.unspecified) + if x > bounds.minX, x + size.width > bounds.maxX { + x = bounds.minX + y += lineHeight + lineSpacing + lineHeight = 0 + } + view.place(at: CGPoint(x: x, y: y), proposal: .unspecified) + x += size.width + spacing + lineHeight = max(lineHeight, size.height) } } } diff --git a/ios/FT8AF/FT8AF/Components/TxStrip.swift b/ios/FT8AF/FT8AF/Components/TxStrip.swift index cd30af50..bd0e7588 100644 --- a/ios/FT8AF/FT8AF/Components/TxStrip.swift +++ b/ios/FT8AF/FT8AF/Components/TxStrip.swift @@ -1,3 +1,4 @@ +import FT8Engine import SwiftUI import UIKit @@ -9,6 +10,7 @@ struct TxStrip: View { var onCallCQ: () -> Void = {} var onStop: () -> Void = {} var onToggleSlot: () -> Void = {} + var onToggleTune: () -> Void = {} var onOpenFrequencyPicker: () -> Void = {} var onVolumeChange: (Int) -> Void = { _ in } @@ -32,8 +34,10 @@ struct TxStrip: View { // CAT status chip CatChip(status: appState.rig.connectionStatus) - // Right: mode + frequency chips + // Right: mode + frequency + tune chips HStack(spacing: 6) { + // Mode pill — static "FT8" (FT4/FT2 deferred; plain, + // non-interactive). TxChip(label: "FT8", color: accent) // Tappable frequency/band chip @@ -41,6 +45,17 @@ struct TxStrip: View { TxChip(label: settings.band, color: textPrimary) } .buttonStyle(.plain) + + // TUNE pill: latches a steady carrier at the TX audio + // frequency; red with a countdown while active. Locked off + // while the sequencer is armed/transmitting — tune and FT8 + // TX are mutually exclusive. + TunePill( + isTuning: tx.isTuning, + remainingSec: tx.tuneRemainingSec, + locked: tx.isActivated || tx.isTransmitting, + action: onToggleTune + ) } // Expand/collapse chevron @@ -163,6 +178,32 @@ struct TxStrip: View { // MARK: - Subviews +/// TUNE toggle pill: idle = plain chip; active = statusBad red with the +/// remaining-seconds countdown (label via the kit's `TuneTone.chipLabel`); +/// dimmed/disabled while the sequencer owns the transmitter. +private struct TunePill: View { + let isTuning: Bool + let remainingSec: Int + let locked: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + Text(TuneTone.chipLabel(isTuning: isTuning, remainingSec: remainingSec)) + .font(.system(size: 11, weight: isTuning ? .bold : .semibold, design: .monospaced)) + .foregroundStyle(isTuning ? .white : (locked ? textMuted.opacity(0.4) : textMuted)) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background( + RoundedRectangle(cornerRadius: 6) + .fill(isTuning ? statusBad : bgSurface3.opacity(locked ? 0.4 : 1.0)) + ) + } + .buttonStyle(.plain) + .disabled(locked && !isTuning) + } +} + private struct CatChip: View { let status: ConnectionStatus diff --git a/ios/FT8AF/FT8AF/Engine/LiveEngine.swift b/ios/FT8AF/FT8AF/Engine/LiveEngine.swift index e0bb184e..276c9845 100644 --- a/ios/FT8AF/FT8AF/Engine/LiveEngine.swift +++ b/ios/FT8AF/FT8AF/Engine/LiveEngine.swift @@ -29,6 +29,15 @@ final class LiveEngine { // QSO engine — created once start() has settings available. private var qsoEngine: QsoEngine? + // TX safety nets (watchdog + stop-after-N) — pure counting in the kit type. + private var supervisor = TxSupervisor(nowMs: 0) + // Stations that called us while we were busy — pure policy in the kit type. + private var callerQueue = CallerQueue() + // Pending delayed TX start (honors txDelayMs); cancelled by stopTx(). + private var txScheduleTask: Task? + // TUNE carrier countdown ticker. + private var tuneTask: Task? + // MARK: - Public control methods (called from UI) /// Start audio capture and kick off decode + waterfall loops. @@ -56,8 +65,14 @@ final class LiveEngine { let qso = QsoEngine(myCall: settings.myCall, myGrid: settings.myGrid) qso.band = settings.band qso.freqMhz = bandToFreqMhz(settings.band) + qso.autoReturnToCq = settings.autoCQAfterQSO qsoEngine = qso + // Fresh TX safety-net + caller-queue state for this session. + supervisor = TxSupervisor(nowMs: Int64(Date().timeIntervalSince1970 * 1000)) + callerQueue.removeAll() + appState.tx.queuedCallers = [] + // Bring up the WSJT-X UDP interface for this session. setupUdp(appState: appState) @@ -134,6 +149,9 @@ final class LiveEngine { udpStatusTask = nil udp.stop() lastUdpConfig = nil + txScheduleTask?.cancel() + txScheduleTask = nil + finishTune() txPlayer.stop() audio.stop() isRunning = false @@ -144,23 +162,35 @@ final class LiveEngine { /// Begin calling CQ. func callCQ() { + stopTune() syncSettingsToQso() appState?.tx.conversationLog.removeAll() + appState?.tx.targetSnr = nil qsoEngine?.startCq() + armSupervisor() syncQsoStatusToUI() } /// Stop any active TX/QSO. func stopTx() { + txScheduleTask?.cancel() + txScheduleTask = nil qsoEngine?.stop() txPlayer.stop() + appState?.tx.isTransmitting = false + appState?.tx.targetSnr = nil + callerQueue.removeAll() syncQsoStatusToUI() } /// Answer a station from a decoded message (user tapped "Call" in QsoSheet). func answerStation(_ msg: DecodeMessage) { + stopTune() syncSettingsToQso() appState?.tx.conversationLog.removeAll() + appState?.tx.targetSnr = msg.snr + callerQueue.remove(callsign: msg.callFrom) + armSupervisor() let decoded = FT8DSP.DecodedMessage( callTo: msg.callTo, callFrom: msg.callFrom, @@ -191,6 +221,124 @@ final class LiveEngine { appState.tx.slotParity = appState.tx.slotParity == 0 ? 1 : 0 } + /// Operator picked a standard message chip (CQ/GRID/RPT/R-RPT/RR73/73) + /// in the Active QSO panel. + func selectTxStage(_ stage: TxStage) { + guard let qso = qsoEngine else { return } + syncSettingsToQso() + qso.setStage(stage) + supervisor.resetAttempts() + syncQsoStatusToUI() + } + + /// Force-log the current QSO (LOG action) and move on: to `nextCallsign` + /// when the operator tapped a queued caller, otherwise to the head of the + /// caller queue, otherwise back to CQ / stop per the auto-CQ setting. + /// Mirrors Android `forceLogAndMoveOn`. + func forceLogAndMoveOn(nextCallsign: String? = nil) { + guard let qso = qsoEngine, let appState else { return } + let nowMs = Int64(Date().timeIntervalSince1970 * 1000) + + if case .completed(let record)? = qso.forceLog() { + logCompletedQso(record) + } + appState.tx.targetSnr = nil + appState.tx.conversationLog.removeAll() + supervisor.resetAttempts() + if appState.settings.autoCQAfterQSO { + supervisor.resetWatchdog(nowMs: nowMs) + } + + // Work the next caller: the tapped one, or the head of the queue. + let next: QueuedCaller? = nextCallsign != nil + ? callerQueue.dequeue(callsign: nextCallsign!) + : callerQueue.dequeueNext() + if let caller = next { + startQsoWithCaller(caller, nowMs: nowMs) + } + syncQsoStatusToUI() + } + + /// Abandon the current target without logging (✕ action): back to the CQ + /// baseline or idle per the auto-CQ setting. Mirrors Android `userResetToCQ`. + func clearActiveQso() { + guard let qso = qsoEngine, let appState else { return } + qso.abandonToCq() + appState.tx.targetSnr = nil + appState.tx.conversationLog.removeAll() + supervisor.resetAttempts() + supervisor.resetWatchdog(nowMs: Int64(Date().timeIntervalSince1970 * 1000)) + syncQsoStatusToUI() + } + + // MARK: - TUNE carrier + + /// Latch/unlatch a steady carrier at the current TX audio frequency for + /// antenna/amplifier tuning. Auto-stops at the settings timeout; the + /// buffer itself is capped at the timeout, so playback ending enforces the + /// hard max-on time even if the countdown ticker dies. + func toggleTune() { + guard let appState else { return } + if appState.tx.isTuning { + stopTune() + appState.toast.show("Tune stopped", icon: "dot.radiowaves.right") + } else { + startTune() + } + } + + private func startTune() { + guard let appState else { return } + guard isRunning else { + appState.toast.show("Start RX before tuning", icon: "exclamationmark.triangle") + return + } + // Tune and FT8 TX are mutually exclusive (the TUNE pill is also + // disabled in the UI while the sequencer is armed). + guard !appState.tx.isActivated, !appState.tx.isTransmitting else { return } + + let timeout = max(1, appState.settings.tuneTimeoutSec) + let samples = TuneTone.samples( + freqHz: appState.waterfall.txFreqHz, + sampleRate: Int(FT8.sampleRate), + durationSec: timeout + ) + guard !samples.isEmpty else { return } + + appState.tx.isTuning = true + appState.tx.tuneRemainingSec = timeout + appState.toast.show("Tuning at \(Int(appState.waterfall.txFreqHz)) Hz", icon: "dot.radiowaves.right") + txPlayer.play(samples) { [weak self] in + Task { @MainActor in self?.finishTune() } + } + tuneTask?.cancel() + tuneTask = Task { [weak self] in + while !Task.isCancelled { + try? await Task.sleep(for: .seconds(1)) + guard !Task.isCancelled, let self, let appState = self.appState, + appState.tx.isTuning else { return } + appState.tx.tuneRemainingSec = max(0, appState.tx.tuneRemainingSec - 1) + } + } + } + + /// Stop the tune carrier if one is active (tap, timeout, or TX taking over). + func stopTune() { + guard let appState, appState.tx.isTuning else { return } + txPlayer.stop() + finishTune() + } + + /// Idempotent tune teardown (called from stop-tap, playback completion, + /// and engine stop). + private func finishTune() { + tuneTask?.cancel() + tuneTask = nil + guard let appState, appState.tx.isTuning else { return } + appState.tx.isTuning = false + appState.tx.tuneRemainingSec = 0 + } + // MARK: - Decode loop /// Polls for slot boundaries and runs the FT8 decoder at each transition. @@ -276,11 +424,14 @@ final class LiveEngine { let dialHz = Int64((Double(bandToFreqMhz(appState.settings.band)) ?? 14.074) * 1_000_000) OnlineLogService.shared.enqueuePskDecodes(decoded, dialFreqHz: dialHz, appState: appState) + let settings = appState.settings + let nowMs = Int64(Date().timeIntervalSince1970 * 1000) + // Log incoming RX messages addressed to us for the conversation panel. - let myCall = appState.settings.myCall.uppercased() + let myCall = settings.myCall.uppercased() if !myCall.isEmpty { for msg in decoded where msg.callTo.uppercased() == myCall { - let utcTime = Self.utcTimeString(from: Int64(Date().timeIntervalSince1970 * 1000)) + let utcTime = Self.utcTimeString(from: nowMs) appState.tx.conversationLog.append( QsoLogEntry(direction: .rx, message: msg.rawText.isEmpty ? "\(msg.callTo) \(msg.callFrom) \(msg.extra)" : msg.rawText, snr: msg.snr, utcTime: utcTime) @@ -288,9 +439,31 @@ final class LiveEngine { } } + // Track the last SNR heard from the current target for the QSO header. + if let dx = qso.status().target?.uppercased(), + let m = decoded.last(where: { $0.callFrom.uppercased() == dx }) { + appState.tx.targetSnr = m.snr + } + + // Hunt / auto-call-follow: lock a station while we have no target. + autoAnswerIfHunting(decoded, slotID: slotID, nowMs: nowMs) + + // Caller queue: stations calling me that aren't the current target + // wait their turn while we're busy (in a QSO or running CQ). + enqueueCallers(decoded, nowMs: nowMs) + // Capture pre-RX target to detect new QSO answers. let previousTarget = qso.status().target + // A reply from the target (or any answer to our CQ) resets the + // stop-after-N no-reply streak. + if !myCall.isEmpty, decoded.contains(where: { m in + m.callTo.uppercased() == myCall + && (previousTarget == nil || m.callFrom.uppercased() == previousTarget!.uppercased()) + }) { + supervisor.noteReply() + } + // Feed decodes to the QSO sequencer. let outcome = qso.processRx(decoded) @@ -312,20 +485,31 @@ final class LiveEngine { } } + // The engine locked a caller on its own — their queue slot is obsolete, + // and a fresh target restarts the safety-net clocks. + if let dx = qso.status().target, + previousTarget?.uppercased() != dx.uppercased() { + callerQueue.remove(callsign: dx) + armSupervisor() + } + // Handle QSO completion. - if case .completed(var record) = outcome { - // Assign a unique ID based on the current record count. - let nextId = Int64((appState.logbook.records.map { $0.id ?? 0 }.max() ?? 0) + 1) - record.id = nextId - appState.logbook.records.insert(record, at: 0) - QsoLogStore.save(appState.logbook.records) - // Broadcast the logged QSO over the WSJT-X UDP interface. - broadcastUdpQso(record) - // Upload to Cloudlog/QRZ when enabled (fire-and-forget with retry; - // marks the record's synced flags and re-persists on success). - OnlineLogService.shared.handleQsoLogged(record, appState: appState) - // Trigger QSO celebration animation. - appState.tx.qsoCompletedAt = Date() + if case .completed(let record) = outcome { + logCompletedQso(record) + // Auto-CQ keeps the run going, so refresh the watchdog on each + // completed QSO (mirrors Android). + if settings.autoCQAfterQSO { + supervisor.resetWatchdog(nowMs: nowMs) + } + } + + // Previous QSO fully wrapped up → work the next queued caller. + serviceCallerQueue(nowMs: nowMs) + + // A hunt that is idle-listening (no target locked, not CQing) is + // behaving correctly — keep the watchdog fresh (mirrors Android). + if appState.tx.huntEnabled, qso.status().target == nil, !settings.huntCallsCQ { + supervisor.resetWatchdog(nowMs: nowMs) } // Sync QSO status back to UI. @@ -335,26 +519,221 @@ final class LiveEngine { // operator's selected TX parity, transmit. The audio keys up immediately, // so it plays in `slotID` itself — gate on that slot's parity (mirrors the // desktop engine's `maybe_transmit`), not the next slot's. + // The TX safety nets run first: watchdog (N minutes of continuous TX + // cycles) and stop-after-N (same message repeatedly unanswered). if let txMsg = qso.txMessage { - if SlotClock.shouldTransmit(slotID: slotID, desiredParity: appState.tx.slotParity) { + if let reason = supervisor.stopReason( + nowMs: nowMs, + watchdogMin: settings.txWatchdogMin, + stopAfterAttempts: settings.stopAfterAttempts + ) { + stopTx() + switch reason { + case .watchdog: + appState.toast.show( + "TX watchdog: stopped after \(settings.txWatchdogMin) min", + icon: "exclamationmark.triangle") + case .noReply: + appState.toast.show( + "No reply after \(settings.stopAfterAttempts) attempts — TX stopped", + icon: "stop.circle") + } + } else if SlotClock.shouldTransmit(slotID: slotID, desiredParity: appState.tx.slotParity) { scheduleTx(message: txMsg, freqHz: appState.waterfall.txFreqHz) + supervisor.noteTransmission(txMsg) } } } - /// Generate FT8 audio and play it through the speaker. + /// Auto-answer while no target is locked: HUNT answers other stations' + /// CQs (skipping blocked and already-worked calls); the auto-call-follow + /// setting answers stations calling us directly while we're idle; the + /// hunt-calls-CQ setting turns a caller-less hunt into a CQ run. + private func autoAnswerIfHunting(_ decoded: [DecodedMessage], slotID: Int64, nowMs: Int64) { + guard let qso = qsoEngine, let appState else { return } + let settings = appState.settings + let myCall = settings.myCall.uppercased() + guard myCall.count >= 3 else { return } + guard qso.status().target == nil else { return } + + let blocked = settings.blockedCallsigns.map { $0.uppercased() } + let worked = Set(appState.logbook.records.map { $0.call.uppercased() }) + func eligible(_ m: DecodedMessage) -> Bool { + let from = m.callFrom.uppercased() + return !from.isEmpty && from != myCall + && !from.contains("<") && !blocked.contains(from) + } + + var candidate: DecodedMessage? + if appState.tx.huntEnabled { + // Strongest un-worked station calling CQ. + candidate = decoded + .filter { $0.callTo.uppercased().hasPrefix("CQ") && eligible($0) + && !worked.contains($0.callFrom.uppercased()) } + .max { $0.snr < $1.snr } + } + if candidate == nil, settings.autoCallFollow, !qso.active { + // Someone calling us directly while we're idle. + candidate = decoded + .filter { $0.callTo.uppercased() == myCall && eligible($0) } + .max { $0.snr < $1.snr } + } + + if let msg = candidate { + appState.tx.conversationLog.removeAll() + qso.answer(msg) + // They transmitted in the slot that just ended, so reply in slots + // of the opposite parity — i.e. the parity of the slot now starting. + appState.tx.slotParity = Int(SlotClock.parity(slotID: slotID)) + appState.tx.targetSnr = msg.snr + armSupervisor() + appState.toast.show("Answering \(msg.callFrom.uppercased())", icon: "target") + } else if appState.tx.huntEnabled, settings.huntCallsCQ, !qso.active { + // Hunting with no callers → call CQ ourselves. + qso.startCq() + armSupervisor() + } + } + + /// Queue stations calling me that aren't the current target (dedup by + /// call, stale entries pruned) and mirror the queue into TxState. + private func enqueueCallers(_ decoded: [DecodedMessage], nowMs: Int64) { + guard let qso = qsoEngine, let appState else { return } + defer { appState.tx.queuedCallers = callerQueue.callers.map(\.callsign) } + callerQueue.removeStale(nowMs: nowMs) + guard qso.active else { return } + let myCall = appState.settings.myCall.uppercased() + guard !myCall.isEmpty else { return } + + let target = qso.status().target + for m in decoded where m.callTo.uppercased() == myCall { + guard CallerQueue.shouldEnqueue( + callsign: m.callFrom, + myCall: myCall, + currentTarget: target, + blocked: appState.settings.blockedCallsigns + ) else { continue } + callerQueue.enqueue(QueuedCaller( + callsign: m.callFrom.uppercased(), + snr: m.snr, + freqHz: m.freqHz, + grid: m.grid, + queuedAtMs: nowMs + )) + } + } + + /// When the previous QSO has fully wrapped up (no target locked), start + /// working the next queued caller. Runs after each slot's RX processing, + /// so it also covers the courtesy-73 case where completion lands a slot + /// before the target clears. + private func serviceCallerQueue(nowMs: Int64) { + guard let qso = qsoEngine, let appState else { return } + guard qso.status().target == nil else { return } + callerQueue.removeStale(nowMs: nowMs) + guard let next = callerQueue.dequeueNext() else { + appState.tx.queuedCallers = callerQueue.callers.map(\.callsign) + return + } + startQsoWithCaller(next, nowMs: nowMs) + } + + /// Begin a QSO with a dequeued caller: they already called us, so if they + /// sent their grid we answer with a report (Tx2), otherwise with our grid. + private func startQsoWithCaller(_ caller: QueuedCaller, nowMs: Int64) { + guard let qso = qsoEngine, let appState else { return } + appState.tx.conversationLog.removeAll() + let msg = FT8DSP.DecodedMessage( + callTo: appState.settings.myCall.uppercased(), + callFrom: caller.callsign, + extra: "", + grid: caller.grid, + rawText: "", + snr: caller.snr, + freqHz: caller.freqHz, + timeSec: 0, + score: 0, + i3: 0, + n3: 0, + a91: [UInt8](repeating: 0, count: 12) + ) + qso.answer(msg) + if !caller.grid.isEmpty { + // They already sent their grid — skip ours and send the report. + qso.setStage(.report) + } + appState.tx.targetSnr = caller.snr + armSupervisor() + appState.tx.queuedCallers = callerQueue.callers.map(\.callsign) + appState.toast.show("Working \(caller.callsign)", icon: "person.wave.2") + } + + /// Persist + broadcast a completed QSO record (shared by the auto-sequence + /// completion path and the force-log action). + private func logCompletedQso(_ record: QsoRecord) { + guard let appState else { return } + var record = record + // Assign a unique ID based on the current record count. + let nextId = Int64((appState.logbook.records.map { $0.id ?? 0 }.max() ?? 0) + 1) + record.id = nextId + appState.logbook.records.insert(record, at: 0) + QsoLogStore.save(appState.logbook.records) + // Broadcast the logged QSO over the WSJT-X UDP interface. + broadcastUdpQso(record) + // Upload to Cloudlog/QRZ when enabled (fire-and-forget with retry; + // marks the record's synced flags and re-persists on success). + OnlineLogService.shared.handleQsoLogged(record, appState: appState) + // Trigger QSO celebration animation. + appState.tx.qsoCompletedAt = Date() + } + + /// Restart both TX safety nets (fresh CQ run / new target). + private func armSupervisor() { + supervisor.resetWatchdog(nowMs: Int64(Date().timeIntervalSince1970 * 1000)) + supervisor.resetAttempts() + } + + /// Generate FT8 audio and play it, honoring the operator's TX delay and + /// the late-start clip rule. private func scheduleTx(message: String, freqHz: Float) { guard let appState else { return } + guard !appState.tx.isTuning else { return } // tune and FT8 TX are exclusive guard let samples = FT8Encoder.generateFT8(message, baseFreqHz: freqHz) else { return } + let txDelayMs = max(0, appState.settings.txDelayMs) + txScheduleTask?.cancel() + txScheduleTask = Task { @MainActor [weak self] in + if txDelayMs > 0 { + try? await Task.sleep(for: .milliseconds(txDelayMs)) + } + guard !Task.isCancelled else { return } + self?.beginTxPlayback(message: message, samples: samples) + } + } + + /// Start playback now: clip the leading `max(0, msIntoCycle - slack)` ms + /// when we're late into the 15 s cycle so the TX still ends on the cycle + /// boundary. An on-time start clips NOTHING — clipping normal starts + /// destroys the leading Costas sync (see CLAUDE.md, TX pipeline gotcha #2). + private func beginTxPlayback(message: String, samples: [Float]) { + guard let appState else { return } + let nowMs = Int64(Date().timeIntervalSince1970 * 1000) + let clipMs = TxTiming.lateStartClipMs( + msIntoCycle: SlotClock.msIntoCycle(atUtcMs: nowMs), + slackMs: Int64(appState.settings.lateStartToleranceMs) + ) + let skip = TxTiming.clipSampleCount(clipMs: clipMs, sampleRate: Int(FT8.sampleRate)) + guard skip < samples.count else { return } // hopelessly late — skip this cycle + let playSamples = skip > 0 ? Array(samples[skip...]) : samples + // Log the TX message to conversation panel. - let utcTime = Self.utcTimeString(from: Int64(Date().timeIntervalSince1970 * 1000)) appState.tx.conversationLog.append( - QsoLogEntry(direction: .tx, message: message, snr: nil, utcTime: utcTime) + QsoLogEntry(direction: .tx, message: message, snr: nil, + utcTime: Self.utcTimeString(from: nowMs)) ) appState.tx.isTransmitting = true - txPlayer.play(samples) { [weak self] in + txPlayer.play(playSamples) { [weak self] in Task { @MainActor in self?.appState?.tx.isTransmitting = false } @@ -439,6 +818,7 @@ final class LiveEngine { qso.myGrid = s.myGrid.uppercased() qso.band = s.band qso.freqMhz = bandToFreqMhz(s.band) + qso.autoReturnToCq = s.autoCQAfterQSO } /// Reflect QSO engine status into the UI TxState. @@ -447,8 +827,13 @@ final class LiveEngine { let status = qso.status() appState.tx.isActivated = status.active appState.tx.stage = mapStage(status.stage) + appState.tx.qsoStage = status.stage appState.tx.targetCall = status.target ?? "" appState.tx.txMessage = status.txMessage ?? "" + appState.tx.queuedCallers = callerQueue.callers.map(\.callsign) + if status.target == nil { + appState.tx.targetSnr = nil + } } private func mapStage(_ stage: TxStage) -> TxUIStage { diff --git a/ios/FT8AF/FT8AF/Navigation/AppTabView.swift b/ios/FT8AF/FT8AF/Navigation/AppTabView.swift index 9c14623c..8591fba1 100644 --- a/ios/FT8AF/FT8AF/Navigation/AppTabView.swift +++ b/ios/FT8AF/FT8AF/Navigation/AppTabView.swift @@ -28,7 +28,9 @@ struct AppTabView: View { // Bottom chrome: ActiveQsoPanel + TxStrip + SlotTimer + TabBar VStack(spacing: 0) { - if showsTxStrip && appState.tx.isActivated { + // Panel also shows while HUNT is armed (silent listening) so + // the "Hunting…" header state has somewhere to live. + if showsTxStrip && (appState.tx.isActivated || appState.tx.huntEnabled) { ActiveQsoPanel() } @@ -38,6 +40,7 @@ struct AppTabView: View { onCallCQ: { appState.engine?.callCQ() }, onStop: { appState.engine?.stopTx() }, onToggleSlot: { appState.engine?.toggleSlotParity() }, + onToggleTune: { appState.engine?.toggleTune() }, onOpenFrequencyPicker: { showFrequencyPicker = true }, onVolumeChange: { newVol in appState.settings.txVolume = newVol diff --git a/ios/FT8AF/FT8AF/Screens/Decode/DecodeRow.swift b/ios/FT8AF/FT8AF/Screens/Decode/DecodeRow.swift index a039bed6..efe770dd 100644 --- a/ios/FT8AF/FT8AF/Screens/Decode/DecodeRow.swift +++ b/ios/FT8AF/FT8AF/Screens/Decode/DecodeRow.swift @@ -1,129 +1,176 @@ +import FT8Engine import SwiftUI +/// A single decoded FT8 message row, mirroring the Android `DecodeRow` design: +/// +/// - Left accent bar — pink for the current call target, amber for CQ +/// - Context label chips (CALLING / CQ / TO YOU) before the callsign +/// - Callsign (bold monospaced), grid locator, status pill on the right +/// - Full decoded message text line +/// - Meta row: segmented signal bar, SNR, audio frequency, distance, +/// relative age ("now" / "32s" / "5m" / "2h") +/// - DXCC entity location line with globe icon (when known) +/// +/// Background tinting: cyan glow for messages directed at the operator, pink +/// soft for the call target, surface card for CQ, transparent otherwise. +/// All classification logic is pure and lives in FT8Engine +/// (`classifyDecode`, `relativeAge`, `gridDistanceText`, `DxccPrefix`). struct DecodeRow: View { let message: DecodeMessage var myCall: String = "" - var workedCalls: Set = [] + var myGrid: String = "" + var highlight: DecodeHighlight? + var isTarget: Bool = false var compact: Bool = false + var now: Date = Date() + var distanceInMiles: Bool = false - var body: some View { - HStack(spacing: 0) { - // UTC time - Text(shortTime) - .font(.system(size: compact ? 10 : 11, weight: .medium, design: .monospaced)) - .foregroundStyle(textFaint) - .frame(width: compact ? 34 : 38, alignment: .leading) - - // SNR badge - Text(snrText) - .font(.system(size: compact ? 10 : 11, weight: .semibold, design: .monospaced)) - .foregroundStyle(snrColor) - .frame(width: compact ? 28 : 32, alignment: .trailing) - .padding(.trailing, compact ? 6 : 8) - - // Frequency - Text(freqText) - .font(.system(size: compact ? 10 : 11, weight: .medium, design: .monospaced)) - .foregroundStyle(textFaint) - .frame(width: compact ? 36 : 40, alignment: .trailing) - .padding(.trailing, compact ? 8 : 10) - - // Callsigns + extra - VStack(alignment: .leading, spacing: compact ? 1 : 2) { - HStack(spacing: 4) { - // CQ / callTo pill - if message.callTo == "CQ" || message.callTo.hasPrefix("CQ ") { - Text("CQ") - .font(.system(size: compact ? 9 : 10, weight: .bold, design: .monospaced)) - .foregroundStyle(bgApp) - .padding(.horizontal, compact ? 4 : 5) - .padding(.vertical, 1) - .background( - RoundedRectangle(cornerRadius: 3) - .fill(accent) - ) - } else { - Text(message.callTo) - .font(.system(size: compact ? 11 : 12, weight: .medium, design: .monospaced)) - .foregroundStyle(signal) - } - - Image(systemName: "arrow.left") - .font(.system(size: compact ? 7 : 8)) - .foregroundStyle(textDim) + private var isCQ: Bool { isCQMessage(callTo: message.callTo) } + private var isToMe: Bool { isDirectedToMe(callTo: message.callTo, myCall: myCall) } + /// Mid-QSO with a third party — de-emphasized but readable (Android: 0.8). + private var isInQsoWithOther: Bool { !isCQ && !isToMe && !isTarget } - Text(message.callFrom) - .font(.system(size: compact ? 12 : 13, weight: .bold, design: .monospaced)) - .foregroundStyle(textPrimary) - } + var body: some View { + HStack(alignment: .top, spacing: 0) { + // Left accent bar — target (pink) wins over CQ (amber) because it + // is the more actionable signal for the operator. + if let accentColor = accentBarColor { + RoundedRectangle(cornerRadius: 99) + .fill(accentColor) + .frame(width: 3, height: compact ? 32 : 52) + Spacer().frame(width: 10) + } else { + Spacer().frame(width: 13) + } - if !compact { + VStack(alignment: .leading, spacing: 0) { + topRow + Spacer().frame(height: compact ? 2 : 4) + if !compact && !fullMessageText.isEmpty { Text(fullMessageText) - .font(.system(size: 10, weight: .medium, design: .monospaced)) - .foregroundStyle(textFaint) + .font(.system(size: 12, design: .monospaced)) + .foregroundStyle(textMuted) .lineLimit(1) + Spacer().frame(height: 4) + } + metaRow + if !compact, let location = locationText { + Spacer().frame(height: 4) + HStack(spacing: 4) { + Image(systemName: "globe") + .font(.system(size: 10)) + .foregroundStyle(textFaint) + Text(location) + .font(.system(size: 10.5, weight: .medium)) + .foregroundStyle(textFaint) + } } } + .padding(.trailing, 12) + } + .padding(.vertical, compact ? 6 : 10) + .frame(maxWidth: .infinity, alignment: .leading) + .background( + RoundedRectangle(cornerRadius: 12) + .fill(rowBackground) + ) + .overlay( + RoundedRectangle(cornerRadius: 12) + .strokeBorder(rowBorder, lineWidth: 1) + ) + .contentShape(Rectangle()) + .opacity(isInQsoWithOther ? 0.8 : 1.0) + .padding(.horizontal, 12) + .padding(.vertical, 3) + } + + // MARK: - Sub-rows + + private var topRow: some View { + HStack(spacing: 8) { + // Context labels can stack — a target station calling CQ shows + // both CALLING and CQ, like Android. + if isTarget { + MessageLabel(text: "CALLING", color: target, bgColor: targetSoft) + } + if isCQ { + MessageLabel(text: "CQ", color: accent, bgColor: accentSoft) + } else if isToMe { + MessageLabel(text: "TO YOU", color: signal, bgColor: signalSoft) + } - Spacer() + Text(message.callFrom) + .font(.system(size: 15, weight: .bold, design: .monospaced)) + .foregroundStyle(callsignColor) - // Chevron - Image(systemName: "chevron.right") - .font(.system(size: compact ? 9 : 10, weight: .semibold)) - .foregroundStyle(textDim) - } - .padding(.horizontal, 16) - .padding(.vertical, compact ? 5 : 8) - .background(rowBackground) - .overlay(alignment: .bottom) { - Rectangle() - .fill(borderSubtle) - .frame(height: 1) + if !message.grid.isEmpty { + Text(message.grid) + .font(.system(size: 11, design: .monospaced)) + .foregroundStyle(textFaint) + } + + Spacer(minLength: 4) + + if let highlight { + StatusPillView(highlight: highlight, compact: true) + } } } - private var rowBackground: Color { - if isForMe { return target.opacity(0.10) } - if isCQ { return accent.opacity(0.06) } - if isWorked { return signal.opacity(0.06) } - return bgApp - } + private var metaRow: some View { + HStack(spacing: 10) { + SignalBarView( + snr: message.snr, + width: compact ? 22 : 28, + height: compact ? 10 : 12 + ) - private var isCQ: Bool { - message.callTo == "CQ" || message.callTo.hasPrefix("CQ ") - } + MetaText("\(message.snr) dB") + MetaText("\(Int(message.freqHz)) Hz") - private var isForMe: Bool { - !myCall.isEmpty && message.callTo.uppercased() == myCall.uppercased() - } + let distance = gridDistanceText(from: myGrid, to: message.grid, inMiles: distanceInMiles) + if !distance.isEmpty { + MetaText(distance) + } + + Spacer(minLength: 4) - private var isWorked: Bool { - workedCalls.contains(message.callFrom.uppercased()) + MetaText(relativeAge(secondsAgo: Int(now.timeIntervalSince(message.arrival)))) + } } - private var shortTime: String { - String(message.utcTime.prefix(5)) + // MARK: - Derived styling + + private var accentBarColor: Color? { + if isTarget { return target } + if isCQ { return accent } + return nil } - private var snrText: String { - message.snr >= 0 ? "+\(message.snr)" : "\(message.snr)" + private var rowBackground: Color { + if isToMe { return signal.opacity(0.08) } // cyan glow + if isTarget { return targetSoft } + if isCQ { return bgSurface } + return .clear } - private var snrColor: Color { - if message.snr >= 0 { return statusConfirmed } - if message.snr >= -10 { return signal } - if message.snr >= -18 { return textMuted } - return statusBad + private var rowBorder: Color { + if isToMe { return signal.opacity(0.22) } + if isTarget { return targetBorder } + if isCQ { return borderSubtle } + return .clear } - private var freqText: String { - String(format: "%.0f", message.freqHz) + private var callsignColor: Color { + if isToMe { return signal } + if isTarget { return target } + return textPrimary } private var fullMessageText: String { var parts: [String] = [] - parts.append(message.callTo) - parts.append(message.callFrom) + if !message.callTo.isEmpty { parts.append(message.callTo) } + if !message.callFrom.isEmpty { parts.append(message.callFrom) } if !message.extra.isEmpty { parts.append(message.extra) } else if !message.grid.isEmpty { @@ -131,4 +178,145 @@ struct DecodeRow: View { } return parts.joined(separator: " ") } + + /// DXCC entity name for the location line ("United States" → "USA" + /// shorthand, matching the Android abbreviation). + private var locationText: String? { + guard let entity = DxccPrefix.entity(for: message.callFrom) else { return nil } + switch entity.name { + case "United States": return "USA" + case "United Kingdom": return "UK" + default: return entity.name + } + } +} + +// MARK: - Status pill + +/// Android-parity status chip: 4 pt corner, glowing dot, hue at 0.12 bg / +/// 0.28 border alpha. Category → hue mapping mirrors `StatusPill.kt` with the +/// pending/needed chips on the amber accent. +private struct StatusPillView: View { + let highlight: DecodeHighlight + var compact: Bool = false + + var body: some View { + HStack(spacing: 5) { + Circle() + .fill(hue) + .frame(width: 6, height: 6) + Text(label) + .font(.system(size: compact ? 9.5 : 11, weight: .semibold)) + .foregroundStyle(hue) + } + .padding(.horizontal, compact ? 6 : 8) + .frame(height: compact ? 18 : 22) + .background( + RoundedRectangle(cornerRadius: 4) + .fill(hue.opacity(0.12)) + ) + .overlay( + RoundedRectangle(cornerRadius: 4) + .strokeBorder(hue.opacity(0.28), lineWidth: 1) + ) + } + + private var label: String { + switch highlight { + case .pending: return "PENDING" + case .pota: return "POTA" + case .newDxcc: return "NEW DXCC" + case .newGrid: return "NEW GRID" + case .newBand: return "NEW BAND" + case .worked: return "WORKED" + case .cq: return "CQ" + } + } + + private var hue: Color { + switch highlight { + case .pending: return accent // amber — needs the operator's action + case .pota: return statusConfirmed // green + case .newDxcc: return statusNew // purple + case .newGrid: return statusWarn // yellow + case .newBand: return statusWorked // cyan + case .worked: return statusWorked // cyan + case .cq: return statusCq // amber + } + } +} + +// MARK: - Signal bar + +/// Small segmented signal-strength bar (5 ascending bars), port of the +/// Android `SignalBar`: fill fraction = (snr + 25) / 30, color by SNR band. +private struct SignalBarView: View { + let snr: Int + var width: CGFloat = 28 + var height: CGFloat = 12 + + private static let bars = 5 + + var body: some View { + let filled = Self.filledBars(snr: snr) + let barWidth = (width - CGFloat(Self.bars - 1) * 2) / CGFloat(Self.bars) + HStack(alignment: .bottom, spacing: 2) { + ForEach(0.. Int { + let norm = min(1, max(0, Float(snr + 25) / 30)) + return min(bars, Int(norm * Float(bars))) + } + + private var barColor: Color { + if snr >= -5 { return statusConfirmed } // green + if snr >= -12 { return signal } // cyan + if snr >= -18 { return accent } // amber + return statusBad // red + } + + private var emptyColor: Color { + Color(red: 148/255, green: 163/255, blue: 184/255).opacity(0.15) + } +} + +// MARK: - Small pieces + +/// Small colored label chip (e.g. "CQ", "TO YOU", "CALLING"). +private struct MessageLabel: View { + let text: String + let color: Color + let bgColor: Color + + var body: some View { + Text(text) + .font(.system(size: 9.5, weight: .bold)) + .kerning(0.6) + .foregroundStyle(color) + .padding(.horizontal, 5) + .padding(.vertical, 1) + .background(RoundedRectangle(cornerRadius: 4).fill(bgColor)) + } +} + +/// Small monospaced metadata text used in the bottom info row. +private struct MetaText: View { + let text: String + init(_ text: String) { self.text = text } + + var body: some View { + Text(text) + .font(.system(size: 10.5, design: .monospaced)) + .foregroundStyle(textFaint) + } } diff --git a/ios/FT8AF/FT8AF/Screens/Decode/DecodeScreen.swift b/ios/FT8AF/FT8AF/Screens/Decode/DecodeScreen.swift index 743339f4..36d49636 100644 --- a/ios/FT8AF/FT8AF/Screens/Decode/DecodeScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Decode/DecodeScreen.swift @@ -1,3 +1,4 @@ +import FT8Engine import SwiftUI struct DecodeScreen: View { @@ -77,40 +78,7 @@ struct DecodeScreen: View { if filteredMessages.isEmpty { emptyState } else { - ScrollViewReader { proxy in - ScrollView { - LazyVStack(spacing: 0) { - ForEach(Array(groupedBySlot.enumerated()), id: \.offset) { groupIdx, group in - // Time divider between slot groups - if groupIdx > 0 { - SlotDivider(time: group.first?.utcTime ?? "") - } - - ForEach(group) { message in - DecodeRow( - message: message, - myCall: appState.settings.myCall, - workedCalls: workedCallSet, - compact: decode.compactMode - ) - .id(message.id) - .onTapGesture { - appState.decode.selectedMessage = message - showQsoSheet = true - } - } - } - } - .padding(.bottom, 160) - } - .onChange(of: decode.messages.first?.id) { _, newId in - if let id = newId { - withAnimation(.easeOut(duration: 0.3)) { - proxy.scrollTo(id, anchor: .top) - } - } - } - } + messageList } Spacer(minLength: 0) @@ -143,39 +111,141 @@ struct DecodeScreen: View { } } - private var filteredMessages: [DecodeMessage] { - let blocked = Set(appState.settings.blockedCallsigns) - let msgs = blocked.isEmpty ? appState.decode.messages : + // MARK: - List + + private var messageList: some View { + let decode = appState.decode + let settings = appState.settings + let index = logbookIndex + let toggles = HighlightToggles( + newDxcc: settings.highlightNewDxcc, + newGrid: settings.highlightNewGrid, + newBand: settings.highlightNewBand, + worked: settings.highlightWorked + ) + let targetCall = appState.tx.targetCall.uppercased() + + return ScrollViewReader { proxy in + ScrollView { + // The 1 s timeline drives the relative-age labels + // ("now" → "32s" → "5m") without any per-row timers. + TimelineView(.periodic(from: .now, by: 1)) { timeline in + LazyVStack(spacing: 0) { + ForEach(Array(groupedBySlot.enumerated()), id: \.offset) { groupIdx, group in + // Time divider between slot groups + if groupIdx > 0 { + SlotDivider(time: group.first?.utcTime ?? "") + } + + ForEach(group) { message in + DecodeRow( + message: message, + myCall: settings.myCall, + myGrid: settings.myGrid, + highlight: classifyDecode( + callFrom: message.callFrom, + callTo: message.callTo, + grid: message.grid, + extra: message.extra, + myCall: settings.myCall, + band: settings.band, + index: index, + toggles: toggles + ), + isTarget: !targetCall.isEmpty && + message.callFrom.uppercased() == targetCall, + compact: decode.compactMode, + now: timeline.date, + distanceInMiles: settings.distanceInMiles + ) + .id(message.id) + .onTapGesture { + appState.decode.selectedMessage = message + showQsoSheet = true + } + } + } + } + .padding(.bottom, 160) + } + } + .onChange(of: decode.messages.first?.id) { _, newId in + if let id = newId { + withAnimation(.easeOut(duration: 0.3)) { + proxy.scrollTo(id, anchor: .top) + } + } + } + } + } + + // MARK: - Filtering + + /// Pre-computed logbook lookups shared by highlight classification and the + /// New DXCC / Needed chips. + private var logbookIndex: LogbookIndex { + LogbookIndex(qsos: appState.logbook.records.map { + LoggedQso(call: $0.call, grid: $0.gridsquare, band: $0.band) + }) + } + + /// Base stage (always-on): blocklist + the persisted settings filters + /// (Show Only CQ, DX Only, continent). These AND with whatever chip is + /// selected, mirroring Android `filterMessages`. + private var baseMessages: [DecodeMessage] { + let settings = appState.settings + let blocked = Set(settings.blockedCallsigns.map { $0.uppercased() }) + var msgs = blocked.isEmpty ? appState.decode.messages : appState.decode.messages.filter { !blocked.contains($0.callFrom.uppercased()) } + if settings.showOnlyCQ { + msgs = msgs.filter { isCQMessage(callTo: $0.callTo) } + } + if settings.dxOnly { + // DX = station's continent known and different from mine (derived + // from my call prefix). Unknown continents are conservatively kept + // out, matching the Android predicate. + let myContinent = DxccPrefix.continent(for: settings.myCall) + msgs = msgs.filter { + guard let mine = myContinent, + let theirs = DxccPrefix.continent(for: $0.callFrom) else { return false } + return theirs != mine + } + } + if settings.continentFilter != "All" { + msgs = msgs.filter { + DxccPrefix.continent(for: $0.callFrom) == settings.continentFilter + } + } + return msgs + } + + private var filteredMessages: [DecodeMessage] { + let msgs = baseMessages + switch appState.decode.activeFilter { case .all: return msgs - case .cq: return msgs.filter { $0.callTo == "CQ" || $0.callTo.hasPrefix("CQ ") } - case .cqPota: return msgs.filter { - $0.extra.contains("K-") || $0.extra.contains("VE-") || $0.extra.contains("DL-") || - $0.callTo == "CQ POTA" || $0.callTo.hasPrefix("CQ POTA") - } + case .cq: return msgs.filter { isCQMessage(callTo: $0.callTo) } + case .cqPota: return msgs.filter { isPotaCq(callTo: $0.callTo, extra: $0.extra) } case .newDxcc: - // Show only callsign prefixes not yet in logbook - let workedPrefixes = Set(appState.logbook.records.map { dxccPrefix($0.call) }) - return msgs.filter { !workedPrefixes.contains(dxccPrefix($0.callFrom)) } + // Stations whose DXCC entity isn't in the logbook yet. + let worked = logbookIndex.workedEntities + return msgs.filter { + guard let entity = DxccPrefix.entity(for: $0.callFrom) else { return true } + return !worked.contains(entity.name) + } case .needed: - // Show CQ stations not yet worked + // CQ stations not yet worked. + let workedCalls = logbookIndex.workedCalls return msgs.filter { - ($0.callTo == "CQ" || $0.callTo.hasPrefix("CQ ")) && - !workedCallSet.contains($0.callFrom.uppercased()) + isCQMessage(callTo: $0.callTo) && + !workedCalls.contains($0.callFrom.uppercased()) + } + case .forMe: + return msgs.filter { + isDirectedToMe(callTo: $0.callTo, myCall: appState.settings.myCall) } - case .forMe: return msgs.filter { $0.callTo.uppercased() == appState.settings.myCall.uppercased() } - } - } - - private func dxccPrefix(_ call: String) -> String { - var prefix = "" - for ch in call.uppercased() { - if ch.isNumber { prefix.append(ch); break } - prefix.append(ch) } - return prefix } /// Group filtered messages by slotIndex for time dividers. @@ -193,10 +263,6 @@ struct DecodeScreen: View { return groups } - private var workedCallSet: Set { - Set(appState.logbook.records.map { $0.call.uppercased() }) - } - private var emptyState: some View { VStack(spacing: 12) { Spacer() diff --git a/ios/FT8AF/FT8AF/Screens/Settings/DecodeFilterSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/DecodeFilterSettings.swift index 88be760a..2e024c9d 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/DecodeFilterSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/DecodeFilterSettings.swift @@ -3,6 +3,23 @@ import SwiftUI struct DecodeFilterSettings: View { @Environment(AppState.self) private var appState + /// Continent picker options: "All" plus the seven continent codes used by the + /// decode filter (matches Android's continent codes). + private static let continentOptions = ["All", "NA", "SA", "EU", "AF", "AS", "OC", "AN"] + + private func continentName(_ code: String) -> String { + switch code { + case "NA": return "North America" + case "SA": return "South America" + case "EU": return "Europe" + case "AF": return "Africa" + case "AS": return "Asia" + case "OC": return "Oceania" + case "AN": return "Antarctica" + default: return "All" + } + } + var body: some View { @Bindable var settings = appState.settings @@ -23,12 +40,27 @@ struct DecodeFilterSettings: View { VStack(alignment: .leading, spacing: 2) { Text("DX Only") .foregroundStyle(textPrimary) - Text("Hide domestic stations, show only DX") + Text("Hide stations on your own continent") .font(.system(size: 12)) .foregroundStyle(textFaint) } } .tint(accent) + + Picker(selection: $settings.continentFilter) { + ForEach(Self.continentOptions, id: \.self) { code in + Text(continentName(code)).tag(code) + } + } label: { + VStack(alignment: .leading, spacing: 2) { + Text("Continent Filter") + .foregroundStyle(textPrimary) + Text("Show only stations from one continent") + .font(.system(size: 12)) + .foregroundStyle(textFaint) + } + } + .tint(textMuted) } header: { Text("Filters") .foregroundStyle(textMuted) @@ -36,33 +68,57 @@ struct DecodeFilterSettings: View { .listRowBackground(bgSurface) Section { - highlightRow("New DXCC", description: "Purple highlight for unworked DXCC entities", color: statusNew) - highlightRow("New Grid", description: "Highlight for unworked grid squares", color: signal) - highlightRow("POTA Activators", description: "Green pill for spotted park activators", color: statusConfirmed) - highlightRow("Worked Stations", description: "Cyan indicator for previously worked calls", color: statusWorked) + highlightToggle( + "New DXCC", description: "Purple pill for unworked DXCC entities", + color: statusNew, isOn: $settings.highlightNewDxcc + ) + highlightToggle( + "New Grid", description: "Yellow pill for unworked grid squares", + color: statusWarn, isOn: $settings.highlightNewGrid + ) + highlightToggle( + "New Band", description: "Cyan pill for stations never worked on this band", + color: signal, isOn: $settings.highlightNewBand + ) + highlightToggle( + "Worked Stations", description: "Cyan pill for previously worked calls", + color: statusWorked, isOn: $settings.highlightWorked + ) } header: { Text("Highlights") .foregroundStyle(textMuted) } footer: { - Text("Highlight rules take effect in Phase 4 when the logbook database is integrated") + Text("Highlights compare each decode against your logbook. Disabled categories fall through to the next in priority.") .foregroundStyle(textFaint) } .listRowBackground(bgSurface) Section { - HStack { - Text("Continent Filter") - .foregroundStyle(textPrimary) - Spacer() - Text("All") - .foregroundStyle(textMuted) + Picker(selection: $settings.distanceInMiles) { + Text("Kilometers").tag(false) + Text("Miles").tag(true) + } label: { + VStack(alignment: .leading, spacing: 2) { + Text("Distance Unit") + .foregroundStyle(textPrimary) + Text("Unit for QSO-path distances in the decode list") + .font(.system(size: 12)) + .foregroundStyle(textFaint) + } } - HStack { - Text("Blocked Callsigns") - .foregroundStyle(textPrimary) - Spacer() - Text("0") - .foregroundStyle(textMuted) + .tint(textMuted) + + NavigationLink { + BlockedCallsignsSettings() + } label: { + HStack { + Text("Blocked Callsigns") + .foregroundStyle(textPrimary) + Spacer() + Text("\(appState.settings.blockedCallsigns.count)") + .font(.system(size: 14, weight: .medium, design: .monospaced)) + .foregroundStyle(textMuted) + } } } header: { Text("Advanced") @@ -77,20 +133,31 @@ struct DecodeFilterSettings: View { .toolbarColorScheme(.dark, for: .navigationBar) .onChange(of: settings.showOnlyCQ) { _, _ in SettingsPersistence.save(appState.settings) } .onChange(of: settings.dxOnly) { _, _ in SettingsPersistence.save(appState.settings) } + .onChange(of: settings.continentFilter) { _, _ in SettingsPersistence.save(appState.settings) } + .onChange(of: settings.highlightNewDxcc) { _, _ in SettingsPersistence.save(appState.settings) } + .onChange(of: settings.highlightNewGrid) { _, _ in SettingsPersistence.save(appState.settings) } + .onChange(of: settings.highlightNewBand) { _, _ in SettingsPersistence.save(appState.settings) } + .onChange(of: settings.highlightWorked) { _, _ in SettingsPersistence.save(appState.settings) } + .onChange(of: settings.distanceInMiles) { _, _ in SettingsPersistence.save(appState.settings) } } - private func highlightRow(_ title: String, description: String, color: Color) -> some View { - HStack(spacing: 10) { - Circle() - .fill(color) - .frame(width: 10, height: 10) - VStack(alignment: .leading, spacing: 2) { - Text(title) - .foregroundStyle(textPrimary) - Text(description) - .font(.system(size: 12)) - .foregroundStyle(textFaint) + private func highlightToggle( + _ title: String, description: String, color: Color, isOn: Binding + ) -> some View { + Toggle(isOn: isOn) { + HStack(spacing: 10) { + Circle() + .fill(color) + .frame(width: 10, height: 10) + VStack(alignment: .leading, spacing: 2) { + Text(title) + .foregroundStyle(textPrimary) + Text(description) + .font(.system(size: 12)) + .foregroundStyle(textFaint) + } } } + .tint(accent) } } diff --git a/ios/FT8AF/FT8AF/Screens/Settings/TransmissionSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/TransmissionSettings.swift index b0a1234e..8c9af797 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/TransmissionSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/TransmissionSettings.swift @@ -181,11 +181,30 @@ struct TransmissionSettings: View { .tint(accent) .frame(width: 100) } + + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Tune Timeout") + .foregroundStyle(textPrimary) + Text("Max carrier time for the TUNE button") + .font(.system(size: 11)) + .foregroundStyle(textFaint) + } + Spacer() + Picker("", selection: $settings.tuneTimeoutSec) { + ForEach([10, 15, 30, 60, 120], id: \.self) { sec in + Text("\(sec) s").tag(sec) + } + } + .pickerStyle(.menu) + .tint(accent) + .frame(width: 100) + } } header: { Text("TX Safety") .foregroundStyle(textMuted) } footer: { - Text("Watchdog prevents runaway TX. Stop-after limits consecutive unanswered CQ calls.") + Text("Watchdog prevents runaway TX. Stop-after limits consecutive unanswered CQ calls. TUNE always stops itself at the timeout.") .foregroundStyle(textFaint) } .listRowBackground(bgSurface) @@ -205,5 +224,6 @@ struct TransmissionSettings: View { .onChange(of: settings.autoCQAfterQSO) { _, _ in SettingsPersistence.save(appState.settings) } .onChange(of: settings.txWatchdogMin) { _, _ in SettingsPersistence.save(appState.settings) } .onChange(of: settings.stopAfterAttempts) { _, _ in SettingsPersistence.save(appState.settings) } + .onChange(of: settings.tuneTimeoutSec) { _, _ in SettingsPersistence.save(appState.settings) } } } diff --git a/ios/FT8AFKit/Sources/FT8Engine/CallerQueue.swift b/ios/FT8AFKit/Sources/FT8Engine/CallerQueue.swift new file mode 100644 index 00000000..186d5ce7 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/CallerQueue.swift @@ -0,0 +1,105 @@ +import Foundation + +/// A station that called us while we were busy with another QSO (or running a +/// CQ), waiting its turn (port of Android's `QueuedCaller`). +public struct QueuedCaller: Equatable, Sendable { + public let callsign: String + public var snr: Int + public var freqHz: Float + public var grid: String + /// When the station was last heard calling us (refreshes on re-hear). + public var queuedAtMs: Int64 + + public init(callsign: String, snr: Int, freqHz: Float, grid: String, queuedAtMs: Int64) { + self.callsign = callsign + self.snr = snr + self.freqHz = freqHz + self.grid = grid + self.queuedAtMs = queuedAtMs + } +} + +/// Pure FIFO caller-queue policy (port of the Android `callerQueue` logic in +/// `FT8TransmitSignal`): dedup by callsign (a re-heard caller refreshes its +/// SNR/frequency/timestamp but keeps its place), a hard capacity cap, and a +/// staleness rule — a caller not re-heard for a few slots has moved on and is +/// pruned rather than called into dead air. +public struct CallerQueue: Equatable, Sendable { + /// Maximum queued callers (Android `MAX_QUEUE_SIZE`). + public static let maxSize = 10 + /// Drop callers not re-heard within this window (~4 FT8 slots — a station + /// still interested re-calls every other slot). + public static let staleAfterMs: Int64 = 60_000 + + public private(set) var callers: [QueuedCaller] = [] + + public init() {} + + public var isEmpty: Bool { callers.isEmpty } + public var count: Int { callers.count } + + /// Whether a heard station belongs in the queue at all: never ourselves, + /// never the station we are already working, never a blocked callsign. + public static func shouldEnqueue( + callsign: String, + myCall: String, + currentTarget: String?, + blocked: [String] + ) -> Bool { + let call = callsign.uppercased() + guard !call.isEmpty else { return false } + if call == myCall.uppercased() { return false } + if let target = currentTarget, call == target.uppercased() { return false } + if blocked.contains(where: { $0.uppercased() == call }) { return false } + return true + } + + /// Add or refresh a caller. Dedup is by callsign: an existing entry keeps + /// its queue position but takes the new SNR/frequency/grid/timestamp. A new + /// caller is appended only while under capacity. + /// - Returns: `true` if the queue now contains the caller. + @discardableResult + public mutating func enqueue(_ caller: QueuedCaller) -> Bool { + let call = caller.callsign.uppercased() + if let idx = callers.firstIndex(where: { $0.callsign.uppercased() == call }) { + callers[idx].snr = caller.snr + callers[idx].freqHz = caller.freqHz + if !caller.grid.isEmpty { callers[idx].grid = caller.grid } + callers[idx].queuedAtMs = caller.queuedAtMs + return true + } + guard callers.count < Self.maxSize else { return false } + callers.append(caller) + return true + } + + /// Drop callers last heard more than `staleAfterMs` ago. + public mutating func removeStale(nowMs: Int64) { + callers.removeAll { nowMs - $0.queuedAtMs > Self.staleAfterMs } + } + + /// Pop the head of the queue (oldest caller first). + public mutating func dequeueNext() -> QueuedCaller? { + callers.isEmpty ? nil : callers.removeFirst() + } + + /// Pop a specific caller by callsign (case-insensitive). + public mutating func dequeue(callsign: String) -> QueuedCaller? { + let call = callsign.uppercased() + guard let idx = callers.firstIndex(where: { $0.callsign.uppercased() == call }) else { + return nil + } + return callers.remove(at: idx) + } + + /// Remove a caller without returning it (e.g. the engine locked them on + /// its own, so their queue slot is obsolete). + public mutating func remove(callsign: String) { + let call = callsign.uppercased() + callers.removeAll { $0.callsign.uppercased() == call } + } + + public mutating func removeAll() { + callers.removeAll() + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/DecodeAnnotator.swift b/ios/FT8AFKit/Sources/FT8Engine/DecodeAnnotator.swift new file mode 100644 index 00000000..ae0f5341 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/DecodeAnnotator.swift @@ -0,0 +1,166 @@ +import Foundation + +/// Highlight category for a decode-list row, mirroring the Android +/// `resolveQsoStatus` priority order (highest first): +/// PENDING (to me) > POTA > NEW DXCC > NEW GRID > NEW BAND > WORKED > CQ. +public enum DecodeHighlight: String, CaseIterable, Sendable { + case pending + case pota + case newDxcc + case newGrid + case newBand + case worked + case cq +} + +/// User toggles gating the logbook-derived highlight categories +/// (Settings → Decode Filters → Highlights). PENDING, POTA and CQ are +/// always-on because they reflect live on-air state, not logbook history. +public struct HighlightToggles: Equatable, Sendable { + public var newDxcc: Bool + public var newGrid: Bool + public var newBand: Bool + public var worked: Bool + + public init(newDxcc: Bool = true, newGrid: Bool = true, newBand: Bool = true, worked: Bool = true) { + self.newDxcc = newDxcc + self.newGrid = newGrid + self.newBand = newBand + self.worked = worked + } +} + +/// One logged QSO, reduced to the fields the annotator needs. +public struct LoggedQso: Equatable, Sendable { + public let call: String + public let grid: String + public let band: String + + public init(call: String, grid: String, band: String) { + self.call = call + self.grid = grid + self.band = band + } +} + +/// Pre-computed lookup index over the logbook, built once per render pass so +/// per-row classification is O(1). All keys are uppercased; grids are +/// truncated to their 4-char square. +public struct LogbookIndex: Sendable { + public let workedCalls: Set + public let workedGrids: Set + public let bandsByCall: [String: Set] + public let workedEntities: Set + + public init(qsos: [LoggedQso]) { + var calls = Set() + var grids = Set() + var bands = [String: Set]() + var entities = Set() + for qso in qsos { + let call = qso.call.uppercased() + guard !call.isEmpty else { continue } + calls.insert(call) + let grid = grid4(qso.grid.uppercased()) + if grid.count >= 4 { grids.insert(grid) } + let band = qso.band.uppercased() + if !band.isEmpty { bands[call, default: []].insert(band) } + if let entity = DxccPrefix.entity(for: call) { + entities.insert(entity.name) + } + } + workedCalls = calls + workedGrids = grids + bandsByCall = bands + workedEntities = entities + } + + public static let empty = LogbookIndex(qsos: []) +} + +/// True when a decode's destination field is a CQ (plain or with modifier, +/// e.g. "CQ", "CQ POTA", "CQ DX"). +public func isCQMessage(callTo: String) -> Bool { + let to = callTo.trimmingCharacters(in: .whitespaces).uppercased() + return to == "CQ" || to.hasPrefix("CQ ") +} + +/// True when a decode is directed at the operator's own callsign. +public func isDirectedToMe(callTo: String, myCall: String) -> Bool { + let my = myCall.trimmingCharacters(in: .whitespaces).uppercased() + guard !my.isEmpty else { return false } + return callTo.trimmingCharacters(in: .whitespaces).uppercased() == my +} + +/// True when a CQ looks like a POTA activation: explicit "CQ POTA" modifier, +/// or a park reference (e.g. "K-1234", "VE-5082") in the free-text/extra field. +public func isPotaCq(callTo: String, extra: String) -> Bool { + guard isCQMessage(callTo: callTo) else { return false } + let to = callTo.trimmingCharacters(in: .whitespaces).uppercased() + if to == "CQ POTA" || to.hasPrefix("CQ POTA ") { return true } + return looksLikeParkRef(extra) +} + +/// True when a token matches the POTA park-reference shape: 1–4 alphanumeric +/// program characters, a dash, then 4–5 digits (K-1234, VE-5082, DL-03000). +public func looksLikeParkRef(_ token: String) -> Bool { + let t = token.trimmingCharacters(in: .whitespaces).uppercased() + guard let dash = t.firstIndex(of: "-") else { return false } + let program = t[t.startIndex.. DecodeHighlight? { + let call = callFrom.trimmingCharacters(in: .whitespaces).uppercased() + let cq = isCQMessage(callTo: callTo) + + if isDirectedToMe(callTo: callTo, myCall: myCall) { return .pending } + if isPotaCq(callTo: callTo, extra: extra) { return .pota } + + if toggles.newDxcc, + let entity = DxccPrefix.entity(for: call), + !index.workedEntities.contains(entity.name) { + return .newDxcc + } + + let square = grid4(grid.uppercased()) + if toggles.newGrid, + square.count >= 4, + gridToLatLon(square) != nil, + !index.workedGrids.contains(square) { + return .newGrid + } + + let workedBands = index.bandsByCall[call] ?? [] + let workedThisBand = workedBands.contains(band.uppercased()) + if toggles.newBand, !workedBands.isEmpty, !workedThisBand { + return .newBand + } + + if toggles.worked, index.workedCalls.contains(call) { + return .worked + } + + if cq { return .cq } + return nil +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/DisplayFormatters.swift b/ios/FT8AFKit/Sources/FT8Engine/DisplayFormatters.swift new file mode 100644 index 00000000..b71d4fbe --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/DisplayFormatters.swift @@ -0,0 +1,53 @@ +import Foundation + +/// Pure display formatters for the decode list (age, distance). Kept in the +/// kit so they are host-testable; the SwiftUI rows are thin wrappers. + +/// Relative age of a decode, Android-style buckets: +/// <5 s → "now", <60 s → "32s", <1 h → "5m", <24 h → "2h", else "3d". +/// Negative deltas (clock skew) clamp to "now". +public func relativeAge(secondsAgo: Int) -> String { + let s = max(0, secondsAgo) + switch s { + case ..<5: return "now" + case ..<60: return "\(s)s" + case ..<3600: return "\(s / 60)m" + case ..<86400: return "\(s / 3600)h" + default: return "\(s / 86400)d" + } +} + +/// Great-circle distance in kilometers between two Maidenhead locators, or +/// nil when either grid is malformed (uses the shared `gridToLatLon`, which +/// also rejects the "RR73"/"RR" sign-off tokens). +public func gridDistanceKm(from myGrid: String, to theirGrid: String) -> Double? { + guard let a = gridToLatLon(myGrid), let b = gridToLatLon(theirGrid) else { return nil } + let earthRadiusKm = 6371.0 + let dLat = (b.0 - a.0) * .pi / 180 + let dLon = (b.1 - a.1) * .pi / 180 + let lat1 = a.0 * .pi / 180 + let lat2 = b.0 * .pi / 180 + let sinDLat = sin(dLat / 2) + let sinDLon = sin(dLon / 2) + let h = sinDLat * sinDLat + cos(lat1) * cos(lat2) * sinDLon * sinDLon + return 2 * earthRadiusKm * asin(min(1, sqrt(h))) +} + +/// Kilometers → miles. +public func kilometersToMiles(_ km: Double) -> Double { km * 0.621371 } + +/// Format a QSO-path distance for the decode meta row: whole units with the +/// operator's preferred unit ("1832 km" / "1139 mi"). Zero/negative → "". +public func formatQsoDistance(km: Double, inMiles: Bool) -> String { + guard km > 0 else { return "" } + let value = inMiles ? kilometersToMiles(km) : km + let rounded = Int(value.rounded()) + guard rounded > 0 else { return "" } + return "\(rounded) \(inMiles ? "mi" : "km")" +} + +/// Convenience: formatted grid-to-grid distance, or "" when unavailable. +public func gridDistanceText(from myGrid: String, to theirGrid: String, inMiles: Bool) -> String { + guard let km = gridDistanceKm(from: myGrid, to: theirGrid) else { return "" } + return formatQsoDistance(km: km, inMiles: inMiles) +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/DxccPrefix.swift b/ios/FT8AFKit/Sources/FT8Engine/DxccPrefix.swift new file mode 100644 index 00000000..cea1b208 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/DxccPrefix.swift @@ -0,0 +1,333 @@ +import Foundation + +/// A DXCC entity resolved from a callsign prefix. +public struct DxccEntity: Equatable, Sendable { + /// Entity display name (e.g. "United States", "Japan"). + public let name: String + /// Continent code: NA / SA / EU / AF / AS / OC / AN. + public let continent: String + + public init(name: String, continent: String) { + self.name = name + self.continent = continent + } +} + +/// Compact callsign-prefix → DXCC-entity heuristic (top ~130 entities). +/// +/// Mirrors what the Android app derives from its bundled callsign database, +/// scaled down to a longest-prefix-match table that covers the entities an +/// operator actually sees on FT8. Used for NEW DXCC highlighting, the decode +/// location line, and the DX-only / continent filters — never for logging, so +/// a rare-prefix miss is cosmetic only. +public enum DxccPrefix { + /// Resolve the DXCC entity for a callsign, or nil when no prefix matches. + /// + /// Portable designators are handled by matching every `/`-separated + /// segment and keeping the segment whose matched prefix is longest — + /// "EA8/W1AW" → Canary Islands (EA8, 3 chars beats W, 1 char); + /// "W1AW/P" → United States (bare "P" matches nothing). + /// Ties go to the earlier segment, so "W1AW/M" stays United States. + public static func entity(for call: String) -> DxccEntity? { + let segments = call.uppercased().split(separator: "/").map(String.init) + var best: (length: Int, entity: DxccEntity)? = nil + for segment in segments { + guard let (length, entity) = longestPrefixMatch(segment) else { continue } + if best == nil || length > best!.length { + best = (length, entity) + } + } + return best?.entity + } + + /// Continent code (NA/SA/EU/AF/AS/OC/AN) for a callsign, or nil when unknown. + public static func continent(for call: String) -> String? { + entity(for: call)?.continent + } + + /// Longest table prefix matching the start of `segment`, with its length. + static func longestPrefixMatch(_ segment: String) -> (Int, DxccEntity)? { + guard !segment.isEmpty else { return nil } + let maxLen = min(4, segment.count) + for len in stride(from: maxLen, through: 1, by: -1) { + let prefix = String(segment.prefix(len)) + if let entity = table[prefix] { + return (len, entity) + } + } + return nil + } + + // MARK: - Prefix table + + private static let usa = DxccEntity(name: "United States", continent: "NA") + private static let canada = DxccEntity(name: "Canada", continent: "NA") + private static let japan = DxccEntity(name: "Japan", continent: "AS") + private static let germany = DxccEntity(name: "Germany", continent: "EU") + private static let england = DxccEntity(name: "England", continent: "EU") + private static let russiaEu = DxccEntity(name: "European Russia", continent: "EU") + private static let russiaAs = DxccEntity(name: "Asiatic Russia", continent: "AS") + private static let ukraine = DxccEntity(name: "Ukraine", continent: "EU") + private static let spain = DxccEntity(name: "Spain", continent: "EU") + private static let brazil = DxccEntity(name: "Brazil", continent: "SA") + private static let cuba = DxccEntity(name: "Cuba", continent: "NA") + private static let netherlands = DxccEntity(name: "Netherlands", continent: "EU") + private static let sweden = DxccEntity(name: "Sweden", continent: "EU") + private static let poland = DxccEntity(name: "Poland", continent: "EU") + private static let southKorea = DxccEntity(name: "South Korea", continent: "AS") + private static let argentina = DxccEntity(name: "Argentina", continent: "SA") + private static let chile = DxccEntity(name: "Chile", continent: "SA") + private static let southAfrica = DxccEntity(name: "South Africa", continent: "AF") + + static let table: [String: DxccEntity] = [ + // ---- North America ---- + "K": usa, "W": usa, "N": usa, "A": usa, + "KL": DxccEntity(name: "Alaska", continent: "NA"), + "KH6": DxccEntity(name: "Hawaii", continent: "OC"), + "KH2": DxccEntity(name: "Guam", continent: "OC"), + "KP4": DxccEntity(name: "Puerto Rico", continent: "NA"), + "KP2": DxccEntity(name: "US Virgin Islands", continent: "NA"), + "VE": canada, "VA": canada, "VO": canada, "VY": canada, + "XE": DxccEntity(name: "Mexico", continent: "NA"), + "XF": DxccEntity(name: "Mexico", continent: "NA"), + "TI": DxccEntity(name: "Costa Rica", continent: "NA"), + "TG": DxccEntity(name: "Guatemala", continent: "NA"), + "YS": DxccEntity(name: "El Salvador", continent: "NA"), + "HR": DxccEntity(name: "Honduras", continent: "NA"), + "YN": DxccEntity(name: "Nicaragua", continent: "NA"), + "HP": DxccEntity(name: "Panama", continent: "NA"), + "HI": DxccEntity(name: "Dominican Republic", continent: "NA"), + "CO": cuba, "CM": cuba, + "6Y": DxccEntity(name: "Jamaica", continent: "NA"), + "ZF": DxccEntity(name: "Cayman Islands", continent: "NA"), + "C6": DxccEntity(name: "Bahamas", continent: "NA"), + "V3": DxccEntity(name: "Belize", continent: "NA"), + "VP9": DxccEntity(name: "Bermuda", continent: "NA"), + "8P": DxccEntity(name: "Barbados", continent: "NA"), + "FM": DxccEntity(name: "Martinique", continent: "NA"), + "FG": DxccEntity(name: "Guadeloupe", continent: "NA"), + "J3": DxccEntity(name: "Grenada", continent: "NA"), + "OX": DxccEntity(name: "Greenland", continent: "NA"), + + // ---- South America ---- + "PY": brazil, "PP": brazil, "PR": brazil, "PS": brazil, "PT": brazil, "PU": brazil, + "LU": argentina, "LW": argentina, + "CE": chile, "CA": chile, "XQ": chile, + "HK": DxccEntity(name: "Colombia", continent: "SA"), + "HJ": DxccEntity(name: "Colombia", continent: "SA"), + "YV": DxccEntity(name: "Venezuela", continent: "SA"), + "OA": DxccEntity(name: "Peru", continent: "SA"), + "HC": DxccEntity(name: "Ecuador", continent: "SA"), + "CX": DxccEntity(name: "Uruguay", continent: "SA"), + "ZP": DxccEntity(name: "Paraguay", continent: "SA"), + "CP": DxccEntity(name: "Bolivia", continent: "SA"), + "PZ": DxccEntity(name: "Suriname", continent: "SA"), + "9Y": DxccEntity(name: "Trinidad & Tobago", continent: "SA"), + + // ---- Europe ---- + "G": england, "M": england, "2E": england, + "GM": DxccEntity(name: "Scotland", continent: "EU"), + "MM": DxccEntity(name: "Scotland", continent: "EU"), + "2M": DxccEntity(name: "Scotland", continent: "EU"), + "GW": DxccEntity(name: "Wales", continent: "EU"), + "MW": DxccEntity(name: "Wales", continent: "EU"), + "2W": DxccEntity(name: "Wales", continent: "EU"), + "GI": DxccEntity(name: "Northern Ireland", continent: "EU"), + "MI": DxccEntity(name: "Northern Ireland", continent: "EU"), + "GD": DxccEntity(name: "Isle of Man", continent: "EU"), + "GJ": DxccEntity(name: "Jersey", continent: "EU"), + "GU": DxccEntity(name: "Guernsey", continent: "EU"), + "EI": DxccEntity(name: "Ireland", continent: "EU"), + "EJ": DxccEntity(name: "Ireland", continent: "EU"), + "F": DxccEntity(name: "France", continent: "EU"), + "DA": germany, "DB": germany, "DC": germany, "DD": germany, "DF": germany, + "DG": germany, "DH": germany, "DJ": germany, "DK": germany, "DL": germany, + "DM": germany, "DN": germany, "DO": germany, "DP": germany, "DR": germany, + "I": DxccEntity(name: "Italy", continent: "EU"), + "EA": spain, "EB": spain, "EC": spain, "ED": spain, "EE": spain, + "EF": spain, "EG": spain, "EH": spain, + "EA6": DxccEntity(name: "Balearic Islands", continent: "EU"), + "EA8": DxccEntity(name: "Canary Islands", continent: "AF"), + "EA9": DxccEntity(name: "Ceuta & Melilla", continent: "AF"), + "CT": DxccEntity(name: "Portugal", continent: "EU"), + "CT3": DxccEntity(name: "Madeira Islands", continent: "AF"), + "CU": DxccEntity(name: "Azores", continent: "EU"), + "ON": DxccEntity(name: "Belgium", continent: "EU"), + "OO": DxccEntity(name: "Belgium", continent: "EU"), + "OT": DxccEntity(name: "Belgium", continent: "EU"), + "PA": netherlands, "PB": netherlands, "PC": netherlands, "PD": netherlands, + "PE": netherlands, "PF": netherlands, "PG": netherlands, "PH": netherlands, + "PI": netherlands, + "HB": DxccEntity(name: "Switzerland", continent: "EU"), + "HB0": DxccEntity(name: "Liechtenstein", continent: "EU"), + "OE": DxccEntity(name: "Austria", continent: "EU"), + "OZ": DxccEntity(name: "Denmark", continent: "EU"), + "LA": DxccEntity(name: "Norway", continent: "EU"), + "LB": DxccEntity(name: "Norway", continent: "EU"), + "SM": sweden, "SA": sweden, "SK": sweden, "SL": sweden, + "OH": DxccEntity(name: "Finland", continent: "EU"), + "OH0": DxccEntity(name: "Aland Islands", continent: "EU"), + "OY": DxccEntity(name: "Faroe Islands", continent: "EU"), + "TF": DxccEntity(name: "Iceland", continent: "EU"), + "SP": poland, "SN": poland, "SO": poland, "SQ": poland, "3Z": poland, + "OK": DxccEntity(name: "Czech Republic", continent: "EU"), + "OL": DxccEntity(name: "Czech Republic", continent: "EU"), + "OM": DxccEntity(name: "Slovakia", continent: "EU"), + "HA": DxccEntity(name: "Hungary", continent: "EU"), + "HG": DxccEntity(name: "Hungary", continent: "EU"), + "YO": DxccEntity(name: "Romania", continent: "EU"), + "YP": DxccEntity(name: "Romania", continent: "EU"), + "YQ": DxccEntity(name: "Romania", continent: "EU"), + "YR": DxccEntity(name: "Romania", continent: "EU"), + "LZ": DxccEntity(name: "Bulgaria", continent: "EU"), + "SV": DxccEntity(name: "Greece", continent: "EU"), + "SW": DxccEntity(name: "Greece", continent: "EU"), + "SX": DxccEntity(name: "Greece", continent: "EU"), + "SY": DxccEntity(name: "Greece", continent: "EU"), + "SZ": DxccEntity(name: "Greece", continent: "EU"), + "9A": DxccEntity(name: "Croatia", continent: "EU"), + "S5": DxccEntity(name: "Slovenia", continent: "EU"), + "E7": DxccEntity(name: "Bosnia-Herzegovina", continent: "EU"), + "YU": DxccEntity(name: "Serbia", continent: "EU"), + "YT": DxccEntity(name: "Serbia", continent: "EU"), + "Z3": DxccEntity(name: "North Macedonia", continent: "EU"), + "ZA": DxccEntity(name: "Albania", continent: "EU"), + "4O": DxccEntity(name: "Montenegro", continent: "EU"), + "Z6": DxccEntity(name: "Kosovo", continent: "EU"), + // Russia: R/UA–UI default to European; the 8/9/0 call areas are Asiatic. + "R": russiaEu, + "UA": russiaEu, "UB": russiaEu, "UC": russiaEu, "UD": russiaEu, + "UE": russiaEu, "UF": russiaEu, "UG": russiaEu, "UH": russiaEu, "UI": russiaEu, + "R8": russiaAs, "R9": russiaAs, "R0": russiaAs, + "RA8": russiaAs, "RA9": russiaAs, "RA0": russiaAs, + "UA8": russiaAs, "UA9": russiaAs, "UA0": russiaAs, + "UR": ukraine, "US": ukraine, "UT": ukraine, "UU": ukraine, "UV": ukraine, + "UW": ukraine, "UX": ukraine, "UY": ukraine, "UZ": ukraine, + "EM": ukraine, "EN": ukraine, "EO": ukraine, + "EU": DxccEntity(name: "Belarus", continent: "EU"), + "EV": DxccEntity(name: "Belarus", continent: "EU"), + "EW": DxccEntity(name: "Belarus", continent: "EU"), + "YL": DxccEntity(name: "Latvia", continent: "EU"), + "LY": DxccEntity(name: "Lithuania", continent: "EU"), + "ES": DxccEntity(name: "Estonia", continent: "EU"), + "ER": DxccEntity(name: "Moldova", continent: "EU"), + "LX": DxccEntity(name: "Luxembourg", continent: "EU"), + "9H": DxccEntity(name: "Malta", continent: "EU"), + "ZB": DxccEntity(name: "Gibraltar", continent: "EU"), + "T7": DxccEntity(name: "San Marino", continent: "EU"), + "HV": DxccEntity(name: "Vatican City", continent: "EU"), + "C3": DxccEntity(name: "Andorra", continent: "EU"), + "3A": DxccEntity(name: "Monaco", continent: "EU"), + + // ---- Asia ---- + "JA": japan, "JE": japan, "JF": japan, "JG": japan, "JH": japan, + "JI": japan, "JJ": japan, "JK": japan, "JL": japan, "JM": japan, + "JN": japan, "JO": japan, "JP": japan, "JQ": japan, "JR": japan, + "JS": japan, "7J": japan, "7K": japan, "7L": japan, "7M": japan, "7N": japan, + "HL": southKorea, "DS": southKorea, "6K": southKorea, + "P5": DxccEntity(name: "North Korea", continent: "AS"), + "B": DxccEntity(name: "China", continent: "AS"), + "BV": DxccEntity(name: "Taiwan", continent: "AS"), + "VR2": DxccEntity(name: "Hong Kong", continent: "AS"), + "XX9": DxccEntity(name: "Macao", continent: "AS"), + "VU": DxccEntity(name: "India", continent: "AS"), + "AP": DxccEntity(name: "Pakistan", continent: "AS"), + "4S": DxccEntity(name: "Sri Lanka", continent: "AS"), + "S2": DxccEntity(name: "Bangladesh", continent: "AS"), + "9N": DxccEntity(name: "Nepal", continent: "AS"), + "A5": DxccEntity(name: "Bhutan", continent: "AS"), + "HS": DxccEntity(name: "Thailand", continent: "AS"), + "E2": DxccEntity(name: "Thailand", continent: "AS"), + "XV": DxccEntity(name: "Vietnam", continent: "AS"), + "3W": DxccEntity(name: "Vietnam", continent: "AS"), + "XU": DxccEntity(name: "Cambodia", continent: "AS"), + "XW": DxccEntity(name: "Laos", continent: "AS"), + "XZ": DxccEntity(name: "Myanmar", continent: "AS"), + "9V": DxccEntity(name: "Singapore", continent: "AS"), + "9M": DxccEntity(name: "Malaysia", continent: "AS"), + "HZ": DxccEntity(name: "Saudi Arabia", continent: "AS"), + "7Z": DxccEntity(name: "Saudi Arabia", continent: "AS"), + "A4": DxccEntity(name: "Oman", continent: "AS"), + "A6": DxccEntity(name: "United Arab Emirates", continent: "AS"), + "A7": DxccEntity(name: "Qatar", continent: "AS"), + "A9": DxccEntity(name: "Bahrain", continent: "AS"), + "9K": DxccEntity(name: "Kuwait", continent: "AS"), + "OD": DxccEntity(name: "Lebanon", continent: "AS"), + "YK": DxccEntity(name: "Syria", continent: "AS"), + "JY": DxccEntity(name: "Jordan", continent: "AS"), + "YI": DxccEntity(name: "Iraq", continent: "AS"), + "EP": DxccEntity(name: "Iran", continent: "AS"), + "4X": DxccEntity(name: "Israel", continent: "AS"), + "4Z": DxccEntity(name: "Israel", continent: "AS"), + "TA": DxccEntity(name: "Turkey", continent: "AS"), + "TB": DxccEntity(name: "Turkey", continent: "AS"), + "TC": DxccEntity(name: "Turkey", continent: "AS"), + "EK": DxccEntity(name: "Armenia", continent: "AS"), + "4J": DxccEntity(name: "Azerbaijan", continent: "AS"), + "4K": DxccEntity(name: "Azerbaijan", continent: "AS"), + "4L": DxccEntity(name: "Georgia", continent: "AS"), + "UN": DxccEntity(name: "Kazakhstan", continent: "AS"), + "UP": DxccEntity(name: "Kazakhstan", continent: "AS"), + "UK": DxccEntity(name: "Uzbekistan", continent: "AS"), + "UJ": DxccEntity(name: "Uzbekistan", continent: "AS"), + "EX": DxccEntity(name: "Kyrgyzstan", continent: "AS"), + "EY": DxccEntity(name: "Tajikistan", continent: "AS"), + "EZ": DxccEntity(name: "Turkmenistan", continent: "AS"), + "T6": DxccEntity(name: "Afghanistan", continent: "AS"), + "JT": DxccEntity(name: "Mongolia", continent: "AS"), + "8Q": DxccEntity(name: "Maldives", continent: "AS"), + + // ---- Oceania ---- + "VK": DxccEntity(name: "Australia", continent: "OC"), + "ZL": DxccEntity(name: "New Zealand", continent: "OC"), + "YB": DxccEntity(name: "Indonesia", continent: "OC"), + "YC": DxccEntity(name: "Indonesia", continent: "OC"), + "YD": DxccEntity(name: "Indonesia", continent: "OC"), + "YE": DxccEntity(name: "Indonesia", continent: "OC"), + "DU": DxccEntity(name: "Philippines", continent: "OC"), + "DV": DxccEntity(name: "Philippines", continent: "OC"), + "DW": DxccEntity(name: "Philippines", continent: "OC"), + "DX": DxccEntity(name: "Philippines", continent: "OC"), + "V8": DxccEntity(name: "Brunei", continent: "OC"), + "P2": DxccEntity(name: "Papua New Guinea", continent: "OC"), + "FK": DxccEntity(name: "New Caledonia", continent: "OC"), + "3D2": DxccEntity(name: "Fiji", continent: "OC"), + "5W": DxccEntity(name: "Samoa", continent: "OC"), + "A3": DxccEntity(name: "Tonga", continent: "OC"), + "YJ": DxccEntity(name: "Vanuatu", continent: "OC"), + + // ---- Africa ---- + "ZS": southAfrica, "ZR": southAfrica, "ZT": southAfrica, "ZU": southAfrica, + "SU": DxccEntity(name: "Egypt", continent: "AF"), + "CN": DxccEntity(name: "Morocco", continent: "AF"), + "7X": DxccEntity(name: "Algeria", continent: "AF"), + "3V": DxccEntity(name: "Tunisia", continent: "AF"), + "5A": DxccEntity(name: "Libya", continent: "AF"), + "ST": DxccEntity(name: "Sudan", continent: "AF"), + "ET": DxccEntity(name: "Ethiopia", continent: "AF"), + "5Z": DxccEntity(name: "Kenya", continent: "AF"), + "5H": DxccEntity(name: "Tanzania", continent: "AF"), + "9J": DxccEntity(name: "Zambia", continent: "AF"), + "Z2": DxccEntity(name: "Zimbabwe", continent: "AF"), + "7Q": DxccEntity(name: "Malawi", continent: "AF"), + "C9": DxccEntity(name: "Mozambique", continent: "AF"), + "A2": DxccEntity(name: "Botswana", continent: "AF"), + "V5": DxccEntity(name: "Namibia", continent: "AF"), + "5N": DxccEntity(name: "Nigeria", continent: "AF"), + "9G": DxccEntity(name: "Ghana", continent: "AF"), + "TU": DxccEntity(name: "Cote d'Ivoire", continent: "AF"), + "6W": DxccEntity(name: "Senegal", continent: "AF"), + "EL": DxccEntity(name: "Liberia", continent: "AF"), + "TZ": DxccEntity(name: "Mali", continent: "AF"), + "5R": DxccEntity(name: "Madagascar", continent: "AF"), + "3B8": DxccEntity(name: "Mauritius", continent: "AF"), + "FR": DxccEntity(name: "Reunion Island", continent: "AF"), + "D2": DxccEntity(name: "Angola", continent: "AF"), + "D4": DxccEntity(name: "Cape Verde", continent: "AF"), + "9Q": DxccEntity(name: "DR Congo", continent: "AF"), + "TR": DxccEntity(name: "Gabon", continent: "AF"), + "TJ": DxccEntity(name: "Cameroon", continent: "AF"), + "9X": DxccEntity(name: "Rwanda", continent: "AF"), + "5X": DxccEntity(name: "Uganda", continent: "AF"), + ] +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/QsoEngine.swift b/ios/FT8AFKit/Sources/FT8Engine/QsoEngine.swift index 60cc4a4d..b6860b3f 100644 --- a/ios/FT8AFKit/Sources/FT8Engine/QsoEngine.swift +++ b/ios/FT8AFKit/Sources/FT8Engine/QsoEngine.swift @@ -240,8 +240,41 @@ public final class QsoEngine { stage = .rr73 } + /// Force-log the current QSO and move on without waiting for the partner's + /// 73 (the Active QSO panel's LOG action; port of Android + /// `forceLogAndMoveOn`). Only produces a record when the QSO progressed far + /// enough to be a real contact — both reports known, i.e. we have sent + /// R-report or beyond (Android `shouldForceLog`: order >= 3). Either way + /// the QSO context is cleared and we return to CQ / stop per + /// `autoReturnToCq`. + public func forceLog() -> QsoOutcome? { + guard let dx = target else { return nil } + let progressed = stage == .rReport || stage == .rr73 || stage == .bye73 + let outcome: QsoOutcome? = progressed ? .completed(makeRecord(dx)) : nil + finishQso() + return outcome + } + + /// Abandon the current target and return to the CQ baseline (the Active QSO + /// panel's ✕ action; port of Android `userResetToCQ`). Nothing is logged. + public func abandonToCq() { + finishQso() + } + private func complete(_ dx: String) -> QsoOutcome { - let record = QsoRecord( + let record = makeRecord(dx) + + // If a courtesy "73" is queued (the .rReport path set stage = .bye73), + // leave pendingTx/stage so the scheduler transmits it; the next slot's + // .bye73 handler in processRx then wraps up. Otherwise finish now. + if stage != .bye73 { + finishQso() + } + return .completed(record) + } + + private func makeRecord(_ dx: String) -> QsoRecord { + QsoRecord( call: dx, gridsquare: gridRcvd ?? "", mode: "FT8", @@ -257,14 +290,6 @@ public final class QsoEngine { myGridsquare: myGrid, comment: "" ) - - // If a courtesy "73" is queued (the .rReport path set stage = .bye73), - // leave pendingTx/stage so the scheduler transmits it; the next slot's - // .bye73 handler in processRx then wraps up. Otherwise finish now. - if stage != .bye73 { - finishQso() - } - return .completed(record) } /// Return to calling CQ, or stop, per `autoReturnToCq`. diff --git a/ios/FT8AFKit/Sources/FT8Engine/TuneTone.swift b/ios/FT8AFKit/Sources/FT8Engine/TuneTone.swift new file mode 100644 index 00000000..967c3a58 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/TuneTone.swift @@ -0,0 +1,54 @@ +import Foundation + +/// Pure generator for the TUNE carrier: a steady sine at the current TX audio +/// frequency, used for antenna/amplifier tuning (port of Android's tune +/// carrier, issue #408). Short raised-cosine ramps at both ends avoid key +/// clicks; the buffer length is capped by the tune safety timeout, so playback +/// simply ending enforces the hard max-on time even if the countdown UI dies. +public enum TuneTone { + /// Ramp length applied at the head and tail of the carrier. + public static let rampMs = 10 + + /// Label for the TUNE pill: the plain label when idle, "TUNE 7s" while the + /// carrier is up so the operator sees the safety timeout running (port of + /// Android `tuneChipLabel`). + public static func chipLabel( + _ label: String = "TUNE", + isTuning: Bool, + remainingSec: Int + ) -> String { + isTuning ? "\(label) \(max(0, remainingSec))s" : label + } + + /// Generate `durationSec` seconds of steady carrier at `freqHz`. + /// Returns an empty buffer for degenerate inputs (non-positive duration, + /// frequency, or sample rate). + public static func samples( + freqHz: Float, + sampleRate: Int, + durationSec: Int, + amplitude: Float = 0.9 + ) -> [Float] { + guard durationSec > 0, sampleRate > 0, freqHz > 0 else { return [] } + let count = sampleRate * durationSec + let rampSamples = min(count / 2, sampleRate * rampMs / 1000) + let omega = 2.0 * Double.pi * Double(freqHz) / Double(sampleRate) + + var out = [Float](repeating: 0, count: count) + for i in 0.. 0 { + // Raised-cosine fade-in / fade-out. + if i < rampSamples { + let t = Double(i) / Double(rampSamples) + a *= 0.5 * (1 - cos(.pi * t)) + } else if i >= count - rampSamples { + let t = Double(count - 1 - i) / Double(rampSamples) + a *= 0.5 * (1 - cos(.pi * t)) + } + } + out[i] = Float(a) + } + return out + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/TxStageSelector.swift b/ios/FT8AFKit/Sources/FT8Engine/TxStageSelector.swift new file mode 100644 index 00000000..67fa1016 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/TxStageSelector.swift @@ -0,0 +1,83 @@ +import Foundation + +/// Pure display/ordering rules for the TX message-selector chips in the +/// Active QSO panel (Android `TxSelector`: CQ / GRID / RPT / R-RPT / RR73 / 73). +/// The chip matching the engine's current stage renders active (amber); chips +/// for stages already passed render completed (cyan). +public enum TxStageSelector { + /// Chip order as displayed. Note the canonical QSO progression is + /// GRID → RPT → R-RPT → RR73 → 73, with CQ as the idle/baseline chip. + public static let chips: [TxStage] = [.cq, .grid, .report, .rReport, .rr73, .bye73] + + /// Short label for a chip, matching Android's `TxSelector` labels. + public static func label(for stage: TxStage) -> String { + switch stage { + case .cq: return "CQ" + case .grid: return "GRID" + case .report: return "RPT" + case .rReport: return "R-RPT" + case .rr73: return "RR73" + case .bye73: return "73" + case .idle: return "" + } + } + + /// Position of a stage in the QSO progression (CQ = 0 … 73 = 5); nil for idle. + public static func index(of stage: TxStage) -> Int? { + chips.firstIndex(of: stage) + } + + /// Whether `chip` is the currently active (queued) message. + public static func isActive(_ chip: TxStage, current: TxStage) -> Bool { + chip == current + } + + /// Whether `chip` represents a stage the QSO has already progressed past. + /// Idle marks nothing completed; CQ never shows as completed (it is the + /// baseline, not a QSO step). + public static func isCompleted(_ chip: TxStage, current: TxStage) -> Bool { + guard current != .idle, chip != .cq, + let chipIdx = index(of: chip), + let curIdx = index(of: current) else { return false } + return chipIdx < curIdx + } +} + +/// Pure header-state rule for the Active QSO panel (Android `StationHeader` +/// wording: Hunting / Calling CQ / "QSOing with X" / "Waiting for X"). +public enum QsoPanelHeader { + public enum State: Equatable { + case hunting + case callingCq + case qsoing(String) + case waiting(String) + case idle + } + + /// - Parameters: + /// - targetCall: locked QSO partner, empty/nil when none + /// - isActivated: the sequencer is armed (CQ run or QSO) + /// - huntEnabled: HUNT (auto-answer CQs) is armed + /// - huntCallsCQ: the hunt-calls-CQ hybrid setting — a CQing hunt shows + /// "Calling CQ" (it *is* transmitting); only a silent listening hunt + /// shows "Hunting" (mirrors Android's `isHunting` rule). + /// - isTransmitting: audio is keyed this cycle + public static func state( + targetCall: String?, + isActivated: Bool, + huntEnabled: Bool, + huntCallsCQ: Bool, + isTransmitting: Bool + ) -> State { + if let dx = targetCall, !dx.isEmpty { + return isTransmitting ? .qsoing(dx) : .waiting(dx) + } + if isActivated { + return (huntEnabled && !huntCallsCQ) ? .hunting : .callingCq + } + if huntEnabled { + return .hunting + } + return .idle + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/TxSupervisor.swift b/ios/FT8AFKit/Sources/FT8Engine/TxSupervisor.swift new file mode 100644 index 00000000..962beae2 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/TxSupervisor.swift @@ -0,0 +1,84 @@ +import Foundation + +/// Pure counting/decision logic for the two TX safety nets (port of Android's +/// `launchSupervision` watchdog + `noReplyLimit` stop-after-N in +/// `FT8TransmitSignal`/`GeneralVariables`): +/// +/// - **Watchdog**: auto-stop any repeating TX (CQ or QSO retries) after N +/// minutes of continuous transmission cycles. The clock starts when TX is +/// armed and is refreshed on events that prove the operator/sequencer is +/// making progress (new CQ run, new target, completed QSO with auto-CQ, +/// hunt idle-listening) — a *stuck* QSO deliberately keeps aging it. +/// - **Stop-after-N**: stop calling after N consecutive transmissions of the +/// *same* message with no reply in between. A stage advance (message text +/// changes) or a reply from the target resets the streak. +/// +/// The type holds no timers and does no I/O: the caller feeds it events with +/// explicit `nowMs` timestamps and asks `stopReason` before each TX cycle, so +/// every rule is unit-testable. +public struct TxSupervisor: Equatable, Sendable { + public enum StopReason: Equatable, Sendable { + /// Continuous TX exceeded the watchdog timeout. + case watchdog + /// The same message went unanswered `stopAfterAttempts` times. + case noReply + } + + /// Epoch ms when the watchdog clock last (re)started. + public private(set) var watchdogStartMs: Int64 + /// Consecutive transmissions of `lastMessage` without an intervening reply. + public private(set) var noReplyCount: Int = 0 + /// The message text whose no-reply streak is being counted. + public private(set) var lastMessage: String? + + public init(nowMs: Int64) { + watchdogStartMs = nowMs + } + + /// Restart the watchdog clock (TX armed, target changed, QSO completed + /// with auto-CQ, hunt idle-listening slot, …). + public mutating func resetWatchdog(nowMs: Int64) { + watchdogStartMs = nowMs + } + + /// A reply from the current target arrived — the run is alive, so the + /// no-reply streak resets (the watchdog is deliberately *not* refreshed + /// here; see the type doc). + public mutating func noteReply() { + noReplyCount = 0 + } + + /// Record one transmission of `message`. Repeating the same text grows + /// the no-reply streak; any change of message (stage advance, new target) + /// starts a fresh streak of 1. + public mutating func noteTransmission(_ message: String) { + if message == lastMessage { + noReplyCount += 1 + } else { + lastMessage = message + noReplyCount = 1 + } + } + + /// Clear the no-reply streak entirely (new QSO/target/CQ run). + public mutating func resetAttempts() { + noReplyCount = 0 + lastMessage = nil + } + + /// Whether TX must stop *before* the next transmission cycle. + /// + /// - Parameters: + /// - nowMs: current epoch ms + /// - watchdogMin: watchdog timeout in minutes, 0 = off + /// - stopAfterAttempts: max consecutive no-reply sends of one message, 0 = off + public func stopReason(nowMs: Int64, watchdogMin: Int, stopAfterAttempts: Int) -> StopReason? { + if watchdogMin > 0, nowMs - watchdogStartMs > Int64(watchdogMin) * 60_000 { + return .watchdog + } + if stopAfterAttempts > 0, noReplyCount >= stopAfterAttempts { + return .noReply + } + return nil + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift b/ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift new file mode 100644 index 00000000..9a484d44 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift @@ -0,0 +1,29 @@ +import Foundation + +/// Pure timing math for TX playback scheduling. +/// +/// The FT8 waveform is 12.64 s in a 15 s cycle, leaving 2.36 s of slack. When +/// playback starts late into the cycle we clip the *leading* audio so the +/// transmission still ends on the cycle boundary — but ONLY the portion past +/// the slack. Clipping anything on an on-time start (e.g. a naive +/// `msIntoCycle % 15000`) chops the leading Costas sync array and produces an +/// audible-but-undecodable signal (see project CLAUDE.md, "FT8 TX audio +/// pipeline" gotcha #2, fixed on Android in PR #93). +public enum TxTiming { + /// Slack between the 12.64 s FT8 waveform and the 15 s cycle. + public static let defaultSlackMs: Int64 = 15_000 - 12_640 // 2360 + + /// Leading milliseconds to clip: `max(0, msIntoCycle - slack)`. + /// An on-time start (anything within the slack) clips nothing. + public static func lateStartClipMs( + msIntoCycle: Int64, + slackMs: Int64 = defaultSlackMs + ) -> Int64 { + max(0, msIntoCycle - max(0, slackMs)) + } + + /// Convert a clip duration to a leading sample count at `sampleRate`. + public static func clipSampleCount(clipMs: Int64, sampleRate: Int) -> Int { + max(0, Int(clipMs) * sampleRate / 1000) + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/CallerQueueTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/CallerQueueTests.swift new file mode 100644 index 00000000..0574fdf0 --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/CallerQueueTests.swift @@ -0,0 +1,106 @@ +import XCTest +@testable import FT8Engine + +final class CallerQueueTests: XCTestCase { + + private func caller(_ call: String, snr: Int = -10, at ms: Int64 = 0) -> QueuedCaller { + QueuedCaller(callsign: call, snr: snr, freqHz: 1500, grid: "FN42", queuedAtMs: ms) + } + + // MARK: enqueue / dedup + + func testEnqueueAppendsInOrder() { + var q = CallerQueue() + q.enqueue(caller("W1AAA")) + q.enqueue(caller("W2BBB")) + XCTAssertEqual(q.callers.map(\.callsign), ["W1AAA", "W2BBB"]) + } + + func testDedupByCallsignRefreshesButKeepsPosition() { + var q = CallerQueue() + q.enqueue(caller("W1AAA", snr: -20, at: 0)) + q.enqueue(caller("W2BBB", at: 0)) + // Re-heard louder and later — updates in place, stays at the head. + XCTAssertTrue(q.enqueue(caller("w1aaa", snr: -3, at: 30_000))) + XCTAssertEqual(q.count, 2) + XCTAssertEqual(q.callers[0].callsign, "W1AAA") + XCTAssertEqual(q.callers[0].snr, -3) + XCTAssertEqual(q.callers[0].queuedAtMs, 30_000) + } + + func testCapacityCap() { + var q = CallerQueue() + for i in 0.. DecodeHighlight? { + classifyDecode( + callFrom: from, callTo: to, grid: grid, extra: extra, + myCall: myCall, band: band, index: index ?? self.index, toggles: toggles + ) + } + + // MARK: - Message-shape helpers + + func testIsCQMessage() { + XCTAssertTrue(isCQMessage(callTo: "CQ")) + XCTAssertTrue(isCQMessage(callTo: "CQ POTA")) + XCTAssertTrue(isCQMessage(callTo: "CQ DX")) + XCTAssertFalse(isCQMessage(callTo: "CQX1AB")) + XCTAssertFalse(isCQMessage(callTo: "W1AW")) + XCTAssertFalse(isCQMessage(callTo: "")) + } + + func testIsDirectedToMe() { + XCTAssertTrue(isDirectedToMe(callTo: "kd2ogr", myCall: myCall)) + XCTAssertFalse(isDirectedToMe(callTo: "W1AW", myCall: myCall)) + XCTAssertFalse(isDirectedToMe(callTo: "KD2OGR", myCall: "")) + } + + func testParkRefShape() { + XCTAssertTrue(looksLikeParkRef("K-1234")) + XCTAssertTrue(looksLikeParkRef("VE-5082")) + XCTAssertTrue(looksLikeParkRef("DL-03000")) + XCTAssertFalse(looksLikeParkRef("RR73")) + XCTAssertFalse(looksLikeParkRef("K-12")) // number too short + XCTAssertFalse(looksLikeParkRef("KABCD-1234")) // program too long + XCTAssertFalse(looksLikeParkRef("K-12A4")) // non-digit number + XCTAssertFalse(looksLikeParkRef("")) + } + + func testIsPotaCq() { + XCTAssertTrue(isPotaCq(callTo: "CQ POTA", extra: "")) + XCTAssertTrue(isPotaCq(callTo: "CQ", extra: "K-1234")) + XCTAssertFalse(isPotaCq(callTo: "CQ", extra: "FN31")) + XCTAssertFalse(isPotaCq(callTo: "W1AW", extra: "K-1234")) // not a CQ + } + + // MARK: - Priority order + + func testPendingBeatsEverything() { + // Directed at me, from a station that is also new DXCC → PENDING wins. + XCTAssertEqual(classify(from: "JA1ABC", to: "KD2OGR", grid: "PM95"), .pending) + } + + func testPotaBeatsNewDxcc() { + // New-DXCC station activating a park → POTA wins (Android order). + XCTAssertEqual(classify(from: "JA1ABC", to: "CQ POTA", grid: "PM95"), .pota) + XCTAssertEqual(classify(from: "JA1ABC", to: "CQ", grid: "PM95", extra: "K-1234"), .pota) + } + + func testNewDxccBeatsNewGrid() { + // Japan not in logbook, grid also new → NEW DXCC wins. + XCTAssertEqual(classify(from: "JA1ABC", to: "CQ", grid: "PM95"), .newDxcc) + } + + func testNewGridWhenEntityWorked() { + // USA entity is worked (W1AW), but EM12 is a new grid. + XCTAssertEqual(classify(from: "K5ABC", to: "CQ", grid: "EM12"), .newGrid) + } + + func testNewBandWhenGridAndEntityWorked() { + // DL4RCK worked on 40M only; on 20M with his already-worked grid → NEW BAND. + XCTAssertEqual(classify(from: "DL4RCK", to: "CQ", grid: "JO31", band: "20M"), .newBand) + } + + func testWorkedOnThisBand() { + // W1AW worked on 20M, known grid → WORKED. + XCTAssertEqual(classify(from: "W1AW", to: "CQ", grid: "FN31", band: "20M"), .worked) + } + + func testPlainCqFallback() { + // Entity/grid toggles off → plain CQ. + let off = HighlightToggles(newDxcc: false, newGrid: false, newBand: false, worked: false) + XCTAssertEqual(classify(from: "JA1ABC", to: "CQ", grid: "PM95", toggles: off), .cq) + } + + func testNilForUninterestingThirdPartyTraffic() { + // Mid-QSO with someone else, everything already worked → no pill. + XCTAssertEqual(classify(from: "W1AW", to: "K5XYZ", grid: "FN31", band: "20M", + toggles: HighlightToggles(worked: false)), nil) + } + + // MARK: - Toggle gating (each category falls through when disabled) + + func testNewDxccToggleFallsThrough() { + let t = HighlightToggles(newDxcc: false) + XCTAssertEqual(classify(from: "JA1ABC", to: "CQ", grid: "PM95", toggles: t), .newGrid) + } + + func testNewGridToggleFallsThrough() { + let t = HighlightToggles(newGrid: false) + // K5ABC: entity worked, grid EM12 new but gated off, never worked → CQ. + XCTAssertEqual(classify(from: "K5ABC", to: "CQ", grid: "EM12", toggles: t), .cq) + } + + func testNewBandToggleFallsThrough() { + let t = HighlightToggles(newBand: false) + XCTAssertEqual(classify(from: "DL4RCK", to: "CQ", grid: "JO31", band: "20M", toggles: t), .worked) + } + + func testWorkedToggleFallsThrough() { + let t = HighlightToggles(worked: false) + XCTAssertEqual(classify(from: "W1AW", to: "CQ", grid: "FN31", band: "20M", toggles: t), .cq) + } + + // MARK: - Grid validity + + func testRr73IsNotANewGrid() { + // "RR73" parses like a locator but is a sign-off; must not classify as NEW GRID. + XCTAssertEqual(classify(from: "K5ABC", to: "CQ", grid: "RR73", + toggles: HighlightToggles(worked: false)), .cq) + } + + func testShortGridIsNotANewGrid() { + XCTAssertEqual(classify(from: "K5ABC", to: "CQ", grid: "FN", + toggles: HighlightToggles(worked: false)), .cq) + } + + func testSixCharGridMatchesWorkedSquare() { + // FN31pr truncates to FN31, which is worked → not a new grid. + XCTAssertEqual(classify(from: "K5ABC", to: "CQ", grid: "FN31pr", + toggles: HighlightToggles(worked: false)), .cq) + } + + // MARK: - LogbookIndex construction + + func testIndexNormalizesCase() { + let idx = LogbookIndex(qsos: [LoggedQso(call: "w1aw", grid: "fn31pr", band: "20m")]) + XCTAssertTrue(idx.workedCalls.contains("W1AW")) + XCTAssertTrue(idx.workedGrids.contains("FN31")) + XCTAssertEqual(idx.bandsByCall["W1AW"], ["20M"]) + XCTAssertTrue(idx.workedEntities.contains("United States")) + } + + func testIndexSkipsEmptyRecords() { + let idx = LogbookIndex(qsos: [LoggedQso(call: "", grid: "", band: "")]) + XCTAssertTrue(idx.workedCalls.isEmpty) + XCTAssertTrue(idx.workedGrids.isEmpty) + XCTAssertTrue(idx.workedEntities.isEmpty) + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/DisplayFormattersTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/DisplayFormattersTests.swift new file mode 100644 index 00000000..320ffde8 --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/DisplayFormattersTests.swift @@ -0,0 +1,90 @@ +import XCTest +@testable import FT8Engine + +final class DisplayFormattersTests: XCTestCase { + + // MARK: - Relative age buckets + + func testAgeNow() { + XCTAssertEqual(relativeAge(secondsAgo: 0), "now") + XCTAssertEqual(relativeAge(secondsAgo: 4), "now") + } + + func testAgeSeconds() { + XCTAssertEqual(relativeAge(secondsAgo: 5), "5s") + XCTAssertEqual(relativeAge(secondsAgo: 32), "32s") + XCTAssertEqual(relativeAge(secondsAgo: 59), "59s") + } + + func testAgeMinutes() { + XCTAssertEqual(relativeAge(secondsAgo: 60), "1m") + XCTAssertEqual(relativeAge(secondsAgo: 5 * 60 + 20), "5m") + XCTAssertEqual(relativeAge(secondsAgo: 3599), "59m") + } + + func testAgeHours() { + XCTAssertEqual(relativeAge(secondsAgo: 3600), "1h") + XCTAssertEqual(relativeAge(secondsAgo: 2 * 3600 + 100), "2h") + XCTAssertEqual(relativeAge(secondsAgo: 86399), "23h") + } + + func testAgeDays() { + XCTAssertEqual(relativeAge(secondsAgo: 86400), "1d") + XCTAssertEqual(relativeAge(secondsAgo: 3 * 86400), "3d") + } + + func testAgeClampsNegative() { + XCTAssertEqual(relativeAge(secondsAgo: -7), "now") + } + + // MARK: - Grid distance + + func testGridDistanceKnownPath() { + // FN31 (Connecticut) → JO31 (western Germany): ~5900 km great circle. + let km = gridDistanceKm(from: "FN31", to: "JO31") + XCTAssertNotNil(km) + XCTAssertEqual(km!, 5900, accuracy: 200) + } + + func testGridDistanceZeroForSameGrid() { + let km = gridDistanceKm(from: "FN31", to: "FN31") + XCTAssertEqual(km!, 0, accuracy: 0.001) + } + + func testGridDistanceNilForMalformedGrid() { + XCTAssertNil(gridDistanceKm(from: "FN31", to: "")) + XCTAssertNil(gridDistanceKm(from: "??", to: "FN31")) + // RR73 is a sign-off token, not a locator. + XCTAssertNil(gridDistanceKm(from: "FN31", to: "RR73")) + } + + // MARK: - Distance formatting & unit conversion + + func testKilometersToMiles() { + XCTAssertEqual(kilometersToMiles(100), 62.1371, accuracy: 0.001) + } + + func testFormatDistanceKm() { + XCTAssertEqual(formatQsoDistance(km: 1832.4, inMiles: false), "1832 km") + XCTAssertEqual(formatQsoDistance(km: 1832.6, inMiles: false), "1833 km") + } + + func testFormatDistanceMiles() { + // 1000 km → 621.371 mi → "621 mi" + XCTAssertEqual(formatQsoDistance(km: 1000, inMiles: true), "621 mi") + } + + func testFormatDistanceEmptyForZero() { + XCTAssertEqual(formatQsoDistance(km: 0, inMiles: false), "") + XCTAssertEqual(formatQsoDistance(km: -5, inMiles: true), "") + // Rounds to zero → suppressed rather than "0 mi". + XCTAssertEqual(formatQsoDistance(km: 0.2, inMiles: true), "") + } + + func testGridDistanceTextEndToEnd() { + XCTAssertEqual(gridDistanceText(from: "FN31", to: "", inMiles: false), "") + let text = gridDistanceText(from: "FN31", to: "JO31", inMiles: false) + XCTAssertTrue(text.hasSuffix(" km")) + XCTAssertFalse(text.isEmpty) + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/DxccPrefixTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/DxccPrefixTests.swift new file mode 100644 index 00000000..b2cdf863 --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/DxccPrefixTests.swift @@ -0,0 +1,107 @@ +import XCTest +@testable import FT8Engine + +final class DxccPrefixTests: XCTestCase { + + // MARK: - Core entities from the task brief + + func testUsaPrefixes() { + XCTAssertEqual(DxccPrefix.entity(for: "W1AW")?.name, "United States") + XCTAssertEqual(DxccPrefix.entity(for: "K5ABC")?.name, "United States") + XCTAssertEqual(DxccPrefix.entity(for: "N0CALL")?.name, "United States") + XCTAssertEqual(DxccPrefix.entity(for: "AA4XX")?.name, "United States") + XCTAssertEqual(DxccPrefix.continent(for: "W1AW"), "NA") + } + + func testJapan() { + XCTAssertEqual(DxccPrefix.entity(for: "JA1ABC")?.name, "Japan") + XCTAssertEqual(DxccPrefix.entity(for: "JR2XYZ")?.name, "Japan") + XCTAssertEqual(DxccPrefix.entity(for: "7K1ABC")?.name, "Japan") + XCTAssertEqual(DxccPrefix.continent(for: "JA1ABC"), "AS") + } + + func testGermany() { + XCTAssertEqual(DxccPrefix.entity(for: "DL4RCK")?.name, "Germany") + XCTAssertEqual(DxccPrefix.entity(for: "DK9IP")?.name, "Germany") + XCTAssertEqual(DxccPrefix.continent(for: "DL4RCK"), "EU") + } + + func testEngland() { + XCTAssertEqual(DxccPrefix.entity(for: "G4ABC")?.name, "England") + XCTAssertEqual(DxccPrefix.entity(for: "M0XYZ")?.name, "England") + XCTAssertEqual(DxccPrefix.entity(for: "2E0ABC")?.name, "England") + XCTAssertEqual(DxccPrefix.continent(for: "G4ABC"), "EU") + } + + func testUkRegionsBeatEngland() { + // Longer prefixes (GM/GW/GI) must win over the 1-char England G. + XCTAssertEqual(DxccPrefix.entity(for: "GM3ABC")?.name, "Scotland") + XCTAssertEqual(DxccPrefix.entity(for: "GW4XYZ")?.name, "Wales") + XCTAssertEqual(DxccPrefix.entity(for: "GI0AAA")?.name, "Northern Ireland") + XCTAssertEqual(DxccPrefix.entity(for: "GD4BEG")?.name, "Isle of Man") + } + + func testRussia() { + XCTAssertEqual(DxccPrefix.entity(for: "UA3ABC")?.name, "European Russia") + XCTAssertEqual(DxccPrefix.entity(for: "R2XYZ")?.name, "European Russia") + XCTAssertEqual(DxccPrefix.continent(for: "UA3ABC"), "EU") + // Asiatic call areas resolve via the longer prefix. + XCTAssertEqual(DxccPrefix.entity(for: "UA9ABC")?.name, "Asiatic Russia") + XCTAssertEqual(DxccPrefix.continent(for: "R0XYZ"), "AS") + } + + func testTaskBriefSpotChecks() { + XCTAssertEqual(DxccPrefix.entity(for: "VK3BDX")?.name, "Australia") + XCTAssertEqual(DxccPrefix.continent(for: "VK3BDX"), "OC") + XCTAssertEqual(DxccPrefix.entity(for: "PY2SEX")?.name, "Brazil") + XCTAssertEqual(DxccPrefix.continent(for: "PY2SEX"), "SA") + XCTAssertEqual(DxccPrefix.entity(for: "EA4FKR")?.name, "Spain") + XCTAssertEqual(DxccPrefix.entity(for: "OH2BFO")?.name, "Finland") + XCTAssertEqual(DxccPrefix.entity(for: "ZL1BYZ")?.name, "New Zealand") + XCTAssertEqual(DxccPrefix.entity(for: "VE3XYZ")?.name, "Canada") + XCTAssertEqual(DxccPrefix.continent(for: "ZS6ABC"), "AF") + } + + // MARK: - Longest-prefix-match mechanics + + func testLongestPrefixWinsOverShorter() { + // EA8 (Canary Islands, AF) must beat EA (Spain, EU). + XCTAssertEqual(DxccPrefix.entity(for: "EA8ABC")?.name, "Canary Islands") + XCTAssertEqual(DxccPrefix.continent(for: "EA8ABC"), "AF") + // AP (Pakistan) must beat the 1-char USA "A". + XCTAssertEqual(DxccPrefix.entity(for: "AP2AM")?.name, "Pakistan") + // KH6 (Hawaii, OC) must beat K (USA, NA). + XCTAssertEqual(DxccPrefix.entity(for: "KH6XYZ")?.name, "Hawaii") + XCTAssertEqual(DxccPrefix.continent(for: "KH6XYZ"), "OC") + // CT3 (Madeira, AF) must beat CT (Portugal, EU). + XCTAssertEqual(DxccPrefix.entity(for: "CT3ABC")?.name, "Madeira Islands") + } + + func testLowercaseInput() { + XCTAssertEqual(DxccPrefix.entity(for: "ja1abc")?.name, "Japan") + XCTAssertEqual(DxccPrefix.entity(for: "w1aw")?.name, "United States") + } + + // MARK: - Portable designators + + func testPortablePrefixSegmentWins() { + // The DXCC-determining segment has the longer matched prefix. + XCTAssertEqual(DxccPrefix.entity(for: "EA8/W1AW")?.name, "Canary Islands") + XCTAssertEqual(DxccPrefix.entity(for: "W1AW/KH6")?.name, "Hawaii") + } + + func testPortableSuffixIgnored() { + // "/P" matches nothing; "/M" ties at length 1 and loses to the first segment. + XCTAssertEqual(DxccPrefix.entity(for: "W1AW/P")?.name, "United States") + XCTAssertEqual(DxccPrefix.entity(for: "W1AW/M")?.name, "United States") + XCTAssertEqual(DxccPrefix.entity(for: "DL4RCK/QRP")?.name, "Germany") + } + + // MARK: - Unknowns + + func testUnknownPrefixReturnsNil() { + XCTAssertNil(DxccPrefix.entity(for: "QQ1ABC")) + XCTAssertNil(DxccPrefix.entity(for: "")) + XCTAssertNil(DxccPrefix.continent(for: "QQ1ABC")) + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/QsoEngineTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/QsoEngineTests.swift index a3ddbada..2261a4c6 100644 --- a/ios/FT8AFKit/Tests/FT8EngineTests/QsoEngineTests.swift +++ b/ios/FT8AFKit/Tests/FT8EngineTests/QsoEngineTests.swift @@ -204,4 +204,74 @@ final class QsoEngineTests: XCTestCase { XCTAssertEqual(FT8Time.yyyymmdd(fromMs: 1_700_000_000_000), "20231114") XCTAssertEqual(FT8Time.hhmmss(fromMs: 1_700_000_000_000), "221320") } + + // MARK: forceLog / abandonToCq (LOG and ✕ panel actions) + + func testForceLogAfterReportsExchangedLogsAndReturnsToCq() { + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.answer(msg("CQ", "K1ABC", "FN42", "FN42", -5)) + _ = e.processRx([msg("K0XYZ", "K1ABC", "-12", "", -8)]) // we sent R-08 → .rReport + XCTAssertEqual(e.status().stage, .rReport) + + guard case .completed(let rec)? = e.forceLog() else { + return XCTFail("expected a logged record after reports were exchanged") + } + XCTAssertEqual(rec.call, "K1ABC") + XCTAssertEqual(rec.rstSent, "-08") + XCTAssertEqual(rec.rstRcvd, "-12") + // autoReturnToCq (default true) → back at the CQ baseline. + XCTAssertEqual(e.status().stage, .cq) + XCTAssertEqual(e.txMessage, "CQ K0XYZ EN37") + XCTAssertNil(e.status().target) + } + + func testForceLogTooEarlyDoesNotLogButStillMovesOn() { + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.answer(msg("CQ", "K1ABC", "FN42", "FN42", -5)) // stage .grid, no reports yet + XCTAssertNil(e.forceLog()) + XCTAssertNil(e.status().target) + XCTAssertEqual(e.status().stage, .cq) + } + + func testForceLogWithNoTargetIsNil() { + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.startCq() + XCTAssertNil(e.forceLog()) + // Untouched: still calling CQ. + XCTAssertEqual(e.txMessage, "CQ K0XYZ EN37") + } + + func testForceLogStopsWhenAutoReturnOff() { + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.autoReturnToCq = false + e.answer(msg("CQ", "K1ABC", "FN42", "FN42", -5)) + _ = e.processRx([msg("K0XYZ", "K1ABC", "-12", "", -8)]) + XCTAssertNotNil(e.forceLog()) + XCTAssertFalse(e.status().active) + XCTAssertNil(e.txMessage) + XCTAssertEqual(e.status().stage, .idle) + } + + func testAbandonToCqDropsTargetWithoutLogging() { + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.answer(msg("CQ", "K1ABC", "FN42", "FN42", -5)) + _ = e.processRx([msg("K0XYZ", "K1ABC", "-12", "", -8)]) + e.abandonToCq() + XCTAssertNil(e.status().target) + XCTAssertEqual(e.status().stage, .cq) + XCTAssertEqual(e.txMessage, "CQ K0XYZ EN37") + } + + func testAutoReturnToCqOffStopsAfterCompletion() { + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.autoReturnToCq = false + e.startCq() + _ = e.processRx([msg("K0XYZ", "K1ABC", "FN42", "FN42", -10)]) + _ = e.processRx([msg("K0XYZ", "K1ABC", "R-15", "", -10)]) + guard case .completed? = e.processRx([msg("K0XYZ", "K1ABC", "73", "", -10)]) else { + return XCTFail("expected completed QSO") + } + XCTAssertFalse(e.status().active) + XCTAssertNil(e.txMessage) + } } diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/TuneToneTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/TuneToneTests.swift new file mode 100644 index 00000000..62f75882 --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/TuneToneTests.swift @@ -0,0 +1,60 @@ +import XCTest +@testable import FT8Engine + +final class TuneToneTests: XCTestCase { + + func testSampleCountMatchesDuration() { + let s = TuneTone.samples(freqHz: 1500, sampleRate: 12_000, durationSec: 3) + XCTAssertEqual(s.count, 36_000) + } + + func testDegenerateInputsReturnEmpty() { + XCTAssertTrue(TuneTone.samples(freqHz: 1500, sampleRate: 12_000, durationSec: 0).isEmpty) + XCTAssertTrue(TuneTone.samples(freqHz: 1500, sampleRate: 0, durationSec: 5).isEmpty) + XCTAssertTrue(TuneTone.samples(freqHz: 0, sampleRate: 12_000, durationSec: 5).isEmpty) + XCTAssertTrue(TuneTone.samples(freqHz: -100, sampleRate: 12_000, durationSec: 5).isEmpty) + } + + func testAmplitudeBoundedAndReachedInSteadyState() { + let amp: Float = 0.9 + let s = TuneTone.samples(freqHz: 1000, sampleRate: 12_000, durationSec: 1, amplitude: amp) + let peak = s.map(abs).max() ?? 0 + XCTAssertLessThanOrEqual(peak, amp + 1e-4) + XCTAssertGreaterThan(peak, amp * 0.99) // full amplitude reached mid-buffer + } + + func testRampsAvoidKeyClicks() { + let s = TuneTone.samples(freqHz: 1500, sampleRate: 12_000, durationSec: 1) + // Starts and ends at (near) zero. + XCTAssertEqual(s.first ?? 1, 0, accuracy: 1e-6) + XCTAssertEqual(abs(s.last ?? 1), 0, accuracy: 0.01) + // Every sample inside the head ramp is attenuated below full scale. + let rampSamples = 12_000 * TuneTone.rampMs / 1000 + let headPeak = s[0.. Date: Mon, 13 Jul 2026 15:27:40 -0500 Subject: [PATCH 4/5] iOS parity batch 3: POTA live data + Android typography - POTA Hunt tab fetches live activator spots from api.pota.app (pull-to- refresh, 60s auto-refresh, FT8 filter, tap-to-hunt band switch) - Activation QSO count derived from logbook records in the activation window (PotaQsoWindow port); history persisted to Documents JSON - Per-park activation ADIF export (PotaAdifExporter field set, MY_SIG=POTA) - Inter + Geist Mono bundled from the Android font set and adopted across all views (UI=Inter, data=Geist Mono, slashed zero); Info.plist migrated to an XcodeGen info block carrying UIAppFonts + existing keys Kit tests 329/329; simulator build green; smoke-run verified in simulator. Co-Authored-By: Claude Fable 5 --- .claude/tasks/session-current.md | 55 ++ ios/FT8AF/FT8AF.xcodeproj/project.pbxproj | 58 +- ios/FT8AF/FT8AF/AppState.swift | 17 +- .../FT8AF/Components/ActiveQsoPanel.swift | 38 +- ios/FT8AF/FT8AF/Components/FilterChip.swift | 2 +- .../Components/FrequencyPickerSheet.swift | 4 +- ios/FT8AF/FT8AF/Components/QsoPathMap.swift | 4 +- ios/FT8AF/FT8AF/Components/ToastOverlay.swift | 4 +- ios/FT8AF/FT8AF/Components/TxStrip.swift | 26 +- .../FT8AF/Engine/PotaActivationStore.swift | 35 ++ ios/FT8AF/FT8AF/Engine/PotaService.swift | 66 ++ ios/FT8AF/FT8AF/Fonts/geist_mono_bold.ttf | Bin 0 -> 151260 bytes ios/FT8AF/FT8AF/Fonts/geist_mono_medium.ttf | Bin 0 -> 150096 bytes ios/FT8AF/FT8AF/Fonts/geist_mono_regular.ttf | Bin 0 -> 149284 bytes ios/FT8AF/FT8AF/Fonts/geist_mono_semibold.ttf | Bin 0 -> 150468 bytes ios/FT8AF/FT8AF/Fonts/inter_variable.ttf | Bin 0 -> 876576 bytes ios/FT8AF/FT8AF/Info.plist | 58 ++ ios/FT8AF/FT8AF/Navigation/AppTabView.swift | 4 +- .../FT8AF/Screens/Decode/DecodeRow.swift | 16 +- .../FT8AF/Screens/Decode/DecodeScreen.swift | 18 +- ios/FT8AF/FT8AF/Screens/Decode/QsoSheet.swift | 24 +- .../Screens/Logbook/ExportLogSheet.swift | 10 +- .../FT8AF/Screens/Logbook/LogbookAwards.swift | 14 +- .../FT8AF/Screens/Logbook/LogbookCharts.swift | 28 +- .../FT8AF/Screens/Logbook/LogbookScreen.swift | 52 +- .../FT8AF/Screens/Logbook/LogbookStats.swift | 10 +- .../Screens/Map/AzimuthalMapCanvas.swift | 4 +- ios/FT8AF/FT8AF/Screens/Map/MapScreen.swift | 20 +- ios/FT8AF/FT8AF/Screens/POTA/PotaScreen.swift | 593 +++++++++++++----- .../FT8AF/Screens/Settings/AboutScreen.swift | 18 +- .../Screens/Settings/AdvancedSettings.swift | 22 +- .../Settings/BlockedCallsignsSettings.swift | 8 +- .../Settings/DecodeFilterSettings.swift | 12 +- .../Screens/Settings/LoggingSettings.swift | 4 +- .../Screens/Settings/RadioAudioSettings.swift | 14 +- .../Screens/Settings/SettingsScreen.swift | 40 +- .../Settings/TransmissionSettings.swift | 18 +- ios/FT8AF/FT8AF/Screens/SplashScreen.swift | 10 +- .../Screens/Waterfall/FrequencyRuler.swift | 2 +- .../Screens/Waterfall/SpectrumStrip.swift | 2 +- .../Screens/Waterfall/WaterfallCanvas.swift | 4 +- .../Screens/Waterfall/WaterfallScreen.swift | 14 +- ios/FT8AF/FT8AF/Theme/Typography.swift | 101 +++ ios/FT8AF/project.yml | 36 +- .../Sources/FT8Engine/PotaActivations.swift | 113 ++++ ios/FT8AFKit/Sources/FT8Engine/PotaAdif.swift | 109 ++++ .../Sources/FT8Engine/PotaSpots.swift | 140 +++++ .../FT8EngineTests/PotaActivationsTests.swift | 127 ++++ .../Tests/FT8EngineTests/PotaAdifTests.swift | 138 ++++ .../Tests/FT8EngineTests/PotaSpotsTests.swift | 162 +++++ 50 files changed, 1858 insertions(+), 396 deletions(-) create mode 100644 ios/FT8AF/FT8AF/Engine/PotaActivationStore.swift create mode 100644 ios/FT8AF/FT8AF/Engine/PotaService.swift create mode 100644 ios/FT8AF/FT8AF/Fonts/geist_mono_bold.ttf create mode 100644 ios/FT8AF/FT8AF/Fonts/geist_mono_medium.ttf create mode 100644 ios/FT8AF/FT8AF/Fonts/geist_mono_regular.ttf create mode 100644 ios/FT8AF/FT8AF/Fonts/geist_mono_semibold.ttf create mode 100644 ios/FT8AF/FT8AF/Fonts/inter_variable.ttf create mode 100644 ios/FT8AF/FT8AF/Info.plist create mode 100644 ios/FT8AF/FT8AF/Theme/Typography.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/PotaActivations.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/PotaAdif.swift create mode 100644 ios/FT8AFKit/Sources/FT8Engine/PotaSpots.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/PotaActivationsTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/PotaAdifTests.swift create mode 100644 ios/FT8AFKit/Tests/FT8EngineTests/PotaSpotsTests.swift diff --git a/.claude/tasks/session-current.md b/.claude/tasks/session-current.md index 46bc969a..66e8b35d 100644 --- a/.claude/tasks/session-current.md +++ b/.claude/tasks/session-current.md @@ -160,3 +160,58 @@ App — `ios/FT8AF/FT8AF/`: - Inert-by-platform: `earlyDecode` (slot-end decode pipeline), `pttDelayMs` (no PTT line on iOS). Both documented above; everything else in the TX/sequencer settings group now does something real. - `autoCQAfterQSO` default-off behavior change called out in #1 — flag in the PR body. - TUNE plays through the same `TxPlayerService`/AVAudioEngine speaker path as FT8 TX; on-device it keys VOX like any TX audio. Requires RX running (toast prompts if not). + +### Agent E — POTA parity + +**Status: COMPLETE** — kit tests 329/329 green (`swift test`; 298 baseline + 31 new), simulator build green (`xcodebuild` iPhone 16 Pro), `xcodegen generate` re-run after adding the two app files. Not committed (coordinator commits). Verified the live endpoint shape with one manual curl; tests use canned fixtures only. + +**What was built** (parity with Android `pota/PotaClient.getActiveSpots`, `PotaSpotsRepository`, `PotaQsoWindow`, `ui/pota/PotaScreen.kt` HuntTab/historyForDisplay, `PotaAdifExporter.buildActivationAdif`; OAuth self-spot/upload stays deferred as documented): + +Kit — pure, network-free logic in `ios/FT8AFKit/Sources/FT8Engine/`: +- `PotaSpots.swift` — `PotaSpot` model (activator, kHz, mode, ref, park name from the feed's `name` key, locationDesc, spotter, spotTime, comments; same row identity Android keys by) + `PotaSpots.decode` (tolerant of string/number `frequency`, null fields, malformed elements skipped; activator uppercased), `forDisplay(ft8Only:)` (case-insensitive FT8 filter + frequency sort, Android's hunt constraint but client-side so the toggle is instant), `ageSeconds` (lenient "yyyy-MM-ddTHH:mm:ss[.frac]" UTC parse, clamps skew; feeds the existing `relativeAge`), `band(forFrequencyKhz:)` (full HF/6m allocations → the app's "20M" band names). +- `PotaActivations.swift` — `PotaActivationRecord` (Codable; parkRef comma-split for two-fers, `parkRefsDisplay`, open end = active) + `potaHistoryForDisplay` (drops active, newest first) + `PotaQsoWindow` port (yyyyMMddHHmmss GMT stamps, Android's variable-width `time_on` normalization incl. dropped-leading-zero odd widths, `openEnd` bound for active sessions, `qsos(in:)`/`qsoCount`). +- `PotaAdif.swift` — `Adif.potaActivationExport(records:activation:)` in a NEW file (Adif.swift untouched, no signature changes): Android's exact field set/order (CALL GRIDSQUARE MODE[/SUBMODE] BAND FREQ RST_SENT RST_RCVD QSO_DATE TIME_ON TIME_OFF STATION_CALLSIGN MY_GRIDSQUARE MY_SIG=POTA MY_SIG_INFO), UTF-8 byte lengths, FT4/FT2 → MODE=MFSK+SUBMODE (the pota.app rejection Android fixed), one document per park with MY_SIG_INFO pinned, window-scoped via PotaQsoWindow, empty window → `[]` (never header-only files), `pota--.adi` filenames (UTC stamp; Android uses local — cosmetic). +- Tests (31 new): `PotaSpotsTests` (canned JSON fixture from a real 2026-07-13 response — decode mapping, uppercasing, int-string freq, null/garbage tolerance, filter+sort, age parse incl. fractional seconds, band map edges), `PotaActivationsTests` (stamps, time_on normalization, window in/ex-clusion incl. same-park repeat visits, sorting, Codable round-trip, history), `PotaAdifTests` (golden single-park document, byte-length UTF-8, empty-field omission, MFSK submodes, two-fer pinning, window scoping, empty→[]). + +App — `ios/FT8AF/FT8AF/`: +- `Engine/PotaService.swift` — `@Observable @MainActor` singleton: URLSession fetch of `api.pota.app/spot/activator` (10 s timeout, same UA/Accept as Android), isLoading/lastError/lastUpdatedAt, `ft8Only` toggle, `displaySpots` via kit. Re-entrant refreshes dropped; cancellation isn't surfaced as an error. +- `Engine/PotaActivationStore.swift` — Documents-dir JSON store (`pota_activations.json`), QsoLogStore pattern; History and a still-open activation survive restarts. +- `Screens/POTA/PotaScreen.swift` — mock data fully removed. Hunt: live spot list (List → native pull-to-refresh), 60 s auto-refresh via `.task(id: selectedTab)` while the tab is visible, loading/empty/error states, FT8 ONLY chip, rows show activator/park pill/park·location/comments/freq/relative age/spotter (+mode chip when non-FT8); tap = band switch through the same path as the TX strip's band chip (settings.band + SettingsPersistence.save) when the spot is FT8 on a mapped band, + targeting toast (else "tune manually" toast). Activate: sessions are `PotaActivationRecord`s; QSO count **derived** from `logbook.records` in the activation window (Observation-driven — no manual increments), elapsed ticks via TimelineView, End freezes the count into the record, Export ADIF button (disabled at 0 QSOs). History: real past activations (park refs, date, QSO count, per-row ADIF share). Share = per-park temp files → UIActivityViewController. +- `AppState.swift` — PotaState body ONLY: replaced the inert `isActivating/activationQsoCount/activationStartTime/parkRefs` with `activations: [PotaActivationRecord]` + derived `current`/`isActivating` (isActivating keeps its name so the ACTIVE header pill logic reads the same). + +**Coordinator notes:** +- Tapping a spot switches band only (mirrors TxStrip's FrequencyPickerSheet path); it does NOT prefill a QSO target — Android opens its QSO sheet with the activator instead, but iOS target selection is engine-owned (LiveEngine is Agent B's file) and out of my ownership. Trivial follow-up if wanted: set a decode-tap-equivalent hook. +- POTA spots are NOT fed into the decode annotator's park-ref enrichment (Android's `PotaSpotsRepository.parkRefFor`); Agent R's `isPotaCq` works off message text. Possible future wire-up via PotaService.shared. +- QSOs logged during an activation aren't tagged MY_SIG in `qso_log.json` (QsoRecord has no sig fields and QsoRecord.swift is Agent D's back-compat surface); the export derives membership from the activation time window instead — same result as Android's window-scoped query, and repeat visits to one park stay separate (tested). +- Build passed with Agent S's in-flight font/typography edits present in the tree; my PotaScreen rewrite predates any font adoption pass on Screens/POTA (rows still use `.system` fonts — Agent S or coordinator can sweep it with the same treatment as other screens). + +### Agent S — Style parity + +**Status: COMPLETE** — kit tests 298/298 green (`swift test`, kit untouched), `xcodegen generate` + simulator build green (iPhone 16 Pro), fonts verified rendering in the simulator. Not committed (coordinator commits). + +**1. Fonts bundled** — `ios/FT8AF/FT8AF/Fonts/`: `inter_variable.ttf`, `geist_mono_{regular,medium,semibold,bold}.ttf` copied from `ft8af/app/src/main/res/font/` (proportional Geist skipped — Android UI font is Inter). CoreText inspection: Geist Mono statics register as PS `GeistMono-{Regular,Medium,SemiBold,Bold}`; the Inter variable TTF exposes **named instances** `Inter-Regular`, `Inter-Regular_Medium`, `Inter-Regular_SemiBold`, `Inter-Regular_Bold` (verified via CTFontManager registration test: all four resolve with distinct weight traits 0/0.2/0.3/0.4), so weight selection works from the single variable file — **no static Inter weights needed, nothing downloaded**. + +**2. Info.plist migration** — `project.yml` now uses an XcodeGen `info:` block (`FT8AF/Info.plist`); `GENERATE_INFOPLIST_FILE` and all `INFOPLIST_KEY_*` settings removed. Migrated keys (the full set present in project.yml/pbxproj — there was no NSLocalNetworkUsageDescription anywhere): +- `NSMicrophoneUsageDescription` (same string) +- `UIApplicationSceneManifest` = { UIApplicationSupportsMultipleScenes: true, UISceneConfigurations: {} } (matches previous generated plist) +- `UIApplicationSupportsIndirectInputEvents` = true +- `UILaunchScreen` = {} (launch-screen generation equivalent) +- `UISupportedInterfaceOrientations` (iPhone list) + `UISupportedInterfaceOrientations~ipad` (previous _iPhone/_iPad values) +- `LSRequiresIPhoneOS` = true +- **new** `UIAppFonts` = the 5 bundled ttf files +Built product's Info.plist inspected via `plutil -p` in DerivedData: all keys + UIAppFonts present, mic string intact, 5 ttf files in the app bundle. + +**3. Typography layer** — new `ios/FT8AF/FT8AF/Theme/Typography.swift`: `Font.ft8afUI(size:weight:)` (Inter named instances; variation-axis `wght` fallback if instances aren't registered; SF fallback if the file fails) and `Font.ft8afMono(size:weight:)` (Geist Mono statics; SF Mono fallback). Slashed zero implemented (no rabbit hole): OpenType `zero` feature via `UIFontDescriptor.featureSettings` with the CoreText `kCTFontOpenTypeFeatureTag/Value` keys — mirrors Android's `fontFeatureSettings = "zero"`. + +**4. App-wide adoption** — all 30 view files under `ios/FT8AF/FT8AF/` swept mechanically (Components, Navigation, Screens/Decode|Logbook|Map|Settings|Waterfall, SplashScreen): `.system(size:…, design: .monospaced)` → `.ft8afMono(size:…)`, `.system(size:…)` → `.ft8afUI(size:…)` (227 call sites, weights preserved incl. ternaries). Zero `.system(` font sites remain outside the exclusions. Notes: +- **Screens/POTA/ untouched per concurrency rule — PotaScreen still uses system fonts; coordinator sweeps it after Agent E lands.** +- The 7 `.system(.body, design:.monospaced)` sites (TextFields) → `.ft8afMono(size: 17)` — fixed 17pt (body default) instead of dynamic type, consistent with the rest of the app's fixed sizes. +- SF Symbol `Image(systemName:)` sites keep their `.font()` for sizing (now ft8af helpers) — verified rendering unchanged. +- `Theme/Typography.swift`'s own `.system` fallbacks intentionally kept. + +**5. Visual verification (simulator)** — app installed + launched on iPhone 16 Pro sim, screenshots inspected: Geist Mono clearly rendering on chips/TX controls with **slashed zero visible** ("0 msgs"); Inter confirmed decisively by ink-width measurement of the Decode empty-state subtitle (rendered 248.7pt vs Inter 249.4pt vs SF 244.1pt at 13pt). No file found where the font failed to load. + +**Coordinator notes:** +- App target has no unit-test target and the typography layer is pure UIKit/SwiftUI font resolution (kit untouched), so no new kit tests; the font-name/weight mapping is trivial and verified by the build + on-sim rendering. +- `xcodegen generate` re-ran; pbxproj regenerated (adds Fonts/ resources + INFOPLIST_FILE). If Agent E regenerates after me nothing is lost — everything is declared in project.yml. +- Android's per-style letterspacing from Type.kt (e.g. -0.02em display, +0.04em labels) not replicated — iOS sites set explicit sizes rather than semantic styles; can be layered on later via `.kerning()` if the coordinator wants closer parity. diff --git a/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj b/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj index a666c35a..838e4646 100644 --- a/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj +++ b/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj @@ -18,14 +18,17 @@ 49DFEC8ED0237764EA3EE3BF /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF21F5D95598018B8A46ED8D /* AppState.swift */; }; 4C0C9DCC39ED6528F0D4CDFF /* RadioAudioSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89642B9763C7FA33CFF0EE57 /* RadioAudioSettings.swift */; }; 4D87F21162B4DCEC806D8785 /* QsoCelebration.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC8DAEDCAE1AD9DE02EB1480 /* QsoCelebration.swift */; }; + 4F742232FB7AE5A02DF90DE8 /* geist_mono_semibold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CFC0DD2F717A726A4887913B /* geist_mono_semibold.ttf */; }; 51B44F6A9F0A05A7B3CB71FC /* FrequencyPickerSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3C698656C5709343F4AEEF0 /* FrequencyPickerSheet.swift */; }; 595BBF17D95723947D5BFFC4 /* PotaScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B35E490F93FDCACEC4FBF66F /* PotaScreen.swift */; }; 5C76E98A4BD4854709C3D0E9 /* AppTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53063BBF11F97C62BC0AFC9 /* AppTabView.swift */; }; 5E2B3105FFFE6C84D86ACD85 /* TransmitGlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF04207CDB5BE8A1CCDD5F5B /* TransmitGlow.swift */; }; 608EF9D6EAE6CDA82E9E86C4 /* QsoSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0290AFE608DC2FC9E1A83E16 /* QsoSheet.swift */; }; 637BC14B19B728028FD01F07 /* FrequencyRuler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81C53609B2CDC9FB3B71116 /* FrequencyRuler.swift */; }; + 65095AFDD1557D1198D61B04 /* geist_mono_bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 87C51D7CB8A8FA5C3F883285 /* geist_mono_bold.ttf */; }; 65A688B883398E75D00BCD7E /* LogbookStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91DB096C5FC7481CA1B7B49B /* LogbookStats.swift */; }; 666D9271E9FE64C20C29CF47 /* FT8Audio in Frameworks */ = {isa = PBXBuildFile; productRef = 8A825C5161C267D978D895B8 /* FT8Audio */; }; + 6A8A11D49F8CA3F24D9DED29 /* PotaActivationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA66E8CE63C02CC35A2BC010 /* PotaActivationStore.swift */; }; 6F7F961AB6678F3E6E60A97A /* LoggingSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB0565117E35A6C64BBF519D /* LoggingSettings.swift */; }; 72300AB5D2B37428777C824D /* MapScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BCE0C13DF4842EC394EFADF /* MapScreen.swift */; }; 771EC057824C1F3698C6DC9B /* FFTProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F287F167508A8E4114F44116 /* FFTProcessor.swift */; }; @@ -35,17 +38,22 @@ 8DA0D562E25925F56B638B00 /* GridDistance.swift in Sources */ = {isa = PBXBuildFile; fileRef = E429D8A3556534F05374F12A /* GridDistance.swift */; }; 8EA75A450404FB948C0A6CDC /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504C9AD4C6667267DFCABEF9 /* SettingsScreen.swift */; }; 8EF984B5816F012A2DF93FDF /* OnlineLogService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D131B05E27A96EC4BEB28782 /* OnlineLogService.swift */; }; + 9D3A2D37F171D6B4D7AA48A3 /* inter_variable.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B42060FC68A03D3F64A7C2EF /* inter_variable.ttf */; }; 9F4A1DE79693EF68A7B1A4D2 /* LogbookAwards.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CCA018AEEE169EDCA338FD /* LogbookAwards.swift */; }; A037B00B861024FA2CA4919F /* QsoPathMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0187218903BBFF71931246 /* QsoPathMap.swift */; }; A5C16922F13DEDBDF69DFCF6 /* FT8AFTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = A41EFDE500F865A3B393171D /* FT8AFTab.swift */; }; + AA448D315E29A08A6A925951 /* geist_mono_medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BF1653386B026C49C8A42365 /* geist_mono_medium.ttf */; }; AA7457556E40D0C0B8F75958 /* FilterChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5306EE5215AE14F95F3329F8 /* FilterChip.swift */; }; AAA8FD89DE336CC404CC41E4 /* FT8Engine in Frameworks */ = {isa = PBXBuildFile; productRef = A5E95C3FF3D4A30E2177E137 /* FT8Engine */; }; AB5310FE1F9048626DC6406B /* LiveEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E09812700CD93E6187CC4BC /* LiveEngine.swift */; }; AC1D4BEDBCC1DBA945DB04D5 /* TxPlayerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B5962B70F4510D76A5BC0A /* TxPlayerService.swift */; }; B28BBB197398A82CDE05B480 /* AboutScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFCB3285583184390D9E9BF /* AboutScreen.swift */; }; + B2C808F5B82B417F7837E115 /* geist_mono_regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DB80BA772AE267AC9AE1FC43 /* geist_mono_regular.ttf */; }; + B4122A07A833C6746F51D5D6 /* PotaService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E27EE4248BF38A15E0F3B2D /* PotaService.swift */; }; B8FD5A2DF3B2A89740B3CB73 /* WorldMapCanvas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BDDB723455CB7AE2BB68EF0 /* WorldMapCanvas.swift */; }; BCEB9305C3EA84C758FAFBA7 /* AzimuthalMapCanvas.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB943F6B81F9D36BA85504A3 /* AzimuthalMapCanvas.swift */; }; CE1ECA0C5BB350A1EDDA7018 /* SlotTimerBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800081E79EC795EBDE2E077E /* SlotTimerBar.swift */; }; + CF1120C958F019240FB25A25 /* Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0CA452882C4980BED953078 /* Typography.swift */; }; D36ECEB73B847787DE373072 /* SpectrumStrip.swift in Sources */ = {isa = PBXBuildFile; fileRef = C96D9C69CFC1480E6A5B64B5 /* SpectrumStrip.swift */; }; DA94D12D7FDE6D10D116E770 /* TxStrip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B1202A7633AE8CBBFCF2C3 /* TxStrip.swift */; }; DFA95C747B2F0763C677F2EC /* ActiveQsoPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0244EBDFC2ADDAE77418FF53 /* ActiveQsoPanel.swift */; }; @@ -83,9 +91,11 @@ 6B9E40312EA203F05764FA56 /* QsoLogStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QsoLogStore.swift; sourceTree = ""; }; 6F1E760AEB31EF3855951728 /* FT8AFApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FT8AFApp.swift; sourceTree = ""; }; 76108617D9AEDEE84BAD1054 /* LogbookScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogbookScreen.swift; sourceTree = ""; }; + 7E27EE4248BF38A15E0F3B2D /* PotaService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PotaService.swift; sourceTree = ""; }; 800081E79EC795EBDE2E077E /* SlotTimerBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlotTimerBar.swift; sourceTree = ""; }; 82A0D66684F5C2F323454D9B /* FT8AFKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = FT8AFKit; path = ../FT8AFKit; sourceTree = SOURCE_ROOT; }; 8493B7CF97F9744DED622F15 /* ToastOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastOverlay.swift; sourceTree = ""; }; + 87C51D7CB8A8FA5C3F883285 /* geist_mono_bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = geist_mono_bold.ttf; sourceTree = ""; }; 89642B9763C7FA33CFF0EE57 /* RadioAudioSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioAudioSettings.swift; sourceTree = ""; }; 8BDDB723455CB7AE2BB68EF0 /* WorldMapCanvas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorldMapCanvas.swift; sourceTree = ""; }; 91DB096C5FC7481CA1B7B49B /* LogbookStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogbookStats.swift; sourceTree = ""; }; @@ -97,21 +107,28 @@ A88688AC6AFBEDC8AC3EF844 /* DecodeFilterSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodeFilterSettings.swift; sourceTree = ""; }; AC8DAEDCAE1AD9DE02EB1480 /* QsoCelebration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QsoCelebration.swift; sourceTree = ""; }; B35E490F93FDCACEC4FBF66F /* PotaScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PotaScreen.swift; sourceTree = ""; }; + B42060FC68A03D3F64A7C2EF /* inter_variable.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = inter_variable.ttf; sourceTree = ""; }; B681B14FC4FED15C628471DF /* WaterfallCanvas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterfallCanvas.swift; sourceTree = ""; }; BB0565117E35A6C64BBF519D /* LoggingSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingSettings.swift; sourceTree = ""; }; BD0187218903BBFF71931246 /* QsoPathMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QsoPathMap.swift; sourceTree = ""; }; + BE0DCBA7E647113F08F32531 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + BF1653386B026C49C8A42365 /* geist_mono_medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = geist_mono_medium.ttf; sourceTree = ""; }; BF21F5D95598018B8A46ED8D /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; C42FB097015693FF14B27553 /* AudioCaptureService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioCaptureService.swift; sourceTree = ""; }; C79C0E0096ADDC018120C361 /* DecodeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodeScreen.swift; sourceTree = ""; }; C81C53609B2CDC9FB3B71116 /* FrequencyRuler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrequencyRuler.swift; sourceTree = ""; }; C96D9C69CFC1480E6A5B64B5 /* SpectrumStrip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpectrumStrip.swift; sourceTree = ""; }; CB943F6B81F9D36BA85504A3 /* AzimuthalMapCanvas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AzimuthalMapCanvas.swift; sourceTree = ""; }; + CFC0DD2F717A726A4887913B /* geist_mono_semibold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = geist_mono_semibold.ttf; sourceTree = ""; }; D131B05E27A96EC4BEB28782 /* OnlineLogService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnlineLogService.swift; sourceTree = ""; }; + DB80BA772AE267AC9AE1FC43 /* geist_mono_regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = geist_mono_regular.ttf; sourceTree = ""; }; DE47CF6272CAE2614E6A2383 /* AdvancedSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettings.swift; sourceTree = ""; }; DF04207CDB5BE8A1CCDD5F5B /* TransmitGlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransmitGlow.swift; sourceTree = ""; }; + E0CA452882C4980BED953078 /* Typography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typography.swift; sourceTree = ""; }; E429D8A3556534F05374F12A /* GridDistance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridDistance.swift; sourceTree = ""; }; F287F167508A8E4114F44116 /* FFTProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FFTProcessor.swift; sourceTree = ""; }; F3C698656C5709343F4AEEF0 /* FrequencyPickerSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrequencyPickerSheet.swift; sourceTree = ""; }; + FA66E8CE63C02CC35A2BC010 /* PotaActivationStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PotaActivationStore.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -263,8 +280,10 @@ BF21F5D95598018B8A46ED8D /* AppState.swift */, 076A186C88F9B5A2FFAAB073 /* Assets.xcassets */, 6F1E760AEB31EF3855951728 /* FT8AFApp.swift */, + BE0DCBA7E647113F08F32531 /* Info.plist */, 7DCF29540DF6FBE5F4ECD5FC /* Components */, B571E3EEC914286395700259 /* Engine */, + 990B4B022C7B0AE88C357BC4 /* Fonts */, 1F327B9757517C23BFA52C86 /* Navigation */, 7F57A25B1504D3F64E790FAC /* Screens */, C97F65A2DD30ABA1707C85D3 /* Theme */, @@ -273,6 +292,18 @@ path = FT8AF; sourceTree = ""; }; + 990B4B022C7B0AE88C357BC4 /* Fonts */ = { + isa = PBXGroup; + children = ( + 87C51D7CB8A8FA5C3F883285 /* geist_mono_bold.ttf */, + BF1653386B026C49C8A42365 /* geist_mono_medium.ttf */, + DB80BA772AE267AC9AE1FC43 /* geist_mono_regular.ttf */, + CFC0DD2F717A726A4887913B /* geist_mono_semibold.ttf */, + B42060FC68A03D3F64A7C2EF /* inter_variable.ttf */, + ); + path = Fonts; + sourceTree = ""; + }; 9B85E3FDCB60E378E9DF2E12 /* Logbook */ = { isa = PBXGroup; children = ( @@ -292,6 +323,8 @@ F287F167508A8E4114F44116 /* FFTProcessor.swift */, 4E09812700CD93E6187CC4BC /* LiveEngine.swift */, D131B05E27A96EC4BEB28782 /* OnlineLogService.swift */, + FA66E8CE63C02CC35A2BC010 /* PotaActivationStore.swift */, + 7E27EE4248BF38A15E0F3B2D /* PotaService.swift */, 6B9E40312EA203F05764FA56 /* QsoLogStore.swift */, 29B5962B70F4510D76A5BC0A /* TxPlayerService.swift */, ); @@ -302,6 +335,7 @@ isa = PBXGroup; children = ( 0FD3B4836E62F9DC32FAB0DE /* Colors.swift */, + E0CA452882C4980BED953078 /* Typography.swift */, ); path = Theme; sourceTree = ""; @@ -379,6 +413,11 @@ buildActionMask = 2147483647; files = ( 80F7FFDDEE3AEE3E706C4A2B /* Assets.xcassets in Resources */, + 65095AFDD1557D1198D61B04 /* geist_mono_bold.ttf in Resources */, + AA448D315E29A08A6A925951 /* geist_mono_medium.ttf in Resources */, + B2C808F5B82B417F7837E115 /* geist_mono_regular.ttf in Resources */, + 4F742232FB7AE5A02DF90DE8 /* geist_mono_semibold.ttf in Resources */, + 9D3A2D37F171D6B4D7AA48A3 /* inter_variable.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -417,7 +456,9 @@ 6F7F961AB6678F3E6E60A97A /* LoggingSettings.swift in Sources */, 72300AB5D2B37428777C824D /* MapScreen.swift in Sources */, 8EF984B5816F012A2DF93FDF /* OnlineLogService.swift in Sources */, + 6A8A11D49F8CA3F24D9DED29 /* PotaActivationStore.swift in Sources */, 595BBF17D95723947D5BFFC4 /* PotaScreen.swift in Sources */, + B4122A07A833C6746F51D5D6 /* PotaService.swift in Sources */, 4D87F21162B4DCEC806D8785 /* QsoCelebration.swift in Sources */, E375A4D50C25F5437C2C9F7F /* QsoLogStore.swift in Sources */, A037B00B861024FA2CA4919F /* QsoPathMap.swift in Sources */, @@ -432,6 +473,7 @@ 5E2B3105FFFE6C84D86ACD85 /* TransmitGlow.swift in Sources */, AC1D4BEDBCC1DBA945DB04D5 /* TxPlayerService.swift in Sources */, DA94D12D7FDE6D10D116E770 /* TxStrip.swift in Sources */, + CF1120C958F019240FB25A25 /* Typography.swift in Sources */, FEB99CFFD070BA6B893ADC83 /* WaterfallCanvas.swift in Sources */, 208767EE6D89D470D7C88E77 /* WaterfallScreen.swift in Sources */, B8FD5A2DF3B2A89740B3CB73 /* WorldMapCanvas.swift in Sources */, @@ -446,13 +488,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSMicrophoneUsageDescription = "FT8AF needs microphone access to capture radio audio for FT8 decoding"; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_FILE = FT8AF/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -525,13 +561,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSMicrophoneUsageDescription = "FT8AF needs microphone access to capture radio audio for FT8 decoding"; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_FILE = FT8AF/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ios/FT8AF/FT8AF/AppState.swift b/ios/FT8AF/FT8AF/AppState.swift index 9a7b8433..9b7bd100 100644 --- a/ios/FT8AF/FT8AF/AppState.swift +++ b/ios/FT8AF/FT8AF/AppState.swift @@ -296,12 +296,21 @@ final class TxState { @Observable @MainActor final class PotaState { + /// Start-form inputs (park reference + optional notes). var parkInput: String = "" - var parkRefs: [String] = [] var notes: String = "" - var isActivating: Bool = false - var activationQsoCount: Int = 0 - var activationStartTime: Date? + /// All activation sessions (past + at most one active). Loaded from + /// `PotaActivationStore` the first time the POTA screen appears and + /// persisted on every change; an unfinished activation therefore survives + /// app restarts. + var activations: [PotaActivationRecord] = [] + /// Whether `activations` has been loaded from disk yet (the POTA screen + /// loads lazily on first appearance). + var activationsLoaded: Bool = false + + /// The in-progress activation, if any (endedAtMs == nil). + var current: PotaActivationRecord? { activations.first { $0.isActive } } + var isActivating: Bool { current != nil } } // MARK: - Settings Persistence diff --git a/ios/FT8AF/FT8AF/Components/ActiveQsoPanel.swift b/ios/FT8AF/FT8AF/Components/ActiveQsoPanel.swift index 8c052ccc..92a102e3 100644 --- a/ios/FT8AF/FT8AF/Components/ActiveQsoPanel.swift +++ b/ios/FT8AF/FT8AF/Components/ActiveQsoPanel.swift @@ -45,17 +45,17 @@ struct ActiveQsoPanel: View { // Header row: QSO label + state + SNR · LOG · ✕ · collapse HStack(spacing: 8) { Text("QSO") - .font(.system(size: 10, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .semibold)) .foregroundStyle(textFaint) Text(headerLabel) - .font(.system(size: 13, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 13, weight: .bold)) .foregroundStyle(accent) .lineLimit(1) if hasTarget, let snr = tx.targetSnr { Text(snr >= 0 ? "+\(snr)dB" : "\(snr)dB") - .font(.system(size: 11, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .medium)) .foregroundStyle(signal) } @@ -67,7 +67,7 @@ struct ActiveQsoPanel: View { appState.engine?.forceLogAndMoveOn() } label: { Text("LOG") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(signal) .padding(.horizontal, 8) .padding(.vertical, 4) @@ -80,7 +80,7 @@ struct ActiveQsoPanel: View { appState.engine?.clearActiveQso() } label: { Image(systemName: "xmark") - .font(.system(size: 11, weight: .semibold)) + .font(.ft8afUI(size: 11, weight: .semibold)) .foregroundStyle(textMuted) .frame(width: 22, height: 22) } @@ -93,7 +93,7 @@ struct ActiveQsoPanel: View { } } label: { Image(systemName: "chevron.down") - .font(.system(size: 10, weight: .semibold)) + .font(.ft8afUI(size: 10, weight: .semibold)) .foregroundStyle(textMuted) .frame(width: 22, height: 22) } @@ -123,7 +123,7 @@ struct ActiveQsoPanel: View { } else if !tx.txMessage.isEmpty { // Fallback: show current TX message if no log entries yet Text(tx.txMessage) - .font(.system(size: 12, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: .medium)) .foregroundStyle(textMuted) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 8) @@ -175,19 +175,19 @@ struct ActiveQsoPanel: View { .frame(width: 6, height: 6) Text(headerLabel) - .font(.system(size: 12, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: .semibold)) .foregroundStyle(textPrimary) if !tx.queuedCallers.isEmpty { Text("+\(tx.queuedCallers.count)") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(signal) } Spacer() Image(systemName: "chevron.up") - .font(.system(size: 10, weight: .semibold)) + .font(.ft8afUI(size: 10, weight: .semibold)) .foregroundStyle(textMuted) } .padding(.horizontal, 14) @@ -218,7 +218,7 @@ private struct TxSelectorRow: View { var body: some View { HStack(spacing: 4) { Text("TX:") - .font(.system(size: 10, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .semibold)) .foregroundStyle(textFaint) ForEach(TxStageSelector.chips, id: \.rawValue) { chip in @@ -229,7 +229,7 @@ private struct TxSelectorRow: View { onSelect(chip) } label: { Text(TxStageSelector.label(for: chip)) - .font(.system(size: 10, weight: isActive ? .bold : .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: isActive ? .bold : .medium)) .foregroundStyle(isActive ? accent : (isCompleted ? signal : textMuted)) .padding(.horizontal, 8) .padding(.vertical, 4) @@ -261,7 +261,7 @@ private struct CallerQueueBar: View { var body: some View { HStack(alignment: .firstTextBaseline, spacing: 4) { Text("QUEUE") - .font(.system(size: 9, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .semibold)) .foregroundStyle(textFaint) WrappingHStack(spacing: 4, lineSpacing: 3) { @@ -270,7 +270,7 @@ private struct CallerQueueBar: View { onTap(callsign) } label: { Text(callsign) - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(signal) .padding(.horizontal, 6) .padding(.vertical, 2) @@ -330,7 +330,7 @@ private struct ConversationRow: View { HStack(spacing: 6) { // Direction badge Text(entry.direction.rawValue) - .font(.system(size: 8, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 8, weight: .bold)) .foregroundStyle(directionColor) .frame(width: 28) .padding(.vertical, 2) @@ -341,7 +341,7 @@ private struct ConversationRow: View { // Message Text(entry.message) - .font(.system(size: 11, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .medium)) .foregroundStyle(textPrimary) .lineLimit(1) @@ -350,13 +350,13 @@ private struct ConversationRow: View { // SNR (for RX) if let snr = entry.snr { Text(snr >= 0 ? "+\(snr)" : "\(snr)") - .font(.system(size: 9, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .semibold)) .foregroundStyle(textFaint) } // Time Text(String(entry.utcTime.prefix(5))) - .font(.system(size: 9, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .medium)) .foregroundStyle(textDim) } .padding(.horizontal, 8) @@ -401,7 +401,7 @@ struct QsoStageDots: View { .fill(isReached(idx) ? accent : textDim.opacity(0.3)) .frame(width: isCurrent(idx) ? 10 : 7, height: isCurrent(idx) ? 10 : 7) Text(item.label) - .font(.system(size: 8, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 8, weight: .semibold)) .foregroundStyle(isReached(idx) ? accent : textFaint) } } diff --git a/ios/FT8AF/FT8AF/Components/FilterChip.swift b/ios/FT8AF/FT8AF/Components/FilterChip.swift index 7227bcaf..7e02f000 100644 --- a/ios/FT8AF/FT8AF/Components/FilterChip.swift +++ b/ios/FT8AF/FT8AF/Components/FilterChip.swift @@ -10,7 +10,7 @@ struct FilterChip: View { var body: some View { Button(action: action) { Text(label) - .font(.system(size: 12, weight: isSelected ? .semibold : .medium, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: isSelected ? .semibold : .medium)) .foregroundStyle(isSelected ? bgApp : textMuted) .padding(.horizontal, 12) .padding(.vertical, 6) diff --git a/ios/FT8AF/FT8AF/Components/FrequencyPickerSheet.swift b/ios/FT8AF/FT8AF/Components/FrequencyPickerSheet.swift index 22d37b4d..815710f8 100644 --- a/ios/FT8AF/FT8AF/Components/FrequencyPickerSheet.swift +++ b/ios/FT8AF/FT8AF/Components/FrequencyPickerSheet.swift @@ -36,14 +36,14 @@ struct FrequencyPickerSheet: View { .frame(width: 8, height: 8) Text(item.band) - .font(.system(size: 16, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 16, weight: .bold)) .foregroundStyle(textPrimary) Text("—") .foregroundStyle(textDim) Text(item.freq) - .font(.system(size: 14, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .medium)) .foregroundStyle(textMuted) Spacer() diff --git a/ios/FT8AF/FT8AF/Components/QsoPathMap.swift b/ios/FT8AF/FT8AF/Components/QsoPathMap.swift index f9f7c5cc..f807cbf6 100644 --- a/ios/FT8AF/FT8AF/Components/QsoPathMap.swift +++ b/ios/FT8AF/FT8AF/Components/QsoPathMap.swift @@ -39,8 +39,8 @@ struct QsoPathMap: View { ctx.stroke(Path(ellipseIn: theirRect), with: .color(signal.opacity(0.5)), lineWidth: 2) // Labels - let myLabel = Text(myGrid).font(.system(size: 8, weight: .bold, design: .monospaced)).foregroundStyle(accent) - let theirLabel = Text(theirGrid).font(.system(size: 8, weight: .bold, design: .monospaced)).foregroundStyle(signal) + let myLabel = Text(myGrid).font(.ft8afMono(size: 8, weight: .bold)).foregroundStyle(accent) + let theirLabel = Text(theirGrid).font(.ft8afMono(size: 8, weight: .bold)).foregroundStyle(signal) let myLabelPt = CGPoint(x: max(20, min(w - 20, myPt.x)), y: max(10, myPt.y - 12)) let theirLabelPt = CGPoint(x: max(20, min(w - 20, theirPt.x)), y: min(h - 10, theirPt.y + 12)) diff --git a/ios/FT8AF/FT8AF/Components/ToastOverlay.swift b/ios/FT8AF/FT8AF/Components/ToastOverlay.swift index ca056ac8..a1823805 100644 --- a/ios/FT8AF/FT8AF/Components/ToastOverlay.swift +++ b/ios/FT8AF/FT8AF/Components/ToastOverlay.swift @@ -13,11 +13,11 @@ struct ToastOverlay: View { HStack(spacing: 8) { if let icon { Image(systemName: icon) - .font(.system(size: 13, weight: .semibold)) + .font(.ft8afUI(size: 13, weight: .semibold)) .foregroundStyle(accent) } Text(message) - .font(.system(size: 13, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 13, weight: .semibold)) .foregroundStyle(textPrimary) } .padding(.horizontal, 16) diff --git a/ios/FT8AF/FT8AF/Components/TxStrip.swift b/ios/FT8AF/FT8AF/Components/TxStrip.swift index bd0e7588..b0b2ae6d 100644 --- a/ios/FT8AF/FT8AF/Components/TxStrip.swift +++ b/ios/FT8AF/FT8AF/Components/TxStrip.swift @@ -25,7 +25,7 @@ struct TxStrip: View { HStack(spacing: 6) { PulseDot(color: tx.isTransmitting ? accent : signal) Text(tx.isTransmitting ? "TX" : "RX") - .font(.system(size: 12, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: .semibold)) .foregroundStyle(textPrimary) } @@ -65,7 +65,7 @@ struct TxStrip: View { } } label: { Image(systemName: tx.expanded ? "chevron.down" : "chevron.up") - .font(.system(size: 11, weight: .semibold)) + .font(.ft8afUI(size: 11, weight: .semibold)) .foregroundStyle(textMuted) .frame(width: 28, height: 28) .background( @@ -81,7 +81,7 @@ struct TxStrip: View { VStack(spacing: 8) { HStack(spacing: 8) { Text("VOL") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(textFaint) // Step down @@ -90,7 +90,7 @@ struct TxStrip: View { onVolumeChange(newVol) } label: { Image(systemName: "minus") - .font(.system(size: 10, weight: .bold)) + .font(.ft8afUI(size: 10, weight: .bold)) .foregroundStyle(textMuted) .frame(width: 24, height: 24) .background(Circle().fill(bgSurface3)) @@ -113,7 +113,7 @@ struct TxStrip: View { onVolumeChange(newVol) } label: { Image(systemName: "plus") - .font(.system(size: 10, weight: .bold)) + .font(.ft8afUI(size: 10, weight: .bold)) .foregroundStyle(textMuted) .frame(width: 24, height: 24) .background(Circle().fill(bgSurface3)) @@ -121,7 +121,7 @@ struct TxStrip: View { .buttonStyle(.plain) Text("\(settings.txVolume)%") - .font(.system(size: 11, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .semibold)) .foregroundStyle(textMuted) .frame(width: 36, alignment: .trailing) } @@ -190,7 +190,7 @@ private struct TunePill: View { var body: some View { Button(action: action) { Text(TuneTone.chipLabel(isTuning: isTuning, remainingSec: remainingSec)) - .font(.system(size: 11, weight: isTuning ? .bold : .semibold, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: isTuning ? .bold : .semibold)) .foregroundStyle(isTuning ? .white : (locked ? textMuted.opacity(0.4) : textMuted)) .padding(.horizontal, 8) .padding(.vertical, 4) @@ -213,7 +213,7 @@ private struct CatChip: View { .fill(dotColor) .frame(width: 6, height: 6) Text("CAT") - .font(.system(size: 9, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .bold)) .foregroundStyle(textMuted) } .padding(.horizontal, 6) @@ -263,7 +263,7 @@ private struct TxChip: View { var body: some View { Text(label) - .font(.system(size: 11, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .semibold)) .foregroundStyle(color) .padding(.horizontal, 8) .padding(.vertical, 4) @@ -296,16 +296,16 @@ private struct ActionButton: View { case .primary: HStack(spacing: 8) { Image(systemName: icon) - .font(.system(size: 14, weight: .semibold)) + .font(.ft8afUI(size: 14, weight: .semibold)) Text(label) - .font(.system(size: 15, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 15, weight: .bold)) } case .secondary: VStack(spacing: 3) { Image(systemName: icon) - .font(.system(size: 14, weight: .semibold)) + .font(.ft8afUI(size: 14, weight: .semibold)) Text(label) - .font(.system(size: 11, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .semibold)) } } } diff --git a/ios/FT8AF/FT8AF/Engine/PotaActivationStore.swift b/ios/FT8AF/FT8AF/Engine/PotaActivationStore.swift new file mode 100644 index 00000000..e6b3d00d --- /dev/null +++ b/ios/FT8AF/FT8AF/Engine/PotaActivationStore.swift @@ -0,0 +1,35 @@ +import FT8Engine +import Foundation + +/// File-based persistence for POTA activation sessions, exactly like +/// `QsoLogStore`: a JSON array in the app's documents directory, loaded when +/// the POTA screen first appears and saved after every change. This is what +/// makes the History tab (and a still-open activation) survive restarts. +enum PotaActivationStore { + private static let fileName = "pota_activations.json" + + static func fileURL() -> URL { + let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + return docs.appendingPathComponent(fileName) + } + + static func load() -> [PotaActivationRecord] { + let url = fileURL() + guard FileManager.default.fileExists(atPath: url.path) else { return [] } + do { + let data = try Data(contentsOf: url) + return try JSONDecoder().decode([PotaActivationRecord].self, from: data) + } catch { + return [] + } + } + + static func save(_ activations: [PotaActivationRecord]) { + do { + let data = try JSONEncoder().encode(activations) + try data.write(to: fileURL(), options: .atomic) + } catch { + // Best-effort: activation-history persistence failure is non-fatal. + } + } +} diff --git a/ios/FT8AF/FT8AF/Engine/PotaService.swift b/ios/FT8AF/FT8AF/Engine/PotaService.swift new file mode 100644 index 00000000..a2ea2d68 --- /dev/null +++ b/ios/FT8AF/FT8AF/Engine/PotaService.swift @@ -0,0 +1,66 @@ +import FT8Engine +import Foundation + +/// Live POTA activator-spot feed for the Hunt tab. Fetch + refresh state only; +/// all response decoding, filtering, and formatting is pure kit logic in +/// `FT8Engine.PotaSpots` (tested there with canned fixtures). +/// +/// Mirrors Android's `PotaSpotsRepository`: same endpoint, 60 s refresh cadence +/// (driven by the screen's `.task` while the Hunt tab is visible), same +/// FT8-mode constraint for hunting — except the filter is applied client-side +/// behind a toggle so it flips instantly without a refetch. +@Observable @MainActor +final class PotaService { + static let shared = PotaService() + + /// All decoded spots from the last successful fetch (every mode). + var spots: [PotaSpot] = [] + var isLoading = false + /// Human-readable failure from the last fetch, nil after a success. + var lastError: String? + var lastUpdatedAt: Date? + /// Hunt-tab constraint: Android hunts FT8-relevant spots only; the toggle + /// lets the operator peek at the full feed. + var ft8Only = true + + /// Spots as the Hunt list shows them (mode filter + frequency sort). + var displaySpots: [PotaSpot] { + PotaSpots.forDisplay(spots, ft8Only: ft8Only) + } + + private init() {} + + /// One fetch of the public activator-spot feed. Safe to call concurrently + /// (re-entrant calls are dropped while a fetch is in flight). + func refresh() async { + if isLoading { return } + guard let url = URL(string: PotaSpots.activatorSpotsURLString) else { return } + isLoading = true + defer { isLoading = false } + + var req = URLRequest(url: url) + req.timeoutInterval = 10 + req.setValue("ft8af-1.0", forHTTPHeaderField: "User-Agent") + req.setValue("application/json", forHTTPHeaderField: "Accept") + + do { + let (data, resp) = try await URLSession.shared.data(for: req) + guard let http = resp as? HTTPURLResponse, http.statusCode == 200 else { + let code = (resp as? HTTPURLResponse)?.statusCode ?? -1 + lastError = "POTA feed error (HTTP \(code))" + return + } + guard let decoded = PotaSpots.decode(data) else { + lastError = "POTA feed returned unexpected data" + return + } + spots = decoded + lastError = nil + lastUpdatedAt = Date() + } catch is CancellationError { + // Tab switched away mid-fetch — not an error. + } catch { + lastError = "POTA feed unreachable" + } + } +} diff --git a/ios/FT8AF/FT8AF/Fonts/geist_mono_bold.ttf b/ios/FT8AF/FT8AF/Fonts/geist_mono_bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..201f8e7ba2250ca4103689e4611a42106c703500 GIT binary patch literal 151260 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 literal 0 HcmV?d00001 diff --git a/ios/FT8AF/FT8AF/Fonts/geist_mono_regular.ttf b/ios/FT8AF/FT8AF/Fonts/geist_mono_regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..643e6ba0731882a61296da368cb0d6362290b882 GIT binary patch literal 149284 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%0DdFpZvw%lOca~0{QDe7_TOlzZd2!?Ikt!oh{@Ay1 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
mv-zMA@Wpb?gV;4$=$eGYg#B6wU5AW0>d&JvKQ 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 n_Ze5ly^UHI4u8YB+$ literal 0 HcmV?d00001 diff --git a/ios/FT8AF/FT8AF/Fonts/geist_mono_semibold.ttf b/ios/FT8AF/FT8AF/Fonts/geist_mono_semibold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ad0c8220182e428fcdd00b909ee73a10b31ff78d GIT binary patch literal 150468 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 IFD-Na2MOGQzW@LL literal 0 HcmV?d00001 diff --git a/ios/FT8AF/FT8AF/Fonts/inter_variable.ttf b/ios/FT8AF/FT8AF/Fonts/inter_variable.ttf new file mode 100644 index 0000000000000000000000000000000000000000..047c92f6e2212473dc436020afed689527076d44 GIT binary patch literal 876576 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

Ek&TdifBe7Pd^mmY)(962bN_SP%pQaf-0_4xU_4 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

us)Vd6A*)Kr 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ⅅz?iuH&b8I;Pc4b>A5fi= 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 + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSMicrophoneUsageDescription + FT8AF needs microphone access to capture radio audio for FT8 decoding + UIAppFonts + + inter_variable.ttf + geist_mono_regular.ttf + geist_mono_medium.ttf + geist_mono_semibold.ttf + geist_mono_bold.ttf + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + + UIApplicationSupportsIndirectInputEvents + + UILaunchScreen + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/FT8AF/FT8AF/Navigation/AppTabView.swift b/ios/FT8AF/FT8AF/Navigation/AppTabView.swift index 8591fba1..8aada9f5 100644 --- a/ios/FT8AF/FT8AF/Navigation/AppTabView.swift +++ b/ios/FT8AF/FT8AF/Navigation/AppTabView.swift @@ -135,13 +135,13 @@ private struct TabBarButton: View { .frame(width: 40, height: 28) } Image(systemName: tab.icon) - .font(.system(size: 16, weight: isSelected ? .semibold : .regular)) + .font(.ft8afUI(size: 16, weight: isSelected ? .semibold : .regular)) .foregroundStyle(isSelected ? accent : textFaint) } .frame(height: 28) Text(tab.label) - .font(.system(size: 10.5, weight: isSelected ? .semibold : .medium)) + .font(.ft8afUI(size: 10.5, weight: isSelected ? .semibold : .medium)) .foregroundStyle(isSelected ? accent : textFaint) } .frame(maxWidth: .infinity) diff --git a/ios/FT8AF/FT8AF/Screens/Decode/DecodeRow.swift b/ios/FT8AF/FT8AF/Screens/Decode/DecodeRow.swift index efe770dd..ae010835 100644 --- a/ios/FT8AF/FT8AF/Screens/Decode/DecodeRow.swift +++ b/ios/FT8AF/FT8AF/Screens/Decode/DecodeRow.swift @@ -48,7 +48,7 @@ struct DecodeRow: View { Spacer().frame(height: compact ? 2 : 4) if !compact && !fullMessageText.isEmpty { Text(fullMessageText) - .font(.system(size: 12, design: .monospaced)) + .font(.ft8afMono(size: 12)) .foregroundStyle(textMuted) .lineLimit(1) Spacer().frame(height: 4) @@ -58,10 +58,10 @@ struct DecodeRow: View { Spacer().frame(height: 4) HStack(spacing: 4) { Image(systemName: "globe") - .font(.system(size: 10)) + .font(.ft8afUI(size: 10)) .foregroundStyle(textFaint) Text(location) - .font(.system(size: 10.5, weight: .medium)) + .font(.ft8afUI(size: 10.5, weight: .medium)) .foregroundStyle(textFaint) } } @@ -100,12 +100,12 @@ struct DecodeRow: View { } Text(message.callFrom) - .font(.system(size: 15, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 15, weight: .bold)) .foregroundStyle(callsignColor) if !message.grid.isEmpty { Text(message.grid) - .font(.system(size: 11, design: .monospaced)) + .font(.ft8afMono(size: 11)) .foregroundStyle(textFaint) } @@ -206,7 +206,7 @@ private struct StatusPillView: View { .fill(hue) .frame(width: 6, height: 6) Text(label) - .font(.system(size: compact ? 9.5 : 11, weight: .semibold)) + .font(.ft8afUI(size: compact ? 9.5 : 11, weight: .semibold)) .foregroundStyle(hue) } .padding(.horizontal, compact ? 6 : 8) @@ -300,7 +300,7 @@ private struct MessageLabel: View { var body: some View { Text(text) - .font(.system(size: 9.5, weight: .bold)) + .font(.ft8afUI(size: 9.5, weight: .bold)) .kerning(0.6) .foregroundStyle(color) .padding(.horizontal, 5) @@ -316,7 +316,7 @@ private struct MetaText: View { var body: some View { Text(text) - .font(.system(size: 10.5, design: .monospaced)) + .font(.ft8afMono(size: 10.5)) .foregroundStyle(textFaint) } } diff --git a/ios/FT8AF/FT8AF/Screens/Decode/DecodeScreen.swift b/ios/FT8AF/FT8AF/Screens/Decode/DecodeScreen.swift index 36d49636..a2ecca7b 100644 --- a/ios/FT8AF/FT8AF/Screens/Decode/DecodeScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Decode/DecodeScreen.swift @@ -12,7 +12,7 @@ struct DecodeScreen: View { // Top bar HStack { Text("Decode") - .font(.system(size: 18, weight: .bold)) + .font(.ft8afUI(size: 18, weight: .bold)) .foregroundStyle(textPrimary) Spacer() @@ -22,7 +22,7 @@ struct DecodeScreen: View { appState.decode.compactMode.toggle() } label: { Image(systemName: decode.compactMode ? "list.bullet" : "list.bullet.indent") - .font(.system(size: 14, weight: .medium)) + .font(.ft8afUI(size: 14, weight: .medium)) .foregroundStyle(decode.compactMode ? accent : textMuted) } .buttonStyle(.plain) @@ -33,7 +33,7 @@ struct DecodeScreen: View { appState.decode.autoClear.toggle() } label: { Image(systemName: decode.autoClear ? "arrow.clockwise.circle.fill" : "arrow.clockwise.circle") - .font(.system(size: 14, weight: .medium)) + .font(.ft8afUI(size: 14, weight: .medium)) .foregroundStyle(decode.autoClear ? accent : textMuted) } .buttonStyle(.plain) @@ -44,14 +44,14 @@ struct DecodeScreen: View { appState.decode.messages.removeAll() } label: { Image(systemName: "trash") - .font(.system(size: 13, weight: .medium)) + .font(.ft8afUI(size: 13, weight: .medium)) .foregroundStyle(textMuted) } .buttonStyle(.plain) .padding(.trailing, 8) Text("\(decode.messages.count) msgs") - .font(.system(size: 12, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: .medium)) .foregroundStyle(textMuted) } .padding(.horizontal, 16) @@ -267,13 +267,13 @@ struct DecodeScreen: View { VStack(spacing: 12) { Spacer() Image(systemName: "waveform.slash") - .font(.system(size: 40)) + .font(.ft8afUI(size: 40)) .foregroundStyle(textFaint) Text("No messages") - .font(.system(size: 16, weight: .medium)) + .font(.ft8afUI(size: 16, weight: .medium)) .foregroundStyle(textMuted) Text("Decoded FT8 messages will appear here") - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textFaint) Spacer() } @@ -290,7 +290,7 @@ private struct SlotDivider: View { .fill(textDim.opacity(0.4)) .frame(height: 1) Text(String(time.prefix(5))) - .font(.system(size: 9, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .medium)) .foregroundStyle(textFaint) Rectangle() .fill(textDim.opacity(0.4)) diff --git a/ios/FT8AF/FT8AF/Screens/Decode/QsoSheet.swift b/ios/FT8AF/FT8AF/Screens/Decode/QsoSheet.swift index d23b60b3..2f68b756 100644 --- a/ios/FT8AF/FT8AF/Screens/Decode/QsoSheet.swift +++ b/ios/FT8AF/FT8AF/Screens/Decode/QsoSheet.swift @@ -14,13 +14,13 @@ struct QsoSheet: View { // Callsign header VStack(spacing: 4) { Text(message.callFrom) - .font(.system(size: 28, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 28, weight: .bold)) .foregroundStyle(textPrimary) HStack(spacing: 8) { if !message.grid.isEmpty { Text(message.grid) - .font(.system(size: 14, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .medium)) .foregroundStyle(textMuted) } @@ -30,7 +30,7 @@ struct QsoSheet: View { } } label: { Text("QRZ") - .font(.system(size: 11, weight: .bold)) + .font(.ft8afUI(size: 11, weight: .bold)) .foregroundStyle(signal) .padding(.horizontal, 8) .padding(.vertical, 3) @@ -64,7 +64,7 @@ struct QsoSheet: View { if appState.tx.isActivated { VStack(spacing: 6) { Text("QSO PROGRESS") - .font(.system(size: 9, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .bold)) .foregroundStyle(textFaint) QsoStageDots(currentStage: appState.tx.stage) .padding(.horizontal, 24) @@ -83,11 +83,11 @@ struct QsoSheet: View { if !message.extra.isEmpty && message.extra != message.grid { HStack { Text("Report") - .font(.system(size: 12, weight: .medium)) + .font(.ft8afUI(size: 12, weight: .medium)) .foregroundStyle(textFaint) Spacer() Text(message.extra) - .font(.system(size: 14, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .semibold)) .foregroundStyle(textPrimary) } .padding(.horizontal, 24) @@ -98,7 +98,7 @@ struct QsoSheet: View { if appState.tx.isActivated && !appState.tx.txMessage.isEmpty { HStack(spacing: 8) { Text(appState.tx.isTransmitting ? "TX NOW" : "TX NEXT") - .font(.system(size: 9, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .bold)) .foregroundStyle(appState.tx.isTransmitting ? bgApp : accent) .padding(.horizontal, 6) .padding(.vertical, 2) @@ -108,7 +108,7 @@ struct QsoSheet: View { ) Text(appState.tx.txMessage) - .font(.system(size: 12, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: .medium)) .foregroundStyle(textPrimary) .lineLimit(1) @@ -133,9 +133,9 @@ struct QsoSheet: View { } label: { HStack(spacing: 8) { Image(systemName: "antenna.radiowaves.left.and.right") - .font(.system(size: 14, weight: .semibold)) + .font(.ft8afUI(size: 14, weight: .semibold)) Text("Call \(message.callFrom)") - .font(.system(size: 16, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 16, weight: .bold)) } .foregroundStyle(bgApp) .frame(maxWidth: .infinity) @@ -172,11 +172,11 @@ private struct StatBadge: View { var body: some View { VStack(spacing: 4) { Text(label) - .font(.system(size: 10, weight: .medium)) + .font(.ft8afUI(size: 10, weight: .medium)) .foregroundStyle(textFaint) .textCase(.uppercase) Text(value) - .font(.system(size: 13, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 13, weight: .semibold)) .foregroundStyle(textPrimary) .lineLimit(1) .minimumScaleFactor(0.7) diff --git a/ios/FT8AF/FT8AF/Screens/Logbook/ExportLogSheet.swift b/ios/FT8AF/FT8AF/Screens/Logbook/ExportLogSheet.swift index 0ad99429..2351da04 100644 --- a/ios/FT8AF/FT8AF/Screens/Logbook/ExportLogSheet.swift +++ b/ios/FT8AF/FT8AF/Screens/Logbook/ExportLogSheet.swift @@ -63,16 +63,16 @@ struct ExportLogSheet: View { } label: { HStack(spacing: 12) { Image(systemName: format.icon) - .font(.system(size: 14, weight: .semibold)) + .font(.ft8afUI(size: 14, weight: .semibold)) .foregroundStyle(selectedFormat == format ? accent : textFaint) .frame(width: 24) VStack(alignment: .leading, spacing: 2) { Text(format.rawValue) - .font(.system(size: 14, weight: .semibold)) + .font(.ft8afUI(size: 14, weight: .semibold)) .foregroundStyle(textPrimary) Text(format.description) - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } @@ -117,9 +117,9 @@ struct ExportLogSheet: View { HStack { Spacer() Image(systemName: "square.and.arrow.up") - .font(.system(size: 14, weight: .semibold)) + .font(.ft8afUI(size: 14, weight: .semibold)) Text("Export \(selectedFormat.rawValue)") - .font(.system(size: 15, weight: .bold)) + .font(.ft8afUI(size: 15, weight: .bold)) Spacer() } .foregroundStyle(bgApp) diff --git a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookAwards.swift b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookAwards.swift index de7253b3..111f57e2 100644 --- a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookAwards.swift +++ b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookAwards.swift @@ -134,13 +134,13 @@ private struct AwardStatCard: View { .rotationEffect(.degrees(-90)) Text("\(value)") - .font(.system(size: 18, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 18, weight: .bold)) .foregroundStyle(textPrimary) } .frame(width: 54, height: 54) Text(title) - .font(.system(size: 10, weight: .semibold)) + .font(.ft8afUI(size: 10, weight: .semibold)) .foregroundStyle(textFaint) .textCase(.uppercase) } @@ -179,7 +179,7 @@ private struct AwardCard: View { VStack(alignment: .leading, spacing: 10) { HStack(spacing: 8) { Image(systemName: icon) - .font(.system(size: 14, weight: .semibold)) + .font(.ft8afUI(size: 14, weight: .semibold)) .foregroundStyle(color) .frame(width: 28, height: 28) .background( @@ -189,10 +189,10 @@ private struct AwardCard: View { VStack(alignment: .leading, spacing: 1) { Text(name) - .font(.system(size: 13, weight: .bold)) + .font(.ft8afUI(size: 13, weight: .bold)) .foregroundStyle(textPrimary) Text(description) - .font(.system(size: 10)) + .font(.ft8afUI(size: 10)) .foregroundStyle(textFaint) .lineLimit(1) } @@ -200,7 +200,7 @@ private struct AwardCard: View { Spacer() Text("\(current)/\(nextMilestone)") - .font(.system(size: 12, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: .bold)) .foregroundStyle(progress >= 1.0 ? statusConfirmed : textMuted) } @@ -227,7 +227,7 @@ private struct AwardCard: View { HStack(spacing: 0) { ForEach(milestones, id: \.self) { ms in Text("\(ms)") - .font(.system(size: 8, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 8, weight: .semibold)) .foregroundStyle(current >= ms ? color : textDim) if ms != milestones.last { Spacer() diff --git a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookCharts.swift b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookCharts.swift index 7a80533c..12572217 100644 --- a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookCharts.swift +++ b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookCharts.swift @@ -84,10 +84,10 @@ private struct BigStatCard: View { var body: some View { VStack(spacing: 4) { Text("\(value)") - .font(.system(size: 22, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 22, weight: .bold)) .foregroundStyle(textPrimary) Text(title) - .font(.system(size: 10, weight: .semibold)) + .font(.ft8afUI(size: 10, weight: .semibold)) .foregroundStyle(textFaint) .textCase(.uppercase) } @@ -112,7 +112,7 @@ private struct BandDonutChart: View { var body: some View { VStack(alignment: .leading, spacing: 8) { Text("BAND DISTRIBUTION") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(textFaint) HStack(spacing: 16) { @@ -128,10 +128,10 @@ private struct BandDonutChart: View { // Center label VStack(spacing: 1) { Text("\(total)") - .font(.system(size: 16, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 16, weight: .bold)) .foregroundStyle(textPrimary) Text("QSOs") - .font(.system(size: 8, weight: .medium)) + .font(.ft8afUI(size: 8, weight: .medium)) .foregroundStyle(textFaint) } } @@ -145,11 +145,11 @@ private struct BandDonutChart: View { .fill(bandColor(for: item.band)) .frame(width: 6, height: 6) Text(item.band) - .font(.system(size: 11, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .semibold)) .foregroundStyle(textMuted) Spacer() Text("\(item.count)") - .font(.system(size: 11, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .bold)) .foregroundStyle(textPrimary) } } @@ -218,11 +218,11 @@ private struct SnrSparkline: View { VStack(alignment: .leading, spacing: 8) { HStack { Text("SIGNAL TREND") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(textFaint) Spacer() Text("Last \(snrValues.count) QSOs") - .font(.system(size: 9, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .medium)) .foregroundStyle(textDim) } @@ -295,11 +295,11 @@ private struct GridHeatmap: View { VStack(alignment: .leading, spacing: 8) { HStack { Text("GRID COVERAGE") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(textFaint) Spacer() Text("\(workedFields.count)/324 fields") - .font(.system(size: 9, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .medium)) .foregroundStyle(textDim) } @@ -357,7 +357,7 @@ private struct AwardProgressSection: View { var body: some View { VStack(alignment: .leading, spacing: 8) { Text("AWARDS PROGRESS") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(textFaint) AwardBar(name: "DXCC Mixed", current: dxccCount, goal: 100, color: signal) @@ -386,11 +386,11 @@ private struct AwardBar: View { VStack(alignment: .leading, spacing: 4) { HStack { Text(name) - .font(.system(size: 11, weight: .semibold)) + .font(.ft8afUI(size: 11, weight: .semibold)) .foregroundStyle(textMuted) Spacer() Text("\(current)/\(goal)") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(textFaint) } GeometryReader { geo in diff --git a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookScreen.swift b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookScreen.swift index fc6bb3da..9b220c0c 100644 --- a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookScreen.swift @@ -23,7 +23,7 @@ struct LogbookScreen: View { // Top bar HStack { Text("Logbook") - .font(.system(size: 18, weight: .bold)) + .font(.ft8afUI(size: 18, weight: .bold)) .foregroundStyle(textPrimary) Spacer() @@ -39,12 +39,12 @@ struct LogbookScreen: View { .controlSize(.mini) .tint(accent) Text("\(service.syncDone)/\(service.syncTotal)") - .font(.system(size: 11, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .medium)) .foregroundStyle(textMuted) } } else { Image(systemName: "icloud.and.arrow.up") - .font(.system(size: 14, weight: .medium)) + .font(.ft8afUI(size: 14, weight: .medium)) .foregroundStyle(textMuted) } } @@ -58,7 +58,7 @@ struct LogbookScreen: View { showImportPicker = true } label: { Image(systemName: "square.and.arrow.down") - .font(.system(size: 14, weight: .medium)) + .font(.ft8afUI(size: 14, weight: .medium)) .foregroundStyle(textMuted) } .buttonStyle(.plain) @@ -69,14 +69,14 @@ struct LogbookScreen: View { showExportSheet = true } label: { Image(systemName: "square.and.arrow.up") - .font(.system(size: 14, weight: .medium)) + .font(.ft8afUI(size: 14, weight: .medium)) .foregroundStyle(textMuted) } .buttonStyle(.plain) .padding(.trailing, 8) Text("\(logbook.totalCount) QSOs") - .font(.system(size: 12, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: .medium)) .foregroundStyle(textMuted) } .padding(.horizontal, 16) @@ -135,10 +135,10 @@ struct LogbookScreen: View { // Search bar HStack(spacing: 8) { Image(systemName: "magnifyingglass") - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textFaint) TextField("Search callsign, band, date...", text: $searchText) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textPrimary) .textInputAutocapitalization(.characters) .autocorrectionDisabled() @@ -147,7 +147,7 @@ struct LogbookScreen: View { searchText = "" } label: { Image(systemName: "xmark.circle.fill") - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textFaint) } .buttonStyle(.plain) @@ -217,13 +217,13 @@ struct LogbookScreen: View { private var emptyState: some View { VStack(spacing: 12) { Image(systemName: "book.closed") - .font(.system(size: 40)) + .font(.ft8afUI(size: 40)) .foregroundStyle(textFaint) Text("No QSOs logged") - .font(.system(size: 16, weight: .medium)) + .font(.ft8afUI(size: 16, weight: .medium)) .foregroundStyle(textMuted) Text("Completed contacts will appear here") - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textFaint) } .frame(maxWidth: .infinity) @@ -286,13 +286,13 @@ struct LogbookScreen: View { private var noResultsState: some View { VStack(spacing: 12) { Image(systemName: "magnifyingglass") - .font(.system(size: 40)) + .font(.ft8afUI(size: 40)) .foregroundStyle(textFaint) Text("No matches") - .font(.system(size: 16, weight: .medium)) + .font(.ft8afUI(size: 16, weight: .medium)) .foregroundStyle(textMuted) Text("No QSOs match \"\(searchText)\"") - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textFaint) } .frame(maxWidth: .infinity) @@ -318,12 +318,12 @@ private struct LogbookRow: View { // Call + grid + sync chips VStack(alignment: .leading, spacing: 2) { Text(record.call) - .font(.system(size: 14, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .bold)) .foregroundStyle(textPrimary) HStack(spacing: 4) { if !record.gridsquare.isEmpty { Text(record.gridsquare) - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(textFaint) } if showCloudlogChip { @@ -340,7 +340,7 @@ private struct LogbookRow: View { // Band pill + freq VStack(spacing: 2) { Text(record.band) - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(bandColor(for: record.band)) .padding(.horizontal, 6) .padding(.vertical, 2) @@ -350,7 +350,7 @@ private struct LogbookRow: View { ) if !record.freq.isEmpty { Text(record.freq) - .font(.system(size: 8, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 8, weight: .medium)) .foregroundStyle(textDim) } } @@ -358,7 +358,7 @@ private struct LogbookRow: View { // SNR Text(record.rstRcvd) - .font(.system(size: 11, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .semibold)) .foregroundStyle(snrColor) .frame(width: 30, alignment: .trailing) .padding(.trailing, 10) @@ -366,10 +366,10 @@ private struct LogbookRow: View { // Date/time VStack(alignment: .trailing, spacing: 1) { Text(formatDate(record.qsoDate)) - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(textFaint) Text(formatTime(record.timeOn)) - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(textFaint) } } @@ -386,7 +386,7 @@ private struct LogbookRow: View { /// Tiny "uploaded to " badge — colored once the record is synced. private func syncChip(_ label: String, synced: Bool, color: Color) -> some View { Text(label) - .font(.system(size: 8, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 8, weight: .bold)) .foregroundStyle(synced ? color : textDim) .padding(.horizontal, 4) .padding(.vertical, 1) @@ -459,7 +459,7 @@ private struct QsoEditSheet: View { .foregroundStyle(textPrimary) Spacer() Text(record.band) - .font(.system(.body, design: .monospaced)) + .font(.ft8afMono(size: 17)) .foregroundStyle(textMuted) } HStack { @@ -467,7 +467,7 @@ private struct QsoEditSheet: View { .foregroundStyle(textPrimary) Spacer() Text(record.freq.isEmpty ? "—" : "\(record.freq) MHz") - .font(.system(.body, design: .monospaced)) + .font(.ft8afMono(size: 17)) .foregroundStyle(textMuted) } } header: { @@ -510,7 +510,7 @@ private struct QsoEditSheet: View { .foregroundStyle(textPrimary) Spacer() TextField("", text: text) - .font(.system(.body, design: .monospaced)) + .font(.ft8afMono(size: 17)) .multilineTextAlignment(.trailing) .foregroundStyle(textPrimary) .textInputAutocapitalization(.characters) diff --git a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookStats.swift b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookStats.swift index a2b35bb7..86bd3581 100644 --- a/ios/FT8AF/FT8AF/Screens/Logbook/LogbookStats.swift +++ b/ios/FT8AF/FT8AF/Screens/Logbook/LogbookStats.swift @@ -11,16 +11,16 @@ struct LogbookStats: View { HStack { VStack(alignment: .leading, spacing: 4) { Text("Total QSOs") - .font(.system(size: 11, weight: .medium)) + .font(.ft8afUI(size: 11, weight: .medium)) .foregroundStyle(textFaint) .textCase(.uppercase) Text("\(totalQsos)") - .font(.system(size: 28, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 28, weight: .bold)) .foregroundStyle(textPrimary) } Spacer() Image(systemName: "antenna.radiowaves.left.and.right") - .font(.system(size: 24)) + .font(.ft8afUI(size: 24)) .foregroundStyle(accent.opacity(0.5)) } .padding(14) @@ -65,10 +65,10 @@ private struct BandStatCard: View { var body: some View { VStack(spacing: 4) { Text(band) - .font(.system(size: 11, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .bold)) .foregroundStyle(bandColor(for: band)) Text("\(count)") - .font(.system(size: 16, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 16, weight: .semibold)) .foregroundStyle(textPrimary) } .frame(maxWidth: .infinity) diff --git a/ios/FT8AF/FT8AF/Screens/Map/AzimuthalMapCanvas.swift b/ios/FT8AF/FT8AF/Screens/Map/AzimuthalMapCanvas.swift index cd091801..f18b3b21 100644 --- a/ios/FT8AF/FT8AF/Screens/Map/AzimuthalMapCanvas.swift +++ b/ios/FT8AF/FT8AF/Screens/Map/AzimuthalMapCanvas.swift @@ -53,7 +53,7 @@ struct AzimuthalMapCanvas: View { // Distance label on right side of ring let labelText = dist >= 1000 ? "\(Int(dist / 1000))k" : "\(Int(dist))" let text = context.resolve(Text(labelText) - .font(.system(size: 8, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 8, weight: .medium)) .foregroundStyle(textFaint)) let labelX = center.x + ringRadius + 2 let labelY = center.y - 5 @@ -82,7 +82,7 @@ struct AzimuthalMapCanvas: View { let lx = center.x + labelDist * CGFloat(sin(rad)) let ly = center.y - labelDist * CGFloat(cos(rad)) let compassText = context.resolve(Text(compassLabels[i]) - .font(.system(size: 9, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .semibold)) .foregroundStyle(i == 0 ? textMuted : textFaint)) context.draw(compassText, at: CGPoint(x: lx, y: ly), anchor: .center) } diff --git a/ios/FT8AF/FT8AF/Screens/Map/MapScreen.swift b/ios/FT8AF/FT8AF/Screens/Map/MapScreen.swift index c14f800f..0f2198ac 100644 --- a/ios/FT8AF/FT8AF/Screens/Map/MapScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Map/MapScreen.swift @@ -20,7 +20,7 @@ struct MapScreen: View { // Top bar HStack { Text("Map") - .font(.system(size: 18, weight: .bold)) + .font(.ft8afUI(size: 18, weight: .bold)) .foregroundStyle(textPrimary) Spacer() @@ -34,9 +34,9 @@ struct MapScreen: View { } label: { HStack(spacing: 4) { Image(systemName: projection == .equirectangular ? "map" : "globe") - .font(.system(size: 11, weight: .medium)) + .font(.ft8afUI(size: 11, weight: .medium)) Text(projection.rawValue) - .font(.system(size: 10, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .semibold)) } .foregroundStyle(textMuted) .padding(.horizontal, 8) @@ -50,7 +50,7 @@ struct MapScreen: View { .padding(.trailing, 8) Text("\(markers.count) stations") - .font(.system(size: 12, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: .medium)) .foregroundStyle(textMuted) } .padding(.horizontal, 16) @@ -145,26 +145,26 @@ struct MapScreen: View { private func markerPopup(_ marker: MapMarker) -> some View { VStack(spacing: 4) { Text(marker.callsign) - .font(.system(size: 14, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .bold)) .foregroundStyle(textPrimary) Text(marker.grid) - .font(.system(size: 11, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .medium)) .foregroundStyle(textMuted) HStack(spacing: 8) { if let snr = marker.snr { Text("SNR: \(snr >= 0 ? "+\(snr)" : "\(snr)")") - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(textFaint) } if let dist = markerDistance(marker) { Text(dist) - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(textFaint) } if let brg = markerBearing(marker) { Text(brg) - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(textFaint) } } @@ -238,7 +238,7 @@ private struct ZoomButton: View { var body: some View { Button(action: action) { Image(systemName: icon) - .font(.system(size: 13, weight: .semibold)) + .font(.ft8afUI(size: 13, weight: .semibold)) .foregroundStyle(textPrimary) .frame(width: 32, height: 32) .background( diff --git a/ios/FT8AF/FT8AF/Screens/POTA/PotaScreen.swift b/ios/FT8AF/FT8AF/Screens/POTA/PotaScreen.swift index 295c9a06..9f809dd0 100644 --- a/ios/FT8AF/FT8AF/Screens/POTA/PotaScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/POTA/PotaScreen.swift @@ -1,4 +1,6 @@ +import FT8Engine import SwiftUI +import UIKit enum PotaTab: String, CaseIterable, Identifiable { case activate = "Activate" @@ -11,13 +13,17 @@ enum PotaTab: String, CaseIterable, Identifiable { struct PotaScreen: View { @Environment(AppState.self) private var appState @State private var selectedTab: PotaTab = .hunt + @State private var shareURLs: [URL] = [] + @State private var showShare = false + + private var spotService: PotaService { PotaService.shared } var body: some View { VStack(spacing: 0) { // Top bar HStack { Text("POTA") - .font(.system(size: 18, weight: .bold)) + .font(.ft8afUI(size: 18, weight: .bold)) .foregroundStyle(textPrimary) Spacer() @@ -28,7 +34,7 @@ struct PotaScreen: View { .fill(statusConfirmed) .frame(width: 6, height: 6) Text("ACTIVE") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(statusConfirmed) } } @@ -58,16 +64,40 @@ struct PotaScreen: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } .background(bgApp) + .onAppear(perform: loadActivationsIfNeeded) + .task(id: selectedTab) { + // Auto-refresh the live spot feed every ~60 s while the Hunt tab is + // visible (Android's PotaSpotsRepository cadence). The task is + // cancelled automatically when the tab changes or the screen leaves. + guard selectedTab == .hunt else { return } + while !Task.isCancelled { + await spotService.refresh() + try? await Task.sleep(nanoseconds: 60_000_000_000) + } + } + .sheet(isPresented: $showShare) { + PotaShareSheet(items: shareURLs) + } + } + + // MARK: - Activation persistence + + private func loadActivationsIfNeeded() { + guard !appState.pota.activationsLoaded else { return } + appState.pota.activations = PotaActivationStore.load() + appState.pota.activationsLoaded = true + } + + private func persistActivations() { + PotaActivationStore.save(appState.pota.activations) } // MARK: - Activate Tab @ViewBuilder private var activateTab: some View { - let pota = appState.pota - - if pota.isActivating { - activeSessionView(pota: pota) + if let current = appState.pota.current { + activeSessionView(current: current) } else { startActivationView } @@ -77,13 +107,13 @@ struct PotaScreen: View { VStack(spacing: 16) { Spacer() Image(systemName: "tree.fill") - .font(.system(size: 40)) + .font(.ft8afUI(size: 40)) .foregroundStyle(statusConfirmed.opacity(0.5)) Text("Park Activation") - .font(.system(size: 16, weight: .semibold)) + .font(.ft8afUI(size: 16, weight: .semibold)) .foregroundStyle(textPrimary) - Text("Enter a POTA park reference (e.g. K-1234)\nto start an activation session") - .font(.system(size: 13)) + Text("Enter a POTA park reference (e.g. US-1234)\nto start an activation session") + .font(.ft8afUI(size: 13)) .foregroundStyle(textMuted) .multilineTextAlignment(.center) @@ -91,10 +121,10 @@ struct PotaScreen: View { VStack(spacing: 8) { HStack { Text("Park Ref:") - .font(.system(size: 13, weight: .medium)) + .font(.ft8afUI(size: 13, weight: .medium)) .foregroundStyle(textMuted) - TextField("K-0000", text: Bindable(appState.pota).parkInput) - .font(.system(size: 14, weight: .medium, design: .monospaced)) + TextField("US-0000", text: Bindable(appState.pota).parkInput) + .font(.ft8afMono(size: 14, weight: .medium)) .foregroundStyle(textPrimary) .padding(.horizontal, 10) .padding(.vertical, 8) @@ -110,10 +140,10 @@ struct PotaScreen: View { // Notes field HStack { Text("Notes:") - .font(.system(size: 13, weight: .medium)) + .font(.ft8afUI(size: 13, weight: .medium)) .foregroundStyle(textMuted) TextField("Optional notes", text: Bindable(appState.pota).notes) - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textPrimary) .padding(.horizontal, 10) .padding(.vertical, 8) @@ -129,61 +159,65 @@ struct PotaScreen: View { startActivation() } label: { Text("Start Activation") - .font(.system(size: 14, weight: .bold)) + .font(.ft8afUI(size: 14, weight: .bold)) .foregroundStyle(bgApp) .frame(width: 200, height: 44) .background( RoundedRectangle(cornerRadius: 10) - .fill(appState.pota.parkInput.isEmpty ? textFaint : statusConfirmed) + .fill(trimmedParkInput.isEmpty ? textFaint : statusConfirmed) ) } .buttonStyle(.plain) - .disabled(appState.pota.parkInput.isEmpty) + .disabled(trimmedParkInput.isEmpty) Spacer() } } - private func activeSessionView(pota: PotaState) -> some View { - VStack(spacing: 16) { + private func activeSessionView(current: PotaActivationRecord) -> some View { + let qsoCount = activationQsoCount(current) + + return VStack(spacing: 16) { // Activation card VStack(spacing: 12) { HStack { Image(systemName: "tree.fill") - .font(.system(size: 16)) + .font(.ft8afUI(size: 16)) .foregroundStyle(statusConfirmed) - Text(pota.parkInput) - .font(.system(size: 18, weight: .bold, design: .monospaced)) + Text(current.parkRefsDisplay) + .font(.ft8afMono(size: 18, weight: .bold)) .foregroundStyle(statusConfirmed) Spacer() } - if !pota.notes.isEmpty { - Text(pota.notes) - .font(.system(size: 12)) + if !current.notes.isEmpty { + Text(current.notes) + .font(.ft8afUI(size: 12)) .foregroundStyle(textMuted) .frame(maxWidth: .infinity, alignment: .leading) } Divider().overlay(borderSubtle) - // Stats row - HStack(spacing: 0) { - activationStat( - label: "QSOs", - value: "\(pota.activationQsoCount)", - color: pota.activationQsoCount >= 10 ? statusConfirmed : accent - ) - activationStat( - label: "Needed", - value: "\(max(0, 10 - pota.activationQsoCount))", - color: pota.activationQsoCount >= 10 ? statusConfirmed : statusWarn - ) - activationStat( - label: "Elapsed", - value: elapsedString(from: pota.activationStartTime), - color: textMuted - ) + // Stats row (elapsed ticks once a second) + TimelineView(.periodic(from: .now, by: 1)) { context in + HStack(spacing: 0) { + activationStat( + label: "QSOs", + value: "\(qsoCount)", + color: qsoCount >= 10 ? statusConfirmed : accent + ) + activationStat( + label: "Needed", + value: "\(max(0, 10 - qsoCount))", + color: qsoCount >= 10 ? statusConfirmed : statusWarn + ) + activationStat( + label: "Elapsed", + value: elapsedString(startedAtMs: current.startedAtMs, now: context.date), + color: textMuted + ) + } } // Progress bar to 10 QSOs @@ -193,15 +227,15 @@ struct PotaScreen: View { RoundedRectangle(cornerRadius: 3) .fill(bgSurface3) RoundedRectangle(cornerRadius: 3) - .fill(pota.activationQsoCount >= 10 ? statusConfirmed : accent) - .frame(width: geo.size.width * min(CGFloat(pota.activationQsoCount) / 10.0, 1.0)) + .fill(qsoCount >= 10 ? statusConfirmed : accent) + .frame(width: geo.size.width * min(CGFloat(qsoCount) / 10.0, 1.0)) } } .frame(height: 6) - Text(pota.activationQsoCount >= 10 ? "Activation complete!" : "\(pota.activationQsoCount)/10 for valid activation") - .font(.system(size: 10, weight: .medium, design: .monospaced)) - .foregroundStyle(pota.activationQsoCount >= 10 ? statusConfirmed : textFaint) + Text(qsoCount >= 10 ? "Activation complete!" : "\(qsoCount)/10 for valid activation") + .font(.ft8afMono(size: 10, weight: .medium)) + .foregroundStyle(qsoCount >= 10 ? statusConfirmed : textFaint) } } .padding(16) @@ -215,12 +249,38 @@ struct PotaScreen: View { ) .padding(.horizontal, 16) + // Export ADIF (share sheet) — one file per park, MY_SIG_INFO pinned. + Button { + exportAdif(for: current) + } label: { + HStack(spacing: 6) { + Image(systemName: "square.and.arrow.up") + .font(.ft8afUI(size: 13, weight: .semibold)) + Text("Export ADIF") + .font(.ft8afUI(size: 14, weight: .bold)) + } + .foregroundStyle(qsoCount > 0 ? signal : textFaint) + .frame(maxWidth: .infinity) + .frame(height: 44) + .background( + RoundedRectangle(cornerRadius: 10) + .fill(signal.opacity(qsoCount > 0 ? 0.14 : 0.05)) + .overlay( + RoundedRectangle(cornerRadius: 10) + .strokeBorder(signal.opacity(qsoCount > 0 ? 0.3 : 0.1), lineWidth: 1) + ) + ) + } + .buttonStyle(.plain) + .disabled(qsoCount == 0) + .padding(.horizontal, 16) + // End activation button Button { endActivation() } label: { Text("End Activation") - .font(.system(size: 14, weight: .bold)) + .font(.ft8afUI(size: 14, weight: .bold)) .foregroundStyle(statusBad) .frame(maxWidth: .infinity) .frame(height: 44) @@ -244,32 +304,58 @@ struct PotaScreen: View { private func activationStat(label: String, value: String, color: Color) -> some View { VStack(spacing: 4) { Text(value) - .font(.system(size: 22, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 22, weight: .bold)) .foregroundStyle(color) Text(label) - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(textFaint) } .frame(maxWidth: .infinity) } + private var trimmedParkInput: String { + appState.pota.parkInput.trimmingCharacters(in: .whitespaces) + } + + /// Live QSO count for an activation, derived from the logbook records that + /// fall inside its time window (the same `PotaQsoWindow` scoping the ADIF + /// export uses). Reading `logbook.records` here means Observation re-renders + /// this screen whenever a QSO is logged — no manual increments to miss. + private func activationQsoCount(_ activation: PotaActivationRecord) -> Int { + PotaQsoWindow.qsoCount( + records: appState.logbook.records, + startedAtMs: activation.startedAtMs, + endedAtMs: activation.endedAtMs + ) + } + private func startActivation() { - appState.pota.isActivating = true - appState.pota.activationQsoCount = 0 - appState.pota.activationStartTime = Date() - if !appState.pota.parkRefs.contains(appState.pota.parkInput) { - appState.pota.parkRefs.append(appState.pota.parkInput) - } + let parkRef = trimmedParkInput.uppercased() + guard !parkRef.isEmpty, appState.pota.current == nil else { return } + let record = PotaActivationRecord( + parkRef: parkRef, + notes: appState.pota.notes.trimmingCharacters(in: .whitespaces), + startedAtMs: Int64(Date().timeIntervalSince1970 * 1000) + ) + appState.pota.activations.append(record) + persistActivations() + appState.pota.parkInput = "" + appState.pota.notes = "" } private func endActivation() { - appState.pota.isActivating = false - appState.pota.activationStartTime = nil + guard let current = appState.pota.current, + let idx = appState.pota.activations.firstIndex(where: { $0.id == current.id }) + else { return } + appState.pota.activations[idx].endedAtMs = Int64(Date().timeIntervalSince1970 * 1000) + // Freeze the derived count into the record for the History list. + appState.pota.activations[idx].qsoCount = activationQsoCount(appState.pota.activations[idx]) + persistActivations() } - private func elapsedString(from date: Date?) -> String { - guard let date else { return "0:00" } - let elapsed = Int(Date().timeIntervalSince(date)) + private func elapsedString(startedAtMs: Int64, now: Date) -> String { + let start = Date(timeIntervalSince1970: Double(startedAtMs) / 1000.0) + let elapsed = max(0, Int(now.timeIntervalSince(start))) let hrs = elapsed / 3600 let mins = (elapsed % 3600) / 60 if hrs > 0 { @@ -278,64 +364,185 @@ struct PotaScreen: View { return "\(mins)m" } + // MARK: - ADIF export + + /// Build the per-park ADIF documents for an activation and hand them to the + /// system share sheet. Multi-park ("two-fer") activations share one file per + /// park, each with MY_SIG_INFO pinned to that single park. + private func exportAdif(for activation: PotaActivationRecord) { + let docs = Adif.potaActivationExport( + records: appState.logbook.records, activation: activation) + guard !docs.isEmpty else { + appState.toast.show("No QSOs in this activation yet", icon: "tree") + return + } + var urls: [URL] = [] + for doc in docs { + let url = FileManager.default.temporaryDirectory + .appendingPathComponent(doc.filename) + do { + try Data(doc.content.utf8).write(to: url, options: .atomic) + urls.append(url) + } catch { + appState.toast.show("Export failed", icon: "exclamationmark.triangle") + return + } + } + shareURLs = urls + showShare = true + } + // MARK: - Hunt Tab private var huntTab: some View { - ScrollView { - LazyVStack(spacing: 0) { - ForEach(PotaSpotMock.spots) { spot in - PotaSpotRow(spot: spot) + VStack(spacing: 0) { + // Header: spot count + FT8-only toggle + HStack { + Text(huntHeaderText) + .font(.ft8afUI(size: 12)) + .foregroundStyle(textMuted) + Spacer() + Button { + spotService.ft8Only.toggle() + } label: { + Text("FT8 ONLY") + .font(.ft8afMono(size: 10, weight: .bold)) + .foregroundStyle(spotService.ft8Only ? accent : textFaint) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background( + RoundedRectangle(cornerRadius: 5) + .fill(spotService.ft8Only ? accent.opacity(0.14) : bgSurface2) + .overlay( + RoundedRectangle(cornerRadius: 5) + .strokeBorder( + spotService.ft8Only ? accent.opacity(0.4) : borderSubtle, + lineWidth: 1) + ) + ) + } + .buttonStyle(.plain) + } + .padding(.horizontal, 16) + .padding(.bottom, 8) + + if let error = spotService.lastError { + HStack(spacing: 6) { + Image(systemName: "wifi.exclamationmark") + .font(.ft8afUI(size: 11)) + Text(error) + .font(.ft8afUI(size: 11, weight: .medium)) + Spacer() } + .foregroundStyle(statusWarn) + .padding(.horizontal, 16) + .padding(.vertical, 6) + .background(statusWarn.opacity(0.08)) } - .padding(.bottom, 100) + + if spotService.displaySpots.isEmpty { + VStack(spacing: 12) { + Spacer() + if spotService.isLoading && spotService.lastUpdatedAt == nil { + ProgressView() + .tint(accent) + Text("Loading live spots…") + .font(.ft8afUI(size: 13)) + .foregroundStyle(textMuted) + } else { + Image(systemName: "antenna.radiowaves.left.and.right.slash") + .font(.ft8afUI(size: 40)) + .foregroundStyle(textFaint) + Text("No active spots") + .font(.ft8afUI(size: 16, weight: .medium)) + .foregroundStyle(textMuted) + Text("Live POTA activator spots will appear here") + .font(.ft8afUI(size: 13)) + .foregroundStyle(textFaint) + } + Spacer() + } + } else { + // List (not ScrollView) so pull-to-refresh works natively. + TimelineView(.periodic(from: .now, by: 30)) { context in + List { + ForEach(spotService.displaySpots) { spot in + PotaSpotRow(spot: spot, now: context.date) { + targetSpot(spot) + } + .listRowInsets(EdgeInsets()) + .listRowBackground(bgApp) + .listRowSeparator(.hidden) + } + // Bottom spacer so the TX strip doesn't cover the last row. + Color.clear + .frame(height: 100) + .listRowBackground(bgApp) + .listRowSeparator(.hidden) + } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .refreshable { + await spotService.refresh() + } + } + } + } + } + + private var huntHeaderText: String { + let n = spotService.displaySpots.count + if n == 0 { return spotService.lastUpdatedAt == nil ? "Live POTA spots" : "No active spots" } + return "\(n) active spot\(n == 1 ? "" : "s")" + } + + /// Tap-to-hunt: for an FT8 spot on a known band, switch the app's band the + /// same way the TX strip's band chip does (settings.band + persist — the + /// engine reads the dial from settings), then confirm with a toast. Non-FT8 + /// or out-of-band spots just get an informational toast (the operator tunes + /// manually, as on Android). + private func targetSpot(_ spot: PotaSpot) { + UISelectionFeedbackGenerator().selectionChanged() + let band = PotaSpots.band(forFrequencyKhz: spot.frequencyKhz) + if spot.isFT8, let band { + if appState.settings.band != band { + appState.settings.band = band + SettingsPersistence.save(appState.settings) + } + appState.toast.show( + "Targeting \(spot.activator) · \(band) (\(spot.reference))", + icon: "scope") + } else { + appState.toast.show( + "\(spot.activator) at \(spot.frequencyText) \(spot.mode) — tune manually", + icon: "dot.radiowaves.left.and.right") } } // MARK: - History Tab private var historyTab: some View { - Group { - if appState.pota.parkRefs.isEmpty { + let history = potaHistoryForDisplay(appState.pota.activations) + return Group { + if history.isEmpty { VStack(spacing: 12) { Spacer() Image(systemName: "clock.arrow.circlepath") - .font(.system(size: 40)) + .font(.ft8afUI(size: 40)) .foregroundStyle(textFaint) Text("No activations yet") - .font(.system(size: 16, weight: .medium)) + .font(.ft8afUI(size: 16, weight: .medium)) .foregroundStyle(textMuted) Text("Past POTA activations will appear here") - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textFaint) Spacer() } } else { ScrollView { LazyVStack(spacing: 0) { - ForEach(appState.pota.parkRefs, id: \.self) { ref in - HStack { - Image(systemName: "tree.fill") - .font(.system(size: 12)) - .foregroundStyle(statusConfirmed.opacity(0.6)) - - Text(ref) - .font(.system(size: 14, weight: .bold, design: .monospaced)) - .foregroundStyle(textPrimary) - - Spacer() - - Image(systemName: "chevron.right") - .font(.system(size: 10, weight: .semibold)) - .foregroundStyle(textFaint) - } - .padding(.horizontal, 16) - .padding(.vertical, 12) - .background(bgApp) - .overlay(alignment: .bottom) { - Rectangle() - .fill(borderSubtle) - .frame(height: 1) - } + ForEach(history) { activation in + historyRow(activation) } } .padding(.bottom, 100) @@ -343,66 +550,41 @@ struct PotaScreen: View { } } } -} - -// MARK: - Mock data - -struct PotaSpotMock: Identifiable { - let id = UUID() - let activator: String - let reference: String - let parkName: String - let freqKhz: Int - let mode: String - let spotTime: String - - static let spots: [PotaSpotMock] = [ - PotaSpotMock(activator: "W1AW", reference: "K-0001", parkName: "Acadia NP", freqKhz: 14074, mode: "FT8", spotTime: "12:28"), - PotaSpotMock(activator: "KD2OGR", reference: "K-1234", parkName: "Harriman SP", freqKhz: 7074, mode: "FT8", spotTime: "12:25"), - PotaSpotMock(activator: "N5TIM", reference: "K-0042", parkName: "Big Bend NP", freqKhz: 14074, mode: "FT8", spotTime: "12:22"), - PotaSpotMock(activator: "VE3XYZ", reference: "VE-0456", parkName: "Algonquin PP", freqKhz: 21074, mode: "FT8", spotTime: "12:18"), - PotaSpotMock(activator: "DL4RCK", reference: "DL-0123", parkName: "Black Forest NP", freqKhz: 14074, mode: "FT8", spotTime: "12:15"), - ] -} -private struct PotaSpotRow: View { - let spot: PotaSpotMock + private func historyRow(_ activation: PotaActivationRecord) -> some View { + HStack(spacing: 10) { + Image(systemName: "tree.fill") + .font(.ft8afUI(size: 12)) + .foregroundStyle(statusConfirmed.opacity(0.6)) - var body: some View { - HStack(spacing: 0) { - // Activator + park VStack(alignment: .leading, spacing: 2) { - HStack(spacing: 6) { - Text(spot.activator) - .font(.system(size: 13, weight: .bold, design: .monospaced)) - .foregroundStyle(textPrimary) - Text(spot.reference) - .font(.system(size: 11, weight: .semibold, design: .monospaced)) - .foregroundStyle(statusConfirmed) - .padding(.horizontal, 5) - .padding(.vertical, 1) - .background( - RoundedRectangle(cornerRadius: 3) - .fill(statusConfirmed.opacity(0.14)) - ) - } - Text(spot.parkName) - .font(.system(size: 11)) - .foregroundStyle(textMuted) - .lineLimit(1) + Text(activation.parkRefsDisplay) + .font(.ft8afMono(size: 14, weight: .bold)) + .foregroundStyle(textPrimary) + Text(historyDateText(activation)) + .font(.ft8afUI(size: 11)) + .foregroundStyle(textFaint) } Spacer() - // Frequency + time - VStack(alignment: .trailing, spacing: 2) { - Text("\(spot.freqKhz) kHz") - .font(.system(size: 11, weight: .medium, design: .monospaced)) - .foregroundStyle(textMuted) - Text(spot.spotTime) - .font(.system(size: 10, weight: .medium, design: .monospaced)) - .foregroundStyle(textFaint) + Text("\(activation.qsoCount) QSO\(activation.qsoCount == 1 ? "" : "s")") + .font(.ft8afMono(size: 11, weight: .semibold)) + .foregroundStyle(activation.qsoCount >= 10 ? statusConfirmed : textMuted) + + Button { + exportAdif(for: activation) + } label: { + Image(systemName: "square.and.arrow.up") + .font(.ft8afUI(size: 14, weight: .semibold)) + .foregroundStyle(signal) + .frame(width: 34, height: 34) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(signal.opacity(0.1)) + ) } + .buttonStyle(.plain) } .padding(.horizontal, 16) .padding(.vertical, 10) @@ -413,4 +595,119 @@ private struct PotaSpotRow: View { .frame(height: 1) } } + + private func historyDateText(_ activation: PotaActivationRecord) -> String { + let start = Date(timeIntervalSince1970: Double(activation.startedAtMs) / 1000.0) + return start.formatted(date: .abbreviated, time: .shortened) + } +} + +// MARK: - Spot row + +private struct PotaSpotRow: View { + let spot: PotaSpot + let now: Date + let onTap: () -> Void + + var body: some View { + Button(action: onTap) { + HStack(alignment: .top, spacing: 0) { + // Activator + park + VStack(alignment: .leading, spacing: 2) { + HStack(spacing: 6) { + Text(spot.activator) + .font(.ft8afMono(size: 13, weight: .bold)) + .foregroundStyle(textPrimary) + Text(spot.reference) + .font(.ft8afMono(size: 11, weight: .semibold)) + .foregroundStyle(statusConfirmed) + .padding(.horizontal, 5) + .padding(.vertical, 1) + .background( + RoundedRectangle(cornerRadius: 3) + .fill(statusConfirmed.opacity(0.14)) + ) + if !spot.isFT8 { + Text(spot.mode) + .font(.ft8afMono(size: 10, weight: .semibold)) + .foregroundStyle(textFaint) + .padding(.horizontal, 4) + .padding(.vertical, 1) + .background( + RoundedRectangle(cornerRadius: 3) + .fill(bgSurface3) + ) + } + } + if !parkLine.isEmpty { + Text(parkLine) + .font(.ft8afUI(size: 11)) + .foregroundStyle(textMuted) + .lineLimit(1) + } + if !spot.comments.isEmpty { + Text("\u{201C}\(spot.comments)\u{201D}") + .font(.ft8afUI(size: 11)) + .foregroundStyle(textFaint) + .lineLimit(1) + } + } + + Spacer(minLength: 8) + + // Frequency + age + spotter + VStack(alignment: .trailing, spacing: 2) { + Text(spot.frequencyText) + .font(.ft8afMono(size: 12, weight: .medium)) + .foregroundStyle(accent) + Text(ageText) + .font(.ft8afMono(size: 10, weight: .medium)) + .foregroundStyle(textFaint) + if !spot.spotter.isEmpty { + Text("de \(spot.spotter)") + .font(.ft8afMono(size: 9)) + .foregroundStyle(textFaint) + .lineLimit(1) + } + } + } + .padding(.horizontal, 16) + .padding(.vertical, 10) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .background(bgApp) + .overlay(alignment: .bottom) { + Rectangle() + .fill(borderSubtle) + .frame(height: 1) + } + } + + private var parkLine: String { + var line = spot.parkName + if !spot.locationDesc.isEmpty { + line += line.isEmpty ? spot.locationDesc : " · \(spot.locationDesc)" + } + return line + } + + private var ageText: String { + guard let secs = PotaSpots.ageSeconds(spotTimeUtc: spot.spotTimeUtc, now: now) else { + return "" + } + return relativeAge(secondsAgo: secs) + } +} + +// MARK: - Share sheet + +private struct PotaShareSheet: UIViewControllerRepresentable { + let items: [URL] + + func makeUIViewController(context: Context) -> UIActivityViewController { + UIActivityViewController(activityItems: items, applicationActivities: nil) + } + + func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} } diff --git a/ios/FT8AF/FT8AF/Screens/Settings/AboutScreen.swift b/ios/FT8AF/FT8AF/Screens/Settings/AboutScreen.swift index 7fe9a0fc..d527e587 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/AboutScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/AboutScreen.swift @@ -10,15 +10,15 @@ struct AboutScreen: View { Section { VStack(spacing: 12) { Image(systemName: "antenna.radiowaves.left.and.right") - .font(.system(size: 40)) + .font(.ft8afUI(size: 40)) .foregroundStyle(accent) Text("FT8AF") - .font(.system(size: 22, weight: .bold)) + .font(.ft8afUI(size: 22, weight: .bold)) .foregroundStyle(textPrimary) Text("FT8 for Amateur Radio") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(textMuted) } .frame(maxWidth: .infinity) @@ -50,7 +50,7 @@ struct AboutScreen: View { .foregroundStyle(textPrimary) Spacer() Image(systemName: "chevron.right") - .font(.system(size: 12)) + .font(.ft8afUI(size: 12)) .foregroundStyle(textFaint) } } @@ -74,13 +74,13 @@ struct AboutScreen: View { Section { VStack(alignment: .leading, spacing: 8) { Text("Based on FT8CN by bg7yoz") - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textMuted) Text("FT8 protocol by K9AN & K1JT") - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textMuted) Text("Open source under GPL-3.0") - .font(.system(size: 13)) + .font(.ft8afUI(size: 13)) .foregroundStyle(textFaint) } .padding(.vertical, 4) @@ -105,7 +105,7 @@ struct AboutScreen: View { .foregroundStyle(textPrimary) Spacer() Text(value) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textMuted) } } @@ -143,7 +143,7 @@ private struct DebugLogView: View { NavigationStack { ScrollView { Text(content) - .font(.system(size: 11, design: .monospaced)) + .font(.ft8afMono(size: 11)) .foregroundStyle(textPrimary) .padding(12) .frame(maxWidth: .infinity, alignment: .leading) diff --git a/ios/FT8AF/FT8AF/Screens/Settings/AdvancedSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/AdvancedSettings.swift index 87b0f79e..49d1ccbe 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/AdvancedSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/AdvancedSettings.swift @@ -15,7 +15,7 @@ struct AdvancedSettings: View { Text("PTT Delay") .foregroundStyle(textPrimary) Text("Delay before audio starts after PTT key") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } Spacer() @@ -35,19 +35,19 @@ struct AdvancedSettings: View { Text("TX Delay") .foregroundStyle(textPrimary) Text("Extra delay before TX audio playback") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } Spacer() HStack(spacing: 4) { TextField("0", value: $settings.txDelayMs, format: .number) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textPrimary) .multilineTextAlignment(.trailing) .keyboardType(.numberPad) .frame(width: 60) Text("ms") - .font(.system(size: 12)) + .font(.ft8afUI(size: 12)) .foregroundStyle(textFaint) } } @@ -58,19 +58,19 @@ struct AdvancedSettings: View { Text("Late Start Tolerance") .foregroundStyle(textPrimary) Text("Max ms into cycle before skipping TX") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } Spacer() HStack(spacing: 4) { TextField("2360", value: $settings.lateStartToleranceMs, format: .number) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textPrimary) .multilineTextAlignment(.trailing) .keyboardType(.numberPad) .frame(width: 60) Text("ms") - .font(.system(size: 12)) + .font(.ft8afUI(size: 12)) .foregroundStyle(textFaint) } } @@ -88,7 +88,7 @@ struct AdvancedSettings: View { VStack(alignment: .leading, spacing: 2) { Text("Enable UDP broadcast").foregroundStyle(textPrimary) Text("Send decodes, status & QSOs to companion apps") - .font(.system(size: 11)).foregroundStyle(textFaint) + .font(.ft8afUI(size: 11)).foregroundStyle(textFaint) } } .tint(accent) @@ -97,7 +97,7 @@ struct AdvancedSettings: View { Text("Server host").foregroundStyle(textPrimary) Spacer() TextField("127.0.0.1", text: $settings.udpHost) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textPrimary) .multilineTextAlignment(.trailing) .autocorrectionDisabled() @@ -110,7 +110,7 @@ struct AdvancedSettings: View { Text("Server port").foregroundStyle(textPrimary) Spacer() TextField("2237", value: $settings.udpPort, format: .number) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textPrimary) .multilineTextAlignment(.trailing) .keyboardType(.numberPad) @@ -122,7 +122,7 @@ struct AdvancedSettings: View { VStack(alignment: .leading, spacing: 2) { Text("Accept UDP requests").foregroundStyle(textPrimary) Text("Let companion apps reply / halt / free-text") - .font(.system(size: 11)).foregroundStyle(textFaint) + .font(.ft8afUI(size: 11)).foregroundStyle(textFaint) } } .tint(accent) diff --git a/ios/FT8AF/FT8AF/Screens/Settings/BlockedCallsignsSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/BlockedCallsignsSettings.swift index 2d516009..5b34eeb5 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/BlockedCallsignsSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/BlockedCallsignsSettings.swift @@ -10,7 +10,7 @@ struct BlockedCallsignsSettings: View { Section { HStack(spacing: 8) { TextField("Enter callsign", text: $newCallsign) - .font(.system(size: 14, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .medium)) .foregroundStyle(textPrimary) .textInputAutocapitalization(.characters) .autocorrectionDisabled() @@ -19,7 +19,7 @@ struct BlockedCallsignsSettings: View { addCallsign() } label: { Image(systemName: "plus.circle.fill") - .font(.system(size: 20)) + .font(.ft8afUI(size: 20)) .foregroundStyle(newCallsign.isEmpty ? textFaint : accent) } .buttonStyle(.plain) @@ -39,14 +39,14 @@ struct BlockedCallsignsSettings: View { ForEach(appState.settings.blockedCallsigns, id: \.self) { call in HStack { Text(call) - .font(.system(size: 14, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .bold)) .foregroundStyle(textPrimary) Spacer() Button { removeCallsign(call) } label: { Image(systemName: "xmark.circle") - .font(.system(size: 16)) + .font(.ft8afUI(size: 16)) .foregroundStyle(statusBad) } .buttonStyle(.plain) diff --git a/ios/FT8AF/FT8AF/Screens/Settings/DecodeFilterSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/DecodeFilterSettings.swift index 2e024c9d..2133b233 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/DecodeFilterSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/DecodeFilterSettings.swift @@ -30,7 +30,7 @@ struct DecodeFilterSettings: View { Text("Show Only CQ") .foregroundStyle(textPrimary) Text("Hide QSO traffic, show only CQ calls") - .font(.system(size: 12)) + .font(.ft8afUI(size: 12)) .foregroundStyle(textFaint) } } @@ -41,7 +41,7 @@ struct DecodeFilterSettings: View { Text("DX Only") .foregroundStyle(textPrimary) Text("Hide stations on your own continent") - .font(.system(size: 12)) + .font(.ft8afUI(size: 12)) .foregroundStyle(textFaint) } } @@ -56,7 +56,7 @@ struct DecodeFilterSettings: View { Text("Continent Filter") .foregroundStyle(textPrimary) Text("Show only stations from one continent") - .font(.system(size: 12)) + .font(.ft8afUI(size: 12)) .foregroundStyle(textFaint) } } @@ -102,7 +102,7 @@ struct DecodeFilterSettings: View { Text("Distance Unit") .foregroundStyle(textPrimary) Text("Unit for QSO-path distances in the decode list") - .font(.system(size: 12)) + .font(.ft8afUI(size: 12)) .foregroundStyle(textFaint) } } @@ -116,7 +116,7 @@ struct DecodeFilterSettings: View { .foregroundStyle(textPrimary) Spacer() Text("\(appState.settings.blockedCallsigns.count)") - .font(.system(size: 14, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .medium)) .foregroundStyle(textMuted) } } @@ -153,7 +153,7 @@ struct DecodeFilterSettings: View { Text(title) .foregroundStyle(textPrimary) Text(description) - .font(.system(size: 12)) + .font(.ft8afUI(size: 12)) .foregroundStyle(textFaint) } } diff --git a/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift index 061270e1..80d2546c 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift @@ -144,7 +144,7 @@ struct LoggingSettings: View { .keyboardType(keyboard) } } - .font(.system(.body, design: .monospaced)) + .font(.ft8afMono(size: 17)) .multilineTextAlignment(.trailing) .foregroundStyle(textPrimary) .textInputAutocapitalization(.never) @@ -172,7 +172,7 @@ struct LoggingSettings: View { .tint(accent) } else if let result { Text(result ? "Pass" : "Fail") - .font(.system(size: 14, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .semibold)) .foregroundStyle(result ? statusConfirmed : statusBad) } } diff --git a/ios/FT8AF/FT8AF/Screens/Settings/RadioAudioSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/RadioAudioSettings.swift index 2605bb2c..1bbb6a18 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/RadioAudioSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/RadioAudioSettings.swift @@ -30,7 +30,7 @@ struct RadioAudioSettings: View { .foregroundStyle(textPrimary) Spacer() Text("Built-in Microphone") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(textMuted) } @@ -40,7 +40,7 @@ struct RadioAudioSettings: View { Text("Spectrum Width") .foregroundStyle(textPrimary) Text("Decode bandwidth") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } Spacer() @@ -66,7 +66,7 @@ struct RadioAudioSettings: View { .foregroundStyle(textPrimary) Spacer() Text("Not connected") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(textFaint) } Button { } label: { @@ -94,7 +94,7 @@ struct RadioAudioSettings: View { .fill(statusColor) .frame(width: 8, height: 8) Text(appState.rig.connectionStatus.rawValue) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textMuted) } } @@ -103,7 +103,7 @@ struct RadioAudioSettings: View { .foregroundStyle(textPrimary) Spacer() Text(formatFreq(appState.rig.currentFreqHz)) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textMuted) } } header: { @@ -133,11 +133,11 @@ struct RadioAudioSettings: View { .fill(bandColor(for: band)) .frame(width: 3, height: 18) Text(band) - .font(.system(size: 14, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 14, weight: .medium)) .foregroundStyle(textPrimary) Spacer() Text(bandFrequency(band)) - .font(.system(size: 12, design: .monospaced)) + .font(.ft8afMono(size: 12)) .foregroundStyle(textFaint) } } diff --git a/ios/FT8AF/FT8AF/Screens/Settings/SettingsScreen.swift b/ios/FT8AF/FT8AF/Screens/Settings/SettingsScreen.swift index e269889b..886069e8 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/SettingsScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/SettingsScreen.swift @@ -30,14 +30,14 @@ struct SettingsScreen: View { } label: { HStack { Image(systemName: "antenna.radiowaves.left.and.right") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(accent) .frame(width: 24) Text("Radio & Audio") .foregroundStyle(textPrimary) Spacer() Text(settings.rigModel.rawValue) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textMuted) } } @@ -54,14 +54,14 @@ struct SettingsScreen: View { } label: { HStack { Image(systemName: "bolt.horizontal") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(accent) .frame(width: 24) Text("Transmission") .foregroundStyle(textPrimary) Spacer() Text("\(settings.txPowerWatts)W / \(settings.pttMode.rawValue)") - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textMuted) } } @@ -78,7 +78,7 @@ struct SettingsScreen: View { } label: { HStack { Image(systemName: "line.3.horizontal.decrease.circle") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(accent) .frame(width: 24) Text("Decode Filters") @@ -91,14 +91,14 @@ struct SettingsScreen: View { } label: { HStack { Image(systemName: "hand.raised") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(accent) .frame(width: 24) Text("Blocked Callsigns") .foregroundStyle(textPrimary) Spacer() Text("\(settings.blockedCallsigns.count)") - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textMuted) } } @@ -115,14 +115,14 @@ struct SettingsScreen: View { } label: { HStack { Image(systemName: "icloud.and.arrow.up") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(accent) .frame(width: 24) Text("Online Logging") .foregroundStyle(textPrimary) Spacer() Text(onlineLoggingSummary) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textMuted) } } @@ -130,7 +130,7 @@ struct SettingsScreen: View { Toggle(isOn: $settings.autoLog) { HStack { Image(systemName: "book") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(accent) .frame(width: 24) Text("Auto-log QSOs") @@ -151,7 +151,7 @@ struct SettingsScreen: View { } label: { HStack { Image(systemName: "gearshape.2") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(accent) .frame(width: 24) Text("Advanced") @@ -171,14 +171,14 @@ struct SettingsScreen: View { } label: { HStack { Image(systemName: "info.circle") - .font(.system(size: 14)) + .font(.ft8afUI(size: 14)) .foregroundStyle(accent) .frame(width: 24) Text("About") .foregroundStyle(textPrimary) Spacer() Text(appVersion) - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textFaint) } } @@ -234,24 +234,24 @@ private struct OperatorIdentityCard: View { RoundedRectangle(cornerRadius: 10) .fill(accent.opacity(0.14)) Text(settings.myCall.isEmpty ? "?" : String(settings.myCall.prefix(2))) - .font(.system(size: 16, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 16, weight: .bold)) .foregroundStyle(accent) } .frame(width: 44, height: 44) VStack(alignment: .leading, spacing: 3) { Text(settings.myCall.isEmpty ? "No Callsign" : settings.myCall) - .font(.system(size: 16, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 16, weight: .bold)) .foregroundStyle(settings.myCall.isEmpty ? textFaint : textPrimary) HStack(spacing: 8) { if !settings.myGrid.isEmpty { Label(settings.myGrid, systemImage: "mappin") - .font(.system(size: 11, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .medium)) .foregroundStyle(textMuted) } Label(settings.rigModel.rawValue, systemImage: "antenna.radiowaves.left.and.right") - .font(.system(size: 11, weight: .medium)) + .font(.ft8afUI(size: 11, weight: .medium)) .foregroundStyle(textFaint) } } @@ -259,7 +259,7 @@ private struct OperatorIdentityCard: View { Spacer() Image(systemName: "pencil.circle") - .font(.system(size: 18)) + .font(.ft8afUI(size: 18)) .foregroundStyle(textFaint) } } @@ -282,7 +282,7 @@ private struct OperatorEditSheet: View { .foregroundStyle(textPrimary) Spacer() TextField("e.g. KD2OGR", text: $callsign) - .font(.system(.body, design: .monospaced)) + .font(.ft8afMono(size: 17)) .multilineTextAlignment(.trailing) .foregroundStyle(textPrimary) .textInputAutocapitalization(.characters) @@ -293,7 +293,7 @@ private struct OperatorEditSheet: View { .foregroundStyle(textPrimary) Spacer() TextField("e.g. FN20", text: $grid) - .font(.system(.body, design: .monospaced)) + .font(.ft8afMono(size: 17)) .multilineTextAlignment(.trailing) .foregroundStyle(textPrimary) .textInputAutocapitalization(.characters) diff --git a/ios/FT8AF/FT8AF/Screens/Settings/TransmissionSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/TransmissionSettings.swift index 8c9af797..2f12a830 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/TransmissionSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/TransmissionSettings.swift @@ -14,7 +14,7 @@ struct TransmissionSettings: View { .foregroundStyle(textPrimary) Spacer() TextField("Watts", value: $settings.txPowerWatts, format: .number) - .font(.system(.body, design: .monospaced)) + .font(.ft8afMono(size: 17)) .multilineTextAlignment(.trailing) .foregroundStyle(textPrimary) .keyboardType(.numberPad) @@ -53,7 +53,7 @@ struct TransmissionSettings: View { .foregroundStyle(textPrimary) Spacer() Text("\(settings.txVolume)%") - .font(.system(size: 14, design: .monospaced)) + .font(.ft8afMono(size: 14)) .foregroundStyle(textMuted) } Slider( @@ -93,7 +93,7 @@ struct TransmissionSettings: View { Text("Hunt: Call CQ") .foregroundStyle(textPrimary) Text("Automatically call CQ when hunting") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } } @@ -104,7 +104,7 @@ struct TransmissionSettings: View { Text("Auto-Call Follow") .foregroundStyle(textPrimary) Text("Automatically respond to stations calling you") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } } @@ -115,7 +115,7 @@ struct TransmissionSettings: View { Text("Early Decode") .foregroundStyle(textPrimary) Text("Start decoding before end of RX cycle") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } } @@ -126,7 +126,7 @@ struct TransmissionSettings: View { Text("Auto-CQ After QSO") .foregroundStyle(textPrimary) Text("Resume calling CQ after completing a QSO") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } } @@ -147,7 +147,7 @@ struct TransmissionSettings: View { Text("TX Watchdog") .foregroundStyle(textPrimary) Text("Auto-stop TX after timeout") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } Spacer() @@ -167,7 +167,7 @@ struct TransmissionSettings: View { Text("Stop After") .foregroundStyle(textPrimary) Text("Max CQ attempts before stopping") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } Spacer() @@ -187,7 +187,7 @@ struct TransmissionSettings: View { Text("Tune Timeout") .foregroundStyle(textPrimary) Text("Max carrier time for the TUNE button") - .font(.system(size: 11)) + .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } Spacer() diff --git a/ios/FT8AF/FT8AF/Screens/SplashScreen.swift b/ios/FT8AF/FT8AF/Screens/SplashScreen.swift index 0c9a9c2e..a5832d67 100644 --- a/ios/FT8AF/FT8AF/Screens/SplashScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/SplashScreen.swift @@ -35,7 +35,7 @@ struct SplashScreen: View { // Center icon Image(systemName: "antenna.radiowaves.left.and.right") - .font(.system(size: 36, weight: .semibold)) + .font(.ft8afUI(size: 36, weight: .semibold)) .foregroundStyle(accent) } .scaleEffect(logoScale) @@ -44,11 +44,11 @@ struct SplashScreen: View { // App title VStack(spacing: 6) { Text("FT8AF") - .font(.system(size: 32, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 32, weight: .bold)) .foregroundStyle(textPrimary) Text("Amateur Radio FT8") - .font(.system(size: 14, weight: .medium)) + .font(.ft8afUI(size: 14, weight: .medium)) .foregroundStyle(textMuted) } .opacity(titleOpacity) @@ -62,14 +62,14 @@ struct SplashScreen: View { .frame(width: 200) Text("Initializing...") - .font(.system(size: 11, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 11, weight: .medium)) .foregroundStyle(textFaint) } .opacity(subtitleOpacity) // Version Text(appVersion) - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(textDim) .opacity(subtitleOpacity) .padding(.bottom, 40) diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift index 9a47a011..c887a515 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift @@ -26,7 +26,7 @@ struct FrequencyRuler: View { // Label context.draw( Text("\(tick.hz)") - .font(.system(size: 8, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 8, weight: .medium)) .foregroundStyle(textFaint), at: CGPoint(x: x, y: 14), anchor: .center diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift index 2707a223..a36e73a5 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift @@ -48,7 +48,7 @@ struct SpectrumStrip: View { // Frequency label let label = context.resolve(Text("\(Int(tf)) Hz") - .font(.system(size: 9, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .bold)) .foregroundStyle(textPrimary)) let labelX = min(max(touchX, 30), size.width - 30) context.draw(label, at: CGPoint(x: labelX, y: 8), anchor: .top) diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift index e3112ac6..6afadffa 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift @@ -90,7 +90,7 @@ struct WaterfallCanvas: View { // Period-start time at the left edge, just below the divider. context.draw( Text(label) - .font(.system(size: 9, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .medium)) .foregroundStyle(textFaint), at: CGPoint(x: 3, y: y + 2), anchor: .topLeading @@ -118,7 +118,7 @@ struct WaterfallCanvas: View { guard x > 0, x < size.width else { continue } let label = context.resolve(Text(msg.callFrom) - .font(.system(size: 8, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 8, weight: .bold)) .foregroundStyle(labelColor(msg))) context.draw(label, at: CGPoint(x: x, y: 8), anchor: .top) diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallScreen.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallScreen.swift index 2eda4453..ed92d093 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallScreen.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallScreen.swift @@ -24,12 +24,12 @@ struct WaterfallScreen: View { // Info bar (bottom) HStack { Text(utcString) - .font(.system(size: 12, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 12, weight: .semibold)) .foregroundStyle(textPrimary) // TX frequency display Text("TX \(Int(appState.waterfall.txFreqHz)) Hz") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(accent) .padding(.horizontal, 6) .padding(.vertical, 2) @@ -63,7 +63,7 @@ struct WaterfallScreen: View { .buttonStyle(.plain) Text("\(appState.waterfall.updateCount)") - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .medium)) .foregroundStyle(textDim) LiveIndicator(isLive: appState.waterfall.isLive) @@ -121,7 +121,7 @@ private struct InputLevelMeter: View { var body: some View { HStack(spacing: 4) { Text("RX") - .font(.system(size: 9, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .bold)) .foregroundStyle(textMuted) // Meter bar: RMS fill + peak tick. @@ -140,7 +140,7 @@ private struct InputLevelMeter: View { .clipShape(RoundedRectangle(cornerRadius: 3)) Text(statusText) - .font(.system(size: 9, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 9, weight: .bold)) .foregroundStyle(color) } } @@ -152,7 +152,7 @@ private struct InfoChip: View { var body: some View { Text(label) - .font(.system(size: 10, weight: .semibold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .semibold)) .foregroundStyle(isOn ? accent : textFaint) .padding(.horizontal, 6) .padding(.vertical, 3) @@ -172,7 +172,7 @@ private struct LiveIndicator: View { .fill(isLive ? statusConfirmed : statusBad) .frame(width: 6, height: 6) Text(isLive ? "LIVE" : "OFF") - .font(.system(size: 10, weight: .bold, design: .monospaced)) + .font(.ft8afMono(size: 10, weight: .bold)) .foregroundStyle(isLive ? statusConfirmed : textFaint) } } diff --git a/ios/FT8AF/FT8AF/Theme/Typography.swift b/ios/FT8AF/FT8AF/Theme/Typography.swift new file mode 100644 index 00000000..e8566248 --- /dev/null +++ b/ios/FT8AF/FT8AF/Theme/Typography.swift @@ -0,0 +1,101 @@ +import SwiftUI +import UIKit +import CoreText + +// MARK: - FT8AF typography (parity with Android theme/Type.kt) +// +// Android uses Inter (a single variable TTF whose `wght` axis is pinned per +// FontWeight) for all UI text, and the Geist Mono statics for tabular data +// columns (callsigns, grids, frequencies, SNR, clocks) whose alignment relies +// on fixed advance widths. The OpenType "zero" (slashed zero) feature is +// enabled app-wide so `0` is unambiguous against `O` (Android issue #438). +// +// iOS mirrors that here: +// Font.ft8afUI(size:weight:) → Inter (falls back to SF) +// Font.ft8afMono(size:weight:) → Geist Mono (falls back to SF Mono) +// Both enable the slashed-zero feature on the resolved font. + +enum FT8AFFonts { + /// PostScript names of the Inter variable font's named instances + /// (verified against inter_variable.ttf's fvar table via CoreText). + static func interName(for weight: Font.Weight) -> String { + switch weight { + case .medium: return "Inter-Regular_Medium" + case .semibold: return "Inter-Regular_SemiBold" + case .bold, .heavy, .black: return "Inter-Regular_Bold" + default: return "Inter-Regular" + } + } + + /// PostScript names of the bundled Geist Mono static weights. + static func geistMonoName(for weight: Font.Weight) -> String { + switch weight { + case .medium: return "GeistMono-Medium" + case .semibold: return "GeistMono-SemiBold" + case .bold, .heavy, .black: return "GeistMono-Bold" + default: return "GeistMono-Regular" + } + } + + /// Inter `wght` axis values matching Android's FontVariation.weight pins. + /// Used only if the named instances aren't registered on this OS. + static func interAxisValue(for weight: Font.Weight) -> CGFloat { + switch weight { + case .medium: return 500 + case .semibold: return 600 + case .bold, .heavy, .black: return 700 + default: return 400 + } + } + + /// The OpenType 'wght' variation axis identifier (four-char code). + private static let wghtAxis = 0x7767_6874 + + /// Resolves a bundled font by PostScript name with slashed zero enabled. + static func uiFont(postScriptName: String, size: CGFloat) -> UIFont? { + guard let base = UIFont(name: postScriptName, size: size) else { return nil } + return withSlashedZero(base, size: size) + } + + /// Fallback for OS versions that don't register a variable font's named + /// instances: take the base Inter face and pin the `wght` axis directly. + static func interVariableFallback(size: CGFloat, weight: Font.Weight) -> UIFont? { + guard let base = UIFont(name: "Inter-Regular", size: size) else { return nil } + let variation: [Int: CGFloat] = [wghtAxis: interAxisValue(for: weight)] + let descriptor = base.fontDescriptor.addingAttributes( + [UIFontDescriptor.AttributeName(rawValue: kCTFontVariationAttribute as String): variation] + ) + return withSlashedZero(UIFont(descriptor: descriptor, size: size), size: size) + } + + /// Enables the OpenType `zero` feature (slashed zero) on a font. Uses the + /// CoreText OpenType feature keys, which UIFontDescriptor.featureSettings + /// bridges through unchanged. + private static func withSlashedZero(_ font: UIFont, size: CGFloat) -> UIFont { + let feature: [UIFontDescriptor.FeatureKey: Any] = [ + UIFontDescriptor.FeatureKey(rawValue: kCTFontOpenTypeFeatureTag as String): "zero", + UIFontDescriptor.FeatureKey(rawValue: kCTFontOpenTypeFeatureValue as String): 1, + ] + let descriptor = font.fontDescriptor.addingAttributes([.featureSettings: [feature]]) + return UIFont(descriptor: descriptor, size: size) + } +} + +extension Font { + /// UI text — Inter (Android's UI family), slashed zero, SF fallback. + static func ft8afUI(size: CGFloat, weight: Font.Weight = .regular) -> Font { + if let ui = FT8AFFonts.uiFont(postScriptName: FT8AFFonts.interName(for: weight), size: size) + ?? FT8AFFonts.interVariableFallback(size: size, weight: weight) { + return Font(ui) + } + return .system(size: size, weight: weight) + } + + /// Data columns — Geist Mono (slashed zero by design), SF Mono fallback. + static func ft8afMono(size: CGFloat, weight: Font.Weight = .regular) -> Font { + if let ui = FT8AFFonts.uiFont(postScriptName: FT8AFFonts.geistMonoName(for: weight), size: size) { + return Font(ui) + } + return .system(size: size, weight: weight, design: .monospaced) + } +} diff --git a/ios/FT8AF/project.yml b/ios/FT8AF/project.yml index cd47b8b4..28eafbfc 100644 --- a/ios/FT8AF/project.yml +++ b/ios/FT8AF/project.yml @@ -16,17 +16,39 @@ targets: platform: iOS sources: - FT8AF + # Explicit Info.plist (generated by XcodeGen) instead of Xcode's + # GENERATE_INFOPLIST_FILE + INFOPLIST_KEY_* settings, because UIAppFonts + # has no INFOPLIST_KEY_ equivalent. Every previous INFOPLIST_KEY_* value + # is carried over below. + info: + path: FT8AF/Info.plist + properties: + LSRequiresIPhoneOS: true + NSMicrophoneUsageDescription: "FT8AF needs microphone access to capture radio audio for FT8 decoding" + UIApplicationSceneManifest: + UIApplicationSupportsMultipleScenes: true + UISceneConfigurations: {} + UIApplicationSupportsIndirectInputEvents: true + UILaunchScreen: {} + UISupportedInterfaceOrientations: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + UISupportedInterfaceOrientations~ipad: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationPortraitUpsideDown + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + UIAppFonts: + - inter_variable.ttf + - geist_mono_regular.ttf + - geist_mono_medium.ttf + - geist_mono_semibold.ttf + - geist_mono_bold.ttf settings: base: - GENERATE_INFOPLIST_FILE: YES - INFOPLIST_KEY_UIApplicationSceneManifest_Generation: YES - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: YES - INFOPLIST_KEY_UILaunchScreen_Generation: YES - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad: "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight" - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone: "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight" SWIFT_VERSION: "5.9" PRODUCT_BUNDLE_IDENTIFIER: radio.ks3ckc.ft8af - INFOPLIST_KEY_NSMicrophoneUsageDescription: "FT8AF needs microphone access to capture radio audio for FT8 decoding" dependencies: - package: FT8AFKit product: FT8DSP diff --git a/ios/FT8AFKit/Sources/FT8Engine/PotaActivations.swift b/ios/FT8AFKit/Sources/FT8Engine/PotaActivations.swift new file mode 100644 index 00000000..37b7586f --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/PotaActivations.swift @@ -0,0 +1,113 @@ +import Foundation + +/// One POTA activation session, persisted as JSON by the app's activation store. +/// Mirrors Android's `pota_activation` row (`PotaActivation` model): a park ref +/// (comma-separated for two-fers), a start/end window, and the QSO count frozen +/// at end time. An open activation has `endedAtMs == nil`. +public struct PotaActivationRecord: Codable, Equatable, Identifiable, Sendable { + public var id: String + public var parkRef: String + public var notes: String + public var startedAtMs: Int64 + public var endedAtMs: Int64? + /// QSO count frozen when the activation ends. While active, derive the live + /// count from the logbook via `PotaQsoWindow.qsoCount` instead. + public var qsoCount: Int + + public init( + id: String = UUID().uuidString, + parkRef: String, + notes: String = "", + startedAtMs: Int64, + endedAtMs: Int64? = nil, + qsoCount: Int = 0 + ) { + self.id = id + self.parkRef = parkRef + self.notes = notes + self.startedAtMs = startedAtMs + self.endedAtMs = endedAtMs + self.qsoCount = qsoCount + } + + public var isActive: Bool { endedAtMs == nil } + + /// Individual park references (splits comma-separated `parkRef`), matching + /// Android's multi-park ("two-fer") handling. + public var parkRefs: [String] { + parkRef.split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespaces) } + .filter { !$0.isEmpty } + } + + /// Human-readable display: "K-1234 + K-5678". + public var parkRefsDisplay: String { parkRefs.joined(separator: " + ") } +} + +/// Past activations to list (newest first). Drops any still-active row so the +/// in-progress activation is never shown as "past" — a direct port of Android's +/// `historyForDisplay`. +public func potaHistoryForDisplay(_ all: [PotaActivationRecord]) -> [PotaActivationRecord] { + all.filter { !$0.isActive }.sorted { $0.startedAtMs > $1.startedAtMs } +} + +/// Scopes logged QSOs to a POTA activation's time window. Port of Android's +/// `PotaQsoWindow` (shared by its DAO and ADIF exporter): QSO rows are compared +/// as fixed-width `yyyyMMddHHmmss` UTC stamps built from `qso_date` + a +/// normalized `time_on`, against stamps of the activation's start/end instants. +public enum PotaQsoWindow { + + /// Upper bound for an open (still-active) activation that has no end time. + public static let openEnd = "99991231235959" + + /// Format an epoch-millis instant as the matching `yyyyMMddHHmmss` UTC stamp. + public static func stamp(ms: Int64) -> String { + let fmt = DateFormatter() + fmt.locale = Locale(identifier: "en_US_POSIX") + fmt.timeZone = TimeZone(identifier: "GMT") + fmt.dateFormat = "yyyyMMddHHmmss" + return fmt.string(from: Date(timeIntervalSince1970: Double(ms) / 1000.0)) + } + + /// Fixed-width stamp for one QSO row. `time_on` is stored variable-width: + /// app-logged rows are HHMMSS, but imported ADIF rows may carry HHMM or drop + /// a leading zero ("815" for 08:15). Same normalization as Android's + /// `ROW_STAMP` SQL: pad even-width times to HHMMSS with trailing zeros; + /// prepend a zero to odd-width times (a dropped leading zero) first. + public static func rowStamp(qsoDate: String, timeOn: String) -> String { + let time: String + if timeOn.isEmpty { + time = "000000" + } else if timeOn.count % 2 == 1 { + time = String(("0" + timeOn + "000000").prefix(6)) + } else { + time = String((timeOn + "000000").prefix(6)) + } + return qsoDate + time + } + + /// The records logged inside the activation window, in QSO order. + /// `endedAtMs == nil` (still active) uses the far-future `openEnd` bound. + public static func qsos( + in records: [QsoRecord], startedAtMs: Int64, endedAtMs: Int64? + ) -> [QsoRecord] { + let start = stamp(ms: startedAtMs) + let end = endedAtMs.map { stamp(ms: $0) } ?? openEnd + return records + .filter { r in + let s = rowStamp(qsoDate: r.qsoDate, timeOn: r.timeOn) + return s >= start && s <= end + } + .sorted { + rowStamp(qsoDate: $0.qsoDate, timeOn: $0.timeOn) + < rowStamp(qsoDate: $1.qsoDate, timeOn: $1.timeOn) + } + } + + /// Live QSO count for an activation (drives the 10-QSO progress bar). + public static func qsoCount( + records: [QsoRecord], startedAtMs: Int64, endedAtMs: Int64? + ) -> Int { + qsos(in: records, startedAtMs: startedAtMs, endedAtMs: endedAtMs).count + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/PotaAdif.swift b/ios/FT8AFKit/Sources/FT8Engine/PotaAdif.swift new file mode 100644 index 00000000..54f14aa4 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/PotaAdif.swift @@ -0,0 +1,109 @@ +import Foundation + +/// Per-park POTA ADIF export — a port of Android's +/// `PotaAdifExporter.buildActivationAdif`. New functions only; the existing +/// `Adif.export`/`Adif.parse` used by the general logbook are untouched. +extension Adif { + + /// One park's ADIF document plus the filename it should be shared under. + /// Mirrors Android's `PotaAdifExporter.NamedAdif`. + public struct PotaDocument: Equatable, Sendable { + public let parkRef: String + public let filename: String + public let content: String + + public init(parkRef: String, filename: String, content: String) { + self.parkRef = parkRef + self.filename = filename + self.content = content + } + } + + /// Build one ADIF document per park reference for `activation`, from the + /// QSOs logged inside its time window (same `PotaQsoWindow` scoping the + /// live progress count uses, so the export always agrees with the UI). + /// + /// pota.app's uploader expects each QSO to carry MY_SIG=POTA / + /// MY_SIG_INFO=; for multi-park activations (two-fers) each + /// document contains the same QSOs with MY_SIG_INFO pinned to that single + /// park. Returns [] when no QSOs fall in the window — callers must treat + /// that as "nothing to share" rather than emitting header-only files. + public static func potaActivationExport( + records: [QsoRecord], activation: PotaActivationRecord + ) -> [PotaDocument] { + let rows = PotaQsoWindow.qsos( + in: records, + startedAtMs: activation.startedAtMs, + endedAtMs: activation.endedAtMs + ) + if rows.isEmpty { return [] } + + let ts = potaFilenameStamp(ms: activation.startedAtMs) + return activation.parkRefs.map { parkRef in + var out = "FT8AF POTA Activation \(parkRef)\n" + out += "3.1.4 " + out += "FT8AF " + out += "\n" + for r in rows { + potaField(&out, "CALL", r.call) + potaField(&out, "GRIDSQUARE", r.gridsquare) + potaMode(&out, r.mode) + potaField(&out, "BAND", r.band) + potaField(&out, "FREQ", r.freq) + potaField(&out, "RST_SENT", r.rstSent) + potaField(&out, "RST_RCVD", r.rstRcvd) + potaField(&out, "QSO_DATE", r.qsoDate) + potaField(&out, "TIME_ON", r.timeOn) + potaField(&out, "TIME_OFF", r.timeOff) + potaField(&out, "STATION_CALLSIGN", r.stationCallsign) + potaField(&out, "MY_GRIDSQUARE", r.myGridsquare) + potaField(&out, "MY_SIG", "POTA") + // Pinned to this single park (not the comma-separated activation ref). + potaField(&out, "MY_SIG_INFO", parkRef) + out += "\n" + } + return PotaDocument( + parkRef: parkRef, + filename: "pota-\(parkRef)-\(ts).adi", + content: out + ) + } + } + + /// FT4/FT2 are ADIF *submodes* of MFSK — a bare `FT2` is what + /// pota.app rejects as invalid. For those, emit MODE=MFSK + SUBMODE; every + /// other mode (FT8 is a first-class ADIF mode) passes through unchanged. + /// Port of Android's `AdifFormat.mfskSubmode` + `PotaAdifExporter.adifMode`. + internal static func mfskSubmode(_ mode: String) -> String? { + let normalized = mode.trimmingCharacters(in: .whitespaces).uppercased() + return (normalized == "FT4" || normalized == "FT2") ? normalized : nil + } + + private static func potaMode(_ out: inout String, _ mode: String) { + if let submode = mfskSubmode(mode) { + potaField(&out, "MODE", "MFSK") + potaField(&out, "SUBMODE", submode) + } else { + potaField(&out, "MODE", mode) + } + } + + /// `value ` — length in UTF-8 bytes (not characters), skipping + /// empty values, exactly like Android's `PotaAdifExporter.adifField`. + private static func potaField(_ out: inout String, _ name: String, _ value: String) { + if value.isEmpty { return } + out += "<\(name):\(value.utf8.count)>\(value) " + } + + /// "yyyyMMdd-HHmm" stamp for the export filename. Rendered in UTC so the + /// same activation always produces the same filename regardless of the + /// device timezone (Android uses local time here; the difference is + /// cosmetic — the filename never reaches the POTA parser). + internal static func potaFilenameStamp(ms: Int64) -> String { + let fmt = DateFormatter() + fmt.locale = Locale(identifier: "en_US_POSIX") + fmt.timeZone = TimeZone(identifier: "GMT") + fmt.dateFormat = "yyyyMMdd-HHmm" + return fmt.string(from: Date(timeIntervalSince1970: Double(ms) / 1000.0)) + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/PotaSpots.swift b/ios/FT8AFKit/Sources/FT8Engine/PotaSpots.swift new file mode 100644 index 00000000..61e9382f --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Engine/PotaSpots.swift @@ -0,0 +1,140 @@ +import Foundation + +/// One row from the live POTA spots feed (`https://api.pota.app/spot/activator`). +/// Field set mirrors Android `radio.ks3ckc.ft8af.pota.model.PotaSpot`: the feed's +/// `name` key is the park name, `frequency` is a *string* in kHz. +public struct PotaSpot: Equatable, Identifiable, Sendable { + public var activator: String + public var frequencyKhz: Double + public var mode: String + public var reference: String + public var parkName: String + public var locationDesc: String + public var spotter: String + /// Raw feed timestamp, e.g. "2026-07-13T19:44:50" (UTC, optional fractional + /// seconds). Parsed by `PotaSpots.ageSeconds`. + public var spotTimeUtc: String + public var comments: String + + /// Same identity Android's hunt list keys rows by. + public var id: String { "\(activator)|\(reference)|\(frequencyKhz)" } + + public var frequencyHz: Int64 { Int64(frequencyKhz * 1000) } + + /// Display frequency, matching Android's `"%.1f kHz"`. + public var frequencyText: String { String(format: "%.1f kHz", frequencyKhz) } + + public var isFT8: Bool { mode.uppercased() == "FT8" } + + public init( + activator: String, frequencyKhz: Double, mode: String, reference: String, + parkName: String, locationDesc: String, spotter: String, + spotTimeUtc: String, comments: String + ) { + self.activator = activator + self.frequencyKhz = frequencyKhz + self.mode = mode + self.reference = reference + self.parkName = parkName + self.locationDesc = locationDesc + self.spotter = spotter + self.spotTimeUtc = spotTimeUtc + self.comments = comments + } +} + +/// Pure decode + display logic for the POTA activator-spot feed. Network-free; +/// the app-side `PotaService` owns the URLSession fetch. +public enum PotaSpots { + + /// The public activator-spot feed (same endpoint Android's `PotaClient` polls). + public static let activatorSpotsURLString = "https://api.pota.app/spot/activator" + + /// Decode the feed's JSON array. Tolerant the way Android's `optString` + /// parsing is: missing/null fields become "", `frequency` may be a string + /// ("14074", "14061.0") or a bare number, unparseable frequencies become 0, + /// and the activator callsign is uppercased. Returns nil only when the + /// payload isn't a JSON array (mirroring Android returning null on a parse + /// error); malformed *elements* are skipped rather than failing the batch. + public static func decode(_ data: Data) -> [PotaSpot]? { + guard let root = try? JSONSerialization.jsonObject(with: data), + let arr = root as? [Any] else { return nil } + var out: [PotaSpot] = [] + out.reserveCapacity(arr.count) + for element in arr { + guard let o = element as? [String: Any] else { continue } + out.append(PotaSpot( + activator: str(o["activator"]).uppercased(), + frequencyKhz: khz(o["frequency"]), + mode: str(o["mode"]), + reference: str(o["reference"]), + parkName: str(o["name"]), + locationDesc: str(o["locationDesc"]), + spotter: str(o["spotter"]), + spotTimeUtc: str(o["spotTime"]), + comments: str(o["comments"]) + )) + } + return out + } + + /// Hunt-list ordering + mode constraint: Android fetches with + /// `modeFilter = "FT8"` (case-insensitive) and sorts ascending by frequency. + /// The iOS toggle applies the same filter client-side so flipping it doesn't + /// need a refetch. + public static func forDisplay(_ spots: [PotaSpot], ft8Only: Bool) -> [PotaSpot] { + let filtered = ft8Only ? spots.filter(\.isFT8) : spots + return filtered.sorted { $0.frequencyKhz < $1.frequencyKhz } + } + + /// Seconds elapsed since the spot's UTC timestamp, or nil when the timestamp + /// doesn't parse. Accepts "yyyy-MM-dd'T'HH:mm:ss" with optional fractional + /// seconds / trailing zone designator (only the first 19 chars are read). + /// Negative ages (clock skew) clamp to 0. Feed the result to `relativeAge`. + public static func ageSeconds(spotTimeUtc: String, now: Date) -> Int? { + let trimmed = String(spotTimeUtc.prefix(19)) + guard trimmed.count == 19 else { return nil } + let fmt = DateFormatter() + fmt.locale = Locale(identifier: "en_US_POSIX") + fmt.timeZone = TimeZone(identifier: "UTC") + fmt.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + guard let date = fmt.date(from: trimmed) else { return nil } + return max(0, Int(now.timeIntervalSince(date))) + } + + /// Amateur band (in the app's "20M" naming) containing a spot frequency, or + /// nil when it falls outside the HF/6m allocations the band picker offers. + /// Ranges are the full band allocations, not just the FT8 dial, so a spot + /// anywhere in-band still maps (the app then tunes the band's FT8 dial). + public static func band(forFrequencyKhz khz: Double) -> String? { + switch khz { + case 1_800..<2_000: return "160M" + case 3_500..<4_000: return "80M" + case 5_250..<5_450: return "60M" + case 7_000..<7_300: return "40M" + case 10_100..<10_150: return "30M" + case 14_000..<14_350: return "20M" + case 18_068..<18_168: return "17M" + case 21_000..<21_450: return "15M" + case 24_890..<24_990: return "12M" + case 28_000..<29_700: return "10M" + case 50_000..<54_000: return "6M" + default: return nil + } + } + + // MARK: - Tolerant JSON field extraction + + private static func str(_ any: Any?) -> String { + if let s = any as? String { return s } + if any == nil || any is NSNull { return "" } + if let n = any as? NSNumber { return n.stringValue } + return "" + } + + private static func khz(_ any: Any?) -> Double { + if let s = any as? String { return Double(s) ?? 0 } + if let n = any as? NSNumber { return n.doubleValue } + return 0 + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/PotaActivationsTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/PotaActivationsTests.swift new file mode 100644 index 00000000..204456f2 --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/PotaActivationsTests.swift @@ -0,0 +1,127 @@ +import XCTest +import FT8Engine + +final class PotaActivationsTests: XCTestCase { + + // 2026-07-13 12:00:00 UTC + private let noonMs: Int64 = 1_783_944_000_000 + + private func rec( + call: String, qsoDate: String, timeOn: String + ) -> QsoRecord { + QsoRecord( + call: call, gridsquare: "", mode: "FT8", rstSent: "", rstRcvd: "", + qsoDate: qsoDate, timeOn: timeOn, qsoDateOff: "", timeOff: "", + band: "20M", freq: "14.074", stationCallsign: "", myGridsquare: "", + comment: "") + } + + // MARK: - Stamps + + func testStampFormatsEpochMillisAsUtc() { + XCTAssertEqual(PotaQsoWindow.stamp(ms: noonMs), "20260713120000") + XCTAssertEqual(PotaQsoWindow.stamp(ms: 0), "19700101000000") + } + + func testRowStampNormalizesVariableWidthTimeOn() { + // App-logged HHMMSS passes through. + XCTAssertEqual(PotaQsoWindow.rowStamp(qsoDate: "20260713", timeOn: "121530"), + "20260713121530") + // Imported HHMM pads with trailing zeros. + XCTAssertEqual(PotaQsoWindow.rowStamp(qsoDate: "20260713", timeOn: "1215"), + "20260713121500") + // Dropped-leading-zero odd width ("815" = 08:15) gets the zero back. + XCTAssertEqual(PotaQsoWindow.rowStamp(qsoDate: "20260713", timeOn: "815"), + "20260713081500") + // Empty time -> midnight. + XCTAssertEqual(PotaQsoWindow.rowStamp(qsoDate: "20260713", timeOn: ""), + "20260713000000") + } + + // MARK: - Window scoping + + func testQsosInWindowIncludesOnlyRecordsBetweenStartAndEnd() { + let records = [ + rec(call: "EARLY", qsoDate: "20260713", timeOn: "115959"), + rec(call: "IN1", qsoDate: "20260713", timeOn: "120000"), // == start: included + rec(call: "IN2", qsoDate: "20260713", timeOn: "123000"), + rec(call: "LATE", qsoDate: "20260713", timeOn: "130001"), + ] + let endMs = noonMs + 3_600_000 // 13:00:00Z + let inWindow = PotaQsoWindow.qsos(in: records, startedAtMs: noonMs, endedAtMs: endMs) + XCTAssertEqual(inWindow.map(\.call), ["IN1", "IN2"]) + XCTAssertEqual( + PotaQsoWindow.qsoCount(records: records, startedAtMs: noonMs, endedAtMs: endMs), 2) + } + + func testOpenActivationUsesFarFutureUpperBound() { + let records = [ + rec(call: "OLD", qsoDate: "20260101", timeOn: "080000"), + rec(call: "NOW", qsoDate: "20260713", timeOn: "140000"), + rec(call: "NEXTDAY", qsoDate: "20260714", timeOn: "010000"), + ] + let inWindow = PotaQsoWindow.qsos(in: records, startedAtMs: noonMs, endedAtMs: nil) + XCTAssertEqual(inWindow.map(\.call), ["NOW", "NEXTDAY"]) + } + + func testQsosInWindowSortedByQsoTimeNotInputOrder() { + let records = [ + rec(call: "SECOND", qsoDate: "20260713", timeOn: "125000"), + rec(call: "FIRST", qsoDate: "20260713", timeOn: "121000"), + ] + let inWindow = PotaQsoWindow.qsos(in: records, startedAtMs: noonMs, endedAtMs: nil) + XCTAssertEqual(inWindow.map(\.call), ["FIRST", "SECOND"]) + } + + func testRepeatActivationAtSameParkOnlyCountsItsOwnWindow() { + // The exporter-drift bug Android's PotaQsoWindow exists to prevent: + // two visits to the same park must not lump QSOs together. + let records = [ + rec(call: "VISIT1", qsoDate: "20260701", timeOn: "100000"), + rec(call: "VISIT2", qsoDate: "20260713", timeOn: "123000"), + ] + XCTAssertEqual( + PotaQsoWindow.qsoCount(records: records, startedAtMs: noonMs, endedAtMs: nil), 1) + } + + // MARK: - Activation record + + func testParkRefsSplitsCommaSeparatedMultiPark() { + let twoFer = PotaActivationRecord( + parkRef: "K-1234, K-5678", startedAtMs: noonMs) + XCTAssertEqual(twoFer.parkRefs, ["K-1234", "K-5678"]) + XCTAssertEqual(twoFer.parkRefsDisplay, "K-1234 + K-5678") + // Trailing comma / blanks are dropped. + let messy = PotaActivationRecord(parkRef: "K-1234,,", startedAtMs: noonMs) + XCTAssertEqual(messy.parkRefs, ["K-1234"]) + } + + func testIsActiveTracksEndedAtMs() { + var a = PotaActivationRecord(parkRef: "K-1234", startedAtMs: noonMs) + XCTAssertTrue(a.isActive) + a.endedAtMs = noonMs + 1000 + XCTAssertFalse(a.isActive) + } + + func testCodableRoundTripIncludingOpenEnd() throws { + let a = PotaActivationRecord( + id: "abc", parkRef: "US-7443", notes: "windy", startedAtMs: noonMs, + endedAtMs: nil, qsoCount: 3) + let data = try JSONEncoder().encode([a]) + let back = try JSONDecoder().decode([PotaActivationRecord].self, from: data) + XCTAssertEqual(back, [a]) + XCTAssertTrue(back[0].isActive) + } + + // MARK: - History + + func testHistoryForDisplayDropsActiveAndSortsNewestFirst() { + let active = PotaActivationRecord(parkRef: "K-0001", startedAtMs: noonMs + 500) + let older = PotaActivationRecord( + parkRef: "K-0002", startedAtMs: noonMs - 86_400_000, endedAtMs: noonMs - 86_000_000) + let newer = PotaActivationRecord( + parkRef: "K-0003", startedAtMs: noonMs - 3_600_000, endedAtMs: noonMs - 3_000_000) + let history = potaHistoryForDisplay([older, active, newer]) + XCTAssertEqual(history.map(\.parkRef), ["K-0003", "K-0002"]) + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/PotaAdifTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/PotaAdifTests.swift new file mode 100644 index 00000000..221984c9 --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/PotaAdifTests.swift @@ -0,0 +1,138 @@ +import XCTest +import FT8Engine + +final class PotaAdifTests: XCTestCase { + + // 2026-07-13 12:00:00 UTC + private let noonMs: Int64 = 1_783_944_000_000 + + private func rec( + call: String, grid: String = "FN31", mode: String = "FT8", + qsoDate: String = "20260713", timeOn: String = "123000", + timeOff: String = "123115" + ) -> QsoRecord { + QsoRecord( + call: call, gridsquare: grid, mode: mode, rstSent: "-05", rstRcvd: "-08", + qsoDate: qsoDate, timeOn: timeOn, qsoDateOff: qsoDate, timeOff: timeOff, + band: "20M", freq: "14.074", stationCallsign: "KD2OGR", + myGridsquare: "FN20", comment: "") + } + + private func activation( + parkRef: String = "US-7443", endedAtMs: Int64? = nil + ) -> PotaActivationRecord { + PotaActivationRecord(parkRef: parkRef, startedAtMs: noonMs, endedAtMs: endedAtMs) + } + + // MARK: - Golden document + + func testSingleParkExportMatchesAndroidFieldSetAndOrder() throws { + let docs = Adif.potaActivationExport( + records: [rec(call: "W1AW")], activation: activation()) + XCTAssertEqual(docs.count, 1) + let doc = docs[0] + XCTAssertEqual(doc.parkRef, "US-7443") + XCTAssertEqual(doc.filename, "pota-US-7443-20260713-1200.adi") + XCTAssertEqual(doc.content, """ + FT8AF POTA Activation US-7443 + 3.1.4 FT8AF + W1AW FN31 FT8 20M 14.074 \ + -05 -08 20260713 123000 \ + 123115 KD2OGR FN20 \ + POTA US-7443 + + """) + } + + func testEmptyFieldsAreOmittedNotEmittedZeroLength() { + var r = rec(call: "W1AW") + r.gridsquare = "" + r.timeOff = "" + let docs = Adif.potaActivationExport(records: [r], activation: activation()) + let content = docs[0].content + XCTAssertFalse(content.contains("FN20")) + XCTAssertTrue(content.contains("W1AW")) + } + + func testFieldLengthIsUtf8BytesNotCharacters() { + // "Ω" is 1 character but 2 UTF-8 bytes; a character count would misalign + // every following field for the POTA parser. + var r = rec(call: "W1AW") + r.stationCallsign = "ΩΩ" + let docs = Adif.potaActivationExport(records: [r], activation: activation()) + XCTAssertTrue(docs[0].content.contains("ΩΩ ")) + } + + // MARK: - Mode handling + + func testFt4AndFt2ExportAsMfskSubmodes() { + let docs = Adif.potaActivationExport( + records: [rec(call: "W1AW", mode: "FT4")], activation: activation()) + XCTAssertTrue(docs[0].content.contains("MFSK FT4 ")) + XCTAssertFalse(docs[0].content.contains("FT4")) + + let ft2 = Adif.potaActivationExport( + records: [rec(call: "W1AW", mode: "ft2")], activation: activation()) + XCTAssertTrue(ft2[0].content.contains("MFSK FT2 ")) + } + + func testFt8AndOtherModesPassThroughUnchanged() { + let docs = Adif.potaActivationExport( + records: [rec(call: "W1AW", mode: "FT8")], activation: activation()) + XCTAssertTrue(docs[0].content.contains("FT8 ")) + XCTAssertFalse(docs[0].content.contains("SUBMODE")) + } + + // MARK: - Multi-park (two-fer) + + func testTwoFerProducesOneDocumentPerParkWithPinnedSigInfo() { + let docs = Adif.potaActivationExport( + records: [rec(call: "W1AW"), rec(call: "JA1ABC", timeOn: "124500")], + activation: activation(parkRef: "K-1234,K-5678")) + XCTAssertEqual(docs.map(\.parkRef), ["K-1234", "K-5678"]) + XCTAssertEqual(docs.map(\.filename), + ["pota-K-1234-20260713-1200.adi", "pota-K-5678-20260713-1200.adi"]) + // Same QSOs in both documents, MY_SIG_INFO pinned to each single park. + for doc in docs { + XCTAssertTrue(doc.content.contains("W1AW")) + XCTAssertTrue(doc.content.contains("JA1ABC")) + XCTAssertTrue(doc.content.contains("\(doc.parkRef) ")) + XCTAssertFalse(doc.content.contains("K-1234,K-5678")) + } + } + + // MARK: - Window scoping + empty result + + func testExportScopesToActivationWindow() { + let records = [ + rec(call: "BEFORE", timeOn: "115900"), + rec(call: "DURING", timeOn: "123000"), + rec(call: "AFTER", timeOn: "140100"), + ] + let endMs = noonMs + 3_600_000 // 13:00Z + let docs = Adif.potaActivationExport( + records: records, activation: activation(endedAtMs: endMs)) + let content = docs[0].content + XCTAssertTrue(content.contains("DURING")) + XCTAssertFalse(content.contains("BEFORE")) + XCTAssertFalse(content.contains("AFTER")) + } + + func testNoQsosInWindowReturnsEmptyListNotHeaderOnlyFiles() { + let docs = Adif.potaActivationExport( + records: [rec(call: "OLD", qsoDate: "20250101")], + activation: activation(parkRef: "K-1234,K-5678")) + XCTAssertEqual(docs, []) + } + + func testFilenameStampIsUtcAcrossActivationStartTimes() { + // Epoch start renders as 1970 in UTC regardless of device timezone. + let docs = Adif.potaActivationExport( + records: [rec(call: "W1AW", qsoDate: "19700101", timeOn: "000000")], + activation: PotaActivationRecord(parkRef: "K-0001", startedAtMs: 0)) + XCTAssertEqual(docs[0].filename, "pota-K-0001-19700101-0000.adi") + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/PotaSpotsTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/PotaSpotsTests.swift new file mode 100644 index 00000000..a5656f95 --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8EngineTests/PotaSpotsTests.swift @@ -0,0 +1,162 @@ +import XCTest +import FT8Engine + +final class PotaSpotsTests: XCTestCase { + + /// Canned excerpt of a real `api.pota.app/spot/activator` response + /// (2026-07-13), covering the shapes the decoder must survive: frequency as + /// "14061.0" and as "14074" (no decimal), null parkName, a lowercase-ish + /// activator, an RBN spotter with suffix, and a non-FT8 mode. + private let fixtureJson = """ + [ + {"spotId": 53435147, "activator": "KU5J", "frequency": "14061.0", "mode": "CW", + "reference": "US-7443", "parkName": null, + "spotTime": "2026-07-13T19:44:50", "spotter": "KG1A", "comments": "TnxTom", + "source": "Web", "invalid": null, + "name": "DeGray Lower Lake State Recreation Area", "locationDesc": "US-AR", + "grid4": "EM34", "grid6": "EM34ke", "latitude": 34.1783, "longitude": -93.0978, + "count": 7, "expire": 1}, + {"spotId": 53436877, "activator": "vb7f", "frequency": "18100.0", "mode": "FT8", + "reference": "CA-5540", "parkName": null, + "spotTime": "2026-07-13T20:14:32", "spotter": "G4IRN-#", + "comments": "RBN -18 dB via G4IRN-#", "source": "RBN", "invalid": null, + "name": "Beacon Hill Park", "locationDesc": "CA-BC", + "grid4": "CN88", "grid6": "CN88hj", "latitude": 48.4124, "longitude": -123.365, + "count": 51, "expire": 1783}, + {"spotId": 53435371, "activator": "VE6BR", "frequency": "14074", "mode": "ft8", + "reference": "CA-0632", "parkName": null, + "spotTime": "2026-07-13T19:48:16", "spotter": "VE6BR", "comments": null, + "source": "Web", "invalid": null, + "name": "Beauvais Lake Provincial Park", "locationDesc": "CA-AB", + "grid4": "DN29", "grid6": "DN29wj", "latitude": 49.4109, "longitude": -114.119, + "count": 15, "expire": 207} + ] + """ + + private var fixtureData: Data { Data(fixtureJson.utf8) } + + // MARK: - Decoding + + func testDecodeParsesAllRowsWithFeedFieldMapping() throws { + let spots = try XCTUnwrap(PotaSpots.decode(fixtureData)) + XCTAssertEqual(spots.count, 3) + + let cw = spots[0] + XCTAssertEqual(cw.activator, "KU5J") + XCTAssertEqual(cw.frequencyKhz, 14061.0, accuracy: 0.001) + XCTAssertEqual(cw.mode, "CW") + XCTAssertEqual(cw.reference, "US-7443") + // parkName comes from the feed's "name" key ("parkName" is null). + XCTAssertEqual(cw.parkName, "DeGray Lower Lake State Recreation Area") + XCTAssertEqual(cw.locationDesc, "US-AR") + XCTAssertEqual(cw.spotter, "KG1A") + XCTAssertEqual(cw.spotTimeUtc, "2026-07-13T19:44:50") + XCTAssertEqual(cw.comments, "TnxTom") + } + + func testDecodeUppercasesActivatorAndParsesIntegerStringFrequency() throws { + let spots = try XCTUnwrap(PotaSpots.decode(fixtureData)) + XCTAssertEqual(spots[1].activator, "VB7F") + // "14074" (no decimal point) still parses. + XCTAssertEqual(spots[2].frequencyKhz, 14074.0, accuracy: 0.001) + // null comments -> "". + XCTAssertEqual(spots[2].comments, "") + } + + func testDecodeReturnsNilForNonArrayPayload() { + XCTAssertNil(PotaSpots.decode(Data("{\"oops\": true}".utf8))) + XCTAssertNil(PotaSpots.decode(Data("not json".utf8))) + } + + func testDecodeSkipsMalformedElementsInsteadOfFailingBatch() throws { + let json = """ + [ 42, {"activator": "W1AW", "frequency": "7074", "mode": "FT8", + "reference": "US-0001"} ] + """ + let spots = try XCTUnwrap(PotaSpots.decode(Data(json.utf8))) + XCTAssertEqual(spots.count, 1) + XCTAssertEqual(spots[0].activator, "W1AW") + // Missing fields become "" (Android optString behavior). + XCTAssertEqual(spots[0].parkName, "") + XCTAssertEqual(spots[0].spotTimeUtc, "") + } + + func testDecodeUnparseableFrequencyBecomesZero() throws { + let json = """ + [{"activator": "W1AW", "frequency": "QRP", "mode": "FT8", "reference": "US-0001"}] + """ + let spots = try XCTUnwrap(PotaSpots.decode(Data(json.utf8))) + XCTAssertEqual(spots[0].frequencyKhz, 0) + } + + // MARK: - Display filter + sort + + func testForDisplayFT8OnlyFiltersCaseInsensitivelyAndSortsByFrequency() throws { + let spots = try XCTUnwrap(PotaSpots.decode(fixtureData)) + let shown = PotaSpots.forDisplay(spots, ft8Only: true) + // CW row dropped; "ft8" (lowercase mode) kept; ascending frequency. + XCTAssertEqual(shown.map(\.activator), ["VE6BR", "VB7F"]) + XCTAssertEqual(shown.map(\.frequencyKhz), [14074.0, 18100.0]) + } + + func testForDisplayAllModesKeepsEverythingSorted() throws { + let spots = try XCTUnwrap(PotaSpots.decode(fixtureData)) + let shown = PotaSpots.forDisplay(spots, ft8Only: false) + XCTAssertEqual(shown.map(\.activator), ["KU5J", "VE6BR", "VB7F"]) + } + + // MARK: - Formatting + + func testFrequencyTextMatchesAndroidFormat() { + let spot = PotaSpot( + activator: "W1AW", frequencyKhz: 14074, mode: "FT8", reference: "US-0001", + parkName: "", locationDesc: "", spotter: "", spotTimeUtc: "", comments: "") + XCTAssertEqual(spot.frequencyText, "14074.0 kHz") + XCTAssertEqual(spot.frequencyHz, 14_074_000) + } + + func testAgeSecondsParsesUtcTimestampWithAndWithoutFraction() { + let now = date(iso: "2026-07-13T20:15:00") + XCTAssertEqual(PotaSpots.ageSeconds(spotTimeUtc: "2026-07-13T20:14:32", now: now), 28) + // Fractional seconds (as POTA sometimes emits) are ignored. + XCTAssertEqual( + PotaSpots.ageSeconds(spotTimeUtc: "2026-07-13T20:14:32.2233333", now: now), 28) + // Future timestamp (clock skew) clamps to 0. + XCTAssertEqual(PotaSpots.ageSeconds(spotTimeUtc: "2026-07-13T20:16:00", now: now), 0) + } + + func testAgeSecondsReturnsNilForGarbage() { + let now = Date() + XCTAssertNil(PotaSpots.ageSeconds(spotTimeUtc: "", now: now)) + XCTAssertNil(PotaSpots.ageSeconds(spotTimeUtc: "yesterday", now: now)) + XCTAssertNil(PotaSpots.ageSeconds(spotTimeUtc: "2026-07-13", now: now)) + } + + // MARK: - Band mapping + + func testBandForFrequencyMapsFT8DialsAndInBandSpots() { + XCTAssertEqual(PotaSpots.band(forFrequencyKhz: 14074), "20M") + XCTAssertEqual(PotaSpots.band(forFrequencyKhz: 7074), "40M") + XCTAssertEqual(PotaSpots.band(forFrequencyKhz: 18100), "17M") + XCTAssertEqual(PotaSpots.band(forFrequencyKhz: 50313), "6M") + // Anywhere in-band maps, not just the FT8 dial. + XCTAssertEqual(PotaSpots.band(forFrequencyKhz: 14061.0), "20M") + XCTAssertEqual(PotaSpots.band(forFrequencyKhz: 28320), "10M") + } + + func testBandForFrequencyReturnsNilOutsideAllocations() { + XCTAssertNil(PotaSpots.band(forFrequencyKhz: 0)) + XCTAssertNil(PotaSpots.band(forFrequencyKhz: 6000)) // between 60m and 40m + XCTAssertNil(PotaSpots.band(forFrequencyKhz: 144_174)) // 2m: not in the picker + } + + // MARK: - Helpers + + private func date(iso: String) -> Date { + let fmt = DateFormatter() + fmt.locale = Locale(identifier: "en_US_POSIX") + fmt.timeZone = TimeZone(identifier: "UTC") + fmt.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + return fmt.date(from: iso)! + } +} From b11a660000fa7003e3e6ef1721a5d23b959ea447 Mon Sep 17 00:00:00 2001 From: Patrick Burns Date: Wed, 15 Jul 2026 07:53:01 -0500 Subject: [PATCH 5/5] iOS parity: review fixes, audio device pickers, TX/RX engine resilience Code-review fixes (all 10 verified findings): - Supervisor stops (watchdog / stop-after-N) now disarm hunt so hunt-calls-CQ cannot silently restart a stopped TX run - Late-start clip slack locked to the physical 2360 ms constant; the late-start tolerance setting is now the skip threshold it describes - stop-after-N counts only transmissions actually handed to the player - forceLog no longer double-logs during the courtesy-73 window - Restored NSLocalNetworkUsageDescription lost in the Info.plist migration - Waterfall overlays/tap-to-tune map against the drawn data span (waterfall.displayMaxHz), not the live setting - PSK Reporter: no sends after opt-out, flush-on-stop, no stranded queue, datagrams capped at 1400 bytes including padding - Spectrum width default 3500 Hz (matches the previous fixed span) Audio device selection (parity with Android's input/output pickers): - Input Device picker backed by AVAudioSession availableInputs, preference persisted by port name and re-applied on replug - Output Device row with the system route picker (AVRoutePickerView) TX/RX audio-engine resilience (found live-testing in the simulator): - TxPlayerService restarts a stopped shared engine before keying (silent-TX) - AudioCaptureService handles AVAudioEngineConfigurationChange by rebuilding the mic tap at the new hardware format and restarting (silent-RX) Kit tests 350/350; simulator build green; CQ TX audio and live FT8 decode verified end-to-end in the simulator. Co-Authored-By: Claude Fable 5 --- .claude/tasks/session-current.md | 217 ------------------ ios/FT8AF/FT8AF.xcodeproj/project.pbxproj | 8 + ios/FT8AF/FT8AF/AppState.swift | 11 +- .../Components/AudioRoutePickerButton.swift | 17 ++ .../FT8AF/Engine/AudioCaptureService.swift | 56 ++++- .../FT8AF/Engine/AudioRouteController.swift | 95 ++++++++ ios/FT8AF/FT8AF/Engine/LiveEngine.swift | 38 ++- ios/FT8AF/FT8AF/Engine/OnlineLogService.swift | 27 ++- ios/FT8AF/FT8AF/Engine/TxPlayerService.swift | 14 ++ ios/FT8AF/FT8AF/Info.plist | 2 + .../Screens/Settings/AdvancedSettings.swift | 4 +- .../Screens/Settings/LoggingSettings.swift | 9 +- .../Screens/Settings/RadioAudioSettings.swift | 42 +++- .../Screens/Waterfall/FrequencyRuler.swift | 8 +- .../Screens/Waterfall/SpectrumStrip.swift | 10 +- .../Screens/Waterfall/WaterfallCanvas.swift | 8 +- ios/FT8AF/project.yml | 1 + .../Sources/FT8Audio/AudioPortLabel.swift | 51 ++++ .../Sources/FT8Engine/PskReporter.swift | 21 +- .../Sources/FT8Engine/QsoEngine.swift | 15 +- ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift | 14 ++ .../FT8AudioTests/AudioPortLabelTests.swift | 52 +++++ .../FT8EngineTests/PskReporterTests.swift | 80 +++++++ .../Tests/FT8EngineTests/QsoEngineTests.swift | 63 +++++ .../Tests/FT8EngineTests/TxTimingTests.swift | 31 +++ 25 files changed, 635 insertions(+), 259 deletions(-) delete mode 100644 .claude/tasks/session-current.md create mode 100644 ios/FT8AF/FT8AF/Components/AudioRoutePickerButton.swift create mode 100644 ios/FT8AF/FT8AF/Engine/AudioRouteController.swift create mode 100644 ios/FT8AFKit/Sources/FT8Audio/AudioPortLabel.swift create mode 100644 ios/FT8AFKit/Tests/FT8AudioTests/AudioPortLabelTests.swift diff --git a/.claude/tasks/session-current.md b/.claude/tasks/session-current.md deleted file mode 100644 index 66e8b35d..00000000 --- a/.claude/tasks/session-current.md +++ /dev/null @@ -1,217 +0,0 @@ -# Session — iOS parity with Android (features + style) - -## Session Overview - -**User Request**: "go ahead and get the ios app in parity in both style and features as the android app" -**Workflow Mode**: Development -**Success Criteria**: iOS app matches Android feature set and visual style for everything feasible without rig hardware; app builds for simulator; `swift test` green; every new code path unit-tested; PR to `dev`. -**Branch**: `feat/ios-parity` (worktree `/Users/patrickburns/Projects/FT8AF-ios-parity`) - -## Strategic Analysis - -**Complexity**: Complex, multi-phase. -**Research**: Full Android + iOS inventories completed (Explore agents). Key gaps: - -- Settings persisted but inert on iOS: spectrum width, decode filters (show-only-CQ, DX-only), TX watchdog / stop-after-N, auto-sequence toggles, PTT/TX delay + late-start tolerance, TX power. -- Missing operating features: caller queue, ActiveQsoPanel TX-message selector chips + LOG/clear actions + header states, TUNE button, CQ options (free text / modifiers), waterfall UTC period timestamps, input-level indicator, DecodeRow status pills / distance / ago / location styling. -- Missing integrations: Cloudlog upload, QRZ logbook upload, PSKReporter sender, POTA real spot feed + activation QSO counting. -- Style: iOS uses SF fonts (Android: Inter + Geist Mono, bundled in `ft8af/app/src/main/res/font/`), app icon missing (placeholder Contents.json), color palette already matches Android dark palette hex-for-hex. - -**Deferred (out of scope this PR, documented in PR body)**: rig CAT transport (no serial on iOS; BLE bridge is its own project — ALC/SWR/tune-via-ATU depend on it), FT4/FT2 mode switching, Hound/Field Day modes, GPS grid + time discipline, needed-DX local notifications, light theme, localization catalogs (16 languages), CarPlay, PSK Reporter map overlay, POTA OAuth self-spot/upload. - -**Build facts**: XcodeGen (`ios/FT8AF/project.yml`, sources glob → run `xcodegen generate` after adding files). Baseline green: app builds (iPhone 16 Pro sim), 129 kit tests pass. Fonts require Info.plist `UIAppFonts` → use project.yml `info:` properties. - -## Task Breakdown - -### Phase 0: Shared plumbing (Central AI) -- [x] Worktree + session file -- [ ] Pre-stage `SettingsState` fields + persistence for all new settings (Cloudlog, QRZ key, PSKReporter, distance unit, etc.) so parallel agents don't collide on AppState.swift - -### Batch 1 (parallel, disjoint files) -- [ ] **Agent D — Logging integrations**: Cloudlog + QRZ logbook upload + PSKReporter sender (pure request-builders in FT8AFKit/FT8Engine + tests; thin app service; LoggingSettings screen; sync chips + catch-up sync in Logbook) -- [ ] **Agent W — Waterfall parity**: spectrum width wired end-to-end, UTC timestamps at period boundaries, input-level indicator (kit geometry + tests; owns LiveEngine waterfall loop + Waterfall screen files) - -### Batch 2 (parallel, disjoint files) -- [ ] **Agent B — Engine/TX parity**: watchdog, stop-after-N, auto-seq toggles wired, late-start clip rule `max(0, msIntoCycle - slack)`, TUNE carrier, caller queue, stage-selector plumbing; ActiveQsoPanel + TxStrip UI upgrades (owns LiveEngine, QsoEngine, Components/) -- [ ] **Agent R — Decode UI parity**: DecodeRow Android-style (accent bar, pills, SNR bar, distance, ago, location), filters wired (show-only-CQ, DX-only, continent), highlights vs logbook (new grid/band/worked, DXCC prefix heuristic) — extracted pure logic + tests (owns Screens/Decode, Util/) - -### Batch 3 (parallel) -- [ ] **Agent E — POTA real data**: live spots from api.pota.app, activation QSO count wired to log, per-park ADIF export -- [ ] **Agent S — Style**: bundle Inter + Geist Mono, typography adoption (UI=Inter, data=Geist Mono), app icon from play_store_icon.png, project.yml info properties - -### Phase 4: Verification & ship (Central AI) -- [ ] `swift test` (kit) green; xcodebuild simulator build green; app smoke-run in simulator -- [ ] Code review pass; commit; PR to `dev` - -## Agent Work Sections - -(appended by agents as they complete) - -### Agent W — Waterfall parity - -**Status: COMPLETE** — kit tests green (209/209), simulator build green. - -**1. Configurable spectrum width, end-to-end (display-only; decoder untouched)** -- `FT8Audio/WaterfallAxis.swift`: fully parametric — every mapping (`fraction`, `clampedFraction`, `hz(forFraction:)`, `tunedTxHz`) now takes `displayMaxHz`. New `maxTxHz(displayMaxHz:)` implements the Android TX clamp `100...min(3000, width-100)` (per task directive "mirror Android"; note this lowers the old 200 Hz floor to Android's 100 — flag if the coordinator wants 200 kept). New `rulerTicks(displayMaxHz:)` + `RulerTick` mirror Android commit 647b12e8 (labels at true `hz/width` fractions, top tick below the far edge for non-500-multiples, degenerate width → single 0 tick). -- `FT8Audio/WaterfallRowBuilder.swift`: `maxHz` → `defaultMaxHz` (3500); `columns(sampleRate:maxHz:)` takes the width (defaulted, so existing callers/tests unchanged). -- `LiveEngine.runWaterfallLoop` reads `settings.spectrumWidthHz` on the main actor **every tick** (new `readSpectrumWidthHz` closure) → live application; column count recomputed per tick. On a width change the `applyWaterfall` closure clears `rows`/`rowTimestamps` (old-width history can't be drawn against the new axis) and updates `WaterfallState.displayMaxHz`. -- Views (`FrequencyRuler`, `SpectrumStrip`, `WaterfallCanvas`) map overlays/tap-to-tune against `Float(appState.settings.spectrumWidthHz)` so the setting applies instantly. - -**2. UTC timestamps at FT8 period boundaries** -- New `FT8Audio/WaterfallTimestampGate.swift`: pure port of Android's `WaterfallTimestampGate` — `shouldDraw(utcMs:slotMs:)` once-per-slot gate (incl. mode-change re-baseline rule), `slotPeriod`, `slotStartMs`, and `utcLabel(forUtcMs:)` ("HH:mm:ss", day-independent, Euclidean-safe). -- Waterfall loop stamps the **period-start** label on the first row of each 15 s slot; `WaterfallState.rowTimestamps: [String?]` kept in lockstep with `rows` (append/trim/clear together). -- `WaterfallCanvas.drawPeriodTimestamps`: subtle full-width divider (`textFaint` 40 %, 0.5 pt) + HH:mm:ss monospaced 9 pt `textFaint` label at the left edge. - -**3. Input level indicator** -- New `FT8Audio/AudioInputLevel.swift`: pure port of Android's `AudioInputLevel` classifier — identical thresholds (clip ≥0.985 linear, high ≥-3 dBFS peak, silent <-75 dBFS RMS, low <-45 dBFS RMS, -120 dBFS floor, NaN/∞ sanitized), `measure(_:)`, `fromPeakRms(peak:rms:)`, `meterFraction(_:)`. -- Waterfall loop meters the most recent ~250 ms window each tick → `WaterfallState.inputPeak/.inputRms` (plain Floats — AppState doesn't import FT8Audio). -- `WaterfallScreen`: new `InputLevelMeter` in the bottom info bar — "RX" label, 44 pt bar (RMS fill + peak tick) and status word; colors silent/low→`textFaint`, good→`statusConfirmed`, high→`statusWarn`, clipping→`statusBad`. - -**Files touched** (all within ownership): `ios/FT8AFKit/Sources/FT8Audio/{WaterfallAxis,WaterfallRowBuilder,WaterfallTimestampGate*,AudioInputLevel*}.swift`, `ios/FT8AFKit/Tests/FT8AudioTests/{WaterfallAxisTests,WaterfallTimestampGateTests*,AudioInputLevelTests*}.swift` (* = new), `ios/FT8AF/FT8AF/Engine/LiveEngine.swift` (waterfall loop + closures + new `WaterfallUpdate` struct only), `ios/FT8AF/FT8AF/Screens/Waterfall/*` (all four), `AppState.swift` (WaterfallState body only: `rowTimestamps`, `displayMaxHz`, `inputPeak`, `inputRms`). `AudioCaptureService.swift` unchanged (metering lives in the waterfall loop). - -**Tests**: `WaterfallAxisTests` (rewritten, 17 tests — width-parametric mapping, TX clamp per width, ruler ticks incl. 2750 non-multiple + degenerate cases), `WaterfallTimestampGateTests` (11 — once-per-slot over polled frames, reset, mode-change, label format, period-start-not-frame-time), `AudioInputLevelTests` (13 — all five statuses, threshold edges, NaN sanitizing, meter fraction). `swift test`: 209/209 pass. - -**Coordinator notes**: -- Ran `xcodegen generate` to get a green app build — Agent D's new `LoggingSettings.swift`/`OnlineLogService.swift` existed on disk but weren't in the project yet (build failed on their `SettingsScreen` reference, not my files). Harmless for them; mentioning so nobody is surprised the pbxproj changed. -- TX tune floor changed 200→100 Hz to mirror Android's `100..width-100` clamp per the task brief; trivial to revert to 200 if desired (single constant `WaterfallAxis.minTxHz` + 2 test expectations). - -## Session Metrics - -Tasks total: 10 · Completed: 1 - -### Agent D — Logging integrations - -**Status: COMPLETE** — kit tests 209/209 green (`swift test`), simulator build green (`xcodebuild` iPhone 16 Pro), `xcodegen generate` run after adding app files. Not committed (coordinator commits). - -**What was built** (parity with Android `ThirdPartyService`, `PskReporterSender`, `ui/settings/LoggingSettings`): - -Kit — pure, network-free protocol builders in `ios/FT8AFKit/Sources/FT8Engine/`: -- `CloudlogClient.swift` — `api/qso` POST request builder (JSON key order byte-identical to Android's JSONStringer; endpoint slash-less to dodge Wavelog/Nextlog 308s), `api/auth/` test URL, whitespace-tolerant `Valid` + `rw` parser, 200/201 success rule. -- `QrzLogbookClient.swift` — logbook.qrz.com form bodies (INSERT/STATUS) with Java-URLEncoder-compatible encoding, `RESULT=` parser, OK/REPLACE=success for INSERT, OK-only for STATUS. -- `PskReporter.swift` — full IPFIX byte-layout port of PskReporterSender: 16-byte header, options template 0x50E2 (5 enterprise-30351 var fields), data template 0x50E3 (6 enterprise fields + standard flowStartSeconds 0x0096), receiver/sender data sets, var-string encoding, 1400-byte MTU splitting, templates on first 3 packets + hourly refresh; `PskDedup` (5-min per call|bandMHz window); `PskReporter.makeSpot` policy (skip self/`<...>`/free-text i3=0&n3=0, strip hash brackets, dial+audio-offset RF freq, ≥4-char locator). Time injected → fully deterministic tests. -- `OnlineLogAdif.swift` — `Adif.singleRecord()`: Android QSLRecordToADIF field order, no ``/QSL flags, freq passed through as MHz. -- `OnlineLogSync.swift` — unsynced-record selection mirroring Android `unsyncedFilter`/`countUnsyncedQSOs`. -- `QsoRecord.swift` — added `syncedCloudlog`/`syncedQrz` Bools; custom `init(from:)` with `decodeIfPresent` defaults so pre-existing `qso_log.json` files still decode (proven by test); memberwise init defaults keep QsoEngine source-compatible. - -Kit tests (45 new) in `Tests/FT8EngineTests/`: `CloudlogClientTests`, `QrzLogbookClientTests`, `PskReporterTests` (golden-byte template/record assertions, MTU split, template cadence, dedup, spot policy), `OnlineLogSyncTests` (incl. legacy-JSON backward-compat decode + single-record ADIF goldens). - -App — `ios/FT8AF/FT8AF/`: -- `Engine/OnlineLogService.swift` — `@Observable @MainActor` singleton; URLSession transport (upload on QSO logged, retry-once, marks synced flags + persists; catch-up `syncAll` with `isSyncing/syncDone/syncTotal` progress; Cloudlog/QRZ test-connection); PSK Reporter NWConnection UDP to report.pskreporter.info:4739 with ~5-min batched flush. -- `Screens/Settings/LoggingSettings.swift` — new screen: Cloudlog (URL/API key/Station ID/enable/Test Connection with spinner + Pass/Fail + toast), QRZ Logbook (key/enable/Test), PSK Reporter toggle; persists via SettingsPersistence. -- `SettingsScreen.swift` — one nav row "Online Logging" (+ enabled-services summary) added to the existing Logging section. -- `Screens/Logbook/LogbookScreen.swift` — CL/QRZ sync chips on rows (signal cyan / confirmed green when synced, shown only when the service is enabled), toolbar cloud-upload button running catch-up sync with progress counter + completion toast. -- `Engine/LiveEngine.swift` — ONLY the two permitted touch points: decode-completion hands decodes to `OnlineLogService.shared.enqueuePskDecodes` (dial = band map + audio offset), QSO-completion calls `handleQsoLogged` after `broadcastUdpQso`. - -**Coordinator notes:** -- AppState.swift untouched (pre-staged fields used as-is). QsoLogStore untouched — Codable back-compat handled inside QsoRecord. -- PSK spots queued while capture runs are flushed by the service's own 5-min timer even if the engine stops; `flushPsk` is also callable on demand if a stop-flush is ever wanted. -- QRZ REPLACE responses count as success (record already on QRZ), matching Android. - -### Agent R — Decode UI parity - -**Status: COMPLETE** — kit tests 256/256 green (`swift test`; 209 baseline + 47 new), simulator build green (`xcodebuild` iPhone 16 Pro), `xcodegen generate` re-run. Not committed (coordinator commits). - -**What was built** (parity with Android `ui/decode/DecodeRow.kt`, `resolveQsoStatus`, `StatusPill.kt`, `SignalBar.kt`, `ui/settings/DecodeFilterSettings.kt`, `filterMessages` in `DecodeScreen.kt`): - -Kit — pure, host-tested logic in `ios/FT8AFKit/Sources/FT8Engine/`: -- `DxccPrefix.swift` — compact prefix→DXCC-entity heuristic (~200 prefixes / ~130 entities incl. all task spot-checks W/K/N/A→USA, JA→Japan, DL→Germany, G/M/2E→England + GM/GW/GI regions, UA/R→Russia with 8/9/0-area Asiatic split, VK, PY, EA (+EA8 Canary AF), OH, ZL, VE…), longest-prefix match over `/`-separated segments (EA8/W1AW→Canary Is, W1AW/P→USA), continent codes NA/SA/EU/AF/AS/OC/AN. -- `DecodeAnnotator.swift` — `DecodeHighlight` enum, `HighlightToggles`, `LoggedQso`/`LogbookIndex` (O(1) worked calls / grid4s / bands-per-call / DXCC entities), `classifyDecode` with Android's priority PENDING > POTA > NEW DXCC > NEW GRID > NEW BAND > WORKED > CQ, each logbook category gated by its toggle (disabled → falls through). Helpers `isCQMessage`, `isDirectedToMe`, `isPotaCq`, `looksLikeParkRef`. RR73/short grids never count as new grids (reuses `gridToLatLon` validity). -- `DisplayFormatters.swift` — `relativeAge` (now/Ns/Nm/Nh/Nd buckets, clamps negative), `gridDistanceKm` (haversine over `gridToLatLon`), `kilometersToMiles`, `formatQsoDistance` (whole km/mi, empty when it rounds to 0), `gridDistanceText`. -- Tests (47 new): `DxccPrefixTests` (14), `DecodeAnnotatorTests` (21 — priority order, all four toggle fall-throughs, park-ref shape, grid validity, index normalization), `DisplayFormattersTests` (12 — age buckets/edges, FN31→JO31 ≈5900 km, unit conversion, zero suppression). - -App — `ios/FT8AF/FT8AF/`: -- `Screens/Decode/DecodeRow.swift` — full Android-style rewrite: 3pt left accent bar (target pink > CQ amber), CALLING/CQ/TO YOU label chips (stack like Android), bold mono 15pt callsign (cyan to-me / pink target), grid, status pill right; message-text line; meta row = 5-segment SignalBar (Android thresholds −5/−12/−18, fill (snr+25)/30) + SNR dB + freq Hz + distance (km/mi per setting) + relative age; DXCC-entity location line with globe icon (USA/UK shorthand); backgrounds to-me signal 0.08/0.22, target TargetSoft/Border, CQ bgSurface/borderSubtle; third-party-QSO rows dimmed to 0.8; compact variant kept (denser paddings, 22pt bar, no msg/location lines). Status pill = 4pt-corner chip, dot + label, hue 0.12 bg / 0.28 border: pending amber, POTA green, NEW DXCC purple, NEW GRID yellow, NEW BAND/WORKED cyan, CQ amber. -- `Screens/Decode/DecodeScreen.swift` — base filter stage ANDs with chips like Android `filterMessages`: blocklist → showOnlyCQ → dxOnly (my continent from my call prefix; unknown continents excluded, matching Android) → continentFilter ≠ "All". Chips now share kit logic (`isCQMessage`, `isPotaCq`, New DXCC via `LogbookIndex.workedEntities`, Needed via worked calls, For Me via `isDirectedToMe`). Rows get `highlight` from `classifyDecode`, `isTarget` from `tx.targetCall`, live `now` from a 1s `TimelineView` (drives the age labels without per-row timers). -- `Screens/Settings/DecodeFilterSettings.swift` — "Phase 4" placeholder removed; real controls: Show-Only-CQ + DX-Only toggles, continent picker (All/NA/SA/EU/AF/AS/OC/AN), four highlight toggles (NewDxcc/NewGrid/NewBand/Worked) with category-colored dots, distance-unit picker (km/mi), Blocked Callsigns row shows the real count and links to `BlockedCallsignsSettings`. All persist via `SettingsPersistence` onChange. -- `AppState.swift` — DecodeMessage struct ONLY: added `var arrival: Date = Date()` (defaulted, so LiveEngine's existing constructor calls capture arrival at decode time — LiveEngine untouched per ownership). - -**Coordinator notes:** -- NEW BAND uses the current `settings.band` at render time; a decode that survives a band change would be re-classified against the new band. Deriving per-message band would need LiveEngine to stamp it — deliberately avoided per ownership rules; cosmetic only. -- No iOS `highlightPota` setting exists in the pre-staged SettingsState, so POTA pills are always-on (Android gates them). Add the field later if wanted — annotator already isolates the category. -- Android's PENDING pill is textMuted; the task brief specified amber for pending — followed the brief. -- Ran `xcodegen generate`; pbxproj regenerated (kit files need no project entries; no new app-target files were added). - -### Agent B — Engine/TX parity - -**Status: COMPLETE** — kit tests green (298/298, `swift test`), simulator build green (`xcodebuild` iPhone 16 Pro), `xcodegen generate` run. Not committed (coordinator commits). Agents D/W's LiveEngine work (PSK hand-off, online-log hook, waterfall loop) preserved untouched; extended around it. - -**1. Auto-sequence toggles wired** -- `autoCQAfterQSO` → `QsoEngine.autoReturnToCq` (set at engine start + every `syncSettingsToQso`). **Behavior change**: default OFF now means the sequencer *stops* after each completed QSO (it previously always returned to CQ); enable the setting for the old behavior. -- HUNT is now real behavior, not just a UI flag: while armed with no target locked, `autoAnswerIfHunting` answers the strongest decoded CQ (skips blocked callsigns, own call, hashed `<...>` calls, and already-worked stations from the logbook), sets `slotParity` to the opposite of the heard slot, and toasts "Answering X". -- `huntCallsCQ`: hunting with no CQ candidates and the engine idle → calls CQ itself (hunt+CQ hybrid; still interrupts to answer a CQ when one appears). -- `autoCallFollow`: while idle (not hunting-locked, engine inactive), a station calling *us* directly is auto-answered. -- `earlyDecode`: **inert on iOS** — the decode pipeline runs once at slot end (`runDecodeLoop` polls slot boundaries); a mid-slot early pass needs a decode-loop restructure that's out of scope. Setting remains persisted; noted here + nothing pretends to use it. - -**2. TX watchdog + stop-after-N** — pure `TxSupervisor` in kit (`Sources/FT8Engine/TxSupervisor.swift`): watchdog = N minutes since last "progress" event (armed at CQ/answer/new-target/force-log; refreshed on QSO completion when auto-CQ is on and on hunt idle-listening slots, mirroring Android's `launchSupervision`; a *stuck* QSO deliberately ages it); stop-after-N = N consecutive transmissions of the same message with no reply (reply or message change resets). LiveEngine checks `stopReason` before every TX cycle → `stopTx()` + toast ("TX watchdog: stopped after N min" / "No reply after N attempts — TX stopped"). 0 = off for both. - -**3. Late-start clip rule** — pure `TxTiming` in kit: `lateStartClipMs = max(0, msIntoCycle − slack)` (slack = `settings.lateStartToleranceMs`, default 2360), **never** modulo — tests explicitly guard the `% 15000` trap (700 ms in → 0 clipped; 3000 ms → 640; 14900 ms → 12540). Applied at actual playback time in `beginTxPlayback`; hopelessly-late TX (clip ≥ waveform) skips the cycle. `txDelayMs` honored as a pre-play delay (cancellable `txScheduleTask`). `pttDelayMs`: **inert on iOS** — there is no PTT line (AVAudioSession/VOX keying is the audio itself), so a keying settle delay has nothing to wait for; documented here, setting untouched. - -**4. TUNE button** — `TuneTone` in kit: pure sine generator (raised-cosine 10 ms anti-click ramps, amplitude cap) + `chipLabel` ("TUNE 7s"). LiveEngine `toggleTune`/`startTune`/`stopTune`: buffer length = `settings.tuneTimeoutSec` so playback ending enforces the hard max-on even if the ticker dies; 1 Hz countdown into `tx.tuneRemainingSec`; stops on tap, timeout, CALL CQ, answer, or engine stop. TUNE pill in TxStrip info row: red (`statusBad`) + bold + countdown while active, dimmed/disabled while the sequencer is armed/transmitting (mutually exclusive with FT8 TX, as Android). Tune Timeout picker (10/15/30/60/120 s) added to TransmissionSettings' TX Safety section. - -**5. Caller queue** — pure `CallerQueue` in kit: dedup by callsign (re-hear refreshes SNR/freq/time, keeps position), capacity 10 (Android `MAX_QUEUE_SIZE`), stale-prune after 60 s (~4 slots), `shouldEnqueue` policy (never self/target/blocked). LiveEngine enqueues stations calling me that aren't the target while the sequencer is active; auto-dequeues the next caller once the previous QSO fully wraps (incl. after the courtesy-73 slot, and even when auto-CQ is off — dequeue takes precedence over stopping, as Android). `ActiveQsoPanel` shows a wrapping QUEUE row (custom `Layout` flow); tapping a callsign = `forceLogAndMoveOn(nextCallsign:)` — logs the current QSO when it progressed far enough, then works that caller (replying with a report when their grid is known, mirroring Android's `checkFunOrderByExtraInfo + 1`). - -**6. ActiveQsoPanel parity** — header states via pure `QsoPanelHeader` in kit (Hunting… / Calling CQ / QSOing with X / Waiting for X, target SNR in cyan; hunt+CQ hybrid correctly reads "Calling CQ" per the Android rule); LOG action (`QsoEngine.forceLog()` — logs only when reports were exchanged, à la Android `shouldForceLog`); ✕ = `QsoEngine.abandonToCq()` (back to CQ or idle per auto-CQ, nothing logged); TX selector chips CQ/GRID/RPT/R-RPT/RR73/73 wired to `engine.selectTxStage` → `QsoEngine.setStage` (active = amber accent + border, completed = cyan signal; pure `TxStageSelector` rules in kit). Conversation log + stage dots kept. Panel now also shows while HUNT is armed (so the Hunting state has somewhere to live) — one-line visibility condition in AppTabView alongside the `onToggleTune` callback wiring. - -**7. TxStrip parity** — TUNE pill added to the info row (see #4); the mode pill was already a static non-interactive "FT8" chip — kept as-is per the brief (FT4/FT2 deferred). HUNT / CALL CQ / TX1-TX2 layout unchanged. - -**Files** (all within ownership): kit — `Sources/FT8Engine/{TxSupervisor,TxTiming,CallerQueue,TuneTone,TxStageSelector}.swift` (new), `QsoEngine.swift` (added `forceLog`/`abandonToCq`, factored `makeRecord`); kit tests — `Tests/FT8EngineTests/{TxSupervisorTests,TxTimingTests,CallerQueueTests,TuneToneTests,TxStageSelectorTests}.swift` (new, 37 tests) + 6 new cases in `QsoEngineTests`; app — `Engine/LiveEngine.swift`, `Components/{ActiveQsoPanel,TxStrip}.swift`, `Navigation/AppTabView.swift` (callback + panel-visibility line), `Screens/Settings/TransmissionSettings.swift` (tune-timeout row), `AppState.swift` (TxState body only: `qsoStage`, `targetSnr`, `queuedCallers`, `isTuning`, `tuneRemainingSec`). `TxPlayerService.swift`/`SlotTimerBar.swift`/`TransmitGlow.swift` needed no changes. - -**Coordinator notes:** -- Inert-by-platform: `earlyDecode` (slot-end decode pipeline), `pttDelayMs` (no PTT line on iOS). Both documented above; everything else in the TX/sequencer settings group now does something real. -- `autoCQAfterQSO` default-off behavior change called out in #1 — flag in the PR body. -- TUNE plays through the same `TxPlayerService`/AVAudioEngine speaker path as FT8 TX; on-device it keys VOX like any TX audio. Requires RX running (toast prompts if not). - -### Agent E — POTA parity - -**Status: COMPLETE** — kit tests 329/329 green (`swift test`; 298 baseline + 31 new), simulator build green (`xcodebuild` iPhone 16 Pro), `xcodegen generate` re-run after adding the two app files. Not committed (coordinator commits). Verified the live endpoint shape with one manual curl; tests use canned fixtures only. - -**What was built** (parity with Android `pota/PotaClient.getActiveSpots`, `PotaSpotsRepository`, `PotaQsoWindow`, `ui/pota/PotaScreen.kt` HuntTab/historyForDisplay, `PotaAdifExporter.buildActivationAdif`; OAuth self-spot/upload stays deferred as documented): - -Kit — pure, network-free logic in `ios/FT8AFKit/Sources/FT8Engine/`: -- `PotaSpots.swift` — `PotaSpot` model (activator, kHz, mode, ref, park name from the feed's `name` key, locationDesc, spotter, spotTime, comments; same row identity Android keys by) + `PotaSpots.decode` (tolerant of string/number `frequency`, null fields, malformed elements skipped; activator uppercased), `forDisplay(ft8Only:)` (case-insensitive FT8 filter + frequency sort, Android's hunt constraint but client-side so the toggle is instant), `ageSeconds` (lenient "yyyy-MM-ddTHH:mm:ss[.frac]" UTC parse, clamps skew; feeds the existing `relativeAge`), `band(forFrequencyKhz:)` (full HF/6m allocations → the app's "20M" band names). -- `PotaActivations.swift` — `PotaActivationRecord` (Codable; parkRef comma-split for two-fers, `parkRefsDisplay`, open end = active) + `potaHistoryForDisplay` (drops active, newest first) + `PotaQsoWindow` port (yyyyMMddHHmmss GMT stamps, Android's variable-width `time_on` normalization incl. dropped-leading-zero odd widths, `openEnd` bound for active sessions, `qsos(in:)`/`qsoCount`). -- `PotaAdif.swift` — `Adif.potaActivationExport(records:activation:)` in a NEW file (Adif.swift untouched, no signature changes): Android's exact field set/order (CALL GRIDSQUARE MODE[/SUBMODE] BAND FREQ RST_SENT RST_RCVD QSO_DATE TIME_ON TIME_OFF STATION_CALLSIGN MY_GRIDSQUARE MY_SIG=POTA MY_SIG_INFO), UTF-8 byte lengths, FT4/FT2 → MODE=MFSK+SUBMODE (the pota.app rejection Android fixed), one document per park with MY_SIG_INFO pinned, window-scoped via PotaQsoWindow, empty window → `[]` (never header-only files), `pota--.adi` filenames (UTC stamp; Android uses local — cosmetic). -- Tests (31 new): `PotaSpotsTests` (canned JSON fixture from a real 2026-07-13 response — decode mapping, uppercasing, int-string freq, null/garbage tolerance, filter+sort, age parse incl. fractional seconds, band map edges), `PotaActivationsTests` (stamps, time_on normalization, window in/ex-clusion incl. same-park repeat visits, sorting, Codable round-trip, history), `PotaAdifTests` (golden single-park document, byte-length UTF-8, empty-field omission, MFSK submodes, two-fer pinning, window scoping, empty→[]). - -App — `ios/FT8AF/FT8AF/`: -- `Engine/PotaService.swift` — `@Observable @MainActor` singleton: URLSession fetch of `api.pota.app/spot/activator` (10 s timeout, same UA/Accept as Android), isLoading/lastError/lastUpdatedAt, `ft8Only` toggle, `displaySpots` via kit. Re-entrant refreshes dropped; cancellation isn't surfaced as an error. -- `Engine/PotaActivationStore.swift` — Documents-dir JSON store (`pota_activations.json`), QsoLogStore pattern; History and a still-open activation survive restarts. -- `Screens/POTA/PotaScreen.swift` — mock data fully removed. Hunt: live spot list (List → native pull-to-refresh), 60 s auto-refresh via `.task(id: selectedTab)` while the tab is visible, loading/empty/error states, FT8 ONLY chip, rows show activator/park pill/park·location/comments/freq/relative age/spotter (+mode chip when non-FT8); tap = band switch through the same path as the TX strip's band chip (settings.band + SettingsPersistence.save) when the spot is FT8 on a mapped band, + targeting toast (else "tune manually" toast). Activate: sessions are `PotaActivationRecord`s; QSO count **derived** from `logbook.records` in the activation window (Observation-driven — no manual increments), elapsed ticks via TimelineView, End freezes the count into the record, Export ADIF button (disabled at 0 QSOs). History: real past activations (park refs, date, QSO count, per-row ADIF share). Share = per-park temp files → UIActivityViewController. -- `AppState.swift` — PotaState body ONLY: replaced the inert `isActivating/activationQsoCount/activationStartTime/parkRefs` with `activations: [PotaActivationRecord]` + derived `current`/`isActivating` (isActivating keeps its name so the ACTIVE header pill logic reads the same). - -**Coordinator notes:** -- Tapping a spot switches band only (mirrors TxStrip's FrequencyPickerSheet path); it does NOT prefill a QSO target — Android opens its QSO sheet with the activator instead, but iOS target selection is engine-owned (LiveEngine is Agent B's file) and out of my ownership. Trivial follow-up if wanted: set a decode-tap-equivalent hook. -- POTA spots are NOT fed into the decode annotator's park-ref enrichment (Android's `PotaSpotsRepository.parkRefFor`); Agent R's `isPotaCq` works off message text. Possible future wire-up via PotaService.shared. -- QSOs logged during an activation aren't tagged MY_SIG in `qso_log.json` (QsoRecord has no sig fields and QsoRecord.swift is Agent D's back-compat surface); the export derives membership from the activation time window instead — same result as Android's window-scoped query, and repeat visits to one park stay separate (tested). -- Build passed with Agent S's in-flight font/typography edits present in the tree; my PotaScreen rewrite predates any font adoption pass on Screens/POTA (rows still use `.system` fonts — Agent S or coordinator can sweep it with the same treatment as other screens). - -### Agent S — Style parity - -**Status: COMPLETE** — kit tests 298/298 green (`swift test`, kit untouched), `xcodegen generate` + simulator build green (iPhone 16 Pro), fonts verified rendering in the simulator. Not committed (coordinator commits). - -**1. Fonts bundled** — `ios/FT8AF/FT8AF/Fonts/`: `inter_variable.ttf`, `geist_mono_{regular,medium,semibold,bold}.ttf` copied from `ft8af/app/src/main/res/font/` (proportional Geist skipped — Android UI font is Inter). CoreText inspection: Geist Mono statics register as PS `GeistMono-{Regular,Medium,SemiBold,Bold}`; the Inter variable TTF exposes **named instances** `Inter-Regular`, `Inter-Regular_Medium`, `Inter-Regular_SemiBold`, `Inter-Regular_Bold` (verified via CTFontManager registration test: all four resolve with distinct weight traits 0/0.2/0.3/0.4), so weight selection works from the single variable file — **no static Inter weights needed, nothing downloaded**. - -**2. Info.plist migration** — `project.yml` now uses an XcodeGen `info:` block (`FT8AF/Info.plist`); `GENERATE_INFOPLIST_FILE` and all `INFOPLIST_KEY_*` settings removed. Migrated keys (the full set present in project.yml/pbxproj — there was no NSLocalNetworkUsageDescription anywhere): -- `NSMicrophoneUsageDescription` (same string) -- `UIApplicationSceneManifest` = { UIApplicationSupportsMultipleScenes: true, UISceneConfigurations: {} } (matches previous generated plist) -- `UIApplicationSupportsIndirectInputEvents` = true -- `UILaunchScreen` = {} (launch-screen generation equivalent) -- `UISupportedInterfaceOrientations` (iPhone list) + `UISupportedInterfaceOrientations~ipad` (previous _iPhone/_iPad values) -- `LSRequiresIPhoneOS` = true -- **new** `UIAppFonts` = the 5 bundled ttf files -Built product's Info.plist inspected via `plutil -p` in DerivedData: all keys + UIAppFonts present, mic string intact, 5 ttf files in the app bundle. - -**3. Typography layer** — new `ios/FT8AF/FT8AF/Theme/Typography.swift`: `Font.ft8afUI(size:weight:)` (Inter named instances; variation-axis `wght` fallback if instances aren't registered; SF fallback if the file fails) and `Font.ft8afMono(size:weight:)` (Geist Mono statics; SF Mono fallback). Slashed zero implemented (no rabbit hole): OpenType `zero` feature via `UIFontDescriptor.featureSettings` with the CoreText `kCTFontOpenTypeFeatureTag/Value` keys — mirrors Android's `fontFeatureSettings = "zero"`. - -**4. App-wide adoption** — all 30 view files under `ios/FT8AF/FT8AF/` swept mechanically (Components, Navigation, Screens/Decode|Logbook|Map|Settings|Waterfall, SplashScreen): `.system(size:…, design: .monospaced)` → `.ft8afMono(size:…)`, `.system(size:…)` → `.ft8afUI(size:…)` (227 call sites, weights preserved incl. ternaries). Zero `.system(` font sites remain outside the exclusions. Notes: -- **Screens/POTA/ untouched per concurrency rule — PotaScreen still uses system fonts; coordinator sweeps it after Agent E lands.** -- The 7 `.system(.body, design:.monospaced)` sites (TextFields) → `.ft8afMono(size: 17)` — fixed 17pt (body default) instead of dynamic type, consistent with the rest of the app's fixed sizes. -- SF Symbol `Image(systemName:)` sites keep their `.font()` for sizing (now ft8af helpers) — verified rendering unchanged. -- `Theme/Typography.swift`'s own `.system` fallbacks intentionally kept. - -**5. Visual verification (simulator)** — app installed + launched on iPhone 16 Pro sim, screenshots inspected: Geist Mono clearly rendering on chips/TX controls with **slashed zero visible** ("0 msgs"); Inter confirmed decisively by ink-width measurement of the Decode empty-state subtitle (rendered 248.7pt vs Inter 249.4pt vs SF 244.1pt at 13pt). No file found where the font failed to load. - -**Coordinator notes:** -- App target has no unit-test target and the typography layer is pure UIKit/SwiftUI font resolution (kit untouched), so no new kit tests; the font-name/weight mapping is trivial and verified by the build + on-sim rendering. -- `xcodegen generate` re-ran; pbxproj regenerated (adds Fonts/ resources + INFOPLIST_FILE). If Agent E regenerates after me nothing is lost — everything is declared in project.yml. -- Android's per-style letterspacing from Type.kt (e.g. -0.02em display, +0.04em labels) not replicated — iOS sites set explicit sizes rather than semantic styles; can be layered on later via `.kerning()` if the coordinator wants closer parity. diff --git a/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj b/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj index 838e4646..37f2eb32 100644 --- a/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj +++ b/ios/FT8AF/FT8AF.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 303BF422AF4C5F94DF696E5D /* FT8Rig in Frameworks */ = {isa = PBXBuildFile; productRef = 4E7B6368450390C0D64A5715 /* FT8Rig */; }; 33736A682F52CCBB13E8168A /* LogbookScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76108617D9AEDEE84BAD1054 /* LogbookScreen.swift */; }; 3BA2BC0EFAF62534D39C3A4A /* DecodeFilterSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88688AC6AFBEDC8AC3EF844 /* DecodeFilterSettings.swift */; }; + 3BCBA78527E44AD070AA1E48 /* AudioRoutePickerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4217D8419318D6A336873605 /* AudioRoutePickerButton.swift */; }; 49DFEC8ED0237764EA3EE3BF /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF21F5D95598018B8A46ED8D /* AppState.swift */; }; 4C0C9DCC39ED6528F0D4CDFF /* RadioAudioSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89642B9763C7FA33CFF0EE57 /* RadioAudioSettings.swift */; }; 4D87F21162B4DCEC806D8785 /* QsoCelebration.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC8DAEDCAE1AD9DE02EB1480 /* QsoCelebration.swift */; }; @@ -65,6 +66,7 @@ EEA0D2F1B9A7E0345F68E909 /* ExportLogSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16235B470705C04E596C8A5B /* ExportLogSheet.swift */; }; F3CB49EBF218C3BB9FAE02D6 /* DecodeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11DF7123A737FE9F5236677F /* DecodeRow.swift */; }; F4E60463DA065CDAE7B84EF3 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E706B1D26B550FE4D798B68 /* Accelerate.framework */; }; + F56F35C07C7374F28A21ADE3 /* AudioRouteController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B94D7FBAC132C5854796F79 /* AudioRouteController.swift */; }; F917448B524020C565485D03 /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3A872FCC168F7F5523C8D69 /* SplashScreen.swift */; }; FEB99CFFD070BA6B893ADC83 /* WaterfallCanvas.swift in Sources */ = {isa = PBXBuildFile; fileRef = B681B14FC4FED15C628471DF /* WaterfallCanvas.swift */; }; /* End PBXBuildFile section */ @@ -81,6 +83,8 @@ 29B5962B70F4510D76A5BC0A /* TxPlayerService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TxPlayerService.swift; sourceTree = ""; }; 2CFCB3285583184390D9E9BF /* AboutScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutScreen.swift; sourceTree = ""; }; 33CCA018AEEE169EDCA338FD /* LogbookAwards.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogbookAwards.swift; sourceTree = ""; }; + 3B94D7FBAC132C5854796F79 /* AudioRouteController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRouteController.swift; sourceTree = ""; }; + 4217D8419318D6A336873605 /* AudioRoutePickerButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRoutePickerButton.swift; sourceTree = ""; }; 4A8FFDE0644205A9AE050BC0 /* BlockedCallsignsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedCallsignsSettings.swift; sourceTree = ""; }; 4B08B1AA6951B99CEFAA521F /* TransmissionSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransmissionSettings.swift; sourceTree = ""; }; 4E09812700CD93E6187CC4BC /* LiveEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveEngine.swift; sourceTree = ""; }; @@ -240,6 +244,7 @@ isa = PBXGroup; children = ( 0244EBDFC2ADDAE77418FF53 /* ActiveQsoPanel.swift */, + 4217D8419318D6A336873605 /* AudioRoutePickerButton.swift */, 5306EE5215AE14F95F3329F8 /* FilterChip.swift */, F3C698656C5709343F4AEEF0 /* FrequencyPickerSheet.swift */, AC8DAEDCAE1AD9DE02EB1480 /* QsoCelebration.swift */, @@ -320,6 +325,7 @@ isa = PBXGroup; children = ( C42FB097015693FF14B27553 /* AudioCaptureService.swift */, + 3B94D7FBAC132C5854796F79 /* AudioRouteController.swift */, F287F167508A8E4114F44116 /* FFTProcessor.swift */, 4E09812700CD93E6187CC4BC /* LiveEngine.swift */, D131B05E27A96EC4BEB28782 /* OnlineLogService.swift */, @@ -434,6 +440,8 @@ 49DFEC8ED0237764EA3EE3BF /* AppState.swift in Sources */, 5C76E98A4BD4854709C3D0E9 /* AppTabView.swift in Sources */, EA1C91B3E65DB77ED5703FD9 /* AudioCaptureService.swift in Sources */, + F56F35C07C7374F28A21ADE3 /* AudioRouteController.swift in Sources */, + 3BCBA78527E44AD070AA1E48 /* AudioRoutePickerButton.swift in Sources */, BCEB9305C3EA84C758FAFBA7 /* AzimuthalMapCanvas.swift in Sources */, 072D76FFDC706DE3E537A3C2 /* BlockedCallsignsSettings.swift in Sources */, 035DDD710715AAC0100E0871 /* Colors.swift in Sources */, diff --git a/ios/FT8AF/FT8AF/AppState.swift b/ios/FT8AF/FT8AF/AppState.swift index 9b7bd100..ec543ed5 100644 --- a/ios/FT8AF/FT8AF/AppState.swift +++ b/ios/FT8AF/FT8AF/AppState.swift @@ -213,7 +213,9 @@ final class SettingsState { var txWatchdogMin: Int = 0 // 0 = off var stopAfterAttempts: Int = 0 // 0 = off // Radio - var spectrumWidthHz: Int = 3000 + // Default 3500 matches the pre-configurable fixed span (desktop WF_MAX_HZ) + // so updating doesn't silently narrow the displayed band. + var spectrumWidthHz: Int = 3500 var enabledBands: [String] = ["160M","80M","40M","30M","20M","17M","15M","12M","10M","6M"] // WSJT-X UDP interface (GridTracker / JTAlert / N1MM / Log4OM interop). var udpEnabled: Bool = false @@ -237,6 +239,9 @@ final class SettingsState { var distanceInMiles: Bool = false // Tune var tuneTimeoutSec: Int = 30 + // Preferred audio input port name ("" = system default); matched by name + // so the choice survives replug/relaunch. + var preferredInputPort: String = "" } // MARK: - Rig @@ -360,6 +365,7 @@ enum SettingsPersistence { d.set(s.continentFilter, forKey: key("continentFilter")) d.set(s.distanceInMiles, forKey: key("distanceInMiles")) d.set(s.tuneTimeoutSec, forKey: key("tuneTimeoutSec")) + d.set(s.preferredInputPort, forKey: key("preferredInputPort")) } @MainActor static func load(into s: SettingsState) { @@ -467,6 +473,9 @@ enum SettingsPersistence { if d.object(forKey: key("tuneTimeoutSec")) != nil { s.tuneTimeoutSec = d.integer(forKey: key("tuneTimeoutSec")) } + if let v = d.string(forKey: key("preferredInputPort")) { + s.preferredInputPort = v + } } private static func key(_ name: String) -> String { prefix + name } diff --git a/ios/FT8AF/FT8AF/Components/AudioRoutePickerButton.swift b/ios/FT8AF/FT8AF/Components/AudioRoutePickerButton.swift new file mode 100644 index 00000000..f21e772f --- /dev/null +++ b/ios/FT8AF/FT8AF/Components/AudioRoutePickerButton.swift @@ -0,0 +1,17 @@ +import AVKit +import SwiftUI + +/// The system output-route picker (AirPlay/USB/Bluetooth/speaker). iOS does +/// not allow apps to set the output route directly — this button opens the +/// OS sheet, mirroring the Android "Audio output device" picker's role. +struct AudioRoutePickerButton: UIViewRepresentable { + func makeUIView(context: Context) -> AVRoutePickerView { + let view = AVRoutePickerView() + view.activeTintColor = UIColor(accent) + view.tintColor = UIColor(textMuted) + view.prioritizesVideoDevices = false + return view + } + + func updateUIView(_ uiView: AVRoutePickerView, context: Context) {} +} diff --git a/ios/FT8AF/FT8AF/Engine/AudioCaptureService.swift b/ios/FT8AF/FT8AF/Engine/AudioCaptureService.swift index 1373dd26..d901b9bd 100644 --- a/ios/FT8AF/FT8AF/Engine/AudioCaptureService.swift +++ b/ios/FT8AF/FT8AF/Engine/AudioCaptureService.swift @@ -44,8 +44,36 @@ final class AudioCaptureService: @unchecked Sendable { try session.setCategory(.playAndRecord, options: [.defaultToSpeaker, .allowBluetooth]) try session.setActive(true) + try installTapAndStart() + + NotificationCenter.default.addObserver( + self, + selector: #selector(handleInterruption), + name: AVAudioSession.interruptionNotification, + object: nil + ) + // The engine stops itself when the audio hardware configuration + // changes (output/input route change, Simulator host-device switch) + // and the input format may change with it. Without rebuilding the + // tap and restarting, RX goes silently deaf while the UI still + // says RX. + NotificationCenter.default.addObserver( + self, + selector: #selector(handleConfigChange), + name: .AVAudioEngineConfigurationChange, + object: engine + ) + } + + /// (Re)build the converter for the current hardware input format, install + /// the tap, and start the engine. Used at startup and again after every + /// engine configuration change. + private func installTapAndStart() throws { let inputNode = engine.inputNode let hwFormat = inputNode.outputFormat(forBus: 0) + guard hwFormat.sampleRate > 0, hwFormat.channelCount > 0 else { + throw AudioCaptureError.formatCreationFailed + } // Target: 12 kHz, mono, Float32 — the FT8 codec sample rate. guard let targetFormat = AVAudioFormat( @@ -76,13 +104,6 @@ final class AudioCaptureService: @unchecked Sendable { lock.lock() _isRunning = true lock.unlock() - - NotificationCenter.default.addObserver( - self, - selector: #selector(handleInterruption), - name: AVAudioSession.interruptionNotification, - object: nil - ) } /// Stop the audio engine and remove the tap. @@ -100,6 +121,11 @@ final class AudioCaptureService: @unchecked Sendable { name: AVAudioSession.interruptionNotification, object: nil ) + NotificationCenter.default.removeObserver( + self, + name: .AVAudioEngineConfigurationChange, + object: engine + ) } // MARK: - Private @@ -150,6 +176,22 @@ final class AudioCaptureService: @unchecked Sendable { )) } + @objc private func handleConfigChange(_ notification: Notification) { + DispatchQueue.main.async { [weak self] in + guard let self else { return } + self.lock.lock() + let wasRunning = self._isRunning + self.lock.unlock() + guard wasRunning else { return } + + self.engine.inputNode.removeTap(onBus: 0) + // The tap is gone, so the audio thread can't touch the reusable + // buffer; drop it in case the chunk geometry changes. + self.reusableOutBuffer = nil + try? self.installTapAndStart() + } + } + @objc private func handleInterruption(_ notification: Notification) { guard let info = notification.userInfo, let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt, diff --git a/ios/FT8AF/FT8AF/Engine/AudioRouteController.swift b/ios/FT8AF/FT8AF/Engine/AudioRouteController.swift new file mode 100644 index 00000000..4810de03 --- /dev/null +++ b/ios/FT8AF/FT8AF/Engine/AudioRouteController.swift @@ -0,0 +1,95 @@ +import AVFoundation +import FT8Audio +import Foundation +import Observation + +/// One selectable audio input, identified by the session port UID (stable +/// while the device stays attached; the persisted preference matches on +/// port *name* instead, which survives replug/relaunch). +struct AudioInputOption: Identifiable, Equatable { + let id: String // AVAudioSessionPortDescription.uid + let name: String // raw port name, used for the persisted preference + let label: String // display label ("Digirig (USB)", "Built-in Microphone") +} + +/// Watches the shared AVAudioSession for the Radio & Audio settings screen: +/// enumerates selectable inputs, tracks the current output route, and applies +/// the operator's preferred input. Output routing is not directly settable on +/// iOS — the screen pairs the current-route readout with the system +/// `AVRoutePickerView`. +@Observable @MainActor +final class AudioRouteController { + var inputs: [AudioInputOption] = [] + var currentInputUid: String = "" + var currentOutputLabel: String = "—" + + @ObservationIgnored private var observer: NSObjectProtocol? + + func start(preferredPortName: String) { + refresh(preferredPortName: preferredPortName) + observer = NotificationCenter.default.addObserver( + forName: AVAudioSession.routeChangeNotification, + object: nil, queue: .main + ) { [weak self] _ in + Task { @MainActor in + self?.refresh(preferredPortName: preferredPortName) + } + } + } + + func stop() { + if let observer { NotificationCenter.default.removeObserver(observer) } + observer = nil + } + + func refresh(preferredPortName: String) { + let session = AVAudioSession.sharedInstance() + inputs = (session.availableInputs ?? []).map { port in + AudioInputOption(id: port.uid, name: port.portName, label: Self.label(for: port)) + } + // Re-apply the persisted preference when its port is present but not + // active (e.g. the USB interface was replugged). + if !preferredPortName.isEmpty, + let wanted = (session.availableInputs ?? []).first(where: { $0.portName == preferredPortName }), + session.currentRoute.inputs.first?.uid != wanted.uid { + try? session.setPreferredInput(wanted) + } + currentInputUid = session.currentRoute.inputs.first?.uid + ?? inputs.first?.id ?? "" + currentOutputLabel = session.currentRoute.outputs.first.map(Self.label(for:)) ?? "—" + } + + /// Selects an input for the shared session. Returns the chosen option's + /// port name for persistence, or nil if the selection failed. + func selectInput(uid: String) -> String? { + let session = AVAudioSession.sharedInstance() + guard let port = (session.availableInputs ?? []).first(where: { $0.uid == uid }) else { + return nil + } + do { + try session.setPreferredInput(port) + currentInputUid = port.uid + return port.portName + } catch { + return nil + } + } + + private static func label(for port: AVAudioSessionPortDescription) -> String { + AudioPortLabel.label(kind: kind(of: port.portType), portName: port.portName) + } + + private static func kind(of type: AVAudioSession.Port) -> AudioPortKind { + switch type { + case .builtInMic: return .builtInMic + case .builtInSpeaker: return .speaker + case .builtInReceiver: return .receiver + case .headsetMic, .headphones, .lineIn, .lineOut: return .wired + case .usbAudio: return .usb + case .bluetoothHFP, .bluetoothA2DP, .bluetoothLE: return .bluetooth + case .airPlay: return .airPlay + case .carAudio: return .carAudio + default: return .other + } + } +} diff --git a/ios/FT8AF/FT8AF/Engine/LiveEngine.swift b/ios/FT8AF/FT8AF/Engine/LiveEngine.swift index 276c9845..d821e85e 100644 --- a/ios/FT8AF/FT8AF/Engine/LiveEngine.swift +++ b/ios/FT8AF/FT8AF/Engine/LiveEngine.swift @@ -156,6 +156,9 @@ final class LiveEngine { audio.stop() isRunning = false appState?.waterfall.isLive = false + // Flush any queued PSK Reporter spots before the session state goes + // away (mirrors Android PskReporterSender.stop()). + OnlineLogService.shared.stopPsk(appState: appState) appState = nil qsoEngine = nil } @@ -527,20 +530,29 @@ final class LiveEngine { watchdogMin: settings.txWatchdogMin, stopAfterAttempts: settings.stopAfterAttempts ) { + // A supervisor stop must also disarm hunt: with hunt left + // armed, the hunt branch would restart CQ next slot with a + // fresh supervisor — runaway TX forever. Only supervisor + // stops disarm; a manual STOP tap keeps hunt as-is. + let huntWasArmed = appState.tx.huntEnabled + appState.tx.huntEnabled = false stopTx() + let huntSuffix = huntWasArmed ? " (hunt disarmed)" : "" switch reason { case .watchdog: appState.toast.show( - "TX watchdog: stopped after \(settings.txWatchdogMin) min", + "TX watchdog: stopped after \(settings.txWatchdogMin) min\(huntSuffix)", icon: "exclamationmark.triangle") case .noReply: appState.toast.show( - "No reply after \(settings.stopAfterAttempts) attempts — TX stopped", + "No reply after \(settings.stopAfterAttempts) attempts — TX stopped\(huntSuffix)", icon: "stop.circle") } } else if SlotClock.shouldTransmit(slotID: slotID, desiredParity: appState.tx.slotParity) { + // The attempt is counted in beginTxPlayback, once playback is + // actually committed — TUNE, encode failure, and late-skip + // slots must not feed the stop-after-N / watchdog bookkeeping. scheduleTx(message: txMsg, freqHz: appState.waterfall.txFreqHz) - supervisor.noteTransmission(txMsg) } } } @@ -713,19 +725,31 @@ final class LiveEngine { /// Start playback now: clip the leading `max(0, msIntoCycle - slack)` ms /// when we're late into the 15 s cycle so the TX still ends on the cycle - /// boundary. An on-time start clips NOTHING — clipping normal starts - /// destroys the leading Costas sync (see CLAUDE.md, TX pipeline gotcha #2). + /// boundary. The slack is the *physical* `TxTiming.defaultSlackMs` + /// constant (15000 − 12640 = 2360 ms) — never the user setting: a smaller + /// slack clips the leading Costas sync of ON-TIME transmissions and makes + /// them audible but undecodable (see CLAUDE.md, TX pipeline gotcha #2). + /// The late-start-tolerance setting is a SKIP threshold instead: when more + /// than that much leading audio would be clipped, the slot is skipped. private func beginTxPlayback(message: String, samples: [Float]) { guard let appState else { return } let nowMs = Int64(Date().timeIntervalSince1970 * 1000) let clipMs = TxTiming.lateStartClipMs( - msIntoCycle: SlotClock.msIntoCycle(atUtcMs: nowMs), - slackMs: Int64(appState.settings.lateStartToleranceMs) + msIntoCycle: SlotClock.msIntoCycle(atUtcMs: nowMs) ) + guard !TxTiming.shouldSkip( + clipMs: clipMs, + toleranceMs: Int64(appState.settings.lateStartToleranceMs) + ) else { return } // too late for the operator's tolerance — skip this cycle let skip = TxTiming.clipSampleCount(clipMs: clipMs, sampleRate: Int(FT8.sampleRate)) guard skip < samples.count else { return } // hopelessly late — skip this cycle let playSamples = skip > 0 ? Array(samples[skip...]) : samples + // Playback is definitely happening now — count the attempt for the + // TX safety nets (stop-after-N / watchdog bookkeeping). Skipped or + // failed slots above never reach this point. + supervisor.noteTransmission(message) + // Log the TX message to conversation panel. appState.tx.conversationLog.append( QsoLogEntry(direction: .tx, message: message, snr: nil, diff --git a/ios/FT8AF/FT8AF/Engine/OnlineLogService.swift b/ios/FT8AF/FT8AF/Engine/OnlineLogService.swift index d205b91e..c7f9a57c 100644 --- a/ios/FT8AF/FT8AF/Engine/OnlineLogService.swift +++ b/ios/FT8AF/FT8AF/Engine/OnlineLogService.swift @@ -169,11 +169,34 @@ final class OnlineLogService { } } + /// Flush-on-stop (Android `PskReporterSender.stop()`): send anything + /// queued immediately, then cancel the pending flush task. Called from + /// `LiveEngine.stop()`. + func stopPsk(appState: AppState?) { + pskFlushTask?.cancel() + pskFlushTask = nil + flushPsk(appState: appState) + } + /// Build the IPFIX datagrams for the queued spots and send them. + /// Re-checks the PSK Reporter toggle at flush time: once the operator + /// opts out, queued spots are dropped and the pending flush task is + /// cancelled — nothing may be sent after opt-out. func flushPsk(appState: AppState?) { - guard !pskQueue.isEmpty, let appState else { return } + guard let appState, appState.settings.pskReporterEnabled else { + pskQueue.removeAll() + pskFlushTask?.cancel() + pskFlushTask = nil + return + } + guard !pskQueue.isEmpty else { return } let myCall = appState.settings.myCall - guard !myCall.isEmpty else { return } + // Spots are only enqueued while a callsign is set; if it was cleared + // since, the queue can never be sent — drop it rather than strand it. + guard !myCall.isEmpty else { + pskQueue.removeAll() + return + } let spots = pskQueue pskQueue.removeAll() let version = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "1.0" diff --git a/ios/FT8AF/FT8AF/Engine/TxPlayerService.swift b/ios/FT8AF/FT8AF/Engine/TxPlayerService.swift index d1076595..b778dfc8 100644 --- a/ios/FT8AF/FT8AF/Engine/TxPlayerService.swift +++ b/ios/FT8AF/FT8AF/Engine/TxPlayerService.swift @@ -61,6 +61,20 @@ final class TxPlayerService: @unchecked Sendable { /// Schedule and play `samples` (mono Float32 at 12 kHz). Interrupts any /// previously playing buffer so a new TX overrides the old one. func play(_ samples: [Float], completion: (() -> Void)? = nil) { + // The shared engine can be stopped at play time (route/config change, + // interruption, Simulator startup) — AVAudioPlayerNode.play() on a + // stopped engine logs "Cannot play yet!" and the TX is silent, and a + // stopped engine can also report an invalid mixer format, which would + // make the deferred attach below bail. Restart it first. + if let engine, !engine.isRunning { + do { + try engine.start() + } catch { + completion?() + return + } + } + guard ensureAttached(), let converter = outputConverter else { completion?() return diff --git a/ios/FT8AF/FT8AF/Info.plist b/ios/FT8AF/FT8AF/Info.plist index 159ae10b..e49a1158 100644 --- a/ios/FT8AF/FT8AF/Info.plist +++ b/ios/FT8AF/FT8AF/Info.plist @@ -20,6 +20,8 @@ 1 LSRequiresIPhoneOS + NSLocalNetworkUsageDescription + FT8AF uses the local network to send WSJT-X UDP messages to companion apps like GridTracker and JTAlert NSMicrophoneUsageDescription FT8AF needs microphone access to capture radio audio for FT8 decoding UIAppFonts diff --git a/ios/FT8AF/FT8AF/Screens/Settings/AdvancedSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/AdvancedSettings.swift index 49d1ccbe..d4bf38e8 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/AdvancedSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/AdvancedSettings.swift @@ -57,7 +57,7 @@ struct AdvancedSettings: View { VStack(alignment: .leading, spacing: 2) { Text("Late Start Tolerance") .foregroundStyle(textPrimary) - Text("Max ms into cycle before skipping TX") + Text("Max ms clipped from a late TX before skipping it") .font(.ft8afUI(size: 11)) .foregroundStyle(textFaint) } @@ -77,7 +77,7 @@ struct AdvancedSettings: View { } header: { Text("Timing").foregroundStyle(textMuted) } footer: { - Text("PTT delay compensates for rig keying latency. Late start tolerance sets how far into a 15-second cycle TX can still start (default 2360 ms = FT8 message duration).") + Text("PTT delay compensates for rig keying latency. A TX starting more than 2.36 s into the 15-second cycle has its leading audio clipped so it still ends on time; late start tolerance is the most clipped audio allowed before the transmission is skipped for that cycle (0 = skip any clipped start).") .foregroundStyle(textFaint) } .listRowBackground(bgSurface) diff --git a/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift index 80d2546c..937a3561 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/LoggingSettings.swift @@ -119,7 +119,14 @@ struct LoggingSettings: View { .onChange(of: settings.cloudlogStationId) { _, _ in persist() } .onChange(of: settings.qrzLogbookEnabled) { _, _ in persist() } .onChange(of: settings.qrzLogbookApiKey) { _, _ in qrzResult = nil; persist() } - .onChange(of: settings.pskReporterEnabled) { _, _ in persist() } + .onChange(of: settings.pskReporterEnabled) { _, newValue in + persist() + // Opting out drops any queued spots and cancels the pending + // flush task right away — nothing may be sent after opt-out. + if !newValue { + OnlineLogService.shared.flushPsk(appState: appState) + } + } } private func persist() { diff --git a/ios/FT8AF/FT8AF/Screens/Settings/RadioAudioSettings.swift b/ios/FT8AF/FT8AF/Screens/Settings/RadioAudioSettings.swift index 1bbb6a18..e26dde0e 100644 --- a/ios/FT8AF/FT8AF/Screens/Settings/RadioAudioSettings.swift +++ b/ios/FT8AF/FT8AF/Screens/Settings/RadioAudioSettings.swift @@ -4,6 +4,22 @@ private let allBands = ["160M","80M","40M","30M","20M","17M","15M","12M","10M"," struct RadioAudioSettings: View { @Environment(AppState.self) private var appState + @State private var audioRoutes = AudioRouteController() + + /// Selection binding for the input-device picker: reflects the session's + /// current input, and on change applies + persists the preference. + private var inputSelection: Binding { + Binding( + get: { audioRoutes.currentInputUid }, + set: { uid in + guard uid != audioRoutes.currentInputUid else { return } + if let portName = audioRoutes.selectInput(uid: uid) { + appState.settings.preferredInputPort = portName + SettingsPersistence.save(appState.settings) + } + } + ) + } var body: some View { @Bindable var settings = appState.settings @@ -25,13 +41,25 @@ struct RadioAudioSettings: View { // Audio input Section { + Picker("Input Device", selection: inputSelection) { + ForEach(audioRoutes.inputs) { input in + Text(input.label).tag(input.id) + } + } + .foregroundStyle(textPrimary) + .tint(accent) + HStack { - Text("Input Device") - .foregroundStyle(textPrimary) + VStack(alignment: .leading, spacing: 2) { + Text("Output Device") + .foregroundStyle(textPrimary) + Text(audioRoutes.currentOutputLabel) + .font(.ft8afUI(size: 11)) + .foregroundStyle(textMuted) + } Spacer() - Text("Built-in Microphone") - .font(.ft8afUI(size: 14)) - .foregroundStyle(textMuted) + AudioRoutePickerButton() + .frame(width: 44, height: 32) } // Spectrum width @@ -45,7 +73,7 @@ struct RadioAudioSettings: View { } Spacer() Picker("", selection: $settings.spectrumWidthHz) { - ForEach([2500, 3000, 4000, 5000], id: \.self) { hz in + ForEach([2500, 3000, 3500, 4000, 5000], id: \.self) { hz in Text("\(hz) Hz").tag(hz) } } @@ -159,6 +187,8 @@ struct RadioAudioSettings: View { .toolbarColorScheme(.dark, for: .navigationBar) .onChange(of: settings.rigModel) { _, _ in SettingsPersistence.save(appState.settings) } .onChange(of: settings.spectrumWidthHz) { _, _ in SettingsPersistence.save(appState.settings) } + .onAppear { audioRoutes.start(preferredPortName: appState.settings.preferredInputPort) } + .onDisappear { audioRoutes.stop() } } private var statusColor: Color { diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift index c887a515..7d0847da 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/FrequencyRuler.swift @@ -2,8 +2,10 @@ import SwiftUI import FT8Audio /// Horizontal ruler showing Hz tick marks across the displayed audio band. -/// Ticks span the operator's spectrum-width setting — the same range the -/// waterfall/spectrum data covers — so the labels line up with the trace. +/// Ticks span `waterfall.displayMaxHz` — the range the drawn waterfall and +/// spectrum data actually cover (the loop keeps it in step with the +/// spectrum-width setting while live) — so the labels line up with the trace +/// even when RX is stopped and the setting changes. /// Tick geometry comes from `WaterfallAxis.rulerTicks`, which places each /// label at its true `hz / width` fraction (mirrors the Android fix for ruler /// labels drifting when the width isn't a 500 Hz multiple, commit 647b12e8). @@ -12,7 +14,7 @@ struct FrequencyRuler: View { var body: some View { let ticks = WaterfallAxis.rulerTicks( - displayMaxHz: Float(appState.settings.spectrumWidthHz) + displayMaxHz: appState.waterfall.displayMaxHz ) Canvas { context, size in for tick in ticks { diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift index a36e73a5..7b365425 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/SpectrumStrip.swift @@ -4,13 +4,17 @@ import FT8Audio /// Vertical bar chart of live FFT magnitudes with a TX frequency marker. /// Reads normalized magnitudes (0...1) from `appState.waterfall.spectrum`. /// Tap or drag to set the TX frequency. All frequency ↔ x mappings use the -/// operator's spectrum-width setting so overlays track the drawn bars. +/// span the drawn data actually covers so overlays track the drawn bars. struct SpectrumStrip: View { @Environment(AppState.self) private var appState @State private var touchFreq: Float? - /// Top of the displayed band — the operator's spectrum-width setting. - private var displayMaxHz: Float { Float(appState.settings.spectrumWidthHz) } + /// Top of the displayed band — the span the drawn rows/spectrum actually + /// cover (`waterfall.displayMaxHz`, maintained by the waterfall loop). + /// NOT the settings width: when the loop isn't running, a settings change + /// hasn't been applied to the data yet, and mapping against it would put + /// overlays and tap-to-tune on the wrong frequencies. + private var displayMaxHz: Float { appState.waterfall.displayMaxHz } var body: some View { GeometryReader { geo in diff --git a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift index 6afadffa..96e518f3 100644 --- a/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift +++ b/ios/FT8AF/FT8AF/Screens/Waterfall/WaterfallCanvas.swift @@ -11,9 +11,11 @@ import FT8Audio struct WaterfallCanvas: View { @Environment(AppState.self) private var appState - /// Top of the displayed band — the operator's spectrum-width setting. All - /// overlays map frequencies against this span so they sit on the trace. - private var displayMaxHz: Float { Float(appState.settings.spectrumWidthHz) } + /// Top of the displayed band — the span the drawn rows actually cover + /// (`waterfall.displayMaxHz`, maintained by the waterfall loop). All + /// overlays map frequencies against this span so they sit on the trace + /// even when the loop is stopped and the settings width has changed. + private var displayMaxHz: Float { appState.waterfall.displayMaxHz } var body: some View { Canvas { context, size in diff --git a/ios/FT8AF/project.yml b/ios/FT8AF/project.yml index 28eafbfc..0cac7065 100644 --- a/ios/FT8AF/project.yml +++ b/ios/FT8AF/project.yml @@ -25,6 +25,7 @@ targets: properties: LSRequiresIPhoneOS: true NSMicrophoneUsageDescription: "FT8AF needs microphone access to capture radio audio for FT8 decoding" + NSLocalNetworkUsageDescription: "FT8AF uses the local network to send WSJT-X UDP messages to companion apps like GridTracker and JTAlert" UIApplicationSceneManifest: UIApplicationSupportsMultipleScenes: true UISceneConfigurations: {} diff --git a/ios/FT8AFKit/Sources/FT8Audio/AudioPortLabel.swift b/ios/FT8AFKit/Sources/FT8Audio/AudioPortLabel.swift new file mode 100644 index 00000000..4ef20205 --- /dev/null +++ b/ios/FT8AFKit/Sources/FT8Audio/AudioPortLabel.swift @@ -0,0 +1,51 @@ +import Foundation + +/// Category of an audio port, mapped from `AVAudioSession.Port` by the app +/// layer. Kept as a plain enum here so label composition stays +/// platform-neutral and host-testable (FT8Audio has no AVFoundation). +public enum AudioPortKind: String, Sendable { + case builtInMic + case speaker + case receiver + case wired + case usb + case bluetooth + case airPlay + case carAudio + case other +} + +/// Display labels for audio input/output ports, matching the Android +/// device-picker convention of " ()" with the transport +/// suffix dropped when the port name already says it. +public enum AudioPortLabel { + public static func label(kind: AudioPortKind, portName: String) -> String { + let name = portName.trimmingCharacters(in: .whitespaces) + switch kind { + case .builtInMic: + return "Built-in Microphone" + case .speaker: + return "Speaker" + case .receiver: + return "Receiver" + case .wired: + return name.isEmpty ? "Wired" : name + case .usb: + return suffixed(name, transport: "USB", fallback: "USB Audio") + case .bluetooth: + return suffixed(name, transport: "Bluetooth", fallback: "Bluetooth Audio") + case .airPlay: + return suffixed(name, transport: "AirPlay", fallback: "AirPlay") + case .carAudio: + return suffixed(name, transport: "CarPlay", fallback: "CarPlay") + case .other: + return name.isEmpty ? "Unknown Device" : name + } + } + + private static func suffixed(_ name: String, transport: String, fallback: String) -> String { + if name.isEmpty { return fallback } + if name.range(of: transport, options: .caseInsensitive) != nil { return name } + return "\(name) (\(transport))" + } +} diff --git a/ios/FT8AFKit/Sources/FT8Engine/PskReporter.swift b/ios/FT8AFKit/Sources/FT8Engine/PskReporter.swift index c16806c6..327810cc 100644 --- a/ios/FT8AFKit/Sources/FT8Engine/PskReporter.swift +++ b/ios/FT8AFKit/Sources/FT8Engine/PskReporter.swift @@ -153,22 +153,35 @@ public final class PskReporterEncoder { let receiverBytes = Self.encodeReceiverDataSet( call: rxCall, grid: rxGrid, software: software, antenna: antenna, rig: rig) - // Header is 16 bytes. + // Header is 16 bytes. Reserve the worst-case sender-set padding + // (3 bytes) inside the budget so the padded datagram can never + // exceed `maxDatagram`. let overhead = 16 + templateBytes.count + receiverBytes.count - let budget = Self.maxDatagram - overhead + let budget = Self.maxDatagram - overhead - 3 - // Encode as many sender records as fit. + // Encode as many sender records as fit. A record that cannot fit + // even alone in an otherwise-empty datagram is pathological — + // drop it (never emit an oversized datagram) and keep going. var senderRecords: [[UInt8]] = [] var senderSetSize = 4 // set header (setId + length) var used = 0 for spot in remaining { let rec = Self.encodeSenderRecord(spot) - if senderSetSize + rec.count > budget && !senderRecords.isEmpty { break } + if senderSetSize + rec.count > budget { + if senderRecords.isEmpty { + used += 1 // oversized single record: drop it + continue + } + break + } senderRecords.append(rec) senderSetSize += rec.count used += 1 } remaining = remaining.dropFirst(used) + // Everything consumed this pass was dropped as oversized — + // nothing to send in this datagram. + if senderRecords.isEmpty { continue } // Pad sender set to a 4-byte boundary. let senderPadding = (4 - (senderSetSize % 4)) % 4 diff --git a/ios/FT8AFKit/Sources/FT8Engine/QsoEngine.swift b/ios/FT8AFKit/Sources/FT8Engine/QsoEngine.swift index b6860b3f..fe9c43c7 100644 --- a/ios/FT8AFKit/Sources/FT8Engine/QsoEngine.swift +++ b/ios/FT8AFKit/Sources/FT8Engine/QsoEngine.swift @@ -60,6 +60,10 @@ public final class QsoEngine { private var reportRcvd: Int? private var pendingTx: String? private var startedMs: Int64 = 0 + /// True once `complete()` has emitted the record for the current QSO — + /// guards the courtesy-73 window (`stage == .bye73`, target still set) + /// against a second identical record from `forceLog()`. + private var recordEmitted = false public init(myCall: String, myGrid: String) { self.myCall = myCall.uppercased() @@ -139,6 +143,7 @@ public final class QsoEngine { gridRcvd = nil reportSent = nil reportRcvd = nil + recordEmitted = false startedMs = FT8Time.nowUnixMs() } @@ -244,13 +249,16 @@ public final class QsoEngine { /// 73 (the Active QSO panel's LOG action; port of Android /// `forceLogAndMoveOn`). Only produces a record when the QSO progressed far /// enough to be a real contact — both reports known, i.e. we have sent - /// R-report or beyond (Android `shouldForceLog`: order >= 3). Either way - /// the QSO context is cleared and we return to CQ / stop per + /// R-report or beyond (Android `shouldForceLog`: order >= 3) — and + /// `complete()` hasn't already emitted it (the courtesy-73 window keeps + /// the target set after completion; a LOG tap there must not double-log). + /// Either way the QSO context is cleared and we return to CQ / stop per /// `autoReturnToCq`. public func forceLog() -> QsoOutcome? { guard let dx = target else { return nil } let progressed = stage == .rReport || stage == .rr73 || stage == .bye73 - let outcome: QsoOutcome? = progressed ? .completed(makeRecord(dx)) : nil + let outcome: QsoOutcome? = (progressed && !recordEmitted) + ? .completed(makeRecord(dx)) : nil finishQso() return outcome } @@ -263,6 +271,7 @@ public final class QsoEngine { private func complete(_ dx: String) -> QsoOutcome { let record = makeRecord(dx) + recordEmitted = true // If a courtesy "73" is queued (the .rReport path set stage = .bye73), // leave pendingTx/stage so the scheduler transmits it; the next slot's diff --git a/ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift b/ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift index 9a484d44..a773d2df 100644 --- a/ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift +++ b/ios/FT8AFKit/Sources/FT8Engine/TxTiming.swift @@ -26,4 +26,18 @@ public enum TxTiming { public static func clipSampleCount(clipMs: Int64, sampleRate: Int) -> Int { max(0, Int(clipMs) * sampleRate / 1000) } + + /// Whether to skip the transmission for this slot entirely. + /// + /// `toleranceMs` is the operator's late-start tolerance (the + /// "Late Start Tolerance" setting): the maximum amount of leading audio + /// they will accept losing to a late-start clip. It is a *skip threshold*, + /// never the clip slack — the clip slack is the physical + /// `defaultSlackMs` constant. A clip larger than the tolerance means the + /// TX would lose too much of its head (Costas sync and beyond), so the + /// whole slot is skipped instead of transmitting a mutilated signal. + /// Negative tolerances are clamped to 0 (skip any clipped start). + public static func shouldSkip(clipMs: Int64, toleranceMs: Int64) -> Bool { + clipMs > max(0, toleranceMs) + } } diff --git a/ios/FT8AFKit/Tests/FT8AudioTests/AudioPortLabelTests.swift b/ios/FT8AFKit/Tests/FT8AudioTests/AudioPortLabelTests.swift new file mode 100644 index 00000000..c6dff64a --- /dev/null +++ b/ios/FT8AFKit/Tests/FT8AudioTests/AudioPortLabelTests.swift @@ -0,0 +1,52 @@ +import XCTest +@testable import FT8Audio + +final class AudioPortLabelTests: XCTestCase { + func testBuiltInMicIgnoresPortName() { + XCTAssertEqual(AudioPortLabel.label(kind: .builtInMic, portName: "iPhone Microphone"), + "Built-in Microphone") + } + + func testSpeakerAndReceiverAreFixed() { + XCTAssertEqual(AudioPortLabel.label(kind: .speaker, portName: "Speaker"), "Speaker") + XCTAssertEqual(AudioPortLabel.label(kind: .receiver, portName: "Receiver"), "Receiver") + } + + func testUsbAppendsTransportSuffix() { + XCTAssertEqual(AudioPortLabel.label(kind: .usb, portName: "CM108 Audio"), + "CM108 Audio (USB)") + } + + func testUsbSkipsSuffixWhenNameMentionsUsb() { + XCTAssertEqual(AudioPortLabel.label(kind: .usb, portName: "USB Audio CODEC"), + "USB Audio CODEC") + XCTAssertEqual(AudioPortLabel.label(kind: .usb, portName: "Digirig usb"), + "Digirig usb") + } + + func testUsbEmptyNameFallsBack() { + XCTAssertEqual(AudioPortLabel.label(kind: .usb, portName: ""), "USB Audio") + } + + func testBluetoothSuffix() { + XCTAssertEqual(AudioPortLabel.label(kind: .bluetooth, portName: "AirPods Pro"), + "AirPods Pro (Bluetooth)") + XCTAssertEqual(AudioPortLabel.label(kind: .bluetooth, portName: ""), "Bluetooth Audio") + } + + func testWiredUsesNameOrFallback() { + XCTAssertEqual(AudioPortLabel.label(kind: .wired, portName: "Headset Microphone"), + "Headset Microphone") + XCTAssertEqual(AudioPortLabel.label(kind: .wired, portName: " "), "Wired") + } + + func testOtherFallsBackToUnknown() { + XCTAssertEqual(AudioPortLabel.label(kind: .other, portName: ""), "Unknown Device") + XCTAssertEqual(AudioPortLabel.label(kind: .other, portName: "HDMI"), "HDMI") + } + + func testWhitespaceTrimmed() { + XCTAssertEqual(AudioPortLabel.label(kind: .usb, portName: " Radio Cable "), + "Radio Cable (USB)") + } +} diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/PskReporterTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/PskReporterTests.swift index dad38b27..5f4e7863 100644 --- a/ios/FT8AFKit/Tests/FT8EngineTests/PskReporterTests.swift +++ b/ios/FT8AFKit/Tests/FT8EngineTests/PskReporterTests.swift @@ -155,6 +155,86 @@ final class PskReporterTests: XCTestCase { XCTAssertEqual(enc.sequenceNumber, UInt32(packets.count)) } + /// True when `needle` appears as a contiguous byte run inside `haystack`. + private func containsBytes(_ haystack: [UInt8], _ needle: [UInt8]) -> Bool { + guard !needle.isEmpty, haystack.count >= needle.count else { return false } + for start in 0...(haystack.count - needle.count) + where Array(haystack[start..<(start + needle.count)]) == needle { + return true + } + return false + } + + func testExactBoundaryBatchNeverExceedsMaxDatagramIncludingPadding() { + // Sweep locator lengths 0/2/4/6 chars to shift the record size (and + // therefore the fit boundary) across every 4-byte padding alignment: + // whatever lands exactly at the budget, the padded datagram must stay + // <= 1400 bytes. + for locLen in [0, 2, 4, 6] { + let enc = PskReporterEncoder(observationDomainId: 1, sequenceNumber: 0) + let loc = locLen > 0 ? String(repeating: "A", count: locLen) : nil + let many = (0..<300).map { i in + spot(call: String(format: "K%03dABC", i), locator: loc) + } + let packets = enc.buildPackets(rxCall: "KD2OGR", rxGrid: "FN20", + software: "FT8AF 1.0", spots: many, + nowMs: 1_700_000_000_000) + XCTAssertGreaterThan(packets.count, 1) + for p in packets { + XCTAssertLessThanOrEqual(p.count, 1400, + "locator length \(locLen) busted the datagram cap") + XCTAssertEqual(p.count % 4, 0) + XCTAssertEqual(Int(p[2]) << 8 | Int(p[3]), p.count) // declared == actual + } + } + } + + func testOversizedSingleRecordDroppedSubsequentRecordsStillSent() { + // A record that cannot fit even alone in an empty datagram is + // pathological: drop it, keep sending the rest, never emit > 1400. + let enc = PskReporterEncoder(observationDomainId: 1, sequenceNumber: 0) + let huge = spot(call: String(repeating: "X", count: 1500)) + let packets = enc.buildPackets( + rxCall: "KD2OGR", rxGrid: "FN20", software: "FT8AF 1.0", + spots: [huge, spot(call: "K1ABC"), spot(call: "W9DEF")], + nowMs: 1_700_000_000_000) + XCTAssertEqual(packets.count, 1) + let p = packets[0] + XCTAssertLessThanOrEqual(p.count, 1400) + XCTAssertTrue(containsBytes(p, Array("K1ABC".utf8))) + XCTAssertTrue(containsBytes(p, Array("W9DEF".utf8))) + XCTAssertFalse(containsBytes(p, Array(String(repeating: "X", count: 20).utf8))) + // Sequence advanced once per emitted packet — dropped records don't + // consume sequence numbers. + XCTAssertEqual(enc.sequenceNumber, 1) + } + + func testOversizedRecordInMiddleOfBatchIsSkipped() { + let enc = PskReporterEncoder(observationDomainId: 1, sequenceNumber: 0) + let huge = spot(call: String(repeating: "X", count: 1500)) + let packets = enc.buildPackets( + rxCall: "KD2OGR", rxGrid: "FN20", software: "FT8AF 1.0", + spots: [spot(call: "K1ABC"), huge, spot(call: "W9DEF")], + nowMs: 1_700_000_000_000) + for p in packets { XCTAssertLessThanOrEqual(p.count, 1400) } + let all = packets.flatMap { $0 } + XCTAssertTrue(containsBytes(all, Array("K1ABC".utf8))) + XCTAssertTrue(containsBytes(all, Array("W9DEF".utf8))) + XCTAssertFalse(containsBytes(all, Array(String(repeating: "X", count: 20).utf8))) + XCTAssertEqual(enc.sequenceNumber, UInt32(packets.count)) + } + + func testAllRecordsOversizedProducesNoPackets() { + let enc = PskReporterEncoder(observationDomainId: 1, sequenceNumber: 0) + let packets = enc.buildPackets( + rxCall: "KD2OGR", rxGrid: "FN20", software: "FT8AF 1.0", + spots: [spot(call: String(repeating: "X", count: 1500)), + spot(call: String(repeating: "Y", count: 2000))], + nowMs: 1_700_000_000_000) + XCTAssertTrue(packets.isEmpty) + XCTAssertEqual(enc.sequenceNumber, 0) + } + func testBuildPacketsEmptySpots() { let enc = PskReporterEncoder(observationDomainId: 1, sequenceNumber: 0) XCTAssertTrue(enc.buildPackets(rxCall: "K", rxGrid: "AA00", software: "s", diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/QsoEngineTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/QsoEngineTests.swift index 2261a4c6..1b1705b7 100644 --- a/ios/FT8AFKit/Tests/FT8EngineTests/QsoEngineTests.swift +++ b/ios/FT8AFKit/Tests/FT8EngineTests/QsoEngineTests.swift @@ -252,6 +252,69 @@ final class QsoEngineTests: XCTestCase { XCTAssertEqual(e.status().stage, .idle) } + func testForceLogDuringCourtesy73WindowDoesNotDoubleLog() { + // RR73-received flow: complete() logs the QSO and queues the courtesy + // 73 (stage .bye73, target still set). A LOG tap in that window must + // NOT emit a second identical record. + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.answer(msg("CQ", "K1ABC", "FN42", "FN42", -5)) + _ = e.processRx([msg("K0XYZ", "K1ABC", "-12", "", -8)]) // → .rReport + guard case .completed? = e.processRx([msg("K0XYZ", "K1ABC", "RR73", "", -8)]) else { + return XCTFail("expected the RR73 to complete and log the QSO") + } + XCTAssertEqual(e.status().stage, .bye73) + XCTAssertEqual(e.status().target, "K1ABC") + + // LOG during the courtesy-73 window: no second record, just move on. + XCTAssertNil(e.forceLog()) + XCTAssertNil(e.status().target) + XCTAssertEqual(e.status().stage, .cq) + } + + func testForceLogMidQsoStillEmitsExactlyOneRecord() { + // Genuine mid-QSO force-log (reports exchanged, NOT completed): + // exactly one record, and a repeat LOG produces nothing further. + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.answer(msg("CQ", "K1ABC", "FN42", "FN42", -5)) + _ = e.processRx([msg("K0XYZ", "K1ABC", "-12", "", -8)]) // → .rReport + guard case .completed(let rec)? = e.forceLog() else { + return XCTFail("expected a logged record from a mid-QSO force-log") + } + XCTAssertEqual(rec.call, "K1ABC") + XCTAssertNil(e.forceLog()) // target cleared — nothing more to log + } + + func testForceLogAfterManualBye73StillLogsOnce() { + // Operator manually picked Tx5 (73) before the sequencer completed: + // nothing has been logged yet, so LOG must still emit the record. + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.answer(msg("CQ", "K1ABC", "FN42", "FN42", -5)) + _ = e.processRx([msg("K0XYZ", "K1ABC", "-12", "", -8)]) // → .rReport + e.setStage(.bye73) + guard case .completed(let rec)? = e.forceLog() else { + return XCTFail("expected a record — this QSO was never logged") + } + XCTAssertEqual(rec.call, "K1ABC") + } + + func testNextQsoAfterCourtesy73LogsNormally() { + // The record-emitted latch must reset with the QSO context: a full + // second QSO with a new station logs its own record. + let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") + e.answer(msg("CQ", "K1ABC", "FN42", "FN42", -5)) + _ = e.processRx([msg("K0XYZ", "K1ABC", "-12", "", -8)]) + _ = e.processRx([msg("K0XYZ", "K1ABC", "RR73", "", -8)]) // logged, .bye73 + _ = e.processRx([]) // courtesy 73 slot passed → back to CQ + + _ = e.processRx([msg("K0XYZ", "W9DEF", "EN52", "EN52", -3)]) + _ = e.processRx([msg("K0XYZ", "W9DEF", "R-06", "", -3)]) + let out = e.processRx([msg("K0XYZ", "W9DEF", "73", "", -3)]) + guard case .completed(let rec)? = out else { + return XCTFail("expected the second QSO to complete and log") + } + XCTAssertEqual(rec.call, "W9DEF") + } + func testAbandonToCqDropsTargetWithoutLogging() { let e = QsoEngine(myCall: "K0XYZ", myGrid: "EN37") e.answer(msg("CQ", "K1ABC", "FN42", "FN42", -5)) diff --git a/ios/FT8AFKit/Tests/FT8EngineTests/TxTimingTests.swift b/ios/FT8AFKit/Tests/FT8EngineTests/TxTimingTests.swift index b592af07..bde8ebe5 100644 --- a/ios/FT8AFKit/Tests/FT8EngineTests/TxTimingTests.swift +++ b/ios/FT8AFKit/Tests/FT8EngineTests/TxTimingTests.swift @@ -41,6 +41,37 @@ final class TxTimingTests: XCTestCase { XCTAssertEqual(TxTiming.lateStartClipMs(msIntoCycle: 100, slackMs: -500), 100) } + // MARK: shouldSkip — the tolerance is a SKIP threshold, never the clip slack + + func testShouldSkipWhenClipExceedsTolerance() { + // clip(3000) = 640; tolerance 500 → too much leading audio lost, skip. + XCTAssertEqual(TxTiming.lateStartClipMs(msIntoCycle: 3000), 640) + XCTAssertTrue(TxTiming.shouldSkip(clipMs: 640, toleranceMs: 500)) + // Same clip within tolerance → transmit (clipped). + XCTAssertFalse(TxTiming.shouldSkip(clipMs: 640, toleranceMs: 640)) + XCTAssertFalse(TxTiming.shouldSkip(clipMs: 640, toleranceMs: 2360)) + } + + func testShouldSkipOnTimeStartNeverSkips() { + // clip(800) = 0 always → never skipped, whatever the tolerance. + XCTAssertEqual(TxTiming.lateStartClipMs(msIntoCycle: 800), 0) + XCTAssertFalse(TxTiming.shouldSkip(clipMs: 0, toleranceMs: 0)) + XCTAssertFalse(TxTiming.shouldSkip(clipMs: 0, toleranceMs: -100)) + XCTAssertFalse(TxTiming.shouldSkip(clipMs: 0, toleranceMs: 2360)) + } + + func testShouldSkipZeroToleranceSkipsAnyClippedStart() { + XCTAssertTrue(TxTiming.shouldSkip(clipMs: 1, toleranceMs: 0)) + XCTAssertTrue(TxTiming.shouldSkip(clipMs: 640, toleranceMs: 0)) + } + + func testShouldSkipClampsNegativeTolerance() { + // A nonsensical negative tolerance behaves like 0, not like a + // magically-permissive threshold. + XCTAssertTrue(TxTiming.shouldSkip(clipMs: 1, toleranceMs: -500)) + XCTAssertTrue(TxTiming.shouldSkip(clipMs: 640, toleranceMs: -1)) + } + func testClipSampleCount() { XCTAssertEqual(TxTiming.clipSampleCount(clipMs: 0, sampleRate: 12_000), 0) XCTAssertEqual(TxTiming.clipSampleCount(clipMs: 640, sampleRate: 12_000), 7680)