Commit graph

1929 commits

Author SHA1 Message Date
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
2c16fd979d
Move ML deps to dev/test only, exclude from production build
- Nx, Axon, EXLA, Polaris deps restricted to only: [:dev, :test]
- model.ex and training mix tasks moved to lib_ml/ (compiled via
  elixirc_paths in dev/test only)
- load_ml_model uses Code.ensure_loaded? + apply/3 to avoid
  compile-time references to ML modules in production
- Verified: MIX_ENV=prod compiles clean with no ML warnings
2026-04-01 12:10:31 -05:00
254e64dedc
Rename qsos to contacts throughout codebase, keep DB table name
Rename all modules, functions, variables, routes, and UI text from
qso/qsos to contact/contacts. Database table stays as "qsos" to avoid
migration. Add /qsos -> /contacts redirects for old URLs.
2026-04-01 11:25:04 -05:00
bc529af392
Update algo.md Finding 10: corrected mode advantage, no SSB on rainscatter 2026-04-01 11:10:11 -05:00
fea9523943
Correct Finding 10: Great Lakes firing squad explains 24G PH anomaly
SSB is not possible on rainscatter - the original analysis was wrong.
The 24 GHz PH advantage is entirely a contest strategy artifact: Great
Lakes operators line up on opposite shores for rapid-fire SSB contacts.
With EN/CM/DM cluster activity removed, CW leads by 16% at 24 GHz.
CW advantage is monotonically increasing with frequency as physics
predicts. No 24 GHz anomaly exists.
2026-04-01 11:07:58 -05:00
b75516ff9f
Partition hrrr_profiles by valid_time for query performance
54M row table partitioned into date-range partitions. Queries with
valid_time filter only scan relevant partitions. Prune now uses
partition-aware DELETE. Fix tests for partitioned constraint behavior
and removed sort-click tests (grouped table uses URL params).
2026-04-01 11:03:33 -05:00
072c418cb3
Fix HRRR prune timeout: batch delete with 60s timeout instead of full table scan 2026-04-01 10:45:09 -05:00
b9aac6f688
Show grouped reciprocal QSOs on main listing, not just search 2026-04-01 10:43:41 -05:00
341a04190a
Show user_submitted column, remove committed column from QSO table 2026-04-01 10:41:25 -05:00
6fbec7ea9b
Fix Dockerfile: copy algo.md before mix compile (read at compile time) 2026-04-01 10:38:35 -05:00
b86c822010
Add live UTC clock to map control panel 2026-04-01 10:38:02 -05:00
b46d592411
Fix callsign search input alignment 2026-04-01 10:35:10 -05:00
7e65327e84
Optimize Dockerfile layer caching
Move COPY priv after mix compile so ML model retraining doesn't bust
the compile cache. Create empty priv dirs before compile for
:code.priv_dir. Move algo.md after compile. Add docs/plans and
analysis outputs to .dockerignore.
2026-04-01 10:32:56 -05:00
95907c90d3
Detailed Finding 10: mode advantage breakdown by band/frequency
CW advantage scales dramatically with frequency: +29% at 10G, +48% at
47G, +221% at 75G. At 24 GHz the pattern reverses (PH wins by 8%)
due to rainscatter favoring wider-bandwidth modes. Above 122 GHz,
100% of contacts are CW — SSB can't close the path.
2026-04-01 10:26:55 -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
02cb4fd67b
Integrate ML model into grid worker, QSO search, and UI improvements
ML Integration:
- Load trained model at app startup, cache compiled predict fn in persistent_term
- Grid worker uses batched ML prediction (10K chunks) when model loaded,
  falls back to algorithm scorer when not
- ML score replaces composite, algorithm factor scores preserved for detail view
- Fix process explosion: single EXLA call per chunk instead of per-grid-point

QSO Features:
- Callsign search (ILIKE on station1/station2) with trigram indexes
- Reciprocal QSO grouping (same pair, same band, same hour)
- Wider layout (max-w-7xl) for data table pages
- QSO Training Data link on map page

Infrastructure:
- Re-enable hourly propagation grid worker in dev
- Track ML model weights in git for Docker builds
- Add btree indexes on qsos (timestamp, band, distance_km)
- Remove nav icons from layout header
2026-04-01 10:14:22 -05:00
489e188956
Add --no-pretrain flag for direct QSO training comparison 2026-04-01 09:36:32 -05:00
9537c97d1d
20-feature model with solar indices, sounding stability, and ducting
Add SFI, Kp max (solar), K-index, lifted index (sounding stability),
and ducting_detected (HRRR) as model features. Training now joins to
solar_indices and nearest sounding (within 6 hours) for both phases.
Model can learn solar/geomagnetic effects if they exist in the data.
2026-04-01 09:31:54 -05:00
07558d17eb
Two-phase training: pretrain on algorithm scores, fine-tune on QSOs
- 15 features: add surface_refractivity and latitude
- Bigger network: 128→64→32 (3 hidden layers)
- Phase 1: pretrain on 500K stratified algorithm scores (all seasons/locations)
- Phase 2: fine-tune on 57K real QSO-HRRR matched data (percentile target)
- Lower LR (0.0003) for fine-tuning to preserve pretrained knowledge
- Model.train accepts :initial_state option for transfer learning
2026-04-01 09:27:27 -05:00
08e4b9abdd
Bigger network (128→64→32) and percentile-based training target
- 3 hidden layers instead of 2 for better feature interaction learning
- Target is within-band distance percentile (0-1) instead of raw
  normalized distance — reduces noise from operator/equipment variation
