@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:
87
backend/app/propagation/itu_r_p526.py
Normal file
87
backend/app/propagation/itu_r_p526.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
Knife-edge diffraction model based on ITU-R P.526.
|
||||
|
||||
Used for calculating additional loss when terrain or obstacles
|
||||
block the line of sight between TX and RX.
|
||||
|
||||
Reference: ITU-R P.526-15
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
|
||||
class KnifeEdgeDiffractionModel:
|
||||
"""
|
||||
Single knife-edge diffraction model.
|
||||
|
||||
Stateless utility — not a full PropagationModel since it calculates
|
||||
additional loss, not total path loss.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def calculate_loss(
|
||||
d1_m: float,
|
||||
d2_m: float,
|
||||
h_m: float,
|
||||
wavelength_m: float,
|
||||
) -> float:
|
||||
"""
|
||||
Calculate diffraction loss over single knife edge.
|
||||
|
||||
Args:
|
||||
d1_m: Distance from TX to obstacle
|
||||
d2_m: Distance from obstacle to RX
|
||||
h_m: Obstacle height above LOS line (positive = above)
|
||||
wavelength_m: Signal wavelength
|
||||
|
||||
Returns:
|
||||
Loss in dB (always >= 0)
|
||||
"""
|
||||
if d1_m <= 0 or d2_m <= 0 or wavelength_m <= 0:
|
||||
return 0.0
|
||||
|
||||
# Fresnel-Kirchhoff parameter
|
||||
v = h_m * math.sqrt(2 * (d1_m + d2_m) / (wavelength_m * d1_m * d2_m))
|
||||
|
||||
# Diffraction loss (Lee approximation)
|
||||
if v < -0.78:
|
||||
L = 0.0
|
||||
elif v < 0:
|
||||
L = 6.02 + 9.11 * v - 1.27 * v ** 2
|
||||
elif v < 2.4:
|
||||
L = 6.02 + 9.11 * v + 1.65 * v ** 2
|
||||
else:
|
||||
L = 12.95 + 20 * math.log10(v)
|
||||
|
||||
return max(0.0, L)
|
||||
|
||||
@staticmethod
|
||||
def calculate_clearance_loss(
|
||||
clearance_m: float,
|
||||
frequency_mhz: float,
|
||||
) -> float:
|
||||
"""
|
||||
Simplified diffraction loss from terrain clearance.
|
||||
|
||||
Matches the existing coverage_service._diffraction_loss logic.
|
||||
|
||||
Args:
|
||||
clearance_m: Minimum LOS clearance (negative = blocked)
|
||||
frequency_mhz: Signal frequency
|
||||
|
||||
Returns:
|
||||
Loss in dB (0 if positive clearance)
|
||||
"""
|
||||
if clearance_m >= 0:
|
||||
return 0.0
|
||||
|
||||
v = abs(clearance_m) / 10
|
||||
|
||||
if v <= 0:
|
||||
loss = 0.0
|
||||
elif v < 2.4:
|
||||
loss = 6.02 + 9.11 * v - 1.27 * v ** 2
|
||||
else:
|
||||
loss = 13.0 + 20 * math.log10(v)
|
||||
|
||||
return min(loss, 40.0)
|
||||
Reference in New Issue
Block a user