add multiplier for simulation time in editor and main window
This commit is contained in:
parent
1094f63b4c
commit
19547f8764
@ -40,24 +40,31 @@
|
|||||||
"trajectory": [
|
"trajectory": [
|
||||||
{
|
{
|
||||||
"maneuver_type": "Fly to Point",
|
"maneuver_type": "Fly to Point",
|
||||||
"duration_s": 10.0,
|
"duration_s": 1.0,
|
||||||
"target_altitude_ft": 10000.0,
|
"target_altitude_ft": 10000.0,
|
||||||
"target_range_nm": 20.0,
|
"target_range_nm": 10.0,
|
||||||
"target_azimuth_deg": 0.0
|
"target_azimuth_deg": 0.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"maneuver_type": "Fly for Duration",
|
"maneuver_type": "Fly for Duration",
|
||||||
"target_velocity_fps": 506.343,
|
"target_velocity_fps": 1687.81,
|
||||||
"target_heading_deg": 10.0,
|
"target_heading_deg": 10.0,
|
||||||
"duration_s": 10.0,
|
"duration_s": 30.0,
|
||||||
"target_altitude_ft": 10000.0
|
"target_altitude_ft": 10000.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"maneuver_type": "Fly for Duration",
|
"maneuver_type": "Fly for Duration",
|
||||||
"target_velocity_fps": 506.343,
|
"target_velocity_fps": 506.343,
|
||||||
"target_heading_deg": 90.0,
|
"target_heading_deg": -90.0,
|
||||||
"duration_s": 10.0,
|
"duration_s": 30.0,
|
||||||
"target_altitude_ft": 10000.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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,7 @@ class SimulationEngine(threading.Thread):
|
|||||||
|
|
||||||
self.communicator = communicator
|
self.communicator = communicator
|
||||||
self.update_queue = update_queue
|
self.update_queue = update_queue
|
||||||
|
self.time_multiplier = 1.0
|
||||||
|
|
||||||
self.scenario: Optional[Scenario] = None
|
self.scenario: Optional[Scenario] = None
|
||||||
self._stop_event = threading.Event()
|
self._stop_event = threading.Event()
|
||||||
@ -43,6 +44,11 @@ class SimulationEngine(threading.Thread):
|
|||||||
self.scenario = scenario
|
self.scenario = scenario
|
||||||
self.scenario.reset_simulation()
|
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):
|
def run(self):
|
||||||
"""The main loop of the simulation thread."""
|
"""The main loop of the simulation thread."""
|
||||||
self.logger.info("Simulation engine thread started.")
|
self.logger.info("Simulation engine thread started.")
|
||||||
@ -58,9 +64,10 @@ class SimulationEngine(threading.Thread):
|
|||||||
current_time = time.monotonic()
|
current_time = time.monotonic()
|
||||||
delta_time = current_time - self._last_tick_time
|
delta_time = current_time - self._last_tick_time
|
||||||
self._last_tick_time = current_time
|
self._last_tick_time = current_time
|
||||||
|
simulated_delta_time = delta_time * self.time_multiplier
|
||||||
|
|
||||||
# --- Simulation Step (always runs) ---
|
# --- Simulation Step (always runs) ---
|
||||||
self.scenario.update_state(delta_time)
|
self.scenario.update_state(simulated_delta_time)
|
||||||
|
|
||||||
updated_targets = self.scenario.get_all_targets()
|
updated_targets = self.scenario.get_all_targets()
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,7 @@ class MainView(tk.Tk):
|
|||||||
self.simulation_engine: Optional[SimulationEngine] = None
|
self.simulation_engine: Optional[SimulationEngine] = None
|
||||||
self.gui_update_queue = Queue()
|
self.gui_update_queue = Queue()
|
||||||
self.is_simulation_running = tk.BooleanVar(value=False)
|
self.is_simulation_running = tk.BooleanVar(value=False)
|
||||||
|
self.time_multiplier = 1.0
|
||||||
|
|
||||||
# --- Window and UI Setup ---
|
# --- Window and UI Setup ---
|
||||||
self.title("Radar Target Simulator")
|
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 = ttk.Button(engine_frame, text="Reset State", command=self._on_reset_simulation)
|
||||||
self.reset_button.pack(side=tk.RIGHT, padx=5, pady=5)
|
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 ---
|
# --- TAB 3: LRU SIMULATION ---
|
||||||
lru_tab = ttk.Frame(left_notebook)
|
lru_tab = ttk.Frame(left_notebook)
|
||||||
left_notebook.add(lru_tab, text="LRU Simulation")
|
left_notebook.add(lru_tab, text="LRU Simulation")
|
||||||
@ -276,6 +283,7 @@ class MainView(tk.Tk):
|
|||||||
|
|
||||||
self.scenario.reset_simulation()
|
self.scenario.reset_simulation()
|
||||||
self.simulation_engine = SimulationEngine(self.target_communicator, self.gui_update_queue)
|
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.load_scenario(self.scenario)
|
||||||
self.simulation_engine.start()
|
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.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.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.new_button.config(state=state)
|
||||||
self.scenario_controls.save_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.edit_button.config(state=state)
|
||||||
self.target_list.tree.config(selectmode="browse" if not is_running else "none")
|
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]):
|
def _on_targets_changed(self, targets: List[Target]):
|
||||||
"""Callback executed when the target list is modified by the user."""
|
"""Callback executed when the target list is modified by the user."""
|
||||||
# 1. Update the internal scenario object
|
# 1. Update the internal scenario object
|
||||||
|
|||||||
@ -31,6 +31,7 @@ class TrajectoryEditorWindow(tk.Toplevel):
|
|||||||
self.existing_ids = existing_ids
|
self.existing_ids = existing_ids
|
||||||
self.result_target: Optional[Target] = None
|
self.result_target: Optional[Target] = None
|
||||||
self.initial_max_range = max_range_nm
|
self.initial_max_range = max_range_nm
|
||||||
|
self.time_multiplier = 1.0
|
||||||
|
|
||||||
self.waypoints: List[Waypoint] = []
|
self.waypoints: List[Waypoint] = []
|
||||||
is_editing = target_to_edit is not None
|
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 = ttk.Button(preview_controls, text="⟲ Reset", command=self._on_preview_reset)
|
||||||
self.reset_button.pack(side=tk.LEFT, padx=5)
|
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 = ttk.Frame(self)
|
||||||
button_frame.pack(fill=tk.X, padx=10, pady=(0, 10), side=tk.BOTTOM)
|
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)
|
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
|
# Passa l'intera lista di waypoint, il PPI ora sa come gestirla
|
||||||
self.ppi_preview.draw_trajectory_preview(self.waypoints)
|
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):
|
def _on_preview_play(self):
|
||||||
if self.is_preview_running.get(): return
|
if self.is_preview_running.get(): return
|
||||||
if not self.waypoints or self.waypoints[0].maneuver_type != ManeuverType.FLY_TO_POINT:
|
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)
|
preview_scenario = Scenario(); preview_scenario.add_target(preview_target)
|
||||||
|
|
||||||
self.preview_engine = SimulationEngine(communicator=None, update_queue=self.gui_update_queue)
|
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.load_scenario(preview_scenario)
|
||||||
self.preview_engine.start()
|
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.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.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")
|
self.wp_tree.config(selectmode="browse" if not is_running else "none")
|
||||||
|
|
||||||
def _on_ok(self):
|
def _on_ok(self):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user