🌐 UberSDR Doppler Monitor

Ionospheric Doppler shift measurements

⬤ Connecting…

Live Status

Doppler shift is measured from a spectrum window centred on each carrier (), using a power-weighted centroid over the 3 dB peak region — the same algorithm used by the UberSDR frequency reference monitor. Typical precision is ~0.01 Hz at SNR > 20 dB. Spectrum updates every ~2 seconds; minute means average ~30 samples. Once a station drops to No signal, it will not return to a valid state until the signal has been continuously above the SNR threshold for a full minute.

Station Frequency Doppler Shift SNR Signal Noise Floor Updated (UTC) State
Loading stations…

Signal Lock

centred on each carrier. Green dashed = nominal frequency. Red marker = detected peak (power-weighted centroid, ~0.01 Hz precision at high SNR). Scroll to zoom · drag to pan · double-click to reset.

Doppler Shift History

Zero line = no Doppler shift. Positive = ionosphere falling (layer compressed). Negative = ionosphere rising. Gaps = signal below SNR threshold. "Absolute frequency" mode shows received Hz.

🗺 Propagation Map

Day/night terminator · great-circle paths · F-layer reflection points · click markers for details

Manage Stations

Settings

Receiver Identity (auto-populated from UberSDR)

Download CSV

Downloads a HamSCI Grape-format CSV file. The file begins with a # metadata header block: the first line is #,<timestamp>,<node>,<grid>,<lat>,<lon>,<elev>,<location>,G1,<station>, followed by a ####### comment block containing station metadata (node number, callsign, grid square, lat/lon/elevation, city/state, beacon, nominal frequency, frequency standard, radio). The final header line is the column header UTC,Freq,Vpk. Each subsequent data row is one 1-second observation: UTC (HH:MM:SS), Freq (received frequency in Hz = nominal + Doppler), Vpk (peak amplitude, linear 0–1).

All endpoints are read-only GET requests unless noted. Responses are JSON unless the endpoint returns CSV. The curl examples below use the URL you are currently viewing this page on.

CSV Download — GET /api/csv

Returns 1-second Grape-format CSV data for a station. Three mutually exclusive time-range modes:

ModeParameterscurl example
Full UTC day station= & date=YYYY-MM-DD curl "http://localhost:8080/api/csv?station=WWV-10&date=2026-05-04" -o wwv10.csv
Relative window station= & last=<N>[s|m|h] curl "http://localhost:8080/api/csv?station=WWV-10&last=15m" -o wwv10_15m.csv
Absolute range station= & start=<RFC3339> [& end=<RFC3339>] curl "http://localhost:8080/api/csv?station=WWV-10&start=2026-05-04T16:45:00Z&end=2026-05-04T17:00:00Z" -o wwv10_range.csv
  • last= units: s seconds, m minutes, h hours (e.g. last=15m, last=1h, last=3600s).
  • When end= is omitted with start=, end defaults to now.
  • Windows that span midnight automatically read from both UTC day files.
  • Output format: Grape # metadata header + UTC,Freq,Vpk rows (identical to the full-day download).

Live Station Data — GET /api/stations

Returns a JSON array of all stations with their current reading, 60-minute baseline mean, and latest spectrum snapshot.

curl http://localhost:8080/api/stations

Live SSE Stream — GET /api/events

Server-Sent Events stream of 1-second Doppler readings. Optional station= filter; omit to receive all stations.

curl -N "http://localhost:8080/api/events?station=WWV-10"
  • Event types: connected (on connect), heartbeat (every 15 s), spectrum (FFT bins), unnamed data events (readings).
FieldTypeDescription
stationstringStation label as configured.
server_timeRFC 3339Wall-clock time the server emitted this event.
reading fields
timestampRFC 3339UTC time of this 1-second measurement.
doppler_hzfloat64Measured carrier offset from nominal frequency (Hz). Positive = higher than nominal.
corrected_doppler_hzfloat64 | omittedDoppler after subtracting the reference station's clock error (Hz). Omitted when no reference station is configured or the reference has no valid signal.
snr_dbfloat32Signal-to-noise ratio of the carrier peak (dB above noise floor).
signal_dbfsfloat32Peak carrier power (dBFS).
noise_dbfsfloat32Estimated noise floor — 5th-percentile power across the full 100 Hz window (dBFS).
validbooltrue when SNR ≥ threshold and the carrier peak passes all quality gates. false readings still carry snr_db, signal_dbfs, and noise_dbfs.
propagation metrics — omitted until ≥10 valid samples; always omitted when valid = false
doppler_spread_hzfloat64 | omittedStandard deviation of doppler_hz over the last ≤60 valid samples. Elevated values (>0.1 Hz) indicate ionospheric spread-F, multipath, or rapid fading. Typical quiet-ionosphere value: <0.05 Hz.
scintillation_s4float64 | omittedAmplitude scintillation index S4 = stddev(A)/mean(A) where A = 10^(signal_dbfs/20) (linear amplitude), over the same rolling window. Values >0.3 indicate moderate scintillation.
multipath_indexfloat64 | omittedRatio of sideband energy to carrier energy in the FFT window: sum of linear power in ±10 Hz sideband (excluding ±4 Hz guard) divided by carrier linear power. Higher values indicate stronger multipath or adjacent interference. Typical clean-path value: <0.01.

