#include "rigcommander.h"
#include <QDebug>

#include "rigidentities.h"
#include "logcategories.h"

// Copytight 2017-2020 Elliott H. Liggett

// This file parses data from the radio and also forms commands to the radio.
// The radio physical interface is handled by the commHandler() instance "comm"

//
// See here for a wonderful CI-V overview:
// http://www.plicht.de/ekki/civ/civ-p0a.html
//
// The IC-7300 "full" manual also contains a command reference.

// How to make spectrum display stop using rigctl:
//  echo "w \0xFE\0xFE\0x94\0xE0\0x27\0x11\0x00\0xFD" | rigctl -m 3073 -r /dev/ttyUSB0 -s 115200 -vvvvv

// Note: When sending \x00, must use QByteArray.setRawData()

rigCommander::rigCommander()
{
    rigState.filter = 0;
    rigState.mode = 0;
    rigState.ptt = 0;
}

rigCommander::~rigCommander()
{
    closeComm();
}


void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp)
{
    // construct
    // TODO: Bring this parameter and the comm port from the UI.
    // Keep in hex in the UI as is done with other CIV apps.

    // civAddr = 0x94; // address of the radio. Decimal is 148.
    civAddr = rigCivAddr; // address of the radio. Decimal is 148.
    usingNativeLAN = false;

    // ---
    setup();
    // ---

    this->rigSerialPort = rigSerialPort;
    this->rigBaudRate = rigBaudRate;

    comm = new commHandler(rigSerialPort, rigBaudRate);
    ptty = new pttyHandler(vsp);

    // data from the comm port to the program:
    connect(comm, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray)));

    // data from the ptty to the rig:
    connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray)));

    // data from the program to the comm port:
    connect(this, SIGNAL(dataForComm(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray)));

    // data from the rig to the ptty:
    connect(comm, SIGNAL(haveDataFromPort(QByteArray)), ptty, SLOT(receiveDataFromRigToPtty(QByteArray)));

    connect(comm, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString)));
    connect(ptty, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString)));

    connect(this, SIGNAL(getMoreDebug()), comm, SLOT(debugThis()));
    connect(this, SIGNAL(getMoreDebug()), ptty, SLOT(debugThis()));
    emit commReady();

}

void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp)
{
    // construct
    // TODO: Bring this parameter and the comm port from the UI.
    // Keep in hex in the UI as is done with other CIV apps.

    // civAddr = 0x94; // address of the radio. Decimal is 148.
    civAddr = rigCivAddr; // address of the radio. Decimal is 148.
    usingNativeLAN = true;

    // ---
    setup();
    // ---

    if (udp == Q_NULLPTR) {

        udp = new udpHandler(prefs,rxSetup,txSetup);

        udpHandlerThread = new QThread(this);

        udp->moveToThread(udpHandlerThread);

        connect(this, SIGNAL(initUdpHandler()), udp, SLOT(init()));
        connect(udpHandlerThread, SIGNAL(finished()), udp, SLOT(deleteLater()));

        udpHandlerThread->start();

        emit initUdpHandler();

        //this->rigSerialPort = rigSerialPort;
        //this->rigBaudRate = rigBaudRate;

        ptty = new pttyHandler(vsp);

        // Data from UDP to the program
        connect(udp, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray)));

        // data from the rig to the ptty:
        connect(udp, SIGNAL(haveDataFromPort(QByteArray)), ptty, SLOT(receiveDataFromRigToPtty(QByteArray)));

        // Audio from UDP
        connect(udp, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));

        // data from the program to the rig:
        connect(this, SIGNAL(dataForComm(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray)));

        // data from the ptty to the rig:
        connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray)));

        connect(this, SIGNAL(haveChangeLatency(quint16)), udp, SLOT(changeLatency(quint16)));
        connect(this, SIGNAL(haveSetVolume(unsigned char)), udp, SLOT(setVolume(unsigned char)));
        connect(udp, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32)));

        // Connect for errors/alerts
        connect(udp, SIGNAL(haveNetworkError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString)));
        connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(handleStatusUpdate(QString)));

        connect(ptty, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString)));
        connect(this, SIGNAL(getMoreDebug()), ptty, SLOT(debugThis()));

        emit haveAfGain(rxSetup.localAFgain);
    }

    // data from the comm port to the program:

    emit commReady();
    emit stateInfo(&rigState);

    pttAllowed = true; // This is for developing, set to false for "safe" debugging. Set to true for deployment.

}

void rigCommander::closeComm()
{
    qDebug(logRig()) << "Closing rig comms";
    if (comm != Q_NULLPTR) {
        delete comm;
    }
    comm = Q_NULLPTR;

    if (udpHandlerThread != Q_NULLPTR) {
        udpHandlerThread->quit();
        udpHandlerThread->wait();
    }
    udp = Q_NULLPTR;

    if (ptty != Q_NULLPTR) {
        delete ptty;
    }
    ptty = Q_NULLPTR;
}

void rigCommander::setup()
{
    // common elements between the two constructors go here:
    setCIVAddr(civAddr);
    spectSeqMax = 0; // this is now set after rig ID determined

    payloadSuffix = QByteArray("\xFD");

    lookingForRig = false;
    foundRig = false;
    oldScopeMode = spectModeUnknown;

    pttAllowed = true; // This is for developing, set to false for "safe" debugging. Set to true for deployment.
}



void rigCommander::process()
{
    // new thread enters here. Do nothing but do check for errors.
    if(comm!=Q_NULLPTR && comm->serialError)
    {
        emit haveSerialPortError(rigSerialPort, QString("Error from commhandler. Check serial port."));
    }
}

void rigCommander::handleSerialPortError(const QString port, const QString errorText)
{
    qInfo(logRig()) << "Error using port " << port << " message: " << errorText;
    emit haveSerialPortError(port, errorText);
}

void rigCommander::handleStatusUpdate(const QString text)
{
    emit haveStatusUpdate(text);
}

bool rigCommander::usingLAN()
{
    return usingNativeLAN;
}

void rigCommander::receiveBaudRate(quint32 baudrate) {
    emit haveBaudRate(baudrate);
}

void rigCommander::findRigs()
{
    // This function sends data to 0x00 ("broadcast") to look for any connected rig.
    lookingForRig = true;
    foundRig = false;

    QByteArray data;
    QByteArray data2;
    //data.setRawData("\xFE\xFE\xa2", 3);
    data.setRawData("\xFE\xFE\x00", 3);
    data.append((char)compCivAddr); // wfview's address, 0xE1
    data2.setRawData("\x19\x00", 2); // get rig ID
    data.append(data2);
    data.append(payloadSuffix);

    emit dataForComm(data);
    return;
}

void rigCommander::prepDataAndSend(QByteArray data)
{
    data.prepend(payloadPrefix);
    //printHex(data, false, true);
    data.append(payloadSuffix);

    if(data[4] != '\x15')
    {
        // We don't print out requests for meter levels
        qDebug(logRig()) << "Final payload in rig commander to be sent to rig: ";
        printHex(data);
    }

    emit dataForComm(data);
}

void rigCommander::powerOn()
{
    QByteArray payload;

    for(int i=0; i < 150; i++)
    {
        payload.append("\xFE");
    }

    payload.append(payloadPrefix); // FE FE 94 E1
    payload.append("\x18\x01");
    payload.append(payloadSuffix); // FD

    qDebug(logRig()) << "Power ON command in rigcommander to be sent to rig: ";
    printHex(payload);

    emit dataForComm(payload);

}

void rigCommander::powerOff()
{
    QByteArray payload;
    payload.setRawData("\x18\x00", 2);
    prepDataAndSend(payload);
}

void rigCommander::enableSpectOutput()
{
    QByteArray payload("\x27\x11\x01");
    prepDataAndSend(payload);
}

void rigCommander::disableSpectOutput()
{
    QByteArray payload;
    payload.setRawData("\x27\x11\x00", 3);
    prepDataAndSend(payload);
}

void rigCommander::enableSpectrumDisplay()
{
    // 27 10 01
    QByteArray payload("\x27\x10\x01");
    prepDataAndSend(payload);
}

void rigCommander::disableSpectrumDisplay()
{
    // 27 10 00
    QByteArray payload;
    payload.setRawData("\x27\x10\x00", 3);
    prepDataAndSend(payload);
}

void rigCommander::setSpectrumBounds(double startFreq, double endFreq, unsigned char edgeNumber)
{
    if((edgeNumber > 4) || (!edgeNumber))
    {
        return;
    }

    unsigned char freqRange = 1; // 1 = VHF, 2 = UHF, 3 = L-Band

    switch(rigCaps.model)
    {
        case model9700:
            if(startFreq > 148)
            {
                freqRange++;
                if(startFreq > 450)
                {
                    freqRange++;
                }
            }
            break;
        case model705:
        case model7300:
        case model7610:
        case model7850:
            // Some rigs do not go past 60 MHz, but we will not encounter
            // requests for those ranges since they are derived from the rig's existing scope range.
            // start value of freqRange is 1.
            if(startFreq > 1.6)
                freqRange++;
            if(startFreq > 2.0)
                freqRange++;
            if(startFreq > 6.0)
                freqRange++;
            if(startFreq > 8.0)
                freqRange++;
            if(startFreq > 11.0)
                freqRange++;
            if(startFreq > 15.0)
                freqRange++;
            if(startFreq > 20.0)
                freqRange++;
            if(startFreq > 22.0)
                freqRange++;
            if(startFreq > 26.0)
                freqRange++;
            if(startFreq > 30.0)
                freqRange++;
            if(startFreq > 45.0)
                freqRange++;
            if(startFreq > 60.0)
                freqRange++;
            if(startFreq > 74.8)
                freqRange++;
            if(startFreq > 108.0)
                freqRange++;
            if(startFreq > 137.0)
                freqRange++;
            if(startFreq > 400.0)
                freqRange++;
            break;
        case modelR8600:
            freqRange = 1;
            edgeNumber = 1;
            break;
        default:
            return;
            break;


    }
    QByteArray lowerEdge = makeFreqPayload(startFreq);
    QByteArray higherEdge = makeFreqPayload(endFreq);


    QByteArray payload;

    payload.setRawData("\x27\x1E", 2);
    payload.append(freqRange);
    payload.append(edgeNumber);

    payload.append(lowerEdge);
    payload.append(higherEdge);

    prepDataAndSend(payload);
}

void rigCommander::getScopeMode()
{
    // center or fixed
    QByteArray payload;
    payload.setRawData("\x27\x14\x00", 3);
    prepDataAndSend(payload);
}

void rigCommander::getScopeEdge()
{
    QByteArray payload;
    payload.setRawData("\x27\x16", 2);
    prepDataAndSend(payload);
}

void rigCommander::setScopeEdge(char edge)
{
    // 1 2 or 3
    // 27 16 00 0X
    if((edge <1) || (edge >4))
        return;
    QByteArray payload;
    payload.setRawData("\x27\x16\x00", 3);
    payload.append(edge);
    prepDataAndSend(payload);
}

void rigCommander::getScopeSpan()
{
    getScopeSpan(false);
}

void rigCommander::getScopeSpan(bool isSub)
{
    QByteArray payload;
    payload.setRawData("\x27\x15", 2);
    payload.append(static_cast<unsigned char>(isSub));
    prepDataAndSend(payload);
}

void rigCommander::setScopeSpan(char span)
{
    // See ICD, page 165, "19-12".
    // 2.5k = 0
    // 5k = 2, etc.
    if((span <0 ) || (span >9))
            return;

    QByteArray payload;
    double freq; // MHz
    payload.setRawData("\x27\x15\x00", 3);
    // next 6 bytes are the frequency
    switch(span)
    {
        case 0:
            // 2.5k
            freq = 2.5E-3;
            break;
        case 1:
            // 5k
            freq = 5.0E-3;
            break;
        case 2:
            freq = 10.0E-3;
            break;
        case 3:
            freq = 25.0E-3;
            break;
        case 4:
            freq = 50.0E-3;
            break;
        case 5:
            freq = 100.0E-3;
            break;
        case 6:
            freq = 250.0E-3;
            break;
        case 7:
            freq = 500.0E-3;
            break;
        case 8:
            freq = 1000.0E-3;
            break;
        case 9:
            freq = 2500.0E-3;
            break;
        default:
            return;
            break;
    }

    payload.append( makeFreqPayload(freq));
    payload.append("\x00");
    // printHex(payload, false, true);
    prepDataAndSend(payload);
}

void rigCommander::setSpectrumMode(spectrumMode spectMode)
{
    QByteArray specModePayload;
    specModePayload.setRawData("\x27\x14\x00", 3);
    specModePayload.append( static_cast<unsigned char>(spectMode) );
    prepDataAndSend(specModePayload);
}

void rigCommander::getSpectrumRefLevel()
{
    QByteArray payload;
    payload.setRawData("\x27\x19\x00", 3);
    prepDataAndSend(payload);
}

void rigCommander::getSpectrumRefLevel(unsigned char mainSub)
{
    QByteArray payload;
    payload.setRawData("\x27\x19", 2);
    payload.append(mainSub);
    prepDataAndSend(payload);
}

void rigCommander::setSpectrumRefLevel(int level)
{
    //qInfo(logRig()) << __func__ << ": Setting scope to level " << level;
    QByteArray setting;
    QByteArray number;
    QByteArray pn;
    setting.setRawData("\x27\x19\x00", 3);

    if(level >= 0)
    {
        pn.setRawData("\x00", 1);
        number = bcdEncodeInt(level*10);
    } else {
        pn.setRawData("\x01", 1);
        number = bcdEncodeInt( (-level)*10 );
    }

    setting.append(number);
    setting.append(pn);

    //qInfo(logRig()) << __func__ << ": scope reference number: " << number << ", PN to: " << pn;
    //printHex(setting, false, true);

    prepDataAndSend(setting);
}


void rigCommander::getSpectrumCenterMode()
{
    QByteArray specModePayload;
    specModePayload.setRawData("\x27\x14", 2);
    prepDataAndSend(specModePayload);
}

void rigCommander::getSpectrumMode()
{
    QByteArray specModePayload;
    specModePayload.setRawData("\x27\x14", 2);
    prepDataAndSend(specModePayload);
}

void rigCommander::setFrequency(freqt freq)
{
    //QByteArray freqPayload = makeFreqPayload(freq);
    QByteArray freqPayload = makeFreqPayload(freq);
    QByteArray cmdPayload;

    cmdPayload.append(freqPayload);
    cmdPayload.prepend('\x00');

    //printHex(cmdPayload, false, true);
    prepDataAndSend(cmdPayload);
    rigState.vfoAFreq = freq;
}

QByteArray rigCommander::makeFreqPayload(freqt freq)
{
    QByteArray result;
    quint64 freqInt = freq.Hz;

    unsigned char a;
    int numchars = 5;
    for (int i = 0; i < numchars; i++) {
        a = 0;
        a |= (freqInt) % 10;
        freqInt /= 10;
        a |= ((freqInt) % 10)<<4;

        freqInt /= 10;

        result.append(a);
        //printHex(result, false, true);
    }

    return result;
}

QByteArray rigCommander::makeFreqPayload(double freq)
{
    quint64 freqInt = (quint64) (freq * 1E6);

    QByteArray result;
    unsigned char a;
    int numchars = 5;
    for (int i = 0; i < numchars; i++) {
        a = 0;
        a |= (freqInt) % 10;
        freqInt /= 10;
        a |= ((freqInt) % 10)<<4;

        freqInt /= 10;

        result.append(a);
        //printHex(result, false, true);
    }
    //qInfo(logRig()) << "encoded frequency for " << freq << " as int " << freqInt;
    //printHex(result, false, true);
    return result;
}

void rigCommander::setRitEnable(bool ritEnabled)
{
    QByteArray payload;

    if(ritEnabled)
    {
        payload.setRawData("\x21\x01\x01", 3);
    } else {
        payload.setRawData("\x21\x01\x00", 3);
    }
    prepDataAndSend(payload);
}

void rigCommander::getRitEnabled()
{
    QByteArray payload;
    payload.setRawData("\x21\x01", 2);
    prepDataAndSend(payload);
}

void rigCommander::getRitValue()
{
    QByteArray payload;
    payload.setRawData("\x21\x00", 2);
    prepDataAndSend(payload);
}

void rigCommander::setRitValue(int ritValue)
{
    QByteArray payload;
    QByteArray freqBytes;
    freqt f;

    bool isNegative = false;
    payload.setRawData("\x21\x00", 2);

    if(ritValue < 0)
    {
        isNegative = true;
        ritValue *= -1;
    }

    if(ritValue > 9999)
        return;

    f.Hz = ritValue;

    freqBytes = makeFreqPayload(f);

    freqBytes.truncate(2);
    payload.append(freqBytes);

    payload.append(QByteArray(1,(char)isNegative));

    prepDataAndSend(payload);
}

void rigCommander::setMode(mode_info m)
{
    QByteArray payload;

    if(rigCaps.model==model706)
    {
        m.filter = '\x01';
    }
    if(m.mk == modeWFM)
    {
        m.filter = '\x01';
    }

    payload.setRawData("\x06", 1);
    payload.append(m.reg);
    payload.append(m.filter);

    prepDataAndSend(payload);
    rigState.mode = m.reg;
    rigState.filter = m.filter;
}

void rigCommander::setMode(unsigned char mode, unsigned char modeFilter)
{
    QByteArray payload;
    if(mode < 0x22 + 1)
    {
        // mode command | filter
        // 0x01 | Filter 01 automatically
        // 0x04 | user-specififed 01, 02, 03 | note, is "read the current mode" on older rigs
        // 0x06 | "default" filter is auto

        payload.setRawData("\x06", 1); // cmd 06 needs filter specified
        //payload.setRawData("\x04", 1); // cmd 04 will apply the default filter, but it seems to always pick FIL 02

        payload.append(mode);
        if(rigCaps.model==model706)
        {
            payload.append("\x01"); // "normal" on IC-706
        } else {
            if(mode == 0x06)
            {
                payload.append(0x01);
            } else {
                payload.append(modeFilter);
            }
        }

        prepDataAndSend(payload);
        rigState.mode = mode;
        rigState.filter = modeFilter;

    }
}

void rigCommander::setDataMode(bool dataOn, unsigned char filter)
{
    QByteArray payload;

    payload.setRawData("\x1A\x06", 2);
    if(dataOn)
    {
        payload.append("\x01", 1); // data mode on
        payload.append(filter);

    } else {
        payload.append("\x00\x00", 2); // data mode off, bandwidth not defined per ICD.
    }
    prepDataAndSend(payload);
    rigState.datamode = dataOn;
}

void rigCommander::getFrequency()
{
    // figure out frequency and then respond with haveFrequency();
    // send request to radio
    // 1. make the data
    QByteArray payload("\x03");
    prepDataAndSend(payload);
}

void rigCommander::getMode()
{
    QByteArray payload("\x04");
    prepDataAndSend(payload);
}

void rigCommander::getDataMode()
{
    QByteArray payload("\x1A\x06");
    prepDataAndSend(payload);
}

void rigCommander::setDuplexMode(duplexMode dm)
{
    QByteArray payload;
    if(dm==dmDupAutoOff)
    {
        payload.setRawData("\x1A\x05\x00\x46\x00", 5);
    } else if (dm==dmDupAutoOn)
    {
        payload.setRawData("\x1A\x05\x00\x46\x01", 5);
    } else {
        payload.setRawData("\x0F", 1);
        payload.append((unsigned char) dm);
    }
    prepDataAndSend(payload);
}

void rigCommander::getDuplexMode()
{
    QByteArray payload;

    // Duplex mode:
    payload.setRawData("\x0F", 1);
    prepDataAndSend(payload);

    // Auto Repeater Mode:
    payload.setRawData("\x1A\x05\x00\x46", 4);
    prepDataAndSend(payload);
}

void rigCommander::getTransmitFrequency()
{
    QByteArray payload;
    payload.setRawData("\x1C\x03", 2);
    prepDataAndSend(payload);
}

void rigCommander::setTone(quint16 tone)
{
    QByteArray fenc = encodeTone(tone);

    QByteArray payload;
    payload.setRawData("\x1B\x00", 2);
    payload.append(fenc);

    //qInfo() << __func__ << "TONE encoded payload: ";
    printHex(payload);

    prepDataAndSend(payload);
}

void rigCommander::setTSQL(quint16 tsql)
{
    QByteArray fenc = encodeTone(tsql);

    QByteArray payload;
    payload.setRawData("\x1B\x01", 2);
    payload.append(fenc);

    //qInfo() << __func__ << "TSQL encoded payload: ";
    printHex(payload);

    prepDataAndSend(payload);
}

void rigCommander::setDTCS(quint16 dcscode, bool tinv, bool rinv)
{

    QByteArray denc = encodeTone(dcscode, tinv, rinv);

    QByteArray payload;
    payload.setRawData("\x1B\x02", 2);
    payload.append(denc);

    //qInfo() << __func__ << "DTCS encoded payload: ";
    printHex(payload);

    prepDataAndSend(payload);
}

QByteArray rigCommander::encodeTone(quint16 tone)
{
    return encodeTone(tone, false, false);
}

QByteArray rigCommander::encodeTone(quint16 tone, bool tinv, bool rinv)
{
    // This function is fine to use for DTCS and TONE
    QByteArray enct;

    unsigned char inv=0;
    inv = inv | (unsigned char)rinv;
    inv = inv | ((unsigned char)tinv) << 4;

    enct.append(inv);

    unsigned char hundreds = tone / 1000;
    unsigned char tens = (tone-(hundreds*1000)) / 100;
    unsigned char ones = (tone -(hundreds*1000)-(tens*100)) / 10;
    unsigned char dec =  (tone -(hundreds*1000)-(tens*100)-(ones*10));

    enct.append(tens | (hundreds<<4));
    enct.append(dec | (ones <<4));

    return enct;
}

quint16 rigCommander::decodeTone(QByteArray eTone)
{
    bool t;
    bool r;
    return decodeTone(eTone, t, r);
}

quint16 rigCommander::decodeTone(QByteArray eTone, bool &tinv, bool &rinv)
{
    // index:  00 01  02 03 04
    // CTCSS:  1B 01  00 12 73 = PL 127.3, decode as 1273
    // D(T)CS: 1B 01  TR 01 23 = T/R Invert bits + DCS code 123

    if (eTone.length() < 5) {
        return 0;
    }
    tinv = false; rinv = false;
    quint16 result = 0;

    if((eTone.at(2) & 0x01) == 0x01)
        tinv = true;
    if((eTone.at(2) & 0x10) == 0x10)
        rinv = true;

    result += (eTone.at(4) & 0x0f);
    result += ((eTone.at(4) & 0xf0) >> 4) *   10;
    result += (eTone.at(3) & 0x0f) *  100;
    result += ((eTone.at(3) & 0xf0) >> 4) * 1000;

    return result;
}

void rigCommander::getTone()
{
    QByteArray payload;
    payload.setRawData("\x1B\x00", 2);
    prepDataAndSend(payload);
}

void rigCommander::getTSQL()
{
    QByteArray payload;
    payload.setRawData("\x1B\x01", 2);
    prepDataAndSend(payload);
}

void rigCommander::getDTCS()
{
    QByteArray payload;
    payload.setRawData("\x1B\x02", 2);
    prepDataAndSend(payload);
}

void rigCommander::getRptAccessMode()
{
    QByteArray payload;
    payload.setRawData("\x16\x5D", 2);
    prepDataAndSend(payload);
}

void rigCommander::setRptAccessMode(rptAccessTxRx ratr)
{
    QByteArray payload;
    payload.setRawData("\x16\x5D", 2);
    payload.append((unsigned char)ratr);
    prepDataAndSend(payload);
}

void rigCommander::setIPP(bool enabled)
{
    QByteArray payload;
    payload.setRawData("\x16\x65", 2);
    if(enabled)
    {
        payload.append("\x01");
    } else {
        payload.append("\x00");
    }
    prepDataAndSend(payload);
}

void rigCommander::getIPP()
{
    QByteArray payload;
    payload.setRawData("\x16\x65", 2);
    prepDataAndSend(payload);
}

void rigCommander::setSatelliteMode(bool enabled)
{
    QByteArray payload;
    payload.setRawData("\x16\x5A", 2);
    if(enabled)
    {
        payload.append("\x01");
    } else {
        payload.append("\x00");
    }
    prepDataAndSend(payload);
}

void rigCommander::getSatelliteMode()
{
    QByteArray payload;
    payload.setRawData("\x16\x5A", 2);
    prepDataAndSend(payload);
}

void rigCommander::getPTT()
{
    QByteArray payload;
    payload.setRawData("\x1C\x00", 2);
    prepDataAndSend(payload);
}

void rigCommander::getBandStackReg(char band, char regCode)
{
    QByteArray payload("\x1A\x01");
    payload.append(band); // [01 through 11]
    payload.append(regCode); // [01...03]. 01 = latest, 03 = oldest
    prepDataAndSend(payload);
}

void rigCommander::setPTT(bool pttOn)
{
    //bool pttAllowed = false;

    if(pttAllowed)
    {
        QByteArray payload("\x1C\x00", 2);
        payload.append((char)pttOn);
        prepDataAndSend(payload);
        rigState.ptt = pttOn;
    }
}

void rigCommander::setCIVAddr(unsigned char civAddr)
{
    // Note: This sets the radio's CIV address
    // the computer's CIV address is defined in the header file.

    this->civAddr = civAddr;
    payloadPrefix = QByteArray("\xFE\xFE");
    payloadPrefix.append(civAddr);
    payloadPrefix.append((char)compCivAddr);
}

void rigCommander::handleNewData(const QByteArray& data)
{
    emit haveDataForServer(data);
    parseData(data);
}

void rigCommander::receiveAudioData(const audioPacket& data)
{
    emit haveAudioData(data);
}

