@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:
114
backend/app/propagation/cost231_wi.py
Normal file
114
backend/app/propagation/cost231_wi.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""
|
||||
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),
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user