/* * 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 "DStarDefines.h" #include "DRATSServer.h" #include "Utils.h" // #define LOOPBACK const unsigned int BUFFER_LENGTH = 30000U; CDRATSServer::CDRATSServer(const wxString& address, unsigned int port, const wxString& callsign, IRepeaterCallback* handler) : wxThread(wxTHREAD_JOINABLE), m_address(address), m_port(port), m_callsign(callsign), m_handler(handler), m_socket(NULL), m_stopped(false), m_readState(SS_FIRST), m_readBuffer(NULL), m_readLength(0U), m_readPos(0U), m_readEnd(false), m_writeText(NULL), m_writeState(SS_FIRST), m_writeBuffer(NULL), m_writeLength(0U) { wxASSERT(handler != NULL); wxASSERT(port > 0U); m_readBuffer = new unsigned char[BUFFER_LENGTH]; m_writeBuffer = new unsigned char[BUFFER_LENGTH]; m_writeText = new unsigned char[6U]; } CDRATSServer::~CDRATSServer() { delete[] m_readBuffer; delete[] m_writeBuffer; delete[] m_writeText; } bool CDRATSServer::open() { m_socket = new CTCPReaderWriterServer(m_address, m_port); bool ret = m_socket->start(); if (!ret) { delete m_socket; m_socket = NULL; return false; } Create(); Run(); return true; } void CDRATSServer::writeHeader(const CHeaderData&) { m_writeState = SS_FIRST; if (m_writeLength > 0U && m_socket != NULL) { CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength); m_socket->write(m_writeBuffer, m_writeLength); } m_writeLength = 0U; } void CDRATSServer::writeData(const CAMBEData& data) { // Sync data isn't sent on if (data.isSync()) { m_writeState = SS_FIRST; return; } if (data.isEnd()) { if (m_writeLength > 0U && m_socket != NULL) { CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength); m_socket->write(m_writeBuffer, m_writeLength); } m_writeLength = 0U; return; } unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES]; unsigned int length = data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES); if (length != DV_FRAME_LENGTH_BYTES) return; unsigned char byte1 = buffer[VOICE_FRAME_LENGTH_BYTES + 0U] ^ SCRAMBLER_BYTE1; unsigned char byte2 = buffer[VOICE_FRAME_LENGTH_BYTES + 1U] ^ SCRAMBLER_BYTE2; unsigned char byte3 = buffer[VOICE_FRAME_LENGTH_BYTES + 2U] ^ SCRAMBLER_BYTE3; switch (m_writeState) { case SS_FIRST: m_writeText[0U] = byte1; m_writeText[1U] = byte2; m_writeText[2U] = byte3; m_writeState = SS_SECOND; return; case SS_SECOND: m_writeText[3U] = byte1; m_writeText[4U] = byte2; m_writeText[5U] = byte3; m_writeState = SS_FIRST; break; } if ((m_writeText[0U] & SLOW_DATA_TYPE_MASK) != SLOW_DATA_TYPE_GPS) return; length = m_writeText[0U] & 0x07; // Maximum value of 5 if (length > 5U) length = 5U; for (unsigned int i = 0U; i < length; i++) { m_writeBuffer[m_writeLength++] = m_writeText[i + 1U]; // Check for [EOB] in the buffer to signal the end of the D-RATS data. // To allow strstr() to run correctly m_writeBuffer[m_writeLength] = 0x00U; if (::strstr((char*)m_writeBuffer, "[EOB]") != NULL) { if (m_socket != NULL) { CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength); m_socket->write(m_writeBuffer, m_writeLength); } m_writeLength = 0U; } } } void CDRATSServer::writeEnd() { if (m_writeLength > 0U && m_socket != NULL) { CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength); m_socket->write(m_writeBuffer, m_writeLength); } m_writeLength = 0U; } void* CDRATSServer::Entry() { wxLogMessage(wxT("Starting the D-RATS Server thread for %s"), m_callsign.c_str()); bool sending = false; unsigned int id = 0U; unsigned char seqNo = 0U; unsigned int sent = 0U; wxStopWatch time; try { while (!m_stopped) { serviceSocket(); if (m_readEnd && !sending) { id = CHeaderData::createId(); // Write header CHeaderData header; header.setMyCall1(m_callsign); header.setMyCall2(wxT("DATA")); header.setYourCall(wxT("CQCQCQ ")); header.setId(id); #if defined(LOOPBACK) writeHeader(header); #else m_handler->process(header, DIR_INCOMING, AS_DRATS); #endif m_readState = SS_FIRST; m_readPos = 0U; sending = true; seqNo = 0U; sent = 0U; time.Start(); } if (m_readEnd && sending) { unsigned int needed = time.Time() / DSTAR_FRAME_TIME_MS; while (sent < needed && sending) { // Write AMBE data CAMBEData data; data.setId(id); unsigned char buffer[DV_FRAME_LENGTH_BYTES]; ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); // Insert sync bytes when the sequence number is zero, slow data otherwise if (seqNo == 0U) { ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); m_readState = SS_FIRST; } else { if (m_readState == SS_FIRST) { unsigned char readText[3U]; ::memset(readText, 'f', 3U); unsigned int length = m_readLength - m_readPos; unsigned char bytes = 5U; if (length < 5U) bytes = length; readText[0U] = SLOW_DATA_TYPE_GPS | bytes; for (unsigned int i = 0U; i < 2U && m_readPos < m_readLength; i++) readText[i + 1U] = m_readBuffer[m_readPos++]; readText[0U] ^= SCRAMBLER_BYTE1; readText[1U] ^= SCRAMBLER_BYTE2; readText[2U] ^= SCRAMBLER_BYTE3; ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES); m_readState = SS_SECOND; } else { unsigned char readText[3U]; ::memset(readText, 'f', 3U); for (unsigned int i = 0U; i < 3U && m_readPos < m_readLength; i++) readText[i] = m_readBuffer[m_readPos++]; readText[0U] ^= SCRAMBLER_BYTE1; readText[1U] ^= SCRAMBLER_BYTE2; readText[2U] ^= SCRAMBLER_BYTE3; ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES); m_readState = SS_FIRST; } } data.setSeq(seqNo); data.setData(buffer, DV_FRAME_LENGTH_BYTES); sent++; #if defined(LOOPBACK) writeData(data); #else m_handler->process(data, DIR_INCOMING, AS_DRATS); #endif if (m_readPos == m_readLength) { if (m_readState == SS_SECOND) { seqNo++; if (seqNo == 21U) seqNo = 0U; unsigned char readText[3U]; readText[0U] = 'f' ^ SCRAMBLER_BYTE1; readText[1U] = 'f' ^ SCRAMBLER_BYTE2; readText[2U] = 'f' ^ SCRAMBLER_BYTE3; ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES); data.setSeq(seqNo); data.setData(buffer, DV_FRAME_LENGTH_BYTES); sent++; #if defined(LOOPBACK) writeData(data); #else m_handler->process(data, DIR_INCOMING, AS_DRATS); #endif } seqNo++; if (seqNo == 21U) seqNo = 0U; if (seqNo == 0U) ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); else ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, NULL_SLOW_DATA_BYTES, DATA_FRAME_LENGTH_BYTES); data.setData(buffer, DV_FRAME_LENGTH_BYTES); data.setSeq(seqNo); data.setEnd(true); sent++; #if defined(LOOPBACK) writeData(data); #else m_handler->process(data, DIR_INCOMING, AS_DRATS); #endif m_readLength = 0U; m_readPos = 0U; m_readEnd = false; sending = false; sent = 0U; } seqNo++; if (seqNo == 21U) seqNo = 0U; } } // 50ms Sleep(50UL); } if (m_socket != NULL) m_socket->stop(); } catch (std::exception& e) { wxString message(e.what(), wxConvLocal); wxLogError(wxT("Exception raised in the D-RATS Server thread - \"%s\""), message.c_str()); } catch (...) { wxLogError(wxT("Unknown exception raised in the D-RATS Server thread")); } wxLogMessage(wxT("Stopping the D-RATS Server thread for %s"), m_callsign.c_str()); return NULL; } void CDRATSServer::close() { m_stopped = true; Wait(); } void CDRATSServer::serviceSocket() { if (m_socket == NULL) { m_readLength = 0U; m_readPos = 0U; m_readEnd = false; return; } int len = m_socket->read(m_readBuffer + m_readLength, BUFFER_LENGTH - m_readLength, 0U); if (len > 0) { m_readLength += len; if (!m_readEnd) { // To allow strstr() to run correctly m_readBuffer[m_readLength] = 0x00U; if (::strstr((char*)m_readBuffer, "[EOB]") != NULL) { CUtils::dump(wxT("To RF"), m_readBuffer, m_readLength); m_readEnd = true; } } } }