FMCW Radar 201 - Maths

This section will cover a bottom up approach to derive some key equations used throughout FMCW radar.

You can open this workbook in Google Colab to experiment with it, by clicking on: image0

Transmitting a chirp

A chirp is defined by a linearly increasing frequency.

\[f(t) = f_{0} + s * t\]

Where:

  • \(f_{0}\) is the frequency at the begining of the chirp

  • s is the slope of frequency increase over time during the chirp

  • t is the time referenced to the beginning of the chirp.

It is beyond the scope of this writeup to demonstrate how phase and instantaneous frequency are related or the conditions of validty of the relations. We will just consider proven and valid that:

\[f(t) = \frac{1}{2 \pi} \cdot \frac{\partial \phi (t)}{\partial t}\]

From which we can derive, that the phase of the transmitted FMCW radar is:

\[\mathit{\Phi_{TX}(t)} = \Phi_0 + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right)\]

Definitions

  • \(\Phi\) is the full phase (including time or distance variables)

  • \(\phi\), \(\phi_0\), \(\phi_1\) are the phase offsets

  • \(\psi\) are the number of cycles (i.e. \(\frac{\phi}{2 \pi}\) )

So the full transmitted signal is

\[Y_{TX}(t) = A_T \cdot \cos(\Phi_{TX}(t)) = A_T \cdot \cos(\Phi_0 + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right))\]

Receiving a chirp

The received signal is the transmitted signal being delayed by a constant time $ :nbsphinx-math:`Delta `$, which translates in phase of the RX signal is time delayed version of TX:

\[\Phi_{RX}(t) = \Phi_{TX}(t-\Delta)\]
\[\Phi_{RX}(t) = \Phi_{0} + 2 \pi \left(f_{0} \left(t - \Delta \right) + \frac{s \left(t- \Delta \right)^{2}}{2}\right)\]

Where:

  • d being the distance to the scatterer (so travelled twice by the radar wave)

  • \(\Delta\) is the time of flight (i.e. \(\frac{2 \cdot d}{c}\))

So the full received signal is:

\[Y_{RX}(t) = A_R \cdot \cos{\phi_{RX}(t)} = A_R \cdot \cos \left({\Phi_{0} + 2 \pi \left(f_{0} \left(t - \Delta \right) + \frac{s \left(t- \Delta \right)^{2}}{2}\right)} \right)\]

Mixer RX and TX leading to IF

The mixer multiplies RX and TX signals

\[Y_{MIX}(t) = Y_{TX}(t) \cdot Y_{RX}(t)\]

replacing $ Y_{TX}(t) $ and $ Y_{RX}(t) $ by their respective equations :

\[Y_{MIX}(t) = A_R \cdot \cos(\Phi_{RX}(t)) \cdot A_T \cdot \cos(\Phi_{TX}(t))\]

We are now going to leverage Trigonometric relations to establish how the IF signal is actually how relatively lower frequency which is why it can be sampled by ADC converters.

Trigonometric relations

Trigonometric relations establish relationship between the product of 2 cosines, with the sum of 2 related cosines in the following relation:

\[A_1 \cdot cos(R) \cdot A_2 \cdot cos(T) = \frac{A_1 \cdot A_2 \cdot cos(R-T)}{2} + \frac{A_1 \cdot A_2 \cdot cos(R+T)}{2}\]

which in our case translates into:

\[A_R \cdot \cos(\mathit{\Phi_R}) \cdot A_T \cdot \cos(\mathit{\Phi_T}) = \frac{A_T \cdot A_R}{2} \cdot \cos(\mathit{\Phi_T}-\mathit{\Phi_R}) + \frac{A_T \cdot A_R}{2} \cdot \cos(\mathit{\Phi_T}+\mathit{\Phi_R})\]

