From 9d44d520177a8e67ac76ce40b9e5777608b0031e Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Sat, 26 May 2018 13:16:24 +0300 Subject: Fix signal processing, use files --- talk.ipynb | 87 ++++++++++++++++++++++++++++++++++++-------------------- talk.slides.html | 82 ++++++++++++++++++++++++++++++++++------------------ 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", + "\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", - "" + "" ] }, { @@ -200,6 +202,17 @@ "*Source: http://mriquestions.com/signal-squiggles.html*" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## GQRX" + ] + }, { "cell_type": "code", "execution_count": null, @@ -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 {
# Load the samples into a numpy array
+# Alternatively, load previously captured samples from rtl_sdr
+# and convert them from unsigned 8-bit samples to a complex IQ array
import numpy as np
-samples = np.array(samples).astype('complex64')
+raw = np.fromfile('/tmp/samples.in', np.uint8).astype(np.float64)
+raw += -127
+raw /= 2**7
+samples = (raw[0::2] + 1j * raw[1::2]).astype(np.complex64)
# We captured too many samples so we need to apply a low pass filter
@@ -12103,7 +12109,7 @@ a.anchor-link {
import scipy.signal as signal
BANDWIDTH = 200e3
-DECIMATION_RATE = int(1.2e6 / BANDWIDTH)
+DECIMATION_RATE = int(1.2e6 / BANDWIDTH) # Decimation factor of 6
samples = signal.decimate(samples, DECIMATION_RATE)
# Apply the demodulation, we use a polar discriminator
+# Apply the Frequency DEmodulation
+# We use something called a polar discriminator
samples = np.angle(samples[1:] * np.conj(samples[:-1]))
@@ -12130,10 +12137,12 @@ a.anchor-link {
# De-emphasis filter - too "sciency"
+# De-emphasis filter
+# Even I'm not sure how/why this works
+
# MAKE THINGS SOUND BETTER
d = BANDWIDTH * 75e-6
@@ -12149,31 +12158,45 @@ a.anchor-link {
-In [ ]:
+In [5]:
# Decimate the signal down to something an audio driver can handle
# We only catch the mono part of the signal
AUDIO_RATE = 50e3
-DECIMATION_RATE = int(1.2e6 / BANDWIDTH / AUDIO_RATE)
+DECIMATION_RATE = int(BANDWIDTH / AUDIO_RATE) # Decimation factor of 4
samples = signal.decimate(samples, DECIMATION_RATE)
+
+# Amplify the signal volume
+samples *= 10000
+samples = samples.astype('int16')
-
+ # HIT IT
-whatever.play(samples)
+import alsaaudio
+
+device = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, device='default')
+device.setchannels(1)
+device.setrate(50000)
+device.setformat(alsaaudio.PCM_FORMAT_S16_LE)
+device.setperiodsize(120)
+
+# Write data in chunks
+for s in np.array_split(samples, 120):
+ device.write(s)