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)
75 lines
2.2 KiB
Python
75 lines
2.2 KiB
Python
"""
|
|
Okumura-Hata empirical propagation model.
|
|
|
|
Valid for:
|
|
- Frequency: 150-1500 MHz
|
|
- Distance: 1-20 km
|
|
- TX height: 30-200 m
|
|
- RX height: 1-10 m
|
|
|
|
Reference: Hata (1980), "Empirical Formula for Propagation Loss
|
|
in Land Mobile Radio Services"
|
|
"""
|
|
|
|
import math
|
|
from app.propagation.base import PropagationModel, PropagationInput, PropagationOutput
|
|
|
|
|
|
class OkumuraHataModel(PropagationModel):
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "Okumura-Hata"
|
|
|
|
@property
|
|
def frequency_range(self) -> tuple:
|
|
return (150, 1500)
|
|
|
|
@property
|
|
def distance_range(self) -> tuple:
|
|
return (100, 20000) # Extended to 100m minimum for practical use
|
|
|
|
def calculate(self, input: PropagationInput) -> PropagationOutput:
|
|
f = input.frequency_mhz
|
|
d = max(input.distance_m / 1000, 0.1) # km, min 100m
|
|
hb = max(input.tx_height_m, 1.0)
|
|
hm = max(input.rx_height_m, 1.0)
|
|
|
|
# Mobile antenna height correction factor
|
|
if input.environment == "urban" and f >= 400:
|
|
# Large city
|
|
a_hm = 3.2 * (math.log10(11.75 * hm) ** 2) - 4.97
|
|
else:
|
|
# Medium/small city
|
|
a_hm = (1.1 * math.log10(f) - 0.7) * hm - (1.56 * math.log10(f) - 0.8)
|
|
|
|
# Basic path loss (urban)
|
|
L_urban = (
|
|
69.55
|
|
+ 26.16 * math.log10(f)
|
|
- 13.82 * math.log10(hb)
|
|
- a_hm
|
|
+ (44.9 - 6.55 * math.log10(hb)) * math.log10(d)
|
|
)
|
|
|
|
# Environment correction
|
|
if input.environment == "suburban":
|
|
L = L_urban - 2 * (math.log10(f / 28) ** 2) - 5.4
|
|
elif input.environment == "rural":
|
|
L = L_urban - 4.78 * (math.log10(f) ** 2) + 18.33 * math.log10(f) - 35.94
|
|
elif input.environment == "open":
|
|
L = L_urban - 4.78 * (math.log10(f) ** 2) + 18.33 * math.log10(f) - 40.94
|
|
else:
|
|
L = L_urban
|
|
|
|
return PropagationOutput(
|
|
path_loss_db=L,
|
|
model_name=self.name,
|
|
is_los=False,
|
|
breakdown={
|
|
"basic_loss": L_urban,
|
|
"environment_correction": L - L_urban,
|
|
"antenna_correction": a_hm,
|
|
},
|
|
)
|