- Add offline.stadia_key to OFFLINE_DEFAULTS in routes/offline.py
- Add stadia_dark and tactical tile providers to Settings.tileProviders
- Update getTileConfig() to inject Stadia API key or fall back to CartoDB dark
- Add setStadiaKey() method for saving and applying the API key
- Show/hide Stadia key row in setTileProvider() and _updateUI()
- Add Stadia options to tile provider select in settings modal
- Add Stadia API key input row to settings modal
- Add TDD tests for stadia_key backend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds initNavGroupState() and saveNavGroupState() functions so the
open/closed state of each .mode-nav-dropdown survives page reloads.
Active groups are never force-closed even if localStorage says closed.
Adds test_nav_state.py with two tests verifying presence of the
functions and data-group attributes on all five nav groups.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6-task plan covering token deepening, nav active state glow, panel pulse
animation, scanline texture, and localStorage nav group persistence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mission Control aesthetic, maps overhaul, mode polish sprint for 12 modes,
and 5 new features (Spectrum Overview, Alerts Engine, Signal Recording,
Signal ID, Mobile PWA) — decomposed into 4 sequential sub-projects.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds ruff pre-commit hook that auto-fixes and formats on every commit,
preventing lint CI failures from reaching GitHub.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(adsb): disable bias-T on stop and warn when toggled while running
The RTL-SDR bias-T hardware register persists after the device is closed,
so toggling bias-T off in the UI and stopping the SDR had no effect on the
actual hardware — verified with a multimeter in issue #205.
- Add disable_bias_t_via_rtl_biast() to rtlsdr.py (mirrors enable, uses -b 0)
- Track adsb_bias_t_active in adsb.py; call disable on stop_adsb() so the
hardware register is cleared when ADS-B is stopped
- Show an inline warning in the UI when the bias-T checkbox is toggled while
any SDR mode is active, since the setting only takes effect at start time
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(lint): remove unused imports in tscm sweep.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Four list-trimming loops used querySelectorAll (static NodeList) inside a
while condition, so .length never decreased — causing infinite loops that
froze the page, or repeated removeChild calls on already-removed nodes
(TypeError: parameter 1 is not of type 'Node').
Also replaces blocking alert() with showInfo() for start errors and adds
a .catch() handler to the start_sensor fetch so network failures surface
cleanly instead of leaving the UI in a broken state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AIS:
- New optional NMEA UDP forwarding via AIS-catcher's -u flag, configurable
from the AIS sidebar (host + port). Lets OpenCPN and other NMEA tools
receive live vessel data directly. All SDR builders updated.
- New GET /ais/vessels endpoint — clean JSON snapshot of tracked vessels
for REST integration
ADS-B:
- New GET /adsb/aircraft endpoint — JSON snapshot of all tracked aircraft,
with optional ?icao= and ?military=true filters. Response includes a
reminder that port 30003 (SBS) is already available for tools like
Virtual Radar Server and OpenCPN's AIS/target plugin.
Closes#90
- Pager and sensor gain inputs changed from unvalidated text fields to
number inputs with min/max/step constraints
- ADS-B dashboard now exposes a gain input in the tracking strip;
previously gain was hardcoded to 40 dB with no user control
- validate_gain() ceiling raised from 50 to 102 dB to support HackRF
(LNA 40 + VGA 62 = 102 dB combined) and LimeSDR (73 dB)
- sdrCapabilities gain_max values corrected: HackRF 62→102, Airspy 21→45
- onSDRTypeChanged() now propagates gain_max to all mode gain inputs so
HTML constraints match the selected SDR's actual range
Closes#162
Adds a "Custom Range" sweep type that lets users specify start/end MHz
instead of using a fixed preset. Useful in dense RF environments where
a full or standard sweep returns too many signals and causes slowdown.
UI shows start/end MHz inputs when "Custom Range" is selected. Range is
validated (0 < start < end ≤ 6000 MHz) before the sweep starts.
Backend threads the ranges through to _scan_rf_signals(), which already
supports arbitrary frequency bands.
Closes#172
/health now includes sdr_claims: a dict mapping 'sdr_type:device_index'
to the mode currently using that device (e.g. {"rtlsdr:0": "pager"}).
Empty when no devices are in use.
/devices/status already existed and returns the full device list with
in_use/used_by per device — documented in the issue response.
Closes#158
Adds three new icon shapes (widebody, bizjet, turboprop) to the existing
set (jet, prop, helicopter, military, glider), giving 8 distinct silhouettes.
Classification covers common ICAO type codes: widebodies (744, 777, A380 etc.),
business jets (Citation, Gulfstream, Learjet etc.), turboprops (ATR, DH8 etc.),
and light GA piston aircraft.
Hover tooltip now shows aircraft type description (e.g. "Airbus A320-200")
when available from the aircraft DB, in addition to callsign and altitude.
Closes#201
Strong passes at 40 dB (the previous default) cause RTL-SDR ADC clipping,
producing a distorted IQ stream that SatDump cannot lock onto. 30 dB is
a safer starting point that still captures weak passes cleanly.
Also adds a UI hint below the gain control explaining the saturation issue.
Closes#185
Remove the split fast-path in doLocateHandoff that called BtLocate.handoff()
directly when the module was already loaded. That path relied on handoff()
internally calling switchMode, causing a double switchMode in the lazy-load
path and no guaranteed mode switch in the fast path.
Now doLocateHandoff always calls switchMode('bt_locate') first (lazy-loading
script/styles as needed), then calls BtLocate.handoff() in .then(). Removed
the redundant switchMode call from BtLocate.handoff() since the caller owns
the mode transition.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the requestAnimationFrame loop in proximity-radar.js with a
CSS @keyframes rotation on .bt-radar-sweep, mirroring the WiFi radar
pattern. Adds two trailing arc paths for a glow effect and updates
setPaused() to toggle animationPlayState instead of the rAF flag.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace static channel bar chart and security dots with a scrolling
2.4 GHz channel heatmap (up to 10 scan snapshots) and an SVG donut
security ring showing WPA2/WPA3/WEP/Open network distribution.
Replaces the 7-column <table> network list with flex div rows featuring
two-line layout (SSID + security badges on top, signal bar + meta on
bottom), coloured left-border threat indicators, and new sort controls.
Renames selectedNetwork → selectedBssid and updateNetworkTable → renderNetworks throughout wifi.js.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
config.VERSION was not updated when the v2.26.13 tag was created,
causing the update checker to always report an update available on
fresh installs and git pulls.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move aircraft_db.json and aircraft_db_meta.json from the project root
to data/adsb/ so they survive container restarts and rebuilds. Add
matching volume mount to both Docker Compose profiles.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mounting ./data:/app/data caused the host directory to shadow the
entire /app/data Python package, making modules like data.oui
unavailable and crashing gunicorn on startup. Mount only the three
runtime output subdirectories instead.
Fixes#200
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>