live_f1-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/.autotest +23 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +65 -0
  6. data/Guardfile +11 -0
  7. data/README.rdoc +62 -0
  8. data/Rakefile +11 -0
  9. data/bin/live_f1_example +74 -0
  10. data/features/fixtures/sessions/2012.03.china.qualifying/7136/keyframes.yaml +32722 -0
  11. data/features/fixtures/sessions/2012.03.china.qualifying/7136/session.key +1 -0
  12. data/features/fixtures/sessions/2012.03.china.qualifying/session.bin +0 -0
  13. data/features/fixtures/sessions/2012.04.bahrain.practice.2/7164/keyframes.yaml +22403 -0
  14. data/features/fixtures/sessions/2012.04.bahrain.practice.2/7164/session.key +1 -0
  15. data/features/fixtures/sessions/2012.04.bahrain.practice.2/session.bin +0 -0
  16. data/features/fixtures/sessions/2012.05.bahrain.race.post/7167/keyframes.yaml +1204 -0
  17. data/features/fixtures/sessions/2012.05.bahrain.race.post/7167/session.key +1 -0
  18. data/features/fixtures/sessions/2012.05.bahrain.race.post/session.bin +0 -0
  19. data/features/live_f1.feature +14 -0
  20. data/features/step_definitions/live_f1_steps.rb +69 -0
  21. data/features/support/env.rb +9 -0
  22. data/lib/live_f1/debug.rb +6 -0
  23. data/lib/live_f1/enum.rb +9 -0
  24. data/lib/live_f1/event.rb +23 -0
  25. data/lib/live_f1/packet/car/best_lap_time.rb +10 -0
  26. data/lib/live_f1/packet/car/driver.rb +14 -0
  27. data/lib/live_f1/packet/car/gap.rb +10 -0
  28. data/lib/live_f1/packet/car/interval.rb +10 -0
  29. data/lib/live_f1/packet/car/lap_count.rb +10 -0
  30. data/lib/live_f1/packet/car/lap_time.rb +10 -0
  31. data/lib/live_f1/packet/car/num_pits.rb +10 -0
  32. data/lib/live_f1/packet/car/number.rb +14 -0
  33. data/lib/live_f1/packet/car/period_1.rb +11 -0
  34. data/lib/live_f1/packet/car/period_2.rb +11 -0
  35. data/lib/live_f1/packet/car/period_3.rb +11 -0
  36. data/lib/live_f1/packet/car/pit_count.rb +18 -0
  37. data/lib/live_f1/packet/car/pit_lap_1.rb +10 -0
  38. data/lib/live_f1/packet/car/pit_lap_2.rb +10 -0
  39. data/lib/live_f1/packet/car/pit_lap_3.rb +10 -0
  40. data/lib/live_f1/packet/car/position.rb +14 -0
  41. data/lib/live_f1/packet/car/position_history.rb +14 -0
  42. data/lib/live_f1/packet/car/position_update.rb +13 -0
  43. data/lib/live_f1/packet/car/sector_1.rb +11 -0
  44. data/lib/live_f1/packet/car/sector_2.rb +11 -0
  45. data/lib/live_f1/packet/car/sector_3.rb +11 -0
  46. data/lib/live_f1/packet/car.rb +13 -0
  47. data/lib/live_f1/packet/decryptable.rb +22 -0
  48. data/lib/live_f1/packet/header.rb +127 -0
  49. data/lib/live_f1/packet/sector_time.rb +28 -0
  50. data/lib/live_f1/packet/sys/commentary.rb +33 -0
  51. data/lib/live_f1/packet/sys/copyright.rb +9 -0
  52. data/lib/live_f1/packet/sys/key_frame.rb +17 -0
  53. data/lib/live_f1/packet/sys/notice.rb +10 -0
  54. data/lib/live_f1/packet/sys/reset.rb +9 -0
  55. data/lib/live_f1/packet/sys/session_start.rb +25 -0
  56. data/lib/live_f1/packet/sys/speed.rb +29 -0
  57. data/lib/live_f1/packet/sys/timestamp.rb +23 -0
  58. data/lib/live_f1/packet/sys/track_status.rb +18 -0
  59. data/lib/live_f1/packet/sys/weather.rb +33 -0
  60. data/lib/live_f1/packet/sys.rb +10 -0
  61. data/lib/live_f1/packet.rb +129 -0
  62. data/lib/live_f1/source/keyframe.rb +30 -0
  63. data/lib/live_f1/source/live.rb +163 -0
  64. data/lib/live_f1/source/log.rb +29 -0
  65. data/lib/live_f1/source/session.rb +41 -0
  66. data/lib/live_f1/source.rb +83 -0
  67. data/lib/live_f1/version.rb +3 -0
  68. data/lib/live_f1.rb +32 -0
  69. data/live_f1-core.gemspec +26 -0
  70. data/spec/live_f1/event_spec.rb +5 -0
  71. data/spec/live_f1/packet/car/best_lap_time_spec.rb +19 -0
  72. data/spec/live_f1/packet/car/driver_spec.rb +20 -0
  73. data/spec/live_f1/packet/car/gap_spec.rb +20 -0
  74. data/spec/live_f1/packet/car/interval_spec.rb +20 -0
  75. data/spec/live_f1/packet/car/lap_count_spec.rb +20 -0
  76. data/spec/live_f1/packet/car/lap_time_spec.rb +20 -0
  77. data/spec/live_f1/packet/car/number_spec.rb +20 -0
  78. data/spec/live_f1/packet/car/period_1_spec.rb +21 -0
  79. data/spec/live_f1/packet/car/period_2_spec.rb +21 -0
  80. data/spec/live_f1/packet/car/period_3_spec.rb +21 -0
  81. data/spec/live_f1/packet/car/pit_count_spec.rb +20 -0
  82. data/spec/live_f1/packet/car/pit_lap_1_spec.rb +20 -0
  83. data/spec/live_f1/packet/car/pit_lap_2_spec.rb +20 -0
  84. data/spec/live_f1/packet/car/pit_lap_3_spec.rb +20 -0
  85. data/spec/live_f1/packet/car/position_history_spec.rb +20 -0
  86. data/spec/live_f1/packet/car/position_spec.rb +20 -0
  87. data/spec/live_f1/packet/car/position_update_spec.rb +19 -0
  88. data/spec/live_f1/packet/car/sector_1_spec.rb +21 -0
  89. data/spec/live_f1/packet/car/sector_2_spec.rb +21 -0
  90. data/spec/live_f1/packet/car/sector_3_spec.rb +21 -0
  91. data/spec/live_f1/packet/header_spec.rb +148 -0
  92. data/spec/live_f1/packet/sys/commentary_spec.rb +45 -0
  93. data/spec/live_f1/packet/sys/copyright_spec.rb +19 -0
  94. data/spec/live_f1/packet/sys/key_frame_spec.rb +39 -0
  95. data/spec/live_f1/packet/sys/notice_spec.rb +20 -0
  96. data/spec/live_f1/packet/sys/reset_spec.rb +26 -0
  97. data/spec/live_f1/packet/sys/session_start_spec.rb +31 -0
  98. data/spec/live_f1/packet/sys/speed_spec.rb +20 -0
  99. data/spec/live_f1/packet/sys/timestamp_spec.rb +34 -0
  100. data/spec/live_f1/packet/sys/track_status_spec.rb +20 -0
  101. data/spec/live_f1/packet/sys/weather_spec.rb +20 -0
  102. data/spec/live_f1/packet_spec.rb +112 -0
  103. data/spec/live_f1/source/keyframe_spec.rb +56 -0
  104. data/spec/live_f1/source/live_spec.rb +106 -0
  105. data/spec/live_f1/source/session_spec.rb +39 -0
  106. data/spec/live_f1/source_spec.rb +140 -0
  107. data/spec/spec_helper.rb +10 -0
  108. data/spec/support/packet_type_examples.rb +122 -0
  109. metadata +337 -0
