Files
rfcp/backend/app/propagation/cost231_wi.py
mytec defa3ad440 @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)
2026-02-01 23:12:26 +02:00

115 lines
3.2 KiB
Python

"""
COST-231 Walfisch-Ikegami model.
Valid for:
- Frequency: 800-2000 MHz
- Distance: 20m-5km
- Urban microcell environments
Accounts for building heights, street widths, and building separation.
Reference: COST 231 Final Report, Chapter 4.
"""
import math
from app.propagation.base import PropagationModel, PropagationInput, PropagationOutput
class Cost231WIModel(PropagationModel):
@property
def name(self) -> str:
return "COST-231-WI"
@property
def frequency_range(self) -> tuple:
return (800, 2000)
@property
def distance_range(self) -> tuple:
return (20, 5000)
def calculate(self, input: PropagationInput) -> PropagationOutput:
f = input.frequency_mhz
d = max(input.distance_m / 1000, 0.02) # km
hb = max(input.tx_height_m, 4.0)
hm = max(input.rx_height_m, 1.0)
# Building parameters (defaults for typical urban)
h_roof = input.building_height_m or 15.0 # avg building height
w = input.street_width_m or 20.0 # street width
b = input.building_separation_m or 30.0 # building separation
delta_hb = hb - h_roof # TX above rooftop
delta_hm = h_roof - hm # rooftop above RX
# Free space loss
L_fs = 32.45 + 20 * math.log10(d) + 20 * math.log10(f)
# LOS case
if delta_hb > 0 and d < 0.5:
L = L_fs
return PropagationOutput(
path_loss_db=L,
model_name=self.name,
is_los=True,
breakdown={"free_space": L_fs, "rooftop_diffraction": 0, "multiscreen": 0},
)
# Rooftop-to-street diffraction (L_rts)
phi = 90.0 # street orientation angle (worst case)
if phi < 35:
L_ori = -10 + 0.354 * phi
elif phi < 55:
L_ori = 2.5 + 0.075 * (phi - 35)
else:
L_ori = 4.0 - 0.114 * (phi - 55)
L_rts = (
-16.9
- 10 * math.log10(w)
+ 10 * math.log10(f)
+ 20 * math.log10(delta_hm)
+ L_ori
)
# Multi-screen diffraction (L_msd)
if delta_hb > 0:
L_bsh = -18 * math.log10(1 + delta_hb)
k_a = 54
k_d = 18
else:
L_bsh = 0
k_a = 54 - 0.8 * abs(delta_hb)
if d >= 0.5:
k_a = max(k_a, 54 - 0.8 * abs(delta_hb) * (d / 0.5))
k_d = 18 - 15 * abs(delta_hb) / h_roof
k_f = -4 + 0.7 * (f / 925 - 1) # medium city
if input.environment == "urban":
k_f = -4 + 1.5 * (f / 925 - 1)
L_msd = (
L_bsh
+ k_a
+ k_d * math.log10(d)
+ k_f * math.log10(f)
- 9 * math.log10(b)
)
# Total NLOS loss
if L_rts + L_msd > 0:
L = L_fs + L_rts + L_msd
else:
L = L_fs
return PropagationOutput(
path_loss_db=L,
model_name=self.name,
is_los=False,
breakdown={
"free_space": L_fs,
"rooftop_diffraction": max(L_rts, 0),
"multiscreen": max(L_msd, 0),
},
)