{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# In Phase (I) and Quadrature (Q) Signals of Image Reject Mixers\n",
"In radios (and other electronics) it is often desired to shift a signal's frequency. This is the job of the mixer circuit. The mixer is represented by a circle with a multiply symbol inside it, as shown below. ![mixer block diagram](Mixer.png) \n",
"\n",
"If we multiply $cos(\\omega_{lo}t)$ with $cos(\\omega_{rf}t)$ we get ${cos[\\pm(\\omega_{lo}-\\omega_{rf})t] + cos[\\pm(\\omega_{lo}+\\omega_{rf})t]}/2$. This contains the desired term, $cos[\\pm(\\omega_{lo}-\\omega_{rf}]t]$, which does the frequency shift. The $\\pm$ is because $cos(-x) = cos(x)$. Negative frequencies are indistinguishable from positive frequencies from the cosine's point of view. This brings up the question of what we mean by negative frequencies, but put that aside for now as we examine how a mixer would work in a software defined radio receiver. To be clear, both $\\omega_{rf}$ and $\\omega_{lo}$ are positive numbers (so, for example, $-\\omega_{rf} < 0$) in this discussion.\n",
"\n",
"The block diagram of a simple software defined receiver is shown below. ![Simple SDR](Simple_SDR.png)\n",
"\n",
"In order to tune in stations at their different frequencies, the frequency shift needs to be adjustable. Generally the frequency to be shifted is the frequency of an adjustable oscillator, called the local oscillator. The frequency of the local oscillator is $f_{lo} = \\omega_{lo}/(2\\pi)$ and is set by some type of control on the local oscillator. The low pass filter just after the mixer removes the $cos[\\pm(\\omega_{lo}+\\omega_{rf})t]$ term from the signals impinging on the sound card, which converts these low frequencies to digital samples. There is still a problem though, because there are actually still two signals in $cos[\\pm(\\omega_{lo}-\\omega_{rf}]t]$ that make it to the data stream from the sound card, one for the \"+\" and one for the \"-\" from the $\\pm$. When we are listening to the receiver, we cannot tell if it was the \"+\" or the \"-\" term that caused what we are listening to, and sometimes two different radio stations will end up getting shifted right on top of one another, one from the \"+\" term and one from the \"-\" term. The \"+\" term and the \"-\" term are known as images of each other. It would be very nice if we could send enough data to the computer so it could distinguish between these two signals. This is actually twice as much data as we are sending with the simple SDR above. Fortunately sound cards often have stereo inputs.\n",
"\n",
"To understand how to solve the image problem above, we need to understand what we mean by negative frequency, and get a more intuitive idea how we can tell the images apart. For a moment, consider a propeller, of unit length, spinning in the counterclockwise direction at 100 revolutions or cycles per second. See the figure below. ![Propeller](Propeller.png) \n",
"\n",
"\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's call that a positive frequency of 100 Hz. What would a negative frequency of 100 Hz be? It seems obvious that it would be the propeller spinning the opposite direction (clockwise). If you look at the propeller from the bottom perspective you would see the tip going back and forth like a cosine wave in time. Looking only from the bottom, you could not tell if it was spinning clockwise or counterclockwise. However, if you could look from the bottom and from the right side ($90^{\\circ}$ from the bottom), you could tell which way it was turning. Remember that the parametric equation describing the tip of the propeller spinning counterclockwise at frequency $\\omega$ is $\\vec v = cos(\\omega t) \\hat {\\mathbf i} + sin(\\omega t)\\hat {\\mathbf j}$. If you spin in the clockwise direction (change $\\omega$ to $-\\omega$), and the $sin(\\omega t)\\hat {\\mathbf j}$ becomes $-sin(\\omega t)\\hat {\\mathbf j}$, because $sin(-x) = -sin(x)$. Another way to represent this circular rotation is in the complex plane, where the real part of the number is in the $\\hat{\\mathbf i}$ direction and the imaginary number is in the $\\hat{\\mathbf j}$ direction. Euler's identity which says $e^{j\\omega t} = cos(\\omega t) + jsin(\\omega t)$ shows that $e^{j\\omega t}$ represents a counter clockwise spin of the propeller, and $e^{-j\\omega t}$ represents a clockwise spin of the propeller. The animation below shows this. \n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"import matplotlib.animation\n",
"import matplotlib.ticker as plticker\n",
"import numpy as np\n",
"from IPython.display import HTML\n",
"\n",
"t = np.linspace(0, 2)\n",
"x = np.cos(np.pi*t)\n",
"y = np.sin(np.pi*t)\n",
"fig, ax = plt.subplots(nrows=2, ncols=2,\n",
" gridspec_kw={'hspace': 0.3, 'wspace': 0.2}, figsize=(8, 8));\n",
"plt.close();\n",
"ax[0][0].set_aspect(1)\n",
"ax[0][0].axis([-1, 1, -1, 1])\n",
"ax[0][1].set_aspect(1)\n",
"ax[1][0].set_aspect(1)\n",
"ax[0][1].axis([0, 2, -1, 1])\n",
"ax[1][0].axis([-1, 1, 0, 2])\n",
"ax[1][1].axis('off')\n",
"x_title = '$\\mathscr {Re}[e^{j\\omega t}]$'\n",
"y_title = '$\\mathscr {Im}[e^{j\\omega t}]$'\n",
"x_label = '$\\mathscr {Im}$'\n",
"y_label = '$\\mathscr {Re}$'\n",
"t_label = 't (s)'\n",
"ax[0][0].set_xlabel(x_label)\n",
"ax[0][1].set_xlabel(t_label)\n",
"ax[1][0].set_xlabel(x_label)\n",
"ax[0][0].set_ylabel(y_label)\n",
"ax[0][0].xaxis.set_label_position(\"top\")\n",
"ax[0][0].xaxis.tick_top()\n",
"ax[0][1].yaxis.set_label_position(\"right\")\n",
"ax[0][1].yaxis.tick_right()\n",
"ax[1][0].yaxis.set_label_position(\"right\")\n",
"ax[1][0].yaxis.tick_right()\n",
"axis_linewidth = 0.75\n",
"real_color = 'cyan'\n",
"im_color = 'magenta'\n",
"complex_color = 'green'\n",
"ax[0][0].axhline(0, color=real_color, linewidth=axis_linewidth)\n",
"ax[0][0].axvline(0, color=im_color, linewidth=axis_linewidth)\n",
"ax[0][1].axhline(0, color='black', linewidth=axis_linewidth)\n",
"ax[1][0].axvline(0, color='black', linewidth=axis_linewidth)\n",
"ax[0][0].set_title('$e^{j\\omega t}$')\n",
"ax[0][1].set_title(y_title)\n",
"ax[1][0].set_title(x_title)\n",
"loc = plticker.MultipleLocator(base=1.0)\n",
"for i in range(len(ax)):\n",
" for j in range(len(ax[0])):\n",
" ax[i, j].xaxis.set_major_locator(loc)\n",
" ax[j, i].yaxis.set_major_locator(loc)\n",
"l1, = ax[0][0].plot([], [], '.', color=complex_color)\n",
"l2, = ax[0][1].plot([], [], '.', color=im_color)\n",
"l3, = ax[1][0].plot([], [], '.', color=real_color)\n",
"ax[0][1].legend([l2],[y_title])\n",
"ax[1][0].legend([l3],[x_title])\n",
"\n",
"def animate(i):\n",
" l1.set_data(x[:i], y[:i])\n",
" l2.set_data(t[:i], y[:i])\n",
" l3.set_data(x[:i], t[:i])\n",
"\n",
"ani = matplotlib.animation.FuncAnimation(fig, animate, frames=len(t), interval=200);\n",
"\n",
"HTML(ani.to_html5_video()) # For just the animation, no controls.\n",
"#HTML(ani.to_jshtml()) # Animation with controls."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The phasor above has positive frequency and advances in the counterclockwise direction. The phasor below has negative frequency and advances in the clockwise direction. The difference between them is seen in the sign of the imaginary component. This is how you tell if the frequency is positive or negative."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"t = np.linspace(0, 2)\n",
"x = np.cos(-np.pi*t)\n",
"y = np.sin(-np.pi*t)\n",
"fig, ax = plt.subplots(nrows=2, ncols=2,\n",
" gridspec_kw={'hspace': 0.3, 'wspace': 0.2}, figsize=(8, 8));\n",
"plt.close();\n",
"ax[0][0].set_aspect(1)\n",
"ax[0][0].axis([-1, 1, -1, 1])\n",
"ax[0][1].set_aspect(1)\n",
"ax[1][0].set_aspect(1)\n",
"ax[0][1].axis([0, 2, -1, 1])\n",
"ax[1][0].axis([-1, 1, 0, 2])\n",
"ax[1][1].axis('off')\n",
"x_title = '$\\mathscr {Re}[e^{-j\\omega t}]$'\n",
"y_title = '$\\mathscr {Im}[e^{-j\\omega t}]$'\n",
"x_label = '$\\mathscr {Im}$'\n",
"y_label = '$\\mathscr {Re}$'\n",
"t_label = 't (s)'\n",
"ax[0][0].set_xlabel(x_label)\n",
"ax[0][1].set_xlabel(t_label)\n",
"ax[1][0].set_xlabel(x_label)\n",
"ax[0][0].set_ylabel(y_label)\n",
"ax[0][0].xaxis.set_label_position(\"top\")\n",
"ax[0][0].xaxis.tick_top()\n",
"ax[0][1].yaxis.set_label_position(\"right\")\n",
"ax[0][1].yaxis.tick_right()\n",
"ax[1][0].yaxis.set_label_position(\"right\")\n",
"ax[1][0].yaxis.tick_right()\n",
"axis_linewidth = 0.75\n",
"real_color = 'cyan'\n",
"im_color = 'magenta'\n",
"complex_color = 'green'\n",
"ax[0][0].axhline(0, color=real_color, linewidth=axis_linewidth)\n",
"ax[0][0].axvline(0, color=im_color, linewidth=axis_linewidth)\n",
"ax[0][1].axhline(0, color='black', linewidth=axis_linewidth)\n",
"ax[1][0].axvline(0, color='black', linewidth=axis_linewidth)\n",
"ax[0][0].set_title('$e^{-j\\omega t}$')\n",
"ax[0][1].set_title(y_title)\n",
"ax[1][0].set_title(x_title)\n",
"loc = plticker.MultipleLocator(base=1.0)\n",
"for i in range(len(ax)):\n",
" for j in range(len(ax[0])):\n",
" ax[i, j].xaxis.set_major_locator(loc)\n",
" ax[j, i].yaxis.set_major_locator(loc)\n",
"l1, = ax[0][0].plot([], [], '.', color=complex_color)\n",
"l2, = ax[0][1].plot([], [], '.', color=im_color)\n",
"l3, = ax[1][0].plot([], [], '.', color=real_color)\n",
"ax[0][1].legend([l2],[y_title])\n",
"ax[1][0].legend([l3],[x_title])\n",
"\n",
"def animate(i):\n",
" l1.set_data(x[:i], y[:i])\n",
" l2.set_data(t[:i], y[:i])\n",
" l3.set_data(x[:i], t[:i])\n",
"\n",
"ani = matplotlib.animation.FuncAnimation(fig, animate, frames=len(t), interval=200);\n",
"\n",
"HTML(ani.to_html5_video()) # For just the animation, no controls.\n",
"#HTML(ani.to_jshtml()) # Animation with controls."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Suppose we were to observe the wave from orthogonal perspectives as described above, and passed the data from those perspectives into the left and right channels respectively, then we could tell which way the vector was spinning, and so would have enough information to listen to only the signals spinning in one way or the other. The block diagram for this receiver is shown below. ![SDR Receiver](SDR_Receiver.png)\n",
"\n",
"This mixer system for this is known as an image reject mixer, and the signal shifted by the $cos(\\omega_{lo}t)$ is known as the in phase signal (I), and the signal shifted by $sin(\\omega_{lo}t)$ (or in some receivers by $-sin(\\omega_{lo}t)$) is known as the quadrature signal (Q)."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.8.2"
}
},
"nbformat": 4,
"nbformat_minor": 4
}