@@ -0,0 +1,14 @@
1
+ Feature: Live timing connection
2
+ In order to follow a Formula 1 race live
3
+ As a user of the LiveF1 library
4
+ I want to receive packets of data
5
+
6
+ Scenario: Connecting before the session has started
7
+ Given the live timing session is about to start
8
+ When I successfully connect to the live timing service
9
+ Then I should receive packets of data
10
+
11
+ Scenario: Connecting after the session has completed
12
+ Given the live timing session has been completed
13
+ When I successfully connect to the live timing service
14
+ Then I should receive packets of data
@@ -0,0 +1,69 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'live_f1'
3
+ require 'cucumber/rspec/doubles'
4
+
5
+ Given /^the live timing session is about to start$/ do
6
+ # fixture_session '2012.04.bahrain.practice.2'
7
+ fixture_session '2012.03.china.qualifying'
8
+ end
9
+
10
+ Given /^the live timing session has been completed$/ do
11
+ fixture_session '2012.05.bahrain.race.post'
12
+ end
13
+
14
+ When /^I successfully connect to the live timing service$/ do
15
+ @stream = LiveF1::Source::Live.new 'gareth@example.com', 'swordfish'
16
+ end
17
+
18
+ Then /^I should receive packets of data$/ do
19
+ packets = []
20
+ @stream.run do |packet|
21
+ packets << packet
22
+ end
23
+ packets.should include_a(LiveF1::Packet::Sys::SessionStart)
24
+ end
25
+
26
+ def fixture_session name
27
+ fixture_base = File.expand_path(File.join(File.dirname(__FILE__),'../fixtures/sessions',name))
28
+
29
+ stream_filename = File.join(fixture_base,'session.bin')
30
+ # Stubbing a Socket with a File may have consequences
31
+ TCPSocket.stub(:open) { File.open(stream_filename) }
32
+
33
+ sessions = Dir[File.join(fixture_base,'*')].select { |f| File.directory? f }
34
+ sessions.each do |session|
35
+ keyframe_data = YAML.load(File.read(File.join(session, "keyframes.yaml")))
36
+ keyframe_data.each do |filename, data|
37
+ url = "http://80.231.178.249/#{filename}"
38
+ FakeWeb.register_uri(:get, url, :body => data)
39
+ end
40
+
41
+ session_number = File.basename(session)
42
+ keyfile = File.join(session,'session.key')
43
+ FakeWeb.register_uri(:post, 'http://80.231.178.249/reg/login', :set_cookie => "USER=abc123def")
44
+ FakeWeb.register_uri(:get, "http://80.231.178.249/reg/getkey/#{session_number}.asp?auth=abc123def", :body => keyfile)
45
+ end
46
+ end
47
+
48
+ # def live_timing_session &block
49
+ # receiver = Object.new
50
+ # class << receiver
51
+ # attr_writer :packets
52
+ #
53
+ # def packets
54
+ # @packets ||= []
55
+ # end
56
+ #
57
+ # def method_missing m, *args
58
+ # self.packets += MockPacket.send(m, *args)
59
+ # end
60
+ # end
61
+ #
62
+ # yield receiver
63
+ #
64
+ # receiver.packets << nil
65
+ #
66
+ # socket = mock("socket")
67
+ # socket.stub(:read) { receiver.packets.shift }
68
+ # TCPSocket.stub(:open) { socket }
69
+ # end
@@ -0,0 +1,9 @@
1
+ require 'fakeweb'
2
+
3
+ RSpec::Matchers.define :include_a do |klass|
4
+ match do |actual|
5
+ actual.any? { |item| klass === item }
6
+ end
7
+ end
8
+
9
+ FakeWeb.allow_net_connect = false
@@ -0,0 +1,6 @@
1
+ module LiveF1
2
+ # Adds a debug accessor to trigger extra debugging within the module
3
+ class << self
4
+ attr_accessor :debug
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ module LiveF1
2
+ # Adds a convenience method for converting between constant values and names
3
+ module Enum
4
+ # Returns the first constant (in definition order) matching the given value
5
+ def name_for value
6
+ constants.detect { |c| const_get(c) == value }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ require 'live_f1/enum'
2
+
3
+ module LiveF1
4
+ # An Event represents a usable metric relating to a car or live session.
5
+ class Event
6
+ module Type
7
+ extend LiveF1::Enum
8
+ RACE = 1
9
+ PRACTICE = 2
10
+ QUALIFYING = 3
11
+ end
12
+ include Type
13
+
14
+ module TrackStatus
15
+ extend LiveF1::Enum
16
+ GREEN_FLAG = 1
17
+ YELLOW_FLAG = 2
18
+ SAFETY_CAR_STANDBY = 3
19
+ SAFETY_CAR_DEPLOYED = 4
20
+ RED_FLAG = 5
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class BestLapTime < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Driver < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+
8
+ def to_s
9
+ data
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Gap < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Interval < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class LapCount < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class LapTime < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class NumPits < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Number < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+
8
+ def to_s
9
+ "%02d" % data.to_i unless data == ""
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Period1 < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ include Packet::SectorTime
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Period2 < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ include Packet::SectorTime
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Period3 < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ include Packet::SectorTime
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class PitCount < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+
8
+ def number
9
+ data.to_i
10
+ end
11
+
12
+ def to_s
13
+ number
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class PitLap1 < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class PitLap2 < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class PitLap3 < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Position < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+
8
+ def to_s
9
+ data.to_s
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class PositionHistory < Car
5
+ include Packet::Type::Long
6
+ include Packet::Decryptable
7
+
8
+ def to_s
9
+ data.bytes.map(&:to_i).inspect
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class PositionUpdate < Car
5
+ include Packet::Type::Special
6
+
7
+ def to_s
8
+ header.data.to_s
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Sector1 < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ include Packet::SectorTime
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Sector2 < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ include Packet::SectorTime
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car
4
+ class Sector3 < Car
5
+ include Packet::Type::Short
6
+ include Packet::Decryptable
7
+ include Packet::SectorTime
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Car < Packet
4
+ def leader
5
+ super.gsub(/Car/) { "Car(%02d)" % header.car }
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ Dir[File.join(File.dirname(__FILE__),'car','*')].each do |file|
12
+ require file
13
+ end
@@ -0,0 +1,22 @@
1
+ module LiveF1
2
+ class Packet
3
+ # Packets which mixin the Decryptable module represent data that is encrypted
4
+ # in the data stream.
5
+ #
6
+ # When setting the packet data we transparently decrypt the data, and also
7
+ # set a raw_data containing the original, encrypted bytes in case they are
8
+ # useful
9
+ module Decryptable
10
+ attr_reader :raw_data
11
+
12
+ def data= bytes
13
+ @raw_data = bytes
14
+ @data = source.decrypt(bytes)
15
+ end
16
+
17
+ def bytes
18
+ @raw_data
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,127 @@
1
+ require_relative '../packet'
2
+ require_relative '../event'
3
+
4
+ module LiveF1
5
+ class Packet
6
+ # The Unknown packet is a special placeholder packet for situations where a
7
+ # zero-length packet is delivered but seems to have no effect on the stream
8
+ class Unknown < Packet
9
+ include Packet::Type::Special
10
+
11
+ def to_s
12
+ "Unknown packet type #{header.packet_type}" + (header.car > 0 ? " for car #{header.car}" : "")
13
+ end
14
+ end
15
+
16
+ # A Header uses 2 bytes of data from a live timing stream to determine all
17
+ # the necessary information about the packet which follows it.
18
+ class Header < Struct.new(:data, :packet_type, :car, :event_type)
19
+ attr_reader :bytes
20
+
21
+ def self.from_source source, event_type
22
+ bytes = source.read_bytes(2)
23
+ raise "No data from #{source.inspect}" unless bytes.to_s.length == 2
24
+ bits = bytes.to_s.reverse.unpack("B*").first
25
+ _, data, packet_type, car = bits.match(/^(.{7})(.{4})(.{5})$/).to_a.map { |s| s.to_i(2) }
26
+
27
+ new(data, packet_type, car, event_type).tap do |header|
28
+ # TODO: Maybe need a nicer way of setting this
29
+ header.instance_variable_set "@bytes", bytes
30
+ end
31
+ end
32
+
33
+ def car?
34
+ !car.zero?
35
+ end
36
+
37
+ def packet_klass
38
+ case
39
+ when car?
40
+ case event_type
41
+ when Event::RACE
42
+ case packet_type
43
+ when 0 then Packet::Car::PositionUpdate
44
+ when 1 then Packet::Car::Position
45
+ when 2 then Packet::Car::Number
46
+ when 3 then Packet::Car::Driver
47
+ when 4 then Packet::Car::Gap
48
+ when 5 then Packet::Car::Interval
49
+ when 6 then Packet::Car::LapTime
50
+ when 7 then Packet::Car::Sector1
51
+ when 8 then Packet::Car::PitLap1
52
+ when 9 then Packet::Car::Sector2
53
+ when 10 then Packet::Car::PitLap2
54
+ when 11 then Packet::Car::Sector3
55
+ when 12 then Packet::Car::PitLap3
56
+ when 13 then Packet::Car::NumPits
57
+ when 14 then nil
58
+ when 15 then Packet::Car::PositionHistory
59
+ end
60
+ when Event::PRACTICE
61
+ case packet_type
62
+ when 0 then Packet::Car::PositionUpdate
63
+ when 1 then Packet::Car::Position
64
+ when 2 then Packet::Car::Number
65
+ when 3 then Packet::Car::Driver
66
+ when 4 then Packet::Car::BestLapTime
67
+ when 5 then Packet::Car::Gap
68
+ when 6 then Packet::Car::Sector1
69
+ when 7 then Packet::Car::Sector2
70
+ when 8 then Packet::Car::Sector3
71
+ when 9 then Packet::Car::LapCount
72
+ when 10 then Packet::Car::LapCount
73
+ when 15 then nil
74
+ end
75
+ when Event::QUALIFYING
76
+ case packet_type
77
+ when 0 then Packet::Car::PositionUpdate
78
+ when 1 then Packet::Car::Position
79
+ when 2 then Packet::Car::Number
80
+ when 3 then Packet::Car::Driver
81
+ when 4 then Packet::Car::Period1
82
+ when 5 then Packet::Car::Period2
83
+ when 6 then Packet::Car::Period3
84
+ when 7 then Packet::Car::Sector1
85
+ when 8 then Packet::Car::Sector2
86
+ when 9 then Packet::Car::Sector3
87
+ when 10 then Packet::Car::LapCount
88
+ when 15 then nil
89
+ end
90
+ else
91
+ raise MissingEventType, "Unrecognised event type (#{event_type.inspect}), can't determine class for car packet #{packet_type.inspect}"
92
+ end
93
+ else
94
+ case packet_type
95
+ when 0 then Packet::Unknown
96
+ when 1 then Packet::Sys::SessionStart
97
+ when 2 then Packet::Sys::KeyFrame
98
+ when 3 then Packet::Unknown
99
+ when 4 then Packet::Sys::Commentary
100
+ when 5 then Packet::Unknown
101
+ when 6 then Packet::Sys::Notice
102
+ when 7 then Packet::Sys::Timestamp
103
+ when 8 then nil # Packet::Unknown
104
+ when 9 then Packet::Sys::Weather
105
+ when 10 then Packet::Sys::Speed
106
+ when 11 then Packet::Sys::TrackStatus
107
+ when 12 then Packet::Sys::Copyright
108
+ when 13 then nil # Packet::Unknown
109
+ when 14 then nil # Packet::Unknown
110
+ when 15 then nil # Packet::Unknown
111
+ end
112
+ end or raise UnexpectedPacket, "Unexpected #{car? ? 'car' : 'sys'} packet type #{packet_type.inspect} for #{Event::Type.name_for event_type} event"
113
+ end
114
+
115
+ class MissingEventType < RuntimeError
116
+ end
117
+
118
+ # An unexpected packet is one that we don't expect to appear in the data stream
119
+ class UnexpectedPacket < RuntimeError
120
+ end
121
+
122
+ # An unknown packet is one that we expect (from experience) to appear in the data stream but don't know its purpose
123
+ class UnknownPacket < RuntimeError
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,28 @@
1
+ module LiveF1
2
+ class Packet
3
+ module SectorTime
4
+
5
+ # Note of which bits appear in packet headers to represent different
6
+ # coloured sectors
7
+ # TODO: Use these colours somewhere
8
+ COLORS = {
9
+ 0b010 => :red,
10
+ 0b110 => :yellow,
11
+ 0b001 => :white,
12
+ 0b100 => :purple,
13
+ 0b011 => :green
14
+ }
15
+
16
+ def seconds
17
+ if match = data.match(/^(?:(\d+):)?(\d+.\d+)$/)
18
+ _, minutes, seconds = match.to_a
19
+ (Rational(minutes.to_i * 60) + Rational(seconds)).to_f
20
+ end
21
+ end
22
+
23
+ def to_s
24
+ seconds || data
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ module LiveF1
4
+ class Packet
5
+ class Sys
6
+ class Commentary < Sys
7
+ include Packet::Type::Long
8
+ include Packet::Decryptable
9
+
10
+ # Is this the last line of this commentary string?
11
+ #
12
+ # If not, the next packet should also be a Commentary packet continuing this text
13
+ def terminal?
14
+ data.bytes.to_a[1] == 1
15
+ end
16
+
17
+ # Returns the line of commentary, which may only be a partial line if
18
+ # this commentary was split over multiple packets
19
+ def line
20
+ # The commentary packet encoding used to be all messed up. Its UTF-8
21
+ # characters were treated as Windows-1252 and then reconverted back
22
+ # to UTF-8. We used to fix that but now the issue has been
23
+ # corrected.
24
+ data[2..-1].force_encoding("UTF-8")#.encode("Windows-1252").force_encoding("UTF-8")
25
+ end
26
+
27
+ def to_s
28
+ "%s%s" % [line, (terminal? ? "" : "…")]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,9 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Sys
4
+ class Copyright < Sys
5
+ include Packet::Type::Long
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module LiveF1
2
+ class Packet
3
+ class Sys
4
+ class KeyFrame < Sys
5
+ include Packet::Type::Short
6
+
7
+ def number
8
+ data.reverse.unpack("B*").first.to_i(2)
9
+ end
10
+
11
+ def to_s
12
+ "Keyframe #{number}"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end