{ "cells": [ { "cell_type": "markdown", "id": "842a475f", "metadata": {}, "source": [ "# Optimizing chirp parameters for accuracy\n", "\n", "[![](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/matt-chv/mmWrt/blob/main/docs/Chirp_Solver.ipynb)" ] }, { "cell_type": "markdown", "id": "75f05d45", "metadata": {}, "source": [ "## The problem\n", "\n", "Range range, resolution and accuracy is a function of the chirp definition.\n", "\n", "While defining optimal chirp bandwidth, slope, sampling frequency and maximum ADC buffer size is a fairly simple problem to solve for a single target.\n", "\n", "Optimizing chirp definition to minimise measurement error over multiple targets can be a more daunting task.\n", "\n", "## The solution\n", "\n", "This workbook shows a simple non-optimised solution for this multi-target error minimisation.\n", "\n", "A likely improvement would be to leverage `scipy.optimize.minimize`" ] }, { "cell_type": "code", "execution_count": null, "id": "ee64b92f", "metadata": {}, "outputs": [], "source": [ "# Install a pip package in the current Jupyter kernel\n", "import sys\n", "!{sys.executable} -m pip install mmWrt" ] }, { "cell_type": "code", "execution_count": 1, "id": "c74d5114", "metadata": {}, "outputs": [], "source": [ "from os.path import abspath, join, pardir\n", "import sys\n", "import matplotlib.pyplot as plt\n", "import matplotlib.cm as cm\n", "from matplotlib import colors\n", "from numpy import arange, where, expand_dims\n", "\n", "# uncomment below if the notebook is launched from project's root folder\n", "dp = abspath(join(\".\",pardir))\n", "sys.path.insert(0, dp)\n", "\n", "\n", "from mmWrt.Raytracing import rt_points # noqa: E402\n", "from mmWrt.Scene import Radar, Transmitter, Receiver, Target # noqa: E402\n", "from mmWrt import RadarSignalProcessing as rsp # noqa: E402\n", "\n", "from tqdm import tqdm" ] }, { "cell_type": "code", "execution_count": 2, "id": "8a440f7d", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|████████████████████████████████████████████████████████████████████| 2249991/2249991 [00:05<00:00, 424840.61it/s]" ] }, { "name": "stdout", "output_type": "stream", "text": [ "optimal config: {'bw': 3000000000.0, 'fs': 100.0, 'slope': 600000000.0, 'error': 0.0}, yields error: 0.0\n", "CPU times: total: 2.81 s\n", "Wall time: 5.31 s\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "%%time\n", "\n", "c = 3e8\n", "\n", "# Define targets, by default non set values are 0\n", "# so below is a target at x0=1.5 and y0=0, z0=0 with no speed vector\n", "target1 = Target(1.5)\n", "target2 = Target(2)\n", "\n", "# initialize the error to the error when no targets are identified\n", "min_error = target1.distance() + target2.distance() # we start at 2.5\n", "\n", "# initialize chirp config with text values\n", "config = {\"bw\": \"?\", \"fs\": \"?\", \"error\": \"?\"}\n", "\n", "# this scan will take minutes\n", "bws = [0.1e9, 0.2e9, 0.3e9, 0.5e9, 1e9, 2e9, 3e9, 4e9]\n", "slopes = range(1, 100)\n", "fss = arange(100, 25e6, 100)\n", "\n", "# this scan takes seconds to verify that min_error is 0 with those settings\n", "bws = [2.9e9, 3e9, 3.1e9]\n", "slopes = [5, 6, 7]\n", "fs = range(50, 200)\n", "\n", "debug_ON = False\n", "\n", "with tqdm(total=len(bws) * len(slopes) * len(fss)) as pbar:\n", " for bw in bws:\n", " for slope_m in slopes:\n", " slope = slope_m * 1e8\n", " for fs in fss:\n", " pbar.update(1)\n", " try:\n", "\n", " radar = Radar(transmitter=Transmitter(bw=bw, slope=slope),\n", " receiver=Receiver(fs=fs,\n", " max_adc_buffer_size=512,\n", " debug=debug_ON),\n", " debug=debug_ON)\n", "\n", " bb = rt_points(radar, [target1, target2], debug=debug_ON)\n", " # data_matrix = bb['adc_cube'][0][0][0]\n", " Distances, range_profile = rsp.range_fft(bb)\n", " ca_cfar = rsp.cfar_ca_1d(range_profile)\n", "\n", " range_profile = range_profile\n", " ca_cfar = ca_cfar\n", " mag_r = abs(range_profile)\n", " mag_c = abs(ca_cfar)\n", " # little hack to remove small FFT ripples : mag_r> 5\n", " target_filter = ((mag_r > mag_c) & (mag_r > 5))\n", "\n", " index_peaks = where(target_filter)[0]\n", " grouped_peaks = rsp.peak_grouping_1d(index_peaks)\n", "\n", " found_targets = [Target(Distances[i])\n", " for i in grouped_peaks]\n", " error = rsp.error([target1, target2], found_targets)\n", " # print(\"error\", error)\n", " if error < min_error:\n", " min_error = error\n", " config = {\"bw\": bw, \"fs\": fs,\n", " \"slope\": slope,\n", " \"error\": error}\n", " except Exception:\n", " pass\n", " # print(str(ex))\n", " # raise\n", "\n", "# yields 0 error for bw=3e9, fs=100, slope=6e8\n", "print(f\"optimal config: {config}, yields error: {min_error}\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.1" } }, "nbformat": 4, "nbformat_minor": 5 }