5import matplotlib.animation
as animation
6import matplotlib.patches
as patches
7import matplotlib.pyplot
as plt
10from matplotlib.lines
import Line2D
19df = pd.read_csv(
"3gpp-channel-consistency-output.txt", sep=
r"\s+", comment=
"#")
27STATE_COL =
"ChannelState"
39state = df[STATE_COL].astype(str)
44snr_diff = snr.diff().fillna(0.0)
45df[
"SNRdiff"] = snr_diff
49prev_state = state.shift(1, fill_value=state.iloc[0])
54for t, s_prev, s_cur
in zip(time, prev_state, state):
58 s_prev_is_nlos = s_prev.startswith(
"NLOS")
59 s_cur_is_nlos = s_cur.startswith(
"NLOS")
61 if s_prev ==
"LOS" and s_cur_is_nlos:
63 elif s_prev_is_nlos
and s_cur ==
"LOS":
68 """Draw vertical lines for LOS/NLOS transitions, with different types."""
71 ax.axvline(t, color=
"green", linestyle=
"-", linewidth=0.9, alpha=0.6)
74 ax.axvline(t, color=
"red", linestyle=
"--", linewidth=0.9, alpha=0.6)
78 Line2D([0], [0], color=
"green", linestyle=
"-", linewidth=1.2, label=
"NLOS → LOS"),
79 Line2D([0], [0], color=
"red", linestyle=
"--", linewidth=1.2, label=
"LOS → NLOS"),
81 ax.legend(handles=legend_lines, loc=
"upper right", fontsize=8)
103max_abs_dsnr = float(np.max(np.abs(snr_diff)))
104if max_abs_dsnr < 1.0:
120frame_step = max(1, n_samples // max_frames)
121frame_indices = range(0, n_samples, frame_step)
128fig, axes = plt.subplots(2, 2, figsize=(24, 9), dpi=150)
132ax_snr_zoom = axes[1, 0]
133ax_snr_diff = axes[1, 1]
138ax_map.set_xlabel(
"X [m]")
139ax_map.set_ylabel(
"Y [m]")
140ax_map.set_aspect(
"equal")
142ax_map.set_xlim(-25, 600)
143ax_map.set_ylim(-25, 1000)
145tx_circle = patches.Circle((0.0, 0.0), 5.0, color=
"blue", alpha=0.35)
146rx_circle = patches.Circle((0.0, 0.0), 5.0, color=
"red", alpha=0.35)
147ax_map.add_patch(tx_circle)
148ax_map.add_patch(rx_circle)
150buildings = pd.read_csv(
151 "3gpp-channel-consistency-buildings.txt", sep=
r"\s+", comment=
"#", header=
None
154for _, b
in buildings.iterrows():
156 rect = patches.Rectangle((x0, y0), x1 - x0, y1 - y0, color=
"gray", alpha=0.5)
157 ax_map.add_patch(rect)
162ax_snr.set_xlabel(
"Time [s]")
163ax_snr.set_ylabel(
"SNR [dB]")
165ax_snr.set_xlim(t_min, t_max)
166ax_snr.set_ylim(snr_ymin, snr_ymax)
167(snr_line,) = ax_snr.plot([], [],
"k-", linewidth=1.5)
174ax_snr_zoom.set_xlabel(
"Time [s]")
175ax_snr_zoom.set_ylabel(
"SNR [dB] (zoom)")
176ax_snr_zoom.grid(
True)
177ax_snr_zoom.set_ylim(snr_ymin, snr_ymax)
178(snr_zoom_line,) = ax_snr_zoom.plot([], [],
"k-", linewidth=1.5)
185ax_snr_diff.set_xlabel(
"Time [s]")
186ax_snr_diff.set_ylabel(
"ΔSNR [dB]")
187ax_snr_diff.grid(
True)
188ax_snr_diff.set_xlim(t_min, t_max)
189ax_snr_diff.set_ylim(snr_diff_ymin, snr_diff_ymax)
190(snr_diff_line,) = ax_snr_diff.plot([], [],
"k-", linewidth=1.5)
197fig_snr, axes_snr = plt.subplots(2, 1, figsize=(10, 6), sharex=
True)
199axes_snr[0].plot(time, snr,
"k-", linewidth=1.2)
200axes_snr[0].set_ylabel(
"SNR [dB]")
201axes_snr[0].grid(
True)
204axes_snr[1].plot(time, snr_diff,
"k-", linewidth=1.2)
205axes_snr[1].set_ylabel(
"ΔSNR [dB]")
206axes_snr[1].set_xlabel(
"Time [s]")
207axes_snr[1].grid(
True)
210axes_snr[0].set_xlim(t_min, t_max)
211axes_snr[0].set_ylim(snr_ymin, snr_ymax)
212axes_snr[1].set_ylim(snr_diff_ymin, snr_diff_ymax)
213fig_snr.tight_layout()
214fig_snr.savefig(
"channel_consistency_snr_and_dsnr.png", dpi=300, bbox_inches=
"tight")
224 t_now = time.iloc[frame_idx]
227 tx_circle.set_center((tx_x.iloc[frame_idx], tx_y.iloc[frame_idx]))
228 rx_circle.set_center((rx_x.iloc[frame_idx], rx_y.iloc[frame_idx]))
229 ax_map.set_title(f
"Time = {t_now:.3f} s")
232 snr_line.set_data(time.iloc[: frame_idx + 1], snr.iloc[: frame_idx + 1])
235 t0 = max(t_min, t_now - zoom_window_s / 2.0)
236 t1 = min(t_max, t_now + zoom_window_s / 2.0)
237 mask = (time >= t0) & (time <= t1)
239 ax_snr_zoom.set_xlim(t0, t1)
240 snr_zoom_line.set_data(time[mask], snr[mask])
243 snr_diff_line.set_data(time.iloc[: frame_idx + 1], snr_diff.iloc[: frame_idx + 1])
245 return (tx_circle, rx_circle, snr_line, snr_zoom_line, snr_diff_line)
251ani = animation.FuncAnimation(
254 frames=frame_indices,
259ani.save(
"3gpp-channel-consistency.gif", writer=
"pillow", fps=25, dpi=90)
add_state_vlines(ax, add_legend=False)