implement a "setup" mode allowing the channel LCN information to be much more easily set;

pull/12/head
Bryan Biedenkapp 4 years ago
parent e3e8d4c6fe
commit 8454ea9ea6

@ -190,6 +190,7 @@
<ClInclude Include="host\calibrate\Console.h" /> <ClInclude Include="host\calibrate\Console.h" />
<ClInclude Include="host\calibrate\HostCal.h" /> <ClInclude Include="host\calibrate\HostCal.h" />
<ClInclude Include="host\Host.h" /> <ClInclude Include="host\Host.h" />
<ClInclude Include="host\setup\HostSetup.h" />
<ClInclude Include="lookups\IdenTableLookup.h" /> <ClInclude Include="lookups\IdenTableLookup.h" />
<ClInclude Include="lookups\LookupTable.h" /> <ClInclude Include="lookups\LookupTable.h" />
<ClInclude Include="lookups\RadioIdLookup.h" /> <ClInclude Include="lookups\RadioIdLookup.h" />
@ -267,6 +268,7 @@
<ClCompile Include="host\calibrate\Console.cpp" /> <ClCompile Include="host\calibrate\Console.cpp" />
<ClCompile Include="host\calibrate\HostCal.cpp" /> <ClCompile Include="host\calibrate\HostCal.cpp" />
<ClCompile Include="host\Host.cpp" /> <ClCompile Include="host\Host.cpp" />
<ClCompile Include="host\setup\HostSetup.cpp" />
<ClCompile Include="lookups\IdenTableLookup.cpp" /> <ClCompile Include="lookups\IdenTableLookup.cpp" />
<ClCompile Include="lookups\RadioIdLookup.cpp" /> <ClCompile Include="lookups\RadioIdLookup.cpp" />
<ClCompile Include="lookups\RSSIInterpolator.cpp" /> <ClCompile Include="lookups\RSSIInterpolator.cpp" />

@ -117,6 +117,12 @@
<Filter Include="Source Files\modem\port"> <Filter Include="Source Files\modem\port">
<UniqueIdentifier>{bd26735c-6610-4bfe-b1c4-27da1cb18e29}</UniqueIdentifier> <UniqueIdentifier>{bd26735c-6610-4bfe-b1c4-27da1cb18e29}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Source Files\host\setup">
<UniqueIdentifier>{fdb4c489-755e-4655-a0e3-b1174c8e7acc}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\host\setup">
<UniqueIdentifier>{145b73dd-7759-4018-9df2-af162f652699}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Defines.h"> <ClInclude Include="Defines.h">
@ -353,6 +359,9 @@
<ClInclude Include="dmr\lc\PrivacyLC.h"> <ClInclude Include="dmr\lc\PrivacyLC.h">
<Filter>Header Files\dmr\lc</Filter> <Filter>Header Files\dmr\lc</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="host\setup\HostSetup.h">
<Filter>Header Files\host\setup</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Log.cpp"> <ClCompile Include="Log.cpp">
@ -568,6 +577,9 @@
<ClCompile Include="dmr\lc\PrivacyLC.cpp"> <ClCompile Include="dmr\lc\PrivacyLC.cpp">
<Filter>Source Files\dmr\lc</Filter> <Filter>Source Files\dmr\lc</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="host\setup\HostSetup.cpp">
<Filter>Source Files\host\setup</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Makefile" /> <None Include="Makefile" />

