-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathIC746.cpp
805 lines (685 loc) · 26.7 KB
/
IC746.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
/*************************************************************************
IC746 CAT Library, by KK4DAS, Dean Souleles
V1.3 1/21/2023
- Included all defined modes in Get/Set Mode (thanks to Mike, KA4CDN)
- Fix to allow frequencies greater than 100MHz -(thanks to Bob, KA1GT)
V1.2 10/22/2021
- Fixes bug that caused "WSJTX Rig Control Error" for versions after 2.2.2
- The problem was caused by an unsupported CIV command being sent by WSJTX
- This fix responds to unimplemented commands with a NACK instead of an ACK
- Bug was reported on the WSJTX groups board.
V1.1 2/3/2021
- various fixes, now works properly with OmniRig and flrig
- smeter now returns proper BCD code - calibrated to emulate ICOM responses
V1.0 1/24/2021
- Initial build
Inspired by: ft857d CAT Library, by Pavel Milanes, CO7WT, pavelmc@gmail.com
Emulates an ICOM 746 CAT functionality to work with all ham radio software that include CAT control
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***************************************************************************/
#include "Arduino.h"
#include "IC746.h"
//#define DEBUG_CAT
#ifdef DEBUG_CAT
#define DEBUG_CAT_DETAIL
//#define DEBUG_CAT_SMETER
#include <SoftwareSerial.h>
SoftwareSerial catDebug(11,12); // RX, TX
String dbg;
#endif
//extern void displayBanner(String s);
// User supplied calback function work variables, must be static
static FuncPtrBoolean catSplit;
static FuncPtrBoolean catSetPtt;
static FuncPtrVoidBoolean catGetPtt;
static FuncPtrVoidLong catGetFreq;
static FuncPtrLong catSetFreq;
static FuncPtrVoidByte catGetMode;
static FuncPtrByte catSetMode;
static FuncPtrVoidByte catGetSmeter;
static FuncPtrByte catSetVFO;
static FuncPtrVoid catAtoB;
static FuncPtrVoid catSwapVfo;
// Command indices
//
// Command structure after preamble and EOM have been discarded
// |56|E0|cmd|sub-cmd|data
// 56 - fixed transceiver address for IC746
// E0 - fixed cat xontroller address
// The sub-command field varies by command. For somme commands the sub-cmd field is not supplied and the data
// begins immediatedly follwing the command.
//
// The following are the array indedes within the command buffer for command elements that are sent with the command
// or are where we put data to return to the conroller
//
#define CAT_IX_TO_ADDR 0
#define CAT_IX_FROM_ADDR 1
#define CAT_IX_CMD 2
#define CAT_IX_SUB_CMD 3
#define CAT_IX_FREQ 3 // Set Freq has no sub-command
#define CAT_IX_MODE 3 // Get mode has no sub-command
#define CAT_IX_TUNE_STEP 3 // Get step has no sub-command
#define CAT_IX_ANT_SEL 3 // Get amt has no sub-command
#define CAT_IX_PTT 4 // PTT RX/TX indicator
#define CAT_IX_IF_FILTER 4 // IF Filter value
#define CAT_IX_SMETER 4 // S Meter 0-255
#define CAT_IX_SQUELCH 4 // Squelch 0=close, 1= open
#define CAT_IX_ID 5
#define CAT_IX_DATA 4 // Data following sub-comand
// Lentgth of commands that request data
#define CAT_RD_LEN_NOSUB 3 // 3 bytes - 56 E0 cc
#define CAT_RD_LEN_SUB 4 // 4 bytes - 56 E0 cc ss (cmd, sub command)
// Length of data responses
#define CAT_SZ_SMETER 6 // 6 bytes - E0 56 15 02 nn nn
#define CAT_SZ_SQUELCH 5 // 5 bytes - E0 56 15 01 nn
#define CAT_SZ_PTT 5 // 5 bytes - E0 56 1C 00 nn
#define CAT_SZ_FREQ 8 // 8 bytes - E0 56 03 ff ff ff ff ff (frequency in little endian BCD)
#define CAT_SZ_MODE 5 // 5 bytes - E0 56 04 mm ff (mode, then filter)
#define CAT_SZ_IF_FILTER 5 // 5 bytes - E0 56 1A 03 nn
#define CAT_SZ_TUNE_STEP 4 // 4 bytes - E0 56 10 nn
#define CAT_SZ_ANT_SEL 4 // 4 bytes - E0 56 12 nn
#define CAT_SZ_ID 5 // 5 bytes - E0 56 19 00 56 (returns RIG ID)
#define CAT_SZ_UNIMP_1B 5 // 5 bytes - E0 56 NN SS 00 (unimplemented commands that require 1 data byte
#define CAT_SZ_UNIMP_2B 6 // 6 bytes - EO 56 NN SS 00 00 (unimplemented commandds that required 2 data bytes
/*
Contructor, simple constructor, it initiates the serial port in the
default mode for the radio: 9600 @ 8N2
*/
void IC746::begin() {
Serial.begin(9600, SERIAL_8N2);
// while (!Serial);;
// Serial.flush();
#ifdef DEBUG_CAT
catDebug.begin(9600);
dbg = "CAT Debug Ready:";
catDebug.println(dbg.c_str());
#endif
}
// Alternative initializer with a custom baudrate and mode
void IC746::begin(long br, int mode) {
/*
Allowed Arduino modes for the serial:
SERIAL_5N1; SERIAL_6N1; SERIAL_7N1; SERIAL_8N1; SERIAL_5N2; SERIAL_6N2;
SERIAL_7N2; SERIAL_8N2; SERIAL_5E1; SERIAL_6E1; SERIAL_7E1; SERIAL_8E1;
SERIAL_5E2; SERIAL_6E2; SERIAL_7E2; SERIAL_8E2; SERIAL_5O1; SERIAL_6O1;
SERIAL_7O1; SERIAL_8O1; SERIAL_5O2; SERIAL_6O2; SERIAL_7O2; SERIAL_8O2
*/
Serial.begin(br, mode);
// Serial.flush();
#ifdef DEBUG_CAT
catDebug.begin(9600);
dbg = "CAT Debug Ready";
catDebug.println(dbg.c_str());
delay(1000);
catDebug.println(dbg.c_str());
catDebug.println(dbg.c_str());
catDebug.println(dbg.c_str());
#endif
}
/*
Linking user supplied callback functions
*/
// PTT
void IC746::addCATPtt(void (*userFunc)(boolean)) {
catSetPtt = userFunc;
}
// Split
void IC746::addCATsplit(void (*userFunc)(boolean)) {
catSplit = userFunc;
}
// VFO A=B - set both VFOs to be the same as the active VFO
void IC746::addCATAtoB(void (*userFunc)(void)) {
catAtoB = userFunc;
}
// Swap Active VFO
void IC746::addCATSwapVfo(void (*userFunc)(void)) {
catSwapVfo = userFunc;
}
// Get the freq of operation, the function must return the freq
void IC746::addCATGetFreq(long (*userFunc)(void)) {
catGetFreq = userFunc;
}
// Get the mode of operation, the function must return the mode
void IC746::addCATGetMode(byte (*userFunc)(void)) {
catGetMode = userFunc;
}
// GetPTT - function must return true for Tx and false for Rx
void IC746::addCATGetPtt(boolean (*userFunc)(void)) {
catGetPtt = userFunc;
}
// S meter (user function must return 0-15)
void IC746::addCATSMeter(byte (*userFunc)(void)) {
catGetSmeter = userFunc;
}
// Set Frequency - user function must accept a long as the freq in hz
void IC746::addCATFSet(void (*userFunc)(long)) {
catSetFreq = userFunc;
}
// Set Mode - user function must interpret MODE per the constants in IC746.h
void IC746::addCATMSet(void (*userFunc)(byte)) {
catSetMode = userFunc;
}
// SEt VFOA or B - user function must interpret VFO per the constants in IC746.h
void IC746::addCATVSet(void (*userFunc)(byte)) {
catSetVFO = userFunc;
}
////////////////////////////////////////////////////////////////////////////////
// Protocol Message Handling
////////////////////////////////////////////////////////////////////////////////
//
// Send a message back to CAT controller
// Format PREAMBLE, PREAMBLE, MSG, EOM
//
void IC746::send(byte *buf, int len) {
int i;
Serial.write(CAT_PREAMBLE);
Serial.write(CAT_PREAMBLE);
for (i = 0; i < len; i++) {
Serial.write(buf[i]);
}
Serial.write(CAT_EOM);
#ifdef DEBUG_CAT_DETAIL
dbg = "sent: ";
dbg += String(len);
dbg += ": ";
for (int i = 0; i < len; i++) {
dbg += String(buf[i], HEX);
dbg += " ";
}
catDebug.println(dbg.c_str());
#endif
}
//
// sendResponse
//
void IC746::sendResponse(byte *buf, int len) {
buf[CAT_IX_FROM_ADDR] = CAT_RIG_ADDR;
buf[CAT_IX_TO_ADDR] = CAT_CTRL_ADDR;
send(buf, len);
}
//
// sendAck() - send back hard-code acknowledge message
//
void IC746::sendAck() {
byte ack[] = {CAT_RIG_ADDR, CAT_CTRL_ADDR, CAT_ACK};
send(ack, 3);
}
//
// sendNack() - send back hard-code negative-acknowledge message
//
void IC746::sendNack() {
byte nack[] = {CAT_RIG_ADDR, CAT_CTRL_ADDR, CAT_NACK};
// displayBanner(String("Nack"));
send(nack, 3);
}
/*
readCMD - state machine to receive a command from the controller
States:
CAT_RCV_WAITING - scan incoming serial data for first preamble byte
CAT_RCV_INIT - second premable byte confirms start of message
CAT_RCV_RECEIVING - fill command buffer until EOM received
Command format
|FE|FE|56|E0|cmd|sub-cmd|data|FD|
FE FE = preamble, FD = end of command
56 = transceiver default address for IC746 (unused)
E0 = CAT controller default address (unused)
Upon successful receipt of EOM, protocol requires echo back of enitre message
On interrupted preamble or buffer overflow (no EOM received), send NAK
On successful receipt of a command the global array cmdBuf
will have the received CAT command (without the preamble and EOM)
*/
boolean IC746::readCmd() {
byte bt;
boolean cmdRcvd = false;
while (Serial.available() && !cmdRcvd) {
bt = byte(Serial.read());
switch (rcvState) {
case CAT_RCV_WAITING: // scan for start of new command
if (bt == CAT_PREAMBLE) {
rcvState = CAT_RCV_INIT;
}
break;
case CAT_RCV_INIT: // check for second preamble byte
if (bt == CAT_PREAMBLE) {
rcvState = CAT_RCV_RECEIVING;
} else { // error - should not happen, reset and report
rcvState = CAT_RCV_WAITING;
bytesRcvd = 0;
sendNack();
}
break;
case CAT_RCV_RECEIVING:
switch (bt) {
case CAT_EOM: // end of message received, return for processing, reset state
#ifdef DEBUG_CAT_DETAIL
dbg = "rcvd: ";
dbg += String(bytesRcvd);
dbg += ": ";
for (int i = 0; i < bytesRcvd; i++) {
dbg += String(cmdBuf[i], HEX);
dbg += " ";
}
catDebug.println(dbg.c_str());
#endif
send(cmdBuf, bytesRcvd); // echo received packet for protocol
cmdRcvd = true;
rcvState = CAT_RCV_WAITING;
cmdLength = bytesRcvd;
bytesRcvd = 0;
break;
default: // fill command buffer
if (bytesRcvd <= CAT_CMD_BUF_LENGTH) {
cmdBuf[bytesRcvd] = bt;
bytesRcvd++;
} else { // overflow - should not happen reset for new comand
rcvState = CAT_RCV_WAITING;
bytesRcvd = 0;
sendNack(); // report error
}
break;
}
break;
}
}
return cmdRcvd;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// COMMAND PROCESSORS
//
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doSmeter() - process the CAT_READ_SMETER Command
// This command has two sub-commands, SMETER and SQUELCH, only SMETER is fully implemented
//
// The SMETER sub command requests the current Return S-meter readingscaled for IC 746 appropriate values
// It calls a user function returns 0-15 S0-9, +10, +20, +30, +40, +50 +60
// The mapping of s-units to IC746 equivalents done imperically using the CatBkt cat test program
//
// The second sub-command, SQUELCH request wheterh Squelch is open or closed. Return a fixed value
// of "Open" to keep the protocol happly
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doSmeter() {
switch (cmdBuf[CAT_IX_SUB_CMD]) {
case CAT_READ_SUB_SMETER:
if (catGetSmeter) {
//S0 S1 S2 S3 S4 S5 S6 S7 S8 S9 +10 +20 +30 +40 +50 +60
const byte smap[] = {0, 15, 25, 40, 55, 65, 75, 90, 100, 120, 135, 150, 170, 190, 210, 241};
byte s = catGetSmeter();
SmetertoBCD(smap[s]);
#ifdef DEBUG_CAT_DETAIL
dbg = "doSmeter- s:";
dbg += String(s);
dbg += " map: ";
dbg += String(smap[s]);
dbg += " BCD: ";
for (int i = CAT_IX_SMETER; i < CAT_IX_SMETER+2; i++) {
dbg += String(cmdBuf[i], HEX);
dbg += " ";
}
catDebug.println(dbg.c_str());
#endif
} else {
cmdBuf[CAT_IX_SMETER] = 0; // user has not supplied S Meter function - keep the protocol happy
cmdBuf[CAT_IX_SMETER + 1] = 0;
}
sendResponse(cmdBuf, CAT_SZ_SMETER);
break;
case CAT_READ_SUB_SQL: // Squelch condition 0=closed, 1=open
cmdBuf[CAT_IX_SQUELCH] = 1;
send(cmdBuf, CAT_SZ_SQUELCH);
break;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doPtt() - process the CAT_PTT Command
// CAT_PTT calls the user supplied functions to either put the rig in to Tx or Rx or to request the rigs
// current Tx/Rx state
//
// A "read ptt" request does not have a data byte and is therefore one byte shorter
// than a "set ptt" request.
// 56 | E0 | 1C | 00 - Read request 1C is PTT command, sub-command 0 is uused, no data byte
// 56 | E0 | 1C | 00 | 01 - Set Tx, trailing data bit 1 for Tx, 0 for Rx
////////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doPtt() {
if (cmdLength == CAT_RD_LEN_SUB) { // Read request
if (catGetPtt) {
cmdBuf[CAT_IX_PTT] = catGetPtt();
sendResponse(cmdBuf, CAT_SZ_PTT);
}
} else { // Set request
if (catSetPtt) {
if (cmdBuf[CAT_IX_PTT] == CAT_PTT_TX) {
catSetPtt(true);
} else {
catSetPtt(false);
}
}
sendAck(); // always acknowledge "set" commands
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doSplit() - process the CAT_SPLIT Command
// Call user supplied function to turn split on or off
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doSplit() {
switch (cmdBuf[CAT_IX_SUB_CMD]) {
case CAT_SPLIT_OFF:
if (catSplit) {
catSplit(false);
}
break;
case CAT_SPLIT_ON:
case CAT_SIMPLE_DUP:
if (catSplit) {
catSplit(true);
}
default:
break;
}
sendAck();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doSetVfo() - process the CAT_SET_VFO Command, calls user supplied functions
// SET_VFO with no sub-command selects VFO Tuning vice Memory Tuning (memory tuning is not implemented)
// SET_VFO has four sub-commands:
// VFOA or VFOB - directs the rig to make the selected VFO the active VFO
// VFO_A_TO_B - directs the rig to copy the make both VFO frequencies the same as the Active VFO
// VFO_SWAP - directs the rig to exchange VFOA and VFOB
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doSetVfo() {
if (cmdLength == CAT_RD_LEN_NOSUB) { // No sub-command - sets VFO Tuning vice memory tuning
sendAck(); // Memory tuning is not implemented so send ack to keep protocol happy
return;
}
switch (cmdBuf[CAT_IX_SUB_CMD]) {
case CAT_VFO_A:
case CAT_VFO_B:
if (catSetVFO) {
catSetVFO(cmdBuf[CAT_IX_SUB_CMD]);
}
break;
case CAT_VFO_A_TO_B:
if (catAtoB) {
catAtoB();
}
break;
case CAT_VFO_SWAP:
if (catSwapVfo) {
catSwapVfo();
}
break;
}
sendAck();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doSetFreq() - proces CAT_SET_FREQ command
// Call the user supplied function to set the righ frequency
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doSetFreq() {
if (catSetFreq) {
catSetFreq(BCDtoFreq()); // Convert the frequency BCD to Long and call the user function
}
sendAck();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doReadFreq() - process the CAT_READ_FREQ command
// Call the user supplied function to read set the rig frequency
// Frequecies are sent and received in BCD - call the support functions to do the conversion
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doReadFreq() {
if (catGetFreq) {
// displayBanner(String("Read Freq"));
FreqtoBCD(catGetFreq()); // get the frequency, convert to BCD and stuff it in the response buffer
sendResponse(cmdBuf, CAT_SZ_FREQ);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doSetMode() - process the CAT_SET_MODE command
// Call the user function to put the rig into the requested mode.
// The rig must handle all of the possible modes
// CAT_MODE_LSB
// CAT_MODE_USB
// CAT_MODE_AM:
// CAT_MODE_CW - (LSB)
// CAT_MODE_RTTY - (LSB)
// CAT_MODE_FM
// CAT_MODE_CW_R - (Reverse - USB)
// CAT_MODE_RTTY_R - (Reverse - LSB)
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doSetMode() {
if (catSetMode) {
catSetMode(cmdBuf[CAT_IX_SUB_CMD]);
}
sendAck();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doReadMode() - process the CAT_READ_MODE command
// Call the user function to query the rig's current mode
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doReadMode() {
if (catGetMode) {
cmdBuf[CAT_IX_MODE] = catGetMode();
cmdBuf[CAT_IX_MODE+1] = CAT_MODE_FILTER1; // protocol filter - return reasonable value
sendResponse(cmdBuf, CAT_SZ_MODE);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doMisc() - process the CAT_MISC command
//
// The MISC command has several unrelated sub-commands
// The only one implemented here is the sub-command to read the IF Filter Setting
// The code sends a hard-coded response since most homebrew rigs won't have such a setting
// but programs like WSJTX and FLDIGI request it
//
// Commands that "set" values are replied to with an ACK message
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doMisc() {
switch (cmdBuf[CAT_IX_SUB_CMD]) {
case CAT_READ_IF_FILTER:
cmdBuf[CAT_IX_IF_FILTER] = 0;
sendResponse(cmdBuf, CAT_SZ_IF_FILTER);
break;
// Not implemented
// Reply with ACK to keep the protocol happy
case CAT_SET_MEM_CHAN:
case CAT_SET_BANDSTACK:
case CAT_SET_MEM_KEYER:
sendAck();
break;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// UNIMPLEMENTED COMMAND STUBS
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doUnimplemented() - reasonable processing for features that are not fully implemented
//
// Unimplimented command handling
//
// These following commands are used to both set and read various parameters in the IC-746 that are not
// typically implemented in a homebrew transceiver.
// Commands requesting the state of various parameters require one or two data bytes returned.
// We return zero in all cases which typically means the requsted feature is OFF - eg AGC, NB, VOX, etc.
// Command that "set" various parameters only require an ACK reply
// A "read" request has no data byte and is one byte shorter than a set request (length =4)
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doUnimplemented_1b() {
if (cmdLength == CAT_RD_LEN_SUB) { // Read request
cmdBuf[CAT_IX_DATA] = 0; // return 0 for all read requests
sendResponse(cmdBuf, CAT_SZ_UNIMP_1B);
} else { // Set parameter request
sendAck(); // Send an acknowledgement to keep the protocol happy
}
}
void IC746::doUnimplemented_2b() {
if (cmdLength == CAT_RD_LEN_SUB) { // Read request
cmdBuf[CAT_IX_DATA] = 0; // return 0 for all read requests
cmdBuf[CAT_IX_DATA+1] = 0;
sendResponse(cmdBuf, CAT_SZ_UNIMP_2B);
} else { // Set parameter request
sendAck(); // Send an acknowledgement to keep the protocol happy
}
}
void IC746::doTuneStep() {
if (cmdLength == CAT_RD_LEN_NOSUB) { // Read request
cmdBuf[CAT_IX_TUNE_STEP] = 0; // return 0 for all read requests
sendResponse(cmdBuf, CAT_SZ_TUNE_STEP);
} else { // Set parameter request
sendAck(); // Send an acknowledgement to keep the protocol happy
}
}
void IC746::doAntSel() {
if (cmdLength == CAT_RD_LEN_NOSUB) { // Read request
cmdBuf[CAT_IX_ANT_SEL] = 0; // return 0 for all read requests
sendResponse(cmdBuf, CAT_SZ_ANT_SEL);
} else { // Set parameter request
sendAck(); // Send an acknowledgement to keep the protocol happy
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// check() - process commands from CAT controller, should be called from the sketch main loop
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::check() {
// do nothing if it was disabled by software
if (!enabled) return;
// Receive a CAT Command
if (!readCmd()) return;
/*
#ifdef DEBUG_CAT_DETAIL
dbg = "rcvd: ";
dbg += String(cmdLength);
dbg += ": ";
for (int i = 0; i < cmdLength; i++) {
dbg += String(cmdBuf[i], HEX);
dbg += " ";
}
catDebug.println(dbg.c_str());
#endif
*/
// Process the command - command opcode is at CAT_IX_CMD location in command buffer
switch (cmdBuf[CAT_IX_CMD]) {
case CAT_PTT:
doPtt();
break;
case CAT_SPLIT:
doSplit();
break;
case CAT_SET_VFO:
doSetVfo();
break;
case CAT_SET_FREQ:
doSetFreq();
break;
case CAT_SET_MODE:
doSetMode();
break;
case CAT_READ_MODE:
doReadMode();
break;
case CAT_READ_FREQ:
doReadFreq();
break;
case CAT_READ_SMETER:
doSmeter();
break;
case CAT_MISC:
doMisc();
break;
case CAT_READ_ID:
cmdBuf[CAT_IX_ID] = CAT_RIG_ADDR; // Send back the transmitter ID
send(cmdBuf, CAT_SZ_ID);
break;
// Unimplemented commands that request one or two bytes of data from the rig - keep the protocol happy
case CAT_SET_RD_STEP:
doTuneStep();
break;
case CAT_SET_RD_ANT:
doAntSel();
break;
case CAT_SET_RD_ATT:
case CAT_SET_RD_PARAMS2:
doUnimplemented_1b();
break;
case CAT_SET_RD_PARAMS1:
case CAT_READ_OFFSET:
doUnimplemented_2b();
break;
default: // For all other commands respond with an NACK
#ifdef DEBUG_CAT
String dbg;
dbg = "unimp cmd: ";
dbg += String(cmdBuf[CAT_IX_CMD], HEX);
dbg += " ";
dbg += String(cmdBuf[CAT_IX_SUB_CMD], HEX);
// displayBanner(dbg);
catDebug.println(dbg.c_str());
#endif
sendNack();
break;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// UTILITY FUNCTIONS
//
///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// BCD FrequencyConversion Routines
// Convert BCD frequency to/from command buffer to/from long
// Format (beginning at first data byte in buffer is
// Byte 0 10Hz | 1Hz
// Byte 1 1KHz | 100Hz
// Byte 2 100KHz | 10KHz
// Byte 3 10MHz | 1MHz
// Example: 7,123,456 is encoded 56 | 34 | 12 | 07
//
long IC746::BCDtoFreq() {
long freq;
freq = cmdBuf[CAT_IX_FREQ] & 0xf; // lower 4 bits
freq += 10L * (cmdBuf[CAT_IX_FREQ] >> 4); // upper 4 bits
freq += 100L * (cmdBuf[CAT_IX_FREQ + 1] & 0xf);
freq += 1000L * (cmdBuf[CAT_IX_FREQ + 1] >> 4);
freq += 10000L * (cmdBuf[CAT_IX_FREQ + 2] & 0xf);
freq += 100000L * (cmdBuf[CAT_IX_FREQ + 2] >> 4);
freq += 1000000L * (cmdBuf[CAT_IX_FREQ + 3] & 0xf);
freq += 10000000L * (cmdBuf[CAT_IX_FREQ + 3] >> 4);
freq += 100000000L * (cmdBuf[CAT_IX_FREQ + 4] & 0xf);
freq += 1000000000L * (cmdBuf[CAT_IX_FREQ + 4] >> 4);
return freq;
}
void IC746::FreqtoBCD(long freq) {
byte ones, tens, hund, thou, ten_thou, hund_thou, mil, ten_mil, hund_mil, thou_mil;
ones = byte(freq % 10);
tens = byte((freq / 10L) % 10);
cmdBuf[CAT_IX_FREQ] = byte((tens << 4)) | ones;
hund = byte((freq / 100L) % 10);
thou = byte((freq / 1000L) % 10);
cmdBuf[CAT_IX_FREQ + 1] = byte((thou << 4)) | hund;
ten_thou = byte((freq / 10000L) % 10);
hund_thou = byte((freq / 100000L) % 10);
cmdBuf[CAT_IX_FREQ + 2] = byte((hund_thou << 4)) | ten_thou;
mil = byte((freq / 1000000L) % 10);
ten_mil = byte(freq / 10000000L) % 10;
cmdBuf[CAT_IX_FREQ + 3] = byte((ten_mil << 4)) | mil;
hund_mil = byte((freq / 100000000L) % 10);
thou_mil = byte(freq / 1000000000L % 10); // Allow frequencies up to 9999.999999 MHz to be enterd
cmdBuf[CAT_IX_FREQ + 4] = byte((thou_mil << 4)) | hund_mil;
}
void IC746::SmetertoBCD(byte s) {
byte ones, tens, hund;
ones = byte(s % 10);
tens = byte((s / byte(10)) % 10);
cmdBuf[CAT_IX_SMETER+1] = byte((tens << 4)) | ones;
hund = byte((s / byte(100)) % 10);
cmdBuf[CAT_IX_SMETER] = hund;
}