@mytec: 3.8.0a done
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user