1
1
forked from iarv/meshview

Updating anb fixing errors

This commit is contained in:
pablorevilla-meshtastic
2026-02-10 11:07:22 -08:00
parent 7d5b638eac
commit a48a3a4141

View File

@@ -1,22 +1,29 @@
import math
from functools import lru_cache
from typing import List, Tuple
from pyitm.itm import ITM
try:
from pyitm import itm
# ----------------------------
# Config defaults (tune later)
# ----------------------------
DEFAULT_CLIMATE = 5 # Continental temperate
DEFAULT_GROUND = 0.005 # Average ground conductivity
DEFAULT_RELIABILITY = 0.5 # Median
ITM_AVAILABLE = True
except Exception:
itm = None
ITM_AVAILABLE = False
DEFAULT_CLIMATE = 5 # Continental temperate
DEFAULT_GROUND = 0.005 # Average ground conductivity
DEFAULT_EPS_DIELECT = 15.0
DEFAULT_DELTA_H = 90.0
DEFAULT_RELIABILITY = 0.5
DEFAULT_MIN_DBM = -130.0
DEFAULT_MAX_DBM = -80.0
DEFAULT_THRESHOLD_DBM = -120.0
EARTH_RADIUS_KM = 6371.0
BEARING_STEP_DEG = 5
def destination_point(lat, lon, bearing_deg, distance_km):
"""
Compute lat/lon from start point, bearing, distance
"""
def destination_point(
lat: float, lon: float, bearing_deg: float, distance_km: float
) -> tuple[float, float]:
lat1 = math.radians(lat)
lon1 = math.radians(lon)
bearing = math.radians(bearing_deg)
@@ -24,13 +31,12 @@ def destination_point(lat, lon, bearing_deg, distance_km):
d = distance_km / EARTH_RADIUS_KM
lat2 = math.asin(
math.sin(lat1) * math.cos(d)
+ math.cos(lat1) * math.sin(d) * math.cos(bearing)
math.sin(lat1) * math.cos(d) + math.cos(lat1) * math.sin(d) * math.cos(bearing)
)
lon2 = lon1 + math.atan2(
math.sin(bearing) * math.sin(d) * math.cos(lat1),
math.cos(d) - math.sin(lat1) * math.sin(lat2)
math.cos(d) - math.sin(lat1) * math.sin(lat2),
)
return math.degrees(lat2), math.degrees(lon2)
@@ -47,37 +53,94 @@ def compute_coverage(
radius_km: float,
step_km: float,
reliability: float,
) -> List[Tuple[float, float, float]]:
"""
Returns list of (lat, lon, rx_dbm)
Uses ITM area mode (no terrain profile)
"""
itm = ITM(
climate=DEFAULT_CLIMATE,
ground_conductivity=DEFAULT_GROUND,
refractivity=301, # standard atmosphere
freq_mhz=freq_mhz,
tx_height_m=tx_height_m,
rx_height_m=rx_height_m,
polarization=1, # vertical
reliability=reliability,
)
) -> list[tuple[float, float, float]]:
if not ITM_AVAILABLE:
return []
points = []
distance = step_km
distance = max(step_km, 1.0)
while distance <= radius_km:
for bearing in range(0, 360, 5):
for bearing in range(0, 360, BEARING_STEP_DEG):
rx_lat, rx_lon = destination_point(lat, lon, bearing, distance)
# ITM returns path loss (dB)
loss_db = itm.path_loss(distance_km=distance)
try:
loss_db, _ = itm.area(
ModVar=2,
deltaH=DEFAULT_DELTA_H,
tht_m=tx_height_m,
rht_m=rx_height_m,
dist_km=distance,
TSiteCriteria=0,
RSiteCriteria=0,
eps_dielect=DEFAULT_EPS_DIELECT,
sgm_conductivity=DEFAULT_GROUND,
eno_ns_surfref=301,
frq_mhz=freq_mhz,
radio_climate=DEFAULT_CLIMATE,
pol=1,
pctTime=reliability,
pctLoc=0.5,
pctConf=0.5,
)
except itm.InputError:
continue
rx_dbm = tx_dbm - loss_db
points.append((rx_lat, rx_lon, rx_dbm))
distance += step_km
return points
@lru_cache(maxsize=512)
def compute_perimeter(
lat: float,
lon: float,
freq_mhz: float,
tx_dbm: float,
tx_height_m: float,
rx_height_m: float,
radius_km: float,
step_km: float,
reliability: float,
threshold_dbm: float,
) -> list[tuple[float, float]]:
if not ITM_AVAILABLE:
return []
perimeter = []
distance = max(step_km, 1.0)
for bearing in range(0, 360, BEARING_STEP_DEG):
last_point = None
dist = distance
while dist <= radius_km:
try:
loss_db, _ = itm.area(
ModVar=2,
deltaH=DEFAULT_DELTA_H,
tht_m=tx_height_m,
rht_m=rx_height_m,
dist_km=dist,
TSiteCriteria=0,
RSiteCriteria=0,
eps_dielect=DEFAULT_EPS_DIELECT,
sgm_conductivity=DEFAULT_GROUND,
eno_ns_surfref=301,
frq_mhz=freq_mhz,
radio_climate=DEFAULT_CLIMATE,
pol=1,
pctTime=reliability,
pctLoc=0.5,
pctConf=0.5,
)
except itm.InputError:
dist += step_km
continue
rx_dbm = tx_dbm - loss_db
if rx_dbm >= threshold_dbm:
last_point = destination_point(lat, lon, bearing, dist)
dist += step_km
if last_point:
perimeter.append(last_point)
return perimeter