The following cells will leverage simpy to prove those equalities, but one can already see that the IF signal is the sum of a low frequency content \(\Phi_T-\Phi_R\) and high-frequency content \(\Phi_T+\Phi_R\). The high-frequency content is implicitely removed in many textbooks. In reality a low pass filter will ensure no aliasing from the R+T signal gets into the IF.

The output of the low pass filter after the MIXER is called the Intermediate Frequency, often abreviated IF signal

\[Y_{IF}(t) = cos\left( 2 \pi \left(\frac{\Delta^{2} s}{2} - \Delta f_{0} - \Delta s t\right) \right)\]

Eq. 3

Which defining:

  • \(\Psi_0 = - \Delta f_{0} + \frac{\Delta f_{c}}{2}\)

  • \(f_c = \Delta \cdot s\)

Becomes

\[Y_{IF}(t) = cos\left( 2 \pi \left(\Psi_0 + f_c \cdot t \right) \right)\]

Changes in distance

Assuming that there is a small change in the distance $ d $, it yields a small change in the time of flight $ \delta`$which from $:nbsphinx-math:Delta $ becomes :math:Delta + delta`

Then the new number of cycle \(\Psi_1\) is

\[\Psi_1 = - f_{0} \left(\Delta + \delta\right) + \frac{f_{c} \left(\Delta + \delta\right)}{2}\]

(Eq. 5)

So the change in number of cycles between the two signals becomes

\[\Psi_1 - \Psi_0 = - \delta f_0 + \frac{\delta f_{c}}{2}\]
From this point on, we make assumptions about the relative frequency of the carrier and the IF.

If we notice that \(f_0 \approx 6 \cdot 10^{10} Hz\) and since the IF is below 100 MHz (just to simplify) \(f_c \lt 10^8 Hz\) we can neglect the second term. It is beyond the scope of this write-up to explore the boundary conditions of validity of this simplification

\[\Psi_1 - \Psi_0 \approx - \delta f_0 = - \delta \cdot \frac{c}{\lambda}\]

Implications for velocity or AoA

Illustration to come

Given that

\[\Psi_1 - \Psi_0 \approx - \delta f_0 = - \delta \cdot \frac{c}{\lambda}\]

or

\[\frac{\partial \Psi }{\partial \Delta} \approx - f_0 = \frac{c}{\lambda}\]
  • for range the change of time of flight is proportional to twice the change of distance (\(\Delta = \frac{2 \cdot D}{c}\))

  • for AoA the change of time of flight is proportional to the change of distance (\(\Delta = \frac{D}{c}\))

Since

\[\frac{\partial \Psi }{\partial D} = \frac{\partial \Psi }{\partial \Delta} \cdot \frac{\partial \Delta }{\partial D}\]

for range the number of cycles is then

\[\frac{\partial \Psi }{\partial D} = \frac{\partial \Psi }{\partial \Delta} \cdot \frac{2}{c}= \frac{c }{\lambda} \cdot \frac{2}{c}= \frac{2}{\lambda}\]

or in phase shift (\(\Phi=2 \cdot \pi \cdot \Psi\))

\[\frac{\partial \Phi }{\partial D} = \frac{4 \cdot \pi}{\lambda}\]

which leads for speed measurement:

\[v = \frac{\partial D }{\partial t} = \frac{\partial D }{\partial \Phi} \cdot \frac{\partial \Phi }{\partial t} = \frac{\lambda}{4 \cdot \pi} \cdot \frac{\partial \Phi }{\partial t}\]

Angle of Arrival (AoA)

For AoA, the number of cycle count changes by half:

\[\frac{\partial \Psi }{\partial D} = \frac{1}{\lambda}\]

or in phase shift

\[\frac{\partial \Phi }{\partial D} = \frac{2 \cdot \pi}{\lambda}\]

with x, the distance between two antennas and theta the angle from boresight

\[\frac{\partial \Phi }{\partial x} = \frac{\partial \Phi }{\partial D} \cdot \frac{\partial D }{\partial x}=\frac{2 \cdot \pi}{\lambda} \cdot x \cdot \mathrm{sin}(\theta)\]

or

\[\theta=sin^{-1}(\frac{\lambda \cdot \frac{\partial \Phi }{\partial x}}{2 \cdot \pi \cdot x})\]

Discrete Fourier Transform (DFT) to the rescue

Since the frequency of the intermediate frequency (IF signal) is related to distance, measuring the frequency of the IF signal is a straightforward way to estimate the distance to the scatterer.

Given X the Discrete Fourier Transform (DFT) of \(Y_{IF}\)

\[\mathscr{F}(Y_{IF}) = X\]

The tone is found by looking for the peak of the magnitude of the DFT by

\[k_p = \operatorname*{argmax}_k \lvert X[k] \rvert\]

For speed (doppler) and angle of arrival (AoA), it is important to note that the DFT transform has a closed form:

\[X[k_p] = A[k_p] \cdot e^{i \cdot \phi[k_p]}\]

Side reminder: another way to look at it is to remember that the Fourier transform or a time delayed signal is the frequency shifted Fourier transform as follows:

\[\mathscr{F}\big\{x(t-t_0)\big\}=X(f)e^{-j2\pi f t_0}\]

so if the phase delay is to change over time (for range) or over angles (for AoA), the second FFT would yield either the speed or angles (to be developped).

Second DFT

Now if we assume that the phase of peak \(k^{th}\) changes over time (say we sampled at regular time \(Y_IF\) and the peak is always at the same location \(k_p\) so that

\[\phi[k_p](t) = 2 \cdot \pi \cdot f_1 \cdot t\]

or in discrete time with T the time interval between each chirps

\[\phi[k_p](m \cdot T) = 2 \cdot \pi \cdot f_1 \cdot m \cdot T\]

Then the DFT of X will have a peak at the closest position to f1

with

\[\mathscr{F}(X_k[m]) = Z\]

then

\[m_p = \operatorname*{argmax}_m \lvert Z[m] \rvert\]

Third and fourth DFT

By similarities if the phase of the $ m_p $ value of Z was to change across sampling we could run a third DFT to identify the slope of the variation.

Most frequent usage of the third DFT is in the following flow:

  1. Range DFT (often a FFT)

  2. Dopple DFT (ibid)

  3. AoA DFT (often azimuth)

At which point depending on the antenna pattern a 4th DFT could be done for elevation (if the array is dense)

If the array is not dense a different cube might be generate

At this point it becomes useful to introduce the concept of radar cube though a misnomer as radar tensor or radar hypercube would be more appropriate since there are 4 dimensions in the data:

  • range

  • doppler

  • azimuth

  • elevation

SYMPY verifications

The following section, goes over the different equations step by step leveraging sympy to verify the absence of typos

[ ]:
# 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("possibly running from Google Colabs, doing required installs")
    !pip install antlr4-python3-runtime==4.11
    !pip install pdflatex
    !sudo apt-get install texlive-latex-recommended
    !sudo apt install texlive-latex-extra
    !sudo apt install dvipng
from sympy.parsing.latex import parse_latex
from IPython.display import Image, display
from sympy import preview
from sympy import init_printing, latex, Symbol
import matplotlib.image as mpimg
from matplotlib import pyplot as plt

# f_0: the fundamental frequency of the chirp being transmitted
f_0 = Symbol(r'f_0') # the chirp fundamental frequency
f_c = Symbol(r'f_c') # the IF frequency
# s the slope of the chirp
s = Symbol(r"s")
# the time referenced to the begining of the chirp
t = Symbol(r"t")
Delta = Symbol(r"\Delta") # initial time of flight
delta = Symbol(r"\delta") # change in time of flight
Phi_0 = Symbol(r'\Phi_0')
Psi_0 = Symbol(r'\Psi_0')

nu = Symbol(r"\nu")
eq_n = 0 # equation numbering
running from git folder, using local path (latest) mmWrt code c:\git\mmWrt

FMCW equation

Chirping

A chirp is defined by a linearly increasing frequency.

\[f(t) = f_{0} + s * t\]

Where:

  • $ f_{0} $ is the frequency at the begining of the chirp

  • s is the slope of frequency increase over time during the chirp

  • t is the time referenced to the beginning of the chirp.

Given that the instant frequency is the derivative of the phase, the phase of transmitted signal is defined by:

\[\mathit{\Phi_{TX}(t)} = \Phi_0 + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right)\]

Definitions

  • $ :nbsphinx-math:`Phi `$ is the full phase (including time or distance variables)

  • $ \phi`$, :math:phi_0`, \(\phi_1\) are the residual phase in TX signal

  • $ :nbsphinx-math:`psi `$ is the residual phase in the IF signal

[ ]:
# sympy quirk seems f_0 needs to be first defined as f o then formally subsituted
phi_TX_t = parse_latex(r"2 \pi \cdot ( \mathit{f o} \cdot t + \frac{s}{2} \cdot t^2 ) + \mathit{Phi}_0")
phi_TX_t = phi_TX_t.subs("f o", f_0)
phi_TX_t = phi_TX_t.subs("Phi",Phi_0)
print("latex of phi_TX_t:")
eq_n += 1
print(f"{latex(phi_TX_t)} (Eq {eq_n})")
print("which gets rendered as:")
phi_TX_t
latex of phi_TX_t:
\Phi_{0} + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right) (Eq 1)
which gets rendered as:
$\displaystyle \Phi_{0} + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right)$

So the full transmitted signal is

\[\cos(\Phi_{TX}(t)) = \cos(\Phi_0 + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right))\]

Received signal

phase of the RX signal is time delayed version of TX:

\[\Phi_{RX}(t) = \Phi_{TX}(t-\Delta)\]

which can be re-written as (the convention to write \(\left(- \Delta + t\right)\) as opposed to \(\left(t - \Delta \right)\) is chosen to make comparison with simpy display of equation.

\[\Phi_{RX}(t) = \Phi_{0} + 2 \pi \left(f_{0} \left(- \Delta + t\right) + \frac{s \left(- \Delta + t\right)^{2}}{2}\right)\]

Where:

  • d being the distance to the scatterer (so travelled twice by the radar wave)

  • $ \Delta `$ is the time of flight (i.e. :math:frac{2 cdot d}{c}`)

  • $ Y_{RX}(t) = A_R \cdot `:nbsphinx-math:cos{phi_{RX}(t)}` $ is the physical signal received by the antennas.

[ ]:
phi_RX_t = phi_TX_t.subs("t", t-Delta)
eq_n += 1
print(f"{latex(phi_RX_t)} (Eq. {eq_n})")
phi_RX_t
\Phi_{0} + 2 \pi \left(f_{0} \left(- \Delta + t\right) + \frac{s \left(- \Delta + t\right)^{2}}{2}\right) (Eq. 2)
$\displaystyle \Phi_{0} + 2 \pi \left(f_{0} \left(- \Delta + t\right) + \frac{s \left(- \Delta + t\right)^{2}}{2}\right)$

Mixer and IF

The mixer multiplies RX and TX signals, the output is called the Intermediate Frequency, often abreviated IF signal

\[Y_{MIX}(t) = Y_{TX}(t) \cdot Y_{RX}(t)\]

replacing $ Y_{TX}(t) $ and $ Y_{RX}(t) $ by their respective equations :

\[Y_{MIX}(t) = A_R \cdot \cos(\Phi_{RX}(t)) \cdot A_T \cdot \cos(\Phi_{TX}(t))\]

We are now going to leverage Trigonometric relations to establish how the IF signal is actually how relatively lower frequency which is why it can be sampled by ADC converters.

Trigonometric relations

Trigonometric relations establish relationship between the product of 2 cosines, with the sum of 2 related cosines in the following relation:

\[A_1 \cdot cos(R) \cdot A_2 \cdot cos(T) = \frac{A_1 \cdot A_2 \cdot cos(R-T)}{2} + \frac{A_1 \cdot A_2 \cdot cos(R+T)}{2}\]

which in our case translates into:

\[A_R \cdot \cos(\mathit{\Phi_R}) \cdot A_T \cdot \cos(\mathit{\Phi_T}) = \frac{A_T \cdot A_R}{2} \cdot \cos(\mathit{\Phi_T}-\mathit{\Phi_R}) + \frac{A_T \cdot A_R}{2} \cdot \cos(\mathit{\Phi_T}+\mathit{\Phi_R})\]

The following cells will leverage simpy to prove those equalities, but one can already see that the IF signal is the sum of a low frequency content \(\Phi_T-\Phi_R\) and high-frequency content \(\Phi_T+\Phi_R\). The high-frequency content is implicitely removed in many textbooks. In reality a low pass filter will ensure no aliasing from the R+T signal gets into the IF.

[ ]:
# Y_T the transmitted signal
Y_T = parse_latex(r"A_T \cdot \cos(\mathit{PhiT})")
# Y_R the received signal
Y_R = parse_latex(r"A_R \cdot \cos(\mathit{PhiR})")
# Y_IF the IF signal (the low frequency content at the output of the mixer)
Y_L = parse_latex(r"\frac{A_T \cdot A_R}{2} \cdot \cos(\mathit{PhiT}-\mathit{PhiR})")
# Y_H: the high frequency content at the output of the mixer
Y_H = parse_latex(r"\frac{A_T \cdot A_R}{2} \cdot \cos(\mathit{PhiT}+\mathit{PhiR})")
# Phi_T the phase of Y_T
PhiT = Symbol("\Phi_T")
# Ph_R the phase of Y_R
PhiR = Symbol("\Phi_R")
Y_R = Y_R.subs("PhiR",PhiR)
Y_T = Y_T.subs("PhiT",PhiT)
Y_L_symbolic = Y_L.subs("PhiR",PhiR)
Y_L_symbolic = Y_L_symbolic.subs("PhiT",PhiT)
Y_H_symbolic = Y_H.subs("PhiR",PhiR)
Y_H_symbolic = Y_H_symbolic.subs("PhiT",PhiT)
# Y_MIX the signal at the output of the mixer before the low pass filter
Y_MIX_simbolic = Y_R * Y_T
print("Y_MIX symbollically is:")
Y_MIX_simbolic
Y_MIX symbollically is:
$\displaystyle A_{R} A_{T} \cos{\left(\Phi_{R} \right)} \cos{\left(\Phi_{T} \right)}$

Now we display this leveraging the trigonometric relations to highlight, high-frequency content and low-frequency content

[ ]:
Y_L_symbolic+Y_H_symbolic
$\displaystyle \frac{A_{R} A_{T} \cos{\left(\Phi_{R} - \Phi_{T} \right)}}{2} + \frac{A_{R} A_{T} \cos{\left(\Phi_{R} + \Phi_{T} \right)}}{2}$

Now we verify that both are the same

[ ]:
assert Y_MIX_simbolic.equals(Y_H_symbolic+Y_L_symbolic)

Mixer output

Mixer output is a 2 tone:

  • Y_H: high-frequency

  • Y_L: low-frequency (aka IF: intermediate frequency)

[ ]:
phi_RX_t + phi_TX_t
$\displaystyle 2 \Phi_{0} + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right) + 2 \pi \left(f_{0} \left(- \Delta + t\right) + \frac{s \left(- \Delta + t\right)^{2}}{2}\right)$

develop and simplify

[ ]:
# phi_2 is the phase of the `high frequency tone`
# here we write it explicitly to simplify the symbolic writting
# we define \mathit{Delta A} for convenience as to also remember it seems
# \mathic can accept spaces
# \mathic does not seem to accept numbers
# \mathic does not seem to accept lower case for first letter ?!?
phi_2_t = parse_latex(r"2 \pi * (2 \cdot \mathit{f o} \cdot t - \mathit{f o} \cdot \mathit{Delta A} + \frac{s}{2} \cdot \mathit{Delta A}^2 - s * t * \mathit{Delta A}  +s*t^2)  + 2 * \mathit{Phi}")
phi_2_t = phi_2_t.subs("Phi",Phi_0)
phi_2_t = phi_2_t.subs("Delta A",Delta)
phi_2_t = phi_2_t.subs("f o",f_0)
phi_2_t
$\displaystyle 2 \Phi_{0} + 2 \pi \left(\frac{\Delta^{2} s}{2} - \Delta f_{0} - \Delta s t + 2 f_{0} t + s t^{2}\right)$

we verify that the developped form and initial formulas are the same

[ ]:
# now we verify the two expressions match
phi_1_t = phi_RX_t+phi_TX_t
assert phi_2_t.equals(phi1_t)

Display the high frequency tone explicit formula

[ ]:
# Y_H_t is the `high frequency` component after the mixer
# we replace the symbol `PhiT` by the explicit formula
# then replace `PhiR` by its respective formula
Y_H_t = Y_H.subs("PhiT",phi_TX_t)
Y_H_t = Y_H_t.subs("PhiR",phi_RX_t)
# now we can display the full explicit equation of the high frequency tone
Y_H_t
$\displaystyle \frac{A_{R} A_{T} \cos{\left(2 \Phi_{0} + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right) + 2 \pi \left(f_{0} \left(- \Delta + t\right) + \frac{s \left(- \Delta + t\right)^{2}}{2}\right) \right)}}{2}$

Focus on the IF (low frequency component)

We derive here the lower frequency tone exact formula

[ ]:
phi_RX_t - phi_TX_t
$\displaystyle - 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right) + 2 \pi \left(f_{0} \left(- \Delta + t\right) + \frac{s \left(- \Delta + t\right)^{2}}{2}\right)$
[ ]:
# now we symbolically define the equation of the low frequency tone of the IF signal
Y_L_t = Y_L.subs("PhiR",phi_RX_t)
Y_L_t = Y_L_t.subs("PhiT",phi_TX_t)
# phi_l is the phase of the `low frequency tone` aka IF tone
# reminder $Delta A$ is required because of `quirks` in parse_latex and/or mathit handling of numbers for symbols
phi_l_t = parse_latex(r"2 \pi * (- \mathit{f o} \cdot \mathit{Delta A} + \frac{s}{2} \cdot \mathit{Delta A}^2 - s * t * \mathit{Delta A} )")
phi_l_t = phi_l_t.subs("Phi",Phi_0)
phi_l_t = phi_l_t.subs("Delta A",Delta)
phi_l_t = phi_l_t.subs("f o",f_0)
phi_l_t
$\displaystyle 2 \pi \left(\frac{\Delta^{2} s}{2} - \Delta f_{0} - \Delta s t\right)$

Eq 3

phase of the IF

[ ]:
eq_n += 1
print(f"{latex(phi_l_t)}  (Eq {eq_n})")
2 \pi \left(\frac{\Delta^{2} s}{2} - \Delta f_{0} - \Delta s t\right)  (Eq 3)
[ ]:
# verify that the two phase definitions match:
assert phi_l_t.equals(phi_RX_t-phi_TX_t)
[ ]:
Y_L_t
$\displaystyle \frac{A_{R} A_{T} \cos{\left(2 \pi \left(f_{0} \left(- \Delta + t\right) + \frac{s \left(- \Delta + t\right)^{2}}{2}\right) - 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right) \right)}}{2}$

Having verified the equation, we now rewrite it for simpler display

we introduce:

*\(f_c\) as the frequency of the received tone in IF (as a function of the slope and the time of flight)

  • \(\psi_0\) the residual phase in the IF

Where, from above we kept the following:

  • \(\Delta\) is the time of flight (i.e. \(\frac{2 \cdot d}{c}\))

[ ]:
fb_explicit = parse_latex(r"\mathit{Delta A} \cdot s")
fb_explicit = fb_explicit.subs("Delta A",Delta)
fc_symbol = Symbol("f_c")

# now we replace fb=Delta*s with fc
phi_l_new = phi_l_t.subs(fb_explicit,fc_symbol)
# and we display the new simplified form of the phase of the IF signal
print("phase of IF signal:")
phi_l_new
phase of IF signal:
$\displaystyle 2 \pi \left(\frac{\Delta f_{c}}{2} - \Delta f_{0} - f_{c} t\right)$

we now define \(\psi_0\) as the residual phase in IF

[ ]:
psi_0 = Symbol("\psi_0")
psi0_latex = parse_latex(r"\mathit{Delta A} \cdot \mathit{fc}/2 - \mathit{Delta A} \cdot \mathit{f o}")
# define psi0_e as the explicit form of the IF phase
# psi0_e = psi0_i.subs("Phi zero",Phi_0)
psi0_e = psi0_latex.subs("Delta A",Delta)
psi0_e = psi0_e.subs("fc",fc_implicit)
psi0_e = psi0_e.subs("f o",f_0)
psi_0
$\displaystyle \psi_{0}$

equals

[ ]:
psi0_e
$\displaystyle - \Delta f_{0} + \frac{\Delta f_{c}}{2}$
[ ]:
eq_n += 1
print(f"{latex(psi0_e)} (Eq. {eq_n})")
- \Delta f_{0} + \frac{\Delta f_{c}}{2} (Eq. 4)
[ ]:
# replace in the IF phase the constant offset by psi_0
phi_l_new = phi_l_new.subs(psi0,psi_0)
phi_l_new
$\displaystyle 2 \pi \left(\frac{\Delta f_{c}}{2} - \Delta f_{0} - f_{c} t\right)$

we now verify that the simplified IF signal is the same as phi_l_t

[ ]:
phi_l_t
$\displaystyle 2 \pi \left(\frac{\Delta^{2} s}{2} - \Delta f_{0} - \Delta s t\right)$

cases where the phase varies

While the underlying assumption in Fast Chirp FMCW (FC-FMCW), is that the distance is not changing during a chirp, the distance may change from chirp to chirp

Where:

$ :nbsphinx-math:`delta `$ is the change in time of flight

[ ]:
# seems that Delta B or delta A are not valid so setting delta as \mathit{D A}
Dd_latex = parse_latex(r"\mathit{Delta A} + \mathit{D A}")
Dd = Dd_latex.subs("Delta A",Delta)
Dd = Dd.subs("D A",delta)
Dd
$\displaystyle \Delta + \delta$
[ ]:
psi1_e = psi0_e.subs(Delta, Dd)
psi1_e
$\displaystyle - f_{0} \left(\Delta + \delta\right) + \frac{f_{c} \left(\Delta + \delta\right)}{2}$
[ ]:
eq_n += 1
print(f"{latex(psi1_e)} (Eq. {eq_n})")
- f_{0} \left(\Delta + \delta\right) + \frac{f_{c} \left(\Delta + \delta\right)}{2} (Eq. 5)
[ ]:
psi1_e-psi0_e
$\displaystyle \Delta f_{0} - \frac{\Delta f_{c}}{2} - f_{0} \left(\Delta + \delta\right) + \frac{f_{c} \left(\Delta + \delta\right)}{2}$
[ ]:
Delta_Psi_latex = parse_latex(r"\frac{\mathit{D A} \cdot \mathit{f c}}{2} - \mathit{D A} \cdot \mathit{f o}")
Delta_Psi = Delta_Psi_latex.subs("D A",delta)
Delta_Psi = Delta_Psi.subs("f c",f_c)
Delta_Psi = Delta_Psi.subs("f o",f_0)
Delta_Psi
$\displaystyle - \delta f_{0} + \frac{\delta f_{c}}{2}$

verify they are both the same

[ ]:
eq_n += 1
print(f"{latex(Delta_Psi)} (Eq. {eq_n})")
- \delta f_{0} + \frac{\delta f_{c}}{2} (Eq. 6)
[ ]:
psi1_e-psi0_e
$\displaystyle \Delta f_{0} - \frac{\Delta f_{c}}{2} - f_{0} \left(\Delta + \delta\right) + \frac{f_{c} \left(\Delta + \delta\right)}{2}$
[ ]:
Delta_Psi
$\displaystyle - \delta f_{0} + \frac{\delta f_{c}}{2}$
[ ]:
assert Delta_Psi.equals(psi1_e-psi0_e)

here we need to consider that f0 ~60e9 and fc ~1e7 MHz

BACK-UP section

debugging symbols in a sympy equations

Delta_Psi.free_symbols

Syntax Examples with Sympy

[ ]:
P = Symbol(r"P")
phi_TX_full = parse_latex(r"2 \pi \cdot ( f_0 \cdot t + \frac{s}{2} \cdot t^2 ) + P")
Y_T = parse_latex(r"A_T \cdot \cos(P)")
print("Phi T:", latex(P))
print("phi full:",latex(phi_TX_full))

Y_T1 = Y_T.subs(P,phi_TX_full)
print("Y1", latex(Y_T1))
print("works")
Phi T: P
phi full: P + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right)
Y1 A_{T} \cos{\left(P + 2 \pi \left(f_{0} t + t^{2} \frac{s}{2}\right) \right)}
works
[ ]:
P0 = Symbol(r"Phi")
x = Symbol("x")
phi_TX_full0 = parse_latex(r"2 \pi \cdot ( f_0 \cdot t + \frac{s}{2} \cdot t^2 ) + P")
Y_T = parse_latex(r"A_T \cdot \mathit{Phi}")
print(f"P0: {P0}")
print(f"phi full: {Y_T}")

Y_T0 = Y_T.subs(P0,x)
print("SUBS:", latex(Y_T0))
print("works")
P0: Phi
phi full: A_{T}*Phi
SUBS: A_{T} x
works
[ ]:
x = Symbol("x")
phi_TX_full0 = parse_latex(r"2 \pi \cdot ( f_0 \cdot t + \frac{s}{2} \cdot t^2 ) + P")
Y_T = parse_latex(r"A_T \cdot \mathit{Phi}")
print(f"P0: {P0}")
print(f"Y_T: {Y_T}")

Y_T0 = Y_T.subs("Phi",x)
print("SUBS:", latex(Y_T0))
print("works")
P0: Phi
Y_T: A_{T}*Phi
SUBS: A_{T} x
works
[ ]:
P5 = Symbol(r"\Psi_{5}")
# P5 = Symbol("P5")   # works
Y = parse_latex(r"A_T \cdot \mathit{psi-5}") # does not work
Y = parse_latex(r"A_T \cdot \mathit{psi 5}") # does not work
Y = parse_latex(r"A_T \cdot \mathit{psi5}") # does not work
Y = parse_latex(r"A_T \cdot \mathit{psiv}") # works
# Y = parse_latex(r"A_T \cdot \mathit{psi v}") # works

print("Y",Y)
Y_T1 = Y.subs("psiv",P5)
print("SUBS:", latex(Y_T1))
P5
Y A_{T}*psiv
SUBS: A_{T} \Psi_{5}
$\displaystyle \Psi_{5}$
[ ]:

[ ]: