File Coverage

blib/lib/Slinke.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Slinke;
2              
3             =head1 NAME
4              
5             Slinke - module to control the Slink-e product produced by Nirvis -
6             visit Nirvis at http://www.nirvis.com
7              
8             =head1 SYNOPSIS
9              
10             use Slinke;
11              
12             # Create a Slinke and read from the infrared port
13             my $slinke = new Slinke;
14             my $data = $slinke->requestInput();
15              
16             foreach my $i ( @$data ) {
17             print "$i\n";
18             }
19              
20             =head1 DESCRIPTION
21              
22             Slink-e is a product that can speak to many different Sony products over
23             the S-Link port. Also, it can receive and transmit infrared signals over
24             8 different transmitters/receivers.
25              
26             For now, the bulk of this code deals with the transmission and reception
27             of these infrared signals.
28              
29             Note that this code borrows heavily from C++ code from Colby Boles. In
30             fact, sometimes I just copied his code and comments verbatim.
31              
32             =head1 METHODS
33              
34             =cut
35              
36 1     1   2038 use strict;
  1         1  
  1         50  
37 1     1   5 use Exporter;
  1         2  
  1         50  
38 1     1   3178 use Device::SerialPort qw( :PARAM :STAT 0.07 );
  0            
  0            
39             use vars qw( @ISA $VERSION @EXPORT );
40              
41             $VERSION = 1.00;
42             @ISA = qw(Exporter);
43              
44             =head2 EXPORT
45              
46             The different port names are exported. These are the following:
47              
48             PORT_SL0 PORT_SL1 PORT_SL2 PORT_SL3 PORT_IR PORT_PAR PORT_SER PORT_SYS
49              
50             =cut
51              
52             @EXPORT = qw( PORT_SL0 PORT_SL1 PORT_SL2 PORT_SL3 PORT_IR PORT_PAR PORT_SER PORT_SYS decodeIR );
53              
54             $Slinke::SLINKE_NUMPORTS = 8;
55             $Slinke::SLINKE_CLK = 20.0e6;
56             $Slinke::PORT_IR_MAXML = 15;
57             $Slinke::IRSKEWADJUST = -100e-6;
58             $Slinke::MAXDATABLOCK = 30; # largest block the slinke can handle at once
59              
60             %Slinke::PORTS = ( PORT_SL0 => 0, PORT_SL1 => 1,
61             PORT_SL2 => 2, PORT_SL3 => 3,
62             PORT_IR => 4, PORT_PAR => 5,
63             PORT_SER => 6, PORT_SYS => 7,
64             );
65              
66             %Slinke::COMMANDS = ( CMD_PORT_DONE => 0x00, CMD_PORT_SM => 0x1F,
67             # port commands
68             # general
69             CMD_DISABLE => 0x02, CMD_ENABLE => 0x03,
70             # S-Link
71             CMD_SENDBITMODE => 0x04,
72             # ir
73             CMD_SETIRFS => 0x04, CMD_GETIRFS => 0x05,
74             CMD_SETIRCFS => 0x06, CMD_GETIRCFS => 0x07,
75             CMD_SETIRTIMEOUT => 0x0C, CMD_GETIRTIMEOUT => 0x0D,
76             CMD_SETIRMINLEN => 0x0E, CMD_GETIRMINLEN => 0x0F,
77             CMD_SETIRTXPORTS => 0x08, CMD_GETIRTXPORTS => 0x13,
78             CMD_SETIRRXPORTEN => 0x09, CMD_GETIRRXPORTEN => 0x12,
79             CMD_SETIRPORTECHO => 0x0A, CMD_GETIRPORTECHO => 0x10,
80             CMD_SETIRRXPORTPOL => 0x0B, CMD_GETIRRXPORTPOL => 0x11,
81             # serial
82             CMD_SETBAUD => 0x08, CMD_GETBAUD => 0x09,
83             # parallel
84             CMD_SETHSMODE => 0x10, CMD_GETHSMODE => 0x11,
85             CMD_SETDIR => 0x12, CMD_GETDIR => 0x13,
86             CMD_SAMPLE => 0x14,
87             # system
88             CMD_GETVERSION => 0x0B, CMD_GETSERIALNO => 0x0C,
89             CMD_SETSERIALNO => 0x0D, CMD_SAVEDEFAULTS => 0x0E,
90             CMD_LOADDEFAULTS => 0x0F, CMD_RESUME => 0xAA,
91             CMD_RESET => 0xFF,
92             # custom for SEG
93             CMD_PLAYMACRO1 => 0x10, CMD_PLAYMACRO2 => 0x11,
94             CMD_STOREMACRO1 => 0x12, CMD_STOREMACRO2 => 0x13,
95             );
96              
97             %Slinke::RESPONSES = ( # port responses
98             RSP_PORT_DONE => 0x00, RSP_PORT_SM => 0x1F,
99             # port special messages
100             # general
101             RSP_DISABLE => 0x02, RSP_ENABLE => 0x03,
102             RSP_TX_TIMEOUT => 0x81, RSP_CMD_ILLEGAL => 0xFF,
103             RSP_RX_ERROR => 0x80,
104             # S-Link
105             RSP_RX_BITMODE => 0x04,
106             # ir
107             RSP_EQRXPORT => 0x01, RSP_EQIRFS => 0x04,
108             RSP_EQIRCFS => 0x06, RSP_EQIRPORTECHO => 0x0A,
109             RSP_EQIRTIMEOUT => 0x0C, RSP_EQIRMINLEN => 0x0E,
110             RSP_EQIRRXPORTEN => 0x09, RSP_EQIRRXPORTPOL => 0x0B,
111             RSP_EQIRTXPORTS => 0x08, RSP_IRFS_ILLEGAL => 0x82,
112             # serial
113             RSP_EQBAUD => 0x08, RSP_SERIALIN_OVERFLOW => 0x83,
114             RSP_SERIALIN_OVERRUN => 0x86, RSP_SERIALIN_FRAMEERROR => 0x85,
115             RSP_BAUD_ILLEGAL => 0x84,
116             # parallel
117             RSP_EQHSMODE => 0x10, RSP_EQDIR => 0x12,
118             # system
119             RSP_EQVERSION => 0x0B, RSP_EQSERIALNO => 0x0C,
120             RSP_DEFAULTSSAVED => 0x0E, RSP_DEFAULTSLOADED => 0x0F,
121             RSP_SEEPROMWRERR => 0x8F,
122             );
123              
124             %Slinke::INVPORTS = reverse %Slinke::PORTS;
125              
126             foreach my $i ( keys %Slinke::RESPONSES ) {
127             push @{$Slinke::INVRESPONSES{ $Slinke::RESPONSES{ $i } } }, $i;
128             }
129              
130             %Slinke::COMMANDMAPS = ( "CMD_GETBAUD" => [ "PORT_SER", "RSP_EQBAUD" ],
131             "CMD_SETBAUD" => [ "PORT_SER", "RSP_EQBAUD" ],
132             "CMD_GETSERIALNO" => [ "PORT_SYS", "RSP_EQSERIALNO" ],
133             "CMD_GETVERSION" => [ "PORT_SYS", "RSP_EQVERSION" ],
134             "CMD_ENABLE" => [ undef, "RSP_ENABLE" ],
135             "CMD_DISABLE" => [ undef, "RSP_DISABLE" ],
136             "CMD_GETIRFS" => [ "PORT_IR", "RSP_EQIRFS" ],
137             "CMD_SETIRFS" => [ "PORT_IR", "RSP_EQIRFS" ],
138             "CMD_GETIRCFS" => [ "PORT_IR", "RSP_EQIRCFS" ],
139             "CMD_SETIRCFS" => [ "PORT_IR", "RSP_EQIRCFS" ],
140             "CMD_GETIRTIMEOUT" => [ "PORT_IR", "RSP_EQIRTIMEOUT" ],
141             "CMD_SETIRTIMEOUT" => [ "PORT_IR", "RSP_EQIRTIMEOUT" ],
142             "CMD_GETIRMINLEN" => [ "PORT_IR", "RSP_EQIRMINLEN" ],
143             "CMD_SETIRMINLEN" => [ "PORT_IR", "RSP_EQIRMINLEN" ],
144             "CMD_GETIRTXPORTS" => [ "PORT_IR", "RSP_EQIRTXPORTS" ],
145             "CMD_SETIRTXPORTS" => [ "PORT_IR" ],
146             "CMD_GETIRRXPORTEN" => [ "PORT_IR", "RSP_EQIRRXPORTEN" ],
147             "CMD_SETIRRXPORTEN" => [ "PORT_IR", "RSP_EQIRRXPORTEN" ],
148             "CMD_GETIRPORTECHO" => [ "PORT_IR", "RSP_EQIRPORTECHO" ],
149             "CMD_SETIRPORTECHO" => [ "PORT_IR", "RSP_EQIRPORTECHO" ],
150             "CMD_GETIRRXPORTPOL" => [ "PORT_IR", "RSP_EQIRRXPORTPOL" ],
151             "CMD_SETIRRXPORTPOL" => [ "PORT_IR", "RSP_EQIRRXPORTPOL" ],
152             "CMD_GETHSMODE" => [ "PORT_PAR", "RSP_EQHSMODE" ],
153             "CMD_SETHSMODE" => [ "PORT_PAR", "RSP_EQHSMODE" ],
154             "CMD_GETDIR" => [ "PORT_PAR", "RSP_EQDIR" ],
155             "CMD_SETDIR" => [ "PORT_PAR", "RSP_EQDIR" ],
156             "CMD_SAMPLE" => [ "PORT_PAR" ],
157             "CMD_RESUME" => [ "PORT_SYS" ],
158             "CMD_RESET" => [ "PORT_SYS" ],
159             "CMD_LOADDEFAULTS" => [ "PORT_SYS", "RSP_DEFAULTSLOADED" ],
160             "CMD_SAVEDEFAULTS" => [ "PORT_SYS", "RSP_DEFAULTSSAVED" ],
161             );
162              
163             %Slinke::RESPONSEFORSL = ( RSP_ENABLE => 0,
164             RSP_DISABLE => 0,
165             );
166              
167             %Slinke::RESPONSEMAPS = ( PORT_SL0 => \%Slinke::RESPONSEFORSL,
168             PORT_SL1 => \%Slinke::RESPONSEFORSL,
169             PORT_SL2 => \%Slinke::RESPONSEFORSL,
170             PORT_SL3 => \%Slinke::RESPONSEFORSL,
171             PORT_IR => { RSP_ENABLE => 0,
172             RSP_DISABLE => 0,
173             RSP_EQIRFS => 2,
174             RSP_EQIRCFS => 2,
175             RSP_EQIRTIMEOUT => 2,
176             RSP_EQIRMINLEN => 1,
177             RSP_EQIRTXPORTS => 1,
178             RSP_EQRXPORT => 1,
179             RSP_EQIRRXPORTEN => 1,
180             RSP_EQIRRXPORTPOL => 1,
181             RSP_EQIRPORTECHO => 16,
182             },
183             PORT_PAR => { RSP_ENABLE => 0,
184             RSP_DISABLE => 0,
185             RSP_EQHSMODE => 1,
186             RSP_EQDIR => 1,
187             },
188             PORT_SYS => { RSP_ENABLE => 0,
189             RSP_DISABLE => 0,
190             RSP_EQVERSION => 1,
191             RSP_EQSERIALNO => 8,
192             RSP_DEFAULTSLOADED => 0,
193             RSP_DEFAULTSSAVED => 0,
194             },
195             PORT_SER => { RSP_EQBAUD => 1,
196             # RSP_FLUSH => 4,
197            
198             },
199             );
200              
201             # Error codes
202             %Slinke::ERRORS = ( RSP_RX_ERROR => [ "Receive Error", 0 ],
203             RSP_TX_TIMEOUT => [ "Transmit Timeout Error - critical, will resume", 1 ],
204             RSP_CMD_ILLEGAL => [ "Illegal Command - critical, will resume", 1 ],
205             RSP_IRFS_ILLEGAL => [ "Illegal Sample Period", 0 ],
206             RSP_SERIALIN_OVERFLOW => [ "Receive overflow - critical, will resume", 1 ],
207             RSP_SERIALIN_OVERRUN => [ "Receive overrun - critical, will resume", 1 ],
208             RSP_SERIALIN_FRAMEERROR => [ "Receive framing error - critical, will resume", 1 ],
209             RSP_BAUD_ILLEGAL => [ "Illegal Baud Rate", 0 ],
210             RSP_SEEPROMWRERR => [ "SEEPROM Write Error", 0 ],
211             );
212              
213             =head2 $slinke = new Slinke( [ DEVICE => $device, SERIALPORT => $serialport ] );
214              
215             Returns a newly created C object.
216              
217             C<$device> is the name of the device that the Slink-e is connected to.
218              
219             If no device is provided, a search is made on the following devices:
220             /dev/ttyS0 /dev/ttyS1 /dev/ttyS2 /dev/ttyS3
221              
222             On windows, COM ports 1-8 are searched
223              
224             If you would rather provide a SerialPort object, you can do that by
225             setting the SERIALPORT argument
226              
227             =cut
228              
229             sub new {
230             my $class = shift;
231             my %args = @_;
232              
233             my $self;
234             $self->{ DEBUG } = 0;
235             $self->{ RETURNONINPUT } = 0;
236             bless ($self, $class);
237              
238             if ( $args{ SERIALPORT } ) {
239             $self->{ SERIALPORT } = $args{ SERIALPORT };
240             }
241             else {
242             my $OS_win = ($^O =~ "MSWin32") ? 1 : 0;
243             my @portsToTry = qw( /dev/ttyS0 /dev/ttyS1 /dev/ttyS2 /dev/ttyS3 );
244             @portsToTry = qw( COM1 COM2 COM3 COM4 COM5 COM6 COM7 COM8 ) if $OS_win;
245            
246             if ( $OS_win ) { require Win32::SerialPort; }
247             else { require Device::SerialPort; }
248            
249             if ( $args{ DEVICE } ) {
250             if ( $OS_win ) { $self->{ SERIALPORT } = new Win32::SerialPort( $args{ DEVICE } ); }
251             else { $self->{ SERIALPORT } = new Device::SerialPort( $args{ DEVICE } ); }
252             }
253             else {
254             foreach my $i ( @portsToTry ) {
255             if ( $OS_win ) { $self->{ SERIALPORT } = new Win32::SerialPort( $i ); }
256             else { $self->{ SERIALPORT } = new Device::SerialPort( $i ); }
257             next if !defined( $self->{ SERIALPORT } );
258              
259             $self->initDevice();
260             $self->loadInternals();
261             my $baud = $self->requestBaud;
262             if ( defined $baud && $baud > 0 ) {
263             $args{ DEVICE } = $i;
264             last;
265             }
266             $self->{ SERIALPORT }->close;
267             }
268             if ( !$args{ DEVICE } ) {
269             die( "Can't find a Slink-e on any of the following: " . join( " ", @portsToTry ) . "\n" );
270             }
271             }
272             }
273              
274             $self->initDevice();
275             $self->loadInternals();
276             my $version = $self->requestFirmwareVersion;
277             if ( !defined $version ) {
278             die "Can't find a Slink-e on $args{ DEVICE }\n";
279             }
280             $self->{ VERSION } = $version;
281              
282             return $self;
283             }
284              
285             sub initDevice {
286             my $this = shift;
287              
288             $this->{ SERIALPORT }->baudrate( 38400 );
289             $this->{ SERIALPORT }->parity( "none" );
290             $this->{ SERIALPORT }->databits( 8 );
291             $this->{ SERIALPORT }->stopbits( 1 );
292             $this->{ SERIALPORT }->handshake( "rts" );
293             }
294              
295             sub loadInternals {
296             my $this = shift;
297              
298             $this->{ BAUD } = $this->requestBaud();
299             $this->setIRSamplingPeriod( 100 / 1e6 );
300             $this->{ IRSAMPLEPERIOD } = $this->requestIRSamplingPeriod();
301             }
302              
303             sub debug {
304             my $this = shift;
305            
306             $this->{ DEBUG } = shift;
307             }
308              
309             sub writeToPort {
310             my $this = shift;
311            
312             my $string;
313             while ( defined( my $s = shift ) ) {
314             my $tmpString = $s;
315             if ( $s =~ /^\d*$/ ) {
316             $tmpString = sprintf( "%02x", $s );
317             $s = chr( $s );
318             }
319             $string .= $s;
320             print $tmpString, " " if $this->{ DEBUG };
321             }
322             print "\n" if $this->{ DEBUG };
323             my $count = $this->{ SERIALPORT }->write( $string );
324             if ( !$count ) {
325             warn "write failed\n";
326             return undef;
327             }
328             elsif ( $count != length($string) ) {
329             warn "write incomplete\n";
330             return undef;
331             }
332              
333             return 1;
334             }
335              
336             sub receive {
337             my $this = shift;
338             my %args = @_;
339             my $timeout = $args{ TIMEOUT } || 60;
340             my $returnOnInput = $args{ RETURNONINPUT } || 0;
341             my $expectInput = $args{ EXPECTINPUT } || 0;
342              
343             $this->{ SERIALPORT }->read_const_time( $timeout );
344             while ( my ( $count, $rch ) = $this->{ SERIALPORT }->read( 1 ) ) {
345             if ( $count != 1 ) {
346             # warn "read of device and datalen unsuccessful\n";
347             return undef if !$expectInput;
348             next;
349             }
350              
351             $rch = ord( $rch );
352            
353             my $device = $rch >> 5;
354             my $datalen = $rch & 0x1F;
355             my $response;
356             my $data;
357              
358             if ( exists $Slinke::INVPORTS{ $device } ) {
359             $device = $Slinke::INVPORTS{ $device };
360             }
361             else {
362             warn "Unknown device: $device\n";
363             return undef;
364             }
365              
366             if ( $datalen != $Slinke::RESPONSES{ RSP_PORT_SM } ) {
367             my $finished = 0;
368             if ( $datalen == 0 ) {
369             if ( $device ne "PORT_IR" ) {
370             my @t;
371             my $str = $this->{ PORTDATA }{ $device };
372             while ( $str ) {
373             my $hex;
374             ( $hex, $str ) = $str =~ /^(..)(.*)/;
375             push @t, hex( $hex );
376             }
377             push @{$this->{ RECEIVED }}, { PORT => $device,
378             DATA => [ @t ],
379             };
380             $this->{ PORTDATA }{ $device } = "";
381             $finished = 1;
382             }
383             elsif ( $this->{ VERSION } < 2.0 ) {
384             my $str = $this->cleanupRLC( substr( $this->{ PORTDATA }{ $device }, 0, -1 ) );
385             my @t = split / /, $str;
386             push @{$this->{ RECEIVED }}, { PORT => $device,
387             DATA => [ @t ],
388             TIME => $this->{ PORTTIME }{ $device },
389             IRPORT => 0x01,
390             };
391              
392             $this->{ PORTTIME }{ $device } = 0;
393             $this->{ PORTDATA }{ $device } = "";
394             $finished = 1;
395             }
396             }
397             else {
398             # we'll be a lot more patient since we know data is coming
399             $this->{ SERIALPORT }->read_const_time( 100 );
400              
401             ( $count, $rch ) = $this->{ SERIALPORT }->read( $datalen );
402             if ( $count != $datalen ) {
403             warn "read of $device unsuccessful - read $count expected $datalen\n";
404             return undef;
405             }
406             if ( $device eq "PORT_IR" ) {
407             if ( !defined($this->{ PORTTIME }{ $device }) ||
408             $this->{ PORTTIME }{ $device } < 1.0 ) {
409             my ( $rlc, $timing ) = $this->int8ToRLC( $rch );
410             $this->{ PORTDATA }{ $device } .= $rlc;
411             $this->{ PORTTIME }{ $device } += $timing;
412             }
413             }
414             else {
415             $rch = unpack( "H*", $rch ) if defined $rch;
416             $this->{ PORTDATA }{ $device } .= $rch;
417             }
418             }
419              
420             next;
421             if ( $finished && $returnOnInput ) {
422             return ( $device, $response, $data )
423             }
424             else {
425             next;
426             }
427             }
428              
429             if ( !exists $Slinke::RESPONSEMAPS{ $device } ) {
430             warn( __PACKAGE__ . " package does not handle device '$device' yet\n");
431             $this->{ SERIALPORT }->input();
432             return undef;
433             }
434            
435             ( $count, $response ) = $this->{ SERIALPORT }->read( 1 );
436             if ( $count != 1 ) {
437             warn "read of $device unsuccessful (datalen = $datalen)\n";
438             return undef;
439             }
440            
441             $response = ord( $response );
442             if ( !exists $Slinke::INVRESPONSES{ $response } ) {
443             warn "Error on device '$device' - Unknown response: $response\n";
444             return undef;
445             }
446            
447             # Since the hex codes can mean different things, we have to
448             # check a list of reponses to get the correct inverse response;
449             my $responseFound = 0;
450             foreach my $i ( @{$Slinke::INVRESPONSES{ $response }} ) {
451             if ( exists $Slinke::RESPONSEMAPS{ $device }{ $i }
452             || exists $Slinke::ERRORS{ $i } ) {
453             $response = $i;
454             $responseFound = 1;
455             last;
456             }
457             }
458              
459             if ( exists $Slinke::ERRORS{ $response } ) {
460             warn "ERROR $response on $device: $Slinke::ERRORS{ $response }->[0]\n";
461             if ( $Slinke::ERRORS{ $response }->[1] ) {
462             $this->resume();
463             }
464             }
465            
466             if ( !$responseFound ) {
467             if ( $#{ $Slinke::INVRESPONSES{ $response }} == 0 ) {
468             $response = @{$Slinke::INVRESPONSES{ $response }}[0];
469             }
470             else {
471             $response = "0x" . uc( sprintf( "%02x", $response ) );
472             }
473            
474             warn( __PACKAGE__ . " package does not handle response '$response' on device '$device' yet\n" );
475             $this->{ SERIALPORT }->input();
476             return undef;
477             }
478            
479             my $bytesToRead = $Slinke::RESPONSEMAPS{ $device }{ $response };
480             if ( $bytesToRead ) {
481             ( $count, $data ) = $this->{ SERIALPORT }->read( $bytesToRead );
482             if ( $count != $bytesToRead ) {
483             warn "Read of $response on $device unsuccessful\n";
484             return undef;
485             }
486            
487             $data = unpack( "H*", $data ) if defined $data;
488             }
489              
490             if ( $device eq "PORT_IR" && $response eq "RSP_EQRXPORT" ) {
491             my $str = $this->cleanupRLC( substr( $this->{ PORTDATA }{ $device }, 0, -1 ) );
492             my @t = split / /, $str;
493             my $irport = 1 << $data;
494             push @{$this->{ RECEIVED }}, { PORT => $device,
495             DATA => [ @t ],
496             TIME => $this->{ PORTTIME }{ $device },
497             IRPORT => $irport,
498             };
499              
500             $this->{ PORTDATA }{ $device } = "";
501             $this->{ PORTTIME }{ $device } = 0;
502              
503             next if !$returnOnInput;
504             }
505            
506             return ( $device, $response, $data );
507             }
508             }
509              
510             sub cleanupRLC {
511             my $this = shift;
512             my @data = split / /, shift;
513              
514             # let's make sure that we alternate even and odd numbers
515             my @newdata;
516             push @newdata, shift @data;
517             while ( defined( my $i = shift @data ) ) {
518             if ( ( $i > 0 && $newdata[$#newdata] > 0 )
519             || ( $i < 0 && $newdata[$#newdata] < 0 ) ) {
520             $newdata[$#newdata] += $i;
521             }
522             else {
523             push @newdata, $i;
524             }
525             }
526            
527             return join( " ", @newdata );
528             }
529              
530             sub int8ToRLC {
531             my $this = shift;
532             my $data = shift;
533              
534             my $oldsign = 33; # don't use 0x00 or 0x80
535            
536             my $numtime = 0;
537             my $num = 0;
538             my $signallen = 0;
539             my $sign = 1;
540             my $numstr;
541            
542             foreach my $i ( split / */, $data ) {
543             $i = ord( $i );
544             $sign = $i & 0x80;
545             $i &= 0x7f;
546              
547             if ($sign != $oldsign) {
548             # signal change
549             if ($oldsign != 33) {
550             # write out num first
551             $num = -$num if $sign == 0x80; # use sign to indicate 0 periods
552            
553             $numtime = $num * $this->{ IRSAMPLEPERIOD } + $Slinke::IRSKEWADJUST;
554             $numstr .= sprintf( "%.1lf ", $numtime * 1e6 ); # convert to microseconds
555             $signallen += abs($numtime);
556             }
557            
558             $oldsign = $sign;
559             $num = $i;
560             }
561             else {
562             # same signal
563             $num += $i;
564             }
565             }
566            
567             # write out the last one
568             $num = -$num if !$sign; # use sign to indicate 0 periods;
569            
570             $numtime = $num * $this->{ IRSAMPLEPERIOD } + $Slinke::IRSKEWADJUST;
571             $numstr .= sprintf( "%.1lf ", $numtime * 1e6 ); # convert to microseconds
572             $signallen += abs($numtime);
573            
574             return ($numstr, $signallen);
575             }
576              
577             sub rlcToInt8 {
578             my $this = shift;
579             my $data = shift;
580            
581             my $outsum = 0.0;
582             my $truesum = 0;
583             my @bin;
584            
585             foreach my $i ( @$data ) {
586             my $sign = $i < 0 ? 0 : 0x80;
587             $i = abs( $i );
588              
589             $truesum += $i;
590             # convert microseconds into the current IR sampling period of the Slink-e
591             $i = int(($truesum-$outsum) / $this->{ IRSAMPLEPERIOD } / 1e6 + 0.5);
592             $outsum += $i * $this->{ IRSAMPLEPERIOD } * 1e6;
593              
594             # break into smaller segments if necessary
595             while ($i > 0) {
596             my $binnum = $i < 127 ? $i : 127;
597             $i -= $binnum;
598             push @bin, ( $binnum + $sign );
599             }
600             }
601            
602             return @bin;
603             }
604              
605             =head2 $slinke->requestInput();
606              
607             This function returns any input from the S-Link ports, the IR ports or the Parallel port
608              
609             The returned element is a hash reference.
610              
611             C is always set, and it will contain the port that returned the data
612              
613             C is a reference to an array of values.
614              
615             C
616             of time that was needed to produce the IR signal
617              
618             C is set for data coming from the IR port. It tells which IR receiver (1-8) the
619             data was received on. Note that you must have a Slink-e of version 2.0 or higher for
620             IRPORT to be greater than 0
621              
622             =cut
623              
624             sub requestInput {
625             my $this = shift;
626              
627             if ( $#{$this->{ RECEIVED }} < 0 ) {
628             $this->receive( RETURNONINPUT => 1 );
629             }
630            
631             return shift @{$this->{ RECEIVED }};
632             }
633              
634             =head2 $slinke->requestSerialNumber()
635              
636             This returns the 8 byte serial number of the Slink-e.
637              
638             =cut
639              
640             sub requestSerialNumber {
641             my $this = shift;
642              
643             return ($this->txrx( COMMAND => "CMD_GETSERIALNO" ))[2];
644             }
645              
646             =head2 $slinke->requestBaud()
647              
648             This returns the baud rate in bps of the Slink-e.
649              
650             =cut
651              
652             sub requestBaud {
653             my $this = shift;
654              
655             $this->resume();
656             my $data = ($this->txrx( COMMAND => "CMD_GETBAUD" ))[2];
657            
658             if ( defined $data ) {
659             $data = hex( $data );
660             $data = 2400 * (1 << $data);
661             return $data;
662             }
663             $this->{ SERIALPORT }->input;
664              
665             for ( my $i=4; $i>=0; $i-- ) {
666             $this->{ SERIALPORT }->baudrate( 2400 * (1 << $i) );
667              
668             $this->resume();
669             my $data = ($this->txrx( COMMAND => "CMD_GETBAUD" ))[2];
670            
671             if ( defined $data ) {
672             $data = hex( $data );
673             $data = 2400 * (1 << $data);
674             return $data;
675             }
676             else {
677             $this->{ SERIALPORT }->input;
678             }
679             }
680              
681             return undef;
682             }
683              
684             =head2 $slinke->setBaud()
685              
686             This sets the baud rate in bps of the Slink-e.
687              
688             =cut
689              
690             sub setBaud {
691             my $this = shift;
692             my $baud = shift;
693              
694             my $bn;
695              
696             if ( $baud == 2400 ) { $bn = 0; }
697             elsif ( $baud == 4800 ) { $bn = 1; }
698             elsif ( $baud == 9600 ) { $bn = 2; }
699             elsif ( $baud == 19200 ) { $bn = 3; }
700             elsif ( $baud == 38400 ) { $bn = 4; }
701             else {
702             warn( "$baud is an invalid baud rate.\n" );
703             return undef;
704             }
705              
706             my $data = ($this->txrx( COMMAND => "CMD_SETBAUD",
707             ARGS => [ $bn ],
708             ))[2];
709             $this->{ SERIALPORT }->input();
710              
711             if ( defined $data ) {
712             $data = hex( $data );
713             $data = 2400 * (1 << $data);
714             $this->{ BAUD } = $data;
715             $this->{ SERIALPORT }->baudrate( $data );
716             $this->resume;
717             }
718             return $data;
719             }
720              
721             =head2 $slinke->requestFirmwareVersion()
722              
723             This returns the firmware version of the Slink-e
724              
725             =cut
726              
727             sub requestFirmwareVersion {
728             my $this = shift;
729              
730             my $data = ($this->txrx( COMMAND => "CMD_GETVERSION" ))[2];
731              
732             if ( defined $data ) {
733             $data = hex( $data );
734             $data = ( $data >> 4) + 0.1 * ($data & 0xF);
735             }
736             return $data;
737             }
738              
739             =head2 $slinke->enablePort( $port )
740              
741             Enables reception on specified port. If port == C I,
742             instead each port is returned to its enabled/disabled state previous to the global
743             disablement.
744              
745             =cut
746              
747             sub enablePort {
748             my $this = shift;
749             my $port = shift;
750              
751             if ( !defined $Slinke::PORTS{ $port } ) {
752             warn "Unrecognized port: $port\n";
753             return undef;
754             }
755            
756             if ( $port eq "PORT_SER" ) {
757             warn "Can't enable port '$port'\n";
758             return undef;
759             }
760              
761             my ( $device, $response ) = $this->txrx( COMMAND => "CMD_ENABLE",
762             PORT => $port,
763             );
764              
765             if ( $device eq $port ) {
766             return 1;
767             }
768             else {
769             warn "Error trying to enable $port\n";
770             return undef;
771             }
772             }
773              
774             =head2 $slinke->disablePort( $port )
775              
776             Disables reception on specified port. If port == C, all ports are
777             disabled. Disabling a port does not prevent the host from sending messages out
778             the port, only receiving them.
779              
780             =cut
781              
782             sub disablePort {
783             my $this = shift;
784             my $port = shift;
785              
786             if ( !defined $Slinke::PORTS{ $port } ) {
787             warn "Unrecognized port: $port\n";
788             return undef;
789             }
790            
791             if ( $port eq "PORT_SER" ) {
792             warn "Can't disable port '$port'\n";
793             return undef;
794             }
795              
796             my ( $device, $response ) = $this->txrx( COMMAND => "CMD_DISABLE",
797             PORT => $port,
798             );
799              
800             if ( $device eq $port ) {
801             return 1;
802             }
803             else {
804             warn "Error trying to disable $port\n";
805             return undef;
806             }
807             }
808              
809             =head2 $slinke->requestIRSamplingPeriod()
810              
811             This returns the infrared sampling period of the Slink-e. Values can
812             range from 50 microseconds to 1 millisecond.
813              
814             The IR sampling period determines the maximum timing resolution which
815             can be achieved when decoding IR signals. In general, the sampling
816             period should be at least 3 times shorter than the shortest pulse you
817             wish to detect. Short sampling periods are necessary when acquiring
818             timing information about new remote signals, but are not necessarily
819             need to output known remote signals since the sampling period need only
820             be the least common multiple of the pulse widths in the signal.
821              
822             The IR sampling period is also used as a timebase for parallel port
823             output signals.
824              
825             =cut
826              
827             sub requestIRSamplingPeriod {
828             my $this = shift;
829             my $data = ($this->txrx( COMMAND => "CMD_GETIRFS" ))[2];
830              
831             if ( defined $data ) {
832             my ( $d1, $d2 ) = $data =~ /(..)(..)/;
833             $d1 = hex( $d1 );
834             $d2 = hex( $d2 );
835             $data = ($d1*256.0+$d2)/($Slinke::SLINKE_CLK / 4.0);
836             }
837              
838             return $data;
839             }
840              
841             =head2 $slinke->setIRSamplingPeriod( $time )
842              
843             This sets the infrared sampling period of the Slink-e. Values can
844             range from 50 microseconds to 1 millisecond in 1/5 microsecond
845             steps. Upon success, this function will return the sampling period.
846             On value, it will return undef.
847              
848             The IR sampling period determines the maximum timing resolution which
849             can be achieved when decoding IR signals. In general, the sampling
850             period should be at least 3 times shorter than the shortest pulse you
851             wish to detect. Short sampling periods are necessary when acquiring
852             timing information about new remote signals, but are not necessarily
853             need to output known remote signals since the sampling period need only
854             be the least common multiple of the pulse widths in the signal.
855              
856             The IR sampling period is also used as a timebase for parallel port
857             output signals.
858              
859             =cut
860              
861             sub setIRSamplingPeriod {
862             my $this = shift;
863             my $sampleRate = shift;
864            
865             my $baud = $this->{ BAUD };
866            
867             my $minper = 1.0 / $baud;
868             $minper = 49.0e-6 if 49.0e-6 > $minper;
869              
870             if ($sampleRate < $minper) {
871             $sampleRate *= 1e6;
872             $minper *= 1e6;
873              
874             warn "$sampleRate is too short of a sampling period ($minper is the shortest at this baud rate)\n";
875             return undef;
876             }
877            
878             my $maxper = 1e-3;
879             if ($sampleRate > $maxper) {
880             $sampleRate *= 1e6;
881             $maxper *= 1e6;
882             warn "$sampleRate is too long of a sampling period ($maxper is the longest)\n";
883             return undef;
884             }
885            
886             my $count = $Slinke::SLINKE_CLK/4*$sampleRate + 0.5;
887            
888             my $d1 = $count >> 8;
889             my $d2 = $count % 256;
890              
891             my $data = ($this->txrx( COMMAND => "CMD_SETIRFS",
892             ARGS => [ $d1, $d2 ],
893             ))[2];
894              
895             if ( defined $data ) {
896             my ( $d1, $d2 ) = $data =~ /(..)(..)/;
897             $d1 = hex( $d1 );
898             $d2 = hex( $d2 );
899             $data = ($d1*256.0+$d2)/($Slinke::SLINKE_CLK / 4.0);
900              
901             if ( $data != $sampleRate ) {
902             warn "Tried setting samplerate of $sampleRate - $data set\n";
903             return undef;
904             }
905             else {
906             $this->{ IRSAMPLEPERIOD } = $data;
907             }
908             }
909             return $data;
910             }
911              
912             =head2 $slinke->requestIRCarrier()
913              
914             This returns the IR carrier frequency of the Slink-e.
915              
916             =cut
917              
918             sub requestIRCarrier {
919             my $this = shift;
920             my $data = ($this->txrx( COMMAND => "CMD_GETIRCFS" ))[2];
921              
922             if ( defined $data ) {
923             my ( $d1, $d2 ) = $data =~ /(..)(..)/;
924              
925             $d1 = hex( $d1 );
926             $d2 = hex( $d2 );
927              
928             if ( $d1 == 0 && $d2 == 0) {
929             $data = 0;
930             }
931             else {
932             $data = ($Slinke::SLINKE_CLK / 4)/((1 << $d1)*($d2+1));
933             }
934             }
935            
936             return $data;
937             }
938              
939             =head2 $slinke->setIRCarrier( $frequency )
940              
941             This sets the IR carrier frequency of the Slink-e. Note that because
942             of the way that the frequency gets set, it will be very unlikely that
943             you will be able to set the exact frequency that you want. However,
944             the Slink-e should be able to handle your frequency within several
945             hundred hertz.
946              
947             Upon success, the frequency that the Slink-e is using will be returned.
948              
949             Upon failure, C is returned.
950              
951             =cut
952              
953             sub setIRCarrier {
954             my $this = shift;
955             my $frequency = shift;
956              
957             my $d1 = 0;
958             my $d2 = 0;
959              
960             if ($frequency != 0.0) {
961             my $count = $Slinke::SLINKE_CLK/4.0/$frequency;
962             if ($count == 0) {
963             my $max = $Slinke::SLINKE_CLK/4.0;
964             warn "$frequency is too high of a carrier frequency ($max is the max)\n";
965             return undef;
966             }
967             elsif ($count > 8*256) {
968             my $min = $Slinke::SLINKE_CLK/4.0/8.0/256.0;
969             warn "$frequency is too low of a carrier frequency ($min is the minimum)\n";
970             return undef;
971             }
972              
973             if ($count < 256) {
974             $d1 = 0;
975             }
976             else {
977             $d1 = POSIX::ceil(log(($count >> 8))/log(2.0));
978             }
979            
980             $d2 = int( $count / (1 << $d1) );
981             $d2--;
982             }
983              
984             my $data = ($this->txrx( COMMAND => "CMD_SETIRCFS",
985             ARGS => [ $d1, $d2 ],
986             ))[2];
987              
988             if ( defined $data ) {
989             my ( $t1, $t2 ) = $data =~ /(..)(..)/;
990             $t1 = hex( $t1 );
991             $t2 = hex( $t2 );
992              
993             if ( $t1 == 0 && $t2 == 0) {
994             $data = 0;
995             }
996             else {
997             $data = ($Slinke::SLINKE_CLK / 4)/((1 << $t1)*($t2+1));
998             }
999              
1000             if ( $d1 != $t1 || $d2 != $t2 ) {
1001             warn "Tried setting frequency of $frequency - $data set\n";
1002             return undef;
1003             }
1004              
1005             }
1006             return $data;
1007              
1008             }
1009              
1010             =head2 $slinke->requestIRTimeoutPeriod()
1011              
1012             This returns the IR timeout period of the Slink-e as measured in sample
1013             periods. The timeout period defines how ling the IR receiver module must
1014             be inactive for the Slink-e to consider a message to be completed.
1015              
1016             =cut
1017              
1018             sub requestIRTimeoutPeriod {
1019             my $this = shift;
1020             my $data = ($this->txrx( COMMAND => "CMD_GETIRTIMEOUT" ))[2];
1021              
1022             if ( defined $data ) {
1023             my ( $d1, $d2 ) = $data =~ /(..)(..)/;
1024              
1025             $d1 = hex( $d1 );
1026             $d2 = hex( $d2 );
1027              
1028             $data = $d1*256 + $d2;
1029             }
1030              
1031             return $data;
1032             }
1033              
1034             =head2 $slinke->setIRTimeoutPeriod( $sample_periods )
1035              
1036             This returns the IR timeout period of the Slink-e as measured in sample
1037             periods. The timeout period defines how ling the IR receiver module must
1038             be inactive for the Slink-e to consider a message to be completed.
1039             Most IR remotes repeat commands many times for one keypress. If you want
1040             to see each command as a separate message, set the timeout period to be
1041             less than the off time between commands. If you to see the keypress as
1042             one long message with repeated commands, set the timeout period to be
1043             longer than the off time between commands. The latter mode is particularly
1044             useful for initially determining the timing information for a new remote.
1045              
1046             On success, the new value of the timeout period will be returned.
1047              
1048             On failure, C is returned.
1049              
1050             =cut
1051              
1052             sub setIRTimeoutPeriod {
1053             my $this = shift;
1054             my $period = int( shift );
1055            
1056             if ($period == 0) {
1057             warn "$period sample periods is too short of a timeout period.\n";
1058             return undef;
1059             }
1060              
1061             if ($period > 65536) {
1062             warn "$period sample periods is too long of a timeout period (65536 periods is the longest)\n";
1063             return undef;
1064             }
1065            
1066             my $d1 = $period >> 8;
1067             my $d2 = $period % 256;
1068            
1069             my $data = ($this->txrx( COMMAND => "CMD_SETIRTIMEOUT",
1070             ARGS => [ $d1, $d2 ],
1071             ))[2];
1072              
1073             if ( defined $data ) {
1074             my ( $d1, $d2 ) = $data =~ /(..)(..)/;
1075              
1076             $d1 = hex( $d1 );
1077             $d2 = hex( $d2 );
1078              
1079             $data = $d1*256 + $d2;
1080             if ( $data != $period ) {
1081             warn "Tried setting timeout period of $period - $data set\n";
1082             return undef;
1083             }
1084             }
1085            
1086             return $data;
1087             }
1088              
1089             =head2 $slinke->requestIRMinimumLength()
1090              
1091             This returns the length of the shortest IR receive message in bytes which
1092             will be considered a valid message.
1093              
1094             =cut
1095              
1096             sub requestIRMinimumLength {
1097             my $this = shift;
1098             my $data = ($this->txrx( COMMAND => "CMD_GETIRMINLEN" ))[2];
1099              
1100             $data = hex( $data ) if defined $data;
1101              
1102             return $data;
1103             }
1104              
1105             =head2 $slinke->setIRMinimumLength( $bytes )
1106              
1107             This set the length of the shortest IR receive message in bytes which
1108             will be considered a valid message. IR receiver modules such as the one
1109             on the Slink-e tend to be very sensitive to both optical and electrical
1110             noise, causing them to occasionally generate false pulses when there is
1111             no actual IR signal. The false pulses are generally of short duration
1112             and do not contain the large number of on/off alternations present in a
1113             true IR remote signal. By setting a minimum message length, false pulses
1114             will be ignored and not reported to the host. The minimum length can
1115             range from 0 to 15 bytes.
1116              
1117             Upon success, the new minimum message length is returned.
1118              
1119             Upon failure, C is returned.
1120              
1121             =cut
1122              
1123             sub setIRMinimumLength {
1124             my $this = shift;
1125             my $length = shift;
1126            
1127             if ($length < 0) {
1128             warn "$length is too short of a minimum message length (0 is the shortest)\n";
1129             return undef;
1130             }
1131              
1132             if ($length > $Slinke::PORT_IR_MAXML) {
1133             warn "$length is too long of a minimum message length ($Slinke::PORT_IR_MAXML is the longest)\n";
1134             return undef;
1135             }
1136              
1137             my $data = ($this->txrx( COMMAND => "CMD_SETIRMINLEN",
1138             ARGS => [ $length ],
1139             ))[2];
1140              
1141             $data = hex( $data ) if defined $data;
1142              
1143             if ( $data != $length ) {
1144             warn "Tried setting IR minimum message length of $length - $data set\n";
1145             return undef;
1146             }
1147              
1148             return $data;
1149             }
1150              
1151             =head2 $slinke->requestIRTransmitPorts()
1152              
1153             This returns the value of the ports that the Slink-e uses for IR transmissions.
1154             The bits represent the 8 IR ports, IR0 being the LSB, IR7 the MSB. A "1" indicates
1155             the port will be used.
1156              
1157             I
1158              
1159             =cut
1160              
1161             sub requestIRTransmitPorts {
1162             my $this = shift;
1163              
1164             if ($this->{ VERSION } < 2.0 ) {
1165             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1166             return undef;
1167             }
1168              
1169             return hex(($this->txrx( COMMAND => "CMD_GETIRTXPORTS" ))[2]);
1170             }
1171              
1172             =head2 $slinke->setIRTransmitPorts( $ports )
1173              
1174             This sets the ports that the Slink-e uses for IR transmissions. The bits represent
1175             the 8 IR ports, IR0 being the LSB, IR7 the MSB. A "1" indicates the port will be used.
1176              
1177             I
1178              
1179             =cut
1180              
1181             sub setIRTransmitPorts {
1182             my $this = shift;
1183             my $port = shift;
1184              
1185             if ($this->{ VERSION } < 2.0 ) {
1186             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1187             return undef;
1188             }
1189              
1190             if ($port < 0 || $port > 255) {
1191             warn "$port is not a valid port (0-0xFF is the acceptable range)\n";
1192             return undef;
1193             }
1194              
1195             $this->txrx( COMMAND => "CMD_SETIRTXPORTS",
1196             ARGS => [ $port ],
1197             );
1198              
1199             }
1200              
1201             =head2 $slinke->requestIRPolarity()
1202              
1203             Reports the polarity sense of each of the IR ports. These settings will also
1204             affect the IR routing system. The bits of the response represent the 8 IR ports,
1205             IR0 being the LSB, IR7 the MSB. A "1" bit indicates that the input is active-low
1206             (when the input goes to 0 Volts), a "0" bit indicates that the input is
1207             active-high (when the input goes to 5 Volts). All ports are active-low by default
1208             so that they will work correctly with the IR receiver modules. If you have some
1209             other low-speed serial device which is active-high (e.g. a Control-S input) that
1210             you would like to connect, you will want to change the polarity on that port.
1211             Be careful with this option - if you set the wrong polarity for a port, the
1212             Slink-e will be locked into a constant receive state and will become unresponsive.
1213              
1214             I
1215              
1216             =cut
1217              
1218             sub requestIRPolarity {
1219             my $this = shift;
1220              
1221             if ($this->{ VERSION } < 2.0 ) {
1222             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1223             return undef;
1224             }
1225              
1226             return hex(($this->txrx( COMMAND => "CMD_GETIRRXPORTPOL" ))[2]);
1227             }
1228              
1229             =head2 $slinke->setIRPolarity( $ports )
1230              
1231             Sets the polarity sense of each of the IR ports. These settings will also affect
1232             the IR routing system. The bits of $ports represent the 8 IR ports, IR0 being the LSB,
1233             IR7 the MSB. A "1" bit indicates that the input is active-low (when the input goes
1234             to 0 Volts), a "0" bit indicates that the input is active-high (when the input
1235             goes to 5 Volts). All ports are active-low by default so that they will work
1236             correctly with the IR receiver modules. If you have some other low-speed serial
1237             device which is active-high (e.g. a Control-S input) that you would like to connect,
1238             you will want to change the polarity on that port. Be careful with this option - if
1239             you set the wrong polarity for a port, the Slink-e will be locked into a constant
1240             receive state and will become unresponsive.
1241              
1242             I
1243              
1244             =cut
1245              
1246             sub setIRPolarity {
1247             my $this = shift;
1248             my $port = shift;
1249              
1250             if ($this->{ VERSION } < 2.0 ) {
1251             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1252             return undef;
1253             }
1254              
1255             if ($port < 0 || $port > 255) {
1256             warn "$port is not a valid port (0-0xFF is the acceptable range)\n";
1257             return undef;
1258             }
1259              
1260             my $data = hex(( $this->txrx( COMMAND => "CMD_SETIRRXPORTEN",
1261             ARGS => [ $port ],
1262             ))[2]);
1263              
1264             if ( $data != $port ) {
1265             my $p = "0x" . uc(sprintf( "%02x", $port ) );
1266             my $d = "0x" . uc(sprintf( "%02x", $data ) );
1267             warn "Tried setting IR receive polarity of $p - $d set\n";
1268             return undef;
1269             }
1270            
1271             return $data;
1272             }
1273              
1274             =head2 $slinke->requestIRReceivePorts()
1275              
1276             This returns the value of the ports that the Slink-e uses for IR reception.
1277             The bits represent the 8 IR ports, IR0 being the LSB, IR7 the MSB. A "1" indicates
1278             the port will be used.
1279              
1280             I
1281              
1282             =cut
1283              
1284             sub requestIRReceivePorts {
1285             my $this = shift;
1286              
1287             if ($this->{ VERSION } < 2.0 ) {
1288             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1289             return undef;
1290             }
1291              
1292             return hex(($this->txrx( COMMAND => "CMD_GETIRRXPORTEN" ))[2]);
1293             }
1294              
1295             =head2 $slinke->setIRReceivePorts( $ports )
1296              
1297             This sets the ports that the Slink-e uses for IR reception. The bits represent
1298             the 8 IR ports, IR0 being the LSB, IR7 the MSB. A "1" indicates the port will be used.
1299              
1300             I
1301              
1302             Upon success, this returns the ports that the Slink-e is using for IR reception.
1303              
1304             Upon failure, C is returned.
1305              
1306             =cut
1307              
1308             sub setIRReceivePorts {
1309             my $this = shift;
1310             my $port = shift;
1311            
1312             if ($this->{ VERSION } < 2.0 ) {
1313             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1314             return undef;
1315             }
1316              
1317             if ($port < 0 || $port > 255) {
1318             warn "$port is not a valid port (0-0xFF is the acceptable range)\n";
1319             return undef;
1320             }
1321              
1322             my $data = hex(( $this->txrx( COMMAND => "CMD_SETIRRXPORTEN",
1323             ARGS => [ $port ],
1324             ))[2]);
1325              
1326             if ( $data != $port ) {
1327             my $p = "0x" . uc(sprintf( "%02x", $port ) );
1328             my $d = "0x" . uc(sprintf( "%02x", $data ) );
1329             warn "Tried setting IR receive port of $p - $d set\n";
1330             return undef;
1331             }
1332            
1333             return $data;
1334             }
1335              
1336             =head2 $slinke->requestIRRoutingTable()
1337              
1338             This response describes the IR routing table. The routelist byte for each
1339             IRRX port specifies which IRTX ports the received signal will be echoed to.
1340             The format for this byte is the same as the Set IR transmit ports command.
1341             The carrier byte specifes the carrier frequency to be used in the routed
1342             signals from a given IRRX port. This byte is equivalent to the CC byte in
1343             the Set IR carrier command. To reduce data storage requirements, no
1344             prescaler value can be specified and the prescaler is defaulted to 0
1345             instead. This means that 15.7khz is the lowest available carrier frequency
1346             for IR routing.
1347              
1348             I
1349              
1350             =cut
1351              
1352             sub requestIRRoutingTable {
1353             my $this = shift;
1354            
1355             if ($this->{ VERSION } < 2.0 ) {
1356             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1357             return undef;
1358             }
1359              
1360             my $data = ($this->txrx( COMMAND => "CMD_GETIRPORTECHO" ))[2];
1361              
1362             my @data;
1363              
1364             while ( $data ) {
1365             my $i;
1366             ( $i, $data ) = $data =~ /(..)(.*)/;
1367              
1368             push @data, hex( $i );
1369              
1370             ( $i, $data ) = $data =~ /(..)(.*)/;
1371             push @data, ( $Slinke::SLINKE_CLK / 4) / ( hex( $i ) + 1 );
1372             }
1373            
1374             return @data;
1375             }
1376              
1377             =head2 $slinke->setIRRoutingTable( @data )
1378              
1379             This command sets up the IR routing table. The routelist byte for each
1380             IRRX port specifies which IRTX ports the received signal will be echoed to.
1381             The format for this byte is the same as the Set IR transmit ports command.
1382             The carrier byte specifes the carrier frequency to be used in the routed
1383             signals from a given IRRX port. This byte is equivalent to the CC byte
1384             in the Set IR carrier command. To reduce data storage requirements, no
1385             prescaler value can be specified and the prescaler is defaulted to 0
1386             instead. This means that 15.7khz is the lowest available carrier frequency
1387             for IR routing.
1388              
1389             I
1390              
1391             =cut
1392              
1393             sub setIRRoutingTable {
1394             my $this = shift;
1395             my @data = @_;
1396            
1397             if ($this->{ VERSION } < 2.0 ) {
1398             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1399             return undef;
1400             }
1401              
1402             for ( my $i=1; $i <= $#data; $i+=2 ) {
1403             my $freq = $data[$i];
1404              
1405             if ( $freq ) {
1406             my $count = int( $Slinke::SLINKE_CLK/4.0/$freq );
1407             if ( !$count ) {
1408             my $max = $Slinke::SLINKE_CLK/4.0;
1409             warn "$freq is too high of a carrier frequency ($max is the max)\n";
1410             return undef;
1411             }
1412             elsif ($count >= 256) {
1413             my $min = $Slinke::SLINKE_CLK/4.0/256.0;
1414             warn "$freq is too low of a carrier frequency ($min is the minimum)\n";
1415             return undef;
1416             }
1417              
1418             $data[$i] = $count - 1;
1419             }
1420             else {
1421             $data[$i] = 0; # indicate no carrier
1422             }
1423             }
1424            
1425             my $data = ($this->txrx( COMMAND => "CMD_SETIRPORTECHO",
1426             ARGS => [ @data ] ))[2];
1427              
1428             my @tmpdata;
1429             my @newdata;
1430             while ( $data ) {
1431             my $i;
1432             ( $i, $data ) = $data =~ /(..)(.*)/;
1433              
1434             push @newdata, hex( $i );
1435             push @tmpdata, hex( $i );
1436              
1437             ( $i, $data ) = $data =~ /(..)(.*)/;
1438             push @tmpdata, hex( $i );
1439             push @newdata, ( $Slinke::SLINKE_CLK / 4) / ( hex( $i ) + 1 );
1440             }
1441              
1442             for ( my $i=0; $i<=$#data; $i++ ) {
1443             if ( $data[$i] != $tmpdata[$i] ) {
1444             warn "Did not get proper return value\n";
1445             return undef;
1446             }
1447             }
1448            
1449             return @newdata;
1450             }
1451              
1452             =head2 $slinke->requestHandshaking()
1453              
1454             Reports the input and output handshaking mode for the Parallel Port.
1455              
1456             As a binary number, the output looks as follows: C<[0 0 0 0 0 0 in out]>
1457              
1458             Only the bits in and out are used.
1459              
1460             in = 0 : Disable Input Handshaking (default at startup). Disable the use
1461             of DISTB/DIO7 as a handshaking pin, in turn freeing it for general I/O use.
1462             When input handshaking is enabled, rising edges on the DISTB input cause
1463             the Parallel Port data to be sampled and sent to the host in the form of a port
1464             receive message. When disabled, sampling of the Parallel Port only occurs when a
1465             sample port message is issued.
1466              
1467             in = 1 : Enable Input Handshaking Enables the use of DISTB/DIO7 as a handshaking
1468             pin, in turn removing it from general I/O use. When input handshaking is enabled,
1469             rising edges on the DISTB input cause the Parallel Port data to be sampled and
1470             sent to the host in the form of a port receive message. When disabled, sampling
1471             of the Parallel Port only occurs when a sample port message is issued.
1472              
1473             out= 0 : Disable Output Handshaking (default at startup). Disable the use of
1474             DOSTB/DIO6 as a handshaking pin, in turn freeing it for general I/O use. When
1475             output handshaking is enabled, each data byte sent out the Parallel Port using
1476             the port send command will be accompanied by a positive DOSTB pulse lasting one
1477             IR sampling period.
1478              
1479             out = 1 : Enable Output Handshaking Enable the use of DOSTB/DIO6 as a handshaking
1480             pin, in turn removing it from general I/O use. When output handshaking is enabled,
1481             each data byte sent out the Parallel Port using the port send command will be
1482             accompanied by a positive DOSTB pulse lasting one IR sampling period.
1483              
1484             =cut
1485              
1486             sub requestHandshaking {
1487             my $this = shift;
1488              
1489             return hex(($this->txrx( COMMAND => "CMD_GETHSMODE" ))[2]);
1490             }
1491              
1492             =head2 $slinke->setHandshaking( $handshaking )
1493              
1494             Sets the input and output handshaking mode for the Parallel Port.
1495              
1496             As a binary number, C<$handshaking> looks as follows: C<[0 0 0 0 0 0 in out]>
1497              
1498             Only the bits in and out are used.
1499              
1500             in = 0 : Disable Input Handshaking (default at startup). Disable the use of
1501             DISTB/DIO7 as a handshaking pin, in turn freeing it for general I/O use. When
1502             input handshaking is enabled, rising edges on the DISTB input cause the Parallel
1503             Port data to be sampled and sent to the host in the form of a port receive message.
1504             When disabled, sampling of the Parallel Port only occurs when a sample port message
1505             is issued.
1506              
1507             in = 1 : Enable Input Handshaking Enables the use of DISTB/DIO7 as a handshaking
1508             pin, in turn removing it from general I/O use. When input handshaking is enabled,
1509             rising edges on the DISTB input cause the Parallel Port data to be sampled and sent
1510             to the host in the form of a port receive message. When disabled, sampling of the
1511             Parallel Port only occurs when a sample port message is issued.
1512              
1513             out= 0 : Disable Output Handshaking (default at startup). Disable the use of
1514             DOSTB/DIO6 as a handshaking pin, in turn freeing it for general I/O use. When
1515             output handshaking is enabled, each data byte sent out the Parallel Port using
1516             the port send command will be accompanied by a positive DOSTB pulse lasting one
1517             IR sampling period.
1518              
1519             out = 1 : Enable Output Handshaking Enable the use of DOSTB/DIO6 as a handshaking
1520             pin, in turn removing it from general I/O use. When output handshaking is enabled,
1521             each data byte sent out the Parallel Port using the port send command will be
1522             accompanied by a positive DOSTB pulse lasting one IR sampling period.
1523              
1524             Upon success, the new handshaking setting is returned
1525              
1526             Upon failure, C is returned.
1527              
1528             =cut
1529              
1530             sub setHandshaking {
1531             my $this = shift;
1532             my $handshaking = shift;
1533            
1534             if ($handshaking < 0 || $handshaking > 3) {
1535             warn "$handshaking is not a valid port (0-0x03 is the acceptable range)\n";
1536             return undef;
1537             }
1538              
1539             my $data = hex(( $this->txrx( COMMAND => "CMD_SETHSMODE",
1540             ARGS => [ $handshaking ],
1541             ))[2]);
1542              
1543             if ( $data != $handshaking ) {
1544             my $p = "0x" . uc(sprintf( "%02x", $handshaking ) );
1545             my $d = "0x" . uc(sprintf( "%02x", $data ) );
1546             warn "Tried setting handshaking mode of $p - $d set\n";
1547             return undef;
1548             }
1549            
1550             return $data;
1551             }
1552              
1553             =head2 $slinke->requestDirection()
1554              
1555             Reports which parallel port lines are inputs or outputs. The bits d7:d0 in
1556             the output correspond 1 to 1 with the Parallel Port I/O lines DIO7:DIO0.
1557             Setting a direction bit to 1 assigns the corresponding DIO line as an
1558             input, while setting it to 0 make it an output. At startup, all DIO
1559             lines are configured as inputs. The use of handshaking on lines DISTB/DIO7
1560             and DOSTB/DIO6 overrides the direction configuration for these lines while
1561             enabled.
1562              
1563             =cut
1564              
1565             sub requestDirection {
1566             my $this = shift;
1567              
1568             return hex(($this->txrx( COMMAND => "CMD_GETDIR" ))[2]);
1569             }
1570              
1571             =head2 $slinke->setDirection( $direction )
1572              
1573             Configures the parallel port lines as inputs or outputs. The bits d7:d0
1574             in the direction byte correspond 1 to 1 with the Parallel Port I/O lines
1575             DIO7:DIO0. Setting a direction bit to 1 assigns the corresponding DIO line
1576             as an input, while setting it to 0 make it an output. At startup, all DIO
1577             lines are configured as inputs. The use of handshaking on lines DISTB/DIO7
1578             and DOSTB/DIO6 overrides the direction configuration for these lines while
1579             enabled. Slink-e will return a configuration direction equals response to
1580             verify your command.
1581              
1582             =cut
1583              
1584             sub setDirection {
1585             my $this = shift;
1586             my $direction = shift;
1587            
1588             if ($direction < 0 || $direction > 255) {
1589             warn "$direction is not a valid direction setting (0-0xFF is the acceptable range)\n";
1590             return undef;
1591             }
1592              
1593             my $data = hex(( $this->txrx( COMMAND => "CMD_SETDIR",
1594             ARGS => [ $direction ],
1595             ))[2]);
1596              
1597             if ( $data != $direction ) {
1598             my $p = "0x" . uc(sprintf( "%02x", $direction ) );
1599             my $d = "0x" . uc(sprintf( "%02x", $data ) );
1600             warn "Tried setting parallel direction configuration of $p - $d set\n";
1601             return undef;
1602             }
1603            
1604             return $data;
1605             }
1606              
1607             =head2 $slinke->sampleParPort()
1608              
1609             Causes the Slink-e to sample the Parallel Port inputs just as if it had
1610             seen a rising edge on DISTB when input handshaking is enabled. This command
1611             works whether input handshaking is enabled or not. The Slink-e will
1612             respond with a port receive message containing the Parallel Port data.
1613              
1614             Note that this function does I actually return the parallel port data.
1615             To get that, you must call the C function.
1616              
1617             =cut
1618              
1619             sub sampleParPort {
1620             my $this = shift;
1621            
1622             $this->txrx( COMMAND => "CMD_SAMPLE" );
1623             }
1624              
1625             =head2 $slinke->sendIR( DATA => $data [, IRPORT => $ports ] )
1626              
1627             This function allows you to send IR signals. The C element should be
1628             an array reference of run length coded signals. If you wish to send the
1629             IR on specific ports, set the C element.
1630              
1631             This function will automatically mute the IR Receivers while data is
1632             being sent so that the receiver will not capture what the transmitter
1633             is sending.
1634              
1635             =cut
1636              
1637             sub sendIR {
1638             my $this = shift;
1639             my %args = @_;
1640             my $oldTX;
1641             my $oldRX;
1642            
1643             if ( $this->{ VERSION } >= 2.0 ) {
1644             if ( defined $args{ IRPORT } ) {
1645             $oldTX = $this->requestIRTransmitPorts;
1646             $this->setIRTransmitPorts( $args{ IRPORT } )
1647             }
1648              
1649             $oldRX = $this->requestIRReceivePorts;
1650             $this->setIRReceivePorts( 0x00 );
1651             }
1652              
1653             my @data = $this->rlcToInt8( $args{ DATA } );
1654              
1655             for ( my $i=0; $i <= $#data; $i += $Slinke::MAXDATABLOCK ) {
1656             my $end = $i + $Slinke::MAXDATABLOCK < $#data ? $i + $Slinke::MAXDATABLOCK - 1 : $#data;
1657             my @dp = @data[$i..$end];
1658            
1659             my $time = 0;
1660             foreach my $j ( @dp[0..$#dp-2] ) {
1661             $time += abs( $j );
1662             }
1663              
1664             my $init = ( $Slinke::PORTS{ PORT_IR } << 5 ) + $end - $i + 1;
1665              
1666             my $status = $this->writeToPort( $init, @dp );
1667             if ( !defined( $status ) ) {
1668             warn "Error in sending IR\n";
1669             return undef;
1670             }
1671             }
1672              
1673             my $init = ( $Slinke::PORTS{ PORT_IR } << 5 );
1674             my $status = $this->writeToPort( $init );
1675             if ( !defined( $status ) ) {
1676             warn "Error in sending end of IR\n";
1677             return undef;
1678             }
1679              
1680             if ( $this->{ VERSION } >= 2.0 ) {
1681             $this->setIRReceivePorts( $oldRX );
1682             $this->setIRTransmitPorts( $oldTX ) if defined $args{ IRPORT };
1683             }
1684             }
1685              
1686             =head2 $slinke->sendData( DATA => $data, PORT => $port )
1687              
1688             This allows data to be sent over a S-Link port or the parallel port.
1689             The C element must be set to either C, C,
1690             C or C. The data element should be an array
1691             reference of the data to be sent.
1692              
1693             =cut
1694              
1695             sub sendData {
1696             my $this = shift;
1697             my %args = @_;
1698            
1699             if ( !defined( $args{ PORT } ) ) {
1700             warn "Must set PORT argument\n";
1701             return undef;
1702             }
1703              
1704             if ( $args{ PORT } ne "PORT_SL0" && $args{ PORT } ne "PORT_SL1"
1705             && $args{ PORT } ne "PORT_SL2" && $args{ PORT } ne "PORT_SL3"
1706             && $args{ PORT } ne "PORT_PAR" ) {
1707             warn "PORT must be one of either: PORT_SL0, PORT_SL1, PORT_SL2, PORT_SL3 or PORT_PAR\n";
1708             return undef;
1709             }
1710              
1711             my @data = @{$args{ DATA }};
1712              
1713             for ( my $i=0; $i <= $#data; $i += $Slinke::MAXDATABLOCK ) {
1714             my $end = $i + $Slinke::MAXDATABLOCK < $#data ? $i + $Slinke::MAXDATABLOCK - 1 : $#data;
1715             my @dp = @data[$i..$end];
1716              
1717             my $init = ( $Slinke::PORTS{ $args{ PORT } } << 5 ) + $end - $i + 1;
1718              
1719             my $status = $this->writeToPort( $init, @dp );
1720             if ( !defined( $status ) ) {
1721             warn "Error in sending data\n";
1722             return undef;
1723             }
1724             }
1725              
1726             my $init = ( $Slinke::PORTS{ $args{ PORT } } << 5 );
1727             my $status = $this->writeToPort( $init );
1728              
1729             if ( !defined( $status ) ) {
1730             warn "Error in sending end of data\n";
1731             return undef;
1732             }
1733              
1734             }
1735              
1736             sub txrx {
1737             my $this = shift;
1738             my %args = @_;
1739              
1740             my $command = $args{ COMMAND };
1741             my $port = $args{ PORT };
1742             if ( !defined $port ) {
1743             if ( !exists $Slinke::COMMANDMAPS{ $command } ) {
1744             warn "Unknown command: $command\n";
1745             return undef;
1746             }
1747             $port = $Slinke::COMMANDMAPS{ $command }->[0];
1748             }
1749              
1750             my $init = ( $Slinke::PORTS{ $port } << 5 ) + $Slinke::COMMANDS{ CMD_PORT_SM };
1751             my $cmd = $Slinke::COMMANDS{ $command };
1752              
1753             my $expectedResponse = $Slinke::COMMANDMAPS{ $command }->[1];
1754              
1755             my $status = $this->writeToPort( $init, $cmd, @{$args{ ARGS }} );
1756             if ( !defined( $status ) ) {
1757             return undef;
1758             }
1759              
1760             if ( defined $expectedResponse ) {
1761             my ( $device, $response, $data ) = $this->receive( EXPECTINPUT => ( $expectedResponse eq "RSP_EQBAUD" ? 0 : 1 ) );
1762              
1763             if ( $expectedResponse ne $response ) {
1764             my $str;
1765             $str .= $device if $device;
1766             $str .= " $data" if $data;
1767             warn "Expected '$expectedResponse' - Received '$response' ($str)\n";
1768             return undef;
1769             }
1770            
1771             return ( $device, $response, $data );
1772             }
1773             else {
1774             return $status;
1775             }
1776              
1777             }
1778              
1779             sub resume {
1780             my $this = shift;
1781              
1782             # sending a single EOM character which won't hurt anything
1783             # and will insure word alignment
1784             $this->writeToPort( 0x00 );
1785             $this->txrx( COMMAND => "CMD_RESUME" );
1786             }
1787              
1788             =head2 $slinke->reset()
1789              
1790             Warm-boots the Slink-e, resetting all defaults including the baud
1791             rate. In version 2.0 or greater, these defaults are loaded from
1792             an EEPROM which is user programmable.
1793              
1794             =cut
1795              
1796             sub reset {
1797             my $this = shift;
1798            
1799             $this->txrx( COMMAND => "CMD_RESET" );
1800             $this->loadInternals;
1801             }
1802              
1803             =head2 $slinke->loadDefaults()
1804              
1805             Causes the Slink-e to load all of the current user settings from EEPROM
1806             memory so that they are returned to their default values, Be wary of the
1807             fact that the baud rate stored in EEPROM could be different than the
1808             current baud rate. In this case communications will be lost until the
1809             host detects the new baud rate. If successful, the Slink-e will send a
1810             defaults loaded response.
1811              
1812             I
1813              
1814             =cut
1815              
1816             sub loadDefaults {
1817             my $this = shift;
1818              
1819             if ($this->{ VERSION } < 2.0 ) {
1820             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1821             return undef;
1822             }
1823              
1824             $this->txrx( COMMAND => "CMD_LOADDEFAULTS" );
1825             $this->loadInternals;
1826             }
1827              
1828             =head2 $slinke->saveDefaults()
1829              
1830             Causes the Slink-e to save all of the current user settings to EEPROM
1831             memory so that they will become the defaults the next time the Slink-e
1832             is reset or powered-up. If successful, the Slink-e will send a defaults
1833             saved response.
1834              
1835             I
1836              
1837             =cut
1838              
1839             sub saveDefaults {
1840             my $this = shift;
1841              
1842             if ($this->{ VERSION } < 2.0 ) {
1843             warn "Current Slink-e version is $this->{VERSION} (need 2.0 or greater)\n";
1844             return undef;
1845             }
1846              
1847             $this->txrx( COMMAND => "CMD_SAVEDEFAULTS" );
1848             }
1849              
1850             =head2 decodeIR( @data )
1851              
1852             This will take the data returned by requestInput and attempt to convert it
1853             to a bit string. This function returns a hash reference.
1854              
1855             Elements of the hash reference
1856              
1857             HEAD => The wakeup call for the device. These are typically the first two
1858             bytes in the data array. This is an array reference.
1859              
1860             CODE => This is a bit string indicating the command that was sent.
1861             For Sony devices, a "P" is thrown in when a pause in the data array
1862             is detected.
1863              
1864             TAIL => The bytes that indicate the end of the data string. These are
1865             typically the last five bytes of the data array. This is an array
1866             reference.
1867              
1868             ENCODING => This is an array reference of two array references that describes
1869             how zeroes and ones are encoded.
1870              
1871             =cut
1872              
1873             my %lastSignal;
1874              
1875             sub decodeIR {
1876             my @data = @_;
1877             my $CUTOFF = 6000;
1878              
1879             if ( $#data < 6 ) {
1880             return \%lastSignal;
1881             }
1882             return undef unless $#data > 6;
1883            
1884             my @head = @data[0..1];
1885             @data = @data[2..$#data];
1886              
1887             my @d = @data;
1888             my @d1;
1889             my @d2;
1890             my $d1avg;
1891             my $d2avg;
1892            
1893             while ( $#d >= 0 ) {
1894             my $t1 = shift @d;
1895             last if !defined $t1;
1896             last if abs($t1) > $CUTOFF;
1897              
1898             my $t2 = shift @d;
1899             last if !defined $t2;
1900             last if abs($t2) > $CUTOFF;
1901              
1902             push @d1, $t1;
1903             $d1avg += $t1;
1904              
1905             push @d2, $t2;
1906             $d2avg += $t2;
1907             }
1908              
1909             @d1 = sort @d1;
1910             @d2 = sort @d2;
1911              
1912             my $d1med = $d1[int( $#d1 /2 + 0.5 )];
1913             $d1avg = $d1avg/($#d1 + 1);
1914              
1915             my $d2med = $d2[int( $#d2 /2 + 0.5 )];
1916             $d2avg = $d2avg/($#d2 + 1);
1917              
1918             $d1avg = .0001 if !$d1avg;
1919             my $d1dif = 0;
1920             my $d2dif = 0;
1921             if ( abs(($d1med - $d1avg) / $d1avg ) > .15 ) {
1922             $d1dif = 1;
1923             }
1924             $d2avg = .0001 if !$d2avg;
1925             if ( abs(($d2med - $d2avg) / $d2avg ) > .15 ) {
1926             $d2dif = 1;
1927             }
1928              
1929             @d = @data;
1930              
1931             my $avg = $d1avg + $d2avg;
1932             my $str;
1933              
1934             my @const;
1935             my @var0;
1936             my @var1;
1937             my $constSum;
1938             my $var0Sum;
1939             my $var1Sum;
1940             my @pause;
1941             my $pauseSum;
1942              
1943             while ( $#d >= 0 ) {
1944             my $pauseFlag = 0;
1945             my $t1 = shift @d;
1946             if ( !defined $t1 ) {
1947             last;
1948             }
1949             if ( abs( $t1 ) > $CUTOFF ) {
1950             if ( $#d > 6 ) {
1951             $pauseFlag = 1;
1952             push @pause, $t1;
1953             $pauseSum += $t1;
1954             }
1955             else {
1956             unshift @d, $t1;
1957             last;
1958             }
1959             }
1960              
1961             my $t2 = shift @d;
1962             if ( !defined $t2 ) {
1963             unshift @d, $t1;
1964             last;
1965             }
1966             if ( abs( $t2 ) > $CUTOFF ) {
1967             if ( $#d > 6 ) {
1968             $pauseFlag = 1;
1969             push @pause, $t2;
1970             $pauseSum += $t2;
1971             }
1972             else {
1973             unshift @d, $t2;
1974             unshift @d, $t1;
1975             last;
1976             }
1977             }
1978              
1979             if ( $d1dif ) {
1980             if ( abs( $t2 ) < $CUTOFF ) {
1981             push @const, $t2;
1982             $constSum += $t2;
1983             }
1984             if ( $t1 > $d1avg ) {
1985             if ( abs( $t1 ) < $CUTOFF ) {
1986             push @var1, $t1;
1987             $var1Sum += $t1;
1988             }
1989             $str .= ( $pauseFlag ? "P" : 1 );
1990             }
1991             else {
1992             if ( abs( $t1 ) < $CUTOFF ) {
1993             push @var0, $t1;
1994             $var0Sum += $t1;
1995             }
1996             $str .= ( $pauseFlag ? "p" : 0 );
1997             }
1998             }
1999             else {
2000             if ( abs( $t1 ) < $CUTOFF ) {
2001             push @const, $t1;
2002             $constSum += $t1;
2003             }
2004            
2005             if ( $t2 < $d2avg ) {
2006             if ( abs( $t2 ) < $CUTOFF ) {
2007             push @var1, $t2;
2008             $var1Sum += $t2;
2009             }
2010             $str .= ( $pauseFlag ? "P" : 1 );
2011             }
2012             else {
2013             if ( abs( $t2 ) < $CUTOFF ) {
2014             push @var0, $t2;
2015             $var0Sum += $t2;
2016             }
2017             $str .= ( $pauseFlag ? "p" : 0 );
2018             }
2019             }
2020              
2021             if ( $pauseFlag ) {
2022             # remove the head again
2023             shift @d;
2024             shift @d;
2025             }
2026             }
2027              
2028             my @zeroSeq;
2029             my @oneSeq;
2030            
2031             if ( $d1dif ) {
2032             push @zeroSeq, $var0Sum / ( $#var0 + 1 );
2033             push @zeroSeq, $constSum / ( $#const + 1 );
2034             push @oneSeq, $var1Sum / ( $#var1 + 1 );
2035             push @oneSeq, $constSum / ( $#const + 1 );
2036             }
2037             else {
2038             push @zeroSeq, $constSum / ( $#const + 1 );
2039             push @zeroSeq, $var0Sum / ( $#var0 + 1 );
2040             push @oneSeq, $constSum / ( $#const + 1 );
2041             push @oneSeq, $var1Sum / ( $#var1 + 1 );
2042             }
2043              
2044             $str =~ s/[pP]*$//g;
2045              
2046             if ( $str =~ /[pP]/ ) {
2047             $lastSignal{ PAUSETIME } = $pauseSum / ( $#pause + 1 );
2048             my $repeat = 0;
2049             while ($str =~ /[pP]/g) { $repeat++ }
2050             $lastSignal{ REPEAT } = $repeat;
2051              
2052             if ( $oneSeq[1] == $zeroSeq[1] ) {
2053             $lastSignal{ PAUSETIME } -= $oneSeq[1];
2054             $str =~ s/p/0p/g;
2055             $str =~ s/P/1P/g;
2056             }
2057              
2058             $str =~ s/[pP].*//g;
2059             }
2060              
2061             $lastSignal{ HEAD } = \@head;
2062             $lastSignal{ TAIL } = \@d;
2063             $lastSignal{ CODE } = $str;
2064             $lastSignal{ ENCODING } = [ \@zeroSeq, \@oneSeq ];
2065              
2066             return \%lastSignal;
2067             }
2068              
2069             sub PORT_SLO { return "PORT_SLO"; }
2070             sub PORT_SL1 { return "PORT_SL1"; }
2071             sub PORT_SL2 { return "PORT_SL2"; }
2072             sub PORT_SL3 { return "PORT_SL3"; }
2073             sub PORT_IR { return "PORT_IR"; }
2074             sub PORT_PAR { return "PORT_PAR"; }
2075             sub PORT_SER { return "PORT_SER"; }
2076             sub PORT_SYS { return "PORT_SYS"; }
2077              
2078             1;
2079              
2080             =head1 TODO
2081              
2082             Add bin mode for S-Link ports
2083              
2084             =head1 AUTHOR
2085              
2086             Brian Paulsen
2087              
2088             Copyright 2000, Brian Paulsen. All rights reserved.
2089              
2090             This library is free software; you can redistribute it and/or modify
2091             it under the same terms as Perl itself.
2092              
2093             Bug reports and comments to Brian@ThePaulsens.com.
2094              
2095             For further information about the Slink-e, visit http://www.nirvis.com
2096              
2097             =cut