import radarsimpy
print("`RadarSimPy` used in this example is version: " + str(radarsimpy.__version__))
`RadarSimPy` used in this example is version: 12.4.0
FMCW Radar¶
Introduction¶
An FMCW (Frequency-Modulated Continuous Wave) radar is a type of radar system that uses frequency modulation to continuously transmit a signal with varying frequency. FMCW radar systems are commonly used in various applications, including automotive radar, weather monitoring, navigation, and industrial sensing. They offer several advantages over other radar types, such as pulsed radar.
Here's how FMCW radar works:
Frequency Modulation: In an FMCW radar, the radar transmitter emits a continuous waveform that changes frequency over time. This waveform is typically a sinusoidal signal. The frequency change occurs in a linear or ramp-like manner, hence the name "Frequency-Modulated Continuous Wave."
Transmit and Receive Phases: The radar emits the frequency-modulated signal, which travels towards the target. When the signal encounters an object, a portion of it gets reflected back towards the radar receiver.
Mixing and Beat Frequency: The reflected signal is mixed with the original transmitted signal in the radar receiver. Because the received signal has traveled a certain distance to the target and back, it experiences a frequency shift due to the Doppler effect and the frequency modulation of the transmitted signal. This frequency shift creates a beat frequency that is proportional to the distance and relative velocity of the target.
Signal Processing: By analyzing the beat frequency, the radar system can determine both the range (distance) and velocity of the target. The range is determined by the phase shift of the beat frequency, and the velocity is derived from the rate of change of the beat frequency over time.
Advantages of FMCW radar include:
Continuous Operation: FMCW radars operate continuously without interruptions, making them well-suited for tracking moving targets and providing real-time data.
Range and Velocity Information: FMCW radar can simultaneously provide information about the range and velocity of targets, making it useful for applications such as automotive collision avoidance and speed measurement.
High Range Resolution: FMCW radar systems can achieve high range resolution due to the continuous waveform and the use of frequency modulation.
Simplicity and Cost-Effectiveness: FMCW radar systems can be simpler and more cost-effective to implement compared to some other radar types, like pulsed radar.
Adaptability to Multiple Targets: FMCW radars can handle multiple targets within their field of view due to their continuous operation and the ability to separate different targets based on their individual beat frequencies.
Despite their advantages, FMCW radars also have limitations, such as reduced performance in certain cluttered environments and susceptibility to interference from other radar systems using similar frequencies. However, advancements in signal processing and technology have helped mitigate many of these challenges.
This illustration showcases a simulation of an FMCW radar using the RadarSimPy
framework. It additionally provides a demonstration of fundamental range and Doppler processing techniques for extracting target range and velocity information.
Create Radar Model¶
Firstly, import the required modules from radarsimpy
. numpy
will also be needed in this example.
import numpy as np
from radarsimpy import Radar, Transmitter, Receiver
Transmitter¶
Setup the basic transmitter parameters through Transmitter module.
Define Antenna patterns¶
In this context, we are defining the radiation patterns for both the transmitter and receiver antennas. Please note that idealized patterns are employed for this purpose, but users have the flexibility to incorporate more realistic antenna patterns into the simulation as needed.
az_angle = np.arange(-80, 81, 1)
az_pattern = 20 * np.log10(np.cos(az_angle / 180 * np.pi) ** 4) + 6
el_angle = np.arange(-80, 81, 1)
el_pattern = 20 * np.log10((np.cos(el_angle / 180 * np.pi)) ** 20) + 6
Plot the antenna patterns
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=[-60, 10]),
xaxis=dict(title="Angle (deg)"),
)
# uncomment this to display interactive plot
# fig.show()
# display static image to reduce size on radarsimx.com
img_bytes = fig.to_image(format="jpg", scale=2)
display(Image(img_bytes))
Define a Transmitter Channel¶
Here, the location and the radiation patterns of the transmitter channel are defined. All the other properties, such as polarization
and modulation
, etc, are using default values as listed in the documentation.
tx_channel = dict(
location=(0, 0, 0),
azimuth_angle=az_angle,
azimuth_pattern=az_pattern,
elevation_angle=el_angle,
elevation_pattern=el_pattern,
)
Define a Radar Transmitter¶
As shown in the diagram below, f
and t
are used to define the waveform modulation. For a linear frequency-modulated continuous waveform (FMCW), it can be specified as f = [fstart, fend]
and t = [tstart, tend]
. If t
is a single number t = t0
, which is equivalent to t = [0, t0]
. The bandwidth of the FMCW is abs(fstart - fend)
. prp
is the pulse repetition period, and prp >= (tend - tstart)
.
| prp
| +-----------+
|
| +---f[1]---> / / /
| / / /
| / / /
| / / /
| / / / ...
| / / /
| / / /
| / / /
| +---f[0]--->/ / /
|
| +-------+
| t[0] t[1]
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.
Define a Receiver Channel¶
Similar to the transmitter channel, the location and radiation patterns of the receiver channel are defined. All the other properties, such as polarization
, antenna patterns
, are using default values as listed in the documentation.
rx_channel = dict(
location=(0, 0, 0),
azimuth_angle=az_angle,
azimuth_pattern=az_pattern,
elevation_angle=el_angle,
elevation_pattern=el_pattern,
)
Define a Radar Receiver¶
fs
– Sampling rate (sps)noise_figure
– Noise figure (dB)rf_gain
– Total RF gain (dB)load_resistor
– Load resistor to convert power to voltage (Ohm)baseband_gain
– Total baseband gain (dB)
rx = Receiver(
fs=2e6,
noise_figure=12,
rf_gain=20,
load_resistor=500,
baseband_gain=30,
channels=[rx_channel],
)
Radar System¶
Use the defined transmitter and receiver to create the radar system.
radar = Radar(transmitter=tx, receiver=rx)
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).
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.sim_radar
module to simulate the baseband samples from the defined radar system and targets.
The output baseband data is a dict including the timestamp and baseband. Both of them are 3-D matrix:
[channels, pulses, ADC samples]
from radarsimpy.simulator import sim_radar
data = sim_radar(radar, targets)
timestamp = data["timestamp"]
baseband = data["baseband"] + data["noise"]
Plot the baseband samples
fig = go.Figure()
fig.add_trace(
go.Scatter(
x=timestamp[0, 0, :],
y=np.real(baseband[0, 0, :]),
name="I",
)
)
fig.add_trace(
go.Scatter(
x=timestamp[0, 0, :],
y=np.imag(baseband[0, 0, :]),
name="Q",
)
)
fig.update_layout(
title="I/Q Baseband Signals",
yaxis=dict(title="Amplitude (V)"),
xaxis=dict(title="Time (s)"),
)
# uncomment this to display interactive plot
# fig.show()
# display static image to reduce size on radarsimx.com
img_bytes = fig.to_image(format="jpg", scale=2)
display(Image(img_bytes))
from scipy import signal
import radarsimpy.processing as proc
range_window = signal.windows.chebwin(radar.sample_prop["samples_per_pulse"], at=60)
range_profile = proc.range_fft(baseband, range_window)
Plot range profile
max_range = (
3e8
* radar.radar_prop["receiver"].bb_prop["fs"]
* radar.radar_prop["transmitter"].waveform_prop["pulse_length"]
/ radar.radar_prop["transmitter"].waveform_prop["bandwidth"]
/ 2
)
range_axis = np.linspace(
0, max_range, radar.sample_prop["samples_per_pulse"], endpoint=False
)
doppler_axis = np.linspace(
0,
radar.radar_prop["transmitter"].waveform_prop["pulses"],
radar.radar_prop["transmitter"].waveform_prop["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",
),
)
# uncomment this to display interactive plot
# fig.show()
# display static image to reduce size on radarsimx.com
img_bytes = fig.to_image(format="jpg", scale=2)
display(Image(img_bytes))
Range-Doppler processing¶
Perform the FFT across the range profiles could obtan the Doppler information from the data, which is also known as the Doppler FFT.
doppler_window = signal.windows.chebwin(
radar.radar_prop["transmitter"].waveform_prop["pulses"], at=60
)
range_doppler = proc.doppler_fft(range_profile, doppler_window)
Plot range-Doppler map
unambiguous_speed = (
3e8 / radar.radar_prop["transmitter"].waveform_prop["prp"][0] / 24.125e9 / 2
)
range_axis = np.linspace(
0, max_range, radar.sample_prop["samples_per_pulse"], endpoint=False
)
doppler_axis = np.linspace(
-unambiguous_speed,
0,
radar.radar_prop["transmitter"].waveform_prop["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",
),
)
# uncomment this to display interactive plot
# fig.show()
# display static image to reduce size on radarsimx.com
img_bytes = fig.to_image(format="jpg", scale=2)
display(Image(img_bytes))