Commit Graph
84 Commits
Author SHA1 Message Date
atsunatsu 75a251eb98 fix: keep last good AMSAT live lists on fetch failures; whitelist mirror
- Failed whitelist fetch (raw.githubusercontent.com unreachable on some
  networks) no longer wipes the persisted whitelist or admits every ISS
  module alias: fall back to the persisted whitelist, and only persist
  when a stream was actually fetched.
- Multi-match collapse without any whitelist now keeps the primary entry
  (smallest catnum, ISS ZARYA=25544) instead of every alias (DESTINY etc.).
- Failed FM/Linear page fetch keeps the previous lists instead of
  clearing the filters.
- Amateur whitelist gets a jsDelivr CDN mirror as fallback source.
2026-09-24 09:17:17 +08:00
atsunatsu 522cac25e8 fix: correct AMSAT live filter satellite lists
- Resolve ISS station modules (ZARYA/UNITY/ZVEZDA/DESTINY/NAUKA) to the
  primary 25544 instead of whichever module appears first in the TLE source
- Match OSCAR designators to local names spelled 'OSCAR N'/'AMSAT-OSCAR N'
  (AO-7 -> OSCAR 7) so Linear lists keep AO-7 on SatNOGS-sourced data
- SSTV virtual type now uses the transceiver service class (Amateur only)
  plus launcher-debris name exclusion, dropping retired weather birds
  (TIROS) and rocket bodies (ARIANE 6 R/B) while keeping ISS/CSS/TEIKYOSAT
- Persist the AMSAT active-frequency whitelist (palewire mirror) on sync;
  used only to disambiguate multi-match names, never as a hard filter
2026-09-24 08:49:53 +08:00
atsunatsu 30000a913e fix: AMSAT name matching token-based; SSTV filters to in-orbit only
- matchesAmSatName: exact token match instead of substring contains,
  so 'ISS' no longer matches AISSAT-1 and 'AO-7' no longer matches
  AO-73 (FUNCUBE-1); resolves missing ISS in FM and missing AO-7 in
  Linear lists
- resolveTypeIds: intersect SSTV catnums with current in-orbit TLE set,
  excluding retired satellites (e.g. TIROS) whose radios are tagged SSTV
- add regression tests for token matching
2026-09-24 02:53:34 +08:00
atsunatsu cf1e2b99b4 fix: transponder filters - mutual exclusion and beacon-free fallback
Two correctness fixes for the FM/Linear/SSTV filters:

1. Satellite type picker: virtual transponder types (AMSAT Live FM,
   AMSAT Live Linear, Live SSTV) are now mutually exclusive with the
   regular TLE-source types - ticking a virtual type clears Amateur/
   CubeSat/etc and vice versa, so selecting 'AMSAT Live FM' no longer
   unions in hundreds of non-transponder satellites. Virtual types
   still combine with each other. Also: an empty virtual-type resolve
   now yields an empty list instead of falling back to 'show all'.

