Merge branch 'tcp'

main
Tom Early 1 year ago
commit 7b28a8786d

20
.gitignore vendored

@ -7,24 +7,8 @@
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Files
tcd.*
# Visual Studio
.vscode

@ -21,6 +21,7 @@
#include <fstream>
#include <vector>
#include <sstream>
#include <regex>
#include "Configure.h"
// ini file keywords
@ -30,7 +31,9 @@
#define DMRGAINOUT "DmrYsfGainOut"
#define DSTARGAININ "DStarGainIn"
#define DSTARGAINOUT "DStarGainOut"
#define TRANSCODED "Transcoded"
#define MODULES "Modules"
#define SERVERADDRESS "ServerAddress"
#define PORT "Port"
static inline void split(const std::string &s, char delim, std::vector<std::string> &v)
{
@ -63,7 +66,10 @@ static inline void trim(std::string &s) {
bool CConfigure::ReadData(const std::string &path)
// returns true on failure
{
std::string modstmp;
std::regex IPv4RegEx = std::regex("^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3,3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]){1,1}$", std::regex::extended);
std::regex IPv6RegEx = std::regex("^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}(:[0-9a-fA-F]{1,4}){1,1}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|([0-9a-fA-F]{1,4}:){1,1}(:[0-9a-fA-F]{1,4}){1,6}|:((:[0-9a-fA-F]{1,4}){1,7}|:))$", std::regex::extended);
std::string modstmp, porttmp;
std::ifstream cfgfile(path.c_str(), std::ifstream::in);
if (! cfgfile.is_open()) {
@ -104,7 +110,11 @@ bool CConfigure::ReadData(const std::string &path)
std::cout << "WARNING: missing key or value: '" << line << "'" << std::endl;
continue;
}
if (0 == key.compare(TRANSCODED))
if (0 == key.compare(SERVERADDRESS))
address.assign(value);
else if (0 == key.compare(PORT))
porttmp.assign(value);
else if (0 == key.compare(MODULES))
modstmp.assign(value);
else if (0 == key.compare(DSTARGAININ))
dstar_in = getSigned(key, value);
@ -139,7 +149,22 @@ bool CConfigure::ReadData(const std::string &path)
return true;
}
std::cout << TRANSCODED << " = " << tcmods << std::endl;
if (! std::regex_match(address, IPv4RegEx) && ! std::regex_match(address, IPv6RegEx))
{
std::cerr << "ERROR: '" << address << "' is malformed, Halt." << std::endl;
return true;
}
port = std::strtoul(porttmp.c_str(), nullptr, 10);
if (port < 1025 || port > 49000)
{
std::cerr << "ERROR: Port '" << porttmp << "' must be between >1024 and <49000. Halt." << std::endl;
return true;
}
std::cout << MODULES << " = " << tcmods << std::endl;
std::cout << SERVERADDRESS << " = " << address << std::endl;
std::cout << PORT << " = " << port << std::endl;
std::cout << DSTARGAININ << " = " << dstar_in << std::endl;
std::cout << DSTARGAINOUT << " = " << dstar_out << std::endl;
std::cout << DMRGAININ << " = " << dmr_in << std::endl;

@ -32,10 +32,13 @@ public:
bool ReadData(const std::string &path);
int GetGain(EGainType gt) const;
std::string GetTCMods(void) const { return tcmods; }
std::string GetAddress(void) const { return address; }
unsigned GetPort(void) const { return port; }
private:
// CFGDATA data;
std::string tcmods;
std::string tcmods, address;
uint16_t port;
int dstar_in, dstar_out, dmr_in, dmr_out, usrp_tx, usrp_rx;
int getSigned(const std::string &key, const std::string &value) const;

@ -22,6 +22,7 @@
#include <sstream>
#include <fstream>
#include <thread>
#include <queue>
#ifdef USE_SW_AMBE2
#include <md380_vocoder.h>
#endif
@ -46,7 +47,7 @@ bool CController::Start()
usrp_rx_num = calcNumerator(g_Conf.GetGain(EGainType::usrprx));
usrp_tx_num = calcNumerator(g_Conf.GetGain(EGainType::usrptx));
if (InitVocoders() || reader.Open(REF2TC))
if (InitVocoders() || tcClient.Open(g_Conf.GetAddress(), g_Conf.GetTCMods(), g_Conf.GetPort()))
{
keep_running = false;
return true;
@ -70,7 +71,7 @@ void CController::Stop()
if (c2Future.valid())
c2Future.get();
reader.Close();
tcClient.Close();
dstar_device->CloseDevice();
dmrsf_device->CloseDevice();
dstar_device.reset();
@ -249,14 +250,18 @@ void CController::ReadReflectorThread()
{
while (keep_running)
{
STCPacket tcpack;
// preemptively check the connection(s)...
tcClient.ReConnect();
std::queue<std::unique_ptr<STCPacket>> queue;
// wait up to 100 ms to read something on the unix port
if (reader.Receive(&tcpack, 100))
tcClient.Receive(queue, 100);
while (! queue.empty())
{
// create a shared pointer to a new packet
// there is only one CTranscoderPacket created for each new STCPacket received from the reflector
auto packet = std::make_shared<CTranscoderPacket>(tcpack);
auto packet = std::make_shared<CTranscoderPacket>(*queue.front());
queue.pop();
switch (packet->GetCodecIn())
{
case ECodecType::dstar:
@ -595,14 +600,11 @@ void CController::ProcessUSRPThread()
void CController::SendToReflector(std::shared_ptr<CTranscoderPacket> packet)
{
// open a socket to the reflector channel
CUnixDgramWriter socket;
std::string name(TC2REF);
name.append(1, packet->GetModule());
socket.SetUp(name.c_str());
// send the packet over the socket
socket.Send(packet->GetTCPacket());
// the socket will automatically close after sending
while (tcClient.Send(packet->GetTCPacket()))
{
tcClient.ReConnect();
}
packet->Sent();
}
@ -648,7 +650,7 @@ void CController::RouteDmrPacket(std::shared_ptr<CTranscoderPacket> packet)
void CController::Dump(const std::shared_ptr<CTranscoderPacket> p, const std::string &title) const
{
std::stringstream line;
line << title << " Mod='" << p->GetModule() << "' SID=" << std::showbase << std::hex << ntohs(p->GetStreamId()) << std::noshowbase << " ET:" << std::setprecision(3) << p->GetTimeMS();
line << title << " Mod='" << p->GetModule() << "' SID=" << std::showbase << std::hex << ntohs(p->GetStreamId()) << std::noshowbase;
ECodecType in = p->GetCodecIn();
if (p->DStarIsSet())
@ -665,6 +667,14 @@ void CController::Dump(const std::shared_ptr<CTranscoderPacket> p, const std::st
line << "**";
else if (ECodecType::c2_3200 == in)
line << '*';
if (p->P25IsSet())
line << " P25";
if (ECodecType::p25 == in)
line << "*";
if (p->USRPIsSet())
line << " USRP";
if (ECodecType::usrp == in)
line << "*";
if (p->IsSecond())
line << " IsSecond";
if (p->IsLast())

@ -29,7 +29,7 @@
#include "codec2.h"
#include "DV3000.h"
#include "DV3003.h"
#include "UnixDgramSocket.h"
#include "TCSocket.h"
class CController
{
@ -48,8 +48,7 @@ protected:
std::future<void> reflectorFuture, c2Future, imbeFuture, usrpFuture;
std::unordered_map<char, int16_t[160]> audio_store;
std::unordered_map<char, uint8_t[8]> data_store;
CUnixDgramReader reader;
CUnixDgramWriter writer;
CTCClient tcClient;
std::unordered_map<char, std::unique_ptr<CCodec2>> c2_16, c2_32;
std::unique_ptr<CDVDevice> dstar_device, dmrsf_device;

@ -0,0 +1 @@
../urfd/reflector/IP.cpp

@ -0,0 +1 @@
../urfd/reflector/IP.h

@ -5,9 +5,9 @@ include tcd.mk
GCC = g++
ifeq ($(debug), true)
CFLAGS = -ggdb3 -W -Werror -Icodec2 -MMD -MD -std=c++11
CFLAGS = -ggdb3 -W -Werror -Icodec2 -MMD -MD -std=c++17
else
CFLAGS = -W -Werror -Icodec2 -MMD -MD -std=c++11
CFLAGS = -W -Werror -Icodec2 -MMD -MD -std=c++17
endif
ifeq ($(swambe2), true)

@ -11,16 +11,14 @@ 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 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.
- *TCP Sockets* are used to communicate between the reflector and this transcoder. This guarantees that packets moving between the reflector and transcoder are never lost and the arrive at their destination in order.
- 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
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.
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 installing 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.
@ -28,14 +26,14 @@ The DVSI devices need an FTDI driver which is available [here](https://ftdichip.
## Download the repository
In the parent directory of you urfd repository:
In the parent directory of you *urfd* repository:
```bash
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**.
To be perfectly clear, the urfd reflector repository clone and this clone **must be in the same directory**. If your transcoder is a remote installation, you still need to `git clone https://github.com/nostar/urfd.git` even though you won't compile anything in the *urfd* repository. Both *tcd* and *urfd* repositories need to be in the same directory as several of the source files in *tcd* are symbolic links to the adjacent *urfd* reflector source code.
## Compiling and configuring *tcd*
@ -47,10 +45,10 @@ 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.ini* defines run-time options. It is especially important that the `Modules` line for the tcd.ini file is exactly the same as the same line in the urfd.ini file! The `ServerAddress` is the url of the server. If the transcoder is local, this is usually `127.0.0.1` or `::1`. If the transcoder is remote, this is the IP address of the server. 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*
## Installing *tcd* when the transcoder is local
It is easiest to install and uninstall *tcd* using the ./radmin scripts in your urfd repo. If you want to do this manually:
@ -58,3 +56,14 @@ It is easiest to install and uninstall *tcd* using the ./radmin scripts in your
sudo make install
sudo make uninstall
```
## Installing *tcd when the transcoder is remote
Use:
- `make` to compile *tcd*.
- `sudo make install` to install and run *tcd*.
- `sudo systemctl *something*` is used to manage the running *tcd*, where `*something*` might be `start`, `stop` or other verbs.
- `sudo journalctl -u tcd -f` to monitor the logs.
- `sudo make uninstall` to uninstall *tcd*.
When started, *tcd* will establish a TCP connection for each transcoded reflector module. If the TCP connection is lost, *tcd* will block until the connection is reestablished. A message will be printed every 10 seconds suggesting that the reflector needs to be restarted.

@ -0,0 +1 @@
../urfd/reflector/TCSocket.cpp

@ -0,0 +1 @@
../urfd/reflector/TCSocket.h

@ -143,11 +143,6 @@ uint32_t CTranscoderPacket::GetSequence() const
return tcpacket.sequence;
}
double CTranscoderPacket::GetTimeMS() const
{
return 1000.0 * tcpacket.rt_timer.time();
}
bool CTranscoderPacket::IsLast() const
{
return tcpacket.is_last;

@ -52,7 +52,6 @@ public:
ECodecType GetCodecIn() const;
uint16_t GetStreamId() const;
uint32_t GetSequence() const;
double GetTimeMS() const;
bool IsLast() const;
bool IsSecond() const;
bool DStarIsSet() const;

@ -1 +0,0 @@
../urfd/reflector/UnixDgramSocket.cpp

@ -1 +0,0 @@
../urfd/reflector/UnixDgramSocket.h

@ -2,10 +2,12 @@
#
# 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
Port = 10100
ServerAddress = 127.0.0.1
# VERY IMPORTANT: This need to be idential to the same line in [Transcder] section of the urfd ini file!
# This will either be a single module (for DVSI-3000), or up to three modules (for DVSI-3003).
Modules = A
# All gain values are in dB.
# Gain values are limited to -24 to +24. Typical values will usually be less.

Loading…
Cancel
Save

Powered by TurnKey Linux.