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