add script to decode rtl_fm recordings
This commit is contained in:
commit
8b67b811d1
|
@ -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
|
Loading…
Reference in New Issue