add multiplier for simulation time in editor and main window

This commit is contained in:
VALLONGOL 2025-10-09 20:37:32 +02:00
parent 1094f63b4c
commit 19547f8764
4 changed files with 60 additions and 7 deletions

View File

@ -40,24 +40,31 @@
"trajectory": [
{
"maneuver_type": "Fly to Point",
"duration_s": 10.0,
"duration_s": 1.0,
"target_altitude_ft": 10000.0,
"target_range_nm": 20.0,
"target_range_nm": 10.0,
"target_azimuth_deg": 0.0
},
{
"maneuver_type": "Fly for Duration",
"target_velocity_fps": 506.343,
"target_velocity_fps": 1687.81,
"target_heading_deg": 10.0,
"duration_s": 10.0,
"duration_s": 30.0,
"target_altitude_ft": 10000.0
},
{
"maneuver_type": "Fly for Duration",
"target_velocity_fps": 506.343,
"target_heading_deg": 90.0,
"duration_s": 10.0,
"target_heading_deg": -90.0,
"duration_s": 30.0,
"target_altitude_ft": 10000.0
},
{
"maneuver_type": "Fly to Point",
"duration_s": 30.0,
"target_altitude_ft": 10000.0,
"target_range_nm": 40.0,
"target_azimuth_deg": 0.0
}
]
}

View File

@ -31,6 +31,7 @@ class SimulationEngine(threading.Thread):
self.communicator = communicator
self.update_queue = update_queue
self.time_multiplier = 1.0
self.scenario: Optional[Scenario] = None
self._stop_event = threading.Event()
@ -43,6 +44,11 @@ class SimulationEngine(threading.Thread):
self.scenario = scenario
self.scenario.reset_simulation()
def set_time_multiplier(self, multiplier: float):
"""Sets the simulation time speed multiplier."""
self.logger.info(f"Setting simulation time multiplier to {multiplier}x")
self.time_multiplier = multiplier
def run(self):
"""The main loop of the simulation thread."""
self.logger.info("Simulation engine thread started.")
@ -58,9 +64,10 @@ class SimulationEngine(threading.Thread):
current_time = time.monotonic()
delta_time = current_time - self._last_tick_time
self._last_tick_time = current_time
simulated_delta_time = delta_time * self.time_multiplier
# --- Simulation Step (always runs) ---
self.scenario.update_state(delta_time)
self.scenario.update_state(simulated_delta_time)
updated_targets = self.scenario.get_all_targets()

View File

@ -51,6 +51,7 @@ class MainView(tk.Tk):
self.simulation_engine: Optional[SimulationEngine] = None
self.gui_update_queue = Queue()
self.is_simulation_running = tk.BooleanVar(value=False)
self.time_multiplier = 1.0
# --- Window and UI Setup ---
self.title("Radar Target Simulator")
@ -140,6 +141,12 @@ class MainView(tk.Tk):
self.reset_button = ttk.Button(engine_frame, text="Reset State", command=self._on_reset_simulation)
self.reset_button.pack(side=tk.RIGHT, padx=5, pady=5)
ttk.Label(engine_frame, text="Speed:").pack(side=tk.LEFT, padx=(10, 2), pady=5)
self.time_multiplier_var = tk.StringVar(value="1x")
self.multiplier_combo = ttk.Combobox(engine_frame, textvariable=self.time_multiplier_var, values=["1x", "2x", "4x", "10x"], state="readonly", width=4)
self.multiplier_combo.pack(side=tk.LEFT, padx=(0, 5), pady=5)
self.multiplier_combo.bind("<<ComboboxSelected>>", self._on_time_multiplier_changed)
# --- TAB 3: LRU SIMULATION ---
lru_tab = ttk.Frame(left_notebook)
left_notebook.add(lru_tab, text="LRU Simulation")
@ -276,6 +283,7 @@ class MainView(tk.Tk):
self.scenario.reset_simulation()
self.simulation_engine = SimulationEngine(self.target_communicator, self.gui_update_queue)
self.simulation_engine.set_time_multiplier(self.time_multiplier)
self.simulation_engine.load_scenario(self.scenario)
self.simulation_engine.start()
@ -314,6 +322,7 @@ class MainView(tk.Tk):
self.start_button.config(state=tk.DISABLED if is_running else tk.NORMAL)
self.stop_button.config(state=tk.NORMAL if is_running else tk.DISABLED)
self.multiplier_combo.config(state="readonly" if not is_running else tk.DISABLED)
self.scenario_controls.new_button.config(state=state)
self.scenario_controls.save_button.config(state=state)
@ -326,6 +335,17 @@ class MainView(tk.Tk):
self.target_list.edit_button.config(state=state)
self.target_list.tree.config(selectmode="browse" if not is_running else "none")
def _on_time_multiplier_changed(self, event=None):
"""Handles changes to the time multiplier selection."""
try:
multiplier_str = self.time_multiplier_var.get().replace('x', '')
self.time_multiplier = float(multiplier_str)
if self.simulation_engine and self.simulation_engine.is_running():
self.simulation_engine.set_time_multiplier(self.time_multiplier)
except ValueError:
self.logger.error(f"Invalid time multiplier value: {self.time_multiplier_var.get()}")
self.time_multiplier = 1.0
def _on_targets_changed(self, targets: List[Target]):
"""Callback executed when the target list is modified by the user."""
# 1. Update the internal scenario object

