line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#####################################################################
|
2
|
|
|
|
|
|
|
# HAM::Device::IcomCIV -- OO Module for Icom CI-V radios
|
3
|
|
|
|
|
|
|
#
|
4
|
|
|
|
|
|
|
# Copyright (c) 2007 Ekkehard (Ekki) Plicht. All rights reserved.
|
5
|
|
|
|
|
|
|
#
|
6
|
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or
|
7
|
|
|
|
|
|
|
# modify it under the same terms as Perl itself.
|
8
|
|
|
|
|
|
|
#
|
9
|
|
|
|
|
|
|
#####################################################################
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=pod
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 NAME
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
HAM::Device::IcomCIV - Class for basic remote control of Icom CI-V Radios
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 MODULE VERSION
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
Version 0.02 02. Dec. 2007
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
use HAM::Device::IcomCIV;
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# initiate first radio
|
26
|
|
|
|
|
|
|
my $rig1 = HAM::Device::IcomCIV->new( undef, '/dev/ttyS1', 1, 19200, 'IC-R8500', 0x4A, 0xE0, 1 );
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my ($lower, $upper) = $rig1->get_bandedges;
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my $freq = $rig1->frequency;
|
31
|
|
|
|
|
|
|
$rig1->frequency(6075000);
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
my $mode = $rig1->mode;
|
34
|
|
|
|
|
|
|
my ($mode, filter) = $rig1->mode;
|
35
|
|
|
|
|
|
|
$rig1->mode('AM');
|
36
|
|
|
|
|
|
|
$rig1->mode('AM', 'NARROW');
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
...
|
39
|
|
|
|
|
|
|
# initiate second radio on same bus (same serial port)
|
40
|
|
|
|
|
|
|
$my $serio = $rig1->get_serioobject;
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
$my $rig2 = HAM::Device::IcomCIV->new( $serio, undef, undef, undef, 'IC-R75', undef, 0xE0, 1 );
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head1 SUPPORTED CI-V FUNCTIONS
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
* Read/Set Frequency
|
47
|
|
|
|
|
|
|
* Read/Set Mode
|
48
|
|
|
|
|
|
|
* Read Band Edges
|
49
|
|
|
|
|
|
|
* Switch to VFO (last selected or A or B or Main or Sub)
|
50
|
|
|
|
|
|
|
* Switch to Memory
|
51
|
|
|
|
|
|
|
* Select Memory channel
|
52
|
|
|
|
|
|
|
* Clear Memory channel
|
53
|
|
|
|
|
|
|
* Transfer Memory to VFO
|
54
|
|
|
|
|
|
|
* Write VFO contents to selected memory
|
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
If you are looking for support of more elaborate CI-V functions see descendants of HAM::Device::IcomCIV, like HAM::Device::IcomICR8500, HAM::Device::IcomICR75 etc. These classes implement functions specific to the radio. If these classes do not implement what you want, derive your own class from HAM::Device::IcomCIV.
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
This module is an OO approach to use Icom radios attached to a serial port.
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
HAM::Device::IcomCIV is not an abstract class, but one you can use directly. It allows
|
63
|
|
|
|
|
|
|
for the most basic form of remote control of practically any Icom radio equipped
|
64
|
|
|
|
|
|
|
with a CI-V port, namely set & get of the displayed frequency, set & get the mode and filter, read the band edges, switch to VFO or Memory (if radio has a VFO), select a memory channel, write VFO data to memory and clear a memory.
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
It supports multiple radios on the same serial port, differentiated by their
|
67
|
|
|
|
|
|
|
CI-V adress. Note: If you have two or more identical models on the same bus, they must use different CI-V adresses.
|
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
This class can be used in a procedural manner or with an event callback mechanism (or both at the same time). When used without callbacks you just use the provided methods for setting or getting radio settings (Frequency, Mode etc.). If you use the callback mechanism you will receive a callback to your sub (set with set_callback) which tells you what happened, i.e. which data was received (see event and status constants).
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head1 EXPORT
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Nothing by default.
|
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=head2 STATUS CONSTANTS (exported on demand)
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
stGOOD all ok, command succeeded
|
78
|
|
|
|
|
|
|
stFAIL command to radio failed
|
79
|
|
|
|
|
|
|
stNOIM command not implemented in this class (not recognized)
|
80
|
|
|
|
|
|
|
stWAIT wait, status update in progress
|
81
|
|
|
|
|
|
|
stINIT occurs only after new() and if no command has been issued yet
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=head2 EVENT CONSTANTS (exported on demand)
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
evSTAT Status GOOD/NOGOOD received
|
86
|
|
|
|
|
|
|
evFREQ A new frequency has been received
|
87
|
|
|
|
|
|
|
evMODE A new mode/filter has been received
|
88
|
|
|
|
|
|
|
evUNKN Unknown command response received
|
89
|
|
|
|
|
|
|
evNOEV Fake, no event has happened so far
|
90
|
|
|
|
|
|
|
evEDGE Band Edges have been received
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=head1 USES
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
HAM::Device::IcomCIVSerialIO
|
95
|
|
|
|
|
|
|
Carp;
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=cut
|
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
package HAM::Device::IcomCIV;
|
100
|
|
|
|
|
|
|
|
101
|
1
|
|
|
1
|
|
27054
|
use 5.008008;
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
47
|
|
102
|
1
|
|
|
1
|
|
7
|
use strict;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
42
|
|
103
|
1
|
|
|
1
|
|
7
|
use warnings;
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
54
|
|
104
|
1
|
|
|
1
|
|
7
|
use Carp;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
130
|
|
105
|
1
|
|
|
1
|
|
2519
|
use HAM::Device::IcomCIVSerialIO;
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
our $VERSION = '0.02';
|
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
############################################################
|
111
|
|
|
|
|
|
|
# Constants, to be exported on demand
|
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
use constant stGOOD => 1; # 0xFB received
|
114
|
|
|
|
|
|
|
use constant stFAIL => 2; # 0xFA received
|
115
|
|
|
|
|
|
|
use constant stWAIT => 3; # Wait, status about to change
|
116
|
|
|
|
|
|
|
use constant stINIT => 4; # After init of object, nothing happened yet
|
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
############################################################
|
119
|
|
|
|
|
|
|
# Events, to be exported on demand
|
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
use constant evSTAT => 1; # status (GOOD/NOGOOD) received
|
122
|
|
|
|
|
|
|
use constant evFREQ => 2; # Frequency received
|
123
|
|
|
|
|
|
|
use constant evMODE => 3; # Mode/Filter received
|
124
|
|
|
|
|
|
|
use constant evUNKN => 4; # Unknown command response received
|
125
|
|
|
|
|
|
|
use constant evNOEV => 5; # No Event has happened, only after init
|
126
|
|
|
|
|
|
|
use constant evEDGE => 6; # Band Edges received
|
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
############################################################
|
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
require Exporter;
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
our @ISA = qw( Exporter );
|
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
our @EXPORT = qw ( );
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
our %EXPORT_TAGS = (
|
137
|
|
|
|
|
|
|
Constants => [ qw (
|
138
|
|
|
|
|
|
|
stGOOD
|
139
|
|
|
|
|
|
|
stFAIL
|
140
|
|
|
|
|
|
|
stNOIM
|
141
|
|
|
|
|
|
|
stWAIT
|
142
|
|
|
|
|
|
|
stINIT
|
143
|
|
|
|
|
|
|
)],
|
144
|
|
|
|
|
|
|
Events => [ qw (
|
145
|
|
|
|
|
|
|
evSTAT
|
146
|
|
|
|
|
|
|
evFREQ
|
147
|
|
|
|
|
|
|
evMODE
|
148
|
|
|
|
|
|
|
evUNKN
|
149
|
|
|
|
|
|
|
evNOEV
|
150
|
|
|
|
|
|
|
evEDGE
|
151
|
|
|
|
|
|
|
)]
|
152
|
|
|
|
|
|
|
);
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
our @EXPORT_OK = ( );
|
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Exporter::export_ok_tags( 'Constants', 'Events');
|
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
$EXPORT_TAGS{ALL} = \@EXPORT_OK;
|
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
############################################################
|
161
|
|
|
|
|
|
|
############################################################
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=pod
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=head1 METHODS
|
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=head2 new( SerialIOobject, SerialDevice, Baudrate, UseLock, RadioModel, RadioAdr, OwnAdr, DebugLevel )
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Creates a new IcomCIV object. Returns the object reference.
|
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=over 4
|
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=item SerialIOobject
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Is a ref to an instance of HAM::Device::IcomCIVSerialIO. If undef a new SerialIO object will be created with the following two parameters. If defined no new SerialIO object will be created but this one will be used instead. This allows to share one SerialIO object between several instances of IcomCIV (see method get_serioobject() ).
|
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Either SerialIOobject or SerialDevice must be set, otherwise new will die.
|
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=item SerialDevice
|
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
Must be present at least once (if you have multiple radios on one serial bus).
|
182
|
|
|
|
|
|
|
Can be undef for subsequent calls for the creation of a 2nd, 3rd etc. radio
|
183
|
|
|
|
|
|
|
which use the same serial port as the first. Default is /dev/ttyS1. If undef the previous parameter mus tbe set.
|
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item Baudrate
|
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
If SerialDevice is given, this value should be given as well. If SerialDevice is undef this value is ignored. If SerialDevice is present and this valus is undef, defaults to 19200.
|
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=item UseLock
|
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Boolean if serial port should use locking or not
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=item RadioModel
|
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
Must be exactly one of the model names defined in this module, case does
|
196
|
|
|
|
|
|
|
not matter. If model is not known the creation of the object will fail. See below for a list of recognized models.
|
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=item RadioAdr
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
The CI-V bus adress of that radio. Can be undef, in that case the default adress
|
201
|
|
|
|
|
|
|
of the specified model is used.
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=item OwnAdr
|
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
The CI-V bus adress of the controller (this computer), usually 0xE0 is used. Can be undef, if so 0xEO is used.
|
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item DebugLevel
|
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Numeric value, 0 disables debugging, increasing values yield more debug output
|
210
|
|
|
|
|
|
|
to STDERR. Default 0.
|
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=back
|
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=cut
|
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
sub new {
|
217
|
|
|
|
|
|
|
my $class = shift;
|
218
|
|
|
|
|
|
|
my $self = {};
|
219
|
|
|
|
|
|
|
$self->{SEROBJ} = shift || undef;
|
220
|
|
|
|
|
|
|
$self->{SERDEV} = shift || '/dev/ttyS1';
|
221
|
|
|
|
|
|
|
$self->{BAUDRATE} = shift || 19200;
|
222
|
|
|
|
|
|
|
$self->{USELOCK} = shift || undef;
|
223
|
|
|
|
|
|
|
$self->{MODEL} = shift || 'Undefined';
|
224
|
|
|
|
|
|
|
$self->{CIV_ADRESS} = shift || get_civ_adress( $self->{MODEL} );
|
225
|
|
|
|
|
|
|
$self->{OWN_ADRESS} = shift || 0xE0;
|
226
|
|
|
|
|
|
|
$self->{DEBUG} = shift || 0;
|
227
|
|
|
|
|
|
|
$self->{FREQ} = -1;
|
228
|
|
|
|
|
|
|
$self->{MODE} = 'undefined';
|
229
|
|
|
|
|
|
|
$self->{FILTER} = 'undefined';
|
230
|
|
|
|
|
|
|
$self->{STATUS} = stINIT;
|
231
|
|
|
|
|
|
|
$self->{EVENT} = evNOEV;
|
232
|
|
|
|
|
|
|
$self->{CBACK} = undef;
|
233
|
|
|
|
|
|
|
$self->{IN_CHECK_RX} = undef;
|
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
croak "Model '$self->{MODEL}' is not recognized! See IcomCIV::Support for supported models." unless ( get_civ_adress( $self->{MODEL} ) );
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
bless ($self, $class);
|
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# Set up new SerialIO object if not given
|
240
|
|
|
|
|
|
|
$self->{SEROBJ} = HAM::Device::IcomCIVSerialIO->new (
|
241
|
|
|
|
|
|
|
$self->{SERDEV},
|
242
|
|
|
|
|
|
|
$self->{BAUDRATE},
|
243
|
|
|
|
|
|
|
$self->{USELOCK},
|
244
|
|
|
|
|
|
|
$self->{DEBUG}
|
245
|
|
|
|
|
|
|
) unless (defined $self->{SEROBJ});
|
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# Tell SerialIO object for which adress I am responsible
|
248
|
|
|
|
|
|
|
$self->{SEROBJ}->set_callback( $self->{CIV_ADRESS}, $self );
|
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
return $self;
|
251
|
|
|
|
|
|
|
};
|
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=pod
|
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=head2 set_callback ( ref_to_sub )
|
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
With this method the callback subroutine is set for later calls. After each received message from the CI-V protocol this sub is called with the following parameters:
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=over 4
|
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=item event
|
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
Is one of
|
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
evSTAT Status GOOD/NOGOOD received
|
268
|
|
|
|
|
|
|
evFREQ A new frequency has been received
|
269
|
|
|
|
|
|
|
evMODE A new mode/filter has been received
|
270
|
|
|
|
|
|
|
evUNKN Unknown command response received
|
271
|
|
|
|
|
|
|
evNOEV Fake, no event has happened so far
|
272
|
|
|
|
|
|
|
evEDGE Band Edges have been received
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=item state
|
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Is one of
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
stGOOD all ok, command succeeded
|
279
|
|
|
|
|
|
|
stFAIL command to radio failed
|
280
|
|
|
|
|
|
|
stNOIM command not implemented in this class (not recognized)
|
281
|
|
|
|
|
|
|
stWAIT wait, status update in progress
|
282
|
|
|
|
|
|
|
stINIT occurs only after new() and if no command has been issued yet
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=item data1, data2
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=back
|
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
Contents of data1 and data2 depends on the specific event:
|
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
Event data1 data2
|
291
|
|
|
|
|
|
|
------------------------------
|
292
|
|
|
|
|
|
|
evFREQ frequency undef
|
293
|
|
|
|
|
|
|
evMODE mode filter
|
294
|
|
|
|
|
|
|
evEDGE loedge hiedge
|
295
|
|
|
|
|
|
|
evSTAT undef undef
|
296
|
|
|
|
|
|
|
evUNKN commandbyte undef
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
The callback function should handle the received data (e.g. display it) and return without much delay. Currently there is no protection that the callback is not called again and again before returning. I.e. your callback function should be re-entrant.
|
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub set_callback {
|
303
|
|
|
|
|
|
|
my $self = shift;
|
304
|
|
|
|
|
|
|
$self->{CBACK} = shift;
|
305
|
|
|
|
|
|
|
};
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=pod
|
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
=head2 get_serioobject()
|
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
Returns the HAM::Device::IcomCIVSerialIO object which was initiated in an earlier instance of HAM::Device::IcomCIV. For use in a subsequent instance of this module for another radio on the same bus.
|
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=cut
|
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
sub get_serioobject {
|
316
|
|
|
|
|
|
|
my $self = shift;
|
317
|
|
|
|
|
|
|
return $self->{SEROBJ};
|
318
|
|
|
|
|
|
|
};
|
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=pod
|
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=head2 process_buffer( buffer )
|
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
This is the central routine which is called whenever a CI-V telegram has been received. It receives a byte buffer, filled with the entire CI-V telegram, including leading 0xFE 0xFE, but excluding the trailing 0xFD.
|
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
This basic class IcomCIV::Radio implements decoding of command responses which are supported by most Icom radios. That is:
|
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
get freq 0x00 or 0x03 received
|
329
|
|
|
|
|
|
|
get mode/filter 0x01 or 0x04 received
|
330
|
|
|
|
|
|
|
get Band edges 0x02 received
|
331
|
|
|
|
|
|
|
GOOD 0xFB received
|
332
|
|
|
|
|
|
|
NOGOOD 0xFA received
|
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
All other responses are not recognized by this class and should be handled by a descendant for an individual model. See HAM::Device::IcomICR8500 or HAM::Device::IcomICR75 for examples of descendant classes.
|
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
At the end of process_buffer the upper layer (application) is called by it's callback (if set).
|
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=cut
|
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub process_buffer {
|
341
|
|
|
|
|
|
|
my $self = shift;
|
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
#break datagram into bytes
|
344
|
|
|
|
|
|
|
my @bytes = unpack("C*", $_[0]);
|
345
|
|
|
|
|
|
|
my ($data1, $data2);
|
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
if ( ($bytes[4] eq 0x00) or ($bytes[4] eq 0x03) ) {
|
348
|
|
|
|
|
|
|
$self->{FREQ} = bcd2int(@bytes[5,6,7,8,9]);
|
349
|
|
|
|
|
|
|
$self->{STATUS} = stGOOD;
|
350
|
|
|
|
|
|
|
$self->{EVENT} = evFREQ;
|
351
|
|
|
|
|
|
|
$data1 = $self->{FREQ};
|
352
|
|
|
|
|
|
|
} elsif ( ($bytes[4] eq 0x01) or ($bytes[4] eq 0x04) ) {
|
353
|
|
|
|
|
|
|
( $self->{MODE}, $self->{FILTER} )
|
354
|
|
|
|
|
|
|
= ( icom2mode($bytes[5], $self->{MODEL}), icom2filter(@bytes[5,6]));
|
355
|
|
|
|
|
|
|
$self->{STATUS} = stGOOD;
|
356
|
|
|
|
|
|
|
$self->{EVENT} = evMODE;
|
357
|
|
|
|
|
|
|
$data1 = $self->{MODE};
|
358
|
|
|
|
|
|
|
$data2 = $self->{FILTER};
|
359
|
|
|
|
|
|
|
} elsif ( $bytes[4] eq 0x02 ) {
|
360
|
|
|
|
|
|
|
$self->{LOEDGE} = bcd2int(@bytes[5,6,7,8,9]);
|
361
|
|
|
|
|
|
|
$self->{HIEDGE} = bcd2int(@bytes[11,12,13,14,15]);
|
362
|
|
|
|
|
|
|
$self->{STATUS} = stGOOD;
|
363
|
|
|
|
|
|
|
$self->{EVENT} = evEDGE;
|
364
|
|
|
|
|
|
|
$data1 = $self->{LOEDGE};
|
365
|
|
|
|
|
|
|
$data2 = $self->{HIEDGE};
|
366
|
|
|
|
|
|
|
} elsif ( $bytes[4] eq 0xFA ) {
|
367
|
|
|
|
|
|
|
$self->{STATUS} = stFAIL;
|
368
|
|
|
|
|
|
|
$self->{EVENT} = evSTAT;
|
369
|
|
|
|
|
|
|
} elsif ( $bytes[4] eq 0xFB ) {
|
370
|
|
|
|
|
|
|
$self->{STATUS} = stGOOD;
|
371
|
|
|
|
|
|
|
$self->{EVENT} = evSTAT;
|
372
|
|
|
|
|
|
|
} else {
|
373
|
|
|
|
|
|
|
$self->{STATUS} = stFAIL;
|
374
|
|
|
|
|
|
|
$self->{EVENT} = evUNKN;
|
375
|
|
|
|
|
|
|
};
|
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
# call callback function of upper layer
|
378
|
|
|
|
|
|
|
if ( $self->{CBACK} ) {
|
379
|
|
|
|
|
|
|
&{ $self->{CBACK} }( $self->{EVENT}, $self->{STATUS}, $data1, $data2 );
|
380
|
|
|
|
|
|
|
};
|
381
|
|
|
|
|
|
|
};
|
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=pod
|
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=head2 frequency( [integer] )
|
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
Sets (when issued with parameter) or gets (without parameter) the frequency of a radio. Frequency is integer in Hz.
|
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
Setting the frequency uses the command 0x05 which yields a GOOD/NOGOOD response from the radio, so expect a status event after setting the frequency (if you use the event callback).
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
Alternatively there exists the method B which uses command 0x00, which does not yield a response from the radio. So you will not receive a status event or any feedback if your command was successful or not, but it's slightly faster.
|
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
Getting a frequency interrogates the radio and waits (blocks) until the radio returns the frequency (integer, in Hz). A later version will implement a timeout.
|
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
If events are used (if callback is set) you will also receive a evFREQ event. Before sending the query command to the radio the status is set to stWAIT, it will change to stGOOD if a frequency was received.
|
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=cut
|
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub frequency {
|
400
|
|
|
|
|
|
|
my $self = shift;
|
401
|
|
|
|
|
|
|
$self->{STATUS} = stWAIT;
|
402
|
|
|
|
|
|
|
if (@_) {
|
403
|
|
|
|
|
|
|
my $str = chr(0x05) . int2bcd( shift, 5 );
|
404
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
405
|
|
|
|
|
|
|
} else {
|
406
|
|
|
|
|
|
|
my $str = chr(0x03);
|
407
|
|
|
|
|
|
|
$self->{EVENT} = evNOEV;
|
408
|
|
|
|
|
|
|
my $res = $self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
409
|
|
|
|
|
|
|
while ( $self->{EVENT} != evFREQ ) {
|
410
|
|
|
|
|
|
|
# wait without timeout
|
411
|
|
|
|
|
|
|
};
|
412
|
|
|
|
|
|
|
return $self->{FREQ};
|
413
|
|
|
|
|
|
|
};
|
414
|
|
|
|
|
|
|
};
|
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
sub set_frequency {
|
417
|
|
|
|
|
|
|
# use command 0x00 to set freq without response from radio.
|
418
|
|
|
|
|
|
|
my $self = shift;
|
419
|
|
|
|
|
|
|
if (@_) {
|
420
|
|
|
|
|
|
|
my $str = chr(0x00) . int2bcd( shift, 5 );
|
421
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
422
|
|
|
|
|
|
|
};
|
423
|
|
|
|
|
|
|
};
|
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=pod
|
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=head2 mode( Modestring [, Filterstring] )
|
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
Sets (when issued with parameter) or gets (without parameter) the current mode and filter of the radio (USB. LSB, AM etc.). Optional parameter is filter, so mode and filter can be set with one call.
|
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
Setting the mode uses the command 0x06 which yields a GOOD/NOGOOD response from the radio, so expect a status event after setting the mode (if you use the event callback).
|
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
Alternatively there exists the method B which uses command 0x01, which does not yield a response from the radio. So you will not receive a status event or any feedback if your command was successful or not.
|
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Getting a mode interrogates the radio and waits (blocks) until a response is received. A later version will implement a timeout.
|
436
|
|
|
|
|
|
|
In scalar context only mode is returned, in list context mode and filter is returned. Returned strings are human readable like USB, LSB, AM etc., and NORMAL, NARROW or WIDE for filter. See IcomCIV::Support for all possible modes and filters.
|
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
If events are used (if callback is set) you will also receive a evMODE event. Before sending the query command to the radio the status is set to stWAIT, it will change to stGOOD if a mode/filter was received.
|
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=cut
|
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub mode {
|
443
|
|
|
|
|
|
|
my $self = shift;
|
444
|
|
|
|
|
|
|
$self->{STATUS} = stWAIT;
|
445
|
|
|
|
|
|
|
if (@_) {
|
446
|
|
|
|
|
|
|
$self->{MODE} = shift;
|
447
|
|
|
|
|
|
|
my $str = chr(0x06) . mode2icom( $self->{MODE} );
|
448
|
|
|
|
|
|
|
if (@_) {
|
449
|
|
|
|
|
|
|
$self->{FILTER} = shift;
|
450
|
|
|
|
|
|
|
$str .= filter2icom( $self->{MODE}, $self->{FILTER} )
|
451
|
|
|
|
|
|
|
};
|
452
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ($self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str);
|
453
|
|
|
|
|
|
|
} else {
|
454
|
|
|
|
|
|
|
my $str = chr(0x04);
|
455
|
|
|
|
|
|
|
$self->{EVENT} = evNOEV;
|
456
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ($self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str);
|
457
|
|
|
|
|
|
|
while ( $self->{EVENT} != evMODE ) {
|
458
|
|
|
|
|
|
|
# wait blocking
|
459
|
|
|
|
|
|
|
};
|
460
|
|
|
|
|
|
|
};
|
461
|
|
|
|
|
|
|
wantarray ? return ( $self->{MODE}, $self->{FILTER} ) : return $self->{MODE};
|
462
|
|
|
|
|
|
|
};
|
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
sub set_mode {
|
465
|
|
|
|
|
|
|
# Use command 0x01 to set mode without response
|
466
|
|
|
|
|
|
|
my $self = shift;
|
467
|
|
|
|
|
|
|
if (@_) {
|
468
|
|
|
|
|
|
|
$self->{MODE} = shift;
|
469
|
|
|
|
|
|
|
my $str = chr(0x01) . mode2icom( shift );
|
470
|
|
|
|
|
|
|
if (@_) {
|
471
|
|
|
|
|
|
|
$self->{FILTER} = shift;
|
472
|
|
|
|
|
|
|
$str .= filter2icom( $self->{MODE}, $self->{FILTER} )
|
473
|
|
|
|
|
|
|
};
|
474
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ($self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str);
|
475
|
|
|
|
|
|
|
};
|
476
|
|
|
|
|
|
|
};
|
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=pod
|
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=head2 status( )
|
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
Returns the current status, one of:
|
483
|
|
|
|
|
|
|
stGOOD all ok, command succeeded
|
484
|
|
|
|
|
|
|
stFAIL command to radio failed
|
485
|
|
|
|
|
|
|
stNOIM command not implemented in this class (not recognized)
|
486
|
|
|
|
|
|
|
stWAIT wait, status update in progress
|
487
|
|
|
|
|
|
|
stINIT occurs only after new() and if no command has been issued yet
|
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=cut
|
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
sub status {
|
492
|
|
|
|
|
|
|
my $self = shift;
|
493
|
|
|
|
|
|
|
return $self->{STATUS};
|
494
|
|
|
|
|
|
|
};
|
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=pod
|
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=head2 get_bandedges( )
|
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Returns the lower and upper frequency limit the radio does support. Frequencies are integer in Hz.
|
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=cut
|
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
sub get_bandedges {
|
505
|
|
|
|
|
|
|
my $self = shift;
|
506
|
|
|
|
|
|
|
my $str = chr(0x02);
|
507
|
|
|
|
|
|
|
$self->{EVENT} = evNOEV;
|
508
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
509
|
|
|
|
|
|
|
while ( $self->{EVENT} != evEDGE ) {
|
510
|
|
|
|
|
|
|
# wait blocking
|
511
|
|
|
|
|
|
|
};
|
512
|
|
|
|
|
|
|
return ( $self->{LOEDGE}, $self->{HIEDGE} );
|
513
|
|
|
|
|
|
|
};
|
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=pod
|
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=head2 select_vfo( [A|B|MAIN|SUB] )
|
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
Selects the VFO mode. If no parameter is given the previously selected VFO (A or B, Main or Sub) is selected. With parameter 'A', 'B', 'Main' or 'Sub' the respective VFO is selected. Works only with radio which have a VFO (not all do).
|
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
If successful status is set to stGOOD and a evSTAT event happens.
|
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=cut
|
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
sub select_vfo {
|
526
|
|
|
|
|
|
|
my $self = shift;
|
527
|
|
|
|
|
|
|
my $str = chr(0x07);
|
528
|
|
|
|
|
|
|
if (@_) {
|
529
|
|
|
|
|
|
|
if ( uc($_[0]) eq 'A') { $str .= chr(0x00) }
|
530
|
|
|
|
|
|
|
elsif ( uc($_[0]) eq 'B') { $str .= chr(0x01) }
|
531
|
|
|
|
|
|
|
elsif ( uc($_[0]) eq 'MAIN') { $str .= chr(0xD0) }
|
532
|
|
|
|
|
|
|
elsif ( uc($_[0]) eq 'SUB') { $str .= chr(0xD1) };
|
533
|
|
|
|
|
|
|
};
|
534
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
535
|
|
|
|
|
|
|
};
|
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=pod
|
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=head2 equal_vfo( )
|
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
Equalizes VFO A and VFO B. If successful status is set to stGOOD and a evSTAT event happens. Works only with radios which have a VFO (not all do).
|
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=cut
|
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
sub equal_vfo {
|
546
|
|
|
|
|
|
|
my $self = shift;
|
547
|
|
|
|
|
|
|
my $str = chr(0x07) . chr(0xA0);
|
548
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
549
|
|
|
|
|
|
|
};
|
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=head2 exchange_vfo( )
|
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
Swaps VFO A and VFO B. If successful status is set to stGOOD and a evSTAT event happens. Works only with radios which have a VFO (not all do).
|
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
=cut
|
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
sub exchange_vfo {
|
558
|
|
|
|
|
|
|
my $self = shift;
|
559
|
|
|
|
|
|
|
my $str = chr(0x07) . chr(0xB0);
|
560
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
561
|
|
|
|
|
|
|
};
|
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
=pod
|
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
=head2 equal_mainsub( )
|
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
Swaps VFOs MAIN and SUB. If successful status is set to stGOOD and a evSTAT event happens. Works only with radios which have a Main/Sub VFO (not all do).
|
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=cut
|
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
sub equal_mainsub {
|
572
|
|
|
|
|
|
|
my $self = shift;
|
573
|
|
|
|
|
|
|
my $str = chr(0x07) . chr(0xB1);
|
574
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
575
|
|
|
|
|
|
|
};
|
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
=pod
|
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=head2 select_mem( [number] )
|
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
Selects memory mode, previously used memory channel if no parameter is given. Or selects memory channel number if parameter is provided.
|
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
If successful status is set to stGOOD and a evSTAT event happens.
|
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=cut
|
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
sub select_mem {
|
588
|
|
|
|
|
|
|
my $self = shift;
|
589
|
|
|
|
|
|
|
my $str = chr(0x08);
|
590
|
|
|
|
|
|
|
if (@_) {
|
591
|
|
|
|
|
|
|
my $n = reverse int2bcd( shift,2 );
|
592
|
|
|
|
|
|
|
$str .= $n;
|
593
|
|
|
|
|
|
|
};
|
594
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
595
|
|
|
|
|
|
|
};
|
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
=pod
|
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=head2 write_mem( )
|
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
Writes the currently displayed frequency and mode to the currently selected VFO. If the selected memory is not empty the previous contents is overwritten.
|
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
If successful status is set to stGOOD and a evSTAT event happens.
|
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=cut
|
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
sub write_mem {
|
608
|
|
|
|
|
|
|
my $self = shift;
|
609
|
|
|
|
|
|
|
my $str = chr(0x09);
|
610
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
611
|
|
|
|
|
|
|
};
|
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=pod
|
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
=head2 xfer_mem( )
|
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
Transfers the contents (Frequency, Mode) of currently selected memory channel to the VFO. Please note that this command behaves differently, depending whether the radio is currently in Memory or VFO mode. Check the authors website for more details.
|
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
If successful status is set to stGOOD and a evSTAT event happens.
|
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=cut
|
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
sub xfer_mem {
|
624
|
|
|
|
|
|
|
my $self = shift;
|
625
|
|
|
|
|
|
|
my $str = chr(0x0A);
|
626
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
627
|
|
|
|
|
|
|
};
|
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=pod
|
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
=head2 clear_mem( )
|
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
Erases the currently selected memory channel. With most radios this works only if the radio is currently in memory mode and fails (status stFAIL) when in VFO Mode.
|
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
If successful status is set to stGOOD and a evSTAT event happens.
|
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=cut
|
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
sub clear_mem {
|
640
|
|
|
|
|
|
|
my $self = shift;
|
641
|
|
|
|
|
|
|
my $str = chr(0x0B);
|
642
|
|
|
|
|
|
|
$self->{SEROBJ}->send_civ( $self->{CIV_ADRESS}, $self->{OWN_ADRESS}, $str );
|
643
|
|
|
|
|
|
|
};
|
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
##########################################################################
|
646
|
|
|
|
|
|
|
# Support functions
|
647
|
|
|
|
|
|
|
# all called as class functions, i.e. without self
|
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=pod
|
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=head1 CLASS FUNCTIONS
|
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
These functions are used internally.
|
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=head2 int2bcd( frequency, want_nr_of_bytes )
|
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
Converts an integer number to a BCD string as used with the CI-V protocol. Length of BCD string is want_nr_of_bytes.
|
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
Frequency as integer, BCD string is LSB first. If frequency results in a longer BCD string than requested by want_nr_of_bytes it is cut off.
|
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
=cut
|
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
sub int2bcd(@) {
|
664
|
|
|
|
|
|
|
my ($int, $nrb) = @_;
|
665
|
|
|
|
|
|
|
my $v = '0000000000' . sprintf('%u',$int);
|
666
|
|
|
|
|
|
|
my $r = '';
|
667
|
|
|
|
|
|
|
my $i;
|
668
|
|
|
|
|
|
|
for ($i=-1; $i>=-5; $i--) {
|
669
|
|
|
|
|
|
|
$r .= chr(hex('0x'.substr($v,$i*2,2)) );
|
670
|
|
|
|
|
|
|
}
|
671
|
|
|
|
|
|
|
return substr($r, 0, $nrb);
|
672
|
|
|
|
|
|
|
};
|
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=pod
|
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=head2 bcd2int( List_of_BCD_bytes )
|
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Converts a list of BCD bytes to an integer number. List of BCD bytes must be LSB first, as used with the Icom CI-V protocol. Returns the resulting frequency as integer.
|
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=cut
|
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
sub bcd2int(@) {
|
684
|
|
|
|
|
|
|
my $f = 0;
|
685
|
|
|
|
|
|
|
my $i = 0;
|
686
|
|
|
|
|
|
|
foreach my $b (@_) {
|
687
|
|
|
|
|
|
|
$f += ( ($b & 0x0F) + ((($b & 0xF0) >>4 ) * 10)) * 100**$i;
|
688
|
|
|
|
|
|
|
$i++;
|
689
|
|
|
|
|
|
|
};
|
690
|
|
|
|
|
|
|
return $f;
|
691
|
|
|
|
|
|
|
};
|
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=pod
|
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=head2 icom2mode( mode, model )
|
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
Converts mode byte as used by the Icom CI-V protocol to a readable string. Returns the mode string.
|
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
Valid modes are (depending on radio):
|
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
LSB, USB,
|
703
|
|
|
|
|
|
|
AM, S-AM,
|
704
|
|
|
|
|
|
|
CW, CW-R,
|
705
|
|
|
|
|
|
|
RTTY, RTTY-R,
|
706
|
|
|
|
|
|
|
FM, WFM,
|
707
|
|
|
|
|
|
|
PSK, PSK-R
|
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
=cut
|
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
#################################################
|
712
|
|
|
|
|
|
|
# Valid for normal mode command, not for modes in a memory
|
713
|
|
|
|
|
|
|
my %icommodes = (
|
714
|
|
|
|
|
|
|
0x00 => 'LSB',
|
715
|
|
|
|
|
|
|
0x01 => 'USB',
|
716
|
|
|
|
|
|
|
0x02 => 'AM',
|
717
|
|
|
|
|
|
|
0x03 => 'CW',
|
718
|
|
|
|
|
|
|
0x04 => 'RTTY',
|
719
|
|
|
|
|
|
|
0x05 => 'FM',
|
720
|
|
|
|
|
|
|
0x06 => 'WFM',
|
721
|
|
|
|
|
|
|
0x07 => 'CW-R',
|
722
|
|
|
|
|
|
|
0x08 => 'RTTY-R',
|
723
|
|
|
|
|
|
|
0x11 => 'S-AM',
|
724
|
|
|
|
|
|
|
0x12 => 'PSK',
|
725
|
|
|
|
|
|
|
0x13 => 'PSK-R',
|
726
|
|
|
|
|
|
|
);
|
727
|
|
|
|
|
|
|
my %revicommodes = (
|
728
|
|
|
|
|
|
|
'LSB' => 0x00,
|
729
|
|
|
|
|
|
|
'USB' => 0x01,
|
730
|
|
|
|
|
|
|
'AM' => 0x02,
|
731
|
|
|
|
|
|
|
'CW' => 0x03,
|
732
|
|
|
|
|
|
|
'RTTY' => 0x04,
|
733
|
|
|
|
|
|
|
'FM' => 0x05,
|
734
|
|
|
|
|
|
|
'WFM' => 0x06,
|
735
|
|
|
|
|
|
|
'CW-R' => 0x07,
|
736
|
|
|
|
|
|
|
'RTTY-R' => 0x08,
|
737
|
|
|
|
|
|
|
'S-AM' => 0x11,
|
738
|
|
|
|
|
|
|
'PSK' => 0x12,
|
739
|
|
|
|
|
|
|
'PSK-R' => 0x13,
|
740
|
|
|
|
|
|
|
'SSB' => 0x05,
|
741
|
|
|
|
|
|
|
);
|
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
sub icom2mode {
|
744
|
|
|
|
|
|
|
my $mode = shift;
|
745
|
|
|
|
|
|
|
my $model = shift;
|
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
if (($mode==0x05) and ($model eq 'IC-R7000')) {
|
748
|
|
|
|
|
|
|
return 'SSB';
|
749
|
|
|
|
|
|
|
}
|
750
|
|
|
|
|
|
|
else {
|
751
|
|
|
|
|
|
|
return $icommodes{$mode};
|
752
|
|
|
|
|
|
|
};
|
753
|
|
|
|
|
|
|
};
|
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
=pod
|
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=head2 mode2icom( mode_string )
|
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
Reverse of above, converts mode string to Icom byte. Returns one mode byte as chr. If mode is not recognized the invalid modebyte '0x99' is returned. If you do not check the return value and send this to the radio, it will respond with a stFAIL status.
|
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
Valid modes: see icom2mode
|
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=cut
|
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
sub mode2icom($) {
|
766
|
|
|
|
|
|
|
my $mode = shift;
|
767
|
|
|
|
|
|
|
return (exists($revicommodes{uc($mode)})) ? chr($revicommodes{uc($mode)}) : chr(0x99);
|
768
|
|
|
|
|
|
|
};
|
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
=pod
|
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
=head2 icom2filter( mode, filter )
|
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
Converts two bytes from the Icom CI-V protocol to Filter width
|
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
Returned filters are:
|
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
NORMAL
|
781
|
|
|
|
|
|
|
NARROW
|
782
|
|
|
|
|
|
|
WIDE
|
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
=cut
|
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
my %icomfilters = (
|
787
|
|
|
|
|
|
|
0x01 => 'WIDE',
|
788
|
|
|
|
|
|
|
0x02 => 'NORMAL',
|
789
|
|
|
|
|
|
|
0x03 => 'NARROW',
|
790
|
|
|
|
|
|
|
);
|
791
|
|
|
|
|
|
|
my %revicomfilters = (
|
792
|
|
|
|
|
|
|
'WIDE' => 0x01,
|
793
|
|
|
|
|
|
|
'NORMAL' => 0x02,
|
794
|
|
|
|
|
|
|
'NARROW' => 0x03,
|
795
|
|
|
|
|
|
|
);
|
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
sub icom2filter(@) {
|
798
|
|
|
|
|
|
|
my ($m, $f) = @_;
|
799
|
|
|
|
|
|
|
if (($m ne 0x02) and ($m ne 0x11)) {
|
800
|
|
|
|
|
|
|
$f++; #when mode not AM shift filterbytes to match table
|
801
|
|
|
|
|
|
|
};
|
802
|
|
|
|
|
|
|
return $icomfilters{ $f };
|
803
|
|
|
|
|
|
|
};
|
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
=pod
|
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
=head2 filter2icom( mode_string, filter_string )
|
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
Converts mode and filter string to Icom filter byte. Requires mode byte as well, because possible filters depend on mode. Returns filter byte as chr. Please note that an invalid filter is not mapped to an invalid code but to 'Normal'.
|
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
Valid mode strings: depending on radio, also see icom2mode
|
814
|
|
|
|
|
|
|
Valid filter strings:
|
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
NORMAL
|
817
|
|
|
|
|
|
|
NARROW
|
818
|
|
|
|
|
|
|
WIDE
|
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=cut
|
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
sub filter2icom(@) {
|
823
|
|
|
|
|
|
|
my ($m, $f) = @_;
|
824
|
|
|
|
|
|
|
$m = uc($m);
|
825
|
|
|
|
|
|
|
$f = uc($f);
|
826
|
|
|
|
|
|
|
if (($m eq 'AM') or ($m eq 'S-AM')) {
|
827
|
|
|
|
|
|
|
if ($f eq 'WIDE') {return chr(0x01)}
|
828
|
|
|
|
|
|
|
elsif ($f eq 'NORMAL') {return chr(0x02)}
|
829
|
|
|
|
|
|
|
elsif ($f eq 'NARROW') {return chr(0x03)}
|
830
|
|
|
|
|
|
|
else {return chr(0x02)} #default normal
|
831
|
|
|
|
|
|
|
}
|
832
|
|
|
|
|
|
|
else { #non-am
|
833
|
|
|
|
|
|
|
if ($f eq 'NORMAL') {return chr(0x01)}
|
834
|
|
|
|
|
|
|
elsif ($f eq 'NARROW') {return chr(0x02)}
|
835
|
|
|
|
|
|
|
else {return chr(0x01)}; #default normal
|
836
|
|
|
|
|
|
|
};
|
837
|
|
|
|
|
|
|
};
|
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
=pod
|
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
=head2 get_civ_adress( model )
|
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
Returns the default CI-V bus adress for a model if found (as integer), otherwise undef.
|
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
Valid models are:
|
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
IC-1271 IC-707 IC-7400 IC-7800 IC-R9000
|
848
|
|
|
|
|
|
|
IC-1275 IC-718 IC-746PRO IC-820 IC-R9500
|
849
|
|
|
|
|
|
|
IC-271 IC-725 IC-751A IC-821 IC-X3
|
850
|
|
|
|
|
|
|
IC-275 IC-726 IC-756 IC-910
|
851
|
|
|
|
|
|
|
IC-375 IC-725 IC-756PRO IC-970
|
852
|
|
|
|
|
|
|
IC-471 IC-726 IC-756PRO2 IC-R10
|
853
|
|
|
|
|
|
|
IC-475 IC-728 IC-756PRO3 IC-R20
|
854
|
|
|
|
|
|
|
IC-575 IC-729 IC-761 IC-R71
|
855
|
|
|
|
|
|
|
IC-7000 IC-735 IC-765 IC-R72
|
856
|
|
|
|
|
|
|
IC-703 IC-736 IC-7700 IC-R75
|
857
|
|
|
|
|
|
|
IC-706 IC-737 IC-775 IC-R7000
|
858
|
|
|
|
|
|
|
IC-706 MK2 IC-738 IC-78 IC-R7100
|
859
|
|
|
|
|
|
|
IC-706 MK2G IC-746 IC-781 IC-R8500
|
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
=cut
|
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
my %icommodels = (
|
865
|
|
|
|
|
|
|
'IC-1271' => 0x24,
|
866
|
|
|
|
|
|
|
'IC-1275' => 0x18,
|
867
|
|
|
|
|
|
|
'IC-271' => 0x20,
|
868
|
|
|
|
|
|
|
'IC-275' => 0x10,
|
869
|
|
|
|
|
|
|
'IC-375' => 0x12,
|
870
|
|
|
|
|
|
|
'IC-471' => 0x22,
|
871
|
|
|
|
|
|
|
'IC-475' => 0x14,
|
872
|
|
|
|
|
|
|
'IC-575' => 0x16,
|
873
|
|
|
|
|
|
|
'IC-7000' => 0x70,
|
874
|
|
|
|
|
|
|
'IC-703' => 0x68,
|
875
|
|
|
|
|
|
|
'IC-706' => 0x48,
|
876
|
|
|
|
|
|
|
'IC-706 MK2' => 0x4e,
|
877
|
|
|
|
|
|
|
'IC-706 MK2G' => 0x58,
|
878
|
|
|
|
|
|
|
'IC-707' => 0x3e,
|
879
|
|
|
|
|
|
|
'IC-718' => 0x5E,
|
880
|
|
|
|
|
|
|
'IC-725' => 0x28,
|
881
|
|
|
|
|
|
|
'IC-726' => 0x30,
|
882
|
|
|
|
|
|
|
'IC-728' => 0x38,
|
883
|
|
|
|
|
|
|
'IC-729' => 0x3A,
|
884
|
|
|
|
|
|
|
'IC-735' => 0x04,
|
885
|
|
|
|
|
|
|
'IC-736' => 0x40,
|
886
|
|
|
|
|
|
|
'IC-737' => 0x3C,
|
887
|
|
|
|
|
|
|
'IC-738' => 0x44,
|
888
|
|
|
|
|
|
|
'IC-746' => 0x56,
|
889
|
|
|
|
|
|
|
'IC-7400' => 0x66,
|
890
|
|
|
|
|
|
|
'IC-746PRO' => 0x66,
|
891
|
|
|
|
|
|
|
'IC-751A' => 0x1c,
|
892
|
|
|
|
|
|
|
'IC-756' => 0x50,
|
893
|
|
|
|
|
|
|
'IC-756PRO' => 0x5C,
|
894
|
|
|
|
|
|
|
'IC-756PRO2' => 0x64,
|
895
|
|
|
|
|
|
|
'IC-756PRO3' => 0x6e,
|
896
|
|
|
|
|
|
|
'IC-761' => 0x1e,
|
897
|
|
|
|
|
|
|
'IC-765' => 0x2c,
|
898
|
|
|
|
|
|
|
'IC-7700' => 0x76, # assumed
|
899
|
|
|
|
|
|
|
'IC-775' => 0x46,
|
900
|
|
|
|
|
|
|
'IC-78' => 0x62,
|
901
|
|
|
|
|
|
|
'IC-781' => 0x26,
|
902
|
|
|
|
|
|
|
'IC-7800' => 0x6a,
|
903
|
|
|
|
|
|
|
'IC-820' => 0x42,
|
904
|
|
|
|
|
|
|
'IC-821' => 0x4c,
|
905
|
|
|
|
|
|
|
'IC-910' => 0x60,
|
906
|
|
|
|
|
|
|
'IC-970' => 0x2e,
|
907
|
|
|
|
|
|
|
'IC-R10' => 0x52,
|
908
|
|
|
|
|
|
|
'IC-R20' => 0x6c,
|
909
|
|
|
|
|
|
|
'IC-R71' => 0x1a,
|
910
|
|
|
|
|
|
|
'IC-R72' => 0x32,
|
911
|
|
|
|
|
|
|
'IC-R75' => 0x5a,
|
912
|
|
|
|
|
|
|
'IC-R7000' => 0x08,
|
913
|
|
|
|
|
|
|
'IC-R7100' => 0x34,
|
914
|
|
|
|
|
|
|
'IC-R8500' => 0x4a,
|
915
|
|
|
|
|
|
|
'IC-R9000' => 0x2a,
|
916
|
|
|
|
|
|
|
'IC-R9500' => 0x72,
|
917
|
|
|
|
|
|
|
'IC-X3' => 0x74, # assumed
|
918
|
|
|
|
|
|
|
);
|
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
sub get_civ_adress {
|
921
|
|
|
|
|
|
|
my $model = uc( shift );
|
922
|
|
|
|
|
|
|
if ( exists $icommodels{ $model } ) {
|
923
|
|
|
|
|
|
|
return $icommodels{ $model } ;
|
924
|
|
|
|
|
|
|
} else {
|
925
|
|
|
|
|
|
|
return undef;
|
926
|
|
|
|
|
|
|
};
|
927
|
|
|
|
|
|
|
};
|
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
=pod
|
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
=head1 SEE ALSO
|
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
HAM::Device::IcomCIVSerialIO
|
935
|
|
|
|
|
|
|
HAM::Device::IcomICR8500
|
936
|
|
|
|
|
|
|
HAM::Device::IcomICR75
|
937
|
|
|
|
|
|
|
and probably other IcomCIV modules
|
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
Icom CI-V Protocol Specification by Icom
|
940
|
|
|
|
|
|
|
Documentation of the CI-V protocol in any recent Icom radio manual
|
941
|
|
|
|
|
|
|
Documentation of the CI-V protocol at the authors website
|
942
|
|
|
|
|
|
|
http://www.df4or.de
|
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
If you are looking for a library which supports more radios than just Icoms, look for 'grig' or 'hamlib'.
|
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
=head1 AUTHOR
|
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
Ekkehard (Ekki) Plicht, DF4OR, Eekki@plicht.deE
|
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
Copyright (c) 2007 Ekkehard (Ekki) Plicht. All rights reserved.
|
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
955
|
|
|
|
|
|
|
modify it under the same terms as Perl itself.
|
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
=cut
|
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
1;
|
960
|
|
|
|
|
|
|
__END__
|