inseriti i dati lat/lon nel file di salvataggio della simulazione
This commit is contained in:
parent
bd12263616
commit
11dc033944
@ -93,7 +93,10 @@ class AnalysisWindow(tk.Toplevel):
|
|||||||
self._update_stats_table(analysis_results[sel_id])
|
self._update_stats_table(analysis_results[sel_id])
|
||||||
self._update_plot(sel_id)
|
self._update_plot(sel_id)
|
||||||
else:
|
else:
|
||||||
self._clear_views()
|
# Provide diagnostic information when analysis cannot be
|
||||||
|
# produced for the selected target (common cause: no
|
||||||
|
# overlapping timestamps between simulated and real samples).
|
||||||
|
self._show_insufficient_data_info(sel_id)
|
||||||
|
|
||||||
def _create_widgets(self):
|
def _create_widgets(self):
|
||||||
main_pane = ttk.PanedWindow(self, orient=tk.VERTICAL)
|
main_pane = ttk.PanedWindow(self, orient=tk.VERTICAL)
|
||||||
@ -305,6 +308,74 @@ class AnalysisWindow(tk.Toplevel):
|
|||||||
self.ax.autoscale_view()
|
self.ax.autoscale_view()
|
||||||
self.canvas.draw_idle()
|
self.canvas.draw_idle()
|
||||||
|
|
||||||
|
def _show_insufficient_data_info(self, target_id: int):
|
||||||
|
"""Display helpful information in the stats table when a target
|
||||||
|
cannot be analyzed (for example because simulated and real time
|
||||||
|
ranges do not overlap). This avoids an empty UI and gives the
|
||||||
|
user actionable context.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Clear previous contents
|
||||||
|
self.stats_tree.delete(*self.stats_tree.get_children())
|
||||||
|
|
||||||
|
history = self._hub.get_target_history(target_id)
|
||||||
|
if history is None:
|
||||||
|
self.stats_tree.insert("", "end", values=("Info", "Target not found", "", ""))
|
||||||
|
self._clear_views()
|
||||||
|
return
|
||||||
|
|
||||||
|
sim_times = [s[0] for s in history.get("simulated", [])]
|
||||||
|
real_times = [r[0] for r in history.get("real", [])]
|
||||||
|
|
||||||
|
sim_count = len(sim_times)
|
||||||
|
real_count = len(real_times)
|
||||||
|
|
||||||
|
sim_range = (
|
||||||
|
(min(sim_times), max(sim_times)) if sim_times else (None, None)
|
||||||
|
)
|
||||||
|
real_range = (
|
||||||
|
(min(real_times), max(real_times)) if real_times else (None, None)
|
||||||
|
)
|
||||||
|
|
||||||
|
# populate the small table with human-readable diagnostic rows
|
||||||
|
self.stats_tree.insert(
|
||||||
|
"",
|
||||||
|
"end",
|
||||||
|
values=("Info", f"Target {target_id}", "", ""),
|
||||||
|
)
|
||||||
|
self.stats_tree.insert(
|
||||||
|
"",
|
||||||
|
"end",
|
||||||
|
values=("Sim samples", str(sim_count), "", ""),
|
||||||
|
)
|
||||||
|
self.stats_tree.insert(
|
||||||
|
"",
|
||||||
|
"end",
|
||||||
|
values=("Sim time range", f"{sim_range[0]} -> {sim_range[1]}", "", ""),
|
||||||
|
)
|
||||||
|
self.stats_tree.insert(
|
||||||
|
"",
|
||||||
|
"end",
|
||||||
|
values=("Real samples", str(real_count), "", ""),
|
||||||
|
)
|
||||||
|
self.stats_tree.insert(
|
||||||
|
"",
|
||||||
|
"end",
|
||||||
|
values=("Real time range", f"{real_range[0]} -> {real_range[1]}", "", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
# keep plot cleared
|
||||||
|
self.line_x.set_data([], [])
|
||||||
|
self.line_y.set_data([], [])
|
||||||
|
self.line_z.set_data([], [])
|
||||||
|
self.ax.relim()
|
||||||
|
self.ax.autoscale_view()
|
||||||
|
self.canvas.draw_idle()
|
||||||
|
except Exception:
|
||||||
|
# Fail silently to avoid breaking the analysis window; show
|
||||||
|
# the cleared view as a fallback.
|
||||||
|
self._clear_views()
|
||||||
|
|
||||||
def _on_close(self):
|
def _on_close(self):
|
||||||
self._active = False
|
self._active = False
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|||||||
@ -85,6 +85,10 @@ class PPIDisplay(ttk.Frame):
|
|||||||
"next_ts": None,
|
"next_ts": None,
|
||||||
"animating": False,
|
"animating": False,
|
||||||
"tick_ms": 33,
|
"tick_ms": 33,
|
||||||
|
# When no new antenna timestamps arrive, perform a small
|
||||||
|
# continuous idle sweep so the UI remains visually active.
|
||||||
|
# Degrees per second to rotate while idle (fallback).
|
||||||
|
"idle_rotation_deg_per_s": 30.0,
|
||||||
}
|
}
|
||||||
self._antenna_line_artist: mpl.lines.Line2D | None = None
|
self._antenna_line_artist: mpl.lines.Line2D | None = None
|
||||||
|
|
||||||
@ -533,6 +537,23 @@ class PPIDisplay(ttk.Frame):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if next_ts <= last_ts:
|
if next_ts <= last_ts:
|
||||||
|
# No upcoming timestamp to interpolate towards. Instead
|
||||||
|
# perform a small idle rotation so the antenna appears to
|
||||||
|
# sweep even when updates are sparse or timestamps do not
|
||||||
|
# advance. This keeps the UI lively and avoids a static
|
||||||
|
# antenna when the data source doesn't provide frequent
|
||||||
|
# azimuth samples.
|
||||||
|
try:
|
||||||
|
idle_speed = float(st.get("idle_rotation_deg_per_s", 30.0))
|
||||||
|
# rotate since last_ts using idle speed
|
||||||
|
dt = max(0.0, now - (last_ts or now))
|
||||||
|
cur = (last_az + idle_speed * dt) % 360
|
||||||
|
# update last_ts so subsequent steps continue smoothly
|
||||||
|
st["last_ts"] = now
|
||||||
|
st["last_az_deg"] = cur
|
||||||
|
# keep animating until explicit stop
|
||||||
|
st["animating"] = True
|
||||||
|
except Exception:
|
||||||
cur = next_az
|
cur = next_az
|
||||||
st["animating"] = False
|
st["animating"] = False
|
||||||
else:
|
else:
|
||||||
|
|||||||
14
tools/scan_archives.py
Normal file
14
tools/scan_archives.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import json, os
|
||||||
|
folder = r'c:\src\____GitProjects\target_simulator\archive_simulations'
|
||||||
|
for fn in os.listdir(folder):
|
||||||
|
if not fn.endswith('.json'):
|
||||||
|
continue
|
||||||
|
p = os.path.join(folder, fn)
|
||||||
|
try:
|
||||||
|
with open(p,'r',encoding='utf-8') as f:
|
||||||
|
j = json.load(f)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
n = len(j.get('simulation_results', {}))
|
||||||
|
if n >= 3:
|
||||||
|
print(fn, n)
|
||||||
Loading…
Reference in New Issue
Block a user