Commit Graph
125 Commits
Author SHA1 Message Date
mckero 2e1d8b9c00 feat(cw): archive decoded history, polish the waterfall, document licensing
三个用户反馈一并解决:

1. 解码文字不再消失 (核心)
   旧实现: 20 秒环形缓冲满了就静默覆盖最旧样本, 文字随之从屏幕消失。
   新实现: CwDeepBuffer 新增 overflow —— 满时被覆盖的旧样本先进 overflow,
   解码器累积到 15 秒就单独解码一次, 结果追加到 historyText (只增不减)。
   UI 记录区显示 historyText + decodedText (历史稳定 + 当前窗口实时)。
   ICwDecoder 接口新增 historyText StateFlow。

   归档音频已离开主窗口, 不再被 CTC 修正, 故其文本是"最终版", 追加安全。
   归档窗口 15 秒: 内容已在 20 秒窗口里解过多次, 短一点几乎无损, 且推理
   开销小。

2. 瀑布图更好看
   配色从"深蓝->青->黄"换成 matplotlib inferno (黑->紫->品红->橙->黄),
   与静态频谱图保持一致。相邻 bin 之间用水平渐变做线性插值, 消除 65 列
   离散方块的像素感。

3. AGPL 合规补漏 (用户提醒: 仓库许可证没体现 AGPL 组件)
   README 新增 License 章节: 声明项目主体 GPL-3.0 + feature/cw 的 DeepCW
   模型 AGPL-3.0-only, 并说明合并作品按 GPL-3.0 §13 / AGPL-3.0 §13 处理。

验证:
- :core:domain:test => 31 个 CW 测试全绿 (CwDeepBufferTest 新增 3 个
  overflow 归档测试: 顺序/清空/reset)
- :core:domain:compileKotlin + :core:data + :feature:cw:compileDebugKotlin
  => BUILD SUCCESSFUL
2026-08-13 08:04:05 +00:00
mckero ac5cbbc49a fix(cw): add crash probes to localize adb-less flash-crash
闪回桌面且无任何 Java 日志 => 崩溃发生在 native 层 (ONNX 库内部段错误)
或进程被系统 LMK 直接杀掉 (内存不足) —— 两种情况 Java 的
uncaughtExceptionHandler 都不会触发, crash_log.txt 自然为空。

新增 CwProbe: 在关键节点向 files/probe_cw.txt 追加时间戳行:
  decoder_constructed -> load_begin -> load_session_ok / load_failed:<类名>
  -> infer_begin -> infer_done
进程若中途死亡, 最后一行的节点就是崩溃/被杀位置, 无需 adb 即可定位。

验证: :core:data:compileDebugKotlin => BUILD SUCCESSFUL
2026-08-13 00:46:56 +00:00
Arty Bishop bd51044690 Added custom frequency offset setting to network reporting 2026-08-12 20:17:26 +02:00
mckero cf49dcfa01 fix(cw): persist decoder failures to files for adb-less diagnostics
在服务器上完整复现了手机端流程 (真实 20s 含噪音频 -> 重采样 -> 频谱 ->
ONNX -> 贪心 CTC), 解码逐字符正确:
  decoded: [CQ CQ DE BG7NTA BG7NTA K 5NN TU 73]   (与参考完全一致)
整条链路逻辑无问题, 闪退属环境/平台层。

应用内已有全局崩溃捕获器 (MainApplication.installCrashHandler) 会把堆栈
写入 files/crash_log.txt —— 无需 adb 即可取到崩溃原因。

本提交为加载与推理两处异常补充落盘:
- 模型加载失败 -> files/deepcw_load_error.txt
- 推理异常 -> files/deepcw_infer_error.txt

用户可在文件管理器按
Android/data/com.rtbishop.look4sat.bg7nta/files/ 路径取回这些日志。

验证: :core:data:compileDebugKotlin => BUILD SUCCESSFUL
2026-08-12 16:43:52 +00:00
mckero 5f297f9233 fix(cw): stop the waterfall crashing and cut APK size by 87MB
三个真机实测暴露的问题:

1. 闪退 (给权限后 3-4 秒必崩, 且注入日志抓不到堆栈)
   CwWaterfallState 用 mutableIntStateOf 记录重绘版本号, 却从音频采集线程
   写入。Compose 快照状态只能在合成线程修改, 从后台线程写会在运行时崩溃 ——
   崩在 Compose 内部, 所以业务类的日志注入抓不到。
   改用 MutableStateFlow (本身线程安全), Canvas 侧 collectAsState 读取。

