megacode/sdr/decode.rb

146 lines
4.3 KiB
Ruby
Executable File

#!/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|
button = value & 7
code = (value >> 3) & 65535
facility = (value >> 19) & 15
printf("- value: 0X%06x, code: %05d, facility: %d, button: %d\n", value, code, facility, button)
end
end