2026-04-01 09:17:36 -05:00
10cbbf47b3
Train ML model on real QSO-HRRR data instead of algorithm scores
Features: HRRR conditions averaged at both QSO endpoints + solar time
Target: distance_km normalized per-band (distance / p99_range, capped at 1.0)
This trains on actual propagation outcomes from 57K+ QSOs, not the
hand-tuned algorithm output.
2026-04-01 09:13:05 -05:00
69b5caf876
Normalize ML features to prevent NaN gradient explosion
Raw features had vastly different scales (pressure ~1013, sin/cos ~[-1,1])
causing gradient explosion. Normalize all atmospheric features to ~[0,1]
using known physical bounds. Add Polaris dep for optimizer.
2026-04-01 09:08:33 -05:00
b391d3a75e
Add ML training pipeline with Polaris optimizer
- Add polaris dep for Adam optimizer (Axon.Optimizers deprecated)
- Fix Model.train/3 to use Polaris.Optimizers.adam
- Training task: mix propagation_train with stratified band sampling
- Model encodes solar time (longitude/15) not fixed timezone
2026-04-01 09:06:10 -05:00
5c9b43d619
Update analysis with solar time, refine algo.md time-of-day section
Solar time (longitude/15) replaces fixed CDT/CST offset for time-of-day
scoring. Correlation analysis shows dramatic improvement at higher
frequencies: 24 GHz rho jumps from 0.056 (UTC) to 0.188 (solar), and
75 GHz corrects from spurious -0.39 to physically correct +0.24.
2026-04-01 09:02:57 -05:00
c12f8cf5ed
Use local solar time for time-of-day scoring, add PWAT factor and pressure refinements
Score time-of-day per grid point using longitude/15 solar offset instead of
hardcoded CST/CDT. Add PWAT as 10th scoring factor. Refine pressure thresholds.
Update ML model and training pipeline to use local solar time.
2026-04-01 08:58:21 -05:00
83a1511c84
Update algo.md with data-driven findings from 57K QSO analysis 2026-04-01 08:41:33 -05:00
b7fc195e82
Ensure stale scores get pruned even when HRRR fetch fails 2026-04-01 08:36:14 -05:00
4bf9d0b4b0
Add mix propagation.analyze task for QSO-HRRR correlation analysis 2026-04-01 08:34:04 -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
8c45125e7c
Comprehensive CLAUDE.md update with full project architecture and setup 2026-03-31 17:52:57 -05:00
b11e0bac84
Always show at least the most recent valid_time in timeline 2026-03-31 17:49:19 -05:00
b550f63005
Always show data timestamp in timeline bar, even with single time 2026-03-31 17:48:39 -05:00
4151f7d314
Filter past hours from forecast timeline (30-min grace for current hour) 2026-03-31 17:47:16 -05:00
6dff6372ab
Default forecast timeline selection to closest hour to now 2026-03-31 17:45:54 -05:00
2b1fd6129d
Disable propagation grid worker and freshness monitor in dev for backfill 2026-03-31 17:27:59 -05:00
e028de51ae
Tighten HRRR grid profile retention from 48h to 24h 2026-03-31 17:20:46 -05:00
fe5228b306
Prevent duplicate PropagationGridWorker jobs via Oban unique constraint
Worker now uses unique: [period: 3600, states: [:available, :scheduled,
:executing, :retryable]] so Oban atomically prevents duplicates.
FreshnessMonitor simplified — no longer does manual oban_jobs query.
2026-03-31 17:16:40 -05:00
09a26f1167
Add Y-axis (score) and X-axis (time) labels to forecast sparkline 2026-03-31 17:13:11 -05:00
88655e40a6
Add meteorological foundations section to algo.md
Comprehensive description of atmospheric science behind the predictions
for meteorologist review: refractivity-propagation connection, four
ducting mechanisms, HRRR model usage, frequency-dependent effects,
diurnal/seasonal cycles, and known limitations.
2026-03-31 17:00:30 -05:00
741534610d
Change timeline label to 'Propagation Forecast' 2026-03-31 16:56:24 -05:00
fe150adf77
Change antenna label to 'Antenna height' 2026-03-31 16:55:40 -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