From d202205659975bd63e2cb76cdc971f65704dc73f Mon Sep 17 00:00:00 2001 From: MrAlders0n <55921894+MrAlders0n@users.noreply.github.com> Date: Thu, 10 Sep 2026 07:17:29 -0400 Subject: [PATCH] fix(observers): rename telemetry airtime fields to seconds (#136) --- db/migrations/033_telemetry_airtime_secs.sql | 4 +++ db/observers.go | 12 +++---- db/queries/queries.sql | 10 +++--- db/sqlc/models.go | 4 +-- db/sqlc/queries.sql.go | 34 ++++++++++---------- docs/docs.go | 4 +-- docs/swagger.json | 4 +-- docs/swagger.yaml | 4 +-- internal/api/observers.go | 5 +-- 9 files changed, 43 insertions(+), 38 deletions(-) create mode 100644 db/migrations/033_telemetry_airtime_secs.sql diff --git a/db/migrations/033_telemetry_airtime_secs.sql b/db/migrations/033_telemetry_airtime_secs.sql new file mode 100644 index 0000000..3ca4d36 --- /dev/null +++ b/db/migrations/033_telemetry_airtime_secs.sql @@ -0,0 +1,4 @@ +-- These always held seconds of radio time from the observer's tx_air_secs/rx_air_secs +-- status fields, never a percentage; name them for what they are. +ALTER TABLE observer_telemetry RENAME COLUMN airtime_tx_pct TO airtime_tx_secs; +ALTER TABLE observer_telemetry RENAME COLUMN airtime_rx_pct TO airtime_rx_secs; diff --git a/db/observers.go b/db/observers.go index 1a71329..43948c1 100644 --- a/db/observers.go +++ b/db/observers.go @@ -158,8 +158,8 @@ func (s *Store) InsertObserverTelemetry(ctx context.Context, observerID uuid.UUI ObserverID: observerID, ReportedAt: pgtype.Timestamptz{Time: reportedAt, Valid: true}, BatteryVoltageMv: batteryMV, - AirtimeTxPct: txAirSecs, - AirtimeRxPct: rxAirSecs, + AirtimeTxSecs: txAirSecs, + AirtimeRxSecs: rxAirSecs, NoiseFloorDb: &noiseFloor, UptimeSeconds: &uptimeSeconds, QueueLength: queueLen, @@ -183,8 +183,8 @@ func (s *Store) GetObserverTelemetry(ctx context.Context, observerID uuid.UUID, points = append(points, api.ObserverTelemetryPoint{ T: v.ReportedAt.Time.UnixMilli(), BatteryMV: v.BatteryVoltageMv, - AirtimeTxPct: v.AirtimeTxPct, - AirtimeRxPct: v.AirtimeRxPct, + AirtimeTxSecs: v.AirtimeTxSecs, + AirtimeRxSecs: v.AirtimeRxSecs, NoiseFloorDB: v.NoiseFloorDb, UptimeSeconds: v.UptimeSeconds, QueueLength: v.QueueLength, @@ -216,8 +216,8 @@ func (s *Store) GetObserverTelemetryBucketed(ctx context.Context, observerID uui points = append(points, api.ObserverTelemetryPoint{ T: r.Bucket.Time.UnixMilli(), BatteryMV: &r.BatteryVoltageMv, - AirtimeTxPct: &r.AirtimeTxPct, - AirtimeRxPct: &r.AirtimeRxPct, + AirtimeTxSecs: &r.AirtimeTxSecs, + AirtimeRxSecs: &r.AirtimeRxSecs, NoiseFloorDB: &r.NoiseFloorDb, UptimeSeconds: &r.UptimeSeconds, QueueLength: &r.QueueLength, diff --git a/db/queries/queries.sql b/db/queries/queries.sql index 7c9a402..2e72472 100644 --- a/db/queries/queries.sql +++ b/db/queries/queries.sql @@ -226,14 +226,14 @@ WHERE id = $1; -- Inserts a telemetry snapshot for an observer. The reported_at timestamp should -- be truncated to the configured resolution before calling to ensure deduplication. INSERT INTO observer_telemetry ( - observer_id, reported_at, battery_voltage_mv, airtime_tx_pct, - airtime_rx_pct, noise_floor_db, uptime_seconds, queue_length, + observer_id, reported_at, battery_voltage_mv, airtime_tx_secs, + airtime_rx_secs, noise_floor_db, uptime_seconds, queue_length, debug_flags, receive_errors ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ON CONFLICT (observer_id, reported_at) DO NOTHING; -- name: GetObserverTelemetry :many -SELECT id, reported_at, battery_voltage_mv, airtime_tx_pct, airtime_rx_pct, +SELECT id, reported_at, battery_voltage_mv, airtime_tx_secs, airtime_rx_secs, noise_floor_db, uptime_seconds, queue_length, debug_flags, receive_errors FROM observer_telemetry WHERE observer_id = $1 @@ -247,8 +247,8 @@ SELECT (date_trunc('day', reported_at) + (EXTRACT(HOUR FROM reported_at)::int / $4::int) * ($4::int * interval '1 hour'))::timestamptz AS bucket, AVG(battery_voltage_mv)::int AS battery_voltage_mv, - GREATEST(MAX(airtime_tx_pct) - MIN(airtime_tx_pct), 0)::real AS airtime_tx_pct, - GREATEST(MAX(airtime_rx_pct) - MIN(airtime_rx_pct), 0)::real AS airtime_rx_pct, + GREATEST(MAX(airtime_tx_secs) - MIN(airtime_tx_secs), 0)::real AS airtime_tx_secs, + GREATEST(MAX(airtime_rx_secs) - MIN(airtime_rx_secs), 0)::real AS airtime_rx_secs, AVG(noise_floor_db)::real AS noise_floor_db, MAX(uptime_seconds)::bigint AS uptime_seconds, AVG(queue_length)::int AS queue_length, diff --git a/db/sqlc/models.go b/db/sqlc/models.go index 9f6ef21..a2a5432 100644 --- a/db/sqlc/models.go +++ b/db/sqlc/models.go @@ -258,8 +258,8 @@ type ObserverTelemetry struct { ObserverID uuid.UUID `json:"observer_id"` ReportedAt pgtype.Timestamptz `json:"reported_at"` BatteryVoltageMv *int32 `json:"battery_voltage_mv"` - AirtimeTxPct *float32 `json:"airtime_tx_pct"` - AirtimeRxPct *float32 `json:"airtime_rx_pct"` + AirtimeTxSecs *float32 `json:"airtime_tx_secs"` + AirtimeRxSecs *float32 `json:"airtime_rx_secs"` NoiseFloorDb *float32 `json:"noise_floor_db"` UptimeSeconds *int64 `json:"uptime_seconds"` QueueLength *int32 `json:"queue_length"` diff --git a/db/sqlc/queries.sql.go b/db/sqlc/queries.sql.go index 45641d4..998889f 100644 --- a/db/sqlc/queries.sql.go +++ b/db/sqlc/queries.sql.go @@ -891,7 +891,7 @@ func (q *Queries) GetObserverScopes(ctx context.Context, observerID uuid.UUID) ( } const getObserverTelemetry = `-- name: GetObserverTelemetry :many -SELECT id, reported_at, battery_voltage_mv, airtime_tx_pct, airtime_rx_pct, +SELECT id, reported_at, battery_voltage_mv, airtime_tx_secs, airtime_rx_secs, noise_floor_db, uptime_seconds, queue_length, debug_flags, receive_errors FROM observer_telemetry WHERE observer_id = $1 @@ -912,8 +912,8 @@ type GetObserverTelemetryRow struct { ID int64 `json:"id"` ReportedAt pgtype.Timestamptz `json:"reported_at"` BatteryVoltageMv *int32 `json:"battery_voltage_mv"` - AirtimeTxPct *float32 `json:"airtime_tx_pct"` - AirtimeRxPct *float32 `json:"airtime_rx_pct"` + AirtimeTxSecs *float32 `json:"airtime_tx_secs"` + AirtimeRxSecs *float32 `json:"airtime_rx_secs"` NoiseFloorDb *float32 `json:"noise_floor_db"` UptimeSeconds *int64 `json:"uptime_seconds"` QueueLength *int32 `json:"queue_length"` @@ -939,8 +939,8 @@ func (q *Queries) GetObserverTelemetry(ctx context.Context, arg GetObserverTelem &i.ID, &i.ReportedAt, &i.BatteryVoltageMv, - &i.AirtimeTxPct, - &i.AirtimeRxPct, + &i.AirtimeTxSecs, + &i.AirtimeRxSecs, &i.NoiseFloorDb, &i.UptimeSeconds, &i.QueueLength, @@ -962,8 +962,8 @@ SELECT (date_trunc('day', reported_at) + (EXTRACT(HOUR FROM reported_at)::int / $4::int) * ($4::int * interval '1 hour'))::timestamptz AS bucket, AVG(battery_voltage_mv)::int AS battery_voltage_mv, - GREATEST(MAX(airtime_tx_pct) - MIN(airtime_tx_pct), 0)::real AS airtime_tx_pct, - GREATEST(MAX(airtime_rx_pct) - MIN(airtime_rx_pct), 0)::real AS airtime_rx_pct, + GREATEST(MAX(airtime_tx_secs) - MIN(airtime_tx_secs), 0)::real AS airtime_tx_secs, + GREATEST(MAX(airtime_rx_secs) - MIN(airtime_rx_secs), 0)::real AS airtime_rx_secs, AVG(noise_floor_db)::real AS noise_floor_db, MAX(uptime_seconds)::bigint AS uptime_seconds, AVG(queue_length)::int AS queue_length, @@ -986,8 +986,8 @@ type GetObserverTelemetryBucketedParams struct { type GetObserverTelemetryBucketedRow struct { Bucket pgtype.Timestamptz `json:"bucket"` BatteryVoltageMv int32 `json:"battery_voltage_mv"` - AirtimeTxPct float32 `json:"airtime_tx_pct"` - AirtimeRxPct float32 `json:"airtime_rx_pct"` + AirtimeTxSecs float32 `json:"airtime_tx_secs"` + AirtimeRxSecs float32 `json:"airtime_rx_secs"` NoiseFloorDb float32 `json:"noise_floor_db"` UptimeSeconds int64 `json:"uptime_seconds"` QueueLength int32 `json:"queue_length"` @@ -1011,8 +1011,8 @@ func (q *Queries) GetObserverTelemetryBucketed(ctx context.Context, arg GetObser if err := rows.Scan( &i.Bucket, &i.BatteryVoltageMv, - &i.AirtimeTxPct, - &i.AirtimeRxPct, + &i.AirtimeTxSecs, + &i.AirtimeRxSecs, &i.NoiseFloorDb, &i.UptimeSeconds, &i.QueueLength, @@ -2016,8 +2016,8 @@ func (q *Queries) InsertObservation(ctx context.Context, arg InsertObservationPa const insertObserverTelemetry = `-- name: InsertObserverTelemetry :exec INSERT INTO observer_telemetry ( - observer_id, reported_at, battery_voltage_mv, airtime_tx_pct, - airtime_rx_pct, noise_floor_db, uptime_seconds, queue_length, + observer_id, reported_at, battery_voltage_mv, airtime_tx_secs, + airtime_rx_secs, noise_floor_db, uptime_seconds, queue_length, debug_flags, receive_errors ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ON CONFLICT (observer_id, reported_at) DO NOTHING @@ -2027,8 +2027,8 @@ type InsertObserverTelemetryParams struct { ObserverID uuid.UUID `json:"observer_id"` ReportedAt pgtype.Timestamptz `json:"reported_at"` BatteryVoltageMv *int32 `json:"battery_voltage_mv"` - AirtimeTxPct *float32 `json:"airtime_tx_pct"` - AirtimeRxPct *float32 `json:"airtime_rx_pct"` + AirtimeTxSecs *float32 `json:"airtime_tx_secs"` + AirtimeRxSecs *float32 `json:"airtime_rx_secs"` NoiseFloorDb *float32 `json:"noise_floor_db"` UptimeSeconds *int64 `json:"uptime_seconds"` QueueLength *int32 `json:"queue_length"` @@ -2043,8 +2043,8 @@ func (q *Queries) InsertObserverTelemetry(ctx context.Context, arg InsertObserve arg.ObserverID, arg.ReportedAt, arg.BatteryVoltageMv, - arg.AirtimeTxPct, - arg.AirtimeRxPct, + arg.AirtimeTxSecs, + arg.AirtimeRxSecs, arg.NoiseFloorDb, arg.UptimeSeconds, arg.QueueLength, diff --git a/docs/docs.go b/docs/docs.go index 9d56007..b7cf2ac 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -3204,10 +3204,10 @@ const docTemplate = `{ "github_com_MeshCore-Beacon_beacon-server_internal_api.ObserverTelemetryPoint": { "type": "object", "properties": { - "airtimeRxPct": { + "airtimeRxSecs": { "type": "number" }, - "airtimeTxPct": { + "airtimeTxSecs": { "type": "number" }, "batteryMv": { diff --git a/docs/swagger.json b/docs/swagger.json index ede5784..5182f72 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -3202,10 +3202,10 @@ "github_com_MeshCore-Beacon_beacon-server_internal_api.ObserverTelemetryPoint": { "type": "object", "properties": { - "airtimeRxPct": { + "airtimeRxSecs": { "type": "number" }, - "airtimeTxPct": { + "airtimeTxSecs": { "type": "number" }, "batteryMv": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 7efd8ad..64962a7 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -576,9 +576,9 @@ definitions: type: object github_com_MeshCore-Beacon_beacon-server_internal_api.ObserverTelemetryPoint: properties: - airtimeRxPct: + airtimeRxSecs: type: number - airtimeTxPct: + airtimeTxSecs: type: number batteryMv: type: integer diff --git a/internal/api/observers.go b/internal/api/observers.go index afe800f..206387b 100644 --- a/internal/api/observers.go +++ b/internal/api/observers.go @@ -49,11 +49,12 @@ type Observer struct { } // ObserverTelemetryPoint is a single telemetry snapshot for an observer. +// Airtime is radio seconds: cumulative since boot on 1h points, per-bucket delta on 6h/24h. type ObserverTelemetryPoint struct { T int64 `json:"t"` // epoch ms BatteryMV *int32 `json:"batteryMv,omitempty"` - AirtimeTxPct *float32 `json:"airtimeTxPct,omitempty"` - AirtimeRxPct *float32 `json:"airtimeRxPct,omitempty"` + AirtimeTxSecs *float32 `json:"airtimeTxSecs,omitempty"` + AirtimeRxSecs *float32 `json:"airtimeRxSecs,omitempty"` NoiseFloorDB *float32 `json:"noiseFloorDb,omitempty"` UptimeSeconds *int64 `json:"uptimeSeconds,omitempty"` QueueLength *int32 `json:"queueLength,omitempty"`