Files
ukmesh/viewshed-worker/rf/config.py
T
BenandClaude Opus 4.6 7994e13175 Add SEO, improve path resolution, per-node antenna heights, and UI refinements
Backend: derive observer IATA from packet topic instead of node table,
send messages separately in WS initial state, add tiered prefix/edge
priors to lazy path resolver for more accurate hash disambiguation.

Frontend: add SeoHead, JsonLd, and vite-seo plugin for per-route meta
tags and structured data. Add earth-curvature correction to custom LOS
tool. Owner portal last-hop chart supports hiding/showing individual
series with cookie persistence. Feed page and path map refactored.
Cache-busting build filenames. Track firstSeenTs on aggregated packets.

Infra: serve robots.txt and sitemap.xml via nginx, add manifest.json,
reduce Mosquitto log verbosity. Viewshed worker supports per-node
antenna heights, configurable profile step, and SNR-median calibration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 17:22:31 +00:00

56 lines
1.8 KiB
Python

import math
import os
import time
ANTENNA_HEIGHT_M = 5
COVERAGE_TARGET_HEIGHT_M = 5
LINK_LOS_MAX_V = float(os.environ.get('LINK_LOS_MAX_V', '1.12'))
K_FACTOR = 4 / 3
R_EARTH_M = 6_371_000
FREQ_MHZ = 868.0
LAMBDA_M = 3e8 / (FREQ_MHZ * 1e6)
PROFILE_STEP_M = float(os.environ.get('PROFILE_STEP_M', '100.0'))
DEFAULT_USABLE_PATH_LOSS_DB = float(os.environ.get('DEFAULT_USABLE_PATH_LOSS_DB', '137.5'))
CALIBRATION_REFRESH_S = int(os.environ.get('COVERAGE_CALIBRATION_REFRESH_S', '900'))
CALIBRATION_MIN_LINKS = int(os.environ.get('COVERAGE_CALIBRATION_MIN_LINKS', '24'))
CALIBRATION_MIN_OBSERVED_COUNT = int(os.environ.get('COVERAGE_CALIBRATION_MIN_OBSERVED_COUNT', '3'))
CALIBRATION_MAX_THRESHOLD_BOOST_DB = float(os.environ.get('COVERAGE_CALIBRATION_MAX_THRESHOLD_BOOST_DB', '2'))
CALIBRATION_PERCENTILE = float(os.environ.get('COVERAGE_CALIBRATION_PERCENTILE', '0.9'))
CALIBRATION_EXTRA_MARGIN_DB = float(os.environ.get('COVERAGE_CALIBRATION_EXTRA_MARGIN_DB', '0.5'))
RF_CALIBRATION = {
'usable_path_loss_db': DEFAULT_USABLE_PATH_LOSS_DB,
'signal_thresholds_db': {
'green': max(116.0, DEFAULT_USABLE_PATH_LOSS_DB - 16.0),
'amber': max(124.0, DEFAULT_USABLE_PATH_LOSS_DB - 8.0),
'red': DEFAULT_USABLE_PATH_LOSS_DB,
},
'samples': 0,
'updated_at': 0.0,
}
RADIO_NEIGHBOR_SYNC = {
'updated_at': 0.0,
}
def current_usable_path_loss_db() -> float:
return float(RF_CALIBRATION['usable_path_loss_db'])
def current_signal_thresholds_db() -> dict[str, float]:
return dict(RF_CALIBRATION['signal_thresholds_db'])
def radio_snr_band(snr_db):
if snr_db is None or not math.isfinite(float(snr_db)):
return 'unknown'
snr = float(snr_db)
if snr >= 8.0:
return 'strong'
if snr >= 2.0:
return 'medium'
if snr >= 0.0:
return 'weak'
return 'poor'