Changelog

What's new in Carrier Wave — iOS, macOS, firmware, and server.

1.93.0

2026-05-30

Added

  • Radio macros (iOS). Build, share, and run reusable CAT routines for your rig — tune-and-set-mode, voice memory triggers, RIT/XIT, CW templates with live preview, vendor-command picker. Enable under Settings → Experiments → Radio CAT Control → Macros. Imports .cwmacro files via AirDrop / Files / Finder.
  • Macros Browse + Updates (iOS). The Macros library's Browse tab lists community-curated starters (POTA hunter, tuner cycle, voice memory, more) — install with one tap. Updates tab alerts you when an installed macro has a newer revision and shows a per-field diff before applying.
  • Public macros catalog at carrierwave.app/manual/macros. Every approved macro from the activities server now has a public page with capability chips, a human-readable step list, args table, and an “Open in Carrier Wave” deep link.
  • Contest as a first-class Sessions intent (iOS). The Sessions tab's 2×2 intent grid is now Activate / Hunt / Casual / Contest. Tap Contest to pick a definition and start scoring in one flow. Standalone Contests destination removed from the Activity tab.
  • Yaesu CAT vendor catalog. Vendor command picker now ships 15 common Yaesu CAT commands (VFO/mode/PTT/power/split/RIT/XIT/preamp/AGC) for FT-857/897/817/991(A) and FT-DX10/710/DX101 generations.

Changed

  • Richer contest picker (iOS). Selecting a contest from session start or QSO edit now opens the full calendar/catalog browser with Live-now and schedule captions, replacing the thin list picker.
  • Sessions intent picker graduated from Experiments (iOS). The intent grid is now the canonical landing for the Sessions tab whenever there's no active session.

Fixed

  • Add Step no longer dismisses the macro editor (iOS). Nested-sheet collapse fixed so adding or editing a step keeps the editor open.
  • Vendor command catalog bootstraps at app launch. The Vendor Command step picker now lists CI-V, Kenwood/Elecraft, and Yaesu commands from first launch instead of showing the empty state.
  • Yaesu vendor commands encode as ASCII CAT. Yaesu commands now route through ASCII (FT-857 onwards) instead of being lumped with CI-V binary encoding. Vintage FT-1000-style binary CAT can still be sent via the raw-bytes step.

CW Sweep 0.13.0

2026-05-28

Added

  • Sessions sidebar with intent picker (macOS). The Logger sidebar is now “Sessions” and lands on a 2×2 intent grid — Activate / Hunt / Casual / Contest — with a Continue strip of paused sessions below.
  • Contest as a first-class Sessions intent (macOS). Tap Contest, pick a definition, and the live operating UI opens inline with the score header, exchange bar, and Cabrillo export on the toolbar. The standalone Contests sidebar item retired in the same release.
  • End / Pause from the toolbar (macOS). The active session now sits next to the layout picker with End and Pause actions. The same commands appear in Cmd+K under “Active Session.”
  • Pre-unification log auto-grouped (macOS). Loose QSOs from before this release are silently grouped into per-day Casual sessions on first launch, so every record lives under a session.
  • Radio macros parity (macOS). CW Sweep gains the full macros experience — library, editor, run window, Finder drop, and cwmacro:// deep links — bringing the iOS feature to the desktop.

Fixed

  • Contester scoreboard stays put (macOS). The contester layout pins the score panel to a slim fixed slot so it no longer drifts as the log fills.
  • Manual frequency / mode changes stick without a radio (macOS). Cmd+Shift+P radio palette, band-map click-to-tune, and vim :freq now update app state and the active session even when no radio is connected.

1.92.0

2026-05-22

Added

  • RST Sent auto-advances to RST Received. Once the value you’re typing into RST Sent reaches the mode’s expected length — 3 characters for CW/digital, 2 for phone — focus jumps to RST Received. Only fires on the upward edge, so editing or shrinking the value doesn’t re-trigger.
  • Hunter spot monitor adds 6m FT8. The PSK Reporter hunter fetch now rotates through 6m (50.313 MHz ±3 kHz) alongside 20 / 40 / 15 / 10 / 17, so VHF sporadic-E openings surface in the hunter spot stream without switching sources.

Changed

  • Unified Day Summary on activity-grid taps. Tapping a cell in the dashboard’s activity grid now opens a full day view: hero stats, band/mode pills with counts, a swipeable insights carousel (map + US states mosaic) covering every QSO on the day, a band timeline, and a Sessions section listing every session that overlapped the UTC day. Sessions that straddle midnight are sliced on both days with a continued/continues chip. Long-press still shows the old quick popover.
  • Hunter Log day summary mirrors session detail. The Hunter Log’s daily summary switched from grey card panels to a List-based layout matching the session detail screen — same hero, same swipeable insights carousel, same band timeline, same QSO list primitives. Both screens now render from one shared set of components, so touching either no longer drifts the other.

Fixed

  • Clear All QSOs now restores from iCloud. Wiping the local logbook used to leave the device empty even with sync on, because the CKSyncEngine change token survived the wipe and the next fetch saw the cloud journals as “already applied.” The wipe path now resets the sync state and rotates the device ID, so the engine re-bootstraps from the cloud snapshot and replays every journal — the same way a fresh install would. Disable sync first if you want a clean wipe.
  • Legacy OP: segments scrubbed from existing notes. QSOs created before the May 6 fix that stopped emitting OP: <name> still carried the prefix baked into their local notes. A one-time background pass on next launch removes those segments from non-user-authored QSOs, clearing the field when nothing else remains.
  • Hunter Log sort affects Club Members too. The Club Members section was re-sorting alphabetically by callsign, ignoring the user’s Recent / Frequency / Needs selection — which read as “sort doesn’t work” because Club Members render above Other Spots. Removed the hardcoded sort so the section inherits ordering from the rest of the list.
  • FT8 “input too high” warning no longer gets stuck. Four converging causes fixed: an out-of-bounds decimator read producing phantom peaks, wide-band peak detection latching on adjacent-channel signals, missed audio route changes keeping clipped-peak history alive against a new device, and stale main-actor timestamps collapsing queued loud samples onto the current second.
  • Carrier Wave Status Bar on CW Sweep now matches the Sync Diagnostic Report. The QSO count in Sweep’s status bar was deduping by UUID, but CloudKit’s private + shared zones can produce mirror rows with the same content and different UUIDs, inflating the displayed total. Switched to dedup by content key and routed metadata-mode filtering through the project’s single source of truth.

