Thursday, August 18, 2016

Decode OOK with rtl_fm and python script only

The goal

Decode OOK from rtl_fm raw output


You don't need to analyze OOK audio manually.


  • any rtl-sdr dongle


While working on my software for my CC1101 atmega32u dongle, I was making tests with doorbell transmitter. To check if my code decodes it properly I needed to record "sound of OOK", then using Audacity look into the wave and manually count all long and short pulses. I've started to wondering if this could automated, because signal I was looking on was pretty good with very low noise.

I've started to google and found this, based on jleahy's answer I created this simple script below.

The script

Command below will start listening on ISM band: 433.92Mhz with AM modulation and 38400 sampling rate. Output will be written to ook.raw file. Almost in the same time will be played by your sound card. Thanks to this you can easily tell if something was send over air.
rtl_fm -M am -s 38400 -f 433.92M -g 0 | tee ook.raw |  play -t raw -r 38400 -es -b 16 -c 1 -V1 -
Gain of rtl-sdr device is set to 0, because I was checking doorbell transmitter that I had in my hand. Thanks to low gain I've got quite large signal/noise separation. Sample rate 38400 = 4 * 9600, which is the proper speed for analyze_ook function that uses regular expressions to decode OOK signal. I could use of course some lower sampling rate, but with bigger one audio is smoother. If you'll decide to change it to something else, remember to change downsample line.

Rf_analyzer module, you can find in source code section of CC1101 Atmega32u USB dongle + python = RFkitten post.
from numpy import memmap, where, trim_zeros, char, mean, max
from scipy.signal import medfilt
from re import split
from sys import argv

import rf_analyzer

# load raw audio file with uint16 encoding and
# 38400 sample rate
data = memmap(argv[1],  dtype='uint16', mode='r')

# "calculate" noise level from begining of a file
noise = max(data[0:1000])

# Sample rate was set to 38400, which is 4 * 9600
# now downsample it 4 times by reading every 4-th integer
data = data[0::4]

# set baudrate for OOK analyzer, it will calculate original
# signal baudrate
baud = 9600 

# create median vector
median = medfilt(data)

# every signal above will be set to 1, every below to 0
# use additional noise floor
normalized = where(median > mean(median) + noise, 1, 0)

# trim trailing and leading zeros
normalized_no_zeros = trim_zeros(normalized)

# convert above [1,1, ... , 0, 0, ... , 1,1] to string 11...00...11
bin_str =  char.mod('%d', normalized_no_zeros).tostring()

# split string into packets separted by high count of '0', 20 is enought for
# 2400 to 9600 data rate
for splitted in split('0{20,}', bin_str):
   if (len(splitted) > 0):
      # make sure that we've got some zeroes at the end for regexp
      splitted+="0" * 20
      # analyze and print
      rf_analyzer.analyze_ook(splitted, baud, bin_input=True)
Here is the result:
$ ./ ook.raw 
Packet len: 52 speed: 2400 OOK decoded: 10001011111110100000010 key: doorbell_button_1_first
Original: 11111111111111000011111000000000000011111000000000000011111000000000000011111111111111000011111000000000000011111111111110000011111111111110000011111111111110000011111111111110000011111111111110000011111111111110000011111111111110000011110000000000000111111111111110000111110000000000000111110000000000000111110000000000000111110000000000000111110000000000000111110000000000000111111111111100000111100000000000000000000

Packet len: 54 speed: 2400 OOK decoded: 110001011111110101010010 key: doorbell_button_1
Original: 11111111111110000011111111111110000011110000000000000011110000000000000011110000000000000111111111111110000111110000000000000111111111111110000111111111111110000111111111111110000111111111111110000111111111111110000111111111111110000111111111111110000111110000000000000111111111111100000111110000000000000111111111111100000111100000000000000111111111111100000111100000000000000111100000000000000111111111111100000111100000000000000000000

Packet len: 54 speed: 2400 OOK decoded: 110001011111110101010010 key: doorbell_button_1
Original: 11111111111110000011111111111110000011110000000000000111110000000000000111110000000000000111111111111110000111110000000000000111111111111110000111111111111110000111111111111110000111111111111110000111111111111110000111111111111100000111111111111100000111100000000000000111111111111100000111100000000000000111111111111100000111100000000000000111111111111100000111100000000000000111100000000000000111111111111100001111100000000000000000000
and so on ...

No comments:

Post a Comment