#include #include "TS590.h" #include "Debug.h" /**********************************************************************/ /*! * @brief Send a command to the PC via CAT. Note that the command * should not include the trailing terminator (;). That will * be automatically added. * @param format * A printf-style format string. * @param args * Zero or more arguments to include in the command. */ void ts590SendCommand(const char* format, ...) { static char outBuf[ts590CommandMaxLength]; va_list args; va_start(args, format); vsprintf(outBuf, format, args); va_end(args); Serial.print(outBuf); Serial.print(";"); } /**********************************************************************/ /*! * @brief Create a new CAT command. It should be initialized with * a 2-character command prefix. * @param pre * A 2-character command prefix. If more than 2 characters * are supplied, only the first two will be used. If less * than two are supplied, then the command will be * initialized with a null prefix. */ TS590Command::TS590Command(const char* pre) : myPrefix(pre), prefixLength(strlen(pre)) {} TS590Command::~TS590Command() {} /*! * @brief Determine whether this is a Read command or not. by * default, if it's a 2-letter command, it's a Read. * @return True if a Read command; false otherwise. */ bool TS590Command::isReadCommand(const char* cmd) const { if (strlen(cmd) == prefixLength) { return true; } else { return false; } } /*! * @brief Process the provided command. If the command is a Set * command, it calls handleCommand(). If Auto Information * is eet (by the rig), sendResponse() is called at the end. * If the command is a Read command, it also calls * sendResponse(). Finally, if necessary, it will return * any error codes to the PC. * @param cmd * The current command string received from the PC via CAT. * It should be null-terminated, and should no longer have * the terminator (;). */ void TS590Command::process(const char* cmd) { theError = NoError; if (isReadCommand(cmd)) { DBGCMD( sendResponse(cmd) ); } else { DBGCMD( handleCommand(cmd) ); switch(theError) { case NoError: if (theRig->isAI()) { DBGCMD( sendResponse(cmd) ); } break; case SyntaxError: DBGCMD( ts590SyntaxError() ); break; case CommError: DBGCMD( ts590CommError() ); break; case ProcessError: DBGCMD( ts590ProcessError() ); break; } } } /*! * @brief Set the syntax error flag. This is cleared at the * beginning of each call to process(). */ void TS590Command::setSyntaxError() { theError = SyntaxError; } /*! * @brief Set the comms error flag. This is cleared at the * beginning of each call to process(). */ void TS590Command::setCommError() { theError = CommError; } /*! * @brief Set the process error flag. This is cleared at the * beginning of each call to process(). */ void TS590Command::setProcessError() { theError = ProcessError; } /*! * @brief Set the rig that will be used to process commands. * @param r * Pointer to the UBitxRig object. */ void TS590Command::setRig(UBitxRig* r) { theRig = r; } /*! * @brief Set the DSP that will be used to process commands. * @param d * Pointer to the UBitxDSP object. */ void TS590Command::setDSP(UBitxDSP* d) { theDSP = d; } UBitxRig* TS590Command::theRig = &Rig; UBitxDSP* TS590Command::theDSP = &DSP; TS590Error TS590Command::theError = NoError; /**********************************************************************/ void TS590Command_Bool::handleCommand(const char* cmd) { setter(cmd[length()] == '0' ? false : true); } void TS590Command_Bool::sendResponse(const char* cmd) { ts590SendCommand("%s%s", prefix(), getter() ? "1" : "0"); } /**********************************************************************/ void TS590Command_UL::handleCommand(const char* cmd) { unsigned val = static_cast(strtoul(&cmd[length()], NULL, 10)); if (val < myMin) { val = myMin; } else if (val > myMax) { val = myMax; } val = (val * mySlope) + myIntercept; setter(val); } void TS590Command_UL::sendResponse(const char* cmd) { unsigned val = getter(); val = (val - myIntercept) / mySlope; if (val < myMin) { val = myMin; } else if (val > myMax) { val = myMax; } ts590SendCommand("%s%0*u", prefix(), myWidth, getter()); } /**********************************************************************/ void TS590_FR::handleCommand(const char* cmd) { if (strlen(cmd) == 3) { switch (cmd[2]) { case '0': rig()->setVFOA(); rig()->setSplitOff(); break; case '1': rig()->setVFOB(); rig()->setSplitOff(); break; case '2': // TODO: Need to add something for channel mode. break; default: setSyntaxError(); } } else { setSyntaxError(); } } void TS590_FR::sendResponse(const char* cmd) { if (rig()->isVFOA()) { ts590SendCommand("FR0"); } else if (rig()->isVFOB()) { ts590SendCommand("FR1"); } else { ts590SendCommand("FR2"); } } /**********************************************************************/ void TS590_FT::handleCommand(const char* cmd) { if (strlen(cmd) == 3) { switch (cmd[2]) { case '0': if (rig()->isVFOA()) { rig()->setSplitOff(); } else if (rig()->isVFOB()) { rig()->setSplitOn(); } else { setSyntaxError(); } break; case '1': if (rig()->isVFOA()) { rig()->setSplitOn(); } else if (rig()->isVFOB()) { rig()->setSplitOff(); } else { setSyntaxError(); } break; default: setSyntaxError(); } } else { setSyntaxError(); } } void TS590_FT::sendResponse(const char* cmd) { if (rig()->isVFOA()) { ts590SendCommand(rig()->isSplit() ? "FT1" : "FT0"); } else if (rig()->isVFOB()) { ts590SendCommand(rig()->isSplit() ? "FT0" : "FT1"); } else { ts590SendCommand("FT2"); } } /**********************************************************************/ void TS590_MD::handleCommand(const char* cmd) { if (strlen(cmd) == 3) { switch (cmd[2]) { case '0': // None (setting failure) case '4': // FM - not supported case '5': // AM - not supported case '6': // FSK - not supported case '8': // None (setting failure) case '9': // FSK-R - not supported setProcessError(); break; case '1': // LSB rig()->setLSB(); break; case '2': // USB rig()->setUSB(); break; case '3': // CW rig()->setCW(); break; case '7': // CW-R rig()->setCWR(); break; default: setSyntaxError(); } } else { setSyntaxError(); } } void TS590_MD::sendResponse(const char* cmd) { if (rig()->isModeCW()) { ts590SendCommand("MD3"); } else if (rig()->isModeCWR()) { ts590SendCommand("MD7"); } else if (rig()->isModeUSB()) { ts590SendCommand("MD2"); } else if (rig()->isModeLSB()) { ts590SendCommand("MD1"); } else { ts590SendCommand("MD0"); } } /**********************************************************************/ int ssbHiCut[14] = {1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3400, 4000, 5000}; int ssbLoCut[12] = {0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}; int ssbWidth[14] = {50, 80, 100, 150, 200, 250, 300, 400, 500, 600, 1000, 1500, 2000, 2500}; int ssbCenter[14] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1750, 1800, 1900, 2000, 2100, 2210}; void TS590_SH::handleCommand(const char* cmd) { if (strlen(cmd) == 4) { index = strtoul(&cmd[2], NULL, 10); if (index < sizeof(ssbHiCut) / sizeof(ssbHiCut[0])) { dsp()->setRxFilterHi(ssbHiCut[index]); } else { setSyntaxError(); } } else { setSyntaxError(); } } void TS590_SH::sendResponse(const char* cmd) { ts590SendCommand("SH%02u", index); } void TS590_SL::handleCommand(const char* cmd) { if (strlen(cmd) == 4) { index = strtoul(&cmd[2], NULL, 10); if (index < sizeof(ssbLoCut) / sizeof(ssbLoCut[0])) { dsp()->setRxFilterLo(ssbLoCut[index]); } else { setSyntaxError(); } } else { setSyntaxError(); } } void TS590_SL::sendResponse(const char* cmd) { ts590SendCommand("SL%02u", index); } /**********************************************************************/ void TS590_VX::handleCommand(const char* cmd) { } void TS590_VX::sendResponse(const char* cmd) { } /**********************************************************************/ void nullSetFunc(unsigned x) { return; } unsigned getIDFunc() { #ifndef USE_TS590SG_CAT return 021; #else return 023; #endif } SetUL setAG = SetUL::create(DSP); GetUL getAG = GetUL::create(DSP); SetUL setAI = [](unsigned v) -> void { v == 0 ? Rig.aiOff() : Rig.aiOn(); }; GetUL getAI = []() -> unsigned { return Rig.isAI() ? 4 : 0; }; //SetUL setSidetone = SetUL::create(...); //GetUL getSidetone = GetUL::create(...); SetUL setUSBin = SetUL::create(DSP); GetUL getUSBin = GetUL::create(DSP); SetUL setUSBout = SetUL::create(DSP); GetUL getUSBout = GetUL::create(DSP); SetUL setACC2in = SetUL::create(DSP); GetUL getACC2in = GetUL::create(DSP); SetUL setACC2out = SetUL::create(DSP); GetUL getACC2out = GetUL::create(DSP); SetUL setVoxDelay = SetUL::create(DSP); GetUL getVoxDelay = GetUL::create(DSP); SetUL setUSBvox = SetUL::create(DSP); GetUL getUSBvox = GetUL::create(DSP); SetUL setACC2vox = SetUL::create(DSP); GetUL getACC2vox = GetUL::create(DSP); SetUL setID = SetUL::create(); GetUL getID = GetUL::create(); TS590Command_UL TS590_AG("AG0", 3, 0, 255, setAG, getAG); TS590Command_UL TS590_AI("AI", 1, 0, 4, setAI, getAI); // TS590_AS // TS590_BD // TS590_BU // TS590_CA // TS590_CD0 // TS590_CD1 // TS590_CD2 // TS590_CH #ifndef USE_TS590SG_CAT //TS590Command_UL TS590_EX034("EX0340000", 2, 0, 14, 50, 300, setSideTone, getSideTone); TS590Command_UL TS590_EX064("EX0640000", 1, 0, 9, setUSBin, getUSBin); TS590Command_UL TS590_EX065("EX0650000", 1, 0, 9, setUSBout, getUSBout); TS590Command_UL TS590_EX066("EX0660000", 1, 0, 9, setACC2in, getACC2in); TS590Command_UL TS590_EX067("EX0670000", 1, 0, 9, setACC2out, getACC2out); TS590Command_UL TS590_EX070("EX0700000", 2, 0, 20, 5, 0, setVoxDelay, getVoxDelay); TS590Command_UL TS590_EX071("EX0710000", 1, 0, 9, setUSBvox, getUSBvox); TS590Command_UL TS590_EX072("EX0720000", 1, 0, 9, setACC2vox, getACC2vox); #else //TS590Command_UL TS590_EX040("EX0400000", 2, 0, 14, 50, 300, setSideTone, getSideTone); TS590Command_UL TS590_EX071("EX0710000", 1, 0, 9, setUSBin, getUSBin); TS590Command_UL TS590_EX072("EX0720000", 1, 0, 9, setUSBout, getUSBout); TS590Command_UL TS590_EX073("EX0730000", 1, 0, 9, setACC2in, getACC2in); TS590Command_UL TS590_EX074("EX0740000", 1, 0, 9, setACC2out, getACC2out); TS590Command_UL TS590_EX077("EX0770000", 2, 0, 20, 5, 0, setVoxDelay, getVoxDelay); TS590Command_UL TS590_EX078("EX0780000", 1, 0, 9, setUSBvox, getUSBvox); TS590Command_UL TS590_EX079("EX0790000", 1, 0, 9, setACC2vox, getACC2vox); #endif TS590Command_UL TS590_ID("ID", 3, 21, 23, setID, getID); TS590_FA cmdFA; TS590_FB cmdFB; TS590_FR cmdFR; TS590_FT cmdFT; TS590_MD cmdMD; TS590_SH cmdSH; TS590_SL cmdSL; TS590Command* catCommands[] = { &cmdFA, &cmdFB, &cmdFR, &cmdFT, &cmdMD, &cmdSH, &cmdSL }; int numCatCommands = sizeof(catCommands) / sizeof(catCommands[0]); /**********************************************************************/ void UBitxTS590::begin() { Serial.begin(9600); // USB is always 12 Mbit/sec #ifdef DEBUG delay(500); Serial.print("DBG: Number of CAT commands: "); Serial.println(numCommands); for (int i = 0; i < numCommands; i++) { Serial.print(" "); Serial.println(commands[i]->prefix()); } #endif } void UBitxTS590::update() { char incomingChar; while (Serial.available()) { if (bufLen < ts590CommandMaxLength) { incomingChar = Serial.read(); if (incomingChar == ';') { buf[bufLen++] = '\0'; strupr(buf); processCommand(); } else if (incomingChar == '\n' && bufLen == 0) { ; } else { buf[bufLen++] = incomingChar; } } else { // too long... we're going to bail on this. ts590SyntaxError(); bufLen = 0; } } } typedef class TS590Command* PCmd; int compareCATCommands(const void* a, const void* b) { TS590Command const *B = *(TS590Command const **)b; int cmp = strncmp((char*)a, (char*)B->prefix(), B->length()); #ifdef DEBUG Serial.print("Comparison: "); Serial.print((char*)a); Serial.print(" ? "); Serial.print((char*)B->prefix()); Serial.print(" --> "); Serial.println(cmp); #endif return cmp; } void UBitxTS590::processCommand() { TS590Command** cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands); if (cmd == NULL) { ts590SyntaxError(); } else { (*cmd)->process(buf); } bufLen = 0; } UBitxTS590 TS590(catCommands, numCatCommands); /**********************************************************************/