2. 同一状态的跨线程数据竞争
   pushSamples 在采集线程写 rows/pending, snapshot() 在绘制线程读, 而
   ArrayDeque 非线程安全 —— 并发 removeFirst()/toList() 会抛
   ConcurrentModificationException 或 IndexOutOfBoundsException。
   两侧统一加锁; FFT 计算放在锁外, 只有队列追加持锁。

3. APK 从 8.4MB 暴涨到 135MB
   onnxruntime-android 的 AAR 自带 4 个架构原生库: arm64 28M + armv7 20M +
   x86 33M + x86_64 34M = 115MB。上次提交移除 abiFilters 时把 x86 系列也
   打包了进去 (仅模拟器需要)。
   重新加上 abiFilters, 保留 arm64-v8a + armeabi-v7a 两个真机 ABI,
   预计降至约 43MB。注意这与上次"恢复 64 位"不冲突: 那次删的是只留
   armeabi-v7a 的限制, 这次是排除 x86 系列。

另外两处加固:
- CwDeepDecoder 的模型加载从 init{} 移入惰性 ensureLoaded(): 加载会触发
  ONNX Runtime 原生库装载, 失败时抛 UnsatisfiedLinkError; 在构造函数中抛出
  会连带崩掉创建它的 composable, try-catch 也救不回来。移到首次使用时执行,
  失败经 errorMessage 上报给 UI。
- ONNX 会话限制 intraOp 线程数为 (核数-1) 且上限 4, 给音频采集和 UI 留出
  余量, 默认行为会铺满所有核心。

验证:
./gradlew :feature:cw:compileDebugKotlin :core:data:compileDebugKotlin => 通过
./gradlew :core:domain:test => 79 个测试全绿
2026-08-12 13:55:03 +00:00
mckero 9b0d543f12 refactor(cw)!: replace legacy CW engine with DeepCW in both entry points
DeepCW 成为唯一 CW 解码内核, 不保留旧引擎作兜底 (用户决定: 完全移植)。

删除旧内核 (1298 行):
- CwDecoder.kt / CwBayesianDecoder.kt / CwChannelTracker.kt / CwSpectrogram.kt
- CwDsp / CwFFT / CwSTFFT / CwGoertzel / CwFilter / CwResampler.kt
- CwDecoderTest.kt
旧内核的自动定频与 squelch 门控由 DeepCW 的 400-1200Hz 固定频窗替代 ——
模型自带定频, 不再需要频谱峰值跟踪和噪声门。

两个 CW 入口都改接 DeepCW:
1. feature:cw 独立整页 CwDecodeScreen.kt 重写为纯 Compose (瀑布图 -> 实时行
   -> 历史区), 移除 AndroidView/LayoutInflater 和被删的 activity_main 布局;
   删除 CwSettingsDialog.kt (旧引擎的手动音调/带宽面板, DeepCW 全自动无需)
2. feature:radar 内嵌可折叠面板改走 ViewModel 的 CW state/action, 移除
   Morse Expert 控制器与 cw_panel_main 布局
   (该面板此前注释写明 "no longer feeds it", ViewModel 里的 CW 通路是死代码)

新增 CwWaterfall.kt: 复用 CwDeepSpectrogram 绘制瀑布图, 显示的正是模型
分析的 400-1200Hz 频段与同一批幅值。

接口接线:
- IMainContainer.provideCwDecoder() 提供 ICwDecoder (实现需 Context 读 assets)
- RadarViewModel 构造新增 cwDecoderFactory, onCleared() 中 close() 释放
  OrtSession 避免原生内存泄漏
- 删除已无调用方的 RadarAction.CwSetToneFreq (DeepCW 定频固定, 无参数可调);
  CwSubState.cwToneFreq 语义改为只读显示检测到的音调

依赖清理:
- feature:radar 不再依赖 feature:cw 与 constraintlayout
- feature:cw 不再依赖 constraintlayout

字符串: feature:cw 清理 Morse Expert 遗留串 (premium/rate/help/
tap_back_again_to_close/purple_500), 按 en/zh/tr/in/id 五语补全新串;
core:presentation 补 radar_cw_tone / radar_cw_waiting 五语。

验证:
./gradlew :core:domain:test => 4 个测试类全绿
./gradlew :feature:cw:compileDebugKotlin => BUILD SUCCESSFUL
./gradlew :feature:radar:compileDebugKotlin => BUILD SUCCESSFUL
./gradlew :core:data:compileDebugKotlin => BUILD SUCCESSFUL
2026-08-12 13:00:25 +00:00
mckero 57762613a8 feat(cw): add CwDeepDecoder wiring ONNX Runtime to the CW pipeline
组装完整解码链路: 麦克风 PCM -> 重采样 3200Hz -> 20 秒滚动缓冲 ->
频谱图 -> ONNX 推理 -> 贪心 CTC -> 文本。

