Various fixes, now works with OmniRig

pull/1/head
kk4das 2021-02-04 08:10:24 -05:00 zatwierdzone przez GitHub
rodzic 9ddee4bad7
commit 1cbcea51c7
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
2 zmienionych plików z 165 dodań i 218 usunięć

210
IC746.cpp
Wyświetl plik

@ -1,6 +1,11 @@
/*************************************************************************
IC746 CAT Library, by KK4DAS, Dean Souleles
V1.1 2/3/202
- 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
@ -25,7 +30,7 @@
//#define DEBUG_CAT
#ifdef DEBUG_CAT
//#define DEBUG_CAT_DETAIL
#define DEBUG_CAT_DETAIL
//#define DEBUG_CAT_SMETER
#include <SoftwareSerial.h>
SoftwareSerial catDebug(6, 7); // RX, TX
@ -59,12 +64,14 @@ static FuncPtrVoid catSwapVfo;
// 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_XCVR_ADDR 0
#define CAT_IX_CTRL_ADDR 1
#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
@ -72,6 +79,24 @@ static FuncPtrVoid catSwapVfo;
#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
@ -79,6 +104,7 @@ static FuncPtrVoid catSwapVfo;
*/
void IC746::begin() {
Serial.begin(9600, SERIAL_8N2);
while (!Serial);;
Serial.flush();
#ifdef DEBUG_CAT
@ -176,6 +202,7 @@ void IC746::addCATVSet(void (*userFunc)(byte)) {
//
void IC746::send(byte *buf, int len) {
int i;
Serial.write(CAT_PREAMBLE);
Serial.write(CAT_PREAMBLE);
@ -183,6 +210,27 @@ void IC746::send(byte *buf, int len) {
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);
}
//
@ -252,6 +300,18 @@ boolean IC746::readCmd() {
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;
@ -295,51 +355,15 @@ boolean IC746::readCmd() {
// of "Open" to keep the protocol happly
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doSmeter() {
switch (cmdBuf[CAT_IX_SUB_CMD]) {
case CAT_READ_SUB_SMETER:
if (catGetSmeter) {
const byte smap[] = {0, 20, 50, 70, 90, 110, 140, 150, 170, 180, 197, 201, 211, 216, 220, 230};
byte s;
s = catGetSmeter();
cmdBuf[CAT_IX_SMETER] = smap[s];
#ifdef DEBUG_CAT_SMETER
dbg = "doSmeter- s: ";
dbg += String(s);
dbg += " map: ";
dbg += String(smap[s]);
catDebug.println(dbg.c_str());
#endif
} else {
cmdBuf[CAT_IX_SMETER] = 0;
}
send(cmdBuf, 5);
break;
case CAT_READ_SUB_SQL: // Squelch condition 0=closed, 1=open
cmdBuf[CAT_IX_SQUELCH] = 1;
send(cmdBuf, 5);
break;
}
}
/*
BCD Version
void IC746::doSmeter() {
switch (cmdBuf[CAT_IX_SUB_CMD]) {
case CAT_READ_SUB_SMETER:
if (catGetSmeter) {
const byte smap[] = {0, 20, 50, 70, 90, 110, 140, 150, 170, 180, 197, 201, 211, 216, 220, 230};
//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);
@ -357,16 +381,16 @@ void IC746::doSmeter() {
cmdBuf[CAT_IX_SMETER] = 0; // user has not supplied S Meter function - keep the protocol happy
cmdBuf[CAT_IX_SMETER + 1] = 0;
}
send(cmdBuf, 6);
sendResponse(cmdBuf, CAT_SZ_SMETER);
break;
case CAT_READ_SUB_SQL: // Squelch condition 0=closed, 1=open
cmdBuf[CAT_IX_SQUELCH] = 1;
send(cmdBuf, 5);
send(cmdBuf, CAT_SZ_SQUELCH);
break;
}
}
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
@ -380,10 +404,10 @@ void IC746::doSmeter() {
// 56 | E0 | 1C | 00 | 01 - Set Tx, trailing data bit 1 for Tx, 0 for Rx
////////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doPtt() {
if (cmdLength == 4) { // Read request
if (cmdLength == CAT_RD_LEN_SUB) { // Read request
if (catGetPtt) {
cmdBuf[CAT_IX_PTT] = catGetPtt();
send(cmdBuf, 5);
sendResponse(cmdBuf, CAT_SZ_PTT);
}
} else { // Set request
if (catSetPtt) {
@ -409,6 +433,7 @@ void IC746::doSplit() {
}
break;
case CAT_SPLIT_ON:
case CAT_SIMPLE_DUP:
if (catSplit) {
catSplit(true);
}
@ -428,8 +453,9 @@ void IC746::doSplit() {
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doSetVfo() {
if (cmdLength == 4) { // No sub-command - sets VFO Tuning vice memory tuning
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]) {
@ -472,7 +498,7 @@ void IC746::doSetFreq() {
void IC746::doReadFreq() {
if (catGetFreq) {
FreqtoBCD(catGetFreq()); // get the frequency, convert to BCD and stuff it in the response buffer
send(cmdBuf, 8);
sendResponse(cmdBuf, CAT_SZ_FREQ);
}
}
@ -499,7 +525,8 @@ void IC746::doSetMode() {
void IC746::doReadMode() {
if (catGetMode) {
cmdBuf[CAT_IX_MODE] = catGetMode();
send(cmdBuf, 4);
cmdBuf[CAT_IX_MODE+1] = CAT_MODE_FILTER1; // protocol filter - return reasonable value
sendResponse(cmdBuf, CAT_SZ_MODE);
}
}
@ -511,17 +538,13 @@ void IC746::doReadMode() {
// 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
//
// The value of 29 was determined by reviewing the IC-7600 CI-V documentation is more complete than the IC-746 doc.
// The value of 29 equates to 2.5KHz which should be a reasonable value.
//
// 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] = 29; // Return a valid value - 29 represents 2.5K filter for SSB
cmdBuf[CAT_IX_IF_FILTER] = 0;
send(cmdBuf, 5);
sendResponse(cmdBuf, CAT_SZ_IF_FILTER);
break;
// Not implemented
@ -534,6 +557,10 @@ void IC746::doMisc() {
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// UNIMPLEMENTED COMMAND STUBS
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
// doUnimplemented() - reasonable processing for features that are not fully implemented
//
@ -541,20 +568,49 @@ void IC746::doMisc() {
//
// 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 a data byte returned.
// 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
// A "read" request has no data byte and is one byte shorter than a set request (length =4)
///////////////////////////////////////////////////////////////////////////////////////////////////////
void IC746::doUnimplemented() {
if (cmdLength == 4) { // Read request
cmdBuf[CAT_IX_PTT] = 0; // return 0 for all read requests
send(cmdBuf, 5);
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
///////////////////////////////////////////////////////////////////////////////////////////////////////
@ -566,7 +622,7 @@ void IC746::check() {
// Receive a CAT Command
if (!readCmd()) return;
/*
#ifdef DEBUG_CAT_DETAIL
dbg = "rcvd: ";
dbg += String(cmdLength);
@ -577,7 +633,7 @@ void IC746::check() {
}
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]) {
@ -619,16 +675,28 @@ void IC746::check() {
case CAT_READ_ID:
cmdBuf[CAT_IX_ID] = CAT_RIG_ADDR; // Send back the transmitter ID
send(cmdBuf, 5);
send(cmdBuf, CAT_SZ_ID);
break;
// Unimplemented commands that request data from the rig - keep the protocol happy
case CAT_ATT:
case CAT_SET_PARAMS2:
// 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();
doUnimplemented_2b();
break;
default: // For all other commands respond with an ACK
#ifdef DEBUG_CAT
dbg = "unimp cmd: ";
@ -698,9 +766,9 @@ void IC746::SmetertoBCD(byte s) {
ones = byte(s % 10);
tens = byte((s / byte(10)) % 10);
cmdBuf[CAT_IX_SMETER] = byte((tens << 4)) | ones;
cmdBuf[CAT_IX_SMETER+1] = byte((tens << 4)) | ones;
hund = byte((s / byte(100)) % 10);
cmdBuf[CAT_IX_SMETER + 1] = hund;
cmdBuf[CAT_IX_SMETER] = hund;
}

173
IC746.h
Wyświetl plik

@ -1,6 +1,11 @@
/*************************************************************************
IC746 CAT Library, by KK4DAS, Dean Souleles
11/24/2021
V1.1 2/3/202
- 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
@ -25,9 +30,9 @@
#ifndef IC746_h
#define IC746_h
#include "Arduino.h"
#include <Arduino.h>
#define CAT_VER "1.1"
/*
CAT Command definitions from IC746 Manual
*/
@ -58,13 +63,13 @@
#define CAT_SET_OFFSET 0x0D // Not implemented
#define CAT_SCAN 0x0E // Not implemented
#define CAT_SPLIT 0x0F
#define CAT_SET_STEP 0x10 // Not implemented
#define CAT_ATT 0x11 // Not implemented
#define CAT_SEL_ANT 0x12 // Not implemented
#define CAT_SET_RD_STEP 0x10 // Not implemented
#define CAT_SET_RD_ATT 0x11 // Not implemented
#define CAT_SET_RD_ANT 0x12 // Not implemented
#define CAT_SET_UT102 0x13 // Not implemented
#define CAT_SET_PARAMS1 0x14 // Not implemented
#define CAT_SET_RD_PARAMS1 0x14 // Not implemented
#define CAT_READ_SMETER 0x15 // Only impemented read S-Meter
#define CAT_SET_PARAMS2 0x16 // Not implemented (various settings)
#define CAT_SET_RD_PARAMS2 0x16 // Not implemented (various settings)
#define CAT_READ_ID 0x19
#define CAT_MISC 0x1A // Only implemented sub-command 3 Read IF filter
#define CAT_SET_TONE 0x1B // Not implemented (VHF/UHF)
@ -82,6 +87,7 @@
#define CAT_MODE_FM 0x05 // Not implemented
#define CAT_MODE_CW_R 0x06 // Not implemented
#define CAT_MODE_RTTY_R 0x07 // Not implemented
#define CAT_MODE_FILTER1 0x01 // Required for "read mode"
// VFO Subcommand
#define CAT_VFO_A 0x00
@ -92,9 +98,9 @@
// Split Subcommand
#define CAT_SPLIT_OFF 0x00
#define CAT_SPLIT_ON 0x01
#define CAT_SIMPLE_DUP 0x02 // Not implemented
#define CAT_MINUS_DUP 0x03 // Not implemented
#define CAT_PLUS_DUP 0x04 // Not implemented
#define CAT_SIMPLE_DUP 0x10 // Not implemented
#define CAT_MINUS_DUP 0x11 // Not implemented
#define CAT_PLUS_DUP 0x12 // Not implemented
// S-Meter / Squelch Subcommand
#define CAT_READ_SUB_SQL 0x01 // Not implemented (squelch)
@ -122,6 +128,8 @@
// defining the funtion type by params
typedef void (*FuncPtrVoid)(void);
typedef long (*FuncPtrVoidLong)(void);
@ -136,156 +144,24 @@ typedef void (*FuncPtrLong)(long);
*/
class IC746 {
public:
// Constructors
// we have two kind of constructors here
void begin(); // default for the radio 9600 @ 8N2
void begin(long baudrate, int mode); // custom baudrate and mode
void check(); // periodic check for serial commands
///////////////////////////////////////////////////////////////////////////////////////////
// Methods - callback functions that link the library to user supplied functions
///////////////////////////////////////////////////////////////////////////////////////////
//
// Examples below assume you include the header file and declare the radio object
// #include <IC746.h>
// IC746 radio = IC746();
//
//addCATPtt - registers a funtion that is called by the cat libary on resceipt of a PTT
// command to put the rig into either Tx or Rx
//
// Example
// void catSplit(boolean ptt) {
// if (ptt) {
// logic to put rig into Tx
// } else {
// logic to put the rig into Rx
// }
// radio.addCATPtt(catPtt);
// the functions that links the lib with user supplied functions
void addCATPtt(void (*)(boolean));
// addCATSplit - registers a funtion that is called by the cat libary on resceipt of a SPLIT
// command to turn split mode on or off
//
// Example
// void catSplit(boolean split) {
// if (split) {
// logic to turn on split mode
// } else {
// logic to turn off split mode
// }
// radio.addCATsplit(catSplit);
void addCATsplit(void (*)(boolean));
// addCATAtoB - registers a funtion that is called by the cat libary on resceipt of an A=B
// command to make both VFOs equal
//
// Example
// void cataAtoB() {
// logic to make the alternate VFO the same as the Active VFO
// }
// radio.addCATAtoB(catAtoB);
void addCATAtoB(void (*)(void));
// addCATSwapVfo - registers a funtion that is called by the cat libary on resceipt of a toggle VFO
// command to switch VFO A and B
//
// Example
// void catSwapVfo() {
// logic to swap VFO A and B
// }
// radio.addCATSwapVfo(catSwapVFO);
void addCATSwapVfo(void (*)(void));
// addCATFSet - registers a funtion that is called by the cat libary on resceipt of SET FREQUENCY
// command to set the active VFO frequency
//
// Example
// void catSetFreq(long freq) {
// logic to tune the radio to freq
// }
// radio.addCATFSet(catSetFreq);
void addCATFSet(void (*)(long));
// addCATMSet - registers a funtion that is called by the cat libary on resceipt of SET MODE
// command to set the active VFO MODE (USB or LSB)
//
// Example
// void catSetMode(byte mode) {
// if (mode == CAT_MODE_LSB) {
// logic to set rig to LSB
// } else {
// logic to set rig to USB
// |
// }
// radio.addCATFSet(catMode);
void addCATMSet(void (*)(byte));
// addCATVSet - registers a funtion that is called by the cat libary on resceipt of SET VFO
// command to set the active VFO to VFOA or VFOB)
//
// Example
// void catSetVfo(byte vfo) {
// if (vfo == CAT_VFO_A) {
// logic to make VFO A active
// } else {
// logic to mmake VFO B activeB
// |
// }
// radio.addCATFVet(catSetVfo);
void addCATVSet(void (*)(byte));
// addCATGetFreq - registers a funtion that is called by the cat libary on resceipt of READ FREQUENCY
// command. The user functuion must return the current frequency as a long
//
// Example
// long catGetFreq() {
// long freq = logic to tune the radio to freq
// return freq;
// }
// radio.addCATGetFreq(catGetFreq);
void addCATGetFreq(long (*)(void));
// addCATGetMode - registers a funtion that is called by the cat libary on resceipt of READ MODE
// command. The user functuion must return the current MODE USB or LSB as a byte
//
// Example
// byte catGetMode() {
// byte mode = logic set mode to CAT_MODE_USB or CAT_MODE_LSB
// return mode;
// }
// radio.addCATGetMode(catGetFreq);
void addCATGetMode(byte (*)(void));
// addCATGetPtt- registers a funtion that is called by the cat libary on resceipt of READ PTT
// command. The user functuion must return the current PTT state CAT_PTT_TX or CAT_PTT_RX as a byte
//
// Example
// byte catGetTxRx() {
// byte ptt = logic set mode to CAT_PTT_TX or CAT_PTT_TX
// return ptt;
// }
// radio.addCATGetPtt(catGetTxRx);
void addCATGetPtt(boolean (*)(void));
// addCATGetSmeter - registers a funtion that is called by the cat libary on resceipt of READ SMETER
// command. The user functuion must return the current S-meter reading as byte
// S-meter values are in the range 0-16, 0-9 are S0-S9, 10-16 are S9+10 thru S9+60
//
// Example
// byte catGetSMeter() {
// byte smeter = logic set smeter to a number from 0-16
// return smeter;
// }
// radio.addCATGetSmeter(catGetSMeter);
void addCATSMeter(byte (*)(void));
// Set enabled to false to stop processing CAT commands
boolean enabled = true;
private:
@ -296,8 +172,8 @@ class IC746 {
int cmdLength = 0;
long freq = 0;
void setFreq(void);
void sent(void);
void send(byte *, int);
void sendResponse(byte *buf, int len);
void sendAck(void);
void sendNack(void);
boolean readCmd(void);
@ -313,7 +189,10 @@ class IC746 {
void doSetMode();
void doReadMode();
void doMisc();
void doUnimplemented();
void doUnimplemented_1b();
void doUnimplemented_2b();
void doTuneStep();
void doAntSel();
};
#endif