Skip to content

Latest commit

 

History

259 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

seismic-sensor

Real-time seismic P-wave detector using a streaming neural network ensemble, deployed to seismic.fib896.com.

How it works

A 3-seed ensemble of StreamingNet models (1D CNN + orbit-permuted Hebbian buffer) listens to live SeedLink feeds from GEOFON and supplementary networks. When N_CONSENSUS stations independently fire within a 240-second window, a detection is logged (widened from 60s on 2026-08-19 — backfilled M5.5-6.9 events showed 98-208s between confirming stations' peak-confidence times, well past the old window). Epicenters are estimated via a flat-earth P-wave arrival time inversion (Nelder-Mead). Detections are cross-checked against USGS and EMSC earthquake catalogs.

SeedLink stream → normalize → StreamingNet × 3 seeds → ensemble vote
→ consensus across stations → epicenter localization → catalog lookup → dashboard

Architecture

StreamingNet — 3-channel 1D CNN with an orbit-permuted Hebbian buffer:

Input: (3, 100)  — 3-component seismogram, 1 second at 100 Hz
→ ConvBlock(3→32) → ConvBlock(32→64) → ConvBlock(64→128) → AdaptiveAvgPool
→ orbit-permuted Hebbian buffer (CYCLES=1, DECAY=0.876, STRENGTH=1.429)
→ Linear(128, 2)  — seismic / noise classifier
→ Linear(128, 1)  — magnitude estimator

The buffer permutation is seeded per ensemble member, diversifying the feature basis across seeds. Pre-trained on the STEAD dataset (chunk2) with magnitude-stratified sampling, then fine-tuned on station-collected regional data.

Performance (STEAD holdout, threshold=0.835): 88.0% precision / 95.7% recall
Performance (after regional fine-tune, eval split): 94.4% / 93.2% / 93.2% precision across seeds, mean F1 = 0.919

Stations

Network Code Location
GE APE Aegean, Greece
GE MORC Morava, Czech Republic
GE KBS Svalbard, Norway
GE WLF Walferdange, Luxembourg
GE MATE Matera, Italy
GE KARP Karpathos, Greece
CX PSGCX Paso Grande, Chile
CX HMBCX Humberstone, Chile
DK GDH Godhavn, Greenland
DK SCO Scoresbysund, Greenland
GE KIBK Kibungo, Rwanda
GE LBTB Lobatse, Botswana
GE PMG Port Moresby, Papua New Guinea
GE HNR Honiara, Solomon Islands

Primary streams via geofon.gfz-potsdam.de:18000 (GEOFON). Station list above reflects fly.toml's STATIONS — check that file for the current live set, since it's changed more often than this table.

IRIS note: rtserve.iris.washington.edu:18000 was upgraded to RingServer/4.5.6 (SeedLink v4.0 protocol) in 2026. obspy EasySeedLinkClient (v3.1) is incompatible — station SELECT and DATA commands are rejected. IRIS stations (IRIS_STATIONS in fly.toml) currently do not stream successfully; set to empty if you want a clean log.

Deploy

# Deploy to Fly.io (requires fly CLI + authenticated account)
# Smart: static/template-only changes push via sftp with no restart;
# anything else triggers a full `fly deploy`
make deploy

# Force-push static/template files to the running container, skipping the smart-deploy diff check
make deploy-static

# Force full rebuild (e.g. after Dockerfile changes)
make deploy-clean

# Tail live logs
make fly-logs

Note: env vars set in fly.toml override the Python defaults in config.py — if you change a default in code (e.g. the P_LEAD_S 0.4→0.5 fix on 2026-08-20), also update fly.toml/fly.alpha.toml or the code change won't actually take effect in prod. This has bitten us before.

Training

From scratch (STEAD)

Requires STEAD chunk2:

wget -c 'https://zenodo.org/records/3911667/files/chunk2.hdf5'
wget -c 'https://zenodo.org/records/3911667/files/chunk2.csv'

pip install torch numpy h5py scipy scikit-learn tqdm pandas

python train.py --data /data/training --checkpoints checkpoints/ --epochs 30

GPU strongly recommended. CPU training for 3 seeds × 30 epochs takes several hours.

Regional fine-tuning

collect_regional.py fetches P-wave windows for M5.5+ events at Pacific/Australian stations (IU.NWAO, II.WRAB, IU.MAJO, IU.SNZO) via IRIS FDSN, using IASP91 travel times to window around the P arrival:

python collect_regional.py   # saves .npz files to ./training/

Then fine-tune the existing ensemble on the collected windows:

python train.py --data training --checkpoints checkpoints --epochs 30 --lr 1e-4
# checkpoints/seed_{n}_pretrain.pt backups are written before overwriting

Fine-tuning uses a two-phase schedule: classifier head only for the first half of epochs (higher LR), then all layers unfrozen at 10× lower LR. A class-balanced weighted sampler handles imbalanced positive/negative ratios.

Local development

cp .env.example .env
# edit .env — set SEEDLINK_SERVER, STATIONS, etc.
make dev       # starts via docker compose
make logs      # tail logs
make shell     # bash into container

Configuration

All parameters are set via environment variables (see fly.toml and .env.example):

Variable Default Description
SEEDLINK_SERVER geofon.gfz-potsdam.de:18000 Primary SeedLink server
IRIS_SERVER rtserve.iris.washington.edu:18000 Secondary SeedLink server (see IRIS note above)
STATIONS GE.APE,... Comma-separated station list for primary server
IRIS_STATIONS IU.COR,... Comma-separated station list for secondary server
CHANNELS HHZ,HHN,HHE Seismic channels
THRESHOLD 0.835 Detection confidence threshold
N_CONSENSUS 2 Stations required to confirm a detection (2 is appropriate for this sparse network)
CONSENSUS_WINDOW 240 Seconds within which consensus must occur (widened from 60 on 2026-08-19, see above)
ALERT_COOLDOWN 60 (300 in prod) Minimum gap between fired alerts overall
PER_STATION_COOLDOWN 120 Minimum gap between alerts from the same station
N_SEEDS 3 Ensemble size
STALTA_ON 1 Enable STA/LTA pre-filter (blocks low-energy noise)
STALTA_SHORT_S 0.5 STA window length (seconds)
STALTA_LONG_S 10.0 LTA window length (seconds)
STALTA_THRESH 2.5 Minimum STA/LTA ratio to pass to consensus
STALTA_LARGE_THRESH 12.0 STA/LTA ratio indicating a large event
THRESHOLD_LARGE 0.45 Lowered confidence threshold used for large-event rescue
MIN_LOGIT_GAP 0.0 Minimum classifier logit gap required (0 = disabled, no empirical baseline yet)
P_LEAD_S 0.5 Model's pre-P horizon; must match between config.py's default and fly.toml's env override — they drifted once, see Deploy note
P_VEL_KM_S 8.0 Assumed P-wave velocity for epicenter localization
LOC_MIN_STA 3 Minimum stations required to attempt localization
USGS_MIN_MAG 4.0 Minimum magnitude for USGS catalog cross-check
EMSC_MIN_MAG 2.0 Minimum magnitude for EMSC fallback cross-check
USGS_SIG_MIN_MAG 5.5 Minimum magnitude to flag as a "significant event" poll
TELE_MATCH_WINDOW 300 ± seconds around expected P-arrival to count as a catalog match
STATION_LOG_DIR / STATION_LOG_RETENTION_DAYS /data/station_log / 3 Persisted per-station confidence log (Fly's own log buffer only keeps a couple minutes — added after a 2026-08-19 missed-M5.5 near Ruteng, Indonesia was unrecoverable at the station-score level)
API_KEY unset If set, required as X-API-Key header (or ?api_key=) on /api/config, /api/stations, and other non-public endpoints

Slack + Bitcoin integrations (undocumented until now): SLACK_WEBHOOK_URL/SLACK_SIGNING_SECRET wire up /slack/command for a Slack slash-command interface; BTCVM_LEDGER_PATH/BTCVM_BROADCAST/BTC_WIF back /api/btcvm, an optional Bitcoin OP_RETURN detection ledger.

Tests

# Python (detection/consensus logic)
pytest tests/ -v

# Cypress E2E (web dashboard — map, replay slider, tooltips)
npm install
npm run e2e             # headless: boots the Flask web layer + runs Cypress
npm run e2e:open        # interactive Cypress runner

The E2E suite boots only the web layer (scripts/e2e_server.py — no torch/seedlink needed) and exists specifically to catch template/static desyncs: a 2026-08-18 incident shipped stale index.html alongside fresh app.js because Flask caches compiled templates and doesn't reload them without a process restart. CI runs both suites (test, e2e jobs) before every deploy.

License

AGPL-3.0-or-later — see LICENSE

About

Real-time multi-station seismic P-wave detector with TDOA epicenter localization and body-wave magnitude estimation, deployed to Fly.io

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages