{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "yXJK8C-NOVcZ" }, "source": [ "# FMCW Radar 201 - Maths\n", "\n", "This section will cover a bottom up approach to derive some key equations used throughout FMCW radar.\n", "\n", "You can open this workbook in Google Colab to experiment with it, by clicking on:\n", "[![](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/matt-chv/mmWrt/blob/main/docs/FMCW-Radar-201_Maths.ipynb)\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "id": "PkwLlEkvPFaO" }, "source": [ "## Transmitting a chirp\n", "\n", "A chirp is defined by a linearly increasing frequency.\n", "\n", "$$ f(t) = f_{0} + s * t$$\n", "\n", "Where:\n", "\n", "* $f_{0}$ is the frequency at the begining of the chirp\n", "* s is the slope of frequency increase over time during the chirp\n", "* t is the time referenced to the beginning of the chirp.\n", "\n", "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.\n", "We will just consider proven and valid that:\n", "\n", "$$ f(t) = \\frac{1}{2 \\pi} \\cdot \\frac{\\partial \\phi (t)}{\\partial t} $$\n", "\n", "From which we can derive, that the phase of the transmitted FMCW radar is:\n", "\n", "$$ \\mathit{\\Phi_{TX}(t)} = \\Phi_0 + 2 \\pi \\left(f_{0} t + t^{2} \\frac{s}{2}\\right) $$\n", "\n", "### Definitions\n", "\n", "* $\\Phi$ is the full phase (including time or distance variables)\n", "* $\\phi$, $\\phi_0$, $\\phi_1$ are the phase offsets\n", "* $\\psi$ are the number of cycles (i.e. $\\frac{\\phi}{2 \\pi}$ )\n", "\n", "So the full transmitted signal is\n", "\n", "$$ 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)) $$\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "id": "F4wD6j6YPNa_" }, "source": [ "## Receiving a chirp\n", "\n", "The received signal is the transmitted signal being delayed by a constant time $ \\Delta $, which translates in phase of the RX signal is time delayed version of TX:\n", "\n", "$$ \\Phi_{RX}(t) = \\Phi_{TX}(t-\\Delta) $$\n", "\n", "$$ \\Phi_{RX}(t) = \\Phi_{0} + 2 \\pi \\left(f_{0} \\left(t - \\Delta \\right) + \\frac{s \\left(t- \\Delta \\right)^{2}}{2}\\right) $$\n", "\n", "Where:\n", "\n", "* d being the distance to the scatterer (so travelled twice by the radar wave)\n", "* $\\Delta$ is the time of flight (i.e. $\\frac{2 \\cdot d}{c}$)\n", "\n", "So the full received signal is:\n", "\n", "$$ 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)$$\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "id": "QVms-SQGPTXI" }, "source": [ "## Mixer RX and TX leading to IF\n", "\n", "The mixer multiplies RX and TX signals\n", "\n", "$$ Y_{MIX}(t) = Y_{TX}(t) \\cdot Y_{RX}(t) $$\n", "replacing $ Y_{TX}(t) $ and $ Y_{RX}(t) $ by their respective equations :\n", "$$ Y_{MIX}(t) = A_R \\cdot \\cos(\\Phi_{RX}(t)) \\cdot A_T \\cdot \\cos(\\Phi_{TX}(t))$$\n", "\n", "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.\n", "\n", "### Trigonometric relations\n", "\n", "Trigonometric relations establish relationship between the product of 2 cosines, with the sum of 2 related cosines in the following relation:\n", "\n", "$$ 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} $$\n", "\n", "which in our case translates into:\n", "\n", "$$ 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}) $$\n", "\n", "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.\n", "\n", "The output of the low pass filter after the MIXER is called the `Intermediate Frequency`, often abreviated `IF signal`\n", "\n", "$$ Y_{IF}(t) = cos\\left( 2 \\pi \\left(\\frac{\\Delta^{2} s}{2} - \\Delta f_{0} - \\Delta s t\\right) \\right) $$ Eq. 3\n", "\n", "Which defining:\n", "\n", "* $\\Psi_0 = - \\Delta f_{0} + \\frac{\\Delta f_{c}}{2}$\n", "* $f_c = \\Delta \\cdot s$\n", "\n", "Becomes\n", "\n", "$$ Y_{IF}(t) = cos\\left( 2 \\pi \\left(\\Psi_0 + f_c \\cdot t \\right) \\right) $$\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "id": "qeVqtRFbRwXj" }, "source": [ "## Changes in distance\n", "\n", "Assuming that there is a small change in the distance $ d $, it yields a small change in the time of flight $ \\delta$which from $\\Delta $ becomes $\\Delta + \\delta$\n", "\n", "Then the new number of cycle $\\Psi_1$ is\n", "\n", "$$ \\Psi_1 = - f_{0} \\left(\\Delta + \\delta\\right) + \\frac{f_{c} \\left(\\Delta + \\delta\\right)}{2} $$ (Eq. 5)\n", "\n", "So the change in number of cycles between the two signals becomes\n", "\n", "$$ \\Psi_1 - \\Psi_0 = - \\delta f_0 + \\frac{\\delta f_{c}}{2} $$\n", "\n", "```{warning}\n", "From this point on, we make assumptions about the relative frequency of the carrier and the IF.\n", "```\n", "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\n", "\n", "$$ \\Psi_1 - \\Psi_0 \\approx - \\delta f_0 = - \\delta \\cdot \\frac{c}{\\lambda} $$" ] }, { "cell_type": "markdown", "metadata": { "id": "PpSjOCbwOVca" }, "source": [ "### Implications for velocity or AoA\n", "\n", "**Illustration to come**\n", "\n", "Given that\n", "\n", "$$ \\Psi_1 - \\Psi_0 \\approx - \\delta f_0 = - \\delta \\cdot \\frac{c}{\\lambda} $$\n", "\n", "or\n", "\n", "$$ \\frac{\\partial \\Psi }{\\partial \\Delta} \\approx - f_0 = \\frac{c}{\\lambda} $$\n", "\n", "* for range the change of time of flight is proportional to twice the change of distance ($\\Delta = \\frac{2 \\cdot D}{c}$)\n", "* for AoA the change of time of flight is proportional to the change of distance ($\\Delta = \\frac{D}{c}$)\n", "\n", "Since\n", "\n", "$$\\frac{\\partial \\Psi }{\\partial D} = \\frac{\\partial \\Psi }{\\partial \\Delta} \\cdot \\frac{\\partial \\Delta }{\\partial D}$$\n", "\n", "\n", "for range the number of cycles is then\n", "\n", "$$ \\frac{\\partial \\Psi }{\\partial D} = \\frac{\\partial \\Psi }{\\partial \\Delta} \\cdot \\frac{2}{c}= \\frac{c }{\\lambda} \\cdot \\frac{2}{c}= \\frac{2}{\\lambda} $$\n", "\n", "or in phase shift ($\\Phi=2 \\cdot \\pi \\cdot \\Psi$)\n", "\n", "$$ \\frac{\\partial \\Phi }{\\partial D} = \\frac{4 \\cdot \\pi}{\\lambda} $$\n", "\n", "which leads for speed measurement:\n", "\n", "$$ 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}$$\n", "\n" ] }, { "cell_type": "markdown", "source": [ "### Angle of Arrival (AoA)\n", "\n", "For AoA, the number of cycle count changes by half:\n", "\n", "$$\\frac{\\partial \\Psi }{\\partial D} = \\frac{1}{\\lambda}$$\n", "\n", "or in phase shift\n", "\n", "$$\\frac{\\partial \\Phi }{\\partial D} = \\frac{2 \\cdot \\pi}{\\lambda}$$\n", "\n", "with x, the distance between two antennas and theta the angle from boresight\n", "\n", "$$\\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)$$\n", "\n", "or\n", "\n", "$$\\theta=sin^{-1}(\\frac{\\lambda \\cdot \\frac{\\partial \\Phi }{\\partial x}}{2 \\cdot \\pi \\cdot x})$$\n" ], "metadata": { "id": "GgodFTN5klx7" } }, { "cell_type": "markdown", "metadata": { "id": "LOG1gMPnUMg5" }, "source": [ "## Discrete Fourier Transform (DFT) to the rescue\n", "\n", "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.\n", "\n", "Given X the `Discrete Fourier Transform` (`DFT`) of $Y_{IF}$\n", "$$ \\mathscr{F}(Y_{IF}) = X $$\n", "\n", "The tone is found by looking for the peak of the magnitude of the DFT by\n", "$$ k_p = \\operatorname*{argmax}_k \\lvert X[k] \\rvert $$\n", "\n", "For speed (`doppler`) and angle of arrival (`AoA`), it is important to note that the DFT transform has a closed form:\n", "\n", "$$ X[k_p] = A[k_p] \\cdot e^{i \\cdot \\phi[k_p]} $$\n", "\n", "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:\n", "\n", "$$\\mathscr{F}\\big\\{x(t-t_0)\\big\\}=X(f)e^{-j2\\pi f t_0}$$\n", "\n", "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)." ] }, { "cell_type": "markdown", "metadata": { "id": "5gY2pIVXzSJe" }, "source": [ "### Second DFT\n", "\n", "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\n", "\n", "$$ \\phi[k_p](t) = 2 \\cdot \\pi \\cdot f_1 \\cdot t $$\n", "\n", "or in discrete time with T the time interval between each chirps\n", "\n", "$$ \\phi[k_p](m \\cdot T) = 2 \\cdot \\pi \\cdot f_1 \\cdot m \\cdot T $$\n", "\n", "Then the DFT of X will have a peak at the closest position to f1\n", "\n", "with\n", "$$ \\mathscr{F}(X_k[m]) = Z $$\n", "\n", "then\n", "$$ m_p = \\operatorname*{argmax}_m \\lvert Z[m] \\rvert $$\n" ] }, { "cell_type": "markdown", "metadata": { "id": "4kiW7zd3USMk" }, "source": [ "### Third and fourth DFT\n", "\n", "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.\n", "\n", "Most frequent usage of the third DFT is in the following flow:\n", "1. Range DFT (often a FFT)\n", "2. Dopple DFT (ibid)\n", "3. AoA DFT (often azimuth)\n", "\n", "At which point depending on the antenna pattern a 4th DFT could be done for elevation (if the array is dense)\n", "\n", "If the array is not dense a different cube might be generate\n", "\n", "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:\n", "* range\n", "* doppler\n", "* azimuth\n", "* elevation" ] }, { "cell_type": "markdown", "metadata": { "id": "rHXwbcCVOVca" }, "source": [ "## SYMPY verifications\n", "\n", "The following section, goes over the different equations step by step leveraging sympy to verify the absence of typos" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "vTKU3XV7Zk5s", "outputId": "dbef217c-3427-414c-ecb5-87795e1ff9bf" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "running from git folder, using local path (latest) mmWrt code c:\\git\\mmWrt\n" ] } ], "source": [ "# Install a pip package in the current Jupyter kernel\n", "import sys\n", "from os.path import abspath, basename, join, pardir\n", "import datetime\n", "\n", "# hack to handle if running from git cloned folder or stand alone (like Google Colab)\n", "cw = basename(abspath(join(\".\")))\n", "dp = abspath(join(\".\",pardir))\n", "if cw==\"docs\" and basename(dp) == \"mmWrt\":\n", " # running from cloned folder\n", " print(\"running from git folder, using local path (latest) mmWrt code\", dp)\n", " sys.path.insert(0, dp)\n", "else:\n", " print(\"possibly running from Google Colabs, doing required installs\")\n", " !pip install antlr4-python3-runtime==4.11\n", " !pip install pdflatex\n", " !sudo apt-get install texlive-latex-recommended\n", " !sudo apt install texlive-latex-extra\n", " !sudo apt install dvipng\n", "from sympy.parsing.latex import parse_latex\n", "from IPython.display import Image, display\n", "from sympy import preview\n", "from sympy import init_printing, latex, Symbol\n", "import matplotlib.image as mpimg\n", "from matplotlib import pyplot as plt\n", "\n", "# f_0: the fundamental frequency of the chirp being transmitted\n", "f_0 = Symbol(r'f_0') # the chirp fundamental frequency\n", "f_c = Symbol(r'f_c') # the IF frequency\n", "# s the slope of the chirp\n", "s = Symbol(r\"s\")\n", "# the time referenced to the begining of the chirp\n", "t = Symbol(r\"t\")\n", "Delta = Symbol(r\"\\Delta\") # initial time of flight\n", "delta = Symbol(r\"\\delta\") # change in time of flight\n", "Phi_0 = Symbol(r'\\Phi_0')\n", "Psi_0 = Symbol(r'\\Psi_0')\n", "\n", "nu = Symbol(r\"\\nu\")\n", "eq_n = 0 # equation numbering" ] }, { "cell_type": "markdown", "metadata": { "id": "AKSgb5u7Jk2T" }, "source": [ "## FMCW equation\n", "\n", "## Chirping\n", "\n", "A chirp is defined by a linearly increasing frequency.\n", "\n", "$$ f(t) = f_{0} + s * t$$\n", "\n", "Where:\n", "\n", "* $ f_{0} $ is the frequency at the begining of the chirp\n", "* s is the slope of frequency increase over time during the chirp\n", "* t is the time referenced to the beginning of the chirp.\n", "\n", "Given that the instant frequency is the derivative of the phase, the phase of transmitted signal is defined by:\n", "\n", "$$ \\mathit{\\Phi_{TX}(t)} = \\Phi_0 + 2 \\pi \\left(f_{0} t + t^{2} \\frac{s}{2}\\right) $$\n", "\n", "## Definitions\n", "\n", "* $ \\Phi $ is the full phase (including time or distance variables)\n", "* $ \\phi$, $\\phi_0$, $\\phi_1$ are the residual phase in TX signal\n", "* $ \\psi $ is the residual phase in the IF signal" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 65 }, "id": "hSUw7s-BYdgv", "outputId": "f2087bab-1ed8-4341-c752-ca5c25205a2b" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "latex of phi_TX_t:\n", "\\Phi_{0} + 2 \\pi \\left(f_{0} t + t^{2} \\frac{s}{2}\\right) (Eq 1)\n", "which gets rendered as:\n" ] }, { "data": { "text/latex": [ "$\\displaystyle \\Phi_{0} + 2 \\pi \\left(f_{0} t + t^{2} \\frac{s}{2}\\right)$" ], "text/plain": [ "\\Phi_0 + 2*pi*(f_0*t + t**2*(s/2))" ] }, "execution_count": 331, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# sympy quirk seems f_0 needs to be first defined as f o then formally subsituted\n", "phi_TX_t = parse_latex(r\"2 \\pi \\cdot ( \\mathit{f o} \\cdot t + \\frac{s}{2} \\cdot t^2 ) + \\mathit{Phi}_0\")\n", "phi_TX_t = phi_TX_t.subs(\"f o\", f_0)\n", "phi_TX_t = phi_TX_t.subs(\"Phi\",Phi_0)\n", "print(\"latex of phi_TX_t:\")\n", "eq_n += 1\n", "print(f\"{latex(phi_TX_t)} (Eq {eq_n})\")\n", "print(\"which gets rendered as:\")\n", "phi_TX_t" ] }, { "cell_type": "markdown", "metadata": { "id": "Z8mqzjX0KSjX" }, "source": [ "So the full transmitted signal is\n", "$$ \\cos(\\Phi_{TX}(t)) = \\cos(\\Phi_0 + 2 \\pi \\left(f_{0} t + t^{2} \\frac{s}{2}\\right)) $$" ] }, { "cell_type": "markdown", "metadata": { "id": "iRorfDhTKhzD" }, "source": [ "## Received signal\n", "\n", "phase of the RX signal is time delayed version of TX:\n", "\n", "$$ \\Phi_{RX}(t) = \\Phi_{TX}(t-\\Delta) $$\n", "\n", "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.\n", "\n", "$$ \\Phi_{RX}(t) = \\Phi_{0} + 2 \\pi \\left(f_{0} \\left(- \\Delta + t\\right) + \\frac{s \\left(- \\Delta + t\\right)^{2}}{2}\\right) $$\n", "\n", "Where:\n", "\n", "* d being the distance to the scatterer (so travelled twice by the radar wave)\n", "* $ \\Delta $ is the time of flight (i.e. $\\frac{2 \\cdot d}{c}$)\n", "* $ Y_{RX}(t) = A_R \\cdot \\cos{\\phi_{RX}(t)} $ is the physical signal received by the antennas.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 85 }, "id": "VCVO6imsy5WG", "outputId": "a4db2197-6478-4e4c-a091-c11b50c82f8b" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\\Phi_{0} + 2 \\pi \\left(f_{0} \\left(- \\Delta + t\\right) + \\frac{s \\left(- \\Delta + t\\right)^{2}}{2}\\right) (Eq. 2)\n" ] }, { "data": { "text/latex": [ "$\\displaystyle \\Phi_{0} + 2 \\pi \\left(f_{0} \\left(- \\Delta + t\\right) + \\frac{s \\left(- \\Delta + t\\right)^{2}}{2}\\right)$" ], "text/plain": [ "\\Phi_0 + 2*pi*(f_0*(-\\Delta + t) + s*(-\\Delta + t)**2/2)" ] }, "execution_count": 332, "metadata": {}, "output_type": "execute_result" } ], "source": [ "phi_RX_t = phi_TX_t.subs(\"t\", t-Delta)\n", "eq_n += 1\n", "print(f\"{latex(phi_RX_t)} (Eq. {eq_n})\")\n", "phi_RX_t" ] }, { "cell_type": "markdown", "metadata": { "id": "B5CE5OgQL47x" }, "source": [ "## Mixer and IF\n", "\n", "The mixer multiplies RX and TX signals, the output is called the `Intermediate Frequency`, often abreviated `IF signal`\n", "\n", "$$ Y_{MIX}(t) = Y_{TX}(t) \\cdot Y_{RX}(t) $$\n", "replacing $ Y_{TX}(t) $ and $ Y_{RX}(t) $ by their respective equations :\n", "$$ Y_{MIX}(t) = A_R \\cdot \\cos(\\Phi_{RX}(t)) \\cdot A_T \\cdot \\cos(\\Phi_{TX}(t))$$\n", "\n", "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.\n", "\n", "## Trigonometric relations\n", "\n", "Trigonometric relations establish relationship between the product of 2 cosines, with the sum of 2 related cosines in the following relation:\n", "\n", "$$ 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} $$\n", "\n", "which in our case translates into:\n", "\n", "$$ 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}) $$\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 56 }, "id": "MbI5auwUNASd", "outputId": "db188b9d-5810-44a7-87d9-8acb47bbfcb0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Y_MIX symbollically is:\n" ] }, { "data": { "text/latex": [ "$\\displaystyle A_{R} A_{T} \\cos{\\left(\\Phi_{R} \\right)} \\cos{\\left(\\Phi_{T} \\right)}$" ], "text/plain": [ "A_{R}*A_{T}*cos(\\Phi_R)*cos(\\Phi_T)" ] }, "execution_count": 273, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Y_T the transmitted signal\n", "Y_T = parse_latex(r\"A_T \\cdot \\cos(\\mathit{PhiT})\")\n", "# Y_R the received signal\n", "Y_R = parse_latex(r\"A_R \\cdot \\cos(\\mathit{PhiR})\")\n", "# Y_IF the IF signal (the low frequency content at the output of the mixer)\n", "Y_L = parse_latex(r\"\\frac{A_T \\cdot A_R}{2} \\cdot \\cos(\\mathit{PhiT}-\\mathit{PhiR})\")\n", "# Y_H: the high frequency content at the output of the mixer\n", "Y_H = parse_latex(r\"\\frac{A_T \\cdot A_R}{2} \\cdot \\cos(\\mathit{PhiT}+\\mathit{PhiR})\")\n", "# Phi_T the phase of Y_T\n", "PhiT = Symbol(\"\\Phi_T\")\n", "# Ph_R the phase of Y_R\n", "PhiR = Symbol(\"\\Phi_R\")\n", "Y_R = Y_R.subs(\"PhiR\",PhiR)\n", "Y_T = Y_T.subs(\"PhiT\",PhiT)\n", "Y_L_symbolic = Y_L.subs(\"PhiR\",PhiR)\n", "Y_L_symbolic = Y_L_symbolic.subs(\"PhiT\",PhiT)\n", "Y_H_symbolic = Y_H.subs(\"PhiR\",PhiR)\n", "Y_H_symbolic = Y_H_symbolic.subs(\"PhiT\",PhiT)\n", "# Y_MIX the signal at the output of the mixer before the low pass filter\n", "Y_MIX_simbolic = Y_R * Y_T\n", "print(\"Y_MIX symbollically is:\")\n", "Y_MIX_simbolic" ] }, { "cell_type": "markdown", "metadata": { "id": "eetUEz5_OVcb" }, "source": [ "Now we display this leveraging the trigonometric relations to highlight, high-frequency content and low-frequency content" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 53 }, "id": "TxTVJHXHO_3F", "outputId": "ff86f2db-7d1b-4a85-9df2-b5dad637c67b" }, "outputs": [ { "data": { "text/latex": [ "$\\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}$" ], "text/plain": [ "A_{R}*A_{T}*cos(\\Phi_R - \\Phi_T)/2 + A_{R}*A_{T}*cos(\\Phi_R + \\Phi_T)/2" ] }, "execution_count": 274, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y_L_symbolic+Y_H_symbolic" ] }, { "cell_type": "markdown", "metadata": { "id": "pPqefmvDOVcb" }, "source": [ "Now we verify that both are the same" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "59vzxH_DO5zH" }, "outputs": [], "source": [ "assert Y_MIX_simbolic.equals(Y_H_symbolic+Y_L_symbolic)" ] }, { "cell_type": "markdown", "metadata": { "id": "DNvoLC3gTYuj" }, "source": [ "## Mixer output\n", "\n", "Mixer output is a 2 tone:\n", "\n", "* Y_H: high-frequency\n", "* Y_L: low-frequency (aka IF: intermediate frequency)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 68 }, "id": "QPFdLv_ZUuYu", "outputId": "d4d4b303-9271-4c6f-b884-9c69ace14ad7" }, "outputs": [ { "data": { "text/latex": [ "$\\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)$" ], "text/plain": [ "2*\\Phi_0 + 2*pi*(f_0*t + t**2*(s/2)) + 2*pi*(f_0*(-\\Delta + t) + s*(-\\Delta + t)**2/2)" ] }, "execution_count": 285, "metadata": {}, "output_type": "execute_result" } ], "source": [ "phi_RX_t + phi_TX_t" ] }, { "cell_type": "markdown", "metadata": { "id": "PjAA-iVVOVcc" }, "source": [ "develop and simplify" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 58 }, "id": "aNZOC_woTmIC", "outputId": "2e096216-72e7-4963-935e-eac1fd61ee9e" }, "outputs": [ { "data": { "text/latex": [ "$\\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)$" ], "text/plain": [ "2*\\Phi_0 + 2*pi*(\\Delta**2*s/2 - \\Delta*f_0 - \\Delta*s*t + 2*f_0*t + s*t**2)" ] }, "execution_count": 305, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# phi_2 is the phase of the `high frequency tone`\n", "# here we write it explicitly to simplify the symbolic writting\n", "# we define \\mathit{Delta A} for convenience as to also remember it seems\n", "# \\mathic can accept spaces\n", "# \\mathic does not seem to accept numbers\n", "# \\mathic does not seem to accept lower case for first letter ?!?\n", "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}\")\n", "phi_2_t = phi_2_t.subs(\"Phi\",Phi_0)\n", "phi_2_t = phi_2_t.subs(\"Delta A\",Delta)\n", "phi_2_t = phi_2_t.subs(\"f o\",f_0)\n", "phi_2_t" ] }, { "cell_type": "markdown", "metadata": { "id": "oZ0jAxMxOVcc" }, "source": [ "we verify that the developped form and initial formulas are the same" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "PopQ20XGX3H3" }, "outputs": [], "source": [ "# now we verify the two expressions match\n", "phi_1_t = phi_RX_t+phi_TX_t\n", "assert phi_2_t.equals(phi1_t)" ] }, { "cell_type": "markdown", "metadata": { "id": "hDNTB69QOVcc" }, "source": [ "Display the `high frequency` tone explicit formula" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 66 }, "id": "x5BB56wjavt3", "outputId": "7b9f04d5-d076-4fdb-9cbb-1905d37aba67" }, "outputs": [ { "data": { "text/latex": [ "$\\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}$" ], "text/plain": [ "A_{R}*A_{T}*cos(2*\\Phi_0 + 2*pi*(f_0*t + t**2*(s/2)) + 2*pi*(f_0*(-\\Delta + t) + s*(-\\Delta + t)**2/2))/2" ] }, "execution_count": 279, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Y_H_t is the `high frequency` component after the mixer\n", "# we replace the symbol `PhiT` by the explicit formula\n", "# then replace `PhiR` by its respective formula\n", "Y_H_t = Y_H.subs(\"PhiT\",phi_TX_t)\n", "Y_H_t = Y_H_t.subs(\"PhiR\",phi_RX_t)\n", "# now we can display the full explicit equation of the high frequency tone\n", "Y_H_t" ] }, { "cell_type": "markdown", "metadata": { "id": "f7L0TgJ0bFbr" }, "source": [ "## Focus on the IF (low frequency component)\n", "\n", "We derive here the lower frequency tone exact formula" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 68 }, "id": "8vSCxdTPbTbx", "outputId": "6ac57862-b32f-40aa-b1a2-f52e575715de" }, "outputs": [ { "data": { "text/latex": [ "$\\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)$" ], "text/plain": [ "-2*pi*(f_0*t + t**2*(s/2)) + 2*pi*(f_0*(-\\Delta + t) + s*(-\\Delta + t)**2/2)" ] }, "execution_count": 280, "metadata": {}, "output_type": "execute_result" } ], "source": [ "phi_RX_t - phi_TX_t" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 58 }, "id": "eiSm_Vq4alG4", "outputId": "fdd73d67-f841-478e-c3df-253b392f0aa2" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle 2 \\pi \\left(\\frac{\\Delta^{2} s}{2} - \\Delta f_{0} - \\Delta s t\\right)$" ], "text/plain": [ "2*pi*(\\Delta**2*s/2 - \\Delta*f_0 - \\Delta*s*t)" ] }, "execution_count": 319, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# now we symbolically define the equation of the low frequency tone of the IF signal\n", "Y_L_t = Y_L.subs(\"PhiR\",phi_RX_t)\n", "Y_L_t = Y_L_t.subs(\"PhiT\",phi_TX_t)\n", "# phi_l is the phase of the `low frequency tone` aka IF tone\n", "# reminder $Delta A$ is required because of `quirks` in parse_latex and/or mathit handling of numbers for symbols\n", "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} )\")\n", "phi_l_t = phi_l_t.subs(\"Phi\",Phi_0)\n", "phi_l_t = phi_l_t.subs(\"Delta A\",Delta)\n", "phi_l_t = phi_l_t.subs(\"f o\",f_0)\n", "phi_l_t" ] }, { "cell_type": "markdown", "metadata": { "id": "ohnJ_p8COVcc" }, "source": [ "### Eq 3\n", "phase of the IF" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "aAim-q0pOVcc", "outputId": "de47a18d-d0e0-4ee2-f0a3-c468955b8e3e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 \\pi \\left(\\frac{\\Delta^{2} s}{2} - \\Delta f_{0} - \\Delta s t\\right) (Eq 3)\n" ] } ], "source": [ "eq_n += 1\n", "print(f\"{latex(phi_l_t)} (Eq {eq_n})\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "VHHvVKFpbm2x" }, "outputs": [], "source": [ "# verify that the two phase definitions match:\n", "assert phi_l_t.equals(phi_RX_t-phi_TX_t)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "VIO8R0IvOVcc", "outputId": "b49692cb-99c7-4dfe-fe64-b4def4f4e61e" }, "outputs": [ { "data": { "text/latex": [ "$\\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}$" ], "text/plain": [ "A_{R}*A_{T}*cos(2*pi*(f_{0}*(-\\Delta + t) + s*(-\\Delta + t)**2/2) - 2*pi*(f_{0}*t + t**2*(s/2)))/2" ] }, "execution_count": 199, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y_L_t" ] }, { "cell_type": "markdown", "metadata": { "id": "RkSamo32OVcd" }, "source": [ "#### Having verified the equation, we now rewrite it for simpler display\n", "\n", "we introduce:\n", "\n", "*$f_c$ as the frequency of the received tone in IF (as a function of the slope and the time of flight)\n", "* $\\psi_0$ the residual phase in the IF\n", "\n", "Where, from above we kept the following:\n", "\n", "* $\\Delta$ is the time of flight (i.e. $\\frac{2 \\cdot d}{c}$)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 75 }, "id": "aIGHTNkvrMuB", "outputId": "4f80e4a9-cb7a-4ee8-d6a1-1c8f8b32fc00" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "phase of IF signal:\n" ] }, { "data": { "text/latex": [ "$\\displaystyle 2 \\pi \\left(\\frac{\\Delta f_{c}}{2} - \\Delta f_{0} - f_{c} t\\right)$" ], "text/plain": [ "2*pi*(\\Delta*f_c/2 - \\Delta*f_{0} - f_c*t)" ] }, "execution_count": 200, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fb_explicit = parse_latex(r\"\\mathit{Delta A} \\cdot s\")\n", "fb_explicit = fb_explicit.subs(\"Delta A\",Delta)\n", "fc_symbol = Symbol(\"f_c\")\n", "\n", "# now we replace fb=Delta*s with fc\n", "phi_l_new = phi_l_t.subs(fb_explicit,fc_symbol)\n", "# and we display the new simplified form of the phase of the IF signal\n", "print(\"phase of IF signal:\")\n", "phi_l_new" ] }, { "cell_type": "markdown", "metadata": { "id": "o32XCxWkOVcd" }, "source": [ "we now define $\\psi_0$ as the residual phase in IF" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "DT3c5AR8tU5s", "outputId": "7677413f-c79b-4ac8-ebdf-027c58b23545" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\psi_{0}$" ], "text/plain": [ "\\psi_0" ] }, "execution_count": 233, "metadata": {}, "output_type": "execute_result" } ], "source": [ "psi_0 = Symbol(\"\\psi_0\")\n", "psi0_latex = parse_latex(r\"\\mathit{Delta A} \\cdot \\mathit{fc}/2 - \\mathit{Delta A} \\cdot \\mathit{f o}\")\n", "# define psi0_e as the explicit form of the IF phase\n", "# psi0_e = psi0_i.subs(\"Phi zero\",Phi_0)\n", "psi0_e = psi0_latex.subs(\"Delta A\",Delta)\n", "psi0_e = psi0_e.subs(\"fc\",fc_implicit)\n", "psi0_e = psi0_e.subs(\"f o\",f_0)\n", "psi_0" ] }, { "cell_type": "markdown", "metadata": { "id": "bYjhNnOdOVcd" }, "source": [ "equals" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "W8o8BIHKOVcd", "outputId": "539db055-8c2e-4d36-c6d0-742ebbabbde7" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle - \\Delta f_{0} + \\frac{\\Delta f_{c}}{2}$" ], "text/plain": [ "-\\Delta*f_0 + \\Delta*f_c/2" ] }, "execution_count": 234, "metadata": {}, "output_type": "execute_result" } ], "source": [ "psi0_e" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "xLzVuBdfOVcd", "outputId": "72fb30f3-34d2-47af-dfe7-c6dfe9b06292" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "- \\Delta f_{0} + \\frac{\\Delta f_{c}}{2} (Eq. 4)\n" ] } ], "source": [ "eq_n += 1\n", "print(f\"{latex(psi0_e)} (Eq. {eq_n})\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 38 }, "id": "QqvrYxnLtnts", "outputId": "9f4c376f-9af8-45e0-ae00-5cc0abbf653e" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle 2 \\pi \\left(\\frac{\\Delta f_{c}}{2} - \\Delta f_{0} - f_{c} t\\right)$" ], "text/plain": [ "2*pi*(\\Delta*f_c/2 - \\Delta*f_{0} - f_c*t)" ] }, "execution_count": 236, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# replace in the IF phase the constant offset by psi_0\n", "phi_l_new = phi_l_new.subs(psi0,psi_0)\n", "phi_l_new" ] }, { "cell_type": "markdown", "metadata": { "id": "EOcZArGXOVcd" }, "source": [ "we now verify that the simplified IF signal is the same as phi_l_t" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "UAaklbl9OVck", "outputId": "94e1a80a-ffc3-45f3-9244-02b447ed37ca" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle 2 \\pi \\left(\\frac{\\Delta^{2} s}{2} - \\Delta f_{0} - \\Delta s t\\right)$" ], "text/plain": [ "2*pi*(\\Delta**2*s/2 - \\Delta*f_{0} - \\Delta*s*t)" ] }, "execution_count": 237, "metadata": {}, "output_type": "execute_result" } ], "source": [ "phi_l_t" ] }, { "cell_type": "markdown", "metadata": { "id": "QnckWLcjOVck" }, "source": [ "## cases where the phase varies\n", "\n", "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\n", "\n", "Where:\n", "\n", "$ \\delta $ is the change in time of flight" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 53 }, "id": "Hz2GrswCuPGL", "outputId": "531b026f-0ae9-4a68-cb57-63d44daf99f6" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\Delta + \\delta$" ], "text/plain": [ "\\Delta + \\delta" ] }, "execution_count": 238, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# seems that Delta B or delta A are not valid so setting delta as \\mathit{D A}\n", "Dd_latex = parse_latex(r\"\\mathit{Delta A} + \\mathit{D A}\")\n", "Dd = Dd_latex.subs(\"Delta A\",Delta)\n", "Dd = Dd.subs(\"D A\",delta)\n", "Dd" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "whhWfu6JOVck", "outputId": "7685785d-3cc0-4629-aaa4-014e3e81c69a" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle - f_{0} \\left(\\Delta + \\delta\\right) + \\frac{f_{c} \\left(\\Delta + \\delta\\right)}{2}$" ], "text/plain": [ "-f_0*(\\Delta + \\delta) + f_c*(\\Delta + \\delta)/2" ] }, "execution_count": 239, "metadata": {}, "output_type": "execute_result" } ], "source": [ "psi1_e = psi0_e.subs(Delta, Dd)\n", "psi1_e" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "81E6KKAKOVck", "outputId": "c0738931-5a56-4c35-bbb4-2d84df8eb8ce" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "- f_{0} \\left(\\Delta + \\delta\\right) + \\frac{f_{c} \\left(\\Delta + \\delta\\right)}{2} (Eq. 5)\n" ] } ], "source": [ "eq_n += 1\n", "print(f\"{latex(psi1_e)} (Eq. {eq_n})\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 53 }, "id": "xLxRczk4vBeL", "outputId": "f45fa36b-fe3d-49ec-e047-4b1fe13377a0" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\Delta f_{0} - \\frac{\\Delta f_{c}}{2} - f_{0} \\left(\\Delta + \\delta\\right) + \\frac{f_{c} \\left(\\Delta + \\delta\\right)}{2}$" ], "text/plain": [ "\\Delta*f_0 - \\Delta*f_c/2 - f_0*(\\Delta + \\delta) + f_c*(\\Delta + \\delta)/2" ] }, "execution_count": 240, "metadata": {}, "output_type": "execute_result" } ], "source": [ "psi1_e-psi0_e" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 52 }, "id": "WiTlwSeQvIQ5", "outputId": "beed869c-7848-4dec-98ab-44fb1eea96f4" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle - \\delta f_{0} + \\frac{\\delta f_{c}}{2}$" ], "text/plain": [ "-\\delta*f_0 + \\delta*f_c/2" ] }, "execution_count": 241, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Delta_Psi_latex = parse_latex(r\"\\frac{\\mathit{D A} \\cdot \\mathit{f c}}{2} - \\mathit{D A} \\cdot \\mathit{f o}\")\n", "Delta_Psi = Delta_Psi_latex.subs(\"D A\",delta)\n", "Delta_Psi = Delta_Psi.subs(\"f c\",f_c)\n", "Delta_Psi = Delta_Psi.subs(\"f o\",f_0)\n", "Delta_Psi" ] }, { "cell_type": "markdown", "metadata": { "id": "RhqCMQHoOVck" }, "source": [ "verify they are both the same" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "QqQ5IGXiOVck", "outputId": "873c5798-4b27-4bfe-facb-820f6869a76f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "- \\delta f_{0} + \\frac{\\delta f_{c}}{2} (Eq. 6)\n" ] } ], "source": [ "eq_n += 1\n", "print(f\"{latex(Delta_Psi)} (Eq. {eq_n})\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "0Mjeigi2OVck", "outputId": "5b20ca5d-5fea-4998-9e0b-5bf95653eb76" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\Delta f_{0} - \\frac{\\Delta f_{c}}{2} - f_{0} \\left(\\Delta + \\delta\\right) + \\frac{f_{c} \\left(\\Delta + \\delta\\right)}{2}$" ], "text/plain": [ "\\Delta*f_0 - \\Delta*f_c/2 - f_0*(\\Delta + \\delta) + f_c*(\\Delta + \\delta)/2" ] }, "execution_count": 242, "metadata": {}, "output_type": "execute_result" } ], "source": [ "psi1_e-psi0_e" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "rnRJCarTOVck", "outputId": "733a8811-efe1-4394-ceb5-6bb6a9e28f8c" }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle - \\delta f_{0} + \\frac{\\delta f_{c}}{2}$" ], "text/plain": [ "-\\delta*f_0 + \\delta*f_c/2" ] }, "execution_count": 247, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Delta_Psi" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "W8focE7cOVck" }, "outputs": [], "source": [ "assert Delta_Psi.equals(psi1_e-psi0_e)" ] }, { "cell_type": "markdown", "metadata": { "id": "d3a6KfEyv01Q" }, "source": [ "here we need to consider that f0 ~60e9 and fc ~1e7 MHz" ] }, { "cell_type": "markdown", "metadata": { "id": "Z_gFxNFscbgo" }, "source": [ "## BACK-UP section" ] }, { "cell_type": "markdown", "metadata": { "id": "7hzYMIo9OVcl" }, "source": [ "debugging symbols in a sympy equations\n", "\n", "Delta_Psi.free_symbols" ] }, { "cell_type": "markdown", "metadata": { "id": "oVWGQVVycz2S" }, "source": [ "### Syntax Examples with Sympy" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "AE8DlVN6c2Bp", "outputId": "a61af6a6-adec-4f50-c2de-aff0af5715d1" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Phi T: P\n", "phi full: P + 2 \\pi \\left(f_{0} t + t^{2} \\frac{s}{2}\\right)\n", "Y1 A_{T} \\cos{\\left(P + 2 \\pi \\left(f_{0} t + t^{2} \\frac{s}{2}\\right) \\right)}\n", "works\n" ] } ], "source": [ "P = Symbol(r\"P\")\n", "phi_TX_full = parse_latex(r\"2 \\pi \\cdot ( f_0 \\cdot t + \\frac{s}{2} \\cdot t^2 ) + P\")\n", "Y_T = parse_latex(r\"A_T \\cdot \\cos(P)\")\n", "print(\"Phi T:\", latex(P))\n", "print(\"phi full:\",latex(phi_TX_full))\n", "\n", "Y_T1 = Y_T.subs(P,phi_TX_full)\n", "print(\"Y1\", latex(Y_T1))\n", "print(\"works\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "PYWl_BKIdJo3", "outputId": "e8475f59-8ab8-4e63-9356-bfa0bd585682" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "P0: Phi\n", "phi full: A_{T}*Phi\n", "SUBS: A_{T} x\n", "works\n" ] } ], "source": [ "P0 = Symbol(r\"Phi\")\n", "x = Symbol(\"x\")\n", "phi_TX_full0 = parse_latex(r\"2 \\pi \\cdot ( f_0 \\cdot t + \\frac{s}{2} \\cdot t^2 ) + P\")\n", "Y_T = parse_latex(r\"A_T \\cdot \\mathit{Phi}\")\n", "print(f\"P0: {P0}\")\n", "print(f\"phi full: {Y_T}\")\n", "\n", "Y_T0 = Y_T.subs(P0,x)\n", "print(\"SUBS:\", latex(Y_T0))\n", "print(\"works\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "v6TMuhdCnAFP", "outputId": "5c86bc82-270d-46d8-c78a-05ade0d0c289" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "P0: Phi\n", "Y_T: A_{T}*Phi\n", "SUBS: A_{T} x\n", "works\n" ] } ], "source": [ "x = Symbol(\"x\")\n", "phi_TX_full0 = parse_latex(r\"2 \\pi \\cdot ( f_0 \\cdot t + \\frac{s}{2} \\cdot t^2 ) + P\")\n", "Y_T = parse_latex(r\"A_T \\cdot \\mathit{Phi}\")\n", "print(f\"P0: {P0}\")\n", "print(f\"Y_T: {Y_T}\")\n", "\n", "Y_T0 = Y_T.subs(\"Phi\",x)\n", "print(\"SUBS:\", latex(Y_T0))\n", "print(\"works\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 72 }, "id": "djWma1SbqUIy", "outputId": "3c74007c-83d5-4a04-ec2a-5836349cf0d9" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Y A_{T}*psiv\n", "SUBS: A_{T} \\Psi_{5}\n" ] }, { "data": { "text/latex": [ "$\\displaystyle \\Psi_{5}$" ], "text/plain": [ "\\Psi_{5}" ] }, "execution_count": 252, "metadata": {}, "output_type": "execute_result" } ], "source": [ "P5 = Symbol(r\"\\Psi_{5}\")\n", "# P5 = Symbol(\"P5\") # works\n", "Y = parse_latex(r\"A_T \\cdot \\mathit{psi-5}\") # does not work\n", "Y = parse_latex(r\"A_T \\cdot \\mathit{psi 5}\") # does not work\n", "Y = parse_latex(r\"A_T \\cdot \\mathit{psi5}\") # does not work\n", "Y = parse_latex(r\"A_T \\cdot \\mathit{psiv}\") # works\n", "# Y = parse_latex(r\"A_T \\cdot \\mathit{psi v}\") # works\n", "\n", "print(\"Y\",Y)\n", "Y_T1 = Y.subs(\"psiv\",P5)\n", "print(\"SUBS:\", latex(Y_T1))\n", "P5" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "2TnWu-MpOVcl" }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JjGsnOyZOVcl" }, "outputs": [], "source": [] } ], "metadata": { "colab": { "collapsed_sections": [ "Z_gFxNFscbgo" ], "provenance": [], "toc_visible": true }, "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.10.9" } }, "nbformat": 4, "nbformat_minor": 0 }