import radarsimpy
radarsimpy.__version__
'10.0.1'
This is an FMCW radar simulation example based on RadarSimPy
.
Define the radiation patterns for the transmitter and receiver antennas.
import numpy as np
az_angle = np.arange(-80, 81, 1)
az_pattern = 20 * np.log10(np.cos(az_angle / 180 * np.pi)) + 6
el_angle = np.arange(-80, 81, 1)
el_pattern = 20 * np.log10((np.cos(el_angle / 180 * np.pi))**8) + 6
import plotly.graph_objs as go
from IPython.display import Image
fig = go.Figure()
fig.add_trace(go.Scatter(
x=az_angle,
y=az_pattern,
name='Azimuth',
))
fig.add_trace(go.Scatter(
x=el_angle,
y=el_pattern,
name='Elevation',
))
fig.update_layout(
title='Antenna Pattern',
yaxis=dict(title='Amplitude (dB)', range=[-10, 10]),
xaxis=dict(title='Angle (deg)'),
)
# fig.show()
# display(SVG(fig.to_image(format='svg', scale=1)))
Image(fig.to_image(format="jpg", scale=2))
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 |
[24.075, 24.175] GHz |
Time ($T$) | t |
80e-6 s |
Transmitted power ($P_t$) | tx_power |
10 dBm |
Pulse repetition period ($PRP$) | prp |
100 us |
Number of pulses | pulses |
256 |
Here, f
and t
define the frequency modulation of the transmitter. The frequency changes from 24.075 GHz
to 24.175 GHz
within 80e-6 s
.
from radarsimpy import Radar, Transmitter, Receiver
tx_channel = dict(
location=(0, 0, 0),
azimuth_angle=az_angle,
azimuth_pattern=az_pattern,
elevation_angle=el_angle,
elevation_pattern=el_pattern,
)
tx = Transmitter(f=[24.075e9, 24.175e9],
t=80e-6,
tx_power=10,
prp=100e-6,
pulses=256,
channels=[tx_channel])
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 |
2 Msps |
Noise figure ($NF$) | noise_figure |
12 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 |
rx_channel = dict(
location=(0, 0, 0),
azimuth_angle=az_angle,
azimuth_pattern=az_pattern,
elevation_angle=el_angle,
elevation_pattern=el_pattern,
)
rx = Receiver(fs=2e6,
noise_figure=12,
rf_gain=20,
load_resistor=500,
baseband_gain=30,
channels=[rx_channel])
Create the FMCW radar model based on all the parameters defined above.
radar = Radar(transmitter=tx, receiver=rx)
Calculate the characteristics of the FMCW radar:
Maximum range (with I/Q baseband): $$R_{max}=\frac{c f_s T}{2B}$$
Maximum range (without I/Q baseband): $$R_{max}=\frac{c f_s T}{4B}$$
Unambiguous velocity: $$v_{ua}=\frac{c}{2 CRP \times f_c}$$ or $$v_{ua}=\pm \frac{c}{4 CRP \times f_c}$$
Range resolution: $$\delta_r=\frac{c}{2B}$$
The propertities of targets are defined here. There are 3 targets in this simulation. The locations of the targets are defined through $(x, y, z)$ coordinates in meters, and the speeds of the targets are defined trough $(v_x, v_y, v_z)$ in $m/s$. The propertites of the targets also includes radar cross-section (RCS (dBsm)) and phase (degree).
target_1 = dict(location=(200, 0, 0), speed=(-5, 0, 0), rcs=20, phase=0)
target_2 = dict(location=(95, 20, 0), speed=(-50, 0, 0), rcs=15, phase=0)
target_3 = dict(location=(30, -5, 0), speed=(-22, 0, 0), rcs=5, 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]$
# Python engine
from radarsimpy.simulator import simpy
# C++ engine
from radarsimpy.simulator import simc
bb_data = simc(radar, targets, noise=True)
time_matrix = bb_data['timestamp']
data_matrix = bb_data['baseband']
Plot the baseband samples
fig = go.Figure()
fig.add_trace(go.Scatter(
x=time_matrix[0, 0, :],
y=np.real(data_matrix[0, 0, :]),
name='I',
))
fig.add_trace(go.Scatter(
x=time_matrix[0, 0, :],
y=np.imag(data_matrix[0, 0, :]),
name='Q',
))
fig.update_layout(
title='I/Q Baseband Signals',
yaxis=dict(title='Amplitude (V)'),
xaxis=dict(title='Time (s)'),
)
# fig.show()
# display(SVG(fig.to_image(format='svg', scale=1)))
Image(fig.to_image(format="jpg", scale=2))
from scipy import signal
import radarsimpy.processing as proc
range_window = signal.chebwin(radar.samples_per_pulse, at=60)
range_profile = proc.range_fft(data_matrix, range_window)
Plot range profiles
max_range = (3e8 * radar.receiver.fs *
radar.transmitter.pulse_length /
radar.transmitter.bandwidth / 2)
range_axis = np.linspace(
0, max_range, radar.samples_per_pulse, endpoint=False)
doppler_axis = np.linspace(
0, radar.transmitter.pulses, radar.transmitter.pulses, endpoint=False)
fig = go.Figure()
fig.add_trace(go.Surface(x=range_axis, y=doppler_axis, z=20 *
np.log10(np.abs(range_profile[0, :, :])), colorscale='Rainbow'))
fig.update_layout(
title='Range Profile',
height=600,
scene=dict(
xaxis=dict(title='Range (m)'),
yaxis=dict(title='Chirp'),
zaxis=dict(title='Amplitude (dB)'),
aspectmode='cube',
),
)
# fig.show()
# display(SVG(fig.to_image(format='svg', scale=1)))
Image(fig.to_image(format="jpg", scale=2))
doppler_window = signal.chebwin(radar.transmitter.pulses, at=60)
range_doppler = proc.doppler_fft(range_profile, doppler_window)
Plot range-Doppler map
max_range = (3e8 * radar.receiver.fs *
radar.transmitter.pulse_length /
radar.transmitter.bandwidth / 2)
unambiguous_speed = 3e8 / radar.transmitter.prp[0] / \
radar.transmitter.fc_vect[0] / 2
range_axis = np.linspace(
0, max_range, radar.samples_per_pulse, endpoint=False)
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.abs(range_doppler[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)'),
aspectmode='cube',
),
)
# fig.show()
# display(SVG(fig.to_image(format='svg', scale=1)))
Image(fig.to_image(format="jpg", scale=2))