Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.1.1] - 2026-09-12
### Fixed
- **Demo Simulator Endpoint:** Fixed hardcoded epicenter in `/demo/trigger-earthquake` to use dynamic centroid coordinates based on requested zone.
- **Demo Calibration Math:** Corrected reverse-engineering calibration constant from `160` to `1.6` to align with the worker node logic.
- **Demo Plausibility:** Reduced default payload magnitude from `7.5` to `5.0`.
- **Whitepaper Documentation:** Expanded Appendix A to fully document the mocked execution cascade of the demo endpoint.
- **Hollywood Simulator:** Added Pisa to the interactive console and database seeding scripts (`seed_world_zones.py`, `hollywood.py`).

## [2.1.0] - 2026-09-10
### Added
- **Grafana Mission Control:** Automated dashboard provisioning via JSON configuration, providing real-time seismographs, IoT telemetry (RSSI, free heap), and backend latency metrics instantly on `docker compose up`.
Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ authors:
given-names: "Riccardo"
alias: "riccardo0731"
title: "QuakeGuard"
version: 2.1.0
version: 2.1.1
identifiers:
- type: doi
value: "10.5281/zenodo.21710405"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ QuakeGuard/
| **v2.0.0** | ✅ Released — Triangulation (multi-node spatial correlation), Hybrid Network Architecture, Automated DevOps Orchestration (Ptyxis), Local Factory Provisioning, GNSS sync, NTP+PPS, ADXL calibration, and INGV FDSN SIL validation |
| **v2.0.1** | ✅ Released — Documentation & Zenodo Sync: PDF/Wiki architectural coherence (Cloudflare, 300s anti-replay), CERN-OHL hardware licensing, SIL vs Firmware threshold clarification, and `CITATION.cff` bump |
| **v2.1.0** | ✅ Released — System Telemetry & Grafana Observability, Captive Portal, Dynamic Demo Onboarding (QR), OTA In-App Updater, Local Mosquitto architecture, Repository Health: Dependabot, ADRs, C4/sequence diagrams, multi-stage Dockerfile, firmware versioning, pinout, flashing guide, hardware compliance |
| **v2.1.1** | Timeseries DB & Mobile Fix — migration to TimescaleDB/InfluxDB; per-sensor chart isolation in React Native mobile |
| **v2.1.1** | ✅ Released — Demo Calibration & Repository Fixes |
| **v2.2** | Heterogeneous Edge Intelligence — hybrid Tier A (STA/LTA) + Tier B (quantized CNN) decision fusion |
| **Future** | Cloud IaC — Kubernetes + Terraform auto-scaling platform (see [ROADMAP.md](ROADMAP.md)) |

Expand Down
7 changes: 7 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ Documentation-only patch aligning the technical whitepaper, GitHub Wiki, and pro

---

## v2.1.1 — Demo Calibration & Repository Fixes (Released)

- ✅ **Demo Plausibility:** Modified the `/demo/trigger-earthquake` endpoint (magnitude 5.0, PostGIS dynamic centroid, 1.6 calibration factor).
- ✅ **Hollywood Simulator:** Added Pisa to local orchestration.
- ✅ **Versioning Fix:** Cleaned up ghost roadmap entries.


## v2.1.0 — System Telemetry & Repository Health (Released)

