line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
2
|
|
|
|
|
|
|
# Audio::Xmpcr::Serial |
3
|
|
|
|
|
|
|
# Copyright Paul Bournival 2003 |
4
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Audio::Xmpcr::Serial; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
$VERSION="1.02"; |
9
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
31
|
|
11
|
1
|
|
|
1
|
|
1369
|
use Device::SerialPort; |
|
1
|
|
|
|
|
67133
|
|
|
1
|
|
|
|
|
69
|
|
12
|
1
|
|
|
1
|
|
1176
|
use bytes; |
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
6
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub new { |
16
|
0
|
|
|
0
|
0
|
|
my($class,$port)=@_; |
17
|
0
|
|
|
|
|
|
my $self={}; |
18
|
0
|
|
|
|
|
|
$self->{port}=$port; |
19
|
0
|
|
0
|
|
|
|
$self->{sdev} = new Device::SerialPort ("$self->{port}") |
20
|
|
|
|
|
|
|
|| die "Can't open USB Port! ($self->{port} $!\n"; |
21
|
0
|
|
|
|
|
|
$self->{sdev}->baudrate(9600); |
22
|
0
|
|
|
|
|
|
$self->{sdev}->parity('none'); |
23
|
0
|
|
|
|
|
|
$self->{sdev}->databits(8); |
24
|
0
|
|
|
|
|
|
$self->{sdev}->stopbits(1); |
25
|
|
|
|
|
|
|
|
26
|
0
|
|
|
|
|
|
$self->{_state}={ |
27
|
|
|
|
|
|
|
power => 0, |
28
|
|
|
|
|
|
|
channel => 0, |
29
|
|
|
|
|
|
|
radioId => "", |
30
|
|
|
|
|
|
|
channels => [], |
31
|
|
|
|
|
|
|
}; |
32
|
|
|
|
|
|
|
|
33
|
0
|
|
|
|
|
|
bless $self,$class; |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
37
|
|
|
|
|
|
|
# a general send/receive method. |
38
|
|
|
|
|
|
|
# if called in a scalar context, returns STATUS: undef=success || errmsg=failed |
39
|
|
|
|
|
|
|
# if called in an array context, returns (STATUS (above),PORTREADSTR) |
40
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
41
|
|
|
|
|
|
|
sub _doop { |
42
|
0
|
|
|
0
|
|
|
my($self,$op,$cmd,$wcnt,$rcnt)=@_; |
43
|
0
|
|
|
|
|
|
my($readstr,$retval,$cnt)=("",undef,0); |
44
|
0
|
0
|
0
|
|
|
|
return("$op: Power isn't on!") |
45
|
|
|
|
|
|
|
if $cmd ne "5AA500050010101001EDED" and ! $self->{_state}{power}; |
46
|
0
|
|
|
|
|
|
$self->{sdev}->write(pack("H*",$cmd)); |
47
|
0
|
0
|
|
|
|
|
$self->{sdev}->read_const_time($wcnt) if defined $wcnt; |
48
|
0
|
0
|
|
|
|
|
if ($rcnt) { |
49
|
0
|
|
|
|
|
|
while($cnt<$rcnt) { |
50
|
0
|
|
|
|
|
|
($cnt,$readstr)=$self->{sdev}->read($rcnt); |
51
|
0
|
|
|
|
|
|
$readstr=join("",unpack("H*",$readstr)); |
52
|
|
|
|
|
|
|
} |
53
|
0
|
0
|
|
|
|
|
$retval=substr($readstr,0,6) eq "5aa500" ? undef : "$op failed"; |
54
|
0
|
0
|
|
|
|
|
$self->{_state}{radioId}=pack("H*",substr($readstr, 46, 16)) |
55
|
|
|
|
|
|
|
if $cmd eq "5AA500050010101001EDED"; |
56
|
|
|
|
|
|
|
} |
57
|
0
|
0
|
|
|
|
|
wantarray ? ($retval,$readstr) : $retval; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
61
|
|
|
|
|
|
|
# turn on/off power |
62
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
63
|
|
|
|
|
|
|
sub power { |
64
|
0
|
|
|
0
|
0
|
|
my($self,$status)=@_; |
65
|
0
|
0
|
|
|
|
|
defined($status) || die "power called improperly\n"; |
66
|
0
|
0
|
|
|
|
|
my $res=$status eq "on" ? |
67
|
|
|
|
|
|
|
$self->_doop("power on","5AA500050010101001EDED",100,40) : |
68
|
|
|
|
|
|
|
$self->_doop("power off","5AA500020100EDED",0,0); |
69
|
|
|
|
|
|
|
|
70
|
0
|
0
|
|
|
|
|
$self->{_state}{power}=($status eq "on" ? 1 : 0) if ! $res; |
|
|
0
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# if powering up, load the channels from the device. |
73
|
0
|
0
|
0
|
|
|
|
if ($status eq "on" and ! $res) { |
74
|
0
|
|
|
|
|
|
sleep(8); |
75
|
0
|
|
|
|
|
|
$self->_buildChannelList; |
76
|
0
|
|
|
|
|
|
$self->setchannel(1); |
77
|
|
|
|
|
|
|
} |
78
|
0
|
|
|
|
|
|
$res; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
82
|
|
|
|
|
|
|
# turn on/off mute |
83
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
84
|
|
|
|
|
|
|
sub mute { |
85
|
0
|
|
|
0
|
0
|
|
my($self,$status)=@_; |
86
|
0
|
0
|
|
|
|
|
defined($status) || die "mute called improperly\n"; |
87
|
0
|
0
|
|
|
|
|
$self->_doop("mute $status",$status eq "on" ? |
88
|
|
|
|
|
|
|
"5AA500021301EDED" : "5AA500021300EDED", 0,10); |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
92
|
|
|
|
|
|
|
# change channel |
93
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
94
|
|
|
|
|
|
|
sub setchannel { |
95
|
0
|
|
|
0
|
0
|
|
my($self,$chan)=@_; |
96
|
0
|
0
|
|
|
|
|
defined($chan) || die "setchannel called improperly\n"; |
97
|
0
|
|
|
|
|
|
$self->{_state}{channel}=$chan; |
98
|
0
|
|
|
|
|
|
$self->_doop("setchannel $chan", |
99
|
0
|
|
|
|
|
|
"5AA500061002@{[sprintf('%02X',$chan)]}000001EDED",3000,12); |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
103
|
|
|
|
|
|
|
# list 1 or all channels |
104
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
105
|
|
|
|
|
|
|
sub list { |
106
|
0
|
|
|
0
|
0
|
|
my($self,$chan)=@_; |
107
|
0
|
|
|
|
|
|
my(@ret,$err,$res); |
108
|
0
|
0
|
|
|
|
|
my @ch=$chan ? ($chan) : @{ $self->{_state}{channels} }; |
|
0
|
|
|
|
|
|
|
109
|
0
|
|
|
|
|
|
for my $ch (@ch) { |
110
|
0
|
|
|
|
|
|
($err,$res)=$self->_doop("channel $ch info", |
111
|
0
|
|
|
|
|
|
"5AA500042508@{[sprintf('%02X',$ch)]}00EDED",100,83); |
112
|
0
|
0
|
|
|
|
|
last if $err; |
113
|
0
|
|
|
|
|
|
push(@ret,{ |
114
|
|
|
|
|
|
|
NUM => $ch, |
115
|
|
|
|
|
|
|
NAME => $self->_prune(pack("H*", substr($res, 20, 32))), |
116
|
|
|
|
|
|
|
CAT => $self->_prune(pack("H*", substr($res, 52, 32))), |
117
|
|
|
|
|
|
|
ARTIST => $self->_prune(pack("H*", substr($res, 88, 32))), |
118
|
|
|
|
|
|
|
SONG => $self->_prune(pack("H*", substr($res, 122, 32))), |
119
|
|
|
|
|
|
|
}); |
120
|
|
|
|
|
|
|
} |
121
|
0
|
0
|
|
|
|
|
$chan ? $ret[0] : @ret; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
125
|
|
|
|
|
|
|
# remove extra spaces and control characters |
126
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
127
|
|
|
|
|
|
|
sub _prune { |
128
|
0
|
|
|
0
|
|
|
my($self,$str)=@_; |
129
|
0
|
|
|
|
|
|
$str =~ s/[^[:graph:] ]//gs; |
130
|
0
|
|
|
|
|
|
$str =~ s/^\s+//; |
131
|
0
|
|
|
|
|
|
$str =~ s/\s+$//; |
132
|
0
|
|
|
|
|
|
$str =~ s#/#-#g; # embedded forward slashes - yuk! |
133
|
0
|
|
|
|
|
|
$str; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
137
|
|
|
|
|
|
|
# builds a list of channels on the radio |
138
|
|
|
|
|
|
|
# this should probably write the list to a file somewhere... |
139
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
140
|
|
|
|
|
|
|
# to be used at power up only!!! |
141
|
|
|
|
|
|
|
sub _buildChannelList { |
142
|
0
|
|
|
0
|
|
|
my($self)=@_; |
143
|
0
|
|
|
|
|
|
my($ch,$lasterr,$res)=("00",undef); |
144
|
|
|
|
|
|
|
# NOTE: PAULB GET RID OF ME LATER! - for debugging only!!!!!!!!!!!! |
145
|
|
|
|
|
|
|
# $self->{_state}{channels}=[1,4,5,6,7,8,9,10,11,12,13,14,15,20,21,22,23,24,25,26,27,28,29,30,31,32,40,41,42,43,44,45,46,47,48,50,51,52,60,61,62,63,64,65,66,67,70,71,72,73,74,75,76,80,81,82,83,90,91,92,93,94,100,101,102,103,104,110,112,113,115,116,121,122,123,124,125,127,129,130,131,132,134,140,141,142,143,144,150,151,152,161,162,163,164,165,166,168,169,170,171]; |
146
|
|
|
|
|
|
|
#return; |
147
|
0
|
|
|
|
|
|
$self->{_state}{channels}=[]; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# build a cache file if none is present |
150
|
0
|
0
|
|
|
|
|
if (! -f "$ENV{HOME}/.xmpcrd-cache") { |
151
|
0
|
0
|
|
|
|
|
open(F,">$ENV{HOME}/.xmpcrd-cache") or die "Can't write cache file: $!"; |
152
|
0
|
|
|
|
|
|
while(1) { |
153
|
0
|
|
|
|
|
|
($lasterr,$res)=$self->_doop("channel $ch info", |
154
|
|
|
|
|
|
|
"5AA500042509${ch}00EDED",100,83); |
155
|
0
|
|
|
|
|
|
$ch=substr($res,14,2); |
156
|
0
|
0
|
0
|
|
|
|
last if $ch eq "00" or $lasterr; |
157
|
0
|
|
|
|
|
|
print F hex($ch) . "\n"; |
158
|
|
|
|
|
|
|
} |
159
|
0
|
|
|
|
|
|
close(F); |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
0
|
|
|
|
|
|
my($line); |
163
|
0
|
0
|
|
|
|
|
open(F,"$ENV{HOME}/.xmpcrd-cache") or die "Can't read cache file: $!"; |
164
|
0
|
|
|
|
|
|
while($line=) { |
165
|
0
|
|
|
|
|
|
chop $line; |
166
|
0
|
|
|
|
|
|
push(@{ $self->{_state}{channels} },$line); |
|
0
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
} |
168
|
0
|
|
|
|
|
|
close(F); |
169
|
|
|
|
|
|
|
|
170
|
0
|
|
|
|
|
|
$lasterr; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
174
|
|
|
|
|
|
|
# obtain general radio status |
175
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
176
|
|
|
|
|
|
|
sub status { |
177
|
0
|
|
|
0
|
0
|
|
my($self)=@_; |
178
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
|
my %cur; |
180
|
0
|
0
|
|
|
|
|
if ($self->{_state}{power}) { |
181
|
0
|
|
|
|
|
|
%cur=%{ $self->list($self->{_state}{channel}) }; |
|
0
|
|
|
|
|
|
|
182
|
0
|
|
|
|
|
|
$cur{RADIOID}= $self->{_state}{radioId}; |
183
|
0
|
|
|
|
|
|
my($err,$ti)=$self->_doop("tech info","5AA5000143EDED",100,32); |
184
|
0
|
|
0
|
|
|
|
$cur{ANTENNA}=int(1+(substr($ti, 16,2) || 0)*33.3); |
185
|
|
|
|
|
|
|
} |
186
|
0
|
0
|
|
|
|
|
$cur{POWER}=$self->{_state}{power} ? "on" : "off"; |
187
|
0
|
|
|
|
|
|
%cur; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
191
|
|
|
|
|
|
|
# event support (i.e., song changing) |
192
|
|
|
|
|
|
|
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
193
|
|
|
|
|
|
|
sub events { |
194
|
0
|
|
|
0
|
0
|
|
die "Whoops! events aren't supported on the serial interface!\n"; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
sub processEvents { |
197
|
0
|
|
|
0
|
0
|
|
die "Whoops! events aren't supported on the serial interface!\n"; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
sub eventFd { |
200
|
0
|
|
|
0
|
0
|
|
die "Whoops! events aren't supported on the serial interface!\n"; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
sub forcelock { |
203
|
0
|
|
|
0
|
0
|
|
die "Whoops! locks aren't supported on the serial interface!\n"; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
1; |