News

Fix Ridge wind direction no longer snapped to 45° sectors

Kaider and Tavo flew Paldiski on the morning of 22 April (09:30–11:30 local), even though the Opportunities page had reported NO-GO for the day with the reason “wrong direction: NW (315°)”. They reported the flights afterwards — which is how the bug was caught. Aitäh!

Every forecast model on Windguru for that morning (HARM-FI 2.5 km, MET Nordic, HARM-DK, Zephyr-HD, HARMONIE) showed 286°–298°, well inside Paldiski’s 259°–310° acceptable window. The forecast itself was fine.

Root cause: the window-level direction aggregate bucketed the four passing morning hours (305°, 293°, 301°, 292°) into one of eight 45° sectors and returned the sector midpoint — here, NW 315°. The direction gate then compared that snapped 315° against Paldiski’s acceptable window and hard-failed. The true circular mean of those hours is 297.7°.

Fix: modal_wind_dir now returns a circular vector mean instead of the sector midpoint. The same snap affected Türisalu, Tabasalu, Toila, and Alajõe too — Alajõe was quietly passing some directions just outside its 135°–225° window because 140° rounded to 135°. Regression tests added for both flavours.

Code: modal_wind_dir in archive/cron/lib/assessors.php. Investigation and fix implemented by Claude Code.

Fix Morning flyable windows no longer masked by afternoon gusts

Pilots flew Tabasalu on the morning of 18 April, but the Opportunities page reported NO-GO for the whole day. Investigation: the forecast was correct — Vanasadam observed gusts up to 10.6 m/s in the afternoon, which tripped the 9 m/s ridge gust hard gate. But the gate was applied to the day’s maximum gust, so a single gusty afternoon hour killed the calm morning window too.

Fix: the gust gate is now also applied per hour. Gusty hours drop out of the flyable window, so a calm sub-window (09–12 UTC in this case) can still be assessed on its own. Simulated against the observed data, 18 April Tabasalu would now have shown a morning GOOD verdict.

While at it, Toila and Alajõe were extended with the same 9 m/s gust gate — previously they did not track gusts at all. All five ridge sites (Paldiski, Türisalu, Tabasalu, Toila, Alajõe) now apply the same gust rule, both per-hour and per-window.

Code: hour_passes_ridge, hour_passes_toila, hour_passes_alajoe, assess_toila, assess_alajoe, aggregate_coastal. Regression tests added. Changes implemented by Claude Code.

New Contrails card in the morning briefing

The briefing now includes a contrail forecast for Tallinn-Harku and Kuusiku at 300 and 250 hPa (~9–10 km cruise altitude). Per location the card shows:

Huge thanks to Mitja Vishnjakov — this one was entirely his idea. It is the kind of sideways thought that makes the whole site better: most of us look down at wind and BLH, Mitja looked up at the sky and asked what the jet trails could tell us. Aitäh, Mitja!

Data: Open-Meteo pressure-level forecast (ECMWF IFS).

Improved Rapla FP/FN confidence indicator — now live

The historical miss-rate indicator described below is now active on the Opportunities page. The daily calibration script ran for the first time today: 19 ERA5 dates evaluated, 99 (verdict, lead) pairs aggregated. All NO-GO forecasts in the post-v4 window had FN 0% at every lead time.

Buckets for MARGINAL/POSSIBLE/GOOD show “·” (fewer than 10 samples) — will fill in over the coming weeks.

New Rapla cells now show historical miss rate

Each Rapla cell on the Opportunities page now carries a small FP or FN number showing how the same verdict has performed historically at that lead time:

Computed daily from assessment_log (stored forecasts since the current Rapla rules took effect on 22 March) vs Open-Meteo ERA5 reanalysis. Only “is the day flyable?” is measured — tier shifts within flyable or within non-flyable don’t count.

Improved Morning briefing: maps and sounding

Three usability improvements to the daily morning briefing page:

Improved Dashboard accuracy overhaul

Major dashboard update driven by the first user feedback from Kaia Kala — thank you! The core insight: Pakri ridge-top station shows a systematic −27° to −30° wind direction bias in the ridge-relevant WSW–NW sector, making it misleading for soaring decisions.

Pakri bias documented from 100,681 matched hourly pairs (2013–2024) against Põhjasadam.

Improved Multi-model BLH verification fixed

The system compares boundary layer height (BLH) forecasts from two models — GFS and MET Norway Nordic — against ERA5 reanalysis to find which predicts thermal ceiling more accurately.

Investigation revealed that the “KNMI” model label was misleading: KNMI HARMONIE does not provide BLH at all. The Open-Meteo seamless wrapper silently substitutes MET Norway Nordic for forecast dates and ERA5 reanalysis for past dates. This made the old comparison circular — ERA5 vs ERA5.

Improved Ridge forecasts now use the best model for each lead day

A 30-day benchmark of 12 weather models against Estonian station observations revealed that no single model is best at all forecast horizons. Ridge soaring sites (Paldiski, Türisalu, Tabasalu, Toila, Alajõe) now automatically select the most accurate wind model based on how far ahead the forecast is:

Each assessment now records which model provided the wind data. Falls back gracefully to the default model if the preferred one is unavailable.

Data Forecast model benchmark published

The Forecast Accuracy Benchmark page now updates weekly with verification statistics for all 12 Windguru models against Estonian weather station observations. Metrics include wind speed MAE, direction MAE, and temperature bias, sorted by accuracy.

