diff --git a/charon_vna/vna.py b/charon_vna/vna.py index ad34309..d23022b 100644 --- a/charon_vna/vna.py +++ b/charon_vna/vna.py @@ -4,7 +4,8 @@ from pathlib import Path from typing import Any, Dict, Tuple import adi -import iio + +# import iio import numpy as np import skrf as rf import xarray as xr @@ -229,158 +230,159 @@ class Charon: # %% -sdr = Charon("ip:192.168.3.1", frequency=np.linspace(1e9, 1.1e9, 11)) +if __name__ == "__main__": + pass -# %% initialization -config = sdr.get_config() -# print(sdr.ctrl.debug_attrs["adi,rx-rf-port-input-select"].value) -# print(sdr.ctrl.debug_attrs["adi,tx-rf-port-input-select"].value) -config + # %% + sdr = Charon("ip:192.168.3.1", frequency=np.linspace(1e9, 1.1e9, 11)) -# %% generate tone -fc = 1e9 -sdr.set_output(frequency=fc + sdr.FREQUENCY_OFFSET, power=-5) + # %% initialization + config = sdr.get_config() + # print(sdr.ctrl.debug_attrs["adi,rx-rf-port-input-select"].value) + # print(sdr.ctrl.debug_attrs["adi,tx-rf-port-input-select"].value) + config -# %% capture data -data = sdr._rx(1, fc=fc) + # %% generate tone + fc = 1e9 + sdr.set_output(frequency=fc + sdr.FREQUENCY_OFFSET, power=-5) -# %% Plot in time -fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True) -axs[0].plot(np.real(data).T) -axs[1].plot(np.imag(data).T) -axs[0].set_ylabel("Real") -axs[1].set_ylabel("Imag") -axs[0].grid(True) -axs[1].grid(True) -axs[-1].set_xlabel("Sample") -axs[-1].set_xlim(0, data.shape[-1]) -fig.show() + # %% capture data + data = sdr._rx(1, fc=fc) -# %% -fig, ax = plt.subplots(1, 1, tight_layout=True) -ax.plot(np.real(data).T, np.imag(data).T) -ax.grid(True) -ax.set_aspect("equal") -ax.set_xlabel("Real") -ax.set_ylabel("Imag") -ax.set_xlim(np.array([-1, 1]) * (2 ** (12 - 1) - 1)) -ax.set_ylim(ax.get_xlim()) -fig.show() + # %% Plot in time + fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True) + axs[0].plot(np.real(data).T) + axs[1].plot(np.imag(data).T) + axs[0].set_ylabel("Real") + axs[1].set_ylabel("Imag") + axs[0].grid(True) + axs[1].grid(True) + axs[-1].set_xlabel("Sample") + axs[-1].set_xlim(0, data.shape[-1]) + fig.show() -# %% Plot in frequency -f = np.fft.fftfreq(data.shape[-1], 1 / sdr.sdr.sample_rate) -RX_BITS = 12 # for each of i, q (including sign bit) -fft_data = np.fft.fft(data, axis=-1, norm="forward") / (2 ** (RX_BITS - 1)) -plt.figure() -for cc, chan in enumerate(sdr.sdr.rx_enabled_channels): - plt.plot( - np.fft.fftshift(f), - db20(np.fft.fftshift(fft_data[cc])), - label=f"Channel {chan}", + # %% + fig, ax = plt.subplots(1, 1, tight_layout=True) + ax.plot(np.real(data).T, np.imag(data).T) + ax.grid(True) + ax.set_aspect("equal") + ax.set_xlabel("Real") + ax.set_ylabel("Imag") + ax.set_xlim(np.array([-1, 1]) * (2 ** (12 - 1) - 1)) + ax.set_ylim(ax.get_xlim()) + fig.show() + + # %% Plot in frequency + f = np.fft.fftfreq(data.shape[-1], 1 / sdr.sdr.sample_rate) + RX_BITS = 12 # for each of i, q (including sign bit) + fft_data = np.fft.fft(data, axis=-1, norm="forward") / (2 ** (RX_BITS - 1)) + plt.figure() + for cc, chan in enumerate(sdr.sdr.rx_enabled_channels): + plt.plot( + np.fft.fftshift(f), + db20(np.fft.fftshift(fft_data[cc])), + label=f"Channel {chan}", + ) + plt.legend() + plt.ylim(-100, 0) + plt.xlabel("Frequency [Hz]") + plt.ylabel("Power [dBfs]") + plt.title(f"Fc = {sdr.sdr.rx_lo / 1e9} GHz") + plt.gca().xaxis.set_major_formatter(EngFormatter()) + plt.grid(True) + plt.show() + + # %% + s = sdr.vna_capture(frequency=np.linspace(70e6, 200e6, 101)) + + # %% Plot Logmag + fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True) + + axs[0].plot(s.frequency, db20(s), label="Measured") + axs[1].plot(s.frequency, np.rad2deg(np.angle((s))), label="Measured") + + axs[0].grid(True) + axs[1].grid(True) + + axs[0].set_ylim(-80, 0) + axs[1].set_ylim(-200, 200) + axs[1].set_xlim(np.min(s.frequency), np.max(s.frequency)) + axs[1].xaxis.set_major_formatter(EngFormatter(places=1)) + axs[1].set_xlabel("Frequency") + + axs[0].set_ylabel("|S11| [dB]") + axs[1].set_ylabel("∠S11 [deg]") + + reference_sparams = None + reference_sparams = dir_ / "RBP-135+_Plus25degC.s2p" + if reference_sparams is not None: + ref = rf.Network(reference_sparams) + 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") + axs[0].legend() + axs[1].legend() + + plt.show() + + # %% SOL calibration + 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 = sdr.vna_capture(frequency=cal_frequency) + input("Connect OPEN and press ENTER...") + open = sdr.vna_capture(frequency=cal_frequency) + input("Connect LOAD and press ENTER...") + load = sdr.vna_capture(frequency=cal_frequency) + + short_net = s2net(short) + open_net = s2net(open) + load_net = s2net(load) + + cal_ideal = rf.media.DefinedGammaZ0(frequency=ideal_cal_frequency) + calibration = rf.calibration.OnePort( + [short_net, open_net, load_net], + [cal_ideal.short(), cal_ideal.open(), cal_ideal.load(0)], ) -plt.legend() -plt.ylim(-100, 0) -plt.xlabel("Frequency [Hz]") -plt.ylabel("Power [dBfs]") -plt.title(f"Fc = {sdr.sdr.rx_lo / 1e9} GHz") -plt.gca().xaxis.set_major_formatter(EngFormatter()) -plt.grid(True) -plt.show() + # %% + s = sdr.vna_capture(frequency=cal_frequency) -# %% -s = sdr.vna_capture(frequency=np.linspace(70e6, 200e6, 101)) + # %% + s_calibrated = calibration.apply_cal(s2net(s)) -# %% Plot Logmag -fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True) + plt.figure() + s_calibrated.plot_s_smith() + # ref.plot_s_smith(m=1, n=1) + plt.show() -axs[0].plot(s.frequency, db20(s), label="Measured") -axs[1].plot(s.frequency, np.rad2deg(np.angle((s))), label="Measured") + plt.figure() + 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) + plt.gca().xaxis.set_major_formatter(EngFormatter()) + plt.grid(True) + plt.xlim(s_calibrated.f[0], s_calibrated.f[-1]) + plt.show() -axs[0].grid(True) -axs[1].grid(True) + plt.figure() + for start, stop in HAM_BANDS: + plt.axvspan(start, stop, alpha=0.1, color="k") + # s_calibrated.plot_s_vswr() + # drop invalid points + vswr = copy.deepcopy(s_calibrated.s_vswr[:, 0, 0]) + vswr[vswr < 1] = np.nan + plt.plot(s_calibrated.f, vswr) + plt.axhline(1, color="k", linestyle="--") + plt.ylabel("VSWR") + plt.xlabel("Frequency [Hz]") + # ref.plot_s_vswr(m=1, n=1) + plt.gca().xaxis.set_major_formatter(EngFormatter()) + plt.grid(True) + plt.ylim(0, 10) + plt.xlim(s_calibrated.f[0], s_calibrated.f[-1]) + plt.show() -axs[0].set_ylim(-80, 0) -axs[1].set_ylim(-200, 200) -axs[1].set_xlim(np.min(s.frequency), np.max(s.frequency)) -axs[1].xaxis.set_major_formatter(EngFormatter(places=1)) -axs[1].set_xlabel("Frequency") - -axs[0].set_ylabel("|S11| [dB]") -axs[1].set_ylabel("∠S11 [deg]") - -reference_sparams = None -reference_sparams = dir_ / "RBP-135+_Plus25degC.s2p" -if reference_sparams is not None: - ref = rf.Network(reference_sparams) - 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") - axs[0].legend() - axs[1].legend() - -plt.show() - - -# %% SOL calibration -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 = sdr.vna_capture(frequency=cal_frequency) -input("Connect OPEN and press ENTER...") -open = sdr.vna_capture(frequency=cal_frequency) -input("Connect LOAD and press ENTER...") -load = sdr.vna_capture(frequency=cal_frequency) - -short_net = s2net(short) -open_net = s2net(open) -load_net = s2net(load) - -cal_ideal = rf.media.DefinedGammaZ0(frequency=ideal_cal_frequency) -calibration = rf.calibration.OnePort( - [short_net, open_net, load_net], - [cal_ideal.short(), cal_ideal.open(), cal_ideal.load(0)], -) - - -# %% -s = sdr.vna_capture(frequency=cal_frequency) - -# %% -s_calibrated = calibration.apply_cal(s2net(s)) - -plt.figure() -s_calibrated.plot_s_smith() -# ref.plot_s_smith(m=1, n=1) -plt.show() - -plt.figure() -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) -plt.gca().xaxis.set_major_formatter(EngFormatter()) -plt.grid(True) -plt.xlim(s_calibrated.f[0], s_calibrated.f[-1]) -plt.show() - -plt.figure() -for start, stop in HAM_BANDS: - plt.axvspan(start, stop, alpha=0.1, color="k") -# s_calibrated.plot_s_vswr() -# drop invalid points -vswr = copy.deepcopy(s_calibrated.s_vswr[:, 0, 0]) -vswr[vswr < 1] = np.nan -plt.plot(s_calibrated.f, vswr) -plt.axhline(1, color="k", linestyle="--") -plt.ylabel("VSWR") -plt.xlabel("Frequency [Hz]") -# ref.plot_s_vswr(m=1, n=1) -plt.gca().xaxis.set_major_formatter(EngFormatter()) -plt.grid(True) -plt.ylim(0, 10) -plt.xlim(s_calibrated.f[0], s_calibrated.f[-1]) -plt.show() - -# %% + # %%