8 our $PACKET_SIZE = 9; # 9 bytes + 0x05
11 my ($class, $args) = @_;
23 sub now { return shift->{now} }
26 my ($self, $time, @bytes) = @_;
28 push @{ $self->{bytes} }, @bytes;
29 @bytes = @{ $self->{bytes} };
35 while (@bytes > $PACKET_SIZE) {
36 if ($bytes[0] != 0x55) {
37 push @bad_bytes, shift @bytes;
42 if ($bytes[$PACKET_SIZE] != 0x05
43 || SCX::CRC::digest(@bytes[0..$PACKET_SIZE-2])
44 != $bytes[$PACKET_SIZE-1]) {
45 push @bad_bytes, shift @bytes;
49 if (@bad_bytes) { # Report previous bad bytes first
50 $self->bad_bytes(@bad_bytes);
54 my @packet = splice @bytes, 0, $PACKET_SIZE+1;
55 $self->log_packet(@packet);
56 $self->parse_packet(@packet);
60 while (@bytes && $bytes[0] != 0x55) {
61 push @bad_bytes, shift @bytes;
63 $self->bad_bytes(@bad_bytes);
66 @{ $self->{bytes} } = @bytes;
73 sub unknown_packet { }
74 sub strange_packet { }
77 sub car_programming { }
87 sub display_change { }
89 sub controller_status { }
92 0xAA => \&bus_free_time_packet,
93 0xCC => \&car_programming_packet,
94 0xD0 => \&reset_packet,
95 0xD3 => \&standings_packet,
96 0xD4 => \&car_lap_time_packet,
97 0xD5 => \&race_setup_packet,
98 0xD6 => \&fuel_level_packet,
99 0xD7 => \&brake_set_packet,
100 0xDB => \&qualification_packet,
101 0xDC => \&end_of_race_packet,
102 0xDD => \&race_start_packet,
103 0xDE => \&display_change_packet,
104 0xEE => \&finish_line_packet,
105 0xFF => \&controller_status_packet,
109 my ($self, @data) = @_;
112 my @args = @data[2..7];
114 my $sub = $COMMANDS{$cmd};
117 $self->unknown_packet($cmd, @args);
121 return &$sub($self, @args);
124 sub bus_free_time_packet {
125 my ($self, @bytes) = @_;
127 $self->strange_packet('bus free time', @bytes)
131 || $bytes[5] != 0xF0;
133 $self->bus_free_time($bytes[1], $bytes[0]);
136 sub car_programming_packet {
137 my ($self, @bytes) = @_;
139 $self->strange_packet('car programming', @bytes)
140 if ($bytes[0] & 0xF8) != 0 || ($bytes[0] & 0x07) > 5
145 || $bytes[5] != 0xFF;
147 $self->car_programming($bytes[0] & 0x07);
151 my ($self, @bytes) = @_;
153 $self->strange_packet('reset', @bytes)
157 || $bytes[5] != 0xAA;
159 $self->reset($bytes[1], $bytes[2]);
162 sub standings_packet {
163 my ($self, @bytes) = @_;
165 self->strange_packet('standings', @bytes)
166 if ($bytes[0] != 0xFF && ($bytes[0] & 0x07) > 5)
167 || ($bytes[1] != 0xFF && ($bytes[1] & 0x07) > 5)
168 || ($bytes[2] != 0xFF && ($bytes[2] & 0x07) > 5)
169 || ($bytes[3] != 0xFF && ($bytes[3] & 0x07) > 5)
170 || ($bytes[4] != 0xFF && ($bytes[4] & 0x07) > 5)
171 || ($bytes[5] != 0xFF && ($bytes[5] & 0x07) > 5);
173 $self->standings(map { $_ != 0xFF ? $_ & (0x07) : () } @bytes);
176 sub car_lap_time_packet {
177 my ($self, @bytes) = @_;
179 $self->strange_packet('car lap time', @bytes)
183 || ($bytes[3] & 0xF0) != 0
187 $self->car_lap_time($bytes[0],
188 256*$bytes[1] + $bytes[2]
189 + ($bytes[3] & 0x02 ? 256 : 0)
190 + ($bytes[3] & 0x01 ? 1 : 0),
191 sprintf('%.3f', 0.01024 * (256*$bytes[4] + $bytes[5]
192 + ($bytes[3] & 0x08 ? 256 : 0)
193 + ($bytes[3] & 0x04 ? 1 : 0))),
194 sprintf('%04b', $bytes[3])
198 sub race_setup_packet {
199 my ($self, @bytes) = @_;
201 $self->strange_packet('race setup')
202 if ($bytes[0] != 0x00 && $bytes[0] != 0xFF)
207 || $bytes[5] != 0xFF;
209 my $rounds = $bytes[0] == 0x00
211 : ($bytes[1] & 0x0F) * 256
212 + ($bytes[2] & 0x0F) * 16
213 + ($bytes[3] & 0x0F);
215 $self->race_setup($rounds);
218 sub fuel_level_packet {
219 my ($self, @bytes) = @_;
221 $self->strange_packet('fuel level')
222 if ($bytes[0] >> 4) > 8
223 || ($bytes[0] & 0x0F) > 8
224 || ($bytes[1] >> 4) > 8
225 || ($bytes[1] & 0x0F) > 8
226 || ($bytes[2] >> 4) > 8
227 || ($bytes[2] & 0x0F) > 8
228 || ($bytes[5] != 0xAA && $bytes[5] != 0xFF);
231 $bytes[0] >> 4, $bytes[0] & 0x0f,
232 $bytes[1] >> 4, $bytes[1] & 0x0f,
233 $bytes[2] >> 4, $bytes[2] & 0x0f,
236 $self->fuel_level(@fuel);
239 sub brake_set_packet {
240 my ($self, @bytes) = @_;
242 $self->strange_packet('brake set')
244 || ($bytes[1] != 0x00 && $bytes[1] != 0x02 && $bytes[1] != 0x04)
248 || $bytes[5] != 0xFF;
250 $self->brake_set($bytes[0],
251 $bytes[1] == 0x00 ? 0
252 : $bytes[1] == 0x02 ? 50
256 sub qualification_packet {
257 my ($self, @bytes) = @_;
259 $self->strange_packet('qualification')
265 || $bytes[5] != 0xFF;
267 my $rounds = ($bytes[0] & 0x0F) * 256
268 + ($bytes[1] & 0x0F) * 16
269 + ($bytes[2] & 0x0F);
270 my $cars = $bytes[3];
272 $self->qualification($rounds, $cars);
275 sub end_of_race_packet {
276 my ($self, @bytes) = @_;
278 $self->strange_packet('end of race')
284 || $bytes[5] != 0xFF;
286 $self->end_of_race();
289 sub race_start_packet {
290 my ($self, @bytes) = @_;
292 $self->strange_packet('race start')
298 || $bytes[5] != 0xAA;
303 sub display_change_packet {
304 my ($self, @bytes) = @_;
306 $self->strange_packet('display change')
312 || $bytes[5] != 0xFF;
314 $self->display_change();
319 # FIXME: we still do not know the meaning of the bytes
320 sub finish_line_packet {
321 my ($self, @bytes) = @_;
324 for my $byte (@bytes) {
332 my $msg = 'Strange finish_line packet'
338 my $byte = $bytes[$i];
341 if $byte != 0xAA && $byte != 0xE7 && $byte != 0xFE;
343 push @cars_finished, $i if $byte == 0xE7;
346 $self->log_cmd('finish_line', $regular, @cars_finished);
347 $self->track->finish_line(
348 $self->{last_read_time},
357 sub finish_line_packet {
358 my ($self, @bytes) = @_;
361 for my $byte (@bytes) {
369 $self->strange_packet('finish line')
375 my $byte = $bytes[$i];
378 if $byte != 0xAA && $byte != 0xE7 && $byte != 0xFE;
380 push @cars_finished, $i if $byte == 0xE7;
383 $self->finish_line($regular, @cars_finished);
386 sub controller_status_packet {
387 my ($self, @bytes) = @_;
390 for my $byte (@bytes) {
391 next if $byte == 0xAA;
393 if ($byte & 0xC0) != 0xC0
394 || ($byte & 0x0F) > 12
397 $self->strange_packet('controller status')
403 my $byte = $bytes[$car];
406 push @ctrl_data, undef;
410 my $light = ($byte & 0x20) ? 0 : 1;
411 my $backbutton = ($byte & 0x10) ? 0 : 1;
412 my $throttle = $byte & 0x0f;
415 throttle => $throttle,
416 button => $backbutton,
421 $self->controller_status(@ctrl_data);