diff --git a/charon_vna/io_.py b/charon_vna/io_.py new file mode 100644 index 0000000..bfa641a --- /dev/null +++ b/charon_vna/io_.py @@ -0,0 +1,12 @@ +from pathlib import Path + +import skrf as rf +import xarray as xr +from util import net2s + + +# scikit-rf has no way to save files aside from touchstone and pickle +def cal2zarr(cal: rf.calibration.Calibration, outpath: Path): + ideals = [net2s(net) for net in cal.ideals] + measured = [net2s(net) for net in cal.measured] + # s.to_zarr(outpath) diff --git a/charon_vna/util.py b/charon_vna/util.py new file mode 100644 index 0000000..e510d71 --- /dev/null +++ b/charon_vna/util.py @@ -0,0 +1,79 @@ +import numpy as np +import skrf as rf +import xarray as xr + +HAM_BANDS = [ + [135.7e3, 137.8e3], + [472e3, 479e3], + [1.8e6, 2e6], + [3.5e6, 4e6], + [5332e3, 5405e3], + [7e6, 7.3e6], + [10.1e6, 10.15e6], + [14e6, 14.35e6], + [18.068e6, 18.168e6], + [21e6, 21.45e6], + [24.89e6, 24.99e6], + [28e6, 29.7e6], + [50e6, 54e6], + [144e6, 148e6], + [219e6, 220e6], + [222e6, 225e6], + [420e6, 450e6], + [902e6, 928e6], + [1240e6, 1300e6], + [2300e6, 2310e6], + [2390e6, 2450e6], + [3400e6, 3450e6], + [5650e6, 5925e6], + [10e9, 10.5e9], + [24e9, 24.25e9], + [47e9, 47.2e9], + [76e9, 81e9], + [122.25e9, 123e9], + [134e9, 141e9], + [241e9, 250e9], + [275e9, np.inf], +] + + +def db10(p): + return 10 * np.log10(np.abs(p)) + + +def db20(p): + return 20 * np.log10(np.abs(p)) + + +def minmax(x): + return (np.min(x), np.max(x)) + + +def s2net(s: xr.DataArray) -> rf.Network: + net = rf.Network(frequency=s.frequency, f_unit="Hz", s=s) + return net + + +def net2s(net: rf.Network) -> xr.DataArray: + port_tuples = net.port_tuples + + m = list(set(t[0] for t in port_tuples)) + m.sort() + m = np.array(m) + m += 1 # skrf uses 0-indexed ports + + n = list(set(t[0] for t in port_tuples)) + n.sort() + n = np.array(n) + n += 1 # skrf uses 0-indexed ports + + s = xr.DataArray( + net.s, + dims=["frequency", "m", "n"], + coords=dict( + frequency=net.f, + m=m, + n=n, + ), + ) + return s diff --git a/charon_vna/vna.py b/charon_vna/vna.py index bb05cd2..5513fc1 100644 --- a/charon_vna/vna.py +++ b/charon_vna/vna.py @@ -1,21 +1,18 @@ # %% imports import copy -import time from pathlib import Path from typing import Optional import adi import iio -import matplotlib as mpl import numpy as np import skrf as rf import xarray as xr from matplotlib import pyplot as plt -from matplotlib.gridspec import GridSpec -from matplotlib.patches import Circle from matplotlib.ticker import EngFormatter from numpy import typing as npt from scipy import signal +from util import HAM_BANDS, db20, net2s, s2net dir_ = Path(__file__).parent @@ -47,18 +44,6 @@ def get_config(sdr: adi.ad9361): return config -def db10(p): - return 10 * np.log10(np.abs(p)) - - -def db20(p): - return 20 * np.log10(np.abs(p)) - - -def minmax(x): - return (np.min(x), np.max(x)) - - def generate_tone(f: float, N: int = 1024, fs: Optional[float] = None): if fs is None: fs = sdr.sample_rate @@ -217,7 +202,7 @@ reference_sparams = None reference_sparams = dir_ / "RBP-135+_Plus25degC.s2p" if reference_sparams is not None: ref = rf.Network(reference_sparams) - rbp135 = xr.DataArray(ref.s, dims=["frequency", "m", "n"], coords=dict(frequency=ref.f, m=[1, 2], n=[1, 2])) + rbp135 = net2s(ref) axs[0].plot(rbp135.frequency, db20(rbp135.sel(m=1, n=1)), label="Datasheet") axs[1].plot(rbp135.frequency, np.rad2deg(np.angle(rbp135.sel(m=2, n=1))), label="Datasheet") @@ -227,15 +212,8 @@ if reference_sparams is not None: plt.show() -# %% -def s2net(s: xr.DataArray) -> rf.Network: - net = rf.Network(frequency=s.frequency) - net.s = s.data - return net - - # %% SOL calibration -cal_frequency = np.linspace(70e6, 600e6, 2001) +cal_frequency = np.linspace(70e6, 600e6, 101) ideal_cal_frequency = rf.Frequency(np.min(cal_frequency), np.max(cal_frequency), len(cal_frequency)) input("Connect SHORT and press ENTER...") short = vna_capture(frequency=cal_frequency) @@ -258,41 +236,6 @@ calibration = rf.calibration.OnePort( # %% s = vna_capture(frequency=cal_frequency) -# %% -ham_bands = [ - [135.7e3, 137.8e3], - [472e3, 479e3], - [1.8e6, 2e6], - [3.5e6, 4e6], - [5332e3, 5405e3], - [7e6, 7.3e6], - [10.1e6, 10.15e6], - [14e6, 14.35e6], - [18.068e6, 18.168e6], - [21e6, 21.45e6], - [24.89e6, 24.99e6], - [28e6, 29.7e6], - [50e6, 54e6], - [144e6, 148e6], - [219e6, 220e6], - [222e6, 225e6], - [420e6, 450e6], - [902e6, 928e6], - [1240e6, 1300e6], - [2300e6, 2310e6], - [2390e6, 2450e6], - [3400e6, 3450e6], - [5650e6, 5925e6], - [10e9, 10.5e9], - [24e9, 24.25e9], - [47e9, 47.2e9], - [76e9, 81e9], - [122.25e9, 123e9], - [134e9, 141e9], - [241e9, 250e9], - [275e9, np.inf], -] - # %% s_calibrated = calibration.apply_cal(s2net(s)) @@ -302,7 +245,7 @@ s_calibrated.plot_s_smith() plt.show() plt.figure() -for start, stop in ham_bands: +for start, stop in HAM_BANDS: plt.axvspan(start, stop, alpha=0.1, color="k") s_calibrated.plot_s_db() # ref.plot_s_db(m=1, n=1) @@ -312,7 +255,7 @@ plt.xlim(s_calibrated.f[0], s_calibrated.f[-1]) plt.show() plt.figure() -for start, stop in ham_bands: +for start, stop in HAM_BANDS: plt.axvspan(start, stop, alpha=0.1, color="k") # s_calibrated.plot_s_vswr() # drop invalid points