import radarsimpy
radarsimpy.__version__
'10.2.0'
This is an example of using DoA algorithms (MUSIC & Esprit) with RadarSimPy
.
Setup the basic transmitter parameters through Transmitter
module.
The following table lists the parameters in this example.
Parameter | Variable in Transmitter |
Value |
---|---|---|
Frequency ($f$) | f |
[60e9, 61e9] GHz |
Time ($T$) | t |
16e-6 s |
Transmitted power ($P_t$) | tx_power |
15 dBm |
Pulse repetition period ($PRP$) | prp |
40 us |
Number of pulses | pulses |
512 |
Setup the receiver parameters through Receiver
module.
The parameters of the receiver are listed in the table below.
Parameter | Variable in Receiver |
Value |
---|---|---|
Sampling rate ($f_s$) | fs |
20 Msps |
Noise figure ($NF$) | noise_figure |
8 dB |
RF gain/loss ($G_{rf}$) | rf_gain |
20 dB |
Load resistor ($R_L$) | load_resistor |
500 $\Omega$ |
Baseband voltage gain ($G_{BB}$) | baseband_gain |
30 dB |
import numpy as np
from radarsimpy import Radar, Transmitter, Receiver
wavelength = 3e8 / 60.5e9
channels = []
N_tx = 2
N_rx = 64
channels.append(
dict(
location=(0, -N_rx/2*wavelength/2, 0),
))
channels.append(
dict(
location=(0, wavelength*N_rx/2-N_rx/2*wavelength/2, 0),
))
tx = Transmitter(f=[61e9, 60e9],
t=[0, 16e-6],
tx_power=15,
prp=40e-6,
pulses=512,
channels=channels)
channels = []
for idx in range(0, N_rx):
channels.append(
dict(
location=(0, wavelength/2*idx-(N_rx-1) * wavelength/4, 0),
))
rx = Receiver(fs=20e6,
noise_figure=8,
rf_gain=20,
load_resistor=500,
baseband_gain=30,
channels=channels)
radar = Radar(transmitter=tx, receiver=rx)
There are 2 transmitters and 64 receivers, the synthesized array is a 1 x 128 uniform linear array.
Note: The modulation is not considered here. Assume the 2 transmitters are orthogonal.
import plotly.graph_objs as go
from IPython.display import Image
fig = go.Figure()
fig.add_trace(
go.Scatter(x=radar.transmitter.locations[:, 1]/wavelength,
y=radar.transmitter.locations[:, 2]/wavelength,
mode='markers',
name='Transmitter',
opacity=0.7,
marker=dict(size=10)))
fig.add_trace(
go.Scatter(x=radar.receiver.locations[:, 1]/wavelength,
y=radar.receiver.locations[:, 2]/wavelength,
mode='markers',
opacity=1,
name='Receiver'))
fig.update_layout(
title='Array configuration',
xaxis=dict(title='y (λ)'),
yaxis=dict(title='z (λ)',
scaleanchor="x",
scaleratio=1),
)
# fig.show()
Image(fig.to_image(format="jpg", scale=2))
Create 3 targets at 40 m. The azimuth angles of the 3 targets are -5, -4, and 45 degrees relative to the radar.
true_theta = [-5, -4, 45]
target_1 = dict(location=(40*np.cos(np.radians(true_theta[0])), 40*np.sin(np.radians(true_theta[0])),
0), speed=(0, 0, 0), rcs=10, phase=0)
target_2 = dict(location=(40*np.cos(np.radians(true_theta[1])), 40*np.sin(np.radians(true_theta[1])),
0), speed=(0, 0, 0), rcs=10, phase=0)
target_3 = dict(location=(40*np.cos(np.radians(true_theta[2])), 40*np.sin(np.radians(true_theta[2])),
0), speed=(0, 0, 0), rcs=10, phase=0)
targets = [target_1, target_2, target_3]
Use the simulator
module to simulate the baseband samples. The user can choose between Python engine simpy
or C++ engine simc
.
The output baseband data is a 3-D matrix:
$[channels, pulses, ADC~samples]$
from radarsimpy.simulator import simc
bb_data = simc(radar, targets, noise=True)
time_matrix = bb_data['timestamp']
baseband = bb_data['baseband']
from scipy import signal
import radarsimpy.processing as proc
range_window = signal.chebwin(radar.samples_per_pulse, at=80)
doppler_window = signal.chebwin(radar.transmitter.pulses, at=60)
range_doppler = proc.range_doppler_fft(
baseband, rwin=range_window, dwin=doppler_window)
Range-Doppler average map
max_range = (3e8 * radar.receiver.fs *
radar.transmitter.pulse_length /
radar.transmitter.bandwidth / 2)
range_axis = np.flip(np.linspace(
0, max_range, radar.samples_per_pulse, endpoint=False))
unambiguous_speed = 3e8 / radar.transmitter.prp[0] / \
radar.transmitter.fc_vect[0] / 2
doppler_axis = np.linspace(
-unambiguous_speed, 0, radar.transmitter.pulses, endpoint=False)
fig = go.Figure()
fig.add_trace(go.Surface(x=range_axis, y=doppler_axis, z=20 *
np.log10(np.mean(np.abs(range_doppler), axis=0)), colorscale='Rainbow'))
fig.update_layout(
title='Range Doppler',
height=600,
scene=dict(
xaxis=dict(title='Range (m)'),
yaxis=dict(title='Velocity (m/s)'),
zaxis=dict(title='Amplitude (dB)'),
),
margin=dict(l=0, r=0, b=60, t=100),
legend=dict(orientation='h'),
)
# fig.show()
Image(fig.to_image(format="jpg", scale=2))
Find the beam vector of the peak and create the covariance matrix.
from scipy import linalg
det_idx = [np.argmax(np.mean(np.abs(range_doppler[:, 0, :]), axis=0))]
bv = range_doppler[:, 0, det_idx[0]]
bv = bv/linalg.norm(bv)
snapshots = 20
bv_snapshot = np.zeros((N_tx*N_rx-snapshots, snapshots), dtype=complex)
for idx in range(0, snapshots):
bv_snapshot[:, idx] = bv[idx:(idx+N_tx*N_rx-snapshots)]
covmat = np.cov(bv_snapshot.conjugate())
FFT of the beam vector. Two peaks can be seen, and it is not able to discriminate the targets at -5 and -4 degrees.
from scipy import fft
from scipy import signal
fft_spec = 20 * \
np.log10(np.abs(fft.fftshift(fft.fft(bv.conjugate(), n=1024))))
fig = go.Figure()
fig.add_trace(go.Scatter(x=np.arcsin(np.linspace(-1, 1, 1024, endpoint=False))/np.pi*180,
y=fft_spec,
name='FFT')
)
fig.update_layout(
title='FFT',
yaxis=dict(title='Amplitude (dB)'),
xaxis=dict(title='Angle (deg)'),
margin=dict(l=10, r=10, b=10, t=40),
)
# fig.show()
Image(fig.to_image(format="jpg", scale=2))