Merge remote-tracking branch 'g4klx/master'

This commit is contained in:
Andy CA6JAU 2017-01-08 18:32:38 -03:00
commit 2047204a86
45 changed files with 897 additions and 292 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -107,6 +107,8 @@ m_dmrSelfOnly(false),
m_dmrPrefixes(),
m_dmrBlackList(),
m_dmrWhiteList(),
m_dmrSlot1TGWhiteList(),
m_dmrSlot2TGWhiteList(),
m_dmrCallHang(3U),
m_dmrTXHang(4U),
m_fusionEnabled(false),
@ -128,7 +130,6 @@ m_dmrNetworkDebug(false),
m_dmrNetworkJitter(300U),
m_dmrNetworkSlot1(true),
m_dmrNetworkSlot2(true),
m_dmrNetworkRSSI(false),
m_fusionNetworkEnabled(false),
m_fusionNetworkMyAddress(),
m_fusionNetworkMyPort(0U),
@ -393,6 +394,22 @@ bool CConf::read()
m_dmrWhiteList.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "Slot1TGWhiteList") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrSlot1TGWhiteList.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "Slot2TGWhiteList") == 0) {
char* p = ::strtok(value, ",\r\n");
while (p != NULL) {
unsigned int id = (unsigned int)::atoi(p);
if (id > 0U)
m_dmrSlot2TGWhiteList.push_back(id);
p = ::strtok(NULL, ",\r\n");
}
} else if (::strcmp(key, "TXHang") == 0)
m_dmrTXHang = (unsigned int)::atoi(value);
else if (::strcmp(key, "CallHang") == 0)
@ -439,8 +456,6 @@ bool CConf::read()
m_dmrNetworkSlot1 = ::atoi(value) == 1;
else if (::strcmp(key, "Slot2") == 0)
m_dmrNetworkSlot2 = ::atoi(value) == 1;
else if (::strcmp(key, "RSSI") == 0)
m_dmrNetworkRSSI = ::atoi(value) == 1;
} else if (section == SECTION_FUSION_NETWORK) {
if (::strcmp(key, "Enable") == 0)
m_fusionNetworkEnabled = ::atoi(value) == 1;
@ -801,6 +816,16 @@ std::vector<unsigned int> CConf::getDMRWhiteList() const
return m_dmrWhiteList;
}
std::vector<unsigned int> CConf::getDMRSlot1TGWhiteList() const
{
return m_dmrSlot1TGWhiteList;
}
std::vector<unsigned int> CConf::getDMRSlot2TGWhiteList() const
{
return m_dmrSlot2TGWhiteList;
}
unsigned int CConf::getDMRCallHang() const
{
return m_dmrCallHang;
@ -906,11 +931,6 @@ bool CConf::getDMRNetworkSlot2() const
return m_dmrNetworkSlot2;
}
bool CConf::getDMRNetworkRSSI() const
{
return m_dmrNetworkRSSI;
}
bool CConf::getFusionNetworkEnabled() const
{
return m_fusionNetworkEnabled;

8
Conf.h
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -100,6 +100,8 @@ public:
std::vector<unsigned int> getDMRPrefixes() const;
std::vector<unsigned int> getDMRBlackList() const;
std::vector<unsigned int> getDMRWhiteList() const;
std::vector<unsigned int> getDMRSlot1TGWhiteList() const;
std::vector<unsigned int> getDMRSlot2TGWhiteList() const;
unsigned int getDMRCallHang() const;
unsigned int getDMRTXHang() const;
@ -129,7 +131,6 @@ public:
unsigned int getDMRNetworkJitter() const;
bool getDMRNetworkSlot1() const;
bool getDMRNetworkSlot2() const;
bool getDMRNetworkRSSI() const;
// The System Fusion Network section
bool getFusionNetworkEnabled() const;
@ -245,6 +246,8 @@ private:
std::vector<unsigned int> m_dmrPrefixes;
std::vector<unsigned int> m_dmrBlackList;
std::vector<unsigned int> m_dmrWhiteList;
std::vector<unsigned int> m_dmrSlot1TGWhiteList;
std::vector<unsigned int> m_dmrSlot2TGWhiteList;
unsigned int m_dmrCallHang;
unsigned int m_dmrTXHang;
@ -270,7 +273,6 @@ private:
unsigned int m_dmrNetworkJitter;
bool m_dmrNetworkSlot1;
bool m_dmrNetworkSlot2;
bool m_dmrNetworkRSSI;
bool m_fusionNetworkEnabled;
std::string m_fusionNetworkMyAddress;

View file

@ -1,6 +1,6 @@
/*
* Copyright (C) 2016 by Simon Rune G7RZU
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -28,20 +28,25 @@ std::vector<unsigned int> CDMRAccessControl::m_whiteList;
std::vector<unsigned int> CDMRAccessControl::m_prefixes;
std::vector<unsigned int> CDMRAccessControl::m_slot1TGWhiteList;
std::vector<unsigned int> CDMRAccessControl::m_slot2TGWhiteList;
bool CDMRAccessControl::m_selfOnly = false;
unsigned int CDMRAccessControl::m_id = 0U;
void CDMRAccessControl::init(const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, bool selfOnly, const std::vector<unsigned int>& prefixes, unsigned int id)
void CDMRAccessControl::init(const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, bool selfOnly, const std::vector<unsigned int>& prefixes, unsigned int id)
{
m_blackList = blacklist;
m_whiteList = whitelist;
m_selfOnly = selfOnly;
m_prefixes = prefixes;
m_id = id;
m_slot1TGWhiteList = slot1TGWhitelist;
m_slot2TGWhiteList = slot2TGWhitelist;
m_blackList = blacklist;
m_whiteList = whitelist;
m_selfOnly = selfOnly;
m_prefixes = prefixes;
m_id = id;
}
bool CDMRAccessControl::validateId(unsigned int id)
bool CDMRAccessControl::validateSrcId(unsigned int id)
{
if (m_selfOnly)
return id == m_id;
@ -64,3 +69,21 @@ bool CDMRAccessControl::validateId(unsigned int id)
return true;
}
bool CDMRAccessControl::validateTGId(unsigned int slotNo, bool group, unsigned int id)
{
if (!group)
return true;
if (slotNo == 1U) {
if (m_slot1TGWhiteList.empty())
return true;
return std::find(m_slot1TGWhiteList.begin(), m_slot1TGWhiteList.end(), id) != m_slot1TGWhiteList.end();
} else {
if (m_slot2TGWhiteList.empty())
return true;
return std::find(m_slot2TGWhiteList.begin(), m_slot2TGWhiteList.end(), id) != m_slot2TGWhiteList.end();
}
}

View file

@ -1,6 +1,6 @@
/*
* Copyright (C) 2016 by Simon Rune G7RZU
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -20,20 +20,23 @@
#include <vector>
#include "DMRLC.h"
class CDMRAccessControl {
public:
static bool validateId(unsigned int id);
static bool validateSrcId(unsigned int id);
static void init(const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, bool selfOnly, const std::vector<unsigned int>& prefixes, unsigned int id);
static bool validateTGId(unsigned int slotNo, bool group, unsigned int id);
static void init(const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, bool selfOnly, const std::vector<unsigned int>& prefixes, unsigned int id);
private:
static std::vector<unsigned int> m_blackList;
static std::vector<unsigned int> m_whiteList;
static std::vector<unsigned int> m_prefixes;
static std::vector<unsigned int> m_slot1TGWhiteList;
static std::vector<unsigned int> m_slot2TGWhiteList;
static bool m_selfOnly;
static unsigned int m_id;
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
*
* 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
@ -21,7 +21,7 @@
#include <cassert>
#include <algorithm>
CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter) :
CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter) :
m_id(id),
m_colorCode(colorCode),
m_modem(modem),
@ -37,7 +37,7 @@ m_lookup(lookup)
assert(rssi != NULL);
// Load black and white lists to DMRAccessControl
CDMRAccessControl::init(blacklist, whitelist, selfOnly, prefixes, id);
CDMRAccessControl::init(blacklist, whitelist, slot1TGWhitelist, slot2TGWhitelist, selfOnly, prefixes, id);
CDMRSlot::init(colorCode, callHang, modem, network, display, duplex, m_lookup, rssi, jitter);
}
@ -68,7 +68,7 @@ bool CDMRControl::processWakeup(const unsigned char* data)
std::string src = m_lookup->find(srcId);
bool ret = CDMRAccessControl::validateId(srcId);
bool ret = CDMRAccessControl::validateSrcId(srcId);
if (!ret) {
LogMessage("Invalid CSBK BS_Dwn_Act received from %s", src.c_str());
return false;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -31,7 +31,7 @@
class CDMRControl {
public:
CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter);
CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter);
~CDMRControl();
bool processWakeup(const unsigned char* data);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -26,21 +26,26 @@
#include <cassert>
#include <cstring>
CDMREmbeddedLC::CDMREmbeddedLC() :
m_rawLC(NULL),
m_state(LCS_NONE)
CDMREmbeddedLC::CDMREmbeddedLC(unsigned int slotNo) :
m_slotNo(slotNo),
m_raw(NULL),
m_state(LCS_NONE),
m_data(NULL),
m_FLCO(FLCO_GROUP),
m_valid(false)
{
// Allow for multi-block non embedded LC data
m_rawLC = new bool[300U];
m_raw = new bool[128U];
m_data = new bool[72U];
}
CDMREmbeddedLC::~CDMREmbeddedLC()
{
delete[] m_rawLC;
delete[] m_raw;
delete[] m_data;
}
// Add LC data (which may consist of 4 blocks) to the data store
CDMRLC* CDMREmbeddedLC::addData(const unsigned char* data, unsigned char lcss)
bool CDMREmbeddedLC::addData(const unsigned char* data, unsigned char lcss)
{
assert(data != NULL);
@ -54,49 +59,48 @@ CDMRLC* CDMREmbeddedLC::addData(const unsigned char* data, unsigned char lcss)
// Is this the first block of a 4 block embedded LC ?
if (lcss == 1U) {
for (unsigned int a = 0U; a < 32U; a++)
m_rawLC[a] = rawData[a + 4U];
m_raw[a] = rawData[a + 4U];
// Show we are ready for the next LC block
m_state = LCS_FIRST;
return NULL;
m_valid = false;
return false;
}
// Is this the 2nd block of a 4 block embedded LC ?
if (lcss == 3U && m_state == LCS_FIRST) {
for (unsigned int a = 0U; a < 32U; a++)
m_rawLC[a + 32U] = rawData[a + 4U];
m_raw[a + 32U] = rawData[a + 4U];
// Show we are ready for the next LC block
m_state = LCS_SECOND;
return NULL;
return false;
}
// Is this the 3rd block of a 4 block embedded LC ?
if (lcss == 3U && m_state == LCS_SECOND) {
for (unsigned int a = 0U; a < 32U; a++)
m_rawLC[a + 64U] = rawData[a + 4U];
m_raw[a + 64U] = rawData[a + 4U];
// Show we are ready for the final LC block
m_state = LCS_THIRD;
return NULL;
return false;
}
// Is this the final block of a 4 block embedded LC ?
if (lcss == 2U && m_state == LCS_THIRD) {
for (unsigned int a = 0U; a < 32U; a++)
m_rawLC[a + 96U] = rawData[a + 4U];
m_raw[a + 96U] = rawData[a + 4U];
// Process the complete data block
return processMultiBlockEmbeddedLC();
return processEmbeddedData();
}
// Is this a single block embedded LC
if (lcss == 0U) {
processSingleBlockEmbeddedLC(rawData + 4U);
return NULL;
}
if (lcss == 0U)
return false;
return NULL;
return false;
}
void CDMREmbeddedLC::setData(const CDMRLC& lc)
@ -143,7 +147,7 @@ void CDMREmbeddedLC::setData(const CDMRLC& lc)
// The data is packed downwards in columns
b = 0U;
for (unsigned int a = 0U; a < 128U; a++) {
m_rawLC[a] = data[b];
m_raw[a] = data[b];
b += 16U;
if (b > 127U)
b -= 127U;
@ -159,7 +163,7 @@ unsigned char CDMREmbeddedLC::getData(unsigned char* data, unsigned char n) cons
bool bits[40U];
::memset(bits, 0x00U, 40U * sizeof(bool));
::memcpy(bits + 4U, m_rawLC + n * 32U, 32U * sizeof(bool));
::memcpy(bits + 4U, m_raw + n * 32U, 32U * sizeof(bool));
unsigned char bytes[5U];
CUtils::bitsToByteBE(bits + 0U, bytes[0U]);
@ -194,7 +198,7 @@ unsigned char CDMREmbeddedLC::getData(unsigned char* data, unsigned char n) cons
}
// Unpack and error check an embedded LC
CDMRLC* CDMREmbeddedLC::processMultiBlockEmbeddedLC()
bool CDMREmbeddedLC::processEmbeddedData()
{
// The data is unpacked downwards in columns
bool data[128U];
@ -202,7 +206,7 @@ CDMRLC* CDMREmbeddedLC::processMultiBlockEmbeddedLC()
unsigned int b = 0U;
for (unsigned int a = 0U; a < 128U; a++) {
data[b] = m_rawLC[a];
data[b] = m_raw[a];
b += 16U;
if (b > 127U)
b -= 127U;
@ -211,33 +215,32 @@ CDMRLC* CDMREmbeddedLC::processMultiBlockEmbeddedLC()
// Hamming (16,11,4) check each row except the last one
for (unsigned int a = 0U; a < 112U; a += 16U) {
if (!CHamming::decode16114(data + a))
return NULL;
return false;
}
// Check the parity bits
for (unsigned int a = 0U; a < 16U; a++) {
bool parity = data[a + 0U] ^ data[a + 16U] ^ data[a + 32U] ^ data[a + 48U] ^ data[a + 64U] ^ data[a + 80U] ^ data[a + 96U] ^ data[a + 112U];
if (parity)
return NULL;
return false;
}
// We have passed the Hamming check so extract the actual payload
bool lcData[72U];
b = 0U;
for (unsigned int a = 0U; a < 11U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 16U; a < 27U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 32U; a < 42U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 48U; a < 58U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 64U; a < 74U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 80U; a < 90U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
for (unsigned int a = 96U; a < 106U; a++, b++)
lcData[b] = data[a];
m_data[b] = data[a];
// Extract the 5 bit CRC
unsigned int crc = 0U;
@ -248,45 +251,65 @@ CDMRLC* CDMREmbeddedLC::processMultiBlockEmbeddedLC()
if (data[106]) crc += 1U;
// Now CRC check this
if (!CCRC::checkFiveBit(lcData, crc))
return NULL;
if (!CCRC::checkFiveBit(m_data, crc))
return false;
CDMRLC* lc = new CDMRLC(lcData);
m_valid = true;
// Extract the FLCO
unsigned char flco;
CUtils::bitsToByteBE(m_data + 0U, flco);
m_FLCO = FLCO(flco & 0x3FU);
char text[80U];
// Only generate the LC when it's the correct FLCO
switch (lc->getFLCO()) {
switch (m_FLCO) {
case FLCO_GROUP:
case FLCO_USER_USER:
return lc;
// ::sprintf(text, "DMR Slot %u, Embedded LC Data", m_slotNo);
// CUtils::dump(1U, text, m_data, 72U);
return true;
case FLCO_GPS_INFO:
CUtils::dump(1U, "DMR, Embedded GPS Info", lcData, 72U);
delete lc;
return NULL;
::sprintf(text, "DMR Slot %u, Embedded GPS Info", m_slotNo);
CUtils::dump(1U, text, m_data, 72U);
return true;
case FLCO_TALKER_ALIAS_HEADER:
CUtils::dump(1U, "DMR, Embedded Talker Alias Header", lcData, 72U);
delete lc;
return NULL;
::sprintf(text, "DMR Slot %u, Embedded Talker Alias Header", m_slotNo);
CUtils::dump(1U, text, m_data, 72U);
return true;
case FLCO_TALKER_ALIAS_BLOCK1:
CUtils::dump(1U, "DMR, Embedded Talker Alias Block 1", lcData, 72U);
delete lc;
return NULL;
::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 1", m_slotNo);
CUtils::dump(1U, text, m_data, 72U);
return true;
case FLCO_TALKER_ALIAS_BLOCK2:
CUtils::dump(1U, "DMR, Embedded Talker Alias Block 2", lcData, 72U);
delete lc;
return NULL;
::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 2", m_slotNo);
CUtils::dump(1U, text, m_data, 72U);
return true;
case FLCO_TALKER_ALIAS_BLOCK3:
CUtils::dump(1U, "DMR, Embedded Talker Alias Block 3", lcData, 72U);
delete lc;
return NULL;
::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 3", m_slotNo);
CUtils::dump(1U, text, m_data, 72U);
return true;
default:
CUtils::dump(1U, "DMR, Unknown Embedded Data", lcData, 72U);
delete lc;
return NULL;
::sprintf(text, "DMR Slot %u, Unknown Embedded Data", m_slotNo);
CUtils::dump(1U, text, m_data, 72U);
return false;
}
}
// Deal with a single block embedded LC
void CDMREmbeddedLC::processSingleBlockEmbeddedLC(const bool* data)
CDMRLC* CDMREmbeddedLC::getLC() const
{
// Nothing interesting, or just NULL (I think)
if (!m_valid)
return NULL;
if (m_FLCO != FLCO_GROUP && m_FLCO != FLCO_USER_USER)
return NULL;
return new CDMRLC(m_data);
}
void CDMREmbeddedLC::reset()
{
m_state = LCS_NONE;
m_valid = false;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -19,6 +19,7 @@
#ifndef DMREmbeddedLC_H
#define DMREmbeddedLC_H
#include "DMRDefines.h"
#include "DMRLC.h"
enum LC_STATE {
@ -31,20 +32,27 @@ enum LC_STATE {
class CDMREmbeddedLC
{
public:
CDMREmbeddedLC();
CDMREmbeddedLC(unsigned int slotNo);
~CDMREmbeddedLC();
CDMRLC* addData(const unsigned char* data, unsigned char lcss);
bool addData(const unsigned char* data, unsigned char lcss);
CDMRLC* getLC() const;
void setData(const CDMRLC& lc);
unsigned char getData(unsigned char* data, unsigned char n) const;
private:
bool* m_rawLC;
LC_STATE m_state;
void reset();
CDMRLC* processMultiBlockEmbeddedLC();
void processSingleBlockEmbeddedLC(const bool* data);
private:
unsigned int m_slotNo;
bool* m_raw;
LC_STATE m_state;
bool* m_data;
FLCO m_FLCO;
bool m_valid;
bool processEmbeddedData();
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -31,7 +31,7 @@ const unsigned int BUFFER_LENGTH = 500U;
const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U;
CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, bool rssi, HW_TYPE hwType) :
CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType) :
m_address(),
m_port(port),
m_id(NULL),
@ -43,7 +43,6 @@ m_socket(local),
m_enabled(false),
m_slot1(slot1),
m_slot2(slot2),
m_rssi(rssi),
m_hwType(hwType),
m_status(WAITING_CONNECT),
m_retryTimer(1000U, 10U),
@ -268,10 +267,7 @@ bool CDMRNetwork::write(const CDMRData& data)
buffer[53U] = data.getBER();
if (m_rssi)
buffer[54U] = data.getRSSI();
else
buffer[54U] = 0x00U;
buffer[54U] = data.getRSSI();
if (m_debug)
CUtils::dump(1U, "Network Transmitted", buffer, HOMEBREW_DATA_PACKET_LENGTH);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -31,7 +31,7 @@
class CDMRNetwork
{
public:
CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, bool rssi, HW_TYPE hwType);
CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType);
~CDMRNetwork();
void setOptions(const std::string& options);
@ -64,7 +64,6 @@ private:
bool m_enabled;
bool m_slot1;
bool m_slot2;
bool m_rssi;
HW_TYPE m_hwType;
enum STATUS {

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
*
* 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
@ -11,6 +11,7 @@
* GNU General Public License for more details.
*/
#include "DMRAccessControl.h"
#include "DMRSlotType.h"
#include "DMRShortLC.h"
#include "DMRTrellis.h"
@ -18,11 +19,11 @@
#include "BPTC19696.h"
#include "DMRSlot.h"
#include "DMRCSBK.h"
#include "DMREMB.h"
#include "Utils.h"
#include "Sync.h"
#include "CRC.h"
#include "Log.h"
#include "DMRAccessControl.h"
#include <cassert>
#include <ctime>
@ -58,7 +59,8 @@ m_slotNo(slotNo),
m_queue(5000U, "DMR Slot"),
m_rfState(RS_RF_LISTENING),
m_netState(RS_NET_IDLE),
m_rfEmbeddedLC(),
m_rfEmbeddedLC(slotNo),
m_netEmbeddedLC(slotNo),
m_rfLC(NULL),
m_netLC(NULL),
m_rfDataHeader(),
@ -83,8 +85,11 @@ m_rfErrs(0U),
m_netErrs(0U),
m_lastFrame(NULL),
m_lastFrameValid(false),
m_lastEMB(),
m_rssi(0U),
m_maxRSSI(0U),
m_minRSSI(0U),
m_aveRSSI(0U),
m_rssiCount(0U),
m_fp(NULL)
{
m_lastFrame = new unsigned char[DMR_FRAME_LENGTH_BYTES + 2U];
@ -102,13 +107,16 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
assert(data != NULL);
if (data[0U] == TAG_LOST && m_rfState == RS_RF_AUDIO) {
LogMessage("DMR Slot %u, RF transmission lost, %.1f seconds, BER: %.1f%%", m_slotNo, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("DMR Slot %u, RF voice transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_slotNo, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("DMR Slot %u, RF voice transmission lost, %.1f seconds, BER: %.1f%%", m_slotNo, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF(true);
return;
}
if (data[0U] == TAG_LOST && m_rfState == RS_RF_DATA) {
LogMessage("DMR Slot %u, RF transmission lost", m_slotNo);
LogMessage("DMR Slot %u, RF data transmission lost", m_slotNo);
writeEndRF();
return;
}
@ -130,6 +138,14 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
}
bool dataSync = (data[1U] & DMR_SYNC_DATA) == DMR_SYNC_DATA;
@ -154,13 +170,20 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
unsigned int srcId = lc->getSrcId();
unsigned int dstId = lc->getDstId();
FLCO flco = lc->getFLCO();
if (!CDMRAccessControl::validateId(srcId)) {
if (!CDMRAccessControl::validateSrcId(srcId)) {
LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId);
delete lc;
return;
}
if (!CDMRAccessControl::validateTGId(m_slotNo, flco == FLCO_GROUP, dstId)) {
LogMessage("DMR Slot %u, RF user %u rejected for using TG %u", m_slotNo, srcId, dstId);
delete lc;
return;
}
m_rfLC = lc;
// Regenerate the LC data
@ -176,12 +199,18 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
data[1U] = 0x00U;
m_rfTimeoutTimer.start();
m_rfEmbeddedLC.reset();
m_rfFrames = 0U;
m_rfSeqNo = 0U;
m_rfBits = 1U;
m_rfErrs = 0U;
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
if (m_duplex) {
m_queue.clear();
m_modem->writeDMRAbort(m_slotNo);
@ -199,11 +228,12 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
std::string dst = m_lookup->find(dstId);
if (m_netState == RS_NET_IDLE) {
setShortLC(m_slotNo, dstId, m_rfLC->getFLCO(), true);
m_display->writeDMR(m_slotNo, src, m_rfLC->getFLCO() == FLCO_GROUP, dst, "R");
setShortLC(m_slotNo, dstId, flco, true);
m_display->writeDMR(m_slotNo, src, flco == FLCO_GROUP, dst, "R");
m_display->writeDMRRSSI(m_slotNo, m_rssi);
}
LogMessage("DMR Slot %u, received RF voice header from %s to %s%s", m_slotNo, src.c_str(), m_rfLC->getFLCO() == FLCO_GROUP ? "TG " : "", dst.c_str());
LogMessage("DMR Slot %u, received RF voice header from %s to %s%s", m_slotNo, src.c_str(), flco == FLCO_GROUP ? "TG " : "", dst.c_str());
} else if (dataType == DT_VOICE_PI_HEADER) {
if (m_rfState != RS_RF_AUDIO)
return;
@ -252,7 +282,10 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
writeQueueRF(data);
}
LogMessage("DMR Slot %u, received RF end of voice transmission, %.1f seconds, BER: %.1f%%", m_slotNo, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("DMR Slot %u, received RF end of voice transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_slotNo, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("DMR Slot %u, received RF end of voice transmission, %.1f seconds, BER: %.1f%%", m_slotNo, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF();
} else if (dataType == DT_DATA_HEADER) {
@ -268,11 +301,16 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
unsigned int srcId = dataHeader.getSrcId();
unsigned int dstId = dataHeader.getDstId();
if (!CDMRAccessControl::validateId(srcId)) {
if (!CDMRAccessControl::validateSrcId(srcId)) {
LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId);
return;
}
if (!CDMRAccessControl::validateTGId(m_slotNo, gi, dstId)) {
LogMessage("DMR Slot %u, RF user %u rejected for using TG %u", m_slotNo, srcId, dstId);
return;
}
m_rfFrames = dataHeader.getBlocks();
m_rfDataHeader = dataHeader;
@ -306,6 +344,7 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
if (m_netState == RS_NET_IDLE) {
setShortLC(m_slotNo, dstId, gi ? FLCO_GROUP : FLCO_USER_USER, false);
m_display->writeDMR(m_slotNo, src, gi, dst, "R");
m_display->writeDMRRSSI(m_slotNo, m_rssi);
}
LogMessage("DMR Slot %u, received RF data header from %s to %s%s, %u blocks", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str(), m_rfFrames);
@ -327,10 +366,15 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
unsigned int dstId = csbk.getDstId();
if (srcId != 0U || dstId != 0U) {
if (!CDMRAccessControl::validateId(srcId)) {
if (!CDMRAccessControl::validateSrcId(srcId)) {
LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId);
return;
}
if (!CDMRAccessControl::validateTGId(m_slotNo, gi, dstId)) {
LogMessage("DMR Slot %u, RF user %u rejected for using TG %u", m_slotNo, srcId, dstId);
return;
}
}
// Regenerate the CSBK data
@ -433,11 +477,16 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
data[0U] = TAG_DATA;
data[1U] = 0x00U;
m_rfEmbeddedLC.reset();
if (m_duplex)
writeQueueRF(data);
writeNetworkRF(data, DT_VOICE_SYNC, errors);
m_display->writeDMRRSSI(m_slotNo, m_rssi);
} else if (m_rfState == RS_RF_LISTENING) {
m_rfEmbeddedLC.reset();
m_rfState = RS_RF_LATE_ENTRY;
}
} else {
@ -458,6 +507,8 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
m_rfErrs += errors;
}
m_rfEmbeddedLC.addData(data + 2U, emb.getLCSS());
m_rfBits += 141U;
m_rfFrames++;
@ -478,17 +529,25 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
if (colorCode != m_colorCode)
return;
CDMRLC* lc = m_rfEmbeddedLC.addData(data + 2U, emb.getLCSS());
m_rfEmbeddedLC.addData(data + 2U, emb.getLCSS());
CDMRLC* lc = m_rfEmbeddedLC.getLC();
if (lc != NULL) {
unsigned int srcId = lc->getSrcId();
unsigned int dstId = lc->getDstId();
FLCO flco = lc->getFLCO();
if (!CDMRAccessControl::validateId(srcId)) {
if (!CDMRAccessControl::validateSrcId(srcId)) {
LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId);
delete lc;
return;
}
if (!CDMRAccessControl::validateTGId(m_slotNo, flco == FLCO_GROUP, dstId)) {
LogMessage("DMR Slot %u, RF user %u rejected for using TG %u", m_slotNo, srcId, dstId);
delete lc;
return;
}
m_rfLC = lc;
// Create a dummy start frame to replace the received frame
@ -508,12 +567,18 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
start[1U] = 0x00U;
m_rfTimeoutTimer.start();
m_rfEmbeddedLC.reset();
m_rfFrames = 0U;
m_rfSeqNo = 0U;
m_rfBits = 1U;
m_rfErrs = 0U;
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
if (m_duplex) {
m_queue.clear();
m_modem->writeDMRAbort(m_slotNo);
@ -557,11 +622,12 @@ void CDMRSlot::writeModem(unsigned char *data, unsigned int len)
std::string dst = m_lookup->find(dstId);
if (m_netState == RS_NET_IDLE) {
setShortLC(m_slotNo, dstId, m_rfLC->getFLCO(), true);
m_display->writeDMR(m_slotNo, src, m_rfLC->getFLCO() == FLCO_GROUP, dst, "R");
setShortLC(m_slotNo, dstId, flco, true);
m_display->writeDMR(m_slotNo, src, flco == FLCO_GROUP, dst, "R");
m_display->writeDMRRSSI(m_slotNo, m_rssi);
}
LogMessage("DMR Slot %u, received RF late entry from %s to %s%s", m_slotNo, src.c_str(), m_rfLC->getFLCO() == FLCO_GROUP ? "TG " : "", dst.c_str());
LogMessage("DMR Slot %u, received RF late entry from %s to %s%s", m_slotNo, src.c_str(), flco == FLCO_GROUP ? "TG " : "", dst.c_str());
}
}
}
@ -778,6 +844,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData)
m_lastFrameValid = false;
m_netTimeoutTimer.start();
m_netEmbeddedLC.reset();
m_netFrames = 0U;
m_netLost = 0U;
@ -823,6 +890,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData)
m_lastFrameValid = false;
m_netTimeoutTimer.start();
m_netEmbeddedLC.reset();
if (m_duplex) {
m_queue.clear();
@ -1071,6 +1139,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData)
writeQueueNet(data);
m_netEmbeddedLC.reset();
m_packetTimer.start();
m_elapsed.start();
@ -1094,9 +1163,12 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData)
m_netBits += 141U;
// Change the color code in the EMB
m_lastEMB.putData(data + 2U);
m_lastEMB.setColorCode(m_colorCode);
m_lastEMB.getData(data + 2U);
CDMREMB emb;
emb.putData(data + 2U);
emb.setColorCode(m_colorCode);
emb.getData(data + 2U);
m_netEmbeddedLC.addData(data + 2U, emb.getLCSS());
data[0U] = TAG_DATA;
data[1U] = 0x00U;
@ -1569,6 +1641,10 @@ void CDMRSlot::insertSilence(unsigned int count)
unsigned char fid = m_netLC->getFID();
CDMREMB emb;
emb.setColorCode(m_colorCode);
emb.setLCSS(0U);
for (unsigned int i = 0U; i < count; i++) {
// Only use our silence frame if its AMBE audio data
if (fid == FID_ETSI || fid == FID_DMRA) {
@ -1581,11 +1657,8 @@ void CDMRSlot::insertSilence(unsigned int count)
if (n == 0U) {
CSync::addDMRAudioSync(data + 2U, m_duplex);
} else {
::memset(data + 2U + 13U, 0x00U, 7U);
m_lastEMB.setColorCode(m_colorCode);
m_lastEMB.setLCSS(0U);
m_lastEMB.getData(data + 2U);
m_netEmbeddedLC.getData(data + 2U, 0U);
emb.getData(data + 2U);
}
writeQueueNet(data);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -31,7 +31,6 @@
#include "DMRData.h"
#include "Display.h"
#include "Defines.h"
#include "DMREMB.h"
#include "Timer.h"
#include "Modem.h"
#include "DMRLC.h"
@ -59,6 +58,7 @@ private:
RPT_RF_STATE m_rfState;
RPT_NET_STATE m_netState;
CDMREmbeddedLC m_rfEmbeddedLC;
CDMREmbeddedLC m_netEmbeddedLC;
CDMRLC* m_rfLC;
CDMRLC* m_netLC;
CDMRDataHeader m_rfDataHeader;
@ -83,8 +83,11 @@ private:
unsigned int m_netErrs;
unsigned char* m_lastFrame;
bool m_lastFrameValid;
CDMREMB m_lastEMB;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
unsigned int m_rssiCount;
FILE* m_fp;
static unsigned int m_colorCode;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
*
* 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
@ -36,7 +36,7 @@ bool CallsignCompare(const std::string& arg, const unsigned char* my)
// #define DUMP_DSTAR
CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, const std::vector<std::string>& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex) :
CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, const std::vector<std::string>& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper) :
m_callsign(NULL),
m_gateway(NULL),
m_selfOnly(selfOnly),
@ -70,9 +70,16 @@ m_rfErrs(0U),
m_netErrs(0U),
m_lastFrame(NULL),
m_lastFrameValid(false),
m_rssiMapper(rssiMapper),
m_rssi(0U),
m_maxRSSI(0U),
m_minRSSI(0U),
m_aveRSSI(0U),
m_rssiCount(0U),
m_fp(NULL)
{
assert(display != NULL);
assert(rssiMapper != NULL);
m_callsign = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
m_gateway = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
@ -111,7 +118,10 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
unsigned char type = data[0U];
if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) {
LogMessage("D-Star, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("D-Star, transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("D-Star, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF();
return false;
}
@ -121,6 +131,50 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
return false;
}
// Have we got RSSI bytes on the end of a D-Star header?
if (len == (DSTAR_HEADER_LENGTH_BYTES + 3U)) {
uint16_t raw = 0U;
raw |= (data[42U] << 8) & 0xFF00U;
raw |= (data[43U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
}
// Have we got RSSI bytes on the end of D-Star data?
if (len == (DSTAR_FRAME_LENGTH_BYTES + 3U)) {
uint16_t raw = 0U;
raw |= (data[13U] << 8) & 0xFF00U;
raw |= (data[14U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
}
if (type == TAG_HEADER) {
CDStarHeader header(data + 1U);
@ -181,6 +235,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfFrames = 1U;
m_rfN = 0U;
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
if (m_duplex) {
// Modify the header
header.setRepeater(false);
@ -203,8 +262,10 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfState = RS_RF_AUDIO;
if (m_netState == RS_NET_IDLE)
if (m_netState == RS_NET_IDLE) {
m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " ");
m_display->writeDStarRSSI(m_rssi);
}
LogMessage("D-Star, received RF header from %8.8s/%4.4s to %8.8s", my1, my2, your);
} else if (type == TAG_EOT) {
@ -217,7 +278,10 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
if (m_duplex)
writeQueueEOTRF();
LogMessage("D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF();
}
@ -248,9 +312,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0)
m_rfN = 0U;
// Regenerate the sync
if (m_rfN == 0U)
// Regenerate the sync and send the RSSI data to the display
if (m_rfN == 0U) {
CSync::addDStarSync(data + 1U);
m_display->writeDStarRSSI(m_rssi);
}
LogDebug("D-Star, audio sequence no. %u, errs: %u/48", m_rfN, errors);
@ -328,6 +394,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfN = 0U;
m_rfFrames = 1U;
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
if (m_duplex) {
unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U];
start[0U] = TAG_HEADER;
@ -377,8 +448,10 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfN = (m_rfN + 1U) % 21U;
if (m_netState == RS_NET_IDLE)
if (m_netState == RS_NET_IDLE) {
m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " ");
m_display->writeDStarRSSI(m_rssi);
}
LogMessage("D-Star, received RF late entry from %8.8s/%4.4s to %8.8s", my1, my2, your);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -19,6 +19,7 @@
#if !defined(DStarControl_H)
#define DStarControl_H
#include "RSSIInterpolator.h"
#include "DStarNetwork.h"
#include "DStarSlowData.h"
#include "DStarDefines.h"
@ -36,7 +37,7 @@
class CDStarControl {
public:
CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, const std::vector<std::string>& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex);
CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, const std::vector<std::string>& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper);
~CDStarControl();
bool writeModem(unsigned char* data, unsigned int len);
@ -79,6 +80,12 @@ private:
unsigned int m_netErrs;
unsigned char* m_lastFrame;
bool m_lastFrameValid;
CRSSIInterpolator* m_rssiMapper;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
unsigned int m_rssiCount;
FILE* m_fp;
void writeNetwork();

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -84,6 +84,12 @@ void CDisplay::writeDStar(const char* my1, const char* my2, const char* your, co
writeDStarInt(my1, my2, your, type, reflector);
}
void CDisplay::writeDStarRSSI(unsigned char rssi)
{
if (rssi != 0U)
writeDStarRSSIInt(rssi);
}
void CDisplay::clearDStar()
{
if (m_timer1.hasExpired()) {
@ -110,6 +116,12 @@ void CDisplay::writeDMR(unsigned int slotNo, const std::string& src, bool group,
writeDMRInt(slotNo, src, group, dst, type);
}
void CDisplay::writeDMRRSSI(unsigned int slotNo, unsigned char rssi)
{
if (rssi != 0U)
writeDMRRSSIInt(slotNo, rssi);
}
void CDisplay::clearDMR(unsigned int slotNo)
{
if (slotNo == 1U) {
@ -144,6 +156,12 @@ void CDisplay::writeFusion(const char* source, const char* dest, const char* typ
writeFusionInt(source, dest, type, origin);
}
void CDisplay::writeFusionRSSI(unsigned char rssi)
{
if (rssi != 0U)
writeFusionRSSIInt(rssi);
}
void CDisplay::clearFusion()
{
if (m_timer1.hasExpired()) {
@ -166,6 +184,12 @@ void CDisplay::writeP25(const char* source, bool group, unsigned int dest, const
writeP25Int(source, group, dest, type);
}
void CDisplay::writeP25RSSI(unsigned char rssi)
{
if (rssi != 0U)
writeP25RSSIInt(rssi);
}
void CDisplay::clearP25()
{
if (m_timer1.hasExpired()) {
@ -236,3 +260,19 @@ void CDisplay::clock(unsigned int ms)
void CDisplay::clockInt(unsigned int ms)
{
}
void CDisplay::writeDStarRSSIInt(unsigned char rssi)
{
}
void CDisplay::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
}
void CDisplay::writeFusionRSSIInt(unsigned char rssi)
{
}
void CDisplay::writeP25RSSIInt(unsigned char rssi)
{
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -36,15 +36,19 @@ public:
void setError(const char* text);
void writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
void writeDStarRSSI(unsigned char rssi);
void clearDStar();
void writeDMR(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
void writeDMRRSSI(unsigned int slotNo, unsigned char rssi);
void clearDMR(unsigned int slotNo);
void writeFusion(const char* source, const char* dest, const char* type, const char* origin);
void writeFusionRSSI(unsigned char rssi);
void clearFusion();
void writeP25(const char* source, bool group, unsigned int dest, const char* type);
void writeP25RSSI(unsigned char rssi);
void clearP25();
void writeCW();
@ -60,15 +64,19 @@ protected:
virtual void setErrorInt(const char* text) = 0;
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) = 0;
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void clearDStarInt() = 0;
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) = 0;
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void clearDMRInt(unsigned int slotNo) = 0;
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin) = 0;
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void clearFusionInt() = 0;
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type) = 0;
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void clearP25Int() = 0;
virtual void writeCWInt() = 0;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016, 2017 by Jonathan Naylor G4KLX & Tony Corbett G0WFV
*
* 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
@ -36,6 +36,11 @@ char m_buffer2[128U];
char m_buffer3[128U];
char m_buffer4[128U];
const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms
const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms
const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms
CHD44780::CHD44780(unsigned int rows, unsigned int cols, const std::string& callsign, unsigned int dmrid, const std::vector<unsigned int>& pins, unsigned int i2cAddress, bool pwm, unsigned int pwmPin, unsigned int pwmBright, unsigned int pwmDim, bool displayClock, bool utc, bool duplex) :
CDisplay(),
m_rows(rows),
@ -59,12 +64,9 @@ m_duplex(duplex),
//m_duplex(true), // uncomment to force duplex display for testing!
m_fd(-1),
m_dmr(false),
m_clockDisplayTimer(1000U, 0U, 250U) // Update the clock display every 250ms
/*
m_dmrScrollTimer1(1000U, 0U, 250U), // Scroll speed for slot 1 - every 250ms
m_dmrScrollTimer2(1000U, 0U, 250U), // Scroll speed for slot 2 - every 250ms
m_dstarScrollTimer(1000U, 0U, 250U) // Scroll speed for D-Star - every 250ms
*/
m_clockDisplayTimer(1000U, 0U, 250U), // Update the clock display every 250ms
m_rssiCount1(0U),
m_rssiCount2(0U)
{
assert(rows > 1U);
assert(cols > 15U);
@ -290,8 +292,6 @@ void CHD44780::pcf8574LCDSetup()
void CHD44780::setIdleInt()
{
//m_dmrScrollTimer1.stop(); // Stop the scroll timer on slot 1
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
m_clockDisplayTimer.start(); // Start the clock display in IDLE only
::lcdClear(m_fd);
@ -330,8 +330,6 @@ void CHD44780::setErrorInt(const char* text)
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
//m_dmrScrollTimer1.stop(); // Stop the scroll timer on slot 1
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
::lcdClear(m_fd);
if (m_pwm) {
@ -357,8 +355,6 @@ void CHD44780::setLockoutInt()
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
//m_dmrScrollTimer1.stop(); // Stop the scroll timer on slot 1
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
::lcdClear(m_fd);
if (m_pwm) {
@ -439,14 +435,20 @@ void CHD44780::writeDStarInt(const char* my1, const char* my2, const char* your,
::lcdPutchar(m_fd, 1);
::lcdPrintf(m_fd, " %.*s", m_cols, m_buffer1);
// Start the D-Star scroll timer if text in m_buffer1 will not fit in the space available
/*if (strlen(m_buffer1) > m_cols) {
::sprintf(m_buffer3, "%.*s", m_cols, DEADSPACE);
strcat(m_buffer1, m_buffer3);
m_dstarScrollTimer.start();
}*/
m_dmr = false;
m_rssiCount1 = 0U;
}
void CHD44780::writeDStarRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U && m_rows > 2) {
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= DSTAR_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CHD44780::clearDStarInt()
@ -456,7 +458,6 @@ void CHD44780::clearDStarInt()
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
//m_dstarScrollTimer.stop();
::lcdClear(m_fd);
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
@ -538,13 +539,6 @@ void CHD44780::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
::sprintf(m_buffer1, "%s>%s%s", src.c_str(), dst.c_str(), DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols - 2U, m_buffer1);
// Start the DMR scroll timer on slot 1 if text in m_buffer1 will not fit in the space available
/*if (strlen(m_buffer1) > m_cols - 5 ) {
::sprintf(m_buffer3, "%.*s", m_cols, DEADSPACE);
strcat(m_buffer1, m_buffer3);
m_dmrScrollTimer1.start();
}*/
::lcdPosition(m_fd, m_cols - 3U, (m_rows / 2) - 1);
::lcdPuts(m_fd, " ");
@ -568,13 +562,6 @@ void CHD44780::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
::sprintf(m_buffer2, "%s>%s%s", src.c_str(), dst.c_str(), DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols - 2U, m_buffer2);
// Start the DMR scroll timer on slot 2 if text in m_buffer2 will not fit in the space available
/*if (strlen(m_buffer2) > m_cols - 5 ) {
::sprintf(m_buffer4, "%.*s", m_cols, DEADSPACE);
strcat(m_buffer2, m_buffer4);
m_dmrScrollTimer2.start();
}*/
::lcdPosition(m_fd, m_cols - 3U, (m_rows / 2));
::lcdPuts(m_fd, " ");
@ -622,6 +609,33 @@ void CHD44780::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
}
}
m_dmr = true;
m_rssiCount1 = 0U;
m_rssiCount2 = 0U;
}
void CHD44780::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
if (m_rows > 2) {
if (slotNo == 1U) {
if (m_rssiCount1 == 0U) {
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= DMR_RSSI_COUNT)
m_rssiCount1 = 0U;
} else {
if (m_rssiCount2 == 0U) {
::lcdPosition(m_fd, (m_cols / 2), 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount2++;
if (m_rssiCount2 >= DMR_RSSI_COUNT)
m_rssiCount2 = 0U;
}
}
}
void CHD44780::clearDMRInt(unsigned int slotNo)
@ -634,16 +648,23 @@ void CHD44780::clearDMRInt(unsigned int slotNo)
if (m_duplex) {
if (slotNo == 1U) {
//m_dmrScrollTimer1.stop(); // Stop the scroll timer on slot 1
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
::lcdPrintf(m_fd, "1 %.*s", m_cols - 2U, LISTENING);
if (m_rows > 2) { // clear slot 1 RSSI
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols / 2, DEADSPACE);
}
} else {
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPrintf(m_fd, "2 %.*s", m_cols - 2U, LISTENING);
if (m_rows > 2) { // cleat slot 2 RSSI
::lcdPosition(m_fd, m_cols / 2, 3);
::lcdPrintf(m_fd, "%.*s", m_cols / 2, DEADSPACE);
}
}
} else {
//m_dmrScrollTimer2.stop(); // Stop the scroll timer on slot 2
if (m_rows > 2U) {
::lcdPosition(m_fd, 0, (m_rows / 2) - 2);
@ -715,6 +736,19 @@ void CHD44780::writeFusionInt(const char* source, const char* dest, const char*
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CHD44780::writeFusionRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U && m_rows > 2) {
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= YSF_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CHD44780::clearFusionInt()
@ -734,12 +768,18 @@ void CHD44780::clearFusionInt()
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
} else if (m_rows == 4U && m_cols == 20U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
} else if (m_rows == 2 && m_cols == 40U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
@ -800,6 +840,19 @@ void CHD44780::writeP25Int(const char* source, bool group, unsigned int dest, co
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CHD44780::writeP25RSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U && m_rows > 2) {
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "-%3udBm", rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= P25_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CHD44780::clearP25Int()
@ -819,12 +872,18 @@ void CHD44780::clearP25Int()
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
} else if (m_rows == 4U && m_cols == 20U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
::lcdPosition(m_fd, 0, 3);
::lcdPrintf(m_fd, "%.*s", m_cols, " ");
} else if (m_rows == 2 && m_cols == 40U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
@ -846,9 +905,6 @@ void CHD44780::clearCWInt()
void CHD44780::clockInt(unsigned int ms)
{
m_clockDisplayTimer.clock(ms);
//m_dmrScrollTimer1.clock(ms);
//m_dmrScrollTimer2.clock(ms);
//m_dstarScrollTimer.clock(ms);
// Idle clock display
if (m_displayClock && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) {
@ -878,35 +934,6 @@ void CHD44780::clockInt(unsigned int ms)
m_clockDisplayTimer.start();
}
/* Scrolling disabled for now as it is slowing things down just enough to screw with the audio processing!
// DMR Slot 1 scrolling
if (m_dmrScrollTimer1.isRunning() && m_dmrScrollTimer1.hasExpired()) {
strncat(m_buffer1, m_buffer1, 1); // Move the first character to the end of the buffer
memmove(m_buffer1, m_buffer1 + 1, strlen(m_buffer1)); // Strip the first character
::lcdPosition(m_fd, 2, (m_rows / 2) - 1); // Position on the LCD
::lcdPrintf(m_fd, "%.*s", m_cols - 5U, m_buffer1); // Print it out
m_dmrScrollTimer1.start(); // Restart the scroll timer
}
// DMR Slot 2 scrolling
if (m_dmrScrollTimer2.isRunning() && m_dmrScrollTimer2.hasExpired()) {
strncat(m_buffer2, m_buffer2, 1);
memmove(m_buffer2, m_buffer2 + 1, strlen(m_buffer2));
::lcdPosition(m_fd, 2, (m_rows / 2));
::lcdPrintf(m_fd, "%.*s", m_cols - 5U, m_buffer2);
m_dmrScrollTimer2.start();
}
// D-Star scrolling
if (m_dstarScrollTimer.isRunning() && m_dstarScrollTimer.hasExpired()) {
strncat(m_buffer1, m_buffer1, 1);
memmove(m_buffer1, m_buffer1 + 1, strlen(m_buffer1));
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
m_dstarScrollTimer.start();
}*/
}
void CHD44780::close()

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016, 2017 by Jonathan Naylor G4KLX & Tony Corbett G0WFV
*
* 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
@ -102,15 +102,19 @@ protected:
virtual void setLockoutInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin);
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void clearP25Int();
virtual void writeCWInt();
@ -140,6 +144,8 @@ private:
int m_fd;
bool m_dmr;
CTimer m_clockDisplayTimer;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
/*
CTimer m_dmrScrollTimer1;
CTimer m_dmrScrollTimer2;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016, 2017 by Tony Corbett G0WFV
*
* 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
@ -84,6 +84,11 @@ bool m_connected(false);
char m_displayBuffer1[BUFFER_MAX_LEN];
char m_displayBuffer2[BUFFER_MAX_LEN];
const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms
const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms
const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms
CLCDproc::CLCDproc(std::string address, unsigned int port, unsigned int localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle) :
CDisplay(),
m_address(address),
@ -97,7 +102,9 @@ m_duplex(duplex),
//m_duplex(true), // uncomment to force duplex display for testing!
m_dimOnIdle(dimOnIdle),
m_dmr(false),
m_clockDisplayTimer(1000U, 0U, 250U) // Update the clock display every 250ms
m_clockDisplayTimer(1000U, 0U, 250U), // Update the clock display every 250ms
m_rssiCount1(0U),
m_rssiCount2(0U)
{
}
@ -242,6 +249,18 @@ void CLCDproc::writeDStarInt(const char* my1, const char* my2, const char* your,
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeDStarRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= DSTAR_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearDStarInt()
@ -301,6 +320,31 @@ void CLCDproc::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
}
socketPrintf(m_socketfd, "output 16"); // Set LED1 color red
m_dmr = true;
m_rssiCount1 = 0U;
m_rssiCount2 = 0U;
}
void CLCDproc::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
if (m_rows > 2) {
if (slotNo == 1U) {
if (m_rssiCount1 == 0U)
socketPrintf(m_socketfd, "widget_set DMR Slot1RSSI %u %u -%3udBm", 1, 4, rssi);
m_rssiCount1++;
if (m_rssiCount1 >= DMR_RSSI_COUNT)
m_rssiCount1 = 0U;
} else {
if (m_rssiCount2 == 0U)
socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u -%3udBm", (m_cols / 2) + 1, 4, rssi);
m_rssiCount2++;
if (m_rssiCount2 >= DMR_RSSI_COUNT)
m_rssiCount2 = 0U;
}
}
}
void CLCDproc::clearDMRInt(unsigned int slotNo)
@ -308,13 +352,17 @@ void CLCDproc::clearDMRInt(unsigned int slotNo)
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_duplex) {
if (slotNo == 1U)
if (slotNo == 1U) {
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2);
else
socketPrintf(m_socketfd, "widget_set DMR Slot1RSSI %u %u %*.s", 1, 4, m_cols / 2, " ");
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"Listening\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1);
socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u %*.s", (m_cols / 2) + 1, 4, m_cols / 2, " ");
}
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 15 2 h 3 Listening");
socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u %*.s", (m_cols / 2) + 1, 4, m_cols / 2, " ");
}
socketPrintf(m_socketfd, "output 1"); // Set LED1 color green
}
@ -342,6 +390,18 @@ void CLCDproc::writeFusionInt(const char* source, const char* dest, const char*
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeFusionRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
socketPrintf(m_socketfd, "widget_set YSF Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= YSF_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearFusionInt()
@ -375,6 +435,18 @@ void CLCDproc::writeP25Int(const char* source, bool group, unsigned int dest, co
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeP25RSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
socketPrintf(m_socketfd, "widget_set P25 Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= P25_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearP25Int()
@ -599,9 +671,11 @@ void CLCDproc::defineScreens()
socketPrintf(m_socketfd, "widget_add DStar Line3 scroller");
socketPrintf(m_socketfd, "widget_add DStar Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 15 2 h 3 Listening");
socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 15 4 h 3 \"\"");
*/
// The DMR Screen
@ -613,11 +687,15 @@ void CLCDproc::defineScreens()
socketPrintf(m_socketfd, "widget_add DMR Slot2_ string");
socketPrintf(m_socketfd, "widget_add DMR Slot1 scroller");
socketPrintf(m_socketfd, "widget_add DMR Slot2 scroller");
socketPrintf(m_socketfd, "widget_add DMR Slot1RSSI string");
socketPrintf(m_socketfd, "widget_add DMR Slot2RSSI string");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set DMR Slot1_ 1 %u 1", m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u 2", m_rows / 2 + 1);
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 1 15 1 h 3 Listening");
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 2 15 2 h 3 Listening");
*/
// The YSF Screen
@ -629,9 +707,11 @@ void CLCDproc::defineScreens()
socketPrintf(m_socketfd, "widget_add YSF Line3 scroller");
socketPrintf(m_socketfd, "widget_add YSF Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set YSF Line2 2 1 15 1 h 3 Listening");
socketPrintf(m_socketfd, "widget_set YSF Line3 3 1 15 1 h 3 \" \"");
socketPrintf(m_socketfd, "widget_set YSF Line4 4 2 15 2 h 3 \" \"");
*/
// The P25 Screen
@ -643,9 +723,11 @@ void CLCDproc::defineScreens()
socketPrintf(m_socketfd, "widget_add P25 Line3 scroller");
socketPrintf(m_socketfd, "widget_add P25 Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set P25 Line3 2 1 15 1 h 3 Listening");
socketPrintf(m_socketfd, "widget_set P25 Line3 3 1 15 1 h 3 \" \"");
socketPrintf(m_socketfd, "widget_set P25 Line4 4 2 15 2 h 3 \" \"");
*/
m_screensDefined = true;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016, 2017 by Tony Corbett G0WFV
*
* 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
@ -40,15 +40,19 @@ protected:
virtual void setLockoutInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin);
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void clearP25Int();
virtual void writeCWInt();
@ -68,6 +72,8 @@ private:
bool m_dimOnIdle;
bool m_dmr;
CTimer m_clockDisplayTimer;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
int socketPrintf(int fd, const char *format, ...);
void defineScreens();

View file

@ -70,6 +70,8 @@ Id=123456
ColorCode=1
SelfOnly=0
# Prefixes=234,235
# Slot1TGWhiteList=
# Slot2TGWhiteList=
CallHang=3
TXHang=4
@ -96,7 +98,6 @@ Jitter=300
# Local=3350
Password=PASSWORD
# Options=
RSSI=0
Slot1=1
Slot2=1
Debug=0

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -297,6 +297,16 @@ int CMMDVMHost::run()
CTimer dmrBeaconTimer(1000U, 4U);
bool dmrBeaconsEnabled = m_dmrEnabled && m_conf.getDMRBeacons();
// For all modes we handle RSSI
std::string rssiMappingFile = m_conf.getModemRSSIMappingFile();
CRSSIInterpolator* rssi = new CRSSIInterpolator;
if (!rssiMappingFile.empty()) {
LogInfo("RSSI");
LogInfo(" Mapping File: %s", rssiMappingFile.c_str());
rssi->load(rssiMappingFile);
}
// For DMR and P25 we try to map IDs to callsigns
if (m_dmrEnabled || m_p25Enabled) {
std::string lookupFile = m_conf.getDMRIdLookupFile();
@ -327,7 +337,7 @@ int CMMDVMHost::run()
if (blackList.size() > 0U)
LogInfo(" Black List: %u", blackList.size());
dstar = new CDStarControl(m_callsign, module, selfOnly, blackList, m_dstarNetwork, m_display, m_timeout, m_duplex);
dstar = new CDStarControl(m_callsign, module, selfOnly, blackList, m_dstarNetwork, m_display, m_timeout, m_duplex, rssi);
}
CDMRControl* dmr = NULL;
@ -338,9 +348,10 @@ int CMMDVMHost::run()
std::vector<unsigned int> prefixes = m_conf.getDMRPrefixes();
std::vector<unsigned int> blackList = m_conf.getDMRBlackList();
std::vector<unsigned int> whiteList = m_conf.getDMRWhiteList();
unsigned int callHang = m_conf.getDMRCallHang();
std::vector<unsigned int> slot1TGWhiteList = m_conf.getDMRSlot1TGWhiteList();
std::vector<unsigned int> slot2TGWhiteList = m_conf.getDMRSlot2TGWhiteList();
unsigned int callHang = m_conf.getDMRCallHang();
unsigned int txHang = m_conf.getDMRTXHang();
std::string rssiMappingFile = m_conf.getModemRSSIMappingFile();
unsigned int jitter = m_conf.getDMRNetworkJitter();
if (txHang > m_rfModeHang)
@ -361,17 +372,15 @@ int CMMDVMHost::run()
LogInfo(" Source ID Black List: %u", blackList.size());
if (whiteList.size() > 0U)
LogInfo(" Source ID White List: %u", whiteList.size());
if (slot1TGWhiteList.size() > 0U)
LogInfo(" Slot 1 TG White List: %u", slot1TGWhiteList.size());
if (slot2TGWhiteList.size() > 0U)
LogInfo(" Slot 2 TG White List: %u", slot2TGWhiteList.size());
LogInfo(" Call Hang: %us", callHang);
LogInfo(" TX Hang: %us", txHang);
CRSSIInterpolator* rssi = new CRSSIInterpolator;
if (!rssiMappingFile.empty()) {
LogInfo(" RSSI Mapping File: %s", rssiMappingFile.c_str());
rssi->load(rssiMappingFile);
}
dmr = new CDMRControl(id, colorCode, callHang, selfOnly, prefixes, blackList, whiteList, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, m_lookup, rssi, jitter);
dmr = new CDMRControl(id, colorCode, callHang, selfOnly, prefixes, blackList, whiteList, slot1TGWhiteList, slot2TGWhiteList, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, m_lookup, rssi, jitter);
m_dmrTXTimer.setTimeout(txHang);
}
@ -383,7 +392,7 @@ int CMMDVMHost::run()
LogInfo("YSF Parameters");
LogInfo(" Remote Gateway: %s", remoteGateway ? "yes" : "no");
ysf = new CYSFControl(m_callsign, m_ysfNetwork, m_display, m_timeout, m_duplex, remoteGateway);
ysf = new CYSFControl(m_callsign, m_ysfNetwork, m_display, m_timeout, m_duplex, remoteGateway, rssi);
}
CP25Control* p25 = NULL;
@ -393,7 +402,7 @@ int CMMDVMHost::run()
LogInfo("P25 Parameters");
LogInfo(" NAC: $%03X", nac);
p25 = new CP25Control(nac, m_p25Network, m_display, m_timeout, m_duplex, m_lookup);
p25 = new CP25Control(nac, m_p25Network, m_display, m_timeout, m_duplex, m_lookup, rssi);
}
setMode(MODE_IDLE);
@ -854,7 +863,6 @@ bool CMMDVMHost::createDMRNetwork()
unsigned int jitter = m_conf.getDMRNetworkJitter();
bool slot1 = m_conf.getDMRNetworkSlot1();
bool slot2 = m_conf.getDMRNetworkSlot2();
bool rssi = m_conf.getDMRNetworkRSSI();
HW_TYPE hwType = m_modem->getHWType();
LogInfo("DMR Network Parameters");
@ -867,9 +875,8 @@ bool CMMDVMHost::createDMRNetwork()
LogInfo(" Jitter: %ums", jitter);
LogInfo(" Slot 1: %s", slot1 ? "enabled" : "disabled");
LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled");
LogInfo(" RSSI: %s", rssi ? "enabled" : "disabled");
m_dmrNetwork = new CDMRNetwork(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, rssi, hwType);
m_dmrNetwork = new CDMRNetwork(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, hwType);
std::string options = m_conf.getDMRNetworkOptions();
if (!options.empty()) {

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -25,6 +25,11 @@
#include <ctime>
#include <clocale>
const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms
const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms
const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms
CNextion::CNextion(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool displayClock, bool utc, unsigned int idleBrightness) :
CDisplay(),
m_callsign(callsign),
@ -35,7 +40,9 @@ m_mode(MODE_IDLE),
m_displayClock(displayClock),
m_utc(utc),
m_idleBrightness(idleBrightness),
m_clockDisplayTimer(1000U, 0U, 400U)
m_clockDisplayTimer(1000U, 0U, 400U),
m_rssiCount1(0U),
m_rssiCount2(0U)
{
assert(serial != NULL);
assert(brightness >= 0U && brightness <= 100U);
@ -143,6 +150,20 @@ void CNextion::writeDStarInt(const char* my1, const char* my2, const char* your,
m_clockDisplayTimer.stop();
m_mode = MODE_DSTAR;
m_rssiCount1 = 0U;
}
void CNextion::writeDStarRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", rssi);
sendCommand(text);
}
m_rssiCount1++;
if (m_rssiCount1 >= DSTAR_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CNextion::clearDStarInt()
@ -150,6 +171,7 @@ void CNextion::clearDStarInt()
sendCommand("t0.txt=\"Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t2.txt=\"\"");
sendCommand("t3.txt=\"\"");
}
void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
@ -186,6 +208,33 @@ void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro
m_clockDisplayTimer.stop();
m_mode = MODE_DMR;
m_rssiCount1 = 0U;
m_rssiCount2 = 0U;
}
void CNextion::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
if (slotNo == 1U) {
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t4.txt=\"-%udBm\"", rssi);
sendCommand(text);
}
m_rssiCount1++;
if (m_rssiCount1 >= DMR_RSSI_COUNT)
m_rssiCount1 = 0U;
} else {
if (m_rssiCount2 == 0U) {
char text[20U];
::sprintf(text, "t5.txt=\"-%udBm\"", rssi);
sendCommand(text);
}
m_rssiCount2++;
if (m_rssiCount2 >= DMR_RSSI_COUNT)
m_rssiCount2 = 0U;
}
}
void CNextion::clearDMRInt(unsigned int slotNo)
@ -193,9 +242,11 @@ void CNextion::clearDMRInt(unsigned int slotNo)
if (slotNo == 1U) {
sendCommand("t0.txt=\"1 Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t4.txt=\"\"");
} else {
sendCommand("t2.txt=\"2 Listening\"");
sendCommand("t3.txt=\"\"");
sendCommand("t5.txt=\"\"");
}
}
@ -226,6 +277,20 @@ void CNextion::writeFusionInt(const char* source, const char* dest, const char*
m_clockDisplayTimer.stop();
m_mode = MODE_YSF;
m_rssiCount1 = 0U;
}
void CNextion::writeFusionRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", rssi);
sendCommand(text);
}
m_rssiCount1++;
if (m_rssiCount1 >= YSF_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CNextion::clearFusionInt()
@ -233,6 +298,7 @@ void CNextion::clearFusionInt()
sendCommand("t0.txt=\"Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t2.txt=\"\"");
sendCommand("t3.txt=\"\"");
}
void CNextion::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
@ -256,6 +322,20 @@ void CNextion::writeP25Int(const char* source, bool group, unsigned int dest, co
m_clockDisplayTimer.stop();
m_mode = MODE_P25;
m_rssiCount1 = 0U;
}
void CNextion::writeP25RSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t2.txt=\"-%udBm\"", rssi);
sendCommand(text);
}
m_rssiCount1++;
if (m_rssiCount1 >= P25_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CNextion::clearP25Int()

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -42,15 +42,19 @@ protected:
virtual void setLockoutInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin);
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void clearP25Int();
virtual void writeCWInt();
@ -68,6 +72,8 @@ private:
bool m_utc;
unsigned int m_idleBrightness;
CTimer m_clockDisplayTimer;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
void sendCommand(const char* command);
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -35,7 +35,7 @@ const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U
#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
CP25Control::CP25Control(unsigned int nac, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup) :
CP25Control::CP25Control(unsigned int nac, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper) :
m_nac(nac),
m_network(network),
m_display(display),
@ -63,10 +63,17 @@ m_netLDU1(NULL),
m_netLDU2(NULL),
m_lastIMBE(NULL),
m_rfLDU(NULL),
m_rssiMapper(rssiMapper),
m_rssi(0U),
m_maxRSSI(0U),
m_minRSSI(0U),
m_aveRSSI(0U),
m_rssiCount(0U),
m_fp(NULL)
{
assert(display != NULL);
assert(lookup != NULL);
assert(rssiMapper != NULL);
m_netLDU1 = new unsigned char[9U * 25U];
m_netLDU2 = new unsigned char[9U * 25U];
@ -101,9 +108,14 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
return false;
if (data[0U] == TAG_LOST) {
LogMessage("P25, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("P25, transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("P25, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_netState == RS_NET_IDLE)
m_display->clearP25();
writeNetwork(m_rfLDU, m_lastDUID, true);
writeNetwork(data + 2U, P25_DUID_TERM, true);
m_rfState = RS_RF_LISTENING;
@ -139,6 +151,28 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
}
}
// Have we got RSSI bytes on the end of a P25 LDU?
if (len == (P25_LDU_FRAME_LENGTH_BYTES + 4U)) {
uint16_t raw = 0U;
raw |= (data[218U] << 8) & 0xFF00U;
raw |= (data[219U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
LogDebug("P25, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
}
if (duid == P25_DUID_LDU1) {
if (m_rfState == RS_RF_LISTENING) {
m_rfData.reset();
@ -148,6 +182,11 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
return false;
}
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
createRFHeader();
writeNetwork(data + 2U, P25_DUID_HEADER, false);
} else {
@ -199,6 +238,8 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
m_display->writeP25(source.c_str(), grp, dstId, "R");
m_rfState = RS_RF_AUDIO;
}
m_display->writeP25RSSI(m_rssi);
} else if (duid == P25_DUID_LDU2) {
if (m_rfState == RS_RF_LISTENING)
return false;
@ -240,6 +281,8 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
data[1U] = 0x00U;
writeQueueRF(data, P25_LDU_FRAME_LENGTH_BYTES + 2U);
}
m_display->writeP25RSSI(m_rssi);
} else if (duid == P25_DUID_TERM || duid == P25_DUID_TERM_LC) {
if (m_rfState == RS_RF_LISTENING)
return false;
@ -262,7 +305,11 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len)
m_rfData.reset();
m_lastDUID = duid;
LogMessage("P25, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("P25, received RF end of transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("P25, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits));
m_display->clearP25();
#if defined(DUMP_P25)

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -19,6 +19,7 @@
#if !defined(P25Control_H)
#define P25Control_H
#include "RSSIInterpolator.h"
#include "P25LowSpeedData.h"
#include "RingBuffer.h"
#include "P25Network.h"
@ -35,7 +36,7 @@
class CP25Control {
public:
CP25Control(unsigned int nac, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup);
CP25Control(unsigned int nac, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper);
~CP25Control();
bool writeModem(unsigned char* data, unsigned int len);
@ -45,34 +46,40 @@ public:
void clock(unsigned int ms);
private:
unsigned int m_nac;
CP25Network* m_network;
CDisplay* m_display;
bool m_duplex;
CDMRLookup* m_lookup;
unsigned int m_nac;
CP25Network* m_network;
CDisplay* m_display;
bool m_duplex;
CDMRLookup* m_lookup;
CRingBuffer<unsigned char> m_queue;
RPT_RF_STATE m_rfState;
RPT_NET_STATE m_netState;
CTimer m_rfTimeout;
CTimer m_netTimeout;
CTimer m_networkWatchdog;
unsigned int m_rfFrames;
unsigned int m_rfBits;
unsigned int m_rfErrs;
unsigned int m_netFrames;
unsigned int m_netLost;
CP25NID m_nid;
unsigned char m_lastDUID;
CP25Audio m_audio;
CP25Data m_rfData;
CP25Data m_netData;
CP25LowSpeedData m_rfLSD;
CP25LowSpeedData m_netLSD;
unsigned char* m_netLDU1;
unsigned char* m_netLDU2;
unsigned char* m_lastIMBE;
unsigned char* m_rfLDU;
FILE* m_fp;
RPT_RF_STATE m_rfState;
RPT_NET_STATE m_netState;
CTimer m_rfTimeout;
CTimer m_netTimeout;
CTimer m_networkWatchdog;
unsigned int m_rfFrames;
unsigned int m_rfBits;
unsigned int m_rfErrs;
unsigned int m_netFrames;
unsigned int m_netLost;
CP25NID m_nid;
unsigned char m_lastDUID;
CP25Audio m_audio;
CP25Data m_rfData;
CP25Data m_netData;
CP25LowSpeedData m_rfLSD;
CP25LowSpeedData m_netLSD;
unsigned char* m_netLDU1;
unsigned char* m_netLDU2;
unsigned char* m_lastIMBE;
unsigned char* m_rfLDU;
CRSSIInterpolator* m_rssiMapper;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
unsigned int m_rssiCount;
FILE* m_fp;
void writeQueueRF(const unsigned char* data, unsigned int length);
void writeQueueNet(const unsigned char* data, unsigned int length);

View file

@ -1,6 +1,6 @@
These are the source files for building the MMDVMHost, the program that interfaces to the MMDVM or DVMega on the one side, and a suitable network on the other. On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it connects to Brand Meister, DMR+, and HB Link, on System Fusion it connects to the YSF Gateway. On P25 it connects to the P25NX network.
These are the source files for building the MMDVMHost, the program that interfaces to the MMDVM or DVMega on the one side, and a suitable network on the other. It supports D-Star, DMR, P25 Phase 1, and System Fusion.
It supports D-Star, DMR, P25 Phase 1, and System Fusion.
On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it can connect to BrandMeister, DMR+, or HB Link, on System Fusion it connects to the YSF Gateway. On P25 it connects to the P25 Gateway.
It builds on 32-bit and 64-bit Linux as well as on Windows using VS2015 on x86 and x64. It can optionally control various Displays. Currently these are:
@ -9,11 +9,15 @@ It builds on 32-bit and 64-bit Linux as well as on Windows using VS2015 on x86 a
- Adafruit 16x2 LCD+Keypad Kits (I2C)
- Connection via PCF8574 GPIO Extender (I2C)
- Nextion TFTs (sizes 2.4", 2.8", 3.2" and 3.5")
- TFT displays sold by Hobbytronics in UK
- TFT display sold by Hobbytronics in UK
- OLED 128x64 (SSD1306)
The HD44780 displays are integrated with wiringPi for Raspberry Pi based platforms. The other displays can be directly connected to the UART on Raspberry Pis or with FT-232RL modules to any USB port.
The Nextion displays can connect to the UART on the Raspberry Pi, or via a USB to TTL serial converter like the FT-232RL. It may also be connected to the UART output of the MMDVM modem (Arduino Due, STM32, Teensy), or to the UART output on the UMP.
The OLED display needs a extra lib see OLED.md
The HD44780 displays are integrated with wiringPi for Raspberry Pi based platforms.
The Hobbytronics TFT Display, which is a Pi-Hat, connects to the UART on the Raspbery Pi.
The OLED display needs a extra library see OLED.md
This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden.

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
*
* 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
@ -24,7 +24,7 @@
// #define DUMP_YSF
CYSFControl::CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway) :
CYSFControl::CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper) :
m_callsign(NULL),
m_network(network),
m_display(display),
@ -56,9 +56,16 @@ m_lastMR(YSF_MR_NOT_BUSY),
m_netN(0U),
m_rfPayload(),
m_netPayload(),
m_rssiMapper(rssiMapper),
m_rssi(0U),
m_maxRSSI(0U),
m_minRSSI(0U),
m_aveRSSI(0U),
m_rssiCount(0U),
m_fp(NULL)
{
assert(display != NULL);
assert(rssiMapper != NULL);
m_rfPayload.setUplink(callsign);
m_rfPayload.setDownlink(callsign);
@ -94,7 +101,10 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
unsigned char type = data[0U];
if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) {
LogMessage("YSF, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("YSF, transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("YSF, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF();
return false;
}
@ -102,6 +112,28 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
if (type == TAG_LOST)
return false;
// Have we got RSSI bytes on the end?
if (len == (YSF_FRAME_LENGTH_BYTES + 4U)) {
uint16_t raw = 0U;
raw |= (data[122U] << 8) & 0xFF00U;
raw |= (data[123U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
LogDebug("YSF, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
}
CYSFFICH fich;
bool valid = fich.decode(data + 2U);
@ -116,6 +148,11 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
m_rfTimeoutTimer.start();
m_rfPayload.reset();
m_rfState = RS_RF_AUDIO;
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
#if defined(DUMP_YSF)
openFile();
#endif
@ -185,6 +222,8 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
}
m_rfFrames++;
m_display->writeFusionRSSI(m_rssi);
} else if (valid && fi == YSF_FI_TERMINATOR) {
CSync::addYSFSync(data + 2U);
@ -209,7 +248,11 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
m_rfFrames++;
LogMessage("YSF, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits));
if (m_rssi != 0U)
LogMessage("YSF, received RF end of transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
LogMessage("YSF, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeEndRF();
return false;
@ -314,6 +357,8 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
#endif
m_rfFrames++;
m_display->writeFusionRSSI(m_rssi);
} else {
CSync::addYSFSync(data + 2U);
@ -329,6 +374,8 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
writeFile(data + 2U);
#endif
m_rfFrames++;
m_display->writeFusionRSSI(m_rssi);
}
return true;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
*
* 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
@ -19,6 +19,7 @@
#if !defined(YSFControl_H)
#define YSFControl_H
#include "RSSIInterpolator.h"
#include "YSFNetwork.h"
#include "YSFDefines.h"
#include "YSFPayload.h"
@ -33,7 +34,7 @@
class CYSFControl {
public:
CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway);
CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper);
~CYSFControl();
bool writeModem(unsigned char* data, unsigned int len);
@ -74,6 +75,12 @@ private:
unsigned char m_netN;
CYSFPayload m_rfPayload;
CYSFPayload m_netPayload;
CRSSIInterpolator* m_rssiMapper;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
unsigned int m_rssiCount;
FILE* m_fp;
void writeQueueRF(const unsigned char* data);