From fb47c2e8701fc7239607985f04f4dcae9cf462d7 Mon Sep 17 00:00:00 2001 From: EA5SW <32942778+EA5SW@users.noreply.github.com> Date: Wed, 6 Dec 2017 00:12:51 +0100 Subject: [PATCH] Add files via upload --- DStarControl.cpp | 1106 ++++++++++++++++++++++++++++++ DStarControl.h | 122 ++++ DStarDefines.h | 88 +++ DStarHeader.cpp | 165 +++++ DStarHeader.h | 58 ++ DStarNetwork.cpp | 333 +++++++++ DStarNetwork.h | 71 ++ DStarSlowData.cpp | 158 +++++ DStarSlowData.h | 52 ++ Golay2087.cpp | 262 +++++++ Golay2087.h | 32 + Golay24128.cpp | 1108 ++++++++++++++++++++++++++++++ Golay24128.h | 32 + HD44780.cpp | 941 +++++++++++++++++++++++++ HD44780.h | 165 +++++ HD44780.layouts | 107 +++ Hamming.cpp | 349 ++++++++++ Hamming.h | 43 ++ ISSUES.txt | 7 + JitterBuffer.cpp | 187 +++++ JitterBuffer.h | 67 ++ LCDproc.cpp | 733 ++++++++++++++++++++ LCDproc.h | 82 +++ LICENCE | 340 +++++++++ Log.cpp | 136 ++++ Log.h | 36 + MMDVM.ini | 192 ++++++ MMDVMHost.cpp | 1371 +++++++++++++++++++++++++++++++++++++ MMDVMHost.h | 87 +++ MMDVMHost.sln | 28 + MMDVMHost.vcxproj | 298 ++++++++ MMDVMHost.vcxproj.filters | 428 ++++++++++++ Makefile | 33 + Makefile.EA5SW | 33 + Makefile.Nextion_HS | 33 + Makefile.Pi | 33 + Makefile.Pi.Adafruit | 33 + Makefile.Pi.HD44780 | 33 + Makefile.Pi.OLED | 33 + Makefile.Pi.PCF8574 | 33 + Makefile.Solaris | 33 + Modem.cpp | 1312 +++++++++++++++++++++++++++++++++++ Modem.h | 154 +++++ ModemSerialPort.cpp | 59 ++ ModemSerialPort.h | 42 ++ Mutex.cpp | 65 ++ Mutex.h | 45 ++ NetworkInfo.cpp | 222 ++++++ NetworkInfo.h | 32 + Nextion.cpp | 680 ++++++++++++++++++ Nextion.h | 95 +++ Nextion_HS.cpp | 1149 +++++++++++++++++++++++++++++++ NullDisplay.cpp | 125 ++++ NullDisplay.h | 59 ++ OLED.cpp | 953 ++++++++++++++++++++++++++ OLED.h | 93 +++ OLED.md | 47 ++ P25Audio.cpp | 339 +++++++++ P25Audio.h | 39 ++ P25Control.cpp | 931 +++++++++++++++++++++++++ P25Control.h | 112 +++ P25Data.cpp | 346 ++++++++++ P25Data.h | 77 +++ P25Defines.h | 60 ++ P25LowSpeedData.cpp | 130 ++++ P25LowSpeedData.h | 44 ++ P25NID.cpp | 152 ++++ P25NID.h | 42 ++ P25Network.cpp | 435 ++++++++++++ P25Network.h | 60 ++ P25Utils.cpp | 101 +++ P25Utils.h | 33 + QR1676.cpp | 115 ++++ QR1676.h | 32 + README.HD44780 | 84 +++ README.daemon | 26 + README.md | 172 +---- RS129.cpp | 130 ++++ RS129.h | 30 + RS241213.cpp | 371 ++++++++++ RS241213.h | 36 + RSSI.dat | 11 + RSSIInterpolator.cpp | 91 +++ RSSIInterpolator.h | 39 ++ RingBuffer.h | 154 +++++ SHA256.cpp | 373 ++++++++++ SHA256.h | 73 ++ SerialController.cpp | 475 +++++++++++++ SerialController.h | 77 +++ SerialPort.cpp | 23 + SerialPort.h | 37 + StopWatch.cpp | 84 +++ StopWatch.h | 46 ++ Sync.cpp | 76 ++ Sync.h | 37 + prebuild.cmd | 38 + 96 files changed, 19989 insertions(+), 155 deletions(-) create mode 100644 DStarControl.cpp create mode 100644 DStarControl.h create mode 100644 DStarDefines.h create mode 100644 DStarHeader.cpp create mode 100644 DStarHeader.h create mode 100644 DStarNetwork.cpp create mode 100644 DStarNetwork.h create mode 100644 DStarSlowData.cpp create mode 100644 DStarSlowData.h create mode 100644 Golay2087.cpp create mode 100644 Golay2087.h create mode 100644 Golay24128.cpp create mode 100644 Golay24128.h create mode 100644 HD44780.cpp create mode 100644 HD44780.h create mode 100644 HD44780.layouts create mode 100644 Hamming.cpp create mode 100644 Hamming.h create mode 100644 ISSUES.txt create mode 100644 JitterBuffer.cpp create mode 100644 JitterBuffer.h create mode 100644 LCDproc.cpp create mode 100644 LCDproc.h create mode 100644 LICENCE create mode 100644 Log.cpp create mode 100644 Log.h create mode 100644 MMDVM.ini create mode 100644 MMDVMHost.cpp create mode 100644 MMDVMHost.h create mode 100644 MMDVMHost.sln create mode 100644 MMDVMHost.vcxproj create mode 100644 MMDVMHost.vcxproj.filters create mode 100644 Makefile create mode 100644 Makefile.EA5SW create mode 100644 Makefile.Nextion_HS create mode 100644 Makefile.Pi create mode 100644 Makefile.Pi.Adafruit create mode 100644 Makefile.Pi.HD44780 create mode 100644 Makefile.Pi.OLED create mode 100644 Makefile.Pi.PCF8574 create mode 100644 Makefile.Solaris create mode 100644 Modem.cpp create mode 100644 Modem.h create mode 100644 ModemSerialPort.cpp create mode 100644 ModemSerialPort.h create mode 100644 Mutex.cpp create mode 100644 Mutex.h create mode 100644 NetworkInfo.cpp create mode 100644 NetworkInfo.h create mode 100644 Nextion.cpp create mode 100644 Nextion.h create mode 100644 Nextion_HS.cpp create mode 100644 NullDisplay.cpp create mode 100644 NullDisplay.h create mode 100644 OLED.cpp create mode 100644 OLED.h create mode 100644 OLED.md create mode 100644 P25Audio.cpp create mode 100644 P25Audio.h create mode 100644 P25Control.cpp create mode 100644 P25Control.h create mode 100644 P25Data.cpp create mode 100644 P25Data.h create mode 100644 P25Defines.h create mode 100644 P25LowSpeedData.cpp create mode 100644 P25LowSpeedData.h create mode 100644 P25NID.cpp create mode 100644 P25NID.h create mode 100644 P25Network.cpp create mode 100644 P25Network.h create mode 100644 P25Utils.cpp create mode 100644 P25Utils.h create mode 100644 QR1676.cpp create mode 100644 QR1676.h create mode 100644 README.HD44780 create mode 100644 README.daemon create mode 100644 RS129.cpp create mode 100644 RS129.h create mode 100644 RS241213.cpp create mode 100644 RS241213.h create mode 100644 RSSI.dat create mode 100644 RSSIInterpolator.cpp create mode 100644 RSSIInterpolator.h create mode 100644 RingBuffer.h create mode 100644 SHA256.cpp create mode 100644 SHA256.h create mode 100644 SerialController.cpp create mode 100644 SerialController.h create mode 100644 SerialPort.cpp create mode 100644 SerialPort.h create mode 100644 StopWatch.cpp create mode 100644 StopWatch.h create mode 100644 Sync.cpp create mode 100644 Sync.h create mode 100644 prebuild.cmd diff --git a/DStarControl.cpp b/DStarControl.cpp new file mode 100644 index 0000000..1dbf238 --- /dev/null +++ b/DStarControl.cpp @@ -0,0 +1,1106 @@ +/* + * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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. + */ + +#include "DStarControl.h" +#include "Utils.h" +#include "Sync.h" +#include "Log.h" + +#include +#include +#include +#include +#include + +const unsigned int MAX_SYNC_BIT_ERRORS = 2U; + +bool CallsignCompare(const std::string& arg, const unsigned char* my) +{ + for (unsigned int i = 0U; i < (DSTAR_LONG_CALLSIGN_LENGTH - 1U); i++) { + if (arg.at(i) != my[i]) + return false; + } + + return true; +} + +// #define DUMP_DSTAR + +CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, bool errorReply, const std::vector& blackList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper) : +m_callsign(NULL), +m_gateway(NULL), +m_selfOnly(selfOnly), +m_ackReply(ackReply), +m_errorReply(errorReply), +m_remoteGateway(remoteGateway), +m_blackList(blackList), +m_network(network), +m_display(display), +m_duplex(duplex), +m_queue(5000U, "D-Star Control"), +m_rfHeader(), +m_netHeader(), +m_rfState(RS_RF_LISTENING), +m_netState(RS_NET_IDLE), +m_net(false), +m_slowData(), +m_rfN(0U), +m_netN(0U), +m_networkWatchdog(1000U, 0U, 1500U), +m_rfTimeoutTimer(1000U, timeout), +m_netTimeoutTimer(1000U, timeout), +m_packetTimer(1000U, 0U, 300U), +m_ackTimer(1000U, 0U, ackTime), +m_errTimer(1000U, 0U, ackTime), +m_interval(), +m_elapsed(), +m_rfFrames(0U), +m_netFrames(0U), +m_netLost(0U), +m_fec(), +m_rfBits(1U), +m_netBits(1U), +m_rfErrs(0U), +m_netErrs(0U), +m_lastFrame(NULL), +m_lastFrameValid(false), +m_rssiMapper(rssiMapper), +m_rssi(0U), +m_maxRSSI(0U), +m_minRSSI(0U), +m_aveRSSI(0U), +m_rssiCount(0U), +m_fp(NULL) +{ + assert(display != NULL); + assert(rssiMapper != NULL); + + m_callsign = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH]; + m_gateway = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH]; + + m_lastFrame = new unsigned char[DSTAR_FRAME_LENGTH_BYTES + 1U]; + + std::string call = callsign; + call.resize(DSTAR_LONG_CALLSIGN_LENGTH - 1U, ' '); + std::string mod = module; + mod.resize(1U, ' '); + call.append(mod); + + std::string gate = callsign; + gate.resize(DSTAR_LONG_CALLSIGN_LENGTH - 1U, ' '); + gate.append("G"); + + for (unsigned int i = 0U; i < DSTAR_LONG_CALLSIGN_LENGTH; i++) { + m_callsign[i] = call.at(i); + m_gateway[i] = gate.at(i); + } + + m_interval.start(); +} + +CDStarControl::~CDStarControl() +{ + delete[] m_callsign; + delete[] m_gateway; + delete[] m_lastFrame; +} + +bool CDStarControl::writeModem(unsigned char *data, unsigned int len) +{ + assert(data != NULL); + + unsigned char type = data[0U]; + + if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) { + if (m_rssi != 0U) + LogMessage("D-Star, transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + else + LogMessage("D-Star, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeEndRF(); + return false; + } + + if (type == TAG_LOST && m_rfState == RS_RF_INVALID) { + m_rfState = RS_RF_LISTENING; + + if (m_netState == RS_NET_IDLE) { + if (m_errorReply) + m_errTimer.start(); + + if (m_network != NULL) + m_network->reset(); + } + + return false; + } + + if (type == TAG_LOST) { + m_rfState = RS_RF_LISTENING; + return false; + } + + // Have we got RSSI bytes on the end of a D-Star header? + if (len == (DSTAR_HEADER_LENGTH_BYTES + 3U)) { + uint16_t raw = 0U; + raw |= (data[42U] << 8) & 0xFF00U; + raw |= (data[43U] << 0) & 0x00FFU; + + // Convert the raw RSSI to dBm + int rssi = m_rssiMapper->interpolate(raw); + LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + + // RSSI is always reported as positive + m_rssi = (rssi >= 0) ? rssi : -rssi; + + if (m_rssi > m_minRSSI) + m_minRSSI = m_rssi; + if (m_rssi < m_maxRSSI) + m_maxRSSI = m_rssi; + + m_aveRSSI += m_rssi; + m_rssiCount++; + } + + // Have we got RSSI bytes on the end of D-Star data? + if (len == (DSTAR_FRAME_LENGTH_BYTES + 3U)) { + uint16_t raw = 0U; + raw |= (data[13U] << 8) & 0xFF00U; + raw |= (data[14U] << 0) & 0x00FFU; + + // Convert the raw RSSI to dBm + int rssi = m_rssiMapper->interpolate(raw); + LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + + // RSSI is always reported as positive + m_rssi = (rssi >= 0) ? rssi : -rssi; + + if (m_rssi > m_minRSSI) + m_minRSSI = m_rssi; + if (m_rssi < m_maxRSSI) + m_maxRSSI = m_rssi; + + m_aveRSSI += m_rssi; + m_rssiCount++; + } + + if (type == TAG_HEADER) { + CDStarHeader header(data + 1U); + m_rfHeader = header; + + unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; + header.getMyCall1(my1); + + // Is this a transmission destined for a repeater? + if (!header.isRepeater()) { + LogMessage("D-Star, non repeater RF header received from %8.8s", my1); + m_rfState = RS_RF_INVALID; + return false; + } + + unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH]; + header.getRPTCall1(callsign); + + // Is it for us? + if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { + LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1); + m_rfState = RS_RF_INVALID; + return false; + } + + if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0) { + LogMessage("D-Star, invalid access attempt from %8.8s", my1); + m_rfState = RS_RF_REJECTED; + return false; + } + + if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) { + LogMessage("D-Star, invalid access attempt from %8.8s", my1); + m_rfState = RS_RF_REJECTED; + return false; + } + + unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH]; + header.getRPTCall2(gateway); + + unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; + header.getMyCall2(my2); + + unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; + header.getYourCall(your); + + m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0; + + // Only start the timeout if not already running + if (!m_rfTimeoutTimer.isRunning()) + m_rfTimeoutTimer.start(); + + m_ackTimer.stop(); + m_errTimer.stop(); + + m_rfBits = 1U; + m_rfErrs = 0U; + + m_rfFrames = 1U; + m_rfN = 0U; + + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; + + if (m_duplex) { + // Modify the header + header.setRepeater(false); + header.setRPTCall1(m_callsign); + header.setRPTCall2(m_callsign); + header.get(data + 1U); + + writeQueueHeaderRF(data); + } + + if (m_net) { + // Modify the header + header.setRepeater(false); + header.setRPTCall1(m_callsign); + header.setRPTCall2(m_gateway); + header.get(data + 1U); + + writeNetworkHeaderRF(data); + } + + m_rfState = RS_RF_AUDIO; + + if (m_netState == RS_NET_IDLE) { + m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " "); + m_display->writeDStarRSSI(m_rssi); + } + + LogMessage("D-Star, received RF header from %8.8s/%4.4s to %8.8s", my1, my2, your); + } else if (type == TAG_EOT) { + if (m_rfState == RS_RF_REJECTED) { + m_rfState = RS_RF_LISTENING; + } else if (m_rfState == RS_RF_INVALID) { + m_rfState = RS_RF_LISTENING; + + if (m_netState == RS_NET_IDLE) { + if (m_errorReply) + m_errTimer.start(); + + if (m_network != NULL) + m_network->reset(); + } + + return false; + } else if (m_rfState == RS_RF_AUDIO) { + if (m_net) + writeNetworkDataRF(DSTAR_END_PATTERN_BYTES, 0U, true); + + if (m_duplex) + writeQueueEOTRF(); + + if (m_rssi != 0U) + LogMessage("D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + else + LogMessage("D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + + writeEndRF(); + } + + return false; + } else if (type == TAG_DATA) { + if (m_rfState == RS_RF_REJECTED) { + return false; + } else if (m_rfState == RS_RF_INVALID) { + return false; + } else if (m_rfState == RS_RF_LISTENING) { + // The sync is regenerated by the modem so can do exact match + if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0) { + m_slowData.start(); + m_rfState = RS_RF_LATE_ENTRY; + } + + return false; + } else if (m_rfState == RS_RF_AUDIO) { + unsigned int errors = 0U; + if (!m_rfHeader.isDataPacket()) { + errors = m_fec.regenerateDStar(data + 1U); + m_display->writeDStarBER(float(errors) / 0.48F); + LogDebug("D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F); + m_rfErrs += errors; + } + + m_rfBits += 48U; + m_rfFrames++; + + // The sync is regenerated by the modem so can do exact match + if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0) + m_rfN = 0U; + + // Regenerate the sync and send the RSSI data to the display + if (m_rfN == 0U) { + CSync::addDStarSync(data + 1U); + m_display->writeDStarRSSI(m_rssi); + } + + if (m_net) + writeNetworkDataRF(data, errors, false); + + if (m_duplex) { + blankDTMF(data + 1U); + writeQueueDataRF(data); + } + + m_rfN = (m_rfN + 1U) % 21U; + } else if (m_rfState == RS_RF_LATE_ENTRY) { + // The sync is regenerated by the modem so can do exact match + if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0) { + m_slowData.reset(); + return false; + } + + CDStarHeader* header = m_slowData.add(data + 1U); + if (header == NULL) + return false; + + m_rfHeader = *header; + + unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; + header->getMyCall1(my1); + + // Is this a transmission destined for a repeater? + if (!header->isRepeater()) { + LogMessage("D-Star, non repeater RF header received from %8.8s", my1); + m_rfState = RS_RF_INVALID; + delete header; + return false; + } + + unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH]; + header->getRPTCall1(callsign); + + // Is it for us? + if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { + LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1); + m_rfState = RS_RF_INVALID; + delete header; + return false; + } + + if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0) { + LogMessage("D-Star, invalid access attempt from %8.8s", my1); + m_rfState = RS_RF_REJECTED; + delete header; + return false; + } + + if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) { + LogMessage("D-Star, invalid access attempt from %8.8s", my1); + m_rfState = RS_RF_REJECTED; + delete header; + return false; + } + + unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH]; + header->getRPTCall2(gateway); + + unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; + header->getMyCall2(my2); + + unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; + header->getYourCall(your); + + m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0; + + // Only reset the timeout if the timeout is not running + if (!m_rfTimeoutTimer.isRunning()) + m_rfTimeoutTimer.start(); + + // Create a dummy start frame to replace the received frame + m_ackTimer.stop(); + m_errTimer.stop(); + + m_rfBits = 1U; + m_rfErrs = 0U; + + m_rfN = 0U; + m_rfFrames = 1U; + + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; + + if (m_duplex) { + unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U]; + start[0U] = TAG_HEADER; + + // Modify the header + header->setRepeater(false); + header->setRPTCall1(m_callsign); + header->setRPTCall2(m_callsign); + header->get(start + 1U); + + writeQueueHeaderRF(start); + } + + if (m_net) { + unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U]; + start[0U] = TAG_HEADER; + + // Modify the header + header->setRepeater(false); + header->setRPTCall1(m_callsign); + header->setRPTCall2(m_gateway); + header->get(start + 1U); + + writeNetworkHeaderRF(start); + } + + delete header; + + unsigned int errors = 0U; + if (!m_rfHeader.isDataPacket()) { + errors = m_fec.regenerateDStar(data + 1U); + LogDebug("D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F); + m_rfErrs += errors; + } + + m_rfBits += 48U; + + if (m_net) + writeNetworkDataRF(data, errors, false); + + if (m_duplex) { + blankDTMF(data + 1U); + writeQueueDataRF(data); + } + + m_rfState = RS_RF_AUDIO; + + m_rfN = (m_rfN + 1U) % 21U; + + if (m_netState == RS_NET_IDLE) { + m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " "); + m_display->writeDStarRSSI(m_rssi); + m_display->writeDStarBER(float(errors) / 0.48F); + } + + LogMessage("D-Star, received RF late entry from %8.8s/%4.4s to %8.8s", my1, my2, your); + } + } else { + CUtils::dump("D-Star, unknown data from modem", data, DSTAR_FRAME_LENGTH_BYTES + 1U); + } + + return true; +} + +unsigned int CDStarControl::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 CDStarControl::writeEndRF() +{ + m_rfState = RS_RF_LISTENING; + + if (m_netState == RS_NET_IDLE) { + m_display->clearDStar(); + + m_ackTimer.start(); + + if (m_network != NULL) + m_network->reset(); + } else { + m_rfTimeoutTimer.stop(); + } +} + +void CDStarControl::writeEndNet() +{ + m_netState = RS_NET_IDLE; + + m_lastFrameValid = false; + + m_display->clearDStar(); + + m_netTimeoutTimer.stop(); + m_networkWatchdog.stop(); + m_packetTimer.stop(); + + if (m_network != NULL) + m_network->reset(); + +#if defined(DUMP_DSTAR) + closeFile(); +#endif +} + +void CDStarControl::writeNetwork() +{ + assert(m_network != NULL); + + unsigned char data[DSTAR_HEADER_LENGTH_BYTES + 2U]; + unsigned int length = m_network->read(data, DSTAR_HEADER_LENGTH_BYTES + 2U); + if (length == 0U) + return; + + if (m_rfState == RS_RF_AUDIO && m_netState == RS_NET_IDLE) + return; + + m_networkWatchdog.start(); + + unsigned char type = data[0U]; + + if (type == TAG_HEADER) { + if (m_netState != RS_NET_IDLE) + return; + + CDStarHeader header(data + 1U); + + unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; + header.getMyCall1(my1); + + unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; + header.getMyCall2(my2); + + unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; + header.getYourCall(your); + + m_netHeader = header; + + m_netTimeoutTimer.start(); + m_packetTimer.start(); + m_ackTimer.stop(); + m_errTimer.stop(); + + m_lastFrameValid = false; + + m_netFrames = 0U; + m_netLost = 0U; + + m_netN = 20U; + + m_netBits = 1U; + m_netErrs = 0U; + + if (m_remoteGateway) { + header.setRepeater(true); + header.setRPTCall1(m_callsign); + header.setRPTCall2(m_callsign); + header.get(data + 1U); + } + + writeQueueHeaderNet(data); + +#if defined(DUMP_DSTAR) + openFile(); + writeFile(data + 1U, length - 1U); +#endif + m_netState = RS_NET_AUDIO; + + LINK_STATUS status = LS_NONE; + unsigned char reflector[DSTAR_LONG_CALLSIGN_LENGTH]; + m_network->getStatus(status, reflector); + if (status == LS_LINKED_DEXTRA || status == LS_LINKED_DPLUS || status == LS_LINKED_DCS || status == LS_LINKED_CCS || status == LS_LINKED_LOOPBACK) { + m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "N", (char*) reflector); + LogMessage("D-Star, received network header from %8.8s/%4.4s to %8.8s via %8.8s", my1, my2, your, reflector); + } else { + m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "N", (char*) " "); + LogMessage("D-Star, received network header from %8.8s/%4.4s to %8.8s", my1, my2, your); + } + + // Something just above here introduces a large delay forcing erroneous(?) insertion of silence packets. + // Starting the elapsed timer here instead of the commented out position above solves that. + m_elapsed.start(); + + } else if (type == TAG_EOT) { + if (m_netState != RS_NET_AUDIO) + return; + + writeQueueEOTNet(); + + data[1U] = TAG_EOT; + +#if defined(DUMP_DSTAR) + writeFile(data + 1U, length - 1U); + closeFile(); +#endif + // We've received the header and EOT haven't we? + m_netFrames += 2U; + LogMessage("D-Star, received network end of transmission, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + + writeEndNet(); + } else if (type == TAG_DATA) { + if (m_netState != RS_NET_AUDIO) + return; + + unsigned char n = data[1U]; + + unsigned int errors = 0U; + if (!m_netHeader.isDataPacket()) + errors = m_fec.regenerateDStar(data + 2U); + + blankDTMF(data + 2U); + + data[1U] = TAG_DATA; + + // Insert silence and reject if in the past + bool ret = insertSilence(data + 1U, n); + if (!ret) + return; + + m_netErrs += errors; + m_netBits += 48U; + + m_netN = n; + + // Regenerate the sync + if (n == 0U) + CSync::addDStarSync(data + 2U); + + m_packetTimer.start(); + m_netFrames++; + +#if defined(DUMP_DSTAR) + writeFile(data + 1U, length - 1U); +#endif + writeQueueDataNet(data + 1U); + } else { + CUtils::dump("D-Star, unknown data from network", data, DSTAR_FRAME_LENGTH_BYTES + 1U); + } +} + +void CDStarControl::clock() +{ + unsigned int ms = m_interval.elapsed(); + m_interval.start(); + + if (m_network != NULL) + writeNetwork(); + + m_ackTimer.clock(ms); + if (m_ackTimer.isRunning() && m_ackTimer.hasExpired()) { + sendAck(); + m_ackTimer.stop(); + } + + m_errTimer.clock(ms); + if (m_errTimer.isRunning() && m_errTimer.hasExpired()) { + sendError(); + m_errTimer.stop(); + } + + m_rfTimeoutTimer.clock(ms); + m_netTimeoutTimer.clock(ms); + + if (m_netState == RS_NET_AUDIO) { + m_networkWatchdog.clock(ms); + + if (m_networkWatchdog.hasExpired()) { + // We're received the header haven't we? + m_netFrames += 1U; + LogMessage("D-Star, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + writeEndNet(); +#if defined(DUMP_DSTAR) + closeFile(); +#endif + } + } + + if (m_netState == RS_NET_AUDIO) { + m_packetTimer.clock(ms); + + if (m_packetTimer.isRunning() && m_packetTimer.hasExpired()) { + unsigned int elapsed = m_elapsed.elapsed(); + unsigned int frames = elapsed / DSTAR_FRAME_TIME; + + if (frames > m_netFrames) { + unsigned int count = frames - m_netFrames; + if (count > 15U) { + LogDebug("D-Star, lost audio for 300ms filling in, elapsed: %ums, expected: %u, received: %u", elapsed, frames, m_netFrames); + insertSilence(count - 2U); + } + } + + m_packetTimer.start(); + } + } +} + +void CDStarControl::writeQueueHeaderRF(const unsigned char *data) +{ + assert(data != NULL); + + if (m_netState != RS_NET_IDLE) + return; + + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + unsigned char len = DSTAR_HEADER_LENGTH_BYTES + 1U; + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("D-Star, overflow in the D-Star RF queue"); + return; + } + + m_queue.addData(&len, 1U); + + m_queue.addData(data, len); +} + +void CDStarControl::writeQueueDataRF(const unsigned char *data) +{ + assert(data != NULL); + + if (m_netState != RS_NET_IDLE) + return; + + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + unsigned char len = DSTAR_FRAME_LENGTH_BYTES + 1U; + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("D-Star, overflow in the D-Star RF queue"); + return; + } + + m_queue.addData(&len, 1U); + + m_queue.addData(data, len); +} + +void CDStarControl::writeQueueEOTRF() +{ + if (m_netState != RS_NET_IDLE) + return; + + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + unsigned char len = 1U; + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("D-Star, overflow in the D-Star RF queue"); + return; + } + + m_queue.addData(&len, 1U); + + unsigned char data = TAG_EOT; + m_queue.addData(&data, len); +} + +void CDStarControl::writeQueueHeaderNet(const unsigned char *data) +{ + assert(data != NULL); + + if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) + return; + + unsigned char len = DSTAR_HEADER_LENGTH_BYTES + 1U; + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("D-Star, overflow in the D-Star RF queue"); + return; + } + + m_queue.addData(&len, 1U); + + m_queue.addData(data, len); +} + +void CDStarControl::writeQueueDataNet(const unsigned char *data) +{ + assert(data != NULL); + + if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) + return; + + unsigned char len = DSTAR_FRAME_LENGTH_BYTES + 1U; + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("D-Star, overflow in the D-Star RF queue"); + return; + } + + m_queue.addData(&len, 1U); + + m_queue.addData(data, len); +} + +void CDStarControl::writeQueueEOTNet() +{ + if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) + return; + + unsigned char len = 1U; + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("D-Star, overflow in the D-Star RF queue"); + return; + } + + m_queue.addData(&len, 1U); + + unsigned char data = TAG_EOT; + m_queue.addData(&data, len); +} + +void CDStarControl::writeNetworkHeaderRF(const unsigned char* data) +{ + assert(data != NULL); + + if (m_network == NULL) + return; + + // Don't send to the network if the timeout has expired + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + m_network->writeHeader(data + 1U, DSTAR_HEADER_LENGTH_BYTES, m_netState != RS_NET_IDLE); +} + +void CDStarControl::writeNetworkDataRF(const unsigned char* data, unsigned int errors, bool end) +{ + assert(data != NULL); + + if (m_network == NULL) + return; + + // Don't send to the network if the timeout has expired + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + m_network->writeData(data + 1U, DSTAR_FRAME_LENGTH_BYTES, errors, end, m_netState != RS_NET_IDLE); +} + +bool CDStarControl::openFile() +{ + if (m_fp != NULL) + return true; + + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + + char name[100U]; + ::sprintf(name, "DStar_%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("DSTAR", 1U, 4U, m_fp); + + return true; +} + +bool CDStarControl::writeFile(const unsigned char* data, unsigned int length) +{ + if (m_fp == NULL) + return false; + + ::fwrite(data, 1U, length, m_fp); + + return true; +} + +void CDStarControl::closeFile() +{ + if (m_fp != NULL) { + ::fclose(m_fp); + m_fp = NULL; + } +} + +bool CDStarControl::insertSilence(const unsigned char* data, unsigned char seqNo) +{ + assert(data != NULL); + + // Check to see if we have any spaces to fill? + unsigned int oldSeqNo = (m_netN + 1U) % 21U; + if (oldSeqNo == seqNo) { + // Just copy the data, nothing else to do here + ::memcpy(m_lastFrame, data, DSTAR_FRAME_LENGTH_BYTES + 1U); + m_lastFrameValid = true; + return true; + } + + unsigned int count; + if (seqNo > oldSeqNo) + count = seqNo - oldSeqNo; + else + count = (21U + seqNo) - oldSeqNo; + + if (count >= 10U) + return false; + + insertSilence(count); + + ::memcpy(m_lastFrame, data, DSTAR_FRAME_LENGTH_BYTES + 1U); + m_lastFrameValid = true; + + return true; +} + +void CDStarControl::insertSilence(unsigned int count) +{ + unsigned char n = (m_netN + 1U) % 21U; + + for (unsigned int i = 0U; i < count; i++) { + if (i < 3U && m_lastFrameValid) { + if (n == 0U) { + ::memcpy(m_lastFrame + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U, DSTAR_NULL_SLOW_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES); + writeQueueDataNet(m_lastFrame); + } else { + ::memcpy(m_lastFrame + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U, DSTAR_NULL_SLOW_DATA_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES); + writeQueueDataNet(m_lastFrame); + } + } else { + m_lastFrameValid = false; + + if (n == 0U) + writeQueueDataNet(DSTAR_NULL_FRAME_SYNC_BYTES); + else + writeQueueDataNet(DSTAR_NULL_FRAME_DATA_BYTES); + } + + m_netN = n; + + m_netFrames++; + m_netLost++; + + n = (n + 1U) % 21U; + } +} + +void CDStarControl::blankDTMF(unsigned char* data) const +{ + assert(data != NULL); + + // DTMF begins with these byte values + if ((data[0] & DSTAR_DTMF_MASK[0]) == DSTAR_DTMF_SIG[0] && (data[1] & DSTAR_DTMF_MASK[1]) == DSTAR_DTMF_SIG[1] && + (data[2] & DSTAR_DTMF_MASK[2]) == DSTAR_DTMF_SIG[2] && (data[3] & DSTAR_DTMF_MASK[3]) == DSTAR_DTMF_SIG[3] && + (data[4] & DSTAR_DTMF_MASK[4]) == DSTAR_DTMF_SIG[4] && (data[5] & DSTAR_DTMF_MASK[5]) == DSTAR_DTMF_SIG[5] && + (data[6] & DSTAR_DTMF_MASK[6]) == DSTAR_DTMF_SIG[6] && (data[7] & DSTAR_DTMF_MASK[7]) == DSTAR_DTMF_SIG[7] && + (data[8] & DSTAR_DTMF_MASK[8]) == DSTAR_DTMF_SIG[8]) + ::memcpy(data, DSTAR_NULL_AMBE_DATA_BYTES, DSTAR_VOICE_FRAME_LENGTH_BYTES); +} + +void CDStarControl::sendAck() +{ + m_rfTimeoutTimer.stop(); + + if (!m_ackReply) + return; + + unsigned char user[DSTAR_LONG_CALLSIGN_LENGTH]; + m_rfHeader.getMyCall1(user); + + CDStarHeader header; + header.setUnavailable(true); + header.setMyCall1(m_callsign); + header.setYourCall(user); + header.setRPTCall1(m_gateway); + header.setRPTCall2(m_callsign); + + unsigned char data[DSTAR_HEADER_LENGTH_BYTES + 1U]; + header.get(data + 1U); + data[0U] = TAG_HEADER; + + writeQueueHeaderRF(data); + + writeQueueDataRF(DSTAR_NULL_FRAME_SYNC_BYTES); + + LINK_STATUS status = LS_NONE; + unsigned char reflector[DSTAR_LONG_CALLSIGN_LENGTH]; + if (m_network != NULL) + m_network->getStatus(status, reflector); + + char text[40U]; + if (status == LS_LINKED_DEXTRA || status == LS_LINKED_DPLUS || status == LS_LINKED_DCS || status == LS_LINKED_CCS || status == LS_LINKED_LOOPBACK) + ::sprintf(text, "%-8.8s BER: %.1f%% ", reflector, float(m_rfErrs * 100U) / float(m_rfBits)); + else + ::sprintf(text, "BER: %.1f%% ", float(m_rfErrs * 100U) / float(m_rfBits)); + m_slowData.setText(text); + + ::memcpy(data, DSTAR_NULL_FRAME_DATA_BYTES, DSTAR_FRAME_LENGTH_BYTES + 1U); + + for (unsigned int i = 0U; i < 19U; i++) { + m_slowData.get(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES); + writeQueueDataRF(data); + } + + writeQueueEOTRF(); +} + +void CDStarControl::sendError() +{ + unsigned char user[DSTAR_LONG_CALLSIGN_LENGTH]; + m_rfHeader.getMyCall1(user); + + CDStarHeader header; + header.setUnavailable(true); + header.setMyCall1(m_callsign); + header.setYourCall(user); + header.setRPTCall1(m_callsign); + header.setRPTCall2(m_callsign); + + unsigned char data[DSTAR_HEADER_LENGTH_BYTES + 1U]; + header.get(data + 1U); + data[0U] = TAG_HEADER; + + writeQueueHeaderRF(data); + + writeQueueDataRF(DSTAR_NULL_FRAME_SYNC_BYTES); + + LINK_STATUS status = LS_NONE; + unsigned char reflector[DSTAR_LONG_CALLSIGN_LENGTH]; + if (m_network != NULL) + m_network->getStatus(status, reflector); + + char text[40U]; + if (status == LS_LINKED_DEXTRA || status == LS_LINKED_DPLUS || status == LS_LINKED_DCS || status == LS_LINKED_CCS || status == LS_LINKED_LOOPBACK) + ::sprintf(text, "%-8.8s BER: %.1f%% ", reflector, float(m_rfErrs * 100U) / float(m_rfBits)); + else + ::sprintf(text, "BER: %.1f%% ", float(m_rfErrs * 100U) / float(m_rfBits)); + m_slowData.setText(text); + + ::memcpy(data, DSTAR_NULL_FRAME_DATA_BYTES, DSTAR_FRAME_LENGTH_BYTES + 1U); + + for (unsigned int i = 0U; i < 19U; i++) { + m_slowData.get(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES); + writeQueueDataRF(data); + } + + writeQueueEOTRF(); +} diff --git a/DStarControl.h b/DStarControl.h new file mode 100644 index 0000000..b47f438 --- /dev/null +++ b/DStarControl.h @@ -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 +#include + +class CDStarControl { +public: + CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, bool errorReply, const std::vector& 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 m_blackList; + CDStarNetwork* m_network; + CDisplay* m_display; + bool m_duplex; + CRingBuffer 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 diff --git a/DStarDefines.h b/DStarDefines.h new file mode 100644 index 0000000..498b35e --- /dev/null +++ b/DStarDefines.h @@ -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 diff --git a/DStarHeader.cpp b/DStarHeader.cpp new file mode 100644 index 0000000..e06ed1a --- /dev/null +++ b/DStarHeader.cpp @@ -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 +#include +#include + +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); +} diff --git a/DStarHeader.h b/DStarHeader.h new file mode 100644 index 0000000..867f1da --- /dev/null +++ b/DStarHeader.h @@ -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 diff --git a/DStarNetwork.cpp b/DStarNetwork.cpp new file mode 100644 index 0000000..443fcac --- /dev/null +++ b/DStarNetwork.cpp @@ -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 +#include +#include + +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); +} diff --git a/DStarNetwork.h b/DStarNetwork.h new file mode 100644 index 0000000..aeebbbf --- /dev/null +++ b/DStarNetwork.h @@ -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 +#include + +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 m_buffer; + CTimer m_pollTimer; + LINK_STATUS m_linkStatus; + unsigned char* m_linkReflector; + + bool writePoll(const char* text); +}; + +#endif diff --git a/DStarSlowData.cpp b/DStarSlowData.cpp new file mode 100644 index 0000000..d33ca7e --- /dev/null +++ b/DStarSlowData.cpp @@ -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 +#include +#include + +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]; + } +} diff --git a/DStarSlowData.h b/DStarSlowData.h new file mode 100644 index 0000000..52e0f5c --- /dev/null +++ b/DStarSlowData.h @@ -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 diff --git a/Golay2087.cpp b/Golay2087.cpp new file mode 100644 index 0000000..61942d8 --- /dev/null +++ b/Golay2087.cpp @@ -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 +#include + +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; +} diff --git a/Golay2087.h b/Golay2087.h new file mode 100644 index 0000000..d54daed --- /dev/null +++ b/Golay2087.h @@ -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 diff --git a/Golay24128.cpp b/Golay24128.cpp new file mode 100644 index 0000000..417da00 --- /dev/null +++ b/Golay24128.cpp @@ -0,0 +1,1108 @@ +/* + * Copyright (C) 2010,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2002 by Robert H. Morelos-Zaragoza. All rights reserved. + */ + +#include "Golay24128.h" + +#include +#include + +const unsigned int ENCODING_TABLE_23127[] = { + 0x000000U, 0x0018EAU, 0x00293EU, 0x0031D4U, 0x004A96U, 0x00527CU, 0x0063A8U, 0x007B42U, 0x008DC6U, 0x00952CU, + 0x00A4F8U, 0x00BC12U, 0x00C750U, 0x00DFBAU, 0x00EE6EU, 0x00F684U, 0x010366U, 0x011B8CU, 0x012A58U, 0x0132B2U, + 0x0149F0U, 0x01511AU, 0x0160CEU, 0x017824U, 0x018EA0U, 0x01964AU, 0x01A79EU, 0x01BF74U, 0x01C436U, 0x01DCDCU, + 0x01ED08U, 0x01F5E2U, 0x0206CCU, 0x021E26U, 0x022FF2U, 0x023718U, 0x024C5AU, 0x0254B0U, 0x026564U, 0x027D8EU, + 0x028B0AU, 0x0293E0U, 0x02A234U, 0x02BADEU, 0x02C19CU, 0x02D976U, 0x02E8A2U, 0x02F048U, 0x0305AAU, 0x031D40U, + 0x032C94U, 0x03347EU, 0x034F3CU, 0x0357D6U, 0x036602U, 0x037EE8U, 0x03886CU, 0x039086U, 0x03A152U, 0x03B9B8U, + 0x03C2FAU, 0x03DA10U, 0x03EBC4U, 0x03F32EU, 0x040D98U, 0x041572U, 0x0424A6U, 0x043C4CU, 0x04470EU, 0x045FE4U, + 0x046E30U, 0x0476DAU, 0x04805EU, 0x0498B4U, 0x04A960U, 0x04B18AU, 0x04CAC8U, 0x04D222U, 0x04E3F6U, 0x04FB1CU, + 0x050EFEU, 0x051614U, 0x0527C0U, 0x053F2AU, 0x054468U, 0x055C82U, 0x056D56U, 0x0575BCU, 0x058338U, 0x059BD2U, + 0x05AA06U, 0x05B2ECU, 0x05C9AEU, 0x05D144U, 0x05E090U, 0x05F87AU, 0x060B54U, 0x0613BEU, 0x06226AU, 0x063A80U, + 0x0641C2U, 0x065928U, 0x0668FCU, 0x067016U, 0x068692U, 0x069E78U, 0x06AFACU, 0x06B746U, 0x06CC04U, 0x06D4EEU, + 0x06E53AU, 0x06FDD0U, 0x070832U, 0x0710D8U, 0x07210CU, 0x0739E6U, 0x0742A4U, 0x075A4EU, 0x076B9AU, 0x077370U, + 0x0785F4U, 0x079D1EU, 0x07ACCAU, 0x07B420U, 0x07CF62U, 0x07D788U, 0x07E65CU, 0x07FEB6U, 0x0803DAU, 0x081B30U, + 0x082AE4U, 0x08320EU, 0x08494CU, 0x0851A6U, 0x086072U, 0x087898U, 0x088E1CU, 0x0896F6U, 0x08A722U, 0x08BFC8U, + 0x08C48AU, 0x08DC60U, 0x08EDB4U, 0x08F55EU, 0x0900BCU, 0x091856U, 0x092982U, 0x093168U, 0x094A2AU, 0x0952C0U, + 0x096314U, 0x097BFEU, 0x098D7AU, 0x099590U, 0x09A444U, 0x09BCAEU, 0x09C7ECU, 0x09DF06U, 0x09EED2U, 0x09F638U, + 0x0A0516U, 0x0A1DFCU, 0x0A2C28U, 0x0A34C2U, 0x0A4F80U, 0x0A576AU, 0x0A66BEU, 0x0A7E54U, 0x0A88D0U, 0x0A903AU, + 0x0AA1EEU, 0x0AB904U, 0x0AC246U, 0x0ADAACU, 0x0AEB78U, 0x0AF392U, 0x0B0670U, 0x0B1E9AU, 0x0B2F4EU, 0x0B37A4U, + 0x0B4CE6U, 0x0B540CU, 0x0B65D8U, 0x0B7D32U, 0x0B8BB6U, 0x0B935CU, 0x0BA288U, 0x0BBA62U, 0x0BC120U, 0x0BD9CAU, + 0x0BE81EU, 0x0BF0F4U, 0x0C0E42U, 0x0C16A8U, 0x0C277CU, 0x0C3F96U, 0x0C44D4U, 0x0C5C3EU, 0x0C6DEAU, 0x0C7500U, + 0x0C8384U, 0x0C9B6EU, 0x0CAABAU, 0x0CB250U, 0x0CC912U, 0x0CD1F8U, 0x0CE02CU, 0x0CF8C6U, 0x0D0D24U, 0x0D15CEU, + 0x0D241AU, 0x0D3CF0U, 0x0D47B2U, 0x0D5F58U, 0x0D6E8CU, 0x0D7666U, 0x0D80E2U, 0x0D9808U, 0x0DA9DCU, 0x0DB136U, + 0x0DCA74U, 0x0DD29EU, 0x0DE34AU, 0x0DFBA0U, 0x0E088EU, 0x0E1064U, 0x0E21B0U, 0x0E395AU, 0x0E4218U, 0x0E5AF2U, + 0x0E6B26U, 0x0E73CCU, 0x0E8548U, 0x0E9DA2U, 0x0EAC76U, 0x0EB49CU, 0x0ECFDEU, 0x0ED734U, 0x0EE6E0U, 0x0EFE0AU, + 0x0F0BE8U, 0x0F1302U, 0x0F22D6U, 0x0F3A3CU, 0x0F417EU, 0x0F5994U, 0x0F6840U, 0x0F70AAU, 0x0F862EU, 0x0F9EC4U, + 0x0FAF10U, 0x0FB7FAU, 0x0FCCB8U, 0x0FD452U, 0x0FE586U, 0x0FFD6CU, 0x1007B4U, 0x101F5EU, 0x102E8AU, 0x103660U, + 0x104D22U, 0x1055C8U, 0x10641CU, 0x107CF6U, 0x108A72U, 0x109298U, 0x10A34CU, 0x10BBA6U, 0x10C0E4U, 0x10D80EU, + 0x10E9DAU, 0x10F130U, 0x1104D2U, 0x111C38U, 0x112DECU, 0x113506U, 0x114E44U, 0x1156AEU, 0x11677AU, 0x117F90U, + 0x118914U, 0x1191FEU, 0x11A02AU, 0x11B8C0U, 0x11C382U, 0x11DB68U, 0x11EABCU, 0x11F256U, 0x120178U, 0x121992U, + 0x122846U, 0x1230ACU, 0x124BEEU, 0x125304U, 0x1262D0U, 0x127A3AU, 0x128CBEU, 0x129454U, 0x12A580U, 0x12BD6AU, + 0x12C628U, 0x12DEC2U, 0x12EF16U, 0x12F7FCU, 0x13021EU, 0x131AF4U, 0x132B20U, 0x1333CAU, 0x134888U, 0x135062U, + 0x1361B6U, 0x13795CU, 0x138FD8U, 0x139732U, 0x13A6E6U, 0x13BE0CU, 0x13C54EU, 0x13DDA4U, 0x13EC70U, 0x13F49AU, + 0x140A2CU, 0x1412C6U, 0x142312U, 0x143BF8U, 0x1440BAU, 0x145850U, 0x146984U, 0x14716EU, 0x1487EAU, 0x149F00U, + 0x14AED4U, 0x14B63EU, 0x14CD7CU, 0x14D596U, 0x14E442U, 0x14FCA8U, 0x15094AU, 0x1511A0U, 0x152074U, 0x15389EU, + 0x1543DCU, 0x155B36U, 0x156AE2U, 0x157208U, 0x15848CU, 0x159C66U, 0x15ADB2U, 0x15B558U, 0x15CE1AU, 0x15D6F0U, + 0x15E724U, 0x15FFCEU, 0x160CE0U, 0x16140AU, 0x1625DEU, 0x163D34U, 0x164676U, 0x165E9CU, 0x166F48U, 0x1677A2U, + 0x168126U, 0x1699CCU, 0x16A818U, 0x16B0F2U, 0x16CBB0U, 0x16D35AU, 0x16E28EU, 0x16FA64U, 0x170F86U, 0x17176CU, + 0x1726B8U, 0x173E52U, 0x174510U, 0x175DFAU, 0x176C2EU, 0x1774C4U, 0x178240U, 0x179AAAU, 0x17AB7EU, 0x17B394U, + 0x17C8D6U, 0x17D03CU, 0x17E1E8U, 0x17F902U, 0x18046EU, 0x181C84U, 0x182D50U, 0x1835BAU, 0x184EF8U, 0x185612U, + 0x1867C6U, 0x187F2CU, 0x1889A8U, 0x189142U, 0x18A096U, 0x18B87CU, 0x18C33EU, 0x18DBD4U, 0x18EA00U, 0x18F2EAU, + 0x190708U, 0x191FE2U, 0x192E36U, 0x1936DCU, 0x194D9EU, 0x195574U, 0x1964A0U, 0x197C4AU, 0x198ACEU, 0x199224U, + 0x19A3F0U, 0x19BB1AU, 0x19C058U, 0x19D8B2U, 0x19E966U, 0x19F18CU, 0x1A02A2U, 0x1A1A48U, 0x1A2B9CU, 0x1A3376U, + 0x1A4834U, 0x1A50DEU, 0x1A610AU, 0x1A79E0U, 0x1A8F64U, 0x1A978EU, 0x1AA65AU, 0x1ABEB0U, 0x1AC5F2U, 0x1ADD18U, + 0x1AECCCU, 0x1AF426U, 0x1B01C4U, 0x1B192EU, 0x1B28FAU, 0x1B3010U, 0x1B4B52U, 0x1B53B8U, 0x1B626CU, 0x1B7A86U, + 0x1B8C02U, 0x1B94E8U, 0x1BA53CU, 0x1BBDD6U, 0x1BC694U, 0x1BDE7EU, 0x1BEFAAU, 0x1BF740U, 0x1C09F6U, 0x1C111CU, + 0x1C20C8U, 0x1C3822U, 0x1C4360U, 0x1C5B8AU, 0x1C6A5EU, 0x1C72B4U, 0x1C8430U, 0x1C9CDAU, 0x1CAD0EU, 0x1CB5E4U, + 0x1CCEA6U, 0x1CD64CU, 0x1CE798U, 0x1CFF72U, 0x1D0A90U, 0x1D127AU, 0x1D23AEU, 0x1D3B44U, 0x1D4006U, 0x1D58ECU, + 0x1D6938U, 0x1D71D2U, 0x1D8756U, 0x1D9FBCU, 0x1DAE68U, 0x1DB682U, 0x1DCDC0U, 0x1DD52AU, 0x1DE4FEU, 0x1DFC14U, + 0x1E0F3AU, 0x1E17D0U, 0x1E2604U, 0x1E3EEEU, 0x1E45ACU, 0x1E5D46U, 0x1E6C92U, 0x1E7478U, 0x1E82FCU, 0x1E9A16U, + 0x1EABC2U, 0x1EB328U, 0x1EC86AU, 0x1ED080U, 0x1EE154U, 0x1EF9BEU, 0x1F0C5CU, 0x1F14B6U, 0x1F2562U, 0x1F3D88U, + 0x1F46CAU, 0x1F5E20U, 0x1F6FF4U, 0x1F771EU, 0x1F819AU, 0x1F9970U, 0x1FA8A4U, 0x1FB04EU, 0x1FCB0CU, 0x1FD3E6U, + 0x1FE232U, 0x1FFAD8U, 0x200F68U, 0x201782U, 0x202656U, 0x203EBCU, 0x2045FEU, 0x205D14U, 0x206CC0U, 0x20742AU, + 0x2082AEU, 0x209A44U, 0x20AB90U, 0x20B37AU, 0x20C838U, 0x20D0D2U, 0x20E106U, 0x20F9ECU, 0x210C0EU, 0x2114E4U, + 0x212530U, 0x213DDAU, 0x214698U, 0x215E72U, 0x216FA6U, 0x21774CU, 0x2181C8U, 0x219922U, 0x21A8F6U, 0x21B01CU, + 0x21CB5EU, 0x21D3B4U, 0x21E260U, 0x21FA8AU, 0x2209A4U, 0x22114EU, 0x22209AU, 0x223870U, 0x224332U, 0x225BD8U, + 0x226A0CU, 0x2272E6U, 0x228462U, 0x229C88U, 0x22AD5CU, 0x22B5B6U, 0x22CEF4U, 0x22D61EU, 0x22E7CAU, 0x22FF20U, + 0x230AC2U, 0x231228U, 0x2323FCU, 0x233B16U, 0x234054U, 0x2358BEU, 0x23696AU, 0x237180U, 0x238704U, 0x239FEEU, + 0x23AE3AU, 0x23B6D0U, 0x23CD92U, 0x23D578U, 0x23E4ACU, 0x23FC46U, 0x2402F0U, 0x241A1AU, 0x242BCEU, 0x243324U, + 0x244866U, 0x24508CU, 0x246158U, 0x2479B2U, 0x248F36U, 0x2497DCU, 0x24A608U, 0x24BEE2U, 0x24C5A0U, 0x24DD4AU, + 0x24EC9EU, 0x24F474U, 0x250196U, 0x25197CU, 0x2528A8U, 0x253042U, 0x254B00U, 0x2553EAU, 0x25623EU, 0x257AD4U, + 0x258C50U, 0x2594BAU, 0x25A56EU, 0x25BD84U, 0x25C6C6U, 0x25DE2CU, 0x25EFF8U, 0x25F712U, 0x26043CU, 0x261CD6U, + 0x262D02U, 0x2635E8U, 0x264EAAU, 0x265640U, 0x266794U, 0x267F7EU, 0x2689FAU, 0x269110U, 0x26A0C4U, 0x26B82EU, + 0x26C36CU, 0x26DB86U, 0x26EA52U, 0x26F2B8U, 0x27075AU, 0x271FB0U, 0x272E64U, 0x27368EU, 0x274DCCU, 0x275526U, + 0x2764F2U, 0x277C18U, 0x278A9CU, 0x279276U, 0x27A3A2U, 0x27BB48U, 0x27C00AU, 0x27D8E0U, 0x27E934U, 0x27F1DEU, + 0x280CB2U, 0x281458U, 0x28258CU, 0x283D66U, 0x284624U, 0x285ECEU, 0x286F1AU, 0x2877F0U, 0x288174U, 0x28999EU, + 0x28A84AU, 0x28B0A0U, 0x28CBE2U, 0x28D308U, 0x28E2DCU, 0x28FA36U, 0x290FD4U, 0x29173EU, 0x2926EAU, 0x293E00U, + 0x294542U, 0x295DA8U, 0x296C7CU, 0x297496U, 0x298212U, 0x299AF8U, 0x29AB2CU, 0x29B3C6U, 0x29C884U, 0x29D06EU, + 0x29E1BAU, 0x29F950U, 0x2A0A7EU, 0x2A1294U, 0x2A2340U, 0x2A3BAAU, 0x2A40E8U, 0x2A5802U, 0x2A69D6U, 0x2A713CU, + 0x2A87B8U, 0x2A9F52U, 0x2AAE86U, 0x2AB66CU, 0x2ACD2EU, 0x2AD5C4U, 0x2AE410U, 0x2AFCFAU, 0x2B0918U, 0x2B11F2U, + 0x2B2026U, 0x2B38CCU, 0x2B438EU, 0x2B5B64U, 0x2B6AB0U, 0x2B725AU, 0x2B84DEU, 0x2B9C34U, 0x2BADE0U, 0x2BB50AU, + 0x2BCE48U, 0x2BD6A2U, 0x2BE776U, 0x2BFF9CU, 0x2C012AU, 0x2C19C0U, 0x2C2814U, 0x2C30FEU, 0x2C4BBCU, 0x2C5356U, + 0x2C6282U, 0x2C7A68U, 0x2C8CECU, 0x2C9406U, 0x2CA5D2U, 0x2CBD38U, 0x2CC67AU, 0x2CDE90U, 0x2CEF44U, 0x2CF7AEU, + 0x2D024CU, 0x2D1AA6U, 0x2D2B72U, 0x2D3398U, 0x2D48DAU, 0x2D5030U, 0x2D61E4U, 0x2D790EU, 0x2D8F8AU, 0x2D9760U, + 0x2DA6B4U, 0x2DBE5EU, 0x2DC51CU, 0x2DDDF6U, 0x2DEC22U, 0x2DF4C8U, 0x2E07E6U, 0x2E1F0CU, 0x2E2ED8U, 0x2E3632U, + 0x2E4D70U, 0x2E559AU, 0x2E644EU, 0x2E7CA4U, 0x2E8A20U, 0x2E92CAU, 0x2EA31EU, 0x2EBBF4U, 0x2EC0B6U, 0x2ED85CU, + 0x2EE988U, 0x2EF162U, 0x2F0480U, 0x2F1C6AU, 0x2F2DBEU, 0x2F3554U, 0x2F4E16U, 0x2F56FCU, 0x2F6728U, 0x2F7FC2U, + 0x2F8946U, 0x2F91ACU, 0x2FA078U, 0x2FB892U, 0x2FC3D0U, 0x2FDB3AU, 0x2FEAEEU, 0x2FF204U, 0x3008DCU, 0x301036U, + 0x3021E2U, 0x303908U, 0x30424AU, 0x305AA0U, 0x306B74U, 0x30739EU, 0x30851AU, 0x309DF0U, 0x30AC24U, 0x30B4CEU, + 0x30CF8CU, 0x30D766U, 0x30E6B2U, 0x30FE58U, 0x310BBAU, 0x311350U, 0x312284U, 0x313A6EU, 0x31412CU, 0x3159C6U, + 0x316812U, 0x3170F8U, 0x31867CU, 0x319E96U, 0x31AF42U, 0x31B7A8U, 0x31CCEAU, 0x31D400U, 0x31E5D4U, 0x31FD3EU, + 0x320E10U, 0x3216FAU, 0x32272EU, 0x323FC4U, 0x324486U, 0x325C6CU, 0x326DB8U, 0x327552U, 0x3283D6U, 0x329B3CU, + 0x32AAE8U, 0x32B202U, 0x32C940U, 0x32D1AAU, 0x32E07EU, 0x32F894U, 0x330D76U, 0x33159CU, 0x332448U, 0x333CA2U, + 0x3347E0U, 0x335F0AU, 0x336EDEU, 0x337634U, 0x3380B0U, 0x33985AU, 0x33A98EU, 0x33B164U, 0x33CA26U, 0x33D2CCU, + 0x33E318U, 0x33FBF2U, 0x340544U, 0x341DAEU, 0x342C7AU, 0x343490U, 0x344FD2U, 0x345738U, 0x3466ECU, 0x347E06U, + 0x348882U, 0x349068U, 0x34A1BCU, 0x34B956U, 0x34C214U, 0x34DAFEU, 0x34EB2AU, 0x34F3C0U, 0x350622U, 0x351EC8U, + 0x352F1CU, 0x3537F6U, 0x354CB4U, 0x35545EU, 0x35658AU, 0x357D60U, 0x358BE4U, 0x35930EU, 0x35A2DAU, 0x35BA30U, + 0x35C172U, 0x35D998U, 0x35E84CU, 0x35F0A6U, 0x360388U, 0x361B62U, 0x362AB6U, 0x36325CU, 0x36491EU, 0x3651F4U, + 0x366020U, 0x3678CAU, 0x368E4EU, 0x3696A4U, 0x36A770U, 0x36BF9AU, 0x36C4D8U, 0x36DC32U, 0x36EDE6U, 0x36F50CU, + 0x3700EEU, 0x371804U, 0x3729D0U, 0x37313AU, 0x374A78U, 0x375292U, 0x376346U, 0x377BACU, 0x378D28U, 0x3795C2U, + 0x37A416U, 0x37BCFCU, 0x37C7BEU, 0x37DF54U, 0x37EE80U, 0x37F66AU, 0x380B06U, 0x3813ECU, 0x382238U, 0x383AD2U, + 0x384190U, 0x38597AU, 0x3868AEU, 0x387044U, 0x3886C0U, 0x389E2AU, 0x38AFFEU, 0x38B714U, 0x38CC56U, 0x38D4BCU, + 0x38E568U, 0x38FD82U, 0x390860U, 0x39108AU, 0x39215EU, 0x3939B4U, 0x3942F6U, 0x395A1CU, 0x396BC8U, 0x397322U, + 0x3985A6U, 0x399D4CU, 0x39AC98U, 0x39B472U, 0x39CF30U, 0x39D7DAU, 0x39E60EU, 0x39FEE4U, 0x3A0DCAU, 0x3A1520U, + 0x3A24F4U, 0x3A3C1EU, 0x3A475CU, 0x3A5FB6U, 0x3A6E62U, 0x3A7688U, 0x3A800CU, 0x3A98E6U, 0x3AA932U, 0x3AB1D8U, + 0x3ACA9AU, 0x3AD270U, 0x3AE3A4U, 0x3AFB4EU, 0x3B0EACU, 0x3B1646U, 0x3B2792U, 0x3B3F78U, 0x3B443AU, 0x3B5CD0U, + 0x3B6D04U, 0x3B75EEU, 0x3B836AU, 0x3B9B80U, 0x3BAA54U, 0x3BB2BEU, 0x3BC9FCU, 0x3BD116U, 0x3BE0C2U, 0x3BF828U, + 0x3C069EU, 0x3C1E74U, 0x3C2FA0U, 0x3C374AU, 0x3C4C08U, 0x3C54E2U, 0x3C6536U, 0x3C7DDCU, 0x3C8B58U, 0x3C93B2U, + 0x3CA266U, 0x3CBA8CU, 0x3CC1CEU, 0x3CD924U, 0x3CE8F0U, 0x3CF01AU, 0x3D05F8U, 0x3D1D12U, 0x3D2CC6U, 0x3D342CU, + 0x3D4F6EU, 0x3D5784U, 0x3D6650U, 0x3D7EBAU, 0x3D883EU, 0x3D90D4U, 0x3DA100U, 0x3DB9EAU, 0x3DC2A8U, 0x3DDA42U, + 0x3DEB96U, 0x3DF37CU, 0x3E0052U, 0x3E18B8U, 0x3E296CU, 0x3E3186U, 0x3E4AC4U, 0x3E522EU, 0x3E63FAU, 0x3E7B10U, + 0x3E8D94U, 0x3E957EU, 0x3EA4AAU, 0x3EBC40U, 0x3EC702U, 0x3EDFE8U, 0x3EEE3CU, 0x3EF6D6U, 0x3F0334U, 0x3F1BDEU, + 0x3F2A0AU, 0x3F32E0U, 0x3F49A2U, 0x3F5148U, 0x3F609CU, 0x3F7876U, 0x3F8EF2U, 0x3F9618U, 0x3FA7CCU, 0x3FBF26U, + 0x3FC464U, 0x3FDC8EU, 0x3FED5AU, 0x3FF5B0U, 0x40063AU, 0x401ED0U, 0x402F04U, 0x4037EEU, 0x404CACU, 0x405446U, + 0x406592U, 0x407D78U, 0x408BFCU, 0x409316U, 0x40A2C2U, 0x40BA28U, 0x40C16AU, 0x40D980U, 0x40E854U, 0x40F0BEU, + 0x41055CU, 0x411DB6U, 0x412C62U, 0x413488U, 0x414FCAU, 0x415720U, 0x4166F4U, 0x417E1EU, 0x41889AU, 0x419070U, + 0x41A1A4U, 0x41B94EU, 0x41C20CU, 0x41DAE6U, 0x41EB32U, 0x41F3D8U, 0x4200F6U, 0x42181CU, 0x4229C8U, 0x423122U, + 0x424A60U, 0x42528AU, 0x42635EU, 0x427BB4U, 0x428D30U, 0x4295DAU, 0x42A40EU, 0x42BCE4U, 0x42C7A6U, 0x42DF4CU, + 0x42EE98U, 0x42F672U, 0x430390U, 0x431B7AU, 0x432AAEU, 0x433244U, 0x434906U, 0x4351ECU, 0x436038U, 0x4378D2U, + 0x438E56U, 0x4396BCU, 0x43A768U, 0x43BF82U, 0x43C4C0U, 0x43DC2AU, 0x43EDFEU, 0x43F514U, 0x440BA2U, 0x441348U, + 0x44229CU, 0x443A76U, 0x444134U, 0x4459DEU, 0x44680AU, 0x4470E0U, 0x448664U, 0x449E8EU, 0x44AF5AU, 0x44B7B0U, + 0x44CCF2U, 0x44D418U, 0x44E5CCU, 0x44FD26U, 0x4508C4U, 0x45102EU, 0x4521FAU, 0x453910U, 0x454252U, 0x455AB8U, + 0x456B6CU, 0x457386U, 0x458502U, 0x459DE8U, 0x45AC3CU, 0x45B4D6U, 0x45CF94U, 0x45D77EU, 0x45E6AAU, 0x45FE40U, + 0x460D6EU, 0x461584U, 0x462450U, 0x463CBAU, 0x4647F8U, 0x465F12U, 0x466EC6U, 0x46762CU, 0x4680A8U, 0x469842U, + 0x46A996U, 0x46B17CU, 0x46CA3EU, 0x46D2D4U, 0x46E300U, 0x46FBEAU, 0x470E08U, 0x4716E2U, 0x472736U, 0x473FDCU, + 0x47449EU, 0x475C74U, 0x476DA0U, 0x47754AU, 0x4783CEU, 0x479B24U, 0x47AAF0U, 0x47B21AU, 0x47C958U, 0x47D1B2U, + 0x47E066U, 0x47F88CU, 0x4805E0U, 0x481D0AU, 0x482CDEU, 0x483434U, 0x484F76U, 0x48579CU, 0x486648U, 0x487EA2U, + 0x488826U, 0x4890CCU, 0x48A118U, 0x48B9F2U, 0x48C2B0U, 0x48DA5AU, 0x48EB8EU, 0x48F364U, 0x490686U, 0x491E6CU, + 0x492FB8U, 0x493752U, 0x494C10U, 0x4954FAU, 0x49652EU, 0x497DC4U, 0x498B40U, 0x4993AAU, 0x49A27EU, 0x49BA94U, + 0x49C1D6U, 0x49D93CU, 0x49E8E8U, 0x49F002U, 0x4A032CU, 0x4A1BC6U, 0x4A2A12U, 0x4A32F8U, 0x4A49BAU, 0x4A5150U, + 0x4A6084U, 0x4A786EU, 0x4A8EEAU, 0x4A9600U, 0x4AA7D4U, 0x4ABF3EU, 0x4AC47CU, 0x4ADC96U, 0x4AED42U, 0x4AF5A8U, + 0x4B004AU, 0x4B18A0U, 0x4B2974U, 0x4B319EU, 0x4B4ADCU, 0x4B5236U, 0x4B63E2U, 0x4B7B08U, 0x4B8D8CU, 0x4B9566U, + 0x4BA4B2U, 0x4BBC58U, 0x4BC71AU, 0x4BDFF0U, 0x4BEE24U, 0x4BF6CEU, 0x4C0878U, 0x4C1092U, 0x4C2146U, 0x4C39ACU, + 0x4C42EEU, 0x4C5A04U, 0x4C6BD0U, 0x4C733AU, 0x4C85BEU, 0x4C9D54U, 0x4CAC80U, 0x4CB46AU, 0x4CCF28U, 0x4CD7C2U, + 0x4CE616U, 0x4CFEFCU, 0x4D0B1EU, 0x4D13F4U, 0x4D2220U, 0x4D3ACAU, 0x4D4188U, 0x4D5962U, 0x4D68B6U, 0x4D705CU, + 0x4D86D8U, 0x4D9E32U, 0x4DAFE6U, 0x4DB70CU, 0x4DCC4EU, 0x4DD4A4U, 0x4DE570U, 0x4DFD9AU, 0x4E0EB4U, 0x4E165EU, + 0x4E278AU, 0x4E3F60U, 0x4E4422U, 0x4E5CC8U, 0x4E6D1CU, 0x4E75F6U, 0x4E8372U, 0x4E9B98U, 0x4EAA4CU, 0x4EB2A6U, + 0x4EC9E4U, 0x4ED10EU, 0x4EE0DAU, 0x4EF830U, 0x4F0DD2U, 0x4F1538U, 0x4F24ECU, 0x4F3C06U, 0x4F4744U, 0x4F5FAEU, + 0x4F6E7AU, 0x4F7690U, 0x4F8014U, 0x4F98FEU, 0x4FA92AU, 0x4FB1C0U, 0x4FCA82U, 0x4FD268U, 0x4FE3BCU, 0x4FFB56U, + 0x50018EU, 0x501964U, 0x5028B0U, 0x50305AU, 0x504B18U, 0x5053F2U, 0x506226U, 0x507ACCU, 0x508C48U, 0x5094A2U, + 0x50A576U, 0x50BD9CU, 0x50C6DEU, 0x50DE34U, 0x50EFE0U, 0x50F70AU, 0x5102E8U, 0x511A02U, 0x512BD6U, 0x51333CU, + 0x51487EU, 0x515094U, 0x516140U, 0x5179AAU, 0x518F2EU, 0x5197C4U, 0x51A610U, 0x51BEFAU, 0x51C5B8U, 0x51DD52U, + 0x51EC86U, 0x51F46CU, 0x520742U, 0x521FA8U, 0x522E7CU, 0x523696U, 0x524DD4U, 0x52553EU, 0x5264EAU, 0x527C00U, + 0x528A84U, 0x52926EU, 0x52A3BAU, 0x52BB50U, 0x52C012U, 0x52D8F8U, 0x52E92CU, 0x52F1C6U, 0x530424U, 0x531CCEU, + 0x532D1AU, 0x5335F0U, 0x534EB2U, 0x535658U, 0x53678CU, 0x537F66U, 0x5389E2U, 0x539108U, 0x53A0DCU, 0x53B836U, + 0x53C374U, 0x53DB9EU, 0x53EA4AU, 0x53F2A0U, 0x540C16U, 0x5414FCU, 0x542528U, 0x543DC2U, 0x544680U, 0x545E6AU, + 0x546FBEU, 0x547754U, 0x5481D0U, 0x54993AU, 0x54A8EEU, 0x54B004U, 0x54CB46U, 0x54D3ACU, 0x54E278U, 0x54FA92U, + 0x550F70U, 0x55179AU, 0x55264EU, 0x553EA4U, 0x5545E6U, 0x555D0CU, 0x556CD8U, 0x557432U, 0x5582B6U, 0x559A5CU, + 0x55AB88U, 0x55B362U, 0x55C820U, 0x55D0CAU, 0x55E11EU, 0x55F9F4U, 0x560ADAU, 0x561230U, 0x5623E4U, 0x563B0EU, + 0x56404CU, 0x5658A6U, 0x566972U, 0x567198U, 0x56871CU, 0x569FF6U, 0x56AE22U, 0x56B6C8U, 0x56CD8AU, 0x56D560U, + 0x56E4B4U, 0x56FC5EU, 0x5709BCU, 0x571156U, 0x572082U, 0x573868U, 0x57432AU, 0x575BC0U, 0x576A14U, 0x5772FEU, + 0x57847AU, 0x579C90U, 0x57AD44U, 0x57B5AEU, 0x57CEECU, 0x57D606U, 0x57E7D2U, 0x57FF38U, 0x580254U, 0x581ABEU, + 0x582B6AU, 0x583380U, 0x5848C2U, 0x585028U, 0x5861FCU, 0x587916U, 0x588F92U, 0x589778U, 0x58A6ACU, 0x58BE46U, + 0x58C504U, 0x58DDEEU, 0x58EC3AU, 0x58F4D0U, 0x590132U, 0x5919D8U, 0x59280CU, 0x5930E6U, 0x594BA4U, 0x59534EU, + 0x59629AU, 0x597A70U, 0x598CF4U, 0x59941EU, 0x59A5CAU, 0x59BD20U, 0x59C662U, 0x59DE88U, 0x59EF5CU, 0x59F7B6U, + 0x5A0498U, 0x5A1C72U, 0x5A2DA6U, 0x5A354CU, 0x5A4E0EU, 0x5A56E4U, 0x5A6730U, 0x5A7FDAU, 0x5A895EU, 0x5A91B4U, + 0x5AA060U, 0x5AB88AU, 0x5AC3C8U, 0x5ADB22U, 0x5AEAF6U, 0x5AF21CU, 0x5B07FEU, 0x5B1F14U, 0x5B2EC0U, 0x5B362AU, + 0x5B4D68U, 0x5B5582U, 0x5B6456U, 0x5B7CBCU, 0x5B8A38U, 0x5B92D2U, 0x5BA306U, 0x5BBBECU, 0x5BC0AEU, 0x5BD844U, + 0x5BE990U, 0x5BF17AU, 0x5C0FCCU, 0x5C1726U, 0x5C26F2U, 0x5C3E18U, 0x5C455AU, 0x5C5DB0U, 0x5C6C64U, 0x5C748EU, + 0x5C820AU, 0x5C9AE0U, 0x5CAB34U, 0x5CB3DEU, 0x5CC89CU, 0x5CD076U, 0x5CE1A2U, 0x5CF948U, 0x5D0CAAU, 0x5D1440U, + 0x5D2594U, 0x5D3D7EU, 0x5D463CU, 0x5D5ED6U, 0x5D6F02U, 0x5D77E8U, 0x5D816CU, 0x5D9986U, 0x5DA852U, 0x5DB0B8U, + 0x5DCBFAU, 0x5DD310U, 0x5DE2C4U, 0x5DFA2EU, 0x5E0900U, 0x5E11EAU, 0x5E203EU, 0x5E38D4U, 0x5E4396U, 0x5E5B7CU, + 0x5E6AA8U, 0x5E7242U, 0x5E84C6U, 0x5E9C2CU, 0x5EADF8U, 0x5EB512U, 0x5ECE50U, 0x5ED6BAU, 0x5EE76EU, 0x5EFF84U, + 0x5F0A66U, 0x5F128CU, 0x5F2358U, 0x5F3BB2U, 0x5F40F0U, 0x5F581AU, 0x5F69CEU, 0x5F7124U, 0x5F87A0U, 0x5F9F4AU, + 0x5FAE9EU, 0x5FB674U, 0x5FCD36U, 0x5FD5DCU, 0x5FE408U, 0x5FFCE2U, 0x600952U, 0x6011B8U, 0x60206CU, 0x603886U, + 0x6043C4U, 0x605B2EU, 0x606AFAU, 0x607210U, 0x608494U, 0x609C7EU, 0x60ADAAU, 0x60B540U, 0x60CE02U, 0x60D6E8U, + 0x60E73CU, 0x60FFD6U, 0x610A34U, 0x6112DEU, 0x61230AU, 0x613BE0U, 0x6140A2U, 0x615848U, 0x61699CU, 0x617176U, + 0x6187F2U, 0x619F18U, 0x61AECCU, 0x61B626U, 0x61CD64U, 0x61D58EU, 0x61E45AU, 0x61FCB0U, 0x620F9EU, 0x621774U, + 0x6226A0U, 0x623E4AU, 0x624508U, 0x625DE2U, 0x626C36U, 0x6274DCU, 0x628258U, 0x629AB2U, 0x62AB66U, 0x62B38CU, + 0x62C8CEU, 0x62D024U, 0x62E1F0U, 0x62F91AU, 0x630CF8U, 0x631412U, 0x6325C6U, 0x633D2CU, 0x63466EU, 0x635E84U, + 0x636F50U, 0x6377BAU, 0x63813EU, 0x6399D4U, 0x63A800U, 0x63B0EAU, 0x63CBA8U, 0x63D342U, 0x63E296U, 0x63FA7CU, + 0x6404CAU, 0x641C20U, 0x642DF4U, 0x64351EU, 0x644E5CU, 0x6456B6U, 0x646762U, 0x647F88U, 0x64890CU, 0x6491E6U, + 0x64A032U, 0x64B8D8U, 0x64C39AU, 0x64DB70U, 0x64EAA4U, 0x64F24EU, 0x6507ACU, 0x651F46U, 0x652E92U, 0x653678U, + 0x654D3AU, 0x6555D0U, 0x656404U, 0x657CEEU, 0x658A6AU, 0x659280U, 0x65A354U, 0x65BBBEU, 0x65C0FCU, 0x65D816U, + 0x65E9C2U, 0x65F128U, 0x660206U, 0x661AECU, 0x662B38U, 0x6633D2U, 0x664890U, 0x66507AU, 0x6661AEU, 0x667944U, + 0x668FC0U, 0x66972AU, 0x66A6FEU, 0x66BE14U, 0x66C556U, 0x66DDBCU, 0x66EC68U, 0x66F482U, 0x670160U, 0x67198AU, + 0x67285EU, 0x6730B4U, 0x674BF6U, 0x67531CU, 0x6762C8U, 0x677A22U, 0x678CA6U, 0x67944CU, 0x67A598U, 0x67BD72U, + 0x67C630U, 0x67DEDAU, 0x67EF0EU, 0x67F7E4U, 0x680A88U, 0x681262U, 0x6823B6U, 0x683B5CU, 0x68401EU, 0x6858F4U, + 0x686920U, 0x6871CAU, 0x68874EU, 0x689FA4U, 0x68AE70U, 0x68B69AU, 0x68CDD8U, 0x68D532U, 0x68E4E6U, 0x68FC0CU, + 0x6909EEU, 0x691104U, 0x6920D0U, 0x69383AU, 0x694378U, 0x695B92U, 0x696A46U, 0x6972ACU, 0x698428U, 0x699CC2U, + 0x69AD16U, 0x69B5FCU, 0x69CEBEU, 0x69D654U, 0x69E780U, 0x69FF6AU, 0x6A0C44U, 0x6A14AEU, 0x6A257AU, 0x6A3D90U, + 0x6A46D2U, 0x6A5E38U, 0x6A6FECU, 0x6A7706U, 0x6A8182U, 0x6A9968U, 0x6AA8BCU, 0x6AB056U, 0x6ACB14U, 0x6AD3FEU, + 0x6AE22AU, 0x6AFAC0U, 0x6B0F22U, 0x6B17C8U, 0x6B261CU, 0x6B3EF6U, 0x6B45B4U, 0x6B5D5EU, 0x6B6C8AU, 0x6B7460U, + 0x6B82E4U, 0x6B9A0EU, 0x6BABDAU, 0x6BB330U, 0x6BC872U, 0x6BD098U, 0x6BE14CU, 0x6BF9A6U, 0x6C0710U, 0x6C1FFAU, + 0x6C2E2EU, 0x6C36C4U, 0x6C4D86U, 0x6C556CU, 0x6C64B8U, 0x6C7C52U, 0x6C8AD6U, 0x6C923CU, 0x6CA3E8U, 0x6CBB02U, + 0x6CC040U, 0x6CD8AAU, 0x6CE97EU, 0x6CF194U, 0x6D0476U, 0x6D1C9CU, 0x6D2D48U, 0x6D35A2U, 0x6D4EE0U, 0x6D560AU, + 0x6D67DEU, 0x6D7F34U, 0x6D89B0U, 0x6D915AU, 0x6DA08EU, 0x6DB864U, 0x6DC326U, 0x6DDBCCU, 0x6DEA18U, 0x6DF2F2U, + 0x6E01DCU, 0x6E1936U, 0x6E28E2U, 0x6E3008U, 0x6E4B4AU, 0x6E53A0U, 0x6E6274U, 0x6E7A9EU, 0x6E8C1AU, 0x6E94F0U, + 0x6EA524U, 0x6EBDCEU, 0x6EC68CU, 0x6EDE66U, 0x6EEFB2U, 0x6EF758U, 0x6F02BAU, 0x6F1A50U, 0x6F2B84U, 0x6F336EU, + 0x6F482CU, 0x6F50C6U, 0x6F6112U, 0x6F79F8U, 0x6F8F7CU, 0x6F9796U, 0x6FA642U, 0x6FBEA8U, 0x6FC5EAU, 0x6FDD00U, + 0x6FECD4U, 0x6FF43EU, 0x700EE6U, 0x70160CU, 0x7027D8U, 0x703F32U, 0x704470U, 0x705C9AU, 0x706D4EU, 0x7075A4U, + 0x708320U, 0x709BCAU, 0x70AA1EU, 0x70B2F4U, 0x70C9B6U, 0x70D15CU, 0x70E088U, 0x70F862U, 0x710D80U, 0x71156AU, + 0x7124BEU, 0x713C54U, 0x714716U, 0x715FFCU, 0x716E28U, 0x7176C2U, 0x718046U, 0x7198ACU, 0x71A978U, 0x71B192U, + 0x71CAD0U, 0x71D23AU, 0x71E3EEU, 0x71FB04U, 0x72082AU, 0x7210C0U, 0x722114U, 0x7239FEU, 0x7242BCU, 0x725A56U, + 0x726B82U, 0x727368U, 0x7285ECU, 0x729D06U, 0x72ACD2U, 0x72B438U, 0x72CF7AU, 0x72D790U, 0x72E644U, 0x72FEAEU, + 0x730B4CU, 0x7313A6U, 0x732272U, 0x733A98U, 0x7341DAU, 0x735930U, 0x7368E4U, 0x73700EU, 0x73868AU, 0x739E60U, + 0x73AFB4U, 0x73B75EU, 0x73CC1CU, 0x73D4F6U, 0x73E522U, 0x73FDC8U, 0x74037EU, 0x741B94U, 0x742A40U, 0x7432AAU, + 0x7449E8U, 0x745102U, 0x7460D6U, 0x74783CU, 0x748EB8U, 0x749652U, 0x74A786U, 0x74BF6CU, 0x74C42EU, 0x74DCC4U, + 0x74ED10U, 0x74F5FAU, 0x750018U, 0x7518F2U, 0x752926U, 0x7531CCU, 0x754A8EU, 0x755264U, 0x7563B0U, 0x757B5AU, + 0x758DDEU, 0x759534U, 0x75A4E0U, 0x75BC0AU, 0x75C748U, 0x75DFA2U, 0x75EE76U, 0x75F69CU, 0x7605B2U, 0x761D58U, + 0x762C8CU, 0x763466U, 0x764F24U, 0x7657CEU, 0x76661AU, 0x767EF0U, 0x768874U, 0x76909EU, 0x76A14AU, 0x76B9A0U, + 0x76C2E2U, 0x76DA08U, 0x76EBDCU, 0x76F336U, 0x7706D4U, 0x771E3EU, 0x772FEAU, 0x773700U, 0x774C42U, 0x7754A8U, + 0x77657CU, 0x777D96U, 0x778B12U, 0x7793F8U, 0x77A22CU, 0x77BAC6U, 0x77C184U, 0x77D96EU, 0x77E8BAU, 0x77F050U, + 0x780D3CU, 0x7815D6U, 0x782402U, 0x783CE8U, 0x7847AAU, 0x785F40U, 0x786E94U, 0x78767EU, 0x7880FAU, 0x789810U, + 0x78A9C4U, 0x78B12EU, 0x78CA6CU, 0x78D286U, 0x78E352U, 0x78FBB8U, 0x790E5AU, 0x7916B0U, 0x792764U, 0x793F8EU, + 0x7944CCU, 0x795C26U, 0x796DF2U, 0x797518U, 0x79839CU, 0x799B76U, 0x79AAA2U, 0x79B248U, 0x79C90AU, 0x79D1E0U, + 0x79E034U, 0x79F8DEU, 0x7A0BF0U, 0x7A131AU, 0x7A22CEU, 0x7A3A24U, 0x7A4166U, 0x7A598CU, 0x7A6858U, 0x7A70B2U, + 0x7A8636U, 0x7A9EDCU, 0x7AAF08U, 0x7AB7E2U, 0x7ACCA0U, 0x7AD44AU, 0x7AE59EU, 0x7AFD74U, 0x7B0896U, 0x7B107CU, + 0x7B21A8U, 0x7B3942U, 0x7B4200U, 0x7B5AEAU, 0x7B6B3EU, 0x7B73D4U, 0x7B8550U, 0x7B9DBAU, 0x7BAC6EU, 0x7BB484U, + 0x7BCFC6U, 0x7BD72CU, 0x7BE6F8U, 0x7BFE12U, 0x7C00A4U, 0x7C184EU, 0x7C299AU, 0x7C3170U, 0x7C4A32U, 0x7C52D8U, + 0x7C630CU, 0x7C7BE6U, 0x7C8D62U, 0x7C9588U, 0x7CA45CU, 0x7CBCB6U, 0x7CC7F4U, 0x7CDF1EU, 0x7CEECAU, 0x7CF620U, + 0x7D03C2U, 0x7D1B28U, 0x7D2AFCU, 0x7D3216U, 0x7D4954U, 0x7D51BEU, 0x7D606AU, 0x7D7880U, 0x7D8E04U, 0x7D96EEU, + 0x7DA73AU, 0x7DBFD0U, 0x7DC492U, 0x7DDC78U, 0x7DEDACU, 0x7DF546U, 0x7E0668U, 0x7E1E82U, 0x7E2F56U, 0x7E37BCU, + 0x7E4CFEU, 0x7E5414U, 0x7E65C0U, 0x7E7D2AU, 0x7E8BAEU, 0x7E9344U, 0x7EA290U, 0x7EBA7AU, 0x7EC138U, 0x7ED9D2U, + 0x7EE806U, 0x7EF0ECU, 0x7F050EU, 0x7F1DE4U, 0x7F2C30U, 0x7F34DAU, 0x7F4F98U, 0x7F5772U, 0x7F66A6U, 0x7F7E4CU, + 0x7F88C8U, 0x7F9022U, 0x7FA1F6U, 0x7FB91CU, 0x7FC25EU, 0x7FDAB4U, 0x7FEB60U, 0x7FF38AU, 0x800C74U, 0x80149EU, + 0x80254AU, 0x803DA0U, 0x8046E2U, 0x805E08U, 0x806FDCU, 0x807736U, 0x8081B2U, 0x809958U, 0x80A88CU, 0x80B066U, + 0x80CB24U, 0x80D3CEU, 0x80E21AU, 0x80FAF0U, 0x810F12U, 0x8117F8U, 0x81262CU, 0x813EC6U, 0x814584U, 0x815D6EU, + 0x816CBAU, 0x817450U, 0x8182D4U, 0x819A3EU, 0x81ABEAU, 0x81B300U, 0x81C842U, 0x81D0A8U, 0x81E17CU, 0x81F996U, + 0x820AB8U, 0x821252U, 0x822386U, 0x823B6CU, 0x82402EU, 0x8258C4U, 0x826910U, 0x8271FAU, 0x82877EU, 0x829F94U, + 0x82AE40U, 0x82B6AAU, 0x82CDE8U, 0x82D502U, 0x82E4D6U, 0x82FC3CU, 0x8309DEU, 0x831134U, 0x8320E0U, 0x83380AU, + 0x834348U, 0x835BA2U, 0x836A76U, 0x83729CU, 0x838418U, 0x839CF2U, 0x83AD26U, 0x83B5CCU, 0x83CE8EU, 0x83D664U, + 0x83E7B0U, 0x83FF5AU, 0x8401ECU, 0x841906U, 0x8428D2U, 0x843038U, 0x844B7AU, 0x845390U, 0x846244U, 0x847AAEU, + 0x848C2AU, 0x8494C0U, 0x84A514U, 0x84BDFEU, 0x84C6BCU, 0x84DE56U, 0x84EF82U, 0x84F768U, 0x85028AU, 0x851A60U, + 0x852BB4U, 0x85335EU, 0x85481CU, 0x8550F6U, 0x856122U, 0x8579C8U, 0x858F4CU, 0x8597A6U, 0x85A672U, 0x85BE98U, + 0x85C5DAU, 0x85DD30U, 0x85ECE4U, 0x85F40EU, 0x860720U, 0x861FCAU, 0x862E1EU, 0x8636F4U, 0x864DB6U, 0x86555CU, + 0x866488U, 0x867C62U, 0x868AE6U, 0x86920CU, 0x86A3D8U, 0x86BB32U, 0x86C070U, 0x86D89AU, 0x86E94EU, 0x86F1A4U, + 0x870446U, 0x871CACU, 0x872D78U, 0x873592U, 0x874ED0U, 0x87563AU, 0x8767EEU, 0x877F04U, 0x878980U, 0x87916AU, + 0x87A0BEU, 0x87B854U, 0x87C316U, 0x87DBFCU, 0x87EA28U, 0x87F2C2U, 0x880FAEU, 0x881744U, 0x882690U, 0x883E7AU, + 0x884538U, 0x885DD2U, 0x886C06U, 0x8874ECU, 0x888268U, 0x889A82U, 0x88AB56U, 0x88B3BCU, 0x88C8FEU, 0x88D014U, + 0x88E1C0U, 0x88F92AU, 0x890CC8U, 0x891422U, 0x8925F6U, 0x893D1CU, 0x89465EU, 0x895EB4U, 0x896F60U, 0x89778AU, + 0x89810EU, 0x8999E4U, 0x89A830U, 0x89B0DAU, 0x89CB98U, 0x89D372U, 0x89E2A6U, 0x89FA4CU, 0x8A0962U, 0x8A1188U, + 0x8A205CU, 0x8A38B6U, 0x8A43F4U, 0x8A5B1EU, 0x8A6ACAU, 0x8A7220U, 0x8A84A4U, 0x8A9C4EU, 0x8AAD9AU, 0x8AB570U, + 0x8ACE32U, 0x8AD6D8U, 0x8AE70CU, 0x8AFFE6U, 0x8B0A04U, 0x8B12EEU, 0x8B233AU, 0x8B3BD0U, 0x8B4092U, 0x8B5878U, + 0x8B69ACU, 0x8B7146U, 0x8B87C2U, 0x8B9F28U, 0x8BAEFCU, 0x8BB616U, 0x8BCD54U, 0x8BD5BEU, 0x8BE46AU, 0x8BFC80U, + 0x8C0236U, 0x8C1ADCU, 0x8C2B08U, 0x8C33E2U, 0x8C48A0U, 0x8C504AU, 0x8C619EU, 0x8C7974U, 0x8C8FF0U, 0x8C971AU, + 0x8CA6CEU, 0x8CBE24U, 0x8CC566U, 0x8CDD8CU, 0x8CEC58U, 0x8CF4B2U, 0x8D0150U, 0x8D19BAU, 0x8D286EU, 0x8D3084U, + 0x8D4BC6U, 0x8D532CU, 0x8D62F8U, 0x8D7A12U, 0x8D8C96U, 0x8D947CU, 0x8DA5A8U, 0x8DBD42U, 0x8DC600U, 0x8DDEEAU, + 0x8DEF3EU, 0x8DF7D4U, 0x8E04FAU, 0x8E1C10U, 0x8E2DC4U, 0x8E352EU, 0x8E4E6CU, 0x8E5686U, 0x8E6752U, 0x8E7FB8U, + 0x8E893CU, 0x8E91D6U, 0x8EA002U, 0x8EB8E8U, 0x8EC3AAU, 0x8EDB40U, 0x8EEA94U, 0x8EF27EU, 0x8F079CU, 0x8F1F76U, + 0x8F2EA2U, 0x8F3648U, 0x8F4D0AU, 0x8F55E0U, 0x8F6434U, 0x8F7CDEU, 0x8F8A5AU, 0x8F92B0U, 0x8FA364U, 0x8FBB8EU, + 0x8FC0CCU, 0x8FD826U, 0x8FE9F2U, 0x8FF118U, 0x900BC0U, 0x90132AU, 0x9022FEU, 0x903A14U, 0x904156U, 0x9059BCU, + 0x906868U, 0x907082U, 0x908606U, 0x909EECU, 0x90AF38U, 0x90B7D2U, 0x90CC90U, 0x90D47AU, 0x90E5AEU, 0x90FD44U, + 0x9108A6U, 0x91104CU, 0x912198U, 0x913972U, 0x914230U, 0x915ADAU, 0x916B0EU, 0x9173E4U, 0x918560U, 0x919D8AU, + 0x91AC5EU, 0x91B4B4U, 0x91CFF6U, 0x91D71CU, 0x91E6C8U, 0x91FE22U, 0x920D0CU, 0x9215E6U, 0x922432U, 0x923CD8U, + 0x92479AU, 0x925F70U, 0x926EA4U, 0x92764EU, 0x9280CAU, 0x929820U, 0x92A9F4U, 0x92B11EU, 0x92CA5CU, 0x92D2B6U, + 0x92E362U, 0x92FB88U, 0x930E6AU, 0x931680U, 0x932754U, 0x933FBEU, 0x9344FCU, 0x935C16U, 0x936DC2U, 0x937528U, + 0x9383ACU, 0x939B46U, 0x93AA92U, 0x93B278U, 0x93C93AU, 0x93D1D0U, 0x93E004U, 0x93F8EEU, 0x940658U, 0x941EB2U, + 0x942F66U, 0x94378CU, 0x944CCEU, 0x945424U, 0x9465F0U, 0x947D1AU, 0x948B9EU, 0x949374U, 0x94A2A0U, 0x94BA4AU, + 0x94C108U, 0x94D9E2U, 0x94E836U, 0x94F0DCU, 0x95053EU, 0x951DD4U, 0x952C00U, 0x9534EAU, 0x954FA8U, 0x955742U, + 0x956696U, 0x957E7CU, 0x9588F8U, 0x959012U, 0x95A1C6U, 0x95B92CU, 0x95C26EU, 0x95DA84U, 0x95EB50U, 0x95F3BAU, + 0x960094U, 0x96187EU, 0x9629AAU, 0x963140U, 0x964A02U, 0x9652E8U, 0x96633CU, 0x967BD6U, 0x968D52U, 0x9695B8U, + 0x96A46CU, 0x96BC86U, 0x96C7C4U, 0x96DF2EU, 0x96EEFAU, 0x96F610U, 0x9703F2U, 0x971B18U, 0x972ACCU, 0x973226U, + 0x974964U, 0x97518EU, 0x97605AU, 0x9778B0U, 0x978E34U, 0x9796DEU, 0x97A70AU, 0x97BFE0U, 0x97C4A2U, 0x97DC48U, + 0x97ED9CU, 0x97F576U, 0x98081AU, 0x9810F0U, 0x982124U, 0x9839CEU, 0x98428CU, 0x985A66U, 0x986BB2U, 0x987358U, + 0x9885DCU, 0x989D36U, 0x98ACE2U, 0x98B408U, 0x98CF4AU, 0x98D7A0U, 0x98E674U, 0x98FE9EU, 0x990B7CU, 0x991396U, + 0x992242U, 0x993AA8U, 0x9941EAU, 0x995900U, 0x9968D4U, 0x99703EU, 0x9986BAU, 0x999E50U, 0x99AF84U, 0x99B76EU, + 0x99CC2CU, 0x99D4C6U, 0x99E512U, 0x99FDF8U, 0x9A0ED6U, 0x9A163CU, 0x9A27E8U, 0x9A3F02U, 0x9A4440U, 0x9A5CAAU, + 0x9A6D7EU, 0x9A7594U, 0x9A8310U, 0x9A9BFAU, 0x9AAA2EU, 0x9AB2C4U, 0x9AC986U, 0x9AD16CU, 0x9AE0B8U, 0x9AF852U, + 0x9B0DB0U, 0x9B155AU, 0x9B248EU, 0x9B3C64U, 0x9B4726U, 0x9B5FCCU, 0x9B6E18U, 0x9B76F2U, 0x9B8076U, 0x9B989CU, + 0x9BA948U, 0x9BB1A2U, 0x9BCAE0U, 0x9BD20AU, 0x9BE3DEU, 0x9BFB34U, 0x9C0582U, 0x9C1D68U, 0x9C2CBCU, 0x9C3456U, + 0x9C4F14U, 0x9C57FEU, 0x9C662AU, 0x9C7EC0U, 0x9C8844U, 0x9C90AEU, 0x9CA17AU, 0x9CB990U, 0x9CC2D2U, 0x9CDA38U, + 0x9CEBECU, 0x9CF306U, 0x9D06E4U, 0x9D1E0EU, 0x9D2FDAU, 0x9D3730U, 0x9D4C72U, 0x9D5498U, 0x9D654CU, 0x9D7DA6U, + 0x9D8B22U, 0x9D93C8U, 0x9DA21CU, 0x9DBAF6U, 0x9DC1B4U, 0x9DD95EU, 0x9DE88AU, 0x9DF060U, 0x9E034EU, 0x9E1BA4U, + 0x9E2A70U, 0x9E329AU, 0x9E49D8U, 0x9E5132U, 0x9E60E6U, 0x9E780CU, 0x9E8E88U, 0x9E9662U, 0x9EA7B6U, 0x9EBF5CU, + 0x9EC41EU, 0x9EDCF4U, 0x9EED20U, 0x9EF5CAU, 0x9F0028U, 0x9F18C2U, 0x9F2916U, 0x9F31FCU, 0x9F4ABEU, 0x9F5254U, + 0x9F6380U, 0x9F7B6AU, 0x9F8DEEU, 0x9F9504U, 0x9FA4D0U, 0x9FBC3AU, 0x9FC778U, 0x9FDF92U, 0x9FEE46U, 0x9FF6ACU, + 0xA0031CU, 0xA01BF6U, 0xA02A22U, 0xA032C8U, 0xA0498AU, 0xA05160U, 0xA060B4U, 0xA0785EU, 0xA08EDAU, 0xA09630U, + 0xA0A7E4U, 0xA0BF0EU, 0xA0C44CU, 0xA0DCA6U, 0xA0ED72U, 0xA0F598U, 0xA1007AU, 0xA11890U, 0xA12944U, 0xA131AEU, + 0xA14AECU, 0xA15206U, 0xA163D2U, 0xA17B38U, 0xA18DBCU, 0xA19556U, 0xA1A482U, 0xA1BC68U, 0xA1C72AU, 0xA1DFC0U, + 0xA1EE14U, 0xA1F6FEU, 0xA205D0U, 0xA21D3AU, 0xA22CEEU, 0xA23404U, 0xA24F46U, 0xA257ACU, 0xA26678U, 0xA27E92U, + 0xA28816U, 0xA290FCU, 0xA2A128U, 0xA2B9C2U, 0xA2C280U, 0xA2DA6AU, 0xA2EBBEU, 0xA2F354U, 0xA306B6U, 0xA31E5CU, + 0xA32F88U, 0xA33762U, 0xA34C20U, 0xA354CAU, 0xA3651EU, 0xA37DF4U, 0xA38B70U, 0xA3939AU, 0xA3A24EU, 0xA3BAA4U, + 0xA3C1E6U, 0xA3D90CU, 0xA3E8D8U, 0xA3F032U, 0xA40E84U, 0xA4166EU, 0xA427BAU, 0xA43F50U, 0xA44412U, 0xA45CF8U, + 0xA46D2CU, 0xA475C6U, 0xA48342U, 0xA49BA8U, 0xA4AA7CU, 0xA4B296U, 0xA4C9D4U, 0xA4D13EU, 0xA4E0EAU, 0xA4F800U, + 0xA50DE2U, 0xA51508U, 0xA524DCU, 0xA53C36U, 0xA54774U, 0xA55F9EU, 0xA56E4AU, 0xA576A0U, 0xA58024U, 0xA598CEU, + 0xA5A91AU, 0xA5B1F0U, 0xA5CAB2U, 0xA5D258U, 0xA5E38CU, 0xA5FB66U, 0xA60848U, 0xA610A2U, 0xA62176U, 0xA6399CU, + 0xA642DEU, 0xA65A34U, 0xA66BE0U, 0xA6730AU, 0xA6858EU, 0xA69D64U, 0xA6ACB0U, 0xA6B45AU, 0xA6CF18U, 0xA6D7F2U, + 0xA6E626U, 0xA6FECCU, 0xA70B2EU, 0xA713C4U, 0xA72210U, 0xA73AFAU, 0xA741B8U, 0xA75952U, 0xA76886U, 0xA7706CU, + 0xA786E8U, 0xA79E02U, 0xA7AFD6U, 0xA7B73CU, 0xA7CC7EU, 0xA7D494U, 0xA7E540U, 0xA7FDAAU, 0xA800C6U, 0xA8182CU, + 0xA829F8U, 0xA83112U, 0xA84A50U, 0xA852BAU, 0xA8636EU, 0xA87B84U, 0xA88D00U, 0xA895EAU, 0xA8A43EU, 0xA8BCD4U, + 0xA8C796U, 0xA8DF7CU, 0xA8EEA8U, 0xA8F642U, 0xA903A0U, 0xA91B4AU, 0xA92A9EU, 0xA93274U, 0xA94936U, 0xA951DCU, + 0xA96008U, 0xA978E2U, 0xA98E66U, 0xA9968CU, 0xA9A758U, 0xA9BFB2U, 0xA9C4F0U, 0xA9DC1AU, 0xA9EDCEU, 0xA9F524U, + 0xAA060AU, 0xAA1EE0U, 0xAA2F34U, 0xAA37DEU, 0xAA4C9CU, 0xAA5476U, 0xAA65A2U, 0xAA7D48U, 0xAA8BCCU, 0xAA9326U, + 0xAAA2F2U, 0xAABA18U, 0xAAC15AU, 0xAAD9B0U, 0xAAE864U, 0xAAF08EU, 0xAB056CU, 0xAB1D86U, 0xAB2C52U, 0xAB34B8U, + 0xAB4FFAU, 0xAB5710U, 0xAB66C4U, 0xAB7E2EU, 0xAB88AAU, 0xAB9040U, 0xABA194U, 0xABB97EU, 0xABC23CU, 0xABDAD6U, + 0xABEB02U, 0xABF3E8U, 0xAC0D5EU, 0xAC15B4U, 0xAC2460U, 0xAC3C8AU, 0xAC47C8U, 0xAC5F22U, 0xAC6EF6U, 0xAC761CU, + 0xAC8098U, 0xAC9872U, 0xACA9A6U, 0xACB14CU, 0xACCA0EU, 0xACD2E4U, 0xACE330U, 0xACFBDAU, 0xAD0E38U, 0xAD16D2U, + 0xAD2706U, 0xAD3FECU, 0xAD44AEU, 0xAD5C44U, 0xAD6D90U, 0xAD757AU, 0xAD83FEU, 0xAD9B14U, 0xADAAC0U, 0xADB22AU, + 0xADC968U, 0xADD182U, 0xADE056U, 0xADF8BCU, 0xAE0B92U, 0xAE1378U, 0xAE22ACU, 0xAE3A46U, 0xAE4104U, 0xAE59EEU, + 0xAE683AU, 0xAE70D0U, 0xAE8654U, 0xAE9EBEU, 0xAEAF6AU, 0xAEB780U, 0xAECCC2U, 0xAED428U, 0xAEE5FCU, 0xAEFD16U, + 0xAF08F4U, 0xAF101EU, 0xAF21CAU, 0xAF3920U, 0xAF4262U, 0xAF5A88U, 0xAF6B5CU, 0xAF73B6U, 0xAF8532U, 0xAF9DD8U, + 0xAFAC0CU, 0xAFB4E6U, 0xAFCFA4U, 0xAFD74EU, 0xAFE69AU, 0xAFFE70U, 0xB004A8U, 0xB01C42U, 0xB02D96U, 0xB0357CU, + 0xB04E3EU, 0xB056D4U, 0xB06700U, 0xB07FEAU, 0xB0896EU, 0xB09184U, 0xB0A050U, 0xB0B8BAU, 0xB0C3F8U, 0xB0DB12U, + 0xB0EAC6U, 0xB0F22CU, 0xB107CEU, 0xB11F24U, 0xB12EF0U, 0xB1361AU, 0xB14D58U, 0xB155B2U, 0xB16466U, 0xB17C8CU, + 0xB18A08U, 0xB192E2U, 0xB1A336U, 0xB1BBDCU, 0xB1C09EU, 0xB1D874U, 0xB1E9A0U, 0xB1F14AU, 0xB20264U, 0xB21A8EU, + 0xB22B5AU, 0xB233B0U, 0xB248F2U, 0xB25018U, 0xB261CCU, 0xB27926U, 0xB28FA2U, 0xB29748U, 0xB2A69CU, 0xB2BE76U, + 0xB2C534U, 0xB2DDDEU, 0xB2EC0AU, 0xB2F4E0U, 0xB30102U, 0xB319E8U, 0xB3283CU, 0xB330D6U, 0xB34B94U, 0xB3537EU, + 0xB362AAU, 0xB37A40U, 0xB38CC4U, 0xB3942EU, 0xB3A5FAU, 0xB3BD10U, 0xB3C652U, 0xB3DEB8U, 0xB3EF6CU, 0xB3F786U, + 0xB40930U, 0xB411DAU, 0xB4200EU, 0xB438E4U, 0xB443A6U, 0xB45B4CU, 0xB46A98U, 0xB47272U, 0xB484F6U, 0xB49C1CU, + 0xB4ADC8U, 0xB4B522U, 0xB4CE60U, 0xB4D68AU, 0xB4E75EU, 0xB4FFB4U, 0xB50A56U, 0xB512BCU, 0xB52368U, 0xB53B82U, + 0xB540C0U, 0xB5582AU, 0xB569FEU, 0xB57114U, 0xB58790U, 0xB59F7AU, 0xB5AEAEU, 0xB5B644U, 0xB5CD06U, 0xB5D5ECU, + 0xB5E438U, 0xB5FCD2U, 0xB60FFCU, 0xB61716U, 0xB626C2U, 0xB63E28U, 0xB6456AU, 0xB65D80U, 0xB66C54U, 0xB674BEU, + 0xB6823AU, 0xB69AD0U, 0xB6AB04U, 0xB6B3EEU, 0xB6C8ACU, 0xB6D046U, 0xB6E192U, 0xB6F978U, 0xB70C9AU, 0xB71470U, + 0xB725A4U, 0xB73D4EU, 0xB7460CU, 0xB75EE6U, 0xB76F32U, 0xB777D8U, 0xB7815CU, 0xB799B6U, 0xB7A862U, 0xB7B088U, + 0xB7CBCAU, 0xB7D320U, 0xB7E2F4U, 0xB7FA1EU, 0xB80772U, 0xB81F98U, 0xB82E4CU, 0xB836A6U, 0xB84DE4U, 0xB8550EU, + 0xB864DAU, 0xB87C30U, 0xB88AB4U, 0xB8925EU, 0xB8A38AU, 0xB8BB60U, 0xB8C022U, 0xB8D8C8U, 0xB8E91CU, 0xB8F1F6U, + 0xB90414U, 0xB91CFEU, 0xB92D2AU, 0xB935C0U, 0xB94E82U, 0xB95668U, 0xB967BCU, 0xB97F56U, 0xB989D2U, 0xB99138U, + 0xB9A0ECU, 0xB9B806U, 0xB9C344U, 0xB9DBAEU, 0xB9EA7AU, 0xB9F290U, 0xBA01BEU, 0xBA1954U, 0xBA2880U, 0xBA306AU, + 0xBA4B28U, 0xBA53C2U, 0xBA6216U, 0xBA7AFCU, 0xBA8C78U, 0xBA9492U, 0xBAA546U, 0xBABDACU, 0xBAC6EEU, 0xBADE04U, + 0xBAEFD0U, 0xBAF73AU, 0xBB02D8U, 0xBB1A32U, 0xBB2BE6U, 0xBB330CU, 0xBB484EU, 0xBB50A4U, 0xBB6170U, 0xBB799AU, + 0xBB8F1EU, 0xBB97F4U, 0xBBA620U, 0xBBBECAU, 0xBBC588U, 0xBBDD62U, 0xBBECB6U, 0xBBF45CU, 0xBC0AEAU, 0xBC1200U, + 0xBC23D4U, 0xBC3B3EU, 0xBC407CU, 0xBC5896U, 0xBC6942U, 0xBC71A8U, 0xBC872CU, 0xBC9FC6U, 0xBCAE12U, 0xBCB6F8U, + 0xBCCDBAU, 0xBCD550U, 0xBCE484U, 0xBCFC6EU, 0xBD098CU, 0xBD1166U, 0xBD20B2U, 0xBD3858U, 0xBD431AU, 0xBD5BF0U, + 0xBD6A24U, 0xBD72CEU, 0xBD844AU, 0xBD9CA0U, 0xBDAD74U, 0xBDB59EU, 0xBDCEDCU, 0xBDD636U, 0xBDE7E2U, 0xBDFF08U, + 0xBE0C26U, 0xBE14CCU, 0xBE2518U, 0xBE3DF2U, 0xBE46B0U, 0xBE5E5AU, 0xBE6F8EU, 0xBE7764U, 0xBE81E0U, 0xBE990AU, + 0xBEA8DEU, 0xBEB034U, 0xBECB76U, 0xBED39CU, 0xBEE248U, 0xBEFAA2U, 0xBF0F40U, 0xBF17AAU, 0xBF267EU, 0xBF3E94U, + 0xBF45D6U, 0xBF5D3CU, 0xBF6CE8U, 0xBF7402U, 0xBF8286U, 0xBF9A6CU, 0xBFABB8U, 0xBFB352U, 0xBFC810U, 0xBFD0FAU, + 0xBFE12EU, 0xBFF9C4U, 0xC00A4EU, 0xC012A4U, 0xC02370U, 0xC03B9AU, 0xC040D8U, 0xC05832U, 0xC069E6U, 0xC0710CU, + 0xC08788U, 0xC09F62U, 0xC0AEB6U, 0xC0B65CU, 0xC0CD1EU, 0xC0D5F4U, 0xC0E420U, 0xC0FCCAU, 0xC10928U, 0xC111C2U, + 0xC12016U, 0xC138FCU, 0xC143BEU, 0xC15B54U, 0xC16A80U, 0xC1726AU, 0xC184EEU, 0xC19C04U, 0xC1ADD0U, 0xC1B53AU, + 0xC1CE78U, 0xC1D692U, 0xC1E746U, 0xC1FFACU, 0xC20C82U, 0xC21468U, 0xC225BCU, 0xC23D56U, 0xC24614U, 0xC25EFEU, + 0xC26F2AU, 0xC277C0U, 0xC28144U, 0xC299AEU, 0xC2A87AU, 0xC2B090U, 0xC2CBD2U, 0xC2D338U, 0xC2E2ECU, 0xC2FA06U, + 0xC30FE4U, 0xC3170EU, 0xC326DAU, 0xC33E30U, 0xC34572U, 0xC35D98U, 0xC36C4CU, 0xC374A6U, 0xC38222U, 0xC39AC8U, + 0xC3AB1CU, 0xC3B3F6U, 0xC3C8B4U, 0xC3D05EU, 0xC3E18AU, 0xC3F960U, 0xC407D6U, 0xC41F3CU, 0xC42EE8U, 0xC43602U, + 0xC44D40U, 0xC455AAU, 0xC4647EU, 0xC47C94U, 0xC48A10U, 0xC492FAU, 0xC4A32EU, 0xC4BBC4U, 0xC4C086U, 0xC4D86CU, + 0xC4E9B8U, 0xC4F152U, 0xC504B0U, 0xC51C5AU, 0xC52D8EU, 0xC53564U, 0xC54E26U, 0xC556CCU, 0xC56718U, 0xC57FF2U, + 0xC58976U, 0xC5919CU, 0xC5A048U, 0xC5B8A2U, 0xC5C3E0U, 0xC5DB0AU, 0xC5EADEU, 0xC5F234U, 0xC6011AU, 0xC619F0U, + 0xC62824U, 0xC630CEU, 0xC64B8CU, 0xC65366U, 0xC662B2U, 0xC67A58U, 0xC68CDCU, 0xC69436U, 0xC6A5E2U, 0xC6BD08U, + 0xC6C64AU, 0xC6DEA0U, 0xC6EF74U, 0xC6F79EU, 0xC7027CU, 0xC71A96U, 0xC72B42U, 0xC733A8U, 0xC748EAU, 0xC75000U, + 0xC761D4U, 0xC7793EU, 0xC78FBAU, 0xC79750U, 0xC7A684U, 0xC7BE6EU, 0xC7C52CU, 0xC7DDC6U, 0xC7EC12U, 0xC7F4F8U, + 0xC80994U, 0xC8117EU, 0xC820AAU, 0xC83840U, 0xC84302U, 0xC85BE8U, 0xC86A3CU, 0xC872D6U, 0xC88452U, 0xC89CB8U, + 0xC8AD6CU, 0xC8B586U, 0xC8CEC4U, 0xC8D62EU, 0xC8E7FAU, 0xC8FF10U, 0xC90AF2U, 0xC91218U, 0xC923CCU, 0xC93B26U, + 0xC94064U, 0xC9588EU, 0xC9695AU, 0xC971B0U, 0xC98734U, 0xC99FDEU, 0xC9AE0AU, 0xC9B6E0U, 0xC9CDA2U, 0xC9D548U, + 0xC9E49CU, 0xC9FC76U, 0xCA0F58U, 0xCA17B2U, 0xCA2666U, 0xCA3E8CU, 0xCA45CEU, 0xCA5D24U, 0xCA6CF0U, 0xCA741AU, + 0xCA829EU, 0xCA9A74U, 0xCAABA0U, 0xCAB34AU, 0xCAC808U, 0xCAD0E2U, 0xCAE136U, 0xCAF9DCU, 0xCB0C3EU, 0xCB14D4U, + 0xCB2500U, 0xCB3DEAU, 0xCB46A8U, 0xCB5E42U, 0xCB6F96U, 0xCB777CU, 0xCB81F8U, 0xCB9912U, 0xCBA8C6U, 0xCBB02CU, + 0xCBCB6EU, 0xCBD384U, 0xCBE250U, 0xCBFABAU, 0xCC040CU, 0xCC1CE6U, 0xCC2D32U, 0xCC35D8U, 0xCC4E9AU, 0xCC5670U, + 0xCC67A4U, 0xCC7F4EU, 0xCC89CAU, 0xCC9120U, 0xCCA0F4U, 0xCCB81EU, 0xCCC35CU, 0xCCDBB6U, 0xCCEA62U, 0xCCF288U, + 0xCD076AU, 0xCD1F80U, 0xCD2E54U, 0xCD36BEU, 0xCD4DFCU, 0xCD5516U, 0xCD64C2U, 0xCD7C28U, 0xCD8AACU, 0xCD9246U, + 0xCDA392U, 0xCDBB78U, 0xCDC03AU, 0xCDD8D0U, 0xCDE904U, 0xCDF1EEU, 0xCE02C0U, 0xCE1A2AU, 0xCE2BFEU, 0xCE3314U, + 0xCE4856U, 0xCE50BCU, 0xCE6168U, 0xCE7982U, 0xCE8F06U, 0xCE97ECU, 0xCEA638U, 0xCEBED2U, 0xCEC590U, 0xCEDD7AU, + 0xCEECAEU, 0xCEF444U, 0xCF01A6U, 0xCF194CU, 0xCF2898U, 0xCF3072U, 0xCF4B30U, 0xCF53DAU, 0xCF620EU, 0xCF7AE4U, + 0xCF8C60U, 0xCF948AU, 0xCFA55EU, 0xCFBDB4U, 0xCFC6F6U, 0xCFDE1CU, 0xCFEFC8U, 0xCFF722U, 0xD00DFAU, 0xD01510U, + 0xD024C4U, 0xD03C2EU, 0xD0476CU, 0xD05F86U, 0xD06E52U, 0xD076B8U, 0xD0803CU, 0xD098D6U, 0xD0A902U, 0xD0B1E8U, + 0xD0CAAAU, 0xD0D240U, 0xD0E394U, 0xD0FB7EU, 0xD10E9CU, 0xD11676U, 0xD127A2U, 0xD13F48U, 0xD1440AU, 0xD15CE0U, + 0xD16D34U, 0xD175DEU, 0xD1835AU, 0xD19BB0U, 0xD1AA64U, 0xD1B28EU, 0xD1C9CCU, 0xD1D126U, 0xD1E0F2U, 0xD1F818U, + 0xD20B36U, 0xD213DCU, 0xD22208U, 0xD23AE2U, 0xD241A0U, 0xD2594AU, 0xD2689EU, 0xD27074U, 0xD286F0U, 0xD29E1AU, + 0xD2AFCEU, 0xD2B724U, 0xD2CC66U, 0xD2D48CU, 0xD2E558U, 0xD2FDB2U, 0xD30850U, 0xD310BAU, 0xD3216EU, 0xD33984U, + 0xD342C6U, 0xD35A2CU, 0xD36BF8U, 0xD37312U, 0xD38596U, 0xD39D7CU, 0xD3ACA8U, 0xD3B442U, 0xD3CF00U, 0xD3D7EAU, + 0xD3E63EU, 0xD3FED4U, 0xD40062U, 0xD41888U, 0xD4295CU, 0xD431B6U, 0xD44AF4U, 0xD4521EU, 0xD463CAU, 0xD47B20U, + 0xD48DA4U, 0xD4954EU, 0xD4A49AU, 0xD4BC70U, 0xD4C732U, 0xD4DFD8U, 0xD4EE0CU, 0xD4F6E6U, 0xD50304U, 0xD51BEEU, + 0xD52A3AU, 0xD532D0U, 0xD54992U, 0xD55178U, 0xD560ACU, 0xD57846U, 0xD58EC2U, 0xD59628U, 0xD5A7FCU, 0xD5BF16U, + 0xD5C454U, 0xD5DCBEU, 0xD5ED6AU, 0xD5F580U, 0xD606AEU, 0xD61E44U, 0xD62F90U, 0xD6377AU, 0xD64C38U, 0xD654D2U, + 0xD66506U, 0xD67DECU, 0xD68B68U, 0xD69382U, 0xD6A256U, 0xD6BABCU, 0xD6C1FEU, 0xD6D914U, 0xD6E8C0U, 0xD6F02AU, + 0xD705C8U, 0xD71D22U, 0xD72CF6U, 0xD7341CU, 0xD74F5EU, 0xD757B4U, 0xD76660U, 0xD77E8AU, 0xD7880EU, 0xD790E4U, + 0xD7A130U, 0xD7B9DAU, 0xD7C298U, 0xD7DA72U, 0xD7EBA6U, 0xD7F34CU, 0xD80E20U, 0xD816CAU, 0xD8271EU, 0xD83FF4U, + 0xD844B6U, 0xD85C5CU, 0xD86D88U, 0xD87562U, 0xD883E6U, 0xD89B0CU, 0xD8AAD8U, 0xD8B232U, 0xD8C970U, 0xD8D19AU, + 0xD8E04EU, 0xD8F8A4U, 0xD90D46U, 0xD915ACU, 0xD92478U, 0xD93C92U, 0xD947D0U, 0xD95F3AU, 0xD96EEEU, 0xD97604U, + 0xD98080U, 0xD9986AU, 0xD9A9BEU, 0xD9B154U, 0xD9CA16U, 0xD9D2FCU, 0xD9E328U, 0xD9FBC2U, 0xDA08ECU, 0xDA1006U, + 0xDA21D2U, 0xDA3938U, 0xDA427AU, 0xDA5A90U, 0xDA6B44U, 0xDA73AEU, 0xDA852AU, 0xDA9DC0U, 0xDAAC14U, 0xDAB4FEU, + 0xDACFBCU, 0xDAD756U, 0xDAE682U, 0xDAFE68U, 0xDB0B8AU, 0xDB1360U, 0xDB22B4U, 0xDB3A5EU, 0xDB411CU, 0xDB59F6U, + 0xDB6822U, 0xDB70C8U, 0xDB864CU, 0xDB9EA6U, 0xDBAF72U, 0xDBB798U, 0xDBCCDAU, 0xDBD430U, 0xDBE5E4U, 0xDBFD0EU, + 0xDC03B8U, 0xDC1B52U, 0xDC2A86U, 0xDC326CU, 0xDC492EU, 0xDC51C4U, 0xDC6010U, 0xDC78FAU, 0xDC8E7EU, 0xDC9694U, + 0xDCA740U, 0xDCBFAAU, 0xDCC4E8U, 0xDCDC02U, 0xDCEDD6U, 0xDCF53CU, 0xDD00DEU, 0xDD1834U, 0xDD29E0U, 0xDD310AU, + 0xDD4A48U, 0xDD52A2U, 0xDD6376U, 0xDD7B9CU, 0xDD8D18U, 0xDD95F2U, 0xDDA426U, 0xDDBCCCU, 0xDDC78EU, 0xDDDF64U, + 0xDDEEB0U, 0xDDF65AU, 0xDE0574U, 0xDE1D9EU, 0xDE2C4AU, 0xDE34A0U, 0xDE4FE2U, 0xDE5708U, 0xDE66DCU, 0xDE7E36U, + 0xDE88B2U, 0xDE9058U, 0xDEA18CU, 0xDEB966U, 0xDEC224U, 0xDEDACEU, 0xDEEB1AU, 0xDEF3F0U, 0xDF0612U, 0xDF1EF8U, + 0xDF2F2CU, 0xDF37C6U, 0xDF4C84U, 0xDF546EU, 0xDF65BAU, 0xDF7D50U, 0xDF8BD4U, 0xDF933EU, 0xDFA2EAU, 0xDFBA00U, + 0xDFC142U, 0xDFD9A8U, 0xDFE87CU, 0xDFF096U, 0xE00526U, 0xE01DCCU, 0xE02C18U, 0xE034F2U, 0xE04FB0U, 0xE0575AU, + 0xE0668EU, 0xE07E64U, 0xE088E0U, 0xE0900AU, 0xE0A1DEU, 0xE0B934U, 0xE0C276U, 0xE0DA9CU, 0xE0EB48U, 0xE0F3A2U, + 0xE10640U, 0xE11EAAU, 0xE12F7EU, 0xE13794U, 0xE14CD6U, 0xE1543CU, 0xE165E8U, 0xE17D02U, 0xE18B86U, 0xE1936CU, + 0xE1A2B8U, 0xE1BA52U, 0xE1C110U, 0xE1D9FAU, 0xE1E82EU, 0xE1F0C4U, 0xE203EAU, 0xE21B00U, 0xE22AD4U, 0xE2323EU, + 0xE2497CU, 0xE25196U, 0xE26042U, 0xE278A8U, 0xE28E2CU, 0xE296C6U, 0xE2A712U, 0xE2BFF8U, 0xE2C4BAU, 0xE2DC50U, + 0xE2ED84U, 0xE2F56EU, 0xE3008CU, 0xE31866U, 0xE329B2U, 0xE33158U, 0xE34A1AU, 0xE352F0U, 0xE36324U, 0xE37BCEU, + 0xE38D4AU, 0xE395A0U, 0xE3A474U, 0xE3BC9EU, 0xE3C7DCU, 0xE3DF36U, 0xE3EEE2U, 0xE3F608U, 0xE408BEU, 0xE41054U, + 0xE42180U, 0xE4396AU, 0xE44228U, 0xE45AC2U, 0xE46B16U, 0xE473FCU, 0xE48578U, 0xE49D92U, 0xE4AC46U, 0xE4B4ACU, + 0xE4CFEEU, 0xE4D704U, 0xE4E6D0U, 0xE4FE3AU, 0xE50BD8U, 0xE51332U, 0xE522E6U, 0xE53A0CU, 0xE5414EU, 0xE559A4U, + 0xE56870U, 0xE5709AU, 0xE5861EU, 0xE59EF4U, 0xE5AF20U, 0xE5B7CAU, 0xE5CC88U, 0xE5D462U, 0xE5E5B6U, 0xE5FD5CU, + 0xE60E72U, 0xE61698U, 0xE6274CU, 0xE63FA6U, 0xE644E4U, 0xE65C0EU, 0xE66DDAU, 0xE67530U, 0xE683B4U, 0xE69B5EU, + 0xE6AA8AU, 0xE6B260U, 0xE6C922U, 0xE6D1C8U, 0xE6E01CU, 0xE6F8F6U, 0xE70D14U, 0xE715FEU, 0xE7242AU, 0xE73CC0U, + 0xE74782U, 0xE75F68U, 0xE76EBCU, 0xE77656U, 0xE780D2U, 0xE79838U, 0xE7A9ECU, 0xE7B106U, 0xE7CA44U, 0xE7D2AEU, + 0xE7E37AU, 0xE7FB90U, 0xE806FCU, 0xE81E16U, 0xE82FC2U, 0xE83728U, 0xE84C6AU, 0xE85480U, 0xE86554U, 0xE87DBEU, + 0xE88B3AU, 0xE893D0U, 0xE8A204U, 0xE8BAEEU, 0xE8C1ACU, 0xE8D946U, 0xE8E892U, 0xE8F078U, 0xE9059AU, 0xE91D70U, + 0xE92CA4U, 0xE9344EU, 0xE94F0CU, 0xE957E6U, 0xE96632U, 0xE97ED8U, 0xE9885CU, 0xE990B6U, 0xE9A162U, 0xE9B988U, + 0xE9C2CAU, 0xE9DA20U, 0xE9EBF4U, 0xE9F31EU, 0xEA0030U, 0xEA18DAU, 0xEA290EU, 0xEA31E4U, 0xEA4AA6U, 0xEA524CU, + 0xEA6398U, 0xEA7B72U, 0xEA8DF6U, 0xEA951CU, 0xEAA4C8U, 0xEABC22U, 0xEAC760U, 0xEADF8AU, 0xEAEE5EU, 0xEAF6B4U, + 0xEB0356U, 0xEB1BBCU, 0xEB2A68U, 0xEB3282U, 0xEB49C0U, 0xEB512AU, 0xEB60FEU, 0xEB7814U, 0xEB8E90U, 0xEB967AU, + 0xEBA7AEU, 0xEBBF44U, 0xEBC406U, 0xEBDCECU, 0xEBED38U, 0xEBF5D2U, 0xEC0B64U, 0xEC138EU, 0xEC225AU, 0xEC3AB0U, + 0xEC41F2U, 0xEC5918U, 0xEC68CCU, 0xEC7026U, 0xEC86A2U, 0xEC9E48U, 0xECAF9CU, 0xECB776U, 0xECCC34U, 0xECD4DEU, + 0xECE50AU, 0xECFDE0U, 0xED0802U, 0xED10E8U, 0xED213CU, 0xED39D6U, 0xED4294U, 0xED5A7EU, 0xED6BAAU, 0xED7340U, + 0xED85C4U, 0xED9D2EU, 0xEDACFAU, 0xEDB410U, 0xEDCF52U, 0xEDD7B8U, 0xEDE66CU, 0xEDFE86U, 0xEE0DA8U, 0xEE1542U, + 0xEE2496U, 0xEE3C7CU, 0xEE473EU, 0xEE5FD4U, 0xEE6E00U, 0xEE76EAU, 0xEE806EU, 0xEE9884U, 0xEEA950U, 0xEEB1BAU, + 0xEECAF8U, 0xEED212U, 0xEEE3C6U, 0xEEFB2CU, 0xEF0ECEU, 0xEF1624U, 0xEF27F0U, 0xEF3F1AU, 0xEF4458U, 0xEF5CB2U, + 0xEF6D66U, 0xEF758CU, 0xEF8308U, 0xEF9BE2U, 0xEFAA36U, 0xEFB2DCU, 0xEFC99EU, 0xEFD174U, 0xEFE0A0U, 0xEFF84AU, + 0xF00292U, 0xF01A78U, 0xF02BACU, 0xF03346U, 0xF04804U, 0xF050EEU, 0xF0613AU, 0xF079D0U, 0xF08F54U, 0xF097BEU, + 0xF0A66AU, 0xF0BE80U, 0xF0C5C2U, 0xF0DD28U, 0xF0ECFCU, 0xF0F416U, 0xF101F4U, 0xF1191EU, 0xF128CAU, 0xF13020U, + 0xF14B62U, 0xF15388U, 0xF1625CU, 0xF17AB6U, 0xF18C32U, 0xF194D8U, 0xF1A50CU, 0xF1BDE6U, 0xF1C6A4U, 0xF1DE4EU, + 0xF1EF9AU, 0xF1F770U, 0xF2045EU, 0xF21CB4U, 0xF22D60U, 0xF2358AU, 0xF24EC8U, 0xF25622U, 0xF267F6U, 0xF27F1CU, + 0xF28998U, 0xF29172U, 0xF2A0A6U, 0xF2B84CU, 0xF2C30EU, 0xF2DBE4U, 0xF2EA30U, 0xF2F2DAU, 0xF30738U, 0xF31FD2U, + 0xF32E06U, 0xF336ECU, 0xF34DAEU, 0xF35544U, 0xF36490U, 0xF37C7AU, 0xF38AFEU, 0xF39214U, 0xF3A3C0U, 0xF3BB2AU, + 0xF3C068U, 0xF3D882U, 0xF3E956U, 0xF3F1BCU, 0xF40F0AU, 0xF417E0U, 0xF42634U, 0xF43EDEU, 0xF4459CU, 0xF45D76U, + 0xF46CA2U, 0xF47448U, 0xF482CCU, 0xF49A26U, 0xF4ABF2U, 0xF4B318U, 0xF4C85AU, 0xF4D0B0U, 0xF4E164U, 0xF4F98EU, + 0xF50C6CU, 0xF51486U, 0xF52552U, 0xF53DB8U, 0xF546FAU, 0xF55E10U, 0xF56FC4U, 0xF5772EU, 0xF581AAU, 0xF59940U, + 0xF5A894U, 0xF5B07EU, 0xF5CB3CU, 0xF5D3D6U, 0xF5E202U, 0xF5FAE8U, 0xF609C6U, 0xF6112CU, 0xF620F8U, 0xF63812U, + 0xF64350U, 0xF65BBAU, 0xF66A6EU, 0xF67284U, 0xF68400U, 0xF69CEAU, 0xF6AD3EU, 0xF6B5D4U, 0xF6CE96U, 0xF6D67CU, + 0xF6E7A8U, 0xF6FF42U, 0xF70AA0U, 0xF7124AU, 0xF7239EU, 0xF73B74U, 0xF74036U, 0xF758DCU, 0xF76908U, 0xF771E2U, + 0xF78766U, 0xF79F8CU, 0xF7AE58U, 0xF7B6B2U, 0xF7CDF0U, 0xF7D51AU, 0xF7E4CEU, 0xF7FC24U, 0xF80148U, 0xF819A2U, + 0xF82876U, 0xF8309CU, 0xF84BDEU, 0xF85334U, 0xF862E0U, 0xF87A0AU, 0xF88C8EU, 0xF89464U, 0xF8A5B0U, 0xF8BD5AU, + 0xF8C618U, 0xF8DEF2U, 0xF8EF26U, 0xF8F7CCU, 0xF9022EU, 0xF91AC4U, 0xF92B10U, 0xF933FAU, 0xF948B8U, 0xF95052U, + 0xF96186U, 0xF9796CU, 0xF98FE8U, 0xF99702U, 0xF9A6D6U, 0xF9BE3CU, 0xF9C57EU, 0xF9DD94U, 0xF9EC40U, 0xF9F4AAU, + 0xFA0784U, 0xFA1F6EU, 0xFA2EBAU, 0xFA3650U, 0xFA4D12U, 0xFA55F8U, 0xFA642CU, 0xFA7CC6U, 0xFA8A42U, 0xFA92A8U, + 0xFAA37CU, 0xFABB96U, 0xFAC0D4U, 0xFAD83EU, 0xFAE9EAU, 0xFAF100U, 0xFB04E2U, 0xFB1C08U, 0xFB2DDCU, 0xFB3536U, + 0xFB4E74U, 0xFB569EU, 0xFB674AU, 0xFB7FA0U, 0xFB8924U, 0xFB91CEU, 0xFBA01AU, 0xFBB8F0U, 0xFBC3B2U, 0xFBDB58U, + 0xFBEA8CU, 0xFBF266U, 0xFC0CD0U, 0xFC143AU, 0xFC25EEU, 0xFC3D04U, 0xFC4646U, 0xFC5EACU, 0xFC6F78U, 0xFC7792U, + 0xFC8116U, 0xFC99FCU, 0xFCA828U, 0xFCB0C2U, 0xFCCB80U, 0xFCD36AU, 0xFCE2BEU, 0xFCFA54U, 0xFD0FB6U, 0xFD175CU, + 0xFD2688U, 0xFD3E62U, 0xFD4520U, 0xFD5DCAU, 0xFD6C1EU, 0xFD74F4U, 0xFD8270U, 0xFD9A9AU, 0xFDAB4EU, 0xFDB3A4U, + 0xFDC8E6U, 0xFDD00CU, 0xFDE1D8U, 0xFDF932U, 0xFE0A1CU, 0xFE12F6U, 0xFE2322U, 0xFE3BC8U, 0xFE408AU, 0xFE5860U, + 0xFE69B4U, 0xFE715EU, 0xFE87DAU, 0xFE9F30U, 0xFEAEE4U, 0xFEB60EU, 0xFECD4CU, 0xFED5A6U, 0xFEE472U, 0xFEFC98U, + 0xFF097AU, 0xFF1190U, 0xFF2044U, 0xFF38AEU, 0xFF43ECU, 0xFF5B06U, 0xFF6AD2U, 0xFF7238U, 0xFF84BCU, 0xFF9C56U, + 0xFFAD82U, 0xFFB568U, 0xFFCE2AU, 0xFFD6C0U, 0xFFE714U, 0xFFFFFEU}; + +static const unsigned int ENCODING_TABLE_24128[] = { + 0x000000U, 0x0018EBU, 0x00293EU, 0x0031D5U, 0x004A97U, 0x00527CU, 0x0063A9U, 0x007B42U, 0x008DC6U, 0x00952DU, + 0x00A4F8U, 0x00BC13U, 0x00C751U, 0x00DFBAU, 0x00EE6FU, 0x00F684U, 0x010367U, 0x011B8CU, 0x012A59U, 0x0132B2U, + 0x0149F0U, 0x01511BU, 0x0160CEU, 0x017825U, 0x018EA1U, 0x01964AU, 0x01A79FU, 0x01BF74U, 0x01C436U, 0x01DCDDU, + 0x01ED08U, 0x01F5E3U, 0x0206CDU, 0x021E26U, 0x022FF3U, 0x023718U, 0x024C5AU, 0x0254B1U, 0x026564U, 0x027D8FU, + 0x028B0BU, 0x0293E0U, 0x02A235U, 0x02BADEU, 0x02C19CU, 0x02D977U, 0x02E8A2U, 0x02F049U, 0x0305AAU, 0x031D41U, + 0x032C94U, 0x03347FU, 0x034F3DU, 0x0357D6U, 0x036603U, 0x037EE8U, 0x03886CU, 0x039087U, 0x03A152U, 0x03B9B9U, + 0x03C2FBU, 0x03DA10U, 0x03EBC5U, 0x03F32EU, 0x040D99U, 0x041572U, 0x0424A7U, 0x043C4CU, 0x04470EU, 0x045FE5U, + 0x046E30U, 0x0476DBU, 0x04805FU, 0x0498B4U, 0x04A961U, 0x04B18AU, 0x04CAC8U, 0x04D223U, 0x04E3F6U, 0x04FB1DU, + 0x050EFEU, 0x051615U, 0x0527C0U, 0x053F2BU, 0x054469U, 0x055C82U, 0x056D57U, 0x0575BCU, 0x058338U, 0x059BD3U, + 0x05AA06U, 0x05B2EDU, 0x05C9AFU, 0x05D144U, 0x05E091U, 0x05F87AU, 0x060B54U, 0x0613BFU, 0x06226AU, 0x063A81U, + 0x0641C3U, 0x065928U, 0x0668FDU, 0x067016U, 0x068692U, 0x069E79U, 0x06AFACU, 0x06B747U, 0x06CC05U, 0x06D4EEU, + 0x06E53BU, 0x06FDD0U, 0x070833U, 0x0710D8U, 0x07210DU, 0x0739E6U, 0x0742A4U, 0x075A4FU, 0x076B9AU, 0x077371U, + 0x0785F5U, 0x079D1EU, 0x07ACCBU, 0x07B420U, 0x07CF62U, 0x07D789U, 0x07E65CU, 0x07FEB7U, 0x0803DAU, 0x081B31U, + 0x082AE4U, 0x08320FU, 0x08494DU, 0x0851A6U, 0x086073U, 0x087898U, 0x088E1CU, 0x0896F7U, 0x08A722U, 0x08BFC9U, + 0x08C48BU, 0x08DC60U, 0x08EDB5U, 0x08F55EU, 0x0900BDU, 0x091856U, 0x092983U, 0x093168U, 0x094A2AU, 0x0952C1U, + 0x096314U, 0x097BFFU, 0x098D7BU, 0x099590U, 0x09A445U, 0x09BCAEU, 0x09C7ECU, 0x09DF07U, 0x09EED2U, 0x09F639U, + 0x0A0517U, 0x0A1DFCU, 0x0A2C29U, 0x0A34C2U, 0x0A4F80U, 0x0A576BU, 0x0A66BEU, 0x0A7E55U, 0x0A88D1U, 0x0A903AU, + 0x0AA1EFU, 0x0AB904U, 0x0AC246U, 0x0ADAADU, 0x0AEB78U, 0x0AF393U, 0x0B0670U, 0x0B1E9BU, 0x0B2F4EU, 0x0B37A5U, + 0x0B4CE7U, 0x0B540CU, 0x0B65D9U, 0x0B7D32U, 0x0B8BB6U, 0x0B935DU, 0x0BA288U, 0x0BBA63U, 0x0BC121U, 0x0BD9CAU, + 0x0BE81FU, 0x0BF0F4U, 0x0C0E43U, 0x0C16A8U, 0x0C277DU, 0x0C3F96U, 0x0C44D4U, 0x0C5C3FU, 0x0C6DEAU, 0x0C7501U, + 0x0C8385U, 0x0C9B6EU, 0x0CAABBU, 0x0CB250U, 0x0CC912U, 0x0CD1F9U, 0x0CE02CU, 0x0CF8C7U, 0x0D0D24U, 0x0D15CFU, + 0x0D241AU, 0x0D3CF1U, 0x0D47B3U, 0x0D5F58U, 0x0D6E8DU, 0x0D7666U, 0x0D80E2U, 0x0D9809U, 0x0DA9DCU, 0x0DB137U, + 0x0DCA75U, 0x0DD29EU, 0x0DE34BU, 0x0DFBA0U, 0x0E088EU, 0x0E1065U, 0x0E21B0U, 0x0E395BU, 0x0E4219U, 0x0E5AF2U, + 0x0E6B27U, 0x0E73CCU, 0x0E8548U, 0x0E9DA3U, 0x0EAC76U, 0x0EB49DU, 0x0ECFDFU, 0x0ED734U, 0x0EE6E1U, 0x0EFE0AU, + 0x0F0BE9U, 0x0F1302U, 0x0F22D7U, 0x0F3A3CU, 0x0F417EU, 0x0F5995U, 0x0F6840U, 0x0F70ABU, 0x0F862FU, 0x0F9EC4U, + 0x0FAF11U, 0x0FB7FAU, 0x0FCCB8U, 0x0FD453U, 0x0FE586U, 0x0FFD6DU, 0x1007B4U, 0x101F5FU, 0x102E8AU, 0x103661U, + 0x104D23U, 0x1055C8U, 0x10641DU, 0x107CF6U, 0x108A72U, 0x109299U, 0x10A34CU, 0x10BBA7U, 0x10C0E5U, 0x10D80EU, + 0x10E9DBU, 0x10F130U, 0x1104D3U, 0x111C38U, 0x112DEDU, 0x113506U, 0x114E44U, 0x1156AFU, 0x11677AU, 0x117F91U, + 0x118915U, 0x1191FEU, 0x11A02BU, 0x11B8C0U, 0x11C382U, 0x11DB69U, 0x11EABCU, 0x11F257U, 0x120179U, 0x121992U, + 0x122847U, 0x1230ACU, 0x124BEEU, 0x125305U, 0x1262D0U, 0x127A3BU, 0x128CBFU, 0x129454U, 0x12A581U, 0x12BD6AU, + 0x12C628U, 0x12DEC3U, 0x12EF16U, 0x12F7FDU, 0x13021EU, 0x131AF5U, 0x132B20U, 0x1333CBU, 0x134889U, 0x135062U, + 0x1361B7U, 0x13795CU, 0x138FD8U, 0x139733U, 0x13A6E6U, 0x13BE0DU, 0x13C54FU, 0x13DDA4U, 0x13EC71U, 0x13F49AU, + 0x140A2DU, 0x1412C6U, 0x142313U, 0x143BF8U, 0x1440BAU, 0x145851U, 0x146984U, 0x14716FU, 0x1487EBU, 0x149F00U, + 0x14AED5U, 0x14B63EU, 0x14CD7CU, 0x14D597U, 0x14E442U, 0x14FCA9U, 0x15094AU, 0x1511A1U, 0x152074U, 0x15389FU, + 0x1543DDU, 0x155B36U, 0x156AE3U, 0x157208U, 0x15848CU, 0x159C67U, 0x15ADB2U, 0x15B559U, 0x15CE1BU, 0x15D6F0U, + 0x15E725U, 0x15FFCEU, 0x160CE0U, 0x16140BU, 0x1625DEU, 0x163D35U, 0x164677U, 0x165E9CU, 0x166F49U, 0x1677A2U, + 0x168126U, 0x1699CDU, 0x16A818U, 0x16B0F3U, 0x16CBB1U, 0x16D35AU, 0x16E28FU, 0x16FA64U, 0x170F87U, 0x17176CU, + 0x1726B9U, 0x173E52U, 0x174510U, 0x175DFBU, 0x176C2EU, 0x1774C5U, 0x178241U, 0x179AAAU, 0x17AB7FU, 0x17B394U, + 0x17C8D6U, 0x17D03DU, 0x17E1E8U, 0x17F903U, 0x18046EU, 0x181C85U, 0x182D50U, 0x1835BBU, 0x184EF9U, 0x185612U, + 0x1867C7U, 0x187F2CU, 0x1889A8U, 0x189143U, 0x18A096U, 0x18B87DU, 0x18C33FU, 0x18DBD4U, 0x18EA01U, 0x18F2EAU, + 0x190709U, 0x191FE2U, 0x192E37U, 0x1936DCU, 0x194D9EU, 0x195575U, 0x1964A0U, 0x197C4BU, 0x198ACFU, 0x199224U, + 0x19A3F1U, 0x19BB1AU, 0x19C058U, 0x19D8B3U, 0x19E966U, 0x19F18DU, 0x1A02A3U, 0x1A1A48U, 0x1A2B9DU, 0x1A3376U, + 0x1A4834U, 0x1A50DFU, 0x1A610AU, 0x1A79E1U, 0x1A8F65U, 0x1A978EU, 0x1AA65BU, 0x1ABEB0U, 0x1AC5F2U, 0x1ADD19U, + 0x1AECCCU, 0x1AF427U, 0x1B01C4U, 0x1B192FU, 0x1B28FAU, 0x1B3011U, 0x1B4B53U, 0x1B53B8U, 0x1B626DU, 0x1B7A86U, + 0x1B8C02U, 0x1B94E9U, 0x1BA53CU, 0x1BBDD7U, 0x1BC695U, 0x1BDE7EU, 0x1BEFABU, 0x1BF740U, 0x1C09F7U, 0x1C111CU, + 0x1C20C9U, 0x1C3822U, 0x1C4360U, 0x1C5B8BU, 0x1C6A5EU, 0x1C72B5U, 0x1C8431U, 0x1C9CDAU, 0x1CAD0FU, 0x1CB5E4U, + 0x1CCEA6U, 0x1CD64DU, 0x1CE798U, 0x1CFF73U, 0x1D0A90U, 0x1D127BU, 0x1D23AEU, 0x1D3B45U, 0x1D4007U, 0x1D58ECU, + 0x1D6939U, 0x1D71D2U, 0x1D8756U, 0x1D9FBDU, 0x1DAE68U, 0x1DB683U, 0x1DCDC1U, 0x1DD52AU, 0x1DE4FFU, 0x1DFC14U, + 0x1E0F3AU, 0x1E17D1U, 0x1E2604U, 0x1E3EEFU, 0x1E45ADU, 0x1E5D46U, 0x1E6C93U, 0x1E7478U, 0x1E82FCU, 0x1E9A17U, + 0x1EABC2U, 0x1EB329U, 0x1EC86BU, 0x1ED080U, 0x1EE155U, 0x1EF9BEU, 0x1F0C5DU, 0x1F14B6U, 0x1F2563U, 0x1F3D88U, + 0x1F46CAU, 0x1F5E21U, 0x1F6FF4U, 0x1F771FU, 0x1F819BU, 0x1F9970U, 0x1FA8A5U, 0x1FB04EU, 0x1FCB0CU, 0x1FD3E7U, + 0x1FE232U, 0x1FFAD9U, 0x200F68U, 0x201783U, 0x202656U, 0x203EBDU, 0x2045FFU, 0x205D14U, 0x206CC1U, 0x20742AU, + 0x2082AEU, 0x209A45U, 0x20AB90U, 0x20B37BU, 0x20C839U, 0x20D0D2U, 0x20E107U, 0x20F9ECU, 0x210C0FU, 0x2114E4U, + 0x212531U, 0x213DDAU, 0x214698U, 0x215E73U, 0x216FA6U, 0x21774DU, 0x2181C9U, 0x219922U, 0x21A8F7U, 0x21B01CU, + 0x21CB5EU, 0x21D3B5U, 0x21E260U, 0x21FA8BU, 0x2209A5U, 0x22114EU, 0x22209BU, 0x223870U, 0x224332U, 0x225BD9U, + 0x226A0CU, 0x2272E7U, 0x228463U, 0x229C88U, 0x22AD5DU, 0x22B5B6U, 0x22CEF4U, 0x22D61FU, 0x22E7CAU, 0x22FF21U, + 0x230AC2U, 0x231229U, 0x2323FCU, 0x233B17U, 0x234055U, 0x2358BEU, 0x23696BU, 0x237180U, 0x238704U, 0x239FEFU, + 0x23AE3AU, 0x23B6D1U, 0x23CD93U, 0x23D578U, 0x23E4ADU, 0x23FC46U, 0x2402F1U, 0x241A1AU, 0x242BCFU, 0x243324U, + 0x244866U, 0x24508DU, 0x246158U, 0x2479B3U, 0x248F37U, 0x2497DCU, 0x24A609U, 0x24BEE2U, 0x24C5A0U, 0x24DD4BU, + 0x24EC9EU, 0x24F475U, 0x250196U, 0x25197DU, 0x2528A8U, 0x253043U, 0x254B01U, 0x2553EAU, 0x25623FU, 0x257AD4U, + 0x258C50U, 0x2594BBU, 0x25A56EU, 0x25BD85U, 0x25C6C7U, 0x25DE2CU, 0x25EFF9U, 0x25F712U, 0x26043CU, 0x261CD7U, + 0x262D02U, 0x2635E9U, 0x264EABU, 0x265640U, 0x266795U, 0x267F7EU, 0x2689FAU, 0x269111U, 0x26A0C4U, 0x26B82FU, + 0x26C36DU, 0x26DB86U, 0x26EA53U, 0x26F2B8U, 0x27075BU, 0x271FB0U, 0x272E65U, 0x27368EU, 0x274DCCU, 0x275527U, + 0x2764F2U, 0x277C19U, 0x278A9DU, 0x279276U, 0x27A3A3U, 0x27BB48U, 0x27C00AU, 0x27D8E1U, 0x27E934U, 0x27F1DFU, + 0x280CB2U, 0x281459U, 0x28258CU, 0x283D67U, 0x284625U, 0x285ECEU, 0x286F1BU, 0x2877F0U, 0x288174U, 0x28999FU, + 0x28A84AU, 0x28B0A1U, 0x28CBE3U, 0x28D308U, 0x28E2DDU, 0x28FA36U, 0x290FD5U, 0x29173EU, 0x2926EBU, 0x293E00U, + 0x294542U, 0x295DA9U, 0x296C7CU, 0x297497U, 0x298213U, 0x299AF8U, 0x29AB2DU, 0x29B3C6U, 0x29C884U, 0x29D06FU, + 0x29E1BAU, 0x29F951U, 0x2A0A7FU, 0x2A1294U, 0x2A2341U, 0x2A3BAAU, 0x2A40E8U, 0x2A5803U, 0x2A69D6U, 0x2A713DU, + 0x2A87B9U, 0x2A9F52U, 0x2AAE87U, 0x2AB66CU, 0x2ACD2EU, 0x2AD5C5U, 0x2AE410U, 0x2AFCFBU, 0x2B0918U, 0x2B11F3U, + 0x2B2026U, 0x2B38CDU, 0x2B438FU, 0x2B5B64U, 0x2B6AB1U, 0x2B725AU, 0x2B84DEU, 0x2B9C35U, 0x2BADE0U, 0x2BB50BU, + 0x2BCE49U, 0x2BD6A2U, 0x2BE777U, 0x2BFF9CU, 0x2C012BU, 0x2C19C0U, 0x2C2815U, 0x2C30FEU, 0x2C4BBCU, 0x2C5357U, + 0x2C6282U, 0x2C7A69U, 0x2C8CEDU, 0x2C9406U, 0x2CA5D3U, 0x2CBD38U, 0x2CC67AU, 0x2CDE91U, 0x2CEF44U, 0x2CF7AFU, + 0x2D024CU, 0x2D1AA7U, 0x2D2B72U, 0x2D3399U, 0x2D48DBU, 0x2D5030U, 0x2D61E5U, 0x2D790EU, 0x2D8F8AU, 0x2D9761U, + 0x2DA6B4U, 0x2DBE5FU, 0x2DC51DU, 0x2DDDF6U, 0x2DEC23U, 0x2DF4C8U, 0x2E07E6U, 0x2E1F0DU, 0x2E2ED8U, 0x2E3633U, + 0x2E4D71U, 0x2E559AU, 0x2E644FU, 0x2E7CA4U, 0x2E8A20U, 0x2E92CBU, 0x2EA31EU, 0x2EBBF5U, 0x2EC0B7U, 0x2ED85CU, + 0x2EE989U, 0x2EF162U, 0x2F0481U, 0x2F1C6AU, 0x2F2DBFU, 0x2F3554U, 0x2F4E16U, 0x2F56FDU, 0x2F6728U, 0x2F7FC3U, + 0x2F8947U, 0x2F91ACU, 0x2FA079U, 0x2FB892U, 0x2FC3D0U, 0x2FDB3BU, 0x2FEAEEU, 0x2FF205U, 0x3008DCU, 0x301037U, + 0x3021E2U, 0x303909U, 0x30424BU, 0x305AA0U, 0x306B75U, 0x30739EU, 0x30851AU, 0x309DF1U, 0x30AC24U, 0x30B4CFU, + 0x30CF8DU, 0x30D766U, 0x30E6B3U, 0x30FE58U, 0x310BBBU, 0x311350U, 0x312285U, 0x313A6EU, 0x31412CU, 0x3159C7U, + 0x316812U, 0x3170F9U, 0x31867DU, 0x319E96U, 0x31AF43U, 0x31B7A8U, 0x31CCEAU, 0x31D401U, 0x31E5D4U, 0x31FD3FU, + 0x320E11U, 0x3216FAU, 0x32272FU, 0x323FC4U, 0x324486U, 0x325C6DU, 0x326DB8U, 0x327553U, 0x3283D7U, 0x329B3CU, + 0x32AAE9U, 0x32B202U, 0x32C940U, 0x32D1ABU, 0x32E07EU, 0x32F895U, 0x330D76U, 0x33159DU, 0x332448U, 0x333CA3U, + 0x3347E1U, 0x335F0AU, 0x336EDFU, 0x337634U, 0x3380B0U, 0x33985BU, 0x33A98EU, 0x33B165U, 0x33CA27U, 0x33D2CCU, + 0x33E319U, 0x33FBF2U, 0x340545U, 0x341DAEU, 0x342C7BU, 0x343490U, 0x344FD2U, 0x345739U, 0x3466ECU, 0x347E07U, + 0x348883U, 0x349068U, 0x34A1BDU, 0x34B956U, 0x34C214U, 0x34DAFFU, 0x34EB2AU, 0x34F3C1U, 0x350622U, 0x351EC9U, + 0x352F1CU, 0x3537F7U, 0x354CB5U, 0x35545EU, 0x35658BU, 0x357D60U, 0x358BE4U, 0x35930FU, 0x35A2DAU, 0x35BA31U, + 0x35C173U, 0x35D998U, 0x35E84DU, 0x35F0A6U, 0x360388U, 0x361B63U, 0x362AB6U, 0x36325DU, 0x36491FU, 0x3651F4U, + 0x366021U, 0x3678CAU, 0x368E4EU, 0x3696A5U, 0x36A770U, 0x36BF9BU, 0x36C4D9U, 0x36DC32U, 0x36EDE7U, 0x36F50CU, + 0x3700EFU, 0x371804U, 0x3729D1U, 0x37313AU, 0x374A78U, 0x375293U, 0x376346U, 0x377BADU, 0x378D29U, 0x3795C2U, + 0x37A417U, 0x37BCFCU, 0x37C7BEU, 0x37DF55U, 0x37EE80U, 0x37F66BU, 0x380B06U, 0x3813EDU, 0x382238U, 0x383AD3U, + 0x384191U, 0x38597AU, 0x3868AFU, 0x387044U, 0x3886C0U, 0x389E2BU, 0x38AFFEU, 0x38B715U, 0x38CC57U, 0x38D4BCU, + 0x38E569U, 0x38FD82U, 0x390861U, 0x39108AU, 0x39215FU, 0x3939B4U, 0x3942F6U, 0x395A1DU, 0x396BC8U, 0x397323U, + 0x3985A7U, 0x399D4CU, 0x39AC99U, 0x39B472U, 0x39CF30U, 0x39D7DBU, 0x39E60EU, 0x39FEE5U, 0x3A0DCBU, 0x3A1520U, + 0x3A24F5U, 0x3A3C1EU, 0x3A475CU, 0x3A5FB7U, 0x3A6E62U, 0x3A7689U, 0x3A800DU, 0x3A98E6U, 0x3AA933U, 0x3AB1D8U, + 0x3ACA9AU, 0x3AD271U, 0x3AE3A4U, 0x3AFB4FU, 0x3B0EACU, 0x3B1647U, 0x3B2792U, 0x3B3F79U, 0x3B443BU, 0x3B5CD0U, + 0x3B6D05U, 0x3B75EEU, 0x3B836AU, 0x3B9B81U, 0x3BAA54U, 0x3BB2BFU, 0x3BC9FDU, 0x3BD116U, 0x3BE0C3U, 0x3BF828U, + 0x3C069FU, 0x3C1E74U, 0x3C2FA1U, 0x3C374AU, 0x3C4C08U, 0x3C54E3U, 0x3C6536U, 0x3C7DDDU, 0x3C8B59U, 0x3C93B2U, + 0x3CA267U, 0x3CBA8CU, 0x3CC1CEU, 0x3CD925U, 0x3CE8F0U, 0x3CF01BU, 0x3D05F8U, 0x3D1D13U, 0x3D2CC6U, 0x3D342DU, + 0x3D4F6FU, 0x3D5784U, 0x3D6651U, 0x3D7EBAU, 0x3D883EU, 0x3D90D5U, 0x3DA100U, 0x3DB9EBU, 0x3DC2A9U, 0x3DDA42U, + 0x3DEB97U, 0x3DF37CU, 0x3E0052U, 0x3E18B9U, 0x3E296CU, 0x3E3187U, 0x3E4AC5U, 0x3E522EU, 0x3E63FBU, 0x3E7B10U, + 0x3E8D94U, 0x3E957FU, 0x3EA4AAU, 0x3EBC41U, 0x3EC703U, 0x3EDFE8U, 0x3EEE3DU, 0x3EF6D6U, 0x3F0335U, 0x3F1BDEU, + 0x3F2A0BU, 0x3F32E0U, 0x3F49A2U, 0x3F5149U, 0x3F609CU, 0x3F7877U, 0x3F8EF3U, 0x3F9618U, 0x3FA7CDU, 0x3FBF26U, + 0x3FC464U, 0x3FDC8FU, 0x3FED5AU, 0x3FF5B1U, 0x40063BU, 0x401ED0U, 0x402F05U, 0x4037EEU, 0x404CACU, 0x405447U, + 0x406592U, 0x407D79U, 0x408BFDU, 0x409316U, 0x40A2C3U, 0x40BA28U, 0x40C16AU, 0x40D981U, 0x40E854U, 0x40F0BFU, + 0x41055CU, 0x411DB7U, 0x412C62U, 0x413489U, 0x414FCBU, 0x415720U, 0x4166F5U, 0x417E1EU, 0x41889AU, 0x419071U, + 0x41A1A4U, 0x41B94FU, 0x41C20DU, 0x41DAE6U, 0x41EB33U, 0x41F3D8U, 0x4200F6U, 0x42181DU, 0x4229C8U, 0x423123U, + 0x424A61U, 0x42528AU, 0x42635FU, 0x427BB4U, 0x428D30U, 0x4295DBU, 0x42A40EU, 0x42BCE5U, 0x42C7A7U, 0x42DF4CU, + 0x42EE99U, 0x42F672U, 0x430391U, 0x431B7AU, 0x432AAFU, 0x433244U, 0x434906U, 0x4351EDU, 0x436038U, 0x4378D3U, + 0x438E57U, 0x4396BCU, 0x43A769U, 0x43BF82U, 0x43C4C0U, 0x43DC2BU, 0x43EDFEU, 0x43F515U, 0x440BA2U, 0x441349U, + 0x44229CU, 0x443A77U, 0x444135U, 0x4459DEU, 0x44680BU, 0x4470E0U, 0x448664U, 0x449E8FU, 0x44AF5AU, 0x44B7B1U, + 0x44CCF3U, 0x44D418U, 0x44E5CDU, 0x44FD26U, 0x4508C5U, 0x45102EU, 0x4521FBU, 0x453910U, 0x454252U, 0x455AB9U, + 0x456B6CU, 0x457387U, 0x458503U, 0x459DE8U, 0x45AC3DU, 0x45B4D6U, 0x45CF94U, 0x45D77FU, 0x45E6AAU, 0x45FE41U, + 0x460D6FU, 0x461584U, 0x462451U, 0x463CBAU, 0x4647F8U, 0x465F13U, 0x466EC6U, 0x46762DU, 0x4680A9U, 0x469842U, + 0x46A997U, 0x46B17CU, 0x46CA3EU, 0x46D2D5U, 0x46E300U, 0x46FBEBU, 0x470E08U, 0x4716E3U, 0x472736U, 0x473FDDU, + 0x47449FU, 0x475C74U, 0x476DA1U, 0x47754AU, 0x4783CEU, 0x479B25U, 0x47AAF0U, 0x47B21BU, 0x47C959U, 0x47D1B2U, + 0x47E067U, 0x47F88CU, 0x4805E1U, 0x481D0AU, 0x482CDFU, 0x483434U, 0x484F76U, 0x48579DU, 0x486648U, 0x487EA3U, + 0x488827U, 0x4890CCU, 0x48A119U, 0x48B9F2U, 0x48C2B0U, 0x48DA5BU, 0x48EB8EU, 0x48F365U, 0x490686U, 0x491E6DU, + 0x492FB8U, 0x493753U, 0x494C11U, 0x4954FAU, 0x49652FU, 0x497DC4U, 0x498B40U, 0x4993ABU, 0x49A27EU, 0x49BA95U, + 0x49C1D7U, 0x49D93CU, 0x49E8E9U, 0x49F002U, 0x4A032CU, 0x4A1BC7U, 0x4A2A12U, 0x4A32F9U, 0x4A49BBU, 0x4A5150U, + 0x4A6085U, 0x4A786EU, 0x4A8EEAU, 0x4A9601U, 0x4AA7D4U, 0x4ABF3FU, 0x4AC47DU, 0x4ADC96U, 0x4AED43U, 0x4AF5A8U, + 0x4B004BU, 0x4B18A0U, 0x4B2975U, 0x4B319EU, 0x4B4ADCU, 0x4B5237U, 0x4B63E2U, 0x4B7B09U, 0x4B8D8DU, 0x4B9566U, + 0x4BA4B3U, 0x4BBC58U, 0x4BC71AU, 0x4BDFF1U, 0x4BEE24U, 0x4BF6CFU, 0x4C0878U, 0x4C1093U, 0x4C2146U, 0x4C39ADU, + 0x4C42EFU, 0x4C5A04U, 0x4C6BD1U, 0x4C733AU, 0x4C85BEU, 0x4C9D55U, 0x4CAC80U, 0x4CB46BU, 0x4CCF29U, 0x4CD7C2U, + 0x4CE617U, 0x4CFEFCU, 0x4D0B1FU, 0x4D13F4U, 0x4D2221U, 0x4D3ACAU, 0x4D4188U, 0x4D5963U, 0x4D68B6U, 0x4D705DU, + 0x4D86D9U, 0x4D9E32U, 0x4DAFE7U, 0x4DB70CU, 0x4DCC4EU, 0x4DD4A5U, 0x4DE570U, 0x4DFD9BU, 0x4E0EB5U, 0x4E165EU, + 0x4E278BU, 0x4E3F60U, 0x4E4422U, 0x4E5CC9U, 0x4E6D1CU, 0x4E75F7U, 0x4E8373U, 0x4E9B98U, 0x4EAA4DU, 0x4EB2A6U, + 0x4EC9E4U, 0x4ED10FU, 0x4EE0DAU, 0x4EF831U, 0x4F0DD2U, 0x4F1539U, 0x4F24ECU, 0x4F3C07U, 0x4F4745U, 0x4F5FAEU, + 0x4F6E7BU, 0x4F7690U, 0x4F8014U, 0x4F98FFU, 0x4FA92AU, 0x4FB1C1U, 0x4FCA83U, 0x4FD268U, 0x4FE3BDU, 0x4FFB56U, + 0x50018FU, 0x501964U, 0x5028B1U, 0x50305AU, 0x504B18U, 0x5053F3U, 0x506226U, 0x507ACDU, 0x508C49U, 0x5094A2U, + 0x50A577U, 0x50BD9CU, 0x50C6DEU, 0x50DE35U, 0x50EFE0U, 0x50F70BU, 0x5102E8U, 0x511A03U, 0x512BD6U, 0x51333DU, + 0x51487FU, 0x515094U, 0x516141U, 0x5179AAU, 0x518F2EU, 0x5197C5U, 0x51A610U, 0x51BEFBU, 0x51C5B9U, 0x51DD52U, + 0x51EC87U, 0x51F46CU, 0x520742U, 0x521FA9U, 0x522E7CU, 0x523697U, 0x524DD5U, 0x52553EU, 0x5264EBU, 0x527C00U, + 0x528A84U, 0x52926FU, 0x52A3BAU, 0x52BB51U, 0x52C013U, 0x52D8F8U, 0x52E92DU, 0x52F1C6U, 0x530425U, 0x531CCEU, + 0x532D1BU, 0x5335F0U, 0x534EB2U, 0x535659U, 0x53678CU, 0x537F67U, 0x5389E3U, 0x539108U, 0x53A0DDU, 0x53B836U, + 0x53C374U, 0x53DB9FU, 0x53EA4AU, 0x53F2A1U, 0x540C16U, 0x5414FDU, 0x542528U, 0x543DC3U, 0x544681U, 0x545E6AU, + 0x546FBFU, 0x547754U, 0x5481D0U, 0x54993BU, 0x54A8EEU, 0x54B005U, 0x54CB47U, 0x54D3ACU, 0x54E279U, 0x54FA92U, + 0x550F71U, 0x55179AU, 0x55264FU, 0x553EA4U, 0x5545E6U, 0x555D0DU, 0x556CD8U, 0x557433U, 0x5582B7U, 0x559A5CU, + 0x55AB89U, 0x55B362U, 0x55C820U, 0x55D0CBU, 0x55E11EU, 0x55F9F5U, 0x560ADBU, 0x561230U, 0x5623E5U, 0x563B0EU, + 0x56404CU, 0x5658A7U, 0x566972U, 0x567199U, 0x56871DU, 0x569FF6U, 0x56AE23U, 0x56B6C8U, 0x56CD8AU, 0x56D561U, + 0x56E4B4U, 0x56FC5FU, 0x5709BCU, 0x571157U, 0x572082U, 0x573869U, 0x57432BU, 0x575BC0U, 0x576A15U, 0x5772FEU, + 0x57847AU, 0x579C91U, 0x57AD44U, 0x57B5AFU, 0x57CEEDU, 0x57D606U, 0x57E7D3U, 0x57FF38U, 0x580255U, 0x581ABEU, + 0x582B6BU, 0x583380U, 0x5848C2U, 0x585029U, 0x5861FCU, 0x587917U, 0x588F93U, 0x589778U, 0x58A6ADU, 0x58BE46U, + 0x58C504U, 0x58DDEFU, 0x58EC3AU, 0x58F4D1U, 0x590132U, 0x5919D9U, 0x59280CU, 0x5930E7U, 0x594BA5U, 0x59534EU, + 0x59629BU, 0x597A70U, 0x598CF4U, 0x59941FU, 0x59A5CAU, 0x59BD21U, 0x59C663U, 0x59DE88U, 0x59EF5DU, 0x59F7B6U, + 0x5A0498U, 0x5A1C73U, 0x5A2DA6U, 0x5A354DU, 0x5A4E0FU, 0x5A56E4U, 0x5A6731U, 0x5A7FDAU, 0x5A895EU, 0x5A91B5U, + 0x5AA060U, 0x5AB88BU, 0x5AC3C9U, 0x5ADB22U, 0x5AEAF7U, 0x5AF21CU, 0x5B07FFU, 0x5B1F14U, 0x5B2EC1U, 0x5B362AU, + 0x5B4D68U, 0x5B5583U, 0x5B6456U, 0x5B7CBDU, 0x5B8A39U, 0x5B92D2U, 0x5BA307U, 0x5BBBECU, 0x5BC0AEU, 0x5BD845U, + 0x5BE990U, 0x5BF17BU, 0x5C0FCCU, 0x5C1727U, 0x5C26F2U, 0x5C3E19U, 0x5C455BU, 0x5C5DB0U, 0x5C6C65U, 0x5C748EU, + 0x5C820AU, 0x5C9AE1U, 0x5CAB34U, 0x5CB3DFU, 0x5CC89DU, 0x5CD076U, 0x5CE1A3U, 0x5CF948U, 0x5D0CABU, 0x5D1440U, + 0x5D2595U, 0x5D3D7EU, 0x5D463CU, 0x5D5ED7U, 0x5D6F02U, 0x5D77E9U, 0x5D816DU, 0x5D9986U, 0x5DA853U, 0x5DB0B8U, + 0x5DCBFAU, 0x5DD311U, 0x5DE2C4U, 0x5DFA2FU, 0x5E0901U, 0x5E11EAU, 0x5E203FU, 0x5E38D4U, 0x5E4396U, 0x5E5B7DU, + 0x5E6AA8U, 0x5E7243U, 0x5E84C7U, 0x5E9C2CU, 0x5EADF9U, 0x5EB512U, 0x5ECE50U, 0x5ED6BBU, 0x5EE76EU, 0x5EFF85U, + 0x5F0A66U, 0x5F128DU, 0x5F2358U, 0x5F3BB3U, 0x5F40F1U, 0x5F581AU, 0x5F69CFU, 0x5F7124U, 0x5F87A0U, 0x5F9F4BU, + 0x5FAE9EU, 0x5FB675U, 0x5FCD37U, 0x5FD5DCU, 0x5FE409U, 0x5FFCE2U, 0x600953U, 0x6011B8U, 0x60206DU, 0x603886U, + 0x6043C4U, 0x605B2FU, 0x606AFAU, 0x607211U, 0x608495U, 0x609C7EU, 0x60ADABU, 0x60B540U, 0x60CE02U, 0x60D6E9U, + 0x60E73CU, 0x60FFD7U, 0x610A34U, 0x6112DFU, 0x61230AU, 0x613BE1U, 0x6140A3U, 0x615848U, 0x61699DU, 0x617176U, + 0x6187F2U, 0x619F19U, 0x61AECCU, 0x61B627U, 0x61CD65U, 0x61D58EU, 0x61E45BU, 0x61FCB0U, 0x620F9EU, 0x621775U, + 0x6226A0U, 0x623E4BU, 0x624509U, 0x625DE2U, 0x626C37U, 0x6274DCU, 0x628258U, 0x629AB3U, 0x62AB66U, 0x62B38DU, + 0x62C8CFU, 0x62D024U, 0x62E1F1U, 0x62F91AU, 0x630CF9U, 0x631412U, 0x6325C7U, 0x633D2CU, 0x63466EU, 0x635E85U, + 0x636F50U, 0x6377BBU, 0x63813FU, 0x6399D4U, 0x63A801U, 0x63B0EAU, 0x63CBA8U, 0x63D343U, 0x63E296U, 0x63FA7DU, + 0x6404CAU, 0x641C21U, 0x642DF4U, 0x64351FU, 0x644E5DU, 0x6456B6U, 0x646763U, 0x647F88U, 0x64890CU, 0x6491E7U, + 0x64A032U, 0x64B8D9U, 0x64C39BU, 0x64DB70U, 0x64EAA5U, 0x64F24EU, 0x6507ADU, 0x651F46U, 0x652E93U, 0x653678U, + 0x654D3AU, 0x6555D1U, 0x656404U, 0x657CEFU, 0x658A6BU, 0x659280U, 0x65A355U, 0x65BBBEU, 0x65C0FCU, 0x65D817U, + 0x65E9C2U, 0x65F129U, 0x660207U, 0x661AECU, 0x662B39U, 0x6633D2U, 0x664890U, 0x66507BU, 0x6661AEU, 0x667945U, + 0x668FC1U, 0x66972AU, 0x66A6FFU, 0x66BE14U, 0x66C556U, 0x66DDBDU, 0x66EC68U, 0x66F483U, 0x670160U, 0x67198BU, + 0x67285EU, 0x6730B5U, 0x674BF7U, 0x67531CU, 0x6762C9U, 0x677A22U, 0x678CA6U, 0x67944DU, 0x67A598U, 0x67BD73U, + 0x67C631U, 0x67DEDAU, 0x67EF0FU, 0x67F7E4U, 0x680A89U, 0x681262U, 0x6823B7U, 0x683B5CU, 0x68401EU, 0x6858F5U, + 0x686920U, 0x6871CBU, 0x68874FU, 0x689FA4U, 0x68AE71U, 0x68B69AU, 0x68CDD8U, 0x68D533U, 0x68E4E6U, 0x68FC0DU, + 0x6909EEU, 0x691105U, 0x6920D0U, 0x69383BU, 0x694379U, 0x695B92U, 0x696A47U, 0x6972ACU, 0x698428U, 0x699CC3U, + 0x69AD16U, 0x69B5FDU, 0x69CEBFU, 0x69D654U, 0x69E781U, 0x69FF6AU, 0x6A0C44U, 0x6A14AFU, 0x6A257AU, 0x6A3D91U, + 0x6A46D3U, 0x6A5E38U, 0x6A6FEDU, 0x6A7706U, 0x6A8182U, 0x6A9969U, 0x6AA8BCU, 0x6AB057U, 0x6ACB15U, 0x6AD3FEU, + 0x6AE22BU, 0x6AFAC0U, 0x6B0F23U, 0x6B17C8U, 0x6B261DU, 0x6B3EF6U, 0x6B45B4U, 0x6B5D5FU, 0x6B6C8AU, 0x6B7461U, + 0x6B82E5U, 0x6B9A0EU, 0x6BABDBU, 0x6BB330U, 0x6BC872U, 0x6BD099U, 0x6BE14CU, 0x6BF9A7U, 0x6C0710U, 0x6C1FFBU, + 0x6C2E2EU, 0x6C36C5U, 0x6C4D87U, 0x6C556CU, 0x6C64B9U, 0x6C7C52U, 0x6C8AD6U, 0x6C923DU, 0x6CA3E8U, 0x6CBB03U, + 0x6CC041U, 0x6CD8AAU, 0x6CE97FU, 0x6CF194U, 0x6D0477U, 0x6D1C9CU, 0x6D2D49U, 0x6D35A2U, 0x6D4EE0U, 0x6D560BU, + 0x6D67DEU, 0x6D7F35U, 0x6D89B1U, 0x6D915AU, 0x6DA08FU, 0x6DB864U, 0x6DC326U, 0x6DDBCDU, 0x6DEA18U, 0x6DF2F3U, + 0x6E01DDU, 0x6E1936U, 0x6E28E3U, 0x6E3008U, 0x6E4B4AU, 0x6E53A1U, 0x6E6274U, 0x6E7A9FU, 0x6E8C1BU, 0x6E94F0U, + 0x6EA525U, 0x6EBDCEU, 0x6EC68CU, 0x6EDE67U, 0x6EEFB2U, 0x6EF759U, 0x6F02BAU, 0x6F1A51U, 0x6F2B84U, 0x6F336FU, + 0x6F482DU, 0x6F50C6U, 0x6F6113U, 0x6F79F8U, 0x6F8F7CU, 0x6F9797U, 0x6FA642U, 0x6FBEA9U, 0x6FC5EBU, 0x6FDD00U, + 0x6FECD5U, 0x6FF43EU, 0x700EE7U, 0x70160CU, 0x7027D9U, 0x703F32U, 0x704470U, 0x705C9BU, 0x706D4EU, 0x7075A5U, + 0x708321U, 0x709BCAU, 0x70AA1FU, 0x70B2F4U, 0x70C9B6U, 0x70D15DU, 0x70E088U, 0x70F863U, 0x710D80U, 0x71156BU, + 0x7124BEU, 0x713C55U, 0x714717U, 0x715FFCU, 0x716E29U, 0x7176C2U, 0x718046U, 0x7198ADU, 0x71A978U, 0x71B193U, + 0x71CAD1U, 0x71D23AU, 0x71E3EFU, 0x71FB04U, 0x72082AU, 0x7210C1U, 0x722114U, 0x7239FFU, 0x7242BDU, 0x725A56U, + 0x726B83U, 0x727368U, 0x7285ECU, 0x729D07U, 0x72ACD2U, 0x72B439U, 0x72CF7BU, 0x72D790U, 0x72E645U, 0x72FEAEU, + 0x730B4DU, 0x7313A6U, 0x732273U, 0x733A98U, 0x7341DAU, 0x735931U, 0x7368E4U, 0x73700FU, 0x73868BU, 0x739E60U, + 0x73AFB5U, 0x73B75EU, 0x73CC1CU, 0x73D4F7U, 0x73E522U, 0x73FDC9U, 0x74037EU, 0x741B95U, 0x742A40U, 0x7432ABU, + 0x7449E9U, 0x745102U, 0x7460D7U, 0x74783CU, 0x748EB8U, 0x749653U, 0x74A786U, 0x74BF6DU, 0x74C42FU, 0x74DCC4U, + 0x74ED11U, 0x74F5FAU, 0x750019U, 0x7518F2U, 0x752927U, 0x7531CCU, 0x754A8EU, 0x755265U, 0x7563B0U, 0x757B5BU, + 0x758DDFU, 0x759534U, 0x75A4E1U, 0x75BC0AU, 0x75C748U, 0x75DFA3U, 0x75EE76U, 0x75F69DU, 0x7605B3U, 0x761D58U, + 0x762C8DU, 0x763466U, 0x764F24U, 0x7657CFU, 0x76661AU, 0x767EF1U, 0x768875U, 0x76909EU, 0x76A14BU, 0x76B9A0U, + 0x76C2E2U, 0x76DA09U, 0x76EBDCU, 0x76F337U, 0x7706D4U, 0x771E3FU, 0x772FEAU, 0x773701U, 0x774C43U, 0x7754A8U, + 0x77657DU, 0x777D96U, 0x778B12U, 0x7793F9U, 0x77A22CU, 0x77BAC7U, 0x77C185U, 0x77D96EU, 0x77E8BBU, 0x77F050U, + 0x780D3DU, 0x7815D6U, 0x782403U, 0x783CE8U, 0x7847AAU, 0x785F41U, 0x786E94U, 0x78767FU, 0x7880FBU, 0x789810U, + 0x78A9C5U, 0x78B12EU, 0x78CA6CU, 0x78D287U, 0x78E352U, 0x78FBB9U, 0x790E5AU, 0x7916B1U, 0x792764U, 0x793F8FU, + 0x7944CDU, 0x795C26U, 0x796DF3U, 0x797518U, 0x79839CU, 0x799B77U, 0x79AAA2U, 0x79B249U, 0x79C90BU, 0x79D1E0U, + 0x79E035U, 0x79F8DEU, 0x7A0BF0U, 0x7A131BU, 0x7A22CEU, 0x7A3A25U, 0x7A4167U, 0x7A598CU, 0x7A6859U, 0x7A70B2U, + 0x7A8636U, 0x7A9EDDU, 0x7AAF08U, 0x7AB7E3U, 0x7ACCA1U, 0x7AD44AU, 0x7AE59FU, 0x7AFD74U, 0x7B0897U, 0x7B107CU, + 0x7B21A9U, 0x7B3942U, 0x7B4200U, 0x7B5AEBU, 0x7B6B3EU, 0x7B73D5U, 0x7B8551U, 0x7B9DBAU, 0x7BAC6FU, 0x7BB484U, + 0x7BCFC6U, 0x7BD72DU, 0x7BE6F8U, 0x7BFE13U, 0x7C00A4U, 0x7C184FU, 0x7C299AU, 0x7C3171U, 0x7C4A33U, 0x7C52D8U, + 0x7C630DU, 0x7C7BE6U, 0x7C8D62U, 0x7C9589U, 0x7CA45CU, 0x7CBCB7U, 0x7CC7F5U, 0x7CDF1EU, 0x7CEECBU, 0x7CF620U, + 0x7D03C3U, 0x7D1B28U, 0x7D2AFDU, 0x7D3216U, 0x7D4954U, 0x7D51BFU, 0x7D606AU, 0x7D7881U, 0x7D8E05U, 0x7D96EEU, + 0x7DA73BU, 0x7DBFD0U, 0x7DC492U, 0x7DDC79U, 0x7DEDACU, 0x7DF547U, 0x7E0669U, 0x7E1E82U, 0x7E2F57U, 0x7E37BCU, + 0x7E4CFEU, 0x7E5415U, 0x7E65C0U, 0x7E7D2BU, 0x7E8BAFU, 0x7E9344U, 0x7EA291U, 0x7EBA7AU, 0x7EC138U, 0x7ED9D3U, + 0x7EE806U, 0x7EF0EDU, 0x7F050EU, 0x7F1DE5U, 0x7F2C30U, 0x7F34DBU, 0x7F4F99U, 0x7F5772U, 0x7F66A7U, 0x7F7E4CU, + 0x7F88C8U, 0x7F9023U, 0x7FA1F6U, 0x7FB91DU, 0x7FC25FU, 0x7FDAB4U, 0x7FEB61U, 0x7FF38AU, 0x800C75U, 0x80149EU, + 0x80254BU, 0x803DA0U, 0x8046E2U, 0x805E09U, 0x806FDCU, 0x807737U, 0x8081B3U, 0x809958U, 0x80A88DU, 0x80B066U, + 0x80CB24U, 0x80D3CFU, 0x80E21AU, 0x80FAF1U, 0x810F12U, 0x8117F9U, 0x81262CU, 0x813EC7U, 0x814585U, 0x815D6EU, + 0x816CBBU, 0x817450U, 0x8182D4U, 0x819A3FU, 0x81ABEAU, 0x81B301U, 0x81C843U, 0x81D0A8U, 0x81E17DU, 0x81F996U, + 0x820AB8U, 0x821253U, 0x822386U, 0x823B6DU, 0x82402FU, 0x8258C4U, 0x826911U, 0x8271FAU, 0x82877EU, 0x829F95U, + 0x82AE40U, 0x82B6ABU, 0x82CDE9U, 0x82D502U, 0x82E4D7U, 0x82FC3CU, 0x8309DFU, 0x831134U, 0x8320E1U, 0x83380AU, + 0x834348U, 0x835BA3U, 0x836A76U, 0x83729DU, 0x838419U, 0x839CF2U, 0x83AD27U, 0x83B5CCU, 0x83CE8EU, 0x83D665U, + 0x83E7B0U, 0x83FF5BU, 0x8401ECU, 0x841907U, 0x8428D2U, 0x843039U, 0x844B7BU, 0x845390U, 0x846245U, 0x847AAEU, + 0x848C2AU, 0x8494C1U, 0x84A514U, 0x84BDFFU, 0x84C6BDU, 0x84DE56U, 0x84EF83U, 0x84F768U, 0x85028BU, 0x851A60U, + 0x852BB5U, 0x85335EU, 0x85481CU, 0x8550F7U, 0x856122U, 0x8579C9U, 0x858F4DU, 0x8597A6U, 0x85A673U, 0x85BE98U, + 0x85C5DAU, 0x85DD31U, 0x85ECE4U, 0x85F40FU, 0x860721U, 0x861FCAU, 0x862E1FU, 0x8636F4U, 0x864DB6U, 0x86555DU, + 0x866488U, 0x867C63U, 0x868AE7U, 0x86920CU, 0x86A3D9U, 0x86BB32U, 0x86C070U, 0x86D89BU, 0x86E94EU, 0x86F1A5U, + 0x870446U, 0x871CADU, 0x872D78U, 0x873593U, 0x874ED1U, 0x87563AU, 0x8767EFU, 0x877F04U, 0x878980U, 0x87916BU, + 0x87A0BEU, 0x87B855U, 0x87C317U, 0x87DBFCU, 0x87EA29U, 0x87F2C2U, 0x880FAFU, 0x881744U, 0x882691U, 0x883E7AU, + 0x884538U, 0x885DD3U, 0x886C06U, 0x8874EDU, 0x888269U, 0x889A82U, 0x88AB57U, 0x88B3BCU, 0x88C8FEU, 0x88D015U, + 0x88E1C0U, 0x88F92BU, 0x890CC8U, 0x891423U, 0x8925F6U, 0x893D1DU, 0x89465FU, 0x895EB4U, 0x896F61U, 0x89778AU, + 0x89810EU, 0x8999E5U, 0x89A830U, 0x89B0DBU, 0x89CB99U, 0x89D372U, 0x89E2A7U, 0x89FA4CU, 0x8A0962U, 0x8A1189U, + 0x8A205CU, 0x8A38B7U, 0x8A43F5U, 0x8A5B1EU, 0x8A6ACBU, 0x8A7220U, 0x8A84A4U, 0x8A9C4FU, 0x8AAD9AU, 0x8AB571U, + 0x8ACE33U, 0x8AD6D8U, 0x8AE70DU, 0x8AFFE6U, 0x8B0A05U, 0x8B12EEU, 0x8B233BU, 0x8B3BD0U, 0x8B4092U, 0x8B5879U, + 0x8B69ACU, 0x8B7147U, 0x8B87C3U, 0x8B9F28U, 0x8BAEFDU, 0x8BB616U, 0x8BCD54U, 0x8BD5BFU, 0x8BE46AU, 0x8BFC81U, + 0x8C0236U, 0x8C1ADDU, 0x8C2B08U, 0x8C33E3U, 0x8C48A1U, 0x8C504AU, 0x8C619FU, 0x8C7974U, 0x8C8FF0U, 0x8C971BU, + 0x8CA6CEU, 0x8CBE25U, 0x8CC567U, 0x8CDD8CU, 0x8CEC59U, 0x8CF4B2U, 0x8D0151U, 0x8D19BAU, 0x8D286FU, 0x8D3084U, + 0x8D4BC6U, 0x8D532DU, 0x8D62F8U, 0x8D7A13U, 0x8D8C97U, 0x8D947CU, 0x8DA5A9U, 0x8DBD42U, 0x8DC600U, 0x8DDEEBU, + 0x8DEF3EU, 0x8DF7D5U, 0x8E04FBU, 0x8E1C10U, 0x8E2DC5U, 0x8E352EU, 0x8E4E6CU, 0x8E5687U, 0x8E6752U, 0x8E7FB9U, + 0x8E893DU, 0x8E91D6U, 0x8EA003U, 0x8EB8E8U, 0x8EC3AAU, 0x8EDB41U, 0x8EEA94U, 0x8EF27FU, 0x8F079CU, 0x8F1F77U, + 0x8F2EA2U, 0x8F3649U, 0x8F4D0BU, 0x8F55E0U, 0x8F6435U, 0x8F7CDEU, 0x8F8A5AU, 0x8F92B1U, 0x8FA364U, 0x8FBB8FU, + 0x8FC0CDU, 0x8FD826U, 0x8FE9F3U, 0x8FF118U, 0x900BC1U, 0x90132AU, 0x9022FFU, 0x903A14U, 0x904156U, 0x9059BDU, + 0x906868U, 0x907083U, 0x908607U, 0x909EECU, 0x90AF39U, 0x90B7D2U, 0x90CC90U, 0x90D47BU, 0x90E5AEU, 0x90FD45U, + 0x9108A6U, 0x91104DU, 0x912198U, 0x913973U, 0x914231U, 0x915ADAU, 0x916B0FU, 0x9173E4U, 0x918560U, 0x919D8BU, + 0x91AC5EU, 0x91B4B5U, 0x91CFF7U, 0x91D71CU, 0x91E6C9U, 0x91FE22U, 0x920D0CU, 0x9215E7U, 0x922432U, 0x923CD9U, + 0x92479BU, 0x925F70U, 0x926EA5U, 0x92764EU, 0x9280CAU, 0x929821U, 0x92A9F4U, 0x92B11FU, 0x92CA5DU, 0x92D2B6U, + 0x92E363U, 0x92FB88U, 0x930E6BU, 0x931680U, 0x932755U, 0x933FBEU, 0x9344FCU, 0x935C17U, 0x936DC2U, 0x937529U, + 0x9383ADU, 0x939B46U, 0x93AA93U, 0x93B278U, 0x93C93AU, 0x93D1D1U, 0x93E004U, 0x93F8EFU, 0x940658U, 0x941EB3U, + 0x942F66U, 0x94378DU, 0x944CCFU, 0x945424U, 0x9465F1U, 0x947D1AU, 0x948B9EU, 0x949375U, 0x94A2A0U, 0x94BA4BU, + 0x94C109U, 0x94D9E2U, 0x94E837U, 0x94F0DCU, 0x95053FU, 0x951DD4U, 0x952C01U, 0x9534EAU, 0x954FA8U, 0x955743U, + 0x956696U, 0x957E7DU, 0x9588F9U, 0x959012U, 0x95A1C7U, 0x95B92CU, 0x95C26EU, 0x95DA85U, 0x95EB50U, 0x95F3BBU, + 0x960095U, 0x96187EU, 0x9629ABU, 0x963140U, 0x964A02U, 0x9652E9U, 0x96633CU, 0x967BD7U, 0x968D53U, 0x9695B8U, + 0x96A46DU, 0x96BC86U, 0x96C7C4U, 0x96DF2FU, 0x96EEFAU, 0x96F611U, 0x9703F2U, 0x971B19U, 0x972ACCU, 0x973227U, + 0x974965U, 0x97518EU, 0x97605BU, 0x9778B0U, 0x978E34U, 0x9796DFU, 0x97A70AU, 0x97BFE1U, 0x97C4A3U, 0x97DC48U, + 0x97ED9DU, 0x97F576U, 0x98081BU, 0x9810F0U, 0x982125U, 0x9839CEU, 0x98428CU, 0x985A67U, 0x986BB2U, 0x987359U, + 0x9885DDU, 0x989D36U, 0x98ACE3U, 0x98B408U, 0x98CF4AU, 0x98D7A1U, 0x98E674U, 0x98FE9FU, 0x990B7CU, 0x991397U, + 0x992242U, 0x993AA9U, 0x9941EBU, 0x995900U, 0x9968D5U, 0x99703EU, 0x9986BAU, 0x999E51U, 0x99AF84U, 0x99B76FU, + 0x99CC2DU, 0x99D4C6U, 0x99E513U, 0x99FDF8U, 0x9A0ED6U, 0x9A163DU, 0x9A27E8U, 0x9A3F03U, 0x9A4441U, 0x9A5CAAU, + 0x9A6D7FU, 0x9A7594U, 0x9A8310U, 0x9A9BFBU, 0x9AAA2EU, 0x9AB2C5U, 0x9AC987U, 0x9AD16CU, 0x9AE0B9U, 0x9AF852U, + 0x9B0DB1U, 0x9B155AU, 0x9B248FU, 0x9B3C64U, 0x9B4726U, 0x9B5FCDU, 0x9B6E18U, 0x9B76F3U, 0x9B8077U, 0x9B989CU, + 0x9BA949U, 0x9BB1A2U, 0x9BCAE0U, 0x9BD20BU, 0x9BE3DEU, 0x9BFB35U, 0x9C0582U, 0x9C1D69U, 0x9C2CBCU, 0x9C3457U, + 0x9C4F15U, 0x9C57FEU, 0x9C662BU, 0x9C7EC0U, 0x9C8844U, 0x9C90AFU, 0x9CA17AU, 0x9CB991U, 0x9CC2D3U, 0x9CDA38U, + 0x9CEBEDU, 0x9CF306U, 0x9D06E5U, 0x9D1E0EU, 0x9D2FDBU, 0x9D3730U, 0x9D4C72U, 0x9D5499U, 0x9D654CU, 0x9D7DA7U, + 0x9D8B23U, 0x9D93C8U, 0x9DA21DU, 0x9DBAF6U, 0x9DC1B4U, 0x9DD95FU, 0x9DE88AU, 0x9DF061U, 0x9E034FU, 0x9E1BA4U, + 0x9E2A71U, 0x9E329AU, 0x9E49D8U, 0x9E5133U, 0x9E60E6U, 0x9E780DU, 0x9E8E89U, 0x9E9662U, 0x9EA7B7U, 0x9EBF5CU, + 0x9EC41EU, 0x9EDCF5U, 0x9EED20U, 0x9EF5CBU, 0x9F0028U, 0x9F18C3U, 0x9F2916U, 0x9F31FDU, 0x9F4ABFU, 0x9F5254U, + 0x9F6381U, 0x9F7B6AU, 0x9F8DEEU, 0x9F9505U, 0x9FA4D0U, 0x9FBC3BU, 0x9FC779U, 0x9FDF92U, 0x9FEE47U, 0x9FF6ACU, + 0xA0031DU, 0xA01BF6U, 0xA02A23U, 0xA032C8U, 0xA0498AU, 0xA05161U, 0xA060B4U, 0xA0785FU, 0xA08EDBU, 0xA09630U, + 0xA0A7E5U, 0xA0BF0EU, 0xA0C44CU, 0xA0DCA7U, 0xA0ED72U, 0xA0F599U, 0xA1007AU, 0xA11891U, 0xA12944U, 0xA131AFU, + 0xA14AEDU, 0xA15206U, 0xA163D3U, 0xA17B38U, 0xA18DBCU, 0xA19557U, 0xA1A482U, 0xA1BC69U, 0xA1C72BU, 0xA1DFC0U, + 0xA1EE15U, 0xA1F6FEU, 0xA205D0U, 0xA21D3BU, 0xA22CEEU, 0xA23405U, 0xA24F47U, 0xA257ACU, 0xA26679U, 0xA27E92U, + 0xA28816U, 0xA290FDU, 0xA2A128U, 0xA2B9C3U, 0xA2C281U, 0xA2DA6AU, 0xA2EBBFU, 0xA2F354U, 0xA306B7U, 0xA31E5CU, + 0xA32F89U, 0xA33762U, 0xA34C20U, 0xA354CBU, 0xA3651EU, 0xA37DF5U, 0xA38B71U, 0xA3939AU, 0xA3A24FU, 0xA3BAA4U, + 0xA3C1E6U, 0xA3D90DU, 0xA3E8D8U, 0xA3F033U, 0xA40E84U, 0xA4166FU, 0xA427BAU, 0xA43F51U, 0xA44413U, 0xA45CF8U, + 0xA46D2DU, 0xA475C6U, 0xA48342U, 0xA49BA9U, 0xA4AA7CU, 0xA4B297U, 0xA4C9D5U, 0xA4D13EU, 0xA4E0EBU, 0xA4F800U, + 0xA50DE3U, 0xA51508U, 0xA524DDU, 0xA53C36U, 0xA54774U, 0xA55F9FU, 0xA56E4AU, 0xA576A1U, 0xA58025U, 0xA598CEU, + 0xA5A91BU, 0xA5B1F0U, 0xA5CAB2U, 0xA5D259U, 0xA5E38CU, 0xA5FB67U, 0xA60849U, 0xA610A2U, 0xA62177U, 0xA6399CU, + 0xA642DEU, 0xA65A35U, 0xA66BE0U, 0xA6730BU, 0xA6858FU, 0xA69D64U, 0xA6ACB1U, 0xA6B45AU, 0xA6CF18U, 0xA6D7F3U, + 0xA6E626U, 0xA6FECDU, 0xA70B2EU, 0xA713C5U, 0xA72210U, 0xA73AFBU, 0xA741B9U, 0xA75952U, 0xA76887U, 0xA7706CU, + 0xA786E8U, 0xA79E03U, 0xA7AFD6U, 0xA7B73DU, 0xA7CC7FU, 0xA7D494U, 0xA7E541U, 0xA7FDAAU, 0xA800C7U, 0xA8182CU, + 0xA829F9U, 0xA83112U, 0xA84A50U, 0xA852BBU, 0xA8636EU, 0xA87B85U, 0xA88D01U, 0xA895EAU, 0xA8A43FU, 0xA8BCD4U, + 0xA8C796U, 0xA8DF7DU, 0xA8EEA8U, 0xA8F643U, 0xA903A0U, 0xA91B4BU, 0xA92A9EU, 0xA93275U, 0xA94937U, 0xA951DCU, + 0xA96009U, 0xA978E2U, 0xA98E66U, 0xA9968DU, 0xA9A758U, 0xA9BFB3U, 0xA9C4F1U, 0xA9DC1AU, 0xA9EDCFU, 0xA9F524U, + 0xAA060AU, 0xAA1EE1U, 0xAA2F34U, 0xAA37DFU, 0xAA4C9DU, 0xAA5476U, 0xAA65A3U, 0xAA7D48U, 0xAA8BCCU, 0xAA9327U, + 0xAAA2F2U, 0xAABA19U, 0xAAC15BU, 0xAAD9B0U, 0xAAE865U, 0xAAF08EU, 0xAB056DU, 0xAB1D86U, 0xAB2C53U, 0xAB34B8U, + 0xAB4FFAU, 0xAB5711U, 0xAB66C4U, 0xAB7E2FU, 0xAB88ABU, 0xAB9040U, 0xABA195U, 0xABB97EU, 0xABC23CU, 0xABDAD7U, + 0xABEB02U, 0xABF3E9U, 0xAC0D5EU, 0xAC15B5U, 0xAC2460U, 0xAC3C8BU, 0xAC47C9U, 0xAC5F22U, 0xAC6EF7U, 0xAC761CU, + 0xAC8098U, 0xAC9873U, 0xACA9A6U, 0xACB14DU, 0xACCA0FU, 0xACD2E4U, 0xACE331U, 0xACFBDAU, 0xAD0E39U, 0xAD16D2U, + 0xAD2707U, 0xAD3FECU, 0xAD44AEU, 0xAD5C45U, 0xAD6D90U, 0xAD757BU, 0xAD83FFU, 0xAD9B14U, 0xADAAC1U, 0xADB22AU, + 0xADC968U, 0xADD183U, 0xADE056U, 0xADF8BDU, 0xAE0B93U, 0xAE1378U, 0xAE22ADU, 0xAE3A46U, 0xAE4104U, 0xAE59EFU, + 0xAE683AU, 0xAE70D1U, 0xAE8655U, 0xAE9EBEU, 0xAEAF6BU, 0xAEB780U, 0xAECCC2U, 0xAED429U, 0xAEE5FCU, 0xAEFD17U, + 0xAF08F4U, 0xAF101FU, 0xAF21CAU, 0xAF3921U, 0xAF4263U, 0xAF5A88U, 0xAF6B5DU, 0xAF73B6U, 0xAF8532U, 0xAF9DD9U, + 0xAFAC0CU, 0xAFB4E7U, 0xAFCFA5U, 0xAFD74EU, 0xAFE69BU, 0xAFFE70U, 0xB004A9U, 0xB01C42U, 0xB02D97U, 0xB0357CU, + 0xB04E3EU, 0xB056D5U, 0xB06700U, 0xB07FEBU, 0xB0896FU, 0xB09184U, 0xB0A051U, 0xB0B8BAU, 0xB0C3F8U, 0xB0DB13U, + 0xB0EAC6U, 0xB0F22DU, 0xB107CEU, 0xB11F25U, 0xB12EF0U, 0xB1361BU, 0xB14D59U, 0xB155B2U, 0xB16467U, 0xB17C8CU, + 0xB18A08U, 0xB192E3U, 0xB1A336U, 0xB1BBDDU, 0xB1C09FU, 0xB1D874U, 0xB1E9A1U, 0xB1F14AU, 0xB20264U, 0xB21A8FU, + 0xB22B5AU, 0xB233B1U, 0xB248F3U, 0xB25018U, 0xB261CDU, 0xB27926U, 0xB28FA2U, 0xB29749U, 0xB2A69CU, 0xB2BE77U, + 0xB2C535U, 0xB2DDDEU, 0xB2EC0BU, 0xB2F4E0U, 0xB30103U, 0xB319E8U, 0xB3283DU, 0xB330D6U, 0xB34B94U, 0xB3537FU, + 0xB362AAU, 0xB37A41U, 0xB38CC5U, 0xB3942EU, 0xB3A5FBU, 0xB3BD10U, 0xB3C652U, 0xB3DEB9U, 0xB3EF6CU, 0xB3F787U, + 0xB40930U, 0xB411DBU, 0xB4200EU, 0xB438E5U, 0xB443A7U, 0xB45B4CU, 0xB46A99U, 0xB47272U, 0xB484F6U, 0xB49C1DU, + 0xB4ADC8U, 0xB4B523U, 0xB4CE61U, 0xB4D68AU, 0xB4E75FU, 0xB4FFB4U, 0xB50A57U, 0xB512BCU, 0xB52369U, 0xB53B82U, + 0xB540C0U, 0xB5582BU, 0xB569FEU, 0xB57115U, 0xB58791U, 0xB59F7AU, 0xB5AEAFU, 0xB5B644U, 0xB5CD06U, 0xB5D5EDU, + 0xB5E438U, 0xB5FCD3U, 0xB60FFDU, 0xB61716U, 0xB626C3U, 0xB63E28U, 0xB6456AU, 0xB65D81U, 0xB66C54U, 0xB674BFU, + 0xB6823BU, 0xB69AD0U, 0xB6AB05U, 0xB6B3EEU, 0xB6C8ACU, 0xB6D047U, 0xB6E192U, 0xB6F979U, 0xB70C9AU, 0xB71471U, + 0xB725A4U, 0xB73D4FU, 0xB7460DU, 0xB75EE6U, 0xB76F33U, 0xB777D8U, 0xB7815CU, 0xB799B7U, 0xB7A862U, 0xB7B089U, + 0xB7CBCBU, 0xB7D320U, 0xB7E2F5U, 0xB7FA1EU, 0xB80773U, 0xB81F98U, 0xB82E4DU, 0xB836A6U, 0xB84DE4U, 0xB8550FU, + 0xB864DAU, 0xB87C31U, 0xB88AB5U, 0xB8925EU, 0xB8A38BU, 0xB8BB60U, 0xB8C022U, 0xB8D8C9U, 0xB8E91CU, 0xB8F1F7U, + 0xB90414U, 0xB91CFFU, 0xB92D2AU, 0xB935C1U, 0xB94E83U, 0xB95668U, 0xB967BDU, 0xB97F56U, 0xB989D2U, 0xB99139U, + 0xB9A0ECU, 0xB9B807U, 0xB9C345U, 0xB9DBAEU, 0xB9EA7BU, 0xB9F290U, 0xBA01BEU, 0xBA1955U, 0xBA2880U, 0xBA306BU, + 0xBA4B29U, 0xBA53C2U, 0xBA6217U, 0xBA7AFCU, 0xBA8C78U, 0xBA9493U, 0xBAA546U, 0xBABDADU, 0xBAC6EFU, 0xBADE04U, + 0xBAEFD1U, 0xBAF73AU, 0xBB02D9U, 0xBB1A32U, 0xBB2BE7U, 0xBB330CU, 0xBB484EU, 0xBB50A5U, 0xBB6170U, 0xBB799BU, + 0xBB8F1FU, 0xBB97F4U, 0xBBA621U, 0xBBBECAU, 0xBBC588U, 0xBBDD63U, 0xBBECB6U, 0xBBF45DU, 0xBC0AEAU, 0xBC1201U, + 0xBC23D4U, 0xBC3B3FU, 0xBC407DU, 0xBC5896U, 0xBC6943U, 0xBC71A8U, 0xBC872CU, 0xBC9FC7U, 0xBCAE12U, 0xBCB6F9U, + 0xBCCDBBU, 0xBCD550U, 0xBCE485U, 0xBCFC6EU, 0xBD098DU, 0xBD1166U, 0xBD20B3U, 0xBD3858U, 0xBD431AU, 0xBD5BF1U, + 0xBD6A24U, 0xBD72CFU, 0xBD844BU, 0xBD9CA0U, 0xBDAD75U, 0xBDB59EU, 0xBDCEDCU, 0xBDD637U, 0xBDE7E2U, 0xBDFF09U, + 0xBE0C27U, 0xBE14CCU, 0xBE2519U, 0xBE3DF2U, 0xBE46B0U, 0xBE5E5BU, 0xBE6F8EU, 0xBE7765U, 0xBE81E1U, 0xBE990AU, + 0xBEA8DFU, 0xBEB034U, 0xBECB76U, 0xBED39DU, 0xBEE248U, 0xBEFAA3U, 0xBF0F40U, 0xBF17ABU, 0xBF267EU, 0xBF3E95U, + 0xBF45D7U, 0xBF5D3CU, 0xBF6CE9U, 0xBF7402U, 0xBF8286U, 0xBF9A6DU, 0xBFABB8U, 0xBFB353U, 0xBFC811U, 0xBFD0FAU, + 0xBFE12FU, 0xBFF9C4U, 0xC00A4EU, 0xC012A5U, 0xC02370U, 0xC03B9BU, 0xC040D9U, 0xC05832U, 0xC069E7U, 0xC0710CU, + 0xC08788U, 0xC09F63U, 0xC0AEB6U, 0xC0B65DU, 0xC0CD1FU, 0xC0D5F4U, 0xC0E421U, 0xC0FCCAU, 0xC10929U, 0xC111C2U, + 0xC12017U, 0xC138FCU, 0xC143BEU, 0xC15B55U, 0xC16A80U, 0xC1726BU, 0xC184EFU, 0xC19C04U, 0xC1ADD1U, 0xC1B53AU, + 0xC1CE78U, 0xC1D693U, 0xC1E746U, 0xC1FFADU, 0xC20C83U, 0xC21468U, 0xC225BDU, 0xC23D56U, 0xC24614U, 0xC25EFFU, + 0xC26F2AU, 0xC277C1U, 0xC28145U, 0xC299AEU, 0xC2A87BU, 0xC2B090U, 0xC2CBD2U, 0xC2D339U, 0xC2E2ECU, 0xC2FA07U, + 0xC30FE4U, 0xC3170FU, 0xC326DAU, 0xC33E31U, 0xC34573U, 0xC35D98U, 0xC36C4DU, 0xC374A6U, 0xC38222U, 0xC39AC9U, + 0xC3AB1CU, 0xC3B3F7U, 0xC3C8B5U, 0xC3D05EU, 0xC3E18BU, 0xC3F960U, 0xC407D7U, 0xC41F3CU, 0xC42EE9U, 0xC43602U, + 0xC44D40U, 0xC455ABU, 0xC4647EU, 0xC47C95U, 0xC48A11U, 0xC492FAU, 0xC4A32FU, 0xC4BBC4U, 0xC4C086U, 0xC4D86DU, + 0xC4E9B8U, 0xC4F153U, 0xC504B0U, 0xC51C5BU, 0xC52D8EU, 0xC53565U, 0xC54E27U, 0xC556CCU, 0xC56719U, 0xC57FF2U, + 0xC58976U, 0xC5919DU, 0xC5A048U, 0xC5B8A3U, 0xC5C3E1U, 0xC5DB0AU, 0xC5EADFU, 0xC5F234U, 0xC6011AU, 0xC619F1U, + 0xC62824U, 0xC630CFU, 0xC64B8DU, 0xC65366U, 0xC662B3U, 0xC67A58U, 0xC68CDCU, 0xC69437U, 0xC6A5E2U, 0xC6BD09U, + 0xC6C64BU, 0xC6DEA0U, 0xC6EF75U, 0xC6F79EU, 0xC7027DU, 0xC71A96U, 0xC72B43U, 0xC733A8U, 0xC748EAU, 0xC75001U, + 0xC761D4U, 0xC7793FU, 0xC78FBBU, 0xC79750U, 0xC7A685U, 0xC7BE6EU, 0xC7C52CU, 0xC7DDC7U, 0xC7EC12U, 0xC7F4F9U, + 0xC80994U, 0xC8117FU, 0xC820AAU, 0xC83841U, 0xC84303U, 0xC85BE8U, 0xC86A3DU, 0xC872D6U, 0xC88452U, 0xC89CB9U, + 0xC8AD6CU, 0xC8B587U, 0xC8CEC5U, 0xC8D62EU, 0xC8E7FBU, 0xC8FF10U, 0xC90AF3U, 0xC91218U, 0xC923CDU, 0xC93B26U, + 0xC94064U, 0xC9588FU, 0xC9695AU, 0xC971B1U, 0xC98735U, 0xC99FDEU, 0xC9AE0BU, 0xC9B6E0U, 0xC9CDA2U, 0xC9D549U, + 0xC9E49CU, 0xC9FC77U, 0xCA0F59U, 0xCA17B2U, 0xCA2667U, 0xCA3E8CU, 0xCA45CEU, 0xCA5D25U, 0xCA6CF0U, 0xCA741BU, + 0xCA829FU, 0xCA9A74U, 0xCAABA1U, 0xCAB34AU, 0xCAC808U, 0xCAD0E3U, 0xCAE136U, 0xCAF9DDU, 0xCB0C3EU, 0xCB14D5U, + 0xCB2500U, 0xCB3DEBU, 0xCB46A9U, 0xCB5E42U, 0xCB6F97U, 0xCB777CU, 0xCB81F8U, 0xCB9913U, 0xCBA8C6U, 0xCBB02DU, + 0xCBCB6FU, 0xCBD384U, 0xCBE251U, 0xCBFABAU, 0xCC040DU, 0xCC1CE6U, 0xCC2D33U, 0xCC35D8U, 0xCC4E9AU, 0xCC5671U, + 0xCC67A4U, 0xCC7F4FU, 0xCC89CBU, 0xCC9120U, 0xCCA0F5U, 0xCCB81EU, 0xCCC35CU, 0xCCDBB7U, 0xCCEA62U, 0xCCF289U, + 0xCD076AU, 0xCD1F81U, 0xCD2E54U, 0xCD36BFU, 0xCD4DFDU, 0xCD5516U, 0xCD64C3U, 0xCD7C28U, 0xCD8AACU, 0xCD9247U, + 0xCDA392U, 0xCDBB79U, 0xCDC03BU, 0xCDD8D0U, 0xCDE905U, 0xCDF1EEU, 0xCE02C0U, 0xCE1A2BU, 0xCE2BFEU, 0xCE3315U, + 0xCE4857U, 0xCE50BCU, 0xCE6169U, 0xCE7982U, 0xCE8F06U, 0xCE97EDU, 0xCEA638U, 0xCEBED3U, 0xCEC591U, 0xCEDD7AU, + 0xCEECAFU, 0xCEF444U, 0xCF01A7U, 0xCF194CU, 0xCF2899U, 0xCF3072U, 0xCF4B30U, 0xCF53DBU, 0xCF620EU, 0xCF7AE5U, + 0xCF8C61U, 0xCF948AU, 0xCFA55FU, 0xCFBDB4U, 0xCFC6F6U, 0xCFDE1DU, 0xCFEFC8U, 0xCFF723U, 0xD00DFAU, 0xD01511U, + 0xD024C4U, 0xD03C2FU, 0xD0476DU, 0xD05F86U, 0xD06E53U, 0xD076B8U, 0xD0803CU, 0xD098D7U, 0xD0A902U, 0xD0B1E9U, + 0xD0CAABU, 0xD0D240U, 0xD0E395U, 0xD0FB7EU, 0xD10E9DU, 0xD11676U, 0xD127A3U, 0xD13F48U, 0xD1440AU, 0xD15CE1U, + 0xD16D34U, 0xD175DFU, 0xD1835BU, 0xD19BB0U, 0xD1AA65U, 0xD1B28EU, 0xD1C9CCU, 0xD1D127U, 0xD1E0F2U, 0xD1F819U, + 0xD20B37U, 0xD213DCU, 0xD22209U, 0xD23AE2U, 0xD241A0U, 0xD2594BU, 0xD2689EU, 0xD27075U, 0xD286F1U, 0xD29E1AU, + 0xD2AFCFU, 0xD2B724U, 0xD2CC66U, 0xD2D48DU, 0xD2E558U, 0xD2FDB3U, 0xD30850U, 0xD310BBU, 0xD3216EU, 0xD33985U, + 0xD342C7U, 0xD35A2CU, 0xD36BF9U, 0xD37312U, 0xD38596U, 0xD39D7DU, 0xD3ACA8U, 0xD3B443U, 0xD3CF01U, 0xD3D7EAU, + 0xD3E63FU, 0xD3FED4U, 0xD40063U, 0xD41888U, 0xD4295DU, 0xD431B6U, 0xD44AF4U, 0xD4521FU, 0xD463CAU, 0xD47B21U, + 0xD48DA5U, 0xD4954EU, 0xD4A49BU, 0xD4BC70U, 0xD4C732U, 0xD4DFD9U, 0xD4EE0CU, 0xD4F6E7U, 0xD50304U, 0xD51BEFU, + 0xD52A3AU, 0xD532D1U, 0xD54993U, 0xD55178U, 0xD560ADU, 0xD57846U, 0xD58EC2U, 0xD59629U, 0xD5A7FCU, 0xD5BF17U, + 0xD5C455U, 0xD5DCBEU, 0xD5ED6BU, 0xD5F580U, 0xD606AEU, 0xD61E45U, 0xD62F90U, 0xD6377BU, 0xD64C39U, 0xD654D2U, + 0xD66507U, 0xD67DECU, 0xD68B68U, 0xD69383U, 0xD6A256U, 0xD6BABDU, 0xD6C1FFU, 0xD6D914U, 0xD6E8C1U, 0xD6F02AU, + 0xD705C9U, 0xD71D22U, 0xD72CF7U, 0xD7341CU, 0xD74F5EU, 0xD757B5U, 0xD76660U, 0xD77E8BU, 0xD7880FU, 0xD790E4U, + 0xD7A131U, 0xD7B9DAU, 0xD7C298U, 0xD7DA73U, 0xD7EBA6U, 0xD7F34DU, 0xD80E20U, 0xD816CBU, 0xD8271EU, 0xD83FF5U, + 0xD844B7U, 0xD85C5CU, 0xD86D89U, 0xD87562U, 0xD883E6U, 0xD89B0DU, 0xD8AAD8U, 0xD8B233U, 0xD8C971U, 0xD8D19AU, + 0xD8E04FU, 0xD8F8A4U, 0xD90D47U, 0xD915ACU, 0xD92479U, 0xD93C92U, 0xD947D0U, 0xD95F3BU, 0xD96EEEU, 0xD97605U, + 0xD98081U, 0xD9986AU, 0xD9A9BFU, 0xD9B154U, 0xD9CA16U, 0xD9D2FDU, 0xD9E328U, 0xD9FBC3U, 0xDA08EDU, 0xDA1006U, + 0xDA21D3U, 0xDA3938U, 0xDA427AU, 0xDA5A91U, 0xDA6B44U, 0xDA73AFU, 0xDA852BU, 0xDA9DC0U, 0xDAAC15U, 0xDAB4FEU, + 0xDACFBCU, 0xDAD757U, 0xDAE682U, 0xDAFE69U, 0xDB0B8AU, 0xDB1361U, 0xDB22B4U, 0xDB3A5FU, 0xDB411DU, 0xDB59F6U, + 0xDB6823U, 0xDB70C8U, 0xDB864CU, 0xDB9EA7U, 0xDBAF72U, 0xDBB799U, 0xDBCCDBU, 0xDBD430U, 0xDBE5E5U, 0xDBFD0EU, + 0xDC03B9U, 0xDC1B52U, 0xDC2A87U, 0xDC326CU, 0xDC492EU, 0xDC51C5U, 0xDC6010U, 0xDC78FBU, 0xDC8E7FU, 0xDC9694U, + 0xDCA741U, 0xDCBFAAU, 0xDCC4E8U, 0xDCDC03U, 0xDCEDD6U, 0xDCF53DU, 0xDD00DEU, 0xDD1835U, 0xDD29E0U, 0xDD310BU, + 0xDD4A49U, 0xDD52A2U, 0xDD6377U, 0xDD7B9CU, 0xDD8D18U, 0xDD95F3U, 0xDDA426U, 0xDDBCCDU, 0xDDC78FU, 0xDDDF64U, + 0xDDEEB1U, 0xDDF65AU, 0xDE0574U, 0xDE1D9FU, 0xDE2C4AU, 0xDE34A1U, 0xDE4FE3U, 0xDE5708U, 0xDE66DDU, 0xDE7E36U, + 0xDE88B2U, 0xDE9059U, 0xDEA18CU, 0xDEB967U, 0xDEC225U, 0xDEDACEU, 0xDEEB1BU, 0xDEF3F0U, 0xDF0613U, 0xDF1EF8U, + 0xDF2F2DU, 0xDF37C6U, 0xDF4C84U, 0xDF546FU, 0xDF65BAU, 0xDF7D51U, 0xDF8BD5U, 0xDF933EU, 0xDFA2EBU, 0xDFBA00U, + 0xDFC142U, 0xDFD9A9U, 0xDFE87CU, 0xDFF097U, 0xE00526U, 0xE01DCDU, 0xE02C18U, 0xE034F3U, 0xE04FB1U, 0xE0575AU, + 0xE0668FU, 0xE07E64U, 0xE088E0U, 0xE0900BU, 0xE0A1DEU, 0xE0B935U, 0xE0C277U, 0xE0DA9CU, 0xE0EB49U, 0xE0F3A2U, + 0xE10641U, 0xE11EAAU, 0xE12F7FU, 0xE13794U, 0xE14CD6U, 0xE1543DU, 0xE165E8U, 0xE17D03U, 0xE18B87U, 0xE1936CU, + 0xE1A2B9U, 0xE1BA52U, 0xE1C110U, 0xE1D9FBU, 0xE1E82EU, 0xE1F0C5U, 0xE203EBU, 0xE21B00U, 0xE22AD5U, 0xE2323EU, + 0xE2497CU, 0xE25197U, 0xE26042U, 0xE278A9U, 0xE28E2DU, 0xE296C6U, 0xE2A713U, 0xE2BFF8U, 0xE2C4BAU, 0xE2DC51U, + 0xE2ED84U, 0xE2F56FU, 0xE3008CU, 0xE31867U, 0xE329B2U, 0xE33159U, 0xE34A1BU, 0xE352F0U, 0xE36325U, 0xE37BCEU, + 0xE38D4AU, 0xE395A1U, 0xE3A474U, 0xE3BC9FU, 0xE3C7DDU, 0xE3DF36U, 0xE3EEE3U, 0xE3F608U, 0xE408BFU, 0xE41054U, + 0xE42181U, 0xE4396AU, 0xE44228U, 0xE45AC3U, 0xE46B16U, 0xE473FDU, 0xE48579U, 0xE49D92U, 0xE4AC47U, 0xE4B4ACU, + 0xE4CFEEU, 0xE4D705U, 0xE4E6D0U, 0xE4FE3BU, 0xE50BD8U, 0xE51333U, 0xE522E6U, 0xE53A0DU, 0xE5414FU, 0xE559A4U, + 0xE56871U, 0xE5709AU, 0xE5861EU, 0xE59EF5U, 0xE5AF20U, 0xE5B7CBU, 0xE5CC89U, 0xE5D462U, 0xE5E5B7U, 0xE5FD5CU, + 0xE60E72U, 0xE61699U, 0xE6274CU, 0xE63FA7U, 0xE644E5U, 0xE65C0EU, 0xE66DDBU, 0xE67530U, 0xE683B4U, 0xE69B5FU, + 0xE6AA8AU, 0xE6B261U, 0xE6C923U, 0xE6D1C8U, 0xE6E01DU, 0xE6F8F6U, 0xE70D15U, 0xE715FEU, 0xE7242BU, 0xE73CC0U, + 0xE74782U, 0xE75F69U, 0xE76EBCU, 0xE77657U, 0xE780D3U, 0xE79838U, 0xE7A9EDU, 0xE7B106U, 0xE7CA44U, 0xE7D2AFU, + 0xE7E37AU, 0xE7FB91U, 0xE806FCU, 0xE81E17U, 0xE82FC2U, 0xE83729U, 0xE84C6BU, 0xE85480U, 0xE86555U, 0xE87DBEU, + 0xE88B3AU, 0xE893D1U, 0xE8A204U, 0xE8BAEFU, 0xE8C1ADU, 0xE8D946U, 0xE8E893U, 0xE8F078U, 0xE9059BU, 0xE91D70U, + 0xE92CA5U, 0xE9344EU, 0xE94F0CU, 0xE957E7U, 0xE96632U, 0xE97ED9U, 0xE9885DU, 0xE990B6U, 0xE9A163U, 0xE9B988U, + 0xE9C2CAU, 0xE9DA21U, 0xE9EBF4U, 0xE9F31FU, 0xEA0031U, 0xEA18DAU, 0xEA290FU, 0xEA31E4U, 0xEA4AA6U, 0xEA524DU, + 0xEA6398U, 0xEA7B73U, 0xEA8DF7U, 0xEA951CU, 0xEAA4C9U, 0xEABC22U, 0xEAC760U, 0xEADF8BU, 0xEAEE5EU, 0xEAF6B5U, + 0xEB0356U, 0xEB1BBDU, 0xEB2A68U, 0xEB3283U, 0xEB49C1U, 0xEB512AU, 0xEB60FFU, 0xEB7814U, 0xEB8E90U, 0xEB967BU, + 0xEBA7AEU, 0xEBBF45U, 0xEBC407U, 0xEBDCECU, 0xEBED39U, 0xEBF5D2U, 0xEC0B65U, 0xEC138EU, 0xEC225BU, 0xEC3AB0U, + 0xEC41F2U, 0xEC5919U, 0xEC68CCU, 0xEC7027U, 0xEC86A3U, 0xEC9E48U, 0xECAF9DU, 0xECB776U, 0xECCC34U, 0xECD4DFU, + 0xECE50AU, 0xECFDE1U, 0xED0802U, 0xED10E9U, 0xED213CU, 0xED39D7U, 0xED4295U, 0xED5A7EU, 0xED6BABU, 0xED7340U, + 0xED85C4U, 0xED9D2FU, 0xEDACFAU, 0xEDB411U, 0xEDCF53U, 0xEDD7B8U, 0xEDE66DU, 0xEDFE86U, 0xEE0DA8U, 0xEE1543U, + 0xEE2496U, 0xEE3C7DU, 0xEE473FU, 0xEE5FD4U, 0xEE6E01U, 0xEE76EAU, 0xEE806EU, 0xEE9885U, 0xEEA950U, 0xEEB1BBU, + 0xEECAF9U, 0xEED212U, 0xEEE3C7U, 0xEEFB2CU, 0xEF0ECFU, 0xEF1624U, 0xEF27F1U, 0xEF3F1AU, 0xEF4458U, 0xEF5CB3U, + 0xEF6D66U, 0xEF758DU, 0xEF8309U, 0xEF9BE2U, 0xEFAA37U, 0xEFB2DCU, 0xEFC99EU, 0xEFD175U, 0xEFE0A0U, 0xEFF84BU, + 0xF00292U, 0xF01A79U, 0xF02BACU, 0xF03347U, 0xF04805U, 0xF050EEU, 0xF0613BU, 0xF079D0U, 0xF08F54U, 0xF097BFU, + 0xF0A66AU, 0xF0BE81U, 0xF0C5C3U, 0xF0DD28U, 0xF0ECFDU, 0xF0F416U, 0xF101F5U, 0xF1191EU, 0xF128CBU, 0xF13020U, + 0xF14B62U, 0xF15389U, 0xF1625CU, 0xF17AB7U, 0xF18C33U, 0xF194D8U, 0xF1A50DU, 0xF1BDE6U, 0xF1C6A4U, 0xF1DE4FU, + 0xF1EF9AU, 0xF1F771U, 0xF2045FU, 0xF21CB4U, 0xF22D61U, 0xF2358AU, 0xF24EC8U, 0xF25623U, 0xF267F6U, 0xF27F1DU, + 0xF28999U, 0xF29172U, 0xF2A0A7U, 0xF2B84CU, 0xF2C30EU, 0xF2DBE5U, 0xF2EA30U, 0xF2F2DBU, 0xF30738U, 0xF31FD3U, + 0xF32E06U, 0xF336EDU, 0xF34DAFU, 0xF35544U, 0xF36491U, 0xF37C7AU, 0xF38AFEU, 0xF39215U, 0xF3A3C0U, 0xF3BB2BU, + 0xF3C069U, 0xF3D882U, 0xF3E957U, 0xF3F1BCU, 0xF40F0BU, 0xF417E0U, 0xF42635U, 0xF43EDEU, 0xF4459CU, 0xF45D77U, + 0xF46CA2U, 0xF47449U, 0xF482CDU, 0xF49A26U, 0xF4ABF3U, 0xF4B318U, 0xF4C85AU, 0xF4D0B1U, 0xF4E164U, 0xF4F98FU, + 0xF50C6CU, 0xF51487U, 0xF52552U, 0xF53DB9U, 0xF546FBU, 0xF55E10U, 0xF56FC5U, 0xF5772EU, 0xF581AAU, 0xF59941U, + 0xF5A894U, 0xF5B07FU, 0xF5CB3DU, 0xF5D3D6U, 0xF5E203U, 0xF5FAE8U, 0xF609C6U, 0xF6112DU, 0xF620F8U, 0xF63813U, + 0xF64351U, 0xF65BBAU, 0xF66A6FU, 0xF67284U, 0xF68400U, 0xF69CEBU, 0xF6AD3EU, 0xF6B5D5U, 0xF6CE97U, 0xF6D67CU, + 0xF6E7A9U, 0xF6FF42U, 0xF70AA1U, 0xF7124AU, 0xF7239FU, 0xF73B74U, 0xF74036U, 0xF758DDU, 0xF76908U, 0xF771E3U, + 0xF78767U, 0xF79F8CU, 0xF7AE59U, 0xF7B6B2U, 0xF7CDF0U, 0xF7D51BU, 0xF7E4CEU, 0xF7FC25U, 0xF80148U, 0xF819A3U, + 0xF82876U, 0xF8309DU, 0xF84BDFU, 0xF85334U, 0xF862E1U, 0xF87A0AU, 0xF88C8EU, 0xF89465U, 0xF8A5B0U, 0xF8BD5BU, + 0xF8C619U, 0xF8DEF2U, 0xF8EF27U, 0xF8F7CCU, 0xF9022FU, 0xF91AC4U, 0xF92B11U, 0xF933FAU, 0xF948B8U, 0xF95053U, + 0xF96186U, 0xF9796DU, 0xF98FE9U, 0xF99702U, 0xF9A6D7U, 0xF9BE3CU, 0xF9C57EU, 0xF9DD95U, 0xF9EC40U, 0xF9F4ABU, + 0xFA0785U, 0xFA1F6EU, 0xFA2EBBU, 0xFA3650U, 0xFA4D12U, 0xFA55F9U, 0xFA642CU, 0xFA7CC7U, 0xFA8A43U, 0xFA92A8U, + 0xFAA37DU, 0xFABB96U, 0xFAC0D4U, 0xFAD83FU, 0xFAE9EAU, 0xFAF101U, 0xFB04E2U, 0xFB1C09U, 0xFB2DDCU, 0xFB3537U, + 0xFB4E75U, 0xFB569EU, 0xFB674BU, 0xFB7FA0U, 0xFB8924U, 0xFB91CFU, 0xFBA01AU, 0xFBB8F1U, 0xFBC3B3U, 0xFBDB58U, + 0xFBEA8DU, 0xFBF266U, 0xFC0CD1U, 0xFC143AU, 0xFC25EFU, 0xFC3D04U, 0xFC4646U, 0xFC5EADU, 0xFC6F78U, 0xFC7793U, + 0xFC8117U, 0xFC99FCU, 0xFCA829U, 0xFCB0C2U, 0xFCCB80U, 0xFCD36BU, 0xFCE2BEU, 0xFCFA55U, 0xFD0FB6U, 0xFD175DU, + 0xFD2688U, 0xFD3E63U, 0xFD4521U, 0xFD5DCAU, 0xFD6C1FU, 0xFD74F4U, 0xFD8270U, 0xFD9A9BU, 0xFDAB4EU, 0xFDB3A5U, + 0xFDC8E7U, 0xFDD00CU, 0xFDE1D9U, 0xFDF932U, 0xFE0A1CU, 0xFE12F7U, 0xFE2322U, 0xFE3BC9U, 0xFE408BU, 0xFE5860U, + 0xFE69B5U, 0xFE715EU, 0xFE87DAU, 0xFE9F31U, 0xFEAEE4U, 0xFEB60FU, 0xFECD4DU, 0xFED5A6U, 0xFEE473U, 0xFEFC98U, + 0xFF097BU, 0xFF1190U, 0xFF2045U, 0xFF38AEU, 0xFF43ECU, 0xFF5B07U, 0xFF6AD2U, 0xFF7239U, 0xFF84BDU, 0xFF9C56U, + 0xFFAD83U, 0xFFB568U, 0xFFCE2AU, 0xFFD6C1U, 0xFFE714U, 0xFFFFFFU}; + +static const unsigned int DECODING_TABLE_23127[] = { + 0x000000U, 0x000001U, 0x000002U, 0x000003U, 0x000004U, 0x000005U, 0x000006U, 0x000007U, 0x000008U, 0x000009U, + 0x00000AU, 0x00000BU, 0x00000CU, 0x00000DU, 0x00000EU, 0x024020U, 0x000010U, 0x000011U, 0x000012U, 0x000013U, + 0x000014U, 0x000015U, 0x000016U, 0x412000U, 0x000018U, 0x000019U, 0x00001AU, 0x180800U, 0x00001CU, 0x200300U, + 0x048040U, 0x001480U, 0x000020U, 0x000021U, 0x000022U, 0x000023U, 0x000024U, 0x000025U, 0x000026U, 0x024008U, + 0x000028U, 0x000029U, 0x00002AU, 0x024004U, 0x00002CU, 0x024002U, 0x024001U, 0x024000U, 0x000030U, 0x000031U, + 0x000032U, 0x008180U, 0x000034U, 0x000C40U, 0x301000U, 0x0C0200U, 0x000038U, 0x043000U, 0x400600U, 0x210040U, + 0x090080U, 0x508000U, 0x002900U, 0x024010U, 0x000040U, 0x000041U, 0x000042U, 0x000043U, 0x000044U, 0x000045U, + 0x000046U, 0x280080U, 0x000048U, 0x000049U, 0x00004AU, 0x002500U, 0x00004CU, 0x111000U, 0x048010U, 0x400A00U, + 0x000050U, 0x000051U, 0x000052U, 0x021200U, 0x000054U, 0x000C20U, 0x048008U, 0x104100U, 0x000058U, 0x404080U, + 0x048004U, 0x210020U, 0x048002U, 0x0A2000U, 0x048000U, 0x048001U, 0x000060U, 0x000061U, 0x000062U, 0x540000U, + 0x000064U, 0x000C10U, 0x010300U, 0x00B000U, 0x000068U, 0x088200U, 0x001880U, 0x210010U, 0x602000U, 0x040180U, + 0x180400U, 0x024040U, 0x000070U, 0x000C04U, 0x086000U, 0x210008U, 0x000C01U, 0x000C00U, 0x420080U, 0x000C02U, + 0x120100U, 0x210002U, 0x210001U, 0x210000U, 0x005200U, 0x000C08U, 0x048020U, 0x210004U, 0x000080U, 0x000081U, + 0x000082U, 0x000083U, 0x000084U, 0x000085U, 0x000086U, 0x280040U, 0x000088U, 0x000089U, 0x00008AU, 0x050200U, + 0x00008CU, 0x00A800U, 0x500100U, 0x001410U, 0x000090U, 0x000091U, 0x000092U, 0x008120U, 0x000094U, 0x160000U, + 0x004A00U, 0x001408U, 0x000098U, 0x404040U, 0x222000U, 0x001404U, 0x090020U, 0x001402U, 0x001401U, 0x001400U, + 0x0000A0U, 0x0000A1U, 0x0000A2U, 0x008110U, 0x0000A4U, 0x401200U, 0x042400U, 0x110800U, 0x0000A8U, 0x300400U, + 0x001840U, 0x482000U, 0x090010U, 0x040140U, 0x208200U, 0x024080U, 0x0000B0U, 0x008102U, 0x008101U, 0x008100U, + 0x090008U, 0x206000U, 0x420040U, 0x008104U, 0x090004U, 0x020A00U, 0x144000U, 0x008108U, 0x090000U, 0x090001U, + 0x090002U, 0x001420U, 0x0000C0U, 0x0000C1U, 0x0000C2U, 0x280004U, 0x0000C4U, 0x280002U, 0x280001U, 0x280000U, + 0x0000C8U, 0x404010U, 0x001820U, 0x128000U, 0x020600U, 0x040120U, 0x016000U, 0x280008U, 0x0000D0U, 0x404008U, + 0x110400U, 0x042800U, 0x003100U, 0x018200U, 0x420020U, 0x280010U, 0x404001U, 0x404000U, 0x080300U, 0x404002U, + 0x300800U, 0x404004U, 0x048080U, 0x001440U, 0x0000E0U, 0x032000U, 0x001808U, 0x004600U, 0x10C000U, 0x040108U, + 0x420010U, 0x280020U, 0x001802U, 0x040104U, 0x001800U, 0x001801U, 0x040101U, 0x040100U, 0x001804U, 0x040102U, + 0x240200U, 0x181000U, 0x420004U, 0x008140U, 0x420002U, 0x000C80U, 0x420000U, 0x420001U, 0x00A400U, 0x404020U, + 0x001810U, 0x210080U, 0x090040U, 0x040110U, 0x420008U, 0x102200U, 0x000100U, 0x000101U, 0x000102U, 0x000103U, + 0x000104U, 0x000105U, 0x000106U, 0x041800U, 0x000108U, 0x000109U, 0x00010AU, 0x002440U, 0x00010CU, 0x200210U, + 0x500080U, 0x098000U, 0x000110U, 0x000111U, 0x000112U, 0x0080A0U, 0x000114U, 0x200208U, 0x0A0400U, 0x104040U, + 0x000118U, 0x200204U, 0x015000U, 0x460000U, 0x200201U, 0x200200U, 0x002820U, 0x200202U, 0x000120U, 0x000121U, + 0x000122U, 0x008090U, 0x000124U, 0x182000U, 0x010240U, 0x600400U, 0x000128U, 0x410800U, 0x2C0000U, 0x101200U, + 0x009400U, 0x0400C0U, 0x002810U, 0x024100U, 0x000130U, 0x008082U, 0x008081U, 0x008080U, 0x444000U, 0x031000U, + 0x002808U, 0x008084U, 0x120040U, 0x084400U, 0x002804U, 0x008088U, 0x002802U, 0x200220U, 0x002800U, 0x002801U, + 0x000140U, 0x000141U, 0x000142U, 0x002408U, 0x000144U, 0x428000U, 0x010220U, 0x104010U, 0x000148U, 0x002402U, + 0x002401U, 0x002400U, 0x084800U, 0x0400A0U, 0x221000U, 0x002404U, 0x000150U, 0x0D0000U, 0x600800U, 0x104004U, + 0x003080U, 0x104002U, 0x104001U, 0x104000U, 0x120020U, 0x009800U, 0x080280U, 0x002410U, 0x410400U, 0x200240U, + 0x048100U, 0x104008U, 0x000160U, 0x205000U, 0x010204U, 0x0A0800U, 0x010202U, 0x040088U, 0x010200U, 0x010201U, + 0x120010U, 0x040084U, 0x40C000U, 0x002420U, 0x040081U, 0x040080U, 0x010208U, 0x040082U, 0x120008U, 0x402200U, + 0x041400U, 0x0080C0U, 0x288000U, 0x000D00U, 0x010210U, 0x104020U, 0x120000U, 0x120001U, 0x120002U, 0x210100U, + 0x120004U, 0x040090U, 0x002840U, 0x481000U, 0x000180U, 0x000181U, 0x000182U, 0x008030U, 0x000184U, 0x014400U, + 0x500008U, 0x022200U, 0x000188U, 0x0A1000U, 0x500004U, 0x204800U, 0x500002U, 0x040060U, 0x500000U, 0x500001U, + 0x000190U, 0x008022U, 0x008021U, 0x008020U, 0x003040U, 0x480800U, 0x250000U, 0x008024U, 0x040C00U, 0x112000U, + 0x080240U, 0x008028U, 0x02C000U, 0x200280U, 0x500010U, 0x001500U, 0x0001A0U, 0x008012U, 0x008011U, 0x008010U, + 0x220800U, 0x040048U, 0x085000U, 0x008014U, 0x006200U, 0x040044U, 0x030400U, 0x008018U, 0x040041U, 0x040040U, + 0x500020U, 0x040042U, 0x008003U, 0x008002U, 0x008001U, 0x008000U, 0x100600U, 0x008006U, 0x008005U, 0x008004U, + 0x601000U, 0x00800AU, 0x008009U, 0x008008U, 0x090100U, 0x040050U, 0x002880U, 0x00800CU, 0x0001C0U, 0x100A00U, + 0x064000U, 0x411000U, 0x003010U, 0x040028U, 0x008C00U, 0x280100U, 0x218000U, 0x040024U, 0x080210U, 0x002480U, + 0x040021U, 0x040020U, 0x500040U, 0x040022U, 0x003004U, 0x220400U, 0x080208U, 0x008060U, 0x003000U, 0x003001U, + 0x003002U, 0x104080U, 0x080202U, 0x404100U, 0x080200U, 0x080201U, 0x003008U, 0x040030U, 0x080204U, 0x030800U, + 0x480400U, 0x04000CU, 0x302000U, 0x008050U, 0x040009U, 0x040008U, 0x010280U, 0x04000AU, 0x040005U, 0x040004U, + 0x001900U, 0x040006U, 0x040001U, 0x040000U, 0x040003U, 0x040002U, 0x014800U, 0x008042U, 0x008041U, 0x008040U, + 0x003020U, 0x040018U, 0x420100U, 0x008044U, 0x120080U, 0x040014U, 0x080220U, 0x008048U, 0x040011U, 0x040010U, + 0x204400U, 0x040012U, 0x000200U, 0x000201U, 0x000202U, 0x000203U, 0x000204U, 0x000205U, 0x000206U, 0x108400U, + 0x000208U, 0x000209U, 0x00020AU, 0x050080U, 0x00020CU, 0x200110U, 0x083000U, 0x400840U, 0x000210U, 0x000211U, + 0x000212U, 0x021040U, 0x000214U, 0x200108U, 0x004880U, 0x0C0020U, 0x000218U, 0x200104U, 0x400420U, 0x00E000U, + 0x200101U, 0x200100U, 0x130000U, 0x200102U, 0x000220U, 0x000221U, 0x000222U, 0x202800U, 0x000224U, 0x401080U, + 0x010140U, 0x0C0010U, 0x000228U, 0x088040U, 0x400410U, 0x101100U, 0x140800U, 0x012400U, 0x208080U, 0x024200U, + 0x000230U, 0x114000U, 0x400408U, 0x0C0004U, 0x02A000U, 0x0C0002U, 0x0C0001U, 0x0C0000U, 0x400402U, 0x020880U, + 0x400400U, 0x400401U, 0x005040U, 0x200120U, 0x400404U, 0x0C0008U, 0x000240U, 0x000241U, 0x000242U, 0x021010U, + 0x000244U, 0x046000U, 0x010120U, 0x400808U, 0x000248U, 0x088020U, 0x304000U, 0x400804U, 0x020480U, 0x400802U, + 0x400801U, 0x400800U, 0x000250U, 0x021002U, 0x021001U, 0x021000U, 0x580000U, 0x018080U, 0x202400U, 0x021004U, + 0x012800U, 0x140400U, 0x080180U, 0x021008U, 0x005020U, 0x200140U, 0x048200U, 0x400810U, 0x000260U, 0x088008U, + 0x010104U, 0x004480U, 0x010102U, 0x320000U, 0x010100U, 0x010101U, 0x088001U, 0x088000U, 0x062000U, 0x088002U, + 0x005010U, 0x088004U, 0x010108U, 0x400820U, 0x240080U, 0x402100U, 0x108800U, 0x021020U, 0x005008U, 0x000E00U, + 0x010110U, 0x0C0040U, 0x005004U, 0x088010U, 0x400440U, 0x210200U, 0x005000U, 0x005001U, 0x005002U, 0x102080U, + 0x000280U, 0x000281U, 0x000282U, 0x050008U, 0x000284U, 0x401020U, 0x004810U, 0x022100U, 0x000288U, 0x050002U, + 0x050001U, 0x050000U, 0x020440U, 0x184000U, 0x208020U, 0x050004U, 0x000290U, 0x082400U, 0x004804U, 0x700000U, + 0x004802U, 0x018040U, 0x004800U, 0x004801U, 0x109000U, 0x020820U, 0x080140U, 0x050010U, 0x442000U, 0x200180U, + 0x004808U, 0x001600U, 0x0002A0U, 0x401004U, 0x1A0000U, 0x004440U, 0x401001U, 0x401000U, 0x208008U, 0x401002U, + 0x006100U, 0x020810U, 0x208004U, 0x050020U, 0x208002U, 0x401008U, 0x208000U, 0x208001U, 0x240040U, 0x020808U, + 0x013000U, 0x008300U, 0x100500U, 0x401010U, 0x004820U, 0x0C0080U, 0x020801U, 0x020800U, 0x400480U, 0x020802U, + 0x090200U, 0x020804U, 0x208010U, 0x102040U, 0x0002C0U, 0x100900U, 0x40A000U, 0x004420U, 0x020408U, 0x018010U, + 0x141000U, 0x280200U, 0x020404U, 0x203000U, 0x080110U, 0x050040U, 0x020400U, 0x020401U, 0x020402U, 0x400880U, + 0x240020U, 0x018004U, 0x080108U, 0x021080U, 0x018001U, 0x018000U, 0x004840U, 0x018002U, 0x080102U, 0x404200U, + 0x080100U, 0x080101U, 0x020410U, 0x018008U, 0x080104U, 0x102020U, 0x240010U, 0x004402U, 0x004401U, 0x004400U, + 0x082800U, 0x401040U, 0x010180U, 0x004404U, 0x510000U, 0x088080U, 0x001A00U, 0x004408U, 0x020420U, 0x040300U, + 0x208040U, 0x102010U, 0x240000U, 0x240001U, 0x240002U, 0x004410U, 0x240004U, 0x018020U, 0x420200U, 0x102008U, + 0x240008U, 0x020840U, 0x080120U, 0x102004U, 0x005080U, 0x102002U, 0x102001U, 0x102000U, 0x000300U, 0x000301U, + 0x000302U, 0x484000U, 0x000304U, 0x200018U, 0x010060U, 0x022080U, 0x000308U, 0x200014U, 0x028800U, 0x101020U, + 0x200011U, 0x200010U, 0x044400U, 0x200012U, 0x000310U, 0x20000CU, 0x142000U, 0x010C00U, 0x200009U, 0x200008U, + 0x409000U, 0x20000AU, 0x200005U, 0x200004U, 0x0800C0U, 0x200006U, 0x200001U, 0x200000U, 0x200003U, 0x200002U, + 0x000320U, 0x060400U, 0x010044U, 0x101008U, 0x010042U, 0x00C800U, 0x010040U, 0x010041U, 0x006080U, 0x101002U, + 0x101001U, 0x101000U, 0x4A0000U, 0x200030U, 0x010048U, 0x101004U, 0x081800U, 0x402040U, 0x224000U, 0x008280U, + 0x100480U, 0x200028U, 0x010050U, 0x0C0100U, 0x058000U, 0x200024U, 0x400500U, 0x101010U, 0x200021U, 0x200020U, + 0x002A00U, 0x200022U, 0x000340U, 0x100880U, 0x010024U, 0x248000U, 0x010022U, 0x081400U, 0x010020U, 0x010021U, + 0x441000U, 0x034000U, 0x080090U, 0x002600U, 0x10A000U, 0x200050U, 0x010028U, 0x400900U, 0x00C400U, 0x402020U, + 0x080088U, 0x021100U, 0x060800U, 0x200048U, 0x010030U, 0x104200U, 0x080082U, 0x200044U, 0x080080U, 0x080081U, + 0x200041U, 0x200040U, 0x080084U, 0x200042U, 0x010006U, 0x402010U, 0x010004U, 0x010005U, 0x010002U, 0x010003U, + 0x010000U, 0x010001U, 0x200C00U, 0x088100U, 0x01000CU, 0x101040U, 0x01000AU, 0x040280U, 0x010008U, 0x010009U, + 0x402001U, 0x402000U, 0x010014U, 0x402002U, 0x010012U, 0x402004U, 0x010010U, 0x010011U, 0x120200U, 0x402008U, + 0x0800A0U, 0x044800U, 0x005100U, 0x200060U, 0x010018U, 0x028400U, 0x000380U, 0x100840U, 0x201400U, 0x022004U, + 0x0C8000U, 0x022002U, 0x022001U, 0x022000U, 0x006020U, 0x408400U, 0x080050U, 0x050100U, 0x011800U, 0x200090U, + 0x500200U, 0x022008U, 0x430000U, 0x045000U, 0x080048U, 0x008220U, 0x100420U, 0x200088U, 0x004900U, 0x022010U, + 0x080042U, 0x200084U, 0x080040U, 0x080041U, 0x200081U, 0x200080U, 0x080044U, 0x200082U, 0x006008U, 0x290000U, + 0x440800U, 0x008210U, 0x100410U, 0x401100U, 0x0100C0U, 0x022020U, 0x006000U, 0x006001U, 0x006002U, 0x101080U, + 0x006004U, 0x040240U, 0x208100U, 0x080C00U, 0x100404U, 0x008202U, 0x008201U, 0x008200U, 0x100400U, 0x100401U, + 0x100402U, 0x008204U, 0x006010U, 0x020900U, 0x080060U, 0x008208U, 0x100408U, 0x2000A0U, 0x061000U, 0x414000U, + 0x100801U, 0x100800U, 0x080018U, 0x100802U, 0x604000U, 0x100804U, 0x0100A0U, 0x022040U, 0x080012U, 0x100808U, + 0x080010U, 0x080011U, 0x020500U, 0x040220U, 0x080014U, 0x00D000U, 0x08000AU, 0x100810U, 0x080008U, 0x080009U, + 0x003200U, 0x018100U, 0x08000CU, 0x440400U, 0x080002U, 0x080003U, 0x080000U, 0x080001U, 0x080006U, 0x2000C0U, + 0x080004U, 0x080005U, 0x029000U, 0x100820U, 0x010084U, 0x004500U, 0x010082U, 0x040208U, 0x010080U, 0x010081U, + 0x006040U, 0x040204U, 0x080030U, 0x620000U, 0x040201U, 0x040200U, 0x010088U, 0x040202U, 0x240100U, 0x402080U, + 0x080028U, 0x008240U, 0x100440U, 0x0A4000U, 0x010090U, 0x201800U, 0x080022U, 0x011400U, 0x080020U, 0x080021U, + 0x408800U, 0x040210U, 0x080024U, 0x102100U, 0x000400U, 0x000401U, 0x000402U, 0x000403U, 0x000404U, 0x000405U, + 0x000406U, 0x108200U, 0x000408U, 0x000409U, 0x00040AU, 0x002140U, 0x00040CU, 0x4C0000U, 0x210800U, 0x001090U, + 0x000410U, 0x000411U, 0x000412U, 0x244000U, 0x000414U, 0x000860U, 0x0A0100U, 0x001088U, 0x000418U, 0x038000U, + 0x400220U, 0x001084U, 0x106000U, 0x001082U, 0x001081U, 0x001080U, 0x000420U, 0x000421U, 0x000422U, 0x091000U, + 0x000424U, 0x000850U, 0x042080U, 0x600100U, 0x000428U, 0x300080U, 0x400210U, 0x048800U, 0x009100U, 0x012200U, + 0x180040U, 0x024400U, 0x000430U, 0x000844U, 0x400208U, 0x122000U, 0x000841U, 0x000840U, 0x01C000U, 0x000842U, + 0x400202U, 0x084100U, 0x400200U, 0x400201U, 0x260000U, 0x000848U, 0x400204U, 0x0010A0U, 0x000440U, 0x000441U, + 0x000442U, 0x002108U, 0x000444U, 0x000830U, 0x405000U, 0x070000U, 0x000448U, 0x002102U, 0x002101U, 0x002100U, + 0x020280U, 0x20C000U, 0x180020U, 0x002104U, 0x000450U, 0x000824U, 0x110080U, 0x488000U, 0x000821U, 0x000820U, + 0x202200U, 0x000822U, 0x281000U, 0x140200U, 0x024800U, 0x002110U, 0x410100U, 0x000828U, 0x048400U, 0x0010C0U, + 0x000460U, 0x000814U, 0x228000U, 0x004280U, 0x000811U, 0x000810U, 0x180008U, 0x000812U, 0x054000U, 0x421000U, + 0x180004U, 0x002120U, 0x180002U, 0x000818U, 0x180000U, 0x180001U, 0x000805U, 0x000804U, 0x041100U, 0x000806U, + 0x000801U, 0x000800U, 0x000803U, 0x000802U, 0x00A080U, 0x00080CU, 0x400240U, 0x210400U, 0x000809U, 0x000808U, + 0x180010U, 0x00080AU, 0x000480U, 0x000481U, 0x000482U, 0x420800U, 0x000484U, 0x014100U, 0x042020U, 0x001018U, + 0x000488U, 0x300020U, 0x08C000U, 0x001014U, 0x020240U, 0x001012U, 0x001011U, 0x001010U, 0x000490U, 0x082200U, + 0x110040U, 0x00100CU, 0x608000U, 0x00100AU, 0x001009U, 0x001008U, 0x040900U, 0x001006U, 0x001005U, 0x001004U, + 0x001003U, 0x001002U, 0x001001U, 0x001000U, 0x0004A0U, 0x300008U, 0x042004U, 0x004240U, 0x042002U, 0x0A8000U, + 0x042000U, 0x042001U, 0x300001U, 0x300000U, 0x030100U, 0x300002U, 0x404800U, 0x300004U, 0x042008U, 0x001030U, + 0x025000U, 0x450000U, 0x280800U, 0x008500U, 0x100300U, 0x0008C0U, 0x042010U, 0x001028U, 0x00A040U, 0x300010U, + 0x400280U, 0x001024U, 0x090400U, 0x001022U, 0x001021U, 0x001020U, 0x0004C0U, 0x049000U, 0x110010U, 0x004220U, + 0x020208U, 0x502000U, 0x008900U, 0x280400U, 0x020204U, 0x090800U, 0x640000U, 0x002180U, 0x020200U, 0x020201U, + 0x020202U, 0x001050U, 0x110002U, 0x220100U, 0x110000U, 0x110001U, 0x0C4000U, 0x0008A0U, 0x110004U, 0x001048U, + 0x00A020U, 0x404400U, 0x110008U, 0x001044U, 0x020210U, 0x001042U, 0x001041U, 0x001040U, 0x480100U, 0x004202U, + 0x004201U, 0x004200U, 0x211000U, 0x000890U, 0x042040U, 0x004204U, 0x00A010U, 0x300040U, 0x001C00U, 0x004208U, + 0x020220U, 0x040500U, 0x180080U, 0x418000U, 0x00A008U, 0x000884U, 0x110020U, 0x004210U, 0x000881U, 0x000880U, + 0x420400U, 0x000882U, 0x00A000U, 0x00A001U, 0x00A002U, 0x0E0000U, 0x00A004U, 0x000888U, 0x204100U, 0x001060U, + 0x000500U, 0x000501U, 0x000502U, 0x002048U, 0x000504U, 0x014080U, 0x0A0010U, 0x600020U, 0x000508U, 0x002042U, + 0x002041U, 0x002040U, 0x009020U, 0x120800U, 0x044200U, 0x002044U, 0x000510U, 0x501000U, 0x0A0004U, 0x010A00U, + 0x0A0002U, 0x04A000U, 0x0A0000U, 0x0A0001U, 0x040880U, 0x084020U, 0x308000U, 0x002050U, 0x410040U, 0x200600U, + 0x0A0008U, 0x001180U, 0x000520U, 0x060200U, 0x104800U, 0x600004U, 0x009008U, 0x600002U, 0x600001U, 0x600000U, + 0x009004U, 0x084010U, 0x030080U, 0x002060U, 0x009000U, 0x009001U, 0x009002U, 0x600008U, 0x212000U, 0x084008U, + 0x041040U, 0x008480U, 0x100280U, 0x000940U, 0x0A0020U, 0x600010U, 0x084001U, 0x084000U, 0x400300U, 0x084002U, + 0x009010U, 0x084004U, 0x002C00U, 0x150000U, 0x000540U, 0x00200AU, 0x002009U, 0x002008U, 0x340000U, 0x081200U, + 0x008880U, 0x00200CU, 0x002003U, 0x002002U, 0x002001U, 0x002000U, 0x410010U, 0x002006U, 0x002005U, 0x002004U, + 0x00C200U, 0x220080U, 0x041020U, 0x002018U, 0x410008U, 0x000920U, 0x0A0040U, 0x104400U, 0x410004U, 0x002012U, + 0x002011U, 0x002010U, 0x410000U, 0x410001U, 0x410002U, 0x002014U, 0x480080U, 0x118000U, 0x041010U, 0x002028U, + 0x026000U, 0x000910U, 0x010600U, 0x600040U, 0x200A00U, 0x002022U, 0x002021U, 0x002020U, 0x009040U, 0x040480U, + 0x180100U, 0x002024U, 0x041002U, 0x000904U, 0x041000U, 0x041001U, 0x000901U, 0x000900U, 0x041004U, 0x000902U, + 0x120400U, 0x084040U, 0x041008U, 0x002030U, 0x410020U, 0x000908U, 0x204080U, 0x028200U, 0x000580U, 0x014004U, + 0x201200U, 0x1C0000U, 0x014001U, 0x014000U, 0x008840U, 0x014002U, 0x040810U, 0x408200U, 0x030020U, 0x0020C0U, + 0x282000U, 0x014008U, 0x500400U, 0x001110U, 0x040808U, 0x220040U, 0x406000U, 0x008420U, 0x100220U, 0x014010U, + 0x0A0080U, 0x001108U, 0x040800U, 0x040801U, 0x040802U, 0x001104U, 0x040804U, 0x001102U, 0x001101U, 0x001100U, + 0x480040U, 0x003800U, 0x030008U, 0x008410U, 0x100210U, 0x014020U, 0x042100U, 0x600080U, 0x030002U, 0x300100U, + 0x030000U, 0x030001U, 0x009080U, 0x040440U, 0x030004U, 0x080A00U, 0x100204U, 0x008402U, 0x008401U, 0x008400U, + 0x100200U, 0x100201U, 0x100202U, 0x008404U, 0x040820U, 0x084080U, 0x030010U, 0x008408U, 0x100208U, 0x422000U, + 0x204040U, 0x001120U, 0x480020U, 0x220010U, 0x008804U, 0x002088U, 0x008802U, 0x014040U, 0x008800U, 0x008801U, + 0x105000U, 0x002082U, 0x002081U, 0x002080U, 0x020300U, 0x040420U, 0x008808U, 0x002084U, 0x220001U, 0x220000U, + 0x110100U, 0x220002U, 0x003400U, 0x220004U, 0x008810U, 0x440200U, 0x040840U, 0x220008U, 0x080600U, 0x002090U, + 0x410080U, 0x188000U, 0x204020U, 0x001140U, 0x480000U, 0x480001U, 0x480002U, 0x004300U, 0x480004U, 0x040408U, + 0x008820U, 0x121000U, 0x480008U, 0x040404U, 0x030040U, 0x0020A0U, 0x040401U, 0x040400U, 0x204010U, 0x040402U, + 0x480010U, 0x220020U, 0x041080U, 0x008440U, 0x100240U, 0x000980U, 0x204008U, 0x092000U, 0x00A100U, 0x011200U, + 0x204004U, 0x500800U, 0x204002U, 0x040410U, 0x204000U, 0x204001U, 0x000600U, 0x000601U, 0x000602U, 0x108004U, + 0x000604U, 0x108002U, 0x108001U, 0x108000U, 0x000608U, 0x005800U, 0x400030U, 0x2A0000U, 0x0200C0U, 0x012020U, + 0x044100U, 0x108008U, 0x000610U, 0x082080U, 0x400028U, 0x010900U, 0x051000U, 0x424000U, 0x202040U, 0x108010U, + 0x400022U, 0x140040U, 0x400020U, 0x400021U, 0x088800U, 0x200500U, 0x400024U, 0x001280U, 0x000620U, 0x060100U, + 0x400018U, 0x0040C0U, 0x284000U, 0x012008U, 0x021800U, 0x108020U, 0x400012U, 0x012004U, 0x400010U, 0x400011U, + 0x012001U, 0x012000U, 0x400014U, 0x012002U, 0x40000AU, 0x209000U, 0x400008U, 0x400009U, 0x100180U, 0x000A40U, + 0x40000CU, 0x0C0400U, 0x400002U, 0x400003U, 0x400000U, 0x400001U, 0x400006U, 0x012010U, 0x400004U, 0x400005U, + 0x000640U, 0x610000U, 0x0C0800U, 0x0040A0U, 0x020088U, 0x081100U, 0x202010U, 0x108040U, 0x020084U, 0x140010U, + 0x019000U, 0x002300U, 0x020080U, 0x020081U, 0x020082U, 0x400C00U, 0x00C100U, 0x140008U, 0x202004U, 0x021400U, + 0x202002U, 0x000A20U, 0x202000U, 0x202001U, 0x140001U, 0x140000U, 0x400060U, 0x140002U, 0x020090U, 0x140004U, + 0x202008U, 0x094000U, 0x103000U, 0x004082U, 0x004081U, 0x004080U, 0x448000U, 0x000A10U, 0x010500U, 0x004084U, + 0x200900U, 0x088400U, 0x400050U, 0x004088U, 0x0200A0U, 0x012040U, 0x180200U, 0x241000U, 0x0B0000U, 0x000A04U, + 0x400048U, 0x004090U, 0x000A01U, 0x000A00U, 0x202020U, 0x000A02U, 0x400042U, 0x140020U, 0x400040U, 0x400041U, + 0x005400U, 0x000A08U, 0x400044U, 0x028100U, 0x000680U, 0x082010U, 0x201100U, 0x004060U, 0x020048U, 0x240800U, + 0x490000U, 0x108080U, 0x020044U, 0x408100U, 0x102800U, 0x050400U, 0x020040U, 0x020041U, 0x020042U, 0x001210U, + 0x082001U, 0x082000U, 0x068000U, 0x082002U, 0x100120U, 0x082004U, 0x004C00U, 0x001208U, 0x214000U, 0x082008U, + 0x4000A0U, 0x001204U, 0x020050U, 0x001202U, 0x001201U, 0x001200U, 0x018800U, 0x004042U, 0x004041U, 0x004040U, + 0x100110U, 0x401400U, 0x042200U, 0x004044U, 0x0C1000U, 0x300200U, 0x400090U, 0x004048U, 0x020060U, 0x012080U, + 0x208400U, 0x080900U, 0x100104U, 0x082020U, 0x400088U, 0x004050U, 0x100100U, 0x100101U, 0x100102U, 0x230000U, + 0x400082U, 0x020C00U, 0x400080U, 0x400081U, 0x100108U, 0x04C000U, 0x400084U, 0x001220U, 0x02000CU, 0x004022U, + 0x004021U, 0x004020U, 0x020008U, 0x020009U, 0x02000AU, 0x004024U, 0x020004U, 0x020005U, 0x020006U, 0x004028U, + 0x020000U, 0x020001U, 0x020002U, 0x020003U, 0x401800U, 0x082040U, 0x110200U, 0x004030U, 0x020018U, 0x018400U, + 0x202080U, 0x440100U, 0x020014U, 0x140080U, 0x080500U, 0x208800U, 0x020010U, 0x020011U, 0x020012U, 0x001240U, + 0x004003U, 0x004002U, 0x004001U, 0x004000U, 0x020028U, 0x004006U, 0x004005U, 0x004004U, 0x020024U, 0x00400AU, + 0x004009U, 0x004008U, 0x020020U, 0x020021U, 0x020022U, 0x00400CU, 0x240400U, 0x004012U, 0x004011U, 0x004010U, + 0x100140U, 0x000A80U, 0x089000U, 0x004014U, 0x00A200U, 0x011100U, 0x4000C0U, 0x004018U, 0x020030U, 0x680000U, + 0x050800U, 0x102400U, 0x000700U, 0x060020U, 0x201080U, 0x010810U, 0x402800U, 0x081040U, 0x044008U, 0x108100U, + 0x190000U, 0x408080U, 0x044004U, 0x002240U, 0x044002U, 0x200410U, 0x044000U, 0x044001U, 0x00C040U, 0x010802U, + 0x010801U, 0x010800U, 0x1000A0U, 0x200408U, 0x0A0200U, 0x010804U, 0x023000U, 0x200404U, 0x400120U, 0x010808U, + 0x200401U, 0x200400U, 0x044010U, 0x200402U, 0x060001U, 0x060000U, 0x08A000U, 0x060002U, 0x100090U, 0x060004U, + 0x010440U, 0x600200U, 0x200840U, 0x060008U, 0x400110U, 0x101400U, 0x009200U, 0x012100U, 0x044020U, 0x080880U, + 0x100084U, 0x060010U, 0x400108U, 0x010820U, 0x100080U, 0x100081U, 0x100082U, 0x007000U, 0x400102U, 0x084200U, + 0x400100U, 0x400101U, 0x100088U, 0x200420U, 0x400104U, 0x028040U, 0x00C010U, 0x081004U, 0x520000U, 0x002208U, + 0x081001U, 0x081000U, 0x010420U, 0x081002U, 0x200820U, 0x002202U, 0x002201U, 0x002200U, 0x020180U, 0x081008U, + 0x044040U, 0x002204U, 0x00C000U, 0x00C001U, 0x00C002U, 0x010840U, 0x00C004U, 0x081010U, 0x202100U, 0x440080U, + 0x00C008U, 0x140100U, 0x080480U, 0x002210U, 0x410200U, 0x200440U, 0x101800U, 0x028020U, 0x200808U, 0x060040U, + 0x010404U, 0x004180U, 0x010402U, 0x081020U, 0x010400U, 0x010401U, 0x200800U, 0x200801U, 0x200802U, 0x002220U, + 0x200804U, 0x504000U, 0x010408U, 0x028010U, 0x00C020U, 0x402400U, 0x041200U, 0x380000U, 0x1000C0U, 0x000B00U, + 0x010410U, 0x028008U, 0x200810U, 0x011080U, 0x400140U, 0x028004U, 0x0C2000U, 0x028002U, 0x028001U, 0x028000U, + 0x201002U, 0x408008U, 0x201000U, 0x201001U, 0x100030U, 0x014200U, 0x201004U, 0x022400U, 0x408001U, 0x408000U, + 0x201008U, 0x408002U, 0x020140U, 0x408004U, 0x044080U, 0x080820U, 0x100024U, 0x082100U, 0x201010U, 0x010880U, + 0x100020U, 0x100021U, 0x100022U, 0x440040U, 0x040A00U, 0x408010U, 0x080440U, 0x124000U, 0x100028U, 0x200480U, + 0x01A000U, 0x001300U, 0x100014U, 0x060080U, 0x201020U, 0x004140U, 0x100010U, 0x100011U, 0x100012U, 0x080808U, + 0x006400U, 0x408020U, 0x030200U, 0x080804U, 0x100018U, 0x080802U, 0x080801U, 0x080800U, 0x100004U, 0x100005U, + 0x100006U, 0x008600U, 0x100000U, 0x100001U, 0x100002U, 0x100003U, 0x10000CU, 0x011040U, 0x400180U, 0x242000U, + 0x100008U, 0x100009U, 0x10000AU, 0x080810U, 0x052000U, 0x100C00U, 0x201040U, 0x004120U, 0x020108U, 0x081080U, + 0x008A00U, 0x440010U, 0x020104U, 0x408040U, 0x080410U, 0x002280U, 0x020100U, 0x020101U, 0x020102U, 0x310000U, + 0x00C080U, 0x220200U, 0x080408U, 0x440004U, 0x100060U, 0x440002U, 0x440001U, 0x440000U, 0x080402U, 0x011020U, + 0x080400U, 0x080401U, 0x020110U, 0x006800U, 0x080404U, 0x440008U, 0x480200U, 0x004102U, 0x004101U, 0x004100U, + 0x100050U, 0x20A000U, 0x010480U, 0x004104U, 0x200880U, 0x011010U, 0x148000U, 0x004108U, 0x020120U, 0x040600U, + 0x403000U, 0x080840U, 0x100044U, 0x011008U, 0x022800U, 0x004110U, 0x100040U, 0x100041U, 0x100042U, 0x440020U, + 0x011001U, 0x011000U, 0x080420U, 0x011002U, 0x100048U, 0x011004U, 0x204200U, 0x028080U}; + +#define X22 0x00400000 /* vector representation of X^{22} */ +#define X11 0x00000800 /* vector representation of X^{11} */ +#define MASK12 0xfffff800 /* auxiliary vector for testing */ +#define GENPOL 0x00000c75 /* generator polinomial, g(x) */ + +static unsigned int get_syndrome_23127(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 = X22; + + if (pattern >= X11) { + while (pattern & MASK12) { + while (!(aux & pattern)) + aux = aux >> 1; + + pattern ^= (aux / X11) * GENPOL; + } + } + + return pattern; +} + +unsigned int CGolay24128::encode23127(unsigned int data) +{ + return ENCODING_TABLE_23127[data]; +} + +unsigned int CGolay24128::encode24128(unsigned int data) +{ + return ENCODING_TABLE_24128[data]; +} + +unsigned int CGolay24128::decode23127(unsigned int code) +{ + unsigned int syndrome = ::get_syndrome_23127(code); + unsigned int error_pattern = DECODING_TABLE_23127[syndrome]; + + code ^= error_pattern; + + return code >> 11; +} + +unsigned int CGolay24128::decode24128(unsigned int code) +{ + return decode23127(code >> 1); +} + +unsigned int CGolay24128::decode24128(unsigned char* bytes) +{ + assert(bytes != NULL); + + unsigned int code = bytes[0U]; + code <<= 8; + code |= bytes[1U]; + code <<= 8; + code |= bytes[2U]; + + return decode23127(code >> 1); +} diff --git a/Golay24128.h b/Golay24128.h new file mode 100644 index 0000000..1ac7852 --- /dev/null +++ b/Golay24128.h @@ -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 diff --git a/HD44780.cpp b/HD44780.cpp new file mode 100644 index 0000000..055e0fc --- /dev/null +++ b/HD44780.cpp @@ -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 +#include +#include +#include + +#include +#include +#include + +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& 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(¤tTime); + + if (m_utc) { + Time = gmtime(¤tTime); + } else { + Time = localtime(¤tTime); + } + + 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() +{ +} diff --git a/HD44780.h b/HD44780.h new file mode 100644 index 0000000..63bdee6 --- /dev/null +++ b/HD44780.h @@ -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 +#include + +#include +#include + +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& 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 diff --git a/HD44780.layouts b/HD44780.layouts new file mode 100644 index 0000000..08b6224 --- /dev/null +++ b/HD44780.layouts @@ -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 | + +--------------------+ diff --git a/Hamming.cpp b/Hamming.cpp new file mode 100644 index 0000000..166e012 --- /dev/null +++ b/Hamming.cpp @@ -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 +#include + + // 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]; +} diff --git a/Hamming.h b/Hamming.h new file mode 100644 index 0000000..393e005 --- /dev/null +++ b/Hamming.h @@ -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 diff --git a/ISSUES.txt b/ISSUES.txt new file mode 100644 index 0000000..b2dab8d --- /dev/null +++ b/ISSUES.txt @@ -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. diff --git a/JitterBuffer.cpp b/JitterBuffer.cpp new file mode 100644 index 0000000..4a29f07 --- /dev/null +++ b/JitterBuffer.cpp @@ -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 +#include +#include + +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; + } + } +} diff --git a/JitterBuffer.h b/JitterBuffer.h new file mode 100644 index 0000000..37471e6 --- /dev/null +++ b/JitterBuffer.h @@ -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 diff --git a/LCDproc.cpp b/LCDproc.cpp new file mode 100644 index 0000000..1ca096f --- /dev/null +++ b/LCDproc.cpp @@ -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 +#include +#include +#include +#include +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#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(¤tTime); + + if (m_utc) + Time = gmtime(¤tTime); + else + Time = localtime(¤tTime); + + 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; +} diff --git a/LCDproc.h b/LCDproc.h new file mode 100644 index 0000000..015bf56 --- /dev/null +++ b/LCDproc.h @@ -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 + +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 diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/LICENCE @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/Log.cpp b/Log.cpp new file mode 100644 index 0000000..fc37ebf --- /dev/null +++ b/Log.cpp @@ -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 +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + +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); + } +} diff --git a/Log.h b/Log.h new file mode 100644 index 0000000..d671ef9 --- /dev/null +++ b/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 + +#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 diff --git a/MMDVM.ini b/MMDVM.ini new file mode 100644 index 0000000..c7c0abd --- /dev/null +++ b/MMDVM.ini @@ -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 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp new file mode 100644 index 0000000..4b0fbd4 --- /dev/null +++ b/MMDVMHost.cpp @@ -0,0 +1,1371 @@ +/* + * 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. + */ + +#include "MMDVMHost.h" +#include "RSSIInterpolator.h" +#include "SerialController.h" +#include "ModemSerialPort.h" +#include "Version.h" +#include "StopWatch.h" +#include "Defines.h" +#include "DStarControl.h" +#include "DMRControl.h" +#include "TFTSerial.h" +#include "NullDisplay.h" +#include "YSFControl.h" +#include "P25Control.h" +#include "Nextion.h" +#include "LCDproc.h" +#include "Thread.h" +#include "Log.h" +#include "GitVersion.h" + +#if defined(HD44780) +#include "HD44780.h" +#endif + +#if defined(OLED) +#include "OLED.h" +#endif + +#include +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#endif + +#if defined(_WIN32) || defined(_WIN64) +const char* DEFAULT_INI_FILE = "MMDVM.ini"; +#else +const char* DEFAULT_INI_FILE = "/etc/MMDVM.ini"; +#endif + +static bool m_killed = false; +static int m_signal = 0; + +#if !defined(_WIN32) && !defined(_WIN64) +static void sigHandler(int signum) +{ + m_killed = true; + m_signal = signum; +} +#endif + +const char* HEADER1 = "This software is for use on amateur radio networks only,"; +const char* HEADER2 = "it is to be used for educational purposes only. Its use on"; +const char* HEADER3 = "commercial networks is strictly prohibited."; +const char* HEADER4 = "Copyright(C) 2015-2017 by Jonathan Naylor, G4KLX and others"; + +int main(int argc, char** argv) +{ + const char* iniFile = DEFAULT_INI_FILE; + if (argc > 1) { + for (int currentArg = 1; currentArg < argc; ++currentArg) { + std::string arg = argv[currentArg]; + if ((arg == "-v") || (arg == "--version")) { + ::fprintf(stdout, "MMDVMHost version %s git #%.7s\n", VERSION, gitversion); + return 0; + } else if (arg.substr(0,1) == "-") { + ::fprintf(stderr, "Usage: MMDVMHost [-v|--version] [filename]\n"); + return 1; + } else { + iniFile = argv[currentArg]; + } + } + } + +#if !defined(_WIN32) && !defined(_WIN64) + ::signal(SIGINT, sigHandler); + ::signal(SIGTERM, sigHandler); + ::signal(SIGHUP, sigHandler); +#endif + + int ret = 0; + + do { + m_signal = 0; + + CMMDVMHost* host = new CMMDVMHost(std::string(iniFile)); + ret = host->run(); + + delete host; + + if (m_signal == 2) + ::LogInfo("MMDVMHost-%s exited on receipt of SIGINT", VERSION); + + if (m_signal == 15) + ::LogInfo("MMDVMHost-%s exited on receipt of SIGTERM", VERSION); + + if (m_signal == 1) + ::LogInfo("MMDVMHost-%s is restarting on receipt of SIGHUP", VERSION); + } while (m_signal == 1); + + ::LogFinalise(); + + return ret; +} + +CMMDVMHost::CMMDVMHost(const std::string& confFile) : +m_conf(confFile), +m_modem(NULL), +m_dstarNetwork(NULL), +m_dmrNetwork(NULL), +m_ysfNetwork(NULL), +m_p25Network(NULL), +m_display(NULL), +m_ump(NULL), +m_mode(MODE_IDLE), +m_dstarRFModeHang(10U), +m_dmrRFModeHang(10U), +m_ysfRFModeHang(10U), +m_p25RFModeHang(10U), +m_dstarNetModeHang(3U), +m_dmrNetModeHang(3U), +m_ysfNetModeHang(3U), +m_p25NetModeHang(3U), +m_modeTimer(1000U), +m_dmrTXTimer(1000U), +m_cwIdTimer(1000U), +m_duplex(false), +m_timeout(180U), +m_dstarEnabled(false), +m_dmrEnabled(false), +m_ysfEnabled(false), +m_p25Enabled(false), +m_cwIdTime(0U), +m_lookup(NULL), +m_callsign(), +m_id(0U), +m_cwCallsign() +{ +} + +CMMDVMHost::~CMMDVMHost() +{ +} + +int CMMDVMHost::run() +{ + bool ret = m_conf.read(); + if (!ret) { + ::fprintf(stderr, "MMDVMHost: cannot read the .ini file\n"); + return 1; + } + + ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel()); + if (!ret) { + ::fprintf(stderr, "MMDVMHost: unable to open the log file\n"); + return 1; + } + +#if !defined(_WIN32) && !defined(_WIN64) + bool m_daemon = m_conf.getDaemon(); + if (m_daemon) { + // Create new process + pid_t pid = ::fork(); + if (pid == -1) { + LogWarning("Couldn't fork() , exiting"); + return -1; + } else if (pid != 0) { + exit(EXIT_SUCCESS); + } + + // Create new session and process group + if (::setsid() == -1){ + LogWarning("Couldn't setsid(), exiting"); + return -1; + } + + // Set the working directory to the root directory + if (::chdir("/") == -1){ + LogWarning("Couldn't cd /, exiting"); + return -1; + } + + ::close(STDIN_FILENO); + ::close(STDOUT_FILENO); + ::close(STDERR_FILENO); + +#if !defined(HD44780) && !defined(OLED) + //If we are currently root... + if (getuid() == 0) { + struct passwd* user = ::getpwnam("mmdvm"); + if (user == NULL) { + LogError("Could not get the mmdvm user, exiting"); + return -1; + } + + uid_t mmdvm_uid = user->pw_uid; + gid_t mmdvm_gid = user->pw_gid; + + //Set user and group ID's to mmdvm:mmdvm + if (::setgid(mmdvm_gid) != 0) { + LogWarning("Could not set mmdvm GID, exiting"); + return -1; + } + + if (::setuid(mmdvm_uid) != 0) { + LogWarning("Could not set mmdvm UID, exiting"); + return -1; + } + + //Double check it worked (AKA Paranoia) + if (::setuid(0) != -1){ + LogWarning("It's possible to regain root - something is wrong!, exiting"); + return -1; + } + } + } +#else + LogWarning("Dropping root permissions in daemon mode is disabled with HD44780 display"); + } +#endif +#endif + + LogInfo(HEADER1); + LogInfo(HEADER2); + LogInfo(HEADER3); + LogInfo(HEADER4); + + LogMessage("MMDVMHost-%s is starting", VERSION); + LogMessage("Built %s %s (GitID #%.7s)", __TIME__, __DATE__, gitversion); + + readParams(); + + ret = createModem(); + if (!ret) + return 1; + + if (m_conf.getUMPEnabled()) { + std::string port = m_conf.getUMPPort(); + + LogInfo("Universal MMDVM Peripheral"); + LogInfo(" Port: %s", port.c_str()); + + m_ump = new CUMP(port); + bool ret = m_ump->open(); + if (!ret) { + delete m_ump; + m_ump = NULL; + } + } + + createDisplay(); + + if (m_dstarEnabled && m_conf.getDStarNetworkEnabled()) { + ret = createDStarNetwork(); + if (!ret) + return 1; + } + + if (m_dmrEnabled && m_conf.getDMRNetworkEnabled()) { + ret = createDMRNetwork(); + if (!ret) + return 1; + } + + if (m_ysfEnabled && m_conf.getFusionNetworkEnabled()) { + ret = createYSFNetwork(); + if (!ret) + return 1; + } + + if (m_p25Enabled && m_conf.getP25NetworkEnabled()) { + ret = createP25Network(); + if (!ret) + return 1; + } + + if (m_conf.getCWIdEnabled()) { + unsigned int time = m_conf.getCWIdTime(); + m_cwCallsign = m_conf.getCWIdCallsign(); + + LogInfo("CW Id Parameters"); + LogInfo(" Time: %u mins", time); + LogInfo(" Callsign: %s", m_cwCallsign.c_str()); + + m_cwIdTime = time * 60U; + + m_cwIdTimer.setTimeout(m_cwIdTime / 4U); + m_cwIdTimer.start(); + } + + CTimer dmrBeaconTimer(1000U, 4U); + bool dmrBeaconsEnabled = m_dmrEnabled && m_conf.getDMRBeacons(); + + // For all modes we handle RSSI + std::string rssiMappingFile = m_conf.getModemRSSIMappingFile(); + + CRSSIInterpolator* rssi = new CRSSIInterpolator; + if (!rssiMappingFile.empty()) { + LogInfo("RSSI"); + LogInfo(" Mapping File: %s", rssiMappingFile.c_str()); + rssi->load(rssiMappingFile); + } + + // For DMR and P25 we try to map IDs to callsigns + if (m_dmrEnabled || m_p25Enabled) { + std::string lookupFile = m_conf.getDMRIdLookupFile(); + unsigned int reloadTime = m_conf.getDMRIdLookupTime(); + + LogInfo("DMR Id Lookups"); + LogInfo(" File: %s", lookupFile.length() > 0U ? lookupFile.c_str() : "None"); + if (reloadTime > 0U) + LogInfo(" Reload: %u hours", reloadTime); + + m_lookup = new CDMRLookup(lookupFile, reloadTime); + m_lookup->read(); + } + + CStopWatch stopWatch; + stopWatch.start(); + + CDStarControl* dstar = NULL; + if (m_dstarEnabled) { + std::string module = m_conf.getDStarModule(); + bool selfOnly = m_conf.getDStarSelfOnly(); + std::vector blackList = m_conf.getDStarBlackList(); + bool ackReply = m_conf.getDStarAckReply(); + unsigned int ackTime = m_conf.getDStarAckTime(); + bool errorReply = m_conf.getDStarErrorReply(); + bool remoteGateway = m_conf.getDStarRemoteGateway(); + m_dstarRFModeHang = m_conf.getDStarModeHang(); + + LogInfo("D-Star RF Parameters"); + LogInfo(" Module: %s", module.c_str()); + LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); + LogInfo(" Ack Reply: %s", ackReply ? "yes" : "no"); + LogInfo(" Ack Time: %ums", ackTime); + LogInfo(" Error Reply: %s", errorReply ? "yes" : "no"); + LogInfo(" Remote Gateway: %s", remoteGateway ? "yes" : "no"); + LogInfo(" Mode Hang: %us", m_dstarRFModeHang); + + if (blackList.size() > 0U) + LogInfo(" Black List: %u", blackList.size()); + + dstar = new CDStarControl(m_callsign, module, selfOnly, ackReply, ackTime, errorReply, blackList, m_dstarNetwork, m_display, m_timeout, m_duplex, remoteGateway, rssi); + } + + CDMRControl* dmr = NULL; + if (m_dmrEnabled) { + unsigned int id = m_conf.getDMRId(); + unsigned int colorCode = m_conf.getDMRColorCode(); + bool selfOnly = m_conf.getDMRSelfOnly(); + bool embeddedLCOnly = m_conf.getDMREmbeddedLCOnly(); + bool dumpTAData = m_conf.getDMRDumpTAData(); + std::vector prefixes = m_conf.getDMRPrefixes(); + std::vector blackList = m_conf.getDMRBlackList(); + std::vector whiteList = m_conf.getDMRWhiteList(); + std::vector slot1TGWhiteList = m_conf.getDMRSlot1TGWhiteList(); + std::vector slot2TGWhiteList = m_conf.getDMRSlot2TGWhiteList(); + unsigned int callHang = m_conf.getDMRCallHang(); + unsigned int txHang = m_conf.getDMRTXHang(); + unsigned int jitter = m_conf.getDMRNetworkJitter(); + m_dmrRFModeHang = m_conf.getDMRModeHang(); + + if (txHang > m_dmrRFModeHang) + txHang = m_dmrRFModeHang; + + if (m_conf.getDMRNetworkEnabled()) { + if (txHang > m_dmrNetModeHang) + txHang = m_dmrNetModeHang; + } + + if (callHang > txHang) + callHang = txHang; + + LogInfo("DMR RF Parameters"); + LogInfo(" Id: %u", id); + LogInfo(" Color Code: %u", colorCode); + LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); + LogInfo(" Embedded LC Only: %s", embeddedLCOnly ? "yes" : "no"); + LogInfo(" Dump Talker Alias Data: %s", dumpTAData ? "yes" : "no"); + LogInfo(" Prefixes: %u", prefixes.size()); + + if (blackList.size() > 0U) + LogInfo(" Source ID Black List: %u", blackList.size()); + if (whiteList.size() > 0U) + LogInfo(" Source ID White List: %u", whiteList.size()); + if (slot1TGWhiteList.size() > 0U) + LogInfo(" Slot 1 TG White List: %u", slot1TGWhiteList.size()); + if (slot2TGWhiteList.size() > 0U) + LogInfo(" Slot 2 TG White List: %u", slot2TGWhiteList.size()); + + LogInfo(" Call Hang: %us", callHang); + LogInfo(" TX Hang: %us", txHang); + LogInfo(" Mode Hang: %us", m_dmrRFModeHang); + + dmr = new CDMRControl(id, colorCode, callHang, selfOnly, embeddedLCOnly, dumpTAData, prefixes, blackList, whiteList, slot1TGWhiteList, slot2TGWhiteList, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, m_lookup, rssi, jitter); + + m_dmrTXTimer.setTimeout(txHang); + } + + CYSFControl* ysf = NULL; + if (m_ysfEnabled) { + bool lowDeviation = m_conf.getFusionLowDeviation(); + bool remoteGateway = m_conf.getFusionRemoteGateway(); + bool selfOnly = m_conf.getFusionSelfOnly(); + bool sqlEnabled = m_conf.getFusionSQLEnabled(); + unsigned char sql = m_conf.getFusionSQL(); + m_ysfRFModeHang = m_conf.getFusionModeHang(); + + LogInfo("YSF RF Parameters"); + LogInfo(" Low Deviation: %s", lowDeviation ? "yes" : "no"); + LogInfo(" Remote Gateway: %s", remoteGateway ? "yes" : "no"); + LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); + LogInfo(" DSQ: %s", sqlEnabled ? "yes" : "no"); + if (sqlEnabled) + LogInfo(" DSQ Value: %u", sql); + LogInfo(" Mode Hang: %us", m_ysfRFModeHang); + + ysf = new CYSFControl(m_callsign, selfOnly, m_ysfNetwork, m_display, m_timeout, m_duplex, lowDeviation, remoteGateway, rssi); + ysf->setSQL(sqlEnabled, sql); + } + + CP25Control* p25 = NULL; + if (m_p25Enabled) { + unsigned int id = m_conf.getP25Id(); + unsigned int nac = m_conf.getP25NAC(); + bool uidOverride = m_conf.getP25OverrideUID(); + bool selfOnly = m_conf.getP25SelfOnly(); + bool remoteGateway = m_conf.getP25RemoteGateway(); + m_p25RFModeHang = m_conf.getP25ModeHang(); + + LogInfo("P25 RF Parameters"); + LogInfo(" Id: %u", id); + LogInfo(" NAC: $%03X", nac); + LogInfo(" UID Override: %s", uidOverride ? "yes" : "no"); + LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); + LogInfo(" Remote Gateway: %s", remoteGateway ? "yes" : "no"); + LogInfo(" Mode Hang: %us", m_p25RFModeHang); + + p25 = new CP25Control(nac, id, selfOnly, uidOverride, m_p25Network, m_display, m_timeout, m_duplex, m_lookup, remoteGateway, rssi); + } + + setMode(MODE_IDLE); + + LogMessage("MMDVMHost-%s is running", VERSION); + + while (!m_killed) { + bool lockout1 = m_modem->hasLockout(); + bool lockout2 = false; + if (m_ump != NULL) + lockout2 = m_ump->getLockout(); + if ((lockout1 || lockout2) && m_mode != MODE_LOCKOUT) + setMode(MODE_LOCKOUT); + else if ((!lockout1 && !lockout2) && m_mode == MODE_LOCKOUT) + setMode(MODE_IDLE); + + bool error = m_modem->hasError(); + if (error && m_mode != MODE_ERROR) + setMode(MODE_ERROR); + else if (!error && m_mode == MODE_ERROR) + setMode(MODE_IDLE); + + if (m_ump != NULL) { + bool tx = m_modem->hasTX(); + m_ump->setTX(tx); + bool cd = m_modem->hasCD(); + m_ump->setCD(cd); + } + + unsigned char data[200U]; + unsigned int len; + bool ret; + + len = m_modem->readDStarData(data); + if (dstar != NULL && len > 0U) { + if (m_mode == MODE_IDLE) { + bool ret = dstar->writeModem(data, len); + if (ret) { + m_modeTimer.setTimeout(m_dstarRFModeHang); + setMode(MODE_DSTAR); + } + } else if (m_mode == MODE_DSTAR) { + dstar->writeModem(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("D-Star modem data received when in mode %u", m_mode); + } + } + + len = m_modem->readDMRData1(data); + if (dmr != NULL && len > 0U) { + if (m_mode == MODE_IDLE) { + if (m_duplex) { + bool ret = dmr->processWakeup(data); + if (ret) { + m_modeTimer.setTimeout(m_dmrRFModeHang); + setMode(MODE_DMR); + dmrBeaconTimer.stop(); + } + } else { + m_modeTimer.setTimeout(m_dmrRFModeHang); + setMode(MODE_DMR); + dmr->writeModemSlot1(data, len); + dmrBeaconTimer.stop(); + } + } else if (m_mode == MODE_DMR) { + if (m_duplex && !m_modem->hasTX()) { + bool ret = dmr->processWakeup(data); + if (ret) { + m_modem->writeDMRStart(true); + m_dmrTXTimer.start(); + } + } else { + bool ret = dmr->writeModemSlot1(data, len); + if (ret) { + dmrBeaconTimer.stop(); + m_modeTimer.start(); + if (m_duplex) + m_dmrTXTimer.start(); + } + } + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("DMR modem data received when in mode %u", m_mode); + } + } + + len = m_modem->readDMRData2(data); + if (dmr != NULL && len > 0U) { + if (m_mode == MODE_IDLE) { + if (m_duplex) { + bool ret = dmr->processWakeup(data); + if (ret) { + m_modeTimer.setTimeout(m_dmrRFModeHang); + setMode(MODE_DMR); + dmrBeaconTimer.stop(); + } + } else { + m_modeTimer.setTimeout(m_dmrRFModeHang); + setMode(MODE_DMR); + dmr->writeModemSlot2(data, len); + dmrBeaconTimer.stop(); + } + } else if (m_mode == MODE_DMR) { + if (m_duplex && !m_modem->hasTX()) { + bool ret = dmr->processWakeup(data); + if (ret) { + m_modem->writeDMRStart(true); + m_dmrTXTimer.start(); + } + } else { + bool ret = dmr->writeModemSlot2(data, len); + if (ret) { + dmrBeaconTimer.stop(); + m_modeTimer.start(); + if (m_duplex) + m_dmrTXTimer.start(); + } + } + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("DMR modem data received when in mode %u", m_mode); + } + } + + len = m_modem->readYSFData(data); + if (ysf != NULL && len > 0U) { + if (m_mode == MODE_IDLE) { + bool ret = ysf->writeModem(data, len); + if (ret) { + m_modeTimer.setTimeout(m_ysfRFModeHang); + setMode(MODE_YSF); + } + } else if (m_mode == MODE_YSF) { + ysf->writeModem(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("System Fusion modem data received when in mode %u", m_mode); + } + } + + len = m_modem->readP25Data(data); + if (p25 != NULL && len > 0U) { + if (m_mode == MODE_IDLE) { + bool ret = p25->writeModem(data, len); + if (ret) { + m_modeTimer.setTimeout(m_p25RFModeHang); + setMode(MODE_P25); + } + } else if (m_mode == MODE_P25) { + p25->writeModem(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("P25 modem data received when in mode %u", m_mode); + } + } + + if (m_modeTimer.isRunning() && m_modeTimer.hasExpired()) + setMode(MODE_IDLE); + + if (dstar != NULL) { + ret = m_modem->hasDStarSpace(); + if (ret) { + len = dstar->readModem(data); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_dstarNetModeHang); + setMode(MODE_DSTAR); + } + if (m_mode == MODE_DSTAR) { + m_modem->writeDStarData(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("D-Star data received when in mode %u", m_mode); + } + } + } + } + + if (dmr != NULL) { + ret = m_modem->hasDMRSpace1(); + if (ret) { + len = dmr->readModemSlot1(data); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_dmrNetModeHang); + setMode(MODE_DMR); + } + if (m_mode == MODE_DMR) { + if (m_duplex) { + m_modem->writeDMRStart(true); + m_dmrTXTimer.start(); + } + m_modem->writeDMRData1(data, len); + dmrBeaconTimer.stop(); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("DMR data received when in mode %u", m_mode); + } + } + } + + ret = m_modem->hasDMRSpace2(); + if (ret) { + len = dmr->readModemSlot2(data); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_dmrNetModeHang); + setMode(MODE_DMR); + } + if (m_mode == MODE_DMR) { + if (m_duplex) { + m_modem->writeDMRStart(true); + m_dmrTXTimer.start(); + } + m_modem->writeDMRData2(data, len); + dmrBeaconTimer.stop(); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("DMR data received when in mode %u", m_mode); + } + } + } + } + + if (ysf != NULL) { + ret = m_modem->hasYSFSpace(); + if (ret) { + len = ysf->readModem(data); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_ysfNetModeHang); + setMode(MODE_YSF); + } + if (m_mode == MODE_YSF) { + m_modem->writeYSFData(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("System Fusion data received when in mode %u", m_mode); + } + } + } + } + + if (p25 != NULL) { + ret = m_modem->hasP25Space(); + if (ret) { + len = p25->readModem(data); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_p25NetModeHang); + setMode(MODE_P25); + } + if (m_mode == MODE_P25) { + m_modem->writeP25Data(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("P25 data received when in mode %u", m_mode); + } + } + } + } + + if (m_dmrNetwork != NULL) { + bool run = m_dmrNetwork->wantsBeacon(); + if (dmrBeaconsEnabled && run && m_mode == MODE_IDLE && !m_modem->hasTX()) { + setMode(MODE_DMR); + dmrBeaconTimer.start(); + } + } + + unsigned int ms = stopWatch.elapsed(); + stopWatch.start(); + + m_display->clock(ms); + + m_modem->clock(ms); + m_modeTimer.clock(ms); + + if (dstar != NULL) + dstar->clock(); + if (dmr != NULL) + dmr->clock(); + if (ysf != NULL) + ysf->clock(ms); + if (p25 != NULL) + p25->clock(ms); + + if (m_dstarNetwork != NULL) + m_dstarNetwork->clock(ms); + if (m_dmrNetwork != NULL) + m_dmrNetwork->clock(ms); + if (m_ysfNetwork != NULL) + m_ysfNetwork->clock(ms); + if (m_p25Network != NULL) + m_p25Network->clock(ms); + + m_cwIdTimer.clock(ms); + if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { + if (m_mode == MODE_IDLE && !m_modem->hasTX()){ + LogDebug("sending CW ID"); + m_display->writeCW(); + m_modem->sendCWId(m_cwCallsign); + + m_cwIdTimer.setTimeout(m_cwIdTime); + m_cwIdTimer.start(); + } + } + + dmrBeaconTimer.clock(ms); + if (dmrBeaconTimer.isRunning() && dmrBeaconTimer.hasExpired()) { + setMode(MODE_IDLE); + dmrBeaconTimer.stop(); + } + + m_dmrTXTimer.clock(ms); + if (m_dmrTXTimer.isRunning() && m_dmrTXTimer.hasExpired()) { + m_modem->writeDMRStart(false); + m_dmrTXTimer.stop(); + } + + if (m_ump != NULL) + m_ump->clock(ms); + + if (ms < 5U) + CThread::sleep(5U); + } + + setMode(MODE_IDLE); + + m_modem->close(); + delete m_modem; + + m_display->close(); + delete m_display; + + if (m_ump != NULL) { + m_ump->close(); + delete m_ump; + } + + if (m_lookup != NULL) + m_lookup->stop(); + + if (m_dstarNetwork != NULL) { + m_dstarNetwork->close(); + delete m_dstarNetwork; + } + + if (m_dmrNetwork != NULL) { + m_dmrNetwork->close(); + delete m_dmrNetwork; + } + + if (m_ysfNetwork != NULL) { + m_ysfNetwork->close(); + delete m_ysfNetwork; + } + + if (m_p25Network != NULL) { + m_p25Network->close(); + delete m_p25Network; + } + + delete dstar; + delete dmr; + delete ysf; + delete p25; + + return 0; +} + +bool CMMDVMHost::createModem() +{ + std::string port = m_conf.getModemPort(); + bool rxInvert = m_conf.getModemRXInvert(); + bool txInvert = m_conf.getModemTXInvert(); + bool pttInvert = m_conf.getModemPTTInvert(); + unsigned int txDelay = m_conf.getModemTXDelay(); + unsigned int dmrDelay = m_conf.getModemDMRDelay(); + float rxLevel = m_conf.getModemRXLevel(); + float cwIdTXLevel = m_conf.getModemCWIdTXLevel(); + float dstarTXLevel = m_conf.getModemDStarTXLevel(); + float dmrTXLevel = m_conf.getModemDMRTXLevel(); + float ysfTXLevel = m_conf.getModemYSFTXLevel(); + float p25TXLevel = m_conf.getModemP25TXLevel(); + bool trace = m_conf.getModemTrace(); + bool debug = m_conf.getModemDebug(); + unsigned int colorCode = m_conf.getDMRColorCode(); + bool lowDeviation = m_conf.getFusionLowDeviation(); + unsigned int rxFrequency = m_conf.getRXFrequency(); + unsigned int txFrequency = m_conf.getTXFrequency(); + int rxOffset = m_conf.getModemRXOffset(); + int txOffset = m_conf.getModemTXOffset(); + int rxDCOffset = m_conf.getModemRXDCOffset(); + int txDCOffset = m_conf.getModemTXDCOffset(); + + LogInfo("Modem Parameters"); + LogInfo(" Port: %s", port.c_str()); + LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no"); + LogInfo(" TX Invert: %s", txInvert ? "yes" : "no"); + LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); + LogInfo(" TX Delay: %ums", txDelay); + LogInfo(" RX Offset: %dHz", rxOffset); + LogInfo(" TX Offset: %dHz", txOffset); + LogInfo(" RX DC Offset: %d", rxDCOffset); + LogInfo(" TX DC Offset: %d", txDCOffset); + LogInfo(" DMR Delay: %u (%.1fms)", dmrDelay, float(dmrDelay) * 0.0416666F); + LogInfo(" RX Level: %.1f%%", rxLevel); + LogInfo(" CW Id TX Level: %.1f%%", cwIdTXLevel); + LogInfo(" D-Star TX Level: %.1f%%", dstarTXLevel); + LogInfo(" DMR TX Level: %.1f%%", dmrTXLevel); + LogInfo(" YSF TX Level: %.1f%%", ysfTXLevel); + LogInfo(" P25 TX Level: %.1f%%", p25TXLevel); + LogInfo(" RX Frequency: %uHz (%uHz)", rxFrequency, rxFrequency + rxOffset); + LogInfo(" TX Frequency: %uHz (%uHz)", txFrequency, txFrequency + txOffset); + + m_modem = new CModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, trace, debug); + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled); + m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel); + m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset); + m_modem->setDMRParams(colorCode); + m_modem->setYSFParams(lowDeviation); + + bool ret = m_modem->open(); + if (!ret) { + delete m_modem; + m_modem = NULL; + return false; + } + + return true; +} + +bool CMMDVMHost::createDStarNetwork() +{ + std::string gatewayAddress = m_conf.getDStarGatewayAddress(); + unsigned int gatewayPort = m_conf.getDStarGatewayPort(); + unsigned int localPort = m_conf.getDStarLocalPort(); + bool debug = m_conf.getDStarNetworkDebug(); + m_dstarNetModeHang = m_conf.getDStarNetworkModeHang(); + + LogInfo("D-Star Network Parameters"); + LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); + LogInfo(" Gateway Port: %u", gatewayPort); + LogInfo(" Local Port: %u", localPort); + LogInfo(" Mode Hang: %us", m_dstarNetModeHang); + + m_dstarNetwork = new CDStarNetwork(gatewayAddress, gatewayPort, localPort, m_duplex, VERSION, debug); + + bool ret = m_dstarNetwork->open(); + if (!ret) { + delete m_dstarNetwork; + m_dstarNetwork = NULL; + return false; + } + + m_dstarNetwork->enable(true); + + return true; +} + +bool CMMDVMHost::createDMRNetwork() +{ + std::string address = m_conf.getDMRNetworkAddress(); + unsigned int port = m_conf.getDMRNetworkPort(); + unsigned int local = m_conf.getDMRNetworkLocal(); + unsigned int id = m_conf.getDMRId(); + std::string password = m_conf.getDMRNetworkPassword(); + bool debug = m_conf.getDMRNetworkDebug(); + unsigned int jitter = m_conf.getDMRNetworkJitter(); + bool slot1 = m_conf.getDMRNetworkSlot1(); + bool slot2 = m_conf.getDMRNetworkSlot2(); + HW_TYPE hwType = m_modem->getHWType(); + m_dmrNetModeHang = m_conf.getDMRNetworkModeHang(); + + LogInfo("DMR Network Parameters"); + LogInfo(" Address: %s", address.c_str()); + LogInfo(" Port: %u", port); + if (local > 0U) + LogInfo(" Local: %u", local); + else + LogInfo(" Local: random"); + LogInfo(" Jitter: %ums", jitter); + LogInfo(" Slot 1: %s", slot1 ? "enabled" : "disabled"); + LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); + LogInfo(" Mode Hang: %us", m_dmrNetModeHang); + + m_dmrNetwork = new CDMRNetwork(address, port, local, id, password, m_duplex, VERSION, debug, slot1, slot2, hwType); + + std::string options = m_conf.getDMRNetworkOptions(); + if (!options.empty()) { + LogInfo(" Options: %s", options.c_str()); + m_dmrNetwork->setOptions(options); + } + + unsigned int rxFrequency = m_conf.getRXFrequency(); + unsigned int txFrequency = m_conf.getTXFrequency(); + unsigned int power = m_conf.getPower(); + unsigned int colorCode = m_conf.getDMRColorCode(); + float latitude = m_conf.getLatitude(); + float longitude = m_conf.getLongitude(); + int height = m_conf.getHeight(); + std::string location = m_conf.getLocation(); + std::string description = m_conf.getDescription(); + std::string url = m_conf.getURL(); + + LogInfo("Info Parameters"); + LogInfo(" Callsign: %s", m_callsign.c_str()); + LogInfo(" RX Frequency: %uHz", rxFrequency); + LogInfo(" TX Frequency: %uHz", txFrequency); + LogInfo(" Power: %uW", power); + LogInfo(" Latitude: %fdeg N", latitude); + LogInfo(" Longitude: %fdeg E", longitude); + LogInfo(" Height: %um", height); + LogInfo(" Location: \"%s\"", location.c_str()); + LogInfo(" Description: \"%s\"", description.c_str()); + LogInfo(" URL: \"%s\"", url.c_str()); + + m_dmrNetwork->setConfig(m_callsign, rxFrequency, txFrequency, power, colorCode, latitude, longitude, height, location, description, url); + + bool ret = m_dmrNetwork->open(); + if (!ret) { + delete m_dmrNetwork; + m_dmrNetwork = NULL; + return false; + } + + m_dmrNetwork->enable(true); + + return true; +} + +bool CMMDVMHost::createYSFNetwork() +{ + std::string myAddress = m_conf.getFusionNetworkMyAddress(); + unsigned int myPort = m_conf.getFusionNetworkMyPort(); + std::string gatewayAddress = m_conf.getFusionNetworkGatewayAddress(); + unsigned int gatewayPort = m_conf.getFusionNetworkGatewayPort(); + m_ysfNetModeHang = m_conf.getFusionNetworkModeHang(); + bool debug = m_conf.getFusionNetworkDebug(); + + LogInfo("System Fusion Network Parameters"); + LogInfo(" Local Address: %s", myAddress.c_str()); + LogInfo(" Local Port: %u", myPort); + LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); + LogInfo(" Gateway Port: %u", gatewayPort); + LogInfo(" Mode Hang: %us", m_ysfNetModeHang); + + m_ysfNetwork = new CYSFNetwork(myAddress, myPort, gatewayAddress, gatewayPort, m_callsign, debug); + + bool ret = m_ysfNetwork->open(); + if (!ret) { + delete m_ysfNetwork; + m_ysfNetwork = NULL; + return false; + } + + m_ysfNetwork->enable(true); + + return true; +} + +bool CMMDVMHost::createP25Network() +{ + std::string gatewayAddress = m_conf.getP25GatewayAddress(); + unsigned int gatewayPort = m_conf.getP25GatewayPort(); + unsigned int localPort = m_conf.getP25LocalPort(); + m_p25NetModeHang = m_conf.getP25NetworkModeHang(); + bool debug = m_conf.getP25NetworkDebug(); + + LogInfo("P25 Network Parameters"); + LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); + LogInfo(" Gateway Port: %u", gatewayPort); + LogInfo(" Local Port: %u", localPort); + LogInfo(" Mode Hang: %us", m_p25NetModeHang); + + m_p25Network = new CP25Network(gatewayAddress, gatewayPort, localPort, debug); + + bool ret = m_p25Network->open(); + if (!ret) { + delete m_p25Network; + m_p25Network = NULL; + return false; + } + + m_p25Network->enable(true); + + return true; +} + +void CMMDVMHost::readParams() +{ + m_dstarEnabled = m_conf.getDStarEnabled(); + m_dmrEnabled = m_conf.getDMREnabled(); + m_ysfEnabled = m_conf.getFusionEnabled(); + m_p25Enabled = m_conf.getP25Enabled(); + m_duplex = m_conf.getDuplex(); + m_callsign = m_conf.getCallsign(); + m_id = m_conf.getId(); + m_timeout = m_conf.getTimeout(); + + LogInfo("General Parameters"); + LogInfo(" Callsign: %s", m_callsign.c_str()); + LogInfo(" Id: %u", m_id); + LogInfo(" Duplex: %s", m_duplex ? "yes" : "no"); + LogInfo(" Timeout: %us", m_timeout); + LogInfo(" D-Star: %s", m_dstarEnabled ? "enabled" : "disabled"); + LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled"); + LogInfo(" YSF: %s", m_ysfEnabled ? "enabled" : "disabled"); + LogInfo(" P25: %s", m_p25Enabled ? "enabled" : "disabled"); +} + +void CMMDVMHost::createDisplay() +{ + std::string type = m_conf.getDisplay(); + unsigned int dmrid = m_conf.getDMRId(); + + LogInfo("Display Parameters"); + LogInfo(" Type: %s", type.c_str()); + + if (type == "TFT Serial") { + std::string port = m_conf.getTFTSerialPort(); + unsigned int brightness = m_conf.getTFTSerialBrightness(); + + LogInfo(" Port: %s", port.c_str()); + LogInfo(" Brightness: %u", brightness); + + ISerialPort* serial = NULL; + if (port == "modem") + serial = new CModemSerialPort(m_modem); + else + serial = new CSerialController(port, SERIAL_9600); + + m_display = new CTFTSerial(m_callsign, dmrid, serial, brightness); + } else if (type == "Nextion") { + std::string port = m_conf.getNextionPort(); + unsigned int brightness = m_conf.getNextionBrightness(); + bool displayClock = m_conf.getNextionDisplayClock(); + bool utc = m_conf.getNextionUTC(); + unsigned int idleBrightness = m_conf.getNextionIdleBrightness(); + unsigned int screenLayout = m_conf.getNextionScreenLayout(); + std::string filesConfig = m_conf.getNextionFilesConfig(); + LogInfo(" Port: %s", port.c_str()); + LogInfo(" Brightness: %u", brightness); + LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); + if (displayClock) + LogInfo(" Display UTC: %s", utc ? "yes" : "no"); + LogInfo(" Idle Brightness: %u", idleBrightness); + LogInfo(" Nextion path Files: %s", filesConfig.c_str()); + switch (screenLayout) { + case 0U: + LogInfo(" Screen Layout: G4KLX (Default)"); + break; + case 2U: + LogInfo(" Screen Layout: ON7LDS"); + break; + default: + LogInfo(" Screen Layout: %u (Unknown)", screenLayout); + break; + } + + if (port == "modem") { + ISerialPort* serial = new CModemSerialPort(m_modem); + m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout,filesConfig); + } else if (port == "ump") { + if (m_ump != NULL) + m_display = new CNextion(m_callsign, dmrid, m_ump, brightness, displayClock, utc, idleBrightness, screenLayout,filesConfig); + } else { + ISerialPort* serial = new CSerialController(port, SERIAL_9600); + m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout,filesConfig); + } + } else if (type == "LCDproc") { + std::string address = m_conf.getLCDprocAddress(); + unsigned int port = m_conf.getLCDprocPort(); + unsigned int localPort = m_conf.getLCDprocLocalPort(); + bool displayClock = m_conf.getLCDprocDisplayClock(); + bool utc = m_conf.getLCDprocUTC(); + bool dimOnIdle = m_conf.getLCDprocDimOnIdle(); + + LogInfo(" Address: %s", address.c_str()); + LogInfo(" Port: %u", port); + + if (localPort == 0 ) + LogInfo(" Local Port: random"); + else + LogInfo(" Local Port: %u", localPort); + + LogInfo(" Dim Display on Idle: %s", dimOnIdle ? "yes" : "no"); + LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); + + if (displayClock) + LogInfo(" Display UTC: %s", utc ? "yes" : "no"); + + m_display = new CLCDproc(address.c_str(), port, localPort, m_callsign, dmrid, displayClock, utc, m_duplex, dimOnIdle); +#if defined(HD44780) + } else if (type == "HD44780") { + unsigned int rows = m_conf.getHD44780Rows(); + unsigned int columns = m_conf.getHD44780Columns(); + std::vector pins = m_conf.getHD44780Pins(); + unsigned int i2cAddress = m_conf.getHD44780i2cAddress(); + bool pwm = m_conf.getHD44780PWM(); + unsigned int pwmPin = m_conf.getHD44780PWMPin(); + unsigned int pwmBright = m_conf.getHD44780PWMBright(); + unsigned int pwmDim = m_conf.getHD44780PWMDim(); + bool displayClock = m_conf.getHD44780DisplayClock(); + bool utc = m_conf.getHD44780UTC(); + + if (pins.size() == 6U) { + LogInfo(" Rows: %u", rows); + LogInfo(" Columns: %u", columns); + +#if defined(ADAFRUIT_DISPLAY) || defined(PCF8574_DISPLAY) + LogInfo(" Device Address: %#x", i2cAddress); +#else + LogInfo(" Pins: %u,%u,%u,%u,%u,%u", pins.at(0U), pins.at(1U), pins.at(2U), pins.at(3U), pins.at(4U), pins.at(5U)); +#endif + + LogInfo(" PWM Backlight: %s", pwm ? "yes" : "no"); + if (pwm) { + LogInfo(" PWM Pin: %u", pwmPin); + LogInfo(" PWM Bright: %u", pwmBright); + LogInfo(" PWM Dim: %u", pwmDim); + } + + LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); + if (displayClock) + LogInfo(" Display UTC: %s", utc ? "yes" : "no"); + + m_display = new CHD44780(rows, columns, m_callsign, dmrid, pins, i2cAddress, pwm, pwmPin, pwmBright, pwmDim, displayClock, utc, m_duplex); + } +#endif + +#if defined(OLED) + } else if (type == "OLED") { + unsigned char type = m_conf.getOLEDType(); + unsigned char brightness = m_conf.getOLEDBrightness(); + bool invert = m_conf.getOLEDInvert(); + bool scroll = m_conf.getOLEDScroll(); + bool duplex = m_conf.getDuplex(); + m_display = new COLED(type, brightness, invert, scroll, duplex); + + if (m_duplex==false){ +LogInfo(" OLED Type=EA5SW"); +} +else +LogInfo(" OLED Type=G4KLX"); + +#endif + } else { + m_display = new CNullDisplay; + } + + if (m_display == NULL) { + LogWarning("No valid display found, disabling"); + m_display = new CNullDisplay; + return; + } + + bool ret = m_display->open(); + if (!ret) { + delete m_display; + m_display = new CNullDisplay; + } +} + +void CMMDVMHost::setMode(unsigned char mode) +{ + assert(m_modem != NULL); + assert(m_display != NULL); + + switch (mode) { + case MODE_DSTAR: + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); + if (m_p25Network != NULL) + m_p25Network->enable(false); + m_modem->setMode(MODE_DSTAR); + if (m_ump != NULL) + m_ump->setMode(MODE_DSTAR); + m_mode = MODE_DSTAR; + m_modeTimer.start(); + m_cwIdTimer.stop(); + break; + + case MODE_DMR: + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); + if (m_p25Network != NULL) + m_p25Network->enable(false); + m_modem->setMode(MODE_DMR); + if (m_ump != NULL) + m_ump->setMode(MODE_DMR); + if (m_duplex) { + m_modem->writeDMRStart(true); + m_dmrTXTimer.start(); + } + m_mode = MODE_DMR; + m_modeTimer.start(); + m_cwIdTimer.stop(); + break; + + case MODE_YSF: + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); + if (m_p25Network != NULL) + m_p25Network->enable(false); + m_modem->setMode(MODE_YSF); + if (m_ump != NULL) + m_ump->setMode(MODE_YSF); + m_mode = MODE_YSF; + m_modeTimer.start(); + m_cwIdTimer.stop(); + break; + + case MODE_P25: + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); + m_modem->setMode(MODE_P25); + if (m_ump != NULL) + m_ump->setMode(MODE_P25); + m_mode = MODE_P25; + m_modeTimer.start(); + m_cwIdTimer.stop(); + break; + + case MODE_LOCKOUT: + LogMessage("Mode set to Lockout"); + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); + if (m_p25Network != NULL) + m_p25Network->enable(false); + if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { + m_modem->writeDMRStart(false); + m_dmrTXTimer.stop(); + } + m_modem->setMode(MODE_IDLE); + if (m_ump != NULL) + m_ump->setMode(MODE_IDLE); + m_display->setLockout(); + m_mode = MODE_LOCKOUT; + m_modeTimer.stop(); + m_cwIdTimer.stop(); + break; + + case MODE_ERROR: + LogMessage("Mode set to Error"); + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); + if (m_p25Network != NULL) + m_p25Network->enable(false); + if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { + m_modem->writeDMRStart(false); + m_dmrTXTimer.stop(); + } + if (m_ump != NULL) + m_ump->setMode(MODE_IDLE); + m_display->setError("MODEM"); + m_mode = MODE_ERROR; + m_modeTimer.stop(); + m_cwIdTimer.stop(); + break; + + default: + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(true); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(true); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(true); + if (m_p25Network != NULL) + m_p25Network->enable(true); + if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { + m_modem->writeDMRStart(false); + m_dmrTXTimer.stop(); + } + m_modem->setMode(MODE_IDLE); + if (m_ump != NULL) + m_ump->setMode(MODE_IDLE); + if (m_mode == MODE_ERROR) { + m_modem->sendCWId(m_callsign); + m_cwIdTimer.setTimeout(m_cwIdTime); + m_cwIdTimer.start(); + } else { + m_cwIdTimer.setTimeout(m_cwIdTime / 4U); + m_cwIdTimer.start(); + } + m_display->setIdle(); + m_mode = MODE_IDLE; + m_modeTimer.stop(); + break; + } +} diff --git a/MMDVMHost.h b/MMDVMHost.h new file mode 100644 index 0000000..190a805 --- /dev/null +++ b/MMDVMHost.h @@ -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 + +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 diff --git a/MMDVMHost.sln b/MMDVMHost.sln new file mode 100644 index 0000000..b130c28 --- /dev/null +++ b/MMDVMHost.sln @@ -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 diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj new file mode 100644 index 0000000..2ca0a02 --- /dev/null +++ b/MMDVMHost.vcxproj @@ -0,0 +1,298 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92} + Win32Proj + MMDVMHost + 10.0.15063.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + 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) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + 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) + + + "$(ProjectDir)prebuild.cmd" $(ProjectDir) + + + prebuild.cmd generates GitVersion.h from git refs heads master + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + true + true + 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) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + true + true + 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) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters new file mode 100644 index 0000000..7fb37a0 --- /dev/null +++ b/MMDVMHost.vcxproj.filters @@ -0,0 +1,428 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ee54b3e --- /dev/null +++ b/Makefile @@ -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 diff --git a/Makefile.EA5SW b/Makefile.EA5SW new file mode 100644 index 0000000..99d7fa6 --- /dev/null +++ b/Makefile.EA5SW @@ -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 diff --git a/Makefile.Nextion_HS b/Makefile.Nextion_HS new file mode 100644 index 0000000..319fcd0 --- /dev/null +++ b/Makefile.Nextion_HS @@ -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 diff --git a/Makefile.Pi b/Makefile.Pi new file mode 100644 index 0000000..cfe8074 --- /dev/null +++ b/Makefile.Pi @@ -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 diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit new file mode 100644 index 0000000..707c362 --- /dev/null +++ b/Makefile.Pi.Adafruit @@ -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 diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 new file mode 100644 index 0000000..857c941 --- /dev/null +++ b/Makefile.Pi.HD44780 @@ -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 diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED new file mode 100644 index 0000000..89731ae --- /dev/null +++ b/Makefile.Pi.OLED @@ -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 diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 new file mode 100644 index 0000000..56c7d34 --- /dev/null +++ b/Makefile.Pi.PCF8574 @@ -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 diff --git a/Makefile.Solaris b/Makefile.Solaris new file mode 100644 index 0000000..ad14d09 --- /dev/null +++ b/Makefile.Solaris @@ -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 diff --git a/Modem.cpp b/Modem.cpp new file mode 100644 index 0000000..0c5d73c --- /dev/null +++ b/Modem.cpp @@ -0,0 +1,1312 @@ +/* + * 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. + */ + +#include "DStarDefines.h" +#include "DMRDefines.h" +#include "YSFDefines.h" +#include "P25Defines.h" +#include "Thread.h" +#include "Modem.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +const unsigned char MMDVM_FRAME_START = 0xE0U; + +const unsigned char MMDVM_GET_VERSION = 0x00U; +const unsigned char MMDVM_GET_STATUS = 0x01U; +const unsigned char MMDVM_SET_CONFIG = 0x02U; +const unsigned char MMDVM_SET_MODE = 0x03U; +const unsigned char MMDVM_SET_FREQ = 0x04U; + +const unsigned char MMDVM_SEND_CWID = 0x0AU; + +const unsigned char MMDVM_DSTAR_HEADER = 0x10U; +const unsigned char MMDVM_DSTAR_DATA = 0x11U; +const unsigned char MMDVM_DSTAR_LOST = 0x12U; +const unsigned char MMDVM_DSTAR_EOT = 0x13U; + +const unsigned char MMDVM_DMR_DATA1 = 0x18U; +const unsigned char MMDVM_DMR_LOST1 = 0x19U; +const unsigned char MMDVM_DMR_DATA2 = 0x1AU; +const unsigned char MMDVM_DMR_LOST2 = 0x1BU; +const unsigned char MMDVM_DMR_SHORTLC = 0x1CU; +const unsigned char MMDVM_DMR_START = 0x1DU; +const unsigned char MMDVM_DMR_ABORT = 0x1EU; + +const unsigned char MMDVM_YSF_DATA = 0x20U; +const unsigned char MMDVM_YSF_LOST = 0x21U; + +const unsigned char MMDVM_P25_HDR = 0x30U; +const unsigned char MMDVM_P25_LDU = 0x31U; +const unsigned char MMDVM_P25_LOST = 0x32U; + +const unsigned char MMDVM_ACK = 0x70U; +const unsigned char MMDVM_NAK = 0x7FU; + +const unsigned char MMDVM_SERIAL = 0x80U; + +const unsigned char MMDVM_DEBUG1 = 0xF1U; +const unsigned char MMDVM_DEBUG2 = 0xF2U; +const unsigned char MMDVM_DEBUG3 = 0xF3U; +const unsigned char MMDVM_DEBUG4 = 0xF4U; +const unsigned char MMDVM_DEBUG5 = 0xF5U; + +const unsigned int MAX_RESPONSES = 30U; + +const unsigned int BUFFER_LENGTH = 2000U; + + +CModem::CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool trace, bool debug) : +m_port(port), +m_dmrColorCode(0U), +m_ysfLoDev(false), +m_duplex(duplex), +m_rxInvert(rxInvert), +m_txInvert(txInvert), +m_pttInvert(pttInvert), +m_txDelay(txDelay), +m_dmrDelay(dmrDelay), +m_rxLevel(0U), +m_cwIdTXLevel(0U), +m_dstarTXLevel(0U), +m_dmrTXLevel(0U), +m_ysfTXLevel(0U), +m_p25TXLevel(0U), +m_trace(trace), +m_debug(debug), +m_rxFrequency(0U), +m_txFrequency(0U), +m_dstarEnabled(false), +m_dmrEnabled(false), +m_ysfEnabled(false), +m_p25Enabled(false), +m_rxDCOffset(0), +m_txDCOffset(0), +m_serial(port, SERIAL_115200, true), +m_buffer(NULL), +m_length(0U), +m_offset(0U), +m_rxDStarData(1000U, "Modem RX D-Star"), +m_txDStarData(1000U, "Modem TX D-Star"), +m_rxDMRData1(1000U, "Modem RX DMR1"), +m_rxDMRData2(1000U, "Modem RX DMR2"), +m_txDMRData1(1000U, "Modem TX DMR1"), +m_txDMRData2(1000U, "Modem TX DMR2"), +m_rxYSFData(1000U, "Modem RX YSF"), +m_txYSFData(1000U, "Modem TX YSF"), +m_rxP25Data(1000U, "Modem RX P25"), +m_txP25Data(1000U, "Modem TX P25"), +m_statusTimer(1000U, 0U, 250U), +m_inactivityTimer(1000U, 2U), +m_playoutTimer(1000U, 0U, 10U), +m_dstarSpace(0U), +m_dmrSpace1(0U), +m_dmrSpace2(0U), +m_ysfSpace(0U), +m_p25Space(0U), +m_tx(false), +m_cd(false), +m_lockout(false), +m_error(false), +m_hwType(HWT_UNKNOWN) +{ + assert(!port.empty()); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CModem::~CModem() +{ + delete[] m_buffer; +} + +void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset) +{ + m_rxFrequency = rxFrequency + rxOffset; + m_txFrequency = txFrequency + txOffset; + m_txDCOffset = txDCOffset; + m_rxDCOffset = rxDCOffset; +} + +void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled) +{ + m_dstarEnabled = dstarEnabled; + m_dmrEnabled = dmrEnabled; + m_ysfEnabled = ysfEnabled; + m_p25Enabled = p25Enabled; +} + +void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel) +{ + m_rxLevel = rxLevel; + m_cwIdTXLevel = cwIdTXLevel; + m_dstarTXLevel = dstarTXLevel; + m_dmrTXLevel = dmrTXLevel; + m_ysfTXLevel = ysfTXLevel; + m_p25TXLevel = p25TXLevel; +} + +void CModem::setDMRParams(unsigned int colorCode) +{ + assert(colorCode < 16U); + + m_dmrColorCode = colorCode; +} + +void CModem::setYSFParams(bool loDev) +{ + m_ysfLoDev = loDev; +} + +bool CModem::open() +{ + ::LogMessage("Opening the MMDVM"); + + bool ret = m_serial.open(); + if (!ret) + return false; + + ret = readVersion(); + if (!ret) { + m_serial.close(); + return false; + } else { + /* Stopping the inactivity timer here when a firmware version has been + successfuly read prevents the death spiral of "no reply from modem..." */ + m_inactivityTimer.stop(); + } + + ret = setFrequency(); + if (!ret) { + m_serial.close(); + return false; + } + + ret = setConfig(); + if (!ret) { + m_serial.close(); + return false; + } + + m_statusTimer.start(); + + m_error = false; + m_offset = 0U; + + return true; +} + +void CModem::clock(unsigned int ms) +{ + // Poll the modem status every 250ms + m_statusTimer.clock(ms); + if (m_statusTimer.hasExpired()) { + readStatus(); + m_statusTimer.start(); + } + + m_inactivityTimer.clock(ms); + if (m_inactivityTimer.hasExpired()) { + LogError("No reply from the modem for some time, resetting it"); + m_error = true; + close(); + + CThread::sleep(2000U); // 2s + while (!open()) + CThread::sleep(5000U); // 5s + } + + RESP_TYPE_MMDVM type = getResponse(); + + if (type == RTM_TIMEOUT) { + // Nothing to do + } else if (type == RTM_ERROR) { + // Nothing to do + } else { + // type == RTM_OK + switch (m_buffer[2U]) { + case MMDVM_DSTAR_HEADER: { + if (m_trace) + CUtils::dump(1U, "RX D-Star Header", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_HEADER; + m_rxDStarData.addData(&data, 1U); + + m_rxDStarData.addData(m_buffer + 3U, m_length - 3U); + } + break; + + case MMDVM_DSTAR_DATA: { + if (m_trace) + CUtils::dump(1U, "RX D-Star Data", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_DATA; + m_rxDStarData.addData(&data, 1U); + + m_rxDStarData.addData(m_buffer + 3U, m_length - 3U); + } + break; + + case MMDVM_DSTAR_LOST: { + if (m_trace) + CUtils::dump(1U, "RX D-Star Lost", m_buffer, m_length); + + unsigned char data = 1U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_LOST; + m_rxDStarData.addData(&data, 1U); + } + break; + + case MMDVM_DSTAR_EOT: { + if (m_trace) + CUtils::dump(1U, "RX D-Star EOT", m_buffer, m_length); + + unsigned char data = 1U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_EOT; + m_rxDStarData.addData(&data, 1U); + } + break; + + case MMDVM_DMR_DATA1: { + if (m_trace) + CUtils::dump(1U, "RX DMR Data 1", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxDMRData1.addData(&data, 1U); + + if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) + data = TAG_EOT; + else + data = TAG_DATA; + m_rxDMRData1.addData(&data, 1U); + + m_rxDMRData1.addData(m_buffer + 3U, m_length - 3U); + } + break; + + case MMDVM_DMR_DATA2: { + if (m_trace) + CUtils::dump(1U, "RX DMR Data 2", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxDMRData2.addData(&data, 1U); + + if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) + data = TAG_EOT; + else + data = TAG_DATA; + m_rxDMRData2.addData(&data, 1U); + + m_rxDMRData2.addData(m_buffer + 3U, m_length - 3U); + } + break; + + case MMDVM_DMR_LOST1: { + if (m_trace) + CUtils::dump(1U, "RX DMR Lost 1", m_buffer, m_length); + + unsigned char data = 1U; + m_rxDMRData1.addData(&data, 1U); + + data = TAG_LOST; + m_rxDMRData1.addData(&data, 1U); + } + break; + + case MMDVM_DMR_LOST2: { + if (m_trace) + CUtils::dump(1U, "RX DMR Lost 2", m_buffer, m_length); + + unsigned char data = 1U; + m_rxDMRData2.addData(&data, 1U); + + data = TAG_LOST; + m_rxDMRData2.addData(&data, 1U); + } + break; + + case MMDVM_YSF_DATA: { + if (m_trace) + CUtils::dump(1U, "RX YSF Data", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxYSFData.addData(&data, 1U); + + data = TAG_DATA; + m_rxYSFData.addData(&data, 1U); + + m_rxYSFData.addData(m_buffer + 3U, m_length - 3U); + } + break; + + case MMDVM_YSF_LOST: { + if (m_trace) + CUtils::dump(1U, "RX YSF Lost", m_buffer, m_length); + + unsigned char data = 1U; + m_rxYSFData.addData(&data, 1U); + + data = TAG_LOST; + m_rxYSFData.addData(&data, 1U); + } + break; + + case MMDVM_P25_HDR: { + if (m_trace) + CUtils::dump(1U, "RX P25 Header", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxP25Data.addData(&data, 1U); + + data = TAG_HEADER; + m_rxP25Data.addData(&data, 1U); + + m_rxP25Data.addData(m_buffer + 3U, m_length - 3U); + } + break; + + case MMDVM_P25_LDU: { + if (m_trace) + CUtils::dump(1U, "RX P25 LDU", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxP25Data.addData(&data, 1U); + + data = TAG_DATA; + m_rxP25Data.addData(&data, 1U); + + m_rxP25Data.addData(m_buffer + 3U, m_length - 3U); + } + break; + + case MMDVM_P25_LOST: { + if (m_trace) + CUtils::dump(1U, "RX P25 Lost", m_buffer, m_length); + + unsigned char data = 1U; + m_rxP25Data.addData(&data, 1U); + + data = TAG_LOST; + m_rxP25Data.addData(&data, 1U); + } + break; + + case MMDVM_GET_STATUS: { + // if (m_trace) + // CUtils::dump(1U, "GET_STATUS", m_buffer, m_length); + + m_tx = (m_buffer[5U] & 0x01U) == 0x01U; + + bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U; + if (adcOverflow) + LogError("MMDVM ADC levels have overflowed"); + + bool rxOverflow = (m_buffer[5U] & 0x04U) == 0x04U; + if (rxOverflow) + LogError("MMDVM RX buffer has overflowed"); + + bool txOverflow = (m_buffer[5U] & 0x08U) == 0x08U; + if (txOverflow) + LogError("MMDVM TX buffer has overflowed"); + + m_lockout = (m_buffer[5U] & 0x10U) == 0x10U; + + bool dacOverflow = (m_buffer[5U] & 0x20U) == 0x20U; + if (dacOverflow) + LogError("MMDVM DAC levels have overflowed"); + + m_cd = (m_buffer[5U] & 0x40U) == 0x40U; + + m_dstarSpace = m_buffer[6U]; + m_dmrSpace1 = m_buffer[7U]; + m_dmrSpace2 = m_buffer[8U]; + m_ysfSpace = m_buffer[9U]; + m_p25Space = m_buffer[10U]; + + m_inactivityTimer.start(); + // LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[5U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, int(m_lockout), int(m_cd)); + } + break; + + // These should not be received, but don't complain if we do + case MMDVM_GET_VERSION: + case MMDVM_ACK: + break; + + case MMDVM_NAK: + LogWarning("Received a NAK from the MMDVM, command = 0x%02X, reason = %u", m_buffer[3U], m_buffer[4U]); + break; + + case MMDVM_DEBUG1: + case MMDVM_DEBUG2: + case MMDVM_DEBUG3: + case MMDVM_DEBUG4: + case MMDVM_DEBUG5: + printDebug(); + break; + + default: + LogMessage("Unknown message, type: %02X", m_buffer[2U]); + CUtils::dump("Buffer dump", m_buffer, m_length); + break; + } + } + + // Only feed data to the modem if the playout timer has expired + m_playoutTimer.clock(ms); + if (!m_playoutTimer.hasExpired()) + return; + + if (m_dstarSpace > 1U && !m_txDStarData.isEmpty()) { + unsigned char buffer[4U]; + m_txDStarData.peek(buffer, 4U); + + if ((buffer[3U] == MMDVM_DSTAR_HEADER && m_dstarSpace > 4U) || + (buffer[3U] == MMDVM_DSTAR_DATA && m_dstarSpace > 1U) || + (buffer[3U] == MMDVM_DSTAR_EOT && m_dstarSpace > 1U)) { + unsigned char len = 0U; + m_txDStarData.getData(&len, 1U); + m_txDStarData.getData(m_buffer, len); + + switch (buffer[3U]) { + case MMDVM_DSTAR_HEADER: + if (m_trace) + CUtils::dump(1U, "TX D-Star Header", m_buffer, len); + m_dstarSpace -= 4U; + break; + case MMDVM_DSTAR_DATA: + if (m_trace) + CUtils::dump(1U, "TX D-Star Data", m_buffer, len); + m_dstarSpace -= 1U; + break; + default: + if (m_trace) + CUtils::dump(1U, "TX D-Star EOT", m_buffer, len); + m_dstarSpace -= 1U; + break; + } + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing D-Star data to the MMDVM"); + + m_playoutTimer.start(); + } + } + + if (m_dmrSpace1 > 1U && !m_txDMRData1.isEmpty()) { + unsigned char len = 0U; + m_txDMRData1.getData(&len, 1U); + m_txDMRData1.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX DMR Data 1", m_buffer, len); + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing DMR data to the MMDVM"); + + m_playoutTimer.start(); + + m_dmrSpace1--; + } + + if (m_dmrSpace2 > 1U && !m_txDMRData2.isEmpty()) { + unsigned char len = 0U; + m_txDMRData2.getData(&len, 1U); + m_txDMRData2.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX DMR Data 2", m_buffer, len); + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing DMR data to the MMDVM"); + + m_playoutTimer.start(); + + m_dmrSpace2--; + } + + if (m_ysfSpace > 1U && !m_txYSFData.isEmpty()) { + unsigned char len = 0U; + m_txYSFData.getData(&len, 1U); + m_txYSFData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX YSF Data", m_buffer, len); + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing YSF data to the MMDVM"); + + m_playoutTimer.start(); + + m_ysfSpace--; + } + + if (m_p25Space > 1U && !m_txP25Data.isEmpty()) { + unsigned char len = 0U; + m_txP25Data.getData(&len, 1U); + m_txP25Data.getData(m_buffer, len); + + if (m_trace) { + if (m_buffer[2U] == MMDVM_P25_HDR) + CUtils::dump(1U, "TX P25 HDR", m_buffer, len); + else + CUtils::dump(1U, "TX P25 LDU", m_buffer, len); + } + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing P25 data to the MMDVM"); + + m_playoutTimer.start(); + + m_p25Space--; + } +} + +void CModem::close() +{ + ::LogMessage("Closing the MMDVM"); + + m_serial.close(); +} + +unsigned int CModem::readDStarData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxDStarData.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxDStarData.getData(&len, 1U); + m_rxDStarData.getData(data, len); + + return len; +} + +unsigned int CModem::readDMRData1(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxDMRData1.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxDMRData1.getData(&len, 1U); + m_rxDMRData1.getData(data, len); + + return len; +} + +unsigned int CModem::readDMRData2(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxDMRData2.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxDMRData2.getData(&len, 1U); + m_rxDMRData2.getData(data, len); + + return len; +} + +unsigned int CModem::readYSFData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxYSFData.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxYSFData.getData(&len, 1U); + m_rxYSFData.getData(data, len); + + return len; +} + +unsigned int CModem::readP25Data(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxP25Data.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxP25Data.getData(&len, 1U); + m_rxP25Data.getData(data, len); + + return len; +} + +// To be implemented later if needed +unsigned int CModem::readSerial(unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + return 0U; +} + +bool CModem::hasDStarSpace() const +{ + unsigned int space = m_txDStarData.freeSpace() / (DSTAR_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writeDStarData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[50U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + + switch (data[0U]) { + case TAG_HEADER: + buffer[2U] = MMDVM_DSTAR_HEADER; + break; + case TAG_DATA: + buffer[2U] = MMDVM_DSTAR_DATA; + break; + case TAG_EOT: + buffer[2U] = MMDVM_DSTAR_EOT; + break; + default: + CUtils::dump(2U, "Unknown D-Star packet type", data, length); + return false; + } + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txDStarData.addData(&len, 1U); + m_txDStarData.addData(buffer, len); + + return true; +} + +bool CModem::hasDMRSpace1() const +{ + unsigned int space = m_txDMRData1.freeSpace() / (DMR_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::hasDMRSpace2() const +{ + unsigned int space = m_txDMRData2.freeSpace() / (DMR_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writeDMRData1(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_DMR_DATA1; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txDMRData1.addData(&len, 1U); + m_txDMRData1.addData(buffer, len); + + return true; +} + +bool CModem::writeDMRData2(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_DMR_DATA2; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txDMRData2.addData(&len, 1U); + m_txDMRData2.addData(buffer, len); + + return true; +} + +bool CModem::hasYSFSpace() const +{ + unsigned int space = m_txYSFData.freeSpace() / (YSF_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writeYSFData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[130U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_YSF_DATA; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txYSFData.addData(&len, 1U); + m_txYSFData.addData(buffer, len); + + return true; +} + +bool CModem::hasP25Space() const +{ + unsigned int space = m_txP25Data.freeSpace() / (P25_LDU_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writeP25Data(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_HEADER && data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[250U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = (data[0U] == TAG_HEADER) ? MMDVM_P25_HDR : MMDVM_P25_LDU; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txP25Data.addData(&len, 1U); + m_txP25Data.addData(buffer, len); + + return true; +} + +bool CModem::writeSerial(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[250U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_SERIAL; + + ::memcpy(buffer + 3U, data, length); + + int ret = m_serial.write(buffer, length + 3U); + + return ret != int(length + 3U); +} + +bool CModem::hasTX() const +{ + return m_tx; +} + +bool CModem::hasCD() const +{ + return m_cd; +} + +bool CModem::hasLockout() const +{ + return m_lockout; +} + +bool CModem::hasError() const +{ + return m_error; +} + +bool CModem::readVersion() +{ + CThread::sleep(2000U); // 2s + + for (unsigned int i = 0U; i < 6U; i++) { + unsigned char buffer[3U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 3U; + buffer[2U] = MMDVM_GET_VERSION; + + // CUtils::dump(1U, "Written", buffer, 3U); + + int ret = m_serial.write(buffer, 3U); + if (ret != 3) + return false; + +#if defined(__APPLE__) + m_serial.setNonblock(true); +#endif + + for (unsigned int count = 0U; count < MAX_RESPONSES; count++) { + CThread::sleep(10U); + RESP_TYPE_MMDVM resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] == MMDVM_GET_VERSION) { + if (::memcmp(m_buffer + 4U, "MMDVM", 5U) == 0) + m_hwType = HWT_MMDVM; + else if (::memcmp(m_buffer + 4U, "DVMEGA", 6U) == 0) + m_hwType = HWT_DVMEGA; + else if (::memcmp(m_buffer + 4U, "ZUMspot", 7U) == 0) + m_hwType = HWT_MMDVM_HS; + + LogInfo("MMDVM protocol version: %u, description: %.*s", m_buffer[3U], m_length - 4U, m_buffer + 4U); + return true; + } + } + + CThread::sleep(1500U); + } + + LogError("Unable to read the firmware version after six attempts"); + + return false; +} + +bool CModem::readStatus() +{ + unsigned char buffer[3U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 3U; + buffer[2U] = MMDVM_GET_STATUS; + + // CUtils::dump(1U, "Written", buffer, 3U); + + return m_serial.write(buffer, 3U) == 3; +} + +bool CModem::setConfig() +{ + unsigned char buffer[20U]; + + buffer[0U] = MMDVM_FRAME_START; + + buffer[1U] = 18U; + + buffer[2U] = MMDVM_SET_CONFIG; + + buffer[3U] = 0x00U; + if (m_rxInvert) + buffer[3U] |= 0x01U; + if (m_txInvert) + buffer[3U] |= 0x02U; + if (m_pttInvert) + buffer[3U] |= 0x04U; + if (m_ysfLoDev) + buffer[3U] |= 0x08U; + if (m_debug) + buffer[3U] |= 0x10U; + if (!m_duplex) + buffer[3U] |= 0x80U; + + buffer[4U] = 0x00U; + if (m_dstarEnabled) + buffer[4U] |= 0x01U; + if (m_dmrEnabled) + buffer[4U] |= 0x02U; + if (m_ysfEnabled) + buffer[4U] |= 0x04U; + if (m_p25Enabled) + buffer[4U] |= 0x08U; + + buffer[5U] = m_txDelay / 10U; // In 10ms units + + buffer[6U] = MODE_IDLE; + + buffer[7U] = (unsigned char)(m_rxLevel * 2.55F + 0.5F); + + buffer[8U] = (unsigned char)(m_cwIdTXLevel * 2.55F + 0.5F); + + buffer[9U] = m_dmrColorCode; + + buffer[10U] = m_dmrDelay; + + buffer[11U] = 128U; // Was OscOffset + + buffer[12U] = (unsigned char)(m_dstarTXLevel * 2.55F + 0.5F); + buffer[13U] = (unsigned char)(m_dmrTXLevel * 2.55F + 0.5F); + buffer[14U] = (unsigned char)(m_ysfTXLevel * 2.55F + 0.5F); + buffer[15U] = (unsigned char)(m_p25TXLevel * 2.55F + 0.5F); + + buffer[16U] = (unsigned char)(m_txDCOffset + 128); + buffer[17U] = (unsigned char)(m_rxDCOffset + 128); + + // CUtils::dump(1U, "Written", buffer, 18U); + + int ret = m_serial.write(buffer, 18U); + if (ret != 18) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_CONFIG command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_CONFIG command from the modem"); + return false; + } + + m_playoutTimer.start(); + + return true; +} + +bool CModem::setFrequency() +{ + unsigned char buffer[15U]; + + buffer[0U] = MMDVM_FRAME_START; + + buffer[1U] = 12U; + + buffer[2U] = MMDVM_SET_FREQ; + + buffer[3U] = 0x00U; + + buffer[4U] = (m_rxFrequency >> 0) & 0xFFU; + buffer[5U] = (m_rxFrequency >> 8) & 0xFFU; + buffer[6U] = (m_rxFrequency >> 16) & 0xFFU; + buffer[7U] = (m_rxFrequency >> 24) & 0xFFU; + + buffer[8U] = (m_txFrequency >> 0) & 0xFFU; + buffer[9U] = (m_txFrequency >> 8) & 0xFFU; + buffer[10U] = (m_txFrequency >> 16) & 0xFFU; + buffer[11U] = (m_txFrequency >> 24) & 0xFFU; + + // CUtils::dump(1U, "Written", buffer, 12U); + + int ret = m_serial.write(buffer, 12U); + if (ret != 12) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_FREQ command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_FREQ command from the modem"); + return false; + } + + return true; +} + +RESP_TYPE_MMDVM CModem::getResponse() +{ + if (m_offset == 0U) { + // Get the start of the frame or nothing at all + int ret = m_serial.read(m_buffer + 0U, 1U); + if (ret < 0) { + LogError("Error when reading from the modem"); + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + if (m_buffer[0U] != MMDVM_FRAME_START) + return RTM_TIMEOUT; + + m_offset = 1U; + } + + if (m_offset == 1U) { + // Get the length of the frame + int ret = m_serial.read(m_buffer + 1U, 1U); + if (ret < 0) { + LogError("Error when reading from the modem"); + m_offset = 0U; + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + if (m_buffer[1U] >= 250U) { + LogError("Invalid length received from the modem - %u", m_buffer[1U]); + m_offset = 0U; + return RTM_ERROR; + } + + m_length = m_buffer[1U]; + m_offset = 2U; + } + + if (m_offset == 2U) { + // Get the frame type + int ret = m_serial.read(m_buffer + 2U, 1U); + if (ret < 0) { + LogError("Error when reading from the modem"); + m_offset = 0U; + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + m_offset = 3U; + } + + if (m_offset >= 3U) { + // Use later two byte length field + if (m_length == 0U) { + int ret = m_serial.read(m_buffer + 3U, 2U); + if (ret < 0) { + LogError("Error when reading from the modem"); + m_offset = 0U; + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + m_length = (m_buffer[3U] << 8) | m_buffer[4U]; + m_offset = 5U; + } + + while (m_offset < m_length) { + int ret = m_serial.read(m_buffer + m_offset, m_length - m_offset); + if (ret < 0) { + LogError("Error when reading from the modem"); + m_offset = 0U; + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + if (ret > 0) + m_offset += ret; + } + } + + m_offset = 0U; + + // CUtils::dump(1U, "Received", m_buffer, m_length); + + return RTM_OK; +} + +HW_TYPE CModem::getHWType() const +{ + return m_hwType; +} + +bool CModem::setMode(unsigned char mode) +{ + unsigned char buffer[4U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 4U; + buffer[2U] = MMDVM_SET_MODE; + buffer[3U] = mode; + + // CUtils::dump(1U, "Written", buffer, 4U); + + return m_serial.write(buffer, 4U) == 4; +} + +bool CModem::sendCWId(const std::string& callsign) +{ + unsigned int length = callsign.length(); + if (length > 200U) + length = 200U; + + unsigned char buffer[205U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_SEND_CWID; + + for (unsigned int i = 0U; i < length; i++) + buffer[i + 3U] = callsign.at(i); + + // CUtils::dump(1U, "Written", buffer, length + 3U); + + return m_serial.write(buffer, length + 3U) == int(length + 3U); +} + +bool CModem::writeDMRStart(bool tx) +{ + if (tx && m_tx) + return true; + if (!tx && !m_tx) + return true; + + unsigned char buffer[4U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 4U; + buffer[2U] = MMDVM_DMR_START; + buffer[3U] = tx ? 0x01U : 0x00U; + + // CUtils::dump(1U, "Written", buffer, 4U); + + return m_serial.write(buffer, 4U) == 4; +} + +bool CModem::writeDMRAbort(unsigned int slotNo) +{ + if (slotNo == 1U) + m_txDMRData1.clear(); + else + m_txDMRData2.clear(); + + unsigned char buffer[4U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 4U; + buffer[2U] = MMDVM_DMR_ABORT; + buffer[3U] = slotNo; + + // CUtils::dump(1U, "Written", buffer, 4U); + + return m_serial.write(buffer, 4U) == 4; +} + +bool CModem::writeDMRShortLC(const unsigned char* lc) +{ + assert(lc != NULL); + + unsigned char buffer[12U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 12U; + buffer[2U] = MMDVM_DMR_SHORTLC; + buffer[3U] = lc[0U]; + buffer[4U] = lc[1U]; + buffer[5U] = lc[2U]; + buffer[6U] = lc[3U]; + buffer[7U] = lc[4U]; + buffer[8U] = lc[5U]; + buffer[9U] = lc[6U]; + buffer[10U] = lc[7U]; + buffer[11U] = lc[8U]; + + // CUtils::dump(1U, "Written", buffer, 12U); + + return m_serial.write(buffer, 12U) == 12; +} + +void CModem::printDebug() +{ + if (m_buffer[2U] == MMDVM_DEBUG1) { + LogMessage("Debug: %.*s", m_length - 3U, m_buffer + 3U); + } else if (m_buffer[2U] == MMDVM_DEBUG2) { + short val1 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; + LogMessage("Debug: %.*s %d", m_length - 5U, m_buffer + 3U, val1); + } else if (m_buffer[2U] == MMDVM_DEBUG3) { + short val1 = (m_buffer[m_length - 4U] << 8) | m_buffer[m_length - 3U]; + short val2 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; + LogMessage("Debug: %.*s %d %d", m_length - 7U, m_buffer + 3U, val1, val2); + } else if (m_buffer[2U] == MMDVM_DEBUG4) { + short val1 = (m_buffer[m_length - 6U] << 8) | m_buffer[m_length - 5U]; + short val2 = (m_buffer[m_length - 4U] << 8) | m_buffer[m_length - 3U]; + short val3 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; + LogMessage("Debug: %.*s %d %d %d", m_length - 9U, m_buffer + 3U, val1, val2, val3); + } else if (m_buffer[2U] == MMDVM_DEBUG5) { + short val1 = (m_buffer[m_length - 8U] << 8) | m_buffer[m_length - 7U]; + short val2 = (m_buffer[m_length - 6U] << 8) | m_buffer[m_length - 5U]; + short val3 = (m_buffer[m_length - 4U] << 8) | m_buffer[m_length - 3U]; + short val4 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; + LogMessage("Debug: %.*s %d %d %d %d", m_length - 11U, m_buffer + 3U, val1, val2, val3, val4); + } +} diff --git a/Modem.h b/Modem.h new file mode 100644 index 0000000..918e49a --- /dev/null +++ b/Modem.h @@ -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 + +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 m_rxDStarData; + CRingBuffer m_txDStarData; + CRingBuffer m_rxDMRData1; + CRingBuffer m_rxDMRData2; + CRingBuffer m_txDMRData1; + CRingBuffer m_txDMRData2; + CRingBuffer m_rxYSFData; + CRingBuffer m_txYSFData; + CRingBuffer m_rxP25Data; + CRingBuffer 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 diff --git a/ModemSerialPort.cpp b/ModemSerialPort.cpp new file mode 100644 index 0000000..51ba01d --- /dev/null +++ b/ModemSerialPort.cpp @@ -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 +#include + +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() +{ +} diff --git a/ModemSerialPort.h b/ModemSerialPort.h new file mode 100644 index 0000000..d6761ce --- /dev/null +++ b/ModemSerialPort.h @@ -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 diff --git a/Mutex.cpp b/Mutex.cpp new file mode 100644 index 0000000..837e340 --- /dev/null +++ b/Mutex.cpp @@ -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 diff --git a/Mutex.h b/Mutex.h new file mode 100644 index 0000000..7ce9f85 --- /dev/null +++ b/Mutex.h @@ -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 +#else +#include +#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 diff --git a/NetworkInfo.cpp b/NetworkInfo.cpp new file mode 100644 index 0000000..1f97866 --- /dev/null +++ b/NetworkInfo.cpp @@ -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 +#include +#include +#include +#include + +#include +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#else +#include +#include +#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 +} diff --git a/NetworkInfo.h b/NetworkInfo.h new file mode 100644 index 0000000..2febcde --- /dev/null +++ b/NetworkInfo.h @@ -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 diff --git a/Nextion.cpp b/Nextion.cpp new file mode 100644 index 0000000..8ada02f --- /dev/null +++ b/Nextion.cpp @@ -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 +#include +#include +#include +#include +//#include + +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(¤tTime); // Get the current time + + if (m_utc) + Time = ::gmtime(¤tTime); + else + Time = ::localtime(¤tTime); + + 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); +} diff --git a/Nextion.h b/Nextion.h new file mode 100644 index 0000000..1332f09 --- /dev/null +++ b/Nextion.h @@ -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 + +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 diff --git a/Nextion_HS.cpp b/Nextion_HS.cpp new file mode 100644 index 0000000..b768381 --- /dev/null +++ b/Nextion_HS.cpp @@ -0,0 +1,1149 @@ +/* + * 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 "Nextion.h" +#include "Log.h" +#include "NetworkInfo.h" +#include "DMRSlot.h" + + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* +#include "Nextion.h" +#include "Log.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +//#include +#include +//#include +//#include +*/ + + +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, const std::string& filesConfig) : +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_filesConfig(filesConfig), +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() +{ +} + + +//using namespace std; + + + +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() + +{ + + + +// INI File Read + +FILE *cfgfile; + int maxline = 256; + int i=0; + char idles[4][256]; + +std::string info ("info.ini"); +std::string path (""); +path = m_filesConfig + info; +const char* file = path.c_str(); +cfgfile = fopen (file, "r"); +if (cfgfile==NULL) printf("can't open file\n"); + while (fgets(idles[i], maxline, cfgfile)) { + idles[i][strlen(idles[i])-1] = '\0'; + i++; + } + fclose(cfgfile); + + + + sendCommand("page MMDVM"); + + + char command0[40]; + ::sprintf(command0, "dim=%u", m_idleBrightness); + sendCommand(command0); + + + char command1[40U]; + ::sprintf(command1, "t0.txt=\"%s/%u\"", m_callsign.c_str(), m_dmrid); + sendCommand(command1); + + char command2[40U]; + ::sprintf(command2, "t30.txt=\"%s\"", idles[1]); + sendCommand(command2); + + char command3[40U]; + ::sprintf(command3, "t31.txt=\"%s\"", idles[2]); + sendCommand(command3); + + char command4[40U]; + +// ::sprintf(command4, "t1.txt=\"%s\"", idles[3]); + + ::sprintf(command4, "t1.txt=\"DmrPlus\""); + + sendCommand(command4); + + + + sendCommand("t36.pco=60965"); + sendCommand("t36.txt=\"Idle\""); + + + + char text2[30U]; + ::sprintf(text2, "t33.txt=\"%s\"", m_ipaddress.c_str()); + sendCommand(text2); + + + + m_clockDisplayTimer.start(); + + m_mode = MODE_IDLE; + + //Parses file into objects in memory + +} + + +void CNextion::setErrorInt(const char* text) +{ + assert(text != NULL); + + sendCommand("page MMDVM"); + + char command[20]; + ::sprintf(command, "dim=%u", m_brightness); + sendCommand(command); + sendCommand("t0.font=11"); + ::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"); + +//printf ("Pagina DMR\n"); + + + if (slotNo == 1U) { + sendCommand("t2.pco=0"); + sendCommand("t2.font=11"); + sendCommand("t2.txt=\"DMR RX\""); + } else { + //SLOT1 + } + } + + char text[130U]; + ::sprintf(text, "dim=%u", m_brightness); + sendCommand(text); + + if (slotNo == 1U) { +// SLOT1 + + } else { + +if (strcmp(type,"R") == 0) { + sendCommand("t2.pco=2016"); + sendCommand("t36.pco=2016"); + sendCommand("t36.txt=\"Busy\""); + sendCommand(text); + +} else { + sendCommand("t2.pco=1024"); + sendCommand("j0.val=99"); + sendCommand("j0.pco=63488"); + sendCommand("t36.pco=63488"); + sendCommand("t36.txt=\"Tx\""); + sendCommand(text); +} + + + ::sprintf(text, "t2.txt=\"%s %s\"", type, src.c_str()); + + +// ::sprintf(text, "t2.txt=\"%s %s\"", type, src.c_str()); +// INI Read Section TG + +// INI File Read + +FILE *cfgfileTG; + int maxlineTG = 256; + int iTG=0 ; + char TGx[131][256]; + std::string tgs ("tg.ini"); + std::string pathtg (""); + pathtg = m_filesConfig + tgs; + const char* filetg = pathtg.c_str(); + cfgfileTG = fopen (filetg, "r"); + if (cfgfileTG==NULL) printf("can't open file\n"); + while (fgets(TGx[iTG], maxlineTG, cfgfileTG)) { + TGx[iTG][strlen(TGx[iTG])-1] = '\0'; + iTG++; + } + fclose(cfgfileTG); + + + + + +// READ INI SYSOP & CONTROL + + FILE *cfgfileCT; + int maxlineCT = 256; + int iCT=0; + char ctrl[7][256]; + std::string tct ("ctrl.ini"); + std::string pathct (""); + pathct = m_filesConfig + tct; + const char* filect = pathct.c_str(); + cfgfileCT = fopen (filect, "r"); + if (cfgfileCT==NULL) printf("can't open file\n"); + while (fgets(ctrl[iCT], maxlineCT, cfgfileCT)) { + ctrl[iCT][strlen(ctrl[iCT])-1] = '\0'; + iCT++; + } + fclose(cfgfileCT); + + +/* READ INI PREFIXES +* +* A prefixes +* +*/ + +FILE *cfgfileA; + int maxlineA = 256; + int iA=0; + char pfxA[10][256]; + std::string pfA ("prefixA.ini"); + std::string pathpfA (""); + pathpfA = m_filesConfig + pfA; + const char* filepfA = pathpfA.c_str(); + cfgfileA = fopen (filepfA, "r"); + if (cfgfileA==NULL) printf("can't open file\n"); + while (fgets(pfxA[iA], maxlineA, cfgfileA)) { + pfxA[iA][strlen(pfxA[iA])-1] = '\0'; + iA++; + } + fclose(cfgfileA); + + +/* READ INI PREFIXES +* +* B prefixes +* +*/ + + +FILE *cfgfileB; + int maxlineB = 256; + int iB=0; + char pfxB[10][256]; + std::string pfB ("prefixB.ini"); + std::string pathpfB (""); + pathpfB = m_filesConfig + pfB; + const char* filepfB = pathpfB.c_str(); + cfgfileB = fopen (filepfB, "r"); + if (cfgfileB==NULL) printf("can't open file\n"); + while (fgets(pfxB[iB], maxlineB, cfgfileB)) { // while not EOF + pfxB[iB][strlen(pfxB[iB])-1] = '\0'; // remove CR from line + iB++; + } + fclose(cfgfileB); + +/* READ INI PREFIXES +* +* A prefixes +* +*/ + + +FILE *cfgfileC; + int maxlineC = 256; + int iC=0; + char pfxC[10][256]; + std::string pfC ("prefixC.ini"); + std::string pathpfC (""); + pathpfC = m_filesConfig + pfC; + const char* filepfC = pathpfC.c_str(); + cfgfileC = fopen (filepfC, "r"); + if (cfgfileC==NULL) printf("can't open file\n"); + while (fgets(pfxC[iC], maxlineC, cfgfileC)) { // while not EOF + pfxC[iC][strlen(pfxC[iC])-1] = '\0'; // remove CR from line + iC++; + } + fclose(cfgfileC); + + +/* +* +* Compare Prefixes +*/ + +for (int p_f=0;p_f<10;p_f=p_f+1) + +if ((strncmp (src.c_str(),pfxA[p_f],3) == 0) || (strncmp (src.c_str(),pfxB[p_f],3) == 0) || (strncmp (src.c_str(),pfxC[p_f],3) == 0)) +{ + char text[130U]; + int p_i=p_f+20; + ::sprintf(text, "p0.pic=%d",p_i); + sendCommand(text); +} + +// FIXED PREFIXES + +else if (strcmp (src.c_str(),"9990") == 0) +{ + char text[130U]; + ::sprintf(text, "p0.pic=6"); + sendCommand(text); +} + +else if (strcmp (src.c_str(),"4000") == 0) +{ + char text[130U]; + ::sprintf(text, "p0.pic=10"); + sendCommand(text); +} + +else { +} + + sendCommand("t2.font=11"); + sendCommand(text); + +// Compare ShutDown Routines + +if ((strcmp (ctrl[3],dst.c_str()) ==0) && (strncmp (src.c_str(),ctrl[0],6) == 0) && (strcmp ("1",ctrl[1]) == 0)) +{ + printf ("Reboot NOW\n"); + system("sudo shutdown -r now"); +} + +else if ((strcmp (ctrl[2],dst.c_str()) ==0) && (strncmp (src.c_str(),ctrl[0],6) == 0) && (strcmp ("1",ctrl[1]) == 0)) +{ +printf ("Shutdown NOW\n"); +system("sudo shutdown -h now"); +close(); +} + +else if ((strcmp (ctrl[4],dst.c_str()) ==0) && (strncmp (src.c_str(),ctrl[0],6) == 0) && (strcmp ("1",ctrl[1]) == 0)) +{ +printf ("DmrPlusmode\n"); +system("mm_plus"); +} + +else if ((strcmp (ctrl[5],dst.c_str()) ==0) && (strncmp (src.c_str(),ctrl[0],6) == 0) && (strcmp ("1",ctrl[1]) == 0)) +{ +printf ("DMRGateway mode\n"); +system("mm_gate"); +} + +else if ((strcmp (ctrl[6],dst.c_str()) ==0) && (strncmp (src.c_str(),ctrl[0],6) == 0) && (strcmp ("1",ctrl[1]) == 0)) +{ +printf ("BrandMeister mode\n"); +system("mm_BM"); +} + + + ::sprintf(text, "t3.txt=\"%s%s\"", group ? "TG: " : "", dst.c_str()); + + +// Compare TG and write Flags + + +for (int flag=0;flag<131;flag=flag+1) + +{ + +if (strcmp (TGx[flag],dst.c_str()) ==0) { + char text[130U]; + int pi=flag+30; + ::sprintf(text, "p5.pic=%d",pi); + sendCommand(text); +} + + + +/* +* +* Fixed TG's +* +*/ + +else if ((strcmp ("9",dst.c_str()) ==0)||(strcmp ("8",dst.c_str()) ==0)) { + char text[130U]; + ::sprintf(text, "p5.pic=12"); + sendCommand(text); +} +else if (strcmp ("4000",dst.c_str()) ==0) { + char text[130U]; + ::sprintf(text, "p5.pic=11"); + sendCommand(text); +} +else if (strcmp (ctrl[0],dst.c_str()) ==0) { + char text[130U]; + ::sprintf(text, "p5.pic=9"); + sendCommand(text); +} +else if (strcmp ("9990",dst.c_str()) ==0) { + char text[130U]; + ::sprintf(text, "p5.pic=7"); + sendCommand(text); +} +else if (strcmp ("6",dst.c_str()) ==0) { + char text[130U]; + ::sprintf(text, "p5.pic=13"); + sendCommand(text); +} + +else + { +// No group programmed + char text[130U]; +// ::sprintf(text, "p5.pic=5"); + sendCommand(text); +}} + + + sendCommand ("t5.txt=\"RSSI: -10udBm\""); + sendCommand ("t7.txt=\"Ber: 0.0%\""); + 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) { + //SLOT1 + + } else { + + + if (m_rssiCount2 == 0U) { + char text[20U]; + + + ::sprintf(text, "t5.txt=\"RSSI: -%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=\"RSSI: -%udBm\"", m_rssiAccum2 / DMR_RSSI_COUNT); + sendCommand(text); + +// int smeter = (m_rssiAccum2 / DMR_RSSI_COUNT); + + +//int smeter2= (m_rssiAccum2 / DMR_RSSI_COUNT); + +int smeter= (m_rssiAccum2/3); + +//printf ("meterOK:%d\n",smeter); + +if (smeter <= 150 && smeter >= 141) //S1 =8 + +{ + char command[30]; + ::sprintf(command, "j0.val=8"); + sendCommand(command); +} +else if (smeter <= 141 && smeter >= 135) //S2= 18 +{ + char command[30]; + ::sprintf(command, "j0.val=18"); + sendCommand(command); +} +else if (smeter <= 135 && smeter >= 129) //S3= 20 +{ + char command[30]; + ::sprintf(command, "j0.val=20"); + sendCommand(command); +} +else if (smeter <= 129 && smeter>= 123) //S4=23 +{ + char command[30]; + ::sprintf(command, "j0.val=23"); + sendCommand(command); +} +if (smeter <= 123 && smeter >= 117) //S5=28 +{ + char command[30]; + ::sprintf(command, "j0.val=28"); + sendCommand(command); +} +else if (smeter <= 117 && smeter >= 111) //S6=32 +{ + char command[30]; + ::sprintf(command, "j0.val=32"); + sendCommand(command); +} +else if (smeter <=111 && smeter >= 105) //S7=38 +{ + char command[30]; + ::sprintf(command, "j0.val=38"); + sendCommand(command); +} +else if (smeter <= 105 && smeter>= 99) //S8=43 +{ + char command[30]; + ::sprintf(command, "j0.val=43"); + sendCommand(command); +} +else if (smeter <= 99 && smeter >= 93) //S9= 48 +{ + char command[30]; + ::sprintf(command, "j0.val=48"); + sendCommand(command); +} +else if (smeter <= 93 && smeter >= 83) //S9+10=55 +{ + char command[30]; + ::sprintf(command, "j0.val=55"); + sendCommand(command); +} +else if (smeter <= 83 && smeter >= 73) //S9+20 =67 +{ + char command[30]; + ::sprintf(command, "j0.val=67"); + sendCommand(command); +} +else if (smeter <= 73 && smeter>= 63) //S9+30 = 72 +{ + char command[30]; + ::sprintf(command, "j0.val=72"); + sendCommand(command); +} +else if (smeter <= 53 && smeter >= 43) //S9+40=82 +{ + char command[30]; + ::sprintf(command, "j0.val=82"); + sendCommand(command); +} +else if (smeter <= 43 && smeter >= 33) //S9+50 =88 +{ + char command[30]; + ::sprintf(command, "j0.val=88"); + sendCommand(command); +} +else if (smeter <= 33 && smeter >= 10) //S9+60 =100 +{ + char command[30]; + ::sprintf(command, "j0.val=100"); + sendCommand(command); +} + + + m_rssiAccum2 = 0U; + m_rssiCount2 = 1U; + + } + } +} + + +void CNextion::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, const char* type) + +{ + char text[40U]; + + if (type[0]==' ') { + if (slotNo == 1U) { + sendCommand("t0.pco=33808"); + } else { + sendCommand("t2.pco=33808"); + } + return; + } + + + if (slotNo == 1U) { + //SLOT1 + } else { + ::sprintf(text, "t2.txt=\"%s %s\"",type,talkerAlias); + + if (strlen((char*)talkerAlias)>16-4) sendCommand("t2.font=11"); + if (strlen((char*)talkerAlias)>20-4) sendCommand("t2.font=11"); + if (strlen((char*)talkerAlias)>24-4) sendCommand("t2.font=11"); + + if (strcmp(type,"R") == 0) { + sendCommand("t2.pco=63488"); + sendCommand(text); +} else { + 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=\"Ber: %.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=\"Ber: %.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\""); + sendCommand("t0.pco=0"); + sendCommand("t0.font=0"); + sendCommand("t1.txt=\"\""); + sendCommand("t4.txt=\"\""); + sendCommand("t6.txt=\"\""); + } else { + sendCommand("t2.txt=\"RX DMR\""); + sendCommand("t2.pco=0"); + sendCommand("t2.font=11"); + sendCommand("t3.txt=\"\""); + sendCommand("t5.txt=\"\""); + sendCommand("t7.txt=\"\""); + sendCommand("p0.pic=4"); + sendCommand("p5.pic=5"); + } +} + +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(¤tTime); // Get the current time + + if (m_utc) + Time = ::gmtime(¤tTime); + else + Time = ::localtime(¤tTime); + + setlocale(LC_TIME,""); + char text[50U]; + strftime(text, 50, "t2.txt=\"%x %X\"", Time); + sendCommand(text); + + +FILE *temperatureFile; +double T; +temperatureFile = fopen ("/sys/class/thermal/thermal_zone0/temp", "r"); +if (temperatureFile == NULL) + ; //print some message +fscanf (temperatureFile, "%lf", &T); +T /= 1000; +//printf ("The temperature is %6.3f C.\n", T); + + + +// Temperatura sin decimales + +//int temp = (int)T; + + +//printf ("Mitemp:%d\n",temp); + + char text1[30U]; + ::sprintf(text1, "t32.txt=\"Temp:%6.3f\"", T); +// ::sprintf(text1, "t32.txt=\"Temp:%d\"", temp); + sendCommand(text1); + +fclose (temperatureFile); + + + m_clockDisplayTimer.start(); // restart the clock display timer + } +} + + + +// CPU USAGE + +// + + + +void CNextion::close() +{ + sendCommand("page MMDVM"); + sendCommand("t1.txt=\"MMDVM STOP\""); + sendCommand("t0.txt=\"MMDVM STOP\""); + sendCommand("t30.txt=\"MMDVM STOP\""); + 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); +} + diff --git a/NullDisplay.cpp b/NullDisplay.cpp new file mode 100644 index 0000000..411dcba --- /dev/null +++ b/NullDisplay.cpp @@ -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 +#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() +{ +} diff --git a/NullDisplay.h b/NullDisplay.h new file mode 100644 index 0000000..7bcb1f3 --- /dev/null +++ b/NullDisplay.h @@ -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 + +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 diff --git a/OLED.cpp b/OLED.cpp new file mode 100644 index 0000000..084983a --- /dev/null +++ b/OLED.cpp @@ -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); +//} + +} diff --git a/OLED.h b/OLED.h new file mode 100644 index 0000000..ec82883 --- /dev/null +++ b/OLED.h @@ -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 + +#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 diff --git a/OLED.md b/OLED.md new file mode 100644 index 0000000..a16934a --- /dev/null +++ b/OLED.md @@ -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 don’t 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/) diff --git a/P25Audio.cpp b/P25Audio.cpp new file mode 100644 index 0000000..80bf492 --- /dev/null +++ b/P25Audio.cpp @@ -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 +#include + +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; + } +} diff --git a/P25Audio.h b/P25Audio.h new file mode 100644 index 0000000..7d77c5b --- /dev/null +++ b/P25Audio.h @@ -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 diff --git a/P25Control.cpp b/P25Control.cpp new file mode 100644 index 0000000..d890ddd --- /dev/null +++ b/P25Control.cpp @@ -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 +#include +#include +#include + +// #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; + } +} diff --git a/P25Control.h b/P25Control.h new file mode 100644 index 0000000..cd0ca7c --- /dev/null +++ b/P25Control.h @@ -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 + +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 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 diff --git a/P25Data.cpp b/P25Data.cpp new file mode 100644 index 0000000..3c9eb66 --- /dev/null +++ b/P25Data.cpp @@ -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 +#include +#include + +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++; + } + } +} diff --git a/P25Data.h b/P25Data.h new file mode 100644 index 0000000..6618da2 --- /dev/null +++ b/P25Data.h @@ -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 diff --git a/P25Defines.h b/P25Defines.h new file mode 100644 index 0000000..14e2915 --- /dev/null +++ b/P25Defines.h @@ -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 diff --git a/P25LowSpeedData.cpp b/P25LowSpeedData.cpp new file mode 100644 index 0000000..45989e2 --- /dev/null +++ b/P25LowSpeedData.cpp @@ -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 +#include + +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]; +} diff --git a/P25LowSpeedData.h b/P25LowSpeedData.h new file mode 100644 index 0000000..41ba306 --- /dev/null +++ b/P25LowSpeedData.h @@ -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 diff --git a/P25NID.cpp b/P25NID.cpp new file mode 100644 index 0000000..0bcd45f --- /dev/null +++ b/P25NID.cpp @@ -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 +#include + +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; +} diff --git a/P25NID.h b/P25NID.h new file mode 100644 index 0000000..2f2a847 --- /dev/null +++ b/P25NID.h @@ -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 diff --git a/P25Network.cpp b/P25Network.cpp new file mode 100644 index 0000000..d0428a8 --- /dev/null +++ b/P25Network.cpp @@ -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 +#include +#include + +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; +} diff --git a/P25Network.h b/P25Network.h new file mode 100644 index 0000000..054c772 --- /dev/null +++ b/P25Network.h @@ -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 +#include + +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 m_buffer; + CP25Audio m_audio; +}; + +#endif diff --git a/P25Utils.cpp b/P25Utils.cpp new file mode 100644 index 0000000..763d7e1 --- /dev/null +++ b/P25Utils.cpp @@ -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 +#include + +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; +} diff --git a/P25Utils.h b/P25Utils.h new file mode 100644 index 0000000..cdfca06 --- /dev/null +++ b/P25Utils.h @@ -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 diff --git a/QR1676.cpp b/QR1676.cpp new file mode 100644 index 0000000..1548fc5 --- /dev/null +++ b/QR1676.cpp @@ -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 +#include + +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; +} diff --git a/QR1676.h b/QR1676.h new file mode 100644 index 0000000..dac2c0f --- /dev/null +++ b/QR1676.h @@ -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 diff --git a/README.HD44780 b/README.HD44780 new file mode 100644 index 0000000..35c2dea --- /dev/null +++ b/README.HD44780 @@ -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. diff --git a/README.daemon b/README.daemon new file mode 100644 index 0000000..d68fb5f --- /dev/null +++ b/README.daemon @@ -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 \ No newline at end of file diff --git a/README.md b/README.md index ec9f911..b0201b3 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/RS129.cpp b/RS129.cpp new file mode 100644 index 0000000..8cad52c --- /dev/null +++ b/RS129.cpp @@ -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 +#include +#include + +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]; +} + diff --git a/RS129.h b/RS129.h new file mode 100644 index 0000000..60f99bd --- /dev/null +++ b/RS129.h @@ -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 diff --git a/RS241213.cpp b/RS241213.cpp new file mode 100644 index 0000000..3e929d0 --- /dev/null +++ b/RS241213.cpp @@ -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 +#include + +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; +} diff --git a/RS241213.h b/RS241213.h new file mode 100644 index 0000000..fe3171a --- /dev/null +++ b/RS241213.h @@ -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 diff --git a/RSSI.dat b/RSSI.dat new file mode 100644 index 0000000..096ae87 --- /dev/null +++ b/RSSI.dat @@ -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 +# diff --git a/RSSIInterpolator.cpp b/RSSIInterpolator.cpp new file mode 100644 index 0000000..b277fbc --- /dev/null +++ b/RSSIInterpolator.cpp @@ -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 +#include +#include +#include + +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(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::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)); +} diff --git a/RSSIInterpolator.h b/RSSIInterpolator.h new file mode 100644 index 0000000..ac1d4ad --- /dev/null +++ b/RSSIInterpolator.h @@ -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 +#include +#include + +class CRSSIInterpolator { +public: + CRSSIInterpolator(); + ~CRSSIInterpolator(); + + bool load(const std::string& filename); + + int interpolate(uint16_t raw) const; + +private: + std::map m_map; +}; + +#endif diff --git a/RingBuffer.h b/RingBuffer.h new file mode 100644 index 0000000..707de1c --- /dev/null +++ b/RingBuffer.h @@ -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 +#include +#include + +template 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 diff --git a/SHA256.cpp b/SHA256.cpp new file mode 100644 index 0000000..b3366e0 --- /dev/null +++ b/SHA256.cpp @@ -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 +#include +#include + +#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; + } +} diff --git a/SHA256.h b/SHA256.h new file mode 100644 index 0000000..7c48f19 --- /dev/null +++ b/SHA256.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 + +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 diff --git a/SerialController.cpp b/SerialController.cpp new file mode 100644 index 0000000..7856c24 --- /dev/null +++ b/SerialController.cpp @@ -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 +#include + +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#else +#include +#include +#include +#include +#include +#include +#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 diff --git a/SerialController.h b/SerialController.h new file mode 100644 index 0000000..63bd8be --- /dev/null +++ b/SerialController.h @@ -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 + +#if defined(_WIN32) || defined(_WIN64) +#include +#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 diff --git a/SerialPort.cpp b/SerialPort.cpp new file mode 100644 index 0000000..a2867dc --- /dev/null +++ b/SerialPort.cpp @@ -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() +{ +} diff --git a/SerialPort.h b/SerialPort.h new file mode 100644 index 0000000..2b633b6 --- /dev/null +++ b/SerialPort.h @@ -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 diff --git a/StopWatch.cpp b/StopWatch.cpp new file mode 100644 index 0000000..77d539d --- /dev/null +++ b/StopWatch.cpp @@ -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 + +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 diff --git a/StopWatch.h b/StopWatch.h new file mode 100644 index 0000000..811047e --- /dev/null +++ b/StopWatch.h @@ -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 +#else +#include +#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 diff --git a/Sync.cpp b/Sync.cpp new file mode 100644 index 0000000..f5404db --- /dev/null +++ b/Sync.cpp @@ -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 +#include +#include + + +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); +} diff --git a/Sync.h b/Sync.h new file mode 100644 index 0000000..3ee59d5 --- /dev/null +++ b/Sync.h @@ -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 diff --git a/prebuild.cmd b/prebuild.cmd new file mode 100644 index 0000000..2dc5f9d --- /dev/null +++ b/prebuild.cmd @@ -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...