mirror of
https://github.com/jonkraft/PhaserRadarLabs.git
synced 2026-01-09 12:37:54 -05:00
Add files via upload
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# %%
|
||||
# Copyright (C) 2019 Analog Devices, Inc.
|
||||
# Copyright (C) 2024 Analog Devices, Inc.
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
@@ -33,30 +33,41 @@
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''FMCW Range Doppler Demo with Phaser (CN0566)
|
||||
Jon Kraft, Jan 20 2024'''
|
||||
Jon Kraft, June 9 2024'''
|
||||
|
||||
# %%
|
||||
# Imports
|
||||
import sys
|
||||
import time
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import time
|
||||
plt.close('all')
|
||||
|
||||
'''This script uses the new Pluto TDD engine
|
||||
As of Jan 2024, this is in the "dev_phaser_merge" branch of https://github.com/analogdevicesinc/pyadi-iio
|
||||
As of March 2024, this is in the main branch of https://github.com/analogdevicesinc/pyadi-iio
|
||||
Also, make sure your Pluto firmware is updated to rev 0.38 (or later)
|
||||
'''
|
||||
#sys.path.insert(0,'/home/analog/cn0566_merge/pyadi-iio/')
|
||||
import adi
|
||||
print(adi.__version__)
|
||||
|
||||
# Parameters
|
||||
'''Key Parameters'''
|
||||
sample_rate = 5e6
|
||||
center_freq = 2.1e9
|
||||
signal_freq = int(sample_rate/10)
|
||||
ramp_time = 200 # us
|
||||
signal_freq = 100e3
|
||||
rx_gain = 40 # must be between -3 and 70
|
||||
tx_gain = -0 # must be between 0 and -88
|
||||
output_freq = 9.9e9
|
||||
chirp_BW = 500e6
|
||||
ramp_time = 500 # us
|
||||
num_chirps = 128
|
||||
plot_data = True
|
||||
save_data = False # saves data for later processing (use "Range_Doppler_Processing.py")
|
||||
f = "phaserRadarData.npy"
|
||||
|
||||
# %%
|
||||
""" Program the basic hardware settings
|
||||
"""
|
||||
# Instantiate all the Devices
|
||||
rpi_ip = "ip:phaser.local" # IP address of the Raspberry Pi
|
||||
sdr_ip = "ip:192.168.2.1" # "192.168.2.1, or pluto.local" # IP address of the Transceiver Block
|
||||
@@ -65,73 +76,60 @@ my_phaser = adi.CN0566(uri=rpi_ip, sdr=my_sdr)
|
||||
|
||||
# Initialize both ADAR1000s, set gains to max, and all phases to 0
|
||||
my_phaser.configure(device_mode="rx")
|
||||
my_phaser.element_spacing = 0.014
|
||||
my_phaser.load_gain_cal()
|
||||
my_phaser.load_phase_cal()
|
||||
for i in range(0, 8):
|
||||
my_phaser.set_chan_phase(i, 0)
|
||||
|
||||
gain_list = [8, 34, 84, 127, 127, 84, 34, 8] # Blackman taper
|
||||
gain_list = [127] * 8
|
||||
#gain_list = [8, 34, 84, 127, 127, 84, 34, 8] # Blackman taper
|
||||
for i in range(0, len(gain_list)):
|
||||
my_phaser.set_chan_gain(i, gain_list[i], apply_cal=True)
|
||||
|
||||
# Setup Raspberry Pi GPIO states
|
||||
try:
|
||||
my_phaser._gpios.gpio_tx_sw = 0 # 0 = TX_OUT_2, 1 = TX_OUT_1
|
||||
my_phaser._gpios.gpio_vctrl_1 = 1 # 1=Use onboard PLL/LO source (0=disable PLL and VCO, and set switch to use external LO input)
|
||||
my_phaser._gpios.gpio_vctrl_2 = 1 # 1=Send LO to transmit circuitry (0=disable Tx path, and send LO to LO_OUT)
|
||||
except:
|
||||
my_phaser.gpios.gpio_tx_sw = 0 # 0 = TX_OUT_2, 1 = TX_OUT_1
|
||||
my_phaser.gpios.gpio_vctrl_1 = 1 # 1=Use onboard PLL/LO source (0=disable PLL and VCO, and set switch to use external LO input)
|
||||
my_phaser.gpios.gpio_vctrl_2 = 1 # 1=Send LO to transmit circuitry (0=disable Tx path, and send LO to LO_OUT)
|
||||
my_phaser._gpios.gpio_tx_sw = 0 # 0 = TX_OUT_2, 1 = TX_OUT_1
|
||||
my_phaser._gpios.gpio_vctrl_1 = 1 # 1=Use onboard PLL/LO source (0=disable PLL and VCO, and set switch to use external LO input)
|
||||
my_phaser._gpios.gpio_vctrl_2 = 1 # 1=Send LO to transmit circuitry (0=disable Tx path, and send LO to LO_OUT)
|
||||
|
||||
# Configure SDR Rx
|
||||
my_sdr.sample_rate = int(sample_rate)
|
||||
my_sdr.rx_lo = int(center_freq) # set this to output_freq - (the freq of the HB100)
|
||||
my_sdr.rx_enabled_channels = [0, 1] # enable Rx1 (voltage0) and Rx2 (voltage1)
|
||||
my_sdr.rx_lo = int(center_freq)
|
||||
my_sdr.rx_enabled_channels = [0, 1] # enable Rx1 and Rx2
|
||||
my_sdr.gain_control_mode_chan0 = 'manual' # manual or slow_attack
|
||||
my_sdr.gain_control_mode_chan1 = 'manual' # manual or slow_attack
|
||||
my_sdr.rx_hardwaregain_chan0 = int(30) # must be between -3 and 70
|
||||
my_sdr.rx_hardwaregain_chan1 = int(30) # must be between -3 and 70
|
||||
my_sdr.rx_hardwaregain_chan0 = int(rx_gain) # must be between -3 and 70
|
||||
my_sdr.rx_hardwaregain_chan1 = int(rx_gain) # must be between -3 and 70
|
||||
|
||||
# Configure SDR Tx
|
||||
my_sdr.tx_lo = int(center_freq)
|
||||
my_sdr.tx_enabled_channels = [0, 1]
|
||||
my_sdr.tx_cyclic_buffer = True # must set cyclic buffer to true for the tdd burst mode. Otherwise Tx will turn on and off randomly
|
||||
my_sdr.tx_cyclic_buffer = True # must set cyclic buffer to true for the tdd burst mode
|
||||
my_sdr.tx_hardwaregain_chan0 = -88 # must be between 0 and -88
|
||||
my_sdr.tx_hardwaregain_chan1 = -0 # must be between 0 and -88
|
||||
my_sdr.tx_hardwaregain_chan1 = int(tx_gain) # must be between 0 and -88
|
||||
|
||||
# Read properties
|
||||
print("RX LO %s" % (my_sdr.rx_lo))
|
||||
|
||||
# Configure the ADF4159 Rampling PLL
|
||||
output_freq = 12.145e9
|
||||
BW = 500e6
|
||||
num_steps = ramp_time # in general it works best if there is 1 step per us
|
||||
my_phaser.frequency = int(output_freq / 4) # Output frequency divided by 4
|
||||
my_phaser.freq_dev_range = int(
|
||||
BW / 4
|
||||
) # frequency deviation range in Hz. This is the total freq deviation of the complete freq ramp
|
||||
my_phaser.freq_dev_step = int(
|
||||
(BW/4) / num_steps
|
||||
) # frequency deviation step in Hz. This is fDEV, in Hz. Can be positive or negative
|
||||
my_phaser.freq_dev_time = int(
|
||||
ramp_time
|
||||
) # total time (in us) of the complete frequency ramp
|
||||
# Configure the ADF4159 Ramping PLL
|
||||
vco_freq = int(output_freq + signal_freq + center_freq)
|
||||
BW = chirp_BW
|
||||
num_steps = int(ramp_time) # in general it works best if there is 1 step per us
|
||||
my_phaser.frequency = int(vco_freq / 4)
|
||||
my_phaser.freq_dev_range = int(BW / 4) # total freq deviation of the complete freq ramp in Hz
|
||||
my_phaser.freq_dev_step = int((BW / 4) / num_steps) # This is fDEV, in Hz. Can be positive or negative
|
||||
my_phaser.freq_dev_time = int(ramp_time) # total time (in us) of the complete frequency ramp
|
||||
print("requested freq dev time (us) = ", ramp_time)
|
||||
ramp_time = my_phaser.freq_dev_time
|
||||
print("actual freq dev time (us) = ", ramp_time)
|
||||
my_phaser.delay_word = 4095 # 12 bit delay word. 4095*PFD = 40.95 us. For sawtooth ramps, this is also the length of the Ramp_complete signal
|
||||
my_phaser.delay_clk = "PFD" # can be 'PFD' or 'PFD*CLK1'
|
||||
my_phaser.delay_start_en = 0 # delay start
|
||||
my_phaser.ramp_delay_en = 0 # delay between ramps.
|
||||
my_phaser.trig_delay_en = 0 # triangle delay
|
||||
my_phaser.ramp_mode = "single_sawtooth_burst" # ramp_mode can be: "disabled", "continuous_sawtooth", "continuous_triangular", "single_sawtooth_burst", "single_ramp_burst"
|
||||
my_phaser.sing_ful_tri = (
|
||||
0 # full triangle enable/disable -- this is used with the single_ramp_burst mode
|
||||
)
|
||||
my_phaser.sing_ful_tri = 0 # full triangle enable/disable -- this is used with the single_ramp_burst mode
|
||||
my_phaser.tx_trig_en = 1 # start a ramp with TXdata
|
||||
my_phaser.enable = 0 # 0 = PLL enable. Write this last to update all the registers
|
||||
|
||||
# %%
|
||||
""" Synchronize chirps to the start of each Pluto receive buffer
|
||||
"""
|
||||
# Configure TDD controller
|
||||
sdr_pins = adi.one_bit_adc_dac(sdr_ip)
|
||||
sdr_pins.gpio_tdd_ext_sync = True # If set to True, this enables external capture triggering using the L24N GPIO on the Pluto. When set to false, an internal trigger pulse will be generated every second
|
||||
@@ -140,67 +138,71 @@ sdr_pins.gpio_phaser_enable = True
|
||||
tdd.enable = False # disable TDD to configure the registers
|
||||
tdd.sync_external = True
|
||||
tdd.startup_delay_ms = 1
|
||||
tdd.frame_length_ms = ramp_time/1e3 + 0.2 # each GPIO toggle is spaced this far apart
|
||||
tdd.frame_length_ms = ramp_time/1e3 + 0.2 # each chirp is spaced this far apart
|
||||
tdd.burst_count = num_chirps # number of chirps in one continuous receive buffer
|
||||
|
||||
tdd.out_channel0_enable = True
|
||||
tdd.out_channel0_polarity = False
|
||||
tdd.out_channel0_on_ms = 0.01 # each GPIO pulse will be 100us (0.6ms - 0.5ms). And the first trigger will happen 0.5ms into the buffer
|
||||
tdd.out_channel0_off_ms = 0.2
|
||||
tdd.out_channel1_enable = True
|
||||
tdd.out_channel1_polarity = False
|
||||
tdd.out_channel1_on_ms = 0
|
||||
tdd.out_channel1_off_ms = 0.1
|
||||
tdd.out_channel2_enable = False
|
||||
tdd.channel[0].enable = True
|
||||
tdd.channel[0].polarity = False
|
||||
tdd.channel[0].on_ms = 0.01
|
||||
tdd.channel[0].off_ms = 0.1
|
||||
tdd.channel[1].enable = True
|
||||
tdd.channel[1].polarity = False
|
||||
tdd.channel[1].on_ms = 0
|
||||
tdd.channel[1].off_ms = 0.1
|
||||
tdd.channel[2].enable = False
|
||||
tdd.enable = True
|
||||
|
||||
# buffer size needs to be greater than the frame_time
|
||||
frame_time = tdd.frame_length_ms*tdd.burst_count # time in ms
|
||||
print("frame_time: ", frame_time, "ms")
|
||||
# From start of each ramp, how many "good" points do we want?
|
||||
# For best freq linearity, stay away from the start of the ramps
|
||||
ramp_time = int(my_phaser.freq_dev_time) # - begin_offset_time)
|
||||
ramp_time_s = ramp_time / 1e6
|
||||
begin_offset_time = 0.1 * ramp_time_s # time in seconds
|
||||
print("actual freq dev time = ", ramp_time)
|
||||
good_ramp_samples = int((ramp_time_s - begin_offset_time) * sample_rate)
|
||||
start_offset_time = tdd.channel[0].on_ms/1e3 + begin_offset_time
|
||||
start_offset_samples = int(start_offset_time * sample_rate)
|
||||
|
||||
# size the fft for the number of ramp data points
|
||||
power=8
|
||||
fft_size = int(2**power)
|
||||
num_samples_frame = int(tdd.frame_length_ms/1000*sample_rate)
|
||||
while num_samples_frame > fft_size:
|
||||
power=power+1
|
||||
fft_size = int(2**power)
|
||||
if power==18:
|
||||
break
|
||||
print("fft_size =", fft_size)
|
||||
|
||||
# Pluto receive buffer size needs to be greater than total time for all chirps
|
||||
total_time = tdd.frame_length_ms * num_chirps # time in ms
|
||||
print("Total Time for all Chirps: ", total_time, "ms")
|
||||
buffer_time = 0
|
||||
power=12
|
||||
while frame_time > buffer_time:
|
||||
while total_time > buffer_time:
|
||||
power=power+1
|
||||
buffer_size = int(2**power)
|
||||
buffer_time = buffer_size/my_sdr.sample_rate*1000 # buffer time in ms
|
||||
buffer_time = buffer_size/sample_rate*1000 # buffer time in ms
|
||||
if power==23:
|
||||
break # max pluto buffer size is 2**23, but for tdd burst mode, set to 2**22
|
||||
print("buffer_size:", buffer_size)
|
||||
my_sdr.rx_buffer_size = buffer_size
|
||||
print("buffer_time:", buffer_time, " ms")
|
||||
|
||||
# Create a sinewave waveform
|
||||
#fs = int(my_sdr.sample_rate)
|
||||
fs = sample_rate
|
||||
print("sample_rate:", fs)
|
||||
N = buffer_size
|
||||
fc = int(signal_freq / (fs / N)) * (fs / N)
|
||||
ts = 1 / float(fs)
|
||||
t = np.arange(0, N * ts, ts)
|
||||
i = np.cos(2 * np.pi * t * fc) * 2 ** 14
|
||||
q = np.sin(2 * np.pi * t * fc) * 2 ** 14
|
||||
iq = 0.9* (i + 1j * q)
|
||||
|
||||
my_sdr._ctx.set_timeout(30000)
|
||||
my_sdr._rx_init_channels()
|
||||
|
||||
# Send data
|
||||
my_sdr.tx([iq, iq])
|
||||
print("buffer_time:", buffer_time, " ms")
|
||||
|
||||
# %%
|
||||
""" Calculate ramp parameters
|
||||
"""
|
||||
PRI = tdd.frame_length_ms / 1e3
|
||||
PRF = 1 / PRI
|
||||
num_bursts = tdd.burst_count
|
||||
|
||||
# Split into frames
|
||||
N_frame = int(PRI / ts)
|
||||
N_frame = int(PRI * float(sample_rate))
|
||||
|
||||
# Obtain range-FFT x-axis
|
||||
c = 3e8
|
||||
wavelength = c / (output_freq - center_freq)
|
||||
ramp_time_s = ramp_time / 1e6
|
||||
wavelength = c / output_freq
|
||||
slope = BW / ramp_time_s
|
||||
freq = np.linspace(-fs / 2, fs / 2, N_frame)
|
||||
freq = np.linspace(-sample_rate / 2, sample_rate / 2, N_frame)
|
||||
dist = (freq - signal_freq) * c / (2 * slope)
|
||||
|
||||
# Resolutions
|
||||
@@ -211,52 +213,35 @@ v_res = wavelength / (2 * num_bursts * PRI)
|
||||
max_doppler_freq = PRF / 2
|
||||
max_doppler_vel = max_doppler_freq * wavelength / 2
|
||||
|
||||
# First ramp starts with some offset (as defined in the TDD section above)
|
||||
start_offset_time = tdd.out_channel0_on_ms/1e3
|
||||
|
||||
# From start of each ramp, how many "good" points do we want?
|
||||
# For best freq linearity, stay away from the start of the ramps
|
||||
begin_offset_time = 0.02e-3
|
||||
good_ramp_time = ramp_time_s - begin_offset_time
|
||||
good_ramp_samples = int(good_ramp_time * fs)
|
||||
start_offset_samples = int((start_offset_time+begin_offset_time)*fs)
|
||||
# %%
|
||||
""" Create a sinewave waveform for Pluto's transmitter
|
||||
"""
|
||||
# Create a sinewave waveform
|
||||
N = int(2**18)
|
||||
fc = int(signal_freq)
|
||||
ts = 1 / float(sample_rate)
|
||||
t = np.arange(0, N * ts, ts)
|
||||
i = np.cos(2 * np.pi * t * fc) * 2 ** 14
|
||||
q = np.sin(2 * np.pi * t * fc) * 2 ** 14
|
||||
iq = 0.9* (i + 1j * q)
|
||||
|
||||
# transmit data from Pluto
|
||||
my_sdr._ctx.set_timeout(30000)
|
||||
my_sdr._rx_init_channels()
|
||||
my_sdr.tx([iq, iq])
|
||||
|
||||
|
||||
# %%
|
||||
range_doppler_fig, ax = plt.subplots(figsize=(14, 7))
|
||||
|
||||
extent = [-max_doppler_vel, max_doppler_vel, dist.min(), dist.max()]
|
||||
|
||||
# %%
|
||||
# Collect data
|
||||
my_phaser.gpios.gpio_burst = 0
|
||||
my_phaser.gpios.gpio_burst = 1
|
||||
my_phaser.gpios.gpio_burst = 0
|
||||
data = my_sdr.rx()
|
||||
chan1 = data[0]
|
||||
chan2 = data[1]
|
||||
sum_data = chan1+chan2
|
||||
|
||||
# Process data
|
||||
# Make a 2D array of the chirps for each burst
|
||||
rx_bursts = np.zeros((num_bursts, good_ramp_samples), dtype=complex)
|
||||
for burst in range(num_bursts):
|
||||
start_index = start_offset_samples + (burst) * N_frame
|
||||
stop_index = start_index + good_ramp_samples
|
||||
rx_bursts[burst] = sum_data[start_index:stop_index]
|
||||
|
||||
rx_bursts_fft = np.fft.fftshift(abs(np.fft.fft2(rx_bursts)))
|
||||
|
||||
|
||||
# %%
|
||||
# Function to collect data
|
||||
i = 0
|
||||
cmn = ''
|
||||
def get_radar_data():
|
||||
global range_doppler
|
||||
# Collect data
|
||||
my_phaser.gpios.gpio_burst = 0
|
||||
my_phaser.gpios.gpio_burst = 1
|
||||
my_phaser.gpios.gpio_burst = 0
|
||||
my_phaser._gpios.gpio_burst = 0
|
||||
my_phaser._gpios.gpio_burst = 1
|
||||
my_phaser._gpios.gpio_burst = 0
|
||||
data = my_sdr.rx()
|
||||
chan1 = data[0]
|
||||
chan2 = data[1]
|
||||
@@ -272,57 +257,69 @@ def get_radar_data():
|
||||
|
||||
rx_bursts_fft = np.fft.fftshift(abs(np.fft.fft2(rx_bursts)))
|
||||
range_doppler_data = np.log10(rx_bursts_fft).T
|
||||
plot_data = range_doppler_data
|
||||
#plot_data = np.clip(plot_data, 0, 6) # clip the data to control the max spectrogram scale
|
||||
return plot_data
|
||||
radar_data = range_doppler_data
|
||||
#radar_data = np.clip(radar_data, 0, 6) # clip the data to control the max spectrogram scale
|
||||
return rx_bursts, radar_data
|
||||
|
||||
# %%
|
||||
|
||||
plot_data = np.log10(rx_bursts_fft).T
|
||||
#plot_data = np.clip(plot_data, 0, 6) # clip the data to control the max spectrogram scale
|
||||
rx_bursts, radar_data = get_radar_data()
|
||||
all_data = []
|
||||
|
||||
if plot_data == True:
|
||||
range_doppler_fig, ax = plt.subplots(figsize=(14, 7))
|
||||
extent = [-max_doppler_vel, max_doppler_vel, dist.min(), dist.max()]
|
||||
print(extent)
|
||||
cmaps = ['inferno', 'plasma']
|
||||
cmn = cmaps[0]
|
||||
try:
|
||||
range_doppler = ax.imshow(radar_data, aspect='auto',
|
||||
extent=extent, origin='lower', cmap=matplotlib.colormaps.get_cmap(cmn),
|
||||
)
|
||||
except:
|
||||
print("Using an older version of MatPlotLIB")
|
||||
from matplotlib.cm import get_cmap
|
||||
range_doppler = ax.imshow(radar_data, aspect='auto', vmin=0, vmax=8,
|
||||
extent=extent, origin='lower', cmap=get_cmap(cmn),
|
||||
)
|
||||
ax.set_title('Range Doppler Spectrum', fontsize=24)
|
||||
ax.set_xlabel('Velocity [m/s]', fontsize=22)
|
||||
ax.set_ylabel('Range [m]', fontsize=22)
|
||||
|
||||
cmaps = ['inferno', 'plasma']
|
||||
cmn = cmaps[0]
|
||||
try:
|
||||
range_doppler = ax.imshow(plot_data, aspect='auto',
|
||||
extent=extent, origin='lower', cmap=matplotlib.colormaps.get_cmap(cmn),
|
||||
)
|
||||
except:
|
||||
print("Using an older version of MatPlotLIB")
|
||||
from matplotlib.cm import get_cmap
|
||||
range_doppler = ax.imshow(plot_data, aspect='auto', vmin=0, vmax=8,
|
||||
extent=extent, origin='lower', cmap=get_cmap(cmn),
|
||||
)
|
||||
ax.set_title('Range Doppler Spectrum', fontsize=24)
|
||||
ax.set_xlabel('Velocity [m/s]', fontsize=22)
|
||||
ax.set_ylabel('Range [m]', fontsize=22)
|
||||
|
||||
max_range = 10
|
||||
ax.set_xlim([-6, 6])
|
||||
ax.set_ylim([0, max_range])
|
||||
ax.set_yticks(np.arange(2, max_range, 2))
|
||||
plt.xticks(fontsize=20)
|
||||
plt.yticks(fontsize=20)
|
||||
|
||||
print("sample_rate = ", sample_rate/1e6, "MHz, ramp_time = ", ramp_time, "us, num_chirps = ", num_chirps)
|
||||
print("CTRL + c to stop the loop")
|
||||
max_range = 20
|
||||
ax.set_xlim([-10, 10])
|
||||
ax.set_ylim([0, max_range])
|
||||
ax.set_yticks(np.arange(2, max_range, 2))
|
||||
plt.xticks(fontsize=20)
|
||||
plt.yticks(fontsize=20)
|
||||
|
||||
print("sample_rate = ", sample_rate/1e6, "MHz, ramp_time = ", ramp_time, "us, num_chirps = ", num_chirps)
|
||||
print("CTRL + c to stop the loop")
|
||||
try:
|
||||
while True:
|
||||
plot_data = get_radar_data()
|
||||
range_doppler.set_data(plot_data)
|
||||
plt.show(block=False)
|
||||
plt.pause(.1)
|
||||
rx_bursts, radar_data = get_radar_data()
|
||||
if save_data == True:
|
||||
all_data.append(rx_bursts)
|
||||
print("save")
|
||||
if plot_data == True:
|
||||
range_doppler.set_data(radar_data)
|
||||
plt.show(block=False)
|
||||
plt.pause(.1)
|
||||
except KeyboardInterrupt: # press ctrl-c to stop the loop
|
||||
pass
|
||||
|
||||
# %%
|
||||
# Pluto transmit shutdown
|
||||
my_sdr.tx_destroy_buffer()
|
||||
print("Buffer Destroyed!")
|
||||
print("Pluto Buffer Cleared!")
|
||||
if save_data == True:
|
||||
np.save(f, all_data)
|
||||
np.save("radar_config.npy", [sample_rate, signal_freq, output_freq, num_chirps, chirp_BW, ramp_time_s, tdd.frame_length_ms])
|
||||
|
||||
# # To disable TDD and revert to non-TDD (standard) mode
|
||||
# disable TDD and revert to non-TDD (standard) mode
|
||||
tdd.enable = False
|
||||
sdr_pins.gpio_phaser_enable = False
|
||||
tdd.out_channel1_polarity = not(sdr_pins.gpio_phaser_enable)
|
||||
tdd.out_channel2_polarity = sdr_pins.gpio_phaser_enable
|
||||
tdd.channel[1].polarity = not(sdr_pins.gpio_phaser_enable)
|
||||
tdd.channel[2].polarity = sdr_pins.gpio_phaser_enable
|
||||
tdd.enable = True
|
||||
tdd.enable = False
|
||||
|
||||
172
Range_Doppler_Processing.py
Normal file
172
Range_Doppler_Processing.py
Normal file
@@ -0,0 +1,172 @@
|
||||
# %%
|
||||
# Copyright (C) 2024 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.
|
||||
|
||||
'''FMCW Range Processing Data from the Phaser (CN0566)
|
||||
Jon Kraft, April 22 2024'''
|
||||
|
||||
# Imports
|
||||
import sys
|
||||
import time
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
plt.close('all')
|
||||
|
||||
from target_detection_dbfs import cfar
|
||||
config = np.load("radar_config.npy") # these files are generated by the "Range_Doppler_Plot.py" program
|
||||
all_data = np.load("phaserRadarData.npy")
|
||||
|
||||
|
||||
# %%
|
||||
""" Calculate and print summary of ramp parameters
|
||||
"""
|
||||
sample_rate = config[0]
|
||||
signal_freq = config[1]
|
||||
output_freq = config[2]
|
||||
num_chirps = int(config[3])
|
||||
chirp_BW = config[4]
|
||||
ramp_time_s = config[5]
|
||||
frame_length_ms = config[6]
|
||||
|
||||
PRI = frame_length_ms / 1e3
|
||||
PRF = 1 / PRI
|
||||
|
||||
# Split into frames
|
||||
N_frame = int(PRI * float(sample_rate))
|
||||
|
||||
# Obtain range-FFT x-axis
|
||||
c = 3e8
|
||||
wavelength = c / output_freq
|
||||
slope = chirp_BW / ramp_time_s
|
||||
freq = np.linspace(-sample_rate / 2, sample_rate / 2, N_frame)
|
||||
dist = (freq - signal_freq) * c / (2 * slope)
|
||||
|
||||
# Resolutions
|
||||
R_res = c / (2 * chirp_BW)
|
||||
v_res = wavelength / (2 * num_chirps * PRI)
|
||||
|
||||
# Doppler spectrum limits
|
||||
max_doppler_freq = PRF / 2
|
||||
max_doppler_vel = max_doppler_freq * wavelength / 2
|
||||
|
||||
print("sample_rate = ", sample_rate/1e6, "MHz, ramp_time = ", int(ramp_time_s*(1e6)), "us, num_chirps = ", num_chirps)
|
||||
|
||||
|
||||
# %%
|
||||
# Function to process data
|
||||
i = 0
|
||||
cmn = ''
|
||||
def get_radar_data():
|
||||
global i
|
||||
print(i)
|
||||
rx_bursts = []
|
||||
rx_bursts = all_data[i]
|
||||
i=int((i+1) % len(all_data))
|
||||
#rx_bursts_fft = np.fft.fft2(rx_bursts)
|
||||
rx_bursts_fft = np.fft.fft(rx_bursts)
|
||||
|
||||
bias = 10
|
||||
num_guard_cells = 16
|
||||
num_ref_cells = 16
|
||||
cfar_method = 'average'
|
||||
use_CFAR = False
|
||||
if use_CFAR == True:
|
||||
for burst in range(num_chirps):
|
||||
threshold, targets = cfar(rx_bursts_fft[burst], num_guard_cells, num_ref_cells, bias, cfar_method)
|
||||
targets = targets.reshape(1,-1) # make a row vector
|
||||
rx_bursts_fft[burst] = targets.filled(min(abs(rx_bursts_fft[burst]))) # fill the values below the threshold with -200 dBFS
|
||||
|
||||
rx_bursts_fft = np.fft.fft(rx_bursts_fft.T).T
|
||||
rx_bursts_fft = np.fft.fftshift(abs(rx_bursts_fft))
|
||||
range_doppler_data = np.log10(rx_bursts_fft).T
|
||||
radar_data = range_doppler_data
|
||||
num_good = len(radar_data[:,0])
|
||||
|
||||
center_delete = 6 # delete ground clutter velocity bins around 0 m/s
|
||||
if center_delete != 0:
|
||||
for g in range(center_delete):
|
||||
end_bin = int(num_chirps/2+center_delete/2)
|
||||
radar_data[:,(end_bin-center_delete+g)] = np.ones(num_good)*4.2
|
||||
|
||||
range_delete = 50 # delete the zero range bins (these are Tx to Rx leakage)
|
||||
if range_delete != 0:
|
||||
for r in range(range_delete):
|
||||
start_bin = int(len(radar_data)/2)
|
||||
radar_data[start_bin+r, :] = np.ones(num_chirps)*4.2
|
||||
radar_data = np.clip(radar_data, 4, 5.5) # clip the data to control the max spectrogram scale
|
||||
return radar_data
|
||||
|
||||
# %%
|
||||
|
||||
radar_data = get_radar_data()
|
||||
|
||||
range_doppler_fig, ax = plt.subplots(figsize=(14, 7))
|
||||
extent = [-max_doppler_vel, max_doppler_vel, dist.min(), dist.max()]
|
||||
cmaps = ['inferno', 'plasma']
|
||||
cmn = cmaps[0]
|
||||
try:
|
||||
range_doppler = ax.imshow(radar_data, aspect='auto',
|
||||
extent=extent, origin='lower', cmap=matplotlib.colormaps.get_cmap(cmn),
|
||||
)
|
||||
except:
|
||||
print("Using an older version of MatPlotLIB")
|
||||
from matplotlib.cm import get_cmap
|
||||
range_doppler = ax.imshow(radar_data, aspect='auto', vmin=0, vmax=8,
|
||||
extent=extent, origin='lower', cmap=get_cmap(cmn),
|
||||
)
|
||||
ax.set_title('Range Doppler Spectrum', fontsize=24)
|
||||
ax.set_xlabel('Velocity [m/s]', fontsize=22)
|
||||
ax.set_ylabel('Range [m]', fontsize=22)
|
||||
|
||||
max_range = 16
|
||||
ax.set_xlim([-8, 8])
|
||||
ax.set_ylim([0, max_range])
|
||||
ax.set_yticks(np.arange(0, max_range, 2))
|
||||
plt.xticks(fontsize=20)
|
||||
plt.yticks(fontsize=20)
|
||||
|
||||
#print("sample_rate = ", sample_rate/1e6, "MHz, ramp_time = ", ramp_time, "us, num_chirps = ", num_chirps)
|
||||
print("CTRL + c to stop the loop")
|
||||
try:
|
||||
while True:
|
||||
radar_data = get_radar_data()
|
||||
range_doppler.set_data(radar_data)
|
||||
plt.show(block=False)
|
||||
plt.pause(0.1)
|
||||
except KeyboardInterrupt: # press ctrl-c to stop the loop
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user