diff --git a/adi/ad9084.py b/adi/ad9084.py index 4f48d97ce..c8520b706 100644 --- a/adi/ad9084.py +++ b/adi/ad9084.py @@ -8,6 +8,8 @@ from adi.rx_tx import rx_tx from adi.sync_start import sync_start +from .fftsniffer import fftsniffer + def _map_to_dict(paths, ch): if ch.attrs["label"].value == "buffer_only": @@ -88,6 +90,10 @@ def __init__(self, uri=""): # Devices with buffers self._rxadc = self._ctx.find_device("axi-ad9084-rx-hpc") self._txdac = self._ctx.find_device("axi-ad9084-tx-hpc") + self._fftsniffer_a = self._ctx.find_device("ad9088-fft-sniffer-A") + self._fftsniffer_b = self._ctx.find_device("ad9088-fft-sniffer-B") + self.fftsniffer_a = fftsniffer(self._fftsniffer_a) + self.fftsniffer_b = fftsniffer(self._fftsniffer_b) # Get DDC and DUC mappings paths = {} diff --git a/adi/fftsniffer.py b/adi/fftsniffer.py new file mode 100644 index 000000000..150828a54 --- /dev/null +++ b/adi/fftsniffer.py @@ -0,0 +1,208 @@ +"""FFT Sniffer Module for AD9084/AD9088""" + +from adi.rx_tx import rx, attribute + +def reset_buffer(func): + """Wrapper for set calls to clear the buffer after setting a new value. + """ + + def wrapper(*args, **kwargs): + if args[0]._rx: + args[0]._rx.rx_destroy_buffer() + args[0]._rx = None + func(*args, **kwargs) + + return wrapper + +class fftsniffer_rx(rx): + _rx_channel_names = ["index", "voltage0_i", "voltage0_q", "magn0"] + _skip_channel_order_check = True + def __init__(self): + pass + + def _super_init(self, rx_buffer_size=2**16): + super().__init__(rx_buffer_size=rx_buffer_size) + +class fftsniffer(attribute): + """FFT Sniffer Class""" + + def __init__(self, fft_sniffer, ctx=None): + self._fft_sniffer = fft_sniffer + self._rx = None + + def capture_fft(self): + """Capture FFT""" + if not self._rx: + self._rx = fftsniffer_rx() + self._rx._rxadc = self._fft_sniffer + self._rx._super_init(rx_buffer_size=self.fft_size) + if self.fft_mode == "Magnitude": + self._rx.rx_enabled_channels = [3] + else: + self._rx.rx_enabled_channels = [1, 2] + if self.fft_mode == "Magnitude": + return self._rx.rx() + data = self._rx.rx() + return data[0] + 1j * data[1] + + def get_bins_freq(self): + """Get Bins Frequency""" + bin_size = self.adc_sampling_rate_Hz / 512 + if self.real_mode: + # 0 -> Fs/2 + return [bin_size * i for i in range(256)] + # -Fs/2 -> Fs/2 + return [bin_size * i for i in range(-256, 256)] + + + @property + def fft_size(self): + return 256 if self.real_mode else 512 + + _fft_mode = "Magnitude" + @property + def fft_mode(self): + """FFT Mode""" + return self._fft_mode + + @fft_mode.setter + @reset_buffer + def fft_mode(self, value): + if value in ["Complex", "Magnitude"]: + self._fft_mode = value + else: + raise ValueError("Invalid FFT Mode. Choose 'Complex' or 'Magnitude'") + self._fft_mode = value + + @property + def real_mode(self): + """Real Mode""" + return self._get_iio_debug_attr("real_mode", self._fft_sniffer) + + @real_mode.setter + @reset_buffer + def real_mode(self, value): + value = "1" if value else "0" + self._set_iio_debug_attr_str("real_mode", value, self._fft_sniffer) + + @property + def sorting_enable(self): + """Sorting Enable""" + v = self._get_iio_debug_attr("sort_enable", self._fft_sniffer) + return v == "1" + + @sorting_enable.setter + @reset_buffer + def sorting_enable(self, value): + value = "1" if value else "0" + self._set_iio_debug_attr_str("sort_enable", value, self._fft_sniffer) + + @property + def continuous_mode(self): + """Continuous Mode""" + return self._get_iio_debug_attr("continuous_mode", self._fft_sniffer) + + @continuous_mode.setter + @reset_buffer + def continuous_mode(self, value): + value = "1" if value else "0" + self._set_iio_debug_attr_str("continuous_mode", value, self._fft_sniffer) + + @property + def alpha_factor(self): + """Alpha Factor""" + return self._get_iio_debug_attr("alpha_factor", self._fft_sniffer) + + @alpha_factor.setter + @reset_buffer + def alpha_factor(self, value): + self._set_iio_debug_attr_str("alpha_factor", value, self._fft_sniffer) + + @property + def adc_sampling_rate_Hz(self): + """ADC Sampling Rate""" + return self._get_iio_debug_attr("adc_sampling_rate_Hz", self._fft_sniffer) + + + @property + def dither_enable(self): + """Dither Enable""" + return self._get_iio_debug_attr("dither_enable", self._fft_sniffer) + + @dither_enable.setter + @reset_buffer + def dither_enable(self, value): + value = "1" if value else "0" + self._set_iio_debug_attr_str("dither_enable", value, self._fft_sniffer) + + @property + def delay_capture_ms(self): + """Delay Capture""" + return self._get_iio_debug_attr("delay_capture_ms", self._fft_sniffer) + + @delay_capture_ms.setter + @reset_buffer + def delay_capture_ms(self, value): + self._set_iio_debug_attr_str("delay_capture_ms", value, self._fft_sniffer) + + @property + def window_enable(self): + """Window Enable""" + return self._get_iio_debug_attr("window_enable", self._fft_sniffer) + + @window_enable.setter + @reset_buffer + def window_enable(self, value): + value = "1" if value else "0" + self._set_iio_debug_attr_str("window_enable", value, self._fft_sniffer) + + @property + def bottom_fft_enable(self): + """Bottom FFT Enable""" + return self._get_iio_debug_attr("bottom_fft_enable", self._fft_sniffer) + + @bottom_fft_enable.setter + @reset_buffer + def bottom_fft_enable(self, value): + value = "1" if value else "0" + self._set_iio_debug_attr_str("bottom_fft_enable", value, self._fft_sniffer) + + @property + def min_threshold(self): + """Min Threshold""" + return self._get_iio_debug_attr("min_threshold", self._fft_sniffer) + + @min_threshold.setter + @reset_buffer + def min_threshold(self, value): + self._set_iio_debug_attr_str("min_threshold", value, self._fft_sniffer) + + @property + def max_threshold(self): + """Max Threshold""" + return self._get_iio_debug_attr("max_threshold", self._fft_sniffer) + + @max_threshold.setter + @reset_buffer + def max_threshold(self, value): + self._set_iio_debug_attr_str("max_threshold", value, self._fft_sniffer) + + @property + def fft_enable_select(self): + """FFT Enable Select""" + return self._get_iio_debug_attr("fft_enable_sel", self._fft_sniffer) + + @fft_enable_select.setter + @reset_buffer + def fft_enable_select(self, value): + self._set_iio_debug_attr_str("fft_enable_sel", value, self._fft_sniffer) + + @property + def fft_hold_select(self): + """FFT Hold Select""" + return self._get_iio_debug_attr("fft_hold_sel", self._fft_sniffer) + + @fft_hold_select.setter + @reset_buffer + def fft_hold_select(self, value): + self._set_iio_debug_attr_str("fft_hold_sel", value, self._fft_sniffer)