Hit area: was Math.max(dotSize * 2, 15) — up to 24px radius around a 4px
dot. Now the CSS hover-flicker is fixed the large hit area is unnecessary
and was the reason dots activated when merely nearby. Changed to dotSize + 4
(proportional, 4px padding around the visual circle).
Overlap spread: compute all band positions first, then run an iterative
push-apart pass (spreadOverlappingDots) that nudges any two dots whose
arc gap is smaller than 2 * maxHitArea + 2px apart. Positions within a
band are stable across renders (same hash angle, same band = same output
before spreading) so dots don't shuffle on every update.
Z-order: sort visible devices by rssi_current ascending before rendering
so the strongest signal lands last in SVG order and receives clicks when
dots stack.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace continuous estimated_distance_m-based radius with proximity band
snapping (immediate/near/far/unknown → fixed radius ratios of 0.15/0.40/
0.70/0.90). The proximity_band is computed server-side from rssi_ema which
is already smoothed, so it changes infrequently — dots now only move when
a device genuinely crosses a band boundary rather than on every RSSI
fluctuation.
Also removes the client-side EMA and positionCache added in the previous
commit, and reverts CSS style.transform back to SVG transform attribute to
avoid coordinate-system mismatch when the SVG is displayed at a scaled size.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The remaining jitter after the in-place DOM rewrite was caused by RSSI
fluctuations propagating directly into dot positions on every 200ms
update cycle.
Two fixes:
1. Client-side EMA (alpha=0.25) on x/y coordinates per device. Each
render blends 25% toward the new raw position and retains 75% of the
smoothed position, filtering high-frequency RSSI noise without hiding
genuine distance changes. positionCache is keyed by device_key and
cleared on device removal or radar reset.
2. CSS transition (transform 0.6s ease-out) on each wrapper element.
Switching from SVG transform attribute to style.transform enables
native CSS transitions, so any remaining position change (e.g. a band
crossing) animates smoothly rather than snapping.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of rebuilding devicesGroup.innerHTML on every render, mutate
existing SVG elements in-place (update transforms, attributes, class
names) and only create/remove elements when devices genuinely appear
or disappear from the visible set.
This eliminates the root cause of both the jitter and the blank-radar
regression: hover state can never be disrupted by a render because the
DOM elements under the cursor are never destroyed. The isHovered /
renderPending / interactionLockUntil state machine and its associated
mouseover/mouseout listeners are removed entirely — they are no longer
needed. A shared buildSelectRing() helper deduplicates the animated
selection ring construction used by renderDevices() and
applySelectionToElement(). Closes#143.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace capture-phase mouseenter/mouseleave with bubbling mouseover/mouseout
for tracking hover state in the ProximityRadar component.
The capture-phase approach caused two problems:
1. Moving between sibling child elements (hit-area → dot circle) fired
mouseleave, prematurely clearing isHovered and triggering a DOM rebuild
that caused visible jitter.
2. When renderDevices() rebuilt innerHTML, the browser fired mouseleave for
the destroyed element with relatedTarget pointing at the newly created
element at the same position, leaving isHovered permanently stuck at true
and suppressing all future renders.
The fix uses mouseover/mouseout (which bubble) with devicesGroup.contains()
to reliably detect whether the cursor genuinely left the device group, immune
to innerHTML rebuilds. Fixes both WiFi and Bluetooth proximity radars as they
share this component. Closes#143.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add missing setThemePreference() and setAnimationsEnabled() functions
to settings-manager.js; sync theme/animations dropdowns in _updateUI
- Fix base.html toggleTheme() saving to wrong localStorage key ('theme'
instead of 'intercept-theme'), causing theme not to persist in ADS-B
and AIS dashboards; also sync button icon and persist to server
- Add [data-theme="light"] CSS variable overrides to adsb_dashboard.css
and ais_dashboard.css so the dashboards respond to light theme
- Fix GPS sky view canvas (gps.js) to read grid/label colours from CSS
variables instead of hardcoded dark hex values; add MutationObserver
to redraw immediately on theme change
- Fix satellite_dashboard.html polar plot functions to read background,
accent and text colours from CSS variables
Closes#139
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Weather Satellite:
- Fix duplicate event listeners on mode re-entry via locationListenersAttached guard
- Add suspend() to stop countdown/SSE stream when switching away from the mode
- Call WeatherSat.suspend() in switchMode() when leaving weathersat
- Fix toggleScheduler() to take the checkbox element as source of truth,
preventing both checkboxes fighting each other
- Reset isRunning/UI state after auto-capture completes (scheduler path)
- Always re-select first pass and reset selectedPassIndex after loadPasses()
- Keep timeline cursor in sync inside selectPass()
- Add seconds to pass ID format to avoid collisions on concurrent passes
- Improve predict_passes() comment clarity; fix trajectory comment
ADS-B dashboard:
- Batch altitude-colour trail segments into runs of same-colour polylines,
reducing Leaflet layer count from O(trail length) to O(colour changes)
for significantly better rendering performance with many aircraft
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New mode providing real-time space weather data from NOAA SWPC, NASA SDO,
and HamQSL APIs. Includes Kp index, solar wind, X-ray flux charts, HF band
conditions, D-RAP absorption maps, aurora forecast, solar imagery, flare
probability, and active solar regions. No SDR hardware required.
Bumps version to 2.20.0. Updates all documentation including README, FEATURES,
USAGE, UI_GUIDE, help modal, and GitHub Pages site.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix SatDump crash reported as "Capture complete" by collecting exit
status via process.wait() before checking returncode
- Fix PTY file descriptor double-close race between stop() and reader
thread by adding thread-safe _close_pty() helper with dedicated lock
- Fix image watcher missing final images by doing post-exit scans after
SatDump process ends, using threading.Event for fast wakeup
- Fix failed image copy permanently skipping file by only marking as
known after successful copy
- Fix frontend error handler not resetting isRunning, preventing new
captures after a crash
- Fix console auto-hide timer leak on rapid complete/error events
- Fix ground track and auto-scheduler ignoring shared ObserverLocation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend cross-mode analytics to include ACARS/VDL2 message counts, APRS
stations, and Meshtastic messages. Refactor count helpers into reusable
_safe_len() and _safe_route_attr() utilities. Add health checks for
rtlamr, dmr, and meshtastic modes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a unified analytics mode under the Security nav group that aggregates
data across all signal modes. Includes emergency squawk alerting (7700/7600/7500),
vertical rate anomaly detection, ACARS/VDL2-to-ADS-B flight correlation,
geofence zones with enter/exit detection for aircraft/vessels/APRS stations,
temporal pattern detection, RSSI history tracking, Meshtastic topology mapping,
and JSON/CSV data export.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace IBM Plex Mono, Space Mono, and JetBrains Mono with Roboto
Condensed across all CSS variables, inline styles, canvas ctx.font
references, and Google Fonts CDN links. Updates 28 files covering
templates, stylesheets, and JS modules for consistent typography.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add VDL2 to syncModeUI setter map and allModes array in agents.js
so agent state sync works for VDL2
- Fix dashboard bottom gap by using flex layout on body instead of
hardcoded calc(100dvh - 160px) height
- Match source stat font-size to other stats (14px) for consistent
strip sizing
- Add left-sidebars wrapper, VDL2 agent mode support, mutual sidebar
collapse, and ACARS/VDL2 modeNames in index.html
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gpsd sends multiple SKY messages per cycle — some contain only DOP
values with an empty satellites array. Previously this would overwrite
the satellite list, causing the sky view to flicker empty. Now DOP-only
SKY messages preserve the existing satellite list. Also adds a 5-second
polling fallback for satellite data since SSE can miss sky updates due
to queue contention.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bluetooth enhancements (service data inspector, appearance codes, MAC
cluster tracking, behavioral flags, IRK badges, distance estimation),
ACARS SoapySDR multi-backend support, dump1090 stale process cleanup,
GPS error state, and proximity radar/signal card UI improvements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New modes:
- BT Locate: SAR Bluetooth device location with GPS-tagged signal trail,
RSSI-based proximity bands, audio alerts, and IRK auto-extraction from
paired devices (macOS plist / Linux BlueZ)
- GPS: Real-time position tracking with live map, speed, heading, altitude,
satellite info, and track recording via gpsd
Bug fixes:
- Fix ABBA deadlock between session lock and aggregator lock in BT Locate
- Fix bleak scan lifecycle tracking in BluetoothScanner (is_scanning property
now cross-checks backend state)
- Fix map tile persistence when switching modes
- Use 15s max_age window for fresh detections in BT Locate poll loop
Documentation:
- Update README, FEATURES.md, USAGE.md, and GitHub Pages with new modes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add .gitignore entry for data/subghz/captures/ to prevent large
IQ recording files from being committed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Throttle audio waterfall rendering (50ms→200ms), eliminate per-frame
Array.from() allocation, drain stale pipe buffer before streaming,
increase chunk size to 8192, and remove debug logging from animation
hot paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Digital voice is intermittent — dsd-fme only outputs PCM during active
voice transmissions. Without input, ffmpeg never wrote the WAV header
and the browser got an empty response. Add an audio bridge thread that
feeds 100ms silence chunks during voice gaps so ffmpeg always has input
and the browser receives a continuous WAV stream. Add auto-reconnect
on the frontend if the audio stream drops while the decoder is running.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stream decoded digital voice audio to the browser via ffmpeg pipeline
(dsd-fme 8kHz PCM → ffmpeg → 44.1kHz WAV → chunked HTTP). Persist
frequency/protocol/gain/ppm settings in localStorage so they survive
page navigation. Add bookmark system for saving and recalling frequencies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>