Add native P25 Reflector and IMBE transcoding

pull/8/head
Doug McLain 3 years ago
parent c30d107e15
commit 8110a3c64c

@ -27,6 +27,7 @@ class xReflector {
public $Interlinks = null;
private $InterlinkXML = null;
private $ReflectorXML = null;
public $TotalNodeCount = null;
public function __construct() {
$this->Nodes = array();
@ -34,6 +35,7 @@ class xReflector {
$this->Peers = array();
$this->Interlinks = array();
$this->Transferinterlink = false;
$this->TotalNodeCount = 5;
}
public function LoadXML() {
@ -457,6 +459,16 @@ class xReflector {
}
return -1;
}
public function SetTotalNodes($n) {
$this->TotalNodeCount = $n;
//error_log(print_r("SetTotalNodes() ".$this->TotalNodeCount.":".$n."\n", TRUE));
}
public function GetTotalNodes() {
//error_log(print_r("GetTotalNodes() ".$this->TotalNodeCount."\n", TRUE));
return $this->TotalNodeCount;
}
}

@ -3,11 +3,10 @@
<th class="col-md-1">#</th>
<th class="col-md-1">Flag</th>
<th class="col-md-2">DV Station</th>
<th class="col-md-1">Band</th>
<th class="col-md-2">Last Heard</th>
<th class="col-md-2">Linked for</th>
<th class="col-md-1">Protocol</th>
<th class="col-md-1">Module</th><?php
<?php
if ($PageOptions['RepeatersPage']['IPModus'] != 'HideIP') {
echo '
@ -17,7 +16,61 @@ if ($PageOptions['RepeatersPage']['IPModus'] != 'HideIP') {
?>
</tr>
<?php
function startsWith($haystack, $needle) {
return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== false;
}
function searchForKey($field, $needle, $array) {
foreach ($array as $key => $val) {
if ($val[$field] === $needle) {
return $key;
}
}
return null;
}
function getLinkedGateways($logLines) {
$gateways = Array();
for ($i = count($logLines); $i>0; $i--) {
if(isset($logLines[$i])){
$logLine = $logLines[$i];
if (strpos($logLine, "Starting P25Reflector")) {
return $gateways;
}
if (strpos($logLine, "No repeaters/gateways linked")) {
return $gateways;
}
if (strpos($logLine, "Currently linked repeaters")) {
for ($j = $i+1; $j <= count($logLines); $j++) {
if(isset($logLines[$j])){
$logLine = $logLines[$j];
if (!startsWith(substr($logLine,27), " ")) {
return $gateways;
} else {
//$Reflector->SetTotalNodes($Reflector->GetTotalNodes() + 1);
$timestamp = substr($logLine, 3, 19);
$callsign = substr($logLine, 31, 11);
//$callsign = explode(" ", $callsign);
$ipport = substr($logLine, 43);
//$ipport = explode(":", $ipport);
$key1 = searchForKey("ipport",$ipport, $gateways);
$key2 = searchForKey("callsign",$callsign, $gateways);
if (($key1 === NULL) && ($key2 == NULL)) {
array_push($gateways, Array('callsign'=>$callsign,'timestamp'=>$timestamp,'ipport'=>$ipport));
}
}
}
}
}
}
}
return $gateways;
}
$Reflector->LoadFlags();
$i = 0;
for ($i=0;$i<$Reflector->NodeCount();$i++) {
@ -29,33 +82,11 @@ for ($i=0;$i<$Reflector->NodeCount();$i++) {
echo '<a href="#" class="tip"><img src="./img/flags/'.$Flag.'.png" class="table-flag" alt="'.$Name.'"><span>'.$Name.'</span></a>';
}
echo '</td>
<td><a href="http://www.aprs.fi/'.$Reflector->Nodes[$i]->GetCallSign();
if ($Reflector->Nodes[$i]->GetSuffix() != "") echo '-'.$Reflector->Nodes[$i]->GetSuffix();
echo '" class="pl" target="_blank">'.$Reflector->Nodes[$i]->GetCallSign();
if ($Reflector->Nodes[$i]->GetSuffix() != "") { echo '-'.$Reflector->Nodes[$i]->GetSuffix(); }
echo '</a></td>
<td>';
if (($Reflector->Nodes[$i]->GetPrefix() == 'REF') || ($Reflector->Nodes[$i]->GetPrefix() == 'XRF')) {
switch ($Reflector->Nodes[$i]->GetPrefix()) {
case 'REF' : echo 'REF-Link'; break;
case 'XRF' : echo 'XRF-Link'; break;
}
}
else {
switch ($Reflector->Nodes[$i]->GetSuffix()) {
case 'A' : echo '23cm'; break;
case 'B' : echo '70cm'; break;
case 'C' : echo '2m'; break;
case 'D' : echo 'Dongle'; break;
case 'G' : echo 'Internet-Gateway'; break;
default : echo '';
}
}
<td>'.$Reflector->Nodes[$i]->GetCallSign();
echo '</td>
<td>'.date("d.m.Y H:i", $Reflector->Nodes[$i]->GetLastHeardTime()).'</td>
<td>'.FormatSeconds(time()-$Reflector->Nodes[$i]->GetConnectTime()).' s</td>
<td>'.$Reflector->Nodes[$i]->GetProtocol().'</td>
<td>'.$Reflector->Nodes[$i]->GetLinkedModule().'</td>';
<td>'.$Reflector->Nodes[$i]->GetProtocol().'</td>';
if ($PageOptions['RepeatersPage']['IPModus'] != 'HideIP') {
echo '<td>';
$Bytes = explode(".", $Reflector->Nodes[$i]->GetIP());
@ -88,9 +119,62 @@ for ($i=0;$i<$Reflector->NodeCount();$i++) {
echo '</td>';
}
echo '</tr>';
if ($i == $PageOptions['RepeatersPage']['LimitTo']) { $i = $Reflector->NodeCount()+1; }
}
//$Reflector->SetTotalNodes($Reflector->NodeCount());
$logLines = array();
//error_log(print_r("logLines ".count($logLines)."\n", TRUE));
if ($log = fopen("/var/log/reflectors/P25-9846-".date("Y-m-d").".log", 'r')) {
while ($logLine = fgets($log)) {
array_push($logLines, $logLine);
}
fclose($log);
}
//error_log(print_r("logLines ".count($logLines)."\n", TRUE));
$gateways = getLinkedGateways($logLines) ;
//error_log(print_r("gateways ".count($gateways)."\n", TRUE));
//$Reflector->SetTotalNodes($Reflector->NodeCount() + count($p25gateways));
foreach ($gateways as $gateway) {
$i += 1;
echo '<tr class="table-center">';
echo "<td>$i</td><td>";
list ($Flag, $Name) = $Reflector->GetFlag($gateway['callsign']);
if (file_exists("./img/flags/".$Flag.".png")) {
echo '<a href="#" class="tip"><img src="./img/flags/'.$Flag.'.png" class="table-flag" alt="'.$Name.'"><span>'.$Name.'</span></a></td>';
}
$cs = explode(" ", $gateway['callsign']);
$ip = explode(":", $gateway['ipport']);
echo "<td>$cs[0]</td><td></td><td></td><td>P25</td><td>$ip[0]</td></tr>";
}
if ($log = fopen("/var/log/reflectors/NXDNReflector-".date("Y-m-d").".log", 'r')) {
while ($logLine = fgets($log)) {
array_push($logLines, $logLine);
}
fclose($log);
}
$gateways = getLinkedGateways($logLines);
foreach ($gateways as $gateway) {
$i += 1;
echo '<tr class="table-center">';
echo "<td>$i</td><td>";
list ($Flag, $Name) = $Reflector->GetFlag($gateway['callsign']);
if (file_exists("./img/flags/".$Flag.".png")) {
echo '<a href="#" class="tip"><img src="./img/flags/'.$Flag.'.png" class="table-flag" alt="'.$Name.'"><span>'.$Name.'</span></a></td>';
}
$cs = explode(" ", $gateway['callsign']);
$ip = explode(":", $gateway['ipport']);
echo "<td>$cs[0]</td><td></td><td></td><td>NXDN</td><td>$ip[0]</td></tr>";
}
?>
</table>

@ -28,6 +28,7 @@ CDvFramePacket::CDvFramePacket()
memset(m_TCPack.dmr, 0, 9);
memset(m_uiDvSync, 0, 7);
memset(m_TCPack.m17, 0, 16);
memset(m_TCPack.p25, 0, 11);
memset(m_Nonce, 0, 14);
m_TCPack.codec_in = ECodecType::none;
};
@ -41,6 +42,7 @@ CDvFramePacket::CDvFramePacket(const SDStarFrame *dvframe, uint16_t sid, uint8_t
memset(m_TCPack.dmr, 0, 9);
memset(m_uiDvSync, 0, 7);
memset(m_TCPack.m17, 0, 16);
memset(m_TCPack.p25, 0, 11);
memset(m_Nonce, 0, 14);
m_TCPack.codec_in = ECodecType::dstar;
}
@ -54,6 +56,7 @@ CDvFramePacket::CDvFramePacket(const uint8_t *ambe, const uint8_t *sync, uint16_
memset(m_TCPack.dstar, 0, 9);
memset(m_uiDvData, 0, 3);
memset(m_TCPack.m17, 0, 16);
memset(m_TCPack.p25, 0, 11);
memset(m_Nonce, 0, 14);
m_TCPack.codec_in = ECodecType::dmr;
}
@ -67,6 +70,7 @@ CDvFramePacket::CDvFramePacket(const uint8_t *ambe, uint16_t sid, uint8_t pid, u
memset(m_TCPack.dstar, 0, 9);
memset(m_uiDvData, 0, 3);
memset(m_TCPack.m17, 0, 16);
memset(m_TCPack.p25, 0, 11);
memset(m_Nonce, 0, 14);
m_TCPack.codec_in = ECodecType::dmr;
uint8_t c[12];
@ -96,6 +100,7 @@ CDvFramePacket::CDvFramePacket(const CM17Packet &m17) : CPacket(m17)
memset(m_uiDvSync, 0, 7);
memcpy(m_TCPack.m17, m17.GetPayload(), 16);
memcpy(m_Nonce, m17.GetNonce(), 14);
memset(m_TCPack.p25, 0, 11);
switch (0x6U & m17.GetFrameType())
{
case 0x4U:
@ -110,6 +115,21 @@ CDvFramePacket::CDvFramePacket(const CM17Packet &m17) : CPacket(m17)
}
}
// p25 constructor
CDvFramePacket::CDvFramePacket(const uint8_t *imbe, uint16_t streamid, bool islast)
: CPacket(streamid, islast)
{
memcpy(m_TCPack.p25, imbe, 11);
memset(m_TCPack.dmr, 0, 9);
memset(m_uiDvSync, 0, 7);
memset(m_TCPack.dstar, 0, 9);
memset(m_uiDvData, 0, 3);
memset(m_TCPack.m17, 0, 16);
memset(m_Nonce, 0, 14);
m_TCPack.codec_in = ECodecType::p25;
}
// Network
unsigned int CDvFramePacket::GetNetworkSize()
{
@ -178,6 +198,8 @@ const uint8_t *CDvFramePacket::GetCodecData(ECodecType type) const
case ECodecType::c2_1600:
case ECodecType::c2_3200:
return m_TCPack.m17;
case ECodecType::p25:
return m_TCPack.p25;
default:
return nullptr;
}

@ -51,6 +51,8 @@ public:
CDvFramePacket(uint16_t streamid, uint8_t counter, const uint8_t *ambe, const uint8_t *dvdata, uint8_t counter1, uint8_t counter2, const uint8_t *ambe2, const uint8_t *dmrsync, ECodecType type, bool islast);
// M17 Frame
CDvFramePacket(const CM17Packet &m17);
// P25 Frame
CDvFramePacket(const uint8_t *imbe, uint16_t streamid, bool islast);
// URF Network
CDvFramePacket(const CBuffer &buf);
static unsigned int GetNetworkSize();

@ -20,7 +20,7 @@
#include "Callsign.h"
#include "Packet.h"
////////////////////////////////////////////////////////////////////////////////////////
// implementation details

@ -89,6 +89,7 @@ bool CGateKeeper::MayLink(const CCallsign &callsign, const CIp &ip, EProtocol pr
case EProtocol::dmrmmdvm:
case EProtocol::ysf:
case EProtocol::m17:
case EProtocol::p25:
#ifndef NO_G3
case EProtocol::g3:
#endif
@ -135,6 +136,7 @@ bool CGateKeeper::MayTransmit(const CCallsign &callsign, const CIp &ip, const EP
case EProtocol::dmrmmdvm:
case EProtocol::ysf:
case EProtocol::m17:
case EProtocol::p25:
#ifndef NO_G3
case EProtocol::g3:
#endif
@ -286,6 +288,8 @@ const std::string CGateKeeper::ProtocolName(const EProtocol p) const
return "URF";
case EProtocol::ysf:
return "YSF";
case EProtocol::p25:
return "P25";
case EProtocol::bm:
return "Brandmeister";
#ifndef NO_G3

@ -50,13 +50,15 @@
#define YSF_IPV4 true
#define XLX_IPV4 true
#define M17_IPV4 true
#define P25_IPV4 true
#define URF_IPV4 true
#define DSTAR_IPV6 true
#define DSTAR_IPV6 false
#define DMR_IPV6 false
#define YSF_IPV6 false
#define XLX_IPV6 false
#define M17_IPV6 true
#define P25_IPV6 false
#define URF_IPV6 true
// version -----------------------------------------------------
@ -77,9 +79,9 @@
// protocols ---------------------------------------------------
#ifndef NO_G3
enum class EProtocol { any, none, dextra, dplus, dcs, bm, urf, dmrplus, dmrmmdvm, ysf, m17, g3 };
enum class EProtocol { any, none, dextra, dplus, dcs, bm, urf, dmrplus, dmrmmdvm, ysf, m17, g3, p25 };
#else
enum class EProtocol { any, none, dextra, dplus, dcs, bm, urf, dmrplus, dmrmmdvm, ysf, m17 };
enum class EProtocol { any, none, dextra, dplus, dcs, bm, urf, dmrplus, dmrmmdvm, ysf, m17, p25 };
#endif
// DExtra
@ -145,6 +147,13 @@ enum class EProtocol { any, none, dextra, dplus, dcs, bm, urf, dmrplus, dmrmmdvm
#define M17_KEEPALIVE_TIMEOUT (M17_KEEPALIVE_PERIOD*10)
#define M17_RECONNECT_PERIOD 5
// P25
#define P25_PORT 41000 // UDP port
#define P25_KEEPALIVE_PERIOD 1 // in seconds
#define P25_KEEPALIVE_TIMEOUT (P25_KEEPALIVE_PERIOD*10) // in seconds
#define P25_AUTOLINK_ENABLE 1 // 1 = enable, 0 = disable auto-link
#define P25_AUTOLINK_MODULE 'A' // module for client to auto-link to
#ifndef NO_G3
// G3 Terminal
#define G3_PRESENCE_PORT 12346 // UDP port

@ -35,7 +35,7 @@ endif
LDFLAGS=-pthread
URFSRCS = Buffer.cpp Callsign.cpp CallsignList.cpp CallsignListItem.cpp Client.cpp Clients.cpp DCSClient.cpp DCSProtocol.cpp DExtraClient.cpp DExtraPeer.cpp DExtraProtocol.cpp DPlusClient.cpp DPlusProtocol.cpp DVFramePacket.cpp DVHeaderPacket.cpp GateKeeper.cpp IP.cpp Notification.cpp Packet.cpp PacketStream.cpp PeerCallsignList.cpp Peer.cpp Peers.cpp Protocol.cpp Protocols.cpp Reflector.cpp SEProtocol.cpp UDPSocket.cpp User.cpp Users.cpp Version.cpp Main.cpp BMClient.cpp BMPeer.cpp BMProtocol.cpp BPTC19696.cpp CRC.cpp DMRIdDir.cpp DMRIdDirFile.cpp DMRIdDirHttp.cpp DMRMMDVMClient.cpp DMRMMDVMProtocol.cpp DMRPlusClient.cpp DMRPlusProtocol.cpp Golay2087.cpp Golay24128.cpp Hamming.cpp M17Client.cpp M17CRC.cpp M17Packet.cpp M17Client.cpp M17Protocol.cpp QR1676.cpp RS129.cpp Semaphore.cpp Utils.cpp WiresXCmd.cpp WiresXCmdHandler.cpp WiresXInfo.cpp URFClient.cpp URFProtocol.cpp URFPeer.cpp YSFClient.cpp YSFConvolution.cpp YSFFich.cpp YSFNode.cpp YSFNodeDir.cpp YSFNodeDirFile.cpp YSFNodeDirHttp.cpp YSFPayload.cpp YSFProtocol.cpp YSFUtils.cpp
URFSRCS = Buffer.cpp Callsign.cpp CallsignList.cpp CallsignListItem.cpp Client.cpp Clients.cpp DCSClient.cpp DCSProtocol.cpp DExtraClient.cpp DExtraPeer.cpp DExtraProtocol.cpp DPlusClient.cpp DPlusProtocol.cpp DVFramePacket.cpp DVHeaderPacket.cpp GateKeeper.cpp IP.cpp Notification.cpp Packet.cpp PacketStream.cpp PeerCallsignList.cpp Peer.cpp Peers.cpp Protocol.cpp Protocols.cpp Reflector.cpp SEProtocol.cpp UDPSocket.cpp User.cpp Users.cpp Version.cpp Main.cpp BMClient.cpp BMPeer.cpp BMProtocol.cpp BPTC19696.cpp CRC.cpp DMRIdDir.cpp DMRIdDirFile.cpp DMRIdDirHttp.cpp DMRMMDVMClient.cpp DMRMMDVMProtocol.cpp DMRPlusClient.cpp DMRPlusProtocol.cpp Golay2087.cpp Golay24128.cpp Hamming.cpp M17Client.cpp M17CRC.cpp M17Packet.cpp M17Client.cpp M17Protocol.cpp P25Client.cpp P25Protocol.cpp QR1676.cpp RS129.cpp Semaphore.cpp Utils.cpp WiresXCmd.cpp WiresXCmdHandler.cpp WiresXInfo.cpp URFClient.cpp URFProtocol.cpp URFPeer.cpp YSFClient.cpp YSFConvolution.cpp YSFFich.cpp YSFNode.cpp YSFNodeDir.cpp YSFNodeDirFile.cpp YSFNodeDirHttp.cpp YSFPayload.cpp YSFProtocol.cpp YSFUtils.cpp
G3SRCS = G3Client.cpp G3Protocol.cpp RawSocket.cpp UDPMsgSocket.cpp

@ -0,0 +1,47 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
// Copyright © 2021 Doug McLain AD8DP
//
// 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 3 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, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "P25Client.h"
////////////////////////////////////////////////////////////////////////////////////////
// constructors
CP25Client::CP25Client()
{
}
CP25Client::CP25Client(const CCallsign &callsign, const CIp &ip, char reflectorModule)
: CClient(callsign, ip, reflectorModule)
{
}
CP25Client::CP25Client(const CP25Client &client)
: CClient(client)
{
}
////////////////////////////////////////////////////////////////////////////////////////
// status
bool CP25Client::IsAlive(void) const
{
return (m_LastKeepaliveTime.time() < P25_KEEPALIVE_TIMEOUT);
}

@ -0,0 +1,41 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
// Copyright © 2021 Doug McLain AD8DP
//
// 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 3 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, see <https://www.gnu.org/licenses/>.
#pragma once
#include "Client.h"
class CP25Client : public CClient
{
public:
// constructors
CP25Client();
CP25Client(const CCallsign &, const CIp &, char = ' ');
CP25Client(const CP25Client &);
// destructor
virtual ~CP25Client() {};
// identity
EProtocol GetProtocol(void) const { return EProtocol::p25; }
const char *GetProtocolName(void) const { return "P25"; }
bool IsNode(void) const { return true; }
// status
bool IsAlive(void) const;
};

@ -0,0 +1,527 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
// Copyright © 2021 Doug McLain AD8DP
//
// 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 3 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, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h>
#include "P25Client.h"
#include "P25Protocol.h"
#include "Reflector.h"
#include "GateKeeper.h"
const uint8_t REC62[] = {0x62U, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x12U, 0x64U, 0x00U, 0x00U, 0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const uint8_t REC63[] = {0x63U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC64[] = {0x64U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC65[] = {0x65U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC66[] = {0x66U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC67[] = {0x67U, 0xF0U, 0x9DU, 0x6AU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC68[] = {0x68U, 0x19U, 0xD4U, 0x26U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC69[] = {0x69U, 0xE0U, 0xEBU, 0x7BU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC6A[] = {0x6AU, 0x00U, 0x00U, 0x02U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const uint8_t REC6B[] = {0x6BU, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x12U, 0x64U, 0x00U, 0x00U, 0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const uint8_t REC6C[] = {0x6CU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC6D[] = {0x6DU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC6E[] = {0x6EU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC6F[] = {0x6FU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC70[] = {0x70U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC71[] = {0x71U, 0xACU, 0xB8U, 0xA4U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC72[] = {0x72U, 0x9BU, 0xDCU, 0x75U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const uint8_t REC73[] = {0x73U, 0x00U, 0x00U, 0x02U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const uint8_t REC80[] = {0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
////////////////////////////////////////////////////////////////////////////////////////
// operation
bool CP25Protocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6)
{
m_uiStreamId = 0;
// base class
if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6))
return false;
// update time
m_LastKeepaliveTime.start();
// done
return true;
}
////////////////////////////////////////////////////////////////////////////////////////
// task
void CP25Protocol::Task(void)
{
CBuffer Buffer;
CIp Ip;
CCallsign Callsign;
char ToLinkModule;
std::unique_ptr<CDvHeaderPacket> Header;
std::unique_ptr<CDvFramePacket> Frame;
// handle incoming packets
#if P25_IPV6==true
#if P25_IPV4==true
if ( ReceiveDS(Buffer, Ip, 20) )
#else
if ( Receive6(Buffer, Ip, 20) )
#endif
#else
if ( Receive4(Buffer, Ip, 20) )
#endif
{
// crack the packet
if ( IsValidDvPacket(Ip, Buffer, Frame) )
{
if( !m_uiStreamId && IsValidDvHeaderPacket(Ip, Buffer, Header) )
{
// callsign muted?
if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, EProtocol::p25) )
{
OnDvHeaderPacketIn(Header, Ip);
}
}
// push the packet
OnDvFramePacketIn(Frame, &Ip);
}
else if ( IsValidConnectPacket(Buffer, &Callsign) )
{
// callsign authorized?
if ( g_GateKeeper.MayLink(Callsign, Ip, EProtocol::p25) )
{
// add client if needed
CClients *clients = g_Reflector.GetClients();
std::shared_ptr<CClient>client = clients->FindClient(Callsign, Ip, EProtocol::p25);
// client already connected ?
if ( client == nullptr )
{
std::cout << "P25 connect packet from " << Callsign << " at " << Ip << std::endl;
// create the client
auto newclient = std::make_shared<CP25Client>(Callsign, Ip);
// aautolink, if enabled
#if P25_AUTOLINK_ENABLE
newclient->SetReflectorModule(P25_AUTOLINK_MODULE);
#endif
// and append
clients->AddClient(newclient);
}
else
{
client->Alive();
}
// acknowledge the request -- P25Reflector simply echoes the packet
Send(Buffer, Ip);
// and done
g_Reflector.ReleaseClients();
}
}
else if ( IsValidDisconnectPacket(Buffer, &Callsign) )
{
std::cout << "P25 disconnect packet from " << Callsign << " at " << Ip << std::endl;
// find client
CClients *clients = g_Reflector.GetClients();
std::shared_ptr<CClient>client = clients->FindClient(Ip, EProtocol::m17);
if ( client != nullptr )
{
// remove it
clients->RemoveClient(client);
}
g_Reflector.ReleaseClients();
}
else
{
// invalid packet
std::string title("Unknown P25 packet from ");
title += Ip.GetAddress();
Buffer.Dump(title);
}
}
// handle end of streaming timeout
CheckStreamsTimeout();
// handle queue from reflector
HandleQueue();
// keep client alive
if ( m_LastKeepaliveTime.time() > P25_KEEPALIVE_PERIOD )
{
//
HandleKeepalives();
// update time
m_LastKeepaliveTime.start();
}
}
////////////////////////////////////////////////////////////////////////////////////////
// streams helpers
void CP25Protocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Header, const CIp &Ip)
{
// find the stream
auto stream = GetStream(Header->GetStreamId(), &Ip);
if ( stream )
{
// stream already open
// skip packet, but tickle the stream
stream->Tickle();
}
else
{
// no stream open yet, open a new one
CCallsign my(Header->GetMyCallsign());
CCallsign rpt1(Header->GetRpt1Callsign());
CCallsign rpt2(Header->GetRpt2Callsign());
// find this client
std::shared_ptr<CClient>client = g_Reflector.GetClients()->FindClient(Ip, EProtocol::p25);
if ( client )
{
// get client callsign
rpt1 = client->GetCallsign();
auto m = client->GetReflectorModule();
Header->SetRpt2Module(m);
rpt2.SetCSModule(m);
// and try to open the stream
if ( (stream = g_Reflector.OpenStream(Header, client)) != nullptr )
{
// keep the handle
m_Streams[stream->GetStreamId()] = stream;
}
}
// release
g_Reflector.ReleaseClients();
// update last heard
g_Reflector.GetUsers()->Hearing(my, rpt1, rpt2);
g_Reflector.ReleaseUsers();
}
}
////////////////////////////////////////////////////////////////////////////////////////
// queue helper
void CP25Protocol::HandleQueue(void)
{
m_Queue.Lock();
while ( !m_Queue.empty() )
{
// get the packet
auto packet = m_Queue.pop();
// get our sender's id
const auto module = packet->GetPacketModule();
// check if it's header and update cache
if ( packet->IsDvHeader() )
{
// this relies on queue feeder setting valid module id
// m_StreamsCache[module] will be created if it doesn't exist
m_StreamsCache[module].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet.get());
m_StreamsCache[module].m_iSeqCounter = 0;
}
else
{
// encode it
CBuffer buffer;
if ( packet->IsDvFrame() )
{
EncodeP25Packet(m_StreamsCache[module].m_dvHeader, (const CDvFramePacket &)*packet.get(), m_StreamsCache[module].m_iSeqCounter++, buffer, packet->IsLastPacket());
}
// send it
if ( buffer.size() > 0 )
{
// and push it to all our clients linked to the module and who are not streaming in
CClients *clients = g_Reflector.GetClients();
auto it = clients->begin();
std::shared_ptr<CClient>client = nullptr;
while ( (client = clients->FindNextClient(EProtocol::p25, it)) != nullptr )
{
// is this client busy ?
if ( !client->IsAMaster() && (client->GetReflectorModule() == module) )
{
// no, send the packet
Send(buffer, client->GetIp());
}
}
g_Reflector.ReleaseClients();
}
}
}
m_Queue.Unlock();
}
////////////////////////////////////////////////////////////////////////////////////////
// packet decoding helpers
bool CP25Protocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign)
{
bool valid = false;
if ( (Buffer.size() == 11) && (Buffer.data()[0] == 0xF0) )
{
callsign->SetCallsign(Buffer.data()+1, 10);
valid = (callsign->IsValid());
}
return valid;
}
bool CP25Protocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign)
{
bool valid = false;
if ( (Buffer.size() == 11) && (Buffer.data()[0] == 0xF1) )
{
callsign->SetCallsign(Buffer.data()+1, 10);
valid = (callsign->IsValid());
}
return valid;
}
bool CP25Protocol::IsValidDvPacket(const CIp &Ip, const CBuffer &Buffer, std::unique_ptr<CDvFramePacket> &frame)
{
if ( (Buffer.size() >= 14) )
{
int offset = 0;
bool last = false;
switch (Buffer.data()[0U]) {
case 0x62U:
offset = 10U;
break;
case 0x63U:
offset = 1U;
break;
case 0x64U:
offset = 5U;
break;
case 0x65U:
offset = 5U;
break;
case 0x66U:
offset = 5U;
break;
case 0x67U:
case 0x68U:
case 0x69U:
offset = 5U;
break;
case 0x6AU:
offset = 4U;
break;
case 0x6BU:
offset = 10U;
break;
case 0x6CU:
offset = 1U;
break;
case 0x6DU:
case 0x6EU:
case 0x6FU:
case 0x70U:
case 0x71U:
case 0x72U:
offset = 5U;
break;
case 0x73U:
offset = 4U;
break;
case 0x80U:
last = true;
m_uiStreamId = 0;
break;
default:
break;
}
frame = std::unique_ptr<CDvFramePacket>(new CDvFramePacket(&(Buffer.data()[offset]), m_uiStreamId, last));
return true;
}
return false;
}
bool CP25Protocol::IsValidDvHeaderPacket(const CIp &Ip, const CBuffer &Buffer, std::unique_ptr<CDvHeaderPacket> &header)
{
if(Buffer.data()[0] == 0x66){
auto stream = GetStream(m_uiStreamId, &Ip);
if ( !stream )
{
uint32_t uiSrcId = ((Buffer.data()[1] << 16) | ((Buffer.data()[2] << 8) & 0xff00) | (Buffer.data()[3] & 0xff));
m_uiStreamId = static_cast<uint32_t>(::rand());
CCallsign csMY = CCallsign("", uiSrcId);
CCallsign rpt1 = CCallsign("", uiSrcId);
CCallsign rpt2 = m_ReflectorCallsign;
rpt1.SetCSModule(P25_MODULE_ID);
rpt2.SetCSModule(' ');
header = std::unique_ptr<CDvHeaderPacket>(new CDvHeaderPacket(csMY, CCallsign("CQCQCQ"), rpt1, rpt2, m_uiStreamId, 0));
}
return true;
}
return false;
}
void CP25Protocol::EncodeP25Packet(const CDvHeaderPacket &Header, const CDvFramePacket &DvFrame, uint32_t iSeq, CBuffer &Buffer, bool islast) const
{
uint32_t uiSrcId = Header.GetMyCallsign().GetDmrid();
uint32_t uiRptrId = Header.GetRpt1Callsign().GetDmrid();
if(islast)
{
Buffer.resize(17);
::memcpy(Buffer.data(), REC80, 17U);
return;
}
switch (iSeq % 18) {
case 0x00U:
Buffer.resize(22);
::memcpy(Buffer.data(), REC62, 22U);
::memcpy(Buffer.data() + 10U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x01U:
Buffer.resize(14);
::memcpy(Buffer.data(), REC63, 14U);
::memcpy(Buffer.data() + 1U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x02U:
Buffer.resize(17);
::memcpy(Buffer.data(), REC64, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x03U:
Buffer.resize(17);
::memcpy(Buffer.data(), REC65, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
Buffer.data()[1U] = (uiRptrId >> 16) & 0xFFU;
Buffer.data()[2U] = (uiRptrId >> 8) & 0xFFU;
Buffer.data()[3U] = (uiRptrId >> 0) & 0xFFU;
break;
case 0x04U:
Buffer.resize(17);
::memcpy(Buffer.data(), REC66, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
Buffer.data()[1U] = (uiSrcId >> 16) & 0xFFU;
Buffer.data()[2U] = (uiSrcId >> 8) & 0xFFU;
Buffer.data()[3U] = (uiSrcId >> 0) & 0xFFU;
break;
case 0x05U:
Buffer.resize(17);
::memcpy(Buffer.data(), REC67, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x06U:
Buffer.resize(17);
::memcpy(Buffer.data(), REC68, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x07U:
Buffer.resize(17);
::memcpy(Buffer.data(), REC69, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x08U:
Buffer.resize(16);
::memcpy(Buffer.data(), REC6A, 16U);
::memcpy(Buffer.data() + 4U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x09U:
Buffer.resize(22);
::memcpy(Buffer.data(), REC6B, 22U);
::memcpy(Buffer.data() + 10U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x0AU:
Buffer.resize(14);
::memcpy(Buffer.data(), REC6C, 14U);
::memcpy(Buffer.data() + 1U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x0BU:
Buffer.resize(17);
::memcpy(Buffer.data(), REC6D, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x0CU:
Buffer.resize(17);
::memcpy(Buffer.data(), REC6E, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x0DU:
Buffer.resize(17);
::memcpy(Buffer.data(), REC6F, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x0EU:
Buffer.resize(17);
::memcpy(Buffer.data(), REC70, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x0FU:
Buffer.resize(17);
::memcpy(Buffer.data(), REC71, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x10U:
Buffer.resize(17);
::memcpy(Buffer.data(), REC72, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
case 0x11U:
Buffer.resize(16);
::memcpy(Buffer.data(), REC73, 16U);
::memcpy(Buffer.data() + 4U, DvFrame.GetCodecData(ECodecType::p25), 11U);
break;
}
}
////////////////////////////////////////////////////////////////////////////////////////
// keepalive helpers
void CP25Protocol::HandleKeepalives(void)
{
// iterate on clients
CClients *clients = g_Reflector.GetClients();
auto it = clients->begin();
std::shared_ptr<CClient>client = nullptr;
while ( (client = clients->FindNextClient(EProtocol::p25, it)) != nullptr )
{
// is this client busy ?
if ( client->IsAMaster() )
{
// yes, just tickle it
client->Alive();
}
// check it's still with us
else if ( !client->IsAlive() )
{
// no, remove it
std::cout << "P25 client " << client->GetCallsign() << " keepalive timeout" << std::endl;
clients->RemoveClient(client);
}
}
g_Reflector.ReleaseClients();
}

@ -0,0 +1,75 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
// Copyright © 2021 Doug McLain AD8DP
//
// 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 3 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, see <https://www.gnu.org/licenses/>.
#pragma once
#include "Timer.h"
#include "Protocol.h"
#include "DVHeaderPacket.h"
#include "DVFramePacket.h"
////////////////////////////////////////////////////////////////////////////////////////
// define
#define P25_MODULE_ID 'B'
////////////////////////////////////////////////////////////////////////////////////////
// class
class CP25StreamCacheItem
{
public:
CP25StreamCacheItem() : m_iSeqCounter(0) {}
CDvHeaderPacket m_dvHeader;
uint32_t m_iSeqCounter;
};
class CP25Protocol : public CProtocol
{
public:
// initialization
bool Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6);
// task
void Task(void);
protected:
// queue helper
void HandleQueue(void);
void HandleKeepalives(void);
// stream helpers
void OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &, const CIp &);
// packet decoding helpers
bool IsValidConnectPacket(const CBuffer &, CCallsign *);
bool IsValidDisconnectPacket(const CBuffer &, CCallsign *);
bool IsValidDvPacket(const CIp &, const CBuffer &, std::unique_ptr<CDvFramePacket> &);
bool IsValidDvHeaderPacket(const CIp &, const CBuffer &, std::unique_ptr<CDvHeaderPacket> &);
// packet encoding helpers
void EncodeP25Packet(const CDvHeaderPacket &, const CDvFramePacket &, uint32_t, CBuffer &Buffer, bool) const;
// for keep alive
CTimer m_LastKeepaliveTime;
// for queue header caches
std::unordered_map<char, CP25StreamCacheItem> m_StreamsCache;
uint32_t m_uiStreamId;
};

@ -138,6 +138,23 @@ CPacket::CPacket(uint16_t sid, uint8_t ysfpid, uint8_t ysfsubpid, uint8_t ysffri
m_bLastPacket = lastpacket;
}
// p25 constructor
CPacket::CPacket(uint16_t sid, bool lastpacket)
{
m_uiStreamId = sid;
m_uiDstarPacketId = 0xFF;
m_uiDmrPacketId = 0xFF;
m_uiDmrPacketSubid = 0xFF;
m_uiYsfPacketId = 0xFF;
m_uiYsfPacketSubId = 0xFF;
m_uiYsfPacketFrameId = 0xFF;
m_uiM17FrameNumber = 0xFFFFFFFFU;
m_cModule = ' ';
m_eOrigin = EOrigin::local;
m_eCodecIn = ECodecType::p25;
m_bLastPacket = lastpacket;
};
// bm constructor
CPacket::CPacket(uint16_t sid, uint8_t dstarpid, uint8_t dmrpid, uint8_t dmrsubpid, uint8_t ysfpid, uint8_t ysfsubpid, uint8_t ysffrid, ECodecType codecIn, bool lastpacket)
{

@ -34,6 +34,7 @@ public:
CPacket(const CBuffer &Buffer);
CPacket(uint16_t sid, uint8_t dstarpid);
CPacket(uint16_t sid, uint8_t dmrpid, uint8_t dmrsubpid, bool lastpacket);
CPacket(uint16_t sid, bool lastpacket);
CPacket(uint16_t sid, uint8_t ysfpid, uint8_t ysfsubpid, uint8_t ysfsubpidmax, bool lastpacket);
CPacket(uint16_t sid, uint8_t dstarpid, uint8_t dmrpid, uint8_t dmrsubpid, uint8_t ysfpid, uint8_t ysfsubpid, uint8_t ysfsubpidmax, ECodecType, bool lastpacket);
CPacket(const CM17Packet &);

@ -26,6 +26,7 @@
#include "YSFProtocol.h"
#include "M17Protocol.h"
#include "BMProtocol.h"
#include "P25Protocol.h"
#ifndef NO_G3
#include "G3Protocol.h"
#endif
@ -77,6 +78,10 @@ bool CProtocols::Init(void)
m_Protocols.emplace_back(std::unique_ptr<CM17Protocol>(new CM17Protocol));
if (! m_Protocols.back()->Initialize("URF", EProtocol::m17, M17_PORT, M17_IPV4, M17_IPV6))
return false;
m_Protocols.emplace_back(std::unique_ptr<CP25Protocol>(new CP25Protocol));
if (! m_Protocols.back()->Initialize("P25", EProtocol::p25, P25_PORT, P25_IPV4, P25_IPV6))
return false;
m_Protocols.emplace_back(std::unique_ptr<CURFProtocol>(new CURFProtocol));
if (! m_Protocols.back()->Initialize("URF", EProtocol::urf, URF_PORT, URF_IPV4, URF_IPV6))

@ -23,7 +23,7 @@
#define TC2REF "TC2URFMod"
#define REF2TC "URF2TC"
enum class ECodecType : std::uint8_t { none = 0, dstar = 1, dmr = 2, c2_1600 = 3, c2_3200 = 4 };
enum class ECodecType : std::uint8_t { none = 0, dstar = 1, dmr = 2, c2_1600 = 3, c2_3200 = 4, p25 = 5 };
using STCPacket = struct tcpacket_tag {
CTimer rt_timer;
@ -35,4 +35,5 @@ using STCPacket = struct tcpacket_tag {
uint8_t dstar[9];
uint8_t dmr[9];
uint8_t m17[16];
uint8_t p25[11];
};

Loading…
Cancel
Save

Powered by TurnKey Linux.