void rigCommander::parseData(QByteArray dataInput)
{
    // TODO: Clean this up.
    // It finally works very nicely, needs to be streamlined.
    //

    int index = 0;
    volatile int count = 0; // debug purposes

    // use this:
    QList <QByteArray> dataList = dataInput.split('\xFD');
    QByteArray data;
    // qInfo(logRig()) << "data list has this many elements: " << dataList.size();
    if (dataList.last().isEmpty())
    {
        dataList.removeLast(); // if the original ended in FD, then there is a blank entry at the end.
    }
    // Only thing is, each frame is missing '\xFD' at the end. So append! Keeps the frames intact.
    for(index = 0; index < dataList.count(); index++)
    {
        data = dataList[index];
        data.append('\xFD'); // because we expect it to be there.
    // foreach(listitem)
    // listitem.append('\xFD');
    // continue parsing...

        count++;
        // Data echo'd back from the rig start with this:
        // fe fe 94 e0 ...... fd

        // Data from the rig that is not an echo start with this:
        // fe fe e0 94 ...... fd (for example, a reply to a query)

        // Data from the rig that was not asked for is sent to controller 0x00:
        // fe fe 00 94 ...... fd (for example, user rotates the tune control or changes the mode)

        //qInfo(logRig()) << "Data received: ";
        //printHex(data, false, true);
        if(data.length() < 4)
        {
            if(data.length())
            {
                // Finally this almost never happens
                // qInfo(logRig()) << "Data length too short: " << data.length() << " bytes. Data:";
                //printHex(data, false, true);
            }
            // no
            //return;
            // maybe:
            // continue;
        }

        if(!data.startsWith("\xFE\xFE"))
        {
            // qInfo(logRig()) << "Warning: Invalid data received, did not start with FE FE.";
            // find 94 e0 and shift over,
            // or look inside for a second FE FE
            // Often a local echo will miss a few bytes at the beginning.
            if(data.startsWith('\xFE'))
            {
                data.prepend('\xFE');
                // qInfo(logRig()) << "Warning: Working with prepended data stream.";
                parseData(payloadIn);
                return;
            } else {
                //qInfo(logRig()) << "Error: Could not reconstruct corrupted data: ";
                //printHex(data, false, true);
                // data.right(data.length() - data.find('\xFE\xFE'));
                // if found do not return and keep going.
                return;
            }
        }

        if((unsigned char)data[02] == civAddr)
        {
            // data is or begins with an echoback from what we sent
            // find the first 'fd' and cut it. Then continue.
            //payloadIn = data.right(data.length() - data.indexOf('\xfd')-1);
            // qInfo(logRig()) << "[FOUND] Trimmed off echo:";
            //printHex(payloadIn, false, true);
            //parseData(payloadIn);
            //return;
        }

        incomingCIVAddr = data[03]; // track the CIV of the sender.

        switch(data[02])
        {
            //    case civAddr: // can't have a variable here :-(
            //        // data is or begins with an echoback from what we sent
            //        // find the first 'fd' and cut it. Then continue.
            //        payloadIn = data.right(data.length() - data.indexOf('\xfd')-1);
            //        //qInfo(logRig()) << "Trimmed off echo:";
            //        //printHex(payloadIn, false, true);
            //        parseData(payloadIn);
            //        break;
            // case '\xE0':

            case (char)0xE0:
            case (char)compCivAddr:
                // data is a reply to some query we sent
                // extract the payload out and parse.
                // payload = getpayload(data); // or something
                // parse (payload); // recursive ok?
                payloadIn = data.right(data.length() - 4);
                if(payloadIn.contains("\xFE"))
                {
                    //qDebug(logRig()) << "Corrupted data contains FE within message body: ";
                    //printHex(payloadIn);
                    break;
                }
                parseCommand();
                break;
            case '\x00':
                // data send initiated by the rig due to user control
                // extract the payload out and parse.
                if((unsigned char)data[03]==compCivAddr)
                {
                    // This is an echo of our own broadcast request.
                    // The data are "to 00" and "from E1"
                    // Don't use it!
                    qDebug(logRig()) << "Caught it! Found the echo'd broadcast request from us! Rig has not responded to broadcast query yet.";
                } else {
                    payloadIn = data.right(data.length() - 4); // Removes FE FE E0 94 part
                    if(payloadIn.contains("\xFE"))
                    {
                        //qDebug(logRig()) << "Corrupted data contains FE within message body: ";
                        //printHex(payloadIn);
                        break;
                    }
                    parseCommand();
                }
                break;
            default:
                // could be for other equipment on the CIV network.
                // just drop for now.
                // relaySendOutData(data);
                break;
        }
    }
    /*
    if(dataList.length() > 1)
    {
        qInfo(logRig()) << "Recovered " << count << " frames from single data with size" << dataList.count();
    }
    */
}

void rigCommander::parseCommand()
{
    // note: data already is trimmed of the beginning FE FE E0 94 stuff.

    bool isSpectrumData = payloadIn.startsWith(QByteArray().setRawData("\x27\x00", 2));

    if( (!isSpectrumData) && (payloadIn[00] != '\x15') )
    {
        // We do not log spectrum and meter data,
        // as they tend to clog up any useful logging.
        printHex(payloadIn);
    }

    switch(payloadIn[00])
    {

        case 00:
            // frequency data
            parseFrequency();
            break;
        case 03:
            parseFrequency();
            break;
        case '\x25':
            if((int)payloadIn[1] == 0)
            {
                emit haveFrequency(parseFrequency(payloadIn, 5));
            }
            break;
        case '\x01':
            //qInfo(logRig()) << "Have mode data";
            this->parseMode();
            break;
        case '\x04':
            //qInfo(logRig()) << "Have mode data";
            this->parseMode();
            break;
        case '\x05':
            //qInfo(logRig()) << "Have frequency data";
            this->parseFrequency();
            break;
        case '\x06':
            //qInfo(logRig()) << "Have mode data";
            this->parseMode();
            break;
        case '\x0F':
            emit haveDuplexMode((duplexMode)(unsigned char)payloadIn[1]);
            break;
        case '\x11':
            emit haveAttenuator((unsigned char)payloadIn.at(1));
            break;
        case '\x12':
            emit haveAntenna((unsigned char)payloadIn.at(1), (bool)payloadIn.at(2));
            break;
        case '\x14':
            // read levels
            parseLevels();
            break;
        case '\x15':
            // Metering such as s, power, etc
            parseLevels();
            break;
        case '\x16':
            parseRegister16();
            break;
        case '\x19':
            // qInfo(logRig()) << "Have rig ID: " << (unsigned int)payloadIn[2];
            // printHex(payloadIn, false, true);
            model = determineRadioModel(payloadIn[2]); // verify this is the model not the CIV
            rigCaps.modelID = payloadIn[2];
            determineRigCaps();
            qInfo(logRig()) << "Have rig ID: decimal: " << (unsigned int)rigCaps.modelID;


            break;
        case '\x21':
            // RIT and Delta TX:
            parseRegister21();
            break;
        case '\x26':
            if((int)payloadIn[1] == 0)
            {
                // This works but LSB comes out as CW?
                // Also, an opportunity to read the data mode
                // payloadIn = payloadIn.right(3);
                // this->parseMode();
            }
            break;
        case '\x27':
            // scope data
            //qInfo(logRig()) << "Have scope data";
            //printHex(payloadIn, false, true);
            parseWFData();
            //parseSpectrum();
            break;
        case '\x1A':
            if(payloadIn[01] == '\x05')
            {
                parseDetailedRegisters1A05();
            } else {
                parseRegisters1A();
            }
            break;
        case '\x1B':
            parseRegister1B();
            break;
        case '\x1C':
            parseRegisters1C();
            break;
        case '\xFB':
            // Fine Business, ACK from rig.
            break;
        case '\xFA':
            // error

            qDebug(logRig()) << "Error (FA) received from rig.";
            printHex(payloadIn, false ,true);
            break;

        default:
            // This gets hit a lot when the pseudo-term is
            // using commands wfview doesn't know yet.
            // qInfo(logRig()) << "Have other data with cmd: " << std::hex << payloadIn[00];
            // printHex(payloadIn, false, true);
            break;
    }
    // is any payload left?

}

void rigCommander::parseLevels()
{
    //qInfo(logRig()) << "Received a level status readout: ";
    // printHex(payloadIn, false, true);

    // wrong: unsigned char level = (payloadIn[2] * 100) + payloadIn[03];
    unsigned char hundreds = payloadIn[2];
    unsigned char tens = (payloadIn[3] & 0xf0) >> 4;
    unsigned char units = (payloadIn[3] & 0x0f);

    unsigned char level = (100*hundreds) + (10*tens) + units;

    //qInfo(logRig()) << "Level is: " << (int)level << " or " << 100.0*level/255.0 << "%";

    // Typical RF gain response (rather low setting):
    // "INDEX: 00 01 02 03 04 "
    // "DATA:  14 02 00 78 fd "


    if(payloadIn[0] == '\x14')
    {
        switch(payloadIn[1])
        {
            case '\x01':
                // AF level - ignore if LAN connection.
                if (udp == Q_NULLPTR) {
                    emit haveAfGain(level);
                    rigState.afGain = level;
                }
                break;
            case '\x02':
                // RX RF Gain
                emit haveRfGain(level);
                rigState.rfGain = level;
                break;
            case '\x03':
                // Squelch level
                emit haveSql(level);
                rigState.squelch = level;
                break;
            case '\x0A':
                // TX RF level
                emit haveTxPower(level);
                rigState.txPower = level;
                break;
            case '\x0B':
                // Mic Gain
                emit haveMicGain(level);
                rigState.micGain = level;
                break;
            case '\x0E':
                // compressor level
                emit haveCompLevel(level);
                rigState.compLevel = level;
                break;
            case '\x15':
                // monitor level
                emit haveMonitorLevel(level);
                rigState.monitorLevel = level;
                break;
            case '\x16':
                // VOX gain
                emit haveVoxGain(level);
                rigState.voxGain = level;
                break;
            case '\x17':
                // anti-VOX gain
                emit haveAntiVoxGain(level);
                rigState.antiVoxGain = level;
                break;

            default:
                qInfo(logRig()) << "Unknown control level (0x14) received at register " << QString("0x%1").arg((int)payloadIn[1],2,16) << " with level " << QString("0x%1").arg((int)level,2,16) << ", int=" << (int)level;
                printHex(payloadIn);
                break;
        }
        return;
    }

    if(payloadIn[0] == '\x15')
    {
        switch(payloadIn[1])
        {
            case '\x02':
                // S-Meter
                emit haveMeter(meterS, level);
                rigState.sMeter = level;
                break;
            case '\x04':
                // Center (IC-R8600)
                emit haveMeter(meterCenter, level);
                rigState.sMeter = level;
                break;
            case '\x11':
                // RF-Power meter
                emit haveMeter(meterPower, level);
                rigState.powerMeter = level;
                break;
            case '\x12':
                // SWR
                emit haveMeter(meterSWR, level);
                rigState.swrMeter = level;
                break;
            case '\x13':
                // ALC
                emit haveMeter(meterALC, level);
                rigState.alcMeter = level;
                break;
            case '\x14':
                // COMP dB reduction
                emit haveMeter(meterComp, level);
                rigState.compMeter = level;
                break;
            case '\x15':
                // VD (12V)
                emit haveMeter(meterVoltage, level);
                rigState.voltageMeter = level;
                break;
            case '\x16':
                // ID
                emit haveMeter(meterCurrent, level);
                rigState.currentMeter = level;
                break;

            default:
                qInfo(logRig()) << "Unknown meter level (0x15) received at register " << (unsigned int) payloadIn[1] << " with level " << level;
                break;
        }


    return;
    }

}

void rigCommander::setTxPower(unsigned char power)
{
    QByteArray payload("\x14\x0A");
    payload.append(bcdEncodeInt(power));
    prepDataAndSend(payload);
}

void rigCommander::setMicGain(unsigned char gain)
{
    QByteArray payload("\x14\x0B");
    payload.append(bcdEncodeInt(gain));
    prepDataAndSend(payload);
}

void rigCommander::getModInput(bool dataOn)
{
    setModInput(inputMic, dataOn, true);
}

void rigCommander::setModInput(rigInput input, bool dataOn)
{
    setModInput(input, dataOn, false);
}

void rigCommander::setModInput(rigInput input, bool dataOn, bool isQuery)
{
//    The input enum is as follows:

//    inputMic=0,
//    inputACC=1,
//    inputUSB=3,
//    inputLAN=5,
//    inputACCA,
//    inputACCB};

    QByteArray payload;
    QByteArray inAsByte;

    if(isQuery)
        input = inputMic;


    switch(rigCaps.model)
    {
        case model9700:
            payload.setRawData("\x1A\x05\x01\x15", 4);
            payload.append((unsigned char)input);
            break;
        case model7610:
            payload.setRawData("\x1A\x05\x00\x91", 4);
            payload.append((unsigned char)input);
            break;
        case model7300:
            payload.setRawData("\x1A\x05\x00\x66", 4);
            payload.append((unsigned char)input);
            break;
        case model7850:
            payload.setRawData("\x1A\x05\x00\x63", 4);
            switch(input)
            {
                case inputMic:
                    inAsByte.setRawData("\x00", 1);
                    break;
                case inputACCA:
                    inAsByte.setRawData("\x01", 1);
                    break;
                case inputACCB:
                    inAsByte.setRawData("\x02", 1);
                    break;
                case inputUSB:
                    inAsByte.setRawData("\x08", 1);
                    break;
                case inputLAN:
                    inAsByte.setRawData("\x09", 1);
                    break;
                default:
                    return;

            }
            payload.append(inAsByte);
            break;
        case model705:
            payload.setRawData("\x1A\x05\x01\x18", 4);
            switch(input)
            {
                case inputMic:
                    inAsByte.setRawData("\x00", 1);
                    break;
                case inputUSB:
                    inAsByte.setRawData("\x01", 1);
                    break;
                case inputLAN: // WLAN
                    inAsByte.setRawData("\x03", 1);
                    break;
                default:
                    return;
            }
            payload.append(inAsByte);
            break;
        case model7700:
            payload.setRawData("\x1A\x05\x00\x32", 4);
            if(input==inputLAN)
            {
                // NOTE: CIV manual says data may range from 0 to 3
                // But data 0x04 does correspond to LAN.
                payload.append("\x04");
            } else {
                payload.append((unsigned char)input);
            }
            break;
        case model7600:
            payload.setRawData("\x1A\x05\x00\x30", 4);
            payload.append((unsigned char)input);
            break;
        case model7100:
            payload.setRawData("\x1A\x05\x00\x90", 4);
            payload.append((unsigned char)input);
            break;
        case model7200:
            payload.setRawData("\x1A\x03\x23", 3);
            switch(input)
            {
                case inputMic:
                    payload.setRawData("\x00", 1);
                    break;
                case inputUSB:
                    payload.setRawData("\x03", 1);
                    break;
                case inputACC:
                    payload.setRawData("\x01", 1);
                    break;
                default:
                    return;
            }
        default:
            break;
    }
    if(dataOn)
    {
        if(rigCaps.model==model7200)
        {
            payload[2] = payload[2] + 1;
        } else {
            payload[3] = payload[3] + 1;
        }
    }

    if(isQuery)
    {
        payload.truncate(4);
    }

    prepDataAndSend(payload);

}