ICwDecoder 放 core:domain (纯 Kotlin, UI 可直接依赖), 实现放 core:data
(需要 Android Context 读 assets 及 ONNX Runtime 原生库)。

关键设计:
- decodedText 是替换语义不是追加: 模型会随上下文增加改写先前字符, 追加
  会把中间态 (BM -> BG7 -> BG7NTA) 永久留在屏幕上
- 推理用 Mutex.tryLock 串行化: 上一次未完成时直接跳过本周期, 慢设备不会
  堆积任务导致 OOM
- 推理在 Dispatchers.Default, 不阻塞音频采集线程
- 模型元数据从 model.onnx.json 读取 (字符表/blank 索引/输入输出名), 不在
  代码里硬编码, 模型更新时无需改代码
- lastInferenceMs 打点: 手机实际推理耗时需真机实测 (估算系数待验证)
- estimatedPitch 由频谱峰值 bin 反算, 仅供 UI 显示 —— 模型 400-1200Hz
  固定窗自带定频, 不需要频谱峰值跟踪参与解码
- 模型加载失败时 errorMessage 置位 (旧内核已删, 无兜底可退)
- close() 释放 OrtSession, 否则原生内存泄漏

验证:
./gradlew :core:data:compileDebugKotlin => BUILD SUCCESSFUL
2026-08-12 12:26:42 +00:00
mckero b8113fc757 build(cw): add onnxruntime-android 1.28.0 to core:data
DeepCW 模型推理需要 ONNX Runtime。该依赖属 Android 平台依赖 (AAR 内含
各 ABI 原生库), 因此放 core:data 而非 core:domain —— 后者按 AGENTS.md
须保持纯 Kotlin/JVM 以留 KMP 迁移余地。

版本固定为 1.28.0 不使用范围区间: release 构建走 R8, 避免依赖意外升级
引入行为变化。

