live_f1-core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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