From e6f20ee45ff9ac6e4195d54eaa56a909a86d3f20 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Wed, 3 May 2023 13:38:06 +0100 Subject: [PATCH] Add memory format specifier to Rig Creator --- memories.cpp | 122 ++++++++++++++++++----- memories.h | 1 + rigcommander.cpp | 250 +++++++++++++++++++++++++++++++++++++++-------- rigcommander.h | 6 ++ rigcreator.cpp | 2 + rigcreator.ui | 28 +++++- rigidentities.h | 2 + rigs/IC-9700.rig | 1 + rigs/ic-7610.rig | 1 + wfviewtypes.h | 10 +- 10 files changed, 355 insertions(+), 68 deletions(-) diff --git a/memories.cpp b/memories.cpp index 3c90046..9aae5c2 100644 --- a/memories.cpp +++ b/memories.cpp @@ -40,30 +40,104 @@ memories::memories(rigCapabilities rigCaps, QWidget *parent) : "Filter" << "Data" << "Duplex" << "Tone Mode" << "DSQL" << "Tone" << "TSQL" << "DTCS" <<"DTCS Pol" << "DV Sql" << "Offset" << "UR" << "R1" << "R2"; ui->table->setHorizontalHeaderLabels(headers); + ui->groupLabel->hide(); + ui->group->hide(); + ui->vfoMode->hide(); + ui->memoryMode->hide(); - if (rigCaps.memGroups < 2) { - ui->table->hideColumn(columnDuplex); - ui->table->hideColumn(columnDSQL); - ui->table->hideColumn(columnDTCS); - ui->table->hideColumn(columnOffset); - ui->table->hideColumn(columnUR); - ui->table->hideColumn(columnR1); - ui->table->hideColumn(columnR2); - } - if (rigCaps.memGroups < 2) { - ui->groupLabel->hide(); - ui->group->hide(); - ui->vfoMode->hide(); - ui->memoryMode->hide(); - } else + // Hide all columns except recall + for (int i=1;itable->columnCount();i++) { - ui->groupLabel->show(); - ui->group->show(); - ui->vfoMode->show(); - ui->memoryMode->show(); + ui->table->hideColumn(i); } + foreach (auto parse, rigCaps.memParser) { + switch (parse.spec) + { + case 'a': + ui->groupLabel->show(); + ui->group->show(); + ui->vfoMode->show(); + ui->memoryMode->show(); + break; + case 'b': + ui->table->showColumn(columnNum); + break; + case 'c': + ui->table->showColumn(columnMemory); + break; + case 'd': + ui->table->showColumn(columnFrequency); + break; + case 'e': + ui->table->showColumn(columnMode); + break; + case 'f': + ui->table->showColumn(columnFilter); + break; + case 'g': + ui->table->showColumn(columnData); + break; + case 'h': + ui->table->showColumn(columnDuplex); + ui->table->showColumn(columnToneMode); + break; + case 'i': + ui->table->showColumn(columnData); + ui->table->showColumn(columnToneMode); + break; + case 'j': + ui->table->showColumn(columnDSQL); + break; + case 'k': + ui->table->showColumn(columnTone); + break; + case 'l': + ui->table->showColumn(columnTSQL); + break; + case 'm': + ui->table->showColumn(columnDTCSPolarity); + break; + case 'n': + ui->table->showColumn(columnDTCS); + break; + case 'o': + ui->table->showColumn(columnDVSquelch); + break; + case 'p': + ui->table->showColumn(columnOffset); + break; + case 'q': + ui->table->showColumn(columnUR); + break; + case 'r': + ui->table->showColumn(columnR1); + break; + case 's': + ui->table->showColumn(columnR2); + break; + case 't': + ui->table->showColumn(columnName); + break; + case 'u': + break; + case 'v': + break; + case 'w': + break; + case 'x': + break; + case 'y': + break; + case 'z': + break; + default: + break; + } + } + + ui->group->blockSignals(true); for (int i=1;i<=rigCaps.memGroups;i++) { @@ -111,9 +185,9 @@ memories::memories(rigCapabilities rigCaps, QWidget *parent) : ui->table->setItemDelegateForColumn(columnNum, numEditor); if (rigCaps.memGroups>1) - nameEditor = new tableEditor(QRegularExpression("[0-9A-Za-z\/\ ]{0,16}$"),ui->table); + nameEditor = new tableEditor(QRegularExpression("[0-9A-Za-z\\/\\ ]{0,16}$"),ui->table); else - nameEditor = new tableEditor(QRegularExpression("[0-9A-Za-z\/\ ]{0,10}$"),ui->table); + nameEditor = new tableEditor(QRegularExpression("[0-9A-Za-z\\/\\ ]{0,10}$"),ui->table); ui->table->setItemDelegateForColumn(columnName, nameEditor); @@ -163,13 +237,13 @@ memories::memories(rigCapabilities rigCaps, QWidget *parent) : dvsqlEditor = new tableEditor(QRegularExpression("[0-9]{0,2}"),ui->table); ui->table->setItemDelegateForColumn(columnDVSquelch, dvsqlEditor); - urEditor = new tableEditor(QRegularExpression("[0-9A-Z\/\ ]{0,8}$"),ui->table); + urEditor = new tableEditor(QRegularExpression("[0-9A-Z\\/\\ ]{0,8}$"),ui->table); ui->table->setItemDelegateForColumn(columnUR, urEditor); - r1Editor = new tableEditor(QRegularExpression("[0-9A-Z\/\ ]{0,8}$"),ui->table); + r1Editor = new tableEditor(QRegularExpression("[0-9A-Z\\/\\ ]{0,8}$"),ui->table); ui->table->setItemDelegateForColumn(columnR1, r1Editor); - r2Editor = new tableEditor(QRegularExpression("[0-9A-Z\/\ ]{0,8}$"),ui->table); + r2Editor = new tableEditor(QRegularExpression("[0-9A-Z\\/\\ ]{0,8}$"),ui->table); ui->table->setItemDelegateForColumn(columnR2, r2Editor); connect(ui->table,SIGNAL(rowAdded(int)),this,SLOT(rowAdded(int))); diff --git a/memories.h b/memories.h index 9ed972b..b339bde 100644 --- a/memories.h +++ b/memories.h @@ -97,6 +97,7 @@ private: Ui::memories *ui; bool extended = false; + enum columns { columnRecall=0, columnNum, diff --git a/rigcommander.cpp b/rigcommander.cpp index ed57866..2d852b2 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -1388,47 +1388,89 @@ void rigCommander::setMemory(memoryType mem) QByteArray payload; char nul = 0x0; - if (getCommand(funcMemoryContents,payload)) + if (getCommand(funcMemoryContents,payload,mem.channel)) { - // Parse the memory entry into a memoryType. - // Format is different for radios with memory groups - - qInfo() << "Mem" << mem.channel <<"Offset" < 1) { - payload.append(mem.group); - } - - payload.append(bcdEncodeInt(mem.channel)); - payload.append(mem.memory); - payload.append(makeFreqPayload(mem.frequency)); - payload.append(mem.mode); - payload.append(mem.filter); - - if (rigCaps.memGroups > 1) { - payload.append(mem.datamode); - payload.append((mem.duplex << 4) | mem.tonemode); - payload.append(mem.dsql << 4); - } - else { - payload.append((mem.datamode << 4 & 0xf0) | (mem.tonemode & 0x0f)); - } - - payload.append(nul); - payload.append(bcdEncodeInt(mem.tone)); - payload.append(nul); - payload.append(bcdEncodeInt(mem.tsql)); - - if (rigCaps.memGroups > 1) { - payload.append(mem.dtcsp); - payload.append(bcdEncodeInt(mem.dtcs)); - payload.append(mem.dvsql); - payload.append(makeFreqPayload(mem.duplexOffset).mid(1,3)); - payload.append(QByteArray(mem.UR).leftJustified(8,' ')); - payload.append(QByteArray(mem.R1).leftJustified(8,' ')); - payload.append(QByteArray(mem.R2).leftJustified(8,' ')); - payload.append(QByteArray(mem.name).leftJustified(16,' ')); - } else { - payload.append(QByteArray(mem.name).leftJustified(10,' ')); + // Format is different for all radios! + foreach (auto parse, rigCaps.memParser) { + switch (parse.spec) + { + case 'a': + payload.append(mem.group); + break; + case 'b': + payload.append(bcdEncodeInt(mem.channel)); + break; + case 'c': + payload.append(mem.memory); + break; + case 'd': + payload.append(makeFreqPayload(mem.frequency)); + break; + case 'e': + payload.append(mem.mode); + break; + case 'f': + payload.append(mem.filter); + break; + case 'g': // single datamode + payload.append(mem.datamode); + break; + case 'h': // combined duplex and tonemode + payload.append((mem.duplex << 4) | mem.tonemode); + break; + case 'i': // combined datamode and tonemode + payload.append((mem.datamode << 4 & 0xf0) | (mem.tonemode & 0x0f)); + break; + case 'j': + payload.append(mem.dsql << 4); + break; + case 'k': + payload.append(nul); + payload.append(bcdEncodeInt(mem.tone)); + break; + case 'l': + payload.append(nul); + payload.append(bcdEncodeInt(mem.tsql)); + break; + case 'm': + payload.append(mem.dtcsp); + break; + case 'n': + payload.append(bcdEncodeInt(mem.dtcs)); + break; + case 'o': + payload.append(mem.dvsql); + break; + case 'p': + payload.append(makeFreqPayload(mem.duplexOffset).mid(1,3)); + break; + case 'q': + payload.append(QByteArray(mem.UR).leftJustified(parse.len,' ')); + break; + case 'r': + payload.append(QByteArray(mem.R1).leftJustified(parse.len,' ')); + break; + case 's': + payload.append(QByteArray(mem.R2).leftJustified(parse.len,' ')); + break; + case 't': + payload.append(QByteArray(mem.name).leftJustified(parse.len,' ')); + break; + case 'u': + break; + case 'v': + break; + case 'w': + break; + case 'x': + break; + case 'y': + break; + case 'z': + break; + default: + break; + } } prepDataAndSend(payload); } @@ -1830,9 +1872,99 @@ void rigCommander::parseCommand() break; case funcMemoryContents: { + // Parse the memory entry into a memoryType: memoryType mem; mem.frequency.Hz=0; + mem.duplexOffset.Hz=0; + int offset = 1; + /* + // 16 is fixed 0x0; + // 19 is fixed 0x0; + */ + foreach (auto parse, rigCaps.memParser) { + QByteArray data = payloadIn.mid(offset+parse.pos,parse.len); + switch (parse.spec) + { + case 'a': + mem.group = bcdHexToUChar(data[0]); + break; + case 'b': + mem.channel = bcdHexToUInt(data[0],data[1]); + break; + case 'c': + mem.memory = data[0]; + break; + case 'd': + mem.frequency = parseRawFrequency(data); + break; + case 'e': + mem.mode=(mode_kind)bcdHexToUChar(data[0]); + break; + case 'f': + mem.filter=data[0]; + break; + case 'g': // single datamode + mem.datamode=data[0]; + break; + case 'h': // combined duplex and tonemode + mem.duplex=duplexMode(data[0] >> 4 & 0x0f); + mem.tonemode=data[0] & 0x0f; + case 'i': // combined datamode and tonemode + mem.datamode=(data[0] >> 4 & 0x0f); + mem.tonemode=data[0] & 0x0f; + break; + case 'j': + mem.dsql = (data[0] >> 4 & 0x0f); + break; + case 'k': + mem.tone = bcdHexToUInt(data[1],data[2]); // First byte is not used + break; + case 'l': + mem.tsql = bcdHexToUInt(data[1],data[2]); // First byte is not used + break; + case 'm': + mem.dtcsp = data[0]; + break; + case 'n': + mem.dtcs = bcdHexToUInt(data[0],data[1]); + break; + case 'o': + mem.dvsql = bcdHexToUChar(data[0]); + break; + case 'p': + mem.duplexOffset = parseRawFrequency(data); + break; + case 'q': + memcpy(mem.UR,data,sizeof(mem.UR)); + break; + case 'r': + memcpy(mem.R1,data,sizeof(mem.R1)); + break; + case 's': + memcpy(mem.R2,data,sizeof(mem.R2)); + break; + case 't': + memcpy(mem.name,data,sizeof(mem.name)); + break; + case 'u': + break; + case 'v': + break; + case 'w': + break; + case 'x': + break; + case 'y': + break; + case 'z': + break; + default: + break; + } + } + + /* if (rigCaps.memGroups > 1) { mem.group = bcdHexToUChar(payloadIn[2]); @@ -1869,6 +2001,7 @@ void rigCommander::parseCommand() mem.tsql = bcdHexToUInt(payloadIn[17],payloadIn[18]); memcpy(mem.name,payloadIn.mid(19,10),sizeof(mem.name)); } + */ emit haveMemory(mem); } case funcMemoryClear: @@ -4262,6 +4395,7 @@ void rigCommander::determineRigCaps() rigCaps.antennas.clear(); rigCaps.filters.clear(); rigCaps.steps.clear(); + rigCaps.memParser.clear(); // modelID should already be set! while (!rigList.contains(rigCaps.modelID)) @@ -4304,6 +4438,7 @@ void rigCommander::determineRigCaps() rigCaps.memGroups = settings->value("MemGroups",0).toUInt(); rigCaps.memories = settings->value("Memories",0).toUInt(); + rigCaps.memFormat = settings->value("MemFormat","").toString(); // Temporary QList to hold the function string lookup // I would still like to find a better way of doing this! QHash funcsLookup; @@ -4464,6 +4599,22 @@ void rigCommander::determineRigCaps() settings->endGroup(); delete settings; + + // Setup memory format. + static QRegularExpression memFmtEx("%(?[-+#0])?(?\\d+|\\*)?(?:\\.(?\\d+|\\*))?(?[abcdefghijklmnopqrstuvwxyz])"); + QRegularExpressionMatchIterator i = memFmtEx.globalMatch(rigCaps.memFormat); + + while (i.hasNext()) { + QRegularExpressionMatch qmatch = i.next(); + if (qmatch.hasCaptured("spec") && qmatch.hasCaptured("pos") && qmatch.hasCaptured("width")) { + rigCaps.memParser.append(memParserFormat(qmatch.captured("spec").at(0).toLatin1(),qmatch.captured("pos").toInt(),qmatch.captured("width").toInt())); + qInfo() << QString("Captured: %0 pos: %1 len: %2").arg(rigCaps.memParser.at(rigCaps.memParser.size()-1).spec). + arg(rigCaps.memParser.at(rigCaps.memParser.size()-1).pos). + arg(rigCaps.memParser.at(rigCaps.memParser.size()-1).len); + } + } + + /* switch(model){ case model7300: @@ -5739,6 +5890,25 @@ freqt rigCommander::parseFrequency(QByteArray data, unsigned char lastPosition) } +freqt rigCommander::parseRawFrequency(QByteArray data) +{ + // Allow raw frequency data to be parsed with no bells and whistles (for memory use) + freqt freqs; + freqs.MHzDouble = 0; + freqs.VFO=selVFO_t::activeVFO; + freqs.Hz = 0; + + for (int i=0;i> 4) * (pow10[i+1]); + } + + freqs.MHzDouble = (double)(freqs.Hz / 1000000.0); + return freqs; +} + + void rigCommander::parseMode() { unsigned char filter=0; diff --git a/rigcommander.h b/rigcommander.h index 7501989..1fedbe2 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "wfviewtypes.h" @@ -415,6 +416,7 @@ private: QByteArray bcdEncodeInt(unsigned int); void parseFrequency(); freqt parseFrequency(QByteArray data, unsigned char lastPosition); // supply index where Mhz is found + freqt parseRawFrequency(QByteArray data); // supply index where Mhz is found freqt parseFrequencyRptOffset(QByteArray data); QByteArray makeFreqPayloadRptOffset(freqt freq); QByteArray makeFreqPayload(double frequency); @@ -516,6 +518,10 @@ private: quint8 guid[GUIDLEN] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; QHash rigList; + quint64 pow10[12] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000 + }; + #ifdef DEBUG_PARSE quint64 averageParseTime=0; int numParseSamples = 0; diff --git a/rigcreator.cpp b/rigcreator.cpp index db5df9c..df3c74a 100644 --- a/rigcreator.cpp +++ b/rigcreator.cpp @@ -109,6 +109,7 @@ void rigCreator::loadRigFile(QString file) ui->memGroups->setText(settings->value("MemGroups","0").toString()); ui->memories->setText(settings->value("Memories","0").toString()); + ui->memoryFormat->setText(settings->value("MemFormat","").toString()); ui->commands->setRowCount(0); int numCommands = settings->beginReadArray("Commands"); @@ -343,6 +344,7 @@ void rigCreator::saveRigFile(QString file) settings->setValue("MemGroups",ui->memGroups->text().toInt()); settings->setValue("Memories",ui->memories->text().toInt()); + settings->setValue("MemFormat",ui->memoryFormat->text()); //settings->remove("Commands"); diff --git a/rigcreator.ui b/rigcreator.ui index df64615..d74f5c7 100644 --- a/rigcreator.ui +++ b/rigcreator.ui @@ -7,7 +7,7 @@ 0 0 978 - 551 + 593 @@ -1043,8 +1043,8 @@ 10 31 - 101 - 51 + 108 + 52 @@ -1079,6 +1079,28 @@ + + + + 10 + 550 + 951 + 31 + + + + + + + Memory Format + + + + + + + + diff --git a/rigidentities.h b/rigidentities.h index 423a422..3dcd402 100644 --- a/rigidentities.h +++ b/rigidentities.h @@ -219,6 +219,8 @@ struct rigCapabilities { quint32 baudRate; quint16 memGroups; quint16 memories; + QString memFormat; + QVector memParser; }; diff --git a/rigs/IC-9700.rig b/rigs/IC-9700.rig index bf09588..39cb62f 100644 --- a/rigs/IC-9700.rig +++ b/rigs/IC-9700.rig @@ -17,6 +17,7 @@ HasTransmit=true HasFDComms=true MemGroups=3 Memories=107 +MemFormat=%1.1a %2.2b %4.1c %5.5d %10.1e %11.1f %12.1g %13.1h %14.1j %15.3k %18.3l %21.1m %22.2n %24.1o %24.4p %28.8q %36.8r %44.8s %52.16t Commands\1\Type=ACC1 Mod Level Commands\1\String=\\x1a\\x05\\x01\\x12 Commands\1\Min=0 diff --git a/rigs/ic-7610.rig b/rigs/ic-7610.rig index 861308d..19fb3a7 100644 --- a/rigs/ic-7610.rig +++ b/rigs/ic-7610.rig @@ -17,6 +17,7 @@ HasTransmit=true HasFDComms=false MemGroups=1 Memories=101 +MemFormat=%1.2b %3.1c %4.5d %9.1e %10.1f %11.1i %12.3k %15.3l %18.10t Commands\1\Type=ACC1 Mod Level Commands\1\String=\\x1A\\x05\\x00\\x88 Commands\1\Min=0 diff --git a/wfviewtypes.h b/wfviewtypes.h index fcd6046..a86405f 100644 --- a/wfviewtypes.h +++ b/wfviewtypes.h @@ -317,7 +317,7 @@ struct memoryType{ quint16 tsql=670; quint8 dsql=0; int dtcs=0; - bool dtcsp=false; + quint8 dtcsp=0; quint8 dvsql=0; freqt duplexOffset; char UR[9]; @@ -326,6 +326,14 @@ struct memoryType{ char name[17]; // 1 more than the absolute max }; + +struct memParserFormat{ + memParserFormat(char spec, int pos, int len) : spec(spec), pos(pos), len(len) {}; + char spec; + int pos; + int len; +}; + enum audioType {qtAudio,portAudio,rtAudio}; enum codecType { LPCM, PCMU, OPUS };