验证:
./gradlew :core:data:dependencies --configuration releaseRuntimeClasspath
=> \--- com.microsoft.onnxruntime:onnxruntime-android:1.28.0
=> BUILD SUCCESSFUL in 3m 48s
2026-08-12 11:52:47 +00:00
Arty Bishop 2f4e3f5802 Added the ability to fully customize data sources via import 2026-08-12 13:01:45 +02:00
Arty Bishop 3f5b48f270 Integrated the AMSAT status page created by MCKero6423 2026-08-11 14:05:55 +02:00
atsunatsuandatsunatsu 060fa2dfcd Added remembering per-satellite doppler offset (#237)
Co-authored-by: atsunatsu <atsunatsu@users.noreply.github.com>
2026-08-11 14:02:15 +02:00
atsunatsuandatsunatsu 8b5960282f Broaden linear transponder detection, logic fixes (#236)
Co-authored-by: atsunatsu <atsunatsu@users.noreply.github.com>
2026-08-08 21:31:44 +02:00
mckero 10eb84690e Added AMSAT satellite status tracking page (#234) 2026-08-08 14:26:36 +02:00
mckero 284183836e refactor(amsat): replace hand-rolled date parsing with SimpleDateFormat
Addresses @AlanCui4080's review feedback about the manual date calculation.
Uses SimpleDateFormat to eliminate the hand-written calendar math (regex +
days-from-epoch calculation). Avoids java.time since it requires desugaring
on minSdk 24, keeping dependencies minimal.

Before: 23 lines of manual day-from-epoch calculation
After: 7 lines using SimpleDateFormat

Ref: https://github.com/rt-bishop/Look4Sat/pull/233#discussion_r1868599947
2026-08-05 17:02:01 +00:00
mckero de85aa1e8b refactor(amsat): align with Material3 conventions per PR #233 review
Addresses feedback from rt-bishop/Look4Sat#233:

1. Migrate hardcoded colors to MainTheme colorScheme
   - Extend darkScheme: tertiary (Active 0xFF648FFF), tertiaryContainer (Telemetry 0xFFFFB000)
   - Extend lightScheme: tertiary (0xFF3C6FE0), tertiaryContainer (0xFFE09800) for contrast
   - Remove top-level Color() constants from SatStatusScreen.kt
   - Add statusColorOf() mapper using MaterialTheme.colorScheme

2. Move HTTP implementation from domain to data layer (Clean Architecture)
   - Delete AmSatApiClient.kt from core:domain (violates AGENTS.md: "Pure Kotlin, NO Android deps")
   - Migrate to IRemoteSource/RemoteSource in core:data (uses existing OkHttp3)
   - AmSatRepository now depends on IRemoteSource instead of AmSatApiClient

3. Inline JSON parsing (prepare for java.time migration)
   - Parse AMSAT API responses (names, reports) in AmSatRepository
   - Time parsing still uses manual logic (java.time desugaring in follow-up)

Before:
  - Hardcoded Color(0xFFXXXXXX) in UI + Repository (no theme support)
  - HttpURLConnection in domain layer (architecture violation)
  - AMSAT colors duplicated across modules

After:
  - MaterialTheme.colorScheme.tertiary/tertiaryContainer (light/dark adaptive)
  - HTTP via data layer RemoteSource (follows AGENTS.md architecture)
  - Single source of truth for AMSAT colors

Ref: https://github.com/rt-bishop/Look4Sat/pull/233#discussion_r1868599947
Ref: AGENTS.md "core:domain - Pure Kotlin (JVM). NO Android dependencies."
2026-08-05 15:26:42 +00:00
mckero f789a15338 fix(status): AMSAT page load failure, refresh spinner, retry button
- P0: fetchStatus() now runs on Dispatchers.IO - the previous
  synchronous URLConnection on the main thread threw
  NetworkOnMainThreadException and showed "load failed" on every open
- P1: refresh button rotates a vector icon (ic_refresh) instead of the
  "↻" text glyph, whose off-center font metrics made the spinner
  orbit around a shifted pivot
- P2: error state gains a Retry button (4 locales); amsat_refresh
  string added (5 locales)
- versionCode 456 (bump for reinstalling over 455), versionName stays 4.5.5
2026-08-05 08:50:01 +00:00
mckero 6221516893 feat(amsat): 4.5.5 official AMSAT API + edge-to-edge status page
- Replace HTML parsing with the official AMSAT Satellite Status API v1
  (catalog.php + reports.php, JSON): AmSatApiClient (pure JVM, hand
  rolled ISO-8601/epoch parsing for minSdk 24) + rewritten
  AmSatRepository (satellite list from catalog, reports slotted into
  6 days x 12 two-hour slots, status colors per report value)
- Fix edge-to-edge: status bar / navigation bar insets on the status
  page (refresh button and update time were unreachable)
- Version 4.5.5 (455), What's new in all 5 locales
2026-08-05 08:12:17 +00:00
mckero c9cab8457d chore(i18n): translate all code comments to English
All Chinese comments (//, /* */, KDoc) across core/app/feature/build-
logic translated to English (550 lines, 73 files after FT8 rollback).
Code logic untouched - comment text only. Verified: all modules
compileDebugKotlin BUILD SUCCESSFUL.
2026-08-05 07:59:35 +00:00
mckero 0e767f4250 feat(wavelog): QRZ grid lookup, LoTW satellite list refresh, RST 59
User-prioritized WaveLog fixes (4.5.5, commit-only per instruction):

- QRZ 对方网格爬虫: QrzGridClient (domain, pure JVM) fetches
  https://www.qrz.com/db/{call} with user-supplied cookies (parses
  EditThisCookie JSON or raw "k=v; k=v"), extracts Grid Square from
  the Detail table. Cookies NEVER built in - entered in settings.
- Settings: WaveLog card top-right gear opens QRZ cookie dialog with
  test query button (detects logged-in callsign from cookie, looks up
  its grid, shows result or failure in the dialog).
- LogTab: on Enter, async lookup of the other station's grid ->
  queue.updateGridsquare -> uploaded with QSO (postQso gridsquare).
- LoTW satellite list: 112 names embedded (lotw.arrl.org config.tq6),
  normalizeSatName maps Celestrak TLE names to LoTW names (SAUDISAT-1C
  -> SO-50, FUNCUBE-1 -> AO-73, DIWATA-2B -> PO-101, ZARYA/ARISS ->
  ARISS); "Update sats" button in WaveLog card downloads the live list
  (LotwSatellitesRepo, SharedPreferences persisted, never in build).
- RST: rst_sent/rst_rcvd = 59/59 in both v1 ADIF and v2 JSON.
- Grid mismatch dialog kept (cloud station grid vs station QTH);
  QSO gridsquare no longer uses station grid.
- New strings in 5 locales; check_strings 9 files OK.
Verified: core:domain/data + feature:settings/radar + app
compileDebugKotlin BUILD SUCCESSFUL.
2026-08-05 06:23:47 +00:00
mckero c9d87be289 fix(aprs): upload feedback, manual report reliability, station position
User testing round 3 (4.5.4): no success feedback on upload, aprs.fi
shows nothing, passcode calculator OK, notification present.

- Manual report now works even when service not running: ACTION_REPORT_NOW
  starts the service first (Toast "not configured" if missing callsign)
- Upload result feedback guaranteed: Toast always shows (short OK /
  long fail+reason), last result persisted (time/ok/detail) and shown
  in the settings card "Last report: HH:mm:ss OK/failed - detail"
- Position source: station position from settingsRepo (user decision)
  with live GPS last-known as fallback
- sendPacket reads the server confirmation line (short 3s timeout);
  server error text (Invalid/error) surfaces in Toast + card
- Strings EN/ZH/TR/IN/ID +4 keys
Verified: compileDebugKotlin all modules BUILD SUCCESSFUL,
check_strings 9 files OK.
2026-08-05 03:29:36 +00:00
mckero 012ea1eeb6 fix(aprs): passcode compute button, crash log, notif permission, login verify
User feedback round 2 (4.5.4): no notification shown, report not
sending, no error visibility on crash. Diagnosis: APRS-IS port 14580
reachable (verified with real login test), 24580 SSL refused; login
format OK. Fixes:

- Settings dialog: "Compute passcode" button - fills passcode from
  callsign via the ported 0x73E2 algorithm (user can see the result)
- Global crash handler in MainApplication: stack trace appended to
  files/crash_log.txt so crashes are diagnosable (user: no crash logs
  were available before)
- POST_NOTIFICATIONS runtime permission requested when enabling APRS
  (Android 13+ otherwise silently hides the service notification)
- AprsIsClient: read the login response (aprsc "# logresp ...
  unverified"/"Invalid") and surface the server message as the error
- AprsForegroundService: Toast on manual report result (OK / failure
  with server reason)
- Strings EN/ZH/TR/IN/ID +3 keys
Verified: compileDebugKotlin all modules BUILD SUCCESSFUL,
check_strings 9 files OK.
2026-08-05 03:06:09 +00:00
mckero 920af7bde2 fix(aprs): crash when enabling APRS with callsign set (foreground service type)
Root cause: user reported crash on enabling APRS with a callsign set
(empty callsign worked because the service stops early and never
reaches startForeground). Android 14+ requires the service to declare
foregroundServiceType when startForeground passes a type; the service
had none -> process died on toggle.

- Manifest: add android:foregroundServiceType="dataSync"
- AprsCard: use startForegroundService() for start/report actions
  (Android 8+ standard for foreground services)
- AprsForegroundService: try-catch around startForeground, fallback
  stopSelf instead of killing the process
- AprsReporter: passcode "-1" (APRSdroid "no auth" convention) also
  auto-computes from callsign
- Merged upstream 418a05e3 (zh wording fix, no conflicts)
Verified: compileDebugKotlin all modules BUILD SUCCESSFUL,
check_strings 9 files OK.
2026-08-05 02:50:40 +00:00
mckero 518d9a4d22 feat(aprs): 4.5.4 APRS-IS network reporting (ported from APRSdroid 1.6.3d)
Core logic extracted from APRSdroid 1.6.3d (jadx reverse-engineered,
verified against APRS spec):
- core/domain/aprs/AprsPacket.kt: passcode algorithm (0x73E2 XOR),
  login line, uncompressed/compressed position encoding (ab0oo)
- core/data/aprs/AprsIsClient.kt: APRS-IS TCP client (login + packet
  lines, 30s reconnect strategy)
- core/data/aprs/AprsReporter.kt: periodic reporting scheduler
  (default 5 min, manual trigger support)
- core/data/aprs/AprsStore.kt: SharedPreferences persistence
  (fill once, saved)
- app/AprsForegroundService.kt: foreground service with notification
  (keeps APRS alive across pages), START_STICKY, last-known position
  provider
- feature/settings/AprsCard.kt: APRS card between DataCard and
  OutputCard, gear button opens settings dialog (server/port/
  callsign/ssid/passcode/interval/status/symbol), manual report button
- Manifest: service registration + FOREGROUND_SERVICE(_DATA_SYNC) +
  POST_NOTIFICATIONS
- Strings in EN/ZH/TR/IN/ID (23 keys x 5)
- Version 4.5.4/454 + What's new 5 languages
2026-08-05 02:07:11 +00:00
Arty Bishop 602b1553a2 v4.4.4 - Icom CAT, components, filters and sources tweaks 2026-08-04 20:51:22 +02:00
Lukas 905995ab44 Added configurable Radar offset to the sensors output (#230) 2026-08-04 18:08:44 +02:00
mckero f7402a3531 feat(status): 4.5.3 AMSAT satellite status page + radar log pass divider
New feature/status module: fetches https://amsat.org/status/ and
renders a live status grid in the official site colors:
- Parser (AmSatParser): 47 satellites x 6 days x 12 two-hour slots,
  official colors (blue=Active, orange=TLM/Beacon, pink=Not Heard,
  deep-orange=Conflicting, gray=none); 598+ report details extracted
  from inline JS tooltips (callsign/date/time/grid)
- Three-level viz: color grid -> report count -> tap day cell opens
  report list dialog
- Manual refresh with spin animation + last-updated timestamp +
  legend row; loading/error states
- New "AMSAT" entry in the More menu (Screen.AmSat), integrated with
  page-order / hide-page settings (SettingsScreen screens list,
  defaultSubMenuOrder, allNavItems, migration for existing users)
- AmSatRepository via IRemoteSource.getStatusHtml() (UA header);
  shared remoteSource promoted to a lazy class property in MainContainer

Radar page Log tab: local entries now grouped by pass session with a
thick divider + satellite label between groups (matches the log page).

What's new updated in EN/ZH/TR/IN/ID. Version bumped to 4.5.3/453.
Not released (user gates all releases).
2026-08-04 15:57:41 +00:00
mckero cda6cf8583 fix(gps,data): official-code fixes - real GPS result callback, no fake update success (4.5.2, release pending)
Official code issues found during review (user-reported):
1. GPS "success" was shown instantly even when no fix was obtained:
   - setStationPosition() always returned true (permissions exception
     swallowed, async requestLocationUpdates without waiting)
   - now: suspend + LocationManagerCompat.getCurrentLocation (GPS
     first, network fallback), permission check upfront, 15s timeout,
     success only when onLocationChanged fires; SettingsRepo takes
     Context for the permission check; ViewModel waits for the real
     result and shows "Unable to get location - check permission and
     GPS/network signal" on failure (4 locales)
2. Data update faked success on total failure:
   - updateFromRemote now counts successful sources; 0 success throws
     IOException -> timestamp NOT refreshed, Toast "Update failed -
     check your network" (4 locales, new IShowToast resId overload)
   - OkHttp timeouts widened: connect 15s / read 20s / write 20s

Verified: check_strings OK; all modules compile.
Release intentionally NOT triggered (user: fix everything first, then
one release).
2026-08-04 14:08:38 +00:00
mckero 916a0d0f0a feat(wavelog,radar,settings): WaveLog logbook integration + Log tab
Background: satellite operators want to log QSOs during a pass while
watching live frequencies. 4.5.2 adds WaveLog (logbook server) API v2
integration: log from the radar page, upload to a self-hosted WaveLog
instance with grid-mismatch protection.

Changes:
- Radar page: new third tab "Log" between Transceivers and SSTV
  - pick a transponder, watch live TX/RX Doppler-corrected frequencies
  - enter callsign, Enter stores locally (UTC time + that second's
    frequencies sampled together)
  - local entry list shows time/frequency/callsign only (no upload
    status, per user: proves the entry was saved)
  - swipe-to-delete: yellow trash while swiping, turns into Undo at
    75%, 5s countdown before auto-delete (custom gesture, no
    SwipeToDismissBox)
- Settings: new WaveLog card (server URL / API key / station ID /
  auto-upload switch / test connection / upload now buttons) matching
  the user's reference screenshot layout
- Upload pipeline: POST /index.php/api/v2/qso with required fields
  (station_profile_id, call, band=SAT, mode, qso_date, time_on UTC)
  plus freq/freq_rx (Hz), gridsquare (from station profile via
  GET /api/v2/station/{id}), sat_name; RST omitted per user
- Grid check: station gridsquare (first 4) vs user QTH (first 4);
  mismatch shows a confirm dialog (ignore & upload / cancel)
- Auto upload: 10-minute in-app retry loop (only when switch on);
  manual upload button; local queue capped at 500, all entries stored
  locally regardless of switch
- Fixed: subMenuOrder was never persisted (4.5.1 regression)
- core/domain: compileOnly org.json (runtime uses Android's)
- Version 4.5.2 (452)

Verified: check_strings.py OK (8 files, 452/4.5.2);
:app:compileDebugKotlin BUILD SUCCESSFUL locally.
2026-08-04 10:29:40 +00:00
mckero 0ea9c0ede1 feat(settings): drag-to-reorder page order in UI Settings card
Users can now change the bottom navigation order (previously fixed):

- New "Page order" section under the Settings toggle in the UI
  Settings card: vertical list of all 7 pages, each with a drag
  handle (new ic_drag drawable, Material drag_indicator glyph) on
  the right.
- Drag a handle up/down: the item follows the finger with live
  swap + animateItem() placement animation; on release the order is
  persisted (OtherSettings.screenOrder, comma-separated in prefs so
  order survives; StringSet would not).
- "Reset order" button restores the default order
  (Satellites/Passes/Radar/Mutual/Roaming/Map/Settings).
- MainScreen now sorts navItems by screenOrder (empty = default,
  stable sort keeps the canonical order); hiddenScreens filtering
  unchanged; Settings entry always visible.
- New strings prefs_ui_order_title / prefs_ui_order_reset in
  en/zh/tr; What's-new updated in all three locales.

Version stays 4.4.9 (覆盖 per user). Verified: core:data tests
pass, settings + app compile.
2026-08-04 00:28:53 +00:00
mckero c101870b71 feat(settings,ui): custom-source toggles default off; new UI Settings card; bump to 4.4.9
Two changes per user request:

1. Data source toggles default OFF with legacy-URL migration
   (continuation of the 4.4.8 fix, now also in the dialog): old
   example.com placeholder URLs are replaced by the real defaults and
   the custom toggles stay off, so the online update never points at a
   dead source after upgrading.

2. New "UI Settings" card between Other Settings and Credits:
   one switch per bottom-navigation page (卫星/过境/雷达/匹配/漫游/地图/设置
   in fixed order). Turning a page off removes it from the nav bar and
   the remaining items close up automatically; order is never
   rearranged. The Settings entry is always visible (switch disabled)
   so the user can never lose access to settings. Persisted via
   OtherSettings.hiddenScreens (StringSet of Screen.screenId; screenId
   added to the Screen sealed class - simpleName is unsafe under R8).

Version bump per fork convention: 4.4.8 -> 4.4.9, versionCode 449.
What's-new updated in en/zh/tr.

Verified: core:data + core:domain tests pass, all modules compile.
2026-08-03 18:14:53 +00:00
mckero 9a4ceddcb0 fix(settings): force custom-source toggles off when legacy example.com URL present
Old installs that once enabled the custom TLE/transceiver toggles kept
the legacy "https://example.com/tle.txt" placeholder in preferences.
Under the new "toggle replaces the online-update default source"
semantics this pointed the All/SatNOGS fetch at a dead URL and broke
satellite updates after upgrading to 4.4.8.

Migration in getDataSourcesSettings(): example.com placeholders are
replaced by the real default URLs (Celestrak All / SatNOGS) and the
toggles are forced OFF unless a non-default, real user URL is set.
Also disable the reset (loop) icon buttons while their toggle is off.

Verified: core:data tests pass, settings + app compile. Version stays
4.4.8 (覆盖 per user).
2026-08-03 17:03:48 +00:00
mckero 8d88100b15 feat(settings): custom URL now overrides the online-update default source
The "Custom URL" dialog's two URL fields now control the default
source used by the online update, per the user's intent (one-time
setup, no extra steps per update):

- Defaults are real URLs now: TLE = Celestrak "All" (active group
  CSV), transceivers = SatNOGS API. The example.com placeholders are
  gone.
- updateFromRemote(): the "All" TLE entry and the "SatNOGS" entry are
  replaced by the user's URLs when the corresponding custom toggle is
  ON and the URL is non-blank; otherwise the real defaults are used.
  The old "Other" appended-source logic is removed - the toggle now
  means "use my URL for this source" instead of "download an extra
  source". Each source (TLE / transceivers) is independent.
- Each URL field gained a reset (loop) icon button on the right that
  restores the default URL text (new ic_reset drawable, Autorenew
  vector). Strings added in en/zh/tr.

Test updated: custom TLE URL data now lands under the "All" type
instead of "Other". Verified: core:data tests pass, settings + app
compile. Version stays 4.4.8 (覆盖 per user).
2026-08-03 16:41:14 +00:00
mckero 97c6d609ac fix(roaming): drop auto GPS polling, show station position directly
Reverts the auto-update machinery after review — the page now simply mirrors the station position (站位) from the shared settingsRepo.stationPosition StateFlow, exactly what the Settings page shows:

- RoamingScreen: removed the LocationManager listeners, the 30s re-request loop, the provider filter and the location-disabled hint. No polling, no auto-updates; coordinates are whatever the station GPS says.
- Settings: removed the '漫游位置实时更新' toggle (stateOfRoamingLive, key, action, strings en/zh/tr) that caused the 'Other' card to overflow — the sixth unlabeled switch clipped past the card's rounded bottom edge was that row overflowing a fixed-height card. Card height back to 268.dp, five rows fit again.

Verified: core:domain tests, roaming/settings/app compile clean; zero references to RoamingLive remain.
2026-08-03 12:24:07 +00:00
mckero 6dbe30b3f6 feat(roaming): faithful QTH定位器 port, live-update toggle, night-mode safe
Rework the Roaming page after user review. It is now a faithful port of the QTH定位器 location panel, not a loose re-skin:

UI (matching the reference layout):
- GPS status dot (real: green when a fresh fix exists, outline when stale/missing) + date + time header
- Lat/Lon rows with DMS and 5-decimal display, big 8-char locator centered below
- 3x3 grid of neighboring 4-char squares with the reference proportions: center column ~2.6x wider (21.4% : 56.2% : 21.4%) and center row the tallest (31.5% : 35.9% : 31.7%); red position marker now placed at the fractional position of the fix inside the center cell (was fixed center)
- No oversized GPS button: live updating is now a Settings toggle '漫游位置实时更新' (stateOfRoamingLive, default on) that drives periodic location refresh

Style & night-mode safety:
- All colors come from MaterialTheme.colorScheme (surfaceVariant/secondaryContainer/error) — no hardcoded cyan/blue from the original app. The red night filter (ColorMatrix keeping only the R channel) blanked the old hardcoded palette; theme colors survive it.
- Chinese nav label '漫游' added to values-zh (was missing, showing English 'Roaming')

Settings:
- OtherSettings.stateOfRoamingLive persisted (default true), toggle row in Other card, height adjusted

Verified: feature:roaming, feature:settings, app compile clean.
2026-08-03 11:18:35 +00:00
mckero 1de5632428 feat: increase station coordinate precision to 5 decimals
Station lat/lon was stored rounded to 4 decimals (~11 m). Bump to 5 decimals (~1.1 m), slightly better than GPS hardware accuracy without showing noise.

- SettingsRepo.setStationPosition: round(4) -> round(5)
- QthConverter.qthToPosition: round(4) -> round(6) so 10-char locators fully roundtrip
- Update QthConverterTest expected values to 6-decimal precision
2026-08-03 09:14:13 +00:00
mckero 9d0d971500 refine: AOS binary-search refinement, getRadios signature cleanup
Refine AOS crossing to ~500ms via binary search instead of linear stepping; simplify getRadios to take satPos; tidy radar view/viewmodel.
2026-08-03 06:47:49 +00:00
atsunatsu d7e1ce11c9 fix: smooth dual-station elevation curve
- Fix cubic Bezier control points (proper Catmull-Rom to Bezier conversion)
- Refine AOS/LOS more robustly by walking from edge into the pass window
- Still 5s sampling for smooth curves
2026-08-02 23:07:28 +08:00
Arty Bishop f9806d7f7f Replaced the types selection dialog with modes selection 2026-07-31 16:04:39 +02:00
Arty Bishop 2d3adfc6a6 Added small tweaks to Sources, Components and strings 2026-07-31 12:27:25 +02:00
Arty Bishop 4585332b1a Fixed manual OMM (.csv) data import, tweaked messaging 2026-07-30 15:56:50 +02:00
Arty Bishop 6d2b0ced49 Implemented AOS window and elevation highlight filters 2026-07-30 13:28:37 +02:00
Rui Oliveira 07504a2a95 Add the option to override the frequency from the radio 2026-07-24 20:29:51 +01:00
Rui Oliveira 81525b6dd3 Map AFSK (e.g. ISS APRS) to FM 2026-07-24 20:10:37 +01:00
Rui Oliveira c7089ab314 Fix setting the mode (e.g. FM, USB, LSB) 2026-07-24 20:10:10 +01:00
Rui Oliveira 77dfb7bb05 Remove PTT-aware logic, which is not necessary
I was setting the Tx frequency only when we were
in Tx mode, but the Icom 705 will accept the
command to set the Tx frequency even when in Rx mode, so
there is no need to check the PTT state before sending the command.
2026-07-24 20:09:43 +01:00
Rui Oliveira 58fb688593 Fix bugs in setting the Tx frequency
Uses 0x25 01 command to set the Tx frequency,
which is the correct command when in split mode.
2026-07-24 20:09:01 +01:00
Rui Oliveira dc596b4348 Fix bugs in setting Rx frequency 2026-07-24 20:08:46 +01:00
Rui Oliveira f9b1a97a37 Refactor "Disconnect" and "Track" logic 2026-07-24 20:08:23 +01:00
Rui Oliveira d700432829 Add initial support for Icom IC-705 radio
- Added support for Icom IC-705 radio model with split mode functionality.
- Updated RadioTrackingService to handle single-radio split mode and dual-radio configurations.
- Modified MainContainer to provide appropriate radio controllers based on selected model.
- Enhanced SettingsRepo to include split mode preference in radio control settings.
- Updated UI in SettingsDialog to allow toggling of split mode for IC-705 and adjust device selection accordingly.
- Improved handling of baud rates based on selected radio model.
- Added extended operations in IRadioController for IC-705 specific commands.
2026-07-24 20:07:11 +01:00
Arty Bishop b17ea2d918 v4.4.3 - Added required tweaks to support Android 17 (API 37) 2026-06-27 14:15:05 +01:00