In [1]:
import radarsimpy
radarsimpy.__version__
Out[1]:
'10.0.1'

FMCW Radar¶



This is an FMCW radar simulation example based on RadarSimPy.

Radar Model¶

Antenna pattern¶

Define the radiation patterns for the transmitter and receiver antennas.

In [2]:
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
In [3]:
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))
Out[3]:

Transmitter¶

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.

In [4]:
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])

Receiver¶

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
In [5]:
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.

In [6]:
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}$$

Targets¶

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).

In [7]:
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]

Simulate Baseband Signals¶

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]$

In [8]:
# 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

In [9]:
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))
Out[9]:

Radar Signal Processing¶

Range profile¶

In [10]:
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

In [11]:
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))
Out[11]:

Range-Doppler processing¶

In [12]:
doppler_window = signal.chebwin(radar.transmitter.pulses, at=60)
range_doppler = proc.doppler_fft(range_profile, doppler_window)

Plot range-Doppler map

In [13]:
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))
Out[13]:
In [ ]: