siistemata la visualizzazione della tabella dei target durante la simulazione

This commit is contained in:
VALLONGOL 2025-11-05 13:59:00 +01:00
parent e08466c77f
commit 0c347be5c8
7 changed files with 87 additions and 45 deletions

View File

@ -389,7 +389,7 @@
"targets": [
{
"target_id": 0,
"active": false,
"active": true,
"traceable": true,
"trajectory": [
{

View File

@ -70,6 +70,7 @@ class Target:
current_azimuth_deg: float = field(init=False, default=0.0)
current_altitude_ft: float = field(init=False, default=0.0)
current_velocity_fps: float = field(init=False, default=0.0)
current_vertical_velocity_fps: float = field(init=False, default=0.0)
current_heading_deg: float = field(init=False, default=0.0)
current_pitch_deg: float = field(init=False, default=0.0)
_pos_x_ft: float = field(init=False, repr=False, default=0.0)
@ -123,19 +124,22 @@ class Target:
math.degrees(math.atan2(dz, dist_2d)) if dist_2d > 0.1 else 0.0
)
self.current_velocity_fps = dist_3d / dt if dt > 0 else 0.0
self.current_vertical_velocity_fps = dz / dt if dt > 0 else 0.0
else:
(
self.current_heading_deg,
self.current_pitch_deg,
self.current_velocity_fps,
) = (0.0, 0.0, 0.0)
self.current_vertical_velocity_fps,
) = (0.0, 0.0, 0.0, 0.0)
else:
self._pos_x_ft, self._pos_y_ft, self._pos_z_ft = 0.0, 0.0, 0.0
(
self.current_velocity_fps,
self.current_vertical_velocity_fps,
self.current_heading_deg,
self.current_pitch_deg,
) = (0.0, 0.0, 0.0)
) = (0.0, 0.0, 0.0, 0.0)
self._update_current_polar_coords()
self.active = bool(self.trajectory)
@ -341,7 +345,8 @@ class Target:
final_pos[2],
final_pos[3],
)
self.current_velocity_fps = 0.0
# Do not reset velocity to 0, keep the last computed value.
self.current_vertical_velocity_fps = 0.0
if self._sim_time_s >= self._total_duration_s:
self.active = False
else:
@ -370,6 +375,7 @@ class Target:
)
if delta_time_s > 0:
self.current_velocity_fps = dist_3d / delta_time_s
self.current_vertical_velocity_fps = dz / delta_time_s
if dist_2d > 0.1:
# Restore original update_state heading computation using atan2(dx, dy).
try:

View File

@ -155,10 +155,13 @@ class SimulationEngine(threading.Thread):
if self.simulation_hub:
for target in active_targets:
# Include velocities in the state tuple for the hub
state_tuple = (
getattr(target, "_pos_x_ft", 0.0),
getattr(target, "_pos_y_ft", 0.0),
getattr(target, "_pos_z_ft", 0.0),
getattr(target, "current_velocity_fps", 0.0),
getattr(target, "current_vertical_velocity_fps", 0.0),
)
self.simulation_hub.add_simulated_state(
target.target_id, tick_timestamp, state_tuple

View File

@ -588,9 +588,6 @@ class MainView(tk.Tk):
self.ppi_widget.update_simulated_targets(display_data.get("simulated", []))
self.ppi_widget.update_real_targets(display_data.get("real", []))
# Update the new active targets table
self.simulation_controls.update_targets_table(display_data.get("simulated", []))
if self.simulation_hub:
# Update antenna sweep line
az_deg, az_ts = self.simulation_hub.get_antenna_azimuth()
@ -605,8 +602,11 @@ class MainView(tk.Tk):
self.simulation_controls.update_ownship_display(ownship_state)
else:
# Ensure display is cleared if no ownship data is present
ownship_state = {}
self.simulation_controls.update_ownship_display({})
# Update the new active targets table, providing ownship state for context
self.simulation_controls.update_targets_table(display_data.get("simulated", []), ownship_state)
if sim_is_running_now:
if self.simulation_engine and self.simulation_engine.scenario:

View File

@ -8,10 +8,10 @@ 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,
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
@ -41,12 +41,20 @@ def build_display_data(
if history.get("simulated"):
# Simulated data is generated relative to (0,0), so no transformation is needed.
last_sim_state = history["simulated"][-1]
_ts, x_ft, y_ft, z_ft = last_sim_state
# 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
@ -64,7 +72,7 @@ def build_display_data(
sim_target.current_heading_deg = float(heading)
except Exception:
pass
# Preserve active flag
sim_target.active = True
if engine and getattr(engine, "scenario", None):
@ -77,14 +85,14 @@ def build_display_data(
# --- 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
_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)
@ -100,7 +108,7 @@ def build_display_data(
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)

View File

@ -3,6 +3,7 @@
import tkinter as tk
from tkinter import ttk
from typing import Dict, Any, List
import math
from target_simulator.core.models import FPS_TO_KNOTS, Target
@ -199,18 +200,18 @@ class SimulationControls(ttk.LabelFrame):
self.targets_tree.heading("id", text="ID")
self.targets_tree.heading("lat", text="Latitude")
self.targets_tree.heading("lon", text="Longitude")
self.targets_tree.heading("alt", text="Altitude")
self.targets_tree.heading("hdg", text="Heading")
self.targets_tree.heading("gnd_speed", text="Ground Speed")
self.targets_tree.heading("vert_speed", text="Vertical Speed")
self.targets_tree.heading("alt", text="Altitude (ft)")
self.targets_tree.heading("hdg", text="Heading (°)")
self.targets_tree.heading("gnd_speed", text="Speed (kn)")
self.targets_tree.heading("vert_speed", text="Vert. Speed (ft/s)")
# Define column properties
self.targets_tree.column("id", width=40, anchor=tk.CENTER, stretch=False)
self.targets_tree.column("id", width=30, anchor=tk.CENTER, stretch=False)
self.targets_tree.column("lat", width=100, anchor=tk.W)
self.targets_tree.column("lon", width=100, anchor=tk.W)
self.targets_tree.column("alt", width=100, anchor=tk.E)
self.targets_tree.column("alt", width=80, anchor=tk.E)
self.targets_tree.column("hdg", width=80, anchor=tk.E)
self.targets_tree.column("gnd_speed", width=100, anchor=tk.E)
self.targets_tree.column("gnd_speed", width=80, anchor=tk.E)
self.targets_tree.column("vert_speed", width=100, anchor=tk.E)
# Layout Treeview and Scrollbar
@ -270,37 +271,57 @@ class SimulationControls(ttk.LabelFrame):
self.gnd_speed_var.set(f"{gnd_speed_kn:.1f} kn")
# Vertical Speed
vz_fps = state.get("velocity_z_fps", 0.0) # Assuming 'vz' is part of state
vz_fps = state.get("velocity_z_fps", 0.0)
self.vert_speed_var.set(f"{vz_fps:+.1f} ft/s")
def update_targets_table(self, targets: List[Target]):
"""Clears and repopulates the active targets table."""
# Clear existing items
def update_targets_table(self, targets: List[Target], ownship_state: Dict[str, Any]):
"""Clears and repopulates the active targets table with geographic data."""
for item in self.targets_tree.get_children():
self.targets_tree.delete(item)
# Insert new items
# Get ownship data needed for conversion
own_lat = ownship_state.get("latitude")
own_lon = ownship_state.get("longitude")
own_pos_xy_ft = ownship_state.get("position_xy_ft")
for target in sorted(targets, key=lambda t: t.target_id):
if not target.active:
continue
# This data is not directly in the Target model, so we calculate it
# based on its relative position for display purposes.
# NOTE: For "real" targets, lat/lon are not directly known without
# ownship data. The current implementation shows polar coords.
# This can be expanded later if geo-referenced data becomes available.
lat_str = f"{target.current_range_nm:.2f} NM" # Placeholder
lon_str = f"{target.current_azimuth_deg:.2f}°" # Placeholder
lat_str = "N/A"
lon_str = "N/A"
alt_str = f"{target.current_altitude_ft:.1f} ft"
hdg_str = f"{target.current_heading_deg:.2f}°"
# Calculate geographic position if possible
if own_lat is not None and own_lon is not None and own_pos_xy_ft:
target_x_ft = getattr(target, "_pos_x_ft", 0.0)
target_y_ft = getattr(target, "_pos_y_ft", 0.0)
own_x_ft, own_y_ft = own_pos_xy_ft
# Assuming ground speed is the 2D velocity for simplicity
# Delta from ownship's current position in meters
delta_east_m = (target_x_ft - own_x_ft) * 0.3048
delta_north_m = (target_y_ft - own_y_ft) * 0.3048
# Equirectangular approximation for lat/lon calculation
earth_radius_m = 6378137.0
dlat = (delta_north_m / earth_radius_m) * (180.0 / math.pi)
dlon = (delta_east_m / (earth_radius_m * math.cos(math.radians(own_lat)))) * (180.0 / math.pi)
target_lat = own_lat + dlat
target_lon = own_lon + dlon
lat_str = f"{abs(target_lat):.5f}° {'N' if target_lat >= 0 else 'S'}"
lon_str = f"{abs(target_lon):.5f}° {'E' if target_lon >= 0 else 'W'}"
alt_str = f"{target.current_altitude_ft:.1f}"
hdg_str = f"{target.current_heading_deg:.2f}"
# Use the now-correct velocity values from the Target object
gnd_speed_kn = target.current_velocity_fps * FPS_TO_KNOTS
gnd_speed_str = f"{gnd_speed_kn:.1f} kn"
gnd_speed_str = f"{gnd_speed_kn:.1f}"
vert_speed_fps = target.current_vertical_velocity_fps
vert_speed_str = f"{vert_speed_fps:+.1f}"
# Vertical speed is not directly in the simple Target model
vert_speed_str = "N/A"
values = (
target.target_id,

View File

@ -15,8 +15,12 @@
- [ ] aprire l'analisi direttamente cliccando sulla riga della tabella
- [ ] creare una procedura di allineamento tra server e client usando il comando di ping da implementare anche sul server
- [ ] funzione di sincronizzazione: è stato aggiunto al server la possibilità di gestire dei messaggi che sono di tipo SY (tag) che sono fatti per gestire il sincronismo tra client e server. In questa nuova tipologia di messaggi io invio un mio timetag che poi il server mi restituirà subito appena lo riceve, facendo così sappiamo in quanto tempo il messaggio che spedisco è arrivato al server, viene letto, e viene risposto il mio numero con anche il timetag del server. Facendo così misurando i delta posso scroprire esattamente il tempo che intercorre tra inviare un messaggio al server e ricevere una risposta. Per come è fatto il server il tempo di applicazione dei nuovi valori per i target sarà al massimo di 1 batch, che può essere variabile, ma a quel punto lo potremmo calibrare in altro modo. Con l'analisi sui sync possiamo sapere come allineare gli orologi.
# FIXME List
- [ ] sistemare la visualizzazione nella tabe simulator, per poter vedere quale scenario è stato selezionato
- [ ] sistemare l'animazione della antenna che adesso non si muove più
- [ ] rivedere la visualizzazione della combobox per scegliere lo scenario da usare.
- [ ] rivedere la visualizzazione della combobox per scegliere lo scenario da usare.
- [ ] quando è finita la simulazione i target nella tabella si devono fermare all'ultima posizione scambiata.
- [ ] quando la traiettoria si ferma deve comparire la x gialla e non deve sparire a fine simulazione