diff --git a/settings.json b/settings.json index c841be2..873e893 100644 --- a/settings.json +++ b/settings.json @@ -195,9 +195,11 @@ "trajectory": [ { "maneuver_type": "Fly to Point", + "target_velocity_fps": 506.343, + "target_heading_deg": 180.0, "duration_s": 10.0, - "target_altitude_ft": 10000.0, - "target_range_nm": 20.0, + "target_altitude_ft": 11000.0, + "target_range_nm": 10.0, "target_azimuth_deg": 0.0 } ], diff --git a/target_simulator/core/models.py b/target_simulator/core/models.py index 217db15..bee187c 100644 --- a/target_simulator/core/models.py +++ b/target_simulator/core/models.py @@ -51,7 +51,8 @@ class Waypoint: for f in fields(self): if not f.name.startswith('_') and f.name != "maneuver_type": val = getattr(self, f.name) - if val is not None: + # Forza la presenza di velocity/heading anche per Fly to Point + if val is not None or f.name in ("target_velocity_fps", "target_heading_deg"): data[f.name] = val return data diff --git a/target_simulator/gui/ppi_display.py b/target_simulator/gui/ppi_display.py index c5d34e0..64c3fe6 100644 --- a/target_simulator/gui/ppi_display.py +++ b/target_simulator/gui/ppi_display.py @@ -255,11 +255,25 @@ class PPIDisplay(ttk.Frame): self._waypoints_plot.set_data(thetas, rs) self._start_plot.set_data([thetas[0]], [rs[0]]) heading_len = 3 - heading_theta = thetas[0] - heading_r2 = rs[0] + heading_len - if hasattr(self, '_heading_artist'): - self._heading_artist.remove() - self._heading_artist, = self.ax.plot([heading_theta, heading_theta], [rs[0], heading_r2], color='red', linewidth=1, alpha=0.8) + # Usa heading del waypoint se disponibile + wp_heading = None + if waypoints and hasattr(waypoints[0], 'target_heading_deg') and waypoints[0].target_heading_deg is not None: + wp_heading = waypoints[0].target_heading_deg + if wp_heading is not None: + # L'heading è rispetto al nord (0°), quindi la freccia va da (r, azimuth) verso (r+len, azimuth+heading) + start_theta = thetas[0] + start_r = rs[0] + heading_theta = start_theta + math.radians(wp_heading) + heading_r2 = start_r + heading_len + if hasattr(self, '_heading_artist'): + self._heading_artist.remove() + self._heading_artist, = self.ax.plot([start_theta, heading_theta], [start_r, heading_r2], color='red', linewidth=1, alpha=0.8) + else: + heading_theta = thetas[0] + heading_r2 = rs[0] + heading_len + if hasattr(self, '_heading_artist'): + self._heading_artist.remove() + self._heading_artist, = self.ax.plot([thetas[0], heading_theta], [rs[0], heading_r2], color='red', linewidth=1, alpha=0.8) self.canvas.draw() self._on_range_selected() return diff --git a/target_simulator/gui/trajectory_editor_window.py b/target_simulator/gui/trajectory_editor_window.py index 8079647..6313f69 100644 --- a/target_simulator/gui/trajectory_editor_window.py +++ b/target_simulator/gui/trajectory_editor_window.py @@ -362,9 +362,16 @@ class TrajectoryEditorWindow(tk.Toplevel): self.sim_progress_var.set(progress) self.sim_time_label.config(text=f"{sim_time:.1f}s / {total_time:.1f}s") # Se la simulazione è finita (tutti i waypoint raggiunti), ferma la preview e aggiorna i tasti + # Ferma la simulazione se il tempo è finito if self.preview_engine and hasattr(self.preview_engine, 'scenario') and self.preview_engine.scenario: - if hasattr(self.preview_engine.scenario, 'is_finished') and self.preview_engine.scenario.is_finished: - self._on_preview_stop() + targets = self.preview_engine.scenario.get_all_targets() + if targets: + t = targets[0] + sim_time = getattr(t, '_sim_time_s', 0.0) + total_time = self._get_total_simulation_time() + if sim_time >= total_time: + self._on_preview_stop() + self._update_preview_button_states() finally: if self.is_preview_running.get(): self.after(GUI_QUEUE_POLL_INTERVAL_MS, self._process_preview_queue) diff --git a/target_simulator/gui/waypoint_editor_window.py b/target_simulator/gui/waypoint_editor_window.py index 23d8630..e68ad5d 100644 --- a/target_simulator/gui/waypoint_editor_window.py +++ b/target_simulator/gui/waypoint_editor_window.py @@ -58,8 +58,14 @@ class WaypointEditorWindow(tk.Toplevel): self.t_range_var.set(wp.target_range_nm or 0.0) self.t_az_var.set(wp.target_azimuth_deg or 0.0) self.t_alt_var.set(wp.target_altitude_ft or 10000.0) - self.t_vel_var.set((wp.target_velocity_fps or 0.0) * FPS_TO_KNOTS) - self.t_hdg_var.set(wp.target_heading_deg or 0.0) + if wp.target_velocity_fps is not None: + self.t_vel_var.set(wp.target_velocity_fps * FPS_TO_KNOTS) + else: + self.t_vel_var.set(0.0) + if wp.target_heading_deg is not None: + self.t_hdg_var.set(wp.target_heading_deg) + else: + self.t_hdg_var.set(0.0) # Carica i parametri dinamici se presenti self.dyn_vel_var.set((getattr(wp, 'dynamic_velocity_fps', 0.0) or 0.0) * FPS_TO_KNOTS) self.lateral_g_var.set(getattr(wp, 'lateral_g', 0.0) or 0.0) @@ -175,6 +181,8 @@ class WaypointEditorWindow(tk.Toplevel): wp.target_azimuth_deg = self.t_az_var.get() wp.target_altitude_ft = self.t_alt_var.get() wp.duration_s = self.duration_var.get() + wp.target_velocity_fps = self.t_vel_var.get() * KNOTS_TO_FPS + wp.target_heading_deg = self.t_hdg_var.get() elif m_type == ManeuverType.FLY_FOR_DURATION: wp.duration_s = self.duration_var.get() wp.target_velocity_fps = self.t_vel_var.get() * KNOTS_TO_FPS