Optimizing chirp parameters for accuracy
The problem
Range range, resolution and accuracy is a function of the chirp definition.
While defining optimal chirp bandwidth, slope, sampling frequency and maximum ADC buffer size is a fairly simple problem to solve for a single target.
Optimizing chirp definition to minimise measurement error over multiple targets can be a more daunting task.
The solution
This workbook shows a simple non-optimised solution for this multi-target error minimisation.
A likely improvement would be to leverage scipy.optimize.minimize
[1]:
# Install a pip package in the current Jupyter kernel
import sys
from os.path import abspath, basename, join, pardir
import datetime
# hack to handle if running from git cloned folder or stand alone (like Google Colab)
cw = basename(abspath(join(".")))
dp = abspath(join(".",pardir))
if cw=="docs" and basename(dp) == "mmWrt":
# running from cloned folder
print("running from git folder, using local path (latest) mmWrt code", dp)
sys.path.insert(0, dp)
else:
print("running standalone, need to ensure mmWrt is installed")
!{sys.executable} -m pip install mmWrt
print(datetime.datetime.now())
running from git folder, using local path (latest) mmWrt code c:\git\mmWrt
2026-07-17 18:47:35.574817
[2]:
from os.path import abspath, join, pardir
import sys
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib import colors
from numpy import arange, where, expand_dims, abs as np_abs
from mmWrt.Raytracing import rt_points # noqa: E402
from mmWrt.Scene import Radar, Transmitter, Receiver, Scatterer # noqa: E402
from mmWrt import RadarSignalProcessing as rsp # noqa: E402
from mmWrt import __version__ as mmWrt_ver
print(mmWrt_ver)
from tqdm import tqdm
0.0.14.dev1+g243a05bf3.d20260717
[3]:
c = 3e8
debug_code = True
scatterer1 = Scatterer(5.1)
min_error = scatterer1.distance()
config = {"bw": "?", "adc_sample_rate": "?", "error": "?"}
bws = arange(30, 40)*1e8
slopes = arange(1, 10)*1e12
adc_sample_rates = arange(1, 100)*1e5 # intentionally small lower bound
with tqdm(total=len(bws) * len(slopes) * len(adc_sample_rates)) as pbar:
for bw in bws:
for slope in slopes:
# slope = slope_m * 1e8
for adc_sample_rate in adc_sample_rates:
pbar.update(1)
try:
chirp_end_time = bw/slope
transmitter = Transmitter(chirp_end_time=chirp_end_time,
chirp_slope=slope)
receiver0 = Receiver(adc_sample_rate=adc_sample_rate,
adc_sample_rate_max=adc_sample_rate+1,
adc_sample_count=32)
radar = Radar(transmitter=transmitter,
receiver=receiver0)
bb = rt_points([radar], [scatterer1],
radar)
Distances, range_profile = rsp.range_fft(bb["adc_cube"]
[0, 0, 0, :], bb)
Distances = Distances/2
cfar_ca = rsp.cfar_ca(range_profile, train_cell_count=5,
guard_cell_count=1, pfa=1e-6)
mag_r = np_abs(range_profile)
mag_c = np_abs(cfar_ca)
# little hack to remove small FFT ripples : mag_r> 5
scatterer_filter = ((mag_r > mag_c) & (mag_r > 5))
index_peaks = where(scatterer_filter)[0]
if index_peaks.size > 0:
grouped_peaks = rsp.peak_grouping_1d(index_peaks, mag_r)
found_scatterers = [Scatterer(Distances[i])
for i in grouped_peaks]
error = rsp.error([scatterer1], found_scatterers)
# print("error", error)
if error < min_error:
min_error = error
config = {"bw": float(bw),
"adc_sample_rate": float(adc_sample_rate),
"slope": float(slope),
"error": float(error)}
except Exception as ex:
if debug_code:
print(str(ex))
raise
# yields 0.002 error for bw=3e9, adc_sample_rate=100, slope=6e8
print(f"optimal config: {config}, yields error: {min_error}")
0%| | 0/8910 [00:00<?, ?it/s]c:\git\mmWrt\mmWrt\RadarSignalProcessing.py:464: ComplexWarning: Casting complex values to real discards the imaginary part
cfar_threshold[idx] = mean_noise * threshold_factor
100%|██████████| 8910/8910 [00:19<00:00, 446.70it/s]
optimal config: {'bw': 3000000000.0, 'adc_sample_rate': 8700000.0, 'slope': 4000000000000.0, 'error': 0.0023437499999996447}, yields error: 0.0023437499999996447
[4]:
assert config["bw"] == 3e9
assert config["adc_sample_rate"] == 8.7e6
assert config["slope"] == 4e12
print(mmWrt_ver)
0.0.14.dev1+g243a05bf3.d20260717