From 7a3a91229387193b78b2d2245b0808ced95ec7c0 Mon Sep 17 00:00:00 2001 From: Jon Kraft Date: Tue, 11 Jul 2023 15:31:40 -0600 Subject: [PATCH] Add files via upload --- PythonExamples/ADAR_pyadi_functions.py | 117 + PythonExamples/SDR_functions.py | 2699 ++++++++++++++++++++++++ PythonExamples/beamsteer.py | 248 +++ 3 files changed, 3064 insertions(+) create mode 100644 PythonExamples/ADAR_pyadi_functions.py create mode 100644 PythonExamples/SDR_functions.py create mode 100644 PythonExamples/beamsteer.py diff --git a/PythonExamples/ADAR_pyadi_functions.py b/PythonExamples/ADAR_pyadi_functions.py new file mode 100644 index 0000000..9aea940 --- /dev/null +++ b/PythonExamples/ADAR_pyadi_functions.py @@ -0,0 +1,117 @@ +# ADAR_functions.py + +import pickle +import numpy as np + +verbose = True + + +def ADAR_init(beam): + # Initialize the ADAR1000 + beam.reset() # Performs a soft reset of the device (writes 0x81 to reg 0x00) + beam._ctrl.reg_write( + 0x400, 0x55 + ) # This trims the LDO value to approx. 1.8V (to the center of its range) + + beam.sequencer_enable = False + beam.beam_mem_enable = False # RAM control vs SPI control of the beam state, reg 0x38, bit 6. False sets bit high and SPI control + beam.bias_mem_enable = False # RAM control vs SPI control of the bias state, reg 0x38, bit 5. False sets bit high and SPI control + beam.pol_state = False # Polarity switch state, reg 0x31, bit 0. True outputs -5V, False outputs 0V + beam.pol_switch_enable = ( + False # Enables switch driver for ADTR1107 switch, reg 0x31, bit 3 + ) + beam.tr_source = "spi" # TR source for chip, reg 0x31 bit 2. 'external' sets bit high, 'spi' sets bit low + beam.tr_spi = ( + "rx" # TR SPI control, reg 0x31 bit 1. 'tx' sets bit high, 'rx' sets bit low + ) + beam.tr_switch_enable = True # Switch driver for external switch, reg0x31, bit 4 + beam.external_tr_polarity = True # Sets polarity of TR switch compared to TR state of ADAR1000. True outputs 0V in Rx mode + + beam.rx_vga_enable = True # Enables Rx VGA, reg 0x2E, bit 0. + beam.rx_vm_enable = True # Enables Rx VGA, reg 0x2E, bit 1. + beam.rx_lna_enable = True # Enables Rx LNA, reg 0x2E, bit 2. + beam.rx_lna_bias_current = 8 # Sets the LNA bias to the middle of its range + beam.rx_vga_vm_bias_current = 22 # Sets the VGA and vector modulator bias. + + +def ADAR_update_Rx(beam): + beam.latch_rx_settings() # Loads Rx vectors from SPI. Writes 0x01 to reg 0x28. + + +def ADAR_update_Tx(beam): + beam.latch_tx_settings() # Loads Tx vectors from SPI. Writes 0x02 to reg 0x28. + + +def ADAR_set_mode(beam, mode): + if mode == "rx": + # Configure the device for Rx mode + beam.mode = ( + "rx" # Mode of operation, bit 5 of reg 0x31. "rx", "tx", or "disabled" + ) + # print("When TR pin is low, ADAR1000 is in Rx mode.") + # beam._ctrl.reg_write(0x031, 180) #Enables T/R switch control. When TR is low, ADAR1000 is Rx mode + SELF_BIASED_LNAs = True + if SELF_BIASED_LNAs: + beam.lna_bias_out_enable = False # Allow the external LNAs to self-bias + else: + beam.lna_bias_on = -0.7 # Set the external LNA bias + # Enable the Rx path for each channel + for channel in beam.channels: + channel.rx_enable = True + + +def ADAR_set_Taper(array, gainList): + for index, element in enumerate(array.elements.values()): + element.rx_gain = int(gainList[index] * 127 / 100 * gcal[index]) + element.rx_attenuator = not bool(gainList[index]) + array.latch_rx_settings() + + +def ADAR_set_Phase( + array, + PhDelta, + phase_step_size, + phaseList +): + for index, element in enumerate(array.elements.values()): + element.rx_phase = ( + (np.rint(PhDelta * index / phase_step_size) * phase_step_size) + phaseList[index] + pcal[index] + ) % 360 + array.latch_rx_settings() + + +def load_gain_cal(filename="gain_cal_val.pkl"): + """ Load gain calibrated value, if not calibrated set all channel gain to maximum. + parameters: + filename: type=string + Provide path of gain calibration file + """ + try: + with open(filename, "rb") as file1: + return pickle.load(file1) # Load gain cal values + except FileNotFoundError: + print("file not found, loading default (all gain at maximum)") + return [1.0] * 8 # .append(0x7F) + + +def load_phase_cal(filename="phase_cal_val.pkl"): + """ Load phase calibrated value, if not calibrated set all channel phase correction to 0. + parameters: + filename: type=string + Provide path of phase calibration file + """ + + try: + with open(filename, "rb") as file: + return pickle.load(file) # Load gain cal values + except FileNotFoundError: + print("file not found, loading default (no phase shift)") + return [0.0] * 8 # .append(0) # if it fails load default value i.e. 0 + + +gcal = load_gain_cal() +pcal = load_phase_cal() + +if verbose == True: + print("Gain cal: ", gcal) + print("Phase cal: ", pcal) diff --git a/PythonExamples/SDR_functions.py b/PythonExamples/SDR_functions.py new file mode 100644 index 0000000..d16a0bc --- /dev/null +++ b/PythonExamples/SDR_functions.py @@ -0,0 +1,2699 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pyadi-iio/examples/phaser/SDR_functions.py at phaser · analogdevicesinc/pyadi-iio + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ Skip to content + + + + + + + + + + + + +
+
+
+ + + + + +
+ +
+
+
+

+ Global navigation +

+
+
+
+
+ +
+
+
+
+ +
+ + + + +
+
+ +
+ +
+
+ + + +
+
+ + +
+ +
+
+
+

+ Navigate back to +

+
+
+ +
+
+
+ + +
+
+ + +
+ +
+
+ + +
+ + +
Create new... +
+ + +
+
+ +
+ + Issues +
+
+ + Pull requests +
+
+ +
+ + + + + + + + + + + Notifications + +
+ + + +
+ + + + + +
+ +
+
+
+

+ Account menu +

+
+
+ + + jonkraft + + + + Jon Kraft + +
+
+
+
+
+ +
+
+
+
+ + +
+ +
+
+ +
+
+
+ +
+ + + + + +
+
+
+ + + +
+ + + + +
+ +
+ + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ +
+ + + + analogdevicesinc  /   + pyadi-iio  /   + +
+
+ + + +
+ + +
+
+ Clear Command Palette +
+
+ + + +
+
+ Tip: + Type # to search pull requests +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type # to search issues +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type # to search discussions +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type ! to search projects +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type @ to search teams +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type @ to search people and organizations +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type > to activate command mode +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Go to your accessibility settings to change your keyboard shortcuts +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type author:@me to search your content +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type is:pr to filter to pull requests +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type is:issue to filter to issues +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type is:project to filter to projects +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type is:open to filter to open content +
+
+ Type ? for help and tips +
+
+
+ +
+ +
+
+ We’ve encountered an error and some results aren't available at this time. Type a new search or try again later. +
+
+ + No results matched your search + + + + + + + + + + +
+ + + + + Search for issues and pull requests + + # + + + + Search for issues, pull requests, discussions, and projects + + # + + + + Search for organizations, repositories, and users + + @ + + + + Search for projects + + ! + + + + Search for files + + / + + + + Activate command mode + + > + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Filter to pull requests + + # is:pr + + + + Filter to issues + + # is:issue + + + + Filter to discussions + + # is:discussion + + + + Filter to projects + + # is:project + + + + Filter to open issues, pull requests, and discussions + + # is:open + + + + + + + + + + + + + + + + +
+
+
+ +
+ + + + + + + + + + +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + +
+ Open in github.dev + Open in a new github.dev tab + Open in codespace + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + diff --git a/PythonExamples/beamsteer.py b/PythonExamples/beamsteer.py new file mode 100644 index 0000000..21955a6 --- /dev/null +++ b/PythonExamples/beamsteer.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +# Must use Python 3 +# Copyright (C) 2022 Analog Devices, Inc. +# +# All rights reserved. +# +# 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. + +''' Simple Beamforming Example Using Phaser and Python''' + +# ============================================================================= +# Import statements +# ============================================================================= +import adi +import ADAR_pyadi_functions as ADAR # import the ADAR1000 functions +import SDR_functions as SDR # import the Pluto SDR functions + +import sys +import pickle +import matplotlib.pyplot as plt +import numpy as np + + + +# ============================================================================= +# User parameters +# ============================================================================= + +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("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 +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 = SignalFreq + Rx_freq # freq of the LTC5548 mixer LO +SampleRate = config.SampleRate +Rx_gain = config.Rx_gain +Tx_gain = config.Tx_gain +RxGain1 = 100 +RxGain2 = 100 +RxGain3 = 100 +RxGain4 = 100 +RxGain5 = 100 +RxGain6 = 100 +RxGain7 = 100 +RxGain8 = 100 +RxPhase1 = config.Rx1_cal +RxPhase2 = config.Rx2_cal +RxPhase3 = config.Rx3_cal +RxPhase4 = config.Rx4_cal +RxPhase5 = config.Rx5_cal +RxPhase6 = config.Rx6_cal +RxPhase7 = config.Rx7_cal +RxPhase8 = config.Rx8_cal +phase_step_size = 2.8125 +c = 299792458 # speed of light in m/s +d = config.d # antenna spacing for phaser is 14mm +gainList = [RxGain1, RxGain2, RxGain3, RxGain4, + RxGain5, RxGain6, RxGain7, RxGain8] +phaseList = [RxPhase1, RxPhase2, RxPhase3, RxPhase4, + RxPhase5, RxPhase6, RxPhase7, RxPhase8] + + +# ============================================================================= +# Hardware setup +# ============================================================================= + +# 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 + +# Initialize Pluto +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 +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], + }, + ) +for device in 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( + rx_array, + 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(sdr, -80) # disable tx output by attenuating it + + +# ============================================================================= +# Define Common Functions +# ============================================================================= +def ConvertPhaseToSteerAngle(PhDelta): + # steering angle theta = arcsin(c*deltaphase/(2*pi*f*d) + value1 = (c * np.radians(np.abs(PhDelta))) / ( + 2 * 3.14159 * (SignalFreq) * 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(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 + + +# ============================================================================================= +# Loop through all the steering angles and record the peak FFT amplitude at each steering angle +# ============================================================================================= +angles = [] # stores the list of steering angles +peak_gains = [] # stores the peak FFT gain received for each steering angle + +steering_step = 1 # steering angle step size (in degrees) +SteerValues = np.arange(-90, 90 + steering_step, steering_step) +# Phase delta = 2*Pi*d*sin(theta)/lambda = 2*Pi*d*sin(theta)*f/c +PhaseValues = np.degrees( + 2*np.pi*d* np.sin(np.radians(SteerValues)) + * SignalFreq / c +) + +for PhDelta in PhaseValues: + ADAR.ADAR_set_Phase( + rx_array, + PhDelta, + phase_step_size, + phaseList + ) + + data = sdr.rx() + data_sum = data[0]+data[1] + sum_dbfs = dbfs(data_sum) + peak_dbfs = max(sum_dbfs) + angles.append(ConvertPhaseToSteerAngle(PhDelta)) + peak_gains.append(peak_dbfs) + + +# ============================================================================= +# Plotting results +# ============================================================================= + +plt.figure(1) +plt.subplot(2, 1, 1) +plt.title("Beam sweep plot") +plt.plot(angles, peak_gains, marker="o", ms=2) +plt.xlabel("Steering angle (deg)") +plt.ylabel("Peak Amplitude (dBFS)") +plt.tight_layout() +plt.show() + + +