@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/base.py
Normal file
87
backend/app/propagation/base.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
Abstract base class for all propagation models.
|
||||
|
||||
Each model implements a single, well-defined propagation algorithm.
|
||||
Models are stateless and can be called concurrently.
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class PropagationInput:
|
||||
"""Input for propagation calculation."""
|
||||
frequency_mhz: float
|
||||
distance_m: float
|
||||
tx_height_m: float
|
||||
rx_height_m: float
|
||||
environment: str = "urban" # urban, suburban, rural, open
|
||||
|
||||
# Optional terrain info
|
||||
terrain_clearance_m: Optional[float] = None
|
||||
terrain_roughness_m: Optional[float] = None
|
||||
|
||||
# Optional building info
|
||||
building_height_m: Optional[float] = None
|
||||
street_width_m: Optional[float] = None
|
||||
building_separation_m: Optional[float] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class PropagationOutput:
|
||||
"""Output from propagation calculation."""
|
||||
path_loss_db: float
|
||||
model_name: str
|
||||
is_los: bool
|
||||
breakdown: dict = field(default_factory=dict)
|
||||
|
||||
|
||||
class PropagationModel(ABC):
|
||||
"""
|
||||
Abstract base class for all propagation models.
|
||||
|
||||
Each model implements a single, well-defined propagation algorithm.
|
||||
Models are stateless and can be called concurrently.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
"""Model name for logging/display."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def frequency_range(self) -> tuple:
|
||||
"""Valid frequency range (min_mhz, max_mhz)."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def distance_range(self) -> tuple:
|
||||
"""Valid distance range (min_m, max_m)."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def calculate(self, input: PropagationInput) -> PropagationOutput:
|
||||
"""
|
||||
Calculate path loss for given input.
|
||||
|
||||
This method MUST be:
|
||||
- Stateless (no side effects)
|
||||
- Thread-safe (can be called concurrently)
|
||||
- Fast (no I/O, no heavy computation)
|
||||
"""
|
||||
pass
|
||||
|
||||
def is_valid_for(self, input: PropagationInput) -> bool:
|
||||
"""Check if this model is valid for given input."""
|
||||
freq_min, freq_max = self.frequency_range
|
||||
dist_min, dist_max = self.distance_range
|
||||
|
||||
return (
|
||||
freq_min <= input.frequency_mhz <= freq_max and
|
||||
dist_min <= input.distance_m <= dist_max
|
||||
)
|
||||
Reference in New Issue
Block a user