@mytec: feat: Phase 3.0 Architecture Refactor ✅
Major refactoring of RFCP backend: - Modular propagation models (8 models) - SharedMemoryManager for terrain data - ProcessPoolExecutor parallel processing - WebSocket progress streaming - Building filtering pipeline (351k → 15k) - 82 unit tests Performance: Standard preset 38s → 5s (7.6x speedup) Known issue: Detailed preset timeout (fix in 3.1.0)
This commit is contained in:
85
backend/app/geometry/los.py
Normal file
85
backend/app/geometry/los.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Line-of-sight checks using terrain profile data.
|
||||
"""
|
||||
|
||||
import math
|
||||
from typing import Optional, Dict, List
|
||||
|
||||
EARTH_RADIUS = 6371000
|
||||
K_FACTOR = 4 / 3 # Standard atmospheric refraction
|
||||
|
||||
|
||||
def check_los_terrain(
|
||||
profile: List[dict],
|
||||
tx_height: float,
|
||||
rx_height: float,
|
||||
) -> dict:
|
||||
"""
|
||||
Check line-of-sight from a terrain elevation profile.
|
||||
|
||||
Args:
|
||||
profile: List of dicts with 'elevation' and 'distance' keys.
|
||||
tx_height: TX antenna height above ground (meters).
|
||||
rx_height: RX height above ground (meters).
|
||||
|
||||
Returns:
|
||||
dict with has_los, clearance, blocked_at
|
||||
"""
|
||||
if not profile:
|
||||
return {"has_los": True, "clearance": 0.0, "blocked_at": None}
|
||||
|
||||
tx_ground = profile[0]["elevation"]
|
||||
rx_ground = profile[-1]["elevation"]
|
||||
tx_total = tx_ground + tx_height
|
||||
rx_total = rx_ground + rx_height
|
||||
total_distance = profile[-1]["distance"]
|
||||
|
||||
min_clearance = float("inf")
|
||||
blocked_at = None
|
||||
|
||||
for point in profile:
|
||||
d = point["distance"]
|
||||
terrain_elev = point["elevation"]
|
||||
|
||||
if total_distance == 0:
|
||||
los_height = tx_total
|
||||
else:
|
||||
los_height = tx_total + (rx_total - tx_total) * (d / total_distance)
|
||||
|
||||
# Earth curvature correction
|
||||
effective_radius = K_FACTOR * EARTH_RADIUS
|
||||
curvature = (d * (total_distance - d)) / (2 * effective_radius)
|
||||
los_height_corrected = los_height - curvature
|
||||
|
||||
clearance = los_height_corrected - terrain_elev
|
||||
|
||||
if clearance < min_clearance:
|
||||
min_clearance = clearance
|
||||
if clearance <= 0:
|
||||
blocked_at = d
|
||||
|
||||
return {
|
||||
"has_los": min_clearance > 0,
|
||||
"clearance": min_clearance,
|
||||
"blocked_at": blocked_at,
|
||||
}
|
||||
|
||||
|
||||
def fresnel_radius(
|
||||
d1_m: float, d2_m: float, wavelength_m: float, zone: int = 1
|
||||
) -> float:
|
||||
"""Calculate Fresnel zone radius at a point along the path.
|
||||
|
||||
Args:
|
||||
d1_m: Distance from TX to point
|
||||
d2_m: Distance from point to RX
|
||||
wavelength_m: Signal wavelength
|
||||
zone: Fresnel zone number (default 1)
|
||||
|
||||
Returns:
|
||||
Radius in meters
|
||||
"""
|
||||
total = d1_m + d2_m
|
||||
if total <= 0:
|
||||
return 0.0
|
||||
return math.sqrt(zone * wavelength_m * d1_m * d2_m / total)
|
||||
Reference in New Issue
Block a user