A separate lead-time analysis showed that high-resolution models (1–2.5 km) are barely better than global models (13–15 km) at D+0, but diverge sharply by D+3. Wind direction degrades faster than speed — by D+5, direction errors exceed the width of a typical ridge site’s optimal wind window.

Improved Wind and gust safety gates for Rapla and ridge sites

The assessors now check gusts, not just mean wind. Key changes:

Improved Forecast accuracy tracking — how far ahead can you trust the verdict?

The system now stores every assessment verdict it produces (with all the weather inputs that went into it) and compares them against ERA5 reanalysis — what the weather actually was. This builds a track record over time showing:

The accuracy pipeline runs locally. Results will feed back into model selection and threshold calibration as data accumulates over the coming weeks.

New Vatsla ground handling site added

The Opportunities page now includes Vatsla — a ground handling spot that works in NE through SSE winds (45°–170°). Complements the existing sites which favour west and south directions.

Improved Harku GH renamed to Lennuplats Kollane

The generic “Harku GH” ground handling site is now called Lennuplats Kollane — its actual name. Same location, same any-direction filter.

Improved Fewer API calls, faster Opportunities page

Ground handling sites that share the same forecast coordinates now reuse a single Open-Meteo response instead of making duplicate API calls. Reduces total requests from 9 to 7 per run, saving 2–4 seconds of runtime.

Improved Tap cells for details on mobile

The Flying Opportunities table now works on phones. Tapping any cell opens a bottom sheet with the full forecast details — gate reasons, window times, wind, BLH, temperature, soil moisture. Previously this information was only accessible via mouse hover on desktop.

Fix Opportunities page no longer crashes when Open-Meteo is slow

The Flying Opportunities page failed intermittently when Open-Meteo responses were slow. With a 30-second per-request timeout and a built-in retry, a single sluggish fetch could hold the script for 62 seconds — long enough for the server to kill the process mid-run. The page would silently stop updating.

Fixed by reducing the Open-Meteo fetch timeout to 15 seconds (worst-case retry cycle: 32 s) and adding set_time_limit(0) to prevent the server’s default 30-second execution cap from applying to long-running cron scripts.

Improved Scenario tests catch logic bugs before deployment

Assessment logic (hourly gates, wind direction windows, verdicts) is now covered by 34 automated scenario tests that run in CI on every commit. This catches inconsistencies like the gust fallback bug — where changing the verdict function without updating the upstream hourly filter caused gusty-but-flyable conditions to be silently rejected.

Pure assessment functions were extracted into a separate library (lib/assessors.php) for testability. The opportunities page shrank from 2200 to 1200 lines.

Fix Opportunities page survives FMI outages

The Flying Opportunities page crashed twice today when the FMI HARMONIE-AROME API went down. The script makes 24 HTTP requests to FMI (4 ridge sites × 6 days); with the API unresponsive, timeouts accumulated to ~480 seconds and exceeded the server’s execution limit.

Fixed with a circuit breaker: if the first HARMONIE request fails, all remaining FMI requests are skipped. Ridge sites gracefully fall back to Open-Meteo data. Also reduced per-request timeout from 20 s to 8 s.

New Soil moisture now in forecasts

Rapla airfield director Kristjan Plamus asked pilots to stay away for ~2 weeks because the grass field is too wet after snowmelt. That got us thinking: does ground moisture affect thermals too, not just the runway?

Turns out, yes. Dry ground heats the air more efficiently — wet ground wastes solar energy on evaporation instead (the Bowen ratio effect). Analysis of 114 Rapla flight dates (438 flights, 2014–2025) against ERA5-Land soil moisture data shows that pilots fly 2.6× farther on dry soil days (median 41.7 km vs 15.8 km on wet days). The effect is strongest on cool, marginal days — exactly when you need the extra signal most.

What changed:

Thresholds (DRY < 0.22, WET > 0.32 m³/m³) are calibrated on ERA5-Land 0–7 cm layer. Forecast API uses 0–1 cm — values are preliminary. Full analysis on the blog.

Improved Ridge sites work in gusty light wind

Ridge soaring sites (Paldiski, Türisalu, Tabasalu) no longer show NO-GO when sustained wind is below 4.0 m/s but gusts are strong enough to fly. Previously, mean wind < 4.0 m/s was an automatic NO-GO — now the system checks gusts and shows MARGINAL instead.

Calibrated from Kaider’s Paldiski flight today: mean wind only 2.8 m/s, but gusts 4.9 m/s — enough for 30 minutes of ridge soaring in fog. The old algorithm said NO-GO; Kaider proved it wrong.

New System status page

A status page now monitors all 11 data sources and shows their freshness. Automated alerts trigger if any source goes stale for more than an hour.

Improved Dashboard reads from database

The main dashboard now reads observations from the database instead of scraping live XML/HTML on every page load. Response time dropped from ~2 seconds to ~5 ms.

New Trip Planner with live flight prices

The Trip Planner combines XC weather forecasts with live flight prices from Tallinn for Ager (Catalonia), Bassano (Italy), and other destinations. Auto-discovers new spots from config files.

Improved Wind direction with HARMONIE-AROME 2.5 km

Ridge sites (Paldiski, Türisalu, Tabasalu) now use the FMI HARMONIE-AROME 2.5 km model for wind direction when available. This high-resolution model outperforms GFS by 50% for direction accuracy near the complex Estonian coastline.

Fix Wind speed unit bug

Fixed a bug where Open-Meteo wind speed was interpreted as m/s but was actually km/h (the API default). All wind thresholds are now correctly calibrated. This fix affected both the opportunities dashboard and the main dashboard.