File Coverage

blib/lib/HAM/Device/IcomCIV.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


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__