summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2018-05-26 13:16:24 +0300
committerYuval Adam <_@yuv.al>2018-05-26 13:16:24 +0300
commit9d44d520177a8e67ac76ce40b9e5777608b0031e (patch)
tree7feac62b12483a722a798faeb18c280b3e6e17a2
parent02fd57fbf3c106c634d30c42829e4e78192cd996 (diff)
Fix signal processing, use files
-rw-r--r--talk.ipynb87
-rw-r--r--talk.slides.html82
2 files changed, 110 insertions, 59 deletions
diff --git a/talk.ipynb b/talk.ipynb
index d8e8de0..220e47e 100644
--- a/talk.ipynb
+++ b/talk.ipynb
@@ -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",
+ "![static/dipole.gif](static/dipole.gif)\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",
- "![static/dipole.gif](static/dipole.gif)\n",
+ "## Hardware Radio\n",
"\n",
- "*Source: https://en.wikipedia.org/wiki/Antenna_(radio)*\n"
+ "![static/radio.jpg](static/radio.jpg)"
]
},
{
"cell_type": "markdown",
- "metadata": {},
+ "metadata": {
+ "slideshow": {
+ "slide_type": "subslide"
+ }
+ },
"source": [
"## Hardware Radio\n",
"\n",
- "![static/radio.jpg](static/radio.jpg)"
+ "![static/gsm.jpg](static/gsm.jpg)"
]
},
{
@@ -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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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&nbsp;[&nbsp;]:</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">&#39;auto&#39;</span> <span class="c1"># tune the gain (AKA &quot;volume&quot;) 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&nbsp;[&nbsp;]:</div>
+<div class="prompt input_prompt">In&nbsp;[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">&#39;complex64&#39;</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">&#39;/tmp/samples.in&#39;</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&nbsp;[&nbsp;]:</div>
+<div class="prompt input_prompt">In&nbsp;[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&nbsp;[&nbsp;]:</div>
+<div class="prompt input_prompt">In&nbsp;[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&nbsp;[&nbsp;]:</div>
+<div class="prompt input_prompt">In&nbsp;[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 &quot;sciency&quot;</span>
+<div class=" highlight hl-ipython3"><pre><span></span><span class="c1"># De-emphasis filter</span>
+<span class="c1"># Even I&#39;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&nbsp;[&nbsp;]:</div>
+<div class="prompt input_prompt">In&nbsp;[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">&#39;int16&#39;</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&nbsp;[&nbsp;]:</div>
+<div class="prompt input_prompt">In&nbsp;[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">&#39;default&#39;</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">&#182;</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>