| 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; |