-
Notifications
You must be signed in to change notification settings - Fork 6
Air Traffic Client Unification phase 1 #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c49acda
80459b9
b48fe66
a5b9304
f22a68d
c5c3355
36df5df
cc0f46e
7a38e2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| name: stream_air_traffic_example | ||
| version: "1.0" | ||
| description: | | ||
| Example scenario demonstrating the unified "Stream Air Traffic" step. | ||
|
|
||
| This step replaces the provider-specific steps: | ||
| - "Generate Simulated Air Traffic Data" (geojson) | ||
| - "Generate BlueSky Simulation Air Traffic Data" (bluesky) | ||
| - "Generate Bayesian Simulation Air Traffic Data" (bayesian) | ||
| - "Fetch OpenSky Data" (opensky) | ||
|
|
||
| All providers can now be used with a single consistent interface. | ||
|
|
||
| steps: | ||
| # Example 1: GeoJSON provider with data generation only (no delivery) | ||
| - step: Stream Air Traffic | ||
| id: geojson_only | ||
| arguments: | ||
| provider: geojson | ||
| duration: 10 | ||
| target: none # Don't send anywhere, just return data | ||
| config_path: config/bern/trajectory_f1.json | ||
| number_of_aircraft: 2 | ||
|
|
||
| # Example 2: GeoJSON with delivery to Flight Blender | ||
| - step: Stream Air Traffic | ||
| id: geojson_stream | ||
| arguments: | ||
| provider: geojson | ||
| duration: 30 | ||
| target: flight_blender | ||
| config_path: config/bern/trajectory_f1.json | ||
atti92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| number_of_aircraft: 2 | ||
| session_ids: | ||
| - "550e8400-e29b-41d4-a716-446655440001" | ||
|
|
||
| # Example 3: BlueSky simulator (requires bluesky-simulator package) | ||
| # - step: Stream Air Traffic | ||
| # id: bluesky_stream | ||
| # arguments: | ||
| # provider: bluesky | ||
| # duration: 30 | ||
| # target: flight_blender | ||
| # config_path: config/bern/blue_sky_sim_bern.scn | ||
|
|
||
| # Example 4: Live OpenSky data for Switzerland region | ||
| # - step: Stream Air Traffic | ||
| # id: opensky_live | ||
| # arguments: | ||
| # provider: opensky | ||
| # duration: 30 | ||
| # target: flight_blender | ||
| # viewport: [45.8389, 47.8229, 5.9962, 10.5226] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| """Air traffic providers module. | ||
|
|
||
| Providers generate or fetch air traffic observation data from various sources. | ||
| """ | ||
|
|
||
| from .factory import ProviderType, create_provider | ||
| from .opensky_provider import DEFAULT_SWITZERLAND_VIEWPORT | ||
| from .protocol import AirTrafficProvider | ||
|
|
||
| __all__ = [ | ||
| "AirTrafficProvider", | ||
| "DEFAULT_SWITZERLAND_VIEWPORT", | ||
| "ProviderType", | ||
| "create_provider", | ||
| ] |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,98 @@ | ||||||||||||||||||||
| """Bayesian air traffic provider - wraps BayesianTrafficClient.""" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from openutm_verification.core.clients.air_traffic.base_client import ( | ||||||||||||||||||||
| BayesianAirTrafficSettings, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| from openutm_verification.core.clients.air_traffic.bayesian_air_traffic_client import ( | ||||||||||||||||||||
| BayesianTrafficClient, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| from openutm_verification.simulator.models.flight_data_types import ( | ||||||||||||||||||||
| FlightObservationSchema, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| class BayesianProvider: | ||||||||||||||||||||
| """Provider that generates air traffic using Bayesian track generation. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Wraps the existing BayesianTrafficClient to provide a consistent interface. | ||||||||||||||||||||
| Note: Requires the cam-track-gen package to be installed. | ||||||||||||||||||||
| """ | ||||||||||||||||||||
|
|
||||||||||||||||||||
| def __init__( | ||||||||||||||||||||
| self, | ||||||||||||||||||||
| config_path: str | None = None, | ||||||||||||||||||||
| number_of_aircraft: int | None = None, | ||||||||||||||||||||
| duration: int | None = None, | ||||||||||||||||||||
| sensor_ids: list[str] | None = None, | ||||||||||||||||||||
| session_ids: list[str] | None = None, | ||||||||||||||||||||
| ): | ||||||||||||||||||||
| """Initialize the Bayesian provider. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Args: | ||||||||||||||||||||
| config_path: Path to config (currently unused by Bayesian client). | ||||||||||||||||||||
| number_of_aircraft: Number of aircraft to simulate. | ||||||||||||||||||||
| duration: Simulation duration in seconds. | ||||||||||||||||||||
| sensor_ids: List of sensor UUID strings. | ||||||||||||||||||||
| session_ids: List of session UUID strings. | ||||||||||||||||||||
| """ | ||||||||||||||||||||
| self._config_path = config_path or "" | ||||||||||||||||||||
atti92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||
| self._number_of_aircraft = number_of_aircraft or 2 | ||||||||||||||||||||
| self._duration = duration or 30 | ||||||||||||||||||||
| self._sensor_ids = sensor_ids or [] | ||||||||||||||||||||
| self._session_ids = session_ids or [] | ||||||||||||||||||||
|
|
||||||||||||||||||||
| @property | ||||||||||||||||||||
| def name(self) -> str: | ||||||||||||||||||||
| """Provider identifier.""" | ||||||||||||||||||||
| return "bayesian" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| @classmethod | ||||||||||||||||||||
| def from_kwargs( | ||||||||||||||||||||
| cls, | ||||||||||||||||||||
| config_path: str | None = None, | ||||||||||||||||||||
| number_of_aircraft: int | None = None, | ||||||||||||||||||||
| duration: int | None = None, | ||||||||||||||||||||
| sensor_ids: list[str] | None = None, | ||||||||||||||||||||
| session_ids: list[str] | None = None, | ||||||||||||||||||||
| **_kwargs, # Ignore unknown kwargs for flexibility | ||||||||||||||||||||
| ) -> "BayesianProvider": | ||||||||||||||||||||
| """Factory method to create provider from keyword arguments.""" | ||||||||||||||||||||
| return cls( | ||||||||||||||||||||
| config_path=config_path, | ||||||||||||||||||||
| number_of_aircraft=number_of_aircraft, | ||||||||||||||||||||
| duration=duration, | ||||||||||||||||||||
| sensor_ids=sensor_ids, | ||||||||||||||||||||
| session_ids=session_ids, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| async def get_observations( | ||||||||||||||||||||
| self, | ||||||||||||||||||||
| duration: int | None = None, | ||||||||||||||||||||
| ) -> list[list[FlightObservationSchema]]: | ||||||||||||||||||||
| """Generate observations using the underlying BayesianTrafficClient. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Args: | ||||||||||||||||||||
| duration: Override duration in seconds. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Returns: | ||||||||||||||||||||
| List of observation lists per aircraft. | ||||||||||||||||||||
| """ | ||||||||||||||||||||
| effective_duration = duration or self._duration | ||||||||||||||||||||
|
|
||||||||||||||||||||
| settings = BayesianAirTrafficSettings( | ||||||||||||||||||||
| simulation_config_path=self._config_path, | ||||||||||||||||||||
| simulation_duration_seconds=effective_duration, | ||||||||||||||||||||
| number_of_aircraft=self._number_of_aircraft, | ||||||||||||||||||||
| sensor_ids=self._sensor_ids, | ||||||||||||||||||||
| session_ids=self._session_ids, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| async with BayesianTrafficClient(settings) as client: | ||||||||||||||||||||
| result = await client.generate_bayesian_sim_air_traffic_data( | ||||||||||||||||||||
|
||||||||||||||||||||
| result = await client.generate_bayesian_sim_air_traffic_data( | |
| # Bypass the @scenario_step wrapper to get the raw observation list | |
| generate_fn = getattr( | |
| BayesianTrafficClient.generate_bayesian_sim_air_traffic_data, | |
| "__wrapped__", | |
| BayesianTrafficClient.generate_bayesian_sim_air_traffic_data, | |
| ) | |
| result = await generate_fn( | |
| client, |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,96 @@ | ||||||||||||||||
| """BlueSky simulation air traffic provider - wraps BlueSkyClient.""" | ||||||||||||||||
|
|
||||||||||||||||
| from __future__ import annotations | ||||||||||||||||
|
|
||||||||||||||||
| from openutm_verification.core.clients.air_traffic.base_client import ( | ||||||||||||||||
| BlueSkyAirTrafficSettings, | ||||||||||||||||
| ) | ||||||||||||||||
| from openutm_verification.core.clients.air_traffic.blue_sky_client import ( | ||||||||||||||||
| BlueSkyClient, | ||||||||||||||||
| ) | ||||||||||||||||
| from openutm_verification.simulator.models.flight_data_types import ( | ||||||||||||||||
| FlightObservationSchema, | ||||||||||||||||
| ) | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| class BlueSkyProvider: | ||||||||||||||||
| """Provider that generates air traffic from BlueSky simulator scenarios. | ||||||||||||||||
|
|
||||||||||||||||
| Wraps the existing BlueSkyClient to provide a consistent interface. | ||||||||||||||||
| Note: Requires the bluesky-simulator package to be installed. | ||||||||||||||||
| """ | ||||||||||||||||
|
|
||||||||||||||||
| def __init__( | ||||||||||||||||
| self, | ||||||||||||||||
| config_path: str | None = None, | ||||||||||||||||
| number_of_aircraft: int | None = None, | ||||||||||||||||
| duration: int | None = None, | ||||||||||||||||
| sensor_ids: list[str] | None = None, | ||||||||||||||||
| session_ids: list[str] | None = None, | ||||||||||||||||
| ): | ||||||||||||||||
| """Initialize the BlueSky provider. | ||||||||||||||||
|
|
||||||||||||||||
| Args: | ||||||||||||||||
| config_path: Path to the BlueSky .scn scenario file. | ||||||||||||||||
| number_of_aircraft: Number of aircraft to simulate. | ||||||||||||||||
| duration: Simulation duration in seconds. | ||||||||||||||||
| sensor_ids: List of sensor UUID strings. | ||||||||||||||||
| session_ids: List of session UUID strings. | ||||||||||||||||
| """ | ||||||||||||||||
| self._config_path = config_path or "" | ||||||||||||||||
|
||||||||||||||||
| self._config_path = config_path or "" | |
| if not config_path or not str(config_path).strip(): | |
| raise ValueError( | |
| "BlueSkyProvider requires a non-empty 'config_path' pointing to a " | |
| "BlueSky .scn scenario file." | |
| ) | |
| self._config_path = config_path |
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like GeoJSONProvider, this defaults config_path to "" and stores it in _config_path, so omitting config_path will override any configured defaults and likely fail when the underlying BlueSky client loads the scenario. Consider validating config_path early with a helpful error, or sourcing a default path from application config when omitted.
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BlueSkyClient.generate_bluesky_sim_air_traffic_data is a @scenario_step, so this call returns a StepResult wrapper rather than the observation list. That will break the provider/streamer interface (and likely causes type errors at runtime). Consider calling the undecorated method (via .__wrapped__) or adding a non-step helper on the client for providers to call.
| return await client.generate_bluesky_sim_air_traffic_data( | |
| generate_raw = client.generate_bluesky_sim_air_traffic_data.__wrapped__ | |
| return await generate_raw( | |
| client, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The config_path uses '.json' extension but the comment and other references suggest it should be '.geojson'. The file extension mismatch could cause confusion. Verify the correct file extension and update the example to match the actual file format.