Station map
Last 7 days
Next daystypical day, from 7-day pattern
All stations
FAQ
About & Data
What is Groundcheck?
A weather app built on real radiosonde (weather balloon) sensor readings instead of a forecast model. Every number you see was actually measured by a sensor in the air, not predicted.
Where does the data come from?
Live telemetry from sonde.mine.nu / zeesen.mine.nu, a public radiosonde-tracking feed. We fetch its
sondes.php endpoint on a schedule.Is this a forecast app?
No. The "Next days" panel is the one exception — it's built from this app's own 7-day historical diurnal pattern applied to each upcoming day, not an external forecast API. Everything else is raw sensor history.
Why only ground-level readings?
Sondes spend most of a flight at altitude, where it's routinely far below freezing. Those readings aren't representative of ground weather, so we filter to a configurable altitude ceiling (currently under ~1000m) and only show near-surface readings.
How often does the data refresh?
A background job fetches new readings every minute, plus deeper daily/weekly sweeps to catch anything missed. The page itself polls for updates every 5 minutes.
Why is a reading sometimes hours or days old?
Radiosonde coverage is sparse by nature — it's wherever a balloon happened to fly, not a dense permanent sensor grid. No new nearby ground-level reading is normal, not a bug.
What's a radiosonde?
A small instrument package carried aloft by a weather balloon, measuring temperature, humidity, and pressure as it ascends (and sometimes descends) through the atmosphere, transmitting data back by radio.
Why do Humidity or Pressure sometimes show "—"?
Not every sonde model carries a working humidity or pressure sensor — some report temperature only. A dash means that sensor had no data, not an app error.
What does "ground-level" actually mean here?
Any reading at or below the app's altitude ceiling (currently ~980-1000m depending on config) — usually captured near a sonde's launch site or during its final descent, not mid-flight.
Why zeesen.mine.nu instead of sonde.mine.nu?
They're mirrors of the same underlying project; zeesen.mine.nu responds faster for large historical fetches, so it's preferred for bulk backfills while both serve the same live data.
Do radiosondes get reused?
Rarely — most are single-use and either land somewhere unrecovered or are found and occasionally relaunched by hobbyists. Each flight gets its own serial ID in the data.
What's the difference between temperature, humidity, and pressure sensors on a sonde?
They're independent physical sensors on the same instrument package — a sonde model can have a working thermometer but a failed or absent humidity/pressure sensor, which is why some readings show partial data.
Accuracy & Coverage
Why does the nearest station show as far away?
This isn't a dense ground-sensor network — it's wherever balloons happened to fly recently. If nothing launched near you, the nearest valid reading may be hundreds of km away. We always show the real distance rather than hide it.
Is this more accurate than Apple Weather / a forecast app?
Different, not simply "more accurate" — this shows what a sensor actually measured, with no smoothing or modeling. A forecast app predicts; this reports.
Why does my location look approximate?
Precise GPS requires a secure (HTTPS) connection. On plain http://, the app falls back to IP-based location, accurate only to city level.
Can I see live in-flight data?
Yes — when the station currently shown is an actual balloon in flight, a live banner appears, fed by a WebSocket connection straight from the source telemetry.
Why did a reading show an obviously wrong temperature once?
Occasionally a sensor glitches or reports a bad packet. We filter out impossible values (like 0,0 coordinates) but can't catch every anomaly — treat any wildly outlying single reading with suspicion.
How far back does history go?
Depends on how long the instance has been running its fetch schedule — the public instance has months of accumulated history; a fresh self-hosted instance starts from whatever backfill window you choose.
Why does the same station ID show wildly different readings on different days?
That's real atmospheric variation, not an error — the same launch site can see very different conditions day to day, especially across seasons.
Can two readings from the same station conflict?
Each reading is keyed by station ID plus its exact receive timestamp, so multiple readings from one station over time are all kept — none overwrite each other.
Is coverage better in some countries than others?
Yes — it depends entirely on where radiosonde receiver stations happen to be tracking flights; Central Europe tends to have denser coverage than sparsely-populated regions.
Using the App
How do I find the station nearest me?
Allow location access when prompted, or use the "Use my location" button — the app matches you to the closest fresh (within 24h) ground-level reading.
What does the map show?
A satellite map with every ground-level station in the selected time range, clustered for performance, plus a "you are here" marker for your own location.
What's the "This day" map option?
Filters the map to only readings from one specific calendar date instead of a rolling window — useful for looking back at a particular day's coverage.
How does Search work?
Type a station ID, a temperature, or a place name. Place names are geocoded via Nominatim (OpenStreetMap) and matched to the nearest reading, with an adjustable date range from 7 days up to all-time.
Why doesn't Search find my city?
The geocoder needs an unambiguous, spelled-correctly place name. Try adding a country ("Springfield, USA") if a common name returns the wrong result.
What's the "Next days" predictor based on?
This app's own average hour-of-day temperature/humidity/pressure pattern computed from the last 7 days of store data, applied identically to each upcoming day and anchored to your current actual reading — not an external forecast service.
Can I clear the cache?
Yes — the footer has a "Clear cache & reload" button. Useful if the page seems stuck showing stale data, especially after adding it to your phone's home screen as an app.
Can I switch between light and dark mode?
The app follows your system's light/dark setting automatically — there's no in-app manual toggle, since it's built entirely with CSS media queries tied to your OS preference.
Does the map remember my zoom/position?
No — it recenters on the current selection each time you switch to the Map tab, rather than persisting a manually-panned view.
Can I see a specific station's full history?
Yes — clicking a station's reading anywhere in the app (map, stations list, search results) switches the current view to that station, showing its own readings.
Why does the desktop layout differ from mobile?
Desktop uses the extra screen space for a sidebar nav and multi-panel grid layout (map + week feed side-by-side) instead of mobile's single-column tab-based flow — same data, same features, different arrangement.
API & Developers
Is there a public API?
Yes — see API docs.
GET /api/data is rate-limited (30 req/60s by default); getting a key via POST /api/keys/new raises that to 9000 req/10s.Do I need an account to use the API?
No account required — the free key endpoint just generates a token on the spot, no signup, no email.
What format does the API return?
Plain JSON — an array of reading objects with id, time, temp, alt, pressure, humidity, lat, lon, and source. See the API docs for the exact shape.
Is there caching on API responses?
No — every response is served with
Cache-Control: no-store, so you always get the freshest data, never a stale cached copy.Is there an MCP server?
Yes — a Model Context Protocol server ships alongside the backend, exposing tools like
current_reading, nearest_station, station_history, and predictor for use with Claude Code or other MCP clients.Can I build my own app on top of this data?
Yes — that's exactly what the public API is for. No special permission needed, just respect the rate limits or grab a key.
What happens if I exceed the rate limit?
You get an HTTP 429 response until the window resets (60 seconds for the default tier, 10 seconds for a keyed tier) — no ban, no penalty, just wait it out or grab a key for a much higher ceiling.
How does the API key expire?
It doesn't — a key generated via
POST /api/keys/new stays valid indefinitely on that server instance unless the instance operator resets its api_keys.json file.Can the MCP server filter by location or recency?
Yes —
nearest_station takes lat/lon and an optional max_age_days, and search_readings supports temperature range filters.Is there a rate limit on the MCP server?
No — it reads directly from the local
data.json file, so there's no network rate limit to worry about, just normal local file I/O.Self-Hosting
Can I run my own instance?
Yes — the full backend (server, live-feed proxy, data fetcher, and frontend) is on GitHub in the
backend/ folder, with one-command launchers for both Linux/Mac and Windows.How do I start it on Linux or Mac?
Clone the repo,
cd backend, then run ./start.sh — it interactively walks you through ports, an altitude filter, initial data backfill, and automatic update scheduling.How do I start it on Windows?
Run
start.ps1 in PowerShell — same setup flow, using Windows Task Scheduler instead of cron for automatic checks. If WSL with a Linux distro is installed, it'll offer to hand off to the fuller start.sh instead.Does self-hosting need any paid services?
No — everything runs on plain Python (standard library only for the server), no database, no cloud dependency required.
Can I customize the ground-level altitude filter myself?
Yes —
start.sh/start.ps1 ask for it directly, or set the GROUNDCHECK_MAX_ALT_M environment variable manually.How do I get automatic updates on my self-hosted instance?
The setup script offers to install cron jobs (or Scheduled Tasks on Windows) that check for new radiosonde data every minute and/or once daily — fully configurable during setup.
Can I expose my self-hosted instance to the internet?
Yes, with something like a Cloudflare Tunnel or reverse proxy in front of it — the server binds to
0.0.0.0 by default so it's reachable on your LAN out of the box.Can I pick my own ports?
Yes —
start.sh/start.ps1 prompt for the HTTP data port and WebSocket live-feed port directly, defaulting to 8765 and 8766.What if I don't want the automatic checks running constantly?
Decline the cron/Task Scheduler setup step, or choose "0" for the custom-interval option during setup — you can always run
update.py manually whenever you want fresh data.How large is the initial backfill?
Depends what you choose — the setup menu offers 1 to 800 days; a 1-day backfill is nearly instant, while an 800-day one can take a long time and use more disk space for
station_store.json.Does self-hosting include the MCP server too?
Yes —
backend/mcp_server.py ships alongside everything else and resolves its data path relative to itself, so it works out of the box against your own instance's data.Android Tablet App
Is there an Android app?
Yes — a Kotlin WebView wrapper around this same web app, built for tablets, with pull-to-refresh, local notifications, and offline-friendly loading of the GitHub Pages URL.
Where do I download the Android app?
From the GitHub Releases page on the project repo — it's distributed as a sideloadable APK, not published on the Play Store.
Do I need to enable anything to install it?
Yes — since it's not from the Play Store, you'll need "Install from unknown sources" enabled for your browser or file manager.
Does the Android app get notifications?
Yes — a background job checks periodically for nearby rain/thunderstorm conditions and posts a real Android notification if one's detected.
Why does the Android app load a website instead of being fully native?
It's intentionally a hardened WebView wrapper loading the live GitHub Pages site — that way the app always shows the current version without needing a new APK build for every web update.
Does the Android app work without internet?
No — it needs a connection to reach the live data, same as the website. There's no offline data cache built in.
Is there pull-to-refresh?
It was tried and removed — it kept conflicting with the embedded Leaflet map's own pan/drag gestures, so the app relies on its normal auto-refresh polling instead.
macOS App
Is there a macOS app?
Yes — a native Swift/WebKit wrapper in the
macos/ folder of the repo, targeting macOS 13.0 and up.How do I build it?
Open
Groundcheck.xcodeproj in Xcode and hit Run, or use xcodebuild from the command line — see the folder's README for exact commands.Does it work on both Intel and Apple Silicon Macs?
Yes — it's a plain WKWebView-based app with no special GPU code; macOS itself handles GPU selection (Intel integrated, discrete AMD, or Apple Silicon) transparently at the OS level.
Is the macOS app sandboxed?
No — App Sandbox was tried and removed after it caused the app to hang on launch with no window ever appearing (confirmed via a
launchservicesd assertion failure in the system log) with a locally ad-hoc-signed build.Where do I get the app icon from?
It's a custom-generated icon (a weather balloon + radiosonde instrument box on a gradient sky) already bundled in the project's asset catalog — no extra download needed.
Troubleshooting
The page looks stuck / stale — what do I do?
Try the "Clear cache & reload" button in the footer first. If you added the app to your phone's home screen (iOS especially), that caching layer is more aggressive — delete and re-add the home-screen icon if the button alone doesn't fix it.
The live feed banner says "unreachable" — is something broken?
Not necessarily — it means the WebSocket connection to the live telemetry feed couldn't connect right now. It retries automatically every 5 seconds; if it's a self-hosted instance, check that
ws_proxy.py is actually running.Why does the map show a blank area?
Either the selected time range has no readings in that region, or satellite tiles are still loading — try zooming/panning slightly to trigger a tile refresh.
My self-hosted instance shows "no data available" everywhere.
data.json is likely empty or missing — run python3 update.py --days 7 (or answer "y" to the setup script's backfill prompt) to populate it.Location permission was denied — now what?
The app falls back to IP-based geolocation automatically (city-level accuracy) — you can still browse normally, just without precise "nearest station" matching.
Privacy, Account & Ads
Are there ads?
No. No ads, no ad SDKs, no third-party trackers or analytics embedded in the app.
Does it cost anything?
No — the app, the API, and self-hosting are all free.
Do I need to sign up for an account?
No. The optional "Account" tab just saves a nickname and a random local token to your browser's own storage — there's no server-side account system, no password, no email collected.
What data does the app collect about me?
Your device's location (only if you grant permission, used purely to find your nearest station) and whatever you type into Search. Nothing is sent to a third party beyond the geocoding lookup itself.
Is my location shared with anyone?
No — it's used client-side only, to compute distance to stations. It isn't stored server-side or sent anywhere beyond the optional geocoding request you trigger yourself in Search.
Can I delete my local account?
Yes — the Account tab has a "Forget this device" button that clears the saved nickname and token from your browser's local storage instantly.
Does the nickname/token get sent to a server anywhere?
No — it's purely local storage in your browser, never transmitted to the app's backend or any third party.
Development & Contributing
Is the source code open?
Yes — the full repo (frontend, backend, Android, macOS) is public on GitHub, linked from the footer.
What's the tech stack?
Hand-written HTML/CSS/JS for the frontend (no build step, no framework), Python for the server/data pipeline/MCP server, Go for the predictor service, Kotlin for Android, and Swift for macOS.
Why no frontend framework?
Deliberately kept simple — single self-contained HTML files avoid a build pipeline entirely, making it trivial to deploy as static files via GitHub Pages.
Can I suggest a feature or report a bug?
Yes — open an issue on the GitHub repo. It's actively maintained.
Search
Search by station ID, a temperature (e.g. 18 or 18°C), or a place —
city, street, country. Place names are geocoded, then matched to the nearest station.
Account
Save a nickname on this device — no password, no server account, just a locally-saved identity.
Welcome back,
Local token
Saved