diff options
| author | Yuval Adam <_@yuv.al> | 2018-05-26 13:16:24 +0300 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2018-05-26 13:16:24 +0300 |
| commit | 9d44d520177a8e67ac76ce40b9e5777608b0031e (patch) | |
| tree | 7feac62b12483a722a798faeb18c280b3e6e17a2 | |
| parent | 02fd57fbf3c106c634d30c42829e4e78192cd996 (diff) | |
Fix signal processing, use files
| -rw-r--r-- | talk.ipynb | 87 | ||||
| -rw-r--r-- | talk.slides.html | 82 |
2 files changed, 110 insertions, 59 deletions
@@ -24,7 +24,6 @@ "## Yuval Adam\n", "\n", " - Full stack developer and systems architecture consultant\n", - " - But I also like to play with radios\n", " - https://yuv.al\n", " - @yuvadm\n", "\n" @@ -42,7 +41,7 @@ "\n", " - I never learned physics, RF engineering or signal processing\n", " - But radios are pretty cool!\n", - " - Hopefully in 25 minutes I can show you some neat things\n", + " - Hopefully I can show you some neat things\n", " - Slides and code @ https://github.com/yuvadm/radio-pyconil-2018" ] }, @@ -54,10 +53,11 @@ } }, "source": [ - "## Agenda\n", + "## Radio Waves\n", + "\n", + "\n", "\n", - " - What are radio waves?\n", - " - Hardware vs software radio" + "*Source: https://en.wikipedia.org/wiki/Antenna_(radio)*\n" ] }, { @@ -68,20 +68,22 @@ } }, "source": [ - "## Radio Waves\n", - "\n", - "\n", + "## Hardware Radio\n", "\n", - "*Source: https://en.wikipedia.org/wiki/Antenna_(radio)*\n" + "" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, "source": [ "## Hardware Radio\n", "\n", - "" + "" ] }, { @@ -201,6 +203,17 @@ ] }, { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## GQRX" + ] + }, + { "cell_type": "code", "execution_count": null, "metadata": { @@ -219,15 +232,13 @@ "sdr.center_freq = 91.8e6 # 91,800,00 Hz frequency for the radio station\n", "sdr.gain = 'auto' # tune the gain (AKA \"volume\") automatically\n", "\n", - "samples = sdr.read_samples(1.2e6 * 8) # collect samples during 8 seconds\n", - "sdr.close()\n", - "\n", - "print(samples[:5])" + "samples = sdr.read_samples(1.2e6 * 10)\n", + "sdr.close()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "slideshow": { "slide_type": "subslide" @@ -235,16 +246,20 @@ }, "outputs": [], "source": [ - "# Load the samples into a numpy array\n", + "# Alternatively, load previously captured samples from rtl_sdr\n", + "# and convert them from unsigned 8-bit samples to a complex IQ array\n", "\n", "import numpy as np\n", "\n", - "samples = np.array(samples).astype('complex64')" + "raw = np.fromfile('/tmp/samples.in', np.uint8).astype(np.float64)\n", + "raw += -127\n", + "raw /= 2**7\n", + "samples = (raw[0::2] + 1j * raw[1::2]).astype(np.complex64)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "slideshow": { "slide_type": "subslide" @@ -265,7 +280,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": { "slideshow": { "slide_type": "subslide" @@ -274,14 +289,14 @@ "outputs": [], "source": [ "# Apply the Frequency DEmodulation\n", - "# We use something called polar discriminator\n", + "# We use something called a polar discriminator\n", "\n", "samples = np.angle(samples[1:] * np.conj(samples[:-1]))" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": { "slideshow": { "slide_type": "subslide" @@ -302,7 +317,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": { "slideshow": { "slide_type": "subslide" @@ -316,22 +331,32 @@ "AUDIO_RATE = 50e3\n", "DECIMATION_RATE = int(BANDWIDTH / AUDIO_RATE) # Decimation factor of 4\n", "\n", - "samples = signal.decimate(samples, DECIMATION_RATE)" + "samples = signal.decimate(samples, DECIMATION_RATE)\n", + "\n", + "# Amplify the signal volume\n", + "samples *= 10000\n", + "samples = samples.astype('int16')" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "slideshow": { - "slide_type": "subslide" - } - }, + "execution_count": 6, + "metadata": {}, "outputs": [], "source": [ "# HIT IT\n", "\n", - "whatever.play(samples)" + "import alsaaudio\n", + "\n", + "device = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, device='default')\n", + "device.setchannels(1)\n", + "device.setrate(50000)\n", + "device.setformat(alsaaudio.PCM_FORMAT_S16_LE)\n", + "device.setperiodsize(120)\n", + "\n", + "# Write data in chunks\n", + "for s in np.array_split(samples, 120):\n", + " device.write(s)" ] }, { diff --git a/talk.slides.html b/talk.slides.html index 31d711f..e00e354 100644 --- a/talk.slides.html +++ b/talk.slides.html @@ -11915,7 +11915,6 @@ a.anchor-link { <div class="text_cell_render border-box-sizing rendered_html"> <h2 id="Yuval-Adam">Yuval Adam<a class="anchor-link" href="#Yuval-Adam">¶</a></h2><ul> <li>Full stack developer and systems architecture consultant</li> -<li>But I also like to play with radios</li> <li><a href="https://yuv.al">https://yuv.al</a></li> <li>@yuvadm</li> </ul> @@ -11930,7 +11929,7 @@ a.anchor-link { <h2 id="This-talk">This talk<a class="anchor-link" href="#This-talk">¶</a></h2><ul> <li>I never learned physics, RF engineering or signal processing</li> <li>But radios are pretty cool!</li> -<li>Hopefully in 25 minutes I can show you some neat things</li> +<li>Hopefully I can show you some neat things</li> <li>Slides and code @ <a href="https://github.com/yuvadm/radio-pyconil-2018">https://github.com/yuvadm/radio-pyconil-2018</a></li> </ul> @@ -11941,10 +11940,8 @@ a.anchor-link { </div> <div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> -<h2 id="Agenda">Agenda<a class="anchor-link" href="#Agenda">¶</a></h2><ul> -<li>What are radio waves?</li> -<li>Hardware vs software radio</li> -</ul> +<h2 id="Radio-Waves">Radio Waves<a class="anchor-link" href="#Radio-Waves">¶</a></h2><p><img src="static/dipole.gif" alt="static/dipole.gif"></p> +<p><em>Source: <a href="https://en.wikipedia.org/wiki/Antenna_(radio">https://en.wikipedia.org/wiki/Antenna_(radio</a>)</em></p> </div> </div> @@ -11953,17 +11950,16 @@ a.anchor-link { </div> <div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> -<h2 id="Radio-Waves">Radio Waves<a class="anchor-link" href="#Radio-Waves">¶</a></h2><p><img src="static/dipole.gif" alt="static/dipole.gif"></p> -<p><em>Source: <a href="https://en.wikipedia.org/wiki/Antenna_(radio">https://en.wikipedia.org/wiki/Antenna_(radio</a>)</em></p> +<h2 id="Hardware-Radio">Hardware Radio<a class="anchor-link" href="#Hardware-Radio">¶</a></h2><p><img src="static/radio.jpg" alt="static/radio.jpg"></p> </div> </div> -</div> +</div></section><section> <div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> </div> <div class="inner_cell"> <div class="text_cell_render border-box-sizing rendered_html"> -<h2 id="Hardware-Radio">Hardware Radio<a class="anchor-link" href="#Hardware-Radio">¶</a></h2><p><img src="static/radio.jpg" alt="static/radio.jpg"></p> +<h2 id="Hardware-Radio">Hardware Radio<a class="anchor-link" href="#Hardware-Radio">¶</a></h2><p><img src="static/gsm.jpg" alt="static/gsm.jpg"></p> </div> </div> @@ -12050,6 +12046,14 @@ a.anchor-link { </div> </div> </div></section></section><section><section> +<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt"> +</div> +<div class="inner_cell"> +<div class="text_cell_render border-box-sizing rendered_html"> +<h2 id="GQRX">GQRX<a class="anchor-link" href="#GQRX">¶</a></h2> +</div> +</div> +</div></section></section><section><section> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> <div class="prompt input_prompt">In [ ]:</div> @@ -12064,10 +12068,8 @@ a.anchor-link { <span class="n">sdr</span><span class="o">.</span><span class="n">center_freq</span> <span class="o">=</span> <span class="mf">91.8e6</span> <span class="c1"># 91,800,00 Hz frequency for the radio station</span> <span class="n">sdr</span><span class="o">.</span><span class="n">gain</span> <span class="o">=</span> <span class="s1">'auto'</span> <span class="c1"># tune the gain (AKA "volume") automatically</span> -<span class="n">samples</span> <span class="o">=</span> <span class="n">sdr</span><span class="o">.</span><span class="n">read_samples</span><span class="p">(</span><span class="mf">1.2e6</span> <span class="o">*</span> <span class="mi">8</span><span class="p">)</span> <span class="c1"># collect samples during 8 seconds</span> +<span class="n">samples</span> <span class="o">=</span> <span class="n">sdr</span><span class="o">.</span><span class="n">read_samples</span><span class="p">(</span><span class="mf">1.2e6</span> <span class="o">*</span> <span class="mi">10</span><span class="p">)</span> <span class="n">sdr</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> - -<span class="nb">print</span><span class="p">(</span><span class="n">samples</span><span class="p">[:</span><span class="mi">5</span><span class="p">])</span> </pre></div> </div> @@ -12077,14 +12079,18 @@ a.anchor-link { </div></section><section> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> -<div class="prompt input_prompt">In [ ]:</div> +<div class="prompt input_prompt">In [1]:</div> <div class="inner_cell"> <div class="input_area"> -<div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># Load the samples into a numpy array</span> +<div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># Alternatively, load previously captured samples from rtl_sdr</span> +<span class="c1"># and convert them from unsigned 8-bit samples to a complex IQ array</span> <span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span> -<span class="n">samples</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">samples</span><span class="p">)</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="s1">'complex64'</span><span class="p">)</span> +<span class="n">raw</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">fromfile</span><span class="p">(</span><span class="s1">'/tmp/samples.in'</span><span class="p">,</span> <span class="n">np</span><span class="o">.</span><span class="n">uint8</span><span class="p">)</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">float64</span><span class="p">)</span> +<span class="n">raw</span> <span class="o">+=</span> <span class="o">-</span><span class="mi">127</span> +<span class="n">raw</span> <span class="o">/=</span> <span class="mi">2</span><span class="o">**</span><span class="mi">7</span> +<span class="n">samples</span> <span class="o">=</span> <span class="p">(</span><span class="n">raw</span><span class="p">[</span><span class="mi">0</span><span class="p">::</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="n">j</span> <span class="o">*</span> <span class="n">raw</span><span class="p">[</span><span class="mi">1</span><span class="p">::</span><span class="mi">2</span><span class="p">])</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">complex64</span><span class="p">)</span> </pre></div> </div> @@ -12094,7 +12100,7 @@ a.anchor-link { </div></section><section> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> -<div class="prompt input_prompt">In [ ]:</div> +<div class="prompt input_prompt">In [2]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># We captured too many samples so we need to apply a low pass filter</span> @@ -12103,7 +12109,7 @@ a.anchor-link { <span class="kn">import</span> <span class="nn">scipy.signal</span> <span class="k">as</span> <span class="nn">signal</span> <span class="n">BANDWIDTH</span> <span class="o">=</span> <span class="mf">200e3</span> -<span class="n">DECIMATION_RATE</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="mf">1.2e6</span> <span class="o">/</span> <span class="n">BANDWIDTH</span><span class="p">)</span> +<span class="n">DECIMATION_RATE</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="mf">1.2e6</span> <span class="o">/</span> <span class="n">BANDWIDTH</span><span class="p">)</span> <span class="c1"># Decimation factor of 6</span> <span class="n">samples</span> <span class="o">=</span> <span class="n">signal</span><span class="o">.</span><span class="n">decimate</span><span class="p">(</span><span class="n">samples</span><span class="p">,</span> <span class="n">DECIMATION_RATE</span><span class="p">)</span> </pre></div> @@ -12115,10 +12121,11 @@ a.anchor-link { </div></section><section> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> -<div class="prompt input_prompt">In [ ]:</div> +<div class="prompt input_prompt">In [3]:</div> <div class="inner_cell"> <div class="input_area"> -<div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># Apply the demodulation, we use a polar discriminator</span> +<div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># Apply the Frequency DEmodulation</span> +<span class="c1"># We use something called a polar discriminator</span> <span class="n">samples</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">angle</span><span class="p">(</span><span class="n">samples</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">conj</span><span class="p">(</span><span class="n">samples</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span> </pre></div> @@ -12130,10 +12137,12 @@ a.anchor-link { </div></section><section> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> -<div class="prompt input_prompt">In [ ]:</div> +<div class="prompt input_prompt">In [4]:</div> <div class="inner_cell"> <div class="input_area"> -<div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># De-emphasis filter - too "sciency"</span> +<div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># De-emphasis filter</span> +<span class="c1"># Even I'm not sure how/why this works</span> + <span class="c1"># MAKE THINGS SOUND BETTER</span> <span class="n">d</span> <span class="o">=</span> <span class="n">BANDWIDTH</span> <span class="o">*</span> <span class="mf">75e-6</span> @@ -12149,31 +12158,45 @@ a.anchor-link { </div></section><section> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> -<div class="prompt input_prompt">In [ ]:</div> +<div class="prompt input_prompt">In [5]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># Decimate the signal down to something an audio driver can handle</span> <span class="c1"># We only catch the mono part of the signal</span> <span class="n">AUDIO_RATE</span> <span class="o">=</span> <span class="mf">50e3</span> -<span class="n">DECIMATION_RATE</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="mf">1.2e6</span> <span class="o">/</span> <span class="n">BANDWIDTH</span> <span class="o">/</span> <span class="n">AUDIO_RATE</span><span class="p">)</span> +<span class="n">DECIMATION_RATE</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">BANDWIDTH</span> <span class="o">/</span> <span class="n">AUDIO_RATE</span><span class="p">)</span> <span class="c1"># Decimation factor of 4</span> <span class="n">samples</span> <span class="o">=</span> <span class="n">signal</span><span class="o">.</span><span class="n">decimate</span><span class="p">(</span><span class="n">samples</span><span class="p">,</span> <span class="n">DECIMATION_RATE</span><span class="p">)</span> + +<span class="c1"># Amplify the signal volume</span> +<span class="n">samples</span> <span class="o">*=</span> <span class="mi">10000</span> +<span class="n">samples</span> <span class="o">=</span> <span class="n">samples</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="s1">'int16'</span><span class="p">)</span> </pre></div> </div> </div> </div> -</div></section><section> +</div> <div class="cell border-box-sizing code_cell rendered"> <div class="input"> -<div class="prompt input_prompt">In [ ]:</div> +<div class="prompt input_prompt">In [6]:</div> <div class="inner_cell"> <div class="input_area"> <div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># HIT IT</span> -<span class="n">whatever</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="n">samples</span><span class="p">)</span> +<span class="kn">import</span> <span class="nn">alsaaudio</span> + +<span class="n">device</span> <span class="o">=</span> <span class="n">alsaaudio</span><span class="o">.</span><span class="n">PCM</span><span class="p">(</span><span class="n">alsaaudio</span><span class="o">.</span><span class="n">PCM_PLAYBACK</span><span class="p">,</span> <span class="n">device</span><span class="o">=</span><span class="s1">'default'</span><span class="p">)</span> +<span class="n">device</span><span class="o">.</span><span class="n">setchannels</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> +<span class="n">device</span><span class="o">.</span><span class="n">setrate</span><span class="p">(</span><span class="mi">50000</span><span class="p">)</span> +<span class="n">device</span><span class="o">.</span><span class="n">setformat</span><span class="p">(</span><span class="n">alsaaudio</span><span class="o">.</span><span class="n">PCM_FORMAT_S16_LE</span><span class="p">)</span> +<span class="n">device</span><span class="o">.</span><span class="n">setperiodsize</span><span class="p">(</span><span class="mi">120</span><span class="p">)</span> + +<span class="c1"># Write data in chunks</span> +<span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">np</span><span class="o">.</span><span class="n">array_split</span><span class="p">(</span><span class="n">samples</span><span class="p">,</span> <span class="mi">120</span><span class="p">):</span> + <span class="n">device</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> </pre></div> </div> @@ -12187,7 +12210,10 @@ a.anchor-link { <div class="text_cell_render border-box-sizing rendered_html"> <h2 id="Caveats">Caveats<a class="anchor-link" href="#Caveats">¶</a></h2><ul> <li>Python, NumPy and SciPy are <strong>very</strong> good at doing fast processing of static data</li> -<li>When handling real-time data, buffering becomes a serious issue</li> +<li>When handling real-time data, buffering becomes a serious issue<ul> +<li>How do you synchronize different input/ouput sample rates on the same flow?</li> +</ul> +</li> <li>GNU Radio<ul> <li>top-notch signal processing framework</li> <li>implemented many DSP primivites</li> |