void rigCommander::setModInputLevel(rigInput input, unsigned char level)
{
    switch(input)
    {
        case inputMic:
            setMicGain(level);
            break;

        case inputACCA:
            setACCGain(level, 0);
            break;

        case inputACCB:
            setACCGain(level, 1);
            break;

        case inputACC:
            setACCGain(level);
            break;

        case inputUSB:
            setUSBGain(level);
            break;

        case inputLAN:
            setLANGain(level);
            break;

        default:
            break;
    }
}

void rigCommander::getModInputLevel(rigInput input)
{
    switch(input)
    {
        case inputMic:
            getMicGain();
            break;

        case inputACCA:
            getACCGain(0);
            break;

        case inputACCB:
            getACCGain(1);
            break;

        case inputACC:
            getACCGain();
            break;

        case inputUSB:
            getUSBGain();
            break;

        case inputLAN:
            getLANGain();
            break;

        default:
            break;
    }
}

QByteArray rigCommander::getUSBAddr()
{
    QByteArray payload;

    switch(rigCaps.model)
    {
        case model705:
            payload.setRawData("\x1A\x05\x01\x16", 4);
            break;
        case model9700:
            payload.setRawData("\x1A\x05\x01\x13", 4);
            break;
        case model7200:
            payload.setRawData("\x1A\x03\x25", 3);
            break;
        case model7100:
        case model7610:
            payload.setRawData("\x1A\x05\x00\x89", 4);
            break;
        case model7300:
            payload.setRawData("\x1A\x05\x00\x65", 4);
            break;
        case model7850:
            payload.setRawData("\x1A\x05\x00\x61", 4);
            break;
        case model7600:
            payload.setRawData("\x1A\x05\x00\x29", 4);
            break;
        default:
            break;
    }
    return payload;
}

void rigCommander::getUSBGain()
{
    QByteArray payload = getUSBAddr();
    prepDataAndSend(payload);
}


void rigCommander::setUSBGain(unsigned char gain)
{
    QByteArray payload = getUSBAddr();
    payload.append(bcdEncodeInt(gain));
    prepDataAndSend(payload);
}

QByteArray rigCommander::getLANAddr()
{
    QByteArray payload;
    switch(rigCaps.model)
    {
        case model705:
            payload.setRawData("\x1A\x05\x01\x17", 4);
            break;
        case model9700:
            payload.setRawData("\x1A\x05\x01\x14", 4);
            break;
        case model7610:
            payload.setRawData("\x1A\x05\x00\x90", 4);
            break;
        case model7850:
            payload.setRawData("\x1A\x05\x00\x62", 4);
            break;
        case model7700:
            payload.setRawData("\x1A\x05\x01\x92", 4);
            break;
        default:
            break;
    }

    return payload;
}

void rigCommander::getLANGain()
{
    QByteArray payload = getLANAddr();
    prepDataAndSend(payload);
}

void rigCommander::setLANGain(unsigned char gain)
{
    QByteArray payload = getLANAddr();
    payload.append(bcdEncodeInt(gain));
    prepDataAndSend(payload);
}

QByteArray rigCommander::getACCAddr(unsigned char ab)
{
    QByteArray payload;

    // Note: the manual for the IC-7600 does not call out a
    // register to adjust the ACC gain.

    // 7850: ACC-A = 0, ACC-B = 1

    switch(rigCaps.model)
    {
        case model9700:
            payload.setRawData("\x1A\x05\x01\x12", 4);
            break;
        case model7100:
            payload.setRawData("\x1A\x05\x00\x87", 4);
            break;
        case model7610:
            payload.setRawData("\x1A\x05\x00\x88", 4);
            break;
        case model7300:
            payload.setRawData("\x1A\x05\x00\x64", 4);
            break;
        case model7850:
            // Note: 0x58 = ACC-A, 0x59 = ACC-B
            if(ab==0)
            {
                // A
                payload.setRawData("\x1A\x05\x00\x58", 4);
            } else {
                // B
                payload.setRawData("\x1A\x05\x00\x59", 4);
            }
            break;
        case model7700:
            payload.setRawData("\x1A\x05\x00\x30", 4);
            break;
        default:
            break;
    }

    return payload;
}

void rigCommander::getACCGain()
{
    QByteArray payload = getACCAddr(0);
    prepDataAndSend(payload);
}


void rigCommander::getACCGain(unsigned char ab)
{
    QByteArray payload = getACCAddr(ab);
    prepDataAndSend(payload);
}


void rigCommander::setACCGain(unsigned char gain)
{
    QByteArray payload = getACCAddr(0);
    payload.append(bcdEncodeInt(gain));
    prepDataAndSend(payload);
}

void rigCommander::setACCGain(unsigned char gain, unsigned char ab)
{
    QByteArray payload = getACCAddr(ab);
    payload.append(bcdEncodeInt(gain));
    prepDataAndSend(payload);
}

void rigCommander::setCompLevel(unsigned char compLevel)
{
    QByteArray payload("\x14\x0E");
    payload.append(bcdEncodeInt(compLevel));
    prepDataAndSend(payload);
}

void rigCommander::setMonitorLevel(unsigned char monitorLevel)
{
    QByteArray payload("\x14\x0E");
    payload.append(bcdEncodeInt(monitorLevel));
    prepDataAndSend(payload);
}

void rigCommander::setVoxGain(unsigned char gain)
{
    QByteArray payload("\x14\x16");
    payload.append(bcdEncodeInt(gain));
    prepDataAndSend(payload);
}

void rigCommander::setAntiVoxGain(unsigned char gain)
{
    QByteArray payload("\x14\x17");
    payload.append(bcdEncodeInt(gain));
    prepDataAndSend(payload);
}


void rigCommander::getRfGain()
{
    QByteArray payload("\x14\x02");
    prepDataAndSend(payload);
}

void rigCommander::getAfGain()
{
    QByteArray payload("\x14\x01");
    prepDataAndSend(payload);
}

void rigCommander::getSql()
{
    QByteArray payload("\x14\x03");
    prepDataAndSend(payload);
}

void rigCommander::getTxLevel()
{
    QByteArray payload("\x14\x0A");
    prepDataAndSend(payload);
}

void rigCommander::getMicGain()
{
    QByteArray payload("\x14\x0B");
    prepDataAndSend(payload);
}

void rigCommander::getCompLevel()
{
    QByteArray payload("\x14\x0E");
    prepDataAndSend(payload);
}

void rigCommander::getMonitorLevel()
{
    QByteArray payload("\x14\x15");
    prepDataAndSend(payload);
}

void rigCommander::getVoxGain()
{
    QByteArray payload("\x14\x16");
    prepDataAndSend(payload);
}

void rigCommander::getAntiVoxGain()
{
    QByteArray payload("\x14\x17");
    prepDataAndSend(payload);
}

void rigCommander::getLevels()
{
    // Function to grab all levels
    getRfGain(); //0x02
    getAfGain(); // 0x01
    getSql(); // 0x03
    getTxLevel(); // 0x0A
    getMicGain(); // 0x0B
    getCompLevel(); // 0x0E
//    getMonitorLevel(); // 0x15
//    getVoxGain(); // 0x16
//    getAntiVoxGain(); // 0x17
}

void rigCommander::getMeters(meterKind meter)
{
    switch(meter)
    {
        case meterS:
            getSMeter();
            break;
        case meterCenter:
            getCenterMeter();
            break;
        case meterSWR:
            getSWRMeter();
            break;
        case meterPower:
            getRFPowerMeter();
            break;
        case meterALC:
            getALCMeter();
            break;
        case meterComp:
            getCompReductionMeter();
            break;
        case meterVoltage:
            getVdMeter();
            break;
        case meterCurrent:
            getIDMeter();
            break;
        default:
            break;
    }
}

void rigCommander::getSMeter()
{
    QByteArray payload("\x15\x02");
    prepDataAndSend(payload);
}

void rigCommander::getCenterMeter()
{
    QByteArray payload("\x15\x04");
    prepDataAndSend(payload);
}

void rigCommander::getRFPowerMeter()
{
    QByteArray payload("\x15\x11");
    prepDataAndSend(payload);
}

void rigCommander::getSWRMeter()
{
    QByteArray payload("\x15\x12");
    prepDataAndSend(payload);
}

void rigCommander::getALCMeter()
{
    QByteArray payload("\x15\x13");
    prepDataAndSend(payload);
}

void rigCommander::getCompReductionMeter()
{
    QByteArray payload("\x15\x14");
    prepDataAndSend(payload);
}

void rigCommander::getVdMeter()
{
    QByteArray payload("\x15\x15");
    prepDataAndSend(payload);
}

void rigCommander::getIDMeter()
{
    QByteArray payload("\x15\x16");
    prepDataAndSend(payload);
}


void rigCommander::setSquelch(unsigned char level)
{
    sendLevelCmd(0x03, level);
}

void rigCommander::setRfGain(unsigned char level)
{
    sendLevelCmd(0x02, level);
}

void rigCommander::setAfGain(unsigned char level)
{
    if (udp == Q_NULLPTR) {
        sendLevelCmd(0x01, level);
    }
    else {
        emit haveSetVolume(level);
    }
}

void rigCommander::setRefAdjustCourse(unsigned char level)
{
    // 1A 05 00 72 0000-0255
    QByteArray payload;
    payload.setRawData("\x1A\x05\x00\x72", 4);
    payload.append(bcdEncodeInt((unsigned int)level));
    prepDataAndSend(payload);

}

void rigCommander::setRefAdjustFine(unsigned char level)
{
    qInfo(logRig()) << __FUNCTION__ << " level: " << level;
    // 1A 05 00 73 0000-0255
    QByteArray payload;
    payload.setRawData("\x1A\x05\x00\x73", 4);
    payload.append(bcdEncodeInt((unsigned int)level));
    prepDataAndSend(payload);
}

void rigCommander::setTime(timekind t)
{
    QByteArray payload;

    switch(rigCaps.model)
    {
        case model705:
            payload.setRawData("\x1A\x05\x01\x66", 4);
            break;
        case model7300:
            payload.setRawData("\x1A\x05\x00\x95", 4);
            break;
        case model7610:
            payload.setRawData("\x1A\x05\x01\x59", 4);
            break;
        case model7700:
            payload.setRawData("\x1A\x05\x00\x59", 4);
            break;
        case model7850:
            payload.setRawData("\x1A\x05\x00\x96", 4);
            break;
        case model9700:
            payload.setRawData("\x1A\x05\x01\x80", 4);
            break;
        case modelR8600:
            payload.setRawData("\x1A\x05\x01\x32", 4);
            break;
        default:
            return;
            break;

    }
    payload.append(convertNumberToHex(t.hours));
    payload.append(convertNumberToHex(t.minutes));
    //qDebug(logRig()) << "Setting time to this: ";
    //printHex(payload);
    prepDataAndSend(payload);
}

void rigCommander::setDate(datekind d)
{
    QByteArray payload;

    switch(rigCaps.model)
    {
        case model705:
            payload.setRawData("\x1A\x05\x01\x65", 4);
            break;
        case model7300:
            payload.setRawData("\x1A\x05\x00\x94", 4);
            break;
        case model7610:
            payload.setRawData("\x1A\x05\x01\x58", 4);
            break;
        case model7700:
            payload.setRawData("\x1A\x05\x00\x58", 4);
            break;
        case model7850:
            payload.setRawData("\x1A\x05\x00\x95", 4);
            break;
        case model9700:
            payload.setRawData("\x1A\x05\x01\x79", 4);
            break;
        case modelR8600:
            payload.setRawData("\x1A\x05\x01\x31", 4);
            break;
        default:
            return;
            break;

    }
    // YYYYMMDD
    payload.append(convertNumberToHex(d.year/100)); // 20
    payload.append(convertNumberToHex(d.year - 100*(d.year/100))); // 21
    payload.append(convertNumberToHex(d.month));
    payload.append(convertNumberToHex(d.day));
    //qDebug(logRig()) << "Setting date to this: ";
    //printHex(payload);
    prepDataAndSend(payload);
}

void rigCommander::setUTCOffset(timekind t)
{
    QByteArray payload;

    switch(rigCaps.model)
    {
        case model705:
            payload.setRawData("\x1A\x05\x01\x70", 4);
            break;
        case model7300:
            payload.setRawData("\x1A\x05\x00\x96", 4);
            break;
        case model7610:
            payload.setRawData("\x1A\x05\x01\x62", 4);
            break;
        case model7700:
            payload.setRawData("\x1A\x05\x00\x61", 4);
            break;
        case model7850:
            // Clock 1:
            payload.setRawData("\x1A\x05\x00\x99", 4);
            break;
        case model9700:
            payload.setRawData("\x1A\x05\x01\x84", 4);
            break;
        case modelR8600:
            payload.setRawData("\x1A\x05\x01\x35", 4);
            break;
        default:
            return;
            break;

    }
    payload.append(convertNumberToHex(t.hours));
    payload.append(convertNumberToHex(t.minutes));
    payload.append((unsigned char)t.isMinus);
    //qDebug(logRig()) << "Setting UTC Offset to this: ";
    //printHex(payload);
    prepDataAndSend(payload);
}

