aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKing Kévin <kingkevin@cuvoodoo.info>2014-07-12 18:15:21 -0700
committerKing Kévin <kingkevin@cuvoodoo.info>2014-07-12 18:15:21 -0700
commit8b67b811d1b176b7b4e992438a24d92ee69f0222 (patch)
tree1df70ec8218b77eac2d4e7a3372108619e5bed9f
add script to decode rtl_fm recordings
-rwxr-xr-xsdr/decode.rb144
1 files changed, 144 insertions, 0 deletions
diff --git a/sdr/decode.rb b/sdr/decode.rb
new file mode 100755
index 0000000..09723f7
--- /dev/null
+++ b/sdr/decode.rb
@@ -0,0 +1,144 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+# ruby: 2.1
+=begin
+this script will open a AM raw audio file gerenate by rtl_fm and decode the megacode message from it
+=end
+
+# constants
+RATE = 24000 # the output sample rate, in Hz
+# the expected samples are little endian signed 16 bits intergers
+THRESHOLD = ((2**16)/2)*0.5
+TOLERANCE = 1.10 # how much deviation to accept
+
+raise "provide raw AM file to decode as argument" unless ARGV[0] and File.exist? ARGV[0] and File.file? ARGV[0]
+
+raw = IO.binread ARGV[0] # read raw file
+samples = raw.unpack "s<*" # get samples (little endian signed 16 bits intergers)
+
+# detect falling edges, after crossing the threshold
+on = false # has the threshold been crossed
+edges = []
+samples.each_index do |i|
+ sample = samples[i]
+ if !on then # detect when threshold is crossed
+ if sample > THRESHOLD then
+ on = true
+ edges << {:sample => i, :rising => true}
+ end
+ else
+ if sample < THRESHOLD then
+ on = false
+ edges << {:sample => i, :rising => false}
+ end
+ end
+end
+
+edges.collect!{|edge| {:ms => edge[:sample]/(RATE/1000.0), :rising => edge[:rising]}} # convert edges to milliseconds
+# the bursts (HF activity) should last 1ms
+# verify if this is true, and ignore oscilastion within this 1ms
+pulses = [] # one transmission has 24 pulses
+pulse_begin = nil # first rising edge
+pulse_end = nil # last falling edge
+edges.each do |edge|
+ raise "nil edge" unless edge
+ # search first pulse (rising edge)
+ unless pulse_begin then
+ next unless edge[:rising]
+ pulse_begin = edge
+ end
+ # detect pulses: falling and rising edge within 1ms
+ # ignore edges within this 1ms
+ if !edge[:rising] then
+ pulse_end ||= edge
+ if pulse_end[:ms]-pulse_begin[:ms]<=1*TOLERANCE then
+ pulse_end = edge
+ else # this is too long for a pulse. discard it
+ pulse_begin = nil
+ end
+ else # rising edge
+ if edge[:ms]-pulse_begin[:ms]>1*TOLERANCE then # this is the beginning of the next pulse
+ raise "two rising egdes without falling edge detected" unless pulse_end # this should not happen
+ pulses << pulse_begin
+ pulse_begin = edge
+ pulse_end = nil
+ end # ignore rising egdes within a pulse
+ end
+end
+# add last pulse
+pulses << pulse_begin if pulse_begin and pulse_end
+
+# split pulses into groups
+# one group has 24 pulses with a bitframe of 6ms
+# a blank bitframe without pulse separates groups
+# we will split groups when no pulse occured within after 2 bitframes
+groups = []
+previous_pulse = nil
+group = []
+pulses.each do |pulse|
+ if previous_pulse then
+ group << previous_pulse
+ if (pulse[:ms]-previous_pulse[:ms])>=2*6*(1-(TOLERANCE-1)) then
+ groups << group
+ group = []
+ end
+ end
+ previous_pulse = pulse
+end
+# add last pulse
+group << pulses[-1]
+groups << group
+
+# transmissions have 24 pulses
+transmissions = groups.select {|group| group.size==24}
+
+# verify that there is exactly 24 times one pulse per 6ms bitframe
+# the pulse is either after 2 ms or 5 ms
+values = []
+transmissions.each_index do |transmission_i|
+ transmission = transmissions[transmission_i]
+ # use the previous pulse to sync
+ sync = transmission[0][:ms]-3 # the first pulse is always in the second halt (after 5 ms)
+ bits = []
+ transmission.each_index do |pulse_i|
+ pulse = transmission[pulse_i]
+ # the next pulse is after 6 or 9 ms
+ offset = pulse[:ms]-sync
+# puts "transmission #{transmission_i}, pulse #{pulse_i}, offset: #{offset} ms"
+ if offset>-1.5 and offset<=1.5 then
+ bits << 0
+ sync = pulse[:ms]+6
+ elsif offset>3-1.5 and offset<=3+1.5 then
+ bits << 1
+ sync = pulse[:ms]-3+6
+ else
+ puts "could not decode bit on transmission #{transmission_i} pulse #{pulse_i}"
+ break
+ end
+ end
+ # if there are 24 bit, decode
+ if bits.size==24 then
+ value = 0
+ bits.each do |bit|
+ value = (value << 1) + bit
+ end
+ values << value
+ else
+# puts "found #{bits.size} bits"
+ end
+end
+
+# print results
+puts "# egdes: #{edges.size}"
+puts "# pulses: #{pulses.size}"
+puts "# groups: #{groups.size} (#{groups.collect{|group| group.size}*', '})"
+puts "# transmissions: #{transmissions.size}"
+puts "# values: #{values.size}"
+unless values.empty? then
+ puts "values: "
+ values.each do |value|
+ data_bits = (value & 0x07)
+ system_code = ((value & 0x7fffff) >> 3)
+ puts "- value: #{value} (0x#{value.to_s(16).rjust(6,'0')}), system code: #{system_code} (0x#{system_code.to_s(16)}), databits: #{data_bits} (0x#{data_bits.to_s(16)})"
+ end
+end