Commit graph

42 commits

Author SHA1 Message Date
c1da0f20b1
Add LiveStash for state recovery across reconnects
- Installed live_stash ~> 0.1 with browser memory adapter
- Map page: recovers selected_band and selected_time on reconnect
- Submit page: recovers active_tab (single/csv) on reconnect
- JS params initialized with initLiveStash wrapper
2026-04-02 13:12:17 -05:00
20ed47397b
Improve map page mobile usability
Collapsible control panel with hamburger toggle, detail panel as
bottom sheet, compact legend moved to top-right, and scaled timeline
buttons for narrow viewports.
2026-04-02 12:59:55 -05:00
1da1cbed43
Fix nil access bugs found in security/bug audit
- TerrainProfileWorker: guard pos1/pos2 nil before accessing lat/lon,
  mark unavailable if missing instead of crashing
- Propagation analysis: guard pos1 nil in compute_factors
- contact_path_points: return [] instead of [{nil, nil}] on missing coords
- JS elevation hook: null check canvas before getContext
2026-04-02 12:38:49 -05:00
1cf92b2166
Cap propagation reach flood fill to 300 km radius
Without a distance limit, the BFS would expand across the entire grid
when conditions were broadly favorable, producing a polygon covering
most of CONUS.
2026-04-02 12:38:07 -05:00
4aaa028ed1
Highlight likely propagation duct, improve contact detail layout
- Mark duct closest to endpoint elevations as likely signal path
- Likely duct drawn with solid borders, bold label, stronger fill
- Other ducts shown dimmed with dashed borders
- Fix path info layout: table columns prevent El wrapping
- Surface observations as compact table, limited to 5 nearest stations
2026-04-01 13:31:56 -05:00
5f8c689035
Improve duct detection and compact contact detail layout
- Use all HRRR profiles along path (start, mid, end) for duct detection
- Fall back to sounding ducts when HRRR too coarse to detect layers
- Merge overlapping ducts, show top 3 strongest with source labels
- Limit surface observations to 5 closest stations along path
- Compact solar conditions as inline row
- Compact band/mode/distance info line
- Fix button text to "Flag as Invalid"
2026-04-01 13:28:42 -05:00
7ba82f4601
Duct visualization, flag invalid contacts, fix HRRR pruning
- Overlay HRRR duct layers on elevation profile chart (green bands)
- Add flagged_invalid field to contacts with toggle button on detail page
- Fix prune_old_grid_profiles to only delete 0.125° grid profiles,
  preserving QSO-linked HRRR data at arbitrary positions
- Auto-enqueue HRRR fetch when viewing contact without HRRR data (<48h)
- Compact solar conditions and contact details layout
- Add band/mode/distance info line to elevation profile section
2026-04-01 13:09:09 -05:00
f7a753626d
Add interactive popups to contact map, dedup reciprocals, darken line colors 2026-04-01 12:39:02 -05:00
cd8d7a0f52
Add contact detail map, elevation profile chart, and UI improvements
- Leaflet map on contact detail pages showing both station locations
- SRTM elevation profile with Chart.js: terrain, LOS, Fresnel zone
- Path info: distance, azimuth, elevation angle in both directions
- Compact 4-column grid layout for contact details
- Contact map page with all contacts plotted
- Rename QSOs to Contacts in page header
- Wrap algo page in standard layout
2026-04-01 12:34:03 -05:00
ea96c93e2d
Rename QSO to Contact in UI, add higher bands, improve submit page
- All menus and UI text now say Contacts instead of QSOs
- Add 68, 122, 134, 241 GHz bands to submit form and validation
- Add info box on submit page explaining why contacts matter
- Larger submit button with icon
- Make HRRR partition migration idempotent for partial re-runs
2026-04-01 12:28:24 -05:00
b86c822010
Add live UTC clock to map control panel 2026-04-01 10:38:02 -05:00
8f4f491a5e
Revert to algorithm scorer as primary, add propagation reach polygon
The ML model undervalues conditions outside Aug/Sep training data
(e.g. April with excellent factors scored 37/100). Algorithm's
physics-based factors handle unseen seasons correctly.

