From 8fa16713d10ee1c6e45dbd4de1e6be1feec7a3cd Mon Sep 17 00:00:00 2001 From: Tom Early Date: Mon, 27 Feb 2023 16:19:50 -0700 Subject: [PATCH 1/7] run-time config --- .gitignore | 4 - Configure.cpp | 172 ++++++++++++++++++++++++++++++++ Configure.h | 43 ++++++++ Controller.cpp | 47 +++++---- Controller.h | 5 +- Main.cpp | 21 +++- Makefile | 10 +- README.md | 13 ++- config/tcd.ini | 15 +++ config/tcd.mk | 9 ++ {systemd => config}/tcd.service | 2 +- 11 files changed, 296 insertions(+), 45 deletions(-) create mode 100644 Configure.cpp create mode 100644 Configure.h create mode 100644 config/tcd.ini create mode 100644 config/tcd.mk rename {systemd => config}/tcd.service (83%) diff --git a/.gitignore b/.gitignore index dc59682..d900137 100644 --- a/.gitignore +++ b/.gitignore @@ -29,9 +29,5 @@ # Visual Studio .vscode -#files -configure.h -configure.mk - # Executables tcd diff --git a/Configure.cpp b/Configure.cpp new file mode 100644 index 0000000..800f775 --- /dev/null +++ b/Configure.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2023 by Thomas A. Early N7TAE + * + * 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 +#include +#include +#include +#include +#include "Configure.h" + +// ini file keywords +#define DMRGAININ "DmrYsfGainIn" +#define DMRGAINOUT "DmrYsfGainOut" +#define DSTARGAININ "DStarGainIn" +#define DSTARGAINOUT "DStarGainOut" +#define TRANSCODED "Transcoded" + +static inline void split(const std::string &s, char delim, std::vector &v) +{ + std::istringstream iss(s); + std::string item; + while (std::getline(iss, item, delim)) + v.push_back(item); +} + +// trim from start (in place) +static inline void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); +} + +// trim from end (in place) +static inline void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return !std::isspace(ch); + }).base(), s.end()); +} + +// trim from both ends (in place) +static inline void trim(std::string &s) { + ltrim(s); + rtrim(s); +} + +bool CConfigure::ReadData(const std::string &path) +// returns true on failure +{ + std::string modstmp; + + std::ifstream cfgfile(path.c_str(), std::ifstream::in); + if (! cfgfile.is_open()) { + std::cerr << "ERROR: '" << path << "' was not found!" << std::endl; + return true; + } + + std::string line; + while (std::getline(cfgfile, line)) + { + trim(line); + if (3 > line.size()) + continue; // can't be anything + if ('#' == line.at(0)) + continue; // skip comments + + std::vector tokens; + split(line, '=', tokens); + // check value for end-of-line comment + if (2 > tokens.size()) + { + std::cout << "WARNING: '" << line << "' does not contain an equal sign, skipping" << std::endl; + continue; + } + auto pos = tokens[1].find('#'); + if (std::string::npos != pos) + { + tokens[1].assign(tokens[1].substr(0, pos)); + rtrim(tokens[1]); // whitespace between the value and the end-of-line comment + } + // trim whitespace from around the '=' + rtrim(tokens[0]); + ltrim(tokens[1]); + const std::string key(tokens[0]); + const std::string value(tokens[1]); + if (key.empty() || value.empty()) + { + std::cout << "WARNING: missing key or value: '" << line << "'" << std::endl; + continue; + } + if (0 == key.compare(TRANSCODED)) + modstmp.assign(value); + else if (0 == key.compare(DSTARGAININ)) + dstar_in = getSigned(key, value); + else if (0 == key.compare(DSTARGAINOUT)) + dstar_out = getSigned(key, value); + else if (0 == key.compare(DMRGAININ)) + dmr_in = getSigned(key, value); + else if (0 == key.compare(DMRGAINOUT)) + dmr_out = getSigned(key, value); + else + badParam(key); + break; + } + cfgfile.close(); + + for (auto c : modstmp) + { + if (isalpha(c)) + { + if (islower(c)) + c = toupper(c); + if (std::string::npos == tcmods.find(c)) + tcmods.append(1, c); + } + } + if (tcmods.empty()) + { + std::cerr << "ERROR: no identifable module letters in '" << modstmp << "'. Halt." << std::endl; + return true; + } + + std::cout << TRANSCODED << " = " << tcmods << std::endl; + std::cout << DSTARGAININ << " = " << int(dstar_in) << std::endl; + std::cout << DSTARGAINOUT << " = " << int(dstar_out) << std::endl; + std::cout << DMRGAININ << " = " << int(dmr_in) << std::endl; + std::cout << DMRGAINOUT << " = " << int(dmr_out) << std::endl; + + return false; +} + +int CConfigure::getSigned(const std::string &key, const std::string &value) const +{ + auto i = std::stoi(value.c_str()); + if ( i < -90 || i > 90 ) + { + std::cout << "WARNING: " << key << " = " << value << " is out of range. Reset to zero!" << std::endl; + i = 0; + } + return i; +} + +void CConfigure::badParam(const std::string &key) const +{ + std::cout << "WARNING: Unexpected parameter: '" << key << "'" << std::endl; +} + +int8_t CConfigure::GetGain(EGainType gt) const +{ + switch (gt) + { + case EGainType::dmrin: return int8_t(dmr_in); + case EGainType::dmrout: return int8_t(dmr_out); + case EGainType::dstarin: return int8_t(dstar_in); + case EGainType::dstarout: return int8_t(dstar_out); + default: return 0; + } +} diff --git a/Configure.h b/Configure.h new file mode 100644 index 0000000..99f573e --- /dev/null +++ b/Configure.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 by Thomas A. Early N7TAE + * + * 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. + */ + +#pragma once + +#include +#include +#include + +enum class EGainType { dmrin, dmrout, dstarin, dstarout }; + +#define IS_TRUE(a) ((a)=='t' || (a)=='T' || (a)=='1') + +class CConfigure +{ +public: + bool ReadData(const std::string &path); + int8_t GetGain(EGainType gt) const; + std::string GetTCMods(void) const { return tcmods; } + +private: + // CFGDATA data; + std::string tcmods; + int dstar_in, dstar_out, dmr_in, dmr_out; + + int getSigned(const std::string &key, const std::string &value) const; + void badParam(const std::string &key) const; +}; diff --git a/Controller.cpp b/Controller.cpp index 10f8bb3..109a83d 100644 --- a/Controller.cpp +++ b/Controller.cpp @@ -22,13 +22,16 @@ #include #include #include + #ifdef USE_SW_AMBE2 #include #endif +#include "Configure.h" #include "TranscoderPacket.h" #include "Controller.h" +extern CConfigure g_Conf; //#define AMBE_GAIN 16 //Encoder gain in dB (I use 16 here) //#define AMBE2_GAIN -24 //Encoder gain in dB (I use -24 here) @@ -38,11 +41,11 @@ int16_t calcGainVal(float db) { float ratio = powf(10.0, (db/20.0)); - + if(db < 0){ ratio = (1/ratio) * (-1); } - + return (int16_t)roundf(ratio); } @@ -52,7 +55,7 @@ bool CController::Start() { usrp_rxgain = calcGainVal(USRP_RXGAIN); usrp_txgain = calcGainVal(USRP_TXGAIN); - + if (InitVocoders() || reader.Open(REF2TC)) { keep_running = false; @@ -130,7 +133,7 @@ bool CController::DiscoverFtdiDevices(std::list(new CCodec2(false)); @@ -210,10 +213,10 @@ bool CController::InitVocoders() dmrsf_device = std::unique_ptr(new CDV3003(Encoding::dmrsf)); #endif } - + if (dstar_device) { - if (dstar_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, DSTAR_IN_GAIN, DSTAR_OUT_GAIN)) + if (dstar_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, g_Conf.GetGain(EGainType::dstarin), g_Conf.GetGain(EGainType::dstarout))) return true; deviceset.pop_front(); } @@ -225,7 +228,7 @@ bool CController::InitVocoders() #ifndef USE_SW_AMBE2 if (dmrsf_device) { - if (dmrsf_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, DMR_IN_GAIN, DMR_OUT_GAIN)) + if (dmrsf_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, g_Conf.GetGain(EGainType::dmrin), g_Conf.GetGain(EGainType::dmrout))) return true; deviceset.pop_front(); } @@ -239,7 +242,7 @@ bool CController::InitVocoders() // and start them (or it) up! dstar_device->Start(); - + #ifndef USE_SW_AMBE2 dmrsf_device->Start(); #endif @@ -331,7 +334,7 @@ void CController::Codec2toAudio(std::shared_ptr packet) { uint8_t ambe2[9]; uint8_t imbe[11]; - + if (packet->IsSecond()) { if (packet->GetCodecIn() == ECodecType::c2_1600) @@ -380,7 +383,7 @@ void CController::Codec2toAudio(std::shared_ptr packet) #else dmrsf_device->AddPacket(packet); #endif - + packet->SetDMRData(ambe2); p25vocoder.encode_4400((int16_t*)packet->GetAudioSamples(), imbe); packet->SetP25Data(imbe); @@ -421,7 +424,7 @@ void CController::AudiotoSWAMBE2(std::shared_ptr packet) int16_t tmp[160]; const int16_t *p = packet->GetAudioSamples(); const uint32_t g = abs(ambe_gain); - + for(int i = 0; i < 160; ++i){ if(ambe_gain < 0){ tmp[i] = p[i] / g; @@ -430,10 +433,10 @@ void CController::AudiotoSWAMBE2(std::shared_ptr packet) tmp[i] = p[i] * g; } } - + md380_encode_fec(ambe2, tmp); packet->SetDMRData(ambe2); - + // we might be all done... send_mux.lock(); if (packet->AllCodecsAreSet() && packet->HasNotBeenSent()) SendToReflector(packet); @@ -494,13 +497,13 @@ void CController::IMBEtoAudio(std::shared_ptr packet) packet->SetAudioSamples(tmp, false); dstar_device->AddPacket(packet); codec2_queue.push(packet); - + #ifdef USE_SW_AMBE2 swambe2_queue.push(packet); #else dmrsf_device->AddPacket(packet); #endif - + usrp_queue.push(packet); } @@ -532,7 +535,7 @@ void CController::AudiotoUSRP(std::shared_ptr packet) int16_t tmp[160]; const int16_t *p = packet->GetAudioSamples(); const uint32_t g = abs(usrp_txgain); - + for(int i = 0; i < 160; ++i){ if(usrp_txgain < 0){ tmp[i] = p[i] / g; @@ -541,9 +544,9 @@ void CController::AudiotoUSRP(std::shared_ptr packet) tmp[i] = p[i] * g; } } - + packet->SetUSRPData(tmp); - + // we might be all done... send_mux.lock(); if (packet->AllCodecsAreSet() && packet->HasNotBeenSent()) SendToReflector(packet); @@ -555,7 +558,7 @@ void CController::USRPtoAudio(std::shared_ptr packet) int16_t tmp[160]; const int16_t *p = packet->GetUSRPData(); const uint32_t g = abs(usrp_rxgain); - + for(int i = 0; i < 160; ++i){ if(usrp_rxgain < 0){ tmp[i] = p[i] / g; @@ -564,18 +567,18 @@ void CController::USRPtoAudio(std::shared_ptr packet) tmp[i] = p[i] * g; } } - + //packet->SetAudioSamples(packet->GetUSRPData(), false); packet->SetAudioSamples(tmp, false); dstar_device->AddPacket(packet); codec2_queue.push(packet); - + #ifdef USE_SW_AMBE2 swambe2_queue.push(packet); #else dmrsf_device->AddPacket(packet); #endif - + imbe_queue.push(packet); } diff --git a/Controller.h b/Controller.h index 5b2ae04..1566027 100644 --- a/Controller.h +++ b/Controller.h @@ -30,7 +30,6 @@ #include "DV3000.h" #include "DV3003.h" #include "UnixDgramSocket.h" -#include "configure.h" class CController { @@ -55,7 +54,7 @@ protected: std::unique_ptr dstar_device, dmrsf_device; CPacketQueue codec2_queue; - + CPacketQueue imbe_queue; CPacketQueue usrp_queue; std::mutex send_mux; @@ -69,7 +68,7 @@ protected: // processing threads void ReadReflectorThread(); void ProcessC2Thread(); - + void ProcessIMBEThread(); void ProcessUSRPThread(); void Codec2toAudio(std::shared_ptr packet); diff --git a/Main.cpp b/Main.cpp index d27f7f7..7a4910d 100644 --- a/Main.cpp +++ b/Main.cpp @@ -18,20 +18,31 @@ #include #include "Controller.h" +#include "Configure.h" // the global controller object -CController Controller; +CConfigure g_Conf; +CController g_Cont; -int main() +int main(int argc, char *argv[]) { - if (Controller.Start()) + if (2 != argc) + { + std::cerr << "ERROR: Usage: " << argv[0] << " PATHTOINIFILE" << std::endl; return EXIT_FAILURE; + } - std::cout << "Hybrid Transcoder version 0.0.5 successfully started" << std::endl; + if (g_Conf.ReadData(argv[1])) + return EXIT_FAILURE; + + if (g_Cont.Start()) + return EXIT_FAILURE; + + std::cout << "Hybrid Transcoder version 0.1.0 successfully started" << std::endl; pause(); - Controller.Stop(); + g_Cont.Stop(); return EXIT_SUCCESS; } diff --git a/Makefile b/Makefile index a616efb..b67209f 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,6 @@ #Copyright (C) 2021 by Thomas A. Early, N7TAE -include configure.mk - -# If you are going to change this path, you will -# need to update the systemd service script -BINDIR = /usr/local/bin - -swambe2 = false +include tcd.mk GCC = g++ @@ -49,7 +43,7 @@ clean : # The install and uninstall targets need to be run by root install : $(EXE) cp $(EXE) $(BINDIR) - cp systemd/$(EXE).service /etc/systemd/system/ + cp $(EXE).service /etc/systemd/system/ systemctl enable $(EXE) systemctl daemon-reload systemctl start $(EXE) diff --git a/README.md b/README.md index 61c23e3..5146c57 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,22 @@ In the parent directory of you urfd repository: ```bash git clone https://github.com/n7tae/tcd.git +cd tcd ``` To be perfectly clear, the urfd reflector repository clone and this clone **must be in the same directory**. -## Configuring, compiling, installing and other activities +## Configuring and compiling -All other activities will be performed by the ./rconfig and ./radmin scripts in your urfd repo. +Copy the three configuration files: `cp config/* .` +Use your favorite text editor: +- *tcd.mk* defines some compile time options. Once you've set these options, do `make` +- *tcd.ini* defines run-time options. It is especially imporant that the `Transcoded` line for the tcd.ini file is exactly the same as the same line in the urfd.ini file! +- *tcd.service* is the systemd service file. be sure the `ExecStart` line will successfully start tcd! + +## Installing and other activities + +If the transcoder is local, all other activities will be performed by the ./radmin scripts in your urfd repo. ## 73 diff --git a/config/tcd.ini b/config/tcd.ini new file mode 100644 index 0000000..05410a7 --- /dev/null +++ b/config/tcd.ini @@ -0,0 +1,15 @@ +# Transcoder Ini file +# +# this is a comment + +# VERY IMPORTANT: This need to be idential to the same line in the urfd ini file! +# this will either be a single letter (for DVSI-3000), or +# three letters (for DVSI-3003) +Transcoded = A + +# Gain (or attenuation) in dB should be integers in the range of +/-90 +# Something is probably terribly wrong if you need more than +/-20 +DStarGainIn = 0 +DStarGainOut = 0 +DmrYsfGainIn = 0 +DmrYsfGainOut = 0 diff --git a/config/tcd.mk b/config/tcd.mk new file mode 100644 index 0000000..8e5cfe8 --- /dev/null +++ b/config/tcd.mk @@ -0,0 +1,9 @@ +# This is where the executable will be installed `make install` +BINDIR = /usr/local/bin + +# set to true to build a transcoder that supports gdb +debug = false + +# set to true to use the md-380 software vocoder +# this will only work on an arm-based system, like a raspberry pi +swambe2 = false diff --git a/systemd/tcd.service b/config/tcd.service similarity index 83% rename from systemd/tcd.service rename to config/tcd.service index a029840..2481c4d 100644 --- a/systemd/tcd.service +++ b/config/tcd.service @@ -7,7 +7,7 @@ After=systemd-user-session.service network.target Type=simple ExecStartPre=-/sbin/rmmod ftdi_sio ExecStartPre=-/sbin/rmmod usbserial -ExecStart=/usr/local/bin/tcd +ExecStart=/usr/local/bin/tcd /PATH_TO_INI_FILE Restart=always [Install] From 708edc90e9e8d179551c31f08032a4227afd55ed Mon Sep 17 00:00:00 2001 From: Tom Early Date: Thu, 2 Mar 2023 12:24:37 -0700 Subject: [PATCH 2/7] run-time config --- Configure.cpp | 39 ++++++++++++++++++++++++----------- Configure.h | 6 +++--- Controller.cpp | 17 ++++++--------- DV3000.cpp | 1 - DV3003.cpp | 1 - DVSIDevice.cpp | 6 ++++-- Main.cpp | 2 +- README.md | 56 +++++++++++++++++++++++++++----------------------- config/tcd.ini | 19 +++++++++-------- 9 files changed, 82 insertions(+), 65 deletions(-) diff --git a/Configure.cpp b/Configure.cpp index 800f775..42539af 100644 --- a/Configure.cpp +++ b/Configure.cpp @@ -24,6 +24,8 @@ #include "Configure.h" // ini file keywords +#define USRPTXGAIN "UsrpTxGain" +#define USRPRXGAIN "UsrpRxGain" #define DMRGAININ "DmrYsfGainIn" #define DMRGAINOUT "DmrYsfGainOut" #define DSTARGAININ "DStarGainIn" @@ -112,6 +114,10 @@ bool CConfigure::ReadData(const std::string &path) dmr_in = getSigned(key, value); else if (0 == key.compare(DMRGAINOUT)) dmr_out = getSigned(key, value); + else if (0 == key.compare(USRPTXGAIN)) + usrp_tx = getSigned(key, value); + else if (0 == key.compare(USRPRXGAIN)) + usrp_rx = getSigned(key, value); else badParam(key); break; @@ -135,10 +141,12 @@ bool CConfigure::ReadData(const std::string &path) } std::cout << TRANSCODED << " = " << tcmods << std::endl; - std::cout << DSTARGAININ << " = " << int(dstar_in) << std::endl; - std::cout << DSTARGAINOUT << " = " << int(dstar_out) << std::endl; - std::cout << DMRGAININ << " = " << int(dmr_in) << std::endl; - std::cout << DMRGAINOUT << " = " << int(dmr_out) << std::endl; + std::cout << DSTARGAININ << " = " << dstar_in << std::endl; + std::cout << DSTARGAINOUT << " = " << dstar_out << std::endl; + std::cout << DMRGAININ << " = " << dmr_in << std::endl; + std::cout << DMRGAINOUT << " = " << dmr_out << std::endl; + std::cout << USRPTXGAIN << " = " << usrp_tx << std::endl; + std::cout << USRPRXGAIN << " = " << usrp_rx << std::endl; return false; } @@ -146,10 +154,15 @@ bool CConfigure::ReadData(const std::string &path) int CConfigure::getSigned(const std::string &key, const std::string &value) const { auto i = std::stoi(value.c_str()); - if ( i < -90 || i > 90 ) + if (i < -36) { - std::cout << "WARNING: " << key << " = " << value << " is out of range. Reset to zero!" << std::endl; - i = 0; + std::cout << "WARNING: " << key << " = " << value << " is too low. Limit to -36!" << std::endl; + i = -36; + } + else if (i > 36) + { + std::cout << "WARNING: " << key << " = " << value << " is too high. Limit to 36!" << std::endl; + i = 36; } return i; } @@ -159,14 +172,16 @@ void CConfigure::badParam(const std::string &key) const std::cout << "WARNING: Unexpected parameter: '" << key << "'" << std::endl; } -int8_t CConfigure::GetGain(EGainType gt) const +int CConfigure::GetGain(EGainType gt) const { switch (gt) { - case EGainType::dmrin: return int8_t(dmr_in); - case EGainType::dmrout: return int8_t(dmr_out); - case EGainType::dstarin: return int8_t(dstar_in); - case EGainType::dstarout: return int8_t(dstar_out); + case EGainType::dmrin: return dmr_in; + case EGainType::dmrout: return dmr_out; + case EGainType::dstarin: return dstar_in; + case EGainType::dstarout: return dstar_out; + case EGainType::usrptx: return usrp_tx; + case EGainType::usrprx: return usrp_rx; default: return 0; } } diff --git a/Configure.h b/Configure.h index 99f573e..e553f38 100644 --- a/Configure.h +++ b/Configure.h @@ -22,7 +22,7 @@ #include #include -enum class EGainType { dmrin, dmrout, dstarin, dstarout }; +enum class EGainType { dmrin, dmrout, dstarin, dstarout, usrptx, usrprx }; #define IS_TRUE(a) ((a)=='t' || (a)=='T' || (a)=='1') @@ -30,13 +30,13 @@ class CConfigure { public: bool ReadData(const std::string &path); - int8_t GetGain(EGainType gt) const; + int GetGain(EGainType gt) const; std::string GetTCMods(void) const { return tcmods; } private: // CFGDATA data; std::string tcmods; - int dstar_in, dstar_out, dmr_in, dmr_out; + int dstar_in, dstar_out, dmr_in, dmr_out, usrp_tx, usrp_rx; int getSigned(const std::string &key, const std::string &value) const; void badParam(const std::string &key) const; diff --git a/Controller.cpp b/Controller.cpp index 109a83d..6dd8723 100644 --- a/Controller.cpp +++ b/Controller.cpp @@ -33,17 +33,12 @@ extern CConfigure g_Conf; -//#define AMBE_GAIN 16 //Encoder gain in dB (I use 16 here) -//#define AMBE2_GAIN -24 //Encoder gain in dB (I use -24 here) -#define USRP_RXGAIN -6 -#define USRP_TXGAIN 3 - int16_t calcGainVal(float db) { - float ratio = powf(10.0, (db/20.0)); + float ratio = powf(10.0f, (db/20.0f)); - if(db < 0){ - ratio = (1/ratio) * (-1); + if(db < 0.0f){ + ratio = (-1.0f/ratio); } return (int16_t)roundf(ratio); @@ -53,8 +48,8 @@ CController::CController() : keep_running(true) {} bool CController::Start() { - usrp_rxgain = calcGainVal(USRP_RXGAIN); - usrp_txgain = calcGainVal(USRP_TXGAIN); + usrp_rxgain = calcGainVal(g_Conf.GetGain(EGainType::usrprx)); + usrp_txgain = calcGainVal(g_Conf.GetGain(EGainType::usrptx)); if (InitVocoders() || reader.Open(REF2TC)) { @@ -134,7 +129,7 @@ bool CController::InitVocoders() { // M17 "devices", one for each module const std::string modules(g_Conf.GetTCMods()); - for ( auto c : modules) + for (auto c : modules) { c2_16[c] = std::unique_ptr(new CCodec2(false)); c2_32[c] = std::unique_ptr(new CCodec2(true)); diff --git a/DV3000.cpp b/DV3000.cpp index 687c945..1efc745 100644 --- a/DV3000.cpp +++ b/DV3000.cpp @@ -33,7 +33,6 @@ #include #include "DV3000.h" -#include "configure.h" #include "Controller.h" extern CController Controller; diff --git a/DV3003.cpp b/DV3003.cpp index 6a59668..1b253ec 100644 --- a/DV3003.cpp +++ b/DV3003.cpp @@ -33,7 +33,6 @@ #include #include "DV3003.h" -#include "configure.h" #include "Controller.h" extern CController Controller; diff --git a/DVSIDevice.cpp b/DVSIDevice.cpp index ecb826e..08868d1 100644 --- a/DVSIDevice.cpp +++ b/DVSIDevice.cpp @@ -34,8 +34,10 @@ #include #include "DVSIDevice.h" -#include "configure.h" #include "Controller.h" +#include "Configure.h" + +extern CConfigure g_Conf; extern CController Controller; @@ -565,7 +567,7 @@ void CDVDevice::dump(const char *title, const void *pointer, int length) const void CDVDevice::FeedDevice() { - const std::string modules(TRANSCODED_MODULES); + const std::string modules(g_Conf.GetTCMods()); const auto n = modules.size(); while (keep_running) { diff --git a/Main.cpp b/Main.cpp index 7a4910d..af9a432 100644 --- a/Main.cpp +++ b/Main.cpp @@ -20,7 +20,7 @@ #include "Controller.h" #include "Configure.h" -// the global controller object +// the global objects CConfigure g_Conf; CController g_Cont; diff --git a/README.md b/README.md index 5146c57..726af6e 100644 --- a/README.md +++ b/README.md @@ -4,55 +4,59 @@ tcd is a hybrid digital voice transcoder for ham radio used by the new URF refle ## Introduction -This will build a new kind of hybrid transcoder that uses AMBE DVSI-based hardware for vocoding digital voice streams used in DStar/DMR/YSF *and* David Rowe's open-source Codec2 used in M17. TCd is optimized for performance by using a highly multi-threaded design that incorporates blocking I/O to make it as efficient as possible. +This will build a new kind of hybrid transcoder that uses AMBE DVSI-based hardware for vocoding digital voice streams used in DStar/DMR/YSF *and* David Rowe's open-source Codec2 used in M17 as well as the open-source P25 vocoder, IMBE. -This is the only transcoder that will work with the [URF reflector](https://github.com/n7tae/urfd). - -The imbe_vocoder library is required for P25 and can be found here: - -https://github.com/nostar/imbe_vocoder - -To use md380_vocoder along with a single DV Dongle on an ARM platform (like RPi) change the line 'swambe2 = false' to 'swambe2 = true' in the Makefile. The md380_vocoder library can be found here: - -https://github.com/nostar/md380_vocoder +This is the only transcoder that will work with the [URF reflector](https://github.com/nostar/urfd). This software is loosely based on LX3JL's **ambed**, but is easily different enough to be considered an entirely original work. Here are some major differences with ambed: -- tcd uses both hardware-based and software-based vocoders, providing a bridge between the closed source vocoders used in DStar, DMR and YSF and open-source vocoders used in M17. -- **UNIX Sockets** are used to communicate between the reflector and this transcoder. This greatly simplifies the code and *significantly* improves transcoding performance. -- AMBE vocoders are dedicated to an assigned reflector channel. This prevents overloading when processing multiple voice streams and provides the best possible performance for the reflector's clients. +- tcd uses both hardware-based and software-based vocoders, providing a bridge between the closed source vocoders used in DStar, DMR and YSF and open-source vocoders used in M17 (Codec2) and P25 (IMBE). +- *UNIX Sockets* are used to communicate between the reflector and this transcoder. This greatly simplifies the code and significantly improves transcoding performance. +- Each configured module has a dedicated encoding and decoding instance running on a different thread. This prevents overloading when processing multiple voice streams and provides the best possible performance for the reflector's clients. ## Constraints and Requirements -This branch uses only one 300x device for the AMBE+(DStar) codec. The md380_vocoder library is used for the AMBE+2 (DMR/YSF/NXDN) codec. This means that this branch of tcd must run on an ARM platform like a RPi. +This branch uses only one 300x device for the AMBE+(DStar) codec. Currently, this program must be run locally with its paired URF reflector. Remote transcoding is not yet supported. -Only systemd-based operating systems are supported. Debian or Ubuntu is recommended. If you want to install this on a non-systemd based OS, you are on your own. Also, by default, tcd is built without gdb support. +Only systemd-based operating systems are supported. Debian or Ubuntu is recommended. If you want to install this on a non-systemd based OS, you are on your own. Also, by default, *tcd* is built without gdb support. + +The P25 IMBE software vocoder library is available [here](https://github.com/nostar/imbe_vocoder). See its README.md file for instructions for compiling and installating this library. + +If you are running tcd on an ARM-base processor, you can opt to use a software-based vocoder library available [here](https://github.com/nostar/md380_vocoder). This library is used for the AMBE+2 (DMR/YSF/NXDN) codec. If you are going to use this library, *tcd* must run on an ARM platform like a RPi. + +The DVSI devices need an FTDI driver which is available [here](https://ftdichip.com/drivers/d2xx-drivers). It's important to know that this driver will only work if the normal Linux kernel ftdi_sio and usbserial drivers are removed. This is automatically done by the system service file used for starting *tcd*. ## Download the repository In the parent directory of you urfd repository: ```bash -git clone https://github.com/n7tae/tcd.git +git clone https://github.com/nostar/tcd.git cd tcd ``` To be perfectly clear, the urfd reflector repository clone and this clone **must be in the same directory**. -## Configuring and compiling +## Compiling and configuring *tcd* + + Copy the three configuration files to the working directory: -Copy the three configuration files: `cp config/* .` -Use your favorite text editor: -- *tcd.mk* defines some compile time options. Once you've set these options, do `make` -- *tcd.ini* defines run-time options. It is especially imporant that the `Transcoded` line for the tcd.ini file is exactly the same as the same line in the urfd.ini file! -- *tcd.service* is the systemd service file. be sure the `ExecStart` line will successfully start tcd! +```bash +cp config/* . +``` -## Installing and other activities +Use your favorite text editor to edit the following files: +- *tcd.mk* defines some compile time options. If you want to use the md380 vocoder, or change the installation directory, specify it here. Once you've set these options, do `make` to compile *tcd*. +- *tcd.ini* defines run-time options. It is especially imporant that the `Transcoded` line for the tcd.ini file is exactly the same as the same line in the urfd.ini file! Suggested values for vocoder gains are provided. +- *tcd.service* is the systemd service file. be sure the `ExecStart` line will successfully start *tcd* by specifying the path to your *tcd* executable and your tcd.ini file. -If the transcoder is local, all other activities will be performed by the ./radmin scripts in your urfd repo. +## Installing *tcd* -## 73 +It is easiest to install and uninstall *tcd* using the ./radmin scripts in your urfd repo. If you want to do this manually: -DE N7TAE +```bash +sudo make install +sudo make uninstall +``` diff --git a/config/tcd.ini b/config/tcd.ini index 05410a7..2a8f23f 100644 --- a/config/tcd.ini +++ b/config/tcd.ini @@ -3,13 +3,16 @@ # this is a comment # VERY IMPORTANT: This need to be idential to the same line in the urfd ini file! -# this will either be a single letter (for DVSI-3000), or -# three letters (for DVSI-3003) +# This will either be a single module (for DVSI-3000), or +# up to three modules (for DVSI-3003). Transcoded = A -# Gain (or attenuation) in dB should be integers in the range of +/-90 -# Something is probably terribly wrong if you need more than +/-20 -DStarGainIn = 0 -DStarGainOut = 0 -DmrYsfGainIn = 0 -DmrYsfGainOut = 0 +# In dB, 6 dB is a factor of 2 in amplitude, 4 in power. +# Gain values are limited to -36 to +36. Typical values will usually be less. +# Below are suggested values that seem to work well with us +DStarGainIn = 16 +DStarGainOut = -16 +DmrYsfGainIn = -3 +DmrYsfGainOut = 0 +UsrpTxGain = 12 +UsrpRxGain = -6 From 95bfedf518f075cd9961c5043ebdd87f2f0a0104 Mon Sep 17 00:00:00 2001 From: Tom Early Date: Thu, 2 Mar 2023 14:39:56 -0700 Subject: [PATCH 3/7] fixed global reference --- DV3000.cpp | 14 +++++++------- DV3003.cpp | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/DV3000.cpp b/DV3000.cpp index 1efc745..6c1e2b6 100644 --- a/DV3000.cpp +++ b/DV3000.cpp @@ -35,7 +35,7 @@ #include "DV3000.h" #include "Controller.h" -extern CController Controller; +extern CController g_Cont; CDV3000::CDV3000(Encoding t) : CDVDevice(t) {} @@ -143,15 +143,15 @@ void CDV3000::ProcessPacket(const SDV_Packet &p) } if (Encoding::dstar == type) // is this a DMR or a DStar device? { - Controller.dstar_mux.lock(); - Controller.RouteDstPacket(packet); - Controller.dstar_mux.unlock(); + g_Cont.dstar_mux.lock(); + g_Cont.RouteDstPacket(packet); + g_Cont.dstar_mux.unlock(); } else { - Controller.dmrst_mux.lock(); - Controller.RouteDmrPacket(packet); - Controller.dmrst_mux.unlock(); + g_Cont.dmrst_mux.lock(); + g_Cont.RouteDmrPacket(packet); + g_Cont.dmrst_mux.unlock(); } } } diff --git a/DV3003.cpp b/DV3003.cpp index 1b253ec..ea42bea 100644 --- a/DV3003.cpp +++ b/DV3003.cpp @@ -35,7 +35,7 @@ #include "DV3003.h" #include "Controller.h" -extern CController Controller; +extern CController g_Cont; CDV3003::CDV3003(Encoding t) : CDVDevice(t) {} @@ -147,15 +147,15 @@ void CDV3003::ProcessPacket(const SDV_Packet &p) } if (Encoding::dstar == type) // is this a DMR or a DStar device? { - Controller.dstar_mux.lock(); - Controller.RouteDstPacket(packet); - Controller.dstar_mux.unlock(); + g_Cont.dstar_mux.lock(); + g_Cont.RouteDstPacket(packet); + g_Cont.dstar_mux.unlock(); } else { - Controller.dmrst_mux.lock(); - Controller.RouteDmrPacket(packet); - Controller.dmrst_mux.unlock(); + g_Cont.dmrst_mux.lock(); + g_Cont.RouteDmrPacket(packet); + g_Cont.dmrst_mux.unlock(); } } } From 510ce97752045b17d3f48e01385aa6b9ded7787b Mon Sep 17 00:00:00 2001 From: Tom Early Date: Thu, 2 Mar 2023 15:30:44 -0700 Subject: [PATCH 4/7] no break! --- Configure.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Configure.cpp b/Configure.cpp index 42539af..34342bd 100644 --- a/Configure.cpp +++ b/Configure.cpp @@ -120,7 +120,6 @@ bool CConfigure::ReadData(const std::string &path) usrp_rx = getSigned(key, value); else badParam(key); - break; } cfgfile.close(); From a3c49722e3dda0f18d9fbf8f631d297c2eb2fbd7 Mon Sep 17 00:00:00 2001 From: Tom Early Date: Sat, 4 Mar 2023 07:50:16 -0700 Subject: [PATCH 5/7] redo run-time config --- Configure.cpp | 12 +++---- Controller.cpp | 95 +++++++++++++++++++++++--------------------------- Controller.h | 5 ++- DV3000.cpp | 1 + DV3003.cpp | 1 + DVSIDevice.cpp | 3 -- config/tcd.ini | 6 ++-- 7 files changed, 57 insertions(+), 66 deletions(-) diff --git a/Configure.cpp b/Configure.cpp index 34342bd..78d00d8 100644 --- a/Configure.cpp +++ b/Configure.cpp @@ -153,15 +153,15 @@ bool CConfigure::ReadData(const std::string &path) int CConfigure::getSigned(const std::string &key, const std::string &value) const { auto i = std::stoi(value.c_str()); - if (i < -36) + if (i < -24) { - std::cout << "WARNING: " << key << " = " << value << " is too low. Limit to -36!" << std::endl; - i = -36; + std::cout << "WARNING: " << key << " = " << value << " is too low. Limit to -24!" << std::endl; + i = -24; } - else if (i > 36) + else if (i > 24) { - std::cout << "WARNING: " << key << " = " << value << " is too high. Limit to 36!" << std::endl; - i = 36; + std::cout << "WARNING: " << key << " = " << value << " is too high. Limit to 24!" << std::endl; + i = 24; } return i; } diff --git a/Controller.cpp b/Controller.cpp index 6dd8723..ad8cf95 100644 --- a/Controller.cpp +++ b/Controller.cpp @@ -22,34 +22,29 @@ #include #include #include - #ifdef USE_SW_AMBE2 #include #endif -#include "Configure.h" #include "TranscoderPacket.h" #include "Controller.h" +#include "Configure.h" extern CConfigure g_Conf; -int16_t calcGainVal(float db) +int32_t CController::calcNumerator(int32_t db) const { - float ratio = powf(10.0f, (db/20.0f)); - - if(db < 0.0f){ - ratio = (-1.0f/ratio); - } + float num = 256.0f * powf(10.0f, (float(db)/20.0f)); - return (int16_t)roundf(ratio); + return int32_t(roundf(num)); } CController::CController() : keep_running(true) {} bool CController::Start() { - usrp_rxgain = calcGainVal(g_Conf.GetGain(EGainType::usrprx)); - usrp_txgain = calcGainVal(g_Conf.GetGain(EGainType::usrptx)); + usrp_rx_num = calcNumerator(g_Conf.GetGain(EGainType::usrprx)); + usrp_tx_num = calcNumerator(g_Conf.GetGain(EGainType::usrptx)); if (InitVocoders() || reader.Open(REF2TC)) { @@ -129,7 +124,7 @@ bool CController::InitVocoders() { // M17 "devices", one for each module const std::string modules(g_Conf.GetTCMods()); - for (auto c : modules) + for ( auto c : modules) { c2_16[c] = std::unique_ptr(new CCodec2(false)); c2_32[c] = std::unique_ptr(new CCodec2(true)); @@ -194,7 +189,8 @@ bool CController::InitVocoders() dstar_device = std::unique_ptr(new CDV3000(Encoding::dstar)); #ifdef USE_SW_AMBE2 md380_init(); - ambe_gain = calcGainVal(DMR_IN_GAIN); + ambe_in_num = calcNumerator(g_Conf.GetGain(EGainType::dmrin)); + ambe_out_num = calcNumerator(g_Conf.GetGain(EGainType::dmrout)); #else dmrsf_device = std::unique_ptr(new CDV3000(Encoding::dmrsf)); #endif @@ -211,7 +207,7 @@ bool CController::InitVocoders() if (dstar_device) { - if (dstar_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, g_Conf.GetGain(EGainType::dstarin), g_Conf.GetGain(EGainType::dstarout))) + if (dstar_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, int8_t(g_Conf.GetGain(EGainType::dstarin)), int8_t(g_Conf.GetGain(EGainType::dstarout)))) return true; deviceset.pop_front(); } @@ -223,7 +219,7 @@ bool CController::InitVocoders() #ifndef USE_SW_AMBE2 if (dmrsf_device) { - if (dmrsf_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, g_Conf.GetGain(EGainType::dmrin), g_Conf.GetGain(EGainType::dmrout))) + if (dmrsf_device->OpenDevice(deviceset.front().first, deviceset.front().second, dvtype, int8_t(g_Conf.GetGain(EGainType::dmrin)), int8_t(g_Conf.GetGain(EGainType::dmrout)))) return true; deviceset.pop_front(); } @@ -375,11 +371,10 @@ void CController::Codec2toAudio(std::shared_ptr packet) #ifdef USE_SW_AMBE2 md380_encode_fec(ambe2, packet->GetAudioSamples()); + packet->SetDMRData(ambe2); #else dmrsf_device->AddPacket(packet); #endif - - packet->SetDMRData(ambe2); p25vocoder.encode_4400((int16_t*)packet->GetAudioSamples(), imbe); packet->SetP25Data(imbe); packet->SetUSRPData((int16_t*)packet->GetAudioSamples()); @@ -414,22 +409,19 @@ void CController::ProcessC2Thread() #ifdef USE_SW_AMBE2 void CController::AudiotoSWAMBE2(std::shared_ptr packet) { - const auto m = packet->GetModule(); uint8_t ambe2[9]; - int16_t tmp[160]; const int16_t *p = packet->GetAudioSamples(); - const uint32_t g = abs(ambe_gain); - for(int i = 0; i < 160; ++i){ - if(ambe_gain < 0){ - tmp[i] = p[i] / g; - } - else{ - tmp[i] = p[i] * g; - } + if (ambe_in_num != 256) + { + int16_t tmp[160]; + for(int i = 0; i < 160; ++i) + tmp[i] = int16_t((p[i] * ambe_in_num) >> 8); + md380_encode_fec(ambe2, tmp); } + else + md380_encode_fec(ambe2, p); - md380_encode_fec(ambe2, tmp); packet->SetDMRData(ambe2); // we might be all done... @@ -440,9 +432,15 @@ void CController::AudiotoSWAMBE2(std::shared_ptr packet) void CController::SWAMBE2toAudio(std::shared_ptr packet) { - int16_t tmp[160] = {0}; + int16_t tmp[160]; md380_decode_fec(packet->GetDMRData(), tmp); + if (ambe_out_num != 256) + { + for (int i=0; i<160; i++) + tmp[i] = (tmp[i] * ambe_out_num) >> 8; + } packet->SetAudioSamples(tmp, false); + dstar_device->AddPacket(packet); codec2_queue.push(packet); imbe_queue.push(packet); @@ -487,7 +485,7 @@ void CController::AudiotoIMBE(std::shared_ptr packet) void CController::IMBEtoAudio(std::shared_ptr packet) { - int16_t tmp[160] = {0}; + int16_t tmp[160] = { 0 }; p25vocoder.decode_4400(tmp, (uint8_t*)packet->GetP25Data()); packet->SetAudioSamples(tmp, false); dstar_device->AddPacket(packet); @@ -527,20 +525,18 @@ void CController::ProcessIMBEThread() void CController::AudiotoUSRP(std::shared_ptr packet) { - int16_t tmp[160]; const int16_t *p = packet->GetAudioSamples(); - const uint32_t g = abs(usrp_txgain); - for(int i = 0; i < 160; ++i){ - if(usrp_txgain < 0){ - tmp[i] = p[i] / g; - } - else{ - tmp[i] = p[i] * g; - } + if (usrp_tx_num != 256) + { + int16_t tmp[160]; + for(int i = 0; i < 160; ++i) + tmp[i] = int16_t((p[i] * usrp_tx_num) >> 8); + packet->SetUSRPData(tmp); } + else + packet->SetUSRPData(p); - packet->SetUSRPData(tmp); // we might be all done... send_mux.lock(); @@ -550,21 +546,18 @@ void CController::AudiotoUSRP(std::shared_ptr packet) void CController::USRPtoAudio(std::shared_ptr packet) { - int16_t tmp[160]; const int16_t *p = packet->GetUSRPData(); - const uint32_t g = abs(usrp_rxgain); - for(int i = 0; i < 160; ++i){ - if(usrp_rxgain < 0){ - tmp[i] = p[i] / g; - } - else{ - tmp[i] = p[i] * g; - } + if (usrp_rx_num != 256) + { + int16_t tmp[160]; + for(int i = 0; i < 160; ++i) + tmp[i] = int16_t((p[i] * usrp_rx_num) >> 8); + packet->SetAudioSamples(tmp, false); } + else + packet->SetAudioSamples(p, false); - //packet->SetAudioSamples(packet->GetUSRPData(), false); - packet->SetAudioSamples(tmp, false); dstar_device->AddPacket(packet); codec2_queue.push(packet); diff --git a/Controller.h b/Controller.h index 1566027..0b65473 100644 --- a/Controller.h +++ b/Controller.h @@ -58,11 +58,10 @@ protected: CPacketQueue imbe_queue; CPacketQueue usrp_queue; std::mutex send_mux; - int16_t ambe_gain; - int16_t usrp_rxgain; - int16_t usrp_txgain; + int32_t ambe_in_num, ambe_out_num, usrp_rx_num, usrp_tx_num; imbe_vocoder p25vocoder; + int32_t calcNumerator(int32_t db) const; bool DiscoverFtdiDevices(std::list> &found); bool InitVocoders(); // processing threads diff --git a/DV3000.cpp b/DV3000.cpp index 6c1e2b6..e707273 100644 --- a/DV3000.cpp +++ b/DV3000.cpp @@ -33,6 +33,7 @@ #include #include "DV3000.h" +#include "Configure.h" #include "Controller.h" extern CController g_Cont; diff --git a/DV3003.cpp b/DV3003.cpp index ea42bea..b1dec1e 100644 --- a/DV3003.cpp +++ b/DV3003.cpp @@ -33,6 +33,7 @@ #include #include "DV3003.h" +#include "Configure.h" #include "Controller.h" extern CController g_Cont; diff --git a/DVSIDevice.cpp b/DVSIDevice.cpp index 08868d1..61409ca 100644 --- a/DVSIDevice.cpp +++ b/DVSIDevice.cpp @@ -34,13 +34,10 @@ #include #include "DVSIDevice.h" -#include "Controller.h" #include "Configure.h" extern CConfigure g_Conf; -extern CController Controller; - CDVDevice::CDVDevice(Encoding t) : type(t), ftHandle(nullptr), buffer_depth(0), keep_running(true) { } diff --git a/config/tcd.ini b/config/tcd.ini index 2a8f23f..0b7ede4 100644 --- a/config/tcd.ini +++ b/config/tcd.ini @@ -7,9 +7,9 @@ # up to three modules (for DVSI-3003). Transcoded = A -# In dB, 6 dB is a factor of 2 in amplitude, 4 in power. -# Gain values are limited to -36 to +36. Typical values will usually be less. -# Below are suggested values that seem to work well with us +# 6 dB is a factor of 2 in amplitude, 4 in power. +# Gain values are limited to -24 to +24. Typical values will usually be less. +# Below are suggested values. DStarGainIn = 16 DStarGainOut = -16 DmrYsfGainIn = -3 From 91ad8ab431aaf8264c894c9f957b4811468d4bad Mon Sep 17 00:00:00 2001 From: Tom Early Date: Sat, 4 Mar 2023 08:37:02 -0700 Subject: [PATCH 6/7] documentation --- README.md | 6 ++---- config/tcd.ini | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 726af6e..47fa3eb 100644 --- a/README.md +++ b/README.md @@ -10,21 +10,19 @@ This is the only transcoder that will work with the [URF reflector](https://gith This software is loosely based on LX3JL's **ambed**, but is easily different enough to be considered an entirely original work. Here are some major differences with ambed: -- tcd uses both hardware-based and software-based vocoders, providing a bridge between the closed source vocoders used in DStar, DMR and YSF and open-source vocoders used in M17 (Codec2) and P25 (IMBE). +- tcd uses both hardware-based and software-based vocoders, providing a bridge between the closed source vocoders used in DStar, DMR NXDN and YSF and open-source vocoders used in M17 (Codec2) and P25 (IMBE). - *UNIX Sockets* are used to communicate between the reflector and this transcoder. This greatly simplifies the code and significantly improves transcoding performance. - Each configured module has a dedicated encoding and decoding instance running on a different thread. This prevents overloading when processing multiple voice streams and provides the best possible performance for the reflector's clients. ## Constraints and Requirements -This branch uses only one 300x device for the AMBE+(DStar) codec. - Currently, this program must be run locally with its paired URF reflector. Remote transcoding is not yet supported. Only systemd-based operating systems are supported. Debian or Ubuntu is recommended. If you want to install this on a non-systemd based OS, you are on your own. Also, by default, *tcd* is built without gdb support. The P25 IMBE software vocoder library is available [here](https://github.com/nostar/imbe_vocoder). See its README.md file for instructions for compiling and installating this library. -If you are running tcd on an ARM-base processor, you can opt to use a software-based vocoder library available [here](https://github.com/nostar/md380_vocoder). This library is used for the AMBE+2 (DMR/YSF/NXDN) codec. If you are going to use this library, *tcd* must run on an ARM platform like a RPi. +If you are running tcd on an ARM-base processor, you can opt to use a software-based vocoder library available [here](https://github.com/nostar/md380_vocoder) for DMR/YSF vocoding. This library is used for the AMBE+2 (DMR/YSF/NXDN) codec. If you are going to use this library, *tcd* must run on an ARM platform like a RPi. Using this software solution means that you only need one DVSI device to handle D-Star vocoding. The DVSI devices need an FTDI driver which is available [here](https://ftdichip.com/drivers/d2xx-drivers). It's important to know that this driver will only work if the normal Linux kernel ftdi_sio and usbserial drivers are removed. This is automatically done by the system service file used for starting *tcd*. diff --git a/config/tcd.ini b/config/tcd.ini index 0b7ede4..15f68a4 100644 --- a/config/tcd.ini +++ b/config/tcd.ini @@ -7,7 +7,7 @@ # up to three modules (for DVSI-3003). Transcoded = A -# 6 dB is a factor of 2 in amplitude, 4 in power. +# All gain values are in dB. # Gain values are limited to -24 to +24. Typical values will usually be less. # Below are suggested values. DStarGainIn = 16 From 6c54b6e80bb0053df605d3cb01b7d81dacf15a1e Mon Sep 17 00:00:00 2001 From: Tom Early Date: Mon, 6 Mar 2023 10:30:53 -0700 Subject: [PATCH 7/7] README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 47fa3eb..d8a34ba 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,9 @@ cp config/* . ``` Use your favorite text editor to edit the following files: -- *tcd.mk* defines some compile time options. If you want to use the md380 vocoder, or change the installation directory, specify it here. Once you've set these options, do `make` to compile *tcd*. +- *tcd.mk* defines some compile time options. If you want to use the md380 vocoder, or change the installation directory, specify it here. Once you've set these options, do `make` to compile *tcd*. If you change `BINDIR`, you need to also change the `ExecStart` in your *tcd.service* file. - *tcd.ini* defines run-time options. It is especially imporant that the `Transcoded` line for the tcd.ini file is exactly the same as the same line in the urfd.ini file! Suggested values for vocoder gains are provided. -- *tcd.service* is the systemd service file. be sure the `ExecStart` line will successfully start *tcd* by specifying the path to your *tcd* executable and your tcd.ini file. +- *tcd.service* is the systemd service file. You will need to modify the `ExecStart` line to successfully start *tcd* by specifying the path to your *tcd* executable and your tcd.ini file. ## Installing *tcd*