@mytec: 3.8.0a done

This commit is contained in:
2026-02-04 00:50:52 +02:00
parent 6dcc5a19b9
commit e392b449cc
6 changed files with 769 additions and 10 deletions

View File

@@ -581,6 +581,60 @@ class CoverageService:
f"({len(grid)} points, model={selected_model.name}, freq={site.frequency}MHz, "
f"env={env}, backend={'GPU' if gpu_service.available else 'CPU/NumPy'}) ━━━")
# ━━━ PHASE 2.6: GPU-Vectorized Terrain LOS + Diffraction ━━━
# This replaces the per-point LOS calculation in workers
t_batch_terrain = time.time()
grid_elevs = np.array([point_elevations.get((lat, lon), 0.0) for lat, lon in grid])
if settings.use_terrain and gpu_service.available:
_clog("━━━ PHASE 2.6: Batch terrain LOS (GPU) ━━━")
has_los_arr, terrain_loss_arr = gpu_service.batch_terrain_los(
site.lat, site.lon, site.height, site_elevation,
grid_lats.get() if hasattr(grid_lats, 'get') else grid_lats,
grid_lons.get() if hasattr(grid_lons, 'get') else grid_lons,
grid_elevs,
pre_distances,
site.frequency,
self.terrain._tile_cache,
num_samples=30,
)
batch_terrain_time = time.time() - t_batch_terrain
blocked_count = np.sum(~has_los_arr)
_clog(f"━━━ PHASE 2.6 done: {batch_terrain_time:.2f}s "
f"({blocked_count}/{len(grid)} blocked by terrain) ━━━")
# Add terrain results to precomputed dict
for i, (lat, lon) in enumerate(grid):
if (lat, lon) in precomputed:
precomputed[(lat, lon)]['has_los'] = bool(has_los_arr[i])
precomputed[(lat, lon)]['terrain_loss'] = float(terrain_loss_arr[i])
else:
_clog("━━━ PHASE 2.6: Skipped (terrain disabled or no GPU) ━━━")
# Initialize with defaults
for lat, lon in grid:
if (lat, lon) in precomputed:
precomputed[(lat, lon)]['has_los'] = True
precomputed[(lat, lon)]['terrain_loss'] = 0.0
# ━━━ PHASE 2.7: GPU-Vectorized Antenna Pattern ━━━
if site.azimuth is not None and site.beamwidth and gpu_service.available:
t_batch_antenna = time.time()
antenna_loss_arr = gpu_service.batch_antenna_pattern(
site.lat, site.lon,
grid_lats.get() if hasattr(grid_lats, 'get') else grid_lats,
grid_lons.get() if hasattr(grid_lons, 'get') else grid_lons,
site.azimuth,
site.beamwidth,
)
for i, (lat, lon) in enumerate(grid):
if (lat, lon) in precomputed:
precomputed[(lat, lon)]['antenna_loss'] = float(antenna_loss_arr[i])
_clog(f"━━━ PHASE 2.7: Batch antenna pattern done: {time.time() - t_batch_antenna:.2f}s ━━━")
else:
for lat, lon in grid:
if (lat, lon) in precomputed:
precomputed[(lat, lon)]['antenna_loss'] = 0.0
# ━━━ PHASE 3: Point calculation ━━━
dominant_path_service._log_count = 0 # Reset diagnostic counter
t_points = time.time()
@@ -1117,6 +1171,9 @@ class CoverageService:
timing,
precomputed_distance=pre.get('distance') if pre else None,
precomputed_path_loss=pre.get('path_loss') if pre else None,
precomputed_has_los=pre.get('has_los') if pre else None,
precomputed_terrain_loss=pre.get('terrain_loss') if pre else None,
precomputed_antenna_loss=pre.get('antenna_loss') if pre else None,
)
if point.rsrp >= settings.min_signal:
points.append(point)
@@ -1139,6 +1196,9 @@ class CoverageService:
timing: dict,
precomputed_distance: Optional[float] = None,
precomputed_path_loss: Optional[float] = None,
precomputed_has_los: Optional[bool] = None,
precomputed_terrain_loss: Optional[float] = None,
precomputed_antenna_loss: Optional[float] = None,
) -> CoveragePoint:
"""Fully synchronous point calculation. All terrain tiles must be pre-loaded."""
@@ -1165,29 +1225,37 @@ class CoverageService:
)
path_loss = model.calculate(prop_input).path_loss_db
# Antenna pattern
antenna_loss = 0.0
if site.azimuth is not None and site.beamwidth:
# Antenna pattern (use precomputed if available)
if precomputed_antenna_loss is not None:
antenna_loss = precomputed_antenna_loss
elif site.azimuth is not None and site.beamwidth:
t0 = time.time()
antenna_loss = self._antenna_pattern_loss(
site.lat, site.lon, lat, lon, site.azimuth, site.beamwidth
)
timing["antenna"] += time.time() - t0
else:
antenna_loss = 0.0
# Terrain LOS (sync)
terrain_loss = 0.0
has_los = True
if settings.use_terrain:
# Terrain LOS (use precomputed if available)
if precomputed_has_los is not None and precomputed_terrain_loss is not None:
has_los = precomputed_has_los
terrain_loss = precomputed_terrain_loss
elif settings.use_terrain:
t0 = time.time()
los_result = self.los.check_line_of_sight_sync(
site.lat, site.lon, site.height, lat, lon, 1.5
)
has_los = los_result["has_los"]
terrain_loss = 0.0
if not has_los:
terrain_loss = self._diffraction_loss(
los_result["clearance"], site.frequency
)
timing["los"] += time.time() - t0
else:
has_los = True
terrain_loss = 0.0
# Building loss (spatial index)
building_loss = 0.0