2. Mutual-match fallback: when the AMSAT lists are empty (first run /
   sync failure) the DB mode filter used downlinkMode alone, so any
   downlink-only beacon sharing a mode label was misclassified
   (ISS/IO-86 CW beacons into Linear, FO-29's FM Digitalker into FM).
   New getIdsWithModesAndUplink() requires uplinkLow IS NOT NULL,
   matching real transponders only. SSTV filter keeps the old query
   (SSTV sats are often downlink-only).

Shared virtual-type names moved to Sources.virtualTypeNames.
2026-09-24 00:51:13 +08:00
atsunatsu 0163c7b217 feat: satellite page transponder/activity filters as virtual types
The satellite selection screen's 'Select satellite type' dialog now lists
three virtual types first (AMSAT Live FM, AMSAT Live Linear, Live SSTV)
before the TLE-source types. Selecting one filters the satellite list to
that transponder/activity set; multiple selections union. 'All' is kept.

- SelectionRepo.getTypesList: virtual types prepended (All preserved)
- SelectionRepo.resolveTypeIds: FM/Linear -> AMSAT catnum sets,
  Live SSTV -> mode=SSTV radios; other types via persisted IDs
- Tests: virtual-type filtering, union, clear, list-order assertion
2026-09-23 23:25:52 +08:00
atsunatsu 85a4b9cfa3 feat: mutual pass FM/Linear transponder filters (AMSAT Live source)
Mutual match page gains FM/Linear transponder-type filters. The
authoritative source is AMSAT's human-maintained Live FM/Linear
Satellites pages, fetched during data sync and stored as catnum sets;
filters resolve to those sets (falling back to DB mode filter when
empty), and unchecking both shows a prompt instead of querying.

- DataParser.parseAmSatLivePage: table rows to satellite names,
  TEVEL2-1 thru TEVEL2-9 range expansion, bracket-alias matching
- SettingsRepo: AMSAT FM/Linear catnum sets persisted in prefs
- DatabaseRepo: fetch AMSAT pages in updateFromRemote, best-effort
  (failure keeps previous list, never blocks the update)
- getIdsWithModes: AND isAlive = 1
- MutualScreen: FilterChip row inside MatchSearchCard
- Tests: parser (4) + viewmodel (3) + repo fakes
2026-09-23 23:25:37 +08:00
atsunatsu 62ad995aeb feat(map): multiple marked stations per grid with pin-to-top
- MarkedStation storage upgraded to an ordered list per grid
  (Map<String, List<MarkedStation>>); JSON array shape with legacy
  v14.1 single-object migration on read
- MapAction: SetMarkedStation appends (duplicate callsign ignored),
  RemoveMarkedStation deletes by callsign, PinMarkedStation moves a
  mark to the top of the list
- MaidenheadGridOverlay: red fill for any marked grid unchanged;
  first-call label mode shows the FIRST (pinned) marked callsign
- MapScreen: one row per mark (pale-yellow callsign), pin icon
  (冖 + stroke ↑, drawn in-app) left of the Marked label — shown only
  when the grid has 2+ marks; pinned row's icon grey, others pale
  yellow; delete icon per row
- New ic_pin.xml vector drawable; strings grid_mark_pin
2026-09-23 15:47:17 +08:00
atsunatsu 4da59bdd17 feat(map): mark a station you want to contact in unworked grids
- New MarkedStation model + ISettingsRepo/SettingsRepo persistence
  (single JSON key markedGridStations, org.json only)
- MapState/MapViewModel: markedGrids state, Set/RemoveMarkedStation
  actions; marks auto-clear once the grid becomes worked
- MaidenheadGridOverlay: red fill (alpha 90, same as green worked
  fill) for marked grids at both zoom levels; first-call label mode
  shows the marked callsign in pale yellow on red cells
- MapScreen: unworked-grid dialog gains a 'mark a station you want
  to contact' button + callsign input dialog; marked row shows red
  callsign, 'Marked' in the count column, marking date, delete icon
- i18n: values/ + values-zh/ (grid_mark_*)
2026-09-23 13:14:38 +08:00
atsunatsu bf492e3f8b feat(passes/radar): port pass history + compass calibration, cumulative what's new
- Pass history (Iu-yang1 fork): PassesSettings.hoursBefore look-back window;
  second 'Time before' slider in the pass filter; completed passes stay
  listed (progress clamped to [0,1]) in the history window.
- Compass calibration (Iu-yang1 fork): OtherSettings.compassOffsetDegrees,
  CompassAccuracy from the magnetic sensor, calibration dialog with
  figure-eight accuracy meter, corrected heading and manual offset field;
  RadarViewModel applies the offset.
- What's New dialog now shows the cumulative 4.4.6->4.4.7 changelog and
  follows the system language (values/ + values-zh/).
- pass_filter_history_hours localized in es/ru/si/tr/uk.
2026-09-21 10:42:45 +08:00
atsunatsu 4b82332133 feat(map): first-call callsign label toggle in VUCC grid mode
Add a 'first call' pill toggle below the grid-mode switch (map top-right,
visible only in grid mode + VUCC award). When on, worked (green) cells are
labelled with the first callsign worked in that grid (earliest confirmed
QSO by time) instead of the Maidenhead code; non-worked cells get no label
at all. Persisted via OtherSettings.stateOfMapFirstCall (full MVI chain).
The feature is VUCC-only: All and other award overlays never draw it.
2026-09-21 09:44:43 +08:00
atsunatsu 9e88609efc feat(amsat): show consecutive same-status report count in status cells
Day-cell numbers now count the most recent consecutive reports sharing the
newest status (counting back from the newest report, stopping at the first
different status), instead of the total report count of the day. Empty gaps
do not break the streak; statuses compare by exact report text; counting
does not cross day boundaries.

- SatDay gains streakCount (computed in AmSatRepository.buildStatuses)
- DayCell renders day.streakCount instead of slot count sum
- Add real org.json to core:data unit tests (android.jar stub cannot parse)
- Add 2 streak tests: mixed-status slot breaks, all-same-status counts all
2026-09-19 04:27:19 +08:00
atsunatsu 322becdda8 feat(satellites): fuzzy name search ignoring separators
The satellite search only matched exact continuous substrings, so names with
dashes, spaces or brackets (AO-7, ISS (ZARYA), FO-29) could not be found by
queries without those separators. Now the query is normalized (lowercased,
non-alphanumeric separators stripped) and split into space-separated tokens,
and every token must appear in the normalized satellite name. This makes
'ao7' match 'AO-7 (AMSAT-OSCAR 7)' and 'iss zarya' match 'ISS (ZARYA)'.
Numeric queries still match catnum exactly. Covered by 8 new unit tests.
2026-09-18 14:49:41 +08:00
atsunatsu 60dfa5d600 feat(lotw): auto-sync grids on app start with separate toggle
Mirrors the ephemeris auto-update check: on every app start, if LoTW
credentials are configured, the new 'auto-sync LoTW grids' toggle is on,
at least one manual sync was done before, and today has not synced yet
(ARRL rate limit, ~once a day), pull confirmations since the last sync
and merge them. Same callsign -> incremental merge; callsign change ->
full replace. Manual dialog and automatic path share the orchestration
(mode resolution, gate, merge/persist) moved to core:domain
LoTWGridSync.kt; LoTWSyncMode moved to core:domain. 8 unit tests.
2026-09-16 20:18:22 +08:00
atsunatsu 5f1eb239ed feat(lotw): cancel in-flight sync, honest progress bar, drop stale hint
- Cancel: the dialog's cancel/back while syncing now aborts the download job
  (cooperative cancellation in the stream reader, CancellationException
  propagated, nothing written to storage). An accidental full sync can be
  stopped immediately.
- Progress: the size estimate is conservative (900 B/record vs measured 720)
  so the bar never hits 100% before the download truly ends; storage writes
  moved to the IO dispatcher (multi-MB per-grid QSO JSON used to freeze the
  main thread after download); when the bar caps, the UI switches to
  'Processing downloaded data…' instead of looking stuck.
- Removed the outdated '3-10 seconds' hint (misleading for large accounts).
2026-09-16 18:17:52 +08:00
atsunatsu fc028f852c feat(lotw): full/incremental sync modes with live progress and time estimate
Two explicit sync buttons in the LoTW dialog:
- Full sync: qso_qslsince=2000-01-01, fresh report replaces all stored data.
- Incremental merge: since=last successful sync date, merged into the stored
  set (grids union, QSOs deduped by call + time). First sync / callsign change
  automatically falls back to full replace so no stale grids from another
  account linger.

Progress & estimates:
- Report body is now streamed; the header's <APP_LoTW_NUMREC> gives the real
  QSL count, total size is estimated as NUMREC * 720 B (measured), and the UI
  shows a determinate bar with 'N QSOs, ~X s remaining' once known.
- Pre-sync hint shows the measured rate (~8 s per 100 QSOs).
- readTimeout raised to 600 s (stall timeout, not a total cap — huge accounts
  legitimately take 30+ min at ARRL's ~10 KB/s stream rate); connectTimeout
  raised to 60 s (a busy server took 36.5 s to accept a connection).

Also: ISettingsRepo gains last-sync date/callsign bookkeeping, and the mutual
test fake implements the new members.
2026-09-16 14:34:00 +08:00
atsunatsu 25b982ac59 feat(vucc): per operated-grid VUCC counting
GridQso gains myGrid (ADIF MY_GRIDSQUARE) captured per QSO during LoTW
parsing (handles alphabetical field order + EOR reset) and persisted as
JSON key 'mg' (absent in pre-myGrid data -> null). Legacy syncs keep the
global VUCC count; one resync unlocks per-grid counting.
2026-09-14 20:45:20 +08:00
atsunatsu 6a6399b606 fix(passes): recalculate passes when the UTC toggle changes
Port of upstream PR #252 (wty2019wty): combine otherSettings.stateOfUtc
into initRepository so an existing AOS time window is reapplied in the
newly selected timezone right after toggling UTC, instead of waiting for
the next selection/position change.
2026-09-14 18:54:22 +08:00
atsunatsu 212dced3c4 feat(data): persist ndot and protect manually imported transceivers
- DB v2 migration: entries.ndot column so hasDecayed() actually works after
  reload (parsed ndot was dropped on write, making the decay detection dead
  code); radios.isCustom column to mark manually imported transceivers.
- updateTransceiversFromFile marks imported radios isCustom=true; remote
  updates now deleteManagedRadios() then insert the fresh snapshot, keeping
  manual imports (no source can refresh them, so nothing would bring them back).
- Wire MIGRATION_1_2 into MainContainer (fallbackToDestructiveMigration=false
  would otherwise crash on upgrade).
2026-09-14 14:30:23 +08:00
atsunatsu 7ef555e377 fix(time): respect UTC toggle for AOS filter window and sunrise/sunset day anchor
- PassesViewModel: use the DEVICE timezone for the natural-day anchor in
  findTodaySunRiseSet instead of the display timezone. With UTC enabled the
  anchor was UTC midnight = 08:00 local for UTC+8 stations, when the sun is
  already up — tripping the white-night guard and rendering '--:--'.
- SatelliteRepo.isAosInRange: follow the UTC display toggle so the pass time
  window is interpreted in UTC when UTC mode is on. Previously the list
  showed UTC times while the filter silently used local time, off by the
  timezone offset (8h for China).
2026-09-14 14:13:16 +08:00
atsunatsu 08a5c8c460 fix(lotw): order-independent ADIF parsing — roamed/work grids dropped by sequential PROP_MODE gate
parseConfirmedGrids and parseRoamedGrids gated grid fields on propMode == SAT
at the moment the field line was read. LoTW emits ADIF fields alphabetically
(GRIDSQUARE/MY_GRIDSQUARE before PROP_MODE), so the gate was never satisfied
on real reports: the worked set AND the roamed set silently came back empty.
Both parsers now buffer the record and decide at <EOR>, matching the proven
parseConfirmedGridQsos pattern. Regression tests cover the alphabetical order.
2026-09-11 23:47:08 +08:00
atsunatsu 1cc0bc770c feat(map): VUCC default chip, own-grid bold at all zooms, roamed-grid blue stripes
- Grid mode now opens with the VUCC award chip selected by default (re-asserted
  on every grid-mode entry; the user's in-mode chip choice is preserved).
- The station's own grid outline is now drawn at every zoom level: 2-char field
  border below the sub-square zoom, 4-char square border above it (was hidden
  at the default entry zoom), stroke widened to 6f.
- Grids the account operated from (LoTW <MY_GRIDSQUARE>, satellite QSOs only)
  are covered with GridMaster-style blue 45° zebra stripes. The stripe paint is
  fully opaque so a worked+roamed cell shows pure-blue stripes with green
  between them — the two colors never alpha-blend into a teal/green mix.
- LoTW sync now parses and persists the roamed set (LoTWResult.Success +
  SettingsRepo key 'roamedGrids'), plumbed through MapState/MapViewModel into
  MaidenheadGridOverlay. Unit tests cover MY_GRIDSQUARE parsing (SAT-only,
  GRIDSQUARE non-leak, 4-char truncation).
2026-09-11 20:27:46 +08:00
atsunatsu 20143cf096 fix(lotw): strip STATE ' // Name' suffix for award stats
LoTW returns STATE as 'CODE // Name' (verified with a real report 2026-09),
e.g. 'GD // Guangdong' (China), '34 // Tottori-ken' (Japan), 'CA // California'
(US). The award calculator matched on the bare CODE, so every mainland China
province (WAPC), all Japan prefectures (WAJA) and US states (WAS) failed —
WAPC only showed HK/MO/TW (entity branch, no STATE needed) and WAJA was empty.

Normalize STATE once at parse time: strip the ' // Name' suffix so the stored
GridQso.state is the clean code consumed by persistence and AwardCalculator.
Add a test covering China/Japan/US formats.
2026-09-08 11:22:24 +08:00
atsunatsu 4a33d1b19a feat(map): award filters with boundary overlay in grid mode
- Grid-mode top bar replaced with award filter chips (All/DXCC/VUCC/
  WAPC/WAJA/WAZ/WAS) showing live progress (count/target)
- New AwardBoundaryOverlay renders real administrative boundaries
  (provinces/prefectures/CQ zones/states/countries) with the VUCC visual
  language: yellow boundary lines, green fill for worked regions, labels
- Boundary assets packaged from public GeoJSON (DataV China, Japan land,
  HB9HIL CQ zones, Natural Earth states/countries) with ADIF match keys:
  WAPC province codes, WAJA ADIF prefecture numbers, US state abbrs,
  ARRL DXCC entity codes
- GridQso extended with dxcc/country/cqz/state parsed from LoTW report
  (qso_qsldetail); AwardCalculator derives six-award progress; STATE is
  interpreted per DXCC entity (ADIF 'STATE depends on DXCC')
- Grid-mode toggle pill floated at map top-right (Grid mode / Satellite mode)
- New tests: AwardCalculatorTest (9 cases), LoTW award-field parsing (3 cases)
2026-09-08 08:22:12 +08:00
atsunatsu 237e4cb27a fix(update): APK asset URL used stale '-release' filename — downloads 404'd
Since v4.4.6-ba7opf.9.9 releases are uploaded as Look4Sat-<version>.apk (no
'-release' suffix), but parseRelease still built the download URL as
Look4Sat-<version>-release.apk. Update check succeeded (page parses) while the
download 404'd ('下载失败请重试'). Align the generated URL with the actual
asset name; mirror-prefixed URLs fixed too.
2026-09-08 06:09:42 +08:00
atsunatsu 86048ee1fd fix(lotw): run report fetch on Dispatchers.IO — main-thread blocking caused ANR/crash
fetchReportBody is a synchronous HttpURLConnection call (up to 30s connect +
120s read) but was invoked directly from a Main-dispatcher viewModelScope
coroutine: the whole UI froze for the duration of the request, then Android
killed the app with an ANR dialog. Wrap both fetch methods in
withContext(Dispatchers.IO). Bug present since the endpoint was introduced.
2026-09-08 04:32:21 +08:00
atsunatsu cf1d8e6539 LoTW sync: distinguish failure causes (bad credentials / rate limit / timeout / network)
Rate limit classification per ARRL policy (one download in progress per user
id; HTTP 503 'Page request limit' observed on throttled pulls) and the HTML
login-error page returned for wrong credentials. Settings screen now shows a
specific message per cause instead of one generic 'check callsign/password/network'.
2026-09-08 04:07:03 +08:00
atsunatsu 2c5b1479f1 test(lotw): VUCC up to 4 grids in one contact; clarify comment 2026-09-08 02:51:24 +08:00
atsunatsu 39d4498a54 feat(map): tap worked grid at zoom>=7 to show per-grid satellite QSO dialog
- LoTW sync now stores per-QSO detail (call/time/sat/mode/bands) grouped by
  gridsquare (PROP_MODE=SAT only), single JSON blob in SharedPreferences
- GridQso model derives band label (V/U, U/V, V/A...) from BAND_RX/BAND
- Tap detection via osmdroid MapEventsOverlay; selected grid outlined green
- Centered dialog: calls sorted by first-contact time, first-QSO summary,
  expandable call rows list subsequent QSOs in that grid; tap outside closes
2026-09-08 02:47:54 +08:00
atsunatsu 9ed2aebc9d fix(lotw): only collect satellite QSO grids (PROP_MODE=SAT); keep both grids of VUCC_GRIDS pairs 2026-09-08 02:09:25 +08:00
atsunatsu c372386a77 fix(data): LoTW sync resilience — longer timeouts (30s/120s), gzip transfer, follow redirects; ARRL server-side latency is the bottleneck 2026-09-08 00:05:22 +08:00
atsunatsu 2c9a5b376f feat: release-notes body in update checker; grid mode centers on station grid on every entry; own grid square drawn with thick outline; low-zoom meridian vanishing while panning fixed 2026-09-07 15:06:33 +08:00
atsunatsu efd6931abb fix: LoTW sync — force full report with qso_qslsince (LoTW default only returns new confirmations since last query) and accept lowercase <eoh> header 2026-09-07 13:51:41 +08:00
atsunatsu c7ca77a943 fix: sync dialogs use typed values directly (save+sync in one step); LoTW card wording; center map on station grid when entering grid mode; center grid labels in cells; update-check falls back to GitHub accelerator mirrors 2026-09-07 07:22:41 +08:00
atsunatsu be6473e692 feat: LoTW confirmed-grids sync card — fetch confirmed QSO grids directly from ARRL LoTW (lotwreport.adi) and merge with Wavelog grids; credentials stored locally 2026-09-07 06:55:42 +08:00
atsunatsu 13df0dfcec fix: Wavelog sync via v1 API logbook_get_worked_grids (3.0.2 has no API v2) 2026-09-07 02:11:40 +08:00
atsunatsu 4e3547649f feat: Maidenhead grid mode with worked-grid highlights from Wavelog (API v2 grid=all) 2026-09-07 01:42:42 +08:00
atsunatsu 7abbeb6eb3 fix: update check via GitHub web endpoint, avoiding API 60 req/hour rate limit 2026-09-06 21:33:41 +08:00
atsunatsu 5166daa297 fix: localize AMSAT date labels in Chinese 2026-09-01 10:18:56 +08:00
atsunatsu 604c2ec59d feat: check for updates page in settings
Add a full-screen update checker accessible from a button at the
bottom of the settings page. It queries the fork's GitHub latest
release and can download + install the APK in place.

- UpdateRepository (core/data): fetch releases/latest via GitHub API,
  parse tag/description/APK asset, stream APK download to cacheDir
- VersionComparator (core/domain): compare 4.4.6-ba7opf.N style tags
  (base version first, then build number; v-prefix tolerant) + tests
- IMainContainer/MainContainer: inject updateRepo
- UpdateCheckerScreen (feature/settings): shows current version,
  checks for updates, displays latest version + release notes, and a
  Download & install button; FileProvider hands the APK to the
  system installer
- SettingsScreen: bottom entry button, full-screen page toggle
- AndroidManifest: REQUEST_INSTALL_PACKAGES permission + FileProvider
- i18n strings (en/zh)
2026-08-29 16:25:51 +08:00
atsunatsu 7f390117b8 feat: data source toggles + HTTP status codes (upstream #242)
Port upstream PR #242 data layer: per-source enabled toggles and HTTP
status codes for custom data sources.

- IRemoteSource: NetworkResult(code, stream) + getNetworkStream
- RemoteSource: return HTTP status code (200 ok / error code / ERR)
- ISettingsRepo: dataSourcesStatus StateFlow<Map<String,Int>>
- SettingsRepo: persist enabled flags (single JSON per key) + status
- DatabaseRepo: filter by enabled toggles, write back status codes
- Settings.kt: DataSourcesSettings + satelliteEnabled/transceiversEnabled
- Sync test fakes (AmSatRepositoryTest, DatabaseRepoTest, Fakes.kt)

Fork-specific features preserved: builtinTypesByUrl reverse lookup,
satnogs migration, map-type data source structure.
2026-08-29 16:21:59 +08:00
atsunatsu 69af1c38bb fix: use real SatNOGS TLE source so newly launched satellites appear
- Point the built-in SatNOGS source at db.satnogs.org/api/tle/?format=3le
  (the CelesTrak satnogs group is a separate, slower subset); rename the
  old CelesTrak source to 'CelesTrak SatNOGS'.
- Add a one-time migration in SettingsRepo: installed apps that already
  persisted a satelliteUrls list containing the old CelesTrak SatNOGS URL
  get the real SatNOGS TLE URL inserted right after it.
- Dedupe imported satellites by catnum and radios by uuid in
  DatabaseRepo.updateFromRemote so multiple sources don't insert the
  same primary key twice.
- Test: importing JAMX-0825b (NORAD 98248) from the real SatNOGS source.
2026-08-28 15:23:23 +08:00
atsunatsu bc6e065804 feat: prefetch AMSAT status on foreground
Start AMSAT status prefetch when the app enters foreground and share the in-flight request with the status page fetch path to avoid duplicate network requests.
2026-08-28 00:41:32 +08:00
atsunatsu 7924a6f69e feat: cache AMSAT status while app is foreground 2026-08-23 19:27:16 +08:00
atsunatsu 433ce85561 feat: upload AMSAT status reports 2026-08-22 19:14:24 +08:00
atsunatsu 9e7fdbfe71 fix: passband RX update now uses full Doppler round-trip instead of pure passband mapping
When adjusting RX in passband mode (via +/- buttons or slider), the old
code used TransponderMapper.mapDownlinkToUplink (pure passband mapping
without Doppler compensation), producing ~10kHz error at LEO velocities.

Fix: use DopplerFrequencyCalculator.computeUplinkFromDownlinkWithOffset
for the full TX-from-RX round-trip, matching the RX→PASSBAND button
onClick logic. Also fixed frame mismatch: position now uses txLow/txRange
(Doppler-shifted frame) consistently.
2026-08-19 22:19:14 +08:00
atsunatsu ee3c1e7058 feat: add AMSAT satellite status tracking page (#234)
Port upstream PR #234 to fork main:
- New feature:status module (SatStatusScreen, SatStatusViewModel)
- New AmSatRepository + IAmSatRepository + SatStatus models
- RemoteSource: AMSAT API methods (getAmSatCatalog, getAmSatReports)
- MainContainer/IMainContainer: DI injection
- MainTheme: AMSAT status colors (tertiary slots)
- Chinese translations added for all AMSAT strings
- 26 unit tests passing

Note: navigation entry not included — upstream PR #234 intentionally
left it out pending UI restructuring. Add AMSAT to your nav bar or
menu as desired.
2026-08-08 22:13:56 +08:00
atsunatsu a08a28df39 fix: recalculate passes when station position changes
Use combine(selectedIds, stationPosition) in SatelliteRepo.initRepository
so that passes are recalculated on either satellite selection change or
station position change. StateFlow already emits distinct values.
2026-08-08 20:30:08 +08: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 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