2014-07-13 03:15:21 +02:00
|
|
|
#!/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|
|
2021-08-28 13:03:10 +02:00
|
|
|
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)
|
2014-07-13 03:15:21 +02:00
|
|
|
end
|
|
|
|
end
|