Merge pull request #1 from n7tae/main

run-time config, higher precision equalizer
main
nostar 3 years ago committed by GitHub
commit 55279d7ba4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

4
.gitignore vendored

@ -29,9 +29,5 @@
# Visual Studio
.vscode
#files
configure.h
configure.mk
# Executables
tcd

@ -0,0 +1,186 @@
/*
* 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 <algorithm>
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include "Configure.h"
// ini file keywords
#define USRPTXGAIN "UsrpTxGain"
#define USRPRXGAIN "UsrpRxGain"
#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<std::string> &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<std::string> 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 if (0 == key.compare(USRPTXGAIN))
usrp_tx = getSigned(key, value);
else if (0 == key.compare(USRPRXGAIN))
usrp_rx = getSigned(key, value);
else
badParam(key);
}
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 << " = " << 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;
}
int CConfigure::getSigned(const std::string &key, const std::string &value) const
{
auto i = std::stoi(value.c_str());
if (i < -24)
{
std::cout << "WARNING: " << key << " = " << value << " is too low. Limit to -24!" << std::endl;
i = -24;
}
else if (i > 24)
{
std::cout << "WARNING: " << key << " = " << value << " is too high. Limit to 24!" << std::endl;
i = 24;
}
return i;
}
void CConfigure::badParam(const std::string &key) const
{
std::cout << "WARNING: Unexpected parameter: '" << key << "'" << std::endl;
}
int CConfigure::GetGain(EGainType gt) const
{
switch (gt)
{
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;
}
}

@ -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 <cstdint>
#include <string>
#include <regex>
enum class EGainType { dmrin, dmrout, dstarin, dstarout, usrptx, usrprx };
#define IS_TRUE(a) ((a)=='t' || (a)=='T' || (a)=='1')
class CConfigure
{
public:
bool ReadData(const std::string &path);
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, usrp_tx, usrp_rx;
int getSigned(const std::string &key, const std::string &value) const;
void badParam(const std::string &key) const;
};

@ -28,31 +28,24 @@
#include "TranscoderPacket.h"
#include "Controller.h"
#include "Configure.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)
#define USRP_RXGAIN -6
#define USRP_TXGAIN 3
int16_t calcGainVal(float db)
int32_t CController::calcNumerator(int32_t db) const
{
float ratio = powf(10.0, (db/20.0));
if(db < 0){
ratio = (1/ratio) * (-1);
}
return (int16_t)roundf(ratio);
float num = 256.0f * powf(10.0f, (float(db)/20.0f));
return int32_t(roundf(num));
}
CController::CController() : keep_running(true) {}
bool CController::Start()
{
usrp_rxgain = calcGainVal(USRP_RXGAIN);
usrp_txgain = calcGainVal(USRP_TXGAIN);
usrp_rx_num = calcNumerator(g_Conf.GetGain(EGainType::usrprx));
usrp_tx_num = calcNumerator(g_Conf.GetGain(EGainType::usrptx));
if (InitVocoders() || reader.Open(REF2TC))
{
keep_running = false;
@ -130,7 +123,7 @@ bool CController::DiscoverFtdiDevices(std::list<std::pair<std::string, std::stri
bool CController::InitVocoders()
{
// M17 "devices", one for each module
const std::string modules(TRANSCODED_MODULES);
const std::string modules(g_Conf.GetTCMods());
for ( auto c : modules)
{
c2_16[c] = std::unique_ptr<CCodec2>(new CCodec2(false));
@ -196,7 +189,8 @@ bool CController::InitVocoders()
dstar_device = std::unique_ptr<CDVDevice>(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<CDVDevice>(new CDV3000(Encoding::dmrsf));
#endif
@ -210,10 +204,10 @@ bool CController::InitVocoders()
dmrsf_device = std::unique_ptr<CDVDevice>(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, int8_t(g_Conf.GetGain(EGainType::dstarin)), int8_t(g_Conf.GetGain(EGainType::dstarout))))
return true;
deviceset.pop_front();
}
@ -225,7 +219,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, int8_t(g_Conf.GetGain(EGainType::dmrin)), int8_t(g_Conf.GetGain(EGainType::dmrout))))
return true;
deviceset.pop_front();
}
@ -239,7 +233,7 @@ bool CController::InitVocoders()
// and start them (or it) up!
dstar_device->Start();
#ifndef USE_SW_AMBE2
dmrsf_device->Start();
#endif
@ -331,7 +325,7 @@ void CController::Codec2toAudio(std::shared_ptr<CTranscoderPacket> packet)
{
uint8_t ambe2[9];
uint8_t imbe[11];
if (packet->IsSecond())
{
if (packet->GetCodecIn() == ECodecType::c2_1600)
@ -415,24 +409,21 @@ void CController::ProcessC2Thread()
#ifdef USE_SW_AMBE2
void CController::AudiotoSWAMBE2(std::shared_ptr<CTranscoderPacket> 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);
}
md380_encode_fec(ambe2, tmp);
else
md380_encode_fec(ambe2, p);
packet->SetDMRData(ambe2);
// we might be all done...
send_mux.lock();
if (packet->AllCodecsAreSet() && packet->HasNotBeenSent()) SendToReflector(packet);
@ -441,9 +432,15 @@ void CController::AudiotoSWAMBE2(std::shared_ptr<CTranscoderPacket> packet)
void CController::SWAMBE2toAudio(std::shared_ptr<CTranscoderPacket> 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);
@ -488,18 +485,18 @@ void CController::AudiotoIMBE(std::shared_ptr<CTranscoderPacket> packet)
void CController::IMBEtoAudio(std::shared_ptr<CTranscoderPacket> 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);
codec2_queue.push(packet);
#ifdef USE_SW_AMBE2
swambe2_queue.push(packet);
#else
dmrsf_device->AddPacket(packet);
#endif
usrp_queue.push(packet);
}
@ -528,21 +525,19 @@ void CController::ProcessIMBEThread()
void CController::AudiotoUSRP(std::shared_ptr<CTranscoderPacket> 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);
}
packet->SetUSRPData(tmp);
else
packet->SetUSRPData(p);
// we might be all done...
send_mux.lock();
if (packet->AllCodecsAreSet() && packet->HasNotBeenSent()) SendToReflector(packet);
@ -551,30 +546,27 @@ void CController::AudiotoUSRP(std::shared_ptr<CTranscoderPacket> packet)
void CController::USRPtoAudio(std::shared_ptr<CTranscoderPacket> 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);
}
//packet->SetAudioSamples(packet->GetUSRPData(), false);
packet->SetAudioSamples(tmp, false);
else
packet->SetAudioSamples(p, 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);
}

@ -30,7 +30,6 @@
#include "DV3000.h"
#include "DV3003.h"
#include "UnixDgramSocket.h"
#include "configure.h"
class CController
{
@ -55,21 +54,20 @@ protected:
std::unique_ptr<CDVDevice> dstar_device, dmrsf_device;
CPacketQueue codec2_queue;
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<std::pair<std::string, std::string>> &found);
bool InitVocoders();
// processing threads
void ReadReflectorThread();
void ProcessC2Thread();
void ProcessIMBEThread();
void ProcessUSRPThread();
void Codec2toAudio(std::shared_ptr<CTranscoderPacket> packet);

@ -33,10 +33,10 @@
#include <thread>
#include "DV3000.h"
#include "configure.h"
#include "Configure.h"
#include "Controller.h"
extern CController Controller;
extern CController g_Cont;
CDV3000::CDV3000(Encoding t) : CDVDevice(t) {}
@ -144,15 +144,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();
}
}
}

@ -33,10 +33,10 @@
#include <thread>
#include "DV3003.h"
#include "configure.h"
#include "Configure.h"
#include "Controller.h"
extern CController Controller;
extern CController g_Cont;
CDV3003::CDV3003(Encoding t) : CDVDevice(t) {}
@ -148,15 +148,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();
}
}
}

@ -34,10 +34,9 @@
#include <thread>
#include "DVSIDevice.h"
#include "configure.h"
#include "Controller.h"
#include "Configure.h"
extern CController Controller;
extern CConfigure g_Conf;
CDVDevice::CDVDevice(Encoding t) : type(t), ftHandle(nullptr), buffer_depth(0), keep_running(true)
{
@ -565,7 +564,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)
{

@ -18,20 +18,31 @@
#include <iostream>
#include "Controller.h"
#include "Configure.h"
// the global controller object
CController Controller;
// the global objects
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;
}

@ -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)

@ -4,46 +4,57 @@ 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 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. 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.
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) 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*.
## 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, compiling, installing and other activities
## Compiling and configuring *tcd*
All other activities will be performed by the ./rconfig and ./radmin scripts in your urfd repo.
Copy the three configuration files to the working directory:
## 73
```bash
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*. 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. 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*
DE N7TAE
It is easiest to install and uninstall *tcd* using the ./radmin scripts in your urfd repo. If you want to do this manually:
```bash
sudo make install
sudo make uninstall
```

@ -0,0 +1,18 @@
# 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 module (for DVSI-3000), or
# up to three modules (for DVSI-3003).
Transcoded = A
# 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
DStarGainOut = -16
DmrYsfGainIn = -3
DmrYsfGainOut = 0
UsrpTxGain = 12
UsrpRxGain = -6

@ -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

@ -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]
Loading…
Cancel
Save

Powered by TurnKey Linux.