- Algorithm is primary scorer, ML infrastructure kept for iteration
- Remove unused ML grid worker code path
- Add client-side propagation reach: BFS flood-fill from clicked point
  through contiguous cells with score >= 50, drawn as convex hull polygon
2026-04-01 10:21:54 -05:00
14e86c4ddb
Filter forecast graph to only show current and future hours 2026-04-01 08:14:29 -05:00
376e855217
Show 'Best at X UTC' with peak score on forecast graph 2026-04-01 07:54:14 -05:00
d9b309ac6d
Escape key closes detail panel and range circle 2026-03-31 18:01:41 -05:00
b550f63005
Always show data timestamp in timeline bar, even with single time 2026-03-31 17:48:39 -05:00
09a26f1167
Add Y-axis (score) and X-axis (time) labels to forecast sparkline 2026-03-31 17:13:11 -05:00
741534610d
Change timeline label to 'Propagation Forecast' 2026-03-31 16:56:24 -05:00
583bcb2b0b
Hide forecast timeline when time spread is under 1 hour 2026-03-31 16:49:30 -05:00
6145f80c22
Add forecast sparkline graph to point detail panel
- point_detail response includes forecast array (score per valid_time)
- SVG sparkline shows score trend across all forecast hours
- Trend indicator: Improving/Declining/Steady based on first vs last score
- Add covering index (band_mhz, lat, lon, valid_time) INCLUDE (score) for
  point_forecast queries
2026-03-31 16:47:52 -05:00
11af9b966f
Fix timeline labels: only closest hour shows "Now", others show offset 2026-03-31 16:46:03 -05:00
e82e631135
HRRR forecast hours (f00-f18) with timeline map UI
- HrrrClient.hrrr_url accepts forecast_hour param (wrfsfcfHH.grib2)
- PropagationGridWorker fetches all 19 forecast hours per run
- Propagation.scores_at/3 queries scores at specific valid_time
- Propagation.available_valid_times/1 returns all forecast times for timeline
- Pruning keeps scores with valid_time >= now - 2h (forecast-aware)
- MapLive: select_time event, timeline data pushed to JS
- JS: forecast timeline bar at bottom of map with clickable hour buttons
- PubSub broadcast sends list of valid_times instead of single time
2026-03-31 16:44:47 -05:00
c775eb2611
Preserve detail panel and range circle across score updates
- Add phx-update="ignore" to detail-panel so LiveView patches don't wipe it
- Re-request point_detail after scores update to refresh with new data
- Range circles persist across updates since they're on a separate layer
2026-03-31 16:28:38 -05:00
ff3a879dff
Rework viewshed: score-aware NLOS range, sidebar detail panel, factor explanations
- Viewshed uses propagation score to set max range (ducting/NLOS distance)
- Terrain checked within radio horizon; clear paths get full atmospheric range
- Detail panel moves from map popup to sidebar for better visibility
- Panel shows instantly on click with loading state
- Dark opaque background for readability over map
- Factor analysis section explains each score with contextual descriptions
2026-03-31 13:49:42 -05:00
8f5b53180b
Use propagation tier color as popup header background 2026-03-31 13:08:21 -05:00
5eaa55448e
Add terrain-aware viewshed on map click
Replace generic range circles with actual LOS coverage polygon computed
from SRTM elevation data. Casts 180 rays (every 2 degrees) from the
clicked point, runs Fresnel/diffraction analysis on each, and renders
the reachable area as a Leaflet polygon.

