Files
heardlog/README.md
Joakim Svensson af08aa0d92 first commit
2026-04-26 17:22:40 +02:00

188 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# heardlog
Samlar APRS-data från två källor och lagrar i TimescaleDB + PostGIS som grund för en täckningskarta.
```
[Shack-LAN] [DMZ]
Direwolf AGW:8000
agw_forwarder.py ──POST/Bearer──► FastAPI :8085 ──► TimescaleDB + PostGIS
rotate.aprs2.net┘ (APRS-IS, outbound från DMZ)
```
| Källa | Tabell | Nyckeldata |
|---|---|---|
| Direwolf AGW (RF) | `rf_frames` | `heard_direct` hördes utan mellanliggande digi |
| APRS-IS regionalt | `is_frames` | Stationer inom 200 km som digipeatas till internet |
`heard_direct = TRUE` är grundstenen för täckningskartan ett bevis på att en station nådde vår mottagare direkt.
---
## Krav
- Docker + Docker Compose (DMZ-server)
- Direwolf med `AGWPORT 8000` i `direwolf.conf` (shack-dator)
- Python 3.10+ och `pip3 install requests` (shack-dator)
---
## DMZ snabbstart
```bash
git clone <repo> heardlog
cd heardlog
cp .env.example .env
# Generera API-nyckel
python3 -c "import secrets; print(secrets.token_hex(32))"
# Klistra in som API_KEY i .env
docker compose up -d
docker compose logs -f collector
```
Förväntad output:
```
INFO db Database schema ready
INFO main Startup complete listening for RF frames
INFO aprs_is APRS-IS login: # logresp SA6ANW-1 unverified, server T2...
```
`unverified` på APRS-IS är förväntat passcode `-1` ger receive-only vilket är tillräckligt.
---
## Shack agw-forwarder
```bash
pip3 install requests
# Skapa konfigurationsfil
cat > heardlog/agw-forwarder/.env << 'EOF'
AGW_HOST=localhost
AGW_PORT=8000
COLLECTOR_URL=http://<dmz-ip>:8085
API_KEY=<samma nyckel som i DMZ .env>
STATION_CALL=SA6ANW-1
LOG_LEVEL=INFO
EOF
# Testa manuellt
cd heardlog/agw-forwarder
python3 agw_forwarder.py
```
### Kör som systemd-tjänster
```bash
# Kopiera service-filer
sudo cp heardlog/agw-forwarder/direwolf.service /etc/systemd/system/
sudo cp heardlog/agw-forwarder/agw-forwarder.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now direwolf
sudo systemctl enable --now agw-forwarder
# Verifiera
journalctl -u direwolf -u agw-forwarder -f
```
`agw-forwarder` har `Wants=direwolf.service` systemd startar Direwolf automatiskt om den inte kör.
---
## Konfiguration
### DMZ (`.env`)
| Variabel | Standard | Beskrivning |
|---|---|---|
| `STATION_CALL` | `SA6ANW-1` | Din anropssignal |
| `DB_PASSWORD` | | Postgres-lösenord |
| `API_KEY` | | Delad hemlighet med forwardern |
| `APRS_IS_PASSCODE` | `-1` | `-1` för receive-only |
| `APRS_IS_FILTER` | `r/58.35/14.05/200` | lat/lon/km runt din QTH |
| `LOG_LEVEL` | `INFO` | `DEBUG` för varje frame |
### Shack (`agw-forwarder/.env`)
| Variabel | Standard | Beskrivning |
|---|---|---|
| `AGW_HOST` | `localhost` | Direwolf-host |
| `AGW_PORT` | `8000` | Direwolf AGW-port |
| `COLLECTOR_URL` | | URL till DMZ-API:t |
| `API_KEY` | | Samma som i DMZ `.env` |
| `STATION_CALL` | `SA6ANW-1` | Din anropssignal |
---
## Implementationsnoteringar
### AGW monitoring-format
Direwolf skickar `U`-frames i monitoring-mode med ett ledande mellanslag och full monitoring-header före `\r`:
```
1:Fm SM6MJW-9 To UXRT8L Via WIDE1-1,WIDE2-1 <UI pid=F0 Len=17 F=0 >[16:46:13]\r<info>
```
Via-pathen extraheras med regex mellan `Via ` och ` <UI`. `heard_direct` avgörs av om någon hop i pathen har `*` (har-repeaterats-flaggan).
### Null-bytes
APRS-frames kan innehålla `0x00`-bytes i info-fältet. Dessa stoppas ut innan DB-insert eftersom Postgres UTF8 inte accepterar null-bytes.
---
## Nyttiga queries
```sql
-- Senaste RF-frames
SELECT ts, src_call, lat, lon, heard_direct, path
FROM rf_frames
ORDER BY ts DESC
LIMIT 20;
-- Direkt-hörda stationer senaste 7 dagarna
SELECT ts, src_call, lat, lon, path
FROM rf_frames
WHERE heard_direct = TRUE
AND ts > NOW() - INTERVAL '7 days'
ORDER BY ts DESC;
-- Digis/stationer hörda senaste 30 min
SELECT src_call, MAX(ts) AS last_seen, COUNT(*) AS frames
FROM rf_frames
WHERE ts > NOW() - INTERVAL '30 minutes'
GROUP BY src_call
ORDER BY last_seen DESC;
-- Täckningspunkter per rutnätscell (0.01°)
SELECT
ROUND(lat::numeric, 2) AS grid_lat,
ROUND(lon::numeric, 2) AS grid_lon,
COUNT(*) AS hits
FROM rf_frames
WHERE heard_direct = TRUE
AND lat IS NOT NULL
GROUP BY grid_lat, grid_lon;
-- APRS-IS senaste timmen
SELECT ts, src_call, lat, lon
FROM is_frames
WHERE ts > NOW() - INTERVAL '1 hour'
ORDER BY ts DESC;
```
---
## API
| Endpoint | Metod | Auth | Beskrivning |
|---|---|---|---|
| `/ingest/rf` | POST | Bearer | RF-frame från forwarder |
| `/health` | GET | | Liveness check |
Swagger UI: `http://<dmz-ip>:8085/docs`