Phrogz
11/14/2007 1:50:00 AM
Attached is my solution (in progress). Before I get onto the analysis
reports, I'm still trying to simplify the data post parsing. But, at
least it's getting there. :)
As you can see from the results, I'm only properly pulling data from
the first day. This is why I enjoy quizzes; I'm sure I tried to fix
this same problem at some point.
#!/usr/bin/env ruby
WHEELBASE_AVG = 100 # inches
INCHES_PER_MILE = 63_360
SECONDS_PER_HOUR = 3600.0
INCHES_PER_SECOND_TO_MPH = 17.6
USAGE = <<ENDUSAGE
Usage:
vehicle_counter [-t time_segment] [-a] data_file
-t,--time the number of minutes per segment (defaults to 60)
-a,--average average time samples across days
(defaults to showing each day indpendently)
ENDUSAGE
ARGS = {
:time_segment => 60,
#:data_file => 'vehicle_counter.data'
}
UNFLAGGED_ARGS = [ :data_file ]
next_arg = UNFLAGGED_ARGS.first
ARGV.each{ |arg|
case arg
when '-t','--time'
next_arg = :time_segment
when '-a','--average'
ARGS[:average] = true
else
if next_arg
if next_arg==:time_segment
arg = arg.to_i
end
ARGS[next_arg] = arg
UNFLAGGED_ARGS.delete( next_arg )
end
next_arg = UNFLAGGED_ARGS.first
end
}
if !ARGS[:data_file] || !ARGS[:time_segment]
puts USAGE
exit
end
class Record
SECONDS_PER_DAY = 3600 * 24
attr_reader :time, :direction, :ms
attr_accessor :speed
def initialize( str, day_offset )
_, @direction, @ms = /([AB])(\d+)/.match( str ).to_a
@ms = @ms.to_i
@time = Time.gm( 2007 ) + ( @ms.to_i / 1000.0 ) + ( day_offset *
SECONDS_PER_DAY )
end
end
# Prepare data
raw_data = IO.readlines( ARGS[:data_file] )
day_changes = 0
records = raw_data.inject([]){ |records,line|
record = Record.new( line, ARGS[:average] ? 0 : day_changes )
if (last_record = records.last) && (record.ms < last_record.ms)
day_changes += 1
end
records << record
}
# Convert axle pairs to speed
last_record = {}
records.each{ |record|
if last_axle = last_record[ record.direction ]
last_axle.speed = WHEELBASE_AVG / ( record.time -
last_axle.time )
last_axle.speed /= INCHES_PER_SECOND_TO_MPH
last_record[ record.direction ] = nil
else
last_record[ record.direction ] = record
end
}
records.delete_if{ |r| r.speed.nil? }
# Figure out which direction gets double hits
possible_directions = records.map{ |r| r.direction }.uniq
double_hit_direction = possible_directions.map{ |dir|
[ records.select{ |r| r.direction == dir }.length , dir ]
}.sort.last.last
# Remove extraneous records
require 'enumerator'
records.each_cons(2){ |r1,r2|
if (r1.direction == double_hit_direction) &&
(r2.direction != double_hit_direction) &&
# 0.02 seconds @ 50mph is ~18 inches
# If the times are this close, it must be a double hit
(r1.time - r2.time).abs < 0.02
r1.speed = nil
end
}
records.delete_if{ |r| r.speed.nil? }
t1 = Time.gm(0)
ms_trigger = 0
slot_count = nil
ms_per_slot = ARGS[ :time_segment ] * 60 * 1000
records << Record.new( 'B99999999', 0 )
records.each{ |r|
if r.ms >= ms_trigger
if slot_count
t2 = t1 + ms_per_slot / 1000.0
print "#{t1.strftime('%H:%M')}..#{t2.strftime('%H:%M')} : "
puts "%4i %s, %4i %s" % possible_directions.map{ |dir|
[slot_count[dir],dir=="A" ? "left" : "right"]
}.flatten
t1 = t2
end
slot_count = Hash[ *possible_directions.map{ |d| [d,0] }.flatten ]
ms_trigger += ms_per_slot
end
slot_count[ r.direction ] += 1
}
Slim2:Code phrogz$ ruby vehicle_counter.rb vehicle_counter.data -t 15
00:00..00:15 : 2 left, 1 right
00:15..00:30 : 1 left, 4 right
00:30..00:45 : 3 left, 2 right
00:45..01:00 : 1 left, 1 right
01:00..01:15 : 0 left, 2 right
01:15..01:30 : 0 left, 1 right
01:30..01:45 : 0 left, 3 right
01:45..02:00 : 1 left, 1 right
02:00..02:15 : 0 left, 1 right
02:15..02:30 : 1 left, 2 right
02:30..02:45 : 0 left, 2 right
02:45..03:00 : 1 left, 1 right
03:00..03:15 : 0 left, 3 right
03:15..03:30 : 0 left, 2 right
03:30..03:45 : 1 left, 1 right
03:45..04:00 : 1 left, 1 right
04:00..04:15 : 1 left, 3 right
04:15..04:30 : 0 left, 1 right
04:30..04:45 : 0 left, 1 right
04:45..05:00 : 1 left, 0 right
05:00..05:15 : 0 left, 5 right
05:15..05:30 : 0 left, 15 right
05:30..05:45 : 1 left, 4 right
05:45..06:00 : 2 left, 8 right
06:00..06:15 : 13 left, 8 right
06:15..06:30 : 18 left, 9 right
06:30..06:45 : 13 left, 9 right
06:45..07:00 : 8 left, 5 right
07:00..07:15 : 18 left, 64 right
07:15..07:30 : 16 left, 62 right
07:30..07:45 : 44 left, 66 right
07:45..08:00 : 44 left, 64 right
08:00..08:15 : 49 left, 59 right
08:15..08:30 : 45 left, 75 right
08:30..08:45 : 46 left, 151 right
08:45..09:00 : 44 left, 169 right
09:00..09:15 : 35 left, 25 right
09:15..09:30 : 32 left, 14 right
09:30..09:45 : 23 left, 16 right
09:45..10:00 : 26 left, 14 right
10:00..10:15 : 29 left, 16 right
10:15..10:30 : 33 left, 15 right
10:30..10:45 : 25 left, 14 right
10:45..11:00 : 23 left, 12 right
11:00..11:15 : 26 left, 32 right
11:15..11:30 : 16 left, 38 right
11:30..11:45 : 35 left, 26 right
11:45..12:00 : 26 left, 26 right
12:00..12:15 : 33 left, 29 right
12:15..12:30 : 25 left, 42 right
12:30..12:45 : 30 left, 27 right
12:45..13:00 : 31 left, 27 right
13:00..13:15 : 27 left, 41 right
13:15..13:30 : 29 left, 32 right
13:30..13:45 : 24 left, 38 right
13:45..14:00 : 31 left, 25 right
14:00..14:15 : 26 left, 41 right
14:15..14:30 : 23 left, 44 right
14:30..14:45 : 28 left, 49 right
14:45..15:00 : 21 left, 46 right
15:00..15:15 : 42 left, 48 right
15:15..15:30 : 55 left, 46 right
15:30..15:45 : 44 left, 55 right
15:45..16:00 : 42 left, 48 right
16:00..16:15 : 41 left, 51 right
16:15..16:30 : 58 left, 41 right
16:30..16:45 : 93 left, 40 right
16:45..17:00 : 102 left, 35 right
17:00..17:15 : 80 left, 41 right
17:15..17:30 : 111 left, 53 right
17:30..17:45 : 114 left, 39 right
17:45..18:00 : 106 left, 39 right
18:00..18:15 : 19 left, 6 right
18:15..18:30 : 21 left, 11 right
18:30..18:45 : 27 left, 7 right
18:45..19:00 : 25 left, 6 right
19:00..19:15 : 9 left, 9 right
19:15..19:30 : 12 left, 7 right
19:30..19:45 : 11 left, 18 right
19:45..20:00 : 3 left, 10 right
20:00..20:15 : 7 left, 11 right
20:15..20:30 : 9 left, 12 right
20:30..20:45 : 9 left, 6 right
20:45..21:00 : 11 left, 8 right
21:00..21:15 : 8 left, 10 right
21:15..21:30 : 7 left, 4 right
21:30..21:45 : 12 left, 11 right
21:45..22:00 : 6 left, 16 right
22:00..22:15 : 10 left, 5 right
22:15..22:30 : 17 left, 5 right
22:30..22:45 : 7 left, 8 right
22:45..23:00 : 16 left, 2 right
23:00..23:15 : 9 left, 2 right
23:15..23:30 : 4 left, 4 right
23:30..23:45 : 4 left, 6 right
23:45..00:00 : 8916 left, 9061 right