Compare commits
54 Commits
master
...
svxlink-us
| Author | SHA1 | Date |
|---|---|---|
|
|
5f75990cb0 | |
|
|
8b549d6024 | |
|
|
e56a8f116a | |
|
|
443976ea81 | |
|
|
03b2931719 | |
|
|
d78d881bfa | |
|
|
cafb011c83 | |
|
|
5689486629 | |
|
|
ffa09e46a4 | |
|
|
255a05a240 | |
|
|
da2731a81d | |
|
|
148c2f9a4c | |
|
|
abb72ab14f | |
|
|
04b43dce3c | |
|
|
202525860a | |
|
|
a4f59aacaa | |
|
|
98bd2ef35a | |
|
|
609896587a | |
|
|
eda0dd6dae | |
|
|
a3d92d68fb | |
|
|
083bb87e4b | |
|
|
02d609e590 | |
|
|
46140a033a | |
|
|
ca06c9b554 | |
|
|
4ba0fce47f | |
|
|
93a7a154a2 | |
|
|
97702098d7 | |
|
|
4f494974bd | |
|
|
b50982b56f | |
|
|
40d1e412a1 | |
|
|
2b1d538e21 | |
|
|
b151754e71 | |
|
|
c8f3808ce9 | |
|
|
9470642e6a | |
|
|
2191a9cd04 | |
|
|
98e8b6206d | |
|
|
9714a208e9 | |
|
|
ea067b6da5 | |
|
|
660e320ea0 | |
|
|
c54e349949 | |
|
|
503818acb5 | |
|
|
58d450e367 | |
|
|
1b06a7c5a4 | |
|
|
c13884713d | |
|
|
5d5a6faa66 | |
|
|
d493e2d5b4 | |
|
|
21b25fa9e7 | |
|
|
d09f71b3cf | |
|
|
bb72918ba5 | |
|
|
bf25eed183 | |
|
|
033e0c2296 | |
|
|
a697dc3c1a | |
|
|
55b3adeff4 | |
|
|
127208e547 |
|
|
@ -0,0 +1,211 @@
|
|||
Configuration svxlink-usrp with SvxReflector, Analoge_Bridge and MMDVM_Bridge
|
||||
=============================================================================
|
||||
|
||||
1.) Basic structure
|
||||
|
||||
|.-................. SvxLink ......................| |........... MMDVM stuff ..........| |.. freeDMR ..>
|
||||
|
||||
|----------| |---------------| |--------------| |-----------| |---------------| |--------------| |-------------|
|
||||
| Radio | | SimplexLogic | | LogicLinking | | UsrpLogic | | Analog_Bridge | | MMDVM_Bridge | | DMR-Network |
|
||||
| 2m/70cm |<->| RepeaterLogic |<->| |<->| |<->| |<->| |<->| |
|
||||
|----------| |---------------| |--------------| |-----------| |---------------| |--------------| |-------------|
|
||||
| |
|
||||
| |
|
||||
|----------| |---------------| |--------------| |---------------|
|
||||
| Analogue | | SvxReflector- | | Reflector- | | md380- |
|
||||
| Network |<->| Server |<->| Logic | | emulator |
|
||||
|----------| |---------------| |--------------| |---------------|
|
||||
|
||||
|
||||
2.) Configuration
|
||||
2.1.) svxLink.conf
|
||||
Usually located in /etc/svxlink/svxlink.conf
|
||||
|
||||
Below is an example configuration for the svxlink.conf file. Only the most important parameters and
|
||||
configuration sections are given
|
||||
|
||||
---
|
||||
[GLOBAL]
|
||||
LOGICS=RepeaterLogic,ReflectorLogic,UsrpLogic
|
||||
LINKS=UsrpLink
|
||||
|
||||
[RepeaterLogic]
|
||||
TYPE=Repeater
|
||||
RX=Rx1
|
||||
TX=Tx1
|
||||
MODULES=ModuleParrot,ModuleTclVoiceMail,ModulePropagationMonitor,ModuleMetarInfo
|
||||
CALLSIGN=DB0ABC
|
||||
SHORT_IDENT_INTERVAL=30
|
||||
LONG_IDENT_INTERVAL=60
|
||||
EVENT_HANDLER=/usr/share/svxlink/events.tcl
|
||||
DEFAULT_LANG=de_DE
|
||||
RGR_SOUND_DELAY=200
|
||||
TX_CTCSS=SQL_OPEN,ANNOUNCEMENT
|
||||
FX_GAIN_NORMAL=0
|
||||
FX_GAIN_LOW=-12
|
||||
#NO_REPEAT=1
|
||||
IDLE_TIMEOUT=6
|
||||
OPEN_ON_DTMF=*
|
||||
OPEN_ON_SQL=1000
|
||||
OPEN_SQL_FLANK=OPEN
|
||||
|
||||
[UsrpLogic]
|
||||
TYPE=Usrp
|
||||
USRP_HOST=127.0.0.1
|
||||
USRP_TX_PORT=44442 <-------------------------------------------------
|
||||
USRP_RX_PORT=44441 <---------------------------------------------- |
|
||||
CALL=DB0ABC | |
|
||||
DMRID=262123 | |
|
||||
RPTID= | |
|
||||
DEFAULT_CC=1 | |
|
||||
DEFAULT_TG=2629 | |
|
||||
DEFAULT_TS=2 | |
|
||||
#PREAMP=3 | |
|
||||
#FILTER_FROM_USRP=HsBq2/0.01/-18/4000 | |
|
||||
FILTER_TO_USRP=BpBu1/650-3800 | |
|
||||
#NET_PREAMP=3 | |
|
||||
JITTER_BUFFER_DELAY=100 | |
|
||||
EVENT_HANDLER=/usr/share/svxlink/events.tcl | |
|
||||
| |
|
||||
[ReflectorLogic] | |
|
||||
TYPE=Reflector | |
|
||||
HOST=10.8.0.1 ---------> to Analogue network, usually a | |
|
||||
PORT=5302 --------------> SvxReflector server | |
|
||||
AUTH_KEY="secret" | |
|
||||
CALLSIGN="DB0ABC | |
|
||||
#JITTER_BUFFER_DELAY=0 | |
|
||||
DEFAULT_TG=9 | |
|
||||
MONITOR_TGS=9 | |
|
||||
ANNOUNCE_REMOTE_MIN_INTERVAL=300 | |
|
||||
EVENT_HANDLER=/usr/share/svxlink/events.tcl | |
|
||||
NODE_INFO_FILE=/etc/svxlink/node_info.json | |
|
||||
MUTE_FIRST_TX_LOC=0 | |
|
||||
MUTE_FIRST_TX_REM=0 | |
|
||||
| |
|
||||
[UsrpLink] | |
|
||||
NAME=Usrp | |
|
||||
CONNECT_LOGICS=RepeaterLogic,UsrpLogic,ReflectorLogic | |
|
||||
DEFAULT_ACTIVE=1 | |
|
||||
---EOF | |
|
||||
| |
|
||||
| |
|
||||
2.2.) Analog_Bridge | |
|
||||
Here we have an example for the Analog_Bridge.ini, | |
|
||||
usually located in /opt/Analog_Bridge/Analog_Bridge.ini | |
|
||||
For details regarding the configuration paramaters | |
|
||||
refer to Analog_Bridge manual page. | |
|
||||
| |
|
||||
--- | |
|
||||
include = dvsm.macro | |
|
||||
| |
|
||||
[GENERAL] | |
|
||||
logLevel = 2 | |
|
||||
exportMetadata = true | |
|
||||
transferRootDir = /tmp | |
|
||||
subscriberFile = /var/lib/dvswitch/subscriber_ids.csv | |
|
||||
decoderFallBack = true | |
|
||||
useEmulator = false | |
|
||||
emulatorAddress = 127.0.0.1:2460 | |
|
||||
pcmPort = 2222 | |
|
||||
| |
|
||||
[USRP] | |
|
||||
address = 127.0.0.1 | |
|
||||
txPort = 44441 <-------------------------------------------------- |
|
||||
rxPort = 44442 <-----------------------------------------------------
|
||||
usrpAudio = AUDIO_UNITY
|
||||
usrpGain = 1.10
|
||||
usrpAGC = -20,10,100
|
||||
tlvAudio = AUDIO_UNITY
|
||||
tlvGain = 0.35
|
||||
|
||||
[AMBE_AUDIO]
|
||||
address = 127.0.0.1
|
||||
txPort = 31103 <-----------------------------------------------------
|
||||
rxPort = 31100 <-------------------------------------------------- |
|
||||
ambeMode = DMR | |
|
||||
minTxTimeMS = 2500 | |
|
||||
gatewayDmrId = 262123 | |
|
||||
repeaterID = 26200123 | |
|
||||
txTg = 2629 | |
|
||||
txTs = 2 | |
|
||||
colorCode = 1 | |
|
||||
| |
|
||||
[MACROS] | |
|
||||
| |
|
||||
[DV3000] | |
|
||||
;address = 127.0.0.1 -------------------> To | |
|
||||
;rxPort = 2460 -------------------------> AMBEServer | |
|
||||
| |
|
||||
address = /dev/ttyUSB4 -----------------> To | |
|
||||
baud = 460800 --------------------------> DV3k-stick | |
|
||||
serial = true | |
|
||||
---EOF | |
|
||||
| |
|
||||
| |
|
||||
2.3.) MMDVM_Bridge | |
|
||||
Usually located in /opt/MMDVM_Bridge/MMDVM_Bridge.ini | |
|
||||
Here is an example for the MMDVM_Bridge.ini | |
|
||||
Only the most important parameters and configuration | |
|
||||
sections are given | |
|
||||
| |
|
||||
--- | |
|
||||
[General] | |
|
||||
Callsign=DB0ABC | |
|
||||
Id=262123 | |
|
||||
Timeout=180 | |
|
||||
Duplex=1 | |
|
||||
| |
|
||||
[Info] | |
|
||||
RXFrequency=431025000 | |
|
||||
TXFrequency=438625000 | |
|
||||
Power=15 | |
|
||||
Latitude=51.000 | |
|
||||
Longitude=12.000 | |
|
||||
Height=123 | |
|
||||
Location=somewhere | |
|
||||
Description=FreeDMR_Germany | |
|
||||
URL=https://svxreflector.org | |
|
||||
| |
|
||||
[Log] | |
|
||||
DisplayLevel=1 | |
|
||||
FileLevel=2 | |
|
||||
FilePath=/var/log/mmdvm | |
|
||||
FileRoot=MMDVM_Bridge | |
|
||||
| |
|
||||
[DMR Id Lookup] | |
|
||||
File=/var/lib/mmdvm/DMRIds.dat | |
|
||||
Time=24 | |
|
||||
| |
|
||||
[DMR] | |
|
||||
Enable=1 | |
|
||||
ColorCode=1 | |
|
||||
EmbeddedLCOnly=1 | |
|
||||
DumpTAData=0 | |
|
||||
| |
|
||||
[DMR Network] | |
|
||||
Enable=1 | |
|
||||
Address=svxreflector.org ---------------> To DMR master server | |
|
||||
Port=62031 -----------------------------> e.g. freeDMR | |
|
||||
Jitter=360 | |
|
||||
Local=62032 | |
|
||||
Password=passw0rd | |
|
||||
Slot1=0 | |
|
||||
Slot2=1 | |
|
||||
Debug=1 | |
|
||||
---EOF | |
|
||||
| |
|
||||
| |
|
||||
2.4.) DV-Switch | |
|
||||
Uually located in /opt/MMDVM_Bridge/DVSwitch.ini | |
|
||||
Example of DVSwitch.ini | |
|
||||
| |
|
||||
--- | |
|
||||
[DMR] | |
|
||||
address = 127.0.0.1 | |
|
||||
txPort = 31100 <-------------------------------------------------- |
|
||||
rxPort = 31103 <-----------------------------------------------------
|
||||
slot = 2
|
||||
exportTG = 2629
|
||||
hangTimerInFrames = 0
|
||||
talkerAlias = %callsign %location %description
|
||||
---EOF
|
||||
|
|
@ -26,9 +26,11 @@ or "-devel".
|
|||
* *libgcrypt*: Cryptographic functions (Required)
|
||||
* *libasound*: Alsa sound system support (Recommended)
|
||||
* *libgsm*: GSM audio codec (Required)
|
||||
* *libjsoncpp*: For json file support (Required)
|
||||
* *libspeex*: The Speex audio codec (Optional)
|
||||
* *librtlsdr*: Support for RTL2832U DVB-T/SDR USB dongles (Optional)
|
||||
* *libqt*: Version 4. Framework for graphical applications (Optional)
|
||||
* *libgpiod*: For gpio support at RaspberryPi’s (Optional)
|
||||
|
||||
There also are some runtime dependencies which normally is needed to run a
|
||||
SvxLink system.
|
||||
|
|
@ -46,11 +48,14 @@ A common command that exist on many distributions is 'useradd'. The 'daemon'
|
|||
group usually exist but if not, look for the 'groupadd' command.
|
||||
|
||||
|
||||
== Build and install ==
|
||||
== Build and install SvxLink with Usrp extension ==
|
||||
SvxLink use the CMake build system. The basic pattern for building using CMake
|
||||
looks like this:
|
||||
|
||||
cd path/to/svxlink/src
|
||||
git clone https://github.com/dl1hrc/svxlink.git
|
||||
cd svxlink
|
||||
git checkout svxlink-usrp
|
||||
cd src
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
|
|
|
|||
|
|
@ -443,6 +443,72 @@ class MsgPacker<std::string>
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
// MsgPacker for std::array
|
||||
template <typename T, size_t N>
|
||||
class MsgPacker<std::array<T, N> >
|
||||
{
|
||||
public:
|
||||
static bool pack(std::ostream &os, const std::array<T, N> &vec) {
|
||||
for (typename std::array<T, N>::const_iterator it = vec.begin();
|
||||
it != vec.end(); ++it) {
|
||||
if (!MsgPacker<T>::pack(os, *it))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static size_t packedSize(const std::array<T, N> &vec) {
|
||||
size_t size = 0;
|
||||
for (typename std::array<T, N>::const_iterator it = vec.begin();
|
||||
it != vec.end(); ++it) {
|
||||
size += MsgPacker<T>::packedSize(*it);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
static bool unpack(std::istream &is, std::array<T, N> &vec) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (!MsgPacker<T>::unpack(is, vec[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// MsgPacker for static arrays
|
||||
template <typename T, std::size_t N> class MsgPacker<T[N]> {
|
||||
public:
|
||||
static bool pack(std::ostream &os, const T (&vec)[N]) {
|
||||
for (const T *it = std::begin(vec); it != std::end(vec); ++it) {
|
||||
if (!MsgPacker<T>::pack(os, *it))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static size_t packedSize(const T (&vec)[N]) {
|
||||
size_t size = 0;
|
||||
for (const T *it = std::begin(vec); it != std::end(vec); ++it) {
|
||||
size += MsgPacker<T>::packedSize(*it);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
static bool unpack(std::istream &is, T (&vec)[N]) {
|
||||
for (T *it = std::begin(vec); it != std::end(vec); ++it) {
|
||||
if (!MsgPacker<T>::unpack(is, *it))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename I>
|
||||
class MsgPacker<std::vector<I> >
|
||||
{
|
||||
|
|
@ -459,7 +525,10 @@ class MsgPacker<std::vector<I> >
|
|||
it != vec.end();
|
||||
++it)
|
||||
{
|
||||
MsgPacker<I>::pack(os, *it);
|
||||
if (!MsgPacker<I>::pack(os, *it))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -487,7 +556,10 @@ class MsgPacker<std::vector<I> >
|
|||
for (int i=0; i<vec_size; ++i)
|
||||
{
|
||||
I val;
|
||||
MsgPacker<I>::unpack(is, val);
|
||||
if (!MsgPacker<I>::unpack(is, val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
vec.push_back(val);
|
||||
}
|
||||
return true;
|
||||
|
|
@ -505,12 +577,18 @@ class MsgPacker<std::set<I> >
|
|||
{
|
||||
return false;
|
||||
}
|
||||
MsgPacker<uint16_t>::pack(os, s.size());
|
||||
if (!MsgPacker<uint16_t>::pack(os, s.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (typename std::set<I>::const_iterator it = s.begin();
|
||||
it != s.end();
|
||||
++it)
|
||||
{
|
||||
MsgPacker<I>::pack(os, *it);
|
||||
if (!MsgPacker<I>::pack(os, *it))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -527,7 +605,10 @@ class MsgPacker<std::set<I> >
|
|||
static bool unpack(std::istream& is, std::set<I>& s)
|
||||
{
|
||||
uint16_t set_size;
|
||||
MsgPacker<uint16_t>::unpack(is, set_size);
|
||||
if (!MsgPacker<uint16_t>::unpack(is, set_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (set_size > std::numeric_limits<uint16_t>::max())
|
||||
{
|
||||
return false;
|
||||
|
|
@ -537,7 +618,10 @@ class MsgPacker<std::set<I> >
|
|||
for (int i=0; i<set_size; ++i)
|
||||
{
|
||||
I val;
|
||||
MsgPacker<I>::unpack(is, val);
|
||||
if (!MsgPacker<I>::unpack(is, val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
s.insert(val);
|
||||
}
|
||||
return true;
|
||||
|
|
@ -846,6 +930,111 @@ class Msg
|
|||
unpack(is, v7) && unpack(is, v8) && unpack(is, v9) &&
|
||||
unpack(is, v10);
|
||||
}
|
||||
|
||||
// need by Usrp protocol (16 params)
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename T11, typename T12, typename T13, typename T14, typename T15,
|
||||
typename T16>
|
||||
bool pack(std::ostream& os, const T1& v1, const T2& v2, const T3& v3,
|
||||
const T4& v4, const T5& v5, const T6& v6, const T7& v7,
|
||||
const T8& v8, const T9& v9, const T10& v10,
|
||||
const T11& v11, const T12& v12, const T13& v13,
|
||||
const T14& v14, const T15& v15, const T16& v16) const
|
||||
{
|
||||
return pack(os, v1) && pack(os, v2) && pack(os, v3) && pack(os, v4) &&
|
||||
pack(os, v5) && pack(os, v6) && pack(os, v7) && pack(os, v8) &&
|
||||
pack(os, v9) && pack(os, v10) && pack(os, v11) && pack(os, v12) &&
|
||||
pack(os, v13) && pack(os, v14) && pack(os, v15) && pack(os, v16);
|
||||
}
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename T11, typename T12, typename T13, typename T14, typename T15,
|
||||
typename T16>
|
||||
|
||||
size_t packedSize(const T1& v1, const T2& v2, const T3& v3,
|
||||
const T4& v4, const T5& v5, const T6& v6,
|
||||
const T7& v7, const T8& v8, const T9& v9,
|
||||
const T10& v10, const T11& v11, const T12& v12,
|
||||
const T13& v13, const T14& v14, const T15& v15,
|
||||
const T16& v16) const
|
||||
{
|
||||
return packedSize(v1) + packedSize(v2) + packedSize(v3) + packedSize(v4) +
|
||||
packedSize(v5) + packedSize(v6) + packedSize(v7) + packedSize(v8) +
|
||||
packedSize(v9) + packedSize(v10) + packedSize(v11) + packedSize(v12) +
|
||||
packedSize(v13) + packedSize(v14) + packedSize(v15) + packedSize(v16);
|
||||
}
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename T11, typename T12, typename T13, typename T14, typename T15,
|
||||
typename T16>
|
||||
|
||||
bool unpack(std::istream& is, T1& v1, T2& v2, T3& v3, T4& v4, T5& v5,
|
||||
T6& v6, T7& v7, T8& v8, T9& v9, T10& v10, T11& v11, T12& v12,
|
||||
T13& v13, T14& v14, T15& v15, T16& v16)
|
||||
{
|
||||
return unpack(is, v1) && unpack(is, v2) && unpack(is, v3) &&
|
||||
unpack(is, v4) && unpack(is, v5) && unpack(is, v6) &&
|
||||
unpack(is, v7) && unpack(is, v8) && unpack(is, v9) &&
|
||||
unpack(is, v10) && unpack(is, v11) && unpack(is, v12) &&
|
||||
unpack(is, v13) && unpack(is, v14) && unpack(is, v15) &&
|
||||
unpack(is, v16);
|
||||
}
|
||||
|
||||
|
||||
// need by Usrp protocol (17 params)
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename T11, typename T12, typename T13, typename T14, typename T15,
|
||||
typename T16, typename T17>
|
||||
bool pack(std::ostream& os, const T1& v1, const T2& v2, const T3& v3,
|
||||
const T4& v4, const T5& v5, const T6& v6, const T7& v7,
|
||||
const T8& v8, const T9& v9, const T10& v10,
|
||||
const T11& v11, const T12& v12, const T13& v13,
|
||||
const T14& v14, const T15& v15, const T16& v16,
|
||||
const T17& v17) const
|
||||
{
|
||||
return pack(os, v1) && pack(os, v2) && pack(os, v3) && pack(os, v4) &&
|
||||
pack(os, v5) && pack(os, v6) && pack(os, v7) && pack(os, v8) &&
|
||||
pack(os, v9) && pack(os, v10) && pack(os, v11) && pack(os, v12) &&
|
||||
pack(os, v13) && pack(os, v14) && pack(os, v15) && pack(os, v16) &&
|
||||
pack(os, v17);
|
||||
}
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename T11, typename T12, typename T13, typename T14, typename T15,
|
||||
typename T16, typename T17>
|
||||
|
||||
size_t packedSize(const T1& v1, const T2& v2, const T3& v3,
|
||||
const T4& v4, const T5& v5, const T6& v6,
|
||||
const T7& v7, const T8& v8, const T9& v9,
|
||||
const T10& v10, const T11& v11, const T12& v12,
|
||||
const T13& v13, const T14& v14, const T15& v15,
|
||||
const T16& v16, const T17& v17) const
|
||||
{
|
||||
return packedSize(v1) + packedSize(v2) + packedSize(v3) + packedSize(v4) +
|
||||
packedSize(v5) + packedSize(v6) + packedSize(v7) + packedSize(v8) +
|
||||
packedSize(v9) + packedSize(v10) + packedSize(v11) + packedSize(v12) +
|
||||
packedSize(v13) + packedSize(v14) + packedSize(v15) + packedSize(v16) +
|
||||
packedSize(v17);
|
||||
}
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||
typename T11, typename T12, typename T13, typename T14, typename T15,
|
||||
typename T16, typename T17>
|
||||
|
||||
bool unpack(std::istream& is, T1& v1, T2& v2, T3& v3, T4& v4, T5& v5,
|
||||
T6& v6, T7& v7, T8& v8, T9& v9, T10& v10, T11& v11, T12& v12,
|
||||
T13& v13, T14& v14, T15& v15, T16& v16, T17& v17)
|
||||
{
|
||||
return unpack(is, v1) && unpack(is, v2) && unpack(is, v3) &&
|
||||
unpack(is, v4) && unpack(is, v5) && unpack(is, v6) &&
|
||||
unpack(is, v7) && unpack(is, v8) && unpack(is, v9) &&
|
||||
unpack(is, v10) && unpack(is, v11) && unpack(is, v12) &&
|
||||
unpack(is, v13) && unpack(is, v14) && unpack(is, v15) &&
|
||||
unpack(is, v16) && unpack(is, v17);
|
||||
}
|
||||
|
||||
}; /* class Msg */
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -760,6 +760,66 @@ variables as documented for networked receivers and transmitters. For example,
|
|||
to lighten the encoder CPU load for the Opus encoder, set OPUS_ENC_COMPLEXITY
|
||||
to something lower than 9.
|
||||
.
|
||||
.SS UsrpLogic
|
||||
.
|
||||
The UsprLogic is used to connect SvxLink with a digital Hamradio network. The
|
||||
communication is based on Udp transmission. Some Hamradio applications
|
||||
are able to accept the protocol (Asterisk, MMDVM).
|
||||
.TP
|
||||
.B TYPE
|
||||
The type for a Usrp Logic is always "Usrp"
|
||||
.TP
|
||||
.B CALL
|
||||
Specify the callsign used at this logic.
|
||||
.TP
|
||||
.B USRP_HOST
|
||||
The hostname of the Usrp server.
|
||||
.TP
|
||||
.B USRP_TX_PORT
|
||||
The Udp port used to send Udp messages to Usrp server
|
||||
.TP
|
||||
.B USRP_RX_PORT
|
||||
The Udp port used to receive Udp messages from the Usrp server.
|
||||
.TP
|
||||
.B DMRID
|
||||
The DMR-ID ssigned to the callsign.
|
||||
.TP
|
||||
.B RPTID
|
||||
The repeater ID. It's not used in most cases.
|
||||
.TP
|
||||
.B DEFAULT_TG
|
||||
The talk group. Default is 0
|
||||
.TP
|
||||
.B DEFAULT_CC
|
||||
The color code. It's not used in most cases, default is 1
|
||||
.TP
|
||||
.B DEFAULT_TS
|
||||
The time slot. It's not used in most cases, default is 1
|
||||
.TP
|
||||
.B PREAMP
|
||||
The signal (internal audio from connected logic) will be amplified/reduced
|
||||
by the specified number of dB. This processed audio is sent to the usrp server.
|
||||
.TP
|
||||
.B NET_PREAMP
|
||||
The signal received from Usrp network will be amplified/reduced by the
|
||||
number of dB. This processed audio is sent to connected logics.
|
||||
.TP
|
||||
.B NET_LIMITER_THRESH
|
||||
Set the threshold, in dBFS, for the audio limiter from the net site. The audio
|
||||
limiter really is a compressor with a very steep compression ratio like 10:1.
|
||||
The limiter is used to help keeping down the audio level to not overmodulate
|
||||
the local connected logics.A good value
|
||||
to start with for this parameter is then -6 but try to lower it further (like
|
||||
-9 to -12) if it does not affect audio quality negatively.
|
||||
.TP
|
||||
.B LOCAL_LIMITER_THRESH
|
||||
Set the threshold, in dBFS, for the audio limiter from the local site to Usrp
|
||||
network. The audio limiter really is a compressor with a very steep compression
|
||||
ratio like 10:1. The limiter is used to help keeping down the audio level to
|
||||
not overmodulate the remote connected usrp nodes. A good value
|
||||
to start with for this parameter is then -6 but try to lower it further (like
|
||||
-9 to -12) if it does not affect audio quality negatively.
|
||||
.
|
||||
.SS QSO Recorder Section
|
||||
.
|
||||
The QSO recorder is used to record all received audio to files on disk. All
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
|
||||
#include <cassert>
|
||||
#include <json/json.h>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -123,7 +125,8 @@ namespace {
|
|||
|
||||
Reflector::Reflector(void)
|
||||
: m_srv(0), m_udp_sock(0), m_tg_for_v1_clients(1), m_random_qsy_lo(0),
|
||||
m_random_qsy_hi(0), m_random_qsy_tg(0), m_http_server(0)
|
||||
m_random_qsy_hi(0), m_random_qsy_tg(0), m_http_server(0),
|
||||
cfg_filename("/tmp/userinfo.json"), debug(false)
|
||||
{
|
||||
TGHandler::instance()->talkerUpdated.connect(
|
||||
mem_fun(*this, &Reflector::onTalkerUpdated));
|
||||
|
|
@ -226,10 +229,87 @@ bool Reflector::initialize(Async::Config &cfg)
|
|||
sigc::mem_fun(*this, &Reflector::httpClientDisconnected));
|
||||
}
|
||||
|
||||
m_cfg->getValue("GLOBAL", "DEBUG", debug);
|
||||
|
||||
if (!m_cfg->getValue("GLOBAL", "USERFILE", cfg_filename))
|
||||
{
|
||||
cfg_filename = "/tmp/svxreflector_userdata-";
|
||||
cfg_filename += to_string(udp_listen_port);
|
||||
cfg_filename += ".json";
|
||||
}
|
||||
|
||||
// reads the user data from json file
|
||||
if (!getUserData())
|
||||
{
|
||||
cout << "*** Can not read user data from json file: " << cfg_filename << endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
} /* Reflector::initialize */
|
||||
|
||||
|
||||
void Reflector::updateUserdata(Json::Value user_arr)
|
||||
{
|
||||
User m_user;
|
||||
for (Json::Value::ArrayIndex i = 0; i != user_arr.size(); i++)
|
||||
{
|
||||
Json::Value& t_userdata = user_arr[i];
|
||||
m_user.id = t_userdata.get("id", "").asString();
|
||||
m_user.name = t_userdata.get("name","").asString();
|
||||
m_user.mode = t_userdata.get("mode","").asString();
|
||||
m_user.call = t_userdata.get("call","").asString();
|
||||
m_user.location = t_userdata.get("location","").asString();
|
||||
m_user.aprs_sym = static_cast<char>(t_userdata.get("sym","").asInt());
|
||||
m_user.aprs_tab = static_cast<char>(t_userdata.get("tab","").asInt());
|
||||
m_user.comment = t_userdata.get("comment","").asString();
|
||||
if (t_userdata.get("last_activity","").asUInt() > 0)
|
||||
{
|
||||
m_user.last_activity = (time_t) t_userdata.get("last_activity","").asUInt();
|
||||
}
|
||||
|
||||
std::map<std::string, User>::iterator iu;
|
||||
iu = userdata.find(m_user.id);
|
||||
if (iu != userdata.end())
|
||||
{
|
||||
iu->second.name= m_user.name;
|
||||
iu->second.mode= m_user.mode;
|
||||
iu->second.aprs_sym = m_user.aprs_sym;
|
||||
iu->second.aprs_tab = m_user.aprs_tab;
|
||||
iu->second.comment = m_user.comment;
|
||||
iu->second.location = m_user.location;
|
||||
if (m_user.last_activity)
|
||||
{
|
||||
iu->second.last_activity = m_user.last_activity;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
{
|
||||
cout << "UPDATE: call=" << m_user.call << ", id=" << m_user.id
|
||||
<< ", name=" << m_user.name << ", location=" << m_user.location
|
||||
<< " (" << m_user.comment << ")" << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userdata.insert(std::pair<std::string, User>(m_user.id, m_user));
|
||||
if (debug)
|
||||
{
|
||||
cout << "New user: call=" << m_user.call << ", id=" << m_user.id
|
||||
<< ", name=" << m_user.name << ", location=" << m_user.location
|
||||
<< " (" << m_user.comment << ")" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
writeUserData(userdata);
|
||||
} /* Reflector::updateUserdata */
|
||||
|
||||
|
||||
void Reflector::updateQsostate(Json::Value eventmessage)
|
||||
{
|
||||
cout << jsonToString(eventmessage) << endl;
|
||||
} /* Reflector::updateQsostate */
|
||||
|
||||
|
||||
void Reflector::nodeList(std::vector<std::string>& nodes) const
|
||||
{
|
||||
nodes.clear();
|
||||
|
|
@ -783,6 +863,98 @@ uint32_t Reflector::nextRandomQsyTg(void)
|
|||
} /* Reflector::nextRandomQsyTg */
|
||||
|
||||
|
||||
bool Reflector::getUserData(void)
|
||||
{
|
||||
// loading user info
|
||||
Json::Value cfg_root;
|
||||
std::ifstream cfgfile(cfg_filename);
|
||||
if (!cfgfile.is_open())
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
cout << "+++ WARNING: Can not open " << cfg_filename << endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
cfgfile >> cfg_root;
|
||||
cfgfile.close();
|
||||
if (cfg_root.size() < 1)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
cout << "+++ WARNING: File (" << cfg_filename << ") contains no userdata"
|
||||
<< endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
for (Json::Value::ArrayIndex i = 0; i != cfg_root.size(); i++)
|
||||
{
|
||||
User m_user;
|
||||
Json::Value& t_userdata = cfg_root[i];
|
||||
m_user.id = t_userdata.get("id", "").asString();
|
||||
m_user.mode = t_userdata.get("mode","").asString();
|
||||
m_user.name = t_userdata.get("name","").asString();
|
||||
m_user.call = t_userdata.get("call","").asString();
|
||||
m_user.location = t_userdata.get("location","").asString();
|
||||
m_user.aprs_sym = static_cast<char>(t_userdata.get("sym","").asInt());
|
||||
m_user.aprs_tab = static_cast<char>(t_userdata.get("tab","").asInt());
|
||||
m_user.comment = t_userdata.get("comment","").asString();
|
||||
m_user.last_activity = (time_t) t_userdata.get("last_activity","").asUInt();
|
||||
userdata[m_user.id] = m_user;
|
||||
}
|
||||
cout << "+++ " << cfg_root.size() << " users loaded from '"
|
||||
<< cfg_filename << "'" << endl;
|
||||
|
||||
return true;
|
||||
} /* Reflector::getUserData */
|
||||
|
||||
|
||||
void Reflector::writeUserData(std::map<std::string, User> userdata)
|
||||
{
|
||||
Json::Value event(Json::arrayValue);
|
||||
std::map<std::string, User>::iterator iu;
|
||||
|
||||
for (iu = userdata.begin(); iu!=userdata.end(); iu++)
|
||||
{
|
||||
Json::Value t_userinfo(Json::objectValue);
|
||||
t_userinfo["id"] = iu->second.id;
|
||||
t_userinfo["call"] = iu->second.call;
|
||||
t_userinfo["mode"] = iu->second.mode;
|
||||
t_userinfo["name"] = iu->second.name;
|
||||
t_userinfo["location"] = iu->second.location;
|
||||
t_userinfo["sym"] = iu->second.aprs_sym;
|
||||
t_userinfo["tab"] = iu->second.aprs_tab;
|
||||
t_userinfo["comment"] = iu->second.comment;
|
||||
t_userinfo["last_activity"] = static_cast<uint32_t>(iu->second.last_activity);
|
||||
event.append(t_userinfo);
|
||||
}
|
||||
|
||||
// sending own Dv user information to the svxreflector network
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["commentStyle"] = "None";
|
||||
builder["indentation"] = ""; //The JSON document is written on a single line
|
||||
Json::StreamWriter* writer = builder.newStreamWriter();
|
||||
std::ofstream outputFileStream(cfg_filename);
|
||||
std::stringstream os;
|
||||
writer->write(event, &os);
|
||||
writer->write(event, &outputFileStream);
|
||||
delete writer;
|
||||
// send user info to client nodes
|
||||
broadcastMsg(MsgStateEvent("Reflector","DvUsers:info",
|
||||
os.str()), v1_client_filter);
|
||||
cout << jsonToString(event) << endl;
|
||||
} /* Reflector::writeUserData */
|
||||
|
||||
|
||||
string Reflector::jsonToString(Json::Value eventmessage)
|
||||
{
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
std::string message = Json::writeString(builder, eventmessage);
|
||||
return message;
|
||||
} /* Reflector::jsonToString */
|
||||
|
||||
|
||||
/*
|
||||
* This file has not been truncated
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
#include <sys/time.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <json/json.h>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -181,6 +182,18 @@ class Reflector : public sigc::trackable
|
|||
*/
|
||||
void requestQsy(ReflectorClient *client, uint32_t tg);
|
||||
|
||||
/**
|
||||
* @brief Updates the user information
|
||||
* @param user array with all data of the useres
|
||||
*/
|
||||
void updateUserdata(Json::Value eventmessage);
|
||||
|
||||
/**
|
||||
* @brief Update Qso information
|
||||
* @param ToDo
|
||||
*/
|
||||
void updateQsostate(Json::Value eventmessage);
|
||||
|
||||
private:
|
||||
typedef std::map<Async::FramedTcpConnection*,
|
||||
ReflectorClient*> ReflectorClientConMap;
|
||||
|
|
@ -195,7 +208,29 @@ class Reflector : public sigc::trackable
|
|||
uint32_t m_random_qsy_hi;
|
||||
uint32_t m_random_qsy_tg;
|
||||
Async::TcpServer<Async::HttpServerConnection>* m_http_server;
|
||||
std::string cfg_filename;
|
||||
bool debug;
|
||||
|
||||
|
||||
// contain user data
|
||||
struct User {
|
||||
std::string id;
|
||||
std::string call;
|
||||
std::string mode;
|
||||
std::string name;
|
||||
std::string location;
|
||||
std::string comment;
|
||||
float lat;
|
||||
float lon;
|
||||
std::string state;
|
||||
short reasonforsending;
|
||||
char aprs_sym;
|
||||
char aprs_tab;
|
||||
time_t last_activity;
|
||||
time_t sent_last_sds;
|
||||
};
|
||||
std::map<std::string, User> userdata;
|
||||
|
||||
Reflector(const Reflector&);
|
||||
Reflector& operator=(const Reflector&);
|
||||
void clientConnected(Async::FramedTcpConnection *con);
|
||||
|
|
@ -212,7 +247,10 @@ class Reflector : public sigc::trackable
|
|||
Async::HttpServerConnection::DisconnectReason reason);
|
||||
void onRequestAutoQsy(uint32_t from_tg);
|
||||
uint32_t nextRandomQsyTg(void);
|
||||
|
||||
bool getUserData(void);
|
||||
void writeUserData(std::map<std::string, User> userdata);
|
||||
std::string jsonToString(Json::Value eventmessage);
|
||||
|
||||
}; /* class Reflector */
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <iterator>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -687,11 +689,33 @@ void ReflectorClient::handleStateEvent(std::istream& is)
|
|||
sendError("Illegal MsgStateEvent protocol message received");
|
||||
return;
|
||||
}
|
||||
cout << "### ReflectorClient::handleStateEvent:"
|
||||
<< " src=" << msg.src()
|
||||
<< " name=" << msg.name()
|
||||
<< " msg=" << msg.msg()
|
||||
<< std::endl;
|
||||
//cout << "### ReflectorClient::handleStateEvent:"
|
||||
// << " src=" << msg.src()
|
||||
// << " name=" << msg.name()
|
||||
// << " msg=" << msg.msg()
|
||||
// << std::endl;
|
||||
|
||||
Json::Value eventmessage;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
std::unique_ptr<Json::CharReader> const reader(rbuilder.newCharReader());
|
||||
std::string jsonReaderError;
|
||||
|
||||
if (!reader->parse(msg.msg().c_str(), msg.msg().c_str() + msg.msg().size(),
|
||||
&eventmessage, &jsonReaderError))
|
||||
{
|
||||
cout << "*** Error: parsing StateEvent message ("
|
||||
<< jsonReaderError << ")" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.name() == "DvUsers:info")
|
||||
{
|
||||
m_reflector->updateUserdata(eventmessage);
|
||||
}
|
||||
else if (msg.name() == "QsoInfo:state")
|
||||
{
|
||||
m_reflector->updateQsostate(eventmessage);
|
||||
}
|
||||
} /* ReflectorClient::handleStateEvent */
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gpio.conf.in
|
|||
${CMAKE_CURRENT_BINARY_DIR}/gpio.conf
|
||||
@ONLY
|
||||
)
|
||||
install_if_not_exists(node_info.json ${SVX_SYSCONF_INSTALL_DIR})
|
||||
install_if_not_exists(dv_users.json ${SVX_SYSCONF_INSTALL_DIR})
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/svxlink_gpio_up.in
|
||||
${RUNTIME_OUTPUT_DIRECTORY}/svxlink_gpio_up
|
||||
@ONLY)
|
||||
|
|
|
|||
|
|
@ -719,6 +719,24 @@ void ReflectorLogic::remoteReceivedPublishStateEvent(
|
|||
}
|
||||
sendMsg(msg);
|
||||
}
|
||||
else if (event_name == "QsoInfo:state")
|
||||
{
|
||||
std::istringstream is(data);
|
||||
Json::Value user_info;
|
||||
is >> user_info;
|
||||
user_info["TG"] = m_selected_tg;
|
||||
string ud =jsonToString(user_info);
|
||||
|
||||
// cout << "sende: " << event_name << "," << ud << endl;
|
||||
MsgStateEvent msg(logic->name(), event_name, ud);
|
||||
sendMsg(msg);
|
||||
}
|
||||
else if (event_name == "Sds:info" || event_name == "DvUsers:info")
|
||||
{
|
||||
// cout << "sende: " << event_name << "," << data << endl;
|
||||
MsgStateEvent msg(logic->name(), event_name, data);
|
||||
sendMsg(msg);
|
||||
}
|
||||
} /* ReflectorLogic::remoteReceivedPublishStateEvent */
|
||||
|
||||
|
||||
|
|
@ -1847,6 +1865,18 @@ void ReflectorLogic::handlePlayDtmf(const std::string& digit, int amp,
|
|||
} /* ReflectorLogic::handlePlayDtmf */
|
||||
|
||||
|
||||
string ReflectorLogic::jsonToString(Json::Value eventmessage)
|
||||
{
|
||||
Json::StreamWriterBuilder builder;
|
||||
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
|
||||
std::ostringstream ostream;
|
||||
writer->write(eventmessage, &ostream);
|
||||
std::string message = ostream.str();
|
||||
message.erase(std::remove_if(message.begin(), message.end(),
|
||||
[](unsigned char x){return std::iscntrl(x);}));
|
||||
return message;
|
||||
} /* ReflectorLogic::jsonToString */
|
||||
|
||||
/*
|
||||
* This file has not been truncated
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ class ReflectorLogic : public LogicBase
|
|||
void handlePlaySilence(int duration);
|
||||
void handlePlayTone(int fq, int amp, int duration);
|
||||
void handlePlayDtmf(const std::string& digit, int amp, int duration);
|
||||
|
||||
std::string jsonToString(Json::Value eventmessage);
|
||||
}; /* class ReflectorLogic */
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,5 +12,7 @@ macro(add_contrib name subdir comment)
|
|||
endif(${WITH_CONTRIB_${name}})
|
||||
endmacro(add_contrib)
|
||||
|
||||
add_contrib(USRP_LOGIC UsrpLogic
|
||||
"Set to ON to build and install contributed logic core UsrpLogic")
|
||||
add_contrib(SIP_LOGIC SipLogic
|
||||
"Set to ON to build and install contributed logic core SipLogic")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
message(
|
||||
"-- The installing EXPERIMENTAL UsrpLogic extension"
|
||||
)
|
||||
|
||||
add_manual_pages(UsrpLogic.conf.5)
|
||||
|
||||
# Build the plugin
|
||||
add_library(UsrpLogic MODULE UsrpLogic.cpp)
|
||||
set_target_properties(UsrpLogic PROPERTIES PREFIX "")
|
||||
set_property(TARGET UsrpLogic PROPERTY NO_SONAME 1)
|
||||
target_link_libraries(UsrpLogic ${LIBS})
|
||||
|
||||
# Install plugin, TCL event handler and config file
|
||||
install(TARGETS UsrpLogic DESTINATION ${SVX_LOGIC_CORE_INSTALL_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/UsrpLogic.tcl
|
||||
DESTINATION ${SVX_SHARE_INSTALL_DIR}/events.d
|
||||
)
|
||||
install_if_not_exists(${CMAKE_CURRENT_SOURCE_DIR}/dv_users.json
|
||||
${SVX_SYSCONF_INSTALL_DIR}
|
||||
)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/UsrpLogic.conf.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/UsrpLogic.conf
|
||||
@ONLY
|
||||
)
|
||||
install_if_not_exists(${CMAKE_CURRENT_BINARY_DIR}/UsrpLogic.conf
|
||||
${SVX_SYSCONF_INSTALL_DIR}/svxlink.d
|
||||
)
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
Contrib: UsrpLogic
|
||||
==================
|
||||
|
||||
----
|
||||
#
|
||||
# UsrpLogic (beta)
|
||||
# Adi Bier / DL1HRC (dl1hrc [at] gmx.de)
|
||||
#
|
||||
----
|
||||
== UsrpLogic ==
|
||||
Provides a Logic to connect to MMDVM services with USRP protocol.
|
||||
|
||||
This branch only works if you have the following things:
|
||||
|
||||
- DV3000 USB-stick (hardware transcoder) or
|
||||
- md380-emu service
|
||||
|
||||
Here are quick-and-dirty installation/update documentation, keep in
|
||||
mind: No configuration will be done!
|
||||
|
||||
The configuration of the Usrp part has been moved to this new file,
|
||||
normally found in /etc/svxlink/svxlink.d
|
||||
|
||||
== First time installation ==
|
||||
including German(!) voice announcements.
|
||||
|
||||
You may use my semi-automatic installation-script from here
|
||||
```
|
||||
http://svxlink.ham-radio-op.net/svxlink/svxlink-usrp-contrib.sh
|
||||
```
|
||||
|
||||
Start installation as root:
|
||||
```
|
||||
wget -O - http://svxlink.ham-radio-op.net/svxlink/svxlink-usrp-contrib.sh | bash
|
||||
```
|
||||
|
||||
== Installation of SvxLink with UsrpLogic extension ==
|
||||
|
||||
```
|
||||
git clone https://github.com/dl1hrc/svxlink.git
|
||||
cd svxlink/src
|
||||
git checkout svxlink-usrp
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DUSE_QT=OFF -DCMAKE_INSTALL_PREFIX=/usr -DSYSCONF_INSTALL_DIR=/etc -DLOCAL_STATE_DIR=/var -DCMAKE_BUILD_TYPE=Release -DWITH_CONTRIB_USRP_LOGIC=ON ..
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
Please not that the cmake-parameter "-DWITH_CONTRIB_USRP_LOGIC=ON" enables the
|
||||
UsrpLogic extension. Without this it will not work.
|
||||
|
||||
== Update an existing usrp-contrib ==
|
||||
```
|
||||
systemctl stop svxlink
|
||||
cd /home/svxlink/svxlink/src/build/
|
||||
make clean
|
||||
git pull
|
||||
sudo make install
|
||||
sudo systemctl restart svxlink
|
||||
```
|
||||
|
||||
The new configuration file UsrpLogic.conf contains the [UsrpLogic]-section
|
||||
from svxlink.conf. This file is now located in /etc/svxlink/svxlink.d directory.
|
||||
Remove the [UsrpLogic]-section from svxlink.conf but let the LOGICS- and LINKS
|
||||
definitions with "Usrp" untouched.
|
||||
|
||||
== Documentation ==
|
||||
- manpage svxlink.conf.5
|
||||
- German pdf is available here: https://github.com/dl1hrc/documentation
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
[UsrpLogic]
|
||||
TYPE=Usrp
|
||||
USRP_HOST=127.0.0.1
|
||||
USRP_TX_PORT=41234
|
||||
USRP_RX_PORT=41233
|
||||
CALL=N0CALL
|
||||
#DMRID=1234567
|
||||
#PREAMP=1
|
||||
#NET_PREAMP=0
|
||||
#FILTER_TO_USRP=BpBu1/600-3500
|
||||
#FILTER_FROM_USRP=HsBq2/0.01/-18/4000
|
||||
DV_USER_INFOFILE=/etc/svxlink/dv_users.json
|
||||
#DEBUG=1
|
||||
EVENT_HANDLER=@SVX_SHARE_INSTALL_DIR@/events.tcl
|
||||
JITTER_BUFFER_DELAY=400
|
||||
NET_LIMITER_THRESH=-1.0
|
||||
LOCAL_LIMITER_THRESH=-1.0
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,268 @@
|
|||
/**
|
||||
@file UsrpLogic.h
|
||||
@brief A logic core that connect to the SvxUsrp
|
||||
@author Tobias Blomberg / SM0SVX & Adi Bier / DL1HRC
|
||||
@date 2021-04-24
|
||||
|
||||
\verbatim
|
||||
SvxLink - A Multi Purpose Voice Services System for Ham Radio Use
|
||||
Copyright (C) 2003-2021 Tobias Blomberg / SM0SVX
|
||||
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
\endverbatim
|
||||
*/
|
||||
|
||||
#ifndef USRP_LOGIC_INCLUDED
|
||||
#define USRP_LOGIC_INCLUDED
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* System Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Project Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <AsyncAudioDecoder.h>
|
||||
#include <AsyncAudioEncoder.h>
|
||||
#include <AsyncTimer.h>
|
||||
#include <AsyncAudioFifo.h>
|
||||
#include <AsyncAudioStreamStateDetector.h>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include "LogicBase.h"
|
||||
#include "UsrpMsg.h"
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Forward declarations
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
namespace Async
|
||||
{
|
||||
class UdpSocket;
|
||||
class AudioValve;
|
||||
};
|
||||
class EventHandler;
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Forward declarations of classes inside of the declared namespace
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Defines & typedefs
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Exported Global Variables
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Class definitions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
@brief A logic core that connect to the SvxUsrp
|
||||
@author Tobias Blomberg / SM0SVX
|
||||
@date 2021-04-24
|
||||
*/
|
||||
class UsrpLogic : public LogicBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param cfg A previously initialized configuration object
|
||||
* @param name The name of the logic core
|
||||
*/
|
||||
UsrpLogic(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize the logic core
|
||||
* @return Returns \em true on success or \em false on failure
|
||||
*/
|
||||
virtual bool initialize(Async::Config &cfgobj,
|
||||
const std::string &logic_name) override;
|
||||
|
||||
/**
|
||||
* @brief Get the audio pipe sink used for writing audio into this logic
|
||||
* @return Returns an audio pipe sink object
|
||||
*/
|
||||
virtual Async::AudioSink *logicConIn(void) { return m_logic_con_in; }
|
||||
|
||||
/**
|
||||
* @brief Get the audio pipe source used for reading audio from this logic
|
||||
* @return Returns an audio pipe source object
|
||||
*/
|
||||
virtual Async::AudioSource *logicConOut(void) { return m_logic_con_out; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~UsrpLogic(void) override {};
|
||||
|
||||
|
||||
private:
|
||||
struct MonitorTgEntry
|
||||
{
|
||||
uint32_t tg;
|
||||
uint8_t prio;
|
||||
mutable int timeout;
|
||||
MonitorTgEntry(uint32_t tg=0) : tg(tg), prio(0), timeout(0) {}
|
||||
bool operator<(const MonitorTgEntry& mte) const { return tg < mte.tg; }
|
||||
bool operator==(const MonitorTgEntry& mte) const { return tg == mte.tg; }
|
||||
operator uint32_t(void) const { return tg; }
|
||||
};
|
||||
|
||||
typedef std::set<MonitorTgEntry> MonitorTgsSet;
|
||||
|
||||
// contain user data
|
||||
struct User {
|
||||
std::string id;
|
||||
std::string mode;
|
||||
std::string call;
|
||||
std::string name;
|
||||
std::string comment;
|
||||
std::string location;
|
||||
float lat;
|
||||
float lon;
|
||||
std::string state;
|
||||
short reasonforsending;
|
||||
char aprs_sym;
|
||||
char aprs_tab;
|
||||
time_t last_activity = 0;
|
||||
time_t sent_last_sds = 0;
|
||||
};
|
||||
std::map<std::string, User> userdata;
|
||||
|
||||
std::string m_usrp_host;
|
||||
uint16_t m_usrp_port;
|
||||
uint16_t m_usrp_rx_port;
|
||||
Async::UdpSocket* m_udp_rxsock;
|
||||
Async::AudioStreamStateDetector* m_logic_con_in;
|
||||
Async::AudioStreamStateDetector* m_logic_con_out;
|
||||
Async::AudioDecoder* m_dec;
|
||||
Async::Timer m_flush_timeout_timer;
|
||||
struct timeval m_last_talker_timestamp;
|
||||
Async::AudioEncoder* m_enc;
|
||||
unsigned m_tg_select_timeout;
|
||||
uint32_t m_selected_tg;
|
||||
Async::Timer m_report_tg_timer;
|
||||
MonitorTgsSet m_monitor_tgs;
|
||||
Async::AudioSource* m_enc_endpoint;
|
||||
// Async::Timer m_tmp_monitor_timer;
|
||||
int udp_seq;
|
||||
// Async::Timer m_qsy_pending_timer;
|
||||
int stored_samples;
|
||||
int16_t *r_buf;
|
||||
std::string m_callsign;
|
||||
bool ident;
|
||||
uint32_t m_dmrid;
|
||||
uint32_t m_rptid;
|
||||
uint8_t m_selected_cc;
|
||||
uint8_t m_selected_ts;
|
||||
float preamp_gain;
|
||||
float net_preamp_gain;
|
||||
EventHandler* m_event_handler;
|
||||
uint32_t m_last_tg;
|
||||
std::string m_last_call;
|
||||
uint32_t m_last_dmrid;
|
||||
short debug;
|
||||
Json::Value m_user_info;
|
||||
bool share_userinfo;
|
||||
Async::Timer m_delay_timer;
|
||||
uint8_t m_last_ts;
|
||||
uint8_t m_last_cc;
|
||||
std::string m_last_mode;
|
||||
|
||||
UsrpLogic(const UsrpLogic&);
|
||||
UsrpLogic& operator=(const UsrpLogic&);
|
||||
void handleMsgError(std::istream& is);
|
||||
void sendEncodedAudio(const void *buf, int count);
|
||||
void flushEncodedAudio(void);
|
||||
void udpDatagramReceived(const Async::IpAddress& addr, uint16_t port,
|
||||
void *buf, int count);
|
||||
void handleStreamStop(void);
|
||||
void handleVoiceStream(UsrpAudioMsg usrp);
|
||||
void sendAudioMsg(UsrpAudioMsg& usrp);
|
||||
void sendStopMsg(void);
|
||||
void sendMetaMsg(void);
|
||||
void sendUdpMessage(std::ostringstream& ss);
|
||||
void sendHeartbeat(void);
|
||||
void sendInfoJson(void);
|
||||
void allEncodedSamplesFlushed(void);
|
||||
void flushTimeout(Async::Timer *t=0);
|
||||
void onDelayTimeout(Async::Timer *t);
|
||||
void handleTimerTick(Async::Timer *t);
|
||||
bool setAudioCodec(void);
|
||||
void onLogicConInStreamStateChanged(bool is_active, bool is_idle);
|
||||
void onLogicConOutStreamStateChanged(bool is_active, bool is_idle);
|
||||
void checkIdle(void);
|
||||
bool isIdle(void);
|
||||
void handleMetaData(std::string metadata);
|
||||
void switchMode(uint8_t mode);
|
||||
void processEvent(const std::string& event);
|
||||
void handleSettingsMsg(std::string infomsg);
|
||||
void handlePlayFile(const std::string& path);
|
||||
void handlePlaySilence(int duration);
|
||||
void handlePlayTone(int fq, int amp, int duration);
|
||||
void handlePlayDtmf(const std::string& digit, int amp,
|
||||
int duration);
|
||||
void onPublishStateEvent(const std::string &event_name, const std::string &msg);
|
||||
void sendUserInfo(void);
|
||||
void publishInfo(std::string type, Json::Value event);
|
||||
|
||||
}; /* class UsrpLogic */
|
||||
|
||||
|
||||
#endif /* USRP_LOGIC_INCLUDED */
|
||||
|
||||
|
||||
/*
|
||||
* This file has not been truncated
|
||||
*/
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
###############################################################################
|
||||
#
|
||||
# UsrpLogic event handlers
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
#
|
||||
# This is the namespace in which all functions below will exist. The name
|
||||
# must match the corresponding section "[UsrpLogic]" in the configuration
|
||||
# file. The name may be changed but it must be changed in both places.
|
||||
#
|
||||
namespace eval UsrpLogic {
|
||||
|
||||
#
|
||||
# Checking to see if this is the correct logic core
|
||||
#
|
||||
if {$logic_name != [namespace tail [namespace current]]} {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when an unknown command is received
|
||||
# cmd - The command string
|
||||
#
|
||||
proc unknown_command {cmd} {
|
||||
Logic::unknown_command $cmd;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when a received command fails
|
||||
#
|
||||
proc command_failed {cmd} {
|
||||
Logic::command_failed $cmd;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed each time a Usrp Meta frame is received
|
||||
# and contains station data
|
||||
#
|
||||
proc usrp_stationdata_received {call tgid dmrid} {
|
||||
set dmr "";
|
||||
set tg "";
|
||||
if {$dmrid > 0} {
|
||||
set dmr "Dmr-ID $dmrid";
|
||||
}
|
||||
if {$tgid > 0} {
|
||||
set tg "TG# $tgid";
|
||||
}
|
||||
puts "Talker $call $dmr $tg";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when the own transmission starts
|
||||
#
|
||||
proc transmission_start {tg} {
|
||||
puts "Transmission starts (TG# $tg)";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when the own transmission stops
|
||||
#
|
||||
proc transmission_stop {tg} {
|
||||
puts "Transmission stops (TG# $tg)";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when the remote transmision has stopped
|
||||
#
|
||||
proc talker_stop {tg {call "unknown"}} {
|
||||
puts "Talker stop: $call TG# $tg";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when a switch between different modes has initiated
|
||||
#
|
||||
proc switch_to_mode {mode} {
|
||||
puts "Send request for $mode";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when the analog_bridge confirm to switch the mode
|
||||
#
|
||||
proc setting_mode {mode} {
|
||||
puts "New mode: $mode";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when json data has received, e.g.:
|
||||
# {"call":"SP2ONG","name":"Waldek"}
|
||||
#
|
||||
proc usrp_jsondata_received {jsondata} {
|
||||
puts "JSON: $jsondata";
|
||||
}
|
||||
|
||||
# end of namespace
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# This file has not been truncated
|
||||
#
|
||||
|
|
@ -0,0 +1,501 @@
|
|||
/**
|
||||
@file UsrpMsg.h
|
||||
@brief Usrp protocol message definitions
|
||||
@author Tobias Blomberg / SM0SVX & Adi Bier / DL1HRC
|
||||
@date 2021-04-26
|
||||
|
||||
\verbatim
|
||||
SvxReflector - An audio reflector for connecting SvxLink Servers
|
||||
Copyright (C) 2003-2021 Tobias Blomberg / SM0SVX
|
||||
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
\endverbatim
|
||||
*/
|
||||
|
||||
#ifndef USRP_MSG_INCLUDED
|
||||
#define USRP_MSG_INCLUDED
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* System Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <AsyncMsg.h>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Project Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Forward declarations
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Forward declarations of classes inside of the declared namespace
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Defines & typedefs
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#define MODE_NONE 0
|
||||
#define MODE_DMR 1
|
||||
#define MODE_P25 2
|
||||
#define MODE_NXDN 3
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Exported Global Variables
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static const unsigned DEFAULT_UDP_HEARTBEAT_TX_CNT_RESET = 15;
|
||||
static const unsigned UDP_HEARTBEAT_RX_CNT_RESET = 60;
|
||||
static const unsigned DEFAULT_TG_SELECT_TIMEOUT = 30;
|
||||
static const int DEFAULT_TMP_MONITOR_TIMEOUT = 3600;
|
||||
static const int USRP_AUDIO_FRAME_LEN = 160;
|
||||
static const int USRP_HEADER_LEN = 32;
|
||||
|
||||
enum { USRP_TYPE_VOICE=0, USRP_TYPE_DTMF=1, USRP_TYPE_TEXT=2,
|
||||
USRP_TYPE_PING=3, USRP_TYPE_TLV=4, USRP_TYPE_VOICE_ADPCM = 5,
|
||||
USRP_TYPE_VOICE_ULAW = 6 };
|
||||
|
||||
enum { TLV_TAG_BEGIN_TX = 0, TLV_TAG_AMBE = 1, TLV_TAG_END_TX = 2,
|
||||
TLV_TAG_TG_TUNE = 3, TLV_TAG_PLAY_AMBE= 4, TLV_TAG_REMOTE_CMD= 5,
|
||||
TLV_TAG_AMBE_49 = 6, TLV_TAG_AMBE_72 = 7, TLV_TAG_SET_INFO = 8,
|
||||
TLV_TAG_IMBE = 9, TLV_TAG_DSAMBE = 10, TLV_TAG_FILE_XFER= 11
|
||||
};
|
||||
|
||||
//std::map<uint8_t, std::string> selected_mode;
|
||||
const std::string selected_mode[] = { "*NONE", "*DMR", "*P25", "*NXDN" };
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Class definitions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for Usrp audio network messages
|
||||
* @author Tobias Blomberg / SM0SVX & Adi Bier / DL1HRC
|
||||
* @date 2021-04-28
|
||||
*/
|
||||
class UsrpAudioMsg : public Async::Msg
|
||||
{
|
||||
public:
|
||||
|
||||
UsrpAudioMsg(uint32_t seq=0, uint32_t memory=0, uint32_t keyup=0,
|
||||
uint32_t talkgroup=0, uint32_t type=0, uint32_t mpxid=0,
|
||||
uint32_t reserved=0, const std::array<int16_t, 160> audio_data={0})
|
||||
: m_seq(htole32(seq)), m_memory(htonl(memory)), m_keyup(keyup),
|
||||
m_talkgroup(htonl(talkgroup)), m_type(htonl(type)),
|
||||
m_mpxid(htonl(mpxid)), m_reserved(htonl(reserved)),
|
||||
m_audio_data(audio_data)
|
||||
{
|
||||
eye = {'U','S','R','P'};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~UsrpAudioMsg(void) {}
|
||||
|
||||
uint32_t type(void) const { return ntohl(m_type); }
|
||||
uint32_t seq(void) const { return ntohl(m_seq); }
|
||||
uint32_t memory(void) const { return ntohl(m_memory); }
|
||||
uint32_t keyup(void) const { return m_keyup; }
|
||||
uint32_t mpxid(void) const { return ntohl(m_mpxid); }
|
||||
uint32_t reserved(void) const { return ntohl(m_reserved); }
|
||||
uint32_t talkgroup(void) const { return ntohl(m_talkgroup); }
|
||||
|
||||
void setTg(uint32_t tg) { m_talkgroup = htole32(tg);}
|
||||
void setType(uint32_t type) { m_type = htole32(type);}
|
||||
void setSeq(uint32_t seq) { m_seq = htole32(seq); }
|
||||
void setKeyup(bool keyup) { (keyup ? m_keyup=1 : m_keyup=0); }
|
||||
|
||||
void setAudioData(int16_t in[USRP_AUDIO_FRAME_LEN*2])
|
||||
{
|
||||
for (size_t x=0; x<USRP_AUDIO_FRAME_LEN; x++)
|
||||
{
|
||||
m_audio_data[x] = htons(in[x]);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<int16_t, USRP_AUDIO_FRAME_LEN>& audioData(void)
|
||||
{ return m_audio_data; }
|
||||
const std::array<int16_t, USRP_AUDIO_FRAME_LEN>& audioData(void)
|
||||
const { return m_audio_data; }
|
||||
|
||||
ASYNC_MSG_MEMBERS(eye, m_seq, m_memory, m_keyup, m_talkgroup, m_type,
|
||||
m_mpxid, m_reserved, m_audio_data)
|
||||
|
||||
private:
|
||||
std::array<char, 4> eye;
|
||||
uint32_t m_seq;
|
||||
uint32_t m_memory;
|
||||
uint32_t m_keyup;
|
||||
uint32_t m_talkgroup;
|
||||
uint32_t m_type;
|
||||
uint32_t m_mpxid;
|
||||
uint32_t m_reserved;
|
||||
std::array<int16_t, USRP_AUDIO_FRAME_LEN> m_audio_data;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for Usrp network header message
|
||||
* @author Tobias Blomberg / SM0SVX & Adi Bier / DL1HRC
|
||||
* @date 2021-04-28
|
||||
*/
|
||||
class UsrpHeaderMsg : public Async::Msg
|
||||
{
|
||||
public:
|
||||
|
||||
UsrpHeaderMsg(uint32_t seq=0, uint32_t keyup=0, uint32_t talkgroup=0,
|
||||
uint32_t type=0)
|
||||
: m_seq(htole32(seq)), m_keyup(keyup),
|
||||
m_talkgroup(htole32(talkgroup)), m_type(htole32(type))
|
||||
{
|
||||
eye = {'U','S','R','P'};
|
||||
m_memory = 0;
|
||||
m_keyup = 0;
|
||||
m_type = 0;
|
||||
m_mpxid = 0;
|
||||
m_reserved = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~UsrpHeaderMsg(void) {}
|
||||
|
||||
uint32_t type(void) const { return ntohl(m_type); }
|
||||
uint32_t seq(void) const { return ntohl(m_seq); }
|
||||
uint32_t memory(void) const { return ntohl(m_memory); }
|
||||
bool keyup(void) const { return (m_keyup & 1 ? true : false); }
|
||||
uint32_t mpxid(void) const { return ntohl(m_mpxid); }
|
||||
uint32_t reserved(void) const { return ntohl(m_reserved); }
|
||||
uint32_t talkgroup(void) const { return ntohl(m_talkgroup); }
|
||||
|
||||
void setTg(uint32_t tg) { m_talkgroup = htole32(tg); }
|
||||
void setSeq(uint32_t seq) { m_seq = htole32(seq); }
|
||||
|
||||
ASYNC_MSG_MEMBERS(eye, m_seq, m_memory, m_keyup, m_talkgroup,
|
||||
m_type, m_mpxid, m_reserved)
|
||||
|
||||
private:
|
||||
std::array<char, 4> eye;
|
||||
uint32_t m_seq;
|
||||
uint32_t m_memory;
|
||||
uint32_t m_keyup;
|
||||
uint32_t m_talkgroup;
|
||||
uint32_t m_type;
|
||||
uint32_t m_mpxid;
|
||||
uint32_t m_reserved;
|
||||
};
|
||||
|
||||
/*
|
||||
template <typename P>
|
||||
class UsrpMsg {
|
||||
UsrpMsg(UsrpHeaderMsg header, P payload) : header(header), payload(p) {}
|
||||
|
||||
private:
|
||||
UsrpHeaderMsg header;
|
||||
P payload;
|
||||
};
|
||||
|
||||
|
||||
UrspHeaderMsg h = ....
|
||||
if(condition(h)) {
|
||||
|
||||
std::vector<uint8_t> payload =...
|
||||
UsrpMsg<std::vector> msg(h, payload);
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Class for UsrpMetaTextMsg message
|
||||
* @author Tobias Blomberg / SM0SVX & Adi Bier / DL1HRC
|
||||
* @date 2021-07-02
|
||||
*/
|
||||
class UsrpMetaTextMsg : public Async::Msg
|
||||
{
|
||||
public:
|
||||
|
||||
UsrpMetaTextMsg(uint32_t seq=0, uint32_t keyup=0,
|
||||
uint32_t talkgroup=0, uint32_t type=0)
|
||||
: m_seq(htole32(seq)), m_keyup(keyup),
|
||||
m_talkgroup(htole32(talkgroup)), m_type(htole32(type))
|
||||
{
|
||||
eye = {'U','S','R','P'};
|
||||
m_memory = 0;
|
||||
m_keyup = 0;
|
||||
m_talkgroup = 0;
|
||||
m_type = htobe32(USRP_TYPE_TEXT);
|
||||
m_mpxid = 0;
|
||||
m_reserved = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~UsrpMetaTextMsg(void) {}
|
||||
|
||||
/**
|
||||
* public methods
|
||||
*/
|
||||
uint32_t type(void) const { return ntohl(m_type); }
|
||||
uint32_t seq(void) const { return m_seq; }
|
||||
uint32_t tg(void) const { return ntohl(m_talkgroup); }
|
||||
uint32_t keyup(void) const { return ntohl(m_keyup); }
|
||||
uint32_t mpx(void) const { return ntohl(m_mpxid); }
|
||||
uint32_t res(void) const { return ntohl(m_reserved); }
|
||||
|
||||
// return true if the message is TLV (first character == 0x08)
|
||||
bool isTlv(void)
|
||||
{
|
||||
return (m_meta == 0x08 ? true : false);
|
||||
}
|
||||
|
||||
ASYNC_MSG_MEMBERS(eye, m_seq, m_memory, m_keyup, m_talkgroup,
|
||||
m_type, m_mpxid, m_reserved, m_meta)
|
||||
|
||||
private:
|
||||
std::array<char, 4> eye;
|
||||
uint32_t m_seq;
|
||||
uint32_t m_memory;
|
||||
uint32_t m_keyup;
|
||||
uint32_t m_talkgroup;
|
||||
uint32_t m_type;
|
||||
uint32_t m_mpxid;
|
||||
uint32_t m_reserved;
|
||||
uint8_t m_meta;
|
||||
}; /* class UsrpMetaTextMsg */
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for Usrp network Metadata message (TLV)
|
||||
* @author Tobias Blomberg / SM0SVX & Adi Bier / DL1HRC
|
||||
* @date 2021-04-28
|
||||
*/
|
||||
class UsrpTlvMetaMsg : public Async::Msg
|
||||
{
|
||||
public:
|
||||
|
||||
UsrpTlvMetaMsg(uint32_t seq=0) : m_seq(htole32(seq))
|
||||
{
|
||||
eye = {'U','S','R','P'};
|
||||
m_memory = 0;
|
||||
m_keyup = 0;
|
||||
m_talkgroup = 0;
|
||||
m_type = htobe32(USRP_TYPE_TEXT);
|
||||
m_mpxid = 0;
|
||||
m_reserved = 0;
|
||||
m_tlv = TLV_TAG_SET_INFO;
|
||||
m_tlvlen = 0x13;
|
||||
m_dmrid = {0,0,0};
|
||||
m_rptid = 0;
|
||||
m_tg = {0,0,0};
|
||||
m_ts = 0;
|
||||
m_cc = 1;
|
||||
m_meta.fill(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~UsrpTlvMetaMsg(void) {}
|
||||
|
||||
// set the own talk group
|
||||
void setTg(uint32_t tg)
|
||||
{
|
||||
std::array<uint8_t, 3> t;
|
||||
t[0] = (tg >> 16) & 0xff;
|
||||
t[1] = (tg >> 8) & 0xff;
|
||||
t[2] = (tg >> 0) & 0xff;
|
||||
m_tg = t;
|
||||
}
|
||||
|
||||
// set USRP_TYPE
|
||||
void setType(uint32_t type)
|
||||
{
|
||||
m_type = htobe32(type);
|
||||
}
|
||||
|
||||
// set the squence
|
||||
void setSeq(uint32_t seq) { m_seq = htole32(seq); }
|
||||
|
||||
// set the own callsign
|
||||
void setCallsign(std::string call)
|
||||
{
|
||||
for (size_t i=0; i<call.length(); i++)
|
||||
{
|
||||
m_meta[i] = call.c_str()[i];
|
||||
}
|
||||
m_tlvlen = static_cast<int>(call.length() + 13) & 0xff;
|
||||
}
|
||||
|
||||
// set Metadata
|
||||
void setMetaData(std::string metadata)
|
||||
{
|
||||
}
|
||||
|
||||
// set DMR-ID
|
||||
void setDmrId(uint32_t dmrid)
|
||||
{
|
||||
std::array<uint8_t, 3> t;
|
||||
t[0] = (dmrid >> 16) & 0xff;
|
||||
t[1] = (dmrid >> 8) & 0xff;
|
||||
t[2] = (dmrid >> 0) & 0xff;
|
||||
m_dmrid = t;
|
||||
}
|
||||
|
||||
// set the own repeater ID
|
||||
void setRptId(uint32_t rptid) { m_rptid = htole32(rptid); }
|
||||
|
||||
// set the color code
|
||||
void setCC(uint8_t cc) { m_cc = cc; }
|
||||
|
||||
// set Time slot
|
||||
void setTS(uint8_t ts)
|
||||
{
|
||||
(ts > 4 ? m_ts = 4 : m_ts = ts);
|
||||
}
|
||||
|
||||
void setTlv(uint8_t tlv)
|
||||
{
|
||||
m_tlv = tlv;
|
||||
}
|
||||
|
||||
void setTlvLen(uint8_t tlvlen)
|
||||
{
|
||||
m_tlvlen = tlvlen;
|
||||
}
|
||||
|
||||
// returns the callsing of the talker
|
||||
std::string getCallsign(void)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
uint8_t call[9];
|
||||
if (m_tlv == TLV_TAG_SET_INFO && m_tlvlen < 0x16)
|
||||
{
|
||||
for(i=0;i<(m_tlvlen-13);i++)
|
||||
{
|
||||
call[i] = m_meta[i];
|
||||
if (m_meta[i] == 0x00) break;
|
||||
}
|
||||
}
|
||||
return std::string(call, call+i);
|
||||
}
|
||||
|
||||
// returns MetaData info
|
||||
std::string getMetaInfo(void)
|
||||
{
|
||||
uint8_t metainfo[306];
|
||||
uint8_t z;
|
||||
if (m_tlv == TLV_TAG_SET_INFO)
|
||||
{
|
||||
for (z=0;z<306;z++)
|
||||
{
|
||||
metainfo[z] = m_meta[z];
|
||||
if (m_meta[z] == 0x00) break;
|
||||
}
|
||||
return std::string(metainfo, metainfo+z);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// returns the TYPE of message
|
||||
uint32_t type(void) const { return ntohl(m_type); }
|
||||
|
||||
// returns the current talkgroup of the talker
|
||||
uint32_t getTg(void)
|
||||
{
|
||||
return ((m_tg[0] << 16) + (m_tg[1] << 8) + (m_tg[2] & 0xff));
|
||||
}
|
||||
|
||||
// returns the DMR ID of the talker
|
||||
uint32_t getDmrId(void)
|
||||
{
|
||||
return (m_dmrid[2] & 0xff) + (m_dmrid[1] << 8) + (m_dmrid[0] << 16);
|
||||
}
|
||||
|
||||
// getTlv
|
||||
uint8_t getTlv(void) { return m_tlv; }
|
||||
|
||||
// getTS
|
||||
uint8_t getTS(void) { return (m_ts > 4 ? 4 : m_ts); }
|
||||
|
||||
// getTlvLen
|
||||
uint8_t getTlvLen(void) { return m_tlvlen; }
|
||||
|
||||
ASYNC_MSG_MEMBERS(eye, m_seq, m_memory, m_keyup, m_talkgroup, m_type,
|
||||
m_mpxid, m_reserved, m_tlv, m_tlvlen, m_dmrid, m_rptid,
|
||||
m_tg, m_ts, m_cc, m_meta)
|
||||
|
||||
private:
|
||||
|
||||
std::array<char, 4> eye; // is always "USRP"
|
||||
uint32_t m_seq; // sequence number
|
||||
uint32_t m_memory; // set to 0 - not used
|
||||
uint32_t m_keyup; // Tx on/off (1/0)
|
||||
uint32_t m_talkgroup; // current talkgroup
|
||||
uint32_t m_type; // type of frame
|
||||
uint32_t m_mpxid; // set to 0 - not used
|
||||
uint32_t m_reserved; // set to 0 - not used
|
||||
uint8_t m_tlv; // see TLV_TAGS
|
||||
uint8_t m_tlvlen; // TLV-length of MetaData
|
||||
std::array<uint8_t, 3> m_dmrid; // DMR-ID length 3 bytes
|
||||
uint32_t m_rptid; // Repeater-ID length 4 bytes
|
||||
std::array<uint8_t, 3> m_tg; // Talkgroup length 3 bytes
|
||||
uint8_t m_ts; // Timeslot length 1 byte
|
||||
uint8_t m_cc; // color code length 1 byte
|
||||
std::array<uint8_t, 306> m_meta; // padding the rest with 0x00
|
||||
};
|
||||
|
||||
#endif /* USRP_MSG_INCLUDED */
|
||||
|
||||
/*
|
||||
* This file has not been truncated
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"ab":{"version":"1.6.2","date":"Wed.Mar.31.13:52:10.EDT.2021"},
|
||||
"dv3000":{"ip":"127.0.0.1","port":"2460","use_serial":"false"},
|
||||
"use_fallback":"true",
|
||||
"use_emulator":"true",
|
||||
"mute":"OFF",
|
||||
"usrp":{"ip":"127.0.0.1","rx_port":"14321","tx_port":"14321","ping":"10",
|
||||
"to_pcm":{"shape":"AUDIO_USE_AGC","gain":"4.00"},
|
||||
"to_ambe":{"shape":"AUDIO_USE_GAIN","gain":"0.35"}
|
||||
},
|
||||
"tlv":{"ip":"127.0.0.1","tx_port":"0","rx_port":"0","ambe_size":"72","ambe_mode":"NXDN"},
|
||||
"digital":{"gw":"1234567","rpt":"123456789","tg":"7","ts":"2","cc":"1","call":"N0CALL"},
|
||||
"last_tune":"7"
|
||||
}
|
||||
|
|
@ -0,0 +1,309 @@
|
|||
USRP protocol used to send via UDP analog audio (2021.07.14)
|
||||
|
||||
The USRP was implemented in Allstar (in 2010, KA1RBI) and used by various hamradio applications like
|
||||
MMDVMHost, USRP2M17, Analog_Bridge, Analog_Reflector, etc
|
||||
|
||||
UDP USRP FRAME:
|
||||
===============
|
||||
USRP Header (32 bytes) + DATA
|
||||
|
||||
Typical sequence for send audio:
|
||||
|
||||
USRP_HEADER (Type_Frame=2, PTT=0) + DATA (The DATA in first byte contain TLV_TAGS: 0x08 (TLV_TAG_SET_INFO)
|
||||
and contain MetaDATA (see below))
|
||||
USRP_HEADER (Type_Frame=0, PTT=1) + DATA (Audio 320 bytes)
|
||||
....
|
||||
USRP_HEADER (Type_Frame=0, PTT=0) + DATA (last Audio or empty buffer 320 bytes)
|
||||
|
||||
|
||||
The first frame type = 2 (USRP_TYPE_TEXT) with MetaData is not obligatory and can be omitted, but it can be useful when,
|
||||
for example, we want to send information about the callsign source from the analog to
|
||||
for example M17 or callsign and its DMR ID to DMR.
|
||||
|
||||
At current in USRP header are used only the following information:
|
||||
USRP string in 0-3 bytes
|
||||
Sequence number (4-7 bytes)
|
||||
PTT status (15 byte) 1 = ON , 0 = OFF
|
||||
Type of Frame (20 byte) 0 Audio data, 1 DTMF, 2 MetaData
|
||||
|
||||
The rest of the information in USRP header should be set to 0
|
||||
|
||||
USRP Header:
|
||||
==============================
|
||||
Bytes Value Notes
|
||||
0-3 USRP string char: USRP
|
||||
4-7 Sequence number
|
||||
8-11 Memory? Set to 0 (not used)
|
||||
12-15 PTT PTT value is stored in 15 byte. 1 for PTT ON start transmission, 0 for PTT OFF stop transmission
|
||||
16-19 Talkgroup? Set to 0 (not used)
|
||||
20-23 Type_Frame Type frame is stored in 20 byte. 0 for Audio (USRP_TYPE_VOICE), 2 for MetaData (USRP_TYPE_TEXT)
|
||||
24-27 Mpxid? Set to 0 (not used)
|
||||
28-31 Reserved? Set to 0 (not used)
|
||||
|
||||
|
||||
Minimum use USRP protocol is:
|
||||
|
||||
Received side
|
||||
-------------
|
||||
Two types of frames are we received
|
||||
|
||||
- USRP_TYPE_TEXT with set TVL_TAG_SET_INFO (0x08) bit where we get information about source callsign and DmrID
|
||||
and Talk Group
|
||||
- USRP_TYPE_VOICE is used to receive stream audio wherein USRP Header is information about KEYUP status (PTT)
|
||||
which allow track start an audio and stop audio stream received
|
||||
|
||||
Transmitting side
|
||||
-----------------
|
||||
Two types of frames are we received
|
||||
|
||||
- USRP_TYPE_TEXT with set TVL_TAG_SET_INFO (0x08) bit where we send information about source callsign and DmrID
|
||||
and Talk Group. DmrID and Talk Group make sense when we have a link to DMR DV
|
||||
- USRP_TYPE_VOICE used to send audio stream where in USRP Header in information about KEYUP status (PTT)
|
||||
which allow track start an audio and stop audio stream received
|
||||
|
||||
|
||||
|
||||
Below short description type of UDP USRP frames
|
||||
-----------------------------------------------
|
||||
|
||||
DATA contain Audio stream (see example 1)
|
||||
===========================================
|
||||
In byte 20 of the USRP HEADER, frame type 0 (USRP_TYPE_VOICE)
|
||||
In byte 15 of the USRP HEADER, status PTT: 1 ON, 0 OFF (last frame)
|
||||
The DATA contains an AUDIO stream.
|
||||
Audio signed 16-bit little-endian PCM audio at 8000 samples/sec, 160 samples per packet.
|
||||
|
||||
The PTT status in the USRP of the header can be used to control PTT or
|
||||
SQL, or information about start and end transmission in an application that is receiving data via USRP.
|
||||
|
||||
|
||||
|
||||
1. DATA = MetaData with Callsign, DmrId, Talk Group (see example 2)
|
||||
===================================================================
|
||||
In byte 20 of the USRP HEADER, frame type 1 (USRP_TYPE_TEXT)
|
||||
The DATA in the first byte contains TLV_TAGS: 0x08 (TLV_TAG_SET_INFO)
|
||||
The next bytes contain MetaData:
|
||||
|
||||
TLVLenght,dmrID, repeaterID, tg, ts, cc, callsign, 0
|
||||
|
||||
TLV Tags Set 0x08 (see TLV_TAGS)
|
||||
TLV Length Value of lenght of MetaData where TLVLenght = 3 + 4 + 3 + 1 + 1 + len(call) + 1
|
||||
DMR Id Lenght 3 bytes. Set 0 or ((dmr_id >> 16) & 0xff),((dmr_id >> 8) & 0xff),(dmr_id & 0xff)
|
||||
RPT Id Lenght 4 bytes. Set 0 or ((rpt_id >> 32) & 0xff),((rpt_id >> 16) & 0xff),((rpt_id >> 8) & 0xff),(rpt_id & 0xff)
|
||||
TalkGroup Lenght 3 bytes. Set 0 or ((tg >> 16) & 0xff),((tg >> 8) & 0xff),(tg & 0xff)
|
||||
TimeSlot# Length 1 byte. Set 0 or Time Slot number
|
||||
ColorCode Length 1 byte. Set 0 or value of ColorCode
|
||||
Callsing Put the callisgn
|
||||
End of MetData Set 0
|
||||
|
||||
Note:
|
||||
For most applications with USRP protocol, it is enough to send in MetaData
|
||||
only callsign and in the case of DMR and P25 network,
|
||||
additionally DMR ID for the given callsign
|
||||
|
||||
The Analog Bridge v1.6.2 set in field CALLSIGN information like:
|
||||
{"call":"N3ABC","name":"Club station"}
|
||||
if our application is connected with Analog Refelctor via BRIDGE mode.
|
||||
|
||||
|
||||
|
||||
2. DATA = MetaData tune Talk Group on Analog Bridge (see example 3)
|
||||
==========================================================
|
||||
In byte 20 of the USRP HEADER, frame type 1 (USRP_TYPE_DTMF)
|
||||
The DATA contains a string where is Talk Group number.
|
||||
For example, we want to set the TG # 26021 string in DATA in
|
||||
bytes is: 3236 3032 3100
|
||||
2 is 32, 6 is 36, etc
|
||||
|
||||
|
||||
3. DATA = MetaData switch DV mode on Analog Bridge (see example 4)
|
||||
==========================================================
|
||||
In byte 20 of the USRP HEADER, frame type 1 (USRP_TYPE_DTMF)
|
||||
The DATA contains a string where the first character
|
||||
is * (star) followed by the Digital Voice mode name.
|
||||
For example, we want to set mode DMR string in DATA: *DMR
|
||||
Available DV Mode Names: DMR, YSF, P25, NXDN, DSTAR
|
||||
|
||||
|
||||
4. DATA = MetaData send request INFO to Analog Bridge (see example 5)
|
||||
=================================================================
|
||||
In byte 20 of the USRP HEADER, frame type 2 (USRP_TYPE_TEXT)
|
||||
The DATA contains a string INFO
|
||||
|
||||
|
||||
5. DATA = MetaData received from Analog Bridge with INFO in JSON format (see example 6)
|
||||
==============================================================================
|
||||
In byte 20 of the USRP HEADER, frame type 2 (USRP_TYPE_TEXT)
|
||||
The DATA contains a string INFO:
|
||||
and next status Analog Bridge in JSON format
|
||||
The length frame is bigger than 320 bytes
|
||||
|
||||
|
||||
Example 1
|
||||
-------------------------------------------
|
||||
Dump with USRP Audio data PTT ON = 1
|
||||
|
||||
0x0000: 4500 017c 524b 4000 4011 e923 7f00 0001 E..|RK@.@..#....
|
||||
0x0010: 7f00 0001 d432 d431 0168 ff7b 5553 5250 .....2.1.h.{USRP
|
||||
0x0020: 0000 000b 0000 0000 0000 0001 0000 0000 ................
|
||||
0x0030: 0000 0000 0000 0000 0000 0000 eaff efff ................
|
||||
0x0040: d5ff c3ff c4ff c6ff c9ff caff e1ff eeff ................
|
||||
|
||||
|
||||
The last USRP frame PTT OFF = 0
|
||||
|
||||
0x0000: 4500 017c 9b6d 4000 4011 a001 7f00 0001 E..|.m@.@.......
|
||||
0x0010: 7f00 0001 d432 d431 0168 ff7b 5553 5250 .....2.1.h.{USRP
|
||||
0x0020: 0000 0050 0000 0000 0000 0000 0000 0000 ...P............
|
||||
0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
|
||||
|
||||
Example 2
|
||||
--------------------------------------------
|
||||
MetaData: Callsign N0CALL DmrID 1234567
|
||||
|
||||
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E.
|
||||
0x0010: 017c cbb9 4000 4011 6fb5 7f00 0001 7f00 .|..@.@.o.......
|
||||
0x0020: 0001 8613 8614 0168 ff7b 5553 5250 0000 .......h.{USRP..
|
||||
0x0030: 00da 0000 0000 0000 0000 0000 0000 0200 ................
|
||||
0x0040: 0000 0000 0000 0000 0000 0813 12d6 8700 ...............
|
||||
0x0050: 0000 0000 0000 0000 4e30 4341 4c4c 0000 ........N0CALL..
|
||||
0x0060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
|
||||
|
||||
Example 3
|
||||
--------------------------------------------
|
||||
|
||||
Tune Talk Group 4000
|
||||
|
||||
0x0000: 4500 017c 9f5b 4000 4011 9c13 7f00 0001 E..|.[@.@.......
|
||||
0x0010: 7f00 0001 d432 d431 0168 ff7b 5553 5250 .....2.1.h.{USRP
|
||||
0x0020: 0000 0002 0000 0000 0000 0000 0000 0000 ................
|
||||
0x0030: 0100 0000 0000 0000 0000 0000 3430 3030 ............4000
|
||||
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
|
||||
Tune Talk Group 7
|
||||
|
||||
0x0000: 4500 017c d985 4000 4011 61e9 7f00 0001 E..|..@.@.a.....
|
||||
0x0010: 7f00 0001 d432 d431 0168 ff7b 5553 5250 .....2.1.h.{USRP
|
||||
0x0020: 0000 0003 0000 0000 0000 0000 0000 0000 ................
|
||||
0x0030: 0100 0000 0000 0000 0000 0000 3700 0000 ............7...
|
||||
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
|
||||
Example 4
|
||||
--------------------------------------------
|
||||
|
||||
Switch mode to YSF
|
||||
|
||||
0x0000: 4500 017c 32ac 4000 4011 08c3 7f00 0001 E..|2.@.@.......
|
||||
0x0010: 7f00 0001 d432 d431 0168 ff7b 5553 5250 .....2.1.h.{USRP
|
||||
0x0020: 0000 0002 0000 0000 0000 0000 0000 0000 ................
|
||||
0x0030: 0100 0000 0000 0000 0000 0000 2a59 5346 ............*YSF
|
||||
0x0040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
|
||||
Analog Bridge send to client INFO about confirm setting mode YSF
|
||||
The byte 20 is type frame 2 USRP_TYPE_TEXT. From byte 33 is
|
||||
string INFO: and next bytes contain a message
|
||||
|
||||
0x0000: 4500 017c 332f 4000 4011 0840 7f00 0001 E..|3/@.@..@....
|
||||
0x0010: 7f00 0001 d431 d432 0168 ff7b 5553 5250 .....1.2.h.{USRP
|
||||
0x0020: 0000 0ac3 0000 0000 0000 0000 0000 0000 ................
|
||||
0x0030: 0200 0000 0000 0000 0000 0000 494e 464f ............INFO
|
||||
0x0040: 3a4d 5347 3a53 6574 7469 6e67 206d 6f64 :MSG:Setting.mod
|
||||
0x0050: 6520 746f 2059 5346 0000 0000 0000 0000 e.to.YSF........
|
||||
0x0060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
|
||||
Example 5
|
||||
-----------------------------------------------
|
||||
|
||||
0x0000: 4500 017c 32eb 4000 4011 0884 7f00 0001 E..|2.@.@.......
|
||||
0x0010: 7f00 0001 d432 d431 0168 ff7b 5553 5250 .....2.1.h.{USRP
|
||||
0x0020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0x0030: 0200 0000 0000 0000 0000 0000 494e 464f ............INFO
|
||||
0x0040: 3a00 0000 0000 0000 0000 0000 0000 0000 :...............
|
||||
|
||||
|
||||
Example 6
|
||||
-----------------------------------------------
|
||||
|
||||
0x0000: 4500 0269 12d4 4000 4011 27ae 7f00 0001 E..i..@.@.'.....
|
||||
0x0010: 7f00 0001 d431 d432 0255 0069 5553 5250 .....1.2.U.iUSRP
|
||||
0x0020: 0000 1495 0000 0000 0000 0000 0000 0000 ................
|
||||
0x0030: 0200 0000 0000 0000 0000 0000 494e 464f ............INFO
|
||||
0x0040: 3a7b 2261 6222 3a7b 2276 6572 7369 6f6e :{"ab":{"version
|
||||
0x0050: 223a 2231 2e36 2e32 222c 2264 6174 6522 ":"1.6.2","date"
|
||||
0x0060: 3a22 5765 6420 4d61 7220 3331 2031 333a :"Wed.Mar.31.13:
|
||||
0x0070: 3532 3a31 3020 4544 5420 3230 3231 227d 52:10.EDT.2021"}
|
||||
0x0080: 2c22 6476 3330 3030 223a 7b22 6970 223a ,"dv3000":{"ip":
|
||||
0x0090: 2231 3237 2e30 2e30 2e31 222c 2270 6f72 "127.0.0.1","por
|
||||
0x00a0: 7422 3a22 3234 3630 222c 2275 7365 5f73 t":"2460","use_s
|
||||
0x00b0: 6572 6961 6c22 3a22 6661 6c73 6522 7d2c erial":"false"},
|
||||
0x00c0: 2275 7365 5f66 616c 6c62 6163 6b22 3a22 "use_fallback":"
|
||||
0x00d0: 7472 7565 222c 2275 7365 5f65 6d75 6c61 true","use_emula
|
||||
0x00e0: 746f 7222 3a22 7472 7565 222c 226d 7574 tor":"true","mut
|
||||
0x00f0: 6522 3a22 4f46 4622 2c22 7573 7270 223a e":"OFF","usrp":
|
||||
0x0100: 7b22 6970 223a 2231 3237 2e30 2e30 2e31 {"ip":"127.0.0.1
|
||||
0x0110: 222c 2272 785f 706f 7274 223a 2231 3433 ","rx_port":"143
|
||||
0x0120: 3231 222c 2274 785f 706f 7274 223a 2231 21","tx_port":"1
|
||||
0x0130: 3433 3231 222c 2270 696e 6722 3a22 3130 4321","ping":"10
|
||||
0x0140: 222c 2274 6f5f 7063 6d22 3a7b 2273 6861 ","to_pcm":{"sha
|
||||
0x0150: 7065 223a 2241 5544 494f 5f55 5345 5f41 pe":"AUDIO_USE_A
|
||||
0x0160: 4743 222c 2267 6169 6e22 3a22 342e 3030 GC","gain":"4.00
|
||||
0x0170: 227d 2c22 746f 5f61 6d62 6522 3a7b 2273 "},"to_ambe":{"s
|
||||
0x0180: 6861 7065 223a 2241 5544 494f 5f55 5345 hape":"AUDIO_USE
|
||||
0x0190: 5f47 4149 4e22 2c22 6761 696e 223a 2230 _GAIN","gain":"0
|
||||
0x01a0: 2e33 3522 7d7d 2c22 746c 7622 3a7b 2269 .35"}},"tlv":{"i
|
||||
0x01b0: 7022 3a22 3132 372e 302e 302e 3122 2c22 p":"127.0.0.1","
|
||||
0x01c0: 7478 5f70 6f72 7422 3a22 3022 2c22 7278 tx_port":"0","rx
|
||||
0x01d0: 5f70 6f72 7422 3a22 3022 2c22 616d 6265 _port":"0","ambe
|
||||
0x01e0: 5f73 697a 6522 3a22 3732 222c 2261 6d62 _size":"72","amb
|
||||
0x01f0: 655f 6d6f 6465 223a 224e 5844 4e22 7d2c e_mode":"NXDN"},
|
||||
0x0200: 2264 6967 6974 616c 223a 7b22 6777 223a "digital":{"gw":
|
||||
0x0210: 2231 3233 3435 3637 222c 2272 7074 223a "1234567","rpt":
|
||||
0x0220: 2231 3233 3435 3637 3839 222c 2274 6722 "123456789","tg"
|
||||
0x0230: 3a22 3722 2c22 7473 223a 2232 222c 2263 :"7","ts":"2","c
|
||||
0x0240: 6322 3a22 3122 2c22 6361 6c6c 223a 224E c":"1","call":"N
|
||||
0x0250: 3043 414C 4C22 7d2c 226c 6173 745f 7475 0CALL"},"last_tu
|
||||
0x0260: 6e65 223a 2237 227d 00 ne":"7"}.
|
||||
|
||||
|
||||
Other information
|
||||
-----------------
|
||||
|
||||
TYPE of USRP frames
|
||||
#######################
|
||||
USRP_TYPE_VOICE = 0 (Audio data)
|
||||
USRP_TYPE_DTMF = 1
|
||||
USRP_TYPE_TEXT = 2 (Meta data)
|
||||
USRP_TYPE_PING = 3
|
||||
USRP_TYPE_TLV = 4
|
||||
USRP_TYPE_VOICE_ADPCM = 5
|
||||
USRP_TYPE_VOICE_ULAW = 6
|
||||
|
||||
|
||||
TLV tags
|
||||
#######################
|
||||
TLV_TAG_BEGIN_TX = 0
|
||||
TLV_TAG_AMBE = 1
|
||||
TLV_TAG_END_TX = 2
|
||||
TLV_TAG_TG_TUNE = 3
|
||||
TLV_TAG_PLAY_AMBE = 4
|
||||
TLV_TAG_REMOTE_CMD = 5
|
||||
TLV_TAG_AMBE_49 = 6
|
||||
TLV_TAG_AMBE_72 = 7
|
||||
TLV_TAG_SET_INFO = 8
|
||||
TLV_TAG_IMBE = 9
|
||||
TLV_TAG_DSAMBE = 10
|
||||
TLV_TAG_FILE_XFER = 11
|
||||
|
||||
|
||||
Information base on:
|
||||
The above information about the USRP protocol is based on an analysis of the dump
|
||||
frames from applications that use USRP as well and from links
|
||||
|
||||
https://github.com/AllStarLink/ASL-Asterisk/blob/develop/asterisk/channels/chan_usrp.h
|
||||
https://github.com/AllStarLink/ASL-Asterisk/blob/develop/asterisk/channels/chan_usrp.c
|
||||
https://github.com/DVSwitch/MMDVM_Bridge/blob/master/dvswitch.sh
|
||||
https://github.com/DVSwitch/USRP_Client/blob/master/pyUC.py
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
[
|
||||
{ "id" : "2620055", "mode" : "DMR", "name" : "Adi", "call" : "DL1HRC-1", "location" : "Leuna", "symbol" : "\\r", "comment" : "DMR ID" },
|
||||
{ "id" : "9031", "mode" : "NXDN", "name" : "Adi", "call" : "DL1HRC-2", "location" : "Leuna", "symbol" : "\\r", "comment" : "SvxLink Sysop" },
|
||||
{ "id" : "09011638300023403", "mode" : "TETRA", "name" : "Adi", "call" : "DL1HRC-3", "location" : "Leuna", "symbol" : "\\E", "comment" : "SvxLink Sysop" },
|
||||
{ "id" : "123456", "mode" : "SPECIAL", "name" : "Adi", "call" : "DL1HRC-4", "location" : "Leuna", "symbol" : "\\r", "comment" : "SvxLink Sysop" }
|
||||
]
|
||||
|
|
@ -109,6 +109,24 @@ EVENT_HANDLER=@SVX_SHARE_INSTALL_DIR@/events.tcl
|
|||
QSY_PENDING_TIMEOUT=15
|
||||
#VERBOSE=1
|
||||
|
||||
[UsrpLogic]
|
||||
TYPE=Usrp
|
||||
USRP_HOST=127.0.0.1
|
||||
USRP_TX_PORT=41234
|
||||
USRP_RX_PORT=41233
|
||||
CALL=N0CALL
|
||||
#DMRID=1234567
|
||||
#PREAMP=1
|
||||
#NET_PREAMP=0
|
||||
#FILTER_TO_USRP=BpBu1/600-3500
|
||||
#FILTER_FROM_USRP=HsBq2/0.01/-18/4000
|
||||
DV_USER_INFOFILE=/etc/svxlink/dv_users.json
|
||||
#DEBUG=1
|
||||
EVENT_HANDLER=@SVX_SHARE_INSTALL_DIR@/events.tcl
|
||||
JITTER_BUFFER_DELAY=400
|
||||
NET_LIMITER_THRESH=-1.0
|
||||
LOCAL_LIMITER_THRESH=-1.0
|
||||
|
||||
[LinkToR4]
|
||||
CONNECT_LOGICS=RepeaterLogic:94:SK3AB,SimplexLogic:92:SK3CD
|
||||
#DEFAULT_ACTIVE=1
|
||||
|
|
|
|||
|
|
@ -38,3 +38,6 @@ SVXSERVER=0.0.6
|
|||
|
||||
# Version for SvxReflector
|
||||
SVXREFLECTOR=1.99.16
|
||||
|
||||
# Version for Usrp support
|
||||
USRPSUPPORT=0.0.1.2
|
||||
|
|
|
|||
Loading…
Reference in New Issue