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_plot(sel_id)
|
||||
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):
|
||||
main_pane = ttk.PanedWindow(self, orient=tk.VERTICAL)
|
||||
@ -305,6 +308,74 @@ class AnalysisWindow(tk.Toplevel):
|
||||
self.ax.autoscale_view()
|
||||
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):
|
||||
self._active = False
|
||||
self.destroy()
|
||||
|
||||
@ -85,6 +85,10 @@ class PPIDisplay(ttk.Frame):
|
||||
"next_ts": None,
|
||||
"animating": False,
|
||||
"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
|
||||
|
||||
@ -533,8 +537,25 @@ class PPIDisplay(ttk.Frame):
|
||||
return
|
||||
|
||||
if next_ts <= last_ts:
|
||||
cur = next_az
|
||||
st["animating"] = False
|
||||
# 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
|
||||
st["animating"] = False
|
||||
else:
|
||||
frac = max(0.0, min(1.0, (now - last_ts) / (next_ts - last_ts)))
|
||||
diff = ((next_az - last_az + 180) % 360) - 180
|
||||
|
||||
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