@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:
3
backend/app/utils/__init__.py
Normal file
3
backend/app/utils/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Utility modules for RFCP backend.
|
||||
"""
|
||||
34
backend/app/utils/logging.py
Normal file
34
backend/app/utils/logging.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
Structured logging for RFCP backend.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
|
||||
|
||||
_log_file = None
|
||||
|
||||
|
||||
def rfcp_log(tag: str, msg: str):
|
||||
"""Log with tag prefix, timestamp, and thread name.
|
||||
|
||||
Writes to stdout and a log file for reliability.
|
||||
"""
|
||||
global _log_file
|
||||
ts = time.strftime('%H:%M:%S')
|
||||
thr = threading.current_thread().name
|
||||
line = f"[{tag} {ts}] [{thr}] {msg}"
|
||||
print(line, flush=True)
|
||||
|
||||
try:
|
||||
if _log_file is None:
|
||||
log_dir = os.environ.get('RFCP_DATA_PATH', './data')
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
log_path = os.path.join(log_dir, 'rfcp-backend.log')
|
||||
_log_file = open(log_path, 'a')
|
||||
_log_file.write(line + '\n')
|
||||
_log_file.flush()
|
||||
except Exception:
|
||||
pass
|
||||
44
backend/app/utils/progress.py
Normal file
44
backend/app/utils/progress.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
Progress reporting for long-running calculations.
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Optional, Callable, Awaitable
|
||||
|
||||
|
||||
class ProgressTracker:
|
||||
"""Track and report calculation progress."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
total: int,
|
||||
callback: Optional[Callable[[str, float, Optional[float]], Awaitable[None]]] = None,
|
||||
phase: str = "calculating",
|
||||
):
|
||||
self.total = total
|
||||
self.callback = callback
|
||||
self.phase = phase
|
||||
self.completed = 0
|
||||
self.start_time = time.time()
|
||||
|
||||
@property
|
||||
def progress(self) -> float:
|
||||
if self.total == 0:
|
||||
return 1.0
|
||||
return self.completed / self.total
|
||||
|
||||
@property
|
||||
def eta_seconds(self) -> Optional[float]:
|
||||
if self.completed == 0:
|
||||
return None
|
||||
elapsed = time.time() - self.start_time
|
||||
rate = self.completed / elapsed
|
||||
remaining = self.total - self.completed
|
||||
return remaining / rate if rate > 0 else None
|
||||
|
||||
def update(self, n: int = 1):
|
||||
self.completed += n
|
||||
|
||||
async def report(self):
|
||||
if self.callback:
|
||||
await self.callback(self.phase, self.progress, self.eta_seconds)
|
||||
54
backend/app/utils/units.py
Normal file
54
backend/app/utils/units.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
RF unit conversions.
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
|
||||
def dbm_to_watts(dbm: float) -> float:
|
||||
"""Convert dBm to watts."""
|
||||
return 10 ** ((dbm - 30) / 10)
|
||||
|
||||
|
||||
def watts_to_dbm(watts: float) -> float:
|
||||
"""Convert watts to dBm."""
|
||||
if watts <= 0:
|
||||
return -float('inf')
|
||||
return 10 * math.log10(watts) + 30
|
||||
|
||||
|
||||
def dbm_to_mw(dbm: float) -> float:
|
||||
"""Convert dBm to milliwatts."""
|
||||
return 10 ** (dbm / 10)
|
||||
|
||||
|
||||
def mw_to_dbm(mw: float) -> float:
|
||||
"""Convert milliwatts to dBm."""
|
||||
if mw <= 0:
|
||||
return -float('inf')
|
||||
return 10 * math.log10(mw)
|
||||
|
||||
|
||||
def frequency_to_wavelength(frequency_mhz: float) -> float:
|
||||
"""Convert frequency (MHz) to wavelength (meters)."""
|
||||
return 300.0 / frequency_mhz
|
||||
|
||||
|
||||
def wavelength_to_frequency(wavelength_m: float) -> float:
|
||||
"""Convert wavelength (meters) to frequency (MHz)."""
|
||||
return 300.0 / wavelength_m
|
||||
|
||||
|
||||
def eirp_dbm(power_dbm: float, gain_dbi: float) -> float:
|
||||
"""Calculate EIRP in dBm."""
|
||||
return power_dbm + gain_dbi
|
||||
|
||||
|
||||
def eirp_watts(power_dbm: float, gain_dbi: float) -> float:
|
||||
"""Calculate EIRP in watts."""
|
||||
return dbm_to_watts(power_dbm + gain_dbi)
|
||||
|
||||
|
||||
def path_loss_to_signal_dbm(power_dbm: float, gain_dbi: float, path_loss_db: float) -> float:
|
||||
"""Calculate received signal level in dBm from EIRP and path loss."""
|
||||
return power_dbm + gain_dbi - path_loss_db
|
||||
Reference in New Issue
Block a user