unsigned char rigCommander::convertNumberToHex(unsigned char num)
{
    // Two digit only
    if(num > 99)
    {
        qInfo(logRig()) << "Invalid numeric conversion from num " << num << " to hex.";
        return 0xFA;
    }
    unsigned char result = 0;
    result =  (num/10) << 4;
    result |= (num - 10*(num/10));
    qDebug(logRig()) << "Converting number: " << num << " to hex: " + QString("0x%1").arg(result, 2, 16, QChar('0'));
    return result;
}

void rigCommander::sendLevelCmd(unsigned char levAddr, unsigned char level)
{
    QByteArray payload("\x14");
    payload.append(levAddr);
    // careful here. The value in the units and tens can't exceed 99.
    // ie, can't do this: 01 f2
    payload.append((int)level/100); // make sure it works with a zero
    // convert the tens:
    int tens = (level - 100*((int)level/100))/10;
    // convert the units:
    int units = level - 100*((int)level/100);
    units = units - 10*((int)(units/10));
    // combine and send:
    payload.append((tens << 4) | (units) ); // make sure it works with a zero

    prepDataAndSend(payload);
}

void rigCommander::getRefAdjustCourse()
{
    // 1A 05 00 72
    QByteArray payload;
    payload.setRawData("\x1A\x05\x00\x72", 4);
    prepDataAndSend(payload);
}

void rigCommander::getRefAdjustFine()
{
    // 1A 05 00 73
    QByteArray payload;
    payload.setRawData("\x1A\x05\x00\x73", 4);
    prepDataAndSend(payload);
}

void rigCommander::parseRegisters1C()
{
    // PTT lives here
    // Not sure if 02 is the right place to switch.
    // TODO: test this function
    switch(payloadIn[01])
    {
        case '\x00':
            parsePTT();
            break;
        case '\x01':
            // ATU status (on/off/tuning)
            parseATU();
            break;
        default:
            break;
    }
}

void rigCommander::parseRegister21()
{
    // Register 21 is RIT and Delta TX
    int ritHz = 0;
    freqt f;
    QByteArray longfreq;

    // Example RIT value reply:
    // Index: 00 01 02 03 04 05
    // DATA:  21 00 32 03 00 fd

    switch(payloadIn[01])
    {
        case '\x00':
            // RIT frequency
            //
            longfreq = payloadIn.mid(2,2);
            longfreq.append(QByteArray(3,'\x00'));
            f = parseFrequency(longfreq, 3);
            ritHz = f.Hz*((payloadIn.at(4)=='\x01')?-1:1);
            emit haveRitFrequency(ritHz);
            break;
        case '\x01':
            // RIT on/off
            if(payloadIn.at(02) == '\x01')
            {
                emit haveRitEnabled(true);
            } else {
                emit haveRitEnabled(false);
            }
            break;
        case '\x02':
            // Delta TX setting on/off
            break;
        default:
            break;
    }
}

void rigCommander::parseATU()
{
    // qInfo(logRig()) << "Have ATU status from radio. Emitting.";
    // Expect:
    // [0]: 0x1c
    // [1]: 0x01
    // [2]: 0 = off, 0x01 = on, 0x02 = tuning in-progress
    emit haveATUStatus((unsigned char) payloadIn[2]);
}

void rigCommander::parsePTT()
{
    // read after payloadIn[02]

    if(payloadIn[2] == (char)0)
    {
        // PTT off
        emit havePTTStatus(false);
    } else {
        // PTT on
        emit havePTTStatus(true);
    }
    rigState.ptt = (unsigned char)payloadIn[2];

}

void rigCommander::parseRegisters1A()
{
    // The simpler of the 1A stuff:

    // 1A 06: data mode on/off
    //    07: IP+ enable/disable
    //    00:   memory contents
    //    01:   band stacking memory contents (last freq used is stored here per-band)
    //    03: filter width
    //    04: AGC rate
    // qInfo(logRig()) << "Looking at register 1A :";
    // printHex(payloadIn, false, true);

    // "INDEX: 00 01 02 03 04 "
    // "DATA:  1a 06 01 03 fd " (data mode enabled, filter width 3 selected)

    switch(payloadIn[01])
    {
        case '\x00':
            // Memory contents
            break;
        case '\x01':
            // band stacking register
            parseBandStackReg();
            break;
        case '\x06':
            // data mode
            // emit havedataMode( (bool) payloadIn[somebit])
            // index
            // 03 04
            // XX YY
            // XX = 00 (off) or 01 (on)
            // YY: filter selected, 01 through 03.;
            // if YY is 00 then XX was also set to 00
            emit haveDataMode((bool)payloadIn[03]);
            rigState.datamode = (bool)payloadIn[03];
            break;
        case '\x07':
            // IP+ status
            break;
        default:
            break;
    }
}

void rigCommander::parseRegister1B()
{
    quint16 tone=0;
    bool tinv = false;
    bool rinv = false;

    switch(payloadIn[01])
    {
        case '\x00':
            // "Repeater tone"
            tone = decodeTone(payloadIn);
            emit haveTone(tone);
            rigState.ctcss = tone;
            break;
        case '\x01':
            // "TSQL tone"
            tone = decodeTone(payloadIn);
            emit haveTSQL(tone);
            rigState.tsql = tone;
            break;
        case '\x02':
            // DTCS (DCS)
            tone = decodeTone(payloadIn, tinv, rinv);
            emit haveDTCS(tone, tinv, rinv);
            rigState.dtcs = tone;
            break;
        case '\x07':
            // "CSQL code (DV mode)"
            tone = decodeTone(payloadIn);
            rigState.csql = tone;
            break;
        default:
            break;
    }
}

void rigCommander::parseRegister16()
{
    //"INDEX: 00 01 02 03 "
    //"DATA:  16 5d 00 fd "
    //               ^-- mode info here
    switch(payloadIn.at(1))
    {
        case '\x5d':
            emit haveRptAccessMode((rptAccessTxRx)payloadIn.at(2));
            break;
        case '\x02':
            // Preamp
            emit havePreamp((unsigned char)payloadIn.at(2));
            rigState.preamp = (unsigned char)payloadIn.at(2);
            break;
        default:
            break;
    }
}

void rigCommander::parseBandStackReg()
{
    //qInfo(logRig()) << "Band stacking register response received: ";
    //printHex(payloadIn, false, true);

    // Reference output, 20 meters, regCode 01 (latest):
    // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 "
    // "DATA:  1a 01 05 01 60 03 23 14 00 00 03 10 00 08 85 00 08 85 fd "

    char band = payloadIn[2];
    char regCode = payloadIn[3];
    freqt freqs = parseFrequency(payloadIn, 7);
    //float freq = (float)freqs.MHzDouble;

    bool dataOn = (payloadIn[11] & 0x10) >> 4; // not sure...
    char mode = payloadIn[9];
    char filter = payloadIn[10];
    // 09, 10 mode
    // 11 digit RH: data mode on (1) or off (0)
    // 11 digit LH: CTCSS 0 = off, 1 = TONE, 2 = TSQL

    // 12, 13 : tone freq setting
    // 14, 15 tone squelch freq setting
    // if more, memory name (label) ascii

    qInfo(logRig()) << "BSR in rigCommander: band: " << QString("%1").arg(band) << " regCode: " << (QString)regCode << " freq Hz: " << freqs.Hz << ", mode: " << (unsigned int)mode << ", filter: " << (unsigned int)filter << " data: " << dataOn;
    //qInfo(logRig()) << "mode: " << (QString)mode << " dataOn: " << dataOn;
    //qInfo(logRig()) << "Freq Hz: " << freqs.Hz;

    emit haveBandStackReg(freqs, mode, filter, dataOn);
}

void rigCommander::parseDetailedRegisters1A05()
{
    // It seems a lot of misc stuff is under this command and subcommand.
    // 1A 05 ...
    // 00 01 02 03 04 ...

    // 02 and 03 make up a BCD'd number:
    // 0001, 0002, 0003, ... 0101, 0102, 0103...

    // 04 is a typical single byte response
    // 04 05 is a typical 0-255 response

    // This file processes the registers which are radically different in each model.
    // It is a work in progress.
    // TODO: inputMod source and gain for models: 7700, and 7600

    int level = (100*bcdHexToUChar(payloadIn[4])) + bcdHexToUChar(payloadIn[5]);

    int subcmd = bcdHexToUChar(payloadIn[3]) + (100*bcdHexToUChar(payloadIn[2]));

    rigInput input;
    input = (rigInput)bcdHexToUChar(payloadIn[4]);
    int inputRaw = bcdHexToUChar(payloadIn[4]);

    switch(rigCaps.model)
    {
        case model9700:
            switch(subcmd)
            {

                case 72:
                    // course reference
                    emit haveRefAdjustCourse(  bcdHexToUChar(payloadIn[5]) + (100*bcdHexToUChar(payloadIn[4])) );
                    break;
                case 73:
                    // fine reference
                    emit haveRefAdjustFine( bcdHexToUChar(payloadIn[5]) + (100*bcdHexToUChar(payloadIn[4])) );
                    break;
                case 112:
                    emit haveACCGain(level, 5);
                    break;
                case 113:
                    emit haveUSBGain(level);
                    break;
                case 114:
                    emit haveLANGain(level);
                    break;
                case 115:
                    emit haveModInput(input, false);
                    break;
                case 116:
                    emit haveModInput(input, true);
                    break;
                default:
                    break;
            }
            break;
        case model7850:
            switch(subcmd)
            {
                case 63:
                    switch(inputRaw)
                    {
                        case 0:
                            input = inputMic;
                            break;
                        case 1:
                            input = inputACCA;
                            break;
                        case 2:
                            input = inputACCB;
                            break;
                        case 8:
                            input = inputUSB;
                            break;
                        case 9:
                            input = inputLAN;
                            break;
                        default:
                            input = inputUnknown;
                            break;
                    }
                    emit haveModInput(input, false);
                    break;
                case 64:
                    switch(inputRaw)
                    {
                        case 0:
                            input = inputMic;
                            break;
                        case 1:
                            input = inputACCA;
                            break;
                        case 2:
                            input = inputACCB;
                            break;
                        case 8:
                            input = inputUSB;
                            break;
                        case 9:
                            input = inputLAN;
                            break;
                        default:
                            input = inputUnknown;
                            break;
                    }
                    emit haveModInput(input, true);
                    break;
                case 58:
                    emit haveACCGain(level, 0);
                    break;
                case 59:
                    emit haveACCGain(level, 1);
                    break;
                case 61:
                    emit haveUSBGain(level);
                    break;
                case 62:
                    emit haveLANGain(level);
                    break;
                default:
                    break;
            }
            break;
        case model7610:
            switch(subcmd)
            {
                case 91:
                    emit haveModInput(input, false);
                    break;
                case 92:
                    emit haveModInput(input, true);
                    break;
                case 88:
                    emit haveACCGain(level, 5);
                    break;
                case 89:
                    emit haveUSBGain(level);
                    break;
                case 90:
                    emit haveLANGain(level);
                    break;
                default:
                    break;
            }
            return;
        case model7600:
            switch(subcmd)
            {
                case 30:
                    emit haveModInput(input, false);
                    break;
                case 31:
                    emit haveModInput(input, true);
                    break;
                case 29:
                    emit haveUSBGain(level);
                    break;
                default:
                    break;
            }
            return;
        case model7300:
            switch(subcmd)
            {
                case 64:
                    emit haveACCGain(level, 5);
                    break;
                case 65:
                    emit haveUSBGain(level);
                    break;
                case 66:
                    emit haveModInput(input, false);
                    break;
                case 67:
                    emit haveModInput(input, true);
                    break;
                default:
                    break;
            }
            return;
        case model7100:
            switch(subcmd)
            {
                case 87:
                    emit haveACCGain(level, 5);
                    break;
                case 89:
                    emit haveUSBGain(level);
                    break;
                case 90:
                    emit haveModInput(input, false);
                    break;
                case 91:
                    emit haveModInput(input, true);
                    break;
                default:
                    break;
            }
            break;
        case model705:
            switch(subcmd)
            {
                case 116:
                    emit haveUSBGain(level);
                    break;
                case 117:
                    emit haveLANGain(level);
                    break;
                case 118:
                    switch(inputRaw)
                    {
                        case 0:
                            input = inputMic;
                            break;
                        case 1:
                            input = inputUSB;
                            break;
                        case 3:
                            input = inputLAN;
                            break;
                        default:
                            input = inputUnknown;
                            break;
                    }
                    emit haveModInput(input, false);
                    break;
                case 119:
                    switch(inputRaw)
                    {
                        case 0:
                            input = inputMic;
                            break;
                        case 1:
                            input = inputUSB;
                            break;
                        case 3:
                            input = inputLAN;
                            break;
                        default:
                            input = inputUnknown;
                            break;
                    }
                    emit haveModInput(input, true);
                    break;
                default:
                    break;
            }
            break;

        default:
            break;
    }
}

