Files
overwatch/simulation/tests/test_standby_and_trails.py
thebadking 34d4406611
Some checks failed
AERIS-10 CI / Python Lint + Tests (push) Has been cancelled
AERIS-10 CI / MCU Firmware Tests (push) Has been cancelled
AERIS-10 CI / FPGA Regression (push) Has been cancelled
AERIS-10 CI / Cross-Layer Contract Tests (push) Has been cancelled
Initial commit.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 07:49:17 +00:00

171 lines
7.4 KiB
Python

import json
import numpy as np
import pytest
from test_audit_regressions import launch_fixture, measurement, seed_stable_inbound
def detect_once(sim):
sim.radar_tracks.clear()
sim.radar_arrivals.append({**measurement(sim.time_s), 'arrival_time_s': sim.time_s})
sim._deliver_radar_tracks()
def deliver_commands(sim):
sim.time_s = max(c['delivery_time_s'] for c in sim.pending_drone_commands)+.01
sim._deliver_drone_commands()
def test_first_point_does_not_launch_and_a_stable_heading_sends_one_drone():
sim = launch_fixture()
detect_once(sim)
assert not sim.pending_drone_commands
assert not sim._early_climbs
seed_stable_inbound(sim, position=(1000., 0., -20.), velocity=(-50., 0., 0.))
detect_once(sim)
assert len(sim.pending_drone_commands) == 1
assert sim.defense_assignments['p']['defender_ids'] == ['d0']
assert not sim._early_climbs
deliver_commands(sim)
launched = next(d for d in sim.drones if d.entity_id == 'd0')
assert launched.flight_reference['path_follow']
assert launched.activated_for == 'p'
start = launched.position_ned_m.copy()
for _ in range(1500):
sim.step()
moved = launched.position_ned_m - start
assert moved[0] < -150.
assert abs(moved[0]) > abs(moved[2]) * 3.
snapshot = sim.snapshot()
row = next(r for r in snapshot['entities'] if r['id'] == 'd0')
assert row['flight_phase'] == 'assigned'
intention = np.array(row['intended_trajectory_ned_m'])
assert intention[-1, 0] < intention[0, 0] - 100.
json.dumps(snapshot, allow_nan=False)
def test_later_radar_samples_stay_on_the_same_drone():
sim = launch_fixture()
seed_stable_inbound(sim, position=(1000., 0., -20.), velocity=(-50., 0., 0.))
detect_once(sim)
committed = set(sim.threat_responses['p']['defender_ids'])
assert committed == set(sim.defense_assignments['p']['defender_ids'])
assert len(committed) == 1
sim.time_s = 5.
far = {**measurement(5.), 'position_ned_m': np.array([-8000., 8000., -20.]), 'arrival_time_s': 5.}
sim.radar_arrivals.append(far)
sim._deliver_radar_tracks()
assert set(sim.defense_assignments['p']['defender_ids']) == committed
assert set(sim.threat_responses['p']['defender_ids']) == committed
assert sum(d.entity_id in committed for d in sim.drones) == 1
def test_a_tentative_track_climbs_one_drone_to_the_attacker_altitude():
sim = launch_fixture()
position = np.array([1000., 0., -800.])
velocity = np.array([-80., 0., 0.])
sim.radar_sample_history['r'] = [{
'target_id': 'p', 'measurement_time_s': sim.time_s - 0.8 + step,
'position_ned_m': (position + velocity * (step - 0.8)).tolist(),
'lateral_sigma_m': 40.0, 'vertical_sigma_m': 25.0, 'accepted': True,
} for step in (0.0, 0.8)]
sim._request_defender('p')
climbs = [c for c in sim.pending_drone_commands if c.get('kind') == 'attacker_climb']
assert len(climbs) == 1
assert climbs[0]['drone_id'] == 'd0'
assert climbs[0]['altitude_m'] > 400.
assert 'p' not in sim.defense_assignments
deliver_commands(sim)
climber = next(d for d in sim.drones if d.entity_id == 'd0')
assert climber.activated_for is None and climber.flight_reference is None
assert climber.target_position_ned_m[2] == pytest.approx(-climbs[0]['altitude_m'])
assert all(d.target_position_ned_m is None for d in sim.drones if d.entity_id != 'd0')
seed_stable_inbound(sim, position=(1000., 0., -800.), velocity=(-80., 0., 0.))
sim._request_defender('p')
assert sim.defense_assignments['p']['defender_ids'] == ['d0']
assert len(sim.defense_assignments) == 1
assert 'p' not in sim._early_climbs
def test_total_command_loss_does_not_launch():
sim = launch_fixture(loss=1.)
seed_stable_inbound(sim, position=(1000., 0., -20.), velocity=(-50., 0., 0.))
detect_once(sim)
for t in (sim.time_s + 1., sim.time_s + 2., sim.time_s + 3.):
sim.time_s = t
sim._service_assignments()
assert not sim.pending_drone_commands
assert not sim.defense_assignments
assert all(d.available and d.activated_for is None for d in sim.drones)
def test_late_climb_packet_cannot_override_assignment():
sim = launch_fixture()
detect_once(sim)
spare = sim.drones[-1]
sim.pending_drone_commands.append({
"kind": "attacker_climb", "drone_id": spare.entity_id,
"threat_id": 'p', "altitude_m": 900.,
"hold_ned_m": spare.position_ned_m.copy(),
"computed_time_s": sim.time_s, "delivery_time_s": sim.time_s + 1.})
spare.activated_for = 'p'
spare.target_position_ned_m = np.array([123., 456., -80.])
deliver_commands(sim)
assert np.array_equal(spare.target_position_ned_m, [123., 456., -80.])
assert spare.activated_for == 'p'
def test_traces_persist_longer_than_old_six_second_window_and_samples_persist():
sim = launch_fixture()
sim.radar_detection_points['p'] = [[1000., 0., -20.]]
for _ in range(1000):
sim.step()
snapshot = sim.snapshot()
path = next(e['trajectory_ned_m'] for e in snapshot['entities'] if e['id'] == 'p')
assert path[0] == [1000., 0., -20.]
assert len(path) >= 50
assert snapshot['radar_detection_points']['p'] == [[1000., 0., -20.]]
def test_intention_preview_does_not_advance_controller_or_truth():
sim = launch_fixture()
seed_stable_inbound(sim)
sim._activate_defenders(sim.radar_tracks)
deliver_commands(sim)
d = next(d for d in sim.drones if d.flight_reference is not None)
position, target = d.position_ned_m.copy(), d.target_position_ned_m.copy()
travel, elapsed = d.flight_reference['travel_m'], d.flight_reference['elapsed_s']
path = np.array(d.intended_trajectory())
assert len(path) == 61 and np.isfinite(path).all()
# The preview runs toward the frontrun station and stays on that segment.
anchor = np.asarray(d.flight_reference['anchor'])
to_anchor = anchor[:2] - path[0, :2]
moved = path[-1, :2] - path[0, :2]
assert float(np.dot(moved, to_anchor)) > 0
cross = abs(float(to_anchor[0] * moved[1] - to_anchor[1] * moved[0]))
assert cross / max(float(np.linalg.norm(to_anchor)), 1.0) < 40
assert np.array_equal(position, d.position_ned_m)
assert np.array_equal(target, d.target_position_ned_m)
assert (travel, elapsed) == (d.flight_reference['travel_m'], d.flight_reference['elapsed_s'])
def test_a_closer_drone_takes_the_climb_and_the_first_returns_home():
sim = launch_fixture()
sim.scenario_config = {"drone_radius_km": 9.0}
far, near = sim.drones[0], sim.drones[1]
far.position_ned_m[:] = [0., 9000., 0.]
near.position_ned_m[:] = [9000., 0., 0.]
sim.remote_drone_estimates[far.entity_id] = far.position_ned_m.copy()
sim.remote_drone_estimates[near.entity_id] = near.position_ned_m.copy()
forecast = {"position": np.array([25000., 0., -1500.]),
"velocity": np.array([-140., 0., 12.]), "quality": "tentative"}
sim._early_climbs['p'] = sim._open_climb('p', far)
sim._relieve_climber('p', forecast)
assert sim._early_climbs['p']['drone_id'] == near.entity_id
assert far.available and far.activated_for is None and far.flight_reference is None
assert far.target_position_ned_m[:2] == pytest.approx([0., 9000.])
assert far.target_position_ned_m[2] == pytest.approx(0.)
assert [e['type'] for e in sim.events if e['type'] == 'drone_recalled'] == ['drone_recalled']
sim._launched_drones['p'] = ['a', 'b', 'c']
assert sim._closer_relief('p', forecast, near.entity_id) is None