- Viewshed module with haversine forward, terrain sweep, async compute
- Antenna height control (default 8 ft) in map panel
- LiveView start_async/handle_async for non-blocking computation
- Remove signal icon from band selector
2026-03-31 13:06:01 -05:00
1aebd7f1cc
Optimize map performance: reduce payload 75%, faster rendering
- Remove factors from heatmap scores, fetch on-demand via point_detail
- Pre-encode initial scores JSON once instead of per-render
- Don't store scores in socket assigns (reduce memory)
- Pre-calculate RGBA color lookup table for canvas rendering
- Batch fillStyle changes, hoist math out of inner loop
- Show topbar immediately on data requests (no 300ms delay)
2026-03-31 10:26:20 -05:00
2f2f3388cd
Rename 'How this works' to 'Scoring Algorithm' 2026-03-31 10:24:04 -05:00
ccac7a5020
Add NTMS title to map panel, make range circles non-interactive 2026-03-31 10:17:16 -05:00
c3a5cf4b1a
Add Maidenhead grid overlay, consolidate map controls, improve UX
- Port grid square rendering from gridmap-web with toggle control
- Consolidate band selector, grid toggle, data timestamp, and links
  into unified control panel
- Add "How this works" (/algo) and "Submit a QSO" (/submit) links
- Add back-to-map link on algo page
- Darken range circles for visibility over propagation overlay
- Fix popup blocking double-click zoom with pointer-events passthrough
- Prevent control panel from intercepting map click/scroll events
2026-03-31 10:15:21 -05:00
aa694c1771
Remove auto zoom-out on range circle click 2026-03-31 09:53:31 -05:00
69b9128c3f
Fix range circles: zoom to fit, increase opacity, add distance tooltips 2026-03-31 09:52:13 -05:00
2e216d4244
Make point detail popup instant with client-side lookup and range circles
- Scores now include factors and valid_time in the viewport query,
  eliminating the server round-trip for popups
- Click shows two range circles: solid inner (typical range) and
  dashed outer (max estimated range), colored by score tier
- Circles disappear when popup closes
- Band info pushed to client on band switch for accurate range estimates
2026-03-31 09:50:48 -05:00
0c295c1558
Add click-to-inspect factor breakdown popup on map
Click anywhere on the propagation map to see a detailed popup with:
- Overall score and tier label with color
- Estimated range for the selected band (CW mode)
- All 9 scoring factors with visual bar charts, individual scores,
  and weight percentages
- Grid point coordinates and data timestamp

Factors are displayed in weight order so users can immediately see
which atmospheric conditions are driving the prediction.
2026-03-31 09:45:50 -05:00
d5140267b1
Show topbar loading indicator while scores are loading 2026-03-31 09:30:20 -05:00
97d4bd9372
Only send visible map scores based on viewport bounds
The JS hook sends map bounds on load and on pan/zoom. The server
queries only scores within those bounds, dramatically reducing the
payload for band switches and map updates. At zoom 7 (DFW area)
this sends ~2k scores instead of ~95k.
2026-03-31 09:01:48 -05:00
66639e3717
Increase grid to 0.125 degrees with smooth canvas overlay
Replace circle markers with a canvas tile layer that renders smooth,
flowing colored regions using bilinear interpolation between grid
points. Colors interpolate between tiers for gradients. ~95k grid
points at 0.125 degree resolution with wgrib2 extraction.
2026-03-31 08:52:07 -05:00
c4de1dd77c
Center map on DFW with ~500 mile radius default view 2026-03-31 08:07:16 -05:00
86e01748ef
Fix legend readability with explicit text color 2026-03-30 17:30:58 -05:00
8cbce9565d
Fix Leaflet hook registration and full-page map
Move PropagationMap hook from colocated .hooks.js to assets/js and
register it directly in app.js hooks config. Fixes "unknown hook"
error at runtime.
2026-03-30 17:29:05 -05:00
5a0113aac2
Add Leaflet map integration with propagation score display
Vendor Leaflet 1.9.4 (JS, CSS, marker images) and wire it into
the esbuild/Tailwind asset pipeline. Create MapLive with band
selector buttons, auto-refresh, and a colocated JS hook that
renders propagation scores as color-coded circle markers with a
legend. Stub Propagation context and BandConfig modules provide
the data interface for the scoring pipeline.

Add nav bar with links to Map, QSOs, and Submit pages.
2026-03-30 17:11:00 -05:00
bc674f0d54
initial 2026-03-28 11:28:47 -05:00