Compare commits

..

No commits in common. "e5a3d327e5bf37c1acac9ec72f5d0b0cad1fb927" and "9a304faa00b3aaddb79149845ec7e8a6af55946b" have entirely different histories.

6 changed files with 55 additions and 90 deletions

View File

@ -414,18 +414,12 @@ Device selection (``--device``) is optional if only one device is detected. Exac
ria view capture.npy --show --no-save
ria view old.npy --legacy --type simple
ria view recordings\qam64_35.npy --type simple
ria view recordings\qam64_35.npy --type full
.. figure:: ../images/recordings/qam64_35.png
.. figure:: ../images/qam64_35.png
:alt: Example output of ria view recordings\qam64_35.npy --type simple
Output of ``ria view recordings\qam64_35.npy --type simple``
.. figure:: ../images/recordings/qam64_35-full.png
:alt: Example output of ria view recordings\qam64_35.npy --type full
Output of ``ria view recordings\qam64_35.npy --type full``
.. _cmd-annotate:

View File

@ -1,5 +1,5 @@
Data Package (ria_toolkit_oss.data)
=======================================
Datatypes Package (ria_toolkit_oss.data)
=============================================
.. |br| raw:: html

View File

@ -3,12 +3,11 @@ import os
import textwrap
from typing import Optional
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import gridspec, ticker
from matplotlib import gridspec
from matplotlib.patches import Patch
from PIL import Image, UnidentifiedImageError
from PIL import Image
from scipy.fft import fft, fftshift
from scipy.signal import spectrogram
from scipy.signal.windows import hann
@ -186,7 +185,7 @@ def view_sig(
logo: Optional[bool] = True,
dark: Optional[bool] = True,
spines: Optional[bool] = False,
title_fontsize: Optional[int] = 25,
title_fontsize: Optional[int] = 35,
subtitle_fontsize: Optional[int] = 15,
) -> None:
"""
@ -231,24 +230,11 @@ def view_sig(
complex_signal = recording.data[0]
sample_rate, center_frequency, _ = extract_metadata_fields(recording.metadata)
subplot_height = 3 * (plot_spectrogram) + 2 * (iq + frequency) + 3 * (constellation or metadata or logo)
subplot_height = 2 * (plot_spectrogram + iq + frequency) + 3 * (constellation or metadata or logo)
subplot_width = max((constellation + metadata or 1), logo * 3)
if dark:
plt.style.use("dark_background")
matplotlib.rcParams.update({
"figure.facecolor": "#161616",
"axes.facecolor": "#161616",
"savefig.facecolor": "#161616",
"savefig.edgecolor": "#161616",
"font.size": 10,
"axes.titlesize": 15,
"axes.labelsize": 10,
"xtick.labelsize": 10,
"ytick.labelsize": 10,
"legend.frameon": False,
"legend.facecolor": "none",
})
logo_path = os.path.dirname(__file__) + "/graphics/Qoherent-logo-white-transparent.png"
else:
plt.style.use("default")
@ -266,8 +252,8 @@ def view_sig(
plot_x_indx = 0
if plot_spectrogram:
spec_ax = plt.subplot(gs[plot_y_indx : plot_y_indx + 3, :])
plot_y_indx = plot_y_indx + 3
spec_ax = plt.subplot(gs[plot_y_indx : plot_y_indx + 2, :])
plot_y_indx = plot_y_indx + 2
fft_size = get_fft_size(plot_length=plot_length)
_, t_spec, Sxx = spectrogram(
@ -294,12 +280,7 @@ def view_sig(
)
set_spines(spec_ax, spines)
spec_ax.set_title("Spectrogram", loc="left", fontsize=subtitle_fontsize)
spec_ax.set_xlabel("Time (s)")
spec_ax.set_ylabel("Frequency (MHz)")
spec_ax.yaxis.set_major_formatter(
ticker.FuncFormatter(lambda x, _: f"{x / 1e6:.1f}")
)
spec_ax.set_title("Spectrogram", loc="center", fontsize=subtitle_fontsize)
if iq:
iq_ax = plt.subplot(gs[plot_y_indx : plot_y_indx + 2, :])
@ -310,13 +291,12 @@ def view_sig(
iq_ax.plot(t, plot_iq.real, color=COLORS["purple"], linewidth=0.6, alpha=0.8, label="I")
iq_ax.plot(t, plot_iq.imag, color=COLORS["magenta"], linewidth=0.6, alpha=0.8, label="Q")
iq_ax.grid(True, alpha=0.2, linewidth=0.5)
iq_ax.grid(False)
iq_ax.set_ylabel("Amplitude")
iq_ax.set_xlim([min(t), max(t)])
iq_ax.set_xlabel("Time (s)")
iq_ax.set_title("IQ Sample Plot", loc="left", fontsize=subtitle_fontsize)
iq_ax.legend(loc="upper right", fontsize=10)
iq_ax.set_title("IQ Sample Plot", fontsize=subtitle_fontsize)
set_spines(iq_ax, spines)
if frequency:
@ -330,12 +310,10 @@ def view_sig(
# Convert to dB
spectrum_db = 20 * np.log10(spectrum + 1e-12) # 20*log for magnitude
freqs = (np.linspace(-sample_rate / 2, sample_rate / 2, len(complex_signal[:plot_length])) + center_frequency) / 1e6
freqs = np.linspace(-sample_rate / 2, sample_rate / 2, len(complex_signal[:plot_length])) + center_frequency
freq_ax.plot(freqs, spectrum_db, color=COLORS["accent"], linewidth=0.8)
freq_ax.set_xlabel("Frequency (MHz)")
freq_ax.set_ylabel("Magnitude (dB)")
freq_ax.grid(True, alpha=0.2, linewidth=0.5)
freq_ax.set_title("Frequency Spectrum (Windowed FFT)", loc="left", fontsize=subtitle_fontsize)
freq_ax.set_title("Frequency Spectrum (Windowed FFT)", fontsize=subtitle_fontsize)
set_spines(freq_ax, spines)
if constellation:
@ -348,7 +326,7 @@ def view_sig(
const_ax.set_ylim([-1 * dimension, dimension])
const_ax.set_xlabel("In-phase (I)")
const_ax.set_ylabel("Quadrature (Q)")
const_ax.set_title("Constellation", loc="left", fontsize=subtitle_fontsize)
const_ax.set_title("Constellation", fontsize=subtitle_fontsize)
const_ax.set_aspect("equal")
if not spines:
@ -397,8 +375,8 @@ def view_sig(
image = Image.open(logo_path) # Open the PNG image using PIL
logo_ax.imshow(image)
except (FileNotFoundError, UnidentifiedImageError, OSError) as exc:
print(f"Warning, could not load logo image: {logo_path}. Reason: {exc}")
except FileNotFoundError:
print(f"Warning, {logo_path} not found.")
fig.subplots_adjust(
left=0.1, # Left margin

View File

@ -119,19 +119,24 @@ def setup_style(*, labels_mode: bool = False, compact_mode: bool = False) -> Non
label_font = 14
else:
base_font = 10
title_font = 15
title_font = 12
label_font = 10
matplotlib.rcParams.update(
{
"figure.facecolor": "#161616",
"axes.facecolor": "#161616",
"savefig.facecolor": "#161616",
"savefig.edgecolor": "#161616",
"figure.facecolor": "#0f172a",
"axes.facecolor": "#1e293b",
"axes.edgecolor": COLORS["muted"],
"axes.labelcolor": COLORS["light"],
"text.color": COLORS["light"],
"xtick.color": COLORS["muted"],
"ytick.color": COLORS["muted"],
"grid.color": COLORS["muted"],
"grid.alpha": 0.3,
"font.size": base_font,
"axes.titlesize": title_font,
"axes.labelsize": label_font,
"figure.titlesize": title_font + 4,
"figure.titlesize": title_font + 2,
"legend.frameon": False,
"legend.facecolor": "none",
"xtick.labelsize": base_font,
@ -189,7 +194,7 @@ def view_simple_sig(
constellation_mode: Optional[bool] = False,
labels_mode: Optional[bool] = False,
slice: Optional[tuple] = None,
title: Optional[str] = "Signal Plot",
title: Optional[str] = "Signal",
):
"""
Create a simple plot of various signal visualizations as a png or svg image.
@ -232,7 +237,7 @@ def view_simple_sig(
spec_signal = signal
if compact_mode:
fig, (ax2, ax1) = plt.subplots(2, 1, figsize=(12, 6), gridspec_kw={"height_ratios": [5, 1]})
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 6), gridspec_kw={"height_ratios": [1, 5]})
show_title = False
show_labels = False
ax_constellation = ax_psd = None
@ -248,24 +253,25 @@ def view_simple_sig(
ax_psd = None
else:
if constellation_mode:
fig, ((ax2, ax1), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))
ax_constellation, ax_psd = ax3, ax4
else:
fig, (ax2, ax1) = plt.subplots(2, 1, figsize=(14, 10))
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10))
ax_constellation = ax_psd = None
show_title = True
show_labels = labels_mode
if show_title:
fig.suptitle(title, fontsize=25)
fig.patch.set_facecolor(matplotlib.rcParams["figure.facecolor"])
fig.suptitle(title, fontsize=16, color=COLORS["light"], y=0.96)
fig.patch.set_facecolor("#0f172a")
total_duration_s = len(signal) / sample_rate_hz if sample_rate_hz else 0.0
t_s = np.linspace(0, total_duration_s, len(display_signal)) if len(display_signal) else np.array([])
ax1.plot(t_s, display_signal.real, color=COLORS["purple"], linewidth=0.6, alpha=0.8, label="I")
ax1.plot(t_s, display_signal.imag, color=COLORS["magenta"], linewidth=0.6, alpha=0.8, label="Q")
ax1.grid(True, alpha=0.2, linewidth=0.5)
ax1.plot(t_s, display_signal.real, color=COLORS["purple"], linewidth=0.8, alpha=0.8, label="I")
ax1.plot(t_s, display_signal.imag, color=COLORS["magenta"], linewidth=0.8, alpha=0.8, label="Q")
ax1.set_xlim(0, total_duration_s)
ax1.grid(True, alpha=0.3)
nfft, overlap = _get_nfft_size(signal=signal, fast_mode=fast_mode)
@ -279,7 +285,7 @@ def view_simple_sig(
)
ax2.set_ylim(center_freq_hz - sample_rate_hz / 2, center_freq_hz + sample_rate_hz / 2)
ax1.set_xlim(ax2.get_xlim())
ax2.set_xlim(0, total_duration_s)
if show_labels:
if horizontal_mode:
@ -288,26 +294,20 @@ def view_simple_sig(
ax2.set_xlabel("Time (s)")
ax1.set_ylabel("Amplitude")
ax1.set_title(f"IQ Sample Plot - {sdr} SDR", loc="left", pad=10, fontsize=15)
ax1.legend(loc="upper right", fontsize=10)
ax1.set_title(f"Time Series - {sdr} SDR", loc="left", pad=10)
ax1.legend(loc="upper right")
ax2.set_ylabel("Frequency (MHz)")
ax2.set_ylabel("Frequency (Hz)")
ax2.set_title(
f"Spectrogram - {center_freq_hz / 1e6:.1f} MHz ± {sample_rate_hz / 2e6:.1f} MHz", loc="left", pad=10, fontsize=15
)
ax2.yaxis.set_major_formatter(
matplotlib.ticker.FuncFormatter(lambda x, _: f"{x / 1e6:.1f}")
f"Spectrogram - {center_freq_hz / 1e6:.1f} MHz ± {sample_rate_hz / 2e6:.1f} MHz", loc="left", pad=10
)
yticks = ax2.get_yticks()
ax2.set_yticklabels([f"{y / 1e6:.1f}" for y in yticks])
elif not compact_mode:
ax1.set_title("IQ Sample Plot", loc="left", pad=10, fontsize=15)
ax1.legend(loc="upper right", fontsize=10)
ax1.set_title("Time Series", loc="left", pad=10)
ax1.legend(loc="upper right", fontsize=8)
ax2.set_xlabel("Time (s)")
ax2.set_ylabel("Frequency (MHz)")
ax2.set_title("Spectrogram", loc="left", pad=10, fontsize=15)
ax2.yaxis.set_major_formatter(
matplotlib.ticker.FuncFormatter(lambda x, _: f"{x / 1e6:.1f}")
)
ax2.set_title("Spectrogram", loc="left", pad=10)
_add_annotations(
annotations=annotations,
@ -339,8 +339,8 @@ def view_simple_sig(
)
ax_constellation.set_xlabel("In-phase (I)")
ax_constellation.set_ylabel("Quadrature (Q)")
ax_constellation.set_title("Constellation", loc="left", fontsize=15)
ax_constellation.grid(True, alpha=0.2, linewidth=0.5)
ax_constellation.set_title("Constellation")
ax_constellation.grid(True, alpha=0.3)
ax_constellation.set_aspect("equal")
if ax_psd is not None:
@ -351,11 +351,11 @@ def view_simple_sig(
freqs = freqs + center_freq_hz
spectrum_db = 10 * np.log10(spectrum + 1e-12)
ax_psd.plot(freqs / 1e6, spectrum_db, color=COLORS["accent"], linewidth=0.8)
ax_psd.plot(freqs / 1e6, spectrum_db, color=COLORS["accent"], linewidth=1.0)
ax_psd.set_xlabel("Frequency (MHz)")
ax_psd.set_ylabel("Power (dB)")
ax_psd.set_title("Power Spectral Density", loc="left", fontsize=15)
ax_psd.grid(True, alpha=0.2, linewidth=0.5)
ax_psd.set_title("Power Spectral Density")
ax_psd.grid(True, alpha=0.3)
if compact_mode:
ax1.set_xticks([])
@ -367,20 +367,13 @@ def view_simple_sig(
else:
plt.tight_layout()
if show_title:
plt.subplots_adjust(top=0.9)
plt.subplots_adjust(top=0.92)
if saveplot:
output_path, extension = set_path(output_path=output_path)
dpi_value = _set_dpi(fast_mode=fast_mode, labels_mode=labels_mode, extension=extension)
plt.savefig(
output_path,
dpi=dpi_value,
bbox_inches="tight",
pad_inches=0.3,
facecolor=matplotlib.rcParams["savefig.facecolor"],
edgecolor=matplotlib.rcParams["savefig.edgecolor"],
)
plt.savefig(output_path, dpi=dpi_value, bbox_inches="tight", facecolor="#0f172a", edgecolor="none")
print(f"Saved signal plot to {output_path}")
return output_path