Compare commits

..

No commits in common. "ec010af9476a8bb1d6ee2f44e1dfa8af5d05b8ac" and "a20217967f7158324c1da32301c8637d15e3a610" have entirely different histories.

View File

@ -18,20 +18,6 @@ dir_ = Path(__file__).parent
# %% connection # %% connection
def generate_tone(f: float, fs: float, N: int = 1024, scale: int = 2**14):
fs = int(fs)
fc = int(f / (fs / N)) * (fs / N)
ts = 1 / float(fs)
t = np.arange(0, N * ts, ts)
i = np.cos(2 * np.pi * t * fc) * scale
q = np.sin(2 * np.pi * t * fc) * scale
iq = i + 1j * q
return iq
class Charon: class Charon:
FREQUENCY_OFFSET = 1e6 FREQUENCY_OFFSET = 1e6
@ -72,19 +58,19 @@ class Charon:
self.sdr.loopback = 0 self.sdr.loopback = 0
self.sdr.gain_control_mode_chan0 = "manual" self.sdr.gain_control_mode_chan0 = "manual"
self.sdr.gain_control_mode_chan1 = "manual" self.sdr.gain_control_mode_chan1 = "manual"
self.sdr.rx_hardwaregain_chan0 = 10 self.sdr.rx_hardwaregain_chan0 = 40
self.sdr.rx_hardwaregain_chan1 = 10 self.sdr.rx_hardwaregain_chan1 = 40
self.sdr.tx_hardwaregain_chan0 = -10 self.sdr.tx_hardwaregain_chan0 = -10
# # switch control # switch control
# ctx = iio.Context(uri) ctx = iio.Context(uri)
# self.ctrl = ctx.find_device("ad9361-phy") self.ctrl = ctx.find_device("ad9361-phy")
# # raw ad9361 register accesss: # raw ad9361 register accesss:
# # https://ez.analog.com/linux-software-drivers/f/q-a/120853/control-fmcomms3-s-gpo-with-python # https://ez.analog.com/linux-software-drivers/f/q-a/120853/control-fmcomms3-s-gpo-with-python
# # https://www.analog.com/media/cn/technical-documentation/user-guides/ad9364_register_map_reference_manual_ug-672.pdf # https://www.analog.com/media/cn/technical-documentation/user-guides/ad9364_register_map_reference_manual_ug-672.pdf
# self.ctrl.reg_write(0x26, 0x90) # bit 7: AuxDAC Manual, bit 4: GPO Manual self.ctrl.reg_write(0x26, 0x90) # bit 7: AuxDAC Manual, bit 4: GPO Manual
# self._set_gpo(self.ports[0] - 1) self._set_gpo(self.ports[0] - 1)
# # TODO: init AuxDAC # TODO: init AuxDAC
def get_config(self) -> Dict[str, Any]: def get_config(self) -> Dict[str, Any]:
config = dict() config = dict()
@ -133,6 +119,17 @@ class Charon:
# tx_gain = pout.coords["tx_gain"][tx_gain_idx] # tx_gain = pout.coords["tx_gain"][tx_gain_idx]
self.sdr.tx_hardwaregain_chan0 = float(tx_gain) self.sdr.tx_hardwaregain_chan0 = float(tx_gain)
def generate_tone(self, f: float, fs: float, N: int = 1024, scale: int = 2**14):
fs = int(fs)
fc = int(f / (fs / N)) * (fs / N)
ts = 1 / float(fs)
t = np.arange(0, N * ts, ts)
i = np.cos(2 * np.pi * t * fc) * scale
q = np.sin(2 * np.pi * t * fc) * scale
iq = i + 1j * q
return iq
def set_output(self, frequency: float, power: float): def set_output(self, frequency: float, power: float):
# TODO: switch to DDS in Pluto # TODO: switch to DDS in Pluto
@ -140,7 +137,7 @@ class Charon:
self.set_output_power(power) self.set_output_power(power)
self.sdr.tx_lo = int(frequency - self.FREQUENCY_OFFSET) self.sdr.tx_lo = int(frequency - self.FREQUENCY_OFFSET)
self.sdr.tx_cyclic_buffer = True self.sdr.tx_cyclic_buffer = True
# self.sdr.tx(generate_tone(f=self.FREQUENCY_OFFSET, fs=self.sdr.sample_rate)) # self.sdr.tx(self.generate_tone(f=self.FREQUENCY_OFFSET, fs=self.sdr.sample_rate))
self.sdr.dds_single_tone(self.FREQUENCY_OFFSET, scale=0.9, channel=0) self.sdr.dds_single_tone(self.FREQUENCY_OFFSET, scale=0.9, channel=0)
def _rx(self, count: int = 1, fc: float | None = None) -> npt.ArrayLike: def _rx(self, count: int = 1, fc: float | None = None) -> npt.ArrayLike:
@ -161,7 +158,7 @@ class Charon:
self.set_output(frequency=frequency, power=-5) self.set_output(frequency=frequency, power=-5)
data = self._rx(1, fc=frequency - self.FREQUENCY_OFFSET) data = self._rx(1, fc=frequency - self.FREQUENCY_OFFSET)
ddc_tone = generate_tone(f=-self.FREQUENCY_OFFSET, fs=self.sdr.sample_rate, scale=1) ddc_tone = self.generate_tone(f=-self.FREQUENCY_OFFSET, fs=self.sdr.sample_rate, scale=1)
ddc_data = data * ddc_tone ddc_data = data * ddc_tone
ddc_rel = ddc_data[1] / ddc_data[0] ddc_rel = ddc_data[1] / ddc_data[0]
@ -205,36 +202,14 @@ class Charon:
s.loc[dict(frequency=frequency)] = self.get_b_over_a(frequency=frequency) s.loc[dict(frequency=frequency)] = self.get_b_over_a(frequency=frequency)
return s return s
def vna_capture(self, frequency: npt.ArrayLike):
s = xr.DataArray(
np.empty(len(frequency), dtype=np.complex128),
dims=["frequency"],
coords=dict(
frequency=frequency,
),
)
for freq in s.frequency.data:
self.set_output(frequency=freq, power=-5)
self.sdr.rx_destroy_buffer()
self.sdr.rx_lo = int(freq)
self.sdr.rx_enabled_channels = [0, 1]
self.sdr.gain_control_mode_chan0 = "manual"
self.sdr.gain_control_mode_chan1 = "manual"
self.sdr.rx_hardwaregain_chan0 = 40
self.sdr.rx_hardwaregain_chan1 = 40
rx = self.sdr.rx()
s.loc[dict(frequency=freq)] = np.mean(rx[1] / rx[0])
return s
# %% # %%
sdr = Charon("ip:192.168.3.1", frequency=np.linspace(1e9, 1.1e9, 11)) sdr = Charon("ip:192.168.3.1", frequency=np.linspace(1e9, 1.1e9, 11))
# %% initialization # %% initialization
config = sdr.get_config() config = sdr.get_config()
# print(sdr.ctrl.debug_attrs["adi,rx-rf-port-input-select"].value) print(sdr.ctrl.debug_attrs["adi,rx-rf-port-input-select"].value)
# print(sdr.ctrl.debug_attrs["adi,tx-rf-port-input-select"].value) print(sdr.ctrl.debug_attrs["adi,tx-rf-port-input-select"].value)
config config
# %% generate tone # %% generate tone
@ -289,7 +264,31 @@ plt.show()
# %% # %%
s = sdr.vna_capture(frequency=np.linspace(70e6, 200e6, 101)) def vna_capture(frequency: npt.ArrayLike):
s = xr.DataArray(
np.empty(len(frequency), dtype=np.complex128),
dims=["frequency"],
coords=dict(
frequency=frequency,
),
)
for freq in s.frequency.data:
set_output(frequency=freq, power=-5)
sdr.rx_destroy_buffer()
sdr.rx_lo = int(freq)
sdr.rx_enabled_channels = [0, 1]
sdr.gain_control_mode_chan0 = "manual"
sdr.gain_control_mode_chan1 = "manual"
sdr.rx_hardwaregain_chan0 = 40
sdr.rx_hardwaregain_chan1 = 40
rx = sdr.rx()
s.loc[dict(frequency=freq)] = np.mean(rx[1] / rx[0])
return s
# %%
s = vna_capture(frequency=np.linspace(70e6, 200e6, 101))
# %% Plot Logmag # %% Plot Logmag
fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True) fig, axs = plt.subplots(2, 1, sharex=True, tight_layout=True)
@ -327,11 +326,11 @@ plt.show()
cal_frequency = np.linspace(70e6, 600e6, 101) cal_frequency = np.linspace(70e6, 600e6, 101)
ideal_cal_frequency = rf.Frequency(np.min(cal_frequency), np.max(cal_frequency), len(cal_frequency)) ideal_cal_frequency = rf.Frequency(np.min(cal_frequency), np.max(cal_frequency), len(cal_frequency))
input("Connect SHORT and press ENTER...") input("Connect SHORT and press ENTER...")
short = sdr.vna_capture(frequency=cal_frequency) short = vna_capture(frequency=cal_frequency)
input("Connect OPEN and press ENTER...") input("Connect OPEN and press ENTER...")
open = sdr.vna_capture(frequency=cal_frequency) open = vna_capture(frequency=cal_frequency)
input("Connect LOAD and press ENTER...") input("Connect LOAD and press ENTER...")
load = sdr.vna_capture(frequency=cal_frequency) load = vna_capture(frequency=cal_frequency)
short_net = s2net(short) short_net = s2net(short)
open_net = s2net(open) open_net = s2net(open)
@ -345,7 +344,7 @@ calibration = rf.calibration.OnePort(
# %% # %%
s = sdr.vna_capture(frequency=cal_frequency) s = vna_capture(frequency=cal_frequency)
# %% # %%
s_calibrated = calibration.apply_cal(s2net(s)) s_calibrated = calibration.apply_cal(s2net(s))