@ -12,7 +12,7 @@
// //
/* /*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Bryan Biedenkapp N2PLL * Copyright (C) 2020,2021 by Bryan Biedenkapp N2PLL
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -32,6 +32,7 @@
#include "HostMain.h" #include "HostMain.h"
#include "host/Host.h" #include "host/Host.h"
#include "host/calibrate/HostCal.h" #include "host/calibrate/HostCal.h"
#include "host/setup/HostSetup.h"
#include "Log.h" #include "Log.h"
using namespace network; using namespace network;
@ -61,6 +62,7 @@ using namespace lookups;
int g_signal = 0; int g_signal = 0;
bool g_calibrate = false; bool g_calibrate = false;
bool g_setup = false;
std::string g_progExe = std::string(__EXE_NAME__); std::string g_progExe = std::string(__EXE_NAME__);
std::string g_iniFile = std::string(DEFAULT_CONF_FILE); std::string g_iniFile = std::string(DEFAULT_CONF_FILE);
std::string g_lockFile = std::string(DEFAULT_LOCK_FILE); std::string g_lockFile = std::string(DEFAULT_LOCK_FILE);
@ -109,9 +111,10 @@ void usage(const char* message, const char* arg)
::fprintf(stderr, "\n\n"); ::fprintf(stderr, "\n\n");
} }
::fprintf(stdout, "usage: %s [-v] [-f] [--cal] [-c <configuration file>]\n\n" ::fprintf(stdout, "usage: %s [-v] [-f] [--cal] [--setup] [-c <configuration file>]\n\n"
" -f foreground mode\n" " -f foreground mode\n"
" --cal calibration mode\n" " --cal calibration mode\n"
" --setup setup mode\n"
"\n" "\n"
" -v show version information\n" " -v show version information\n"
" -h show this screen\n" " -h show this screen\n"
@ -144,6 +147,9 @@ int checkArgs(int argc, char* argv[])
else if (IS("--cal")) { else if (IS("--cal")) {
g_calibrate = true; g_calibrate = true;
} }
else if (IS("--setup")) {
g_setup = true;
}
else if (IS("-c")) { else if (IS("-c")) {
if (argc-- <= 0) if (argc-- <= 0)
usage("error: %s", "must specify the configuration file to use"); usage("error: %s", "must specify the configuration file to use");
@ -209,10 +215,17 @@ int main(int argc, char** argv)
do { do {
g_signal = 0; g_signal = 0;
if (g_calibrate) { if (g_calibrate || g_setup) {
HostCal* cal = new HostCal(g_iniFile); if (g_setup) {
ret = cal->run(); HostSetup* setup = new HostSetup(g_iniFile);
delete cal; ret = setup->run();
delete setup;
}
else {
HostCal* cal = new HostCal(g_iniFile);
ret = cal->run();
delete cal;
}
} }
else { else {
Host* host = new Host(g_iniFile); Host* host = new Host(g_iniFile);

@ -46,6 +46,7 @@
#define LOG_P25 "P25" #define LOG_P25 "P25"
#define LOG_DMR "DMR" #define LOG_DMR "DMR"
#define LOG_CAL "CAL" #define LOG_CAL "CAL"
#define LOG_SETUP "SETUP"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Macros // Macros

@ -68,6 +68,7 @@ OBJECTS = \
yaml/Yaml.o \ yaml/Yaml.o \
host/calibrate/Console.o \ host/calibrate/Console.o \
host/calibrate/HostCal.o \ host/calibrate/HostCal.o \
host/setup/HostSetup.o \
host/Host.o \ host/Host.o \
Log.o \ Log.o \
Mutex.o \ Mutex.o \
@ -86,4 +87,4 @@ dvmhost: $(OBJECTS)
$(CXX) $(CFLAGS) -c -o $@ $< $(CXX) $(CFLAGS) -c -o $@ $<
clean: clean:
$(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o modem/port/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o $(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o modem/port/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o host/setup/*.o

@ -68,6 +68,7 @@ OBJECTS = \
yaml/Yaml.o \ yaml/Yaml.o \
host/calibrate/Console.o \ host/calibrate/Console.o \
host/calibrate/HostCal.o \ host/calibrate/HostCal.o \
host/setup/HostSetup.o \
host/Host.o \ host/Host.o \
Log.o \ Log.o \
Mutex.o \ Mutex.o \
@ -86,4 +87,4 @@ dvmhost: $(OBJECTS)
$(CXX) $(CFLAGS) -c -o $@ $< $(CXX) $(CFLAGS) -c -o $@ $<
clean: clean:
$(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o modem/port/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o $(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o modem/port/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o host/setup/*.o

@ -68,6 +68,7 @@ OBJECTS = \
yaml/Yaml.o \ yaml/Yaml.o \
host/calibrate/Console.o \ host/calibrate/Console.o \
host/calibrate/HostCal.o \ host/calibrate/HostCal.o \
host/setup/HostSetup.o \
host/Host.o \ host/Host.o \
Log.o \ Log.o \
Mutex.o \ Mutex.o \
@ -86,4 +87,4 @@ dvmhost: $(OBJECTS)
$(CXX) $(CFLAGS) -c -o $@ $< $(CXX) $(CFLAGS) -c -o $@ $<
clean: clean:
$(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o modem/port/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o $(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o modem/port/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o host/setup/*.o

@ -43,7 +43,6 @@
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
/// <summary> /// <summary>
/// Initializes a new instance of the Console class. /// Initializes a new instance of the Console class.
@ -174,3 +173,72 @@ void Console::close()
::fprintf(stderr, "tcsetattr: returned %d\r\n", n); ::fprintf(stderr, "tcsetattr: returned %d\r\n", n);
} }
#endif #endif
/// <summary>
/// Retrieves an array of characters input on the keyboard.
/// </summary>
/// <param name="line"></param>
/// <param name="max"></param>
/// <param name="mask"></param>
/// <returns></returns>
int Console::getLine(char line[], int max, char mask)
{
int nch = 0;
int c;
bool skipNext = false;
max = max - 1; /* leave room for '\0' */
while ((c = getChar()) != '\n') {
if (c != -1) {
if (c == 10 || c == 13)
break;
// skip "double-byte" control characters
if (c == 224) {
skipNext = true;
continue;
}
if (skipNext) {
skipNext = false;
continue;
}
// has characters and backspace character?
if (nch > 0 && (c == 127 || c == 8)) {
// handle backspace
::fputc(0x8, stdout);
::fputc(' ', stdout);
::fputc(0x8, stdout);
::fflush(stdout);
line[--nch] = 0;
}
else {
// skip control characters
if (iscntrl(c))
continue;
if (nch < max) {
// valid mask character?
if (' ' - 1 < mask && mask < 127)
::fputc(mask, stdout);
else
::fputc(c, stdout);
::fflush(stdout);
line[nch++] = c;
}
}
}
}
if (c == EOF && nch == 0)
return EOF;
::fputc('\n', stdout);
::fflush(stdout);
line[nch] = '\0';
return nch;
}

