Joakim Svensson 42ba6feed4 first commit
2026-04-26 17:20:58 +02:00
2026-04-26 17:20:58 +02:00
2026-04-26 17:20:58 +02:00
2026-04-26 17:20:58 +02:00
2026-04-26 17:20:58 +02:00
2026-04-26 17:20:58 +02:00
2026-04-26 17:20:58 +02:00

aprs-collector

[Shack-LAN]                        [DMZ]
Direwolf AGW:8000
      ???
agw_forwarder.py  ??????POST/Bearer????????? FastAPI :8080/ingest/rf ????????? TimescaleDB
                                          ???
                          rotate.aprs2.net???  (APRS-IS, outbound)

DMZ ??? snabbstart

cp .env.example .env

# Generera en API-nyckel
python3 -c "import secrets; print(secrets.token_hex(32))"
# Klistra in i .env som API_KEY

docker compose up -d
docker compose logs -f collector

S??tt en reverse proxy (Caddy/nginx) framf??r port 8080 om du vill ha TLS.


Shack ??? agw-forwarder

Kopiera mappen agw-forwarder/ till Direwolf-datorn.

pip3 install requests

export AGW_HOST=localhost
export AGW_PORT=8000
export COLLECTOR_URL=http://<dmz-ip>:8080
export API_KEY=<samma nyckel som i DMZ .env>
export STATION_CALL=SA6ANW-1

python3 agw_forwarder.py

K??r som systemd-tj??nst

# Redigera agw-forwarder.service ??? fyll i COLLECTOR_URL och API_KEY
sudo cp agw-forwarder.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now agw-forwarder
journalctl -u agw-forwarder -f

Resiliens

Forwardern har en intern k?? (2000 frames). Om DMZ ??r tillf??lligt on??bar buffras frames i minnet och skickas n??r anslutningen ??terkommer. Vid omstart av forwardern f??rsvinner buffrade frames ??? tillr??ckligt f??r de flesta avbrott.


API

Endpoint Metod Auth Beskrivning
/ingest/rf POST Bearer RF-frame fr??n forwarder
/health GET ??? Liveness check

Swagger UI: http://<dmz-ip>:8080/docs


Nyttiga queries

-- 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 som h??rts senaste 30 min (= "online")
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
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;
Description
No description provided
Readme 84 KiB
Languages
Python 54.9%
HTML 44.8%
Dockerfile 0.3%