Add files via upload

master
EA5SW 8 years ago committed by GitHub
parent 124f47981b
commit fb47c2e870
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

File diff suppressed because it is too large Load Diff

@ -0,0 +1,122 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(DStarControl_H)
#define DStarControl_H
#include "RSSIInterpolator.h"
#include "DStarNetwork.h"
#include "DStarSlowData.h"
#include "DStarDefines.h"
#include "DStarHeader.h"
#include "RingBuffer.h"
#include "StopWatch.h"
#include "AMBEFEC.h"
#include "Display.h"
#include "Defines.h"
#include "Timer.h"
#include "Modem.h"
#include <string>
#include <vector>
class CDStarControl {
public:
CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, bool errorReply, const std::vector<std::string>& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper);
~CDStarControl();
bool writeModem(unsigned char* data, unsigned int len);
unsigned int readModem(unsigned char* data);
void clock();
private:
unsigned char* m_callsign;
unsigned char* m_gateway;
bool m_selfOnly;
bool m_ackReply;
bool m_errorReply;
bool m_remoteGateway;
std::vector<std::string> m_blackList;
CDStarNetwork* m_network;
CDisplay* m_display;
bool m_duplex;
CRingBuffer<unsigned char> m_queue;
CDStarHeader m_rfHeader;
CDStarHeader m_netHeader;
RPT_RF_STATE m_rfState;
RPT_NET_STATE m_netState;
bool m_net;
CDStarSlowData m_slowData;
unsigned char m_rfN;
unsigned char m_netN;
CTimer m_networkWatchdog;
CTimer m_rfTimeoutTimer;
CTimer m_netTimeoutTimer;
CTimer m_packetTimer;
CTimer m_ackTimer;
CTimer m_errTimer;
CStopWatch m_interval;
CStopWatch m_elapsed;
unsigned int m_rfFrames;
unsigned int m_netFrames;
unsigned int m_netLost;
CAMBEFEC m_fec;
unsigned int m_rfBits;
unsigned int m_netBits;
unsigned int m_rfErrs;
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();
void writeQueueHeaderRF(const unsigned char* data);
void writeQueueDataRF(const unsigned char* data);
void writeQueueEOTRF();
void writeQueueHeaderNet(const unsigned char* data);
void writeQueueDataNet(const unsigned char* data);
void writeQueueEOTNet();
void writeNetworkHeaderRF(const unsigned char* data);
void writeNetworkDataRF(const unsigned char* data, unsigned int errors, bool end);
void writeEndRF();
void writeEndNet();
bool openFile();
bool writeFile(const unsigned char* data, unsigned int length);
void closeFile();
bool insertSilence(const unsigned char* data, unsigned char seqNo);
void insertSilence(unsigned int count);
void blankDTMF(unsigned char* data) const;
void sendAck();
void sendError();
};
#endif

@ -0,0 +1,88 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(DStarDefines_H)
#define DStarDefines_H
#include "Defines.h"
const unsigned int DSTAR_HEADER_LENGTH_BYTES = 41U;
const unsigned int DSTAR_FRAME_LENGTH_BYTES = 12U;
const unsigned char DSTAR_END_PATTERN_BYTES[] = { TAG_EOT, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A };
const unsigned int DSTAR_END_PATTERN_LENGTH_BYTES = 6U;
const unsigned char DSTAR_NULL_AMBE_DATA_BYTES[] = { 0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8 };
const unsigned char DSTAR_NULL_SLOW_SYNC_BYTES[] = { 0x55, 0x2D, 0x16 };
// Note that these are already scrambled, 0x66 0x66 0x66 otherwise
const unsigned char DSTAR_NULL_SLOW_DATA_BYTES[] = { 0x16, 0x29, 0xF5 };
const unsigned char DSTAR_NULL_FRAME_SYNC_BYTES[] = { TAG_DATA, 0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x55, 0x2D, 0x16 };
const unsigned char DSTAR_NULL_FRAME_DATA_BYTES[] = { TAG_DATA, 0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x16, 0x29, 0xF5 };
const unsigned int DSTAR_VOICE_FRAME_LENGTH_BYTES = 9U;
const unsigned int DSTAR_DATA_FRAME_LENGTH_BYTES = 3U;
const unsigned int DSTAR_LONG_CALLSIGN_LENGTH = 8U;
const unsigned int DSTAR_SHORT_CALLSIGN_LENGTH = 4U;
const unsigned char DSTAR_SLOW_DATA_TYPE_MASK = 0xF0U;
const unsigned char DSTAR_SLOW_DATA_TYPE_GPSDATA = 0x30U;
const unsigned char DSTAR_SLOW_DATA_TYPE_TEXT = 0x40U;
const unsigned char DSTAR_SLOW_DATA_TYPE_HEADER = 0x50U;
const unsigned char DSTAR_SLOW_DATA_TYPE_SQUELCH = 0xC0U;
const unsigned char DSTAR_SLOW_DATA_LENGTH_MASK = 0x0FU;
const unsigned char DSTAR_SCRAMBLER_BYTES[] = {0x70U, 0x4FU, 0x93U};
const unsigned char DSTAR_DATA_MASK = 0x80U;
const unsigned char DSTAR_REPEATER_MASK = 0x40U;
const unsigned char DSTAR_INTERRUPTED_MASK = 0x20U;
const unsigned char DSTAR_CONTROL_SIGNAL_MASK = 0x10U;
const unsigned char DSTAR_URGENT_MASK = 0x08U;
const unsigned char DSTAR_REPEATER_CONTROL = 0x07U;
const unsigned char DSTAR_AUTO_REPLY = 0x06U;
const unsigned char DSTAR_RESEND_REQUESTED = 0x04U;
const unsigned char DSTAR_ACK_FLAG = 0x03U;
const unsigned char DSTAR_NO_RESPONSE = 0x02U;
const unsigned char DSTAR_RELAY_UNAVAILABLE = 0x01U;
const unsigned char DSTAR_SYNC_BYTES[] = {0x55U, 0x2DU, 0x16U};
const unsigned char DSTAR_DTMF_MASK[] = { 0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U };
const unsigned char DSTAR_DTMF_SIG[] = { 0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U };
const unsigned int DSTAR_FRAME_TIME = 20U;
enum LINK_STATUS {
LS_NONE,
LS_PENDING_IRCDDB,
LS_LINKING_LOOPBACK,
LS_LINKING_DEXTRA,
LS_LINKING_DPLUS,
LS_LINKING_DCS,
LS_LINKING_CCS,
LS_LINKED_LOOPBACK,
LS_LINKED_DEXTRA,
LS_LINKED_DPLUS,
LS_LINKED_DCS,
LS_LINKED_CCS
};
#endif

@ -0,0 +1,165 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "DStarHeader.h"
#include "CRC.h"
#include <cstdio>
#include <cassert>
#include <cstring>
CDStarHeader::CDStarHeader(const unsigned char* header) :
m_header(NULL)
{
assert(header != NULL);
m_header = new unsigned char[DSTAR_HEADER_LENGTH_BYTES];
::memcpy(m_header, header, DSTAR_HEADER_LENGTH_BYTES);
}
CDStarHeader::CDStarHeader() :
m_header(NULL)
{
m_header = new unsigned char[DSTAR_HEADER_LENGTH_BYTES];
::memset(m_header, ' ', DSTAR_HEADER_LENGTH_BYTES);
m_header[0U] = 0x00U;
m_header[1U] = 0x00U;
m_header[2U] = 0x00U;
}
CDStarHeader::~CDStarHeader()
{
delete[] m_header;
}
CDStarHeader& CDStarHeader::operator=(const CDStarHeader& header)
{
if (&header != this)
::memcpy(m_header, header.m_header, DSTAR_HEADER_LENGTH_BYTES);
return *this;
}
bool CDStarHeader::isRepeater() const
{
return (m_header[0U] & DSTAR_REPEATER_MASK) == DSTAR_REPEATER_MASK;
}
void CDStarHeader::setRepeater(bool on)
{
if (on)
m_header[0U] |= DSTAR_REPEATER_MASK;
else
m_header[0U] &= ~DSTAR_REPEATER_MASK;
}
bool CDStarHeader::isDataPacket() const
{
return (m_header[0U] & DSTAR_DATA_MASK) == DSTAR_DATA_MASK;
}
void CDStarHeader::setUnavailable(bool on)
{
if (on)
m_header[0U] |= DSTAR_RELAY_UNAVAILABLE;
else
m_header[0U] &= ~DSTAR_RELAY_UNAVAILABLE;
}
void CDStarHeader::getMyCall1(unsigned char* call1) const
{
assert(call1 != NULL);
::memcpy(call1, m_header + 27U, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::getMyCall2(unsigned char* call2) const
{
assert(call2 != NULL);
::memcpy(call2, m_header + 35U, DSTAR_SHORT_CALLSIGN_LENGTH);
}
void CDStarHeader::setMyCall1(const unsigned char* call1)
{
assert(call1 != NULL);
::memcpy(m_header + 27U, call1, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::setMyCall2(const unsigned char* call2)
{
assert(call2 != NULL);
::memcpy(m_header + 35U, call2, DSTAR_SHORT_CALLSIGN_LENGTH);
}
void CDStarHeader::getRPTCall1(unsigned char* call1) const
{
assert(call1 != NULL);
::memcpy(call1, m_header + 11U, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::getRPTCall2(unsigned char* call2) const
{
assert(call2 != NULL);
::memcpy(call2, m_header + 3U, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::setRPTCall1(const unsigned char* call1)
{
assert(call1 != NULL);
::memcpy(m_header + 11U, call1, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::setRPTCall2(const unsigned char* call2)
{
assert(call2 != NULL);
::memcpy(m_header + 3U, call2, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::getYourCall(unsigned char* call) const
{
assert(call != NULL);
::memcpy(call, m_header + 19U, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::setYourCall(const unsigned char* call)
{
assert(call != NULL);
::memcpy(m_header + 19U, call, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::get(unsigned char* header) const
{
assert(header != NULL);
::memcpy(header, m_header, DSTAR_HEADER_LENGTH_BYTES);
CCRC::addCCITT161(header, DSTAR_HEADER_LENGTH_BYTES);
}

@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DStarHeader_H
#define DStarHeader_H
class CDStarHeader {
public:
CDStarHeader(const unsigned char* header);
CDStarHeader();
~CDStarHeader();
bool isRepeater() const;
void setRepeater(bool on);
bool isDataPacket() const;
void setUnavailable(bool on);
void getMyCall1(unsigned char* call1) const;
void getMyCall2(unsigned char* call2) const;
void setMyCall1(const unsigned char* call1);
void setMyCall2(const unsigned char* call2);
void getRPTCall1(unsigned char* call1) const;
void getRPTCall2(unsigned char* call2) const;
void setRPTCall1(const unsigned char* call1);
void setRPTCall2(const unsigned char* call2);
void getYourCall(unsigned char* call) const;
void setYourCall(const unsigned char* call);
void get(unsigned char* header) const;
CDStarHeader& operator=(const CDStarHeader& header);
private:
unsigned char* m_header;
};
#endif

@ -0,0 +1,333 @@
/*
* Copyright (C) 2009-2014,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "DStarNetwork.h"
#include "StopWatch.h"
#include "Defines.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned int BUFFER_LENGTH = 100U;
CDStarNetwork::CDStarNetwork(const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int localPort, bool duplex, const char* version, bool debug) :
m_socket(localPort),
m_address(),
m_port(gatewayPort),
m_duplex(duplex),
m_version(version),
m_debug(debug),
m_enabled(false),
m_outId(0U),
m_outSeq(0U),
m_inId(0U),
m_buffer(1000U, "D-Star Network"),
m_pollTimer(1000U, 60U),
m_linkStatus(LS_NONE),
m_linkReflector(NULL)
{
m_address = CUDPSocket::lookup(gatewayAddress);
m_linkReflector = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
CStopWatch stopWatch;
::srand(stopWatch.start());
}
CDStarNetwork::~CDStarNetwork()
{
delete[] m_linkReflector;
}
bool CDStarNetwork::open()
{
LogMessage("Opening D-Star network connection");
if (m_address.s_addr == INADDR_NONE)
return false;
m_pollTimer.start();
return m_socket.open();
}
bool CDStarNetwork::writeHeader(const unsigned char* header, unsigned int length, bool busy)
{
assert(header != NULL);
unsigned char buffer[50U];
buffer[0] = 'D';
buffer[1] = 'S';
buffer[2] = 'R';
buffer[3] = 'P';
buffer[4] = busy ? 0x22U : 0x20U;
// Create a random id for this transmission
m_outId = (::rand() % 65535U) + 1U;
buffer[5] = m_outId / 256U; // Unique session id
buffer[6] = m_outId % 256U;
buffer[7] = 0U;
::memcpy(buffer + 8U, header, length);
m_outSeq = 0U;
if (m_debug)
CUtils::dump(1U, "D-Star Network Header Sent", buffer, 49U);
for (unsigned int i = 0U; i < 2U; i++) {
bool ret = m_socket.write(buffer, 49U, m_address, m_port);
if (!ret)
return false;
}
return true;
}
bool CDStarNetwork::writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end, bool busy)
{
assert(data != NULL);
unsigned char buffer[30U];
buffer[0] = 'D';
buffer[1] = 'S';
buffer[2] = 'R';
buffer[3] = 'P';
buffer[4] = busy ? 0x23U : 0x21U;
buffer[5] = m_outId / 256U; // Unique session id
buffer[6] = m_outId % 256U;
// If this is a data sync, reset the sequence to zero
if (data[9] == 0x55 && data[10] == 0x2D && data[11] == 0x16)
m_outSeq = 0U;
buffer[7] = m_outSeq;
if (end)
buffer[7] |= 0x40U; // End of data marker
buffer[8] = errors;
m_outSeq++;
if (m_outSeq > 0x14U)
m_outSeq = 0U;
::memcpy(buffer + 9U, data, length);
if (m_debug)
CUtils::dump(1U, "D-Star Network Data Sent", buffer, length + 9U);
return m_socket.write(buffer, length + 9U, m_address, m_port);
}
bool CDStarNetwork::writePoll(const char* text)
{
assert(text != NULL);
unsigned char buffer[40U];
buffer[0] = 'D';
buffer[1] = 'S';
buffer[2] = 'R';
buffer[3] = 'P';
buffer[4] = 0x0A; // Poll with text
unsigned int length = ::strlen(text);
// Include the nul at the end also
::memcpy(buffer + 5U, text, length + 1U);
// if (m_debug)
// CUtils::dump(1U, "D-Star Network Poll Sent", buffer, 6U + length);
return m_socket.write(buffer, 6U + length, m_address, m_port);
}
void CDStarNetwork::clock(unsigned int ms)
{
m_pollTimer.clock(ms);
if (m_pollTimer.hasExpired()) {
char text[60U];
#if defined(_WIN32) || defined(_WIN64)
if (m_duplex)
::sprintf(text, "win_mmdvm-%s", m_version);
else
::sprintf(text, "win_mmdvm-dvmega-%s", m_version);
#else
if (m_duplex)
::sprintf(text, "linux_mmdvm-%s", m_version);
else
::sprintf(text, "linux_mmdvm-dvmega-%s", m_version);
#endif
writePoll(text);
m_pollTimer.start();
}
unsigned char buffer[BUFFER_LENGTH];
in_addr address;
unsigned int port;
int length = m_socket.read(buffer, BUFFER_LENGTH, address, port);
if (length <= 0)
return;
// Check if the data is for us
if (m_address.s_addr != address.s_addr || m_port != port) {
LogMessage("D-Star packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port);
return;
}
// Invalid packet type?
if (::memcmp(buffer, "DSRP", 4U) != 0)
return;
switch (buffer[4]) {
case 0x00U: // NETWORK_TEXT;
if (m_debug)
CUtils::dump(1U, "D-Star Network Status Received", buffer, length);
m_linkStatus = LINK_STATUS(buffer[25U]);
::memcpy(m_linkReflector, buffer + 26U, DSTAR_LONG_CALLSIGN_LENGTH);
LogMessage("D-Star link status set to \"%20.20s\"", buffer + 5U);
return;
case 0x01U: // NETWORK_TEMPTEXT;
case 0x04U: // NETWORK_STATUS1..5
case 0x24U: // NETWORK_DD_DATA
return;
case 0x20U: // NETWORK_HEADER
if (m_inId == 0U && m_enabled) {
if (m_debug)
CUtils::dump(1U, "D-Star Network Header Received", buffer, length);
m_inId = buffer[5] * 256U + buffer[6];
unsigned char c = length - 7U;
m_buffer.addData(&c, 1U);
c = TAG_HEADER;
m_buffer.addData(&c, 1U);
m_buffer.addData(buffer + 8U, length - 8U);
}
break;
case 0x21U: // NETWORK_DATA
if (m_enabled) {
if (m_debug)
CUtils::dump(1U, "D-Star Network Data Received", buffer, length);
uint16_t id = buffer[5] * 256U + buffer[6];
// Check that the stream id matches the valid header, reject otherwise
if (id == m_inId && m_enabled) {
unsigned char ctrl[3U];
ctrl[0U] = length - 7U;
// Is this the last packet in the stream?
if ((buffer[7] & 0x40) == 0x40) {
m_inId = 0U;
ctrl[1U] = TAG_EOT;
} else {
ctrl[1U] = TAG_DATA;
}
ctrl[2U] = buffer[7] & 0x3FU;
m_buffer.addData(ctrl, 3U);
m_buffer.addData(buffer + 9U, length - 9U);
}
}
break;
default:
CUtils::dump("Unknown D-Star packet from the Gateway", buffer, length);
break;
}
}
unsigned int CDStarNetwork::read(unsigned char* data, unsigned int length)
{
assert(data != NULL);
if (m_buffer.isEmpty())
return 0U;
unsigned char c = 0U;
m_buffer.getData(&c, 1U);
assert(c <= 100U);
assert(c <= length);
unsigned char buffer[100U];
m_buffer.getData(buffer, c);
switch (buffer[0U]) {
case TAG_HEADER:
case TAG_DATA:
case TAG_EOT:
::memcpy(data, buffer, c);
return c;
default:
return 0U;
}
}
void CDStarNetwork::reset()
{
m_inId = 0U;
}
void CDStarNetwork::close()
{
m_socket.close();
LogMessage("Closing D-Star network connection");
}
void CDStarNetwork::enable(bool enabled)
{
if (enabled && !m_enabled)
reset();
m_enabled = enabled;
}
void CDStarNetwork::getStatus(LINK_STATUS& status, unsigned char* reflector)
{
assert(reflector != NULL);
status = m_linkStatus;
::memcpy(reflector, m_linkReflector, DSTAR_LONG_CALLSIGN_LENGTH);
}

@ -0,0 +1,71 @@
/*
* Copyright (C) 2009-2014,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DStarNetwork_H
#define DStarNetwork_H
#include "DStarDefines.h"
#include "RingBuffer.h"
#include "UDPSocket.h"
#include "Timer.h"
#include <cstdint>
#include <string>
class CDStarNetwork {
public:
CDStarNetwork(const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int localPort, bool duplex, const char* version, bool debug);
~CDStarNetwork();
bool open();
void enable(bool enabled);
bool writeHeader(const unsigned char* header, unsigned int length, bool busy);
bool writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end, bool busy);
void getStatus(LINK_STATUS& status, unsigned char* reflector);
unsigned int read(unsigned char* data, unsigned int length);
void reset();
void close();
void clock(unsigned int ms);
private:
CUDPSocket m_socket;
in_addr m_address;
unsigned int m_port;
bool m_duplex;
const char* m_version;
bool m_debug;
bool m_enabled;
uint16_t m_outId;
uint8_t m_outSeq;
uint16_t m_inId;
CRingBuffer<unsigned char> m_buffer;
CTimer m_pollTimer;
LINK_STATUS m_linkStatus;
unsigned char* m_linkReflector;
bool writePoll(const char* text);
};
#endif

@ -0,0 +1,158 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarSlowData.h"
#include "DStarDefines.h"
#include "CRC.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
CDStarSlowData::CDStarSlowData() :
m_header(NULL),
m_ptr(0U),
m_buffer(NULL),
m_text(NULL),
m_textPtr(0U),
m_state(SDD_FIRST)
{
m_header = new unsigned char[50U]; // DSTAR_HEADER_LENGTH_BYTES
m_buffer = new unsigned char[DSTAR_DATA_FRAME_LENGTH_BYTES * 2U];
m_text = new unsigned char[24U];
}
CDStarSlowData::~CDStarSlowData()
{
delete[] m_header;
delete[] m_buffer;
delete[] m_text;
}
CDStarHeader* CDStarSlowData::add(const unsigned char* data)
{
assert(data != NULL);
switch (m_state) {
case SDD_FIRST:
m_buffer[0U] = data[9U] ^ DSTAR_SCRAMBLER_BYTES[0U];
m_buffer[1U] = data[10U] ^ DSTAR_SCRAMBLER_BYTES[1U];
m_buffer[2U] = data[11U] ^ DSTAR_SCRAMBLER_BYTES[2U];
m_state = SDD_SECOND;
return NULL;
case SDD_SECOND:
m_buffer[3U] = data[9U] ^ DSTAR_SCRAMBLER_BYTES[0U];
m_buffer[4U] = data[10U] ^ DSTAR_SCRAMBLER_BYTES[1U];
m_buffer[5U] = data[11U] ^ DSTAR_SCRAMBLER_BYTES[2U];
m_state = SDD_FIRST;
break;
}
if ((m_buffer[0U] & DSTAR_SLOW_DATA_TYPE_MASK) != DSTAR_SLOW_DATA_TYPE_HEADER)
return NULL;
if (m_ptr >= 45U)
return NULL;
::memcpy(m_header + m_ptr, m_buffer + 1U, 5U);
m_ptr += 5U;
// Clean up the data
m_header[0U] &= (DSTAR_INTERRUPTED_MASK | DSTAR_URGENT_MASK | DSTAR_REPEATER_MASK);
m_header[1U] = 0x00U;
m_header[2U] = 0x00U;
for (unsigned int i = 3U; i < 39U; i++)
m_header[i] &= 0x7FU;
// Check the CRC
bool ret = CCRC::checkCCITT161(m_header, DSTAR_HEADER_LENGTH_BYTES);
if (!ret) {
if (m_ptr == 45U)
LogMessage("D-Star, invalid slow data header");
return NULL;
}
return new CDStarHeader(m_header);
}
void CDStarSlowData::start()
{
::memset(m_header, 0x00U, DSTAR_HEADER_LENGTH_BYTES);
m_ptr = 0U;
m_state = SDD_FIRST;
}
void CDStarSlowData::reset()
{
m_ptr = 0U;
m_state = SDD_FIRST;
}
void CDStarSlowData::setText(const char* text)
{
assert(text != NULL);
m_text[0U] = DSTAR_SLOW_DATA_TYPE_TEXT | 0U;
m_text[1U] = text[0U];
m_text[2U] = text[1U];
m_text[3U] = text[2U];
m_text[4U] = text[3U];
m_text[5U] = text[4U];
m_text[6U] = DSTAR_SLOW_DATA_TYPE_TEXT | 1U;
m_text[7U] = text[5U];
m_text[8U] = text[6U];
m_text[9U] = text[7U];
m_text[10U] = text[8U];
m_text[11U] = text[9U];
m_text[12U] = DSTAR_SLOW_DATA_TYPE_TEXT | 2U;
m_text[13U] = text[10U];
m_text[14U] = text[11U];
m_text[15U] = text[12U];
m_text[16U] = text[13U];
m_text[17U] = text[14U];
m_text[18U] = DSTAR_SLOW_DATA_TYPE_TEXT | 3U;
m_text[19U] = text[15U];
m_text[20U] = text[16U];
m_text[21U] = text[17U];
m_text[22U] = text[18U];
m_text[23U] = text[19U];
m_textPtr = 0U;
}
void CDStarSlowData::get(unsigned char* data)
{
assert(data != NULL);
if (m_textPtr < 24U) {
data[0U] = m_text[m_textPtr++] ^ DSTAR_SCRAMBLER_BYTES[0U];
data[1U] = m_text[m_textPtr++] ^ DSTAR_SCRAMBLER_BYTES[1U];
data[2U] = m_text[m_textPtr++] ^ DSTAR_SCRAMBLER_BYTES[2U];
} else {
data[0U] = 'f' ^ DSTAR_SCRAMBLER_BYTES[0U];
data[1U] = 'f' ^ DSTAR_SCRAMBLER_BYTES[1U];
data[2U] = 'f' ^ DSTAR_SCRAMBLER_BYTES[2U];
}
}

@ -0,0 +1,52 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DStarSlowData_H
#define DStarSlowData_H
#include "DStarHeader.h"
class CDStarSlowData {
public:
CDStarSlowData();
~CDStarSlowData();
CDStarHeader* add(const unsigned char* data);
void start();
void reset();
void setText(const char* text);
void get(unsigned char* data);
private:
unsigned char* m_header;
unsigned int m_ptr;
unsigned char* m_buffer;
unsigned char* m_text;
unsigned int m_textPtr;
enum SDD_STATE {
SDD_FIRST,
SDD_SECOND
};
SDD_STATE m_state;
};
#endif

@ -0,0 +1,262 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Golay2087.h"
#include <cstdio>
#include <cassert>
const unsigned int ENCODING_TABLE_2087[] =
{0x0000U, 0xB08EU, 0xE093U, 0x501DU, 0x70A9U, 0xC027U, 0x903AU, 0x20B4U, 0x60DCU, 0xD052U, 0x804FU, 0x30C1U,
0x1075U, 0xA0FBU, 0xF0E6U, 0x4068U, 0x7036U, 0xC0B8U, 0x90A5U, 0x202BU, 0x009FU, 0xB011U, 0xE00CU, 0x5082U,
0x10EAU, 0xA064U, 0xF079U, 0x40F7U, 0x6043U, 0xD0CDU, 0x80D0U, 0x305EU, 0xD06CU, 0x60E2U, 0x30FFU, 0x8071U,
0xA0C5U, 0x104BU, 0x4056U, 0xF0D8U, 0xB0B0U, 0x003EU, 0x5023U, 0xE0ADU, 0xC019U, 0x7097U, 0x208AU, 0x9004U,
0xA05AU, 0x10D4U, 0x40C9U, 0xF047U, 0xD0F3U, 0x607DU, 0x3060U, 0x80EEU, 0xC086U, 0x7008U, 0x2015U, 0x909BU,
0xB02FU, 0x00A1U, 0x50BCU, 0xE032U, 0x90D9U, 0x2057U, 0x704AU, 0xC0C4U, 0xE070U, 0x50FEU, 0x00E3U, 0xB06DU,
0xF005U, 0x408BU, 0x1096U, 0xA018U, 0x80ACU, 0x3022U, 0x603FU, 0xD0B1U, 0xE0EFU, 0x5061U, 0x007CU, 0xB0F2U,
0x9046U, 0x20C8U, 0x70D5U, 0xC05BU, 0x8033U, 0x30BDU, 0x60A0U, 0xD02EU, 0xF09AU, 0x4014U, 0x1009U, 0xA087U,
0x40B5U, 0xF03BU, 0xA026U, 0x10A8U, 0x301CU, 0x8092U, 0xD08FU, 0x6001U, 0x2069U, 0x90E7U, 0xC0FAU, 0x7074U,
0x50C0U, 0xE04EU, 0xB053U, 0x00DDU, 0x3083U, 0x800DU, 0xD010U, 0x609EU, 0x402AU, 0xF0A4U, 0xA0B9U, 0x1037U,
0x505FU, 0xE0D1U, 0xB0CCU, 0x0042U, 0x20F6U, 0x9078U, 0xC065U, 0x70EBU, 0xA03DU, 0x10B3U, 0x40AEU, 0xF020U,
0xD094U, 0x601AU, 0x3007U, 0x8089U, 0xC0E1U, 0x706FU, 0x2072U, 0x90FCU, 0xB048U, 0x00C6U, 0x50DBU, 0xE055U,
0xD00BU, 0x6085U, 0x3098U, 0x8016U, 0xA0A2U, 0x102CU, 0x4031U, 0xF0BFU, 0xB0D7U, 0x0059U, 0x5044U, 0xE0CAU,
0xC07EU, 0x70F0U, 0x20EDU, 0x9063U, 0x7051U, 0xC0DFU, 0x90C2U, 0x204CU, 0x00F8U, 0xB076U, 0xE06BU, 0x50E5U,
0x108DU, 0xA003U, 0xF01EU, 0x4090U, 0x6024U, 0xD0AAU, 0x80B7U, 0x3039U, 0x0067U, 0xB0E9U, 0xE0F4U, 0x507AU,
0x70CEU, 0xC040U, 0x905DU, 0x20D3U, 0x60BBU, 0xD035U, 0x8028U, 0x30A6U, 0x1012U, 0xA09CU, 0xF081U, 0x400FU,
0x30E4U, 0x806AU, 0xD077U, 0x60F9U, 0x404DU, 0xF0C3U, 0xA0DEU, 0x1050U, 0x5038U, 0xE0B6U, 0xB0ABU, 0x0025U,
0x2091U, 0x901FU, 0xC002U, 0x708CU, 0x40D2U, 0xF05CU, 0xA041U, 0x10CFU, 0x307BU, 0x80F5U, 0xD0E8U, 0x6066U,
0x200EU, 0x9080U, 0xC09DU, 0x7013U, 0x50A7U, 0xE029U, 0xB034U, 0x00BAU, 0xE088U, 0x5006U, 0x001BU, 0xB095U,
0x9021U, 0x20AFU, 0x70B2U, 0xC03CU, 0x8054U, 0x30DAU, 0x60C7U, 0xD049U, 0xF0FDU, 0x4073U, 0x106EU, 0xA0E0U,
0x90BEU, 0x2030U, 0x702DU, 0xC0A3U, 0xE017U, 0x5099U, 0x0084U, 0xB00AU, 0xF062U, 0x40ECU, 0x10F1U, 0xA07FU,
0x80CBU, 0x3045U, 0x6058U, 0xD0D6U};
const unsigned int DECODING_TABLE_1987[] =
{0x00000U, 0x00001U, 0x00002U, 0x00003U, 0x00004U, 0x00005U, 0x00006U, 0x00007U, 0x00008U, 0x00009U, 0x0000AU, 0x0000BU, 0x0000CU,
0x0000DU, 0x0000EU, 0x24020U, 0x00010U, 0x00011U, 0x00012U, 0x00013U, 0x00014U, 0x00015U, 0x00016U, 0x00017U, 0x00018U, 0x00019U,
0x0001AU, 0x0001BU, 0x0001CU, 0x0001DU, 0x48040U, 0x01480U, 0x00020U, 0x00021U, 0x00022U, 0x00023U, 0x00024U, 0x00025U, 0x00026U,
0x24008U, 0x00028U, 0x00029U, 0x0002AU, 0x24004U, 0x0002CU, 0x24002U, 0x24001U, 0x24000U, 0x00030U, 0x00031U, 0x00032U, 0x08180U,
0x00034U, 0x00C40U, 0x00036U, 0x00C42U, 0x00038U, 0x43000U, 0x0003AU, 0x43002U, 0x02902U, 0x24012U, 0x02900U, 0x24010U, 0x00040U,
0x00041U, 0x00042U, 0x00043U, 0x00044U, 0x00045U, 0x00046U, 0x00047U, 0x00048U, 0x00049U, 0x0004AU, 0x02500U, 0x0004CU, 0x0004DU,
0x48010U, 0x48011U, 0x00050U, 0x00051U, 0x00052U, 0x21200U, 0x00054U, 0x00C20U, 0x48008U, 0x48009U, 0x00058U, 0x00059U, 0x48004U,
0x48005U, 0x48002U, 0x48003U, 0x48000U, 0x48001U, 0x00060U, 0x00061U, 0x00062U, 0x00063U, 0x00064U, 0x00C10U, 0x10300U, 0x0B000U,
0x00068U, 0x00069U, 0x01880U, 0x01881U, 0x40181U, 0x40180U, 0x24041U, 0x24040U, 0x00070U, 0x00C04U, 0x00072U, 0x00C06U, 0x00C01U,
0x00C00U, 0x00C03U, 0x00C02U, 0x05204U, 0x00C0CU, 0x48024U, 0x48025U, 0x05200U, 0x00C08U, 0x48020U, 0x48021U, 0x00080U, 0x00081U,
0x00082U, 0x00083U, 0x00084U, 0x00085U, 0x00086U, 0x00087U, 0x00088U, 0x00089U, 0x0008AU, 0x50200U, 0x0008CU, 0x0A800U, 0x01411U,
0x01410U, 0x00090U, 0x00091U, 0x00092U, 0x08120U, 0x00094U, 0x00095U, 0x04A00U, 0x01408U, 0x00098U, 0x00099U, 0x01405U, 0x01404U,
0x01403U, 0x01402U, 0x01401U, 0x01400U, 0x000A0U, 0x000A1U, 0x000A2U, 0x08110U, 0x000A4U, 0x000A5U, 0x42400U, 0x42401U, 0x000A8U,
0x000A9U, 0x01840U, 0x01841U, 0x40141U, 0x40140U, 0x24081U, 0x24080U, 0x000B0U, 0x08102U, 0x08101U, 0x08100U, 0x000B4U, 0x08106U,
0x08105U, 0x08104U, 0x20A01U, 0x20A00U, 0x08109U, 0x08108U, 0x01423U, 0x01422U, 0x01421U, 0x01420U, 0x000C0U, 0x000C1U, 0x000C2U,
0x000C3U, 0x000C4U, 0x000C5U, 0x000C6U, 0x000C7U, 0x000C8U, 0x000C9U, 0x01820U, 0x01821U, 0x20600U, 0x40120U, 0x16000U, 0x16001U,
0x000D0U, 0x000D1U, 0x42801U, 0x42800U, 0x03100U, 0x18200U, 0x03102U, 0x18202U, 0x000D8U, 0x000D9U, 0x48084U, 0x01444U, 0x48082U,
0x01442U, 0x48080U, 0x01440U, 0x000E0U, 0x32000U, 0x01808U, 0x04600U, 0x40109U, 0x40108U, 0x0180CU, 0x4010AU, 0x01802U, 0x40104U,
0x01800U, 0x01801U, 0x40101U, 0x40100U, 0x01804U, 0x40102U, 0x0A408U, 0x08142U, 0x08141U, 0x08140U, 0x00C81U, 0x00C80U, 0x00C83U,
0x00C82U, 0x0A400U, 0x0A401U, 0x01810U, 0x01811U, 0x40111U, 0x40110U, 0x01814U, 0x40112U, 0x00100U, 0x00101U, 0x00102U, 0x00103U,
0x00104U, 0x00105U, 0x00106U, 0x41800U, 0x00108U, 0x00109U, 0x0010AU, 0x02440U, 0x0010CU, 0x0010DU, 0x0010EU, 0x02444U, 0x00110U,
0x00111U, 0x00112U, 0x080A0U, 0x00114U, 0x00115U, 0x00116U, 0x080A4U, 0x00118U, 0x00119U, 0x15000U, 0x15001U, 0x02822U, 0x02823U,
0x02820U, 0x02821U, 0x00120U, 0x00121U, 0x00122U, 0x08090U, 0x00124U, 0x00125U, 0x10240U, 0x10241U, 0x00128U, 0x00129U, 0x0012AU,
0x24104U, 0x09400U, 0x400C0U, 0x02810U, 0x24100U, 0x00130U, 0x08082U, 0x08081U, 0x08080U, 0x31001U, 0x31000U, 0x02808U, 0x08084U,
0x02806U, 0x0808AU, 0x02804U, 0x08088U, 0x02802U, 0x02803U, 0x02800U, 0x02801U, 0x00140U, 0x00141U, 0x00142U, 0x02408U, 0x00144U,
0x00145U, 0x10220U, 0x10221U, 0x00148U, 0x02402U, 0x02401U, 0x02400U, 0x400A1U, 0x400A0U, 0x02405U, 0x02404U, 0x00150U, 0x00151U,
0x00152U, 0x02418U, 0x03080U, 0x03081U, 0x03082U, 0x03083U, 0x09801U, 0x09800U, 0x02411U, 0x02410U, 0x48102U, 0x09804U, 0x48100U,
0x48101U, 0x00160U, 0x00161U, 0x10204U, 0x10205U, 0x10202U, 0x40088U, 0x10200U, 0x10201U, 0x40085U, 0x40084U, 0x02421U, 0x02420U,
0x40081U, 0x40080U, 0x10208U, 0x40082U, 0x41402U, 0x080C2U, 0x41400U, 0x080C0U, 0x00D01U, 0x00D00U, 0x10210U, 0x10211U, 0x40095U,
0x40094U, 0x02844U, 0x080C8U, 0x40091U, 0x40090U, 0x02840U, 0x02841U, 0x00180U, 0x00181U, 0x00182U, 0x08030U, 0x00184U, 0x14400U,
0x22201U, 0x22200U, 0x00188U, 0x00189U, 0x0018AU, 0x08038U, 0x40061U, 0x40060U, 0x40063U, 0x40062U, 0x00190U, 0x08022U, 0x08021U,
0x08020U, 0x03040U, 0x03041U, 0x08025U, 0x08024U, 0x40C00U, 0x40C01U, 0x08029U, 0x08028U, 0x2C000U, 0x2C001U, 0x01501U, 0x01500U,
0x001A0U, 0x08012U, 0x08011U, 0x08010U, 0x40049U, 0x40048U, 0x08015U, 0x08014U, 0x06200U, 0x40044U, 0x30400U, 0x08018U, 0x40041U,
0x40040U, 0x40043U, 0x40042U, 0x08003U, 0x08002U, 0x08001U, 0x08000U, 0x08007U, 0x08006U, 0x08005U, 0x08004U, 0x0800BU, 0x0800AU,
0x08009U, 0x08008U, 0x40051U, 0x40050U, 0x02880U, 0x0800CU, 0x001C0U, 0x001C1U, 0x64000U, 0x64001U, 0x03010U, 0x40028U, 0x08C00U,
0x08C01U, 0x40025U, 0x40024U, 0x02481U, 0x02480U, 0x40021U, 0x40020U, 0x40023U, 0x40022U, 0x03004U, 0x03005U, 0x08061U, 0x08060U,
0x03000U, 0x03001U, 0x03002U, 0x03003U, 0x0300CU, 0x40034U, 0x30805U, 0x30804U, 0x03008U, 0x40030U, 0x30801U, 0x30800U, 0x4000DU,
0x4000CU, 0x08051U, 0x08050U, 0x40009U, 0x40008U, 0x10280U, 0x4000AU, 0x40005U, 0x40004U, 0x01900U, 0x40006U, 0x40001U, 0x40000U,
0x40003U, 0x40002U, 0x14800U, 0x08042U, 0x08041U, 0x08040U, 0x03020U, 0x40018U, 0x08045U, 0x08044U, 0x40015U, 0x40014U, 0x08049U,
0x08048U, 0x40011U, 0x40010U, 0x40013U, 0x40012U, 0x00200U, 0x00201U, 0x00202U, 0x00203U, 0x00204U, 0x00205U, 0x00206U, 0x00207U,
0x00208U, 0x00209U, 0x0020AU, 0x50080U, 0x0020CU, 0x0020DU, 0x0020EU, 0x50084U, 0x00210U, 0x00211U, 0x00212U, 0x21040U, 0x00214U,
0x00215U, 0x04880U, 0x04881U, 0x00218U, 0x00219U, 0x0E001U, 0x0E000U, 0x0021CU, 0x0021DU, 0x04888U, 0x0E004U, 0x00220U, 0x00221U,
0x00222U, 0x00223U, 0x00224U, 0x00225U, 0x10140U, 0x10141U, 0x00228U, 0x00229U, 0x0022AU, 0x24204U, 0x12401U, 0x12400U, 0x24201U,
0x24200U, 0x00230U, 0x00231U, 0x00232U, 0x21060U, 0x2A000U, 0x2A001U, 0x2A002U, 0x2A003U, 0x20881U, 0x20880U, 0x20883U, 0x20882U,
0x05040U, 0x05041U, 0x05042U, 0x24210U, 0x00240U, 0x00241U, 0x00242U, 0x21010U, 0x00244U, 0x46000U, 0x10120U, 0x10121U, 0x00248U,
0x00249U, 0x0024AU, 0x21018U, 0x20480U, 0x20481U, 0x20482U, 0x20483U, 0x00250U, 0x21002U, 0x21001U, 0x21000U, 0x18081U, 0x18080U,
0x21005U, 0x21004U, 0x12800U, 0x12801U, 0x21009U, 0x21008U, 0x05020U, 0x05021U, 0x48200U, 0x48201U, 0x00260U, 0x00261U, 0x10104U,
0x04480U, 0x10102U, 0x10103U, 0x10100U, 0x10101U, 0x62002U, 0x62003U, 0x62000U, 0x62001U, 0x05010U, 0x05011U, 0x10108U, 0x10109U,
0x0500CU, 0x21022U, 0x21021U, 0x21020U, 0x05008U, 0x00E00U, 0x10110U, 0x10111U, 0x05004U, 0x05005U, 0x05006U, 0x21028U, 0x05000U,
0x05001U, 0x05002U, 0x05003U, 0x00280U, 0x00281U, 0x00282U, 0x50008U, 0x00284U, 0x00285U, 0x04810U, 0x22100U, 0x00288U, 0x50002U,
0x50001U, 0x50000U, 0x20440U, 0x20441U, 0x50005U, 0x50004U, 0x00290U, 0x00291U, 0x04804U, 0x04805U, 0x04802U, 0x18040U, 0x04800U,
0x04801U, 0x20821U, 0x20820U, 0x50011U, 0x50010U, 0x0480AU, 0x01602U, 0x04808U, 0x01600U, 0x002A0U, 0x002A1U, 0x04441U, 0x04440U,
0x002A4U, 0x002A5U, 0x04830U, 0x04444U, 0x06100U, 0x20810U, 0x50021U, 0x50020U, 0x06104U, 0x20814U, 0x50025U, 0x50024U, 0x20809U,
0x20808U, 0x13000U, 0x08300U, 0x04822U, 0x2080CU, 0x04820U, 0x04821U, 0x20801U, 0x20800U, 0x20803U, 0x20802U, 0x20805U, 0x20804U,
0x04828U, 0x20806U, 0x002C0U, 0x002C1U, 0x04421U, 0x04420U, 0x20408U, 0x18010U, 0x2040AU, 0x18012U, 0x20404U, 0x20405U, 0x50041U,
0x50040U, 0x20400U, 0x20401U, 0x20402U, 0x20403U, 0x18005U, 0x18004U, 0x21081U, 0x21080U, 0x18001U, 0x18000U, 0x04840U, 0x18002U,
0x20414U, 0x1800CU, 0x21089U, 0x21088U, 0x20410U, 0x18008U, 0x20412U, 0x1800AU, 0x04403U, 0x04402U, 0x04401U, 0x04400U, 0x10182U,
0x04406U, 0x10180U, 0x04404U, 0x01A02U, 0x0440AU, 0x01A00U, 0x04408U, 0x20420U, 0x40300U, 0x20422U, 0x40302U, 0x04413U, 0x04412U,
0x04411U, 0x04410U, 0x18021U, 0x18020U, 0x10190U, 0x18022U, 0x20841U, 0x20840U, 0x01A10U, 0x20842U, 0x05080U, 0x05081U, 0x05082U,
0x05083U, 0x00300U, 0x00301U, 0x00302U, 0x00303U, 0x00304U, 0x00305U, 0x10060U, 0x22080U, 0x00308U, 0x00309U, 0x28800U, 0x28801U,
0x44402U, 0x44403U, 0x44400U, 0x44401U, 0x00310U, 0x00311U, 0x10C01U, 0x10C00U, 0x00314U, 0x00315U, 0x10070U, 0x10C04U, 0x00318U,
0x00319U, 0x28810U, 0x10C08U, 0x44412U, 0x00000U, 0x44410U, 0x44411U, 0x00320U, 0x60400U, 0x10044U, 0x10045U, 0x10042U, 0x0C800U,
0x10040U, 0x10041U, 0x06080U, 0x06081U, 0x06082U, 0x06083U, 0x1004AU, 0x0C808U, 0x10048U, 0x10049U, 0x58008U, 0x08282U, 0x08281U,
0x08280U, 0x10052U, 0x0C810U, 0x10050U, 0x10051U, 0x58000U, 0x58001U, 0x58002U, 0x08288U, 0x02A02U, 0x02A03U, 0x02A00U, 0x02A01U,
0x00340U, 0x00341U, 0x10024U, 0x10025U, 0x10022U, 0x10023U, 0x10020U, 0x10021U, 0x34001U, 0x34000U, 0x02601U, 0x02600U, 0x1002AU,
0x34004U, 0x10028U, 0x10029U, 0x0C400U, 0x0C401U, 0x21101U, 0x21100U, 0x60800U, 0x60801U, 0x10030U, 0x10031U, 0x0C408U, 0x34010U,
0x21109U, 0x21108U, 0x60808U, 0x60809U, 0x10038U, 0x28420U, 0x10006U, 0x10007U, 0x10004U, 0x10005U, 0x10002U, 0x10003U, 0x10000U,
0x10001U, 0x1000EU, 0x40284U, 0x1000CU, 0x1000DU, 0x1000AU, 0x40280U, 0x10008U, 0x10009U, 0x10016U, 0x10017U, 0x10014U, 0x10015U,
0x10012U, 0x10013U, 0x10010U, 0x10011U, 0x05104U, 0x44802U, 0x44801U, 0x44800U, 0x05100U, 0x05101U, 0x10018U, 0x28400U, 0x00380U,
0x00381U, 0x22005U, 0x22004U, 0x22003U, 0x22002U, 0x22001U, 0x22000U, 0x06020U, 0x06021U, 0x50101U, 0x50100U, 0x11800U, 0x11801U,
0x22009U, 0x22008U, 0x45001U, 0x45000U, 0x08221U, 0x08220U, 0x04902U, 0x22012U, 0x04900U, 0x22010U, 0x06030U, 0x45008U, 0x08229U,
0x08228U, 0x11810U, 0x11811U, 0x04908U, 0x22018U, 0x06008U, 0x06009U, 0x08211U, 0x08210U, 0x100C2U, 0x22022U, 0x100C0U, 0x22020U,
0x06000U, 0x06001U, 0x06002U, 0x06003U, 0x06004U, 0x40240U, 0x06006U, 0x40242U, 0x08203U, 0x08202U, 0x08201U, 0x08200U, 0x08207U,
0x08206U, 0x08205U, 0x08204U, 0x06010U, 0x20900U, 0x08209U, 0x08208U, 0x61002U, 0x20904U, 0x61000U, 0x61001U, 0x29020U, 0x29021U,
0x100A4U, 0x22044U, 0x100A2U, 0x22042U, 0x100A0U, 0x22040U, 0x20504U, 0x40224U, 0x0D005U, 0x0D004U, 0x20500U, 0x40220U, 0x0D001U,
0x0D000U, 0x03204U, 0x18104U, 0x08261U, 0x08260U, 0x03200U, 0x18100U, 0x03202U, 0x18102U, 0x11421U, 0x11420U, 0x00000U, 0x11422U,
0x03208U, 0x18108U, 0x0D011U, 0x0D010U, 0x29000U, 0x29001U, 0x10084U, 0x04500U, 0x10082U, 0x40208U, 0x10080U, 0x10081U, 0x06040U,
0x40204U, 0x06042U, 0x40206U, 0x40201U, 0x40200U, 0x10088U, 0x40202U, 0x29010U, 0x08242U, 0x08241U, 0x08240U, 0x10092U, 0x40218U,
0x10090U, 0x10091U, 0x11401U, 0x11400U, 0x11403U, 0x11402U, 0x40211U, 0x40210U, 0x10098U, 0x40212U, 0x00400U, 0x00401U, 0x00402U,
0x00403U, 0x00404U, 0x00405U, 0x00406U, 0x00407U, 0x00408U, 0x00409U, 0x0040AU, 0x02140U, 0x0040CU, 0x0040DU, 0x01091U, 0x01090U,
0x00410U, 0x00411U, 0x00412U, 0x00413U, 0x00414U, 0x00860U, 0x01089U, 0x01088U, 0x00418U, 0x38000U, 0x01085U, 0x01084U, 0x01083U,
0x01082U, 0x01081U, 0x01080U, 0x00420U, 0x00421U, 0x00422U, 0x00423U, 0x00424U, 0x00850U, 0x42080U, 0x42081U, 0x00428U, 0x00429U,
0x48801U, 0x48800U, 0x09100U, 0x12200U, 0x24401U, 0x24400U, 0x00430U, 0x00844U, 0x00432U, 0x00846U, 0x00841U, 0x00840U, 0x1C000U,
0x00842U, 0x00438U, 0x0084CU, 0x010A5U, 0x010A4U, 0x00849U, 0x00848U, 0x010A1U, 0x010A0U, 0x00440U, 0x00441U, 0x00442U, 0x02108U,
0x00444U, 0x00830U, 0x70001U, 0x70000U, 0x00448U, 0x02102U, 0x02101U, 0x02100U, 0x20280U, 0x20281U, 0x02105U, 0x02104U, 0x00450U,
0x00824U, 0x00452U, 0x00826U, 0x00821U, 0x00820U, 0x00823U, 0x00822U, 0x24802U, 0x02112U, 0x24800U, 0x02110U, 0x00829U, 0x00828U,
0x48400U, 0x010C0U, 0x00460U, 0x00814U, 0x04281U, 0x04280U, 0x00811U, 0x00810U, 0x00813U, 0x00812U, 0x54000U, 0x54001U, 0x02121U,
0x02120U, 0x00819U, 0x00818U, 0x0081BU, 0x0081AU, 0x00805U, 0x00804U, 0x41100U, 0x00806U, 0x00801U, 0x00800U, 0x00803U, 0x00802U,
0x0A080U, 0x0080CU, 0x0A082U, 0x0080EU, 0x00809U, 0x00808U, 0x0080BU, 0x0080AU, 0x00480U, 0x00481U, 0x00482U, 0x00483U, 0x00484U,
0x14100U, 0x42020U, 0x01018U, 0x00488U, 0x00489U, 0x01015U, 0x01014U, 0x20240U, 0x01012U, 0x01011U, 0x01010U, 0x00490U, 0x00491U,
0x0100DU, 0x0100CU, 0x0100BU, 0x0100AU, 0x01009U, 0x01008U, 0x40900U, 0x01006U, 0x01005U, 0x01004U, 0x01003U, 0x01002U, 0x01001U,
0x01000U, 0x004A0U, 0x004A1U, 0x42004U, 0x04240U, 0x42002U, 0x42003U, 0x42000U, 0x42001U, 0x30102U, 0x30103U, 0x30100U, 0x30101U,
0x4200AU, 0x01032U, 0x42008U, 0x01030U, 0x25000U, 0x25001U, 0x08501U, 0x08500U, 0x008C1U, 0x008C0U, 0x42010U, 0x01028U, 0x0A040U,
0x0A041U, 0x01025U, 0x01024U, 0x01023U, 0x01022U, 0x01021U, 0x01020U, 0x004C0U, 0x49000U, 0x04221U, 0x04220U, 0x20208U, 0x20209U,
0x08900U, 0x08901U, 0x20204U, 0x20205U, 0x02181U, 0x02180U, 0x20200U, 0x20201U, 0x20202U, 0x01050U, 0x0A028U, 0x008A4U, 0x0104DU,
0x0104CU, 0x008A1U, 0x008A0U, 0x01049U, 0x01048U, 0x0A020U, 0x0A021U, 0x01045U, 0x01044U, 0x20210U, 0x01042U, 0x01041U, 0x01040U,
0x04203U, 0x04202U, 0x04201U, 0x04200U, 0x00891U, 0x00890U, 0x42040U, 0x04204U, 0x0A010U, 0x0A011U, 0x01C00U, 0x04208U, 0x20220U,
0x40500U, 0x20222U, 0x40502U, 0x0A008U, 0x00884U, 0x04211U, 0x04210U, 0x00881U, 0x00880U, 0x00883U, 0x00882U, 0x0A000U, 0x0A001U,
0x0A002U, 0x0A003U, 0x0A004U, 0x00888U, 0x01061U, 0x01060U, 0x00500U, 0x00501U, 0x00502U, 0x02048U, 0x00504U, 0x14080U, 0x00506U,
0x14082U, 0x00508U, 0x02042U, 0x02041U, 0x02040U, 0x09020U, 0x09021U, 0x44200U, 0x02044U, 0x00510U, 0x00511U, 0x10A01U, 0x10A00U,
0x4A001U, 0x4A000U, 0x4A003U, 0x4A002U, 0x40880U, 0x40881U, 0x02051U, 0x02050U, 0x40884U, 0x01182U, 0x01181U, 0x01180U, 0x00520U,
0x60200U, 0x00522U, 0x60202U, 0x09008U, 0x09009U, 0x0900AU, 0x0900BU, 0x09004U, 0x09005U, 0x30080U, 0x02060U, 0x09000U, 0x09001U,
0x09002U, 0x09003U, 0x41042U, 0x08482U, 0x41040U, 0x08480U, 0x00941U, 0x00940U, 0x41044U, 0x00942U, 0x09014U, 0x09015U, 0x02C04U,
0x08488U, 0x09010U, 0x09011U, 0x02C00U, 0x02C01U, 0x00540U, 0x0200AU, 0x02009U, 0x02008U, 0x08882U, 0x0200EU, 0x08880U, 0x0200CU,
0x02003U, 0x02002U, 0x02001U, 0x02000U, 0x02007U, 0x02006U, 0x02005U, 0x02004U, 0x0C200U, 0x0C201U, 0x41020U, 0x02018U, 0x00921U,
0x00920U, 0x41024U, 0x00922U, 0x02013U, 0x02012U, 0x02011U, 0x02010U, 0x02017U, 0x02016U, 0x02015U, 0x02014U, 0x41012U, 0x0202AU,
0x41010U, 0x02028U, 0x26000U, 0x00910U, 0x10600U, 0x10601U, 0x02023U, 0x02022U, 0x02021U, 0x02020U, 0x09040U, 0x40480U, 0x02025U,
0x02024U, 0x41002U, 0x00904U, 0x41000U, 0x41001U, 0x00901U, 0x00900U, 0x41004U, 0x00902U, 0x4100AU, 0x02032U, 0x41008U, 0x02030U,
0x00909U, 0x00908U, 0x28201U, 0x28200U, 0x00580U, 0x14004U, 0x00582U, 0x14006U, 0x14001U, 0x14000U, 0x08840U, 0x14002U, 0x40810U,
0x40811U, 0x30020U, 0x020C0U, 0x14009U, 0x14008U, 0x01111U, 0x01110U, 0x40808U, 0x40809U, 0x08421U, 0x08420U, 0x14011U, 0x14010U,
0x01109U, 0x01108U, 0x40800U, 0x40801U, 0x40802U, 0x01104U, 0x40804U, 0x01102U, 0x01101U, 0x01100U, 0x03801U, 0x03800U, 0x30008U,
0x08410U, 0x14021U, 0x14020U, 0x42100U, 0x42101U, 0x30002U, 0x30003U, 0x30000U, 0x30001U, 0x09080U, 0x40440U, 0x30004U, 0x30005U,
0x08403U, 0x08402U, 0x08401U, 0x08400U, 0x08407U, 0x08406U, 0x08405U, 0x08404U, 0x40820U, 0x40821U, 0x30010U, 0x08408U, 0x40824U,
0x01122U, 0x01121U, 0x01120U, 0x08806U, 0x0208AU, 0x08804U, 0x02088U, 0x08802U, 0x14040U, 0x08800U, 0x08801U, 0x02083U, 0x02082U,
0x02081U, 0x02080U, 0x20300U, 0x40420U, 0x08808U, 0x02084U, 0x03404U, 0x03405U, 0x08814U, 0x02098U, 0x03400U, 0x03401U, 0x08810U,
0x08811U, 0x40840U, 0x40841U, 0x02091U, 0x02090U, 0x40844U, 0x01142U, 0x01141U, 0x01140U, 0x04303U, 0x04302U, 0x04301U, 0x04300U,
0x40409U, 0x40408U, 0x08820U, 0x08821U, 0x40405U, 0x40404U, 0x30040U, 0x020A0U, 0x40401U, 0x40400U, 0x40403U, 0x40402U, 0x41082U,
0x08442U, 0x41080U, 0x08440U, 0x00981U, 0x00980U, 0x41084U, 0x00982U, 0x0A100U, 0x11200U, 0x0A102U, 0x11202U, 0x40411U, 0x40410U,
0x40413U, 0x40412U, 0x00600U, 0x00601U, 0x00602U, 0x00603U, 0x00604U, 0x00605U, 0x00606U, 0x00607U, 0x00608U, 0x05800U, 0x0060AU,
0x05802U, 0x200C0U, 0x12020U, 0x44100U, 0x44101U, 0x00610U, 0x00611U, 0x10901U, 0x10900U, 0x51000U, 0x51001U, 0x51002U, 0x10904U,
0x00618U, 0x05810U, 0x01285U, 0x01284U, 0x51008U, 0x01282U, 0x01281U, 0x01280U, 0x00620U, 0x60100U, 0x040C1U, 0x040C0U, 0x12009U,
0x12008U, 0x21800U, 0x21801U, 0x12005U, 0x12004U, 0x12007U, 0x12006U, 0x12001U, 0x12000U, 0x12003U, 0x12002U, 0x00630U, 0x00A44U,
0x040D1U, 0x040D0U, 0x00A41U, 0x00A40U, 0x21810U, 0x00A42U, 0x12015U, 0x12014U, 0x00000U, 0x12016U, 0x12011U, 0x12010U, 0x12013U,
0x12012U, 0x00640U, 0x00641U, 0x040A1U, 0x040A0U, 0x20088U, 0x20089U, 0x2008AU, 0x040A4U, 0x20084U, 0x20085U, 0x19000U, 0x02300U,
0x20080U, 0x20081U, 0x20082U, 0x20083U, 0x0C100U, 0x0C101U, 0x21401U, 0x21400U, 0x00A21U, 0x00A20U, 0x00A23U, 0x00A22U, 0x20094U,
0x20095U, 0x19010U, 0x21408U, 0x20090U, 0x20091U, 0x20092U, 0x28120U, 0x04083U, 0x04082U, 0x04081U, 0x04080U, 0x00A11U, 0x00A10U,
0x10500U, 0x04084U, 0x200A4U, 0x0408AU, 0x04089U, 0x04088U, 0x200A0U, 0x12040U, 0x200A2U, 0x12042U, 0x00A05U, 0x00A04U, 0x04091U,
0x04090U, 0x00A01U, 0x00A00U, 0x00A03U, 0x00A02U, 0x05404U, 0x00A0CU, 0x28105U, 0x28104U, 0x05400U, 0x00A08U, 0x28101U, 0x28100U,
0x00680U, 0x00681U, 0x04061U, 0x04060U, 0x20048U, 0x20049U, 0x2004AU, 0x04064U, 0x20044U, 0x20045U, 0x50401U, 0x50400U, 0x20040U,
0x20041U, 0x20042U, 0x01210U, 0x68002U, 0x68003U, 0x68000U, 0x68001U, 0x04C02U, 0x0120AU, 0x04C00U, 0x01208U, 0x20054U, 0x01206U,
0x01205U, 0x01204U, 0x20050U, 0x01202U, 0x01201U, 0x01200U, 0x18800U, 0x04042U, 0x04041U, 0x04040U, 0x42202U, 0x04046U, 0x42200U,
0x04044U, 0x20064U, 0x0404AU, 0x04049U, 0x04048U, 0x20060U, 0x12080U, 0x20062U, 0x12082U, 0x18810U, 0x04052U, 0x04051U, 0x04050U,
0x4C009U, 0x4C008U, 0x42210U, 0x04054U, 0x20C01U, 0x20C00U, 0x20C03U, 0x20C02U, 0x4C001U, 0x4C000U, 0x01221U, 0x01220U, 0x2000CU,
0x04022U, 0x04021U, 0x04020U, 0x20008U, 0x20009U, 0x2000AU, 0x04024U, 0x20004U, 0x20005U, 0x20006U, 0x04028U, 0x20000U, 0x20001U,
0x20002U, 0x20003U, 0x2001CU, 0x04032U, 0x04031U, 0x04030U, 0x20018U, 0x18400U, 0x2001AU, 0x18402U, 0x20014U, 0x20015U, 0x20016U,
0x01244U, 0x20010U, 0x20011U, 0x20012U, 0x01240U, 0x04003U, 0x04002U, 0x04001U, 0x04000U, 0x20028U, 0x04006U, 0x04005U, 0x04004U,
0x20024U, 0x0400AU, 0x04009U, 0x04008U, 0x20020U, 0x20021U, 0x20022U, 0x0400CU, 0x04013U, 0x04012U, 0x04011U, 0x04010U, 0x00A81U,
0x00A80U, 0x04015U, 0x04014U, 0x0A200U, 0x11100U, 0x04019U, 0x04018U, 0x20030U, 0x20031U, 0x50800U, 0x50801U, 0x00700U, 0x60020U,
0x10811U, 0x10810U, 0x4400AU, 0x60024U, 0x44008U, 0x44009U, 0x44006U, 0x02242U, 0x44004U, 0x02240U, 0x44002U, 0x44003U, 0x44000U,
0x44001U, 0x0C040U, 0x10802U, 0x10801U, 0x10800U, 0x0C044U, 0x10806U, 0x10805U, 0x10804U, 0x23000U, 0x23001U, 0x10809U, 0x10808U,
0x44012U, 0x44013U, 0x44010U, 0x44011U, 0x60001U, 0x60000U, 0x60003U, 0x60002U, 0x60005U, 0x60004U, 0x10440U, 0x10441U, 0x60009U,
0x60008U, 0x44024U, 0x6000AU, 0x09200U, 0x12100U, 0x44020U, 0x44021U, 0x60011U, 0x60010U, 0x10821U, 0x10820U, 0x07003U, 0x07002U,
0x07001U, 0x07000U, 0x23020U, 0x60018U, 0x28045U, 0x28044U, 0x09210U, 0x28042U, 0x28041U, 0x28040U, 0x0C010U, 0x0C011U, 0x02209U,
0x02208U, 0x10422U, 0x10423U, 0x10420U, 0x10421U, 0x02203U, 0x02202U, 0x02201U, 0x02200U, 0x20180U, 0x20181U, 0x44040U, 0x02204U,
0x0C000U, 0x0C001U, 0x0C002U, 0x10840U, 0x0C004U, 0x0C005U, 0x0C006U, 0x10844U, 0x0C008U, 0x0C009U, 0x02211U, 0x02210U, 0x0C00CU,
0x28022U, 0x28021U, 0x28020U, 0x60041U, 0x60040U, 0x10404U, 0x04180U, 0x10402U, 0x10403U, 0x10400U, 0x10401U, 0x02223U, 0x02222U,
0x02221U, 0x02220U, 0x1040AU, 0x28012U, 0x10408U, 0x28010U, 0x0C020U, 0x0C021U, 0x41200U, 0x41201U, 0x00B01U, 0x00B00U, 0x10410U,
0x28008U, 0x11081U, 0x11080U, 0x28005U, 0x28004U, 0x28003U, 0x28002U, 0x28001U, 0x28000U, 0x52040U, 0x14204U, 0x22405U, 0x22404U,
0x14201U, 0x14200U, 0x22401U, 0x22400U, 0x20144U, 0x20145U, 0x44084U, 0x022C0U, 0x20140U, 0x20141U, 0x44080U, 0x44081U, 0x40A08U,
0x10882U, 0x10881U, 0x10880U, 0x14211U, 0x14210U, 0x1A008U, 0x10884U, 0x40A00U, 0x40A01U, 0x40A02U, 0x01304U, 0x1A002U, 0x01302U,
0x1A000U, 0x01300U, 0x60081U, 0x60080U, 0x04141U, 0x04140U, 0x60085U, 0x60084U, 0x104C0U, 0x04144U, 0x06400U, 0x06401U, 0x30200U,
0x30201U, 0x06404U, 0x40640U, 0x30204U, 0x30205U, 0x08603U, 0x08602U, 0x08601U, 0x08600U, 0x00000U, 0x08606U, 0x08605U, 0x08604U,
0x11041U, 0x11040U, 0x30210U, 0x11042U, 0x11045U, 0x11044U, 0x1A020U, 0x01320U, 0x52000U, 0x52001U, 0x04121U, 0x04120U, 0x20108U,
0x20109U, 0x08A00U, 0x08A01U, 0x20104U, 0x20105U, 0x02281U, 0x02280U, 0x20100U, 0x20101U, 0x20102U, 0x20103U, 0x0C080U, 0x0C081U,
0x0C082U, 0x04130U, 0x0C084U, 0x06808U, 0x08A10U, 0x08A11U, 0x11021U, 0x11020U, 0x11023U, 0x11022U, 0x20110U, 0x06800U, 0x20112U,
0x06802U, 0x04103U, 0x04102U, 0x04101U, 0x04100U, 0x10482U, 0x04106U, 0x10480U, 0x04104U, 0x11011U, 0x11010U, 0x04109U, 0x04108U,
0x20120U, 0x40600U, 0x20122U, 0x40602U, 0x11009U, 0x11008U, 0x22800U, 0x04110U, 0x1100DU, 0x1100CU, 0x22804U, 0x04114U, 0x11001U,
0x11000U, 0x11003U, 0x11002U, 0x11005U, 0x11004U, 0x28081U, 0x28080U};
#define X18 0x00040000 /* vector representation of X^{18} */
#define X11 0x00000800 /* vector representation of X^{11} */
#define MASK8 0xfffff800 /* auxiliary vector for testing */
#define GENPOL 0x00000c75 /* generator polinomial, g(x) */
unsigned int CGolay2087::getSyndrome1987(unsigned int pattern)
/*
* Compute the syndrome corresponding to the given pattern, i.e., the
* remainder after dividing the pattern (when considering it as the vector
* representation of a polynomial) by the generator polynomial, GENPOL.
* In the program this pattern has several meanings: (1) pattern = infomation
* bits, when constructing the encoding table; (2) pattern = error pattern,
* when constructing the decoding table; and (3) pattern = received vector, to
* obtain its syndrome in decoding.
*/
{
unsigned int aux = X18;
if (pattern >= X11) {
while (pattern & MASK8) {
while (!(aux & pattern))
aux = aux >> 1;
pattern ^= (aux / X11) * GENPOL;
}
}
return pattern;
}
unsigned char CGolay2087::decode(const unsigned char* data)
{
assert(data != NULL);
unsigned int code = (data[0U] << 11) + (data[1U] << 3) + (data[2U] >> 5);
unsigned int syndrome = getSyndrome1987(code);
unsigned int error_pattern = DECODING_TABLE_1987[syndrome];
if (error_pattern != 0x00U)
code ^= error_pattern;
return code >> 11;
}
void CGolay2087::encode(unsigned char* data)
{
assert(data != NULL);
unsigned int value = data[0U];
unsigned int cksum = ENCODING_TABLE_2087[value];
data[1U] = cksum & 0xFFU;
data[2U] = cksum >> 8;
}

@ -0,0 +1,32 @@
/*
* Copyright (C) 2015 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef Golay2087_H
#define Golay2087_H
class CGolay2087 {
public:
static void encode(unsigned char* data);
static unsigned char decode(const unsigned char* data);
private:
static unsigned int getSyndrome1987(unsigned int pattern);
};
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,32 @@
/*
* Copyright (C) 2010,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef Golay24128_H
#define Golay24128_H
class CGolay24128 {
public:
static unsigned int encode23127(unsigned int data);
static unsigned int encode24128(unsigned int data);
static unsigned int decode23127(unsigned int code);
static unsigned int decode24128(unsigned int code);
static unsigned int decode24128(unsigned char* bytes);
};
#endif

@ -0,0 +1,941 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "HD44780.h"
#include "Log.h"
#include <wiringPi.h>
#include <softPwm.h>
#include <lcd.h>
#include <pthread.h>
#include <cstdio>
#include <cassert>
#include <cstring>
const char* LISTENING = "Listening ";
const char* DEADSPACE = " ";
char m_buffer1[128U];
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),
m_cols(cols),
m_callsign(callsign),
m_dmrid(dmrid),
m_rb(pins.at(0U)),
m_strb(pins.at(1U)),
m_d0(pins.at(2U)),
m_d1(pins.at(3U)),
m_d2(pins.at(4U)),
m_d3(pins.at(5U)),
m_i2cAddress(i2cAddress),
m_pwm(pwm),
m_pwmPin(pwmPin),
m_pwmBright(pwmBright),
m_pwmDim(pwmDim),
m_displayClock(displayClock),
m_utc(utc),
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_rssiCount1(0U),
m_rssiCount2(0U)
{
assert(rows > 1U);
assert(cols > 15U);
}
// Text-based custom character for "from"
unsigned char fmChar[8] =
{
0b11100,
0b10000,
0b11000,
0b10000,
0b00101,
0b00111,
0b00101,
0b00101
};
// Text-based custom character for "to"
unsigned char toChar[8] =
{
0b11100,
0b01000,
0b01000,
0b01000,
0b00010,
0b00101,
0b00101,
0b00010
};
// Icon-based custom character for RF traffic
unsigned char rfChar[8] =
{
0b11111,
0b10101,
0b01110,
0b00100,
0b00100,
0b00100,
0b00100,
0b00000
};
// Icon-based custom character for network traffic
unsigned char ipChar[8] =
{
0b00000,
0b01110,
0b10001,
0b00100,
0b01010,
0b00000,
0b00100,
0b00000
};
// Icon-based custom character for call to talkgroup
unsigned char tgChar[8] =
{
0b01110,
0b10001,
0b10001,
0b10001,
0b01010,
0b01100,
0b10000,
0b00000
};
// Icon-based custom character for private call
unsigned char privChar[8] =
{
0b00100,
0b00000,
0b11111,
0b01110,
0b01110,
0b01010,
0b01010,
0b00000
};
CHD44780::~CHD44780()
{
}
bool CHD44780::open()
{
::wiringPiSetup();
if (m_pwm) {
if (m_pwmPin != 1U) {
::softPwmCreate(m_pwmPin, 0, 100);
::softPwmWrite(m_pwmPin, m_pwmDim);
} else {
::pinMode(m_pwmPin, PWM_OUTPUT);
::pwmWrite(m_pwmPin, (m_pwmDim / 100) * 1024);
}
}
#ifdef ADAFRUIT_DISPLAY
adafruitLCDSetup();
#endif
#ifdef PCF8574_DISPLAY
pcf8574LCDSetup();
#endif
m_fd = ::lcdInit(m_rows, m_cols, 4, m_rb, m_strb, m_d0, m_d1, m_d2, m_d3, 0, 0, 0, 0);
if (m_fd == -1) {
LogError("Unable to open the HD44780");
return false;
}
::lcdDisplay(m_fd, 1);
::lcdCursor(m_fd, 0);
::lcdCursorBlink(m_fd, 0);
::lcdCharDef(m_fd, 0, fmChar);
::lcdCharDef(m_fd, 1, toChar);
::lcdCharDef(m_fd, 2, rfChar);
::lcdCharDef(m_fd, 3, ipChar);
::lcdCharDef(m_fd, 4, privChar);
::lcdCharDef(m_fd, 5, tgChar);
return true;
}
#ifdef ADAFRUIT_DISPLAY
void CHD44780::adafruitLCDSetup()
{
// The other control pins are initialised with lcdInit()
::mcp23017Setup(AF_BASE, m_i2cAddress);
// Backlight LEDs
::pinMode(AF_RED, OUTPUT);
::pinMode(AF_GREEN, OUTPUT);
::pinMode(AF_BLUE, OUTPUT);
// Control signals
::pinMode(AF_RW, OUTPUT);
::digitalWrite(AF_RW, LOW);
m_rb = AF_RS;
m_strb = AF_E;
m_d0 = AF_D0;
m_d1 = AF_D1;
m_d2 = AF_D2;
m_d3 = AF_D3;
}
void CHD44780::adafruitLCDColour(ADAFRUIT_COLOUR colour)
{
switch (colour) {
case AC_OFF:
::digitalWrite(AF_RED, AF_OFF);
::digitalWrite(AF_GREEN, AF_OFF);
::digitalWrite(AF_BLUE, AF_OFF);
break;
case AC_WHITE:
::digitalWrite(AF_RED, AF_ON);
::digitalWrite(AF_GREEN, AF_ON);
::digitalWrite(AF_BLUE, AF_ON);
break;
case AC_RED:
::digitalWrite(AF_RED, AF_ON);
::digitalWrite(AF_GREEN, AF_OFF);
::digitalWrite(AF_BLUE, AF_OFF);
break;
case AC_GREEN:
::digitalWrite(AF_RED, AF_OFF);
::digitalWrite(AF_GREEN, AF_ON);
::digitalWrite(AF_BLUE, AF_OFF);
break;
case AC_BLUE:
::digitalWrite(AF_RED, AF_OFF);
::digitalWrite(AF_GREEN, AF_OFF);
::digitalWrite(AF_BLUE, AF_ON);
break;
case AC_PURPLE:
::digitalWrite(AF_RED, AF_ON);
::digitalWrite(AF_GREEN, AF_OFF);
::digitalWrite(AF_BLUE, AF_ON);
break;
case AC_YELLOW:
::digitalWrite(AF_RED, AF_ON);
::digitalWrite(AF_GREEN, AF_ON);
::digitalWrite(AF_BLUE, AF_OFF);
break;
case AC_ICE:
::digitalWrite(AF_RED, AF_OFF);
::digitalWrite(AF_GREEN, AF_ON);
::digitalWrite(AF_BLUE, AF_ON);
break;
default:
break;
}
}
#endif
#ifdef PCF8574_DISPLAY
void CHD44780::pcf8574LCDSetup()
{
// Initalize PFC8574
::pcf8574Setup(AF_BASE, m_i2cAddress);
// Turn on backlight
::pinMode (AF_BL, OUTPUT);
::digitalWrite (AF_BL, 1);
// Set LCD to write mode.
::pinMode (AF_RW, OUTPUT);
::digitalWrite (AF_RW, 0);
m_rb = AF_RS;
m_strb = AF_E;
m_d0 = AF_D0;
m_d1 = AF_D1;
m_d2 = AF_D2;
m_d3 = AF_D3;
}
#endif
void CHD44780::setIdleInt()
{
m_clockDisplayTimer.start(); // Start the clock display in IDLE only
::lcdClear(m_fd);
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_WHITE);
#endif
if (m_pwm) {
if (m_pwmPin != 1U)
::softPwmWrite(m_pwmPin, m_pwmDim);
else
::pwmWrite(m_pwmPin, (m_pwmDim / 100) * 1024);
}
// Print callsign and ID at on top row for all screen sizes
::lcdPosition(m_fd, 0, 0);
::lcdPrintf(m_fd, "%-6s", m_callsign.c_str());
::lcdPosition(m_fd, m_cols - 7, 0);
::lcdPrintf(m_fd, "%7u", m_dmrid);
// Print MMDVM and Idle on bottom row for all screen sizes
::lcdPosition(m_fd, 0, m_rows - 1);
::lcdPuts(m_fd, "MMDVM");
::lcdPosition(m_fd, m_cols - 4, m_rows - 1);
::lcdPuts(m_fd, "Idle"); // Gets overwritten by clock on 2 line screen
m_dmr = false;
}
void CHD44780::setErrorInt(const char* text)
{
assert(text != NULL);
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_RED);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
::lcdClear(m_fd);
if (m_pwm) {
if (m_pwmPin != 1U)
::softPwmWrite(m_pwmPin, m_pwmBright);
else
::pwmWrite(m_pwmPin, (m_pwmBright / 100) * 1024);
}
::lcdPosition(m_fd, 0, 0);
::lcdPuts(m_fd, "MMDVM");
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%s ERROR", text);
m_dmr = false;
}
void CHD44780::setLockoutInt()
{
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_RED);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
::lcdClear(m_fd);
if (m_pwm) {
if (m_pwmPin != 1U)
::softPwmWrite(m_pwmPin, m_pwmBright);
else
::pwmWrite(m_pwmPin, (m_pwmBright / 100) * 1024);
}
::lcdPosition(m_fd, 0, 0);
::lcdPuts(m_fd, "MMDVM");
::lcdPosition(m_fd, 0, 1);
::lcdPuts(m_fd, "Lockout");
m_dmr = false;
}
void CHD44780::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != NULL);
assert(my2 != NULL);
assert(your != NULL);
assert(type != NULL);
assert(reflector != NULL);
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_RED);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
::lcdClear(m_fd);
if (m_pwm) {
if (m_pwmPin != 1U)
::softPwmWrite(m_pwmPin, m_pwmBright);
else
::pwmWrite(m_pwmPin, (m_pwmBright / 100) * 1024);
}
if (m_rows > 2U) {
::lcdPosition(m_fd, 0, (m_rows / 2) - 2);
::sprintf(m_buffer1, "%s%s", "D-Star", DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
}
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
::lcdPutchar(m_fd, 0);
::lcdPrintf(m_fd, " %.8s/%.4s", my1, my2);
::lcdPosition(m_fd, m_cols - 1, (m_rows / 2) - 1);
if (strcmp(type, "R") == 0) {
::lcdPutchar(m_fd, 2);
} else {
::lcdPutchar(m_fd, 3);
}
::sprintf(m_buffer1, "%.8s", your);
char *p = m_buffer1;
for (; *p; ++p) {
if (*p == ' ')
*p = '_';
}
if (strcmp(reflector, " ") != 0) {
if (m_rows == 2 && m_cols == 40) {
::sprintf(m_buffer3, " via %.8s", reflector);
strcat(m_buffer1, m_buffer3);
} else if (m_rows > 2) {
::sprintf(m_buffer3, "via %.8s", reflector);
::lcdPosition(m_fd, 0, (m_rows / 2) + 1);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer3);
}
}
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPutchar(m_fd, 1);
::lcdPrintf(m_fd, " %.*s", m_cols, m_buffer1);
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()
{
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_PURPLE);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
::lcdClear(m_fd);
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
::sprintf(m_buffer2, "%s%s", "D-Star", DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer2);
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
}
void CHD44780::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
{
assert(type != NULL);
if (!m_dmr) {
m_clockDisplayTimer.stop(); // Stop the clock display
::lcdClear(m_fd);
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_GREEN);
#endif
if (m_pwm) {
if (m_pwmPin != 1U)
::softPwmWrite(m_pwmPin, m_pwmBright);
else
::pwmWrite(m_pwmPin, (m_pwmBright / 100) * 1024);
}
if (m_duplex) {
if (m_rows > 2U) {
::lcdPosition(m_fd, 0, (m_rows / 2) - 2);
::sprintf(m_buffer1, "%s%s", "DMR", DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
}
if (slotNo == 1U) {
//m_dmrScrollTimer2.stop();
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPrintf(m_fd, "2 %.*s", m_cols - 2U, LISTENING);
} else {
//m_dmrScrollTimer1.stop();
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
::lcdPrintf(m_fd, "1 %.*s", m_cols - 2U, LISTENING);
}
} else {
//m_dmrScrollTimer2.stop();
if (m_rows > 2U) {
::lcdPosition(m_fd, 0, (m_rows / 2) - 2);
::sprintf(m_buffer1, "%s", DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
}
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
::sprintf(m_buffer1, "%s%s", "DMR", DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
}
}
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_RED);
#endif
if (m_duplex) {
if (m_rows > 2U) {
::lcdPosition(m_fd, 0, (m_rows / 2) - 2);
::sprintf(m_buffer1, "%s%s", "DMR", DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
}
if (slotNo == 1U) {
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
::lcdPuts(m_fd, "1 ");
if (m_cols > 16 )
::sprintf(m_buffer1, "%s > %s%s%s", src.c_str(), group ? "TG" : "", dst.c_str(), DEADSPACE);
else
::sprintf(m_buffer1, "%s>%s%s", src.c_str(), dst.c_str(), DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols - 2U, m_buffer1);
::lcdPosition(m_fd, m_cols - 3U, (m_rows / 2) - 1);
::lcdPuts(m_fd, " ");
if (group) {
::lcdPutchar(m_fd, 5);
} else {
::lcdPutchar(m_fd, 4);
}
if (strcmp(type, "R") == 0) {
::lcdPutchar(m_fd, 2);
} else {
::lcdPutchar(m_fd, 3);
}
} else {
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPuts(m_fd, "2 ");
if (m_cols > 16 )
::sprintf(m_buffer2, "%s > %s%s%s", src.c_str(), group ? "TG" : "", dst.c_str(), DEADSPACE);
else
::sprintf(m_buffer2, "%s>%s%s", src.c_str(), dst.c_str(), DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols - 2U, m_buffer2);
::lcdPosition(m_fd, m_cols - 3U, (m_rows / 2));
::lcdPuts(m_fd, " ");
if (group) {
::lcdPutchar(m_fd, 5);
} else {
::lcdPutchar(m_fd, 4);
}
if (strcmp(type, "R") == 0) {
::lcdPutchar(m_fd, 2);
} else {
::lcdPutchar(m_fd, 3);
}
}
} else {
if (m_rows > 2U) {
::lcdPosition(m_fd, 0, (m_rows / 2) - 2);
::sprintf(m_buffer1, "%s%s", "DMR", DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
}
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
::lcdPutchar(m_fd, 0);
::sprintf(m_buffer2, " %s%s", src.c_str(), DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols - 4U, m_buffer2);
::lcdPosition(m_fd, m_cols - 1U, (m_rows / 2) - 1);
if (strcmp(type, "R") == 0) {
::lcdPutchar(m_fd, 2);
} else {
::lcdPutchar(m_fd, 3);
}
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPutchar(m_fd, 1);
::sprintf(m_buffer2, " %s%s%s", group ? "TG" : "", dst.c_str(), DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols - 4U, m_buffer2);
::lcdPosition(m_fd, m_cols - 1U, (m_rows / 2));
if (group) {
::lcdPutchar(m_fd, 5);
} else {
::lcdPutchar(m_fd, 4);
}
}
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)
{
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_PURPLE);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_duplex) {
if (slotNo == 1U) {
::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 {
::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 {
if (m_rows > 2U) {
::lcdPosition(m_fd, 0, (m_rows / 2) - 2);
::sprintf(m_buffer1, "%s", DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
}
::lcdPosition(m_fd, 0, (m_rows / 2) - 1);
::sprintf(m_buffer2, "%s%s", "DMR", DEADSPACE);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer2);
::lcdPosition(m_fd, 0, (m_rows / 2));
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
}
}
void CHD44780::writeFusionInt(const char* source, const char* dest, const char* type, const char* origin)
{
assert(source != NULL);
assert(dest != NULL);
assert(type != NULL);
assert(origin != NULL);
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_RED);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
::lcdClear(m_fd);
if (m_pwm) {
if (m_pwmPin != 1U)
::softPwmWrite(m_pwmPin, m_pwmBright);
else
::pwmWrite(m_pwmPin, (m_pwmBright / 100) * 1024);
}
::lcdPosition(m_fd, 0, 0);
::lcdPuts(m_fd, "System Fusion");
if (m_rows == 2U && m_cols == 16U) {
char m_buffer1[16U];
::sprintf(m_buffer1, "%.10s >", source);
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
} else if (m_rows == 4U && m_cols == 16U) {
char m_buffer1[16U];
::sprintf(m_buffer1, "%.10s >", source);
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
::sprintf(m_buffer1, "%.10s", dest);
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
} else if (m_rows == 4U && m_cols == 20U) {
char m_buffer1[20U];
::sprintf(m_buffer1, "%.10s >", source);
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
::sprintf(m_buffer1, "%.10s", dest);
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
} else if (m_rows == 2 && m_cols == 40U) {
char m_buffer1[40U];
::sprintf(m_buffer1, "%.10s > %.10s", source, dest);
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
}
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()
{
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_PURPLE);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_rows == 2U && m_cols == 16U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
} else if (m_rows == 4U && m_cols == 16U) {
::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 == 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);
}
}
void CHD44780::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
{
assert(source != NULL);
assert(type != NULL);
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_RED);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
::lcdClear(m_fd);
if (m_pwm) {
if (m_pwmPin != 1U)
::softPwmWrite(m_pwmPin, m_pwmBright);
else
::pwmWrite(m_pwmPin, (m_pwmBright / 100) * 1024);
}
::lcdPosition(m_fd, 0, 0);
::lcdPuts(m_fd, "P25");
if (m_rows == 2U && m_cols == 16U) {
char m_buffer1[16U];
::sprintf(m_buffer1, "%.10s >", source);
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
} else if (m_rows == 4U && m_cols == 16U) {
char m_buffer1[16U];
::sprintf(m_buffer1, "%.10s >", source);
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
::sprintf(m_buffer1, "%s%u", group ? "TG" : "", dest);
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
} else if (m_rows == 4U && m_cols == 20U) {
char m_buffer1[20U];
::sprintf(m_buffer1, "%.10s >", source);
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
::sprintf(m_buffer1, "%s%u", group ? "TG" : "", dest);
::lcdPosition(m_fd, 0, 2);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
} else if (m_rows == 2 && m_cols == 40U) {
char m_buffer1[40U];
::sprintf(m_buffer1, "%.10s > %s%u", source, group ? "TG" : "", dest);
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1);
}
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()
{
#ifdef ADAFRUIT_DISPLAY
adafruitLCDColour(AC_PURPLE);
#endif
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_rows == 2U && m_cols == 16U) {
::lcdPosition(m_fd, 0, 1);
::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING);
} else if (m_rows == 4U && m_cols == 16U) {
::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 == 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);
}
}
void CHD44780::writeCWInt()
{
::lcdPosition(m_fd, m_cols - 5, m_rows - 1);
::lcdPuts(m_fd, "CW TX");
}
void CHD44780::clearCWInt()
{
::lcdPosition(m_fd, m_cols - 5, m_rows - 1);
::lcdPuts(m_fd, " Idle");
}
void CHD44780::clockInt(unsigned int ms)
{
m_clockDisplayTimer.clock(ms);
// Idle clock display
if (m_displayClock && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) {
time_t currentTime;
struct tm *Time;
time(&currentTime);
if (m_utc) {
Time = gmtime(&currentTime);
} else {
Time = localtime(&currentTime);
}
setlocale(LC_TIME,"");
strftime(m_buffer1, 128, "%X", Time); // Time
strftime(m_buffer2, 128, "%x", Time); // Date
if (m_cols == 16U && m_rows == 2U) {
::lcdPosition(m_fd, m_cols - 10, 1);
::lcdPrintf(m_fd, "%s%.*s", strlen(m_buffer1) > 8 ? "" : " ", 10, m_buffer1);
} else {
::lcdPosition(m_fd, (m_cols - (strlen(m_buffer1) == 8 ? 8 : 10)) / 2, m_rows == 2 ? 1 : 2);
::lcdPrintf(m_fd, "%.*s", strlen(m_buffer1) == 8 ? 8 : 10, m_buffer1);
::lcdPosition(m_fd, (m_cols - strlen(m_buffer2)) / 2, m_rows == 2 ? 0 : 1);
::lcdPrintf(m_fd, "%s", m_buffer2);
}
m_clockDisplayTimer.start();
}
}
void CHD44780::close()
{
}

@ -0,0 +1,165 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(HD44780_H)
#define HD44780_H
#include "Display.h"
#include "Timer.h"
#include <string>
#include <vector>
#include <mcp23017.h>
#include <pcf8574.h>
enum ADAFRUIT_COLOUR {
AC_OFF,
AC_WHITE,
AC_RED,
AC_GREEN,
AC_BLUE,
AC_PURPLE,
AC_YELLOW,
AC_ICE
};
// Defines for the Adafruit Pi LCD interface board
#ifdef ADAFRUIT_DISPLAY
#define AF_BASE 100
/* Not yet used defines (for possible future use)
*
* #define AF_SELECT (AF_BASE + 0)
* #define AF_RIGHT (AF_BASE + 1)
* #define AF_DOWN (AF_BASE + 2)
* #define AF_UP (AF_BASE + 3)
* #define AF_LEFT (AF_BASE + 4)
*/
#define AF_RED (AF_BASE + 6)
#define AF_GREEN (AF_BASE + 7)
#define AF_BLUE (AF_BASE + 8)
#define AF_D3 (AF_BASE + 9)
#define AF_D2 (AF_BASE + 10)
#define AF_D1 (AF_BASE + 11)
#define AF_D0 (AF_BASE + 12)
#define AF_E (AF_BASE + 13)
#define AF_RW (AF_BASE + 14)
#define AF_RS (AF_BASE + 15)
#define AF_ON LOW
#define AF_OFF HIGH
// #define MCP23017 0x20
#endif
// Define for HD44780 connected via a PCF8574 GPIO extender
#ifdef PCF8574_DISPLAY
#define AF_BASE 100
#define AF_RS (AF_BASE + 0)
#define AF_RW (AF_BASE + 1)
#define AF_E (AF_BASE + 2)
#define AF_BL (AF_BASE + 3)
#define AF_D0 (AF_BASE + 4)
#define AF_D1 (AF_BASE + 5)
#define AF_D2 (AF_BASE + 6)
#define AF_D3 (AF_BASE + 7)
// #define PCF8574 0x27
#endif
class CHD44780 : public CDisplay
{
public:
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);
virtual ~CHD44780();
virtual bool open();
virtual void close();
protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
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();
virtual void clearCWInt();
virtual void clockInt(unsigned int ms);
private:
unsigned int m_rows;
unsigned int m_cols;
std::string m_callsign;
unsigned int m_dmrid;
unsigned int m_rb;
unsigned int m_strb;
unsigned int m_d0;
unsigned int m_d1;
unsigned int m_d2;
unsigned int m_d3;
unsigned int m_i2cAddress;
bool m_pwm;
unsigned int m_pwmPin;
unsigned int m_pwmBright;
unsigned int m_pwmDim;
bool m_displayClock;
bool m_utc;
bool m_duplex;
int m_fd;
bool m_dmr;
CTimer m_clockDisplayTimer;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
/*
CTimer m_dmrScrollTimer1;
CTimer m_dmrScrollTimer2;
CTimer m_dstarScrollTimer;
*/
#ifdef ADAFRUIT_DISPLAY
void adafruitLCDSetup();
void adafruitLCDColour(ADAFRUIT_COLOUR colour);
#endif
#ifdef PCF8574_DISPLAY
void pcf8574LCDSetup();
#endif
};
#endif

@ -0,0 +1,107 @@
IDLE SCREEN LAYOUTS
-------------------
16 x 2
------
With clock Without clock
---------- -------------
0 1 0 1
0123456789012345 0123456789012345
+----------------+ +----------------+
0|AAAAAA NNNNNNN| 0|AAAAAA NNNNNNN|
1|MMDVM HH:MM:SS| 1|MMDVM Idle|
+----------------+ +----------------+
40 x 2
------
With clock Without clock
---------- -------------
0 1 2 3 0 1 2 3
0123456789012345678901234567890123456789 0123456789012345678901234567890123456789
+----------------------------------------+ +----------------------------------------+
0|AAAAAA DD/MM/YY NNNNNNN| 0|AAAAAA NNNNNNN|
1|MMDVM HH:MM:SS Idle| 1|MMDVM Idle|
+----------------------------------------+ +----------------------------------------+
16 x 4
------
With clock Without clock
---------- -------------
0 1 0 1
0123456789012345 0123456789012345
+----------------+ +----------------+
0|AAAAAA NNNNNNN| 0|AAAAAA NNNNNNN|
1| DD/MM/YY | 1| |
2| HH:MM:SS | 2| |
3|MMDVM Idle| 3|MMDVM Idle|
+----------------+ +----------------+
20 x 4
------
With clock Without clock
---------- -------------
0 1 0 1
01234567890123456479 01234567890123456789
+--------------------+ +--------------------+
0|AAAAAA NNNNNNN| 0|AAAAAA NNNNNNN|
1| DD/MM/YY | 1| |
2| HH:MM:SS | 2| |
3|MMDVM Idle| 3|MMDVM Idle|
+--------------------+ +--------------------+
D-STAR LAYOUTS
-------------
16 x 2
------
0 1
0123456789012345
+----------------+
0|F AAAAAAAA/AAAAX|
1|T AAAAAAAA |
+----------------+
40 x 2
------
0 1 2 3
0123456789012345678901234567890123456789
+----------------------------------------+
0|F AAAAAAAA/AAAA X|
1|T AAAAAAAA via AAAAAAAA |
+----------------------------------------+
16 x 4
------
0 1
0123456789012345
+----------------+
0|D-Star |
1|F AAAAAAAA/AAAAX|
2|T AAAAAAAA |
3|via AAAAAAAA |
+----------------+
20 x 4
------
0 1
01234567890123456479
+--------------------+
0|D-Star |
1|F AAAAAAAA/AAAA X|
2|T AAAAAAAA |
3|via AAAAAAAA |
+--------------------+

@ -0,0 +1,349 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Hamming.h"
#include <cstdio>
#include <cassert>
// Hamming (15,11,3) check a boolean data array
bool CHamming::decode15113_1(bool* d)
{
assert(d != NULL);
// Calculate the parity it should have
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6];
bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9];
bool c2 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[8] ^ d[10];
bool c3 = d[0] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[9] ^ d[10];
unsigned char n = 0U;
n |= (c0 != d[11]) ? 0x01U : 0x00U;
n |= (c1 != d[12]) ? 0x02U : 0x00U;
n |= (c2 != d[13]) ? 0x04U : 0x00U;
n |= (c3 != d[14]) ? 0x08U : 0x00U;
switch (n)
{
// Parity bit errors
case 0x01U: d[11] = !d[11]; return true;
case 0x02U: d[12] = !d[12]; return true;
case 0x04U: d[13] = !d[13]; return true;
case 0x08U: d[14] = !d[14]; return true;
// Data bit errors
case 0x0FU: d[0] = !d[0]; return true;
case 0x07U: d[1] = !d[1]; return true;
case 0x0BU: d[2] = !d[2]; return true;
case 0x03U: d[3] = !d[3]; return true;
case 0x0DU: d[4] = !d[4]; return true;
case 0x05U: d[5] = !d[5]; return true;
case 0x09U: d[6] = !d[6]; return true;
case 0x0EU: d[7] = !d[7]; return true;
case 0x06U: d[8] = !d[8]; return true;
case 0x0AU: d[9] = !d[9]; return true;
case 0x0CU: d[10] = !d[10]; return true;
// No bit errors
default: return false;
}
}
void CHamming::encode15113_1(bool* d)
{
assert(d != NULL);
// Calculate the checksum this row should have
d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6];
d[12] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9];
d[13] = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[8] ^ d[10];
d[14] = d[0] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[9] ^ d[10];
}
// Hamming (15,11,3) check a boolean data array
bool CHamming::decode15113_2(bool* d)
{
assert(d != NULL);
// Calculate the checksum this row should have
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
bool c1 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9];
bool c2 = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10];
bool c3 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10];
unsigned char n = 0x00U;
n |= (c0 != d[11]) ? 0x01U : 0x00U;
n |= (c1 != d[12]) ? 0x02U : 0x00U;
n |= (c2 != d[13]) ? 0x04U : 0x00U;
n |= (c3 != d[14]) ? 0x08U : 0x00U;
switch (n) {
// Parity bit errors
case 0x01U: d[11] = !d[11]; return true;
case 0x02U: d[12] = !d[12]; return true;
case 0x04U: d[13] = !d[13]; return true;
case 0x08U: d[14] = !d[14]; return true;
// Data bit errors
case 0x09U: d[0] = !d[0]; return true;
case 0x0BU: d[1] = !d[1]; return true;
case 0x0FU: d[2] = !d[2]; return true;
case 0x07U: d[3] = !d[3]; return true;
case 0x0EU: d[4] = !d[4]; return true;
case 0x05U: d[5] = !d[5]; return true;
case 0x0AU: d[6] = !d[6]; return true;
case 0x0DU: d[7] = !d[7]; return true;
case 0x03U: d[8] = !d[8]; return true;
case 0x06U: d[9] = !d[9]; return true;
case 0x0CU: d[10] = !d[10]; return true;
// No bit errors
default: return false;
}
}
void CHamming::encode15113_2(bool* d)
{
assert(d != NULL);
// Calculate the checksum this row should have
d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
d[12] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9];
d[13] = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10];
d[14] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10];
}
// Hamming (13,9,3) check a boolean data array
bool CHamming::decode1393(bool* d)
{
assert(d != NULL);
// Calculate the checksum this column should have
bool c0 = d[0] ^ d[1] ^ d[3] ^ d[5] ^ d[6];
bool c1 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7];
bool c2 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
bool c3 = d[0] ^ d[2] ^ d[4] ^ d[5] ^ d[8];
unsigned char n = 0x00U;
n |= (c0 != d[9]) ? 0x01U : 0x00U;
n |= (c1 != d[10]) ? 0x02U : 0x00U;
n |= (c2 != d[11]) ? 0x04U : 0x00U;
n |= (c3 != d[12]) ? 0x08U : 0x00U;
switch (n) {
// Parity bit errors
case 0x01U: d[9] = !d[9]; return true;
case 0x02U: d[10] = !d[10]; return true;
case 0x04U: d[11] = !d[11]; return true;
case 0x08U: d[12] = !d[12]; return true;
// Data bit erros
case 0x0FU: d[0] = !d[0]; return true;
case 0x07U: d[1] = !d[1]; return true;
case 0x0EU: d[2] = !d[2]; return true;
case 0x05U: d[3] = !d[3]; return true;
case 0x0AU: d[4] = !d[4]; return true;
case 0x0DU: d[5] = !d[5]; return true;
case 0x03U: d[6] = !d[6]; return true;
case 0x06U: d[7] = !d[7]; return true;
case 0x0CU: d[8] = !d[8]; return true;
// No bit errors
default: return false;
}
}
void CHamming::encode1393(bool* d)
{
assert(d != NULL);
// Calculate the checksum this column should have
d[9] = d[0] ^ d[1] ^ d[3] ^ d[5] ^ d[6];
d[10] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7];
d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
d[12] = d[0] ^ d[2] ^ d[4] ^ d[5] ^ d[8];
}
// Hamming (10,6,3) check a boolean data array
bool CHamming::decode1063(bool* d)
{
assert(d != NULL);
// Calculate the checksum this column should have
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[5];
bool c1 = d[0] ^ d[1] ^ d[3] ^ d[5];
bool c2 = d[0] ^ d[2] ^ d[3] ^ d[4];
bool c3 = d[1] ^ d[2] ^ d[3] ^ d[4];
unsigned char n = 0x00U;
n |= (c0 != d[6]) ? 0x01U : 0x00U;
n |= (c1 != d[7]) ? 0x02U : 0x00U;
n |= (c2 != d[8]) ? 0x04U : 0x00U;
n |= (c3 != d[9]) ? 0x08U : 0x00U;
switch (n) {
// Parity bit errors
case 0x01U: d[6] = !d[6]; return true;
case 0x02U: d[7] = !d[7]; return true;
case 0x04U: d[8] = !d[8]; return true;
case 0x08U: d[9] = !d[9]; return true;
// Data bit erros
case 0x07U: d[0] = !d[0]; return true;
case 0x0BU: d[1] = !d[1]; return true;
case 0x0DU: d[2] = !d[2]; return true;
case 0x0EU: d[3] = !d[3]; return true;
case 0x0CU: d[4] = !d[4]; return true;
case 0x03U: d[5] = !d[5]; return true;
// No bit errors
default: return false;
}
}
void CHamming::encode1063(bool* d)
{
assert(d != NULL);
// Calculate the checksum this column should have
d[6] = d[0] ^ d[1] ^ d[2] ^ d[5];
d[7] = d[0] ^ d[1] ^ d[3] ^ d[5];
d[8] = d[0] ^ d[2] ^ d[3] ^ d[4];
d[9] = d[1] ^ d[2] ^ d[3] ^ d[4];
}
// A Hamming (16,11,4) Check
bool CHamming::decode16114(bool* d)
{
assert(d != NULL);
// Calculate the checksum this column should have
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
bool c1 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9];
bool c2 = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10];
bool c3 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10];
bool c4 = d[0] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[9] ^ d[10];
// Compare these with the actual bits
unsigned char n = 0x00U;
n |= (c0 != d[11]) ? 0x01U : 0x00U;
n |= (c1 != d[12]) ? 0x02U : 0x00U;
n |= (c2 != d[13]) ? 0x04U : 0x00U;
n |= (c3 != d[14]) ? 0x08U : 0x00U;
n |= (c4 != d[15]) ? 0x10U : 0x00U;
switch (n) {
// Parity bit errors
case 0x01U: d[11] = !d[11]; return true;
case 0x02U: d[12] = !d[12]; return true;
case 0x04U: d[13] = !d[13]; return true;
case 0x08U: d[14] = !d[14]; return true;
case 0x10U: d[15] = !d[15]; return true;
// Data bit errors
case 0x19U: d[0] = !d[0]; return true;
case 0x0BU: d[1] = !d[1]; return true;
case 0x1FU: d[2] = !d[2]; return true;
case 0x07U: d[3] = !d[3]; return true;
case 0x0EU: d[4] = !d[4]; return true;
case 0x15U: d[5] = !d[5]; return true;
case 0x1AU: d[6] = !d[6]; return true;
case 0x0DU: d[7] = !d[7]; return true;
case 0x13U: d[8] = !d[8]; return true;
case 0x16U: d[9] = !d[9]; return true;
case 0x1CU: d[10] = !d[10]; return true;
// No bit errors
case 0x00U: return true;
// Unrecoverable errors
default: return false;
}
}
void CHamming::encode16114(bool* d)
{
assert(d != NULL);
d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8];
d[12] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9];
d[13] = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10];
d[14] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10];
d[15] = d[0] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[9] ^ d[10];
}
// A Hamming (17,12,3) Check
bool CHamming::decode17123(bool* d)
{
assert(d != NULL);
// Calculate the checksum this column should have
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9];
bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10];
bool c2 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11];
bool c3 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10];
bool c4 = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11];
// Compare these with the actual bits
unsigned char n = 0x00U;
n |= (c0 != d[12]) ? 0x01U : 0x00U;
n |= (c1 != d[13]) ? 0x02U : 0x00U;
n |= (c2 != d[14]) ? 0x04U : 0x00U;
n |= (c3 != d[15]) ? 0x08U : 0x00U;
n |= (c4 != d[16]) ? 0x10U : 0x00U;
switch (n) {
// Parity bit errors
case 0x01U: d[12] = !d[12]; return true;
case 0x02U: d[13] = !d[13]; return true;
case 0x04U: d[14] = !d[14]; return true;
case 0x08U: d[15] = !d[15]; return true;
case 0x10U: d[16] = !d[16]; return true;
// Data bit errors
case 0x1BU: d[0] = !d[0]; return true;
case 0x1FU: d[1] = !d[1]; return true;
case 0x17U: d[2] = !d[2]; return true;
case 0x07U: d[3] = !d[3]; return true;
case 0x0EU: d[4] = !d[4]; return true;
case 0x1CU: d[5] = !d[5]; return true;
case 0x11U: d[6] = !d[6]; return true;
case 0x0BU: d[7] = !d[7]; return true;
case 0x16U: d[8] = !d[8]; return true;
case 0x05U: d[9] = !d[9]; return true;
case 0x0AU: d[10] = !d[10]; return true;
case 0x14U: d[11] = !d[11]; return true;
// No bit errors
case 0x00U: return true;
// Unrecoverable errors
default: return false;
}
}
void CHamming::encode17123(bool* d)
{
assert(d != NULL);
d[12] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9];
d[13] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10];
d[14] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11];
d[15] = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10];
d[16] = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11];
}

@ -0,0 +1,43 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef Hamming_H
#define Hamming_H
class CHamming {
public:
static void encode15113_1(bool* d);
static bool decode15113_1(bool* d);
static void encode15113_2(bool* d);
static bool decode15113_2(bool* d);
static void encode1393(bool* d);
static bool decode1393(bool* d);
static void encode1063(bool* d);
static bool decode1063(bool* d);
static void encode16114(bool* d);
static bool decode16114(bool* d);
static void encode17123(bool* d);
static bool decode17123(bool* d);
};
#endif

@ -0,0 +1,7 @@
D-Star: On some radios, the header is not decoded correctly. It looks like frequency drift at the beginning of the transmission.
DMR: DMO mode doesn't wake up older radios like other radios do.
YSF: No known issues.
P25: Upgrade the filters, processing power in the Due permiting.

@ -0,0 +1,187 @@
/*
* Copyright (C) 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "JitterBuffer.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
CJitterBuffer::CJitterBuffer(unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, unsigned int topSequenceNumber, bool debug) :
m_blockSize(blockSize),
m_blockTime(blockTime),
m_topSequenceNumber(topSequenceNumber),
m_debug(debug),
m_blockCount(0U),
m_timer(1000U, 0U, jitterTime),
m_stopWatch(),
m_running(false),
m_buffer(NULL),
m_headSequenceNumber(0U),
m_lastData(NULL),
m_lastDataLength(0U)
{
assert(blockSize > 0U);
assert(blockTime > 0U);
assert(jitterTime > 0U);
assert(topSequenceNumber > 0U);
m_blockCount = (jitterTime / blockTime) * 2U + 1U;
m_buffer = new JitterEntry[m_blockCount];
for (unsigned int i = 0U; i < m_blockCount; i++)
m_buffer[i].m_data = new unsigned char[m_blockSize];
m_lastData = new unsigned char[m_blockSize];
reset();
}
CJitterBuffer::~CJitterBuffer()
{
for (unsigned int i = 0U; i < m_blockCount; i++)
delete[] m_buffer[i].m_data;
delete[] m_buffer;
delete[] m_lastData;
}
bool CJitterBuffer::addData(const unsigned char* data, unsigned int length, unsigned int sequenceNumber)
{
assert(data != NULL);
assert(length > 0U);
assert(length <= m_blockSize);
unsigned int headSequenceNumber = m_headSequenceNumber % m_topSequenceNumber;
unsigned int tailSequenceNumber = (m_headSequenceNumber + m_blockCount) % m_topSequenceNumber;
// Is the data out of sequence?
if (headSequenceNumber < tailSequenceNumber) {
if (sequenceNumber < headSequenceNumber || sequenceNumber >= tailSequenceNumber) {
if (m_debug)
LogDebug("JitterBuffer: rejecting frame with seqNo=%u, raw=%u, head=%u, tail=%u", sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber);
return false;
}
} else {
if (sequenceNumber >= tailSequenceNumber && sequenceNumber < headSequenceNumber) {
if (m_debug)
LogDebug("JitterBuffer: rejecting frame with seqNo=%u, raw=%u, head=%u, tail=%u", sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber);
return false;
}
}
unsigned int number;
if (sequenceNumber >= headSequenceNumber)
number = sequenceNumber - headSequenceNumber;
else
number = (sequenceNumber + m_blockCount) - headSequenceNumber;;
unsigned int index = (m_headSequenceNumber + number) % m_blockCount;
// Do we already have the data?
if (m_buffer[index].m_length > 0U) {
if (m_debug)
LogDebug("JitterBuffer: rejecting duplicate frame with seqNo=%u, raw=%u, head=%u, tail=%u", sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber);
return false;
}
LogDebug("JitterBuffer: adding frame with seqNo=%u, raw=%u, head=%u, tail=%u", sequenceNumber, m_headSequenceNumber, headSequenceNumber, tailSequenceNumber);
::memcpy(m_buffer[index].m_data, data, length);
m_buffer[index].m_length = length;
if (!m_timer.isRunning()) {
LogDebug("JitterBuffer: starting the timer");
m_timer.start();
}
return true;
}
JB_STATUS CJitterBuffer::getData(unsigned char* data, unsigned int& length)
{
assert(data != NULL);
if (!m_running)
return JBS_NO_DATA;
unsigned int sequenceNumber = m_stopWatch.elapsed() / m_blockTime + 3U;
if (m_headSequenceNumber > sequenceNumber)
return JBS_NO_DATA;
unsigned int head = m_headSequenceNumber % m_blockCount;
m_headSequenceNumber++;
if (m_buffer[head].m_length > 0U) {
LogDebug("JitterBuffer: returning data, elapsed=%ums, raw=%u, head=%u", m_stopWatch.elapsed(), m_headSequenceNumber - 1U, head);
::memcpy(data, m_buffer[head].m_data, m_buffer[head].m_length);
length = m_buffer[head].m_length;
// Save this data in case no more data is available next time
::memcpy(m_lastData, m_buffer[head].m_data, m_buffer[head].m_length);
m_lastDataLength = m_buffer[head].m_length;
m_buffer[head].m_length = 0U;
return JBS_DATA;
}
if (m_debug)
LogDebug("JitterBuffer: no data available, elapsed=%ums, raw=%u, head=%u", m_stopWatch.elapsed(), m_headSequenceNumber - 1U, head);
// Return the last data frame if we have it
if (m_lastDataLength > 0U) {
LogDebug("JitterBuffer: returning the last received frame");
::memcpy(data, m_lastData, m_lastDataLength);
length = m_lastDataLength;
return JBS_MISSING;
}
return JBS_NO_DATA;
}
void CJitterBuffer::reset()
{
for (unsigned int i = 0U; i < m_blockCount; i++)
m_buffer[i].m_length = 0U;
m_headSequenceNumber = 0U;
m_lastDataLength = 0U;
m_timer.stop();
m_running = false;
}
void CJitterBuffer::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_timer.isRunning() && m_timer.hasExpired()) {
if (!m_running) {
m_stopWatch.start();
m_running = true;
}
}
}

@ -0,0 +1,67 @@
/*
* Copyright (C) 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(JITTERBUFFER_H)
#define JITTERBUFFER_H
#include "StopWatch.h"
#include "Timer.h"
enum JB_STATUS {
JBS_NO_DATA,
JBS_DATA,
JBS_MISSING
};
class CJitterBuffer {
public:
CJitterBuffer(unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, unsigned int topSequenceNumber, bool debug);
~CJitterBuffer();
bool addData(const unsigned char* data, unsigned int length, unsigned int sequenceNumber);
JB_STATUS getData(unsigned char* data, unsigned int& length);
void reset();
void clock(unsigned int ms);
private:
unsigned int m_blockSize;
unsigned int m_blockTime;
unsigned int m_topSequenceNumber;
bool m_debug;
unsigned int m_blockCount;
CTimer m_timer;
CStopWatch m_stopWatch;
bool m_running;
struct JitterEntry
{
unsigned char* m_data;
unsigned int m_length;
};
JitterEntry* m_buffer;
unsigned int m_headSequenceNumber;
unsigned char* m_lastData;
unsigned int m_lastDataLength;
};
#endif

@ -0,0 +1,733 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Some LCD displays include additional LEDs for status.
* If they exist, the LDCproc server will use the output command.
* If the LEDs do not exist, the command is ignored.
* to control these LEDs Below are the values for the Crystalfontz CFA-635.
* N4IRS
* LED 1 (DMR)
* Green 1 0000 0001
* Red 16 0001 0000
* Yellow 17 0001 0001
* LED 2 (P25)
* Green 2 0000 0010
* Red 32 0010 0000
* Yellow 34 0010 0010
* LED 3 (Fusion)
* Green 4 0000 0100
* Red 64 0100 0000
* Yellow 68 1000 0100
* LED 4 (D-Star)
* Green 8 0000 1000
* Red 128 1000 0000
* Yellow 136 1000 1000
*/
#include "LCDproc.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <clocale>
#include <ctime>
#if !defined(_WIN32) && !defined(_WIN64)
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#else
#include <winsock.h>
#endif
#define BUFFER_MAX_LEN 128
int m_socketfd;
char m_buffer[BUFFER_MAX_LEN];
fd_set m_readfds, m_writefds;
struct timeval m_timeout;
int m_recvsize;
unsigned int m_rows(0);
unsigned int m_cols(0);
bool m_screensDefined(false);
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),
m_port(port),
m_localPort(localPort),
m_callsign(callsign),
m_dmrid(dmrid),
m_displayClock(displayClock),
m_utc(utc),
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_rssiCount1(0U),
m_rssiCount2(0U)
{
}
CLCDproc::~CLCDproc()
{
}
bool CLCDproc::open()
{
const char *server;
unsigned int port, localPort;
struct sockaddr_in serverAddress, clientAddress;
struct hostent *h;
server = m_address.c_str();
port = m_port;
localPort = m_localPort;
/* Create TCP socket */
m_socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (m_socketfd == -1) {
LogError("LCDproc, failed to create socket");
return false;
}
/* Sets client address (random port - need to specify manual port from ini file?) */
clientAddress.sin_family = AF_INET;
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
//clientAddress.sin_port = htons(0);
clientAddress.sin_port = htons(localPort);
/* Bind the address to the socket */
if (bind(m_socketfd, (struct sockaddr *)&clientAddress, sizeof(clientAddress)) == -1) {
LogError("LCDproc, error whilst binding address");
return false;
}
/* Lookup the hostname address */
h = gethostbyname(server);
/* Sets server address */
serverAddress.sin_family = h->h_addrtype;
memcpy((char*)&serverAddress.sin_addr.s_addr, h->h_addr_list[0], h->h_length);
serverAddress.sin_port = htons(port);
if (connect(m_socketfd, (struct sockaddr * )&serverAddress, sizeof(serverAddress))==-1) {
LogError("LCDproc, cannot connect to server");
return false;
}
socketPrintf(m_socketfd, "hello"); // Login to the LCD server
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
return true;
}
void CLCDproc::setIdleInt()
{
m_clockDisplayTimer.start(); // Start the clock display in IDLE only
if (m_screensDefined) {
socketPrintf(m_socketfd, "screen_set DStar -priority hidden");
socketPrintf(m_socketfd, "screen_set DMR -priority hidden");
socketPrintf(m_socketfd, "screen_set YSF -priority hidden");
socketPrintf(m_socketfd, "screen_set P25 -priority hidden");
socketPrintf(m_socketfd, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
void CLCDproc::setErrorInt(const char* text)
{
assert(text != NULL);
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_screensDefined) {
socketPrintf(m_socketfd, "screen_set DStar -priority hidden");
socketPrintf(m_socketfd, "screen_set DMR -priority hidden");
socketPrintf(m_socketfd, "screen_set YSF -priority hidden");
socketPrintf(m_socketfd, "screen_set P25 -priority hidden");
socketPrintf(m_socketfd, "widget_set Status Status %u %u Error", m_cols - 4, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
void CLCDproc::setLockoutInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_screensDefined) {
socketPrintf(m_socketfd, "screen_set DStar -priority hidden");
socketPrintf(m_socketfd, "screen_set DMR -priority hidden");
socketPrintf(m_socketfd, "screen_set YSF -priority hidden");
socketPrintf(m_socketfd, "screen_set P25 -priority hidden");
socketPrintf(m_socketfd, "widget_set Status Status %u %u Lockout", m_cols - 6, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
// LED 4 Green 8 Red 128 Yellow 136
void CLCDproc::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != NULL);
assert(my2 != NULL);
assert(your != NULL);
assert(type != NULL);
assert(reflector != NULL);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set DStar -priority foreground");
socketPrintf(m_socketfd, "widget_set DStar Mode 1 1 \"D-Star\"");
::sprintf(m_displayBuffer1, "%.8s", your);
char *p = m_displayBuffer1;
for (; *p; ++p) {
if (*p == ' ')
*p = '_';
}
if (strcmp(reflector, " ") != 0)
sprintf(m_displayBuffer2, " via %.8s", reflector);
else
memset(m_displayBuffer2, 0, BUFFER_MAX_LEN);
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s to %s%s\"", m_cols - 1, my1, my2, m_displayBuffer1, m_displayBuffer2);
} else {
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s\"", m_cols - 1, my1, my2);
socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, m_displayBuffer1, m_displayBuffer2);
socketPrintf(m_socketfd, "output 128"); // Set LED4 color red
}
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()
{
m_clockDisplayTimer.stop(); // Stop the clock display
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 \"\"");
socketPrintf(m_socketfd, "output 8"); // Set LED4 color green
}
// LED 1 Green 1 Red 16 Yellow 17
void CLCDproc::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
{
assert(type != NULL);
if (!m_dmr) {
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set DMR -priority foreground");
if (m_duplex) {
if (m_rows > 2U)
socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR");
if (slotNo == 1U)
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);
else
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 Slot1_ 1 %u \"\"", m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u \"\"", m_rows / 2 + 1);
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot2 1 %u %u %u h 3 \"\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1);
}
}
if (m_duplex) {
if (m_rows > 2U)
socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR");
if (slotNo == 1U)
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2, m_cols - 1, m_rows / 2, src.c_str(), group ? "TG" : "", dst.c_str());
else
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1, src.c_str(), group ? "TG" : "", dst.c_str());
} else {
socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s > %s%s\"", m_cols - 1, src.c_str(), group ? "TG" : "", dst.c_str());
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s >\"", m_cols - 1, src.c_str());
socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, group ? "TG" : "", dst.c_str());
}
}
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)
{
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_duplex) {
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);
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
}
// LED 3 Green 4 Red 64 Yellow 68
void CLCDproc::writeFusionInt(const char* source, const char* dest, const char* type, const char* origin)
{
assert(source != NULL);
assert(dest != NULL);
assert(type != NULL);
assert(origin != NULL);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set YSF -priority foreground");
socketPrintf(m_socketfd, "widget_set YSF Mode 1 1 \"System Fusion\"");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, dest);
} else {
socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s >\"", source);
socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"%s%u\"", dest);
socketPrintf(m_socketfd, "output 64"); // Set LED3 color red
}
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()
{
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 Listening");
socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set YSF Line4 1 4 15 4 h 3 \"\"");
socketPrintf(m_socketfd, "output 4"); // Set LED3 color green
}
// LED 2 Green 2 Red 32 Yellow 34
void CLCDproc::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
{
assert(source != NULL);
assert(type != NULL);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set P25 -priority foreground");
socketPrintf(m_socketfd, "widget_set P25 Mode 1 1 P25");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, group ? "TG" : "", dest);
} else {
socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s >\"", source);
socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"%s%u\"", group ? "TG" : "", dest);
socketPrintf(m_socketfd, "output 32"); // Set LED2 color red
}
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()
{
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "widget_set P25 Line3 1 2 15 2 h 3 Listening");
socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set P25 Line4 1 4 15 4 h 3 \"\"");
socketPrintf(m_socketfd, "output 2"); // Set LED2 color green
}
void CLCDproc::writeCWInt()
{
}
void CLCDproc::clearCWInt()
{
}
void CLCDproc::clockInt(unsigned int ms)
{
m_clockDisplayTimer.clock(ms);
// Idle clock display
if (m_displayClock && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) {
time_t currentTime;
struct tm *Time;
time(&currentTime);
if (m_utc)
Time = gmtime(&currentTime);
else
Time = localtime(&currentTime);
setlocale(LC_TIME, "");
strftime(m_displayBuffer1, 128, "%X", Time); // Time
strftime(m_displayBuffer2, 128, "%x", Time); // Date
if (m_cols < 26U && m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set Status Time %u 2 \"%s%s\"", m_cols - 9, strlen(m_displayBuffer1) > 8 ? "" : " ", m_displayBuffer1);
} else {
socketPrintf(m_socketfd, "widget_set Status Time %u %u \"%s\"", (m_cols - (strlen(m_displayBuffer1) == 8 ? 6 : 8)) / 2, m_rows / 2, m_displayBuffer1);
socketPrintf(m_socketfd, "widget_set Status Date %u %u \"%s\"", (m_cols - (strlen(m_displayBuffer1) == 8 ? 6 : 8)) / 2, m_rows / 2 + 1, m_displayBuffer2);
}
m_clockDisplayTimer.start();
}
// We must set all this information on each select we do
FD_ZERO(&m_readfds); // empty readfds
// Then we put all the descriptors we want to wait for in a mask = m_readfds
FD_SET(m_socketfd, &m_readfds);
// Timeout, we will stop waiting for information
m_timeout.tv_sec = 0;
m_timeout.tv_usec = 0;
/* The first parameter is the biggest descriptor + 1. The first one was 0, so
* every other descriptor will be bigger
*
* readfds = &m_readfds
* writefds = we are not waiting for writefds
* exceptfds = we are not waiting for exception fds
*/
if (select(m_socketfd + 1, &m_readfds, NULL, NULL, &m_timeout) == -1)
LogError("LCDproc, error on select");
// If something was received from the server...
if (FD_ISSET(m_socketfd, &m_readfds)) {
m_recvsize = recv(m_socketfd, m_buffer, BUFFER_MAX_LEN, 0);
if (m_recvsize == -1)
LogError("LCDproc, cannot receive information");
m_buffer[m_recvsize] = '\0';
char *argv[256];
size_t len = strlen(m_buffer);
// Now split the string into tokens...
int argc = 0;
int newtoken = 1;
for (size_t i = 0U; i < len; i++) {
switch (m_buffer[i]) {
case ' ':
newtoken = 1;
m_buffer[i] = 0;
break;
default: /* regular chars, keep tokenizing */
if (newtoken)
argv[argc++] = m_buffer + i;
newtoken = 0;
break;
case '\0':
case '\n':
m_buffer[i] = 0;
if (argc > 0) {
if (0 == strcmp(argv[0], "listen")) {
LogDebug("LCDproc, the %s screen is displayed", argv[1]);
} else if (0 == strcmp(argv[0], "ignore")) {
LogDebug("LCDproc, the %s screen is hidden", argv[1]);
} else if (0 == strcmp(argv[0], "key")) {
LogDebug("LCDproc, Key %s", argv[1]);
} else if (0 == strcmp(argv[0], "menu")) {
} else if (0 == strcmp(argv[0], "connect")) {
// connect LCDproc 0.5.7 protocol 0.3 lcd wid 16 hgt 2 cellwid 5 cellhgt 8
int a;
for (a = 1; a < argc; a++) {
if (0 == strcmp(argv[a], "wid"))
m_cols = atoi(argv[++a]);
else if (0 == strcmp(argv[a], "hgt"))
m_rows = atoi(argv[++a]);
else if (0 == strcmp(argv[a], "cellwid")) {
//lcd_cellwid = atoi(argv[++a]);
} else if (0 == strcmp(argv[a], "cellhgt")) {
//lcd_cellhgt = atoi(argv[++a]);
}
}
m_connected = true;
socketPrintf(m_socketfd, "client_set -name MMDVMHost");
} else if (0 == strcmp(argv[0], "bye")) {
//close the socket- todo
} else if (0 == strcmp(argv[0], "success")) {
//LogDebug("LCDproc, command successful");
} else if (0 == strcmp(argv[0], "huh?")) {
sprintf(m_displayBuffer1, "LCDproc, command failed:");
sprintf(m_displayBuffer2, " ");
int j;
for (j = 1; j < argc; j++) {
strcat(m_displayBuffer1, m_displayBuffer2);
strcat(m_displayBuffer1, argv[j]);
}
LogDebug("%s", m_displayBuffer1);
}
}
/* Restart tokenizing */
argc = 0;
newtoken = 1;
break;
} /* switch( m_buffer[i] ) */
}
}
if (!m_screensDefined && m_connected)
defineScreens();
}
void CLCDproc::close()
{
}
int CLCDproc::socketPrintf(int fd, const char *format, ...)
{
char buf[BUFFER_MAX_LEN];
va_list ap;
va_start(ap, format);
int size = vsnprintf(buf, BUFFER_MAX_LEN, format, ap);
va_end(ap);
if (size < 0) {
LogError("LCDproc, socketPrintf: vsnprintf failed");
return -1;
}
if (size > BUFFER_MAX_LEN)
LogWarning("LCDproc, socketPrintf: vsnprintf truncated message");
FD_ZERO(&m_writefds); // empty writefds
FD_SET(m_socketfd, &m_writefds);
m_timeout.tv_sec = 0;
m_timeout.tv_usec = 0;
if (select(m_socketfd + 1, NULL, &m_writefds, NULL, &m_timeout) == -1)
LogError("LCDproc, error on select");
if (FD_ISSET(m_socketfd, &m_writefds)) {
if (send(m_socketfd, buf, int(strlen(buf) + 1U), 0) == -1) {
LogError("LCDproc, cannot send data");
return -1;
}
}
return 0;
}
void CLCDproc::defineScreens()
{
// The Status Screen
socketPrintf(m_socketfd, "screen_add Status");
socketPrintf(m_socketfd, "screen_set Status -name Status -heartbeat on -priority info -backlight %s", m_dimOnIdle ? "off" : "on");
socketPrintf(m_socketfd, "widget_add Status Callsign string");
socketPrintf(m_socketfd, "widget_add Status DMRNumber string");
socketPrintf(m_socketfd, "widget_add Status Title string");
socketPrintf(m_socketfd, "widget_add Status Status string");
socketPrintf(m_socketfd, "widget_add Status Time string");
socketPrintf(m_socketfd, "widget_add Status Date string");
socketPrintf(m_socketfd, "widget_set Status Callsign 1 1 %s", m_callsign.c_str());
socketPrintf(m_socketfd, "widget_set Status DMRNumber %u 1 %u", m_cols - 7, m_dmrid);
socketPrintf(m_socketfd, "widget_set Status Title 1 %u MMDVM", m_rows);
socketPrintf(m_socketfd, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows);
// The DStar Screen
socketPrintf(m_socketfd, "screen_add DStar");
socketPrintf(m_socketfd, "screen_set DStar -name DStar -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add DStar Mode string");
socketPrintf(m_socketfd, "widget_add DStar Line2 scroller");
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
socketPrintf(m_socketfd, "screen_add DMR");
socketPrintf(m_socketfd, "screen_set DMR -name DMR -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add DMR Mode string");
socketPrintf(m_socketfd, "widget_add DMR Slot1_ string");
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
socketPrintf(m_socketfd, "screen_add YSF");
socketPrintf(m_socketfd, "screen_set YSF -name YSF -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add YSF Mode string");
socketPrintf(m_socketfd, "widget_add YSF Line2 scroller");
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
socketPrintf(m_socketfd, "screen_add P25");
socketPrintf(m_socketfd, "screen_set P25 -name P25 -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add P25 Mode string");
socketPrintf(m_socketfd, "widget_add P25 Line2 scroller");
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;
}

@ -0,0 +1,82 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(LCDproc_H)
#define LCDproc_H
#include "Display.h"
#include "Timer.h"
#include <string>
class CLCDproc : public CDisplay
{
public:
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);
virtual ~CLCDproc();
virtual bool open();
virtual void close();
protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
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();
virtual void clearCWInt();
virtual void clockInt(unsigned int ms);
private:
std::string m_address;
unsigned int m_port;
unsigned int m_localPort;
std::string m_callsign;
unsigned int m_dmrid;
bool m_displayClock;
bool m_utc;
bool m_duplex;
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();
};
#endif

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

@ -0,0 +1,136 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Log.h"
#if defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
#else
#include <sys/time.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <ctime>
#include <cassert>
#include <cstring>
static unsigned int m_fileLevel = 2U;
static std::string m_filePath;
static std::string m_fileRoot;
static FILE* m_fpLog = NULL;
static unsigned int m_displayLevel = 2U;
static struct tm m_tm;
static char LEVELS[] = " DMIWEF";
static bool LogOpen()
{
if (m_fileLevel == 0U)
return true;
time_t now;
::time(&now);
struct tm* tm = ::gmtime(&now);
if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) {
if (m_fpLog != NULL)
return true;
} else {
if (m_fpLog != NULL)
::fclose(m_fpLog);
}
char filename[100U];
#if defined(_WIN32) || defined(_WIN64)
::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
#else
::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
#endif
m_fpLog = ::fopen(filename, "a+t");
m_tm = *tm;
return m_fpLog != NULL;
}
bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel)
{
m_filePath = filePath;
m_fileRoot = fileRoot;
m_fileLevel = fileLevel;
m_displayLevel = displayLevel;
return ::LogOpen();
}
void LogFinalise()
{
if (m_fpLog != NULL)
::fclose(m_fpLog);
}
void Log(unsigned int level, const char* fmt, ...)
{
assert(fmt != NULL);
char buffer[300U];
#if defined(_WIN32) || defined(_WIN64)
SYSTEMTIME st;
::GetSystemTime(&st);
::sprintf(buffer, "%c: %04u-%02u-%02u %02u:%02u:%02u.%03u ", LEVELS[level], st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
#else
struct timeval now;
::gettimeofday(&now, NULL);
struct tm* tm = ::gmtime(&now.tv_sec);
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000U);
#endif
va_list vl;
va_start(vl, fmt);
::vsprintf(buffer + ::strlen(buffer), fmt, vl);
va_end(vl);
if (level >= m_fileLevel && m_fileLevel != 0U) {
bool ret = ::LogOpen();
if (!ret)
return;
::fprintf(m_fpLog, "%s\n", buffer);
::fflush(m_fpLog);
}
if (level >= m_displayLevel && m_displayLevel != 0U) {
::fprintf(stdout, "%s\n", buffer);
::fflush(stdout);
}
if (level == 6U) { // Fatal
::fclose(m_fpLog);
exit(1);
}
}

36
Log.h

@ -0,0 +1,36 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(LOG_H)
#define LOG_H
#include <string>
#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__)
#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__)
#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__)
#define LogWarning(fmt, ...) Log(4U, fmt, ##__VA_ARGS__)
#define LogError(fmt, ...) Log(5U, fmt, ##__VA_ARGS__)
#define LogFatal(fmt, ...) Log(6U, fmt, ##__VA_ARGS__)
extern void Log(unsigned int level, const char* fmt, ...);
extern bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel);
extern void LogFinalise();
#endif

@ -0,0 +1,192 @@
[General]
Callsign=G9BF
Id=123456
Timeout=180
Duplex=1
# ModeHang=10
RFModeHang=10
NetModeHang=3
Display=None
Daemon=0
[Info]
RXFrequency=435000000
TXFrequency=435000000
Power=1
Latitude=0.0
Longitude=0.0
Height=0
Location=Nowhere
Description=Multi-Mode Repeater
URL=www.google.co.uk
[Log]
# Logging levels, 0=No logging
DisplayLevel=1
FileLevel=1
FilePath=.
FileRoot=MMDVM
[CW Id]
Enable=1
Time=10
# Callsign=
[DMR Id Lookup]
File=DMRIds.dat
Time=24
[Modem]
# Port=/dev/ttyACM0
Port=\\.\COM3
TXInvert=1
RXInvert=0
PTTInvert=0
TXDelay=100
RXOffset=0
TXOffset=0
DMRDelay=0
RXLevel=50
TXLevel=50
RXDCOffset=0
TXDCOffset=0
# CWIdTXLevel=50
# D-StarTXLevel=50
# DMRTXLevel=50
# YSFTXLevel=50
# P25TXLevel=50
RSSIMappingFile=RSSI.dat
Trace=0
Debug=0
[UMP]
Enable=0
# Port=\\.\COM4
Port=/dev/ttyACM1
[D-Star]
Enable=1
Module=C
SelfOnly=0
AckReply=1
AckTime=750
ErrorReply=1
RemoteGateway=0
# ModeHang=10
[DMR]
Enable=1
Beacons=1
ColorCode=1
SelfOnly=0
EmbeddedLCOnly=0
DumpTAData=1
# Prefixes=234,235
# Slot1TGWhiteList=
# Slot2TGWhiteList=
CallHang=3
TXHang=4
# ModeHang=10
[System Fusion]
Enable=1
LowDeviation=0
SelfOnly=0
#DSQ=1
RemoteGateway=0
# ModeHang=10
[P25]
Enable=1
NAC=293
SelfOnly=0
OverrideUIDCheck=0
RemoteGateway=0
# ModeHang=10
[D-Star Network]
Enable=1
GatewayAddress=127.0.0.1
GatewayPort=20010
LocalPort=20011
# ModeHang=3
Debug=0
[DMR Network]
Enable=1
Address=44.131.4.1
Port=62031
Jitter=300
# Local=62032
Password=PASSWORD
# Options=
Slot1=1
Slot2=1
# ModeHang=3
Debug=0
[System Fusion Network]
Enable=1
LocalAddress=127.0.0.1
LocalPort=3200
GatewayAddress=127.0.0.1
GatewayPort=4200
# ModeHang=3
Debug=0
[P25 Network]
Enable=1
GatewayAddress=127.0.0.1
GatewayPort=42020
LocalPort=32010
# ModeHang=3
Debug=0
[TFT Serial]
# Port=modem
Port=/dev/ttyAMA0
Brightness=50
[HD44780]
Rows=2
Columns=16
# For basic HD44780 displays (4-bit connection)
# rs, strb, d0, d1, d2, d3
Pins=11,10,0,1,2,3
# Device address for I2C
I2CAddress=0x20
# PWM backlight
PWM=0
PWMPin=21
PWMBright=100
PWMDim=16
DisplayClock=1
UTC=0
[Nextion]
# Port=modem
Port=/dev/ttyAMA0
Brightness=50
DisplayClock=1
UTC=0
#Screen Layout: 0=G4KLX 2=ON7LDS
ScreenLayout=2
IdleBrightness=20
[OLED]
Type=3
Brightness=0
Invert=0
Scroll=1
[LCDproc]
Address=localhost
Port=13666
#LocalPort=13667
DimOnIdle=0
DisplayClock=1
UTC=0

File diff suppressed because it is too large Load Diff

@ -0,0 +1,87 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(MMDVMHOST_H)
#define MMDVMHOST_H
#include "DStarNetwork.h"
#include "YSFNetwork.h"
#include "P25Network.h"
#include "DMRNetwork.h"
#include "DMRLookup.h"
#include "Display.h"
#include "Timer.h"
#include "Modem.h"
#include "Conf.h"
#include "UMP.h"
#include <string>
class CMMDVMHost
{
public:
CMMDVMHost(const std::string& confFile);
~CMMDVMHost();
int run();
private:
CConf m_conf;
CModem* m_modem;
CDStarNetwork* m_dstarNetwork;
CDMRNetwork* m_dmrNetwork;
CYSFNetwork* m_ysfNetwork;
CP25Network* m_p25Network;
CDisplay* m_display;
CUMP* m_ump;
unsigned char m_mode;
unsigned int m_dstarRFModeHang;
unsigned int m_dmrRFModeHang;
unsigned int m_ysfRFModeHang;
unsigned int m_p25RFModeHang;
unsigned int m_dstarNetModeHang;
unsigned int m_dmrNetModeHang;
unsigned int m_ysfNetModeHang;
unsigned int m_p25NetModeHang;
CTimer m_modeTimer;
CTimer m_dmrTXTimer;
CTimer m_cwIdTimer;
bool m_duplex;
unsigned int m_timeout;
bool m_dstarEnabled;
bool m_dmrEnabled;
bool m_ysfEnabled;
bool m_p25Enabled;
unsigned int m_cwIdTime;
CDMRLookup* m_lookup;
std::string m_callsign;
unsigned int m_id;
std::string m_cwCallsign;
void readParams();
bool createModem();
bool createDStarNetwork();
bool createDMRNetwork();
bool createYSFNetwork();
bool createP25Network();
void createDisplay();
void setMode(unsigned char mode);
};
#endif

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MMDVMHost", "MMDVMHost.vcxproj", "{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Debug|x64.ActiveCfg = Debug|x64
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Debug|x64.Build.0 = Debug|x64
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Debug|x86.ActiveCfg = Debug|Win32
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Debug|x86.Build.0 = Debug|Win32
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x64.ActiveCfg = Release|x64
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x64.Build.0 = Release|x64
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x86.ActiveCfg = Release|Win32
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

@ -0,0 +1,298 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>MMDVMHost</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PreBuildEvent>
<Command>"$(ProjectDir)prebuild.cmd" $(ProjectDir)</Command>
</PreBuildEvent>
<PreBuildEvent>
<Message>prebuild.cmd generates GitVersion.h from git refs heads master</Message>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AMBEFEC.h" />
<ClInclude Include="BCH.h" />
<ClInclude Include="BPTC19696.h" />
<ClInclude Include="Conf.h" />
<ClInclude Include="CRC.h" />
<ClInclude Include="Defines.h" />
<ClInclude Include="Display.h" />
<ClInclude Include="DMRAccessControl.h" />
<ClInclude Include="DMRControl.h" />
<ClInclude Include="DMRCSBK.h" />
<ClInclude Include="DMRData.h" />
<ClInclude Include="DMRDataHeader.h" />
<ClInclude Include="DMRDefines.h" />
<ClInclude Include="DMREMB.h" />
<ClInclude Include="DMREmbeddedData.h" />
<ClInclude Include="DMRFullLC.h" />
<ClInclude Include="DMRLC.h" />
<ClInclude Include="DMRNetwork.h" />
<ClInclude Include="DMRShortLC.h" />
<ClInclude Include="DMRSlot.h" />
<ClInclude Include="DMRSlotType.h" />
<ClInclude Include="DMRTrellis.h" />
<ClInclude Include="DStarControl.h" />
<ClInclude Include="DStarDefines.h" />
<ClInclude Include="DStarHeader.h" />
<ClInclude Include="DStarNetwork.h" />
<ClInclude Include="DStarSlowData.h" />
<ClInclude Include="Golay2087.h" />
<ClInclude Include="Golay24128.h" />
<ClInclude Include="Hamming.h" />
<ClInclude Include="DMRLookup.h" />
<ClInclude Include="JitterBuffer.h" />
<ClInclude Include="LCDproc.h" />
<ClInclude Include="Log.h" />
<ClInclude Include="MMDVMHost.h" />
<ClInclude Include="Modem.h" />
<ClInclude Include="ModemSerialPort.h" />
<ClInclude Include="Mutex.h" />
<ClInclude Include="NetworkInfo.h" />
<ClInclude Include="Nextion.h" />
<ClInclude Include="NullDisplay.h" />
<ClInclude Include="P25Audio.h" />
<ClInclude Include="P25Control.h" />
<ClInclude Include="P25Defines.h" />
<ClInclude Include="P25Data.h" />
<ClInclude Include="P25LowSpeedData.h" />
<ClInclude Include="P25Network.h" />
<ClInclude Include="P25NID.h" />
<ClInclude Include="P25Utils.h" />
<ClInclude Include="QR1676.h" />
<ClInclude Include="RingBuffer.h" />
<ClInclude Include="RS129.h" />
<ClInclude Include="RS241213.h" />
<ClInclude Include="RSSIInterpolator.h" />
<ClInclude Include="SerialController.h" />
<ClInclude Include="SerialPort.h" />
<ClInclude Include="SHA256.h" />
<ClInclude Include="StopWatch.h" />
<ClInclude Include="Sync.h" />
<ClInclude Include="TFTSerial.h" />
<ClInclude Include="Thread.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="UDPSocket.h" />
<ClInclude Include="UMP.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="Version.h" />
<ClInclude Include="YSFControl.h" />
<ClInclude Include="YSFConvolution.h" />
<ClInclude Include="YSFDefines.h" />
<ClInclude Include="YSFFICH.h" />
<ClInclude Include="YSFNetwork.h" />
<ClInclude Include="YSFPayload.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="AMBEFEC.cpp" />
<ClCompile Include="BCH.cpp" />
<ClCompile Include="BPTC19696.cpp" />
<ClCompile Include="Conf.cpp" />
<ClCompile Include="CRC.cpp" />
<ClCompile Include="Display.cpp" />
<ClCompile Include="DMRAccessControl.cpp" />
<ClCompile Include="DMRControl.cpp" />
<ClCompile Include="DMRCSBK.cpp" />
<ClCompile Include="DMRData.cpp" />
<ClCompile Include="DMRDataHeader.cpp" />
<ClCompile Include="DMREMB.cpp" />
<ClCompile Include="DMREmbeddedData.cpp" />
<ClCompile Include="DMRFullLC.cpp" />
<ClCompile Include="DMRLC.cpp" />
<ClCompile Include="DMRLookup.cpp" />
<ClCompile Include="DMRNetwork.cpp" />
<ClCompile Include="DMRShortLC.cpp" />
<ClCompile Include="DMRSlot.cpp" />
<ClCompile Include="DMRSlotType.cpp" />
<ClCompile Include="DMRTrellis.cpp" />
<ClCompile Include="DStarControl.cpp" />
<ClCompile Include="DStarHeader.cpp" />
<ClCompile Include="DStarNetwork.cpp" />
<ClCompile Include="DStarSlowData.cpp" />
<ClCompile Include="Golay2087.cpp" />
<ClCompile Include="Golay24128.cpp" />
<ClCompile Include="Hamming.cpp" />
<ClCompile Include="JitterBuffer.cpp" />
<ClCompile Include="LCDproc.cpp" />
<ClCompile Include="Log.cpp" />
<ClCompile Include="MMDVMHost.cpp" />
<ClCompile Include="Modem.cpp" />
<ClCompile Include="ModemSerialPort.cpp" />
<ClCompile Include="Mutex.cpp" />
<ClCompile Include="NetworkInfo.cpp" />
<ClCompile Include="Nextion.cpp" />
<ClCompile Include="NullDisplay.cpp" />
<ClCompile Include="P25Audio.cpp" />
<ClCompile Include="P25Control.cpp" />
<ClCompile Include="P25Data.cpp" />
<ClCompile Include="P25LowSpeedData.cpp" />
<ClCompile Include="P25Network.cpp" />
<ClCompile Include="P25NID.cpp" />
<ClCompile Include="P25Utils.cpp" />
<ClCompile Include="QR1676.cpp" />
<ClCompile Include="RS129.cpp" />
<ClCompile Include="RS241213.cpp" />
<ClCompile Include="RSSIInterpolator.cpp" />
<ClCompile Include="SerialController.cpp" />
<ClCompile Include="SerialPort.cpp" />
<ClCompile Include="SHA256.cpp" />
<ClCompile Include="StopWatch.cpp" />
<ClCompile Include="Sync.cpp" />
<ClCompile Include="TFTSerial.cpp" />
<ClCompile Include="Thread.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="UDPSocket.cpp" />
<ClCompile Include="UMP.cpp" />
<ClCompile Include="Utils.cpp" />
<ClCompile Include="YSFNetwork.cpp" />
<ClCompile Include="YSFPayload.cpp" />
<ClCompile Include="YSFControl.cpp" />
<ClCompile Include="YSFConvolution.cpp" />
<ClCompile Include="YSFFICH.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,428 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="BPTC19696.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Conf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CRC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Defines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Display.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRControl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRDefines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRSlot.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarDefines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Golay2087.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Golay24128.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Hamming.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Log.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MMDVMHost.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Modem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="NullDisplay.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="QR1676.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RingBuffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RS129.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SerialController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SHA256.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="StopWatch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TFTSerial.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Timer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UDPSocket.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="YSFDefines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="AMBEFEC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRDataHeader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarHeader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarSlowData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarControl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="YSFConvolution.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="YSFFICH.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRCSBK.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Sync.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMREMB.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMREmbeddedData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRFullLC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRLC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRShortLC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRSlotType.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="YSFControl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="YSFPayload.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Nextion.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRLookup.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="YSFNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Thread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRTrellis.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRAccessControl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="P25Defines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="P25Control.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="P25NID.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="P25Audio.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="P25Data.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="P25Utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="P25LowSpeedData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="P25Network.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BCH.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RS241213.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SerialPort.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ModemSerialPort.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Mutex.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LCDproc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UMP.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RSSIInterpolator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="NetworkInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="JitterBuffer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="BPTC19696.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Conf.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CRC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Display.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRControl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRSlot.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Golay2087.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Golay24128.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Hamming.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Log.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="MMDVMHost.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Modem.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NullDisplay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="QR1676.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RS129.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SerialController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SHA256.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="StopWatch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TFTSerial.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Timer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UDPSocket.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AMBEFEC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DStarNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRDataHeader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DStarHeader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DStarSlowData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DStarControl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="YSFConvolution.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="YSFFICH.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRCSBK.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Sync.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMREMB.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMREmbeddedData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRFullLC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRLC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRShortLC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRSlotType.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="YSFControl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="YSFPayload.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Nextion.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRLookup.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="YSFNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Thread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRTrellis.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRAccessControl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="P25Control.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="P25NID.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="P25Audio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="P25Data.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="P25Utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="P25LowSpeedData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="P25Network.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="BCH.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RS241213.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SerialPort.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ModemSerialPort.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Mutex.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LCDproc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UMP.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RSSIInterpolator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NetworkInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="JitterBuffer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

@ -0,0 +1,33 @@
# This makefile is for all platforms, but doesn't include support for the HD44780 display on the Raspberry Pi.
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread
LIBS = -lpthread
LDFLAGS = -g
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o \
DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \
Golay24128.o Hamming.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o P25Audio.o P25Control.o \
P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o \
Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -0,0 +1,33 @@
# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed.
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DOLED -I/usr/local/include
LIBS = -lArduiPi_OLED -lpthread
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o \
DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \
Golay24128.o Hamming.o JitterBuffer.o OLED.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion_HS.o NullDisplay.o P25Audio.o \
P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \
SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -0,0 +1,33 @@
# This makefile is for all platforms, but doesn't include support for the HD44780 display on the Raspberry Pi.
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread
LIBS = -lpthread
LDFLAGS = -g
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o \
DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \
Golay24128.o Hamming.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion_HS.o NullDisplay.o P25Audio.o P25Control.o \
P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o \
Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -0,0 +1,33 @@
# This makefile is for use with the Raspberry Pi. The wiringpi library is needed.
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DRASPBERRY_PI -I/usr/local/include
LIBS = -lwiringPi -lwiringPiDev -lpthread
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o \
DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \
Golay24128.o Hamming.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o P25Audio.o P25Control.o \
P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o \
Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -0,0 +1,33 @@
# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed.
# Support for the Adafruit i2c 16 x 2 RGB LCD Pi Plate
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHD44780 -DADAFRUIT_DISPLAY -I/usr/local/include
LIBS = -lwiringPi -lwiringPiDev -lpthread
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o \
DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \
Golay24128.o Hamming.o HD44780.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o P25Audio.o \
P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o \
StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -0,0 +1,33 @@
# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed.
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHD44780 -I/usr/local/include
LIBS = -lwiringPi -lwiringPiDev -lpthread
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o \
DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \
Golay24128.o Hamming.o HD44780.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o P25Audio.o \
P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o \
StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -0,0 +1,33 @@
# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed.
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DOLED -I/usr/local/include
LIBS = -lArduiPi_OLED -lpthread
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o \
DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \
Golay24128.o Hamming.o JitterBuffer.o OLED.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o P25Audio.o \
P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o \
SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -0,0 +1,33 @@
# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed.
# Support for the HD44780 connected via a PCF8574 8-bit GPIO expander IC
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHD44780 -DPCF8574_DISPLAY -I/usr/local/include
LIBS = -lwiringPi -lwiringPiDev -lpthread
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o \
DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \
Golay24128.o Hamming.o HD44780.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o P25Audio.o \
P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o \
StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -0,0 +1,33 @@
# This makefile is for Solaris using gcc
CC = gcc
CXX = g++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread
LIBS = -lpthread -lsocket
LDFLAGS = -g
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o \
DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \
Golay24128.o Hamming.o JitterBuffer.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o P25Audio.o P25Control.o \
P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o \
Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,154 @@
/*
* Copyright (C) 2011-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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef MODEM_H
#define MODEM_H
#include "SerialController.h"
#include "RingBuffer.h"
#include "Defines.h"
#include "Timer.h"
#include <string>
enum RESP_TYPE_MMDVM {
RTM_OK,
RTM_TIMEOUT,
RTM_ERROR
};
class CModem {
public:
CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool trace, bool debug);
~CModem();
void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset);
void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled);
void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25Enabled);
void setDMRParams(unsigned int colorCode);
void setYSFParams(bool loDev);
bool open();
unsigned int readDStarData(unsigned char* data);
unsigned int readDMRData1(unsigned char* data);
unsigned int readDMRData2(unsigned char* data);
unsigned int readYSFData(unsigned char* data);
unsigned int readP25Data(unsigned char* data);
unsigned int readSerial(unsigned char* data, unsigned int length);
bool hasDStarSpace() const;
bool hasDMRSpace1() const;
bool hasDMRSpace2() const;
bool hasYSFSpace() const;
bool hasP25Space() const;
bool hasTX() const;
bool hasCD() const;
bool hasLockout() const;
bool hasError() const;
bool writeDStarData(const unsigned char* data, unsigned int length);
bool writeDMRData1(const unsigned char* data, unsigned int length);
bool writeDMRData2(const unsigned char* data, unsigned int length);
bool writeYSFData(const unsigned char* data, unsigned int length);
bool writeP25Data(const unsigned char* data, unsigned int length);
bool writeDMRStart(bool tx);
bool writeDMRShortLC(const unsigned char* lc);
bool writeDMRAbort(unsigned int slotNo);
bool writeSerial(const unsigned char* data, unsigned int length);
bool setMode(unsigned char mode);
bool sendCWId(const std::string& callsign);
HW_TYPE getHWType() const;
void clock(unsigned int ms);
void close();
private:
std::string m_port;
unsigned int m_dmrColorCode;
bool m_ysfLoDev;
bool m_duplex;
bool m_rxInvert;
bool m_txInvert;
bool m_pttInvert;
unsigned int m_txDelay;
unsigned int m_dmrDelay;
float m_rxLevel;
float m_cwIdTXLevel;
float m_dstarTXLevel;
float m_dmrTXLevel;
float m_ysfTXLevel;
float m_p25TXLevel;
bool m_trace;
bool m_debug;
unsigned int m_rxFrequency;
unsigned int m_txFrequency;
bool m_dstarEnabled;
bool m_dmrEnabled;
bool m_ysfEnabled;
bool m_p25Enabled;
int m_rxDCOffset;
int m_txDCOffset;
CSerialController m_serial;
unsigned char* m_buffer;
unsigned int m_length;
unsigned int m_offset;
CRingBuffer<unsigned char> m_rxDStarData;
CRingBuffer<unsigned char> m_txDStarData;
CRingBuffer<unsigned char> m_rxDMRData1;
CRingBuffer<unsigned char> m_rxDMRData2;
CRingBuffer<unsigned char> m_txDMRData1;
CRingBuffer<unsigned char> m_txDMRData2;
CRingBuffer<unsigned char> m_rxYSFData;
CRingBuffer<unsigned char> m_txYSFData;
CRingBuffer<unsigned char> m_rxP25Data;
CRingBuffer<unsigned char> m_txP25Data;
CTimer m_statusTimer;
CTimer m_inactivityTimer;
CTimer m_playoutTimer;
unsigned int m_dstarSpace;
unsigned int m_dmrSpace1;
unsigned int m_dmrSpace2;
unsigned int m_ysfSpace;
unsigned int m_p25Space;
bool m_tx;
bool m_cd;
bool m_lockout;
bool m_error;
HW_TYPE m_hwType;
bool readVersion();
bool readStatus();
bool setConfig();
bool setFrequency();
void printDebug();
RESP_TYPE_MMDVM getResponse();
};
#endif

@ -0,0 +1,59 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "ModemSerialPort.h"
#include <cstdio>
#include <cassert>
CModemSerialPort::CModemSerialPort(CModem* modem) :
m_modem(modem)
{
assert(modem != NULL);
}
CModemSerialPort::~CModemSerialPort()
{
}
bool CModemSerialPort::open()
{
return true;
}
int CModemSerialPort::write(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
bool ret = m_modem->writeSerial(data, length);
return ret ? int(length) : -1;
}
int CModemSerialPort::read(unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
return m_modem->readSerial(data, length);
}
void CModemSerialPort::close()
{
}

@ -0,0 +1,42 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef ModemSerialPort_H
#define ModemSerialPort_H
#include "SerialPort.h"
#include "Modem.h"
class CModemSerialPort : public ISerialPort {
public:
CModemSerialPort(CModem* modem);
virtual ~CModemSerialPort();
virtual bool open();
virtual int read(unsigned char* buffer, unsigned int length);
virtual int write(const unsigned char* buffer, unsigned int length);
virtual void close();
private:
CModem* m_modem;
};
#endif

@ -0,0 +1,65 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Mutex.h"
#if defined(_WIN32) || defined(_WIN64)
CMutex::CMutex() :
m_handle()
{
m_handle = ::CreateMutex(NULL, FALSE, NULL);
}
CMutex::~CMutex()
{
::CloseHandle(m_handle);
}
void CMutex::lock()
{
::WaitForSingleObject(m_handle, INFINITE);
}
void CMutex::unlock()
{
::ReleaseMutex(m_handle);
}
#else
CMutex::CMutex() :
m_mutex(PTHREAD_MUTEX_INITIALIZER)
{
}
CMutex::~CMutex()
{
}
void CMutex::lock()
{
::pthread_mutex_lock(&m_mutex);
}
void CMutex::unlock()
{
::pthread_mutex_unlock(&m_mutex);
}
#endif

@ -0,0 +1,45 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(MUTEX_H)
#define MUTEX_H
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <pthread.h>
#endif
class CMutex
{
public:
CMutex();
~CMutex();
void lock();
void unlock();
private:
#if defined(_WIN32) || defined(_WIN64)
HANDLE m_handle;
#else
pthread_mutex_t m_mutex;
#endif
};
#endif

@ -0,0 +1,222 @@
/*
* Copyright (C) 2017 by Lieven De Samblanx ON7LDS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "NetworkInfo.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <ctime>
#include <clocale>
#include <sys/types.h>
#if !defined(_WIN32) && !defined(_WIN64)
#include <ifaddrs.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#else
#include <ws2tcpip.h>
#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")
#ifndef NO_ERROR
#define NO_ERROR 0
#endif
#ifndef ERROR_BUFFER_OVERFLOW
#define ERROR_BUFFER_OVERFLOW 111
#endif
#ifndef ERROR_INSUFFICIENT_BUFFER
#define ERROR_INSUFFICIENT_BUFFER 122
#endif
#endif
CNetworkInfo::CNetworkInfo()
{
}
CNetworkInfo::~CNetworkInfo()
{
}
void CNetworkInfo::getNetworkInterface(unsigned char* info)
{
LogInfo("Interfaces Info");
::strcpy((char*)info, "(address unknown)");
#if !defined(_WIN32) && !defined(_WIN64)
const unsigned int IFLISTSIZ = 25U;
FILE* fp = ::fopen("/proc/net/route" , "r");
if (fp == NULL) {
LogError("Unabled to open /proc/route");
return;
}
char* dflt = NULL;
char line[100U];
while (::fgets(line, 100U, fp)) {
char* p1 = strtok(line , " \t");
char* p2 = strtok(NULL , " \t");
if (p1 != NULL && p2 != NULL) {
if (::strcmp(p2, "00000000") == 0) {
dflt = p1;
break;
}
}
}
::fclose(fp);
if (dflt == NULL) {
LogError("Unable to find the default route");
return;
}
char interfacelist[IFLISTSIZ][50+INET6_ADDRSTRLEN];
for (unsigned int n = 0U; n < IFLISTSIZ; n++)
interfacelist[n][0] = 0;
struct ifaddrs* ifaddr;
if (::getifaddrs(&ifaddr) == -1) {
LogError("getifaddrs failure");
return;
}
unsigned int ifnr = 0U;
for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL)
continue;
int family = ifa->ifa_addr->sa_family;
if (family == AF_INET || family == AF_INET6) {
char host[NI_MAXHOST];
int s = ::getnameinfo(ifa->ifa_addr, family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (s != 0) {
LogError("getnameinfo() failed: %s\n", gai_strerror(s));
continue;
}
if (family == AF_INET) {
::sprintf(interfacelist[ifnr], "%s: %s", ifa->ifa_name, host);
LogInfo(" IPv4: %s", interfacelist[ifnr]);
} else {
::sprintf(interfacelist[ifnr], "%s: %s", ifa->ifa_name, host);
LogInfo(" IPv6: %s", interfacelist[ifnr]);
}
ifnr++;
}
}
::freeifaddrs(ifaddr);
LogInfo(" Default interface is : %s" , dflt);
for (unsigned int n = 0U; n < ifnr; n++) {
char* p = ::strchr(interfacelist[n], '%');
if (p != NULL)
*p = 0;
if (::strstr(interfacelist[n], dflt) != 0) {
::strcpy((char*)info, interfacelist[n]);
break;
}
}
LogInfo(" IP to show: %s", info);
#else
PMIB_IPFORWARDTABLE pIpForwardTable = (MIB_IPFORWARDTABLE *)::malloc(sizeof(MIB_IPFORWARDTABLE));
if (pIpForwardTable == NULL) {
LogError("Error allocating memory");
return;
}
DWORD dwSize = 0U;
if (::GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
::free(pIpForwardTable);
pIpForwardTable = (MIB_IPFORWARDTABLE *)::malloc(dwSize);
if (pIpForwardTable == NULL) {
LogError("Error allocating memory");
return;
}
}
DWORD ret = ::GetIpForwardTable(pIpForwardTable, &dwSize, 0);
if (ret != NO_ERROR) {
::free(pIpForwardTable);
LogError("GetIpForwardTable failed.");
return;
}
DWORD found = 999U;
for (DWORD i = 0U; i < pIpForwardTable->dwNumEntries; i++) {
if (pIpForwardTable->table[i].dwForwardDest == 0U) {
found = i;
break;
}
}
if (found == 999U) {
::free(pIpForwardTable);
LogError("Unable to find the default destination in the routing table.");
return;
}
DWORD ifnr = pIpForwardTable->table[found].dwForwardIfIndex;
::free(pIpForwardTable);
PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)::malloc(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL) {
LogError("Error allocating memory");
return;
}
ULONG buflen = sizeof(IP_ADAPTER_INFO);
if (::GetAdaptersInfo(pAdapterInfo, &buflen) == ERROR_BUFFER_OVERFLOW) {
::free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *)::malloc(buflen);
if (pAdapterInfo == NULL) {
LogError("Error allocating memory");
return;
}
}
if (::GetAdaptersInfo(pAdapterInfo, &buflen) != NO_ERROR) {
::free(pAdapterInfo);
LogError("Call to GetAdaptersInfo failed.");
return;
}
PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
while (pAdapter != NULL) {
LogInfo(" IP : %s", pAdapter->IpAddressList.IpAddress.String);
if (pAdapter->Index == ifnr)
::strcpy((char*)info, pAdapter->IpAddressList.IpAddress.String);
pAdapter = pAdapter->Next;
}
::free(pAdapterInfo);
LogInfo(" IP to show: %s", info);
#endif
}

@ -0,0 +1,32 @@
/*
* Copyright (C) 2017 by Lieven De Samblanx ON7LDS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(NETWORKINFO_H)
#define NETWORKINFO_H
class CNetworkInfo {
public:
CNetworkInfo();
~CNetworkInfo();
void getNetworkInterface(unsigned char* info);
private:
};
#endif

@ -0,0 +1,680 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "NetworkInfo.h"
#include "Nextion.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <ctime>
#include <clocale>
//#include <unistd.h>
const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms
const unsigned int DSTAR_BER_COUNT = 63U; // 63 * 20ms = 1260ms
const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms
const unsigned int DMR_BER_COUNT = 24U; // 24 * 60ms = 1440ms
const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int YSF_BER_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms
const unsigned int P25_BER_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, unsigned int screenLayout) :
CDisplay(),
m_callsign(callsign),
m_ipaddress("(ip unknown)"),
m_dmrid(dmrid),
m_serial(serial),
m_brightness(brightness),
m_mode(MODE_IDLE),
m_displayClock(displayClock),
m_utc(utc),
m_idleBrightness(idleBrightness),
m_screenLayout(screenLayout),
m_clockDisplayTimer(1000U, 0U, 400U),
m_rssiAccum1(0U),
m_rssiAccum2(0U),
m_berAccum1(0.0F),
m_berAccum2(0.0F),
m_rssiCount1(0U),
m_rssiCount2(0U),
m_berCount1(0U),
m_berCount2(0U)
{
assert(serial != NULL);
assert(brightness >= 0U && brightness <= 100U);
}
CNextion::~CNextion()
{
}
bool CNextion::open()
{
unsigned char info[100U];
CNetworkInfo* m_network;
bool ret = m_serial->open();
if (!ret) {
LogError("Cannot open the port for the Nextion display");
delete m_serial;
return false;
}
info[0]=0;
m_network = new CNetworkInfo;
m_network->getNetworkInterface(info);
m_ipaddress = (char*)info;
sendCommand("bkcmd=0");
setIdle();
return true;
}
void CNextion::setIdleInt()
{
sendCommand("page MMDVM");
char command[30U];
::sprintf(command, "dim=%u", m_idleBrightness);
sendCommand(command);
::sprintf(command, "t0.txt=\"%s/%u\"", m_callsign.c_str(), m_dmrid);
sendCommand(command);
sendCommand("t1.txt=\"MMDVM IDLE\"");
::sprintf(command, "t3.txt=\"%s\"", m_ipaddress.c_str());
sendCommand(command);
m_clockDisplayTimer.start();
m_mode = MODE_IDLE;
}
void CNextion::setErrorInt(const char* text)
{
assert(text != NULL);
sendCommand("page MMDVM");
char command[20];
::sprintf(command, "dim=%u", m_brightness);
sendCommand(command);
::sprintf(command, "t0.txt=\"%s\"", text);
sendCommand(command);
sendCommand("t1.txt=\"ERROR\"");
m_clockDisplayTimer.stop();
m_mode = MODE_ERROR;
}
void CNextion::setLockoutInt()
{
sendCommand("page MMDVM");
char command[20];
::sprintf(command, "dim=%u", m_brightness);
sendCommand(command);
sendCommand("t0.txt=\"LOCKOUT\"");
m_clockDisplayTimer.stop();
m_mode = MODE_LOCKOUT;
}
void CNextion::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != NULL);
assert(my2 != NULL);
assert(your != NULL);
assert(type != NULL);
assert(reflector != NULL);
if (m_mode != MODE_DSTAR)
sendCommand("page DStar");
char text[30U];
::sprintf(text, "dim=%u", m_brightness);
sendCommand(text);
::sprintf(text, "t0.txt=\"%s %.8s/%4.4s\"", type, my1, my2);
sendCommand(text);
::sprintf(text, "t1.txt=\"%.8s\"", your);
sendCommand(text);
if (::strcmp(reflector, " ") != 0) {
::sprintf(text, "t2.txt=\"via %.8s\"", reflector);
sendCommand(text);
}
m_clockDisplayTimer.stop();
m_mode = MODE_DSTAR;
m_rssiAccum1 = 0U;
m_berAccum1 = 0.0F;
m_rssiCount1 = 0U;
m_berCount1 = 0U;
}
void CNextion::writeDStarRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", rssi);
sendCommand(text);
m_rssiCount1 = 1U;
return;
}
m_rssiAccum1 += rssi;
m_rssiCount1++;
if (m_rssiCount1 == DSTAR_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", m_rssiAccum1 / DSTAR_RSSI_COUNT);
sendCommand(text);
m_rssiAccum1 = 0U;
m_rssiCount1 = 1U;
}
}
void CNextion::writeDStarBERInt(float ber)
{
if (m_berCount1 == 0U) {
char text[20U];
::sprintf(text, "t4.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount1 = 1U;
return;
}
m_berAccum1 += ber;
m_berCount1++;
if (m_berCount1 == DSTAR_BER_COUNT) {
char text[20U];
::sprintf(text, "t4.txt=\"%.1f%%\"", m_berAccum1 / float(DSTAR_BER_COUNT));
sendCommand(text);
m_berAccum1 = 0.0F;
m_berCount1 = 1U;
}
}
void CNextion::clearDStarInt()
{
sendCommand("t0.txt=\"Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t2.txt=\"\"");
sendCommand("t3.txt=\"\"");
sendCommand("t4.txt=\"\"");
}
void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
{
assert(type != NULL);
if (m_mode != MODE_DMR) {
sendCommand("page DMR");
if (slotNo == 1U) {
if (m_screenLayout == 2U) {
sendCommand("t2.pco=0");
sendCommand("t2.font=4");
}
sendCommand("t2.txt=\"2 Listening\"");
} else {
if (m_screenLayout == 2U) {
sendCommand("t0.pco=0");
sendCommand("t0.font=4");
}
sendCommand("t0.txt=\"1 Listening\"");
}
}
char text[30U];
::sprintf(text, "dim=%u", m_brightness);
sendCommand(text);
if (slotNo == 1U) {
::sprintf(text, "t0.txt=\"1 %s %s\"", type, src.c_str());
if (m_screenLayout == 2U) {
sendCommand("t0.pco=0");
sendCommand("t0.font=4");
}
sendCommand(text);
::sprintf(text, "t1.txt=\"%s%s\"", group ? "TG" : "", dst.c_str());
sendCommand(text);
} else {
::sprintf(text, "t2.txt=\"2 %s %s\"", type, src.c_str());
if (m_screenLayout == 2U) {
sendCommand("t2.pco=0");
sendCommand("t2.font=4");
}
sendCommand(text);
::sprintf(text, "t3.txt=\"%s%s\"", group ? "TG" : "", dst.c_str());
sendCommand(text);
}
m_clockDisplayTimer.stop();
m_mode = MODE_DMR;
m_rssiAccum1 = 0U;
m_rssiAccum2 = 0U;
m_berAccum1 = 0.0F;
m_berAccum2 = 0.0F;
m_rssiCount1 = 0U;
m_rssiCount2 = 0U;
m_berCount1 = 0U;
m_berCount2 = 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 = 1U;
return;
}
m_rssiAccum1 += rssi;
m_rssiCount1++;
if (m_rssiCount1 == DMR_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t4.txt=\"-%udBm\"", m_rssiAccum1 / DMR_RSSI_COUNT);
sendCommand(text);
m_rssiAccum1 = 0U;
m_rssiCount1 = 1U;
}
} else {
if (m_rssiCount2 == 0U) {
char text[20U];
::sprintf(text, "t5.txt=\"-%udBm\"", rssi);
sendCommand(text);
m_rssiCount2 = 1U;
return;
}
m_rssiAccum2 += rssi;
m_rssiCount2++;
if (m_rssiCount2 == DMR_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t5.txt=\"-%udBm\"", m_rssiAccum2 / DMR_RSSI_COUNT);
sendCommand(text);
m_rssiAccum2 = 0U;
m_rssiCount2 = 1U;
}
}
}
void CNextion::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, const char* type)
{
if (m_screenLayout < 2U)
return;
if (type[0] == ' ') {
if (slotNo == 1U)
sendCommand("t0.pco=33808");
else
sendCommand("t2.pco=33808");
return;
}
if (slotNo == 1U) {
char text[40U];
::sprintf(text, "t0.txt=\"1 %s %s\"", type, talkerAlias);
if (m_screenLayout == 2U) {
if (::strlen((char*)talkerAlias) > (16U-4U))
sendCommand("t0.font=3");
if (::strlen((char*)talkerAlias) > (20U-4U))
sendCommand("t0.font=2");
if (::strlen((char*)talkerAlias) > (24U-4U))
sendCommand("t0.font=1");
}
sendCommand("t0.pco=1024");
sendCommand(text);
} else {
char text[40U];
::sprintf(text, "t2.txt=\"2 %s %s\"", type, talkerAlias);
if (m_screenLayout == 2U) {
if (::strlen((char*)talkerAlias) > (16U-4U))
sendCommand("t2.font=3");
if (::strlen((char*)talkerAlias) > (20U-4U))
sendCommand("t2.font=2");
if (::strlen((char*)talkerAlias) > (24U-4U))
sendCommand("t2.font=1");
}
sendCommand("t2.pco=1024");
sendCommand(text);
}
}
void CNextion::writeDMRBERInt(unsigned int slotNo, float ber)
{
if (slotNo == 1U) {
if (m_berCount1 == 0U) {
char text[20U];
::sprintf(text, "t6.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount1 = 1U;
return;
}
m_berAccum1 += ber;
m_berCount1++;
if (m_berCount1 == DMR_BER_COUNT) {
char text[20U];
::sprintf(text, "t6.txt=\"%.1f%%\"", m_berAccum1 / DMR_BER_COUNT);
sendCommand(text);
m_berAccum1 = 0U;
m_berCount1 = 1U;
}
} else {
if (m_berCount2 == 0U) {
char text[20U];
::sprintf(text, "t7.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount2 = 1U;
return;
}
m_berAccum2 += ber;
m_berCount2++;
if (m_berCount2 == DMR_BER_COUNT) {
char text[20U];
::sprintf(text, "t7.txt=\"%.1f%%\"", m_berAccum2 / DMR_BER_COUNT);
sendCommand(text);
m_berAccum2 = 0U;
m_berCount2 = 1U;
}
}
}
void CNextion::clearDMRInt(unsigned int slotNo)
{
if (slotNo == 1U) {
sendCommand("t0.txt=\"1 Listening\"");
if (m_screenLayout == 2U) {
sendCommand("t0.pco=0");
sendCommand("t0.font=4");
}
sendCommand("t1.txt=\"\"");
sendCommand("t4.txt=\"\"");
sendCommand("t6.txt=\"\"");
} else {
sendCommand("t2.txt=\"2 Listening\"");
if (m_screenLayout == 2U) {
sendCommand("t2.pco=0");
sendCommand("t2.font=4");
}
sendCommand("t3.txt=\"\"");
sendCommand("t5.txt=\"\"");
sendCommand("t7.txt=\"\"");
}
}
void CNextion::writeFusionInt(const char* source, const char* dest, const char* type, const char* origin)
{
assert(source != NULL);
assert(dest != NULL);
assert(type != NULL);
assert(origin != NULL);
if (m_mode != MODE_YSF)
sendCommand("page YSF");
char text[30U];
::sprintf(text, "dim=%u", m_brightness);
sendCommand(text);
::sprintf(text, "t0.txt=\"%s %.10s\"", type, source);
sendCommand(text);
::sprintf(text, "t1.txt=\"%.10s\"", dest);
sendCommand(text);
if (::strcmp(origin, " ") != 0) {
::sprintf(text, "t2.txt=\"at %.10s\"", origin);
sendCommand(text);
}
m_clockDisplayTimer.stop();
m_mode = MODE_YSF;
m_rssiAccum1 = 0U;
m_berAccum1 = 0.0F;
m_rssiCount1 = 0U;
m_berCount1 = 0U;
}
void CNextion::writeFusionRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", rssi);
sendCommand(text);
m_rssiCount1 = 1U;
return;
}
m_rssiAccum1 += rssi;
m_rssiCount1++;
if (m_rssiCount1 == YSF_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t3.txt=\"-%udBm\"", m_rssiAccum1 / YSF_RSSI_COUNT);
sendCommand(text);
m_rssiAccum1 = 0U;
m_rssiCount1 = 1U;
}
}
void CNextion::writeFusionBERInt(float ber)
{
if (m_berCount1 == 0U) {
char text[20U];
::sprintf(text, "t4.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount1 = 1U;
return;
}
m_berAccum1 += ber;
m_berCount1++;
if (m_berCount1 == YSF_BER_COUNT) {
char text[20U];
::sprintf(text, "t4.txt=\"%.1f%%\"", m_berAccum1 / float(YSF_BER_COUNT));
sendCommand(text);
m_berAccum1 = 0.0F;
m_berCount1 = 1U;
}
}
void CNextion::clearFusionInt()
{
sendCommand("t0.txt=\"Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t2.txt=\"\"");
sendCommand("t3.txt=\"\"");
sendCommand("t4.txt=\"\"");
}
void CNextion::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
{
assert(source != NULL);
assert(type != NULL);
if (m_mode != MODE_P25)
sendCommand("page P25");
char text[30U];
::sprintf(text, "dim=%u", m_brightness);
sendCommand(text);
::sprintf(text, "t0.txt=\"%s %.10s\"", type, source);
sendCommand(text);
::sprintf(text, "t1.txt=\"%s%u\"", group ? "TG" : "", dest);
sendCommand(text);
m_clockDisplayTimer.stop();
m_mode = MODE_P25;
m_rssiAccum1 = 0U;
m_berAccum1 = 0.0F;
m_rssiCount1 = 0U;
m_berCount1 = 0U;
}
void CNextion::writeP25RSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
char text[20U];
::sprintf(text, "t2.txt=\"-%udBm\"", rssi);
sendCommand(text);
m_rssiCount1 = 1U;
return;
}
m_rssiAccum1 += rssi;
m_rssiCount1++;
if (m_rssiCount1 == P25_RSSI_COUNT) {
char text[20U];
::sprintf(text, "t2.txt=\"-%udBm\"", m_rssiAccum1 / P25_RSSI_COUNT);
sendCommand(text);
m_rssiAccum1 = 0U;
m_rssiCount1 = 1U;
}
}
void CNextion::writeP25BERInt(float ber)
{
if (m_berCount1 == 0U) {
char text[20U];
::sprintf(text, "t3.txt=\"%.1f%%\"", ber);
sendCommand(text);
m_berCount1 = 1U;
return;
}
m_berAccum1 += ber;
m_berCount1++;
if (m_berCount1 == P25_BER_COUNT) {
char text[20U];
::sprintf(text, "t3.txt=\"%.1f%%\"", m_berAccum1 / float(P25_BER_COUNT));
sendCommand(text);
m_berAccum1 = 0.0F;
m_berCount1 = 1U;
}
}
void CNextion::clearP25Int()
{
sendCommand("t0.txt=\"Listening\"");
sendCommand("t1.txt=\"\"");
sendCommand("t2.txt=\"\"");
sendCommand("t3.txt=\"\"");
}
void CNextion::writeCWInt()
{
sendCommand("t1.txt=\"Sending CW Ident\"");
m_clockDisplayTimer.start();
m_mode = MODE_CW;
}
void CNextion::clearCWInt()
{
sendCommand("t1.txt=\"MMDVM IDLE\"");
}
void CNextion::clockInt(unsigned int ms)
{
// Update the clock display in IDLE mode every 400ms
m_clockDisplayTimer.clock(ms);
if (m_displayClock && (m_mode == MODE_IDLE || m_mode == MODE_CW) && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) {
time_t currentTime;
struct tm *Time;
::time(&currentTime); // Get the current time
if (m_utc)
Time = ::gmtime(&currentTime);
else
Time = ::localtime(&currentTime);
setlocale(LC_TIME,"");
char text[50U];
strftime(text, 50, "t2.txt=\"%x %X\"", Time);
sendCommand(text);
m_clockDisplayTimer.start(); // restart the clock display timer
}
}
void CNextion::close()
{
sendCommand("page MMDVM");
sendCommand("t1.txt=\"MMDVM STOPPED\"");
m_serial->close();
delete m_serial;
}
void CNextion::sendCommand(const char* command)
{
assert(command != NULL);
m_serial->write((unsigned char*)command, ::strlen(command));
m_serial->write((unsigned char*)"\xFF\xFF\xFF", 3U);
}

@ -0,0 +1,95 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(NEXTION_H)
#define NEXTION_H
#include "Display.h"
#include "Defines.h"
#include "SerialPort.h"
#include "Timer.h"
#include <string>
class CNextion : public CDisplay
{
public:
CNextion(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool displayClock, bool utc, unsigned int idleBrightness, unsigned int screenLayout,const std::string& filesConfig);
virtual ~CNextion();
virtual bool open();
virtual void close();
protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
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 writeDStarBERInt(float ber);
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 writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, const char* type);
virtual void writeDMRBERInt(unsigned int slotNo, float ber);
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 writeFusionBERInt(float ber);
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 writeP25BERInt(float ber);
virtual void clearP25Int();
virtual void writeCWInt();
virtual void clearCWInt();
virtual void clockInt(unsigned int ms);
private:
std::string m_callsign;
std::string m_ipaddress;
unsigned int m_dmrid;
ISerialPort* m_serial;
unsigned int m_brightness;
unsigned char m_mode;
bool m_displayClock;
bool m_utc;
unsigned int m_idleBrightness;
unsigned int m_screenLayout;
std::string m_filesConfig;
CTimer m_clockDisplayTimer;
unsigned int m_rssiAccum1;
unsigned int m_rssiAccum2;
float m_berAccum1;
float m_berAccum2;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
unsigned int m_berCount1;
unsigned int m_berCount2;
void sendCommand(const char* command);
};
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,125 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "NullDisplay.h"
#if defined(RASPBERRY_PI)
#include <wiringPi.h>
#endif
#define LED_STATUS 28
CNullDisplay::CNullDisplay() :
CDisplay()
{
}
CNullDisplay::~CNullDisplay()
{
}
bool CNullDisplay::open()
{
#if defined(RASPBERRY_PI)
::wiringPiSetup();
::pinMode(LED_STATUS, OUTPUT);
::digitalWrite(LED_STATUS, 0);
#endif
return true;
}
void CNullDisplay::setIdleInt()
{
}
void CNullDisplay::setErrorInt(const char* text)
{
}
void CNullDisplay::setLockoutInt()
{
}
void CNullDisplay::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearDStarInt()
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearDMRInt(unsigned int slotNo)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeFusionInt(const char* source, const char* dest, const char* type, const char* origin)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearFusionInt()
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearP25Int()
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeCWInt()
{
}
void CNullDisplay::clearCWInt()
{
}
void CNullDisplay::close()
{
}

@ -0,0 +1,59 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(NULLDISPLAY_H)
#define NULLDISPLAY_H
#include "Display.h"
#include <string>
class CNullDisplay : public CDisplay
{
public:
CNullDisplay();
virtual ~CNullDisplay();
virtual bool open();
virtual void close();
protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearP25Int();
virtual void writeCWInt();
virtual void clearCWInt();
private:
};
#endif

@ -0,0 +1,953 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "OLED.h"
#include "NetworkInfo.h"
#include "Log.h"
const unsigned char bigote [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xF8, 0x00, 0x00,
0x00, 0x00, 0x0F, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x31, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x03, 0xFF, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0xFF, 0xF0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xF8, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFC, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x03, 0x00, 0xFE, 0x00, 0xFF, 0xFC, 0x00,
0x00, 0x03, 0xE3, 0xFF, 0x80, 0x7F, 0xFC, 0x00, 0x00, 0x01, 0xE7, 0xC1, 0xE0, 0x7F, 0xF8, 0x00,
0x00, 0x03, 0xF7, 0xFF, 0xE0, 0x7F, 0xF8, 0x00, 0x00, 0x03, 0xE3, 0xFF, 0xF0, 0x7E, 0x70, 0x00,
0x00, 0x02, 0xE3, 0xF3, 0xE0, 0x3C, 0xB0, 0x00, 0x00, 0x00, 0xC1, 0xE0, 0x60, 0x07, 0xF0, 0x00,
0x00, 0x00, 0x00, 0x03, 0x80, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03, 0xF0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x0F, 0xE0, 0x00,
0x00, 0x00, 0x0C, 0xC0, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x00, 0x3C, 0xE0, 0x00, 0x08, 0x80, 0x00,
0x00, 0x00, 0x7B, 0xF0, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFC, 0x00, 0x1F, 0x00, 0x00,
0x00, 0x03, 0xFF, 0xFC, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFC, 0x10, 0x3C, 0x00, 0x00,
0x00, 0x0F, 0xFF, 0xFE, 0x30, 0x3C, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x3C, 0x00, 0x00,
0x00, 0x1F, 0xFF, 0xFF, 0xF0, 0x3C, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x1C, 0x00, 0x00,
0x00, 0x3F, 0xFD, 0xFF, 0xF0, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x79, 0xFF, 0xE0, 0x0F, 0x00, 0x00,
0x00, 0x3C, 0x00, 0xFF, 0x86, 0x0F, 0xFC, 0x00, 0x00, 0x38, 0x00, 0x00, 0x06, 0x0F, 0xFE, 0x00,
0x00, 0x18, 0x00, 0x7E, 0x0E, 0x0B, 0xFF, 0x80, 0x00, 0x08, 0xFE, 0xFF, 0xF8, 0x1B, 0xFF, 0xF0,
0x00, 0x01, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFE, 0x00, 0x07, 0xFD, 0xFF, 0xF0, 0x43, 0xFF, 0xFF,
0x00, 0x1F, 0xFC, 0x3F, 0x00, 0xC7, 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x0F, 0xC7, 0x87, 0xFF, 0xFF,
0x1F, 0xFF, 0xFE, 0x3F, 0xCF, 0x07, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x3F, 0xFE, 0x0F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x0F, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFB, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF
};
const unsigned char discon [] = {
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xC4, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0xE3, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xA0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1B, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char parrot [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00,
0x46, 0x00, 0x02, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x42, 0x00, 0x02, 0x00, 0x3B, 0xF0, 0x00, 0x00,
0x4E, 0x00, 0x02, 0x00, 0x71, 0xE8, 0x00, 0x00, 0x70, 0x00, 0x03, 0x80, 0xF5, 0xC0, 0x00, 0x00,
0x47, 0x55, 0x32, 0x00, 0xF1, 0xBC, 0x00, 0x00, 0x41, 0x67, 0x4A, 0x00, 0xFB, 0x3C, 0x00, 0x00,
0x47, 0x44, 0x4A, 0x00, 0xFF, 0x7E, 0x00, 0x00, 0x45, 0x44, 0x4A, 0x00, 0xFF, 0x6E, 0x00, 0x00,
0x47, 0x44, 0x32, 0x00, 0xFF, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x82, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x00
};
const unsigned char XLX_B [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x07, 0xC1, 0xE7, 0x03, 0xE0, 0xF0, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x03, 0xC3, 0xC7, 0x01, 0xE1, 0xE0, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x01, 0xE3, 0x87, 0x00, 0xF1, 0xC0, 0x01, 0xFF, 0xC0, 0x00, 0xE0, 0x00, 0x00, 0x08, 0x00, 0x01,
0x01, 0xE7, 0x87, 0x00, 0xF3, 0xC0, 0x03, 0xC1, 0xC0, 0x00, 0xE0, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0xE7, 0x07, 0x00, 0x73, 0x80, 0x07, 0x80, 0x01, 0xC3, 0xFC, 0x3C, 0x00, 0x08, 0x00, 0x01,
0x00, 0x7E, 0x07, 0x00, 0x3F, 0x00, 0x07, 0x80, 0x07, 0xF3, 0xFC, 0x7E, 0x00, 0x08, 0x00, 0x01,
0x00, 0x7E, 0x07, 0x00, 0x3F, 0x00, 0x07, 0x00, 0x06, 0x7B, 0xFC, 0xE7, 0x00, 0x08, 0x00, 0x01,
0x00, 0x3C, 0x07, 0x00, 0x1E, 0x00, 0x07, 0x1F, 0xC0, 0x38, 0xE1, 0xC3, 0x80, 0x08, 0x00, 0x01,
0x00, 0x7E, 0x07, 0x00, 0x3F, 0x07, 0xE7, 0x1F, 0xC0, 0xF8, 0xE1, 0xC3, 0x80, 0x08, 0x00, 0x01,
0x00, 0x7E, 0x07, 0x00, 0x3F, 0x07, 0xE7, 0x03, 0xC7, 0xF8, 0xE1, 0xFF, 0x80, 0x08, 0x00, 0x0F,
0x00, 0xE7, 0x07, 0x00, 0x73, 0x87, 0xE7, 0x83, 0xCE, 0x38, 0xE1, 0xFF, 0x80, 0x08, 0x00, 0x0F,
0x01, 0xE7, 0x87, 0x00, 0xF3, 0xC0, 0x03, 0xC3, 0xCE, 0x38, 0xE1, 0xC0, 0x00, 0x08, 0x00, 0x01,
0x01, 0xC3, 0x87, 0xF8, 0xE1, 0xC0, 0x03, 0xFF, 0xCE, 0x78, 0xF1, 0xE0, 0x00, 0x08, 0x00, 0x01,
0x03, 0xC3, 0xC7, 0xF9, 0xE1, 0xE0, 0x01, 0xFF, 0xCF, 0xF8, 0xFC, 0xFF, 0x80, 0x08, 0x00, 0x01,
0x07, 0x81, 0xE7, 0xFB, 0xC0, 0xF0, 0x00, 0x7F, 0x87, 0xB8, 0x7C, 0x7F, 0x80, 0x08, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF
};
const unsigned char XLX [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
0x71, 0xDC, 0x1C, 0x70, 0x00, 0x00, 0x00, 0x41, 0x71, 0xDC, 0x1C, 0x70, 0x3C, 0x00, 0x00, 0x41,
0x3B, 0x9C, 0x0E, 0xE0, 0xFC, 0x02, 0x00, 0x41, 0x1B, 0x1C, 0x06, 0xC0, 0xC0, 0x07, 0x80, 0x41,
0x1E, 0x1C, 0x07, 0x81, 0x9C, 0xF2, 0x3C, 0x41, 0x0E, 0x1C, 0x03, 0x81, 0x9C, 0x32, 0x24, 0x41,
0x1F, 0x1C, 0x07, 0xC1, 0x84, 0xD2, 0x3C, 0x47, 0x3B, 0x1C, 0x0E, 0xC0, 0xEC, 0x92, 0x20, 0x41,
0x33, 0x9C, 0x0C, 0xE0, 0x7C, 0xFB, 0xBC, 0x41, 0x71, 0xDF, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x41,
0x60, 0xDF, 0xD8, 0x70, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
};
//Logo MMDVM for Idle Screen
static unsigned char logo_glcd_bmp[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xF8, 0x03, 0xFC, 0x7F, 0x80, 0x3F, 0xC7, 0xFF, 0xFC, 0xF8, 0x00, 0xF9, 0xFC, 0x01, 0xFE,
0x01, 0xFC, 0x07, 0xFC, 0x7F, 0xC0, 0x7F, 0xC4, 0x00, 0x02, 0x48, 0x00, 0x91, 0xFE, 0x03, 0xFE,
0x03, 0xFC, 0x07, 0xFC, 0x7F, 0xC0, 0x7F, 0xC5, 0xFF, 0xF1, 0x24, 0x01, 0x23, 0xFE, 0x03, 0xFE,
0x03, 0xFE, 0x0F, 0xBC, 0x7B, 0xE0, 0xFB, 0xC5, 0x00, 0x09, 0x24, 0x01, 0x23, 0xDF, 0x07, 0xDE,
0x07, 0xDE, 0x0F, 0x3C, 0x79, 0xE0, 0xF3, 0xC5, 0x00, 0x05, 0x12, 0x02, 0x47, 0xCF, 0x07, 0x9E,
0x07, 0x9F, 0x1F, 0x3C, 0x79, 0xF1, 0xF3, 0xC5, 0x00, 0x05, 0x12, 0x02, 0x47, 0x8F, 0x8F, 0x9E,
0x0F, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC5, 0x00, 0x05, 0x09, 0x04, 0x8F, 0x87, 0x8F, 0x1E,
0x0F, 0x0F, 0xBE, 0x3C, 0x78, 0xFB, 0xE3, 0xC5, 0x00, 0x05, 0x09, 0x04, 0x8F, 0x07, 0xDF, 0x1E,
0x1F, 0x07, 0xFC, 0x3C, 0x78, 0x7F, 0xC3, 0xC5, 0x00, 0x05, 0x04, 0x89, 0x1F, 0x03, 0xFE, 0x1E,
0x1E, 0x03, 0xFC, 0x3C, 0x78, 0x7F, 0xC3, 0xC5, 0x00, 0x09, 0x04, 0x89, 0x1E, 0x01, 0xFE, 0x1E,
0x3E, 0x03, 0xF8, 0x3C, 0x78, 0x3F, 0x83, 0xC5, 0xFF, 0xF1, 0x02, 0x72, 0x3E, 0x01, 0xFC, 0x1E,
0x3C, 0x01, 0xF0, 0x3C, 0x78, 0x1F, 0x03, 0xC4, 0x00, 0x02, 0x02, 0x02, 0x3C, 0x00, 0xF8, 0x1E,
0x7C, 0x01, 0xF0, 0x3C, 0x78, 0x1F, 0x03, 0xC7, 0xFF, 0xFC, 0x01, 0xFC, 0x7C, 0x00, 0xF8, 0x1E,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
//Logo D-Star 128x16 px
static unsigned char logo_dstar_bmp[] =
{
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x60, 0x03, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xF0, 0xFF, 0xFE, 0x07, 0x80, 0x3F, 0xF8,
0x00, 0x00, 0xC0, 0x07, 0xC1, 0xE0, 0x00, 0x00, 0x78, 0x7C, 0xFF, 0xFE, 0x0F, 0xC0, 0x3F, 0xFC,
0x00, 0x01, 0xC0, 0x07, 0x80, 0xF0, 0x00, 0x00, 0xE0, 0x3C, 0x07, 0x80, 0x0F, 0xC0, 0x78, 0x0E,
0x00, 0x03, 0xC0, 0x07, 0x80, 0x70, 0x00, 0x00, 0xE0, 0x38, 0x07, 0x00, 0x1B, 0xC0, 0x78, 0x0E,
0x00, 0x07, 0xC0, 0x07, 0x80, 0x70, 0x00, 0x01, 0xE0, 0x00, 0x07, 0x00, 0x33, 0xC0, 0x70, 0x1E,
0x07, 0xFF, 0xFE, 0x07, 0x00, 0x70, 0x00, 0x01, 0xF8, 0x00, 0x07, 0x00, 0x63, 0xC0, 0x70, 0x3C,
0x01, 0xFF, 0xF8, 0x0F, 0x00, 0x71, 0xFF, 0xE0, 0xFF, 0xF0, 0x0E, 0x00, 0xE1, 0xE0, 0xFF, 0xE0,
0x00, 0x7F, 0xE0, 0x0F, 0x00, 0x60, 0x00, 0x00, 0x03, 0xF8, 0x0E, 0x00, 0xC1, 0xE0, 0xFF, 0xE0,
0x00, 0x3F, 0x80, 0x0E, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x0E, 0x01, 0xFF, 0xE0, 0xE0, 0x70,
0x00, 0x7F, 0x00, 0x1E, 0x00, 0xE0, 0x00, 0x03, 0x80, 0x70, 0x0C, 0x03, 0xFC, 0xE0, 0xE0, 0x30,
0x00, 0xFF, 0x00, 0x1E, 0x01, 0xC0, 0x00, 0x07, 0x80, 0xE0, 0x1C, 0x07, 0x00, 0xE1, 0xE0, 0x38,
0x01, 0xEF, 0x00, 0x1C, 0x07, 0x80, 0x00, 0x07, 0xC1, 0xE0, 0x1C, 0x06, 0x00, 0xF1, 0xC0, 0x38,
0x03, 0x87, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x1C, 0x0C, 0x00, 0xF3, 0xC0, 0x38,
0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char bm [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x17, 0x70, 0x18, 0x30, 0x00,
0x3B, 0x00, 0x00, 0x17, 0x70, 0x03, 0xB1, 0x80, 0x3B, 0x77, 0x3C, 0xF7, 0x73, 0x9F, 0xFB, 0xDC,
0x3E, 0x71, 0xBE, 0x97, 0x56, 0x5E, 0x36, 0x58, 0x3F, 0x63, 0xA6, 0x96, 0xD7, 0xDF, 0xB7, 0xD8,
0x3B, 0x6D, 0xA6, 0x96, 0xD6, 0x19, 0xB6, 0x18, 0x3F, 0x6F, 0xA6, 0xF6, 0xD7, 0x9F, 0xBB, 0xD8,
0x3E, 0x63, 0xA6, 0xF6, 0xD3, 0x9F, 0xBB, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC, 0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC,
0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC, 0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC,
0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC, 0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC
};
//Logo Fusion 128x16
const unsigned char logo_fusion_bmp [] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xFC, 0x00, 0x1F, 0xE1, 0xFE, 0x1F, 0xFF, 0xF8, 0x7F, 0xC3, 0xFF, 0xFF, 0x1F, 0xFF, 0xFE,
0x03, 0xFC, 0x00, 0x3F, 0xC3, 0xFC, 0x3F, 0x80, 0x00, 0x7F, 0x87, 0xF0, 0xFF, 0x0F, 0xF1, 0xFF,
0x07, 0xFF, 0xFC, 0x7F, 0x83, 0xF8, 0x7F, 0x80, 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0x1F, 0xE1, 0xFE,
0x0F, 0xFF, 0xF0, 0x7F, 0x07, 0xF0, 0xFF, 0xFF, 0xC1, 0xFF, 0x1F, 0xE1, 0xFE, 0x3F, 0xC3, 0xFC,
0x0F, 0xF0, 0x00, 0xFE, 0x0F, 0xE0, 0x7F, 0xFF, 0xE1, 0xFE, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC,
0x1F, 0xE0, 0x01, 0xFC, 0x1F, 0xE0, 0x1F, 0xFF, 0xE3, 0xFC, 0x3F, 0xC3, 0xF8, 0x7F, 0x87, 0xF8,
0x3F, 0xC0, 0x03, 0xFC, 0x3F, 0xC0, 0x00, 0x3F, 0xC7, 0xF8, 0x7F, 0x87, 0xF8, 0xFF, 0x0F, 0xF0,
0x7F, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0x07, 0xF8, 0x7F, 0xCF, 0xE1, 0xFF, 0x1F, 0xF8,
0x7F, 0x80, 0x01, 0xFF, 0xFF, 0xC7, 0xFF, 0xFC, 0x0F, 0xF0, 0x3F, 0xFF, 0x81, 0xFE, 0x1F, 0xF0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char plus [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xE0, 0x00, 0x00,
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00,
0x00, 0x01, 0xE6, 0x17, 0x80, 0x40, 0x3F, 0x00, 0x00, 0x01, 0xF7, 0x36, 0x40, 0x40, 0x07, 0xC0,
0x00, 0x71, 0xB7, 0x36, 0x4E, 0x55, 0xC0, 0xE0, 0x00, 0xE1, 0xB7, 0xF7, 0x89, 0x55, 0x00, 0xE0,
0x01, 0xC1, 0xB6, 0xD6, 0xCE, 0x54, 0xC0, 0x60, 0x03, 0x81, 0xE6, 0xD6, 0x68, 0x5D, 0xC0, 0xC0,
0x03, 0xC0, 0x00, 0x00, 0x08, 0x00, 0x01, 0x80, 0x01, 0xF0, 0x00, 0x00, 0x08, 0x00, 0x0E, 0x00,
0x00, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char plus_B [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x07, 0xFC, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0xF8, 0x3C, 0x07, 0x9F, 0xE0, 0x00, 0x38, 0x00, 0x00, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x0E, 0x1C, 0x07, 0x0C, 0x70, 0x00, 0x18, 0x00, 0x00, 0x1F, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x07, 0x06, 0x1E, 0x0F, 0x0C, 0x30, 0x00, 0x18, 0x00, 0x00, 0x07, 0xF0, 0x00,
0x00, 0x00, 0x07, 0x07, 0x07, 0x1E, 0x0F, 0x0C, 0x30, 0x4C, 0x18, 0x00, 0x1C, 0x03, 0xF8, 0x00,
0x00, 0x00, 0x0E, 0x07, 0x03, 0x17, 0x1B, 0x0C, 0x70, 0xFE, 0x18, 0xC6, 0x36, 0x00, 0xFC, 0x00,
0x00, 0x00, 0x38, 0x07, 0x03, 0x13, 0x13, 0x0F, 0xE0, 0xE7, 0x18, 0xC6, 0x62, 0x00, 0x7C, 0x00,
0x00, 0x00, 0xF0, 0x07, 0x03, 0x1B, 0x33, 0x0F, 0xC0, 0xC3, 0x18, 0xC6, 0x70, 0x00, 0x7C, 0x00,
0x00, 0x01, 0xE0, 0x07, 0x03, 0x19, 0xB3, 0x0C, 0xC0, 0xC3, 0x18, 0xC6, 0x3C, 0x00, 0x7C, 0x00,
0x00, 0x03, 0xC0, 0x07, 0x07, 0x19, 0xE3, 0x0C, 0x60, 0xC3, 0x18, 0xC6, 0x0E, 0x00, 0x7C, 0x00,
0x00, 0x07, 0xC0, 0x07, 0x06, 0x18, 0xC3, 0x0C, 0x30, 0xC3, 0x18, 0xC6, 0x46, 0x00, 0x7C, 0x00,
0x00, 0x07, 0xC0, 0x07, 0x1C, 0x18, 0xC3, 0x0C, 0x38, 0xE6, 0x18, 0xDE, 0x62, 0x00, 0xF8, 0x00,
0x00, 0x07, 0xC0, 0x0F, 0xF8, 0x3C, 0xCF, 0x9F, 0x1E, 0xFC, 0x3C, 0x77, 0x7E, 0x00, 0xF0, 0x00,
0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00,
0x00, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00,
0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00,
0x00, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char bm_B [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3E, 0x0F, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3F, 0x1F, 0x80, 0x07, 0x80, 0x07, 0x00, 0x00, 0x00,
0x07, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3F, 0x1F, 0x80, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00,
0x07, 0x8F, 0x80, 0x00, 0x00, 0x00, 0x07, 0x3F, 0x1F, 0x83, 0x00, 0x07, 0x87, 0x03, 0x80, 0x00,
0x07, 0x87, 0xBF, 0x3F, 0x8F, 0xF0, 0xFF, 0x3F, 0x1F, 0x8F, 0xC7, 0x9F, 0xDF, 0xCF, 0xE3, 0xF0,
0x07, 0xFF, 0x3F, 0x7F, 0xCF, 0xF9, 0xFF, 0x3F, 0xBF, 0x9F, 0xE7, 0xBF, 0xDF, 0xCF, 0xF3, 0xF0,
0x07, 0xFE, 0x3F, 0x61, 0xCF, 0x79, 0xCF, 0x3B, 0xB7, 0x9C, 0x77, 0xBC, 0x07, 0x1C, 0x73, 0xC0,
0x07, 0xFF, 0x3C, 0x1F, 0xCF, 0x39, 0xCF, 0x3B, 0xF7, 0xBF, 0xF7, 0xBF, 0x87, 0x1F, 0xF3, 0x80,
0x07, 0x87, 0xBC, 0x3F, 0xCF, 0x39, 0xCF, 0x3B, 0xF7, 0xBF, 0xE7, 0x9F, 0xE7, 0x1F, 0xF3, 0x80,
0x07, 0x87, 0xBC, 0x71, 0xCF, 0x39, 0xCF, 0x39, 0xE7, 0xBC, 0x07, 0x87, 0xE7, 0x1C, 0x03, 0x80,
0x07, 0xFF, 0xBC, 0x71, 0xCF, 0x39, 0xCF, 0x39, 0xE7, 0x9E, 0x67, 0xB0, 0xE7, 0x1E, 0x73, 0x80,
0x07, 0xFF, 0x3C, 0x7F, 0xCF, 0x39, 0xFF, 0x39, 0xE7, 0x9F, 0xE7, 0xBF, 0xE7, 0xCF, 0xF3, 0x80,
0x07, 0xFE, 0x3C, 0x1F, 0xCF, 0x38, 0xFF, 0x39, 0xE7, 0x8F, 0xC7, 0x9F, 0xC7, 0xCF, 0xE3, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0
};
//Logo DMR 128x16 px
static unsigned char logo_dmr_bmp[] =
{
0x00, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xF8, 0x00, 0x00, 0x1F, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00,
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xFC, 0x00, 0x00, 0x3F, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xE1, 0xFE, 0x00, 0x00, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x0F, 0xF1, 0xFF, 0x80, 0x01, 0xFF, 0x1F, 0x80, 0x00, 0x1F, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x03, 0xF9, 0xFF, 0xC0, 0x03, 0xFF, 0x1F, 0x80, 0x00, 0x0F, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x01, 0xF9, 0xFF, 0xE0, 0x07, 0xFF, 0x1F, 0x80, 0x00, 0x0F, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x01, 0xFD, 0xF3, 0xF0, 0x1F, 0x9F, 0x1F, 0x80, 0x00, 0x1F, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x00, 0xFD, 0xF1, 0xFC, 0x3F, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x00, 0xFD, 0xF0, 0xFE, 0x7E, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x01, 0xFD, 0xF0, 0x7F, 0xFC, 0x1F, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x01, 0xF9, 0xF0, 0x1F, 0xF0, 0x1F, 0x1F, 0x81, 0xFC, 0x00, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x07, 0xF9, 0xF0, 0x0F, 0xE0, 0x1F, 0x1F, 0x80, 0x7F, 0x00, 0x00, 0x00,
0x00, 0x01, 0xF8, 0x00, 0x3F, 0xF1, 0xF0, 0x07, 0xC0, 0x1F, 0x1F, 0x80, 0x3F, 0xC0, 0x00, 0x00,
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x03, 0x80, 0x1F, 0x1F, 0x80, 0x0F, 0xF0, 0x00, 0x00,
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x01, 0xF0, 0x00, 0x00, 0x1F, 0x1F, 0x80, 0x03, 0xFC, 0x00, 0x00,
0x00, 0x01, 0xFF, 0xFF, 0xF0, 0x01, 0xF0, 0x00, 0x00, 0x1F, 0x1F, 0x80, 0x01, 0xFF, 0x00, 0x00
};
COLED::COLED(unsigned char displayType, unsigned char displayBrightness, bool displayInvert, bool displayScroll, bool duplex) :
m_ipaddress("(ip unknown)"),
m_displayType(displayType),
m_displayBrightness(displayBrightness),
m_displayInvert(displayInvert),
m_displayScroll(displayScroll),
m_duplex(duplex)
{
}
COLED::~COLED()
{
}
bool COLED::open()
{
// SPI
if (display.oled_is_spi_proto(m_displayType))
{
// SPI change parameters to fit to your LCD
if ( !display.init(OLED_SPI_DC,OLED_SPI_RESET,OLED_SPI_CS, m_displayType) )
return false;
}
else
{
// I2C change parameters to fit to your LCD
if ( !display.init(OLED_I2C_RESET, m_displayType) )
return false;
}
display.begin();
display.invertDisplay(m_displayInvert ? 1 : 0);
if (m_displayBrightness > 0U)
display.setBrightness(m_displayBrightness);
// init done
display.clearDisplay(); // clears the screen buffer
display.display(); // display it (clear display)
//init IP reading
unsigned char info[100U];
CNetworkInfo* m_network;
info[0]=0;
m_network = new CNetworkInfo;
m_network->getNetworkInterface(info);
m_ipaddress = (char*)info;
// OLED_statusbar();
display.clearDisplay(); // clears the screen buffer
display.display();
display.drawBitmap(48, 0, bigote, 64,64, WHITE);
// display.setCursor(0,OLED_LINE1);
// display.print("Startup");
display.display();
delay (6000);
return true;
}
void COLED::setIdleInt()
{
m_mode = MODE_IDLE;
display.clearDisplay();
// OLED Type 0
if (m_duplex ==1U){
if (m_displayScroll==1U){
display.startscrolldiagright(0x00,0x0f);
}
display.drawBitmap(0, 0, logo_glcd_bmp, 128, 16, WHITE);
display.setCursor(0,30);
display.setTextSize(2);
display.print("Idle");
display.setTextSize(1);
display.display();
}
// OLED type 2
else if (m_duplex !=1U){
if (m_displayScroll==1U){
display.startscrollleft(0x00,0x0f);
}
display.drawBitmap(0, 0, plus_B, 128, 32, WHITE);
OLED_statusbar();
display.setTextColor(WHITE);
display.setCursor(0,OLED_LINE5);
display.setTextSize(1);
display.printf("%s",m_ipaddress.c_str());
display.display();
FILE *temperatureFile;
double T;
temperatureFile = fopen ("/sys/class/thermal/thermal_zone0/temp", "r");
if (temperatureFile == NULL){
LogError("No temperature available");
}
else {
fscanf (temperatureFile, "%lf", &T);
T /= 1000;
//printf ("The temperature is %6.3f C.\n", T);
int temp = (int)T;
LogInfo ("Temperature is:%d",temp);
display.setCursor(25,OLED_LINE3);
display.setTextSize(2);
display.printf("Temp:%d",temp);
fclose (temperatureFile);
}
display.setTextSize(1);
display.display();
}
}
void COLED::setErrorInt(const char* text)
{
m_mode = MODE_ERROR;
display.clearDisplay();
OLED_statusbar();
display.setCursor(0,OLED_LINE1);
display.printf("%s\n",text);
display.display();
}
void COLED::setLockoutInt()
{
m_mode = MODE_LOCKOUT;
display.clearDisplay();
OLED_statusbar();
display.setCursor(0,30);
display.setTextSize(3);
display.print("Lockout");
display.setTextSize(1);
display.display();
}
void COLED::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
m_mode = MODE_DSTAR;
display.clearDisplay();
display.fillRect(0,OLED_LINE1,display.width(),display.height(),BLACK); //clear everything beneath logo
display.setCursor(0,OLED_LINE2);
display.printf("%s %.8s/%4.4s",type,my1,my2);
display.setCursor(0,OLED_LINE3);
display.printf("-> %.8s",your);
display.setCursor(0,OLED_LINE5);
display.printf("via %.8s",reflector);
OLED_statusbar();
display.display();
}
void COLED::clearDStarInt()
{
display.fillRect(0,OLED_LINE1, display.width(),display.height(),BLACK); //clear everything beneath the logo
display.setCursor(40,38);
display.print("Listening");
display.display();
}
void COLED::writeDMRInt(unsigned int slotNo,const std::string& src,bool group,const std::string& dst,const char* type)
{
if (m_mode != MODE_DMR) {
display.clearDisplay();
OLED_statusbar();
m_mode = MODE_DMR;
if (slotNo == 1U && m_duplex!=0U)
{
OLED_statusbar();
display.stopscroll();
display.fillRect(0,OLED_LINE4,display.width(),20,BLACK); //20=> clear 2 lines
display.setTextSize(1);
display.setCursor(0,OLED_LINE4);
display.print("2 Listening");
}
else if (m_duplex==0U)
{
;
// OLED_statusbar();
/// display.fillRect(0,OLED_LINE2,display.width(),80,BLACK); //20=> clear 2 lines
// display.setCursor(30,OLED_LINE2);
// display.setTextSize(2);
// display.print("DMR RX");
}
else if (m_duplex!=0U)
{
OLED_statusbar();
display.stopscroll();
display.fillRect(0,OLED_LINE2,display.width(),20,BLACK); //20=> clear 2 lines
display.setTextSize(1);
display.setCursor(0,OLED_LINE2);
display.print("1 Listening");
}
}
if (slotNo == 1U && m_duplex!=0U)
{
OLED_statusbar();
display.stopscroll();
display.fillRect(0,OLED_LINE2,display.width(),20,BLACK);
display.setCursor(0,OLED_LINE2);
display.printf("%i %s %s",slotNo,type,src.c_str());
display.setCursor(0,OLED_LINE3);
display.printf("%s%s",group ? "TG" : "",dst.c_str());
}
else if (m_duplex!=0U){
display.stopscroll();
display.fillRect(0,OLED_LINE4,display.width(),20,BLACK);
display.setCursor(0,OLED_LINE4);
display.printf("%i %s %s",slotNo,type,src.c_str());
display.setCursor(0,OLED_LINE5);
display.printf("%s%s", group ? "TG" : "", dst.c_str());
}
else if (m_duplex==0U)
{
display.stopscroll();
// OLED_statusbar();
//CALLSIGN Size 2
display.fillRect(0,OLED_LINE2,display.width(),60,BLACK);
display.setCursor(5,OLED_LINE2);
display.setTextSize(2);
display.printf("%s %s",type,src.c_str());
// TG
if (strcmp ("8",dst.c_str())!=0 || strcmp ("6",dst.c_str()) !=0 || strcmp ("9",dst.c_str())!=0|| strcmp ("9990",dst.c_str())!=0 || strcmp ("4000",dst.c_str())!=0 ){
display.fillRect(0, 0, display.width(), 16, BLACK);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.drawBitmap(0, 0, bm, 64, 16, WHITE);
}
if (strcmp ("6",dst.c_str()) ==0) {
display.fillRect(0, 0, display.width(), 16, BLACK);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.drawBitmap(0, 0, XLX, 64, 16, WHITE);
}
if (strcmp ("9",dst.c_str()) ==0) {
display.fillRect(0, 0, display.width(), 16, BLACK);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.drawBitmap(0, 0, plus, 64, 16, WHITE);
}
if (strcmp ("4000",dst.c_str()) ==0 ||strcmp("4000",src.c_str()) ==0) {
display.fillRect(0, 0, display.width(), 16, BLACK);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.drawBitmap(0, 0, discon, 64, 16, WHITE);
}
if (strcmp ("9990",dst.c_str()) ==0 ||strcmp("9990",src.c_str()) ==0) {
display.fillRect(0, 0, display.width(), 16, BLACK);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.drawBitmap(0, 0, parrot, 64, 16, WHITE);
}
// display.fillRect(70,OLED_LINE6,display.width(),12,BLACK);
display.setCursor(70,OLED_LINE6);
display.setTextColor(WHITE);
display.setTextSize(1);
display.printf("%s%s", group ? "TG:" : "", dst.c_str());
// ShutDown & Reboot compares
if ((strcmp ("99999",dst.c_str()) ==0))
{
display.clearDisplay();
OLED_statusbar();
display.setCursor(20,OLED_LINE3);
display.setTextSize(2);
display.printf("Reboot");
display.display();
printf ("Reboot\n");
system("sudo shutdown -r now");
delay (1000);
}
else if ((strcmp ("99998",dst.c_str()) ==0))
{
display.clearDisplay();
OLED_statusbar();
display.setCursor(20,OLED_LINE3);
display.setTextSize(2);
display.printf("STOP");
display.display();
printf ("Shutdown\n");
system("sudo shutdown -h now");
delay (1000);
}
else if ((strcmp ("99997",dst.c_str()) ==0))
{
display.clearDisplay();
OLED_statusbar();
display.setCursor(20,OLED_LINE3);
display.setTextSize(2);
display.printf("Load +");
display.display();
printf ("DmrPlus\n");
system("mm_plus");
delay (100);
}
else if ((strcmp ("99996",dst.c_str()) ==0))
{
display.clearDisplay();
OLED_statusbar();
display.setCursor(20,OLED_LINE3);
display.setTextSize(2);
display.printf("Load Gate");
display.display();
printf ("DMRGateway\n");
system("mm_gate");
delay (100);
}
else if ((strcmp ("99995",dst.c_str()) ==0))
{
display.clearDisplay();
OLED_statusbar();
display.setCursor(20,OLED_LINE3);
display.setTextSize(2);
display.printf("Load BM");
display.display();
printf ("BrandMeister\n");
system("mm_BM");
delay (100);
}
else if ((strcmp ("99990",dst.c_str()) ==0))
{
display.clearDisplay();
OLED_statusbar();
display.setCursor(20,OLED_LINE3);
display.setTextSize(2);
display.printf("Wifi Off");
display.display();
printf ("Wifi Off\n");
system("sudo rfkill block 0");
}
else if ((strcmp ("99991",dst.c_str()) ==0))
{
display.clearDisplay();
OLED_statusbar();
display.setCursor(20,OLED_LINE3);
display.setTextSize(2);
display.printf("Wifi On");
display.display();
printf ("Wifi On\n");
system("sudo rfkill unblock 0");
}
// OLED_statusbar();
display.display();
}
}
void COLED::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, const char* type)
{
//if (m_mode != MODE_DMR) {
// display.clearDisplay();
// m_mode = MODE_DMR;
// }
if (m_duplex!=0U){
;
}
else if (m_duplex==0U){
char text[40U];
if (type[0]==' ') {
if (slotNo == 1U) {
// sendCommand("t0.pco=33808");
} else {
// sendCommand("t2.pco=33808");
}
return;
}
if (slotNo == 1U) {
} else {
::sprintf(text, "%s",talkerAlias);
if (strlen((char*)talkerAlias)>16-4)
//printf ("%s%s\n","TalkerAlias>16:",text);
;
//display.fillRect(0,OLED_LINE4,display.width(),10,BLACK);
display.setCursor(0,OLED_LINE4);
display.printf("%s%s","TA:",text);
display.display();
if (strlen((char*)talkerAlias)>20-4)
//printf ("%s%s\n","TalkerAlias>20:",text);
;
//display.setCursor(0,OLED_LINE4);
//display.printf("%s",text);
if (strlen((char*)talkerAlias)>24-4)
//printf ("%s%s\n","TalkerAlias:>24",text);
;
//display.setCursor(0,OLED_LINE4);
//display.printf("%s%s","TA:",text);
}
}
// OLED_statusbar();
// display.display();
}
void COLED::clearDMRInt(unsigned int slotNo)
{
if (slotNo == 1U)
{
display.fillRect(0, OLED_LINE2, display.width(), 20, BLACK);
display.setCursor(0,OLED_LINE2);
display.print("1 Listening");
}
else if (slotNo != 1U && m_duplex!=0U)
{
display.fillRect(0, OLED_LINE4, display.width(), 20, BLACK);
display.setCursor(0, OLED_LINE4);
display.print("2 Listening");
}
else if (slotNo != 1U && m_duplex==0U)
{
display.fillRect(0, OLED_LINE2, display.width(), 40, BLACK);
display.setCursor(25, OLED_LINE2);
display.setTextSize(2);
display.print("STANDBY");
}
display.display();
display.display();
}
void COLED::writeFusionInt(const char* source, const char* dest, const char* type, const char* origin)
{
m_mode = MODE_YSF;
display.clearDisplay();
display.fillRect(0,OLED_LINE1,display.width(),display.height(),BLACK);
display.setCursor(0,OLED_LINE2);
display.printf("%s %.10s", type, source);
display.setCursor(0,OLED_LINE3);
display.printf(" %.10s", dest);
OLED_statusbar();
display.display();
}
void COLED::clearFusionInt()
{
display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK);
display.setCursor(40,38);
display.print("Listening");
display.display();
}
void COLED::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
{
m_mode = MODE_P25;
display.clearDisplay();
display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK);
display.setCursor(0,OLED_LINE2);
display.printf("%s %.10s", type, source);
display.setCursor(0,OLED_LINE3);
display.printf(" %s%u", group ? "TG" : "", dest);
OLED_statusbar();
display.display();
}
void COLED::clearP25Int()
{
display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK);
display.setCursor(40,38);
display.print("Listening");
display.display();
}
void COLED::writeCWInt()
{
display.clearDisplay();
display.setCursor(0,30);
display.setTextSize(3);
display.print("CW TX");
display.setTextSize(1);
display.display();
display.startscrollright(0x02,0x0f);
}
void COLED::clearCWInt()
{
display.clearDisplay();
display.setCursor(0,30);
display.setTextSize(3);
display.print("Idle");
display.setTextSize(1);
display.display();
display.startscrollleft(0x02,0x0f);
}
void COLED::close()
{
display.setCursor(0,30);
display.setTextSize(3);
display.print("MMDVM STOP");
display.close();
}
void COLED::OLED_statusbar()
{
// display.stopscroll();
// display.fillRect(0, 0, display.width(), 16, BLACK);
display.setTextColor(WHITE);
display.setCursor(0,0);
if (m_mode == MODE_DMR && m_duplex== 0U){
// display.drawBitmap(0, 0, bm, 64, 16, WHITE);
;
}
// DmrPlus LOGO
// display.drawBitmap(0, 0, plus_B, 128, 32, WHITE);
// XLX LOGO
// display.drawBitmap(0, 0, XLX_B, 128, 26, WHITE);
// BrandMeister LOGO
// display.drawBitmap(0, 0, bm_B, 128, 26, WHITE);
else if (m_mode == MODE_DMR && m_duplex != 0U ){
display.drawBitmap(0, 0, logo_dmr_bmp, 128, 16, WHITE);
}
else if (m_mode == MODE_DSTAR){
display.drawBitmap(0, 0, logo_dstar_bmp, 128, 16, WHITE);
}
else if (m_mode == MODE_YSF){
display.drawBitmap(0, 0, logo_fusion_bmp, 128, 16, WHITE);
}
else if (m_mode == MODE_P25){
display.print("P25");
}
display.display();
//if (m_oledLayout == 2U){
//display.drawBitmap(0, 0, bm_B, 128, 26, WHITE);
//}
// // MMDVM LOGO
//else if (m_oledLayout != 2U){
// display.drawBitmap(0, 0, logo_glcd_bmp, 128, 16, WHITE);
//}
}

@ -0,0 +1,93 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(OLED_H)
#define OLED_H
#define OLED_STATUSBAR 0
#define OLED_LINE1 16
#define OLED_LINE2 26
#define OLED_LINE3 36
#define OLED_LINE4 46
#define OLED_LINE5 56
/* OLED for HotSpots
*
*
*/
#define OLED_LINE6 6
#include "Display.h"
#include "Defines.h"
#include <string>
#include "ArduiPi_OLED_lib.h"
#include "Adafruit_GFX.h"
#include "ArduiPi_OLED.h"
class COLED : public CDisplay
{
public:
COLED(unsigned char displayType, unsigned char displayBrighness, bool displayInvert, bool displayScroll, bool duplex);
virtual ~COLED();
virtual bool open();
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, const char* type);
virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearP25Int();
virtual void writeCWInt();
virtual void clearCWInt();
virtual void close();
private:
std::string m_ipaddress;
const char* m_slot1_state;
const char* m_slot2_state;
unsigned char m_mode;
unsigned char m_displayType;
unsigned char m_displayBrightness;
bool m_displayInvert;
bool m_displayScroll;
bool m_duplex;
ArduiPi_OLED display;
void OLED_statusbar();
};
#endif

@ -0,0 +1,47 @@
# Prerequisite
Enable I2C and SPI modules directly with raspi-config tool, issue a
```
sudo raspi-config
```
Then go to menu Advanced Option, select SPI and under question ” Would you like the SPI kernel module to be loaded by default ?”, select Yes, Do the same thing for I2C Advanced Option.
As I dont use monitor or TV connected to Pi, I decreased dedicated video memory, always in raspi-config, go to Advanced Options then Memory Split, then type 16 Mo (the minimal) used for GPU, then select Finish, and select Yes when asked to reboot.
To be able to compile you will need the compiler and some others tools, issue a :
```
sudo apt-get install build-essential git-core libi2c-dev i2c-tools lm-sensors
```
*italic* Sometimes I2C and SPI modules are not started and thus he cannot start the sample code. The solution to start the modules at startup by adding the two following lines into the file /etc/modules
```
i2c-dev
spidev
```
Reboot, then you MUST see SPI and I2C devices with the following command
```
root@raspberrypi:~# ls /dev/i2c*
/dev/i2c-0
root@raspberrypi:~# ls /dev/spi*
/dev/spidev0.0 /dev/spidev0.1
```
# Installation of the generic Driver
The Driver is based on Adafruit Arduino library, I ported the code to be able to compile and run on Raspberry Pi but added also some features.
Get all the file from github dedicated repo :
```
git clone https://github.com/hallard/ArduiPi_OLED
cd ArduiPi_OLED
sudo make
```
# Building MMDVMHost
```
make -f Makefile.Pi.OLED
```
The initial guide is written by [Charles](http://hallard.me/adafruit-oled-display-driver-for-pi/)

@ -0,0 +1,339 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "P25Audio.h"
#include "P25Utils.h"
#include "Golay24128.h"
#include "Hamming.h"
#include <cstdio>
#include <cassert>
const unsigned int IMBE_INTERLEAVE[] = {
0, 7, 12, 19, 24, 31, 36, 43, 48, 55, 60, 67, 72, 79, 84, 91, 96, 103, 108, 115, 120, 127, 132, 139,
1, 6, 13, 18, 25, 30, 37, 42, 49, 54, 61, 66, 73, 78, 85, 90, 97, 102, 109, 114, 121, 126, 133, 138,
2, 9, 14, 21, 26, 33, 38, 45, 50, 57, 62, 69, 74, 81, 86, 93, 98, 105, 110, 117, 122, 129, 134, 141,
3, 8, 15, 20, 27, 32, 39, 44, 51, 56, 63, 68, 75, 80, 87, 92, 99, 104, 111, 116, 123, 128, 135, 140,
4, 11, 16, 23, 28, 35, 40, 47, 52, 59, 64, 71, 76, 83, 88, 95, 100, 107, 112, 119, 124, 131, 136, 143,
5, 10, 17, 22, 29, 34, 41, 46, 53, 58, 65, 70, 77, 82, 89, 94, 101, 106, 113, 118, 125, 130, 137, 142};
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#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])
CP25Audio::CP25Audio() :
m_fec()
{
}
CP25Audio::~CP25Audio()
{
}
unsigned int CP25Audio::process(unsigned char* data)
{
assert(data != NULL);
unsigned int errs = 0U;
unsigned char imbe[18U];
CP25Utils::decode(data, imbe, 114U, 262U);
errs += m_fec.regenerateIMBE(imbe);
CP25Utils::encode(imbe, data, 114U, 262U);
CP25Utils::decode(data, imbe, 262U, 410U);
errs += m_fec.regenerateIMBE(imbe);
CP25Utils::encode(imbe, data, 262U, 410U);
CP25Utils::decode(data, imbe, 452U, 600U);
errs += m_fec.regenerateIMBE(imbe);
CP25Utils::encode(imbe, data, 452U, 600U);
CP25Utils::decode(data, imbe, 640U, 788U);
errs += m_fec.regenerateIMBE(imbe);
CP25Utils::encode(imbe, data, 640U, 788U);
CP25Utils::decode(data, imbe, 830U, 978U);
errs += m_fec.regenerateIMBE(imbe);
CP25Utils::encode(imbe, data, 830U, 978U);
CP25Utils::decode(data, imbe, 1020U, 1168U);
errs += m_fec.regenerateIMBE(imbe);
CP25Utils::encode(imbe, data, 1020U, 1168U);
CP25Utils::decode(data, imbe, 1208U, 1356U);
errs += m_fec.regenerateIMBE(imbe);
CP25Utils::encode(imbe, data, 1208U, 1356U);
CP25Utils::decode(data, imbe, 1398U, 1546U);
errs += m_fec.regenerateIMBE(imbe);
CP25Utils::encode(imbe, data, 1398U, 1546U);
CP25Utils::decode(data, imbe, 1578U, 1726U);
errs += m_fec.regenerateIMBE(imbe);
CP25Utils::encode(imbe, data, 1578U, 1726U);
return errs;
}
void CP25Audio::decode(const unsigned char* data, unsigned char* imbe, unsigned int n)
{
assert(data != NULL);
assert(imbe != NULL);
unsigned char temp[18U];
switch (n) {
case 0U:
CP25Utils::decode(data, temp, 114U, 262U);
break;
case 1U:
CP25Utils::decode(data, temp, 262U, 410U);
break;
case 2U:
CP25Utils::decode(data, temp, 452U, 600U);
break;
case 3U:
CP25Utils::decode(data, temp, 640U, 788U);
break;
case 4U:
CP25Utils::decode(data, temp, 830U, 978U);
break;
case 5U:
CP25Utils::decode(data, temp, 1020U, 1168U);
break;
case 6U:
CP25Utils::decode(data, temp, 1208U, 1356U);
break;
case 7U:
CP25Utils::decode(data, temp, 1398U, 1546U);
break;
case 8U:
CP25Utils::decode(data, temp, 1578U, 1726U);
break;
default:
return;
}
bool bit[144U];
// De-interleave
for (unsigned int i = 0U; i < 144U; i++) {
unsigned int n = IMBE_INTERLEAVE[i];
bit[i] = READ_BIT(temp, n);
}
// now ..
// 12 voice bits 0
// 11 golay bits 12
//
// 12 voice bits 23
// 11 golay bits 35
//
// 12 voice bits 46
// 11 golay bits 58
//
// 12 voice bits 69
// 11 golay bits 81
//
// 11 voice bits 92
// 4 hamming bits 103
//
// 11 voice bits 107
// 4 hamming bits 118
//
// 11 voice bits 122
// 4 hamming bits 133
//
// 7 voice bits 137
// c0
unsigned int c0data = 0U;
for (unsigned int i = 0U; i < 12U; i++)
c0data = (c0data << 1) | (bit[i] ? 0x01U : 0x00U);
bool prn[114U];
// Create the whitening vector and save it for future use
unsigned int p = 16U * c0data;
for (unsigned int i = 0U; i < 114U; i++) {
p = (173U * p + 13849U) % 65536U;
prn[i] = p >= 32768U;
}
// De-whiten some bits
for (unsigned int i = 0U; i < 114U; i++)
bit[i + 23U] ^= prn[i];
unsigned int offset = 0U;
for (unsigned int i = 0U; i < 12U; i++, offset++)
WRITE_BIT(imbe, offset, bit[i + 0U]);
for (unsigned int i = 0U; i < 12U; i++, offset++)
WRITE_BIT(imbe, offset, bit[i + 23U]);
for (unsigned int i = 0U; i < 12U; i++, offset++)
WRITE_BIT(imbe, offset, bit[i + 46U]);
for (unsigned int i = 0U; i < 12U; i++, offset++)
WRITE_BIT(imbe, offset, bit[i + 69U]);
for (unsigned int i = 0U; i < 11U; i++, offset++)
WRITE_BIT(imbe, offset, bit[i + 92U]);
for (unsigned int i = 0U; i < 11U; i++, offset++)
WRITE_BIT(imbe, offset, bit[i + 107U]);
for (unsigned int i = 0U; i < 11U; i++, offset++)
WRITE_BIT(imbe, offset, bit[i + 122U]);
for (unsigned int i = 0U; i < 7U; i++, offset++)
WRITE_BIT(imbe, offset, bit[i + 137U]);
}
void CP25Audio::encode(unsigned char* data, const unsigned char* imbe, unsigned int n)
{
assert(data != NULL);
assert(imbe != NULL);
bool bTemp[144U];
bool* bit = bTemp;
// c0
unsigned int c0 = 0U;
for (unsigned int i = 0U; i < 12U; i++) {
bool b = READ_BIT(imbe, i);
c0 = (c0 << 1) | (b ? 0x01U : 0x00U);
}
unsigned int g2 = CGolay24128::encode23127(c0);
for (int i = 23; i >= 0; i--) {
bit[i] = (g2 & 0x01U) == 0x01U;
g2 >>= 1;
}
bit += 23U;
// c1
unsigned int c1 = 0U;
for (unsigned int i = 12U; i < 24U; i++) {
bool b = READ_BIT(imbe, i);
c1 = (c1 << 1) | (b ? 0x01U : 0x00U);
}
g2 = CGolay24128::encode23127(c1);
for (int i = 23; i >= 0; i--) {
bit[i] = (g2 & 0x01U) == 0x01U;
g2 >>= 1;
}
bit += 23U;
// c2
unsigned int c2 = 0;
for (unsigned int i = 24U; i < 36U; i++) {
bool b = READ_BIT(imbe, i);
c2 = (c2 << 1) | (b ? 0x01U : 0x00U);
}
g2 = CGolay24128::encode23127(c2);
for (int i = 23; i >= 0; i--) {
bit[i] = (g2 & 0x01U) == 0x01U;
g2 >>= 1;
}
bit += 23U;
// c3
unsigned int c3 = 0U;
for (unsigned int i = 36U; i < 48U; i++) {
bool b = READ_BIT(imbe, i);
c3 = (c3 << 1) | (b ? 0x01U : 0x00U);
}
g2 = CGolay24128::encode23127(c3);
for (int i = 23; i >= 0; i--) {
bit[i] = (g2 & 0x01U) == 0x01U;
g2 >>= 1;
}
bit += 23U;
// c4
for (unsigned int i = 0U; i < 11U; i++)
bit[i] = READ_BIT(imbe, i + 48U);
CHamming::encode15113_1(bit);
bit += 15U;
// c5
for (unsigned int i = 0U; i < 11U; i++)
bit[i] = READ_BIT(imbe, i + 59U);
CHamming::encode15113_1(bit);
bit += 15U;
// c6
for (unsigned int i = 0U; i < 11U; i++)
bit[i] = READ_BIT(imbe, i + 70U);
CHamming::encode15113_1(bit);
bit += 15U;
// c7
for (unsigned int i = 0U; i < 7U; i++)
bit[i] = READ_BIT(imbe, i + 81U);
bool prn[114U];
// Create the whitening vector and save it for future use
unsigned int p = 16U * c0;
for (unsigned int i = 0U; i < 114U; i++) {
p = (173U * p + 13849U) % 65536U;
prn[i] = p >= 32768U;
}
// Whiten some bits
for (unsigned int i = 0U; i < 114U; i++)
bTemp[i + 23U] ^= prn[i];
unsigned char temp[18U];
// Interleave
for (unsigned int i = 0U; i < 144U; i++) {
unsigned int n = IMBE_INTERLEAVE[i];
WRITE_BIT(temp, n, bTemp[i]);
}
switch (n) {
case 0U:
CP25Utils::encode(temp, data, 114U, 262U);
break;
case 1U:
CP25Utils::encode(temp, data, 262U, 410U);
break;
case 2U:
CP25Utils::encode(temp, data, 452U, 600U);
break;
case 3U:
CP25Utils::encode(temp, data, 640U, 788U);
break;
case 4U:
CP25Utils::encode(temp, data, 830U, 978U);
break;
case 5U:
CP25Utils::encode(temp, data, 1020U, 1168U);
break;
case 6U:
CP25Utils::encode(temp, data, 1208U, 1356U);
break;
case 7U:
CP25Utils::encode(temp, data, 1398U, 1546U);
break;
case 8U:
CP25Utils::encode(temp, data, 1578U, 1726U);
break;
default:
return;
}
}

@ -0,0 +1,39 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(P25Audio_H)
#define P25Audio_H
#include "AMBEFEC.h"
class CP25Audio {
public:
CP25Audio();
~CP25Audio();
unsigned int process(unsigned char* data);
void encode(unsigned char* data, const unsigned char* imbe, unsigned int n);
void decode(const unsigned char* data, unsigned char* imbe, unsigned int n);
private:
CAMBEFEC m_fec;
};
#endif

@ -0,0 +1,931 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "P25Control.h"
#include "P25Defines.h"
#include "P25Utils.h"
#include "Utils.h"
#include "Sync.h"
#include "Log.h"
#include <cassert>
#include <cstdio>
#include <cstring>
#include <ctime>
// #define DUMP_P25
const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
#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, unsigned int id, bool selfOnly, bool uidOverride, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup, bool remoteGateway, CRSSIInterpolator* rssiMapper) :
m_nac(nac),
m_id(id),
m_selfOnly(selfOnly),
m_uidOverride(uidOverride),
m_remoteGateway(remoteGateway),
m_network(network),
m_display(display),
m_duplex(duplex),
m_lookup(lookup),
m_queue(1000U, "P25 Control"),
m_rfState(RS_RF_LISTENING),
m_netState(RS_NET_IDLE),
m_rfTimeout(1000U, timeout),
m_netTimeout(1000U, timeout),
m_networkWatchdog(1000U, 0U, 1500U),
m_rfFrames(0U),
m_rfBits(0U),
m_rfErrs(0U),
m_netFrames(0U),
m_netLost(0U),
m_nid(nac),
m_lastDUID(P25_DUID_TERM),
m_audio(),
m_rfData(),
m_netData(),
m_rfLSD(),
m_netLSD(),
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];
::memset(m_netLDU1, 0x00U, 9U * 25U);
::memset(m_netLDU2, 0x00U, 9U * 25U);
m_lastIMBE = new unsigned char[11U];
::memcpy(m_lastIMBE, P25_NULL_IMBE, 11U);
m_rfLDU = new unsigned char[P25_LDU_FRAME_LENGTH_BYTES];
::memset(m_rfLDU, 0x00U, P25_LDU_FRAME_LENGTH_BYTES);
}
CP25Control::~CP25Control()
{
delete[] m_netLDU1;
delete[] m_netLDU2;
delete[] m_lastIMBE;
delete[] m_rfLDU;
}
bool CP25Control::writeModem(unsigned char* data, unsigned int len)
{
assert(data != NULL);
bool sync = data[1U] == 0x01U;
if (data[0U] == TAG_LOST && m_rfState == RS_RF_AUDIO) {
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;
m_rfTimeout.stop();
m_rfData.reset();
#if defined(DUMP_P25)
closeFile();
#endif
return false;
}
if (data[0U] == TAG_LOST) {
m_rfState = RS_RF_LISTENING;
return false;
}
if (!sync && m_rfState == RS_RF_LISTENING)
return false;
// Decode the NID
bool valid = m_nid.decode(data + 2U);
if (m_rfState == RS_RF_LISTENING && !valid)
return false;
unsigned char duid = m_nid.getDUID();
if (!valid) {
switch (m_lastDUID) {
case P25_DUID_HEADER:
case P25_DUID_LDU2:
duid = P25_DUID_LDU1;
break;
case P25_DUID_LDU1:
duid = P25_DUID_LDU2;
break;
default:
break;
}
}
// 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();
bool ret = m_rfData.decodeLDU1(data + 2U);
if (!ret) {
m_lastDUID = duid;
return false;
}
unsigned int srcId = m_rfData.getSrcId();
if (m_selfOnly) {
if (m_id > 99999999U) { // Check that the Config DMR-ID is bigger than 8 digits
if (srcId != m_id / 100U)
return false;
}
else if (m_id > 9999999U) { // Check that the Config DMR-ID is bigger than 7 digits
if (srcId != m_id / 10U)
return false;
}
else if (srcId != m_id) { // All other cases
return false;
}
}
if (!m_uidOverride) {
bool found = m_lookup->exists(srcId);
if (!found)
return false;
}
bool grp = m_rfData.getLCF() == P25_LCF_GROUP;
unsigned int dstId = m_rfData.getDstId();
std::string source = m_lookup->find(srcId);
LogMessage("P25, received RF transmission from %s to %s%u", source.c_str(), grp ? "TG " : "", dstId);
m_display->writeP25(source.c_str(), grp, dstId, "R");
m_rfState = RS_RF_AUDIO;
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
createRFHeader();
writeNetwork(data + 2U, P25_DUID_HEADER, false);
} else if (m_rfState == RS_RF_AUDIO) {
writeNetwork(m_rfLDU, m_lastDUID, false);
}
if (m_rfState == RS_RF_AUDIO) {
// Regenerate Sync
CSync::addP25Sync(data + 2U);
// Regenerate NID
m_nid.encode(data + 2U, P25_DUID_LDU1);
// Regenerate LDU1 Data
m_rfData.encodeLDU1(data + 2U);
// Regenerate the Low Speed Data
m_rfLSD.process(data + 2U);
// Regenerate Audio
unsigned int errors = m_audio.process(data + 2U);
LogDebug("P25, LDU1 audio, errs: %u/1233 (%.1f%%)", errors, float(errors) / 12.33F);
m_display->writeP25BER(float(errors) / 12.33F);
m_rfBits += 1233U;
m_rfErrs += errors;
m_rfFrames++;
m_lastDUID = duid;
// Add busy bits
addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true);
#if defined(DUMP_P25)
writeFile(data + 2U, len - 2U);
#endif
::memcpy(m_rfLDU, data + 2U, P25_LDU_FRAME_LENGTH_BYTES);
if (m_duplex) {
data[0U] = TAG_DATA;
data[1U] = 0x00U;
writeQueueRF(data, P25_LDU_FRAME_LENGTH_BYTES + 2U);
}
m_display->writeP25RSSI(m_rssi);
return true;
}
} else if (duid == P25_DUID_LDU2) {
if (m_rfState == RS_RF_AUDIO) {
writeNetwork(m_rfLDU, m_lastDUID, false);
// Regenerate Sync
CSync::addP25Sync(data + 2U);
// Regenerate NID
m_nid.encode(data + 2U, P25_DUID_LDU2);
// Add the dummy LDU2 data
m_rfData.encodeLDU2(data + 2U);
// Regenerate the Low Speed Data
m_rfLSD.process(data + 2U);
// Regenerate Audio
unsigned int errors = m_audio.process(data + 2U);
LogDebug("P25, LDU2 audio, errs: %u/1233 (%.1f%%)", errors, float(errors) / 12.33F);
m_display->writeP25BER(float(errors) / 12.33F);
m_rfBits += 1233U;
m_rfErrs += errors;
m_rfFrames++;
m_lastDUID = duid;
// Add busy bits
addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true);
#if defined(DUMP_P25)
writeFile(data + 2U, len - 2U);
#endif
::memcpy(m_rfLDU, data + 2U, P25_LDU_FRAME_LENGTH_BYTES);
if (m_duplex) {
data[0U] = TAG_DATA;
data[1U] = 0x00U;
writeQueueRF(data, P25_LDU_FRAME_LENGTH_BYTES + 2U);
}
m_display->writeP25RSSI(m_rssi);
return true;
}
} else if (duid == P25_DUID_TERM || duid == P25_DUID_TERM_LC) {
if (m_rfState == RS_RF_AUDIO) {
writeNetwork(m_rfLDU, m_lastDUID, true);
::memset(data + 2U, 0x00U, P25_TERM_FRAME_LENGTH_BYTES);
// Regenerate Sync
CSync::addP25Sync(data + 2U);
// Regenerate NID
m_nid.encode(data + 2U, P25_DUID_TERM);
// Add busy bits
addBusyBits(data + 2U, P25_TERM_FRAME_LENGTH_BITS, false, true);
m_rfState = RS_RF_LISTENING;
m_rfTimeout.stop();
m_rfData.reset();
m_lastDUID = duid;
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)
closeFile();
#endif
writeNetwork(data + 2U, P25_DUID_TERM, true);
if (m_duplex) {
data[0U] = TAG_EOT;
data[1U] = 0x00U;
writeQueueRF(data, P25_TERM_FRAME_LENGTH_BYTES + 2U);
}
}
}
return false;
}
unsigned int CP25Control::readModem(unsigned char* data)
{
assert(data != NULL);
if (m_queue.isEmpty())
return 0U;
unsigned char len = 0U;
m_queue.getData(&len, 1U);
m_queue.getData(data, len);
return len;
}
void CP25Control::writeNetwork()
{
unsigned char data[100U];
unsigned int length = m_network->read(data, 100U);
if (length == 0U)
return;
if (m_rfState != RS_RF_LISTENING && m_netState == RS_NET_IDLE)
return;
m_networkWatchdog.start();
switch (data[0U]) {
case 0x62U:
::memcpy(m_netLDU1 + 0U, data, 22U);
checkNetLDU2();
break;
case 0x63U:
::memcpy(m_netLDU1 + 25U, data, 14U);
checkNetLDU2();
break;
case 0x64U:
::memcpy(m_netLDU1 + 50U, data, 17U);
checkNetLDU2();
break;
case 0x65U:
::memcpy(m_netLDU1 + 75U, data, 17U);
checkNetLDU2();
break;
case 0x66U:
::memcpy(m_netLDU1 + 100U, data, 17U);
checkNetLDU2();
break;
case 0x67U:
::memcpy(m_netLDU1 + 125U, data, 17U);
checkNetLDU2();
break;
case 0x68U:
::memcpy(m_netLDU1 + 150U, data, 17U);
checkNetLDU2();
break;
case 0x69U:
::memcpy(m_netLDU1 + 175U, data, 17U);
checkNetLDU2();
break;
case 0x6AU:
::memcpy(m_netLDU1 + 200U, data, 16U);
checkNetLDU2();
if (m_netState != RS_NET_IDLE)
createNetLDU1();
break;
case 0x6BU:
::memcpy(m_netLDU2 + 0U, data, 22U);
checkNetLDU1();
break;
case 0x6CU:
::memcpy(m_netLDU2 + 25U, data, 14U);
checkNetLDU1();
break;
case 0x6DU:
::memcpy(m_netLDU2 + 50U, data, 17U);
checkNetLDU1();
break;
case 0x6EU:
::memcpy(m_netLDU2 + 75U, data, 17U);
checkNetLDU1();
break;
case 0x6FU:
::memcpy(m_netLDU2 + 100U, data, 17U);
checkNetLDU1();
break;
case 0x70U:
::memcpy(m_netLDU2 + 125U, data, 17U);
checkNetLDU1();
break;
case 0x71U:
::memcpy(m_netLDU2 + 150U, data, 17U);
checkNetLDU1();
break;
case 0x72U:
::memcpy(m_netLDU2 + 175U, data, 17U);
checkNetLDU1();
break;
case 0x73U:
::memcpy(m_netLDU2 + 200U, data, 16U);
if (m_netState == RS_NET_IDLE) {
createNetHeader();
createNetLDU1();
} else {
checkNetLDU1();
}
createNetLDU2();
break;
case 0x80U:
createNetTerminator();
break;
default:
break;
}
}
void CP25Control::clock(unsigned int ms)
{
if (m_network != NULL)
writeNetwork();
m_rfTimeout.clock(ms);
m_netTimeout.clock(ms);
if (m_netState == RS_NET_AUDIO) {
m_networkWatchdog.clock(ms);
if (m_networkWatchdog.hasExpired()) {
LogMessage("P25, network watchdog has expired, %.1f seconds, %u%% packet loss", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames);
m_display->clearP25();
m_networkWatchdog.stop();
m_netState = RS_NET_IDLE;
m_netData.reset();
m_netTimeout.stop();
}
}
}
void CP25Control::writeQueueRF(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
if (m_rfTimeout.isRunning() && m_rfTimeout.hasExpired())
return;
unsigned int space = m_queue.freeSpace();
if (space < (length + 1U)) {
LogError("P25, overflow in the P25 RF queue");
return;
}
unsigned char len = length;
m_queue.addData(&len, 1U);
m_queue.addData(data, len);
}
void CP25Control::writeQueueNet(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
if (m_netTimeout.isRunning() && m_netTimeout.hasExpired())
return;
unsigned int space = m_queue.freeSpace();
if (space < (length + 1U)) {
LogError("P25, overflow in the P25 RF queue");
return;
}
unsigned char len = length;
m_queue.addData(&len, 1U);
m_queue.addData(data, len);
}
void CP25Control::writeNetwork(const unsigned char *data, unsigned char type, bool end)
{
assert(data != NULL);
if (m_network == NULL)
return;
if (m_rfTimeout.isRunning() && m_rfTimeout.hasExpired())
return;
switch (type)
{
case P25_DUID_LDU1:
m_network->writeLDU1(data, m_rfData, m_rfLSD, end);
break;
case P25_DUID_LDU2:
m_network->writeLDU2(data, m_rfData, m_rfLSD, end);
break;
default:
break;
}
}
void CP25Control::addBusyBits(unsigned char* data, unsigned int length, bool b1, bool b2)
{
assert(data != NULL);
for (unsigned int ss0Pos = P25_SS0_START; ss0Pos < length; ss0Pos += P25_SS_INCREMENT) {
unsigned int ss1Pos = ss0Pos + 1U;
WRITE_BIT(data, ss0Pos, b1);
WRITE_BIT(data, ss1Pos, b2);
}
}
void CP25Control::checkNetLDU1()
{
if (m_netState == RS_NET_IDLE)
return;
// Check for an unflushed LDU1
if (m_netLDU1[0U] != 0x00U || m_netLDU1[25U] != 0x00U || m_netLDU1[50U] != 0x00U ||
m_netLDU1[75U] != 0x00U || m_netLDU1[100U] != 0x00U || m_netLDU1[125U] != 0x00U ||
m_netLDU1[150U] != 0x00U || m_netLDU1[175U] != 0x00U || m_netLDU1[200U] != 0x00U)
createNetLDU1();
}
void CP25Control::checkNetLDU2()
{
if (m_netState == RS_NET_IDLE)
return;
// Check for an unflushed LDU1
if (m_netLDU2[0U] != 0x00U || m_netLDU2[25U] != 0x00U || m_netLDU2[50U] != 0x00U ||
m_netLDU2[75U] != 0x00U || m_netLDU2[100U] != 0x00U || m_netLDU2[125U] != 0x00U ||
m_netLDU2[150U] != 0x00U || m_netLDU2[175U] != 0x00U || m_netLDU2[200U] != 0x00U)
createNetLDU2();
}
void CP25Control::insertMissingAudio(unsigned char* data)
{
if (data[0U] == 0x00U) {
::memcpy(data + 10U, m_lastIMBE, 11U);
m_netLost++;
} else {
::memcpy(m_lastIMBE, data + 10U, 11U);
}
if (data[25U] == 0x00U) {
::memcpy(data + 26U, m_lastIMBE, 11U);
m_netLost++;
} else {
::memcpy(m_lastIMBE, data + 26U, 11U);
}
if (data[50U] == 0x00U) {
::memcpy(data + 55U, m_lastIMBE, 11U);
m_netLost++;
} else {
::memcpy(m_lastIMBE, data + 55U, 11U);
}
if (data[75U] == 0x00U) {
::memcpy(data + 80U, m_lastIMBE, 11U);
m_netLost++;
} else {
::memcpy(m_lastIMBE, data + 80U, 11U);
}
if (data[100U] == 0x00U) {
::memcpy(data + 105U, m_lastIMBE, 11U);
m_netLost++;
} else {
::memcpy(m_lastIMBE, data + 105U, 11U);
}
if (data[125U] == 0x00U) {
::memcpy(data + 130U, m_lastIMBE, 11U);
m_netLost++;
} else {
::memcpy(m_lastIMBE, data + 130U, 11U);
}
if (data[150U] == 0x00U) {
::memcpy(data + 155U, m_lastIMBE, 11U);
m_netLost++;
} else {
::memcpy(m_lastIMBE, data + 155U, 11U);
}
if (data[175U] == 0x00U) {
::memcpy(data + 180U, m_lastIMBE, 11U);
m_netLost++;
} else {
::memcpy(m_lastIMBE, data + 180U, 11U);
}
if (data[200U] == 0x00U) {
::memcpy(data + 204U, m_lastIMBE, 11U);
m_netLost++;
} else {
::memcpy(m_lastIMBE, data + 204U, 11U);
}
}
void CP25Control::createRFHeader()
{
unsigned char buffer[P25_HDR_FRAME_LENGTH_BYTES + 2U];
::memset(buffer, 0x00U, P25_HDR_FRAME_LENGTH_BYTES + 2U);
buffer[0U] = TAG_HEADER;
buffer[1U] = 0x00U;
// Add the sync
CSync::addP25Sync(buffer + 2U);
// Add the NID
m_nid.encode(buffer + 2U, P25_DUID_HEADER);
// Add the dummy header
m_rfData.encodeHeader(buffer + 2U);
// Add busy bits
addBusyBits(buffer + 2U, P25_HDR_FRAME_LENGTH_BITS, false, true);
m_rfFrames = 0U;
m_rfErrs = 0U;
m_rfBits = 1U;
m_rfTimeout.start();
m_lastDUID = P25_DUID_HEADER;
::memset(m_rfLDU, 0x00U, P25_LDU_FRAME_LENGTH_BYTES);
#if defined(DUMP_P25)
openFile();
writeFile(buffer + 2U, buffer - 2U);
#endif
if (m_duplex) {
buffer[0U] = TAG_HEADER;
buffer[1U] = 0x00U;
writeQueueRF(buffer, P25_HDR_FRAME_LENGTH_BYTES + 2U);
}
}
void CP25Control::createNetHeader()
{
unsigned char lcf = m_netLDU1[51U];
unsigned char mfId = m_netLDU1[52U];
unsigned int dstId = (m_netLDU1[76U] << 16) + (m_netLDU1[77U] << 8) + m_netLDU1[78U];
unsigned int srcId = (m_netLDU1[101U] << 16) + (m_netLDU1[102U] << 8) + m_netLDU1[103U];
unsigned char algId = m_netLDU2[126U];
unsigned int kId = (m_netLDU2[127U] << 8) + m_netLDU2[128U];
unsigned char mi[P25_MI_LENGTH_BYTES];
::memcpy(mi + 0U, m_netLDU2 + 51U, 3U);
::memcpy(mi + 3U, m_netLDU2 + 76U, 3U);
::memcpy(mi + 6U, m_netLDU2 + 101U, 3U);
m_netData.reset();
m_netData.setMI(mi);
m_netData.setAlgId(algId);
m_netData.setKId(kId);
m_netData.setLCF(lcf);
m_netData.setMFId(mfId);
m_netData.setSrcId(srcId);
m_netData.setDstId(dstId);
std::string source = m_lookup->find(srcId);
LogMessage("P25, received network transmission from %s to %s%u", source.c_str(), lcf == P25_LCF_GROUP ? "TG " : "", dstId);
m_display->writeP25(source.c_str(), lcf == P25_LCF_GROUP, dstId, "N");
m_netState = RS_NET_AUDIO;
m_netTimeout.start();
m_netFrames = 0U;
m_netLost = 0U;
unsigned char buffer[P25_HDR_FRAME_LENGTH_BYTES + 2U];
::memset(buffer, 0x00U, P25_HDR_FRAME_LENGTH_BYTES + 2U);
buffer[0U] = TAG_HEADER;
buffer[1U] = 0x00U;
// Add the sync
CSync::addP25Sync(buffer + 2U);
// Add the NID
m_nid.encode(buffer + 2U, P25_DUID_HEADER);
// Add the dummy header
m_netData.encodeHeader(buffer + 2U);
// Add busy bits
if (m_remoteGateway)
addBusyBits(buffer + 2U, P25_HDR_FRAME_LENGTH_BITS, true, false);
else
addBusyBits(buffer + 2U, P25_HDR_FRAME_LENGTH_BITS, false, true);
writeQueueNet(buffer, P25_HDR_FRAME_LENGTH_BYTES + 2U);
}
void CP25Control::createNetLDU1()
{
insertMissingAudio(m_netLDU1);
unsigned char buffer[P25_LDU_FRAME_LENGTH_BYTES + 2U];
::memset(buffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 2U);
buffer[0U] = TAG_DATA;
buffer[1U] = 0x00U;
// Add the sync
CSync::addP25Sync(buffer + 2U);
// Add the NID
m_nid.encode(buffer + 2U, P25_DUID_LDU1);
// Add the LDU1 data
m_netData.encodeLDU1(buffer + 2U);
// Add the Audio
m_audio.encode(buffer + 2U, m_netLDU1 + 10U, 0U);
m_audio.encode(buffer + 2U, m_netLDU1 + 26U, 1U);
m_audio.encode(buffer + 2U, m_netLDU1 + 55U, 2U);
m_audio.encode(buffer + 2U, m_netLDU1 + 80U, 3U);
m_audio.encode(buffer + 2U, m_netLDU1 + 105U, 4U);
m_audio.encode(buffer + 2U, m_netLDU1 + 130U, 5U);
m_audio.encode(buffer + 2U, m_netLDU1 + 155U, 6U);
m_audio.encode(buffer + 2U, m_netLDU1 + 180U, 7U);
m_audio.encode(buffer + 2U, m_netLDU1 + 204U, 8U);
// Add the Low Speed Data
m_netLSD.setLSD1(m_netLDU1[201U]);
m_netLSD.setLSD2(m_netLDU1[202U]);
m_netLSD.encode(buffer + 2U);
// Add busy bits
if (m_remoteGateway)
addBusyBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false);
else
addBusyBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true);
writeQueueNet(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U);
::memset(m_netLDU1, 0x00U, 9U * 25U);
m_netFrames += 9U;
}
void CP25Control::createNetLDU2()
{
insertMissingAudio(m_netLDU2);
unsigned char buffer[P25_LDU_FRAME_LENGTH_BYTES + 2U];
::memset(buffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 2U);
buffer[0U] = TAG_DATA;
buffer[1U] = 0x00U;
// Add the sync
CSync::addP25Sync(buffer + 2U);
// Add the NID
m_nid.encode(buffer + 2U, P25_DUID_LDU2);
// Add the dummy LDU2 data
m_netData.encodeLDU2(buffer + 2U);
// Add the Audio
m_audio.encode(buffer + 2U, m_netLDU2 + 10U, 0U);
m_audio.encode(buffer + 2U, m_netLDU2 + 26U, 1U);
m_audio.encode(buffer + 2U, m_netLDU2 + 55U, 2U);
m_audio.encode(buffer + 2U, m_netLDU2 + 80U, 3U);
m_audio.encode(buffer + 2U, m_netLDU2 + 105U, 4U);
m_audio.encode(buffer + 2U, m_netLDU2 + 130U, 5U);
m_audio.encode(buffer + 2U, m_netLDU2 + 155U, 6U);
m_audio.encode(buffer + 2U, m_netLDU2 + 180U, 7U);
m_audio.encode(buffer + 2U, m_netLDU2 + 204U, 8U);
// Add the Low Speed Data
m_netLSD.setLSD1(m_netLDU2[201U]);
m_netLSD.setLSD2(m_netLDU2[202U]);
m_netLSD.encode(buffer + 2U);
// Add busy bits
if (m_remoteGateway)
addBusyBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false);
else
addBusyBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true);
writeQueueNet(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U);
::memset(m_netLDU2, 0x00U, 9U * 25U);
m_netFrames += 9U;
}
void CP25Control::createNetTerminator()
{
unsigned char buffer[P25_TERM_FRAME_LENGTH_BYTES + 2U];
::memset(buffer, 0x00U, P25_TERM_FRAME_LENGTH_BYTES + 2U);
buffer[0U] = TAG_EOT;
buffer[1U] = 0x00U;
// Add the sync
CSync::addP25Sync(buffer + 2U);
// Add the NID
m_nid.encode(buffer + 2U, P25_DUID_TERM);
// Add busy bits
if (m_remoteGateway)
addBusyBits(buffer + 2U, P25_TERM_FRAME_LENGTH_BITS, true, false);
else
addBusyBits(buffer + 2U, P25_TERM_FRAME_LENGTH_BITS, false, true);
writeQueueNet(buffer, P25_TERM_FRAME_LENGTH_BYTES + 2U);
LogMessage("P25, network end of transmission, %.1f seconds, %u%% packet loss", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames);
m_display->clearP25();
m_netTimeout.stop();
m_networkWatchdog.stop();
m_netData.reset();
m_netState = RS_NET_IDLE;
}
bool CP25Control::openFile()
{
if (m_fp != NULL)
return true;
time_t t;
::time(&t);
struct tm* tm = ::localtime(&t);
char name[100U];
::sprintf(name, "P25_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
m_fp = ::fopen(name, "wb");
if (m_fp == NULL)
return false;
::fwrite("P25", 1U, 3U, m_fp);
return true;
}
bool CP25Control::writeFile(const unsigned char* data, unsigned char length)
{
if (m_fp == NULL)
return false;
::fwrite(&length, 1U, 1U, m_fp);
::fwrite(data, 1U, length, m_fp);
return true;
}
void CP25Control::closeFile()
{
if (m_fp != NULL) {
::fclose(m_fp);
m_fp = NULL;
}
}

@ -0,0 +1,112 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(P25Control_H)
#define P25Control_H
#include "RSSIInterpolator.h"
#include "P25LowSpeedData.h"
#include "RingBuffer.h"
#include "P25Network.h"
#include "DMRLookup.h"
#include "P25Audio.h"
#include "Defines.h"
#include "Display.h"
#include "P25Data.h"
#include "P25NID.h"
#include "Modem.h"
#include "Timer.h"
#include <cstdio>
class CP25Control {
public:
CP25Control(unsigned int nac, unsigned int id, bool selfOly, bool uidOverride, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup, bool remoteGateway, CRSSIInterpolator* rssiMapper);
~CP25Control();
bool writeModem(unsigned char* data, unsigned int len);
unsigned int readModem(unsigned char* data);
void clock(unsigned int ms);
private:
unsigned int m_nac;
unsigned int m_id;
bool m_selfOnly;
bool m_uidOverride;
bool m_remoteGateway;
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;
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);
void writeNetwork(const unsigned char *data, unsigned char type, bool end);
void writeNetwork();
void addBusyBits(unsigned char* data, unsigned int length, bool b1, bool b2);
void checkNetLDU1();
void checkNetLDU2();
void insertMissingAudio(unsigned char* data);
void createRFHeader();
void createNetHeader();
void createNetLDU1();
void createNetLDU2();
void createNetTerminator();
bool openFile();
bool writeFile(const unsigned char* data, unsigned char length);
void closeFile();
};
#endif

@ -0,0 +1,346 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "P25Data.h"
#include "P25Defines.h"
#include "P25Utils.h"
#include "Hamming.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned char DUMMY_HEADER[] = {
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x08U, 0xDCU, 0x60U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U, 0x93U, 0xE7U, 0x73U, 0x77U, 0x57U, 0xD6U, 0xD3U, 0xCFU, 0x77U,
0xEEU, 0x82U, 0x93U, 0xE2U, 0x2FU, 0xF3U, 0xD5U, 0xF5U, 0xBEU, 0xBCU, 0x54U, 0x0DU, 0x9CU, 0x29U, 0x3EU, 0x46U,
0xE3U, 0x28U, 0xB0U, 0xB7U, 0x73U, 0x76U, 0x1EU, 0x26U, 0x0CU, 0x75U, 0x5BU, 0xF7U, 0x4DU, 0x5FU, 0x5AU, 0x37U,
0x18U};
const unsigned char DUMMY_LDU2[] = {
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x80U, 0x00U, 0x00U, 0xACU, 0xB8U, 0xA4U, 0x9BU,
0xDCU, 0x75U
};
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#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])
CP25Data::CP25Data() :
m_mi(NULL),
m_mfId(0U),
m_algId(0x80U),
m_kId(0U),
m_lcf(0x00U),
m_emergency(false),
m_srcId(0U),
m_dstId(0U),
m_rs241213()
{
m_mi = new unsigned char[P25_MI_LENGTH_BYTES];
}
CP25Data::~CP25Data()
{
delete[] m_mi;
}
void CP25Data::encodeHeader(unsigned char* data)
{
assert(data != NULL);
CP25Utils::encode(DUMMY_HEADER, data, 114U, 780U);
}
bool CP25Data::decodeLDU1(const unsigned char* data)
{
assert(data != NULL);
unsigned char rs[18U];
unsigned char raw[5U];
CP25Utils::decode(data, raw, 410U, 452U);
decodeLDUHamming(raw, rs + 0U);
CP25Utils::decode(data, raw, 600U, 640U);
decodeLDUHamming(raw, rs + 3U);
CP25Utils::decode(data, raw, 788U, 830U);
decodeLDUHamming(raw, rs + 6U);
CP25Utils::decode(data, raw, 978U, 1020U);
decodeLDUHamming(raw, rs + 9U);
CP25Utils::decode(data, raw, 1168U, 1208U);
decodeLDUHamming(raw, rs + 12U);
CP25Utils::decode(data, raw, 1356U, 1398U);
decodeLDUHamming(raw, rs + 15U);
try {
bool ret = m_rs241213.decode(rs);
if (!ret)
return false;
} catch (...) {
CUtils::dump(2U, "P25, RS carshed with input data", rs, 18U);
return false;
}
unsigned int srcId = (rs[6U] << 16) + (rs[7U] << 8) + rs[8U];
switch (rs[0U]) {
case P25_LCF_GROUP:
m_emergency = (rs[2U] & 0x80U) == 0x80U;
m_dstId = (rs[4U] << 8) + rs[5U];
m_srcId = srcId;
break;
case P25_LCF_PRIVATE:
m_emergency = false;
m_dstId = (rs[3U] << 16) + (rs[4U] << 8) + rs[5U];
m_srcId = srcId;
break;
default:
return false;
}
m_lcf = rs[0U];
m_mfId = rs[1U];
return true;
}
void CP25Data::encodeLDU1(unsigned char* data)
{
assert(data != NULL);
unsigned char rs[18U];
::memset(rs, 0x00U, 18U);
rs[0U] = m_lcf;
rs[1U] = m_mfId;
switch (m_lcf) {
case P25_LCF_GROUP:
rs[2U] = m_emergency ? 0x80U : 0x00U;
rs[4U] = (m_dstId >> 8) & 0xFFU;
rs[5U] = (m_dstId >> 0) & 0xFFU;
rs[6U] = (m_srcId >> 16) & 0xFFU;
rs[7U] = (m_srcId >> 8) & 0xFFU;
rs[8U] = (m_srcId >> 0) & 0xFFU;
break;
case P25_LCF_PRIVATE:
rs[3U] = (m_dstId >> 16) & 0xFFU;
rs[4U] = (m_dstId >> 8) & 0xFFU;
rs[5U] = (m_dstId >> 0) & 0xFFU;
rs[6U] = (m_srcId >> 16) & 0xFFU;
rs[7U] = (m_srcId >> 8) & 0xFFU;
rs[8U] = (m_srcId >> 0) & 0xFFU;
break;
default:
LogMessage("P25, unknown LCF value in LDU1 - $%02X", m_lcf);
break;
}
m_rs241213.encode(rs);
unsigned char raw[5U];
encodeLDUHamming(raw, rs + 0U);
CP25Utils::encode(raw, data, 410U, 452U);
encodeLDUHamming(raw, rs + 3U);
CP25Utils::encode(raw, data, 600U, 640U);
encodeLDUHamming(raw, rs + 6U);
CP25Utils::encode(raw, data, 788U, 830U);
encodeLDUHamming(raw, rs + 9U);
CP25Utils::encode(raw, data, 978U, 1020U);
encodeLDUHamming(raw, rs + 12U);
CP25Utils::encode(raw, data, 1168U, 1208U);
encodeLDUHamming(raw, rs + 15U);
CP25Utils::encode(raw, data, 1356U, 1398U);
}
void CP25Data::encodeLDU2(unsigned char* data)
{
assert(data != NULL);
unsigned char raw[5U];
encodeLDUHamming(raw, DUMMY_LDU2 + 0U);
CP25Utils::encode(raw, data, 410U, 452U);
encodeLDUHamming(raw, DUMMY_LDU2 + 3U);
CP25Utils::encode(raw, data, 600U, 640U);
encodeLDUHamming(raw, DUMMY_LDU2 + 6U);
CP25Utils::encode(raw, data, 788U, 830U);
encodeLDUHamming(raw, DUMMY_LDU2 + 9U);
CP25Utils::encode(raw, data, 978U, 1020U);
encodeLDUHamming(raw, DUMMY_LDU2 + 12U);
CP25Utils::encode(raw, data, 1168U, 1208U);
encodeLDUHamming(raw, DUMMY_LDU2 + 15U);
CP25Utils::encode(raw, data, 1356U, 1398U);
}
void CP25Data::setMI(const unsigned char* mi)
{
assert(mi != NULL);
::memcpy(m_mi, mi, P25_MI_LENGTH_BYTES);
}
void CP25Data::getMI(unsigned char* mi) const
{
assert(mi != NULL);
::memcpy(mi, m_mi, P25_MI_LENGTH_BYTES);
}
void CP25Data::setMFId(unsigned char id)
{
m_mfId = id;
}
unsigned char CP25Data::getMFId() const
{
return m_mfId;
}
void CP25Data::setAlgId(unsigned char id)
{
m_algId = id;
}
unsigned char CP25Data::getAlgId() const
{
return m_algId;
}
void CP25Data::setKId(unsigned int id)
{
m_kId = id;
}
unsigned int CP25Data::getKId() const
{
return m_kId;
}
void CP25Data::setSrcId(unsigned int id)
{
m_srcId = id;
}
unsigned int CP25Data::getSrcId() const
{
return m_srcId;
}
void CP25Data::setEmergency(bool on)
{
m_emergency = on;
}
bool CP25Data::getEmergency() const
{
return m_emergency;
}
void CP25Data::setLCF(unsigned char lcf)
{
m_lcf = lcf;
}
unsigned char CP25Data::getLCF() const
{
return m_lcf;
}
void CP25Data::setDstId(unsigned int id)
{
m_dstId = id;
}
unsigned int CP25Data::getDstId() const
{
return m_dstId;
}
void CP25Data::reset()
{
::memset(m_mi, 0x00U, P25_MI_LENGTH_BYTES);
m_algId = 0x80U;
m_kId = 0x0000U;
m_lcf = P25_LCF_GROUP;
m_mfId = 0x00U;
m_srcId = 0U;
m_dstId = 0U;
m_emergency = false;
}
void CP25Data::decodeLDUHamming(const unsigned char* data, unsigned char* raw)
{
unsigned int n = 0U;
unsigned int m = 0U;
for (unsigned int i = 0U; i < 4U; i++) {
bool hamming[10U];
for (unsigned int j = 0U; j < 10U; j++) {
hamming[j] = READ_BIT(data, n);
n++;
}
CHamming::decode1063(hamming);
for (unsigned int j = 0U; j < 6U; j++) {
WRITE_BIT(raw, m, hamming[j]);
m++;
}
}
}
void CP25Data::encodeLDUHamming(unsigned char* data, const unsigned char* raw)
{
unsigned int n = 0U;
unsigned int m = 0U;
for (unsigned int i = 0U; i < 4U; i++) {
bool hamming[10U];
for (unsigned int j = 0U; j < 6U; j++) {
hamming[j] = READ_BIT(raw, m);
m++;
}
CHamming::encode1063(hamming);
for (unsigned int j = 0U; j < 10U; j++) {
WRITE_BIT(data, n, hamming[j]);
n++;
}
}
}

@ -0,0 +1,77 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(P25Data_H)
#define P25Data_H
#include "RS241213.h"
class CP25Data {
public:
CP25Data();
~CP25Data();
void encodeHeader(unsigned char* data);
bool decodeLDU1(const unsigned char* data);
void encodeLDU1(unsigned char* data);
void encodeLDU2(unsigned char* data);
void setMI(const unsigned char* mi);
void getMI(unsigned char* mi) const;
void setMFId(unsigned char id);
unsigned char getMFId() const;
void setAlgId(unsigned char id);
unsigned char getAlgId() const;
void setKId(unsigned int id);
unsigned int getKId() const;
void setEmergency(bool on);
bool getEmergency() const;
void setSrcId(unsigned int Id);
unsigned int getSrcId() const;
void setLCF(unsigned char lcf);
unsigned char getLCF() const;
void setDstId(unsigned int id);
unsigned int getDstId() const;
void reset();
private:
unsigned char* m_mi;
unsigned char m_mfId;
unsigned char m_algId;
unsigned int m_kId;
unsigned char m_lcf;
bool m_emergency;
unsigned int m_srcId;
unsigned int m_dstId;
CRS241213 m_rs241213;
void decodeLDUHamming(const unsigned char* raw, unsigned char* data);
void encodeLDUHamming(unsigned char* data, const unsigned char* raw);
};
#endif

@ -0,0 +1,60 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(P25DEFINES_H)
#define P25DEFINES_H
const unsigned int P25_HDR_FRAME_LENGTH_BYTES = 99U;
const unsigned int P25_HDR_FRAME_LENGTH_BITS = P25_HDR_FRAME_LENGTH_BYTES * 8U;
const unsigned int P25_LDU_FRAME_LENGTH_BYTES = 216U;
const unsigned int P25_LDU_FRAME_LENGTH_BITS = P25_LDU_FRAME_LENGTH_BYTES * 8U;
const unsigned int P25_TERM_FRAME_LENGTH_BYTES = 18U;
const unsigned int P25_TERM_FRAME_LENGTH_BITS = P25_TERM_FRAME_LENGTH_BYTES * 8U;
const unsigned int P25_TERMLC_FRAME_LENGTH_BYTES = 54U;
const unsigned int P25_TERMLC_FRAME_LENGTH_BITS = P25_TERMLC_FRAME_LENGTH_BYTES * 8U;
const unsigned int P25_SYNC_LENGTH_BYTES = 6U;
const unsigned int P25_NID_LENGTH_BYTES = 8U;
const unsigned int P25_NID_LENGTH_BITS = P25_NID_LENGTH_BYTES * 8U;
const unsigned char P25_SYNC_BYTES[] = {0x55U, 0x75U, 0xF5U, 0xFFU, 0x77U, 0xFFU};
const unsigned char P25_SYNC_BYTES_LENGTH = 6U;
const unsigned int P25_MI_LENGTH_BYTES = 9U;
const unsigned char P25_LCF_GROUP = 0x00U;
const unsigned char P25_LCF_PRIVATE = 0x03U;
const unsigned int P25_SS0_START = 70U;
const unsigned int P25_SS1_START = 71U;
const unsigned int P25_SS_INCREMENT = 72U;
const unsigned char P25_DUID_HEADER = 0x00U;
const unsigned char P25_DUID_TERM = 0x03U;
const unsigned char P25_DUID_LDU1 = 0x05U;
const unsigned char P25_DUID_LDU2 = 0x0AU;
const unsigned char P25_DUID_PDU = 0x0CU;
const unsigned char P25_DUID_TERM_LC = 0x0FU;
const unsigned char P25_NULL_IMBE[] = {0x04U, 0x0CU, 0xFDU, 0x7BU, 0xFBU, 0x7DU, 0xF2U, 0x7BU, 0x3DU, 0x9EU, 0x45U};
#endif

@ -0,0 +1,130 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "P25LowSpeedData.h"
#include "P25Utils.h"
#include <cstdio>
#include <cassert>
const unsigned char CCS_PARITY[] = {
0x00U, 0x39U, 0x72U, 0x4BU, 0xE4U, 0xDDU, 0x96U, 0xAFU, 0xF1U, 0xC8U, 0x83U, 0xBAU, 0x15U, 0x2CU, 0x67U, 0x5EU,
0xDBU, 0xE2U, 0xA9U, 0x90U, 0x3FU, 0x06U, 0x4DU, 0x74U, 0x2AU, 0x13U, 0x58U, 0x61U, 0xCEU, 0xF7U, 0xBCU, 0x85U,
0x8FU, 0xB6U, 0xFDU, 0xC4U, 0x6BU, 0x52U, 0x19U, 0x20U, 0x7EU, 0x47U, 0x0CU, 0x35U, 0x9AU, 0xA3U, 0xE8U, 0xD1U,
0x54U, 0x6DU, 0x26U, 0x1FU, 0xB0U, 0x89U, 0xC2U, 0xFBU, 0xA5U, 0x9CU, 0xD7U, 0xEEU, 0x41U, 0x78U, 0x33U, 0x0AU,
0x27U, 0x1EU, 0x55U, 0x6CU, 0xC3U, 0xFAU, 0xB1U, 0x88U, 0xD6U, 0xEFU, 0xA4U, 0x9DU, 0x32U, 0x0BU, 0x40U, 0x79U,
0xFCU, 0xC5U, 0x8EU, 0xB7U, 0x18U, 0x21U, 0x6AU, 0x53U, 0x0DU, 0x34U, 0x7FU, 0xE9U, 0xD0U, 0x9BU, 0xA2U, 0xA8U,
0x91U, 0xDAU, 0xE3U, 0x4CU, 0x75U, 0x3EU, 0x07U, 0x59U, 0x60U, 0x2BU, 0x12U, 0xBDU, 0x84U, 0xCFU, 0xF6U, 0x73U,
0x4AU, 0x01U, 0x38U, 0x97U, 0xAEU, 0xE5U, 0xDCU, 0x82U, 0xBBU, 0xF0U, 0xC9U, 0x66U, 0x5FU, 0x14U, 0x2DU, 0x4EU,
0x77U, 0x3CU, 0x05U, 0xAAU, 0x93U, 0xD8U, 0xE1U, 0xBFU, 0x86U, 0xCDU, 0xF4U, 0x5BU, 0x62U, 0x29U, 0x10U, 0x95U,
0xACU, 0xE7U, 0xDEU, 0x71U, 0x48U, 0x03U, 0x3AU, 0x64U, 0x5DU, 0x16U, 0x2FU, 0x80U, 0xB9U, 0xF2U, 0xCBU, 0xC1U,
0xF8U, 0xB3U, 0x8AU, 0x25U, 0x1CU, 0x57U, 0x6EU, 0x30U, 0x09U, 0x42U, 0x7BU, 0xD4U, 0xEDU, 0xA6U, 0x9FU, 0x1AU,
0x23U, 0x68U, 0x51U, 0xFEU, 0xC7U, 0x8CU, 0xB5U, 0xEBU, 0xD2U, 0x99U, 0xA0U, 0x0FU, 0x36U, 0x7DU, 0x44U, 0x69U,
0x50U, 0x1BU, 0x22U, 0x8DU, 0xB4U, 0xFFU, 0xC6U, 0x98U, 0xA1U, 0xEAU, 0xD3U, 0x7CU, 0x45U, 0x0EU, 0x37U, 0xB2U,
0x8BU, 0xC0U, 0xF9U, 0x56U, 0x6FU, 0x24U, 0x1DU, 0x43U, 0x7AU, 0x31U, 0x08U, 0xA7U, 0x9EU, 0xD5U, 0xECU, 0xE6U,
0xDFU, 0x94U, 0xADU, 0x02U, 0x3BU, 0x70U, 0x49U, 0x17U, 0x2EU, 0x65U, 0x5CU, 0xF3U, 0xCAU, 0x81U, 0xB8U, 0x3DU,
0x04U, 0x4FU, 0x76U, 0xD9U, 0xE0U, 0xABU, 0x92U, 0xCCU, 0xF5U, 0xBEU, 0x87U, 0x28U, 0x11U, 0x5AU, 0x63U};
const unsigned int MAX_CCS_ERRS = 4U;
CP25LowSpeedData::CP25LowSpeedData() :
m_lsd1(0x00U),
m_lsd2(0x00U)
{
}
CP25LowSpeedData::~CP25LowSpeedData()
{
}
void CP25LowSpeedData::process(unsigned char* data)
{
assert(data != NULL);
unsigned char lsd[4U];
CP25Utils::decode(data, lsd, 1546U, 1578U);
for (unsigned int a = 0x00U; a < 0x100U; a++) {
unsigned char ccs[2U];
ccs[0U] = a;
ccs[1U] = encode(ccs[0U]);
unsigned int errs = CP25Utils::compare(ccs, lsd + 0U, 2U);
if (errs < MAX_CCS_ERRS) {
lsd[0U] = ccs[0U];
lsd[1U] = ccs[1U];
break;
}
}
for (unsigned int a = 0x00U; a < 0x100U; a++) {
unsigned char ccs[2U];
ccs[0U] = a;
ccs[1U] = encode(ccs[0U]);
unsigned int errs = CP25Utils::compare(ccs, lsd + 2U, 2U);
if (errs < MAX_CCS_ERRS) {
lsd[2U] = ccs[0U];
lsd[3U] = ccs[1U];
break;
}
}
m_lsd1 = lsd[0U];
m_lsd2 = lsd[2U];
CP25Utils::encode(lsd, data, 1546U, 1578U);
}
void CP25LowSpeedData::encode(unsigned char* data) const
{
assert(data != NULL);
unsigned char lsd[4U];
lsd[0U] = m_lsd1;
lsd[1U] = encode(m_lsd1);
lsd[2U] = m_lsd2;
lsd[3U] = encode(m_lsd2);
CP25Utils::encode(lsd, data, 1546U, 1578U);
}
unsigned char CP25LowSpeedData::getLSD1() const
{
return m_lsd1;
}
void CP25LowSpeedData::setLSD1(unsigned char lsd1)
{
m_lsd1 = lsd1;
}
unsigned char CP25LowSpeedData::getLSD2() const
{
return m_lsd2;
}
void CP25LowSpeedData::setLSD2(unsigned char lsd2)
{
m_lsd2 = lsd2;
}
unsigned char CP25LowSpeedData::encode(unsigned char in) const
{
return CCS_PARITY[in];
}

@ -0,0 +1,44 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(P25LowSpeedData_H)
#define P25LowSpeedData_H
class CP25LowSpeedData {
public:
CP25LowSpeedData();
~CP25LowSpeedData();
void process(unsigned char* data);
void encode(unsigned char* data) const;
unsigned char getLSD1() const;
void setLSD1(unsigned char lsd1);
unsigned char getLSD2() const;
void setLSD2(unsigned char lsd2);
private:
unsigned char m_lsd1;
unsigned char m_lsd2;
unsigned char encode(const unsigned char in) const;
};
#endif

@ -0,0 +1,152 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "P25NID.h"
#include "P25Defines.h"
#include "P25Utils.h"
#include "BCH.h"
#include <cstdio>
#include <cassert>
const unsigned int MAX_NID_ERRS = 5U;
CP25NID::CP25NID(unsigned int nac) :
m_duid(0U),
m_hdr(NULL),
m_ldu1(NULL),
m_ldu2(NULL),
m_termlc(NULL),
m_term(NULL)
{
CBCH bch;
m_hdr = new unsigned char[P25_NID_LENGTH_BYTES];
m_hdr[0U] = (nac >> 4) & 0xFFU;
m_hdr[1U] = (nac << 4) & 0xF0U;
m_hdr[1U] |= P25_DUID_HEADER;
bch.encode(m_hdr);
m_hdr[7U] &= 0xFEU; // Clear the parity bit
m_ldu1 = new unsigned char[P25_NID_LENGTH_BYTES];
m_ldu1[0U] = (nac >> 4) & 0xFFU;
m_ldu1[1U] = (nac << 4) & 0xF0U;
m_ldu1[1U] |= P25_DUID_LDU1;
bch.encode(m_ldu1);
m_ldu1[7U] |= 0x01U; // Set the parity bit
m_ldu2 = new unsigned char[P25_NID_LENGTH_BYTES];
m_ldu2[0U] = (nac >> 4) & 0xFFU;
m_ldu2[1U] = (nac << 4) & 0xF0U;
m_ldu2[1U] |= P25_DUID_LDU2;
bch.encode(m_ldu2);
m_ldu2[7U] |= 0x01U; // Set the parity bit
m_termlc = new unsigned char[P25_NID_LENGTH_BYTES];
m_termlc[0U] = (nac >> 4) & 0xFFU;
m_termlc[1U] = (nac << 4) & 0xF0U;
m_termlc[1U] |= P25_DUID_TERM_LC;
bch.encode(m_termlc);
m_termlc[7U] &= 0xFEU; // Clear the parity bit
m_term = new unsigned char[P25_NID_LENGTH_BYTES];
m_term[0U] = (nac >> 4) & 0xFFU;
m_term[1U] = (nac << 4) & 0xF0U;
m_term[1U] |= P25_DUID_TERM;
bch.encode(m_term);
m_term[7U] &= 0xFEU; // Clear the parity bit
}
CP25NID::~CP25NID()
{
delete[] m_hdr;
delete[] m_ldu1;
delete[] m_ldu2;
delete[] m_termlc;
delete[] m_term;
}
bool CP25NID::decode(const unsigned char* data)
{
assert(data != NULL);
unsigned char nid[P25_NID_LENGTH_BYTES];
CP25Utils::decode(data, nid, 48U, 114U);
unsigned int errs = CP25Utils::compare(nid, m_ldu1, P25_NID_LENGTH_BYTES);
if (errs < MAX_NID_ERRS) {
m_duid = P25_DUID_LDU1;
return true;
}
errs = CP25Utils::compare(nid, m_ldu2, P25_NID_LENGTH_BYTES);
if (errs < MAX_NID_ERRS) {
m_duid = P25_DUID_LDU2;
return true;
}
errs = CP25Utils::compare(nid, m_term, P25_NID_LENGTH_BYTES);
if (errs < MAX_NID_ERRS) {
m_duid = P25_DUID_TERM;
return true;
}
errs = CP25Utils::compare(nid, m_termlc, P25_NID_LENGTH_BYTES);
if (errs < MAX_NID_ERRS) {
m_duid = P25_DUID_TERM_LC;
return true;
}
errs = CP25Utils::compare(nid, m_hdr, P25_NID_LENGTH_BYTES);
if (errs < MAX_NID_ERRS) {
m_duid = P25_DUID_HEADER;
return true;
}
return false;
}
void CP25NID::encode(unsigned char* data, unsigned char duid) const
{
assert(data != NULL);
switch (duid) {
case P25_DUID_HEADER:
CP25Utils::encode(m_hdr, data, 48U, 114U);
break;
case P25_DUID_LDU1:
CP25Utils::encode(m_ldu1, data, 48U, 114U);
break;
case P25_DUID_LDU2:
CP25Utils::encode(m_ldu2, data, 48U, 114U);
break;
case P25_DUID_TERM:
CP25Utils::encode(m_term, data, 48U, 114U);
break;
case P25_DUID_TERM_LC:
CP25Utils::encode(m_termlc, data, 48U, 114U);
break;
default:
break;
}
}
unsigned char CP25NID::getDUID() const
{
return m_duid;
}

@ -0,0 +1,42 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(P25NID_H)
#define P25NID_H
class CP25NID {
public:
CP25NID(unsigned int nac);
~CP25NID();
bool decode(const unsigned char* data);
unsigned char getDUID() const;
void encode(unsigned char* data, unsigned char duid) const;
private:
unsigned char m_duid;
unsigned char* m_hdr;
unsigned char* m_ldu1;
unsigned char* m_ldu2;
unsigned char* m_termlc;
unsigned char* m_term;
};
#endif

@ -0,0 +1,435 @@
/*
* Copyright (C) 2009-2014,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "P25Network.h"
#include "P25Defines.h"
#include "Defines.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned char REC62[] = {
0x62U, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x12U, 0x64U, 0x00U, 0x00U, 0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const unsigned char REC63[] = {
0x63U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC64[] = {
0x64U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC65[] = {
0x65U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC66[] = {
0x66U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC67[] = {
0x67U, 0xF0U, 0x9DU, 0x6AU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC68[] = {
0x68U, 0x19U, 0xD4U, 0x26U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC69[] = {
0x69U, 0xE0U, 0xEBU, 0x7BU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC6A[] = {
0x6AU, 0x00U, 0x00U, 0x02U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const unsigned char REC6B[] = {
0x6BU, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x12U, 0x64U, 0x00U, 0x00U, 0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const unsigned char REC6C[] = {
0x6CU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC6D[] = {
0x6DU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC6E[] = {
0x6EU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC6F[] = {
0x6FU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC70[] = {
0x70U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC71[] = {
0x71U, 0xACU, 0xB8U, 0xA4U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC72[] = {
0x72U, 0x9BU, 0xDCU, 0x75U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
const unsigned char REC73[] = {
0x73U, 0x00U, 0x00U, 0x02U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const unsigned char REC80[] = {
0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const unsigned int BUFFER_LENGTH = 100U;
CP25Network::CP25Network(const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int localPort, bool debug) :
m_socket(localPort),
m_address(),
m_port(gatewayPort),
m_debug(debug),
m_enabled(false),
m_buffer(1000U, "P25 Network"),
m_audio()
{
m_address = CUDPSocket::lookup(gatewayAddress);
}
CP25Network::~CP25Network()
{
}
bool CP25Network::open()
{
LogMessage("Opening P25 network connection");
if (m_address.s_addr == INADDR_NONE)
return false;
return m_socket.open();
}
bool CP25Network::writeLDU1(const unsigned char* ldu1, const CP25Data& control, const CP25LowSpeedData& lsd, bool end)
{
assert(ldu1 != NULL);
unsigned char buffer[22U];
// The '62' record
::memcpy(buffer, REC62, 22U);
m_audio.decode(ldu1, buffer + 10U, 0U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 22U);
bool ret = m_socket.write(buffer, 22U, m_address, m_port);
if (!ret)
return false;
// The '63' record
::memcpy(buffer, REC63, 14U);
m_audio.decode(ldu1, buffer + 1U, 1U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 14U);
ret = m_socket.write(buffer, 14U, m_address, m_port);
if (!ret)
return false;
// The '64' record
::memcpy(buffer, REC64, 17U);
buffer[1U] = control.getLCF();
buffer[2U] = control.getMFId();
m_audio.decode(ldu1, buffer + 5U, 2U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '65' record
::memcpy(buffer, REC65, 17U);
unsigned int id = control.getDstId();
buffer[1U] = (id >> 16) & 0xFFU;
buffer[2U] = (id >> 8) & 0xFFU;
buffer[3U] = (id >> 0) & 0xFFU;
m_audio.decode(ldu1, buffer + 5U, 3U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '66' record
::memcpy(buffer, REC66, 17U);
id = control.getSrcId();
buffer[1U] = (id >> 16) & 0xFFU;
buffer[2U] = (id >> 8) & 0xFFU;
buffer[3U] = (id >> 0) & 0xFFU;
m_audio.decode(ldu1, buffer + 5U, 4U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '67' record
::memcpy(buffer, REC67, 17U);
m_audio.decode(ldu1, buffer + 5U, 5U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '68' record
::memcpy(buffer, REC68, 17U);
m_audio.decode(ldu1, buffer + 5U, 6U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '69' record
::memcpy(buffer, REC69, 17U);
m_audio.decode(ldu1, buffer + 5U, 7U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '6A' record
::memcpy(buffer, REC6A, 16U);
buffer[1U] = lsd.getLSD1();
buffer[2U] = lsd.getLSD2();
m_audio.decode(ldu1, buffer + 4U, 8U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU1 Sent", buffer, 16U);
ret = m_socket.write(buffer, 16U, m_address, m_port);
if (!ret)
return false;
if (end) {
if (m_debug)
CUtils::dump(1U, "P25 Network END Sent", REC80, 17U);
ret = m_socket.write(REC80, 17U, m_address, m_port);
if (!ret)
return false;
}
return true;
}
bool CP25Network::writeLDU2(const unsigned char* ldu2, const CP25Data& control, const CP25LowSpeedData& lsd, bool end)
{
assert(ldu2 != NULL);
unsigned char buffer[22U];
// The '6B' record
::memcpy(buffer, REC6B, 22U);
m_audio.decode(ldu2, buffer + 10U, 0U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 22U);
bool ret = m_socket.write(buffer, 22U, m_address, m_port);
if (!ret)
return false;
// The '6C' record
::memcpy(buffer, REC6C, 14U);
m_audio.decode(ldu2, buffer + 1U, 1U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 14U);
ret = m_socket.write(buffer, 14U, m_address, m_port);
if (!ret)
return false;
unsigned char mi[P25_MI_LENGTH_BYTES];
control.getMI(mi);
// The '6D' record
::memcpy(buffer, REC6D, 17U);
buffer[1U] = mi[0U];
buffer[2U] = mi[1U];
buffer[3U] = mi[2U];
m_audio.decode(ldu2, buffer + 5U, 2U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '6E' record
::memcpy(buffer, REC6E, 17U);
buffer[1U] = mi[3U];
buffer[2U] = mi[4U];
buffer[3U] = mi[5U];
m_audio.decode(ldu2, buffer + 5U, 3U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '6F' record
::memcpy(buffer, REC6F, 17U);
buffer[1U] = mi[6U];
buffer[2U] = mi[7U];
buffer[3U] = mi[8U];
m_audio.decode(ldu2, buffer + 5U, 4U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '70' record
::memcpy(buffer, REC70, 17U);
buffer[1U] = control.getAlgId();
unsigned int id = control.getKId();
buffer[2U] = (id >> 8) & 0xFFU;
buffer[3U] = (id >> 0) & 0xFFU;
m_audio.decode(ldu2, buffer + 5U, 5U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '71' record
::memcpy(buffer, REC71, 17U);
m_audio.decode(ldu2, buffer + 5U, 6U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '72' record
::memcpy(buffer, REC72, 17U);
m_audio.decode(ldu2, buffer + 5U, 7U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 17U);
ret = m_socket.write(buffer, 17U, m_address, m_port);
if (!ret)
return false;
// The '73' record
::memcpy(buffer, REC73, 16U);
buffer[1U] = lsd.getLSD1();
buffer[2U] = lsd.getLSD2();
m_audio.decode(ldu2, buffer + 4U, 8U);
if (m_debug)
CUtils::dump(1U, "P25 Network LDU2 Sent", buffer, 16U);
ret = m_socket.write(buffer, 16U, m_address, m_port);
if (!ret)
return false;
if (end) {
if (m_debug)
CUtils::dump(1U, "P25 Network END Sent", REC80, 17U);
ret = m_socket.write(REC80, 17U, m_address, m_port);
if (!ret)
return false;
}
return true;
}
void CP25Network::clock(unsigned int ms)
{
unsigned char buffer[BUFFER_LENGTH];
in_addr address;
unsigned int port;
int length = m_socket.read(buffer, BUFFER_LENGTH, address, port);
if (length <= 0)
return;
// Check if the data is for us
if (m_address.s_addr != address.s_addr || m_port != port) {
LogMessage("P25 packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port);
return;
}
if (!m_enabled)
return;
if (m_debug)
CUtils::dump(1U, "P25 Network Data Received", buffer, length);
unsigned char c = length;
m_buffer.addData(&c, 1U);
m_buffer.addData(buffer, length);
}
unsigned int CP25Network::read(unsigned char* data, unsigned int length)
{
assert(data != NULL);
if (m_buffer.isEmpty())
return 0U;
unsigned char c = 0U;
m_buffer.getData(&c, 1U);
assert(c <= length);
m_buffer.getData(data, c);
return c;
}
void CP25Network::close()
{
m_socket.close();
LogMessage("Closing P25 network connection");
}
void CP25Network::enable(bool enabled)
{
m_enabled = enabled;
}

@ -0,0 +1,60 @@
/*
* Copyright (C) 2009-2014,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef P25Network_H
#define P25Network_H
#include "P25LowSpeedData.h"
#include "RingBuffer.h"
#include "UDPSocket.h"
#include "P25Audio.h"
#include "P25Data.h"
#include <cstdint>
#include <string>
class CP25Network {
public:
CP25Network(const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int localPort, bool debug);
~CP25Network();
bool open();
void enable(bool enabled);
bool writeLDU1(const unsigned char* ldu1, const CP25Data& control, const CP25LowSpeedData& lsd, bool end);
bool writeLDU2(const unsigned char* ldu2, const CP25Data& control, const CP25LowSpeedData& lsd, bool end);
unsigned int read(unsigned char* data, unsigned int length);
void close();
void clock(unsigned int ms);
private:
CUDPSocket m_socket;
in_addr m_address;
unsigned int m_port;
bool m_debug;
bool m_enabled;
CRingBuffer<unsigned char> m_buffer;
CP25Audio m_audio;
};
#endif

@ -0,0 +1,101 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "P25Utils.h"
#include "P25Defines.h"
#include <cstdio>
#include <cassert>
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#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])
void CP25Utils::decode(const unsigned char* in, unsigned char* out, unsigned int start, unsigned int stop)
{
assert(in != NULL);
assert(out != NULL);
// Move the SSx positions to the range needed
unsigned int ss0Pos = P25_SS0_START;
unsigned int ss1Pos = P25_SS1_START;
while (ss0Pos < start) {
ss0Pos += P25_SS_INCREMENT;
ss1Pos += P25_SS_INCREMENT;
}
unsigned int n = 0U;
for (unsigned int i = start; i < stop; i++) {
if (i == ss0Pos) {
ss0Pos += P25_SS_INCREMENT;
} else if (i == ss1Pos) {
ss1Pos += P25_SS_INCREMENT;
} else {
bool b = READ_BIT(in, i);
WRITE_BIT(out, n, b);
n++;
}
}
}
void CP25Utils::encode(const unsigned char* in, unsigned char* out, unsigned int start, unsigned int stop)
{
assert(in != NULL);
assert(out != NULL);
// Move the SSx positions to the range needed
unsigned int ss0Pos = P25_SS0_START;
unsigned int ss1Pos = P25_SS1_START;
while (ss0Pos < start) {
ss0Pos += P25_SS_INCREMENT;
ss1Pos += P25_SS_INCREMENT;
}
unsigned int n = 0U;
for (unsigned int i = start; i < stop; i++) {
if (i == ss0Pos) {
ss0Pos += P25_SS_INCREMENT;
} else if (i == ss1Pos) {
ss1Pos += P25_SS_INCREMENT;
} else {
bool b = READ_BIT(in, n);
WRITE_BIT(out, i, b);
n++;
}
}
}
unsigned int CP25Utils::compare(const unsigned char* data1, const unsigned char* data2, unsigned int length)
{
assert(data1 != NULL);
assert(data2 != NULL);
unsigned int errs = 0U;
for (unsigned int i = 0U; i < length; i++) {
unsigned char v = data1[i] ^ data2[i];
while (v != 0U) {
v &= v - 1U;
errs++;
}
}
return errs;
}

@ -0,0 +1,33 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(P25Utils_H)
#define P25Utils_H
class CP25Utils {
public:
static void encode(const unsigned char* in, unsigned char* out, unsigned int start, unsigned int stop);
static void decode(const unsigned char* in, unsigned char* out, unsigned int start, unsigned int stop);
static unsigned int compare(const unsigned char* data1, const unsigned char* data2, unsigned int length);
private:
};
#endif

@ -0,0 +1,115 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "QR1676.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
const unsigned int ENCODING_TABLE_1676[] =
{0x0000U, 0x0273U, 0x04E5U, 0x0696U, 0x09C9U, 0x0BBAU, 0x0D2CU, 0x0F5FU, 0x11E2U, 0x1391U, 0x1507U, 0x1774U,
0x182BU, 0x1A58U, 0x1CCEU, 0x1EBDU, 0x21B7U, 0x23C4U, 0x2552U, 0x2721U, 0x287EU, 0x2A0DU, 0x2C9BU, 0x2EE8U,
0x3055U, 0x3226U, 0x34B0U, 0x36C3U, 0x399CU, 0x3BEFU, 0x3D79U, 0x3F0AU, 0x411EU, 0x436DU, 0x45FBU, 0x4788U,
0x48D7U, 0x4AA4U, 0x4C32U, 0x4E41U, 0x50FCU, 0x528FU, 0x5419U, 0x566AU, 0x5935U, 0x5B46U, 0x5DD0U, 0x5FA3U,
0x60A9U, 0x62DAU, 0x644CU, 0x663FU, 0x6960U, 0x6B13U, 0x6D85U, 0x6FF6U, 0x714BU, 0x7338U, 0x75AEU, 0x77DDU,
0x7882U, 0x7AF1U, 0x7C67U, 0x7E14U, 0x804FU, 0x823CU, 0x84AAU, 0x86D9U, 0x8986U, 0x8BF5U, 0x8D63U, 0x8F10U,
0x91ADU, 0x93DEU, 0x9548U, 0x973BU, 0x9864U, 0x9A17U, 0x9C81U, 0x9EF2U, 0xA1F8U, 0xA38BU, 0xA51DU, 0xA76EU,
0xA831U, 0xAA42U, 0xACD4U, 0xAEA7U, 0xB01AU, 0xB269U, 0xB4FFU, 0xB68CU, 0xB9D3U, 0xBBA0U, 0xBD36U, 0xBF45U,
0xC151U, 0xC322U, 0xC5B4U, 0xC7C7U, 0xC898U, 0xCAEBU, 0xCC7DU, 0xCE0EU, 0xD0B3U, 0xD2C0U, 0xD456U, 0xD625U,
0xD97AU, 0xDB09U, 0xDD9FU, 0xDFECU, 0xE0E6U, 0xE295U, 0xE403U, 0xE670U, 0xE92FU, 0xEB5CU, 0xEDCAU, 0xEFB9U,
0xF104U, 0xF377U, 0xF5E1U, 0xF792U, 0xF8CDU, 0xFABEU, 0xFC28U, 0xFE5BU};
const unsigned int DECODING_TABLE_1576[] =
{0x0000U, 0x0001U, 0x0002U, 0x0003U, 0x0004U, 0x0005U, 0x0006U, 0x4020U, 0x0008U, 0x0009U, 0x000AU, 0x000BU,
0x000CU, 0x000DU, 0x2081U, 0x2080U, 0x0010U, 0x0011U, 0x0012U, 0x0013U, 0x0014U, 0x0C00U, 0x0016U, 0x0C02U,
0x0018U, 0x0120U, 0x001AU, 0x0122U, 0x4102U, 0x0124U, 0x4100U, 0x4101U, 0x0020U, 0x0021U, 0x0022U, 0x4004U,
0x0024U, 0x4002U, 0x4001U, 0x4000U, 0x0028U, 0x0110U, 0x1800U, 0x1801U, 0x002CU, 0x400AU, 0x4009U, 0x4008U,
0x0030U, 0x0108U, 0x0240U, 0x0241U, 0x0034U, 0x4012U, 0x4011U, 0x4010U, 0x0101U, 0x0100U, 0x0103U, 0x0102U,
0x0105U, 0x0104U, 0x1401U, 0x1400U, 0x0040U, 0x0041U, 0x0042U, 0x0043U, 0x0044U, 0x0045U, 0x0046U, 0x4060U,
0x0048U, 0x0049U, 0x0301U, 0x0300U, 0x004CU, 0x1600U, 0x0305U, 0x0304U, 0x0050U, 0x0051U, 0x0220U, 0x0221U,
0x3000U, 0x4200U, 0x3002U, 0x4202U, 0x0058U, 0x1082U, 0x1081U, 0x1080U, 0x3008U, 0x4208U, 0x2820U, 0x1084U,
0x0060U, 0x0061U, 0x0210U, 0x0211U, 0x0480U, 0x0481U, 0x4041U, 0x4040U, 0x0068U, 0x2402U, 0x2401U, 0x2400U,
0x0488U, 0x3100U, 0x2810U, 0x2404U, 0x0202U, 0x0880U, 0x0200U, 0x0201U, 0x0206U, 0x0884U, 0x0204U, 0x0205U,
0x0141U, 0x0140U, 0x0208U, 0x0209U, 0x2802U, 0x0144U, 0x2800U, 0x2801U, 0x0080U, 0x0081U, 0x0082U, 0x0A00U,
0x0084U, 0x0085U, 0x2009U, 0x2008U, 0x0088U, 0x0089U, 0x2005U, 0x2004U, 0x2003U, 0x2002U, 0x2001U, 0x2000U,
0x0090U, 0x0091U, 0x0092U, 0x1048U, 0x0602U, 0x0C80U, 0x0600U, 0x0601U, 0x0098U, 0x1042U, 0x1041U, 0x1040U,
0x2013U, 0x2012U, 0x2011U, 0x2010U, 0x00A0U, 0x00A1U, 0x00A2U, 0x4084U, 0x0440U, 0x0441U, 0x4081U, 0x4080U,
0x6000U, 0x1200U, 0x6002U, 0x1202U, 0x6004U, 0x2022U, 0x2021U, 0x2020U, 0x0841U, 0x0840U, 0x2104U, 0x0842U,
0x2102U, 0x0844U, 0x2100U, 0x2101U, 0x0181U, 0x0180U, 0x0B00U, 0x0182U, 0x5040U, 0x0184U, 0x2108U, 0x2030U,
0x00C0U, 0x00C1U, 0x4401U, 0x4400U, 0x0420U, 0x0421U, 0x0422U, 0x4404U, 0x0900U, 0x0901U, 0x1011U, 0x1010U,
0x0904U, 0x2042U, 0x2041U, 0x2040U, 0x0821U, 0x0820U, 0x1009U, 0x1008U, 0x4802U, 0x0824U, 0x4800U, 0x4801U,
0x1003U, 0x1002U, 0x1001U, 0x1000U, 0x0501U, 0x0500U, 0x1005U, 0x1004U, 0x0404U, 0x0810U, 0x1100U, 0x1101U,
0x0400U, 0x0401U, 0x0402U, 0x0403U, 0x040CU, 0x0818U, 0x1108U, 0x1030U, 0x0408U, 0x0409U, 0x040AU, 0x2060U,
0x0801U, 0x0800U, 0x0280U, 0x0802U, 0x0410U, 0x0804U, 0x0412U, 0x0806U, 0x0809U, 0x0808U, 0x1021U, 0x1020U,
0x5000U, 0x2200U, 0x5002U, 0x2202U};
#define X14 0x00004000 /* vector representation of X^{14} */
#define X8 0x00000100 /* vector representation of X^{8} */
#define MASK7 0xffffff00 /* auxiliary vector for testing */
#define GENPOL 0x00000139 /* generator polinomial, g(x) */
unsigned int CQR1676::getSyndrome1576(unsigned int pattern)
/*
* Compute the syndrome corresponding to the given pattern, i.e., the
* remainder after dividing the pattern (when considering it as the vector
* representation of a polynomial) by the generator polynomial, GENPOL.
* In the program this pattern has several meanings: (1) pattern = infomation
* bits, when constructing the encoding table; (2) pattern = error pattern,
* when constructing the decoding table; and (3) pattern = received vector, to
* obtain its syndrome in decoding.
*/
{
unsigned int aux = X14;
if (pattern >= X8) {
while (pattern & MASK7) {
while (!(aux & pattern))
aux = aux >> 1;
pattern ^= (aux / X8) * GENPOL;
}
}
return pattern;
}
// Compute the EMB against a precomputed list of correct words
void CQR1676::encode(unsigned char* data)
{
assert(data != NULL);
unsigned int value = (data[0U] >> 1) & 0x7FU;
unsigned int cksum = ENCODING_TABLE_1676[value];
data[0U] = cksum >> 8;
data[1U] = cksum & 0xFFU;
}
unsigned char CQR1676::decode(const unsigned char* data)
{
assert(data != NULL);
unsigned int code = (data[0U] << 7) + (data[1U] >> 1);
unsigned int syndrome = getSyndrome1576(code);
unsigned int error_pattern = DECODING_TABLE_1576[syndrome];
code ^= error_pattern;
return code >> 7;
}

@ -0,0 +1,32 @@
/*
* Copyright (C) 2015 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef QR1676_H
#define QR1676_H
class CQR1676 {
public:
static void encode(unsigned char* data);
static unsigned char decode(const unsigned char* data);
private:
static unsigned int getSyndrome1576(unsigned int pattern);
};
#endif

@ -0,0 +1,84 @@
- HD44780 LCD SUPPORT -
Support for the HD44780 LCD is via the wiringPi library available at
http://wiringpi.com/download-and-install/ which must be installed in all cases.
The HD44780 in 4-bit mode is probably the most common connection method and
wiring details can be found at http://wiringpi.com/dev-lib/lcd-library/
The setup that is commonly used for MMDVM is the 4-bit connection that is also
documented within wiringPi. The HD44780 displays have a standardized 16-pin
connector (14 pins if without backlight). The pin numbers on HD44780 refer to
this standard numbering.
The pin numbers on Raspberry Pi side are the physical pin numbers of the GPIO
pin header, in brackets the wiringPi pin number as these are referred to in the
MMDVM.ini file.
The wiring ist as follows:
HD44780 pin Raspberry Pi GPIO
GND 1 -------- 6 GND
VCC 2 -------- 2 +5V
V0 3 -------- Trimmer between +5V and GND for contrast
RS 4 -------- 26 CE1 (11)
RW 5 -------- 6 GND
E 6 -------- 24 CE0 (10)
D0 7
D1 8
D2 9
D3 10
D4 11 -------- 11 GPIO0 (0)
D5 12 -------- 12 GPIO1 (1)
D6 13 -------- 13 GPIO2 (2)
D7 14 -------- 15 GPIO3 (3)
VCC 15 -------- 2 +5V
GND 16 -------- 6 GND
The relevant part in the MMDVM.ini is like outlined below.
[HD44780]
Rows=4
Columns=20
# For basic HD44780 displays (4-bit connection)
# rs, strb, d0, d1, d2, d3
Pins=11,10,0,1,2,3
To compile MMDVMHost with support for the HD44780 use the following commands.
# make clean
# make -f Makefile.Pi.HD44780
Other HD44780 variations exist that connect via I2C. Support is also via
wiringPi, but to compile for each I2C device requires a different Makefile
depending on the IC attached to the LCD.
- ADAFRUIT RGB LCD SHIELD -
The Adafruit RGB LCD Shield is available from https://www.adafruit.com/product/714
I2C is via the MCP23017 IC and is enabled with Makefile.Pi.Adafruit. The
I2C device address in MMDVM.ini should be set to 0x20.
- PCF8574 IC -
HD44780 LCDs connected via a PCF8574 Remote 8-Bit I/O Expander are available on
eBay for very little money! This IC uses Makefile.Pi.PCF8574 and the I2C
device address should be set to 0x27.
- BEWARE -
The I2C device address can be manually configured on some devices!
- CHECK YOUR ACTUAL DEVICE ADDRESS -
More information on configuring and using I2C on the RPi including how to probe
the I2C bus for device addresses can be found at
https://learn.sparkfun.com/tutorials/raspberry-pi-spi-and-i2c-tutorial#i2c-on-pi
- PWM BACKLIGHT CONTROL -
Whilst connected in 4-bit mode or via the PCF8574 IC, the LED backlight can be
wired for control via PWM. Connect the backlight +ve pin to any spare GPIO pin
and configure MMDVM.ini as appropriate. By default, wiringPi pin 21 is
configured.

@ -0,0 +1,26 @@
On Linux, to run MMDVMHost as a daemon, set "Daemon=1" in the ini file.
When this is set, MMDVMHost will attempt to drop privileges to user "mmdvm" and
group "mmdvm". If this user and group do not exist on your system, an error
will occur and
MMDVMHost will not start.
To add these users, please do the following from the Linux command line:
groupadd mmdvm
useradd mmdvm -g mmdvm -s /sbin/nologin
usermod mmdvm -G dialout
Note, without the last line, MMDVMHost will not be able to open the modem.
Also note, when running as a daemon, STDIN, STDOUT and STDERR are closed, so
you must use a logfile to capture logging and the logfile entry in the ini file
must be given a full path as MMDVMHost calls "cd /" when daemonising. The same
applies to the DMRIds.dat file.
Also, please note that the code to drop privileges is currently disabled when
MMDVMHost is compiled with the HD44780 display as it's currently not possible
to use this display as a non-root user.
Simon - G7RZU

@ -1,164 +1,26 @@
Now Nextion Screen is more complex, all development are made and tested into a Raspberry Pi and use some routines of this hardware. It's possible that only work in Rpi and not in Orange Pi or similar
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.
Are made with DMR only in mind for Hotspots, so only use Timeslot2 and not tested in mixed mode (P25,Dstar,etc) The code made a new screens for Nextion screens.
On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it can connect to BrandMeister, DMR+, HB Link, XLX or [DMRGateway](https://github.com/g4klx/DMRGateway) (to connect to multiple DMR networks at once) on System Fusion it connects to the YSF Gateway. On P25 it connects to the P25 Gateway.
At this time displays:
It builds on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2017 on x86 and x64. It can optionally control various Displays. Currently these are:
Some extra information:: Locator, City and Frequency.
- HD44780 (sizes 2x16, 2x40, 4x16, 4x20)
- Support for HD44780 via 4 bit GPIO connection (user selectable pins)
- 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 display sold by Hobbytronics in UK
- OLED 128x64 (SSD1306)
- LCDproc
Real IP number at startup od Pi, etho or wlan detects. Temperature of Raspberry Pi. Real time Smeter for MMDVM Modems that outs RSSI information (MMDVM modes with radios as Motorola,for example) Fake Smeters for the rests of types (DVMEGA,HS_HAT,etc) Change of colors in sequence Idle/RX/TX of Hotspot. Talker Alias display of information send by master. Decode of TG number and display of picture associated to this TG. Now TG's are the EA most used. Decode of Callsign Prefix and display of picture associated to this Prefix.Now are the EA,EB & EC prefix Decode of 9990 and 4000 commands also displaying pictures Decode of own callsign and display of own picture. Remote Reboot and Shutdown of Pi eligible by sysop via DMR
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.
Install & Run:
The HD44780 displays are integrated with wiringPi for Raspberry Pi based platforms.
New entry in MMDVM.ini for path to config files in Nextion section: FilesConfig=/home/pi/MMDVMHost/etc/
The Hobbytronics TFT Display, which is a Pi-Hat, connects to the UART on the Raspbery Pi.
You have now 6 config files (work in progress to put in only one), don't comment or delete any line, the code are simple and need to end with a C/R line to EOF
The OLED display needs a extra library see OLED.md
info.ini contains information for MMDVM home screen:
The LCDproc support enables the use of a multitude of other LCD screens. See the [supported devices](http://lcdproc.omnipotent.net/hardware.php3) page on the LCDproc website for more info.
Callsign Location Frequency DMR Network Name
ctrl.ini contains:
callsign of owner remote commands on/off TG number to shutdown Pi TG number to reboot Pi TG number to change mode to dmrplus network TG number to change mode to DmrGateway mode TG number to change mode to BrandMeister network
prefixA.ini, prefixB.ini and prefixC.ini contains a list of prefix to combine for displkay a picture map into a Nextion. example: EA-EA9, EB0-EB9,EC0-EC9, are the official prefix for Spain to hams (don't include a special stations)
tg.ini contains the number of the TG's (BrandMeister Network) to display a picture logo into a Nextion. The first TG number equals to picture 30 into the Nextion tft files.
Nextion tft (compiled) and hmi (source) files contain the pictures to work.
The editable pics are 200x100 and 100x50 pixels
Open the Nextion HMI in Editor and observe the picture list Also Open the Nextion.ini file in a editor to see references
0-19 are semi-fixed pictures, change the 8 & 9 with the replace opcion for your own callsign picture
20-29 are the prefixes pictures 3 diferents prefixes are allocated for every pic, in Nextion.ini section [WPX] you see PXA0,PXB0 and PXC0 and the prefixes EA0 EB0 and EC0 When you transmit or receive a transmission from these prefixes are displayed the pic 20 If the transmission comes from EA2,EB2 or EC2 prefixes, display picture 21 and all others.
The TG list are from picture 30 to picture 137, and works same that prefix, the list are in the tg.ini file.
example: the TG localed in first position are 214, when receive a transmission of this TG displays the picture 30 in the Nextion. 214 (EA national Talkgroup) have the picture 30 example of change: If yoy want receive the 3100 USA Talkgroup replace the image in the Nextion display (picture 30) for a USA Flag, change the entry 214 for 3100 in tg.ini
Remember that any change in tg.ini are needed to change in the Nextion Screen and upload again to the display.
You control if the remote is active when 1 or deactivate when 0 into ctrl.ini
99998 are Shutdown TG to transmit to shutdown the Pi 99999 are Reboot TG to transmit to reboot the Pi TG number to change mode to dmrplus network TG number to change mode to DmrGateway mode TG number to change mode to BrandMeister network
Only the Owner callsign are allowed to TX into those TG to activate functions.
New TTS (Text To Speech) callsigns via Festival
Installation in Raspberry Pi:
sudo apt-get install festival
Default voices are english, if you want spanish voices install packages for Guadalinex: https://github.com/guadalinex-archive/hispavoces
wget https://github.com/guadalinex-archive/hispavoces/raw/master/packages/festvox-palpc16k_1.0.0_all.deb wget https://github.com/guadalinex-archive/hispavoces/raw/master/packages/festvox-sflpc16k_1.0.0_all.deb
sudo dkpg -i festvox-sflpc16k_1.0.0_all.deb sudo dkpg -i festvox-palpc16k_1.0.0_all.deb
edit festival.scm and put last line prefered voice:
sudo nano /etc/festival.scm ;;(set! voice_default 'voice_JuntaDeAndalucia_es_sf_diphone) (set! voice_default 'voice_JuntaDeAndalucia_es_pa_diphone)
Edit OLED.cpp before compile and change your own callsign and phonetic of voices:
Line 539 if (strcmp ("EA5SW",src.c_str()) !=0){
The phonetic in spanish changed, for example, Mike in spanish are best "Maik"
Line 581 else if (c == 'M'){ Line 582 strcat(voice," Maik ");
I put all operations in RAMDISK,create a simple bash to execute before MMDVMHost
#!/bin/bash sudo mkdir -p /ram sudo mount -t tmpfs -o size=120k tmpfs /ram sudo touch /ram/mm_voice.sh sudo chmod 777 /ram/mm_voice.sh
To compile MMDVMHost use:
make -f Makefile Nextion_HS
Thanks to the effort of: G4KLX, ON7LDS,G0WFV, VK6LS & more for code EA4ATS for all graphics, a very BIG JOB !! EA5DHO for test,test,test,test and TEST and ideas!!
73 & DX from EA5SW
OLED
In MMDVM.ini
If Duplex are = 1 the default OLED Layout are use.
If Duplex are = 0 the Layout are change to HotSpot mode
The mods are made with a personal HotSpot in mind. DVMega, MMDVM_HS, low cost or ZumSpot are best candidates.
This code works with DMR SlotTime 2 only, all Slot 1 info are pulled out.
The Talker Alias, IP number and Temperature for Raspberry Pi are added. In Orange Pi the code for temperature works with small changes.
If no temperature file are read, Temp don't display.
Are added some logos for differents networks in 128x26 & 128 x 32 formats for the Idle Screen and small logos(64x16) for the TX/RX screens, also a logos for Parrot and disconnect codes (9990 and 4000)
All TG codes are easy to change into OLED.cpp
Example TG codes:
4000 display a broken chains logo, 9990 display a parrot logo, 9 or 8 displays a DmrPlus logo into tx/rx screen, 6 displays a XLX logo any other TG display a BrandMeister Logo.
Code also have a small remote command to shutdown,reboot and change modes of operation, simply when TX into a determined TG number:
9999 Reboot Raspberry
9998 Shutdown Raspberry
9997 Send mm_plus command to start a new MMDDVMHost in dmrplus mode (mm_plus is an script file)
9996 Send mm_gate command to start a new MMDDVMHost in DMRGateway mode (mm_gate is an script file)
9995 Send mm_BM command to start a new MMDDVMHost in BrandMeister mode (mm_BM is an script file)
9990 Put Wifi OFF
9991 Put Wifi ON
New TTS (Text To Speech) callsigns via Festival
Installation in Raspberry Pi:
sudo apt-get install festival
Default voices are english, if you want spanish voices install packages for Guadalinex:
https://github.com/guadalinex-archive/hispavoces
wget https://github.com/guadalinex-archive/hispavoces/raw/master/packages/festvox-palpc16k_1.0.0_all.deb
wget https://github.com/guadalinex-archive/hispavoces/raw/master/packages/festvox-sflpc16k_1.0.0_all.deb
sudo dpkg -i festvox-sflpc16k_1.0.0_all.deb
sudo dpkg -i festvox-palpc16k_1.0.0_all.deb
edit festival.scm and put last line prefered voice:
sudo nano /etc/festival.scm
;;(set! voice_default 'voice_JuntaDeAndalucia_es_sf_diphone)
(set! voice_default 'voice_JuntaDeAndalucia_es_pa_diphone)
Edit OLED.cpp before compile and change your own callsign and phonetic of voices:
Line 539 if (strcmp ("EA5SW",src.c_str()) !=0){
The phonetic in spanish changed, for example, Mike in spanish are best "Maik"
Line 581 else if (c == 'M'){
Line 582 strcat(voice," Maik ");
I put all operations in RAMDISK,create a simple bash to execute before MMDVMHost
#!/bin/bash
sudo mkdir -p /ram
sudo mount -t tmpfs -o size=120k tmpfs /ram
sudo touch /ram/mm_voice.sh
sudo chmod 777 /ram/mm_voice.sh
73 & DX EA5SW Jose
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.

@ -0,0 +1,130 @@
/*
* Copyright (C) 2015 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "RS129.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned int NPAR = 3U;
/* Maximum degree of various polynomials. */
const unsigned int MAXDEG = NPAR * 2U;
/* Generator Polynomial */
const unsigned char POLY[] = {64U, 56U, 14U, 1U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U};
const unsigned char EXP_TABLE[] = {
0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1DU, 0x3AU, 0x74U, 0xE8U, 0xCDU, 0x87U, 0x13U, 0x26U,
0x4CU, 0x98U, 0x2DU, 0x5AU, 0xB4U, 0x75U, 0xEAU, 0xC9U, 0x8FU, 0x03U, 0x06U, 0x0CU, 0x18U, 0x30U, 0x60U, 0xC0U,
0x9DU, 0x27U, 0x4EU, 0x9CU, 0x25U, 0x4AU, 0x94U, 0x35U, 0x6AU, 0xD4U, 0xB5U, 0x77U, 0xEEU, 0xC1U, 0x9FU, 0x23U,
0x46U, 0x8CU, 0x05U, 0x0AU, 0x14U, 0x28U, 0x50U, 0xA0U, 0x5DU, 0xBAU, 0x69U, 0xD2U, 0xB9U, 0x6FU, 0xDEU, 0xA1U,
0x5FU, 0xBEU, 0x61U, 0xC2U, 0x99U, 0x2FU, 0x5EU, 0xBCU, 0x65U, 0xCAU, 0x89U, 0x0FU, 0x1EU, 0x3CU, 0x78U, 0xF0U,
0xFDU, 0xE7U, 0xD3U, 0xBBU, 0x6BU, 0xD6U, 0xB1U, 0x7FU, 0xFEU, 0xE1U, 0xDFU, 0xA3U, 0x5BU, 0xB6U, 0x71U, 0xE2U,
0xD9U, 0xAFU, 0x43U, 0x86U, 0x11U, 0x22U, 0x44U, 0x88U, 0x0DU, 0x1AU, 0x34U, 0x68U, 0xD0U, 0xBDU, 0x67U, 0xCEU,
0x81U, 0x1FU, 0x3EU, 0x7CU, 0xF8U, 0xEDU, 0xC7U, 0x93U, 0x3BU, 0x76U, 0xECU, 0xC5U, 0x97U, 0x33U, 0x66U, 0xCCU,
0x85U, 0x17U, 0x2EU, 0x5CU, 0xB8U, 0x6DU, 0xDAU, 0xA9U, 0x4FU, 0x9EU, 0x21U, 0x42U, 0x84U, 0x15U, 0x2AU, 0x54U,
0xA8U, 0x4DU, 0x9AU, 0x29U, 0x52U, 0xA4U, 0x55U, 0xAAU, 0x49U, 0x92U, 0x39U, 0x72U, 0xE4U, 0xD5U, 0xB7U, 0x73U,
0xE6U, 0xD1U, 0xBFU, 0x63U, 0xC6U, 0x91U, 0x3FU, 0x7EU, 0xFCU, 0xE5U, 0xD7U, 0xB3U, 0x7BU, 0xF6U, 0xF1U, 0xFFU,
0xE3U, 0xDBU, 0xABU, 0x4BU, 0x96U, 0x31U, 0x62U, 0xC4U, 0x95U, 0x37U, 0x6EU, 0xDCU, 0xA5U, 0x57U, 0xAEU, 0x41U,
0x82U, 0x19U, 0x32U, 0x64U, 0xC8U, 0x8DU, 0x07U, 0x0EU, 0x1CU, 0x38U, 0x70U, 0xE0U, 0xDDU, 0xA7U, 0x53U, 0xA6U,
0x51U, 0xA2U, 0x59U, 0xB2U, 0x79U, 0xF2U, 0xF9U, 0xEFU, 0xC3U, 0x9BU, 0x2BU, 0x56U, 0xACU, 0x45U, 0x8AU, 0x09U,
0x12U, 0x24U, 0x48U, 0x90U, 0x3DU, 0x7AU, 0xF4U, 0xF5U, 0xF7U, 0xF3U, 0xFBU, 0xEBU, 0xCBU, 0x8BU, 0x0BU, 0x16U,
0x2CU, 0x58U, 0xB0U, 0x7DU, 0xFAU, 0xE9U, 0xCFU, 0x83U, 0x1BU, 0x36U, 0x6CU, 0xD8U, 0xADU, 0x47U, 0x8EU, 0x01U,
0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1DU, 0x3AU, 0x74U, 0xE8U, 0xCDU, 0x87U, 0x13U, 0x26U, 0x4CU,
0x98U, 0x2DU, 0x5AU, 0xB4U, 0x75U, 0xEAU, 0xC9U, 0x8FU, 0x03U, 0x06U, 0x0CU, 0x18U, 0x30U, 0x60U, 0xC0U, 0x9DU,
0x27U, 0x4EU, 0x9CU, 0x25U, 0x4AU, 0x94U, 0x35U, 0x6AU, 0xD4U, 0xB5U, 0x77U, 0xEEU, 0xC1U, 0x9FU, 0x23U, 0x46U,
0x8CU, 0x05U, 0x0AU, 0x14U, 0x28U, 0x50U, 0xA0U, 0x5DU, 0xBAU, 0x69U, 0xD2U, 0xB9U, 0x6FU, 0xDEU, 0xA1U, 0x5FU,
0xBEU, 0x61U, 0xC2U, 0x99U, 0x2FU, 0x5EU, 0xBCU, 0x65U, 0xCAU, 0x89U, 0x0FU, 0x1EU, 0x3CU, 0x78U, 0xF0U, 0xFDU,
0xE7U, 0xD3U, 0xBBU, 0x6BU, 0xD6U, 0xB1U, 0x7FU, 0xFEU, 0xE1U, 0xDFU, 0xA3U, 0x5BU, 0xB6U, 0x71U, 0xE2U, 0xD9U,
0xAFU, 0x43U, 0x86U, 0x11U, 0x22U, 0x44U, 0x88U, 0x0DU, 0x1AU, 0x34U, 0x68U, 0xD0U, 0xBDU, 0x67U, 0xCEU, 0x81U,
0x1FU, 0x3EU, 0x7CU, 0xF8U, 0xEDU, 0xC7U, 0x93U, 0x3BU, 0x76U, 0xECU, 0xC5U, 0x97U, 0x33U, 0x66U, 0xCCU, 0x85U,
0x17U, 0x2EU, 0x5CU, 0xB8U, 0x6DU, 0xDAU, 0xA9U, 0x4FU, 0x9EU, 0x21U, 0x42U, 0x84U, 0x15U, 0x2AU, 0x54U, 0xA8U,
0x4DU, 0x9AU, 0x29U, 0x52U, 0xA4U, 0x55U, 0xAAU, 0x49U, 0x92U, 0x39U, 0x72U, 0xE4U, 0xD5U, 0xB7U, 0x73U, 0xE6U,
0xD1U, 0xBFU, 0x63U, 0xC6U, 0x91U, 0x3FU, 0x7EU, 0xFCU, 0xE5U, 0xD7U, 0xB3U, 0x7BU, 0xF6U, 0xF1U, 0xFFU, 0xE3U,
0xDBU, 0xABU, 0x4BU, 0x96U, 0x31U, 0x62U, 0xC4U, 0x95U, 0x37U, 0x6EU, 0xDCU, 0xA5U, 0x57U, 0xAEU, 0x41U, 0x82U,
0x19U, 0x32U, 0x64U, 0xC8U, 0x8DU, 0x07U, 0x0EU, 0x1CU, 0x38U, 0x70U, 0xE0U, 0xDDU, 0xA7U, 0x53U, 0xA6U, 0x51U,
0xA2U, 0x59U, 0xB2U, 0x79U, 0xF2U, 0xF9U, 0xEFU, 0xC3U, 0x9BU, 0x2BU, 0x56U, 0xACU, 0x45U, 0x8AU, 0x09U, 0x12U,
0x24U, 0x48U, 0x90U, 0x3DU, 0x7AU, 0xF4U, 0xF5U, 0xF7U, 0xF3U, 0xFBU, 0xEBU, 0xCBU, 0x8BU, 0x0BU, 0x16U, 0x2CU,
0x58U, 0xB0U, 0x7DU, 0xFAU, 0xE9U, 0xCFU, 0x83U, 0x1BU, 0x36U, 0x6CU, 0xD8U, 0xADU, 0x47U, 0x8EU, 0x01U, 0x00U};
const unsigned char LOG_TABLE[] = {
0x00U, 0x00U, 0x01U, 0x19U, 0x02U, 0x32U, 0x1AU, 0xC6U, 0x03U, 0xDFU, 0x33U, 0xEEU, 0x1BU, 0x68U, 0xC7U, 0x4BU,
0x04U, 0x64U, 0xE0U, 0x0EU, 0x34U, 0x8DU, 0xEFU, 0x81U, 0x1CU, 0xC1U, 0x69U, 0xF8U, 0xC8U, 0x08U, 0x4CU, 0x71U,
0x05U, 0x8AU, 0x65U, 0x2FU, 0xE1U, 0x24U, 0x0FU, 0x21U, 0x35U, 0x93U, 0x8EU, 0xDAU, 0xF0U, 0x12U, 0x82U, 0x45U,
0x1DU, 0xB5U, 0xC2U, 0x7DU, 0x6AU, 0x27U, 0xF9U, 0xB9U, 0xC9U, 0x9AU, 0x09U, 0x78U, 0x4DU, 0xE4U, 0x72U, 0xA6U,
0x06U, 0xBFU, 0x8BU, 0x62U, 0x66U, 0xDDU, 0x30U, 0xFDU, 0xE2U, 0x98U, 0x25U, 0xB3U, 0x10U, 0x91U, 0x22U, 0x88U,
0x36U, 0xD0U, 0x94U, 0xCEU, 0x8FU, 0x96U, 0xDBU, 0xBDU, 0xF1U, 0xD2U, 0x13U, 0x5CU, 0x83U, 0x38U, 0x46U, 0x40U,
0x1EU, 0x42U, 0xB6U, 0xA3U, 0xC3U, 0x48U, 0x7EU, 0x6EU, 0x6BU, 0x3AU, 0x28U, 0x54U, 0xFAU, 0x85U, 0xBAU, 0x3DU,
0xCAU, 0x5EU, 0x9BU, 0x9FU, 0x0AU, 0x15U, 0x79U, 0x2BU, 0x4EU, 0xD4U, 0xE5U, 0xACU, 0x73U, 0xF3U, 0xA7U, 0x57U,
0x07U, 0x70U, 0xC0U, 0xF7U, 0x8CU, 0x80U, 0x63U, 0x0DU, 0x67U, 0x4AU, 0xDEU, 0xEDU, 0x31U, 0xC5U, 0xFEU, 0x18U,
0xE3U, 0xA5U, 0x99U, 0x77U, 0x26U, 0xB8U, 0xB4U, 0x7CU, 0x11U, 0x44U, 0x92U, 0xD9U, 0x23U, 0x20U, 0x89U, 0x2EU,
0x37U, 0x3FU, 0xD1U, 0x5BU, 0x95U, 0xBCU, 0xCFU, 0xCDU, 0x90U, 0x87U, 0x97U, 0xB2U, 0xDCU, 0xFCU, 0xBEU, 0x61U,
0xF2U, 0x56U, 0xD3U, 0xABU, 0x14U, 0x2AU, 0x5DU, 0x9EU, 0x84U, 0x3CU, 0x39U, 0x53U, 0x47U, 0x6DU, 0x41U, 0xA2U,
0x1FU, 0x2DU, 0x43U, 0xD8U, 0xB7U, 0x7BU, 0xA4U, 0x76U, 0xC4U, 0x17U, 0x49U, 0xECU, 0x7FU, 0x0CU, 0x6FU, 0xF6U,
0x6CU, 0xA1U, 0x3BU, 0x52U, 0x29U, 0x9DU, 0x55U, 0xAAU, 0xFBU, 0x60U, 0x86U, 0xB1U, 0xBBU, 0xCCU, 0x3EU, 0x5AU,
0xCBU, 0x59U, 0x5FU, 0xB0U, 0x9CU, 0xA9U, 0xA0U, 0x51U, 0x0BU, 0xF5U, 0x16U, 0xEBU, 0x7AU, 0x75U, 0x2CU, 0xD7U,
0x4FU, 0xAEU, 0xD5U, 0xE9U, 0xE6U, 0xE7U, 0xADU, 0xE8U, 0x74U, 0xD6U, 0xF4U, 0xEAU, 0xA8U, 0x50U, 0x58U, 0xAFU};
/* multiplication using logarithms */
static unsigned char gmult(unsigned char a, unsigned char b)
{
if (a == 0U || b == 0U)
return 0U;
unsigned int i = LOG_TABLE[a];
unsigned int j = LOG_TABLE[b];
return EXP_TABLE[i + j];
}
/* Simulate a LFSR with generator polynomial for n byte RS code.
* Pass in a pointer to the data array, and amount of data.
*
* The parity bytes are deposited into parity.
*/
void CRS129::encode(const unsigned char* msg, unsigned int nbytes, unsigned char* parity)
{
assert(msg != NULL);
assert(parity != NULL);
for (unsigned int i = 0U; i < NPAR + 1U; i++)
parity[i] = 0x00U;
for (unsigned int i = 0U; i < nbytes; i++) {
unsigned char dbyte = msg[i] ^ parity[NPAR - 1U];
for (int j = NPAR - 1; j > 0; j--)
parity[j] = parity[j - 1] ^ ::gmult(POLY[j], dbyte);
parity[0] = ::gmult(POLY[0], dbyte);
}
}
// Reed-Solomon (12,9) check
bool CRS129::check(const unsigned char* in)
{
assert(in != NULL);
unsigned char parity[4U];
encode(in, 9U, parity);
return in[9U] == parity[2U] && in[10U] == parity[1U] && in[11U] == parity[0U];
}

@ -0,0 +1,30 @@
/*
* Copyright (C) 2015 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(RS129_H)
#define RS129_H
class CRS129
{
public:
static bool check(const unsigned char* in);
static void encode(const unsigned char* msg, unsigned int nbytes, unsigned char* parity);
};
#endif

@ -0,0 +1,371 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "RS241213.h"
#include <cstdio>
#include <cassert>
const unsigned char ENCODE_MATRIX[12U][24U] = {
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 062, 044, 003, 025, 014, 016, 027, 003, 053, 004, 036, 047},
{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 011, 012, 011, 011, 016, 064, 067, 055, 001, 076, 026, 073},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 003, 001, 005, 075, 014, 006, 020, 044, 066, 006, 070, 066},
{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 021, 070, 027, 045, 016, 067, 023, 064, 073, 033, 044, 021},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 030, 022, 003, 075, 015, 015, 033, 015, 051, 003, 053, 050},
{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 001, 041, 027, 056, 076, 064, 021, 053, 004, 025, 001, 012},
{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 061, 076, 021, 055, 076, 001, 063, 035, 030, 013, 064, 070},
{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 024, 022, 071, 056, 021, 035, 073, 042, 057, 074, 043, 076},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 072, 042, 005, 020, 043, 047, 033, 056, 001, 016, 013, 076},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 072, 014, 065, 054, 035, 025, 041, 016, 015, 040, 071, 026},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 073, 065, 036, 061, 042, 022, 017, 004, 044, 020, 025, 005},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 071, 005, 055, 003, 071, 034, 060, 011, 074, 002, 041, 050}};
const unsigned int rsGFexp[64] = {
1, 2, 4, 8, 16, 32, 3, 6, 12, 24, 48, 35, 5, 10, 20, 40,
19, 38, 15, 30, 60, 59, 53, 41, 17, 34, 7, 14, 28, 56, 51, 37,
9, 18, 36, 11, 22, 44, 27, 54, 47, 29, 58, 55, 45, 25, 50, 39,
13, 26, 52, 43, 21, 42, 23, 46, 31, 62, 63, 61, 57, 49, 33, 0 };
const unsigned int rsGFlog[64] = {
63, 0, 1, 6, 2, 12, 7, 26, 3, 32, 13, 35, 8, 48, 27, 18,
4, 24, 33, 16, 14, 52, 36, 54, 9, 45, 49, 38, 28, 41, 19, 56,
5, 62, 25, 11, 34, 31, 17, 47, 15, 23, 53, 51, 37, 44, 55, 40,
10, 61, 46, 30, 50, 22, 39, 43, 29, 60, 42, 21, 20, 59, 57, 58 };
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#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])
static unsigned char bin2Hex(const unsigned char* input, unsigned int offset)
{
unsigned char output = 0x00U;
output |= READ_BIT(input, offset + 0U) ? 0x20U : 0x00U;
output |= READ_BIT(input, offset + 1U) ? 0x10U : 0x00U;
output |= READ_BIT(input, offset + 2U) ? 0x08U : 0x00U;
output |= READ_BIT(input, offset + 3U) ? 0x04U : 0x00U;
output |= READ_BIT(input, offset + 4U) ? 0x02U : 0x00U;
output |= READ_BIT(input, offset + 5U) ? 0x01U : 0x00U;
return output;
}
static void hex2Bin(unsigned char input, unsigned char* output, unsigned int offset)
{
WRITE_BIT(output, offset + 0U, input & 0x20U);
WRITE_BIT(output, offset + 1U, input & 0x10U);
WRITE_BIT(output, offset + 2U, input & 0x08U);
WRITE_BIT(output, offset + 3U, input & 0x04U);
WRITE_BIT(output, offset + 4U, input & 0x02U);
WRITE_BIT(output, offset + 5U, input & 0x01U);
}
CRS241213::CRS241213()
{
}
CRS241213::~CRS241213()
{
}
bool CRS241213::decode(unsigned char* data)
{
assert(data != NULL);
unsigned char HB[24U];
unsigned int offset = 0U;
for (unsigned int i = 0U; i < 24U; i++, offset += 6U)
HB[i] = bin2Hex(data, offset);
//RS (63,63-nroots,nroots+1) decoder where nroots = number of parity bits
// rsDec(8, 39) rsDec(16, 27) rsDec(12, 39)
const int nroots = 12;
int lambda[18];//Err+Eras Locator poly
int S[17];//syndrome poly
int b[18];
int t[18];
int omega[18];
int root[17];
int reg[18];
int locn[17];
int i, j, count, r, el, SynError, DiscrR, q, DegOmega, tmp, num1, num2, den, DegLambda;
//form the syndromes; i.e., evaluate HB(x) at roots of g(x)
for (i = 0; i <= nroots - 1; i++) {
S[i] = HB[0];
}
for (j = 1; j <= 23; j++) { // XXX was 62
for (i = 0; i <= nroots - 1; i++) {
if (S[i] == 0) {
S[i] = HB[j];
} else {
S[i] = HB[j] ^ rsGFexp[(rsGFlog[S[i]] + i + 1) % 63];
}
}
}
//convert syndromes to index form, checking for nonzero condition
SynError = 0;
for (i = 0; i <= nroots - 1; i++) {
SynError = SynError | S[i];
S[i] = rsGFlog[S[i]];
}
if (SynError == 0) {
//if syndrome is zero, rsData[] is a codeword and there are
//no errors to correct. So return rsData[] unmodified
count = 0;
return true;
}
for (i = 1; i <= nroots; i++) {
lambda[i] = 0;
}
lambda[0] = 1;
for (i = 0; i <= nroots; i++) {
b[i] = rsGFlog[lambda[i]];
}
//begin Berlekamp-Massey algorithm to determine error+erasure
//locator polynomial
r = 0;
el = 0;
while (r < nroots) { //r is the step number
r = r + 1;
//compute discrepancy at the r-th step in poly-form
DiscrR = 0;
for (i = 0; i <= r - 1; i++) {
if ((lambda[i] != 0) && (S[r - i - 1] != 63)) {
DiscrR = DiscrR ^ rsGFexp[(rsGFlog[lambda[i]] + S[r - i - 1]) % 63];
}
}
DiscrR = rsGFlog[DiscrR];//index form
if (DiscrR == 63) {
//shift elements upward one step
for (i = nroots; i >= 1; i += -1) {
b[i] = b[i - 1];
}
b[0] = 63;
} else {
//t(x) <-- lambda(x) - DiscrR*x*b(x)
t[0] = lambda[0];
for (i = 0; i <= nroots - 1; i++) {
if (b[i] != 63) {
t[i + 1] = lambda[i + 1] ^ rsGFexp[(DiscrR + b[i]) % 63];
} else {
t[i + 1] = lambda[i + 1];
}
}
if (2 * el <= r - 1) {
el = r - el;
//b(x) <-- inv(DiscrR) * lambda(x)
for (i = 0; i <= nroots; i++) {
if (lambda[i]) {
b[i] = (rsGFlog[lambda[i]] - DiscrR + 63) % 63;
} else {
b[i] = 63;
}
}
} else {
//shift elements upward one step
for (i = nroots; i >= 1; i += -1) {
b[i] = b[i - 1];
}
b[0] = 63;
}
for (i = 0; i <= nroots; i++) {
lambda[i] = t[i];
}
}
} /* end while() */
//convert lambda to index form and compute deg(lambda(x))
DegLambda = 0;
for (i = 0; i <= nroots; i++) {
lambda[i] = rsGFlog[lambda[i]];
if (lambda[i] != 63) {
DegLambda = i;
}
}
//Find roots of the error+erasure locator polynomial by Chien search
for (i = 1; i <= nroots; i++) {
reg[i] = lambda[i];
}
count = 0;//number of roots of lambda(x)
for (i = 1; i <= 63; i++) {
q = 1;//lambda[0] is always 0
for (j = DegLambda; j >= 1; j += -1) {
if (reg[j] != 63) {
reg[j] = (reg[j] + j) % 63;
q = q ^ rsGFexp[reg[j]];
}
}
if (q == 0) { //it is a root
//store root (index-form) and error location number
root[count] = i;
locn[count] = i - 40;
//if wehave max possible roots, abort search to save time
count = count + 1;
if (count == DegLambda) {
break;
}
}
}
if (DegLambda != count) {
//deg(lambda) unequal to number of roots => uncorrectable error detected
return false;
}
//compute err+eras evaluator poly omega(x)
// = s(x)*lambda(x) (modulo x**nroots). in index form. Also find deg(omega).
DegOmega = 0;
for (i = 0; i <= nroots - 1; i++) {
tmp = 0;
if (DegLambda < i) {
j = DegLambda;
} else {
j = i;
}
for ( /* j = j */; j >= 0; j += -1) {
if ((S[i - j] != 63) && (lambda[j] != 63)) {
tmp = tmp ^ rsGFexp[(S[i - j] + lambda[j]) % 63];
}
}
if (tmp) {
DegOmega = i;
}
omega[i] = rsGFlog[tmp];
}
omega[nroots] = 63;
//compute error values in poly-form:
// num1 = omega(inv(X(l)))
// num2 = inv(X(l))**(FCR - 1)
// den = lambda_pr(inv(X(l)))
for (j = count - 1; j >= 0; j += -1) {
num1 = 0;
for (i = DegOmega; i >= 0; i += -1) {
if (omega[i] != 63) {
num1 = num1 ^ rsGFexp[(omega[i] + i * root[j]) % 63];
}
}
num2 = rsGFexp[0];
den = 0;
// lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i]
if (DegLambda < nroots) {
i = DegLambda;
} else {
i = nroots;
}
for (i = i & ~1; i >= 0; i += -2) {
if (lambda[i + 1] != 63) {
den = den ^ rsGFexp[(lambda[i + 1] + i * root[j]) % 63];
}
}
if (den == 0) {
return false;
}
// apply error to data
if (num1 != 0) {
if(locn[j] < 24)
HB[locn[j]] = HB[locn[j]] ^ (rsGFexp[(rsGFlog[num1] + rsGFlog[num2] + 63 - rsGFlog[den]) % 63]);
}
}
offset = 0U;
for (unsigned int i = 0U; i < 12U; i++, offset += 6U)
hex2Bin(HB[i], data, offset);
return true;
}
void CRS241213::encode(unsigned char* data)
{
assert(data != NULL);
unsigned char codeword[24U];
for (unsigned int i = 0U; i < 24U; i++) {
codeword[i] = 0x00U;
unsigned int offset = 0U;
for (unsigned int j = 0U; j < 12U; j++, offset += 6U) {
unsigned char hexbit = bin2Hex(data, offset);
codeword[i] ^= gf6Mult(hexbit, ENCODE_MATRIX[j][i]);
}
}
unsigned int offset = 0U;
for (unsigned int i = 0U; i < 24U; i++, offset += 6U)
hex2Bin(codeword[i], data, offset);
}
// GF(2 ^ 6) multiply(for Reed - Solomon encoder)
unsigned char CRS241213::gf6Mult(unsigned char a, unsigned char b) const
{
unsigned char p = 0x00U;
for (unsigned int i = 0U; i < 6U; i++) {
if ((b & 0x01U) == 0x01U)
p ^= a;
a <<= 1;
if ((a & 0x40U) == 0x40U)
a ^= 0x43U; // primitive polynomial : x ^ 6 + x + 1
b >>= 1;
}
return p;
}

@ -0,0 +1,36 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(RS241213_H)
#define RS241213_H
class CRS241213
{
public:
CRS241213();
~CRS241213();
bool decode(unsigned char* data);
void encode(unsigned char* data);
private:
unsigned char gf6Mult(unsigned char a, unsigned char b) const;
};
#endif

@ -0,0 +1,11 @@
# This file maps the raw RSSI values to dBm values to send to the DMR network. A number of data
# points should be entered and the software will use those to work out the in-between values.
#
# The format of the file is:
# Raw RSSI Value dBm Value
#
# For example
# 1134 -90
# 1123 -100
# 1000 -109
#

@ -0,0 +1,91 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "RSSIInterpolator.h"
#include "Log.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
CRSSIInterpolator::CRSSIInterpolator() :
m_map()
{
}
CRSSIInterpolator::~CRSSIInterpolator()
{
m_map.clear();
}
bool CRSSIInterpolator::load(const std::string& filename)
{
FILE* fp = ::fopen(filename.c_str(), "rt");
if (fp == NULL) {
LogWarning("Cannot open the RSSI data file - %s", filename.c_str());
return false;
}
char buffer[100U];
while (::fgets(buffer, 100, fp) != NULL) {
if (buffer[0U] == '#')
continue;
char* p1 = ::strtok(buffer, " \t\r\n");
char* p2 = ::strtok(NULL, " \t\r\n");
if (p1 != NULL && p2 != NULL) {
uint16_t raw = uint16_t(::atoi(p1));
int rssi = ::atoi(p2);
m_map.insert(std::pair<uint16_t, int>(raw, rssi));
}
}
::fclose(fp);
LogInfo("Loaded %u RSSI data mapping points from %s", m_map.size(), filename.c_str());
return true;
}
int CRSSIInterpolator::interpolate(uint16_t val) const
{
if (m_map.empty())
return 0;
std::map<uint16_t, int>::const_iterator it = m_map.lower_bound(val);
if (it == m_map.end())
return m_map.rbegin()->second;
if (it == m_map.begin())
return it->second;
uint16_t x2 = it->first;
int y2 = it->second;
--it;
uint16_t x1 = it->first;
int y1 = it->second;
float p = float(val - x1) / float(x2 - x1);
return int((1.0F - p) * float(y1) + p * float(y2));
}

@ -0,0 +1,39 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(RSSIINTERPOLATOR_H)
#define RSSIINTERPOLATOR_H
#include <cstdint>
#include <string>
#include <map>
class CRSSIInterpolator {
public:
CRSSIInterpolator();
~CRSSIInterpolator();
bool load(const std::string& filename);
int interpolate(uint16_t raw) const;
private:
std::map<uint16_t, int> m_map;
};
#endif

@ -0,0 +1,154 @@
/*
* Copyright (C) 2006-2009,2012,2013,2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef RingBuffer_H
#define RingBuffer_H
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
template<class T> class CRingBuffer {
public:
CRingBuffer(unsigned int length, const char* name) :
m_length(length),
m_name(name),
m_buffer(NULL),
m_iPtr(0U),
m_oPtr(0U)
{
assert(length > 0U);
assert(name != NULL);
m_buffer = new T[length];
::memset(m_buffer, 0x00, m_length * sizeof(T));
}
~CRingBuffer()
{
delete[] m_buffer;
}
bool addData(const T* buffer, unsigned int nSamples)
{
if (nSamples >= freeSpace()) {
LogError("%s buffer overflow, clearing the buffer. (%u >= %u)", m_name, nSamples, freeSpace());
clear();
return false;
}
for (unsigned int i = 0U; i < nSamples; i++) {
m_buffer[m_iPtr++] = buffer[i];
if (m_iPtr == m_length)
m_iPtr = 0U;
}
return true;
}
bool getData(T* buffer, unsigned int nSamples)
{
if (dataSize() < nSamples) {
LogError("**** Underflow in %s ring buffer, %u < %u", m_name, dataSize(), nSamples);
return false;
}
for (unsigned int i = 0U; i < nSamples; i++) {
buffer[i] = m_buffer[m_oPtr++];
if (m_oPtr == m_length)
m_oPtr = 0U;
}
return true;
}
bool peek(T* buffer, unsigned int nSamples)
{
if (dataSize() < nSamples) {
LogError("**** Underflow peek in %s ring buffer, %u < %u", m_name, dataSize(), nSamples);
return false;
}
unsigned int ptr = m_oPtr;
for (unsigned int i = 0U; i < nSamples; i++) {
buffer[i] = m_buffer[ptr++];
if (ptr == m_length)
ptr = 0U;
}
return true;
}
void clear()
{
m_iPtr = 0U;
m_oPtr = 0U;
::memset(m_buffer, 0x00, m_length * sizeof(T));
}
unsigned int freeSpace() const
{
unsigned int len = m_length;
if (m_oPtr > m_iPtr)
len = m_oPtr - m_iPtr;
else if (m_iPtr > m_oPtr)
len = m_length - (m_iPtr - m_oPtr);
if (len > m_length)
len = 0U;
return len;
}
unsigned int dataSize() const
{
return m_length - freeSpace();
}
bool hasSpace(unsigned int length) const
{
return freeSpace() > length;
}
bool hasData() const
{
return m_oPtr != m_iPtr;
}
bool isEmpty() const
{
return m_oPtr == m_iPtr;
}
private:
unsigned int m_length;
const char* m_name;
T* m_buffer;
unsigned int m_iPtr;
unsigned int m_oPtr;
};
#endif

@ -0,0 +1,373 @@
/*
* Copyright (C) 2005, 2006, 2008 Free Software Foundation, Inc.
* Copyright (C) 2011,2015 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "SHA256.h"
#include <cstdio>
#include <cstring>
#include <cassert>
#ifdef WORDS_BIGENDIAN
# define SWAP(n) (n)
#else
# define SWAP(n) \
(((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
#endif
#define BLOCKSIZE 4096
#if BLOCKSIZE % 64 != 0
# error "invalid BLOCKSIZE"
#endif
/* This array contains the bytes used to pad the buffer to the next
64-byte boundary. */
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
/*
Takes a pointer to a 256 bit block of data (eight 32 bit ints) and
intializes it to the start constants of the SHA256 algorithm. This
must be called before using hash in the call to sha256_hash
*/
CSHA256::CSHA256() :
m_state(NULL),
m_total(NULL),
m_buflen(0U),
m_buffer(NULL)
{
m_state = new uint32_t[8U];
m_total = new uint32_t[2U];
m_buffer = new uint32_t[32U];
init();
}
CSHA256::~CSHA256()
{
delete[] m_state;
delete[] m_total;
delete[] m_buffer;
}
void CSHA256::init()
{
m_state[0] = 0x6a09e667UL;
m_state[1] = 0xbb67ae85UL;
m_state[2] = 0x3c6ef372UL;
m_state[3] = 0xa54ff53aUL;
m_state[4] = 0x510e527fUL;
m_state[5] = 0x9b05688cUL;
m_state[6] = 0x1f83d9abUL;
m_state[7] = 0x5be0cd19UL;
m_total[0] = m_total[1] = 0;
m_buflen = 0;
}
/* Copy the value from v into the memory location pointed to by *cp,
If your architecture allows unaligned access this is equivalent to
* (uint32_t *) cp = v */
static inline void set_uint32(unsigned char* cp, uint32_t v)
{
assert(cp != NULL);
::memcpy(cp, &v, sizeof v);
}
/* Put result from CTX in first 32 bytes following RESBUF. The result
must be in little endian byte order. */
unsigned char* CSHA256::read(unsigned char* resbuf)
{
assert(resbuf != NULL);
for (unsigned int i = 0U; i < 8U; i++)
set_uint32(resbuf + i * sizeof(m_state[0]), SWAP(m_state[i]));
return resbuf;
}
/* Process the remaining bytes in the internal buffer and the usual
prolog according to the standard and write the result to RESBUF. */
void CSHA256::conclude()
{
/* Take yet unprocessed bytes into account. */
unsigned int bytes = m_buflen;
unsigned int size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4;
/* Now count remaining bytes. */
m_total[0] += bytes;
if (m_total[0] < bytes)
++m_total[1];
/* Put the 64-bit file length in *bits* at the end of the buffer.
Use set_uint32 rather than a simple assignment, to avoid risk of
unaligned access. */
set_uint32((unsigned char*)&m_buffer[size - 2], SWAP((m_total[1] << 3) | (m_total[0] >> 29)));
set_uint32((unsigned char*)&m_buffer[size - 1], SWAP(m_total[0] << 3));
::memcpy(&((char*)m_buffer)[bytes], fillbuf, (size - 2) * 4 - bytes);
/* Process last bytes. */
processBlock((unsigned char*)m_buffer, size * 4);
}
unsigned char* CSHA256::finish(unsigned char* resbuf)
{
assert(resbuf != NULL);
conclude();
return read(resbuf);
}
/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The
result is always in little endian byte order, so that a byte-wise
output yields to the wanted ASCII representation of the message
digest. */
unsigned char* CSHA256::buffer(const unsigned char* buffer, unsigned int len, unsigned char* resblock)
{
assert(buffer != NULL);
assert(resblock != NULL);
/* Initialize the computation context. */
init();
/* Process whole buffer but last len % 64 bytes. */
processBytes(buffer, len);
/* Put result in desired memory area. */
return finish(resblock);
}
void CSHA256::processBytes(const unsigned char* buffer, unsigned int len)
{
assert(buffer != NULL);
/* When we already have some bits in our internal buffer concatenate
both inputs first. */
if (m_buflen != 0U) {
unsigned int left_over = m_buflen;
unsigned int add = 128U - left_over > len ? len : 128U - left_over;
::memcpy(&((char*)m_buffer)[left_over], buffer, add);
m_buflen += add;
if (m_buflen > 64U) {
processBlock((unsigned char*)m_buffer, m_buflen & ~63U);
m_buflen &= 63U;
/* The regions in the following copy operation cannot overlap. */
::memcpy(m_buffer, &((char*)m_buffer)[(left_over + add) & ~63U], m_buflen);
}
buffer += add;
len -= add;
}
/* Process available complete blocks. */
if (len >= 64U) {
//#if !_STRING_ARCH_unaligned
//# define alignof(type) offsetof (struct { char c; type x; }, x)
//# define UNALIGNED_P(p) (((unsigned int) p) % alignof (uint32_t) != 0)
// if (UNALIGNED_P (buffer)) {
// while (len > 64U) {
// ::memcpy(m_buffer, buffer, 64U);
// processBlock((unsigned char*)m_buffer, 64U);
// buffer += 64U;
// len -= 64U;
// }
// } else
//#endif
{
processBlock(buffer, len & ~63U);
buffer += (len & ~63U);
len &= 63U;
}
}
/* Move remaining bytes in internal buffer. */
if (len > 0U) {
unsigned int left_over = m_buflen;
::memcpy(&((char*)m_buffer)[left_over], buffer, len);
left_over += len;
if (left_over >= 64U) {
processBlock((unsigned char*)m_buffer, 64U);
left_over -= 64U;
::memcpy(m_buffer, &m_buffer[16], left_over);
}
m_buflen = left_over;
}
}
/* --- Code below is the primary difference between sha1.c and sha256.c --- */
/* SHA256 round constants */
#define K(I) roundConstants[I]
static const uint32_t roundConstants[64] = {
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL,
};
/* Round functions. */
#define F2(A,B,C) ( ( A & B ) | ( C & ( A | B ) ) )
#define F1(E,F,G) ( G ^ ( E & ( F ^ G ) ) )
/* Process LEN bytes of BUFFER, accumulating context into CTX.
It is assumed that LEN % 64 == 0.
Most of this code comes from GnuPG's cipher/sha1.c. */
void CSHA256::processBlock(const unsigned char* buffer, unsigned int len)
{
assert(buffer != NULL);
const uint32_t* words = (uint32_t*)buffer;
unsigned int nwords = len / sizeof(uint32_t);
const uint32_t* endp = words + nwords;
uint32_t x[16];
uint32_t a = m_state[0];
uint32_t b = m_state[1];
uint32_t c = m_state[2];
uint32_t d = m_state[3];
uint32_t e = m_state[4];
uint32_t f = m_state[5];
uint32_t g = m_state[6];
uint32_t h = m_state[7];
/* First increment the byte count. FIPS PUB 180-2 specifies the possible
length of the file up to 2^64 bits. Here we only compute the
number of bytes. Do a double word increment. */
m_total[0] += len;
if (m_total[0] < len)
++m_total[1];
#define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
#define S0(x) (rol(x,25)^rol(x,14)^(x>>3))
#define S1(x) (rol(x,15)^rol(x,13)^(x>>10))
#define SS0(x) (rol(x,30)^rol(x,19)^rol(x,10))
#define SS1(x) (rol(x,26)^rol(x,21)^rol(x,7))
#define M(I) (tm = S1(x[(I-2)&0x0f]) + x[(I-7)&0x0f] + S0(x[(I-15)&0x0f]) + x[I&0x0f], x[I&0x0f] = tm)
#define R(A,B,C,D,E,F,G,H,K,M) do { t0 = SS0(A) + F2(A,B,C); \
t1 = H + SS1(E) + F1(E,F,G) + K + M; \
D += t1; H = t0 + t1; \
} while(0)
while (words < endp) {
uint32_t tm;
uint32_t t0, t1;
/* FIXME: see sha1.c for a better implementation. */
for (unsigned int t = 0U; t < 16U; t++) {
x[t] = SWAP(*words);
words++;
}
R( a, b, c, d, e, f, g, h, K( 0), x[ 0] );
R( h, a, b, c, d, e, f, g, K( 1), x[ 1] );
R( g, h, a, b, c, d, e, f, K( 2), x[ 2] );
R( f, g, h, a, b, c, d, e, K( 3), x[ 3] );
R( e, f, g, h, a, b, c, d, K( 4), x[ 4] );
R( d, e, f, g, h, a, b, c, K( 5), x[ 5] );
R( c, d, e, f, g, h, a, b, K( 6), x[ 6] );
R( b, c, d, e, f, g, h, a, K( 7), x[ 7] );
R( a, b, c, d, e, f, g, h, K( 8), x[ 8] );
R( h, a, b, c, d, e, f, g, K( 9), x[ 9] );
R( g, h, a, b, c, d, e, f, K(10), x[10] );
R( f, g, h, a, b, c, d, e, K(11), x[11] );
R( e, f, g, h, a, b, c, d, K(12), x[12] );
R( d, e, f, g, h, a, b, c, K(13), x[13] );
R( c, d, e, f, g, h, a, b, K(14), x[14] );
R( b, c, d, e, f, g, h, a, K(15), x[15] );
R( a, b, c, d, e, f, g, h, K(16), M(16) );
R( h, a, b, c, d, e, f, g, K(17), M(17) );
R( g, h, a, b, c, d, e, f, K(18), M(18) );
R( f, g, h, a, b, c, d, e, K(19), M(19) );
R( e, f, g, h, a, b, c, d, K(20), M(20) );
R( d, e, f, g, h, a, b, c, K(21), M(21) );
R( c, d, e, f, g, h, a, b, K(22), M(22) );
R( b, c, d, e, f, g, h, a, K(23), M(23) );
R( a, b, c, d, e, f, g, h, K(24), M(24) );
R( h, a, b, c, d, e, f, g, K(25), M(25) );
R( g, h, a, b, c, d, e, f, K(26), M(26) );
R( f, g, h, a, b, c, d, e, K(27), M(27) );
R( e, f, g, h, a, b, c, d, K(28), M(28) );
R( d, e, f, g, h, a, b, c, K(29), M(29) );
R( c, d, e, f, g, h, a, b, K(30), M(30) );
R( b, c, d, e, f, g, h, a, K(31), M(31) );
R( a, b, c, d, e, f, g, h, K(32), M(32) );
R( h, a, b, c, d, e, f, g, K(33), M(33) );
R( g, h, a, b, c, d, e, f, K(34), M(34) );
R( f, g, h, a, b, c, d, e, K(35), M(35) );
R( e, f, g, h, a, b, c, d, K(36), M(36) );
R( d, e, f, g, h, a, b, c, K(37), M(37) );
R( c, d, e, f, g, h, a, b, K(38), M(38) );
R( b, c, d, e, f, g, h, a, K(39), M(39) );
R( a, b, c, d, e, f, g, h, K(40), M(40) );
R( h, a, b, c, d, e, f, g, K(41), M(41) );
R( g, h, a, b, c, d, e, f, K(42), M(42) );
R( f, g, h, a, b, c, d, e, K(43), M(43) );
R( e, f, g, h, a, b, c, d, K(44), M(44) );
R( d, e, f, g, h, a, b, c, K(45), M(45) );
R( c, d, e, f, g, h, a, b, K(46), M(46) );
R( b, c, d, e, f, g, h, a, K(47), M(47) );
R( a, b, c, d, e, f, g, h, K(48), M(48) );
R( h, a, b, c, d, e, f, g, K(49), M(49) );
R( g, h, a, b, c, d, e, f, K(50), M(50) );
R( f, g, h, a, b, c, d, e, K(51), M(51) );
R( e, f, g, h, a, b, c, d, K(52), M(52) );
R( d, e, f, g, h, a, b, c, K(53), M(53) );
R( c, d, e, f, g, h, a, b, K(54), M(54) );
R( b, c, d, e, f, g, h, a, K(55), M(55) );
R( a, b, c, d, e, f, g, h, K(56), M(56) );
R( h, a, b, c, d, e, f, g, K(57), M(57) );
R( g, h, a, b, c, d, e, f, K(58), M(58) );
R( f, g, h, a, b, c, d, e, K(59), M(59) );
R( e, f, g, h, a, b, c, d, K(60), M(60) );
R( d, e, f, g, h, a, b, c, K(61), M(61) );
R( c, d, e, f, g, h, a, b, K(62), M(62) );
R( b, c, d, e, f, g, h, a, K(63), M(63) );
a = m_state[0] += a;
b = m_state[1] += b;
c = m_state[2] += c;
d = m_state[3] += d;
e = m_state[4] += e;
f = m_state[5] += f;
g = m_state[6] += g;
h = m_state[7] += h;
}
}

@ -0,0 +1,73 @@
/*
* Copyright (C) 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
* Copyright (C) 2011,2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef SHA256_H
#define SHA256_H
#include <cstdint>
enum {
SHA256_DIGEST_SIZE = 256 / 8
};
class CSHA256 {
public:
CSHA256();
~CSHA256();
/* Starting with the result of former calls of this function (or the
initialization function update the context for the next LEN bytes
starting at BUFFER.
It is necessary that LEN is a multiple of 64!!! */
void processBlock(const unsigned char* buffer, unsigned int len);
/* Starting with the result of former calls of this function (or the
initialization function update the context for the next LEN bytes
starting at BUFFER.
It is NOT required that LEN is a multiple of 64. */
void processBytes(const unsigned char* buffer, unsigned int len);
/* Process the remaining bytes in the buffer and put result from CTX
in first 32 bytes following RESBUF. The result is always in little
endian byte order, so that a byte-wise output yields to the wanted
ASCII representation of the message digest. */
unsigned char* finish(unsigned char* resbuf);
/* Put result from CTX in first 32 bytes following RESBUF. The result is
always in little endian byte order, so that a byte-wise output yields
to the wanted ASCII representation of the message digest. */
unsigned char* read(unsigned char* resbuf);
/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The
result is always in little endian byte order, so that a byte-wise
output yields to the wanted ASCII representation of the message
digest. */
unsigned char* buffer(const unsigned char* buffer, unsigned int len, unsigned char* resblock);
private:
uint32_t* m_state;
uint32_t* m_total;
unsigned int m_buflen;
uint32_t* m_buffer;
void init();
void conclude();
};
#endif

@ -0,0 +1,475 @@
/*
* Copyright (C) 2002-2004,2007-2011,2013,2014-2017 by Jonathan Naylor G4KLX
* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "SerialController.h"
#include "Log.h"
#include <cstring>
#include <cassert>
#include <sys/types.h>
#if defined(_WIN32) || defined(_WIN64)
#include <setupapi.h>
#include <winioctl.h>
#else
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <cerrno>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
CSerialController::CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) :
m_device(device),
m_speed(speed),
m_assertRTS(assertRTS),
m_handle(INVALID_HANDLE_VALUE)
{
assert(!device.empty());
}
CSerialController::~CSerialController()
{
}
bool CSerialController::open()
{
assert(m_handle == INVALID_HANDLE_VALUE);
DWORD errCode;
std::string baseName = m_device.substr(4U); // Convert "\\.\COM10" to "COM10"
m_handle = ::CreateFileA(m_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (m_handle == INVALID_HANDLE_VALUE) {
LogError("Cannot open device - %s, err=%04lx", m_device.c_str(), ::GetLastError());
return false;
}
DCB dcb;
if (::GetCommState(m_handle, &dcb) == 0) {
LogError("Cannot get the attributes for %s, err=%04lx", m_device.c_str(), ::GetLastError());
::ClearCommError(m_handle, &errCode, NULL);
::CloseHandle(m_handle);
return false;
}
dcb.BaudRate = DWORD(m_speed);
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.fParity = FALSE;
dcb.StopBits = ONESTOPBIT;
dcb.fInX = FALSE;
dcb.fOutX = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDsrSensitivity = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
if (::SetCommState(m_handle, &dcb) == 0) {
LogError("Cannot set the attributes for %s, err=%04lx", m_device.c_str(), ::GetLastError());
::ClearCommError(m_handle, &errCode, NULL);
::CloseHandle(m_handle);
return false;
}
COMMTIMEOUTS timeouts;
if (!::GetCommTimeouts(m_handle, &timeouts)) {
LogError("Cannot get the timeouts for %s, err=%04lx", m_device.c_str(), ::GetLastError());
::ClearCommError(m_handle, &errCode, NULL);
::CloseHandle(m_handle);
return false;
}
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0UL;
timeouts.ReadTotalTimeoutConstant = 0UL;
if (!::SetCommTimeouts(m_handle, &timeouts)) {
LogError("Cannot set the timeouts for %s, err=%04lx", m_device.c_str(), ::GetLastError());
::ClearCommError(m_handle, &errCode, NULL);
::CloseHandle(m_handle);
return false;
}
if (::EscapeCommFunction(m_handle, CLRDTR) == 0) {
LogError("Cannot clear DTR for %s, err=%04lx", m_device.c_str(), ::GetLastError());
::ClearCommError(m_handle, &errCode, NULL);
::CloseHandle(m_handle);
return false;
}
if (::EscapeCommFunction(m_handle, m_assertRTS ? SETRTS : CLRRTS) == 0) {
LogError("Cannot set/clear RTS for %s, err=%04lx", m_device.c_str(), ::GetLastError());
::ClearCommError(m_handle, &errCode, NULL);
::CloseHandle(m_handle);
return false;
}
::ClearCommError(m_handle, &errCode, NULL);
return true;
}
int CSerialController::read(unsigned char* buffer, unsigned int length)
{
assert(m_handle != INVALID_HANDLE_VALUE);
assert(buffer != NULL);
unsigned int ptr = 0U;
while (ptr < length) {
int ret = readNonblock(buffer + ptr, length - ptr);
if (ret < 0) {
return ret;
} else if (ret == 0) {
if (ptr == 0U)
return 0;
} else {
ptr += ret;
}
}
return int(length);
}
int CSerialController::readNonblock(unsigned char* buffer, unsigned int length)
{
assert(m_handle != INVALID_HANDLE_VALUE);
assert(buffer != NULL);
if (length == 0U)
return 0;
DWORD errors;
COMSTAT status;
if (::ClearCommError(m_handle, &errors, &status) == 0) {
LogError("Error from ClearCommError for %s, err=%04lx", m_device.c_str(), ::GetLastError());
return -1;
}
if (status.cbInQue == 0UL)
return 0;
DWORD readLength = status.cbInQue;
if (length < readLength)
readLength = length;
DWORD bytes = 0UL;
BOOL ret = ::ReadFile(m_handle, buffer, readLength, &bytes, NULL);
if (!ret) {
LogError("Error from ReadFile for %s: %04lx", m_device.c_str(), ::GetLastError());
return -1;
}
return int(bytes);
}
int CSerialController::write(const unsigned char* buffer, unsigned int length)
{
assert(m_handle != INVALID_HANDLE_VALUE);
assert(buffer != NULL);
if (length == 0U)
return 0;
unsigned int ptr = 0U;
while (ptr < length) {
DWORD bytes = 0UL;
BOOL ret = ::WriteFile(m_handle, buffer + ptr, length - ptr, &bytes, NULL);
if (!ret) {
LogError("Error from WriteFile for %s: %04lx", m_device.c_str(), ::GetLastError());
return -1;
}
ptr += bytes;
}
return int(length);
}
void CSerialController::close()
{
assert(m_handle != INVALID_HANDLE_VALUE);
::CloseHandle(m_handle);
m_handle = INVALID_HANDLE_VALUE;
}
#else
CSerialController::CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) :
m_device(device),
m_speed(speed),
m_assertRTS(assertRTS),
m_fd(-1)
{
assert(!device.empty());
}
CSerialController::~CSerialController()
{
}
bool CSerialController::open()
{
assert(m_fd == -1);
#if defined(__APPLE__)
m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); /*open in block mode under OSX*/
#else
m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0);
#endif
if (m_fd < 0) {
LogError("Cannot open device - %s", m_device.c_str());
return false;
}
if (::isatty(m_fd) == 0) {
LogError("%s is not a TTY device", m_device.c_str());
::close(m_fd);
return false;
}
termios termios;
if (::tcgetattr(m_fd, &termios) < 0) {
LogError("Cannot get the attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
#if defined(__APPLE__)
termios.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
termios.c_cflag &= ~CSIZE;
termios.c_cflag |= CS8; /* 8-bit characters */
termios.c_cflag &= ~PARENB; /* no parity bit */
termios.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
termios.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
termios.c_cc[VMIN] = 1;
termios.c_cc[VTIME] = 1;
#else
termios.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG);
termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY);
termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS);
termios.c_cflag |= CS8;
termios.c_oflag &= ~(OPOST);
termios.c_cc[VMIN] = 0;
termios.c_cc[VTIME] = 10;
#endif
switch (m_speed) {
case SERIAL_1200:
::cfsetospeed(&termios, B1200);
::cfsetispeed(&termios, B1200);
break;
case SERIAL_2400:
::cfsetospeed(&termios, B2400);
::cfsetispeed(&termios, B2400);
break;
case SERIAL_4800:
::cfsetospeed(&termios, B4800);
::cfsetispeed(&termios, B4800);
break;
case SERIAL_9600:
::cfsetospeed(&termios, B9600);
::cfsetispeed(&termios, B9600);
break;
case SERIAL_19200:
::cfsetospeed(&termios, B19200);
::cfsetispeed(&termios, B19200);
break;
case SERIAL_38400:
::cfsetospeed(&termios, B38400);
::cfsetispeed(&termios, B38400);
break;
case SERIAL_115200:
::cfsetospeed(&termios, B115200);
::cfsetispeed(&termios, B115200);
break;
case SERIAL_230400:
::cfsetospeed(&termios, B230400);
::cfsetispeed(&termios, B230400);
break;
default:
LogError("Unsupported serial port speed - %d", int(m_speed));
::close(m_fd);
return false;
}
if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) {
LogError("Cannot set the attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
if (m_assertRTS) {
unsigned int y;
if (::ioctl(m_fd, TIOCMGET, &y) < 0) {
LogError("Cannot get the control attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
y |= TIOCM_RTS;
if (::ioctl(m_fd, TIOCMSET, &y) < 0) {
LogError("Cannot set the control attributes for %s", m_device.c_str());
::close(m_fd);
return false;
}
}
#if defined(__APPLE__)
setNonblock(false);
#endif
return true;
}
#if defined(__APPLE__)
int CSerialController::setNonblock(bool nonblock)
{
int flag = ::fcntl(m_fd, F_GETFD, 0);
if (nonblock)
flag |= O_NONBLOCK;
else
flag &= ~O_NONBLOCK;
return ::fcntl(m_fd, F_SETFL, flag);
}
#endif
int CSerialController::read(unsigned char* buffer, unsigned int length)
{
assert(buffer != NULL);
assert(m_fd != -1);
if (length == 0U)
return 0;
unsigned int offset = 0U;
while (offset < length) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_fd, &fds);
int n;
if (offset == 0U) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
n = ::select(m_fd + 1, &fds, NULL, NULL, &tv);
if (n == 0)
return 0;
} else {
n = ::select(m_fd + 1, &fds, NULL, NULL, NULL);
}
if (n < 0) {
LogError("Error from select(), errno=%d", errno);
return -1;
}
if (n > 0) {
ssize_t len = ::read(m_fd, buffer + offset, length - offset);
if (len < 0) {
if (errno != EAGAIN) {
LogError("Error from read(), errno=%d", errno);
return -1;
}
}
if (len > 0)
offset += len;
}
}
return length;
}
bool CSerialController::canWrite(){
#if defined(__APPLE__)
fd_set wset;
FD_ZERO(&wset);
FD_SET(m_fd, &wset);
struct timeval timeo;
timeo.tv_sec = 0;
timeo.tv_usec = 0;
int rc = select(m_fd + 1, NULL, &wset, NULL, &timeo);
if (rc >0 && FD_ISSET(m_fd, &wset))
return true;
return false;
#else
return true;
#endif
}
int CSerialController::write(const unsigned char* buffer, unsigned int length)
{
assert(buffer != NULL);
assert(m_fd != -1);
if (length == 0U)
return 0;
unsigned int ptr = 0U;
while (ptr < length) {
ssize_t n = 0U;
if (canWrite())
n = ::write(m_fd, buffer + ptr, length - ptr);
if (n < 0) {
if (errno != EAGAIN) {
LogError("Error returned from write(), errno=%d", errno);
return -1;
}
}
if (n > 0)
ptr += n;
}
return length;
}
void CSerialController::close()
{
assert(m_fd != -1);
::close(m_fd);
m_fd = -1;
}
#endif

@ -0,0 +1,77 @@
/*
* Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017 by Jonathan Naylor G4KLX
* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef SerialController_H
#define SerialController_H
#include "SerialPort.h"
#include <string>
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif
enum SERIAL_SPEED {
SERIAL_1200 = 1200,
SERIAL_2400 = 2400,
SERIAL_4800 = 4800,
SERIAL_9600 = 9600,
SERIAL_19200 = 19200,
SERIAL_38400 = 38400,
SERIAL_76800 = 76800,
SERIAL_115200 = 115200,
SERIAL_230400 = 230400
};
class CSerialController : public ISerialPort {
public:
CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS = false);
virtual ~CSerialController();
virtual bool open();
virtual int read(unsigned char* buffer, unsigned int length);
virtual int write(const unsigned char* buffer, unsigned int length);
virtual void close();
#if defined(__APPLE__)
virtual int setNonblock(bool nonblock);
#endif
private:
std::string m_device;
SERIAL_SPEED m_speed;
bool m_assertRTS;
#if defined(_WIN32) || defined(_WIN64)
HANDLE m_handle;
#else
int m_fd;
#endif
#if defined(_WIN32) || defined(_WIN64)
int readNonblock(unsigned char* buffer, unsigned int length);
#else
bool canWrite();
#endif
};
#endif

@ -0,0 +1,23 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "SerialPort.h"
ISerialPort::~ISerialPort()
{
}

@ -0,0 +1,37 @@
/*
* Copyright (C) 2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef SerialPort_H
#define SerialPort_H
class ISerialPort {
public:
virtual ~ISerialPort() = 0;
virtual bool open() = 0;
virtual int read(unsigned char* buffer, unsigned int length) = 0;
virtual int write(const unsigned char* buffer, unsigned int length) = 0;
virtual void close() = 0;
private:
};
#endif

@ -0,0 +1,84 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "StopWatch.h"
#if defined(_WIN32) || defined(_WIN64)
CStopWatch::CStopWatch() :
m_frequency(),
m_start()
{
::QueryPerformanceFrequency(&m_frequency);
}
CStopWatch::~CStopWatch()
{
}
unsigned long CStopWatch::start()
{
::QueryPerformanceCounter(&m_start);
return (unsigned long)(m_start.QuadPart / m_frequency.QuadPart);
}
unsigned int CStopWatch::elapsed()
{
LARGE_INTEGER now;
::QueryPerformanceCounter(&now);
LARGE_INTEGER temp;
temp.QuadPart = (now.QuadPart - m_start.QuadPart) * 1000;
return (unsigned int)(temp.QuadPart / m_frequency.QuadPart);
}
#else
#include <cstdio>
CStopWatch::CStopWatch() :
m_start()
{
}
CStopWatch::~CStopWatch()
{
}
unsigned long CStopWatch::start()
{
::gettimeofday(&m_start, NULL);
return m_start.tv_usec;
}
unsigned int CStopWatch::elapsed()
{
struct timeval now;
::gettimeofday(&now, NULL);
unsigned int elapsed = (now.tv_sec - m_start.tv_sec) * 1000U;
elapsed += now.tv_usec / 1000U;
elapsed -= m_start.tv_usec / 1000U;
return elapsed;
}
#endif

@ -0,0 +1,46 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(STOPWATCH_H)
#define STOPWATCH_H
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <sys/time.h>
#endif
class CStopWatch
{
public:
CStopWatch();
~CStopWatch();
unsigned long start();
unsigned int elapsed();
private:
#if defined(_WIN32) || defined(_WIN64)
LARGE_INTEGER m_frequency;
LARGE_INTEGER m_start;
#else
struct timeval m_start;
#endif
};
#endif

@ -0,0 +1,76 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Sync.h"
#include "DStarDefines.h"
#include "DMRDefines.h"
#include "YSFDefines.h"
#include "P25Defines.h"
#include <cstdio>
#include <cassert>
#include <cstring>
void CSync::addDStarSync(unsigned char* data)
{
assert(data != NULL);
::memcpy(data + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES);
}
void CSync::addDMRDataSync(unsigned char* data, bool duplex)
{
assert(data != NULL);
if (duplex) {
for (unsigned int i = 0U; i < 7U; i++)
data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | BS_SOURCED_DATA_SYNC[i];
} else {
for (unsigned int i = 0U; i < 7U; i++)
data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | MS_SOURCED_DATA_SYNC[i];
}
}
void CSync::addDMRAudioSync(unsigned char* data, bool duplex)
{
assert(data != NULL);
if (duplex) {
for (unsigned int i = 0U; i < 7U; i++)
data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | BS_SOURCED_AUDIO_SYNC[i];
} else {
for (unsigned int i = 0U; i < 7U; i++)
data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | MS_SOURCED_AUDIO_SYNC[i];
}
}
void CSync::addYSFSync(unsigned char* data)
{
assert(data != NULL);
::memcpy(data, YSF_SYNC_BYTES, YSF_SYNC_LENGTH_BYTES);
}
void CSync::addP25Sync(unsigned char* data)
{
assert(data != NULL);
::memcpy(data, P25_SYNC_BYTES, P25_SYNC_LENGTH_BYTES);
}

@ -0,0 +1,37 @@
/*
* Copyright (C) 2015,2016 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(SYNC_H)
#define SYNC_H
class CSync
{
public:
static void addDStarSync(unsigned char* data);
static void addDMRDataSync(unsigned char* data, bool duplex);
static void addDMRAudioSync(unsigned char* data, bool duplex);
static void addYSFSync(unsigned char* data);
static void addP25Sync(unsigned char* data);
private:
};
#endif

@ -0,0 +1,38 @@
@echo off
REM This pre-build file is for MSVS VC++. It parses the git master hash and
REM converts it into GitVersion.h for compiling into builds. [George M1GEO]
cd %1
setlocal enabledelayedexpansion
set HEADFILE=.git\HEAD
set HASHFILE=0
if exist %HEADFILE% (
for /F "tokens=4 delims=/:" %%a in ('type %HEADFILE%') do set HEADBRANCH=%%a
set HASHFILE=.git\refs\heads\!HEADBRANCH!
echo Found Git HEAD file: %HEADFILE%
echo Git HEAD branch: !HEADBRANCH!
echo Git HASH file: !HASHFILE!
call :USEHASH
) else (
echo No head file :(
call :USENULL
)
goto :EOF
:USENULL
set GITHASH=0000000000000000000000000000000000000000
goto :WRITEGITVERSIONHEADER
:USEHASH
for /f %%i in ('type !HASHFILE!') do set GITHASH=%%i
goto :WRITEGITVERSIONHEADER
:WRITEGITVERSIONHEADER
echo // File contains Git commit ID SHA1 present at buildtime (prebuild.cmd) > GitVersion.h
echo const char *gitversion = "%GITHASH%"; >> GitVersion.h
echo Current Git HASH: %GITHASH%
goto :FINISHED
:FINISHED
echo GitVersion.h written...
Loading…
Cancel
Save

Powered by TurnKey Linux.