void rigCommander::parseWFData()
{
    freqt freqSpan;
    bool isSub;
    switch(payloadIn[1])
    {
        case 0:
            // Chunk of spectrum
            parseSpectrum();
            break;
        case 0x10:
            // confirming scope is on
            break;
        case 0x11:
            // confirming output enabled/disabled of wf data.
            break;
        case 0x14:
            // fixed or center
            emit haveSpectrumMode(static_cast<spectrumMode>((unsigned char)payloadIn[3]));
            // [1] 0x14
            // [2] 0x00
            // [3] 0x00 (center), 0x01 (fixed), 0x02, 0x03
            break;
        case 0x15:
            // read span in center mode
            // [1] 0x15
            // [2] to [8] is span encoded as a frequency
            isSub = payloadIn.at(2)==0x01;
            freqSpan = parseFrequency(payloadIn, 6);
            emit haveScopeSpan(freqSpan, isSub);
            qInfo(logRig()) << "Received 0x15 center span data: for frequency " << freqSpan.Hz;
            //printHex(payloadIn, false, true);
            break;
        case 0x16:
            // read edge mode center in edge mode
            emit haveScopeEdge((char)payloadIn[2]);
            qInfo(logRig()) << "Received 0x16 edge in center mode:";
            printHex(payloadIn, false, true);
            // [1] 0x16
            // [2] 0x01, 0x02, 0x03: Edge 1,2,3
            break;
        case 0x17:
            // Hold status (only 9700?)
            qInfo(logRig()) << "Received 0x17 hold status - need to deal with this!";
            printHex(payloadIn, false, true);
            break;
        case 0x19:
            // scope reference level
            // [1] 0x19
            // [2] 0x00
            // [3] 10dB digit, 1dB digit
            // [4] 0.1dB digit, 0
            // [5] 0x00 = +, 0x01 = -
            parseSpectrumRefLevel();
            break;
        default:
            qInfo(logRig()) << "Unknown waveform data received: ";
            printHex(payloadIn, false, true);
            break;
    }
}


mode_info rigCommander::createMode(mode_kind m, unsigned char reg, QString name)
{
    mode_info mode;
    mode.mk = m;
    mode.reg = reg;
    mode.name = name;
    return mode;
}

centerSpanData rigCommander::createScopeCenter(centerSpansType s, QString name)
{
    centerSpanData csd;
    csd.cstype = s;
    csd.name = name;
    return csd;
}

void rigCommander::determineRigCaps()
{
    //TODO: Determine available bands (low priority, rig will reject out of band requests anyway)

    std::vector <bandType> standardHF;
    std::vector <bandType> standardVU;

    // Most commonly supported "HF" bands:
    standardHF = {band6m, band10m, band10m, band12m,
                 band15m, band17m, band20m, band30m,
                 band40m, band60m, band80m, band160m};

    standardVU = {band70cm, band2m};

    std::vector <mode_info> commonModes;
    commonModes = { createMode(modeLSB, 0x00, "LSB"), createMode(modeUSB, 0x01, "USB"),
                    createMode(modeFM, 0x05, "FM"), createMode(modeAM, 0x02, "AM"),
                    createMode(modeCW, 0x03, "CW"), createMode(modeCW_R, 0x07, "CW-R"),
                    createMode(modeRTTY, 0x04, "RTTY"), createMode(modeRTTY_R, 0x08, "RTTY-R")
                  };



    rigCaps.model = model;
    rigCaps.civ = incomingCIVAddr;

    rigCaps.hasDD = false;
    rigCaps.hasDV = false;
    rigCaps.hasDataModes = true; // USB-D, LSB-D, etc
    rigCaps.hasATU = false;

    rigCaps.hasCTCSS = false;
    rigCaps.hasDTCS = false;

    rigCaps.spectSeqMax = 0;
    rigCaps.spectAmpMax = 0;
    rigCaps.spectLenMax = 0;
    rigCaps.scopeCenterSpans = { createScopeCenter(cs2p5k, "±2.5k"), createScopeCenter(cs5k, "±5k"),
                                 createScopeCenter(cs10k, "±10k"), createScopeCenter(cs25k, "±25k"),
                                 createScopeCenter(cs50k, "±50k"), createScopeCenter(cs100k, "±100k"),
                                 createScopeCenter(cs250k, "±250k"), createScopeCenter(cs500k, "±500k")
                               };

    
    rigCaps.hasFDcomms = true; // false for older radios

    // Clear inputs/preamps/attenuators lists in case we have re-connected.
    rigCaps.preamps.clear();
    rigCaps.attenuators.clear();
    rigCaps.inputs.clear();
    rigCaps.inputs.append(inputMic);

    rigCaps.hasAttenuator = true; // Verify that all recent rigs have attenuators
    rigCaps.attenuators.push_back('\x00');
    rigCaps.hasPreamp = true;
    rigCaps.preamps.push_back('\x00');

    rigCaps.hasAntennaSel = false;
    rigCaps.hasRXAntenna = false;

    rigCaps.hasTransmit = true;
    rigCaps.hasPTTCommand = true;

    // Common, reasonable defaults for most supported HF rigs:
    rigCaps.bsr[band160m] = 0x01;
    rigCaps.bsr[band80m] = 0x02;
    rigCaps.bsr[band40m] = 0x03;
    rigCaps.bsr[band30m] = 0x04;
    rigCaps.bsr[band20m] = 0x05;
    rigCaps.bsr[band17m] = 0x06;
    rigCaps.bsr[band15m] = 0x07;
    rigCaps.bsr[band12m] = 0x08;
    rigCaps.bsr[band10m] = 0x09;
    rigCaps.bsr[band6m] = 0x10;
    rigCaps.bsr[bandGen] = 0x11;

    // Bands that seem to change with every model:
    rigCaps.bsr[band2m] = 0x00;
    rigCaps.bsr[band70cm] = 0x00;
    rigCaps.bsr[band23cm] = 0x00;

    // These bands generally aren't defined:
    rigCaps.bsr[band4m] = 0x00;
    rigCaps.bsr[band60m] = 0x00;
    rigCaps.bsr[bandWFM] = 0x00;
    rigCaps.bsr[bandAir] = 0x00;
    rigCaps.bsr[band630m] = 0x00;
    rigCaps.bsr[band2200m] = 0x00;

    switch(model){
        case model7300:
            rigCaps.modelName = QString("IC-7300");
            rigCaps.hasSpectrum = true;
            rigCaps.spectSeqMax = 11;
            rigCaps.spectAmpMax = 160;
            rigCaps.spectLenMax = 475;
            rigCaps.inputs.append(inputUSB);
            rigCaps.inputs.append(inputACC);
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasATU = true;
            rigCaps.hasCTCSS = true;
            rigCaps.attenuators.push_back('\x20');
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(band4m);
            rigCaps.bands.push_back(bandGen);
            rigCaps.bands.push_back(band630m);
            rigCaps.bands.push_back(band2200m);
            rigCaps.modes = commonModes;
            break;
        case modelR8600:
            rigCaps.modelName = QString("IC-R8600");
            rigCaps.hasSpectrum = true;
            rigCaps.spectSeqMax = 11;
            rigCaps.spectAmpMax = 160;
            rigCaps.spectLenMax = 475;
            rigCaps.inputs.clear();
            rigCaps.hasLan = true;
            rigCaps.hasEthernet = true;
            rigCaps.hasWiFi = false;
            rigCaps.hasTransmit = false;
            rigCaps.hasPTTCommand = false;
            rigCaps.hasCTCSS = true;
            rigCaps.hasDTCS = true;
            rigCaps.hasDV = true;
            rigCaps.attenuators.push_back('\x10');
            rigCaps.attenuators.push_back('\x20');
            rigCaps.attenuators.push_back('\x30');
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.hasAntennaSel = true;
            rigCaps.antennas = {0x00, 0x01, 0x02};
            rigCaps.bands = standardHF;
            rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end());
            rigCaps.bands.insert(rigCaps.bands.end(), {band23cm, band4m, band630m, band2200m, bandGen});
            rigCaps.modes = commonModes;
            rigCaps.modes.insert(rigCaps.modes.end(), {
                                     createMode(modeWFM, 0x06, "WFM"), createMode(modeS_AMD, 0x11, "S-AM (D)"),
                                     createMode(modeS_AML, 0x14, "S-AM(L)"), createMode(modeS_AMU, 0x15, "S-AM(U)"),
                                     createMode(modeP25, 0x16, "P25"), createMode(modedPMR, 0x18, "dPMR"),
                                     createMode(modeNXDN_VN, 0x19, "NXDN-VN"), createMode(modeNXDN_N, 0x20, "NXDN-N"),
                                     createMode(modeDCR, 0x21, "DCR")});
            rigCaps.scopeCenterSpans.insert(rigCaps.scopeCenterSpans.end(), {createScopeCenter(cs1M, "±1M"), createScopeCenter(cs2p5M, "±2.5M")});
            break;
        case model9700:
            rigCaps.modelName = QString("IC-9700");
            rigCaps.hasSpectrum = true;
            rigCaps.spectSeqMax = 11;
            rigCaps.spectAmpMax = 160;
            rigCaps.spectLenMax = 475;
            rigCaps.inputs.append(inputLAN);
            rigCaps.inputs.append(inputUSB);
            rigCaps.inputs.append(inputACC);
            rigCaps.hasLan = true;
            rigCaps.hasEthernet = true;
            rigCaps.hasWiFi = false;
            rigCaps.hasDD = true;
            rigCaps.hasDV = true;
            rigCaps.hasCTCSS = true;
            rigCaps.hasDTCS = true;
            rigCaps.attenuators.push_back('\x10');
            rigCaps.preamps.push_back('\x01');
            rigCaps.bands = standardVU;
            rigCaps.bands.push_back(band23cm);
            rigCaps.bsr[band23cm] = 0x03;
            rigCaps.bsr[band70cm] = 0x02;
            rigCaps.bsr[band2m] = 0x01;
            rigCaps.modes = commonModes;
            rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modeDV, 0x17, "DV"),
                                                       createMode(modeDD, 0x22, "DD")});
            break;
        case model910h:
            rigCaps.modelName = QString("IC-910H");
            rigCaps.hasSpectrum = false;
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasDD = false;
            rigCaps.hasDV = false;
            rigCaps.hasCTCSS = true;
            rigCaps.hasDTCS = true;
            rigCaps.hasATU = false;
            rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x10' , '\x20', '\x30'});
            rigCaps.preamps.push_back('\x01');
            rigCaps.bands = standardVU;
            rigCaps.bands.push_back(band23cm);
            rigCaps.bsr[band23cm] = 0x03;
            rigCaps.bsr[band70cm] = 0x02;
            rigCaps.bsr[band2m] = 0x01;
            rigCaps.modes = commonModes;
            break;
        case model7600:
            rigCaps.modelName = QString("IC-7600");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.append(inputACC);
            rigCaps.inputs.append(inputUSB);
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasATU = true;
            rigCaps.hasCTCSS = false;
            rigCaps.hasDTCS = false;
            rigCaps.attenuators.insert(rigCaps.attenuators.end(), {0x00, 0x06, 0x12, 0x18});
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.antennas = {0x00, 0x01};
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(bandGen);
            rigCaps.bsr[bandGen] = 0x11;
            rigCaps.modes = commonModes;
            rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modePSK, 0x12, "PSK"),
                                                       createMode(modePSK_R, 0x13, "PSK-R")});
            break;
        case model7610:
            rigCaps.modelName = QString("IC-7610");
            rigCaps.hasSpectrum = true;
            rigCaps.spectSeqMax = 15;
            rigCaps.spectAmpMax = 200;
            rigCaps.spectLenMax = 689;
            rigCaps.inputs.append(inputLAN);
            rigCaps.inputs.append(inputUSB);
            rigCaps.inputs.append(inputACC);
            rigCaps.hasLan = true;
            rigCaps.hasEthernet = true;
            rigCaps.hasWiFi = false;
            rigCaps.hasCTCSS = true;
            rigCaps.attenuators.insert(rigCaps.attenuators.end(),
                                      {'\x03', '\x06', '\x09', '\x12',\
                                       '\x15', '\x18', '\x21', '\x24',\
                                       '\x27', '\x30', '\x33', '\x36',
                                       '\x39', '\x42', '\x45'});
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.hasAntennaSel = true;
            rigCaps.antennas = {0x00, 0x01};
            rigCaps.hasATU = true;
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(bandGen);
            rigCaps.bands.push_back(band630m);
            rigCaps.bands.push_back(band2200m);
            rigCaps.modes = commonModes;
            rigCaps.hasRXAntenna = true;
            break;
        case model7850:
            rigCaps.modelName = QString("IC-785x");
            rigCaps.hasSpectrum = true;
            rigCaps.spectSeqMax = 15;
            rigCaps.spectAmpMax = 136;
            rigCaps.spectLenMax = 689;
            rigCaps.inputs.append(inputLAN);
            rigCaps.inputs.append(inputUSB);
            rigCaps.inputs.append(inputACCA);
            rigCaps.inputs.append(inputACCB);
            rigCaps.hasLan = true;
            rigCaps.hasEthernet = true;
            rigCaps.hasWiFi = false;
            rigCaps.hasATU = true;
            rigCaps.hasCTCSS = true;
            rigCaps.attenuators.insert(rigCaps.attenuators.end(),
                                      {'\x03', '\x06', '\x09',
                                       '\x12', '\x15', '\x18', '\x21'});
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.hasAntennaSel = true;
            rigCaps.antennas = {0x00, 0x01, 0x02, 0x03};
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(bandGen);
            rigCaps.bands.push_back(band630m);
            rigCaps.bands.push_back(band2200m);
            rigCaps.modes = commonModes;
            rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modePSK, 0x12, "PSK"),
                                                       createMode(modePSK_R, 0x13, "PSK-R")});
            rigCaps.hasRXAntenna = true;
            break;
        case model705:
            rigCaps.modelName = QString("IC-705");
            rigCaps.hasSpectrum = true;
            rigCaps.spectSeqMax = 11;
            rigCaps.spectAmpMax = 160;
            rigCaps.spectLenMax = 475;
            rigCaps.inputs.append(inputLAN);
            rigCaps.inputs.append(inputUSB);
            rigCaps.hasLan = true;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = true;
            rigCaps.hasDD = true;
            rigCaps.hasDV = true;
            rigCaps.hasATU = true;
            rigCaps.hasCTCSS = true;
            rigCaps.hasDTCS = true;
            rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x10' , '\x20'});
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.bands = standardHF;
            rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end());
            rigCaps.bands.push_back(bandGen);
            rigCaps.bands.push_back(bandAir);
            rigCaps.bands.push_back(bandWFM);
            rigCaps.bsr[band70cm] = 0x14;
            rigCaps.bsr[band2m] = 0x13;
            rigCaps.bsr[bandAir] = 0x12;
            rigCaps.bsr[bandWFM] = 0x11;
            rigCaps.bsr[bandGen] = 0x15;
            rigCaps.bands.push_back(band630m);
            rigCaps.bands.push_back(band2200m);
            rigCaps.modes = commonModes;
            rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modeWFM, 0x06, "WFM"),
                                                       createMode(modeDV, 0x17, "DV")});
            break;
        case model7000:
            rigCaps.modelName = QString("IC-7000");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.append(inputACC);
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasATU = true;
            rigCaps.hasCTCSS = true;
            rigCaps.hasDTCS = true;
            rigCaps.attenuators.push_back('\x12');
            rigCaps.preamps.push_back('\x01');
            rigCaps.bands = standardHF;
            rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end());
            rigCaps.bands.push_back(bandGen);
            rigCaps.bsr[band2m] = 0x11;
            rigCaps.bsr[band70cm] = 0x12;
            rigCaps.bsr[bandGen] = 0x13;
            rigCaps.modes = commonModes;
            break;
        case model7410:
            rigCaps.modelName = QString("IC-7410");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.append(inputACC);
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = true;
            rigCaps.hasATU = true;
            rigCaps.hasCTCSS = true;
            rigCaps.hasDTCS = true;
            rigCaps.attenuators.push_back('\x12');
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.antennas = {0x00, 0x01};
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(bandGen);
            rigCaps.bsr[bandGen] = 0x11;
            rigCaps.modes = commonModes;
            break;
        case model7100:
            rigCaps.modelName = QString("IC-7100");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.append(inputUSB);
            rigCaps.inputs.append(inputACC);
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasATU = true;
            rigCaps.hasCTCSS = true;
            rigCaps.hasDTCS = true;
            rigCaps.attenuators.push_back('\x12');
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.bands = standardHF;
            rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end());
            rigCaps.bands.push_back(band4m);
            rigCaps.bands.push_back(bandGen);
            rigCaps.bsr[band2m] = 0x11;
            rigCaps.bsr[band70cm] = 0x12;
            rigCaps.bsr[bandGen] = 0x13;
            rigCaps.modes = commonModes;
            rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modeWFM, 0x06, "WFM"),
                                                       createMode(modeDV, 0x17, "DV")});
            break;
        case model7200:
            rigCaps.modelName = QString("IC-7200");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.append(inputUSB);
            rigCaps.inputs.append(inputACC);
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasATU = true;
            rigCaps.hasCTCSS = true;
            rigCaps.hasDTCS = true;
            rigCaps.attenuators.push_back('\x20');
            rigCaps.preamps.push_back('\x01');
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(bandGen);
            rigCaps.bsr[bandGen] = 0x11;
            rigCaps.modes = commonModes;
            break;    
        case model7700:
            rigCaps.modelName = QString("IC-7700");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.append(inputLAN);
            //rigCaps.inputs.append(inputSPDIF);
            rigCaps.inputs.append(inputACC);
            rigCaps.hasLan = true;
            rigCaps.hasEthernet = true;
            rigCaps.hasWiFi = false;
            rigCaps.hasCTCSS = true;
            rigCaps.attenuators.insert(rigCaps.attenuators.end(),
                                      {'\x06', '\x12', '\x18'});
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.hasAntennaSel = true;
            rigCaps.antennas = {0x00, 0x01, 0x02, 0x03}; // not sure if 0x03 works
            rigCaps.hasATU = true;
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(bandGen);
            rigCaps.bands.push_back(band630m);
            rigCaps.bands.push_back(band2200m);
            rigCaps.modes = commonModes;
            rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modePSK, 0x12, "PSK"),
                                                       createMode(modePSK_R, 0x13, "PSK-R")});
            break;
        case model706:
            rigCaps.modelName = QString("IC-706");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.clear();
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasATU = true;
            rigCaps.hasPTTCommand = false;
            rigCaps.hasDataModes = false;
            rigCaps.attenuators.push_back('\x20');
            rigCaps.bands = standardHF;
            rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end());
            rigCaps.bands.push_back(bandGen);
            rigCaps.modes = commonModes;
            rigCaps.modes.insert(rigCaps.modes.end(), createMode(modeWFM, 0x06, "WFM"));
            break;
        case model718:
            rigCaps.modelName = QString("IC-718");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.clear();
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasATU = false;
            rigCaps.hasPTTCommand = false;
            rigCaps.hasDataModes = false;
            rigCaps.attenuators.push_back('\x20');
            rigCaps.preamps.push_back('\x01');
            rigCaps.bands =   {band10m, band10m, band12m,
                               band15m, band17m, band20m, band30m,
                               band40m, band60m, band80m, band160m, bandGen};
            rigCaps.modes = { createMode(modeLSB, 0x00, "LSB"), createMode(modeUSB, 0x01, "USB"),
                              createMode(modeAM, 0x02, "AM"),
                              createMode(modeCW, 0x03, "CW"), createMode(modeCW_R, 0x07, "CW-R"),
                              createMode(modeRTTY, 0x04, "RTTY"), createMode(modeRTTY_R, 0x08, "RTTY-R")
                            };
            break;
        case model756pro:
            rigCaps.modelName = QString("IC-756 Pro");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.clear();
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasATU = true;
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x06' , '\x12', '\x18'});
            rigCaps.antennas = {0x00, 0x01};
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(bandGen);
            rigCaps.bsr[bandGen] = 0x11;
            rigCaps.modes = commonModes;
            break;
        case model756proii:
            rigCaps.modelName = QString("IC-756 Pro II");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.clear();
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasATU = true;
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x06' , '\x12', '\x18'});
            rigCaps.antennas = {0x00, 0x01};
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(bandGen);
            rigCaps.bsr[bandGen] = 0x11;
            rigCaps.modes = commonModes;
            break;
        case model756proiii:
            rigCaps.modelName = QString("IC-756 Pro III");
            rigCaps.hasSpectrum = false;
            rigCaps.inputs.clear();
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasATU = true;
            rigCaps.preamps.push_back('\x01');
            rigCaps.preamps.push_back('\x02');
            rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x06' , '\x12', '\x18'});
            rigCaps.antennas = {0x00, 0x01};
            rigCaps.bands = standardHF;
            rigCaps.bands.push_back(bandGen);
            rigCaps.bsr[bandGen] = 0x11;
            rigCaps.modes = commonModes;
            break;
        default:
            rigCaps.modelName = QString("IC-0x%1").arg(rigCaps.modelID, 2, 16);
            rigCaps.hasSpectrum = false;
            rigCaps.spectSeqMax = 0;
            rigCaps.spectAmpMax = 0;
            rigCaps.spectLenMax = 0;
            rigCaps.inputs.clear();
            rigCaps.hasLan = false;
            rigCaps.hasEthernet = false;
            rigCaps.hasWiFi = false;
            rigCaps.hasFDcomms = false;
            rigCaps.hasPreamp = false;
            rigCaps.hasAntennaSel = false;
            rigCaps.attenuators.push_back('\x10');
            rigCaps.attenuators.push_back('\x12');
            rigCaps.attenuators.push_back('\x20');
            rigCaps.bands = standardHF;
            rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end());
            rigCaps.bands.insert(rigCaps.bands.end(), {band23cm, band4m, band630m, band2200m, bandGen});
            rigCaps.modes = commonModes;
            qInfo(logRig()) << "Found unknown rig: 0x" << QString("%1").arg(rigCaps.modelID, 2, 16);
            break;
    }
    haveRigCaps = true;

    if(lookingForRig)
    {
        lookingForRig = false;
        foundRig = true;

        qDebug(logRig()) << "---Rig FOUND from broadcast query:";
        this->civAddr = incomingCIVAddr; // Override and use immediately.
        payloadPrefix = QByteArray("\xFE\xFE");
        payloadPrefix.append(civAddr);
        payloadPrefix.append((char)compCivAddr);
        // if there is a compile-time error, remove the following line, the "hex" part is the issue:
        qInfo(logRig()) << "Using incomingCIVAddr: (int): " << this->civAddr << " hex: " << hex << this->civAddr;
        emit discoveredRigID(rigCaps);
    } else {
        if(!foundRig)
        {
            emit discoveredRigID(rigCaps);
            foundRig = true;
        }
        emit haveRigID(rigCaps);
    }
}

