ADAR1000
Recommended for New Designs
The ADAR1000 is a 4-channel, X and Ku frequency band, beamforming core chip for phased arrays. This device operates in half-duplex between receive and...
Datasheet
ADAR1000 on Analog.com
Have anyone tried GNURADIO with Phaser?
I am wondering if anyone has a GNURADIO file that works with the phaser and can share it (it does not matter what is for, I need to check what hardware blocks are used)
I have tried to build a system for the Phaser based on the ADAR1000 GNURadio tutorials but it does not work
My objective is to build a monopole tracking with GNURadio and the phaser
Hi Ameno,
Here is a talk that Mark Thoren and I did last year at the GNU radio conference. It could help you:
https://www.youtube.com/live/i17fZ7J8e_c?si=aO_co_t9LqTYcG0b&t=20144
And attached are the GRC flowgraph files from that demo (remove the .txt from the file names):
# Copyright (C) 2023 Analog Devices, Inc. # All rights reserved. # jon.kraft@analog.com # wiki.analog.com/phaser # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # - Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # - Neither the name of Analog Devices, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # - The use of this software may or may not infringe the patent rights # of one or more patent holders. This license does not release you # from the requirement that you obtain separate licenses from these # patent holders to use this software. # - Use of the software either in source or binary form, must be run # on or directly connected to an Analog Devices Inc. component. # # THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE ARE DISCLAIMED. # # IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, INTELLECTUAL PROPERTY # RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ Embedded Python Blocks: Each time this file is saved, GRC will instantiate the first class it finds to get ports and parameters of your block. The arguments to __init__ will be the parameters. All of them are required to have default values! """ import sys import pickle import os import adi import numpy as np sys.path.append('/home/analog/pyadi-iio/examples/phaser/') import SDR_functions as SDR # import the Pluto SDR functions import ADAR_pyadi_functions_edited1 as ADAR # import the ADAR1000 functions from gnuradio import gr class blk(gr.sync_block): def __init__(self, step_size=3, save_trace=False, clear_trace=False, gain1=100, gain2=100, gain3=100, gain4=100, gain5=100, gain6=100, gain7=100, gain8=100, phase1=0, phase2=0, phase3=0, phase4=0, phase5=0, phase6=0, phase7=0, phase8=0, null_enable_1 = False, null_angle_1=0, null_enable_2 = False, null_angle_2=0): # only default arguments here """arguments to this function show up as parameters in GRC""" gr.sync_block.__init__( self, name='Phaser Python Control Block', # will show up in GRC in_sig=[], out_sig=[np.complex64, np.complex64] ) # if an attribute with the same name as a parameter is found, # a callback is registered (properties work, too) # ============================================================================= # User parameters # ============================================================================= self.step_size = step_size # steering angle step size (in degrees) self.save_trace = save_trace self.clear_trace = clear_trace self.saved_trace = np.ones(4094)*(0-100000j) self.gain1=gain1 self.gain2=gain2 self.gain3=gain3 self.gain4=gain4 self.gain5=gain5 self.gain6=gain6 self.gain7=gain7 self.gain8=gain8 self.phase1=phase1 self.phase2=phase2 self.phase3=phase3 self.phase4=phase4 self.phase5=phase5 self.phase6=phase6 self.phase7=phase7 self.phase8=phase8 self.null_enable_1 = null_enable_1 self.null_angle_1 = null_angle_1 self.null_enable_2 = null_enable_2 self.null_angle_2 = null_angle_2 rpi_ip = "ip:phaser.local" # default IP address of Phaser's Raspberry Pi sdr_ip = "ip:192.168.2.1" # default Pluto IP address # select which signal source to use # HB100 (external source) # OUT1 (transmit freq is set in config.py) # OUT2 (transmit freq is set in config.py) SignalSource = 'HB100' # 'HB100', 'OUT1', or 'OUT2' # config.py has all the key parameters that you might want to modify try: import config as config except: print("Make sure config.py is in this directory") sys.exit(0) # ============================================================================= # Variables setup # ============================================================================= # if using HB100, load the signal frequency from "phaser_find_hb100.py" output file if SignalSource == 'HB100': try: #with open("/home/analog/pyadi-iio/examples/phaser/hb100_freq_val.pkl", "rb") as file1: with open("hb100_freq_val.pkl", "rb") as file1: config.SignalFreq = pickle.load(file1) print("Found signal freq file, ", config.SignalFreq/1e9, " GHz") except: print("No signal freq found, keeping at ", config.SignalFreq/1e9, " GHz") """SET DEFAULT VALUES""" sdr_address = sdr_ip self.SignalFreq = config.SignalFreq Tx_freq = config.Tx_freq # Pluto's Tx LO freq. Rx_freq = config.Rx_freq # Pluto's Rx LO freq LO_freq = self.SignalFreq + Rx_freq # freq of the LTC5548 mixer LO SampleRate = config.SampleRate Rx_gain = config.Rx_gain Tx_gain = config.Tx_gain self.RxPhase1 = config.Rx1_cal self.RxPhase2 = config.Rx2_cal self.RxPhase3 = config.Rx3_cal self.RxPhase4 = config.Rx4_cal self.RxPhase5 = config.Rx5_cal self.RxPhase6 = config.Rx6_cal self.RxPhase7 = config.Rx7_cal self.RxPhase8 = config.Rx8_cal self.phase_step_size = 2.8125 self.c = 299792458 # speed of light in m/s self.d = config.d # antenna spacing for phaser is 14mm self.gainList = np.array([ self.gain1, self.gain2, self.gain3, self.gain4, self.gain5, self.gain6, self.gain7, self.gain8 ]) self.phaseList = np.array([ self.RxPhase1, self.RxPhase2, self.RxPhase3, self.RxPhase4, self.RxPhase5, self.RxPhase6, self.RxPhase7, self.RxPhase8 ]) # Use the onboard VCO to generate the LO? Or apply source to EXT_LO? gpios = adi.one_bit_adc_dac(rpi_ip) gpios.gpio_vctrl_1 = 1 # 1=Use onboard PLL/LO source (0=use external LO input) gpios.gpio_vctrl_2 = 1 # 1=Send LO to transmit circuitry (0=disable Tx path and send LO to LO_OUT) # setup GPIOs to control if Tx is output on OUT1 or OUT2 gpios.gpio_div_mr = 1 gpios.gpio_div_s0 = 0 gpios.gpio_div_s1 = 0 gpios.gpio_div_s2 = 0 attempt = True while attempt: try: # Initialize Pluto self.sdr = SDR.SDR_init( sdr_address, SampleRate, Tx_freq, Rx_freq, Rx_gain, Tx_gain, config.buffer_size, ) SDR.SDR_LO_init(rpi_ip, LO_freq) # Set Phaser's ADF4159 to the LO_freq # Intialize the ADAR1000 receive array self.rx_array = adi.adar1000_array( uri=rpi_ip, chip_ids=["BEAM0", "BEAM1"], # these are the ADAR1000s' labels in the device tree device_map=[[1], [2]], element_map=[[1, 2, 3, 4, 5, 6, 7, 8]], device_element_map={ 1: [7, 8, 5, 6], # i.e. channel2 of device1 (BEAM0), maps to element 8 2: [3, 4, 1, 2], }, ) attempt = False print('Connected to Phaser') except: print('Could not connect to Phaser') continue for device in self.rx_array.devices.values(): ADAR.ADAR_init(device) # resets the ADAR1000 ADAR.ADAR_set_mode(device, "rx") # ADAR1000s on Phaser are receive only, so mode is always "rx" ADAR.ADAR_set_Taper( self.rx_array, self.gainList ) # Set transmitter to either OUT1 or OUT2 SMA port. Or disable if using HB100 if SignalSource == 'OUT1': # use Phaser's OUT1 SMA port as the transmitter gpios.gpio_tx_sw = 1 # 0=OUT2, 1=OUT1 gpios.gpio_vctrl_2 = 1 # 1=Send LO to transmit circuitry elif SignalSource == 'OUT2': # use OUT2 as the transmitter gpios.gpio_tx_sw = 0 # 0=OUT2, 1=OUT1 gpios.gpio_vctrl_2 = 1 # 1=Send LO to transmit circuitry else: # use HB100 as the transmit signal source gpios.gpio_tx_sw = 0 SDR.SDR_setTx(self.sdr, -80) # disable tx output by attenuating it def ConvertPhaseToSteerAngle(self, PhDelta): # steering angle theta = arcsin(c*deltaphase/(2*pi*f*d) value1 = (self.c * np.radians(np.abs(PhDelta))) / ( 2 * 3.14159 * (self.SignalFreq) * self.d) clamped_value1 = max(min(1, value1), -1) # arcsin argument must be between 1 and -1 theta = np.degrees(np.arcsin(clamped_value1)) if PhDelta >= 0: SteerAngle = theta # positive PhaseDelta covers 0deg to 90 deg else: SteerAngle = -theta # negative phase delta covers 0 deg to -90 deg return SteerAngle def dbfs(self, raw_data): # function to convert IQ samples to FFT plot, scaled in dBFS NumSamples = len(raw_data) win = np.hamming(NumSamples) y = raw_data * win s_fft = np.fft.fft(y) / np.sum(win) s_shift = np.fft.fftshift(s_fft) s_dbfs = 20*np.log10(np.abs(s_shift)/(2**11)) # Pluto is a signed 12 bit ADC, so use 2^11 to convert to dBFS return s_dbfs def updateGainPhase(self): self.gainList = np.array([ self.gain1, self.gain2, self.gain3, self.gain4, self.gain5, self.gain6, self.gain7, self.gain8 ]) # gains from 0 to 100 self.phaseList = np.array([ self.RxPhase1 + self.phase1, self.RxPhase2 + self.phase2, self.RxPhase3 + self.phase3, self.RxPhase4 + self.phase4, self.RxPhase5 + self.phase5, self.RxPhase6 + self.phase6, self.RxPhase7 + self.phase7, self.RxPhase8 + self.phase8 ]) def null_vec(self, null_angle): null_phase = 2*np.pi*self.SignalFreq*self.d*np.sin(np.deg2rad(null_angle))/self.c null_phases = np.array([0,1,2,3,4,5,6,7])*null_phase # create array with phase shifts for null angle polar_null_phases = np.exp(1j * null_phases) # null steer vector return polar_null_phases def work(self, input_items, output_items): save_this_trace=False clear_this_trace=False enable_null_1 = self.null_enable_1 enable_null_2 = self.null_enable_2 if enable_null_1 == True: polar_null_phases = self.null_vec(self.null_angle_1) wn1 = self.gainList * polar_null_phases # beam weights for null direction if enable_null_2 == True: polar_null_phases = self.null_vec(self.null_angle_2) wn2 = self.gainList * polar_null_phases # beam weights for null direction SteerValues = np.arange(-90, 90 + self.step_size, self.step_size) # Phase delta = 2*Pi*d*sin(theta)/lambda = 2*Pi*d*sin(theta)*f/c PhaseValues = np.degrees( 2*np.pi*self.d* np.sin(np.radians(SteerValues)) * self.SignalFreq / self.c) self.updateGainPhase() for x in range(len(PhaseValues)): if self.save_trace == True: save_this_trace = True if self.clear_trace == True: clear_this_trace = True PhDelta = PhaseValues[x] steer_phases = (np.array([0,1,2,3,4,5,6,7])*PhDelta) % 360 wd = self.gainList * np.exp(1j * np.deg2rad(steer_phases)) # wd is the beam weights for desired steering direction # details here: https://www.mathworks.com/help/phased/ug/array-pattern-synthesis.html if enable_null_1 == True: wn1_herm = np.conjugate(wn1.reshape(1,len(wn1))) rn = wn1_herm @ wd / (wn1_herm @ wn1) wd = wd - wn1 * rn if enable_null_2 == True: wn2_herm = np.conjugate(wn2.reshape(1,len(wn2))) rn = wn2_herm @ wd / (wn2_herm @ wn2) wd = wd - wn2 * rn new_gains = np.abs(wd) new_gains = np.clip(new_gains, 0, 100) new_phases = np.angle(wd) new_phases = np.rad2deg(new_phases) ADAR.ADAR_set_Taper( self.rx_array, new_gains ) ADAR.ADAR_set_Phase( self.rx_array, 0, self.phase_step_size, new_phases ) data = self.sdr.rx() data_sum = data[0]+data[1] sum_dbfs = self.dbfs(data_sum) peak_dbfs = max(sum_dbfs) output_items[0][x] = (1*(self.ConvertPhaseToSteerAngle(PhDelta)) + 1j*peak_dbfs) output_items[1][x] = 0-10000000j for x in range(len(output_items[0])-len(SteerValues)): # fill in the empty parts of the array with large negative numbers to make it plot nicely output_items[0][len(SteerValues)+x] = 0-10000000j if save_this_trace==True: for x in range(len(self.saved_trace)): self.saved_trace[x] = output_items[0][x] if clear_this_trace==True: self.saved_trace = np.ones(4094)*(0-100000j) for x in range(len(self.saved_trace)): output_items[1][x] = self.saved_trace[x] return len(output_items[0])
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # SPDX-License-Identifier: GPL-3.0 # # GNU Radio Python Flow Graph # Title: GNU Radio Phaser # GNU Radio version: 3.8.2.0 from distutils.version import StrictVersion if __name__ == '__main__': import ctypes import sys if sys.platform.startswith('linux'): try: x11 = ctypes.cdll.LoadLibrary('libX11.so') x11.XInitThreads() except: print("Warning: failed to XInitThreads()") from PyQt5 import Qt from gnuradio import qtgui import sip from gnuradio import gr from gnuradio.filter import firdes import sys import signal from argparse import ArgumentParser from gnuradio.eng_arg import eng_float, intx from gnuradio import eng_notation from gnuradio.qtgui import Range, RangeWidget import epy_block_0 from gnuradio import qtgui class phaser_grc(gr.top_block, Qt.QWidget): def __init__(self): gr.top_block.__init__(self, "GNU Radio Phaser") Qt.QWidget.__init__(self) self.setWindowTitle("GNU Radio Phaser") qtgui.util.check_set_qss() try: self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) except: pass self.top_scroll_layout = Qt.QVBoxLayout() self.setLayout(self.top_scroll_layout) self.top_scroll = Qt.QScrollArea() self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) self.top_scroll_layout.addWidget(self.top_scroll) self.top_scroll.setWidgetResizable(True) self.top_widget = Qt.QWidget() self.top_scroll.setWidget(self.top_widget) self.top_layout = Qt.QVBoxLayout(self.top_widget) self.top_grid_layout = Qt.QGridLayout() self.top_layout.addLayout(self.top_grid_layout) self.settings = Qt.QSettings("GNU Radio", "phaser_grc") try: if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"): self.restoreGeometry(self.settings.value("geometry").toByteArray()) else: self.restoreGeometry(self.settings.value("geometry")) except: pass ################################################## # Variables ################################################## self.step_size = step_size = 1.5 self.save_trace = save_trace = False self.phase8 = phase8 = 0 self.phase7 = phase7 = 0 self.phase6 = phase6 = 0 self.phase5 = phase5 = 0 self.phase4 = phase4 = 0 self.phase3 = phase3 = 0 self.phase2 = phase2 = 0 self.phase1 = phase1 = 0 self.null_enable_2 = null_enable_2 = False self.null_enable_1 = null_enable_1 = False self.null_angle_2 = null_angle_2 = 10 self.null_angle_1 = null_angle_1 = 30 self.gain8 = gain8 = 100 self.gain7 = gain7 = 100 self.gain6 = gain6 = 100 self.gain5 = gain5 = 100 self.gain4 = gain4 = 100 self.gain3 = gain3 = 100 self.gain2 = gain2 = 100 self.gain1 = gain1 = 100 self.clear_trace = clear_trace = False ################################################## # Blocks ################################################## self.tab = Qt.QTabWidget() self.tab_widget_0 = Qt.QWidget() self.tab_layout_0 = Qt.QBoxLayout(Qt.QBoxLayout.TopToBottom, self.tab_widget_0) self.tab_grid_layout_0 = Qt.QGridLayout() self.tab_layout_0.addLayout(self.tab_grid_layout_0) self.tab.addTab(self.tab_widget_0, 'control') self.tab_widget_1 = Qt.QWidget() self.tab_layout_1 = Qt.QBoxLayout(Qt.QBoxLayout.TopToBottom, self.tab_widget_1) self.tab_grid_layout_1 = Qt.QGridLayout() self.tab_layout_1.addLayout(self.tab_grid_layout_1) self.tab.addTab(self.tab_widget_1, 'gain') self.tab_widget_2 = Qt.QWidget() self.tab_layout_2 = Qt.QBoxLayout(Qt.QBoxLayout.TopToBottom, self.tab_widget_2) self.tab_grid_layout_2 = Qt.QGridLayout() self.tab_layout_2.addLayout(self.tab_grid_layout_2) self.tab.addTab(self.tab_widget_2, 'phase') self.tab_widget_3 = Qt.QWidget() self.tab_layout_3 = Qt.QBoxLayout(Qt.QBoxLayout.TopToBottom, self.tab_widget_3) self.tab_grid_layout_3 = Qt.QGridLayout() self.tab_layout_3.addLayout(self.tab_grid_layout_3) self.tab.addTab(self.tab_widget_3, 'null steering') self.top_grid_layout.addWidget(self.tab, 0, 0, 6, 2) for r in range(0, 6): self.top_grid_layout.setRowStretch(r, 1) for c in range(0, 2): self.top_grid_layout.setColumnStretch(c, 1) self._step_size_range = Range(0.5, 5, .5, 1.5, 200) self._step_size_win = RangeWidget(self._step_size_range, self.set_step_size, 'step_size', "counter_slider", float) self.tab_grid_layout_0.addWidget(self._step_size_win, 0, 0, 1, 3) for r in range(0, 1): self.tab_grid_layout_0.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_0.setColumnStretch(c, 1) _save_trace_push_button = Qt.QPushButton('') _save_trace_push_button = Qt.QPushButton('save_trace') self._save_trace_choices = {'Pressed': True, 'Released': False} _save_trace_push_button.pressed.connect(lambda: self.set_save_trace(self._save_trace_choices['Pressed'])) _save_trace_push_button.released.connect(lambda: self.set_save_trace(self._save_trace_choices['Released'])) self.top_grid_layout.addWidget(_save_trace_push_button, 5, 2, 1, 1) for r in range(5, 6): self.top_grid_layout.setRowStretch(r, 1) for c in range(2, 3): self.top_grid_layout.setColumnStretch(c, 1) self._phase8_range = Range(-180, 180, 1, 0, 200) self._phase8_win = RangeWidget(self._phase8_range, self.set_phase8, 'phase8', "counter_slider", int) self.tab_grid_layout_2.addWidget(self._phase8_win, 7, 0, 1, 3) for r in range(7, 8): self.tab_grid_layout_2.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_2.setColumnStretch(c, 1) self._phase7_range = Range(-180, 180, 1, 0, 200) self._phase7_win = RangeWidget(self._phase7_range, self.set_phase7, 'phase7', "counter_slider", int) self.tab_grid_layout_2.addWidget(self._phase7_win, 6, 0, 1, 3) for r in range(6, 7): self.tab_grid_layout_2.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_2.setColumnStretch(c, 1) self._phase6_range = Range(-180, 180, 1, 0, 200) self._phase6_win = RangeWidget(self._phase6_range, self.set_phase6, 'phase6', "counter_slider", int) self.tab_grid_layout_2.addWidget(self._phase6_win, 5, 0, 1, 3) for r in range(5, 6): self.tab_grid_layout_2.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_2.setColumnStretch(c, 1) self._phase5_range = Range(-180, 180, 1, 0, 200) self._phase5_win = RangeWidget(self._phase5_range, self.set_phase5, 'phase5', "counter_slider", int) self.tab_grid_layout_2.addWidget(self._phase5_win, 4, 0, 1, 3) for r in range(4, 5): self.tab_grid_layout_2.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_2.setColumnStretch(c, 1) self._phase4_range = Range(-180, 180, 1, 0, 200) self._phase4_win = RangeWidget(self._phase4_range, self.set_phase4, 'phase4', "counter_slider", int) self.tab_grid_layout_2.addWidget(self._phase4_win, 3, 0, 1, 3) for r in range(3, 4): self.tab_grid_layout_2.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_2.setColumnStretch(c, 1) self._phase3_range = Range(-180, 180, 1, 0, 200) self._phase3_win = RangeWidget(self._phase3_range, self.set_phase3, 'phase3', "counter_slider", int) self.tab_grid_layout_2.addWidget(self._phase3_win, 2, 0, 1, 3) for r in range(2, 3): self.tab_grid_layout_2.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_2.setColumnStretch(c, 1) self._phase2_range = Range(-180, 180, 1, 0, 200) self._phase2_win = RangeWidget(self._phase2_range, self.set_phase2, 'phase2', "counter_slider", int) self.tab_grid_layout_2.addWidget(self._phase2_win, 1, 0, 1, 3) for r in range(1, 2): self.tab_grid_layout_2.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_2.setColumnStretch(c, 1) self._phase1_range = Range(-180, 180, 1, 0, 200) self._phase1_win = RangeWidget(self._phase1_range, self.set_phase1, 'phase1', "counter_slider", int) self.tab_grid_layout_2.addWidget(self._phase1_win, 0, 0, 1, 3) for r in range(0, 1): self.tab_grid_layout_2.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_2.setColumnStretch(c, 1) _null_enable_2_check_box = Qt.QCheckBox('null_enable_2') self._null_enable_2_choices = {True: True, False: False} self._null_enable_2_choices_inv = dict((v,k) for k,v in self._null_enable_2_choices.items()) self._null_enable_2_callback = lambda i: Qt.QMetaObject.invokeMethod(_null_enable_2_check_box, "setChecked", Qt.Q_ARG("bool", self._null_enable_2_choices_inv[i])) self._null_enable_2_callback(self.null_enable_2) _null_enable_2_check_box.stateChanged.connect(lambda i: self.set_null_enable_2(self._null_enable_2_choices[bool(i)])) self.tab_grid_layout_3.addWidget(_null_enable_2_check_box, 1, 4, 1, 1) for r in range(1, 2): self.tab_grid_layout_3.setRowStretch(r, 1) for c in range(4, 5): self.tab_grid_layout_3.setColumnStretch(c, 1) _null_enable_1_check_box = Qt.QCheckBox('null_enable_1') self._null_enable_1_choices = {True: True, False: False} self._null_enable_1_choices_inv = dict((v,k) for k,v in self._null_enable_1_choices.items()) self._null_enable_1_callback = lambda i: Qt.QMetaObject.invokeMethod(_null_enable_1_check_box, "setChecked", Qt.Q_ARG("bool", self._null_enable_1_choices_inv[i])) self._null_enable_1_callback(self.null_enable_1) _null_enable_1_check_box.stateChanged.connect(lambda i: self.set_null_enable_1(self._null_enable_1_choices[bool(i)])) self.tab_grid_layout_3.addWidget(_null_enable_1_check_box, 0, 4, 1, 1) for r in range(0, 1): self.tab_grid_layout_3.setRowStretch(r, 1) for c in range(4, 5): self.tab_grid_layout_3.setColumnStretch(c, 1) self._null_angle_2_range = Range(-90, 90, 1, 10, 200) self._null_angle_2_win = RangeWidget(self._null_angle_2_range, self.set_null_angle_2, 'null_angle_2', "counter_slider", int) self.tab_grid_layout_3.addWidget(self._null_angle_2_win, 1, 0, 1, 3) for r in range(1, 2): self.tab_grid_layout_3.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_3.setColumnStretch(c, 1) self._null_angle_1_range = Range(-90, 90, 1, 30, 200) self._null_angle_1_win = RangeWidget(self._null_angle_1_range, self.set_null_angle_1, 'null_angle_1', "counter_slider", int) self.tab_grid_layout_3.addWidget(self._null_angle_1_win, 0, 0, 1, 3) for r in range(0, 1): self.tab_grid_layout_3.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_3.setColumnStretch(c, 1) self._gain8_range = Range(0, 100, 1, 100, 200) self._gain8_win = RangeWidget(self._gain8_range, self.set_gain8, 'gain8', "counter_slider", int) self.tab_grid_layout_1.addWidget(self._gain8_win, 7, 0, 1, 3) for r in range(7, 8): self.tab_grid_layout_1.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_1.setColumnStretch(c, 1) self._gain7_range = Range(0, 100, 1, 100, 200) self._gain7_win = RangeWidget(self._gain7_range, self.set_gain7, 'gain7', "counter_slider", int) self.tab_grid_layout_1.addWidget(self._gain7_win, 6, 0, 1, 3) for r in range(6, 7): self.tab_grid_layout_1.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_1.setColumnStretch(c, 1) self._gain6_range = Range(0, 100, 1, 100, 200) self._gain6_win = RangeWidget(self._gain6_range, self.set_gain6, 'gain6', "counter_slider", int) self.tab_grid_layout_1.addWidget(self._gain6_win, 5, 0, 1, 3) for r in range(5, 6): self.tab_grid_layout_1.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_1.setColumnStretch(c, 1) self._gain5_range = Range(0, 100, 1, 100, 200) self._gain5_win = RangeWidget(self._gain5_range, self.set_gain5, 'gain5', "counter_slider", int) self.tab_grid_layout_1.addWidget(self._gain5_win, 4, 0, 1, 3) for r in range(4, 5): self.tab_grid_layout_1.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_1.setColumnStretch(c, 1) self._gain4_range = Range(0, 100, 1, 100, 200) self._gain4_win = RangeWidget(self._gain4_range, self.set_gain4, 'gain4', "counter_slider", int) self.tab_grid_layout_1.addWidget(self._gain4_win, 3, 0, 1, 3) for r in range(3, 4): self.tab_grid_layout_1.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_1.setColumnStretch(c, 1) self._gain3_range = Range(0, 100, 1, 100, 200) self._gain3_win = RangeWidget(self._gain3_range, self.set_gain3, 'gain3', "counter_slider", int) self.tab_grid_layout_1.addWidget(self._gain3_win, 2, 0, 1, 3) for r in range(2, 3): self.tab_grid_layout_1.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_1.setColumnStretch(c, 1) self._gain2_range = Range(0, 100, 1, 100, 200) self._gain2_win = RangeWidget(self._gain2_range, self.set_gain2, 'gain2', "counter_slider", int) self.tab_grid_layout_1.addWidget(self._gain2_win, 1, 0, 1, 3) for r in range(1, 2): self.tab_grid_layout_1.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_1.setColumnStretch(c, 1) self._gain1_range = Range(0, 100, 1, 100, 200) self._gain1_win = RangeWidget(self._gain1_range, self.set_gain1, 'gain1', "counter_slider", int) self.tab_grid_layout_1.addWidget(self._gain1_win, 0, 0, 1, 3) for r in range(0, 1): self.tab_grid_layout_1.setRowStretch(r, 1) for c in range(0, 3): self.tab_grid_layout_1.setColumnStretch(c, 1) _clear_trace_push_button = Qt.QPushButton('') _clear_trace_push_button = Qt.QPushButton('clear_trace') self._clear_trace_choices = {'Pressed': True, 'Released': False} _clear_trace_push_button.pressed.connect(lambda: self.set_clear_trace(self._clear_trace_choices['Pressed'])) _clear_trace_push_button.released.connect(lambda: self.set_clear_trace(self._clear_trace_choices['Released'])) self.top_grid_layout.addWidget(_clear_trace_push_button, 5, 3, 1, 1) for r in range(5, 6): self.top_grid_layout.setRowStretch(r, 1) for c in range(3, 4): self.top_grid_layout.setColumnStretch(c, 1) self.qtgui_const_sink_x_0 = qtgui.const_sink_c( 4094, #size "", #name 2 #number of inputs ) self.qtgui_const_sink_x_0.set_update_time(0.10) self.qtgui_const_sink_x_0.set_y_axis(-75, 0) self.qtgui_const_sink_x_0.set_x_axis(-89, 89) self.qtgui_const_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, "") self.qtgui_const_sink_x_0.enable_autoscale(False) self.qtgui_const_sink_x_0.enable_grid(True) self.qtgui_const_sink_x_0.enable_axis_labels(True) self.qtgui_const_sink_x_0.disable_legend() labels = ['', '', '', '', '', '', '', '', '', ''] widths = [2, 1, 1, 1, 1, 1, 1, 1, 1, 1] colors = ["blue", "red", "red", "red", "red", "red", "red", "red", "red", "red"] styles = [1, 1, 0, 0, 0, 0, 0, 0, 0, 0] markers = [-1, -1, 0, 0, 0, 0, 0, 0, 0, 0] alphas = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] for i in range(2): if len(labels[i]) == 0: self.qtgui_const_sink_x_0.set_line_label(i, "Data {0}".format(i)) else: self.qtgui_const_sink_x_0.set_line_label(i, labels[i]) self.qtgui_const_sink_x_0.set_line_width(i, widths[i]) self.qtgui_const_sink_x_0.set_line_color(i, colors[i]) self.qtgui_const_sink_x_0.set_line_style(i, styles[i]) self.qtgui_const_sink_x_0.set_line_marker(i, markers[i]) self.qtgui_const_sink_x_0.set_line_alpha(i, alphas[i]) self._qtgui_const_sink_x_0_win = sip.wrapinstance(self.qtgui_const_sink_x_0.pyqwidget(), Qt.QWidget) self.top_grid_layout.addWidget(self._qtgui_const_sink_x_0_win, 0, 2, 5, 3) for r in range(0, 5): self.top_grid_layout.setRowStretch(r, 1) for c in range(2, 5): self.top_grid_layout.setColumnStretch(c, 1) self.epy_block_0 = epy_block_0.blk(step_size=step_size, save_trace=save_trace, clear_trace=clear_trace, gain1=gain1, gain2=gain2, gain3=gain3, gain4=gain4, gain5=gain5, gain6=gain6, gain7=gain7, gain8=gain8, phase1=phase1, phase2=phase2, phase3=phase3, phase4=phase4, phase5=phase5, phase6=phase6, phase7=phase7, phase8=phase8, null_enable_1=null_enable_1, null_angle_1=null_angle_1, null_enable_2=null_enable_2, null_angle_2=null_angle_2) ################################################## # Connections ################################################## self.connect((self.epy_block_0, 1), (self.qtgui_const_sink_x_0, 1)) self.connect((self.epy_block_0, 0), (self.qtgui_const_sink_x_0, 0)) def closeEvent(self, event): self.settings = Qt.QSettings("GNU Radio", "phaser_grc") self.settings.setValue("geometry", self.saveGeometry()) event.accept() def get_step_size(self): return self.step_size def set_step_size(self, step_size): self.step_size = step_size self.epy_block_0.step_size = self.step_size def get_save_trace(self): return self.save_trace def set_save_trace(self, save_trace): self.save_trace = save_trace self.epy_block_0.save_trace = self.save_trace def get_phase8(self): return self.phase8 def set_phase8(self, phase8): self.phase8 = phase8 self.epy_block_0.phase8 = self.phase8 def get_phase7(self): return self.phase7 def set_phase7(self, phase7): self.phase7 = phase7 self.epy_block_0.phase7 = self.phase7 def get_phase6(self): return self.phase6 def set_phase6(self, phase6): self.phase6 = phase6 self.epy_block_0.phase6 = self.phase6 def get_phase5(self): return self.phase5 def set_phase5(self, phase5): self.phase5 = phase5 self.epy_block_0.phase5 = self.phase5 def get_phase4(self): return self.phase4 def set_phase4(self, phase4): self.phase4 = phase4 self.epy_block_0.phase4 = self.phase4 def get_phase3(self): return self.phase3 def set_phase3(self, phase3): self.phase3 = phase3 self.epy_block_0.phase3 = self.phase3 def get_phase2(self): return self.phase2 def set_phase2(self, phase2): self.phase2 = phase2 self.epy_block_0.phase2 = self.phase2 def get_phase1(self): return self.phase1 def set_phase1(self, phase1): self.phase1 = phase1 self.epy_block_0.phase1 = self.phase1 def get_null_enable_2(self): return self.null_enable_2 def set_null_enable_2(self, null_enable_2): self.null_enable_2 = null_enable_2 self._null_enable_2_callback(self.null_enable_2) self.epy_block_0.null_enable_2 = self.null_enable_2 def get_null_enable_1(self): return self.null_enable_1 def set_null_enable_1(self, null_enable_1): self.null_enable_1 = null_enable_1 self._null_enable_1_callback(self.null_enable_1) self.epy_block_0.null_enable_1 = self.null_enable_1 def get_null_angle_2(self): return self.null_angle_2 def set_null_angle_2(self, null_angle_2): self.null_angle_2 = null_angle_2 self.epy_block_0.null_angle_2 = self.null_angle_2 def get_null_angle_1(self): return self.null_angle_1 def set_null_angle_1(self, null_angle_1): self.null_angle_1 = null_angle_1 self.epy_block_0.null_angle_1 = self.null_angle_1 def get_gain8(self): return self.gain8 def set_gain8(self, gain8): self.gain8 = gain8 self.epy_block_0.gain8 = self.gain8 def get_gain7(self): return self.gain7 def set_gain7(self, gain7): self.gain7 = gain7 self.epy_block_0.gain7 = self.gain7 def get_gain6(self): return self.gain6 def set_gain6(self, gain6): self.gain6 = gain6 self.epy_block_0.gain6 = self.gain6 def get_gain5(self): return self.gain5 def set_gain5(self, gain5): self.gain5 = gain5 self.epy_block_0.gain5 = self.gain5 def get_gain4(self): return self.gain4 def set_gain4(self, gain4): self.gain4 = gain4 self.epy_block_0.gain4 = self.gain4 def get_gain3(self): return self.gain3 def set_gain3(self, gain3): self.gain3 = gain3 self.epy_block_0.gain3 = self.gain3 def get_gain2(self): return self.gain2 def set_gain2(self, gain2): self.gain2 = gain2 self.epy_block_0.gain2 = self.gain2 def get_gain1(self): return self.gain1 def set_gain1(self, gain1): self.gain1 = gain1 self.epy_block_0.gain1 = self.gain1 def get_clear_trace(self): return self.clear_trace def set_clear_trace(self, clear_trace): self.clear_trace = clear_trace self.epy_block_0.clear_trace = self.clear_trace def main(top_block_cls=phaser_grc, options=None): if StrictVersion("4.5.0") <= StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"): style = gr.prefs().get_string('qtgui', 'style', 'raster') Qt.QApplication.setGraphicsSystem(style) qapp = Qt.QApplication(sys.argv) tb = top_block_cls() tb.start() tb.show() def sig_handler(sig=None, frame=None): Qt.QApplication.quit() signal.signal(signal.SIGINT, sig_handler) signal.signal(signal.SIGTERM, sig_handler) timer = Qt.QTimer() timer.start(500) timer.timeout.connect(lambda: None) def quitting(): tb.stop() tb.wait() qapp.aboutToQuit.connect(quitting) qapp.exec_() if __name__ == '__main__': main()
options: parameters: author: '' category: '[GRC Hier Blocks]' cmake_opt: '' comment: '' copyright: '' description: '' gen_cmake: 'On' gen_linking: dynamic generate_options: qt_gui hier_block_src_path: '.:' id: phaser_grc max_nouts: '0' output_language: python placement: (0,0) qt_qss_theme: '' realtime_scheduling: '' run: 'True' run_command: '{python} -u {filename}' run_options: prompt sizing_mode: fixed thread_safe_setters: '' title: GNU Radio Phaser window_size: (1000,1000) states: bus_sink: false bus_source: false bus_structure: null coordinate: [8, 8] rotation: 0 state: enabled blocks: - name: clear_trace id: variable_qtgui_push_button parameters: comment: '' gui_hint: 5,3,1,1 label: '' pressed: 'True' released: 'False' type: bool value: 'False' states: bus_sink: false bus_source: false bus_structure: null coordinate: [1072, 420.0] rotation: 0 state: true - name: gain1 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@1: 0, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '0' step: '1' stop: '100' value: '100' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [432, 32.0] rotation: 0 state: true - name: gain2 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@1: 1, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '0' step: '1' stop: '100' value: '100' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [568, 32.0] rotation: 0 state: true - name: gain3 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@1: 2, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '0' step: '1' stop: '100' value: '100' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [704, 32.0] rotation: 0 state: true - name: gain4 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@1: 3, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '0' step: '1' stop: '100' value: '100' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [840, 32.0] rotation: 0 state: true - name: gain5 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@1: 4, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '0' step: '1' stop: '100' value: '100' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [976, 32.0] rotation: 0 state: true - name: gain6 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@1: 5, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '0' step: '1' stop: '100' value: '100' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [1112, 32.0] rotation: 0 state: true - name: gain7 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@1: 6, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '0' step: '1' stop: '100' value: '100' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [1248, 32.0] rotation: 0 state: true - name: gain8 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@1: 7, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '0' step: '1' stop: '100' value: '100' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [1384, 32.0] rotation: 0 state: true - name: null_angle_1 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@3: 0, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-90' step: '1' stop: '90' value: '30' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [760, 300.0] rotation: 0 state: true - name: null_angle_2 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@3: 1, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-90' step: '1' stop: '90' value: '10' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [760, 428.0] rotation: 0 state: true - name: null_enable_1 id: variable_qtgui_check_box parameters: comment: '' 'false': 'False' gui_hint: 'tab@3: 0, 4, 1, 1' label: '' 'true': 'True' type: bool value: 'False' states: bus_sink: false bus_source: false bus_structure: null coordinate: [880, 308.0] rotation: 0 state: true - name: null_enable_2 id: variable_qtgui_check_box parameters: comment: '' 'false': 'False' gui_hint: 'tab@3: 1, 4, 1, 1' label: '' 'true': 'True' type: bool value: 'False' states: bus_sink: false bus_source: false bus_structure: null coordinate: [888, 428.0] rotation: 0 state: true - name: phase1 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@2: 0, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-180' step: '1' stop: '180' value: '0' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [432, 168.0] rotation: 0 state: true - name: phase2 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@2: 1, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-180' step: '1' stop: '180' value: '0' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [568, 168.0] rotation: 0 state: true - name: phase3 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@2: 2, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-180' step: '1' stop: '180' value: '0' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [704, 168.0] rotation: 0 state: true - name: phase4 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@2: 3, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-180' step: '1' stop: '180' value: '0' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [840, 168.0] rotation: 0 state: true - name: phase5 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@2: 4, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-180' step: '1' stop: '180' value: '0' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [976, 168.0] rotation: 0 state: true - name: phase6 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@2: 5, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-180' step: '1' stop: '180' value: '0' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [1112, 168.0] rotation: 0 state: true - name: phase7 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@2: 6, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-180' step: '1' stop: '180' value: '0' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [1248, 168.0] rotation: 0 state: true - name: phase8 id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@2: 7, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: int start: '-180' step: '1' stop: '180' value: '0' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [1384, 168.0] rotation: 0 state: true - name: save_trace id: variable_qtgui_push_button parameters: comment: '' gui_hint: 5,2,1,1 label: '' pressed: 'True' released: 'False' type: bool value: 'False' states: bus_sink: false bus_source: false bus_structure: null coordinate: [1072, 308.0] rotation: 0 state: true - name: step_size id: variable_qtgui_range parameters: comment: '' gui_hint: 'tab@0: 0, 0, 1, 3' label: '' min_len: '200' orient: Qt.Horizontal rangeType: float start: '0.5' step: '.5' stop: '5' value: '1.5' widget: counter_slider states: bus_sink: false bus_source: false bus_structure: null coordinate: [192, 172.0] rotation: 0 state: true - name: epy_block_0 id: epy_block parameters: _source_code: "# Copyright (C) 2023 Analog Devices, Inc.\n# All rights reserved.\n\ # jon.kraft@analog.com\n# wiki.analog.com/phaser\n\n# Redistribution and use\ \ in source and binary forms, with or without modification,\n# are permitted\ \ provided that the following conditions are met:\n# - Redistributions of\ \ source code must retain the above copyright\n# notice, this list of\ \ conditions and the following disclaimer.\n# - Redistributions in binary\ \ form must reproduce the above copyright\n# notice, this list of conditions\ \ and the following disclaimer in\n# the documentation and/or other materials\ \ provided with the\n# distribution.\n# - Neither the name of Analog\ \ Devices, Inc. nor the names of its\n# contributors may be used to endorse\ \ or promote products derived\n# from this software without specific prior\ \ written permission.\n# - The use of this software may or may not infringe\ \ the patent rights\n# of one or more patent holders. This license does\ \ not release you\n# from the requirement that you obtain separate licenses\ \ from these\n# patent holders to use this software.\n# - Use of the\ \ software either in source or binary form, must be run\n# on or directly\ \ connected to an Analog Devices Inc. component.\n#\n# THIS SOFTWARE IS PROVIDED\ \ BY ANALOG DEVICES \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n# INCLUDING,\ \ BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A\n\ # PARTICULAR PURPOSE ARE DISCLAIMED.\n#\n# IN NO EVENT SHALL ANALOG DEVICES\ \ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n# EXEMPLARY, OR\ \ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, INTELLECTUAL PROPERTY\n\ # RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\ \ PROFITS; OR\n# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\ \ LIABILITY, WHETHER IN CONTRACT,\n# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\ \ OR OTHERWISE) ARISING IN ANY WAY OUT OF\n# THE USE OF THIS SOFTWARE, EVEN\ \ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\n\"\"\"\nEmbedded Python\ \ Blocks:\n\nEach time this file is saved, GRC will instantiate the first class\ \ it finds\nto get ports and parameters of your block. The arguments to __init__\ \ will\nbe the parameters. All of them are required to have default values!\n\ \"\"\"\nimport sys\nimport pickle\nimport os\n\nimport adi\nimport numpy as\ \ np\nsys.path.append('/home/analog/pyadi-iio/examples/phaser/')\nimport SDR_functions\ \ as SDR # import the Pluto SDR functions\nimport ADAR_pyadi_functions_edited1\ \ as ADAR # import the ADAR1000 functions\nfrom gnuradio import gr\n\nclass\ \ blk(gr.sync_block):\n def __init__(self, step_size=3, save_trace=False,\ \ clear_trace=False,\n gain1=100, gain2=100, gain3=100, gain4=100,\ \ gain5=100, gain6=100, gain7=100, gain8=100,\n phase1=0, phase2=0,\ \ phase3=0, phase4=0, phase5=0, phase6=0, phase7=0, phase8=0,\n \ \ null_enable_1 = False, null_angle_1=0, null_enable_2 = False, null_angle_2=0):\ \ # only default arguments here\n \"\"\"arguments to this function show\ \ up as parameters in GRC\"\"\"\n gr.sync_block.__init__(\n \ \ self,\n name='Phaser Python Control Block', # will show up\ \ in GRC\n in_sig=[],\n out_sig=[np.complex64, np.complex64]\n\ \ )\n # if an attribute with the same name as a parameter is found,\n\ \ # a callback is registered (properties work, too)\n \n \ \ # =============================================================================\n\ \ # User parameters\n # =============================================================================\n\ \ self.step_size = step_size # steering angle step size (in degrees)\n\ \ self.save_trace = save_trace\n self.clear_trace = clear_trace\n\ \ self.saved_trace = np.ones(4094)*(0-100000j)\n \n self.gain1=gain1\n\ \ self.gain2=gain2\n self.gain3=gain3\n self.gain4=gain4\n\ \ self.gain5=gain5\n self.gain6=gain6\n self.gain7=gain7\n\ \ self.gain8=gain8\n \n self.phase1=phase1\n self.phase2=phase2\n\ \ self.phase3=phase3\n self.phase4=phase4\n self.phase5=phase5\n\ \ self.phase6=phase6\n self.phase7=phase7\n self.phase8=phase8\n\ \ \n self.null_enable_1 = null_enable_1\n self.null_angle_1\ \ = null_angle_1\n self.null_enable_2 = null_enable_2\n self.null_angle_2\ \ = null_angle_2\n \n rpi_ip = \"ip:phaser.local\" # default\ \ IP address of Phaser's Raspberry Pi\n sdr_ip = \"ip:192.168.2.1\" \ \ # default Pluto IP address\n \n # select which signal source\ \ to use\n # HB100 (external source)\n # OUT1 (transmit freq\ \ is set in config.py)\n # OUT2 (transmit freq is set in config.py)\n\ \ SignalSource = 'HB100' # 'HB100', 'OUT1', or 'OUT2'\n \n\ \ # config.py has all the key parameters that you might want to modify\n\ \ try:\n import config as config\n except:\n \ \ print(\"Make sure config.py is in this directory\")\n sys.exit(0)\n\ \ \n \n # =============================================================================\n\ \ # Variables setup\n # =============================================================================\n\ \ \n # if using HB100, load the signal frequency from \"phaser_find_hb100.py\"\ \ output file\n if SignalSource == 'HB100':\n try:\n \ \ #with open(\"/home/analog/pyadi-iio/examples/phaser/hb100_freq_val.pkl\"\ , \"rb\") as file1: \n with open(\"hb100_freq_val.pkl\", \"rb\"\ ) as file1: \n config.SignalFreq = pickle.load(file1)\n \ \ print(\"Found signal freq file, \", config.SignalFreq/1e9, \"\ \ GHz\")\n except:\n print(\"No signal freq found,\ \ keeping at \", config.SignalFreq/1e9, \" GHz\")\n \n \"\"\"\ SET DEFAULT VALUES\"\"\"\n sdr_address = sdr_ip\n self.SignalFreq\ \ = config.SignalFreq\n Tx_freq = config.Tx_freq # Pluto's Tx LO freq.\n\ \ Rx_freq = config.Rx_freq # Pluto's Rx LO freq\n LO_freq = self.SignalFreq\ \ + Rx_freq # freq of the LTC5548 mixer LO\n SampleRate = config.SampleRate\n\ \ Rx_gain = config.Rx_gain\n Tx_gain = config.Tx_gain\n \ \ self.RxPhase1 = config.Rx1_cal\n self.RxPhase2 = config.Rx2_cal\n\ \ self.RxPhase3 = config.Rx3_cal\n self.RxPhase4 = config.Rx4_cal\n\ \ self.RxPhase5 = config.Rx5_cal\n self.RxPhase6 = config.Rx6_cal\n\ \ self.RxPhase7 = config.Rx7_cal\n self.RxPhase8 = config.Rx8_cal\n\ \ self.phase_step_size = 2.8125\n self.c = 299792458 # speed\ \ of light in m/s\n self.d = config.d # antenna spacing for phaser\ \ is 14mm\n self.gainList = np.array([\n self.gain1,\n \ \ self.gain2,\n self.gain3,\n self.gain4,\n \ \ self.gain5,\n self.gain6,\n self.gain7,\n \ \ self.gain8\n ])\n self.phaseList = np.array([\n\ \ self.RxPhase1,\n self.RxPhase2,\n self.RxPhase3,\n\ \ self.RxPhase4,\n self.RxPhase5,\n self.RxPhase6,\n\ \ self.RxPhase7,\n self.RxPhase8\n ]) \ \ \n \n # Use the onboard VCO to generate the LO? Or apply\ \ source to EXT_LO?\n gpios = adi.one_bit_adc_dac(rpi_ip)\n gpios.gpio_vctrl_1\ \ = 1 # 1=Use onboard PLL/LO source (0=use external LO input)\n gpios.gpio_vctrl_2\ \ = 1 # 1=Send LO to transmit circuitry (0=disable Tx path and send LO to\ \ LO_OUT)\n \n # setup GPIOs to control if Tx is output on OUT1\ \ or OUT2\n gpios.gpio_div_mr = 1\n gpios.gpio_div_s0 = 0\n \ \ gpios.gpio_div_s1 = 0\n gpios.gpio_div_s2 = 0\n attempt\ \ = True\n while attempt:\n try:\n # Initialize\ \ Pluto\n self.sdr = SDR.SDR_init(\n sdr_address,\n\ \ SampleRate,\n Tx_freq,\n \ \ Rx_freq,\n Rx_gain,\n Tx_gain,\n\ \ config.buffer_size,\n )\n \ \ SDR.SDR_LO_init(rpi_ip, LO_freq) # Set Phaser's ADF4159 to the LO_freq\n\ \ \n # Intialize the ADAR1000 receive array\n\ \ self.rx_array = adi.adar1000_array(\n uri=rpi_ip,\n\ \ chip_ids=[\"BEAM0\", \"BEAM1\"], # these are the ADAR1000s'\ \ labels in the device tree\n device_map=[[1], [2]],\n \ \ element_map=[[1, 2, 3, 4, 5, 6, 7, 8]],\n \ \ device_element_map={\n 1: [7, 8, 5, 6], # i.e.\ \ channel2 of device1 (BEAM0), maps to element 8\n 2:\ \ [3, 4, 1, 2],\n },\n )\n \ \ attempt = False\n print('Connected to Phaser')\n \ \ except:\n print('Could not connect to Phaser')\n \ \ continue\n for device in self.rx_array.devices.values():\n\ \ ADAR.ADAR_init(device) # resets the ADAR1000\n ADAR.ADAR_set_mode(device,\ \ \"rx\") # ADAR1000s on Phaser are receive only, so mode is always \"rx\"\n\ \ ADAR.ADAR_set_Taper(\n self.rx_array,\n self.gainList\n\ \ )\n # Set transmitter to either OUT1 or OUT2 SMA port. \ \ Or disable if using HB100\n if SignalSource == 'OUT1': # use Phaser's\ \ OUT1 SMA port as the transmitter\n gpios.gpio_tx_sw = 1 #\ \ 0=OUT2, 1=OUT1\n gpios.gpio_vctrl_2 = 1 # 1=Send LO to transmit\ \ circuitry\n elif SignalSource == 'OUT2': # use OUT2 as the transmitter\n\ \ gpios.gpio_tx_sw = 0 # 0=OUT2, 1=OUT1\n gpios.gpio_vctrl_2\ \ = 1 # 1=Send LO to transmit circuitry\n else: # use HB100 as the\ \ transmit signal source\n gpios.gpio_tx_sw = 0 \n SDR.SDR_setTx(self.sdr,\ \ -80) # disable tx output by attenuating it\n \n def ConvertPhaseToSteerAngle(self,\ \ PhDelta):\n # steering angle theta = arcsin(c*deltaphase/(2*pi*f*d)\n\ \ value1 = (self.c * np.radians(np.abs(PhDelta))) / (\n 2\ \ * 3.14159 * (self.SignalFreq) * self.d)\n clamped_value1 = max(min(1,\ \ value1), -1) # arcsin argument must be between 1 and -1\n theta =\ \ np.degrees(np.arcsin(clamped_value1))\n if PhDelta >= 0:\n \ \ SteerAngle = theta # positive PhaseDelta covers 0deg to 90 deg\n \ \ else:\n SteerAngle = -theta # negative phase delta covers 0\ \ deg to -90 deg\n return SteerAngle \n \n def dbfs(self,\ \ raw_data):\n # function to convert IQ samples to FFT plot, scaled in\ \ dBFS\n NumSamples = len(raw_data)\n win = np.hamming(NumSamples)\n\ \ y = raw_data * win\n s_fft = np.fft.fft(y) / np.sum(win)\n \ \ s_shift = np.fft.fftshift(s_fft)\n s_dbfs = 20*np.log10(np.abs(s_shift)/(2**11))\ \ # Pluto is a signed 12 bit ADC, so use 2^11 to convert to dBFS\n \ \ return s_dbfs\n \n def updateGainPhase(self):\n self.gainList\ \ = np.array([\n self.gain1,\n self.gain2,\n \ \ self.gain3,\n self.gain4,\n self.gain5,\n \ \ self.gain6,\n self.gain7,\n self.gain8\n \ \ ]) # gains from 0 to 100\n \n self.phaseList = np.array([\n\ \ self.RxPhase1 + self.phase1,\n self.RxPhase2 + self.phase2,\n\ \ self.RxPhase3 + self.phase3,\n self.RxPhase4 + self.phase4,\n\ \ self.RxPhase5 + self.phase5,\n self.RxPhase6 + self.phase6,\n\ \ self.RxPhase7 + self.phase7,\n self.RxPhase8 + self.phase8\n\ \ ])\n \n def null_vec(self, null_angle):\n null_phase\ \ = 2*np.pi*self.SignalFreq*self.d*np.sin(np.deg2rad(null_angle))/self.c\n \ \ null_phases = np.array([0,1,2,3,4,5,6,7])*null_phase # create array\ \ with phase shifts for null angle\n polar_null_phases = np.exp(1j *\ \ null_phases) # null steer vector\n return polar_null_phases\n\n \ \ def work(self, input_items, output_items):\n save_this_trace=False\n\ \ clear_this_trace=False\n enable_null_1 = self.null_enable_1\n\ \ enable_null_2 = self.null_enable_2\n if enable_null_1 == True:\n\ \ polar_null_phases = self.null_vec(self.null_angle_1)\n \ \ wn1 = self.gainList * polar_null_phases # beam weights for null direction\n\ \ if enable_null_2 == True:\n polar_null_phases = self.null_vec(self.null_angle_2)\n\ \ wn2 = self.gainList * polar_null_phases # beam weights for null\ \ direction\n \n SteerValues = np.arange(-90, 90 + self.step_size,\ \ self.step_size)\n # Phase delta = 2*Pi*d*sin(theta)/lambda = 2*Pi*d*sin(theta)*f/c\n\ \ PhaseValues = np.degrees(\n 2*np.pi*self.d* np.sin(np.radians(SteerValues))\n\ \ * self.SignalFreq / self.c)\n self.updateGainPhase()\n \ \ \n for x in range(len(PhaseValues)):\n if self.save_trace\ \ == True:\n save_this_trace = True\n if self.clear_trace\ \ == True:\n clear_this_trace = True\n PhDelta = PhaseValues[x]\n\ \ steer_phases = (np.array([0,1,2,3,4,5,6,7])*PhDelta) % 360\n \ \ wd = self.gainList * np.exp(1j * np.deg2rad(steer_phases))\n \ \ # wd is the beam weights for desired steering direction\n \ \ # details here: https://www.mathworks.com/help/phased/ug/array-pattern-synthesis.html\n\ \ \n if enable_null_1 == True:\n wn1_herm\ \ = np.conjugate(wn1.reshape(1,len(wn1)))\n rn = wn1_herm @ wd\ \ / (wn1_herm @ wn1)\n wd = wd - wn1 * rn\n \n\ \ if enable_null_2 == True:\n wn2_herm = np.conjugate(wn2.reshape(1,len(wn2)))\n\ \ rn = wn2_herm @ wd / (wn2_herm @ wn2)\n wd =\ \ wd - wn2 * rn\n \n new_gains = np.abs(wd)\n \ \ new_gains = np.clip(new_gains, 0, 100)\n new_phases = np.angle(wd)\n\ \ new_phases = np.rad2deg(new_phases)\n \n \ \ ADAR.ADAR_set_Taper(\n self.rx_array,\n new_gains\n\ \ )\n ADAR.ADAR_set_Phase(\n self.rx_array,\n\ \ 0,\n self.phase_step_size,\n \ \ new_phases\n )\n \n data = self.sdr.rx()\n\ \ data_sum = data[0]+data[1]\n sum_dbfs = self.dbfs(data_sum)\n\ \ peak_dbfs = max(sum_dbfs)\n output_items[0][x] = (1*(self.ConvertPhaseToSteerAngle(PhDelta))\ \ + 1j*peak_dbfs)\n output_items[1][x] = 0-10000000j\n \ \ \n for x in range(len(output_items[0])-len(SteerValues)):\n \ \ # fill in the empty parts of the array with large negative numbers to\ \ make it plot nicely\n output_items[0][len(SteerValues)+x] = 0-10000000j\n\ \ if save_this_trace==True:\n for x in range(len(self.saved_trace)):\n\ \ self.saved_trace[x] = output_items[0][x]\n if clear_this_trace==True:\n\ \ self.saved_trace = np.ones(4094)*(0-100000j)\n for x in\ \ range(len(self.saved_trace)):\n output_items[1][x] = self.saved_trace[x]\n\ \n return len(output_items[0])\n" affinity: '' alias: '' clear_trace: clear_trace comment: '' gain1: gain1 gain2: gain2 gain3: gain3 gain4: gain4 gain5: gain5 gain6: gain6 gain7: gain7 gain8: gain8 maxoutbuf: '0' minoutbuf: '0' null_angle_1: null_angle_1 null_angle_2: null_angle_2 null_enable_1: null_enable_1 null_enable_2: null_enable_2 phase1: phase1 phase2: phase2 phase3: phase3 phase4: phase4 phase5: phase5 phase6: phase6 phase7: phase7 phase8: phase8 save_trace: save_trace step_size: step_size states: _io_cache: ('Phaser Python Control Block', 'blk', [('step_size', '3'), ('save_trace', 'False'), ('clear_trace', 'False'), ('gain1', '100'), ('gain2', '100'), ('gain3', '100'), ('gain4', '100'), ('gain5', '100'), ('gain6', '100'), ('gain7', '100'), ('gain8', '100'), ('phase1', '0'), ('phase2', '0'), ('phase3', '0'), ('phase4', '0'), ('phase5', '0'), ('phase6', '0'), ('phase7', '0'), ('phase8', '0'), ('null_enable_1', 'False'), ('null_angle_1', '0'), ('null_enable_2', 'False'), ('null_angle_2', '0')], [], [('0', 'complex', 1), ('1', 'complex', 1)], 'arguments to this function show up as parameters in GRC', ['clear_trace', 'gain1', 'gain2', 'gain3', 'gain4', 'gain5', 'gain6', 'gain7', 'gain8', 'null_angle_1', 'null_angle_2', 'null_enable_1', 'null_enable_2', 'phase1', 'phase2', 'phase3', 'phase4', 'phase5', 'phase6', 'phase7', 'phase8', 'save_trace', 'step_size']) bus_sink: false bus_source: false bus_structure: null coordinate: [184, 300.0] rotation: 0 state: true - name: qtgui_const_sink_x_0 id: qtgui_const_sink_x parameters: affinity: '' alias: '' alpha1: '1.0' alpha10: '1.0' alpha2: '1.0' alpha3: '1.0' alpha4: '1.0' alpha5: '1.0' alpha6: '1.0' alpha7: '1.0' alpha8: '1.0' alpha9: '1.0' autoscale: 'False' axislabels: 'True' color1: '"blue"' color10: '"red"' color2: '"red"' color3: '"red"' color4: '"red"' color5: '"red"' color6: '"red"' color7: '"red"' color8: '"red"' color9: '"red"' comment: '' grid: 'True' gui_hint: 0,2,5,3 label1: '' label10: '' label2: '' label3: '' label4: '' label5: '' label6: '' label7: '' label8: '' label9: '' legend: 'False' marker1: '-1' marker10: '0' marker2: '-1' marker3: '0' marker4: '0' marker5: '0' marker6: '0' marker7: '0' marker8: '0' marker9: '0' name: '""' nconnections: '2' size: '4094' style1: '1' style10: '0' style2: '1' style3: '0' style4: '0' style5: '0' style6: '0' style7: '0' style8: '0' style9: '0' tr_chan: '0' tr_level: '0.0' tr_mode: qtgui.TRIG_MODE_FREE tr_slope: qtgui.TRIG_SLOPE_POS tr_tag: '""' type: complex update_time: '0.10' width1: '2' width10: '1' width2: '1' width3: '1' width4: '1' width5: '1' width6: '1' width7: '1' width8: '1' width9: '1' xmax: '89' xmin: '-89' ymax: '0' ymin: '-75' states: bus_sink: false bus_source: false bus_structure: null coordinate: [424, 464.0] rotation: 0 state: true - name: tab id: qtgui_tab_widget parameters: alias: '' comment: '' gui_hint: 0,0,6,2 label0: control label1: gain label10: Tab 10 label11: Tab 11 label12: Tab 12 label13: Tab 13 label14: Tab 14 label15: Tab 15 label16: Tab 16 label17: Tab 17 label18: Tab 18 label19: Tab 19 label2: phase label3: null steering label4: Tab 4 label5: Tab 5 label6: Tab 6 label7: Tab 7 label8: Tab 8 label9: Tab 9 num_tabs: '4' states: bus_sink: false bus_source: false bus_structure: null coordinate: [200, 36.0] rotation: 0 state: true connections: - [epy_block_0, '0', qtgui_const_sink_x_0, '0'] - [epy_block_0, '1', qtgui_const_sink_x_0, '1'] metadata: file_format: 1
I couldn't make it run. The "Phaser Python Control Block" from GNURadio says "Can't interpret source code: module 'iio' has no attribute 'Context'
I did the following workarounds:
-In "epy_block_0.py" I changed "ADAR_pyadi_functions_edited1" by ADAR_pyadi_functions, as this is the standar name of the folder it comes with the installation.
-Also in "epy_block_0.py" used the workaround for the conflict between gr-iio and pyadi-iio, adding "import iiopy as iio" as you did in the ADAR1000 GRC files.
I save it all.
Then I tried to run the GRC file again the error still. I also checked, that the "epy_block" (using open editor option in the block) within GRC did not implemented the changes I made (they are visible only in the 'epy_block_0.py' file of the folder), I do not know what is going on.
Ok, it could be that something has changed with the new GRC version. By end of Aug, I'll have looked into this and will publish a guide on how to set this up. But in the meantime, you can see from my scripts what the general flow is -- we're just using the GRC python module/block to implement pyadi-iio code. So perhaps start with a simpler setup and then you'll be able to fix what is broken as you progress.