Minute-Mean History — GET /api/history

Returns a JSON array of 1-minute mean readings for a station.

ParameterDescription
station=Required. Station label.
date=YYYY-MM-DDOptional. Return a specific UTC day from disk. Omit for the rolling 24 h in-memory window.
smooth=NOptional. Rolling average window in minutes (1–10). Default 1 (no smoothing).
curl "http://localhost:8080/api/history?station=WWV-10&smooth=5"

Global Settings — GET / POST /api/settings

GET returns current settings JSON. POST updates settings (requires auth if a UI password is configured).

curl http://localhost:8080/api/settings

Audio Preview — GET /api/audio/preview

Streams a live mono WAV audio preview of the station's carrier frequency. The connection is established on demand and dropped when the client disconnects. Useful for verifying the carrier is audible.

ParameterDescription
station=Required. Station label.
  • Response: audio/wav with a streaming WAV header (mono, S16LE PCM). Sample rate is read from the UberSDR audio stream (default 12000 Hz).
  • The audio is centred 1 kHz below the carrier frequency (dial frequency = carrier − 1000 Hz), so the carrier tone appears at 1 kHz in the audio.
  • GET /api/audio/info?station=<label> returns { sample_rate, dial_freq_hz, carrier_freq_hz, label } without opening the stream.
Use caseCommand
Play directly (Linux, ALSA) curl -sN "http://localhost:8080/api/audio/preview?station=WWV-10" | aplay -f S16_LE -r 12000 -c 1
Play with ffmpeg upsample to 48 kHz (Linux/macOS) curl -sN "http://localhost:8080/api/audio/preview?station=WWV-10" | ffmpeg -i pipe:0 -af aresample=48000 -f wav pipe:1 | aplay
Play with ffmpeg on macOS (CoreAudio) curl -sN "http://localhost:8080/api/audio/preview?station=WWV-10" | ffmpeg -i pipe:0 -af aresample=48000 -f wav pipe:1 | afplay -
Save to file curl -sN "http://localhost:8080/api/audio/preview?station=WWV-10" -o wwv10_audio.wav

Raw IQ Stream — GET /api/iq/stream

Streams a live stereo WAV of raw IQ samples centred on the station's carrier frequency (±6 kHz, 12 kHz total bandwidth). Compatible with SDR tools and the Web Audio API.

ParameterDescription
station=Required. Station label.
  • Response: audio/wav with a streaming WAV header. Format: 2 channels (I = left, Q = right), S16LE PCM, sample rate from UberSDR IQ stream (default 12000 Hz).
  • Centre frequency is the station's nominal carrier frequency. The ±6 kHz window captures the full 100 Hz Doppler monitoring band with wide margin.
  • GET /api/iq/info?station=<label> returns { sample_rate, centre_freq_hz, bandwidth_hz, label } without opening the stream.
Use caseCommand
Save to WAV file curl -sN "http://localhost:8080/api/iq/stream?station=WWV-10" -o wwv10_iq.wav
Pipe to GNU Radio / SDR tools (raw S16LE interleaved IQ) curl -sN "http://localhost:8080/api/iq/stream?station=WWV-10" | ffmpeg -i pipe:0 -f s16le -ar 12000 -ac 2 pipe:1 | your-sdr-tool
Upsample to 48 kHz stereo WAV with ffmpeg curl -sN "http://localhost:8080/api/iq/stream?station=WWV-10" | ffmpeg -i pipe:0 -af aresample=48000 wwv10_iq_48k.wav
Convert to complex float32 for numpy / scipy curl -sN "http://localhost:8080/api/iq/stream?station=WWV-10" | ffmpeg -i pipe:0 -f f32le -ar 12000 -ac 2 pipe:1 > wwv10_iq.cf32