Comprehensive repository maturity improvements: CI hardening, architectural documentation, developer experience, and compliance foundations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT a.magnitude as \"Peak Magnitude\"\nFROM alerts a\nJOIN zones z ON a.zone_id = z.id\nWHERE z.city IN ($zone)\nORDER BY a.created_at DESC\nLIMIT 1",
"rawSql": "SELECT MAX(log(GREATEST(r.value, 1) / 160.0) + 3.0) as \"Peak Magnitude\"\nFROM readings r\nJOIN sensors s ON r.sensor_id = s.id\nJOIN zones z ON s.zone_id = z.id\nWHERE z.city IN ($zone) AND $__timeFilter(r.recorded_at)",
"refId": "A",
"select": [
[
Expand All @@ -262,7 +262,7 @@
}
]
],
"timeColumn": "created_at",
"timeColumn": "recorded_at",
"where": [
{
"name": "$__timeFilter",
Expand Down Expand Up @@ -332,7 +332,7 @@
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n a.created_at AS \"Timestamp\",\n z.city AS \"Zone Name\",\n a.magnitude AS \"Magnitude\"\nFROM alerts a\nLEFT JOIN zones z ON a.zone_id = z.id\nWHERE z.city IN ($zone)\nORDER BY a.created_at DESC\nLIMIT 10",
"rawSql": "WITH actual_alerts AS (\n SELECT\n a.created_at AS \"Timestamp\",\n z.city AS \"Zone Name\",\n a.magnitude::text AS \"Magnitude\"\n FROM alerts a\n LEFT JOIN zones z ON a.zone_id = z.id\n WHERE z.city IN ($zone)\n ORDER BY a.created_at DESC\n LIMIT 10\n)\nSELECT * FROM actual_alerts\nUNION ALL\nSELECT\n NOW() AS \"Timestamp\",\n 'No alerts yet...' AS \"Zone Name\",\n '-' AS \"Magnitude\"\nWHERE NOT EXISTS (SELECT 1 FROM actual_alerts);",
"refId": "A",
"select": [
[
Expand Down Expand Up @@ -953,13 +953,13 @@
"type": "postgres",
"uid": "timescaledb"
},
"definition": "SELECT DISTINCT z.city FROM zones z JOIN sensors s ON s.zone_id = z.id WHERE s.active = true ORDER BY z.city ASC",
"definition": "SELECT city FROM zones ORDER BY city ASC",
"hide": 0,
"includeAll": true,
"multi": true,
"name": "zone",
"options": [],
"query": "SELECT DISTINCT z.city FROM zones z JOIN sensors s ON s.zone_id = z.id WHERE s.active = true ORDER BY z.city ASC",
"query": "SELECT city FROM zones ORDER BY city ASC",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
Expand Down
34 changes: 22 additions & 12 deletions backend/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ async def lifespan(app: FastAPI):
listener_task.cancel()

# Initialize FastAPI
app = FastAPI(title="QuakeGuard Backend", version="2.1.0", lifespan=lifespan)
app = FastAPI(title="QuakeGuard Backend", version="2.1.1", lifespan=lifespan)

# ==========================================
# MIDDLEWARE
Expand Down Expand Up @@ -304,26 +304,36 @@ async def trigger_demo_earthquake(
Instantly triggers a simulated TRIANGULATED earthquake alert.
Bypasses the IoT ingestion pipeline and broadcasts directly to all connected WebSocket clients.
"""
db = next(get_db())

# We resolve the actual zone name if we can for realism
zone = db.query(models.Zone).filter(models.Zone.id == payload.zone_id).first()

lat = 45.4642
lon = 9.1900
if zone and zone.geom is not None:
lon_scalar = db.scalar(func.ST_X(func.ST_Centroid(zone.geom)))
lat_scalar = db.scalar(func.ST_Y(func.ST_Centroid(zone.geom)))
if lon_scalar is not None and lat_scalar is not None:
lon = float(lon_scalar)
lat = float(lat_scalar)

# Construct the exact payload format expected by the frontend WebSocketContext
alert_data = {
"type": "TRIANGULATED",
"zone_id": payload.zone_id,
"magnitude": payload.magnitude,
"message": payload.message,
"latitude": 45.4642, # Mock epicenter (Milan, or any city)
"longitude": 9.1900,
"latitude": lat,
"longitude": lon,
"origin_time": datetime.now(timezone.utc).isoformat(),
"timestamp": datetime.now(timezone.utc).isoformat()
}

# Publish directly to the Redis Pub/Sub channel for live updates
await redis_client.publish("quake_alerts", json.dumps(alert_data))

db = next(get_db())

# We resolve the actual zone name if we can for realism
zone = db.query(models.Zone).filter(models.Zone.id == payload.zone_id).first()


# 1. Create an Alert record so we can link the EmergencyReport
demo_alert = models.Alert(
zone_id=payload.zone_id,
Expand Down Expand Up @@ -380,9 +390,9 @@ async def trigger_demo_earthquake(

for i, mag in enumerate(demo_magnitudes):
# Reverse engineer the raw sensor value:
# magnitude = log10(sensorValue / 160) + 3.0
# 10^(magnitude - 3.0) = sensorValue / 160
raw_value = int(160 * (10 ** (mag - 3.0)))
# magnitude = log10(sensorValue / 1.6) + 3.0
# 10^(magnitude - 3.0) = sensorValue / 1.6
raw_value = int(1.6 * (10 ** (mag - 3.0)))

# Space them out by 1 second leading up to now
record_time = now_dt - timedelta(seconds=(len(demo_magnitudes) - i - 1))
Expand Down Expand Up @@ -588,7 +598,7 @@ def get_zone_readings(zone_id: int, limit: int = 60, db: Session = Depends(get_d
zone = db.query(models.Zone).filter(models.Zone.id == zone_id).first()
if not zone:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=ZONE_NOT_FOUND)
limit = max(1, min(limit, 200))
limit = max(1, min(limit, 1000))
return (
db.query(models.Reading)
.join(models.Sensor, models.Reading.sensor_id == models.Sensor.id)
Expand Down
2 changes: 1 addition & 1 deletion backend/src/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,5 @@ class EmergencyReport(BaseModel):
class DemoAlertRequest(BaseModel):
"""Payload for manually triggering a simulated earthquake alert."""
zone_id: int = Field(default=1, description="Target Zone ID for the alert")
magnitude: float = Field(default=7.5, le=10.0, description="Simulated earthquake magnitude")
magnitude: float = Field(default=5.0, le=10.0, description="Simulated earthquake magnitude")
message: str = Field(default="Simulated Critical Event", description="Custom alert message")
2 changes: 1 addition & 1 deletion backend/tests/unit/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class TestDemoAlertRequest:
def test_default_values(self):
d = DemoAlertRequest()
assert d.zone_id == 1
assert d.magnitude == 7.5
assert d.magnitude == 5.0
assert d.message == "Simulated Critical Event"


Expand Down
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ docs/

| Directory | Covers |
|-----------|--------|
| [`whitepaper/`](whitepaper/README.md) | Typst sources for the technical report, compiled into `QuakeGuard_Technical_Report_v2.1.0.pdf` |
| [`whitepaper/`](whitepaper/README.md) | Typst sources for the technical report, compiled into `QuakeGuard_Technical_Report_v2.1.1.pdf` |
| [`web/`](web/) | QuakeGuard page artifacts (html/css/js/demo + assets) synced to the `GiZano.github.io` Pages site via CI |

## Compiling the whitepaper PDF
Expand All @@ -25,7 +25,7 @@ Requires [Typst](https://typst.app/) (`typst` on PATH) and the Liberation Serif

```bash
cd docs
typst compile whitepaper/main.typ whitepaper/QuakeGuard_Technical_Report_v2.1.0.pdf
typst compile whitepaper/main.typ whitepaper/QuakeGuard_Technical_Report_v2.1.1.pdf
```

The output PDF is a local build artifact and is intentionally **not** tracked in git.
Expand Down
2 changes: 1 addition & 1 deletion docs/web/assets/badges/release.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/web/assets/quakeguard/quakeguard-whitepaper.pdf
Binary file not shown.
5 changes: 4 additions & 1 deletion docs/web/quakeguard.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"alternateName": "QuakeGuard \u2014 Distributed Earthquake Early Warning System",
"applicationCategory": "IoTApplication",
"operatingSystem": "Web, ESP32-C3, React Native (Android/iOS)",
"softwareVersion": "2.1.0",
"softwareVersion": "2.1.1",
"url": "https://giovanni-zanotti.is-a.dev/projects/quakeguard.html",
"description": "Full-stack IoT architecture for real-time earthquake detection and early warning. ESP32-C3 + ADXL345 edge sensors with on-device STA/LTA and optional GNSS, ECDSA-signed telemetry over MQTT or USB CDC serial fallback (Zero-Trust), FastAPI + Redis Streams + TimescaleDB/PostGIS backend with per-area geo-zoning, and a React Native app with on-premise AI emergency reports (Ollama).",
"codeRepository": "https://github.com/GiZano/QuakeGuard",
Expand Down Expand Up @@ -781,6 +781,9 @@ <h3 class="text-center mb-4" style="color:#fff;" data-i18n="roadmap_dev">Develop
Released</span>
<p data-i18n="road_21"><strong>Data Dashboards & Observability</strong> — Grafana live visualization of system telemetry, End-to-End Latency tracking, and true Local Mosquitto resilience.</p>
</div>
<div class="road-item done"><span class="ver" data-i18n="road_211_label">v2.1.1 · Released</span>
<p data-i18n="road_211"><strong>Demo Calibration & Repository Fixes</strong> — dynamic GIS epicenter calculation, precise ADXL345 mock calibration, and Pisa Hollywood simulator.</p>
</div>
</div>
</div>
<div class="col-md-6">
Expand Down
2 changes: 2 additions & 0 deletions docs/web/quakeguard.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions docs/whitepaper/12-roadmap.typ
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
[v1.2.x], [On-Premise AI, Geo-Zoning, Serial Fallback],
[v1.3.0], [Synchronized GNSS (NTP + PPS Time Discipline)],
[v2.0.0], [Epicenter Triangulation & Hardware Assembly],
[v2.1.0], [Grafana Observability, System Telemetry, Captive Portal Onboarding, Dynamic Demo Onboarding (QR), OTA In-App Updates, Hollywood Simulator, and Repository Health (ADRs, CI Hardening, Dependabot)]
[v2.1.0], [Grafana Observability, System Telemetry, Captive Portal Onboarding, Dynamic Demo Onboarding (QR), OTA In-App Updates, Hollywood Simulator, and Repository Health (ADRs, CI Hardening, Dependabot)],
[v2.1.1], [Demo Calibration & Repository Fixes],
)

== Next Steps
- *v2.1.1 - Timeseries Migration:* Migration to TimescaleDB and InfluxDB for optimized sensor isolation.
- *v2.2.0 - Edge AI (Two-Tier Cluster):* A hierarchical Decision Fusion network where ubiquitous ESP32-C3 sensors (Tier A) act as triggers, and intelligent ESP32-S3 nodes (Tier B) run quantized INT8 CNNs via ESP-DL to confirm or discard triggers.

== Detailed v1.2.x Changelog
Expand Down
8 changes: 7 additions & 1 deletion docs/whitepaper/16-appendix.typ
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ The QuakeGuard Control Plane exposes a REST API via FastAPI. The full OpenAPI 3.
- `GET /reports/{alert_id}`: Fetches the AI-generated emergency report for a confirmed alert.

*Demo & Simulation*
- `POST /demo/trigger-earthquake`: Simulates a critical seismic event for demo/testing (payload `zone_id`, `magnitude`, `message`; defaults to `zone_id:1`, `magnitude:7.5`). Bypasses the IoT ingestion pipeline and publishes directly to the `quake_alerts` Redis channel, triggering the mobile alert within milliseconds.
- `POST /demo/trigger-earthquake`: Simulates a critical seismic event for testing and demonstrations. Requires API key authentication (`verify_api_key`). Bypasses the standard IoT ingestion pipeline to execute a complete mock cascade:
1. Resolves the geographic centroid for the requested `zone_id` (defaults to Milan coordinates if geometry is missing).
2. Publishes the event directly to the `quake_alerts` Redis channel for instant WebSocket broadcast.
3. Creates an `Alert` record (`is_triangulated=True`) and an `EmergencyReport` in `PENDING` state.
4. Triggers the AI Worker for text report generation by pushing to the `ai_report_queue`.
5. Injects 5 synthetic historical sensor readings into the database to populate telemetry graphs, applying reverse-calibration (K_CALIBRATION=1.6) to derive realistic raw hardware values.
(Payload: `zone_id`, `magnitude`, `message`; defaults to `zone_id: 1`, `magnitude: 5.0`).

*Telemetry & Analytics*
- `GET /sensors/{sensor_id}/statistics`: Returns time-series aggregates (count, max magnitude) for a specific sensor, leveraging TimescaleDB continuous aggregates.
Expand Down
4 changes: 2 additions & 2 deletions docs/whitepaper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ Note: The `assets/` directory contains logos and color palettes used by the repo
Requires [Typst](https://typst.app/) (`typst` on PATH) and the Liberation Serif font family.

```bash
cd docs
typst compile main.typ QuakeGuard_Technical_Report_v2.1.0.pdf
cd docs/whitepaper
typst compile main.typ QuakeGuard_Technical_Report_v2.1.1.pdf
```

The output PDF is a local build artifact and is intentionally **not** tracked in git.
Expand Down
Loading
Loading