View File

@ -31,6 +31,7 @@ class TrajectoryEditorWindow(tk.Toplevel):
self.existing_ids = existing_ids
self.result_target: Optional[Target] = None
self.initial_max_range = max_range_nm
self.time_multiplier = 1.0
self.waypoints: List[Waypoint] = []
is_editing = target_to_edit is not None
@ -89,6 +90,12 @@ class TrajectoryEditorWindow(tk.Toplevel):
self.reset_button = ttk.Button(preview_controls, text="⟲ Reset", command=self._on_preview_reset)
self.reset_button.pack(side=tk.LEFT, padx=5)
ttk.Label(preview_controls, text="Speed:").pack(side=tk.LEFT, padx=(10, 2), pady=5)
self.time_multiplier_var = tk.StringVar(value="1x")
self.multiplier_combo = ttk.Combobox(preview_controls, textvariable=self.time_multiplier_var, values=["1x", "2x", "4x", "10x"], state="readonly", width=4)
self.multiplier_combo.pack(side=tk.LEFT, padx=(0, 5), pady=5)
self.multiplier_combo.bind("<<ComboboxSelected>>", self._on_time_multiplier_changed)
button_frame = ttk.Frame(self)
button_frame.pack(fill=tk.X, padx=10, pady=(0, 10), side=tk.BOTTOM)
ttk.Button(button_frame, text="Cancel", command=self._on_cancel).pack(side=tk.RIGHT)
@ -181,6 +188,16 @@ class TrajectoryEditorWindow(tk.Toplevel):
# Passa l'intera lista di waypoint, il PPI ora sa come gestirla
self.ppi_preview.draw_trajectory_preview(self.waypoints)
def _on_time_multiplier_changed(self, event=None):
"""Handles changes to the time multiplier selection."""
try:
multiplier_str = self.time_multiplier_var.get().replace('x', '')
self.time_multiplier = float(multiplier_str)
if self.preview_engine and self.preview_engine.is_running():
self.preview_engine.set_time_multiplier(self.time_multiplier)
except ValueError:
self.time_multiplier = 1.0
def _on_preview_play(self):
if self.is_preview_running.get(): return
if not self.waypoints or self.waypoints[0].maneuver_type != ManeuverType.FLY_TO_POINT:
@ -194,6 +211,7 @@ class TrajectoryEditorWindow(tk.Toplevel):
preview_scenario = Scenario(); preview_scenario.add_target(preview_target)
self.preview_engine = SimulationEngine(communicator=None, update_queue=self.gui_update_queue)
self.preview_engine.set_time_multiplier(self.time_multiplier)
self.preview_engine.load_scenario(preview_scenario)
self.preview_engine.start()
@ -226,6 +244,7 @@ class TrajectoryEditorWindow(tk.Toplevel):
self.play_button.config(state=tk.DISABLED if is_running else tk.NORMAL)
self.stop_button.config(state=tk.NORMAL if is_running else tk.DISABLED)
self.multiplier_combo.config(state="readonly" if not is_running else tk.DISABLED)
self.wp_tree.config(selectmode="browse" if not is_running else "none")
def _on_ok(self):