From 33f8041cbd1b6cb515b4fac9d7b628713b10897e Mon Sep 17 00:00:00 2001 From: Vincent Grafe Date: Thu, 25 Jun 2026 13:08:06 -0400 Subject: [PATCH] Configure generated NetRID flight data --- .../resources/netrid/flight_data.py | 6 +++ .../netrid/flight_data_resources_test.py | 52 ++++++++++++++++++- .../adjacent_circular_flights_simulator.py | 25 ++++++--- ...CircularFlightsSimulatorConfiguration.json | 8 +++ 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/monitoring/uss_qualifier/resources/netrid/flight_data.py b/monitoring/uss_qualifier/resources/netrid/flight_data.py index 6c73f321ae..09613c8937 100644 --- a/monitoring/uss_qualifier/resources/netrid/flight_data.py +++ b/monitoring/uss_qualifier/resources/netrid/flight_data.py @@ -59,6 +59,12 @@ class AdjacentCircularFlightsSimulatorConfiguration(ImplicitDict): flight_start_shift: int = 0 """Delay generated flight starts from the reference time to spread flights over time. Expressed in seconds. Use 0 to disable.""" + num_flights: int = 6 + """Number of adjacent circular flights to generate.""" + + duration: int = 30 + """Number of seconds of telemetry to generate for each flight.""" + class FlightDataKMLFileConfiguration(ImplicitDict): reference_time: StringBasedDateTime = StringBasedDateTime("2022-01-01T00:00:00Z") diff --git a/monitoring/uss_qualifier/resources/netrid/flight_data_resources_test.py b/monitoring/uss_qualifier/resources/netrid/flight_data_resources_test.py index 1511c8d01e..98a4a8fef0 100644 --- a/monitoring/uss_qualifier/resources/netrid/flight_data_resources_test.py +++ b/monitoring/uss_qualifier/resources/netrid/flight_data_resources_test.py @@ -94,4 +94,54 @@ def test_adjacent_circular_flights_simuation_source(): specs = FlightDataSpecification( adjacent_circular_flights_simulation_source=AdjacentCircularFlightsSimulatorConfiguration() ) - FlightDataResource(specs, "test") + resource = FlightDataResource(specs, "test") + + assert len(resource.flight_collection.flights) == 6 + assert [len(f.states) for f in resource.flight_collection.flights] == [ + 30, + 30, + 30, + 30, + 30, + 30, + ] + + +def test_adjacent_circular_flights_simulation_source_configuration(): + specs = FlightDataSpecification( + adjacent_circular_flights_simulation_source=AdjacentCircularFlightsSimulatorConfiguration( + num_flights=4, + duration=61, + ) + ) + + resource = FlightDataResource(specs, "test") + + assert len(resource.flight_collection.flights) == 4 + assert [len(f.states) for f in resource.flight_collection.flights] == [ + 61, + 61, + 61, + 61, + ] + + +@pytest.mark.parametrize( + "num_flights,duration", + [ + (0, 61), + (4, 0), + ], +) +def test_adjacent_circular_flights_simulation_source_invalid_configuration( + num_flights, duration +): + specs = FlightDataSpecification( + adjacent_circular_flights_simulation_source=AdjacentCircularFlightsSimulatorConfiguration( + num_flights=num_flights, + duration=duration, + ) + ) + + with pytest.raises(ValueError): + FlightDataResource(specs, "test") diff --git a/monitoring/uss_qualifier/resources/netrid/simulation/adjacent_circular_flights_simulator.py b/monitoring/uss_qualifier/resources/netrid/simulation/adjacent_circular_flights_simulator.py index 76c7b72a86..63c36d9e9c 100644 --- a/monitoring/uss_qualifier/resources/netrid/simulation/adjacent_circular_flights_simulator.py +++ b/monitoring/uss_qualifier/resources/netrid/simulation/adjacent_circular_flights_simulator.py @@ -40,6 +40,12 @@ def __init__(self, config: AdjacentCircularFlightsSimulatorConfiguration) -> Non self.maxx = config.maxx self.maxy = config.maxy self.utm_zone = config.utm_zone + if config.num_flights < 1: + raise ValueError("num_flights must be at least 1") + if config.duration < 1: + raise ValueError("duration must be at least 1 second") + self.num_flights = config.num_flights + self.duration = config.duration self.altitude_agl = 50.0 @@ -182,15 +188,18 @@ def generate_flight_grid_and_path_points( self, altitude_of_ground_level_wgs_84: float ): """Generate a series of boxes (grid) within the given bounding box to have areas for different flight tracks within each box""" - # Compute the box where the flights will be created. For a the sample bounds given, over Bern, Switzerland, a division by 2 produces a cell_size of 0.0025212764739985793, a division of 3 is 0.0016808509826657196 and division by 4 0.0012606382369992897. As the cell size goes smaller more number of flights can be accomodated within the grid. For the study area bounds we build a 3x2 box for six flights by creating 3 column 2 row grid. - N_COLS = 3 - N_ROWS = 2 - cell_size_x = (self.maxx - self.minx) / (N_COLS) # create three columns - cell_size_y = (self.maxy - self.miny) / (N_ROWS) # create two rows + # Arrange cells into a compact grid. The default 6 flights preserves the + # previous 3-column by 2-row layout. + n_rows = round(self.num_flights**0.5) + n_cols = -(-self.num_flights // n_rows) + cell_size_x = (self.maxx - self.minx) / n_cols + cell_size_y = (self.maxy - self.miny) / n_rows grid_cells = [] - for u0 in range(0, N_COLS): # 3 columns + for u0 in range(0, n_cols): x0 = self.minx + (u0 * cell_size_x) - for v0 in range(0, N_ROWS): # 2 rows + for v0 in range(0, n_rows): + if len(grid_cells) >= self.num_flights: + break y0 = self.miny + (v0 * cell_size_y) x1 = x0 + cell_size_x y1 = y0 + cell_size_y @@ -356,7 +365,7 @@ def generate_aircraft_states( ) my_path_generator.generate_query_bboxes() - my_path_generator.generate_rid_state(duration=30) + my_path_generator.generate_rid_state(duration=my_path_generator.duration) flights = my_path_generator.flights result = FlightRecordCollection(flights=flights) diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/AdjacentCircularFlightsSimulatorConfiguration.json b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/AdjacentCircularFlightsSimulatorConfiguration.json index 94ad03e5e7..1e400220d1 100644 --- a/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/AdjacentCircularFlightsSimulatorConfiguration.json +++ b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/AdjacentCircularFlightsSimulatorConfiguration.json @@ -10,6 +10,10 @@ "altitude_of_ground_level_wgs_84": { "type": "integer" }, + "duration": { + "description": "Number of seconds of telemetry to generate for each flight.", + "type": "integer" + }, "flight_start_shift": { "description": "Delay generated flight starts from the reference time to spread flights over time. Expressed in seconds. Use 0 to disable.", "type": "integer" @@ -30,6 +34,10 @@ "description": "Southern edge of bounding box (degrees latitude)", "type": "number" }, + "num_flights": { + "description": "Number of adjacent circular flights to generate.", + "type": "integer" + }, "random_seed": { "description": "Pseudorandom seed that should be used, or specify None to use default Random.", "type": [