1.91.0

2026-05-19

Added

  • Weather Radar overlay on the Map tab. The Weather Radar experiment now extends to the Parks & Summits map — a cloud-rain toolbar button toggles a NEXRAD CONUS base-reflectivity overlay alongside park polygons, trails, and pins. The overlay pans and zooms in lockstep with the basemap, an opacity picker (25 / 50 / 75 / 100 %) tunes the look, and an NWS active-alerts chip surfaces severity-tinted warnings for the visible area. US only.
  • Per-radio connection profile + session auto-connect. Each radio in your inventory can now store its own connection profile — transport (BLE / WiFi / LAN), protocol, saved device, host/port, and CW Bridge routing. Picking a radio at session start auto-connects using that profile instead of the global Radio settings. A one-shot migration moves your existing Radio settings onto the matching inventory item.
  • Propagation Check detail view, redesigned. Tapping a saved propagation check now opens a richer results screen: an inline map with geodesic arcs from your grid to each skimmer (band-colored pins, “You” pin at home), an azimuthal/standard map toggle, a newest-first spots list with callsign / band / source / SNR / distance, and stats that honor HamDB-resolved skimmer grids. The map card honors your share-card map theme.
  • Notes & equipment on propagation checks. Each saved propagation check can carry notes plus the radio / antenna / key / mic you used, edited via the same pickers the session metadata sheet uses. Hidden on the detail view when nothing is set.
  • CAT macros (foundation). New macro subsystem in the shared CarrierWaveData package — a model for sharable .cwmacro documents, a template engine for {TOKEN} / {name:format} placeholder expansion (BCD, zero-padded decimal, hex), and a vendor command catalog bundled as JSON so adding an Icom or Kenwood command no longer requires a release. Editor and run surface are follow-up work — no UI yet.

Changed

  • Faster Parks & Summits map. Map clustering moved off the main thread via an actor-backed quadtree (ClusterMap), so panning and zooming the full 22 000-pin POTA + SOTA set no longer freezes the UI. The map only ever sees the visible cluster bubbles plus de-clustered leaves for the current zoom level — cluster bubbles are green for parks-only, brown for summits-only, teal for mixed, and tap zoom-to-fits the underlying members.

Fixed

  • Keyer bar self-heals on bridge reconnect. If the CW Bridge’s initial USB-status push was dropped or raced during handshake reconnect, the keyer row stayed hidden in CW sessions until the user kicked something. Field and Sweep now both interrogate the bridge for status immediately after handshake and again every three seconds while framed, so missed pushes self-heal within one tick.
  • iCloud restore on a new device no longer fails with “backup file not found.” On a fresh device, iCloud Drive replicates the backup directory listing but not the file contents until something asks for them. Field now downloads the backup database and session photos from iCloud and waits for the download to complete (up to 180 s) before validating, with a per-file progress overlay so the user sees N of M files ready instead of a frozen screen.

1.90.0

2026-05-09

Added

  • New Hunt widget. Small home-screen widget showing propagation rating, K/A/SFI gauges, the four band-condition pairs, and a binoculars badge with the live count of active POTA + SOTA + RBN spots. Tap to jump straight into the Hunter Log — works whether or not you have a session running.
  • Solar widget surfaces a space-storm summary line. The Solar widget’s small variant now shows a one-line headline beneath the K/A/SFI gauges describing the worst active space-weather event (geomagnetic storm, solar flare, CME) with an impact-tinted icon. Lock-screen rectangular and inline variants pick up the same headline. When everything is calm the line reads “All quiet” with a shield icon.

Changed

  • Session END button reads “END” (all caps). Both portrait and landscape session-header end buttons bumped to all caps to match the visual weight of the red destructive treatment.
  • Solar widget tap no longer auto-opens the On Air Now sheet. Tapping the Solar widget previously navigated to the Dashboard and immediately presented the OnAirCard pull-up sheet, which made it impossible to enter the app from the widget without dismissing a sheet first. The deep link now just selects the Dashboard.

Fixed

  • Dashboard scroll and Grid Tracker typing no longer chop on tab return. Five-cause performance regression diagnosed and fixed: friendship queries in three On-Air cards no longer invalidate on every unrelated SwiftData save; dashboard recompute now serialises its four full-table scans instead of running them concurrently and waits for the tab transition to finish; the On Air Now panel skips re-fetching solar/DX/spot data within 60 s of the last load; Grid Tracker filter/sort is debounced and cached per input change instead of recomputing per keystroke (and per visible row); the Grid Heatmap Canvas only redraws when the underlying data changes, not on every keystroke in the search field above it.
  • iPad sidebar settings now match what’s actually shown. On first launch on iPad, the tab settings screen listed CW / Activity / Events / Remote under “Hidden” while the actual sidebar still showed them — the two were reading different default sets. They now agree, and the sidebar column has been tightened to a more compact width so the detail pane has more horizontal room.
  • Ended sessions no longer resurrect themselves after kill/relaunch. Long-standing bug where a session ended on iPhone would come back as the active session on the next launch — even after multiple ends. Root cause was a four-layer leak in the continuity layer: stale LAN snapshots from CW Sweep on Mac (or another peer) could replay an “active” view onto the just-ended local record, clearing endedAt and flipping status back to active. The fix refuses to overwrite a locally-ended session from any remote source, and heals corrupt records on cold launch. The user’s deliberate end-session action now always wins.
  • Activation Suggestion widget now opens directly to Sessions. Previously the widget had no deep link, so tapping it fell through to whatever tab was last selected (often Dashboard). It now routes to the Sessions tab, matching the Active Session widget’s behavior.

1.89.0

2026-05-07

