Phase 2.2: performance optimizations, debug tools, app close fix
This commit is contained in:
@@ -44,8 +44,10 @@ class StreetCanyonService:
|
||||
CORNER_LOSS_90 = 10.0 # dB for 90-degree turn
|
||||
CORNER_LOSS_45 = 4.0 # dB for 45-degree turn
|
||||
|
||||
def __init__(self, cache_dir: str = "/opt/rfcp/backend/data/streets"):
|
||||
self.cache_dir = Path(cache_dir)
|
||||
def __init__(self):
|
||||
import os
|
||||
self.data_path = Path(os.environ.get('RFCP_DATA_PATH', './data'))
|
||||
self.cache_dir = self.data_path / 'osm' / 'streets'
|
||||
self.cache_dir.mkdir(exist_ok=True, parents=True)
|
||||
self._cache: dict[str, List[Street]] = {}
|
||||
|
||||
@@ -56,21 +58,28 @@ class StreetCanyonService:
|
||||
) -> List[Street]:
|
||||
"""Fetch street network from OSM"""
|
||||
|
||||
cache_key = f"{min_lat:.3f}_{min_lon:.3f}_{max_lat:.3f}_{max_lon:.3f}"
|
||||
cache_key = f"{min_lat:.2f}_{min_lon:.2f}_{max_lat:.2f}_{max_lon:.2f}"
|
||||
|
||||
# Check cache
|
||||
# Check memory cache
|
||||
if cache_key in self._cache:
|
||||
return self._cache[cache_key]
|
||||
|
||||
# Check disk cache
|
||||
cache_file = self.cache_dir / f"{cache_key}.json"
|
||||
if cache_file.exists():
|
||||
with open(cache_file) as f:
|
||||
data = json.load(f)
|
||||
streets = [Street(**s) for s in data]
|
||||
self._cache[cache_key] = streets
|
||||
return streets
|
||||
try:
|
||||
with open(cache_file) as f:
|
||||
data = json.load(f)
|
||||
streets = [Street(**s) for s in data]
|
||||
self._cache[cache_key] = streets
|
||||
print(f"[Streets] Cache hit for {cache_key}")
|
||||
return streets
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fetch from Overpass
|
||||
print(f"[Streets] Fetching from Overpass API for {cache_key}...")
|
||||
|
||||
query = f"""
|
||||
[out:json][timeout:30];
|
||||
way["highway"]({min_lat},{min_lon},{max_lat},{max_lon});
|
||||
@@ -85,20 +94,21 @@ class StreetCanyonService:
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
except Exception as e:
|
||||
print(f"Street fetch error: {e}")
|
||||
print(f"[Streets] Fetch error: {e}")
|
||||
return []
|
||||
|
||||
streets = self._parse_streets(data)
|
||||
|
||||
# Cache
|
||||
with open(cache_file, 'w') as f:
|
||||
json.dump([{
|
||||
"id": s.id,
|
||||
"name": s.name,
|
||||
"geometry": s.geometry,
|
||||
"width": s.width,
|
||||
"highway_type": s.highway_type
|
||||
} for s in streets], f)
|
||||
# Cache to disk
|
||||
if streets:
|
||||
with open(cache_file, 'w') as f:
|
||||
json.dump([{
|
||||
"id": s.id,
|
||||
"name": s.name,
|
||||
"geometry": s.geometry,
|
||||
"width": s.width,
|
||||
"highway_type": s.highway_type
|
||||
} for s in streets], f)
|
||||
|
||||
self._cache[cache_key] = streets
|
||||
return streets
|
||||
@@ -360,4 +370,42 @@ class StreetCanyonService:
|
||||
return self.CORNER_LOSS_90 + (turn_angle - 90) * 0.2 # Extra loss for sharp turns
|
||||
|
||||
|
||||
def calculate_street_canyon_loss_sync(
|
||||
self,
|
||||
tx_lat: float, tx_lon: float, tx_height: float,
|
||||
rx_lat: float, rx_lon: float, rx_height: float,
|
||||
frequency_mhz: float,
|
||||
streets: List[Street]
|
||||
) -> Tuple[float, List[Tuple[float, float]]]:
|
||||
"""Sync version (no I/O in the async original)"""
|
||||
street_path = self._find_street_path(tx_lat, tx_lon, rx_lat, rx_lon, streets)
|
||||
|
||||
if not street_path:
|
||||
return float('inf'), []
|
||||
|
||||
total_loss = 0.0
|
||||
total_distance = 0.0
|
||||
|
||||
for i in range(len(street_path) - 1):
|
||||
p1 = street_path[i]
|
||||
p2 = street_path[i + 1]
|
||||
|
||||
from app.services.terrain_service import TerrainService
|
||||
segment_dist = TerrainService.haversine_distance(p1[0], p1[1], p2[0], p2[1])
|
||||
total_distance += segment_dist
|
||||
|
||||
if segment_dist > 0:
|
||||
segment_loss = 32.4 + 20 * np.log10(frequency_mhz) + 20 * np.log10(segment_dist / 1000 + 0.001)
|
||||
total_loss += segment_loss * (segment_dist / total_distance) if total_distance > 0 else 0
|
||||
|
||||
if i > 0:
|
||||
corner_angle = self._calculate_corner_angle(
|
||||
street_path[i - 1], p1, p2
|
||||
)
|
||||
corner_loss = self._corner_loss(corner_angle)
|
||||
total_loss += corner_loss
|
||||
|
||||
return total_loss, street_path
|
||||
|
||||
|
||||
street_canyon_service = StreetCanyonService()
|
||||
|
||||
Reference in New Issue
Block a user