S1005403_RisCC/target_simulator/gui/ppi_adapter.py

123 lines
4.9 KiB
Python

# target_simulator/gui/ppi_adapter.py
from typing import Dict, List, Optional
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 (assumed to be relative) ---
if history.get("simulated"):
# Simulated data is generated relative to (0,0), so no transformation is needed.
last_sim_state = history["simulated"][-1]
# Unpack the state tuple, now expecting velocities
if len(last_sim_state) >= 6:
_ts, x_ft, y_ft, z_ft, vel_fps, vert_vel_fps = last_sim_state[:6]
else: # Fallback for old data format
_ts, x_ft, y_ft, z_ft = last_sim_state
vel_fps, vert_vel_fps = 0.0, 0.0
sim_target = Target(target_id=tid, trajectory=[])
setattr(sim_target, "_pos_x_ft", x_ft)
setattr(sim_target, "_pos_y_ft", y_ft)
setattr(sim_target, "_pos_z_ft", z_ft)
sim_target.current_velocity_fps = vel_fps
sim_target.current_vertical_velocity_fps = vert_vel_fps
sim_target._update_current_polar_coords()
# Preserve heading information from the engine/scenario if available
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
# Preserve active flag
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:
# Calculate position relative to the ownship
rel_x_ft = abs_x_ft - ownship_pos_xy_ft[0]
rel_y_ft = abs_y_ft - ownship_pos_xy_ft[1]
# The z-coordinate (altitude) is typically absolute, but for display
# we can treat it as relative to the ownship's altitude.
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()
# Copy last-known heading if hub provides it
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}