void rigCommander::parseSpectrum()
{
    if(!haveRigCaps)
    {
        qDebug(logRig()) << "Spectrum received in rigCommander, but rigID is incomplete.";
        return;
    }
    if(rigCaps.spectSeqMax == 0)
    {
        // there is a chance this will happen with rigs that support spectrum. Once our RigID query returns, we will parse correctly.
        qInfo(logRig()) << "Warning: Spectrum sequence max was zero, yet spectrum was received.";
        return;
    }
    // Here is what to expect:
    // payloadIn[00] = '\x27';
    // payloadIn[01] = '\x00';
    // payloadIn[02] = '\x00';
    //
    // Example long: (sequences 2-10, 50 pixels)
    // "INDEX: 00 01 02 03 04 05 06 07 08 09 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 "
    // "DATA:  27 00 00 07 11 27 13 15 01 00 22 21 09 08 06 19 0e 20 23 25 2c 2d 17 27 29 16 14 1b 1b 21 27 1a 18 17 1e 21 1b 24 21 22 23 13 19 23 2f 2d 25 25 0a 0e 1e 20 1f 1a 0c fd "
    //                  ^--^--(seq 7/11)
    //                        ^-- start waveform data 0x00 to 0xA0, index 05 to 54
    //

    // Example medium: (sequence #11)
    // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 "
    // "DATA:  27 00 00 11 11 0b 13 21 23 1a 1b 22 1e 1a 1d 13 21 1d 26 28 1f 19 1a 18 09 2c 2c 2c 1a 1b fd "

    // Example short: (sequence #1) includes center/fixed mode at [05]. No pixels.
    // "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 "
    // "DATA:  27 00 00 01 11 01 00 00 00 14 00 00 00 35 14 00 00 fd "
    //                        ^-- mode 00 (center) or 01 (fixed)
    //                                     ^--14.00 MHz lower edge
    //                                                    ^-- 14.350 MHz upper edge
    //                                                          ^-- possibly 00=in range 01 = out of range

    // Note, the index used here, -1, matches the ICD in the owner's manual.
    // Owner's manual + 1 = our index.

    // divs: Mode: Waveinfo: Len:   Comment:
    // 2-10  var   var       56     Minimum wave information w/waveform data
    // 11    10    26        31     Minimum wave information w/waveform data
    // 1     1     0         18     Only Wave Information without waveform data

    freqt fStart;
    freqt fEnd;

    unsigned char sequence = bcdHexToUChar(payloadIn[03]);
    //unsigned char sequenceMax = bcdHexToDecimal(payloadIn[04]);


    // unsigned char waveInfo = payloadIn[06]; // really just one byte?
    //qInfo(logRig()) << "Spectrum Data received: " << sequence << "/" << sequenceMax << " mode: " << scopeMode << " waveInfo: " << waveInfo << " length: " << payloadIn.length();

    // Sequnce 2, index 05 is the start of data
    // Sequence 11. index 05, is the last chunk
    // Sequence 11, index 29, is the actual last pixel (it seems)

    // It looks like the data length may be variable, so we need to detect it each time.
    // start at payloadIn.length()-1 (to override the FD). Never mind, index -1 bad.
    // chop off FD.
    if ((sequence == 1) && (sequence < rigCaps.spectSeqMax))
    {

        spectrumMode scopeMode = (spectrumMode)bcdHexToUChar(payloadIn[05]); // 0=center, 1=fixed

        if(scopeMode != oldScopeMode)
        {
            //TODO: support the other two modes (firmware 1.40)
            // Modes:
            // 0x00 Center
            // 0x01 Fixed
            // 0x02 Scroll-C
            // 0x03 Scroll-F
            emit haveSpectrumMode(scopeMode);
            oldScopeMode = scopeMode;
        }


        // wave information
        spectrumLine.clear();
        // For Fixed, and both scroll modes, the following produces correct information:
        fStart = parseFrequency(payloadIn, 9);
        spectrumStartFreq = fStart.MHzDouble;
        fEnd = parseFrequency(payloadIn, 14);
        spectrumEndFreq = fEnd.MHzDouble;
        if(scopeMode == spectModeCenter)
        {
            // "center" mode, start is actuall center, end is bandwidth.
            spectrumStartFreq -= spectrumEndFreq;
            spectrumEndFreq = spectrumStartFreq + 2*(spectrumEndFreq);
            // emit haveSpectrumCenterSpan(span);
        }

        if (payloadIn.length() > 400) // Must be a LAN packet.
        {
            payloadIn.chop(1);
            //spectrumLine.append(payloadIn.mid(17,475)); // write over the FD, last one doesn't, oh well.
            spectrumLine.append(payloadIn.right(payloadIn.length()-17)); // write over the FD, last one doesn't, oh well.
            emit haveSpectrumData(spectrumLine, spectrumStartFreq, spectrumEndFreq);
        }
    } else if ((sequence > 1) && (sequence < rigCaps.spectSeqMax))
    {
        // spectrum from index 05 to index 54, length is 55 per segment. Length is 56 total. Pixel data is 50 pixels.
        // sequence numbers 2 through 10, 50 pixels each. Total after sequence 10 is 450 pixels.
        payloadIn.chop(1);
        spectrumLine.insert(spectrumLine.length(), payloadIn.right(payloadIn.length() - 5)); // write over the FD, last one doesn't, oh well.
        //qInfo(logRig()) << "sequence: " << sequence << "spec index: " << (sequence-2)*55 << " payloadPosition: " << payloadIn.length() - 5 << " payload length: " << payloadIn.length();
    } else if (sequence == rigCaps.spectSeqMax)
    {
        // last spectrum, a little bit different (last 25 pixels). Total at end is 475 pixels (7300).
        payloadIn.chop(1);
        spectrumLine.insert(spectrumLine.length(), payloadIn.right(payloadIn.length() - 5));
        //qInfo(logRig()) << "sequence: " << sequence << " spec index: " << (sequence-2)*55 << " payloadPosition: " << payloadIn.length() - 5 << " payload length: " << payloadIn.length();
        emit haveSpectrumData(spectrumLine, spectrumStartFreq, spectrumEndFreq);
    }
}