Added

  • Activation start wizard auto-suggests the 3 nearest references within 10 mi. Tap POTA, SOTA, or WWFF in the session wizard and the closest references appear instantly — no need to open the search picker first. GPS-first with a grid-square fallback. The picker (magnifying-glass button) is still there for searching by name or going beyond 10 mi.
  • Inline S2S Summit field for SOTA logging. The Logger expanded fields row now shows a Summit field for SOTA sessions in place of Park, so summit-to-summit references no longer require a trip to the QSO edit sheet. Dual POTA + SOTA sessions show both: Summit on the primary row next to Grid, Park beneath.
  • SOTA session details get Brag Sheet and Export ADIF. SOTA-only and any non-POTA session now have full action menus. The brag sheet is program-neutral — the same card renders POTA, SOTA, and casual sessions with the right SF Symbol and reference type. Per-session ADIF export emits MY_SOTA_REF for SOTA sessions, both MY_SOTA_REF and MY_POTA_REF for dual sessions, and S2S/P2P tags from each QSO’s own references.
  • SOTA self-spotting and multi-program SPOT dispatch. SPOT previously hardcoded POTA; sessions activating SOTA only, or dual POTA + SOTA, returned an error. The command now dispatches per-program: POTA spots fan out across every park reference (n-fer behaviour preserved), SOTA spots one self-spot to SOTAwatch. SPOT REPEAT and the QSY auto-spot use the same dispatcher, so SOTA-only sessions also get repeating and post-QSY spots.
  • 8-character Maidenhead grid option for MY_GRIDSQUARE. New Logger setting picks 4 / 6 / 8 character precision. 8-character grids matter for VHF/UHF and grid-square contests where 6-character is too coarse. The same precision is honoured wherever Carrier Wave derives a grid — POTA park / SOTA summit coordinates, GPS, manual entry — and round-trips through the parser unchanged.

Changed

  • SOTA / POTA session grid pinned to summit/park surveyed coordinates. Session start used to fall back from a caller-provided myGrid straight to GPS — unreliable at session start with a cold fix, indoors, or with the phone in a backpack at the summit. The Logger now resolves session.myGrid in priority order: caller-provided → SOTA summit lat/lng → POTA park lat/lng → GPS, honouring your grid precision preference.
  • SOTA activation threshold is 4 QSOs, not 10. The Logger session-header QSO chip and the watch’s activation progress ring both hardcoded 10, so SOTA-only sessions never hit their actual qualifying line. iOS, watch, and widgets now read one shared LoggingSession.activationThreshold source (POTA = 10, SOTA = 4, WWFF = 44, lowest of any active program).

Fixed

  • Nearby-spot frequency popup no longer reappears after dismissal. The frequency-warning banner used the warning’s message string as its dismissal key and cleared the dismissed set on every session.frequency / session.mode change. Any retune, mode auto-switch, or session-model re-emission of the same value cleared the dismissed set and the popup popped right back. The banner now uses a stable dismissal key (spot:<activator>:<reference> for spot warnings) and the dismissed set is cleared only when the active session id changes.
  • Reject Upload / Reopen Session confirmations now appear from the session detail view. Both confirmationDialog modifiers were attached to the parent Sessions list, so when the user tapped these from inside a pushed session detail view, the dialog stayed dormant until the user popped back. The session detail view now owns its own confirmations and runs the action immediately.

1.88.0

2026-05-07

