added vegetation
This commit is contained in:
Binary file not shown.
+22
-2
@@ -11,7 +11,12 @@ EARTH_RADIUS_M = 6_371_000.0
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LosSample:
|
||||
lat: float
|
||||
lon: float
|
||||
distance_m: float
|
||||
surface_m: float
|
||||
path_height_m: float
|
||||
obstacle_height_m: float
|
||||
clearance_m: float
|
||||
fresnel_radius_m: float
|
||||
required_clearance_m: float
|
||||
@@ -25,6 +30,7 @@ class LosResult:
|
||||
first_fresnel_clearance_pct: float
|
||||
worst_sample: LosSample | None
|
||||
obstructions: list[LosSample]
|
||||
samples: list[LosSample]
|
||||
|
||||
|
||||
def wavelength(freq_hz: float) -> float:
|
||||
@@ -66,14 +72,19 @@ def los_analysis(
|
||||
tx_elevation = profile.samples[0].ground_m + tx_height_agl
|
||||
rx_elevation = profile.samples[-1].ground_m + rx_height_agl
|
||||
obstructions: list[LosSample] = []
|
||||
samples: list[LosSample] = []
|
||||
worst_sample: LosSample | None = None
|
||||
min_ratio = float("inf")
|
||||
geometric_los = True
|
||||
|
||||
for sample in profile.samples[1:-1]:
|
||||
for sample in profile.samples:
|
||||
d1 = sample.distance_m
|
||||
d2 = total_distance - d1
|
||||
path_height = tx_elevation + (rx_elevation - tx_elevation) * (d1 / total_distance)
|
||||
path_height = (
|
||||
tx_elevation
|
||||
if total_distance == 0
|
||||
else tx_elevation + (rx_elevation - tx_elevation) * (d1 / total_distance)
|
||||
)
|
||||
obstacle_height = sample.surface_m + earth_bulge(d1, d2, k)
|
||||
clearance_m = path_height - obstacle_height
|
||||
f1 = fresnel_radius(lmbda, d1, d2)
|
||||
@@ -82,12 +93,20 @@ def los_analysis(
|
||||
obstruction_type = sample.dominant_obstruction
|
||||
|
||||
los_sample = LosSample(
|
||||
lat=sample.lat,
|
||||
lon=sample.lon,
|
||||
distance_m=d1,
|
||||
surface_m=sample.surface_m,
|
||||
path_height_m=path_height,
|
||||
obstacle_height_m=obstacle_height,
|
||||
clearance_m=clearance_m,
|
||||
fresnel_radius_m=f1,
|
||||
required_clearance_m=required,
|
||||
obstruction_type=obstruction_type,
|
||||
)
|
||||
samples.append(los_sample)
|
||||
if d1 == 0 or d1 == total_distance:
|
||||
continue
|
||||
if worst_sample is None or clearance_m - required < (
|
||||
worst_sample.clearance_m - worst_sample.required_clearance_m
|
||||
):
|
||||
@@ -104,4 +123,5 @@ def los_analysis(
|
||||
first_fresnel_clearance_pct=100.0 if min_ratio == float("inf") else min_ratio,
|
||||
worst_sample=worst_sample,
|
||||
obstructions=obstructions,
|
||||
samples=samples,
|
||||
)
|
||||
|
||||
Binary file not shown.
@@ -59,6 +59,22 @@ class Obstruction(BaseModel):
|
||||
type: Literal["terrain", "building", "canopy"]
|
||||
|
||||
|
||||
class FresnelProfileSample(BaseModel):
|
||||
i: int
|
||||
lat: float
|
||||
lon: float
|
||||
distance_m: float
|
||||
surface_m: float
|
||||
path_height_m: float
|
||||
obstacle_height_m: float
|
||||
clearance_m: float
|
||||
fresnel_radius_m: float
|
||||
required_clearance_m: float
|
||||
fresnel_lower_60pct_m: float
|
||||
fresnel_lower_100pct_m: float
|
||||
type: Literal["terrain", "building", "canopy"]
|
||||
|
||||
|
||||
class LosResponse(BaseModel):
|
||||
los_clear: bool
|
||||
geometric_los: bool
|
||||
@@ -66,6 +82,7 @@ class LosResponse(BaseModel):
|
||||
worst_obstruction: Obstruction | None
|
||||
obstructions: list[Obstruction]
|
||||
geometric_obstructions: list[Obstruction]
|
||||
fresnel_profile: list[FresnelProfileSample]
|
||||
fresnel_violations_count: int
|
||||
geometric_obstructions_count: int
|
||||
building_obstructions_count: int
|
||||
|
||||
Binary file not shown.
@@ -14,6 +14,7 @@ from app.core.surface import SurfaceProfile, build_surface_profile
|
||||
from app.models.common import DataSources
|
||||
from app.models.terrain import (
|
||||
ElevationResponse,
|
||||
FresnelProfileSample,
|
||||
LosRequest,
|
||||
LosResponse,
|
||||
Obstruction,
|
||||
@@ -117,6 +118,23 @@ def los(
|
||||
type=sample.obstruction_type,
|
||||
)
|
||||
|
||||
def convert_fresnel_sample(i: int, sample: LosSample) -> FresnelProfileSample:
|
||||
return FresnelProfileSample(
|
||||
i=i,
|
||||
lat=sample.lat,
|
||||
lon=sample.lon,
|
||||
distance_m=sample.distance_m,
|
||||
surface_m=sample.surface_m,
|
||||
path_height_m=sample.path_height_m,
|
||||
obstacle_height_m=sample.obstacle_height_m,
|
||||
clearance_m=sample.clearance_m,
|
||||
fresnel_radius_m=sample.fresnel_radius_m,
|
||||
required_clearance_m=sample.required_clearance_m,
|
||||
fresnel_lower_60pct_m=sample.path_height_m - sample.required_clearance_m,
|
||||
fresnel_lower_100pct_m=sample.path_height_m - sample.fresnel_radius_m,
|
||||
type=sample.obstruction_type,
|
||||
)
|
||||
|
||||
geometric_obstructions = [
|
||||
sample for sample in result.obstructions if sample.clearance_m < 0
|
||||
]
|
||||
@@ -133,6 +151,9 @@ def los(
|
||||
worst_obstruction=convert(result.worst_sample) if result.worst_sample else None,
|
||||
obstructions=[convert(sample) for sample in result.obstructions],
|
||||
geometric_obstructions=[convert(sample) for sample in geometric_obstructions],
|
||||
fresnel_profile=[
|
||||
convert_fresnel_sample(i, sample) for i, sample in enumerate(result.samples)
|
||||
],
|
||||
fresnel_violations_count=len(result.obstructions),
|
||||
geometric_obstructions_count=len(geometric_obstructions),
|
||||
building_obstructions_count=len(building_obstructions),
|
||||
|
||||
Reference in New Issue
Block a user