sistemata la rotazione della mappa ppi seguendo il platform azimuth fornito dallo scenario, selezionando heading-up
This commit is contained in:
parent
e72aa8314e
commit
7c7bbe57ba
@ -640,9 +640,14 @@ class MainView(tk.Tk):
|
|||||||
|
|
||||||
if self.simulation_hub:
|
if self.simulation_hub:
|
||||||
# Update antenna sweep line
|
# Update antenna sweep line
|
||||||
az_deg, az_ts = self.simulation_hub.get_antenna_azimuth()
|
if self.ppi_widget.animate_antenna_var.get():
|
||||||
if az_deg is not None:
|
az_deg, _ = self.simulation_hub.get_antenna_azimuth()
|
||||||
self.ppi_widget.update_antenna_azimuth(az_deg, timestamp=az_ts)
|
if az_deg is not None:
|
||||||
|
self.ppi_widget.render_antenna_line(az_deg)
|
||||||
|
else:
|
||||||
|
self.ppi_widget.render_antenna_line(None) # Hide if no data
|
||||||
|
else:
|
||||||
|
self.ppi_widget.render_antenna_line(None) # Hide if animation is off
|
||||||
|
|
||||||
# Update ownship state for both PPI orientation and status display
|
# Update ownship state for both PPI orientation and status display
|
||||||
ownship_state = self.simulation_hub.get_ownship_state()
|
ownship_state = self.simulation_hub.get_ownship_state()
|
||||||
|
|||||||
@ -335,11 +335,18 @@ class DebugPayloadRouter:
|
|||||||
|
|
||||||
# --- Propagate antenna azimuth to hub ---
|
# --- Propagate antenna azimuth to hub ---
|
||||||
if self._hub:
|
if self._hub:
|
||||||
plat_az_rad = scenario_dict.get(
|
# Get platform heading and relative antenna sweep
|
||||||
"ant_nav_az", scenario_dict.get("platform_azimuth")
|
heading_rad = scenario_dict.get("true_heading", scenario_dict.get("platform_azimuth"))
|
||||||
)
|
sweep_rad = scenario_dict.get("ant_nav_az")
|
||||||
if plat_az_rad is not None:
|
|
||||||
az_deg = math.degrees(float(plat_az_rad))
|
total_az_rad = None
|
||||||
|
if heading_rad is not None:
|
||||||
|
total_az_rad = float(heading_rad)
|
||||||
|
if sweep_rad is not None:
|
||||||
|
total_az_rad += float(sweep_rad)
|
||||||
|
|
||||||
|
if total_az_rad is not None:
|
||||||
|
az_deg = math.degrees(total_az_rad)
|
||||||
self._hub.set_antenna_azimuth(az_deg, timestamp=time.monotonic())
|
self._hub.set_antenna_azimuth(az_deg, timestamp=time.monotonic())
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -383,14 +390,23 @@ class DebugPayloadRouter:
|
|||||||
return v.get(key)
|
return v.get(key)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
plat_az = _find_scenario_field(obj, "ant_nav_az") or _find_scenario_field(
|
# Find platform heading and relative antenna sweep
|
||||||
|
heading_val = _find_scenario_field(obj, "true_heading") or _find_scenario_field(
|
||||||
obj, "platform_azimuth"
|
obj, "platform_azimuth"
|
||||||
)
|
)
|
||||||
if plat_az is not None:
|
sweep_val = _find_scenario_field(obj, "ant_nav_az")
|
||||||
|
|
||||||
|
total_az_val = None
|
||||||
|
if heading_val is not None:
|
||||||
|
total_az_val = float(heading_val)
|
||||||
|
if sweep_val is not None:
|
||||||
|
total_az_val += float(sweep_val)
|
||||||
|
|
||||||
|
if total_az_val is not None:
|
||||||
try:
|
try:
|
||||||
# Values may be in radians or degrees; if small absolute
|
# Values may be in radians or degrees; if small absolute
|
||||||
# value (< 2*pi) assume radians and convert.
|
# value (< 2*pi) assume radians and convert.
|
||||||
val = float(plat_az)
|
val = float(total_az_val)
|
||||||
if abs(val) <= (2 * math.pi + 0.01):
|
if abs(val) <= (2 * math.pi + 0.01):
|
||||||
az_deg = math.degrees(val)
|
az_deg = math.degrees(val)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import collections
|
|||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||||
import matplotlib as mpl
|
import matplotlib as mpl
|
||||||
from typing import List, Dict
|
from typing import List, Dict, Optional
|
||||||
|
|
||||||
from target_simulator.core.models import Target, Waypoint, ManeuverType, NM_TO_FT
|
from target_simulator.core.models import Target, Waypoint, ManeuverType, NM_TO_FT
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
self.display_mode_var = tk.StringVar(value="North-Up")
|
self.display_mode_var = tk.StringVar(value="North-Up")
|
||||||
|
|
||||||
self.canvas = None
|
self.canvas = None
|
||||||
self._ownship_artist: mpl.lines.Line2D | None = None
|
self._ownship_artist: Optional[mpl.lines.Line2D] = None
|
||||||
self.ownship_heading_deg = 0.0
|
self.ownship_heading_deg = 0.0
|
||||||
|
|
||||||
self._create_controls()
|
self._create_controls()
|
||||||
@ -78,19 +78,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
self._last_update_summary_time = time.monotonic()
|
self._last_update_summary_time = time.monotonic()
|
||||||
self._update_summary_interval_s = 1.0
|
self._update_summary_interval_s = 1.0
|
||||||
|
|
||||||
self._antenna_state = {
|
self._antenna_line_artist: Optional[mpl.lines.Line2D] = None
|
||||||
"last_az_deg": None,
|
|
||||||
"last_ts": None,
|
|
||||||
"next_az_deg": None,
|
|
||||||
"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
|
|
||||||
|
|
||||||
def _on_display_options_changed(self, *args):
|
def _on_display_options_changed(self, *args):
|
||||||
self.clear_all_targets()
|
self.clear_all_targets()
|
||||||
@ -134,7 +122,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
radar_frame,
|
radar_frame,
|
||||||
text="Animate Antenna",
|
text="Animate Antenna",
|
||||||
variable=self.animate_antenna_var,
|
variable=self.animate_antenna_var,
|
||||||
command=self._on_antenna_animate_changed,
|
command=self._force_redraw,
|
||||||
).pack(anchor="w", pady=(4, 0))
|
).pack(anchor="w", pady=(4, 0))
|
||||||
|
|
||||||
# Section 2: Display Mode
|
# Section 2: Display Mode
|
||||||
@ -209,17 +197,25 @@ class PPIDisplay(ttk.Frame):
|
|||||||
real_sw.grid(row=2, column=0, padx=(0, 4))
|
real_sw.grid(row=2, column=0, padx=(0, 4))
|
||||||
ttk.Label(legend_frame, text="Real").grid(row=2, column=1, sticky="w")
|
ttk.Label(legend_frame, text="Real").grid(row=2, column=1, sticky="w")
|
||||||
|
|
||||||
|
def _force_redraw(self):
|
||||||
|
if self.canvas:
|
||||||
|
self.canvas.draw_idle()
|
||||||
|
|
||||||
def _create_plot(self):
|
def _create_plot(self):
|
||||||
fig = Figure(figsize=(5, 5), dpi=100, facecolor="#3E3E3E")
|
fig = Figure(figsize=(5, 5), dpi=100, facecolor="#3E3E3E")
|
||||||
fig.subplots_adjust(left=0.05, right=0.95, top=0.9, bottom=0.05)
|
fig.subplots_adjust(left=0.05, right=0.95, top=0.9, bottom=0.05)
|
||||||
self.ax = fig.add_subplot(111, projection="polar", facecolor="#2E2E2E")
|
self.ax = fig.add_subplot(111, projection="polar", facecolor="#2E2E2E")
|
||||||
self.ax.set_theta_zero_location("N")
|
self.ax.set_theta_zero_location("N")
|
||||||
# self.ax.set_theta_direction(-1) # Clockwise <- RIGA RIMOSSA
|
# Default direction is CCW (antiorario), which is correct for our model
|
||||||
|
# self.ax.set_theta_direction(-1) # This was the old incorrect way
|
||||||
self.ax.set_rlabel_position(90)
|
self.ax.set_rlabel_position(90)
|
||||||
self.ax.set_ylim(0, self.range_var.get())
|
self.ax.set_ylim(0, self.range_var.get())
|
||||||
|
|
||||||
|
# Correct labels for CCW positive convention
|
||||||
angles_deg = np.arange(0, 360, 30)
|
angles_deg = np.arange(0, 360, 30)
|
||||||
labels = [f"{a}°" for a in angles_deg]
|
labels = [f"{(a - 360) if a > 180 else a}°" for a in angles_deg]
|
||||||
self.ax.set_thetagrids(angles_deg, labels)
|
self.ax.set_thetagrids(angles_deg, labels)
|
||||||
|
|
||||||
self.ax.tick_params(axis="x", colors="white", labelsize=8)
|
self.ax.tick_params(axis="x", colors="white", labelsize=8)
|
||||||
self.ax.tick_params(axis="y", colors="white", labelsize=8)
|
self.ax.tick_params(axis="y", colors="white", labelsize=8)
|
||||||
self.ax.grid(color="white", linestyle="--", linewidth=0.5, alpha=0.5)
|
self.ax.grid(color="white", linestyle="--", linewidth=0.5, alpha=0.5)
|
||||||
@ -238,20 +234,26 @@ class PPIDisplay(ttk.Frame):
|
|||||||
)
|
)
|
||||||
self._ownship_artist.set_visible(True)
|
self._ownship_artist.set_visible(True)
|
||||||
|
|
||||||
(self._path_plot,) = self.ax.plot([], [], "g--", linewidth=1.5)
|
(self._path_plot,) = self.ax.plot([], [], "g--", linewidth=1.5, zorder=3)
|
||||||
(self._start_plot,) = self.ax.plot([], [], "go", markersize=8)
|
(self._start_plot,) = self.ax.plot([], [], "go", markersize=8, zorder=3)
|
||||||
(self._waypoints_plot,) = self.ax.plot([], [], "y+", markersize=10, mew=2)
|
(self._waypoints_plot,) = self.ax.plot([], [], "y+", markersize=10, mew=2, zorder=3)
|
||||||
self.preview_artists = [self._path_plot, self._start_plot, self._waypoints_plot]
|
self.preview_artists = [self._path_plot, self._start_plot, self._waypoints_plot]
|
||||||
|
|
||||||
limit_rad = np.deg2rad(self.scan_limit_deg)
|
limit_rad = np.deg2rad(self.scan_limit_deg)
|
||||||
(self._scan_line_1,) = self.ax.plot(
|
(self._scan_line_1,) = self.ax.plot(
|
||||||
[limit_rad, limit_rad], [0, self.max_range], "y--", linewidth=1
|
[limit_rad, limit_rad], [0, self.max_range], "y--", linewidth=1, zorder=1
|
||||||
)
|
)
|
||||||
(self._scan_line_2,) = self.ax.plot(
|
(self._scan_line_2,) = self.ax.plot(
|
||||||
[-limit_rad, -limit_rad], [0, self.max_range], "y--", linewidth=1
|
[-limit_rad, -limit_rad], [0, self.max_range], "y--", linewidth=1, zorder=1
|
||||||
)
|
)
|
||||||
(self._antenna_line_artist,) = self.ax.plot(
|
(self._antenna_line_artist,) = self.ax.plot(
|
||||||
[], [], color="lightgray", linestyle="--", linewidth=1.2, alpha=0.85
|
[],
|
||||||
|
[],
|
||||||
|
color="lightgray",
|
||||||
|
linestyle="--",
|
||||||
|
linewidth=1.2,
|
||||||
|
alpha=0.85,
|
||||||
|
zorder=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.canvas = FigureCanvasTkAgg(fig, master=self)
|
self.canvas = FigureCanvasTkAgg(fig, master=self)
|
||||||
@ -379,6 +381,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
if show_points or show_trail:
|
if show_points or show_trail:
|
||||||
for t in new_data:
|
for t in new_data:
|
||||||
if t.active:
|
if t.active:
|
||||||
|
# No negation needed anymore
|
||||||
pos = (np.deg2rad(t.current_azimuth_deg), t.current_range_nm)
|
pos = (np.deg2rad(t.current_azimuth_deg), t.current_range_nm)
|
||||||
trail_data[t.target_id].append(pos)
|
trail_data[t.target_id].append(pos)
|
||||||
|
|
||||||
@ -410,7 +413,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
r_nm = target.current_range_nm
|
r_nm = target.current_range_nm
|
||||||
theta_rad_plot = np.deg2rad(target.current_azimuth_deg)
|
theta_rad_plot = np.deg2rad(target.current_azimuth_deg)
|
||||||
(dot,) = self.ax.plot(
|
(dot,) = self.ax.plot(
|
||||||
theta_rad_plot, r_nm, "o", markersize=6, color=color, alpha=0.6
|
theta_rad_plot, r_nm, "o", markersize=6, color=color, alpha=0.6, zorder=5
|
||||||
)
|
)
|
||||||
artist_list.append(dot)
|
artist_list.append(dot)
|
||||||
(x_mark,) = self.ax.plot(
|
(x_mark,) = self.ax.plot(
|
||||||
@ -421,6 +424,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
markersize=8,
|
markersize=8,
|
||||||
markeredgewidth=0.9,
|
markeredgewidth=0.9,
|
||||||
linestyle="",
|
linestyle="",
|
||||||
|
zorder=6
|
||||||
)
|
)
|
||||||
label_artist_list.append(x_mark)
|
label_artist_list.append(x_mark)
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -441,7 +445,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
theta_rad_plot = np.deg2rad(target.current_azimuth_deg)
|
theta_rad_plot = np.deg2rad(target.current_azimuth_deg)
|
||||||
|
|
||||||
(dot,) = self.ax.plot(
|
(dot,) = self.ax.plot(
|
||||||
theta_rad_plot, r_nm, "o", markersize=marker_size, color=color
|
theta_rad_plot, r_nm, "o", markersize=marker_size, color=color, zorder=5
|
||||||
)
|
)
|
||||||
artist_list.append(dot)
|
artist_list.append(dot)
|
||||||
|
|
||||||
@ -464,6 +468,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
[r_nm, r_end_nm],
|
[r_nm, r_end_nm],
|
||||||
color=color,
|
color=color,
|
||||||
linewidth=1.2,
|
linewidth=1.2,
|
||||||
|
zorder=4,
|
||||||
)
|
)
|
||||||
artist_list.append(line)
|
artist_list.append(line)
|
||||||
|
|
||||||
@ -475,6 +480,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
fontsize=8,
|
fontsize=8,
|
||||||
ha="center",
|
ha="center",
|
||||||
va="bottom",
|
va="bottom",
|
||||||
|
zorder=7,
|
||||||
)
|
)
|
||||||
label_artist_list.append(txt)
|
label_artist_list.append(txt)
|
||||||
|
|
||||||
@ -483,7 +489,7 @@ class PPIDisplay(ttk.Frame):
|
|||||||
if len(trail) > 1:
|
if len(trail) > 1:
|
||||||
thetas, rs = zip(*trail)
|
thetas, rs = zip(*trail)
|
||||||
(line,) = self.ax.plot(
|
(line,) = self.ax.plot(
|
||||||
thetas, rs, color=color, linestyle="-", linewidth=0.8, alpha=0.7
|
thetas, rs, color=color, linestyle="-", linewidth=0.8, alpha=0.7, zorder=3
|
||||||
)
|
)
|
||||||
artist_list.append(line)
|
artist_list.append(line)
|
||||||
|
|
||||||
@ -511,29 +517,6 @@ class PPIDisplay(ttk.Frame):
|
|||||||
if self.canvas:
|
if self.canvas:
|
||||||
self.canvas.draw()
|
self.canvas.draw()
|
||||||
|
|
||||||
def _on_antenna_animate_changed(self):
|
|
||||||
st = self._antenna_state
|
|
||||||
enabled = self.animate_antenna_var.get()
|
|
||||||
if not enabled:
|
|
||||||
st["animating"] = False
|
|
||||||
if self._antenna_line_artist:
|
|
||||||
self._antenna_line_artist.set_visible(False)
|
|
||||||
else:
|
|
||||||
if self._antenna_line_artist:
|
|
||||||
self._antenna_line_artist.set_visible(True)
|
|
||||||
last_ts, next_ts = st.get("last_ts"), st.get("next_ts")
|
|
||||||
if (
|
|
||||||
last_ts is not None
|
|
||||||
and next_ts is not None
|
|
||||||
and next_ts > last_ts
|
|
||||||
and not st.get("animating")
|
|
||||||
):
|
|
||||||
st["animating"] = True
|
|
||||||
self.after(st.get("tick_ms", 33), self._antenna_animation_step)
|
|
||||||
|
|
||||||
if self.canvas:
|
|
||||||
self.canvas.draw_idle()
|
|
||||||
|
|
||||||
def clear_previews(self):
|
def clear_previews(self):
|
||||||
for artist in self.preview_artists:
|
for artist in self.preview_artists:
|
||||||
artist.set_data([], [])
|
artist.set_data([], [])
|
||||||
@ -586,117 +569,6 @@ class PPIDisplay(ttk.Frame):
|
|||||||
if self.canvas:
|
if self.canvas:
|
||||||
self.canvas.draw()
|
self.canvas.draw()
|
||||||
|
|
||||||
def update_antenna_azimuth(self, az_deg: float, timestamp: float = None):
|
|
||||||
try:
|
|
||||||
now = time.monotonic()
|
|
||||||
ts = float(timestamp) if timestamp is not None else now
|
|
||||||
az = float(az_deg) % 360
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
|
|
||||||
st = self._antenna_state
|
|
||||||
if not self.animate_antenna_var.get():
|
|
||||||
st.update(
|
|
||||||
{
|
|
||||||
"last_az_deg": az,
|
|
||||||
"last_ts": ts,
|
|
||||||
"next_az_deg": az,
|
|
||||||
"next_ts": ts,
|
|
||||||
"animating": False,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if self._antenna_line_artist:
|
|
||||||
self._antenna_line_artist.set_visible(False)
|
|
||||||
if self.canvas:
|
|
||||||
self.canvas.draw_idle()
|
|
||||||
return
|
|
||||||
|
|
||||||
if st["last_az_deg"] is None or st["last_ts"] is None:
|
|
||||||
st.update(
|
|
||||||
{"last_az_deg": az, "last_ts": ts, "next_az_deg": az, "next_ts": ts}
|
|
||||||
)
|
|
||||||
self._render_antenna_line(az)
|
|
||||||
return
|
|
||||||
|
|
||||||
cur_az, cur_ts = st["last_az_deg"], st["last_ts"]
|
|
||||||
next_az, next_ts = st.get("next_az_deg"), st.get("next_ts")
|
|
||||||
|
|
||||||
if next_az is not None and next_ts is not None and next_ts > cur_ts:
|
|
||||||
frac = max(0.0, min(1.0, (now - cur_ts) / (next_ts - cur_ts)))
|
|
||||||
diff = ((next_az - cur_az + 180) % 360) - 180
|
|
||||||
cur_az = (cur_az + diff * frac) % 360
|
|
||||||
cur_ts = now
|
|
||||||
|
|
||||||
st.update(
|
|
||||||
{"last_az_deg": cur_az, "last_ts": cur_ts, "next_az_deg": az, "next_ts": ts}
|
|
||||||
)
|
|
||||||
|
|
||||||
if not st["animating"]:
|
|
||||||
st["animating"] = True
|
|
||||||
self.after(st["tick_ms"], self._antenna_animation_step)
|
|
||||||
|
|
||||||
def _antenna_animation_step(self):
|
|
||||||
st = self._antenna_state
|
|
||||||
if not self.animate_antenna_var.get():
|
|
||||||
st["animating"] = False
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
last_ts, next_ts = st.get("last_ts"), st.get("next_ts")
|
|
||||||
last_az, next_az = st.get("last_az_deg"), st.get("next_az_deg")
|
|
||||||
now = time.monotonic()
|
|
||||||
|
|
||||||
if any(v is None for v in [last_az, next_az, last_ts, next_ts]):
|
|
||||||
st["animating"] = False
|
|
||||||
return
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
cur = (last_az + diff * frac) % 360
|
|
||||||
if frac >= 1.0:
|
|
||||||
st["last_az_deg"], st["last_ts"] = next_az, next_ts
|
|
||||||
st["animating"] = False
|
|
||||||
|
|
||||||
self._render_antenna_line(cur)
|
|
||||||
except Exception:
|
|
||||||
st["animating"] = False
|
|
||||||
|
|
||||||
if st["animating"]:
|
|
||||||
self.after(st["tick_ms"], self._antenna_animation_step)
|
|
||||||
|
|
||||||
def _render_antenna_line(self, az_deg: float):
|
|
||||||
try:
|
|
||||||
theta = np.deg2rad(float(az_deg))
|
|
||||||
max_r = self.ax.get_ylim()[1]
|
|
||||||
if self._antenna_line_artist:
|
|
||||||
self._antenna_line_artist.set_visible(self.animate_antenna_var.get())
|
|
||||||
self._antenna_line_artist.set_data([theta, theta], [0, max_r])
|
|
||||||
if self.canvas:
|
|
||||||
self.canvas.draw_idle()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def draw_trajectory_preview(self, waypoints: List[Waypoint], use_spline: bool):
|
def draw_trajectory_preview(self, waypoints: List[Waypoint], use_spline: bool):
|
||||||
self.clear_previews()
|
self.clear_previews()
|
||||||
self.clear_trails()
|
self.clear_trails()
|
||||||
@ -745,3 +617,24 @@ class PPIDisplay(ttk.Frame):
|
|||||||
if self.range_var.get() not in valid_steps:
|
if self.range_var.get() not in valid_steps:
|
||||||
self.range_var.set(max_range_nm)
|
self.range_var.set(max_range_nm)
|
||||||
self._on_range_selected()
|
self._on_range_selected()
|
||||||
|
|
||||||
|
def render_antenna_line(self, az_deg: Optional[float]):
|
||||||
|
"""Directly renders the antenna line at a given absolute azimuth."""
|
||||||
|
try:
|
||||||
|
if self._antenna_line_artist is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if az_deg is None or not self.animate_antenna_var.get():
|
||||||
|
self._antenna_line_artist.set_visible(False)
|
||||||
|
else:
|
||||||
|
theta = np.deg2rad(float(az_deg))
|
||||||
|
max_r = self.ax.get_ylim()[1]
|
||||||
|
self._antenna_line_artist.set_data([theta, theta], [0, max_r])
|
||||||
|
self._antenna_line_artist.set_visible(True)
|
||||||
|
|
||||||
|
# This method is called frequently, so we rely on the main loop's
|
||||||
|
# final draw call to update the canvas, avoiding redundant draws.
|
||||||
|
# self.canvas.draw_idle()
|
||||||
|
except Exception:
|
||||||
|
# Silently fail to prevent logging floods
|
||||||
|
pass
|
||||||
4
todo.md
4
todo.md
@ -17,6 +17,10 @@
|
|||||||
|
|
||||||
- [ ] funzione di sincronizzazione: è stato aggiunto al server la possibilità di gestire dei messaggi che sono di tipo SY (tag) che sono fatti per gestire il sincronismo tra client e server. In questa nuova tipologia di messaggi io invio un mio timetag che poi il server mi restituirà subito appena lo riceve, facendo così sappiamo in quanto tempo il messaggio che spedisco è arrivato al server, viene letto, e viene risposto il mio numero con anche il timetag del server. Facendo così misurando i delta posso scroprire esattamente il tempo che intercorre tra inviare un messaggio al server e ricevere una risposta. Per come è fatto il server il tempo di applicazione dei nuovi valori per i target sarà al massimo di 1 batch, che può essere variabile, ma a quel punto lo potremmo calibrare in altro modo. Con l'analisi sui sync possiamo sapere come allineare gli orologi.
|
- [ ] funzione di sincronizzazione: è stato aggiunto al server la possibilità di gestire dei messaggi che sono di tipo SY (tag) che sono fatti per gestire il sincronismo tra client e server. In questa nuova tipologia di messaggi io invio un mio timetag che poi il server mi restituirà subito appena lo riceve, facendo così sappiamo in quanto tempo il messaggio che spedisco è arrivato al server, viene letto, e viene risposto il mio numero con anche il timetag del server. Facendo così misurando i delta posso scroprire esattamente il tempo che intercorre tra inviare un messaggio al server e ricevere una risposta. Per come è fatto il server il tempo di applicazione dei nuovi valori per i target sarà al massimo di 1 batch, che può essere variabile, ma a quel punto lo potremmo calibrare in altro modo. Con l'analisi sui sync possiamo sapere come allineare gli orologi.
|
||||||
|
|
||||||
|
- [ ] Aggiungere un tasto per duplicare uno scenario da uno già presente e dargli un nome diverso
|
||||||
|
- [ ] aggiungere una funzione automatica durante il salvataggio dello scenario che cancelli quelli più vecchi di 10 salvataggi fa, per evitare che aumentino in numero senza controllo
|
||||||
|
|
||||||
|
|
||||||
# FIXME List
|
# FIXME List
|
||||||
|
|
||||||
- [ ] sistemare la visualizzazione nella tabe simulator, per poter vedere quale scenario è stato selezionato
|
- [ ] sistemare la visualizzazione nella tabe simulator, per poter vedere quale scenario è stato selezionato
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user