first commit

This commit is contained in:
2026-06-17 13:26:35 +03:00
commit f4f254116b
31 changed files with 1358 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
from __future__ import annotations
from typing import Any, Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator
class GeoAntenna(BaseModel):
lat: float
lng: float
antenna_height_m: float = Field(default=2.0, ge=0)
@field_validator("lat")
@classmethod
def validate_lat(cls, value: float) -> float:
if not -90 <= value <= 90:
raise ValueError("lat must be between -90 and 90")
return value
@field_validator("lng")
@classmethod
def validate_lng(cls, value: float) -> float:
if not -180 <= value <= 180:
raise ValueError("lng must be between -180 and 180")
return value
class LinkRequest(BaseModel):
model_config = ConfigDict(populate_by_name=True)
from_: GeoAntenna = Field(alias="from")
to: GeoAntenna
frequency_mhz: float = Field(gt=0)
fresnel_clearance: float = Field(default=0.6, ge=0, le=1)
tx_power_dbm: float = 20.0
rx_sensitivity_dbm: float = -137.0
sample_step_m: float = Field(default=30.0, gt=0)
class LinkBudget(BaseModel):
tx_power_dbm: float
rx_sensitivity_dbm: float
margin_db: float
status: Literal["good", "marginal", "poor"]
class Obstacle(BaseModel):
lat: float
lng: float
distance_from_start_m: float
terrain_height_m: float
canopy_height_m: float
building_height_m: float
total_height_m: float
fresnel_radius_m: float
clearance_m: float
class PathProfilePoint(BaseModel):
distance_m: float
lat: float
lng: float
terrain_m: float
canopy_m: float
building_m: float
total_m: float
los_height_m: float
fresnel_radius_m: float
clearance_m: float
class LinkResponse(BaseModel):
distance_m: float
los: bool
fresnel_clear: bool
free_space_loss_db: float
link_budget: LinkBudget
obstacles: list[Obstacle]
path_profile: list[PathProfilePoint]
class CoverageRequest(BaseModel):
origin: GeoAntenna
frequency_mhz: float = Field(gt=0)
radius_m: float = Field(gt=0)
resolution_m: float = Field(gt=0)
fresnel_clearance: float = Field(default=0.6, ge=0, le=1)
tx_power_dbm: float = 20.0
rx_sensitivity_dbm: float = -137.0
sample_step_m: float = Field(default=30.0, gt=0)
class CoverageResponse(BaseModel):
origin: GeoAntenna
coverage_geojson: dict[str, Any]
class ElevationProfilePoint(BaseModel):
distance_m: float
lat: float
lng: float
terrain_m: float
class ElevationProfileResponse(BaseModel):
distance_m: float
path_profile: list[ElevationProfilePoint]
class DataLayersHealth(BaseModel):
srtm: bool
canopy: bool
buildings: bool
class HealthResponse(BaseModel):
status: Literal["ok", "degraded"]
data_layers: DataLayersHealth