- Round grid-derived lat/lon in maybe_fill_latlon changeset step
- Format lat/lon to 6 decimal places on index table
- Migrate existing beacon data to 6 decimal precision
- Add comma separators to EIRP mW display (e.g. 10,000 mW)
- Extract shared add_commas helper for format_freq and format_mw
Height in feet doesn't need decimal precision. Migrates the DB
column, updates schema type, and strips trailing .0 from form
input so the integer cast succeeds.
- Add CommaNumber JS hook for live comma formatting while typing
frequency MHz in the beacon form
- Strip commas server-side before changeset validation
- Order beacon list by most recently added (desc inserted_at)
- Round lat/lon to 6 decimal places in changeset and display
- Round height_ft to integer in changeset and display
- Display coords at 6 decimal places on show page
Without -s, wgrib2 -lon only outputs msg:offset:lon=X,lat=Y,val=Z
with no variable name or level. The -s flag adds the short inventory
(d=DATE:VAR:LEVEL:...) so the parser can identify which variable
each value belongs to.
setup-buildx-action downloads from GitHub Releases which is slow
and unreliable from the Forgejo runner. Plain docker build/push
works fine since Docker is already on the runner host.
Points spread coast-to-coast created a ~476k cell bounding grid
(350 messages × 476k cells × 4 bytes ≈ 665 MB), causing OOM.
Switch to -lon which extracts values at specific lat/lon points
with text output. One wgrib2 call, one file scan, negligible
BEAM memory regardless of point geographic spread.
- Stream profile storage and score upsert instead of materializing
full 20k+ item lists (propagation_grid_worker, propagation.ex)
- GC between forecast hours and store/compute phases to reclaim
~400 MB of grid data between steps
- Single-pass field extraction in scorer.ex path_integrated_conditions
instead of 6 separate Enum traversals
- Eliminate intermediate merged map in fetch_grid by combining
merge + profile build into one pipe
- Fix UUID bug: bingenerate → generate in native grid worker
(same issue previously fixed in nexrad_worker)
The parallel download was holding all ~530MB of range responses in
memory before writing to disk. Now each range is fetched and written
one at a time, so only one chunk is in memory at a time.
Instead of holding ~530MB GRIB binary in BEAM memory, download
ranges directly to a temp file and run wgrib2 on it. Peak memory
drops from ~530MB to just HTTP chunk buffers.
Release.backtest_all, climatology, native_derive now enqueue an
AdminTaskWorker job on the new :admin queue and return immediately.
Progress visible in Oban Web at /admin/oban.
- Add `mix backtest --all` for consolidated pass/fail table across all features
- Add Backtest.consolidated_report/2 and to_consolidated_markdown/1
- Add Features.all_features/0 to auto-discover backtestable features
- Add `mix import_contest_logs` for bulk ARRL contest CSV import with dedup
- Fix hrrr_climatology to batch by (month, hour) to avoid query timeout
- Fix Repo.query! result pattern (Postgrex.Result, not tuple)
- Backtest reports for all Phase 1-6 features
Mix tasks that call app.start were also booting Oban's cron scheduler,
causing PropagationGridWorker and other cron jobs to fire during
backfills. Add Oban.pause_all_queues(Oban) immediately after app.start
in every mix task that only needs Repo access.
FrontalAnalysis module (Weather.FrontalAnalysis):
- detect_fronts/3 computes the Thermal Front Parameter (TFP) from
2D grids of surface temperature and pressure using Nx vectorized
ops. TFP = -nabla|nabla(theta)| . nabla(theta)/|nabla(theta)|.
Most negative values mark cold fronts.
- central_gradient/1 for 2D finite differences with edge handling
- nearest_front/3 finds closest front point with distance and bearing
- path_front_angle/2 computes angle between a QSO path and the
front (0 = parallel = good, 90 = crosses = dead)
Backtest feature stubs for distance_to_front and parallel_to_front
(return nil until the pipeline caches per-cell frontal features from
the hourly HRRR grid run). The FrontalAnalysis module itself is
tested and ready for integration.
NEXRAD spike docs also included in this commit.
NCEI ASOS 5-minute data client (Weather.NceiMetarClient):
- fetch/3 pulls per-station monthly .dat files from NCEI C00418
- parse/1 decodes the fixed-width METAR format including precise
T-group temperatures (T02110094 → 21.1/9.4°C)
- metar_5min_observations table: schema-identical to
surface_observations, separate table to avoid mixing cadences
Weather.recent_surface_obs/3 prefers 5-min data when available,
falls back to the hourly surface_observations table.
Data URL: https://www.ncei.noaa.gov/data/automated-surface-observing-system-five-minute/access/YYYY/MM/asos-5min-KXXX-YYYYMM.dat
Available back to 1996.
Phase 3 NEXRAD spike: IEM n0q composite available at 5-min cadence
back to 2022+. Compression-ratio proxy shows afternoon images have
13-81% more texture than dawn (directionally correct), but the n0q
product thresholds out the faint clear-air returns needed for BL
stability detection. Parked until MRMS or Level III products can be
investigated. See docs/research/nexrad_spike.md.
Phase 6: hrrr_climatology table aggregating surface_temp_c by
(lat, lon, month, hour) from the 42M+ hrrr_profiles grid-point
rows. mix hrrr_climatology builds it via a single SQL GROUP BY +
upsert. Backtest.Features.temperature_anomaly computes current_temp
minus climatological mean — the meteorologist's "temperature
deviation above normal" predictor for summer afternoon enhancement.
Add Propagation.Region module with 8 CONUS climate zones (gulf_coast,
southeast, southern_plains, corn_belt, northeast, desert_southwest,
pacific_northwest, mountain_west) and per-region monthly seasonal
adjustment multipliers.
The scorer's score_season now takes lat/lon and applies a regional
multiplier from Region.seasonal_adjustment on top of the band's
seasonal_base + seasonal_adj. Gulf coast August gets a 1.15x boost
(drier, better for ducting) while Corn Belt August gets a 0.80x
penalty (corn evapotranspiration = miserable dewpoints).
Adjustments are hand-tuned starting points from the meteorologist's
qualitative guidance. Phase 9 recalibration will refine them from
backtest data.