Added

  • iOS Remote pane redesigned as a control surface. The Form-with-sections layout is gone. The Radio pane now has a hero VFO display (tabular monospaced digits, MHz.kHz at 56pt and a smaller Hz triplet), a TX/RX dot LED next to the connected Sweep name, an audio strip with a chunky speaker button + thick volume slider, and an inline tune entry. The nested TabView for Radio/Scope/Waterfall/Spots/CW that collided with the system tab bar is replaced by a horizontal capsule chip strip mounted under the nav title.
  • RX audio over the Remote LAN. CW Sweep now forwards the radio’s RX audio to Carrier Wave: Field over the new radio.audio RemoteOp topic. Field hosts a foreground-only audio player with a speaker toggle and volume slider in the Remote Radio pane; subscription is gated on the toggle so Sweep skips encoding when no client is listening. An opt-in Opus codec drops bandwidth from ~256 kbps PCM to ~24 kbps — meaningful on cell uplink.
  • Tap straight key on the iOS Remote. New pad in the Radio sub-tab: tap for a dit, hold past 180 ms for a dah. Each gesture fires a single CW element through Sweep’s keyer. The remote keyer path is now transport-agnostic — previously bridge-only, it now routes through CW Bridge → WinKeyer → the radio’s CAT keyer (CI-V 1A 02, Kenwood/Elecraft/Yaesu KY) automatically.
  • Callsign membership chips for CWops, BKG, and SKCC. Carrier Wave now ships a daily-refreshed offline cache of public membership lists. The callsign card and QSO detail view show inline chips for each match (e.g. 🎹 CWops #1532, 🤜 BKG #29, 🔑 SKCC #6707T). Lookups are local once cached, so chips appear instantly during contesting with no per-callsign network call.
  • CWT contest strike card. During CWT (and any contest with member-list enrichment) the separate QRZ card + RST/QTH row + contest exchange card collapse into one dense card that fits above the keyboard fold: callsign + membership chips + dupe / NEW MULT badge in the header, a single status line summarising name · state · grid · distance · bearing · prior-contact-count, two big monospaced exchange inputs (NAME and #/S/P/C, 50pt tall) for thumb-targeting at 100 QSOs/hr, and a tertiary 47Q · 47P · 12M · 564 score footer.
  • CWT auto-fill for non-CWops members too. Per CWT rules, non-members send first name + state / province / DXCC prefix. Stations with no CWops row now get both fields prefilled: name from QRZ (preferring the <nickname> tag, ALL CAPS), and identifier from W/VE state/province or the DXCC primary prefix derived from the callsign. CWops member numbers still take precedence when present.
  • {MYNAME} keyer macro. New macro that expands to the operator’s first name (e.g. TU {MYNAME}TU JAY). Mirrored from the user profile into UserDefaults["loggerDefaultName"] and synced via iCloud KVS alongside the callsign, with a one-shot keychain backfill on the keyer settings screen for users who saved their profile before this change.
  • Enriched share link landing pages with great-circle map and club chips. The /link/{call,park,sum,qso,act}/:id share-URL routes now ship a simplified equirectangular world map with an orange great-circle arc and glowing endpoint dots, plus a parallel fetch against the callsign-enrichment API that renders inline membership pill chips. Brand styling is fully Carrier Wave-themed (orange/cyan accents, inline wave-logo SVG in the brand bar, signal-gradient page background). The QSO/activation/callsign/park/summit fragment is still never sent in HTTP requests — the page only carries opaque path tokens.

Fixed

  • {MYCALL} keyer macro now resolves to the operator’s callsign. The CW Messages editor and slot-row preview were reading the wrong UserDefaults key (stationCallsign instead of loggerDefaultCallsign), so previews always fell back to the placeholder regardless of the operator’s profile. Both views now read the canonical key.
  • CWT auto-fill clears name and number when the callsign is cleared. The matcher returns the fields it wrote so the logger tracks auto-filled values and can clear them when the callsign input goes empty — no more lingering data from the previous QSO.
  • iOS Remote waterfall stretched lines, washing out the time axis. Each waterfall row now renders at exactly 1 pt tall (newest at top, older lines push down by 1 pt each frame), with the rest of the canvas left empty when the 200-line history hasn’t filled it yet. Restores the natural arrival cadence so an operator can see how recent a signal is from how far down the screen it sits.
  • Server callsign-enrichment endpoints returning 500 instead of data. /v1/callsign-enrichment/{callsign} and /dataset were using the wrong Axum extractor (Extension(pool) while the rest of the v1 router supplied the connection pool via with_state(pool)), so both handlers responded with “Pool extension missing”. Switched both to State(pool).
  • iOS FT8 mode-toggle race that left FT8 silently broken until force-quit. Toggling Logger mode away from FT8 fired a fire-and-forget Task { await ft8Manager.stop() } while immediately nilling the manager. FT8AudioEngine.stop() ended with setActive(false) on the process-wide audio session; toggling back to FT8 before that finished meant the new manager’s setActive(true) was raced in by the old setActive(false), leaving a black waterfall with no decodes. The audio session belongs to the process, not the FT8 component — stop() no longer deactivates it.
  • iOS FT8 start failures were silently swallowed. An unsupported sample rate (Bluetooth/USB-C route the OS picked), mic permission denied, or any other init failure used to leave the user staring at a black waterfall with no diagnosis — the view did try? await ft8Manager.start(). The session view now renders an orange error banner with a Retry button driven by a new retryStart() path that fully tears down the engine before re-running configure + start; FT8AudioEngine.start() also cleans up partial state on throw so the retry has a known-good baseline.
  • iOS FT8 waterfall FFT moved off the main thread. The per-buffer FFT + per-bin percentile sort (the recent low-band yellow-block fix) was running on @MainActor, ~6 times/sec, on the same thread as SwiftUI rebuilds and TX state transitions in the conversation card — a plausible contributor to the freeze-on-answer reports. Split FT8WaterfallData into a small @MainActor @Observable published-state class plus a new FT8WaterfallProcessor actor that owns the FFT setup, Hann window, and pending-sample buffer. The per-bin reference algorithm (window size, percentile, sliding sort) is byte-for-byte identical to the existing fix.

CW Sweep 0.12.0

2026-05-05

Added

  • CW Sweep updates itself. Auto-update via Sparkle 2 — “Check for Updates…” lives in the CW Sweep menu, and a daily background check pulls new releases straight from carrierwave.app/sweep/appcast.xml the day they’re notarized. DMGs are Ed25519-signed and verified before install. The new Sweep download page covers fresh installs.
  • Sessions sync between Field and Sweep automatically. Start a POTA, SOTA, or contest activation on either device and the other picks it up without pressing Sync, Connect, or pairing. Three layers cooperate so this works whether the devices are co-located or apart — CloudKit-side active-session normalization, silent LAN auto-reconnect, and a new activeSession RemoteOp topic. Marked alpha while the lifecycle plumbing settles.
  • Waterfall is a first-class panel. The previously orphaned waterfall view can be dropped into any custom CW Sweep layout via the layout editor. Source-agnostic — rendering reads from a shared WaterfallStore any radio producer can push into, with a placeholder state that explains which radios produce spectrum data.
  • FlexRadio (SmartSDR) panadapter ingestion. CW Sweep binds a UDP listener, requests a panadapter from SmartSDR over the existing TCP control session, and decodes incoming VITA-49 frames into normalized spectrum lines that feed the shared waterfall store. Defaults to 1024 bins / 100 kHz / 10 fps; geometry follows the active slice frequency.
  • Distance and bearing in the Query Workbench. New cw_* SQL functions: cw_distance_km / cw_distance_mi / cw_bearing_deg(lat1, lon1, lat2, lon2), grid-aware versions (cw_grid_distance_km, cw_grid_bearing_deg), user-relative helpers (cw_user_grid(), cw_user_distance_km(lat, lon), …), and cw_now() for time math. Two starter queries demonstrate “spots sorted by nearest” and “spots heard by nearby spotters”.
  • Spot coordinates denormalised in scope.spot_observations. New spot_lat / spot_lon columns are populated at observation-write time, so distance queries don’t need to join through anything to compute the user → DX great-circle distance.

Changed

  • Query Workbench polish. Full-width / full-height results table that pins to top-left, alternating row striping, click-to-sort column headers (three-state with stable sort and NULLs last), and a Copy CSV button. The SQL editor gained code-editor key behaviours: Tab inserts four spaces, Backspace at a 4-column boundary removes four leading spaces in one keystroke, and Shift-Tab dedents.
  • Sync progress bar actually moves. CW Sweep advances the Uploading… N remaining count after each successful CloudKit batch instead of staying frozen at the initial dirty count for the whole sync.

Fixed

  • Tune commands silently dropped on every non-CW-Bridge transport. Frequency, PTT, XIT, and RIT-offset commands had been no-ops on Flex TCP, serial CAT, IcomWiFi, and SCU-LAN10 since the April set-and-verify refactor — only the CW Bridge implements the verified-set frame, and every other transport returned UNSUPPORTED so nothing reached the wire. CW Sweep now falls back to encoding via the protocol handler and writing raw bytes whenever set-verify reports the field as unsupported, restoring pre-refactor behavior.
  • VFO no longer jumps powers of 10. After the CW Bridge auto-detected the radio and swapped protocol handlers, two RadioSessions were both consuming the shared BLE byte stream and decoding partial frames. CW Sweep now cancels the old session’s tasks (without tearing down the transport) before installing the new one.
  • Big sync no longer roasts the device. Fixed a runaway loop where every 10-record CloudKit batch save kicked off a fresh full scan of all dirty rows and rebuilt CKRecords from scratch. Force Full Sync now drains progressively without burning the battery.
  • Hunter Log spot rows no longer wrap. Tightened so each spot stays a single line, with club & award memberships rendered in a secondary row underneath instead of pushing the primary fields onto a second line.
  • Scroll-wheel theft. Scrolling the Query Workbench schema browser no longer steals events from the workspace band scope. The Query Workbench schema browser also scrolls correctly inside its split pane now.
  • received_at stored as a number. Spot observations now write received_at as Unix epoch seconds REAL (matching cw_now() and last_seen_at) instead of a formatted text string, so SQL like cw_now() - received_at returns sensible ages.

1.87.0

2026-05-04

Added

  • Sweep waterfall on the iOS Remote tab. CW Sweep now broadcasts its active waterfall over the LAN at up to 10 fps, and Carrier Wave: Field renders it in the Remote tab strip with the same color ramp as the macOS panel. The iOS view only subscribes while visible, so background tabs don’t pay the bandwidth cost.
  • Waterfall is a first-class CW Sweep panel. The previously orphaned waterfall view is now exposed as PanelIdentifier.waterfall and can be dropped into any custom Sweep layout via the layout editor. Source-agnostic — rendering reads from a shared WaterfallStore any radio producer can push into.
  • FlexRadio (SmartSDR) panadapter ingestion. CW Sweep now binds a UDP listener, requests a panadapter from SmartSDR over the existing TCP control session, and decodes incoming VITA-49 frames into normalized spectrum lines that feed the shared waterfall store. Defaults to 1024 bins / 100 kHz / 10 fps; geometry follows the active slice frequency.

Fixed

  • CW Sweep tune commands silently dropped on every non-CW-Bridge transport. Frequency, PTT, XIT, and RIT-offset commands had been no-ops on Flex TCP, serial CAT, IcomWiFi, and SCU-LAN10 since the April set-and-verify refactor — only the CW Bridge implements the verified-set frame, and every other transport returned UNSUPPORTED so nothing reached the wire. Sweep now falls back to encoding via the protocol handler and writing raw bytes whenever set-verify reports the field as unsupported, restoring pre-refactor behavior.
  • iOS FT8 overload banners firing under normal operating conditions. Three compounding issues fixed: the “too hot” RMS threshold was tuned for clipping not normal headroom; there was no hysteresis so banners flapped; and the waterfall used a fixed magnitude scale that painted hot-but-unclipped audio entirely red. The waterfall now renormalises to dB-above-noise over a 30 dB span (matching WSJT-X) and the overload tracker uses split enter/exit thresholds with a 1.5 s dwell. The audio engine also re-installs the input tap on USB-C DAC route changes mid-session.
  • Quick entry accepts more UTC time formats. Typing W1AW 14:30 in a quick-entry field now sets the timestamp override to 14:30 UTC. The parser accepts HH:MM, HH:MMZ, HHMMZ, and bare HHMM (e.g. 0930, 1430) — matching how the paper-log scanner has always read handwritten times. Bare 4-digit values that are also valid amateur frequencies (e.g. 1804 on 160m) keep their frequency interpretation; append Z to force a time.

1.86.0

2026-05-04

Added

  • Phone-to-Mac remote control (alpha). Pair Carrier Wave: Field with CW Sweep over your local network. The phone shows live frequency, mode, and TX state from Sweep, plus the curated spot feed; tap a spot or type a frequency to tune the radio. Includes a CW keyer pane that triggers Sweep’s slot keyer, with Sweep expanding macros ({CALL}, {HISCALL}, {RST}, {NAME}, {HISSTATE}, {FREQ}, {MYCALL}) from its Quick Entry box. LAN-only over plain TCP with token auth; TLS, push, voice, and audio streaming are follow-ups. Hidden by default — enable from Settings → Experiments as Remote Control of CW Sweep.
  • CW keyer presets. The new PRESETS tile in the Remote CW pane programs slot templates in a single tap. Built-in POTA preset writes CQ CQ POTA DE {MYCALL} K into slot 1 and BK RR TU {HISSTATE} ES 73 EE into slot 2.
  • Distance and bearing in CW Sweep’s Query Workbench. New cw_* SQL functions: cw_distance_km / cw_distance_mi / cw_bearing_deg(lat1, lon1, lat2, lon2), grid-aware versions (cw_grid_distance_km, cw_grid_bearing_deg), user-relative helpers (cw_user_grid(), cw_user_distance_km(lat, lon), …), and cw_now() for time math. Two starter queries demonstrate “spots sorted by nearest” and “spots heard by nearby spotters”.
  • Spot coordinates denormalised in CW Sweep. scope.spot_observations now stores spot_lat / spot_lon at write time, so distance queries don’t need to join through anything to compute the user → DX great-circle distance.

Changed

  • Remote CW pane redesigned for “phone as dashboard accessory”. Slot buttons are now an adaptive 2–4 column grid of large tiles — bold slot label on top, monospaced template text below — with matching tiles for STOP, CUSTOM, and PRESETS. Empty slots render as ghost + Slot N tiles.
  • Remote CW reads its macro context from CW Sweep. Removed the parallel current-QSO entry strip on iOS — Sweep’s Quick Entry box is now the single source of truth for CW macro values, and the iOS keyer just sends the slot id for Sweep to expand at play time.
  • FT8 audio level meter. Replaced the No Signal / Low / OK / Hot status pill with an inline 8-pip level meter that lights green → yellow → red as the live RMS climbs, with a peak indicator dot when the buffer hits clipping.
  • Sweep Query Workbench polish. Full-width / full-height results table that pins to top-left, alternating row striping, click-to-sort column headers (three-state with stable sort and NULLs last), and a Copy CSV button. The SQL editor gained code-editor key behaviours: Tab inserts four spaces, Backspace at a 4-column boundary removes four leading spaces in one keystroke, and Shift-Tab dedents.
  • Sync progress bar actually moves. CW Sweep and iOS now advance the Uploading… N remaining count after each successful CloudKit batch instead of staying frozen at the initial dirty count for the whole sync.

Fixed

  • POTA Activations crash (iOS). Fixed an EXC_BREAKPOINT that could hit the POTA Activations list when iCloud sync arrived mid-render. Per-activation upload status (pendingCount, uploadedCount, …) is now precomputed at struct init and the activations list is cached, so view body reads never traverse SwiftData while CKSyncEngine is merging.
  • FT8 “Call CQ” menu (iOS). The CQ / CQ POTA / CQ DX / CQ SOTA menu options now actually open the channel-picker sheet on iOS 18+ — previously the menu’s dismissal animation raced with the sheet state and the sheet never presented.
  • FT8 false “audio is clipping” banner (iOS). The overload tracker now uses peak magnitude instead of RMS, so the critical warning only fires when ~40% of recent buffers actually clip at digital full-scale — not whenever the rig was hooked up at any reasonable level.
  • CW Sweep VFO no longer jumps powers of 10. After the CW Bridge auto-detected the radio and swapped protocol handlers, two RadioSessions were both consuming the shared BLE byte stream and decoding partial frames. Sweep now cancels the old session’s tasks (without tearing down the transport) before installing the new one.
  • Big sync no longer roasts the device. Fixed a runaway loop where every 10-record CloudKit batch save kicked off a fresh full scan of all dirty rows and rebuilt CKRecords from scratch. Force Full Sync now drains progressively without burning the battery.
  • Friend requests no longer say “not found” after accepting. The accept endpoint is idempotent now — a duplicate accept (double-tap, retry, second device) returns the existing record at 200 instead of a 404. The iOS Accept/Decline buttons also disable while a request is in flight.
  • iOS Remote tune commands stuck after Sweep restart. The iOS client now cancels its TCP connection on send failure so a half-open socket doesn’t leave you on a zombie session; the keyboard also dismisses correctly after typing a tune frequency.
  • CW Sweep scroll-wheel theft. Scrolling the Query Workbench schema browser no longer steals events from the workspace band scope. The Query Workbench schema browser also scrolls correctly inside its split pane now.
  • CW Sweep received_at stored as a number. Spot observations now write received_at as Unix epoch seconds REAL (matching cw_now() and last_seen_at) instead of a formatted text string, so SQL like cw_now() - received_at returns sensible ages.

CW Sweep 0.10.0

2026-05-02

Added

  • CW Bridge over Bluetooth (macOS). Pair the Carrier Wave CW Bridge from CW Sweep over BLE — scan, pair, connect, and forget from a dedicated section in the Radio Control tab. The bridge auto-detects the attached radio (CI-V / Kenwood / Elecraft) and CW Sweep hot-swaps to the right protocol handler. Live status card shows firmware version, USB presence, detected radio, handshake state, and keyer availability.
  • Server-published contests (macOS). Contest definitions now come from the server (format v0.3) instead of bundled templates — same source of truth as iOS, with shared scoring, dupe checking, exchange parsing, and Cabrillo export from a single ContestDefinition model.
  • SKCC member badges (macOS). Spots from Straight Key Century Club members display the operator’s number and award level (Centurion / Tribune / Senator) in the spot inspector header.
  • Share callsigns and QSOs (macOS). Share enriched callsign / QSO / park / SOTA links via the macOS share sheet. Recipients with Carrier Wave installed deeplink straight into the matching detail view via https://carrierwave.app/link/….
  • What’s New sheet (macOS). Release notes now ship in CW Sweep — presented on first launch after an update, suppressed on fresh install.

Changed

  • Band scope redesign (macOS). Structured rows with diagonal leader lines from a linear frequency axis, row displacement for crowded spots, vim-style keyboard navigation (j/k spots, J/K mults, h/l pan, ? help), right-click context menu (tune, copy callsign, QRZ lookup, watch marker), fit-to-spots zoom (0 / FIT), and a keyboard shortcut overlay.
  • iCloud sync is journal-only. Removed the per-record CKSyncEngine path on both iOS and macOS. CW Sweep now uses only the journal engine: dirty records batch into ChangeJournal records plus a periodic SQLite snapshot for new-device bootstrap. The Sync Report / Sync Repair UI is gone in favor of the simpler model. CW Sweep also re-fetches journal entries when the window comes forward (didBecomeActive) so QSOs logged on iOS appear on Mac the moment you focus the app.

Security

  • BLE pairing now requires LE Secure Connections + MITM passkey. The CW Bridge generates a random 6-digit passkey on each first-pair attempt and shows it on the OLED; macOS prompts you to enter it. Bonds persist on the bridge so paired Macs reconnect silently. Closes the window where an unbonded peer could inject CAT commands or snoop USB / jack / radio-info broadcasts.

1.85.0

2026-05-02

Added

  • Tiles on the Air integration & scoring. Tag a session as TOTA at start and Carrier Wave detects the activated 6-character Maidenhead tile(s) from your GPS track, computes a four-tier score ledger (Confirmed / Estimated from spots / Asserted / Pending server-confirmed), and ships an ADIF upload path. Standard and Mobile categories, QRP ×2, winter / ruck / elevation / pets / kids percentages, Winlink / APRS / VarAC flat bonuses, and Tile-to-Tile (T3) detection from the spots feed are all there. See the manual and tilesontheair.com.
  • TOTA session detail view. Sessions tagged TOTA get a dedicated section with the tile name, score breakdown, path map (track polyline + tile boundary overlay) with GPX export, bonus chips, T3 candidate list, Personal Effort Factor badge, and a refresh-spots button.
  • Personal Effort Factor (PEF). Opt in from Settings → Tiles on the Air with a 1–10 stepper. PEF scales the distance term of your TOTA score on the Personal Effort leaderboard only — Standard ranking is unaffected. Frozen onto each session at start.
  • What’s New, on demand. Settings → About → What’s New opens the in-app release notes any time, not just on first launch after an update.
  • Brag sheets, redesigned. A new unified purple look for every session brag sheet (TOTA, POTA, contests, casual, periodic stats), driven from a shared theme. The TOTA brag sheet is the canonical card — tile name headline, score with ESTIMATED/OFFICIAL label, contact map (geodesic arcs from QTH), path map (track + tile boundary), bonus chips, eight-column stat grid (QSOs / Distance / Bands / Duration / DXCC / Farthest / Modes / Q/hr), equipment chips, and a mini timeline.
  • Brag sheet sharing copies the image. Sharing a brag sheet now produces a high-resolution PNG that lands in Copy Image / Save Image / AirDrop / Messages with maps and theming intact — same UX iOS already had for POTA share cards. Themed map renderers (midnight / warm / ocean / arctic / ember / azimuthal) snapshot correctly into the shared image.
  • Edit TOTA after the session. Edit Session now exposes TOTA category, distance, elevation, ruck weight, winter override, first-ever-tile, and bonus toggles. PEF stays read-only because the rules require it to be frozen at session start. The whole section locks once the activation has been submitted.
  • Distance-zero banner on TOTA score. When a TOTA session has QSOs but no distance recorded, the score ledger now shows an orange banner explaining that Standard scoring requires a non-zero distance — rather than reading 0 with no context.
  • TOTA icon on session rows. Sessions tagged TOTA show the tile-grid icon in the sessions list alongside the existing tree (POTA), leaf (WWFF), and mountain (SOTA) icons.

Changed

  • iCloud sync is journal-only. Removed the per-record CKSyncEngine path on both iOS and macOS. Both apps now use only the journal engine: dirty records batch into ChangeJournal records plus a periodic SQLite snapshot for new-device bootstrap. The "Journal Sync" toggle in Settings is gone — journal is the only path. Existing per-record CKRecords in your CloudKit zone are inert and will be cleaned up in a future zone wipe.
  • About → Version reads from the bundle. Replaced a hardcoded version string with a live read from the app bundle so the Settings screen always matches the running build.

Fixed

  • Frequency parsing with commas. Inputs like 14,060 now parse as 14.060 MHz instead of being silently dropped. Fixes en-AU/en-GB locales where the keyboard inserts commas.
  • Mid-session frequency edit. Tapping a logged QSO inside an active session now exposes Frequency and Band in the edit sheet — a CW operator can fix a wrong-frequency QSO without leaving the session. The band auto-derives from the frequency.
  • QSO edit auto-derives band. Editing a QSO in the Logs tab no longer requires picking the band manually after entering a frequency. Save is no longer disabled when the band picker is empty as long as the frequency parses to an amateur band.
  • iPad sidebar after Hunter Log. Sidebar tabs no longer get stuck after navigating Dashboard → Hunter Log. Detail-column nav stack now rebuilds on tab change.
  • Start Session sheet scroll jitter. The bottom warning banner no longer fights the scroll layout when scrolling all the way down then back up.
  • Hunter Log loading state. "Loading spots…" no longer sits indefinitely on first open. The first spot fetch fires immediately and switches to the empty state once it completes regardless of result.
  • WiFi CAT thermal/battery drain. Stopped two CPU-pinning loops in the Icom RS-BA1 transport when the radio is unreachable: UDP receive errors now back off 500 ms instead of busy-looping, and a failed connect tears down both UDP channels so failed retries no longer leak threads.
  • QRZ uploads off the main thread. Background QRZ uploads no longer hold the main actor; logger UI stays smooth during sync.

1.84.2

2026-04-29

Fixed

  • Apple Watch battery drain. Stopped a WatchConnectivity activate/deactivate loop that pinned a CPU core and drained battery on phones with no paired Apple Watch. The session re-activation that lets a newly-paired Watch take over is now throttled to once every 30s.

1.84.1

2026-04-27

Fixed

  • SOTA spot taps in HUNT. Tapping a SOTA spot in the HUNT panel now prefills the logger with callsign, frequency, and notes (summit ref, name, points). The tap was previously a no-op. FT8 in-session routing also recognizes SOTA spot taps.
  • N-fer ADIF split. Split ADIF export now emits one file per park for N-fer POTA sessions — a 5-fer produces 5 ADIFs, a 3-fer produces 3, each with the right MY_POTA_REF. The "Split by Park" toggle also appears whenever any QSO carries a multi-park reference.

1.84.0

2026-04-26

Added

  • HUNT command. Type HUNT N9HO 14060 (or include a mode) in the logger to log a one-off off-frequency QSO without changing your active session. Missing freq/mode falls back to the session. Bare HUNT still opens the activator-spots panel.
  • Files.app access. Carrier Wave now appears under Files → On My iPhone → Carrier Wave, exposing Documents/QSOLedger/*.adi for self-service ADIF recovery without the in-app share UI.
  • What's New sheet. First-launch-after-update sheet showing release highlights bundled with the binary — iOS and macOS each track their own last-seen version.
  • iOS data-loss debuggability. ContainerInitFailure record (timestamp, error, version, store/WAL/SHM file sizes, emergency-backup filename) now ships with the diagnostic bundle alongside every emergency-backup snapshot — future incidents arrive with the broken store + structured "why" attached.
  • CW Sweep: Full CW Bridge support over BLE (CWBP). New radio-model entry, Radio Control tab section for scan/pair/connect/monitor, BLE handshake → bridge auto-detection → protocol hot-swap (CIV / Kenwood / Elecraft).
  • Firmware: QRP Labs QCX and QMX radio support, TRS jack plug detection (mic/phone), FT8 encoding and decoding on the bridge with shared CFT8 codec.

Changed

  • Recovery Mode replaces silent ModelContainer wipe. If the database fails to open at launch, the broken file is preserved (and copied to Library/EmergencyBackup/) and a full-screen recovery view shows the failure record and three explicit actions: Restore from this backup, Send diagnostics, and Reset to empty database (typed-confirmation gate). The user is locked into recovery mode until they pick one. (CAR-246)

Security

  • Firmware BLE NUS pairing. Bridge now requires LE Secure Connections + MITM-protected pairing with a random 6-digit passkey shown on the OLED. Bonds persist in NVS for silent reconnect; the NUS RX characteristic requires authenticated encryption. Closes the window where an unbonded peer could inject CAT commands or snoop USB / jack / radio-info broadcasts.

1.83.0

2026-04-22

Added

  • Xiegu G90 full CI-V support. RIT/XIT offset set/clear and power level read/set for all CI-V radios (G90, IC-705, IC-7300). Kenwood/Elecraft power level via PC command.
  • CW memory keyer over BLE bridge. Eight configurable message slots with template variables and prosign support. Keyer button row in the logger, settings page with tappable macro pills and live preview, long-press inline editing.
  • Protobuf bridge protocol. Migrated BLE bridge from custom binary framing to protobuf serialization. Protocol version 2.
  • Horizon Survey (alpha). LIDAR-based 360° obstruction profile tool. Polar plot with takeoff-angle slider, blocked-bearing analysis, and gap guidance. Requires iPhone Pro with LIDAR.
  • Send Debug Info. Session detail action that gathers full session state, QSOs, service presences, spots, sync log, and ADIF into a shareable bundle.
  • Contests browser. Full catalog of server-published contest definitions in the Activity tab with live/upcoming times, schedule, rules summary, friend participation, and drill-into detail.
  • Server-published contest definitions (v0.3). Phase-based scoring engine, dupe checking, exchange parsing, Cabrillo export, off-time badges, and band-change violation counts — all driven from a single shared model.
  • Contest calendar enrichment. Live countdown, colored chips, contest detail sheet with Log Now / Remind / Add to Calendar actions, and full month-grid calendar view.
  • QSO editor overhaul. Editable operator callsign, collapsible sections, rich pickers for DXCC/mode/band/state/contest/parks, ADIF catalog with custom entry support, and Other ADIF Fields disclosure for imported logs.
  • RBN Lookup dashboard card. Check Reverse Beacon Network spots for any callsign from the dashboard without starting a session.
  • SKCC spot enrichment. Daily roster download tags spots with SKCC member number and award level on both iOS and CW Sweep.
  • Group QSOs into session. Select logged QSOs in the Logs tab and create a completed session. Smart defaults for activation type, callsign, and time range.
  • QSON export and safety ledger. Full log export in Ham2K-compatible JSON format with append-only monthly ledger files.
  • Enriched share links. Rich link previews for callsigns, QSOs, parks, and SOTA summits via iMessage/Mail/Notes. Deep links into the app.
  • CoreSpotlight indexing. QSOs and sessions searchable from the iOS home screen and Siri. Background reindex with deep-link tap-back.
  • Spotlight Debug (Developer). In-app view showing reindex state, indexed counts, and force-rebuild button.
  • Server: Contest visualizer at /contests, seed script for loading contest definitions.

Changed

  • macOS band scope redesign. Structured rows, diagonal leader lines, crowded-spot displacement, vim-style keyboard nav, right-click context menu, fit-to-spots zoom, keyboard help overlay.
  • Centralized polling timer. All periodic network polling consolidated behind a single 60-second timer for better battery life.
  • Onboarding simplified. Reduced to callsign-only entry. Service credentials and community registration moved to a persistent Getting Started card on the Dashboard.
  • Merged Callsign & RBN Lookup cards. Single dashboard card with unified search, enriched profile, and inline RBN spots.
  • QSO detail redesign. Hero card with callsign/name/badges, colored band/mode/freq pills, RST exchange, distance/bearing/efficiency strip, side-by-side station cards, activity chips, and quote-style notes.
  • Edit QSO tappable section headers. Collapsible sections now use explicit button headers with rotating chevrons.
  • Group into Session improvements. Shows which sessions block QSOs, with option to unlink and regroup. Orphaned QSOs auto-included.
  • Safety Ledger improvements. ADIF/QSON format pills, iCloud Drive mirroring for ledger files.
  • Daily Activity redesign. Day-at-a-glance QSO map, merged date header, full-fidelity QSO rows with enrichment.
  • Brag sheet map improvements. Antimeridian-aware viewport, gray line overlay, magenta trace lines, dot+halo pin style.
  • POTA uploads — fully manual via ADIF export. Upload buttons on the session list and activation rows now open the ADIF export sheet instead of uploading directly. Users save the file, then upload it themselves on pota.app. First-time instruction card walks through the steps. POTA upload page URL corrected to /user/logs.

Fixed

  • POTA upload ADIF validation. Comprehensive field validator catches invalid modes/bands before upload. Generic modes normalized to ADIF-compliant equivalents.
  • QRZ upload count mismatch. Credential checks aligned with ServiceConfiguration; missing QRZ presences backfilled.
  • RBN spotter-grid enrichment. Skimmer callsign suffixes now stripped before grid lookup so mini-map appears correctly.
  • iCloud sync duplicates. Fixed ~3x duplicate QSOs for phone-mode contacts from dedup key mismatch.
  • iCloud sync re-uploads. Dirty flags only set when merge produces data the server doesn't have.
  • Multiple crash fixes. ClubsSyncService, Activity feed, FriendsSyncService, and all remaining Dictionary(uniqueKeysWithValues:) sites hardened against duplicate keys.
  • Brag sheet map stripes. Antimeridian longitude-wrap picking closest representation to viewport center.
  • Spotlight deep linking, reindex reliability, UTType, priority, and edited-QSO detection.

1.82.1

2026-04-06

Bug fix release.

1.82.0

2026-04-06

Added

  • QSO safety ledger. Append-only ADIF file records every QSO outside SwiftData — survives database destruction.
  • Location-based call sign prefix picker. GPS-based portable prefix suggestions with searchable country list.

Fixed

  • Critical: ModelContainer init failure silently deleting entire database — now creates emergency backup before reset.
  • Critical: iCloud snapshot restore replacing local database without validating contents.
  • Prevent uploading empty database snapshots to iCloud.
  • Auto-backup no longer overwrites good backups with empty database after data loss.
  • Backup pruning prefers backups with data over empty ones.
  • iCloud Drive backup sync prefers backups with data over empty ones.

1.81.0

2026-04-02

Added

  • CW Sweep: Icom WiFi (RS-BA1) radio connection support for IC-705 and IC-7610.

Fixed

  • IC-705 WiFi sessions disconnecting after ~60 seconds (missing periodic reauth).
  • Frequency/mode changes from logger not reaching radio over WiFi.
  • WiFi reconnect leaving connectionStatus stuck at .connecting permanently.

1.80.0

2026-04-02

Added

  • Grid square display for rove sessions.
  • Instagram album-style park card carousel with GIS boundaries and grid overlay.
  • RST sent/received on QSO rows.
  • Percentile rankings for QSO count, duration, and rate.

Changed

  • Session detail redesign. Three-tier layout: hero summary, QSO list, insights carousel. Unified POTA/regular session hero card with equipment display, band/mode badges, and shift windows.

Fixed

  • iCloud sync duplicate QSOs appearing as triplicates in session detail.

1.79.0

2026-04-02

Added

  • Per-service sync mode. Bidirectional / Upload Only / Download Only for QRZ, POTA, and eQSL.
  • DONKI API note field for solar flares and CMEs in space weather views.
  • Journal-based iCloud sync engine. SQLite snapshot bootstrap for instant device setup, batched JSON journal entries, cross-device sync mode toggle via KVS.
  • Snapshot and journal sync telemetry with file size and duration tracking.

Changed

  • Renamed "Sync Sources" to "Sync Services" throughout the app.
  • Enriched spots widgets with activity type icons, band/mode, park/summit references, and SOTA spot source.