Realtime environmental awareness: distributed sound localization + classification + common operating picture
Project description
MinimapPR
Realtime environmental awareness: distributed sound localization + classification + common operating picture.
This repository now includes a complete Phase 1 core build focused on the base case:
- functional two-node ingestion model (point node + Sirith tetrahedral array node)
- TDOA localization backend (GCC-PHAT + least-squares solve)
- canonical event envelope (
event_id,source_type, TOA/TOR,time_quality, provenance refs) - core schema persistence for nodes, observations, detections, tracks, labels, alerts, and track updates
- geographic COP frontend with node health, GDOP overlay, symbology, uncertainty ellipses, velocity vectors, track table, and detection feed
First Delivery Scope (Implemented)
FastAPIbackend for node ingestion, localization, classification, tracking, and APIs- queue-driven fusion node runtime (ingest stage decoupled from localization/classification workers)
SQLitepersistence for Phase 1 core tables (nodes,observations,detections,tracks,labels,alerts,track_updates) plus activeenvironmentingestion/persistence- automatic snippet retention cleanup (self-cleaning raw audio extracts)
- snippet serving endpoint (
/api/v1/detections/{id}/audio) - node audio debug endpoint for recent per-node listen checks (
/api/v1/nodes/{node_id}/audio/recent) - live websocket feed with server-side subscription filtering (zone/category/confidence/track status)
- geographic Leaflet COP dashboard
- deterministic simulation stream with both required node types
Project Layout
minimappr/main.py: API server + lifecycleminimappr/core/: buffering, localization, tracking, fusion-node orchestrationminimappr/classifiers/: classifier interface, heuristic backend, optional YAMNet backendminimappr/storage/db.py: SQLite schema + persistenceminimappr/frontend/: basic web UIminimappr-ingest-sidecar/: Rust firmware-facing ingest spool proxyminimappr/sim/run_demo.py: realtime two-node simulator (point + Sirith tetra)firmware/: shared embedded node runtime + Sirith/point firmware targetstests/: localization and classifier tests
Quick Start
pip install minimappr[full] # installs birdnet + tensorflow + tensorflow-hub
minimappr # starts server at :8080
- Create environment and install dependencies.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
- Start server.
uvicorn minimappr.main:app --host 0.0.0.0 --port 8080 --reload
- In a second terminal, run two-node live demo stream.
source .venv/bin/activate
python -m minimappr.sim.run_demo --server http://127.0.0.1:8080
- Open UI.
http://127.0.0.1:8080
You should see nodes appear, detections populate, and tracks move on the map.
Node Types in MVP
1) Point node (ESP32-style stream node)
- Single-channel microphone stream
- Node type:
point - Intended for GPS/PPS-timestamped network localization
2) Sirith tetrahedral node
- Four-channel array stream
- Node type:
sirith_tetra - Default geometry: regular tetrahedron with 50 mm edge (
schematicskept for hardware reference) - In this MVP it contributes 4 independent channels to the backend solver
Firmware (ESP32 + Pico)
Firmware projects are in firmware/:
firmware/lib/minimap_node_core: shared node runtime/protocol/transportfirmware/lib/minimap_audio_esp32: ESP32 I2S audio sourcesfirmware/lib/minimap_audio_pico: RP2040/RP2350 Pico TDM audio sourcesfirmware/nodes/sirith_tetra: Sirith tetrahedral node firmware (dual-I2S -> 4 channels)firmware/nodes/sirith_tetra_pico: Sirith tetrahedral Pico W / Pico 2 W firmware (TDM-4)firmware/nodes/point_single_mic: reference point node firmware
Full firmware setup and build instructions are in firmware/README.md.
Ingestion Protocol (Current)
Endpoint: POST /api/v1/ingest/frame
Payload:
{
"node": {
"id": "point-node-01",
"node_type": "point",
"position_m": [0.0, 0.0, 2.0],
"sensor_offsets_m": [[0.0, 0.0, 0.0]],
"capabilities": ["audio", "gps_pps"],
"metadata": {}
},
"frame": {
"start_time_ns": 1739810000000000000,
"sample_rate_hz": 16000,
"channels": 1,
"encoding": "pcm16le",
"samples_b64": "...",
"sequence": 42
},
"environment": {
"temperature_c": 21.4,
"humidity_fraction": 0.52,
"pressure_pa": 101325.0,
"source": "onboard_sensor"
}
}
Notes:
- audio payload is interleaved
pcm16le, base64 encoded frame.channelsmust matchlen(node.sensor_offsets_m)- timestamps are per-frame start timestamps in
ns - optional per-frame timing quality metadata supported:
time_quality,toa_ns,tor_ns - optional environmental payload supported:
environment.temperature_c(minimum), humidity/pressure/wind/lux optional - firmware-compatible fallback: if
node.metadata.temperature_cis provided, it is ingested intoenvironmenteven without an explicitenvironmentobject - response
triggered=truemeans an event candidate was queued for fusion workers; detection emission is asynchronous
Firmware Ingest Sidecar
For deployment, firmware should post high-rate batch ingest to the Rust sidecar instead of the Python UI/API process.
# Terminal 1: Python UI/API and spool consumer
.venv/bin/python -m minimappr
# Terminal 2: Rust fast-path proxy
MINIMAPPR_INGEST_SPOOL_DIR=data/spool ./dist/minimappr-ingest-sidecar
Defaults:
- Python FastAPI UI/API listens on
:8080. - Rust sidecar listens on
:8081. - Sidecar accepts
POST /api/v1/ingest/binaryandPOST /api/v1/ingest/store-forward, streams bodies todata/spool/tmp/, atomically publishes complete items todata/spool/ready/, then returns202 Accepted. - Python drains
data/spool/ready/, drops items older thanMINIMAPPR_INGEST_SPOOL_READY_TTL_SECONDS(default60), and moves parse/delivery failures todata/spool/failed/. - Set
MINIMAPPR_DIRECT_INGEST_ENABLED=falseto disable direct Python access to firmware batch ingest endpoints.
Firmware deployments should point the node ingest port at 8081.
Processing Pipeline
- Ingest timestamped audio frames.
- Append channel streams to rolling per-sensor buffers.
- Trigger candidate events from frame RMS threshold.
- Enqueue trigger candidates to fusion workers.
- Build synchronized multi-sensor windows.
- Run GCC-PHAT TDOA measurement and nonlinear 3D solve.
- Classify event audio (heuristic baseline, pluggable backend).
- Associate/update track.
- Persist detection + track and emit live websocket event.
- Save mono snippet for retention window; periodic cleanup removes expired snippets.
Classifier System
Implemented classifier architecture is pluggable:
- default:
heuristic(fast baseline labels:bird_like,speech_like,impulse,machine_hum,ambient,unknown) - optional:
yamnet(if TensorFlow dependencies are installed) - optional:
birdnet(direct bird species classifier, better suited to bird-focused testing with longer clips)
Set backend with:
export MINIMAPPR_CLASSIFIER=heuristic
# or
export MINIMAPPR_CLASSIFIER=yamnet
# or
export MINIMAPPR_CLASSIFIER=birdnet
For bird-focused testing, there is also a bundled runtime preset:
export MINIMAPPR_RUNTIME_PROFILE=birdnet_omni_testing
That preset switches to direct BirdNET, disables beamformed classification, skips localization before classification, and expands the classification clip to a 30 s trailing omni window while keeping the short localization trigger window intact.
For production wildlife deployment, use the hybrid preset:
export MINIMAPPR_RUNTIME_PROFILE=birdnet_hybrid_production
That preset uses direct BirdNET, keeps omnidirectional classification on a 30 s reporting window, runs SRP-PHAT localization on a low-frequency band for the sirith tetrahedral array, and emits one canonical detection per label/reporting window with localized detections preferred over omni-only detections.
API Endpoints
GET /healthGET /api/v1/configGET /api/v1/fusion/statusGET /api/v1/federation/statusPOST /api/v1/ingest/frameGET /api/v1/nodesGET /api/v1/detections?limit=100GET /api/v1/tracks?limit=200&include_standby=falseGET /api/v1/cop/statusGET /api/v1/alerts?limit=100GET /api/v1/environment?limit=500&node_id=...GET /api/v1/environment/current?x=...&y=...&z=...GET /api/v1/detections/{detection_id}/audioGET /api/v1/nodes/{node_id}/audio/recent?seconds=10POST /api/v1/federation/heartbeat(peer-to-peer)POST /api/v1/federation/snapshot(peer-to-peer)WS /ws/live
Audio Path Validation (Developer)
Use node-level debug audio playback when detections are absent and you need to verify that audio ingest is healthy.
- Open the COP dashboard and use the
Node Audio Debugpanel. - Click
Listenon a node to request the most recent buffered clip. - Confirm it sounds reasonable; if not, inspect node health and ingest transport before classifier tuning.
Equivalent API call:
curl "http://127.0.0.1:8080/api/v1/nodes/http-node-1/audio/recent?seconds=10" --output node_recent.wav
Runtime Configuration
Key env vars:
MINIMAPPR_HOST(default0.0.0.0)MINIMAPPR_PORT(default8080)MINIMAPPR_DB_PATH(defaultdata/minimappr.db)MINIMAPPR_SNIPPET_DIR(defaultdata/snippets)MINIMAPPR_SNIPPET_RETENTION_SECONDS(default3600)MINIMAPPR_TRIGGER_RMS(default0.015)MINIMAPPR_TRIGGER_COOLDOWN_SECONDS(default0.8)MINIMAPPR_LOCALIZATION_WINDOW_SECONDS(default0.08)MINIMAPPR_CLASSIFICATION_WINDOW_SECONDS(default0, inheritsMINIMAPPR_LOCALIZATION_WINDOW_SECONDS)MINIMAPPR_DEFAULT_TEMPERATURE_C(default20.0)MINIMAPPR_DEFAULT_HUMIDITY(default0.5)MINIMAPPR_ENVIRONMENT_READING_MAX_AGE_SECONDS(default300.0,0disables staleness cutoff)MINIMAPPR_SITE_ORIGIN_SOURCE(autodefault; uses the midpoint of active nodes with GPSposition_geowhen available, otherwise falls back to configured site-origin coordinates)MINIMAPPR_SITE_ORIGIN_RECONCILE_DELAY_SECONDS(default30.0; whenMINIMAPPR_SITE_ORIGIN_SOURCE=autostarts on configured fallback coordinates, wait this long and retry once before any ingest has been accepted so nodes can finish reporting after a cold start)MINIMAPPR_SITE_ORIGIN_LAT(default37.7749)MINIMAPPR_SITE_ORIGIN_LON(default-122.4194)MINIMAPPR_SITE_ORIGIN_ALT_M(default0.0)MINIMAPPR_COORDINATE_MODE(flatorgeodetic; defaultflat)MINIMAPPR_RUNTIME_PROFILE(default,birdnet_omni_testing, orbirdnet_hybrid_production)MINIMAPPR_CLASSIFIER(heuristic,yamnet, orbirdnet)MINIMAPPR_BIRDNET_TRIGGER_MIN_CONFIDENCE(default0.40; minimum confidence required before BirdNET detections are emitted or BirdNET chain stages are triggered)MINIMAPPR_BIRDNET_GEO_MIN_CONFIDENCE(default0.03; BirdNET geo-model occurrence threshold used to build the site-specific species allow-list fromMINIMAPPR_SITE_ORIGIN_LAT/LON)MINIMAPPR_DETECTION_MIN_CONFIDENCE(default0.05; hides lower-confidence detections from detection APIs/UI and soundscape rendering)MINIMAPPR_SKIP_LOCALIZATION_FOR_CLASSIFICATION(falsedefault)MINIMAPPR_LOCALIZATION_BAND_MIN_HZ/MINIMAPPR_LOCALIZATION_BAND_MAX_HZ(optional localization-only bandpass)MINIMAPPR_REPORTING_WINDOW_SECONDS(canonical detection dedupe window; default30)MINIMAPPR_TRACKING_FILTER(lineardefault, orkalman)MINIMAPPR_KALMAN_PROCESS_NOISE(default2.0)MINIMAPPR_KALMAN_MEASUREMENT_NOISE(default1.5)MINIMAPPR_KALMAN_INITIAL_POSITION_VARIANCE(default4.0)MINIMAPPR_KALMAN_INITIAL_VELOCITY_VARIANCE(default16.0)MINIMAPPR_FUSION_WORKER_COUNT(default1)MINIMAPPR_FUSION_EVENT_QUEUE_SIZE(default256)MINIMAPPR_NODE_DEGRADED_AFTER_SECONDS(default15.0)MINIMAPPR_NODE_OFFLINE_AFTER_SECONDS(default45.0)MINIMAPPR_EVENT_STALE_SECONDS(default30.0)MINIMAPPR_RETENTION_TRACK_UPDATES_SECONDS(default604800, set-1to disable cleanup)MINIMAPPR_RETENTION_ALERTS_SECONDS(default2592000, set-1to disable cleanup)MINIMAPPR_RETENTION_ENVIRONMENT_SECONDS(default604800, set-1to disable cleanup)MINIMAPPR_RETENTION_DROPPED_TRACKS_SECONDS(default604800, set-1to disable cleanup)MINIMAPPR_FEDERATION_ENABLED(falsedefault)MINIMAPPR_FEDERATION_SERVER_ID(srv-localdefault)MINIMAPPR_FEDERATION_PEERS_CONFIG_PATH(data/federation_peers.jsondefault)MINIMAPPR_FEDERATION_PEERS_JSON(optional inline JSON peer config override)MINIMAPPR_FEDERATION_AUTH_TOKEN(optional shared token/fallback peer auth token)MINIMAPPR_FEDERATION_PUBLISH_INTERVAL_SECONDS(default1.0)MINIMAPPR_FEDERATION_HEARTBEAT_INTERVAL_SECONDS(default2.0)MINIMAPPR_FEDERATION_LINK_TIMEOUT_SECONDS(default8.0)MINIMAPPR_FEDERATION_REQUEST_TIMEOUT_SECONDS(default2.5)MINIMAPPR_FEDERATION_TRACK_TTL_SECONDS(default20.0)MINIMAPPR_FEDERATION_DECONFLICT_MAHALANOBIS_GATE(default4.5)MINIMAPPR_FEDERATION_TQI_HYSTERESIS(default0.05)
Testing
source .venv/bin/activate
pytest -q
5-minute soak harness:
source .venv/bin/activate
python scripts/run_soak.py --duration 300
Frontend Development (Leptos/WASM)
The operator UI lives in minimappr-frontend/ (Rust → WASM via Leptos 0.8 + Trunk).
End users who pip install minimappr get the pre-built WASM bundle — no Rust toolchain needed.
Contributors editing the UI need cargo, trunk, and the wasm32-unknown-unknown target.
One-time contributor setup
cargo install trunk
rustup target add wasm32-unknown-unknown
Dev loop (live-reload)
# Terminal 1: FastAPI backend
.venv/bin/python -m minimappr
# Terminal 2: Trunk dev server (proxies /api and /ws to :8000)
cd minimappr-frontend && trunk serve
# Open http://localhost:8080 in a browser
Release build (required before python -m build)
scripts/build_frontend.sh
# Outputs: minimappr/frontend/{index.html,*.js,*.wasm,*.css}
To build all Rust deliverables, including the ingest sidecar:
scripts/build_rust.sh --all
# Outputs: minimappr/frontend/{index.html,*.js,*.wasm,*.css}
# Outputs: dist/minimappr-ingest-sidecar
Pre-publish check
ls minimappr/frontend/*.wasm # must exist before packaging
python -m build
unzip -l dist/minimappr-*.whl | grep frontend # confirm wasm is included
Roadmap Foundation Included
This MVP lays groundwork for the broader goals in your notes/proposals:
- additional sensor modalities
- richer model chaining (speech/STT/Home Assistant integration)
- federated fusion server topology
- richer COP layers (GDOP overlays, zones, alerting policies)
- advanced tracking and multi-hypothesis association
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file minimappr-0.2.0.tar.gz.
File metadata
- Download URL: minimappr-0.2.0.tar.gz
- Upload date:
- Size: 908.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0b218e5440f33f4443d38fd70c4a8084ad29d38ef6068829610a7e4c07ac84f
|
|
| MD5 |
d30fc5ea2ba1fc6279f910a3d322b2f4
|
|
| BLAKE2b-256 |
cc9a55076e921c945266c114297ec2b381e0b17aea81ab2f976bd4fb36b402d6
|
File details
Details for the file minimappr-0.2.0-py3-none-any.whl.
File metadata
- Download URL: minimappr-0.2.0-py3-none-any.whl
- Upload date:
- Size: 826.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6519967861d94a7f39ec98c94a6626b63f5d0fdd06c7218271c7fc508f1bcc09
|
|
| MD5 |
667ff48328395196119da34ad7731c75
|
|
| BLAKE2b-256 |
d054d25f75a01ef1607f3dfff308d1053cc9f1ea80b18f3dff74db5c9c8b83e7
|