S1005403_RisCC/target_simulator/gui/ppi_adapter.py

139 lines
5.4 KiB
Python

"""PPI adapter utilities.
This module converts absolute states stored in the SimulationStateHub into
lightweight Target objects positioned relative to ownship and suitable for
consumption by the :class:`target_simulator.gui.ppi_display.PPIDisplay`.
Public API:
- build_display_data(simulation_hub, ...): returns a dict with keys
``'simulated'`` and ``'real'`` containing lists of targets for display.
"""
from typing import Dict, List, Optional, TYPE_CHECKING
if TYPE_CHECKING:
# Avoid runtime imports; enable type checking for forward references
from target_simulator.core.simulation_engine import SimulationEngine
from target_simulator.gui.ppi_display import PPIDisplay
from target_simulator.core.models import Scenario
from logging import Logger
import math
from target_simulator.core.models import Target
from target_simulator.analysis.simulation_state_hub import SimulationStateHub
def build_display_data(
simulation_hub: SimulationStateHub,
scenario: Optional["Scenario"] = None,
engine: Optional["SimulationEngine"] = None,
ppi_widget: Optional["PPIDisplay"] = None,
logger: Optional["Logger"] = None,
) -> Dict[str, List[Target]]:
"""
Builds PPI display data from the simulation hub, converting absolute
'real' coordinates to relative coordinates based on the ownship's position.
Returns a dict with keys 'simulated' and 'real' containing lightweight
Target objects suitable for passing to PPIDisplay.
"""
simulated_targets_for_ppi: List[Target] = []
real_targets_for_ppi: List[Target] = []
if not simulation_hub:
return {"simulated": [], "real": []}
# Get ownship state for coordinate transformation
ownship_state = simulation_hub.get_ownship_state()
ownship_pos_xy_ft = ownship_state.get("position_xy_ft")
target_ids = simulation_hub.get_all_target_ids()
for tid in target_ids:
history = simulation_hub.get_target_history(tid)
if not history:
continue
# --- Process Simulated Data (transforming from absolute to relative) ---
if history.get("simulated"):
last_sim_state = history["simulated"][-1]
if len(last_sim_state) >= 6:
_ts, x_ft, y_ft, z_ft, vel_fps, vert_vel_fps = last_sim_state[:6]
else:
_ts, x_ft, y_ft, z_ft = last_sim_state
vel_fps, vert_vel_fps = 0.0, 0.0
# Convert absolute simulation coordinates to relative-to-ownship coordinates
rel_x_ft, rel_y_ft = x_ft, y_ft
if ownship_pos_xy_ft:
rel_x_ft = x_ft - ownship_pos_xy_ft[0]
rel_y_ft = y_ft - ownship_pos_xy_ft[1]
sim_target = Target(target_id=tid, trajectory=[])
setattr(sim_target, "_pos_x_ft", rel_x_ft)
setattr(sim_target, "_pos_y_ft", rel_y_ft)
setattr(
sim_target, "_pos_z_ft", z_ft
) # Altitude is handled relative to ground
sim_target.current_velocity_fps = vel_fps
sim_target.current_vertical_velocity_fps = vert_vel_fps
sim_target._update_current_polar_coords()
try:
heading = None
if engine and getattr(engine, "scenario", None):
t = engine.scenario.get_target(tid)
if t:
heading = getattr(t, "current_heading_deg", None)
if heading is None and scenario:
t2 = scenario.get_target(tid)
if t2:
heading = getattr(t2, "current_heading_deg", None)
if heading is not None:
sim_target.current_heading_deg = float(heading)
except Exception:
pass
sim_target.active = True
if engine and getattr(engine, "scenario", None):
t_engine = engine.scenario.get_target(tid)
if t_engine is not None:
sim_target.active = bool(getattr(t_engine, "active", True))
simulated_targets_for_ppi.append(sim_target)
# --- Process Real Data (transforming from absolute to relative) ---
if history.get("real"):
last_real_state = history["real"][-1]
_ts, abs_x_ft, abs_y_ft, abs_z_ft = last_real_state[:4]
rel_x_ft, rel_y_ft = abs_x_ft, abs_y_ft
if ownship_pos_xy_ft:
rel_x_ft = abs_x_ft - ownship_pos_xy_ft[0]
rel_y_ft = abs_y_ft - ownship_pos_xy_ft[1]
ownship_alt_ft = ownship_state.get("altitude_ft", 0.0)
rel_z_ft = abs_z_ft - ownship_alt_ft
real_target = Target(target_id=tid, trajectory=[])
setattr(real_target, "_pos_x_ft", rel_x_ft)
setattr(real_target, "_pos_y_ft", rel_y_ft)
setattr(real_target, "_pos_z_ft", rel_z_ft)
real_target._update_current_polar_coords()
hdg = simulation_hub.get_real_heading(tid)
if hdg is not None:
real_target.current_heading_deg = float(hdg) % 360
real_target.active = True
real_targets_for_ppi.append(real_target)
if logger:
logger.debug(
"PPI Adapter: Built display data (simulated=%d, real=%d)",
len(simulated_targets_for_ppi),
len(real_targets_for_ppi),
)
return {"simulated": simulated_targets_for_ppi, "real": real_targets_for_ppi}