@ -55,6 +55,9 @@ public:
/// <summary>Retrieves a character input on the keyboard.</summary> /// <summary>Retrieves a character input on the keyboard.</summary>
int getChar(); int getChar();
/// <summary>Retrieves an array of characters input on the keyboard.</summary>
int getLine(char line[], int max, char mask);
/// <summary>Closes the terminal console.</summary> /// <summary>Closes the terminal console.</summary>
void close(); void close();

@ -198,6 +198,7 @@ int HostCal::run()
::LogInfo(">> Modem Calibration"); ::LogInfo(">> Modem Calibration");
yaml::Node systemConf = m_conf["system"]; yaml::Node systemConf = m_conf["system"];
m_duplex = systemConf["duplex"].as<bool>(true);
// try to load bandplan identity table // try to load bandplan identity table
std::string idenLookupFile = systemConf["iden_table"]["file"].as<std::string>(); std::string idenLookupFile = systemConf["iden_table"]["file"].as<std::string>();
@ -442,6 +443,7 @@ int HostCal::run()
setTXDCOffset(1); setTXDCOffset(1);
break; break;
/** Engineering Commands */
case '-': case '-':
setDMRSymLevel3Adj(-1); setDMRSymLevel3Adj(-1);
break; break;

@ -0,0 +1,424 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the MMDVMCal project. (https://github.com/g4klx/MMDVMCal)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2021 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "host/setup/HostSetup.h"
#include "HostMain.h"
#include "Log.h"
#include "Utils.h"
using namespace lookups;
#include <cstdio>
#include <algorithm>
#if !defined(_WIN32) && !defined(_WIN64)
#include <unistd.h>
#endif
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the HostSetup class.
/// </summary>
/// <param name="confFile">Full-path to the configuration file.</param>
HostSetup::HostSetup(const std::string& confFile) :
m_confFile(confFile),
m_conf(),
m_console(),
m_duplex(true),
m_identity("ABCD123"),
m_callsign("ABCD123"),
m_rxFrequency(0U),
m_txFrequency(0U),
m_channelId(0U),
m_channelNo(0U),
m_idenTable(NULL)
{
/* stub */
}
/// <summary>
/// Finalizes a instance of the HostSetup class.
/// </summary>
HostSetup::~HostSetup()
{
/* stub */
}
/// <summary>
/// Executes the processing loop.
/// </summary>
/// <returns>Zero if successful, otherwise error occurred.</returns>
int HostSetup::run()
{
bool ret = yaml::Parse(m_conf, m_confFile.c_str());
if (!ret) {
::fatal("cannot read the configuration file, %s\n", m_confFile.c_str());
}
// initialize system logging
ret = ::LogInitialise("", "", 0U, 1U);
if (!ret) {
::fprintf(stderr, "unable to open the log file\n");
return 1;
}
getHostVersion();
::LogInfo(">> Modem Setup");
yaml::Node systemConf = m_conf["system"];
m_duplex = systemConf["duplex"].as<bool>(true);
// try to load bandplan identity table
std::string idenLookupFile = systemConf["iden_table"]["file"].as<std::string>();
uint32_t idenReloadTime = systemConf["iden_table"]["time"].as<uint32_t>(0U);
if (idenLookupFile.length() <= 0U) {
::LogError(LOG_HOST, "No bandplan identity table? This must be defined!");
return 1;
}
LogInfo("Iden Table Lookups");
LogInfo(" File: %s", idenLookupFile.length() > 0U ? idenLookupFile.c_str() : "None");
if (idenReloadTime > 0U)
LogInfo(" Reload: %u mins", idenReloadTime);
m_idenTable = new IdenTableLookup(idenLookupFile, idenReloadTime);
m_idenTable->read();
LogInfo("General Parameters");
m_identity = systemConf["identity"].as<std::string>();
::LogInfo(" Identity: %s", m_identity.c_str());
yaml::Node cwId = systemConf["cwId"];
uint32_t time = systemConf["cwId"].as<uint32_t>(10U);
m_callsign = systemConf["cwId"].as<std::string>();
LogInfo("CW Id Parameters");
LogInfo(" Time: %u mins", time);
LogInfo(" Callsign: %s", m_callsign.c_str());
m_callsign = cwId["callsign"].as<std::string>();
yaml::Node rfssConfig = systemConf["config"];
m_channelId = (uint8_t)rfssConfig["channelId"].as<uint32_t>(0U);
if (m_channelId > 15U) { // clamp to 15
m_channelId = 15U;
}
m_channelNo = (uint32_t)::strtoul(rfssConfig["channelNo"].as<std::string>("1").c_str(), NULL, 16);
if (m_channelNo == 0U) { // clamp to 1
m_channelNo = 1U;
}
if (m_channelNo > 4095U) { // clamp to 4095
m_channelNo = 4095U;
}
if (!calculateRxTxFreq()) {
return false;
}
IdenTable entry = m_idenTable->find(m_channelId);
if (entry.baseFrequency() == 0U) {
::LogError(LOG_HOST, "Channel Id %u has an invalid base frequency.", m_channelId);
return false;
}
LogInfo("System Config Parameters");
LogInfo(" RX Frequency: %uHz", m_rxFrequency);
LogInfo(" TX Frequency: %uHz", m_txFrequency);
LogInfo(" Base Frequency: %uHz", entry.baseFrequency());
LogInfo(" TX Offset: %fMHz", entry.txOffsetMhz());
// open terminal console
ret = m_console.open();
if (!ret) {
return 1;
}
displayHelp();
printStatus();
bool end = false;
while (!end) {
int c = m_console.getChar();
switch (c) {
/** Setup Commands */
case 'I':
{
char value[9] = { '\0' };
::fprintf(stdout, "> Identity ? ");
::fflush(stdout);
m_console.getLine(value, 9, 0);
m_identity = std::string(value);
writeConfig();
}
break;
case 'i':
{
char value[3] = { '\0' };
::fprintf(stdout, "> Channel ID ? ");
::fflush(stdout);
m_console.getLine(value, 3, 0);
uint8_t prevChannelId = m_channelId;
// bryanb: appease the compiler...
uint32_t channelId = m_channelId;
sscanf(value, "%u", &channelId);
m_channelId = (uint8_t)channelId;
IdenTable entry = m_idenTable->find(m_channelId);
if (entry.baseFrequency() == 0U) {
::LogError(LOG_SETUP, "Channel Id %u has an invalid base frequency.", m_channelId);
m_channelId = prevChannelId;
}
writeConfig();
}
break;
case 'c':
{
char value[5] = { '\0' };
::fprintf(stdout, "> Channel No ? ");
::fflush(stdout);
m_console.getLine(value, 5, 0);
uint8_t prevChannelNo = m_channelNo;
sscanf(value, "%u", &m_channelNo);
if (m_channelNo < 0 || m_channelNo > 4096) {
::LogError(LOG_SETUP, "Channel No %u is invalid.", m_channelNo);
m_channelNo = prevChannelNo;
}
writeConfig();
}
break;
case 'f':
{
char value[10] = { '\0' };
::fprintf(stdout, "> Tx Frequency (Hz) ? ");
::fflush(stdout);
m_console.getLine(value, 10, 0);
uint32_t txFrequency = m_txFrequency;
sscanf(value, "%u", &txFrequency);
IdenTable entry = m_idenTable->find(m_channelId);
if (txFrequency < entry.baseFrequency()) {
::LogError(LOG_SETUP, "Tx Frequency %uHz is out of band range for base frequency %uHz. Tx Frequency must be greater then base frequency!", txFrequency, entry.baseFrequency());
break;
}
if (txFrequency > entry.baseFrequency() + 25500000) {
::LogError(LOG_SETUP, "Tx Frequency %uHz is out of band range for base frequency %uHz. Tx Frequency must be no more then 25.5 Mhz higher then base frequency!", txFrequency, entry.baseFrequency());
break;
}
uint32_t prevTxFrequency = m_txFrequency;
m_txFrequency = txFrequency;
uint32_t prevRxFrequency = m_rxFrequency;
m_rxFrequency = m_txFrequency + (entry.txOffsetMhz() * 1000000);
float spaceHz = entry.chSpaceKhz() * 1000;
uint32_t rootFreq = m_txFrequency - entry.baseFrequency();
uint8_t prevChannelNo = m_channelNo;
m_channelNo = rootFreq / spaceHz;
if (m_channelNo < 0 || m_channelNo > 4096) {
::LogError(LOG_SETUP, "Channel No %u is invalid.", m_channelNo);
m_channelNo = prevChannelNo;
m_txFrequency = prevTxFrequency;
m_rxFrequency = prevRxFrequency;
break;
}
writeConfig();
}
break;
/** General Commands */
case '`':
printStatus();
break;
case 'V':
getHostVersion();
break;
case 'H':
case 'h':
displayHelp();
break;
case 'S':
case 's':
{
yaml::Serialize(m_conf, m_confFile.c_str(), yaml::SerializeConfig(4, 64, false, false));
LogMessage(LOG_SETUP, " - Saved configuration to %s", m_confFile.c_str());
}
break;
case 'Q':
case 'q':
end = true;
break;
case 13:
case 10:
case -1:
break;
default:
LogError(LOG_SETUP, "Unknown command - %c (H/h for help)", c);
break;
}
sleep(5U);
}
m_console.close();
return 0;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Helper to print the help to the console.
/// </summary>
void HostSetup::displayHelp()
{
LogMessage(LOG_SETUP, "General Commands:");
LogMessage(LOG_SETUP, " ` Display current settings");
LogMessage(LOG_SETUP, " V Display version of host");
LogMessage(LOG_SETUP, " H/h Display help");
LogMessage(LOG_SETUP, " S/s Save settings to configuration file");
LogMessage(LOG_SETUP, " Q/q Quit");
LogMessage(LOG_SETUP, "Setup Commands:");
LogMessage(LOG_SETUP, " I Set identity (logical name)");
LogMessage(LOG_SETUP, " i Set logical channel ID");
LogMessage(LOG_SETUP, " c Set logical channel number (by channel number)");
LogMessage(LOG_SETUP, " f Set logical channel number (by Tx frequency)");
}
/// <summary>
/// Helper to calculate the Rx/Tx frequencies.
/// </summary>
bool HostSetup::calculateRxTxFreq()
{
IdenTable entry = m_idenTable->find(m_channelId);
if (entry.baseFrequency() == 0U) {
::LogError(LOG_HOST, "Channel Id %u has an invalid base frequency.", m_channelId);
return false;
}
if (m_channelNo == 0U) { // clamp to 1
m_channelNo = 1U;
}
if (m_channelNo > 4095U) { // clamp to 4095
m_channelNo = 4095U;
}
if (m_duplex) {
if (entry.txOffsetMhz() == 0U) {
::LogError(LOG_HOST, "Channel Id %u has an invalid Tx offset.", m_channelId);
return false;
}
uint32_t calcSpace = (uint32_t)(entry.chSpaceKhz() / 0.125);
float calcTxOffset = entry.txOffsetMhz() * 1000000;
m_rxFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo)) + calcTxOffset);
m_txFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo)));
}
else {
uint32_t calcSpace = (uint32_t)(entry.chSpaceKhz() / 0.125);
m_rxFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo)));
m_txFrequency = m_rxFrequency;
}
return true;
}
/// <summary>
/// Write configuration file.
/// </summary>
/// <returns>True, if configuration is written, otherwise false.</returns>
bool HostSetup::writeConfig()
{
m_conf["system"]["identity"] = m_identity;
m_conf["system"]["config"]["channelId"] = __INT_STR(m_channelId);
m_conf["system"]["config"]["channelNo"] = __INT_STR(m_channelNo);
printStatus();
return true;
}
/// <summary>
/// Helper to sleep the thread.
/// </summary>
/// <param name="ms">Milliseconds to sleep.</param>
void HostSetup::sleep(uint32_t ms)
{
#if defined(_WIN32) || defined(_WIN64)
::Sleep(ms);
#else
::usleep(ms * 1000);
#endif
}
/// <summary>
/// Prints the current status of the calibration.
/// </summary>
void HostSetup::printStatus()
{
IdenTable entry = m_idenTable->find(m_channelId);
if (entry.baseFrequency() == 0U) {
::LogError(LOG_HOST, "Channel Id %u has an invalid base frequency.", m_channelId);
}
calculateRxTxFreq();
LogMessage(LOG_SETUP, " - Channel ID: %u, Channel No: %u", m_channelId, m_channelNo);
LogMessage(LOG_SETUP, " - Base Freq: %uHz, TX Offset: %fMHz, Bandwidth: %fKHz, Channel Spacing: %fKHz", entry.baseFrequency(), entry.txOffsetMhz(), entry.chBandwidthKhz(), entry.chSpaceKhz());
LogMessage(LOG_SETUP, " - Rx Freq: %uHz, Tx Freq: %uHz, Identity: %s, Callsign: %s", m_rxFrequency, m_txFrequency, m_identity.c_str(), m_callsign.c_str());
}