void rigCommander::parseSpectrumRefLevel()
{
    // 00: 27
    // 01: 19
    // 02: 00 (fixed)
    // 03: XX
    // 04: x0
    // 05: 00 (+) or 01 (-)

    unsigned char negative = payloadIn[5];
    int value = bcdHexToUInt(payloadIn[3], payloadIn[4]);
    value = value / 10;
    if(negative){
        value *= (-1*negative);
    }
    emit haveSpectrumRefLevel(value);
}

unsigned char rigCommander::bcdHexToUChar(unsigned char in)
{
    unsigned char out = 0;
    out = in & 0x0f;
    out += ((in & 0xf0) >> 4)*10;
    return out;
}

unsigned int rigCommander::bcdHexToUInt(unsigned char hundreds, unsigned char tensunits)
{
    // convert:
    // hex data: 0x41 0x23
    // convert to uint:
    // uchar: 4123
    unsigned char thousands = ((hundreds & 0xf0)>>4);
    unsigned int rtnVal;
    rtnVal = (hundreds & 0x0f)*100;
    rtnVal += ((tensunits & 0xf0)>>4)*10;
    rtnVal += (tensunits & 0x0f);
    rtnVal += thousands * 1000;

    return rtnVal;
}

unsigned char rigCommander::bcdHexToUChar(unsigned char hundreds, unsigned char tensunits)
{
    // convert:
    // hex data: 0x01 0x23
    // convert to uchar:
    // uchar: 123

    //unsigned char thousands = ((hundreds & 0xf0)>>4);
    unsigned char rtnVal;
    rtnVal = (hundreds & 0x0f)*100;
    rtnVal += ((tensunits & 0xf0)>>4)*10;
    rtnVal += (tensunits & 0x0f);
    //rtnVal += thousands * 1000;

    return rtnVal;
}

QByteArray rigCommander::bcdEncodeInt(unsigned int num)
{
    if(num > 9999)
    {
        qInfo(logRig()) << __FUNCTION__ << "Error, number is too big for four-digit conversion: " << num;
        return QByteArray();
    }

    char thousands = num / 1000;
    char hundreds = (num - (1000*thousands)) / 100;
    char tens = (num - (1000*thousands) - (100*hundreds)) / 10;
    char units = (num - (1000*thousands) - (100*hundreds) - (10*tens));

    char b0 = hundreds | (thousands << 4);
    char b1 = units | (tens << 4);

    //qInfo(logRig()) << __FUNCTION__ << " encoding value " << num << " as hex:";
    //printHex(QByteArray(b0), false, true);
    //printHex(QByteArray(b1), false, true);


    QByteArray result;
    result.append(b0).append(b1);
    return result;
}

void rigCommander::parseFrequency()
{
    freqt freq;
    freq.Hz = 0;
    freq.MHzDouble = 0;

    // process payloadIn, which is stripped.
    // float frequencyMhz
    //    payloadIn[04] = ; // XX MHz
    //    payloadIn[03] = ; //   XX0     KHz
    //    payloadIn[02] = ; //     X.X   KHz
    //    payloadIn[01] = ; //      . XX KHz

    // printHex(payloadIn, false, true);
    frequencyMhz = 0.0;
    if(payloadIn.length() == 7)
    {
        // 7300 has these digits too, as zeros.
        // IC-705 or IC-9700 with higher frequency data available.
        frequencyMhz += 100*(payloadIn[05] & 0x0f);
        frequencyMhz += (1000*((payloadIn[05] & 0xf0) >> 4));

        freq.Hz += (payloadIn[05] & 0x0f) * 1E6 *          100;
        freq.Hz += ((payloadIn[05] & 0xf0) >> 4) * 1E6 *  1000;

    }

    freq.Hz += (payloadIn[04] & 0x0f) * 1E6;
    freq.Hz += ((payloadIn[04] & 0xf0) >> 4) * 1E6 *        10;

    frequencyMhz += payloadIn[04] & 0x0f;
    frequencyMhz += 10*((payloadIn[04] & 0xf0) >> 4);

    // KHz land:
    frequencyMhz += ((payloadIn[03] & 0xf0) >>4)/10.0 ;
    frequencyMhz += (payloadIn[03] & 0x0f) / 100.0;

    frequencyMhz += ((payloadIn[02] & 0xf0) >> 4) / 1000.0;
    frequencyMhz += (payloadIn[02] & 0x0f) / 10000.0;

    frequencyMhz += ((payloadIn[01] & 0xf0) >> 4) /  100000.0;
    frequencyMhz += (payloadIn[01] & 0x0f)        / 1000000.0;

    freq.Hz += payloadIn[01] & 0x0f;
    freq.Hz += ((payloadIn[01] & 0xf0) >> 4)*      10;

    freq.Hz  += (payloadIn[02] & 0x0f) *           100;
    freq.Hz  += ((payloadIn[02] & 0xf0) >> 4) *   1000;

    freq.Hz  += (payloadIn[03] & 0x0f) *         10000;
    freq.Hz  += ((payloadIn[03] & 0xf0) >>4) *  100000;

    freq.MHzDouble = frequencyMhz;

    rigState.vfoAFreq = freq;
    emit haveFrequency(freq);
}

freqt rigCommander::parseFrequency(QByteArray data, unsigned char lastPosition)
{
    // process payloadIn, which is stripped.
    // float frequencyMhz
    //    payloadIn[04] = ; // XX MHz
    //    payloadIn[03] = ; //   XX0     KHz
    //    payloadIn[02] = ; //     X.X   KHz
    //    payloadIn[01] = ; //      . XX KHz

    //printHex(data, false, true);

    // TODO: Check length of data array prior to reading +/- position

    // NOTE: This function was written on the IC-7300, which has no need for 100 MHz and 1 GHz.
    //       Therefore, this function has to go to position +1 to retrieve those numbers for the IC-9700.

    // TODO: 64-bit value is incorrect, multiplying by wrong numbers.

    float freq = 0.0;

    freqt freqs;
    freqs.MHzDouble = 0;
    freqs.Hz = 0;

    // MHz:
    freq += 100*(data[lastPosition+1] & 0x0f);
    freq += (1000*((data[lastPosition+1] & 0xf0) >> 4));

    freq += data[lastPosition] & 0x0f;
    freq += 10*((data[lastPosition] & 0xf0) >> 4);

    freqs.Hz += (data[lastPosition] & 0x0f) * 1E6;
    freqs.Hz += ((data[lastPosition] & 0xf0) >> 4) * 1E6 *     10; //   10 MHz

    if(data.length() >= lastPosition+1)
    {
        freqs.Hz += (data[lastPosition+1] & 0x0f) * 1E6 *         100; //  100 MHz
        freqs.Hz += ((data[lastPosition+1] & 0xf0) >> 4) * 1E6 * 1000; // 1000 MHz
    }


    // Hz:
    freq += ((data[lastPosition-1] & 0xf0) >>4)/10.0 ;
    freq += (data[lastPosition-1] & 0x0f) / 100.0;

    freq += ((data[lastPosition-2] & 0xf0) >> 4) / 1000.0;
    freq += (data[lastPosition-2] & 0x0f) / 10000.0;

    freq += ((data[lastPosition-3] & 0xf0) >> 4) / 100000.0;
    freq += (data[lastPosition-3] & 0x0f) / 1000000.0;

    freqs.Hz += (data[lastPosition-1] & 0x0f) *          10E3; // 10 KHz
    freqs.Hz += ((data[lastPosition-1] & 0xf0) >> 4) *  100E3; // 100 KHz

    freqs.Hz += (data[lastPosition-2] & 0x0f) *           100; // 100 Hz
    freqs.Hz += ((data[lastPosition-2] & 0xf0) >> 4) *   1000; // 1 KHz

    freqs.Hz += (data[lastPosition-3] & 0x0f) *             1; // 1 Hz
    freqs.Hz += ((data[lastPosition-3] & 0xf0) >> 4) *     10; // 10 Hz

    freqs.MHzDouble = (double)(freqs.Hz / 1000000.0);
    return freqs;
}


void rigCommander::parseMode()
{
    unsigned char filter;
    if(payloadIn[2] != '\xFD')
    {
        filter = payloadIn[2];
    } else {
        filter = 0;
    }
    rigState.mode = (unsigned char)payloadIn[01];
    rigState.filter = filter;
    emit haveMode((unsigned char)payloadIn[01], filter);
}


void rigCommander::startATU()
{
    QByteArray payload("\x1C\x01\x02");
    prepDataAndSend(payload);
}

void rigCommander::setATU(bool enabled)
{
    QByteArray payload;

    if(enabled)
    {
        payload.setRawData("\x1C\x01\x01", 3);
    } else {
        payload.setRawData("\x1C\x01\x00", 3);
    }
    prepDataAndSend(payload);
}

void rigCommander::getATUStatus()
{
    //qInfo(logRig()) << "Sending out for ATU status in RC.";
    QByteArray payload("\x1C\x01");
    prepDataAndSend(payload);
}

void rigCommander::getAttenuator()
{
    QByteArray payload("\x11");
    prepDataAndSend(payload);
}

void rigCommander::getPreamp()
{
    QByteArray payload("\x16\x02");
    prepDataAndSend(payload);
}

void rigCommander::getAntenna()
{
    // This one might neet some thought
    // as it seems each antenna has to be checked.
    // Maybe 0x12 alone will do it.
    QByteArray payload("\x12");
    prepDataAndSend(payload);
}

void rigCommander::setAttenuator(unsigned char att)
{
    QByteArray payload("\x11");
    payload.append(att);
    prepDataAndSend(payload);
}

void rigCommander::setPreamp(unsigned char pre)
{
    QByteArray payload("\x16\x02");
    payload.append(pre);
    prepDataAndSend(payload);
}

void rigCommander::setAntenna(unsigned char ant, bool rx)
{
    QByteArray payload("\x12");
    payload.append(ant);
    payload.append((unsigned char)rx); // 0x00 = use for TX and RX
    prepDataAndSend(payload);
}

void rigCommander::getRigID()
{
    QByteArray payload;
    payload.setRawData("\x19\x00", 2);
    prepDataAndSend(payload);
}

void rigCommander::changeLatency(const quint16 value)
{
    emit haveChangeLatency(value);
}

void rigCommander::sayAll()
{
    QByteArray payload;
    payload.setRawData("\x13\x00", 2);
    prepDataAndSend(payload);
}

void rigCommander::sayFrequency()
{
    QByteArray payload;
    payload.setRawData("\x13\x01", 2);
    prepDataAndSend(payload);
}

void rigCommander::sayMode()
{
    QByteArray payload;
    payload.setRawData("\x13\x02", 2);
    prepDataAndSend(payload);
}

// Other:

QByteArray rigCommander::stripData(const QByteArray &data, unsigned char cutPosition)
{
    QByteArray rtndata;
    if(data.length() < cutPosition)
    {
        return rtndata;
    }

    rtndata = data.right(cutPosition);
    return rtndata;
}

void rigCommander::getDebug()
{
    // generic debug function for development.
    emit getMoreDebug();
}

void rigCommander::printHex(const QByteArray &pdata)
{
    printHex(pdata, false, true);
}

void rigCommander::printHex(const QByteArray &pdata, bool printVert, bool printHoriz)
{
    qDebug(logRig()) << "---- Begin hex dump -----:";
    QString sdata("DATA:  ");
    QString index("INDEX: ");
    QStringList strings;

    for(int i=0; i < pdata.length(); i++)
    {
        strings << QString("[%1]: %2").arg(i,8,10,QChar('0')).arg((unsigned char)pdata[i], 2, 16, QChar('0'));
        sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0')) );
        index.append(QString("%1 ").arg(i, 2, 10, QChar('0')));
    }

    if(printVert)
    {
        for(int i=0; i < strings.length(); i++)
        {
            //sdata = QString(strings.at(i));
            qDebug(logRig()) << strings.at(i);
        }
    }

    if(printHoriz)
    {
        qDebug(logRig()) << index;
        qDebug(logRig()) << sdata;
    }
    qDebug(logRig()) << "----- End hex dump -----";
}

void rigCommander::dataFromServer(QByteArray data)
{
    //qInfo(logRig()) << "emit dataForComm()";
    emit dataForComm(data);
}