@ -0,0 +1,89 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the MMDVMCal project. (https://github.com/g4klx/MMDVMCal)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2021 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(__HOST_SETUP_H__)
#define __HOST_SETUP_H__
#include "Defines.h"
#include "host/calibrate/Console.h"
#include "host/Host.h"
#include "lookups/IdenTableLookup.h"
#include "yaml/Yaml.h"
#include <string>
// ---------------------------------------------------------------------------
// Class Declaration
// This class implements an interactive session to setup the DVM.
// ---------------------------------------------------------------------------
class HOST_SW_API HostSetup {
public:
/// <summary>Initializes a new instance of the HostSetup class.</summary>
HostSetup(const std::string& confFile);
/// <summary>Finalizes a instance of the HostSetup class.</summary>
~HostSetup();
/// <summary>Executes the processing loop.</summary>
int run();
private:
const std::string& m_confFile;
yaml::Node m_conf;
Console m_console;
bool m_duplex;
std::string m_identity;
std::string m_callsign;
uint32_t m_rxFrequency;
uint32_t m_txFrequency;
uint8_t m_channelId;
uint32_t m_channelNo;
lookups::IdenTableLookup* m_idenTable;
/// <summary>Helper to print the help to the console.</summary>
void displayHelp();
/// <summary>Helper to calculate the Rx/Tx frequencies.</summary>
bool calculateRxTxFreq();
/// <summary>Write configuration file.</summary>
bool writeConfig();
/// <summary>Helper to sleep the thread.</summary>
void sleep(uint32_t ms);
/// <summary>Prints the current status.</summary>
void printStatus();
};
#endif // __HOST_SETUP_H__
Loading…
Cancel
Save

Powered by TurnKey Linux.