Add experimental RT8 GPS data handling.

This commit is contained in:
Jonathan Naylor 2017-05-30 14:30:05 +01:00
parent 8bc79767c5
commit aa063ff205
21 changed files with 61859 additions and 6 deletions

111
APRSHelper.cpp Normal file
View file

@ -0,0 +1,111 @@
/*
* Copyright (C) 2017 Tony Bailey
*
* 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.
*
*
************************************************************
*
* A wrapper class for the APRS utilities send GPS data to an APRS server
*
*/
#include "APRSHelper.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <algorithm>
#include <iostream>
CAPRSHelper::CAPRSHelper(const std::string& callsign, const std::string& suffix, const std::string& password, const std::string& address, unsigned int port)
:
m_writer(callsign, suffix, password, address, port),
m_buffer(NULL)
{
m_buffer = new unsigned char[300U];
}
CAPRSHelper::~CAPRSHelper()
{
delete[] m_buffer;
}
bool CAPRSHelper::open()
{
return m_writer.open();
}
void CAPRSHelper::send(std::string callsign, float latitude, float longitude, int altitude )
{
unsigned char source[10U];
copy( callsign.begin(), callsign.end(), source );
char type[11U];
::strcpy(type, "MD-390/RT8");
// Source: Callsign
// Type: Radio Type
// symbol?:
// default (unset) is a House '/-'
// 0x24U
// 0x25U
// 0x26U - ??
// 0x28U - Human
// 0x29U - Normal Car '/>'
// lat
// long
//::sprintf(m_buffer[4U], 0x28U);
m_writer.write(source, type, 0x28U, latitude, longitude, altitude);
}
void CAPRSHelper::close()
{
m_writer.close();
}
/*
* Uncomment this section for local testing and build it without MMDVMHost in the Makefile
*/
/*
int main(void)
{
std::string callsign = "N9OTJ";
std::string suffix = "11";
std::string password = "14479";
std::string address = "aprs.mcducklabs.com";
int port = 14580;
CAPRSHelper* helper = new CAPRSHelper(callsign, suffix, password, address, port);
helper->open();
// Wait 6 seconds for the server to connect before sending data
usleep(1000*1000*6);
// Send the GPS packet
helper->send();
// Need to go into an endless loop and give the APRSWriterThread time to cycle through the message queue
for (;;)
{
// Ctrl-C out of the test program once the packet is sent
}
delete helper;
return 0;
}
*/

42
APRSHelper.h Normal file
View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2017 by Tony Bailey
*
* 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(APRSHelper_H)
#define APRSHelper_H
#include "APRSWriter.h"
#include <string>
class CAPRSHelper {
public:
CAPRSHelper(const std::string& callsign, const std::string& suffix, const std::string& password, const std::string& address, unsigned int port);
~CAPRSHelper();
bool open();
void send(std::string callsign, float latitude, float longitude, int altitude);
void close();
private:
CAPRSWriter m_writer;
unsigned char* m_buffer;
};
#endif

202
APRSWriter.cpp Normal file
View file

@ -0,0 +1,202 @@
/*
* Copyright (C) 2010-2014,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 "APRSWriter.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cmath>
CAPRSWriter::CAPRSWriter(const std::string& callsign, const std::string& suffix, const std::string& password, const std::string& address, unsigned int port) :
m_thread(NULL),
m_enabled(false),
m_idTimer(1000U, 20U * 60U), // 20 minutes
m_callsign(callsign),
m_txFrequency(0U),
m_rxFrequency(0U),
m_latitude(0.0F),
m_longitude(0.0F),
m_height(0)
{
assert(!callsign.empty());
assert(!password.empty());
assert(!address.empty());
assert(port > 0U);
if (!suffix.empty()) {
m_callsign.append("-");
m_callsign.append(suffix.substr(0U, 2U));
}
m_thread = new CAPRSWriterThread(m_callsign, password, address, port);
}
CAPRSWriter::~CAPRSWriter()
{
}
void CAPRSWriter::setInfo(unsigned int txFrequency, unsigned int rxFrequency, float latitude, float longitude, int height)
{
m_txFrequency = txFrequency;
m_rxFrequency = rxFrequency;
m_latitude = latitude;
m_longitude = longitude;
m_height = height;
}
bool CAPRSWriter::open()
{
return m_thread->start();
}
void CAPRSWriter::write(const unsigned char* source, const char* type, unsigned char radio, float fLatitude, float fLongitude, int altitude)
{
assert(source != NULL);
assert(type != NULL);
char callsign[11U];
::memcpy(callsign, source, 10U);
callsign[10U] = 0x00U;
size_t n = ::strspn(callsign, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
callsign[n] = 0x00U;
double tempLat = ::fabs(fLatitude);
double tempLong = ::fabs(fLongitude);
double latitude = ::floor(tempLat);
double longitude = ::floor(tempLong);
latitude = (tempLat - latitude) * 60.0 + latitude * 100.0;
longitude = (tempLong - longitude) * 60.0 + longitude * 100.0;
char lat[20U];
::sprintf(lat, "%07.2lf", latitude);
char lon[20U];
::sprintf(lon, "%08.2lf", longitude);
char symbol;
switch (radio) {
case 0x24U:
case 0x28U:
symbol = '[';
break;
case 0x25U:
case 0x29U:
symbol = '>';
break;
case 0x26U:
symbol = 'r';
break;
default:
symbol = '-';
break;
}
char output[300U];
::sprintf(output, "%s-D>APDPRS,DMR*,qAR,%s:!%s%c/%s%c%c/A=%06.0f %s via MMDVM",
callsign, m_callsign.c_str(),
lat, (fLatitude < 0.0F) ? 'S' : 'N',
lon, (fLongitude < 0.0F) ? 'W' : 'E',
symbol, float(altitude) * 3.28F, type);
m_thread->write(output);
}
void CAPRSWriter::clock(unsigned int ms)
{
m_idTimer.clock(ms);
if (m_idTimer.hasExpired()) {
sendIdFrames();
m_idTimer.start();
}
}
void CAPRSWriter::close()
{
m_thread->stop();
}
void CAPRSWriter::sendIdFrames()
{
if (!m_thread->isConnected())
return;
// Default values aren't passed on
if (m_latitude == 0.0F && m_longitude == 0.0F)
return;
char desc[100U];
if (m_txFrequency != 0U) {
float offset = float(int(m_rxFrequency) - int(m_txFrequency)) / 1000000.0F;
::sprintf(desc, "MMDVM Voice %.5lfMHz %c%.4lfMHz",
float(m_txFrequency) / 1000000.0F,
offset < 0.0F ? '-' : '+',
::fabs(offset));
} else {
::strcpy(desc, "MMDVM Voice");
}
const char* band = "4m";
if (m_txFrequency >= 1200000000U)
band = "1.2";
else if (m_txFrequency >= 420000000U)
band = "440";
else if (m_txFrequency >= 144000000U)
band = "2m";
else if (m_txFrequency >= 50000000U)
band = "6m";
else if (m_txFrequency >= 28000000U)
band = "10m";
double tempLat = ::fabs(m_latitude);
double tempLong = ::fabs(m_longitude);
double latitude = ::floor(tempLat);
double longitude = ::floor(tempLong);
latitude = (tempLat - latitude) * 60.0 + latitude * 100.0;
longitude = (tempLong - longitude) * 60.0 + longitude * 100.0;
char lat[20U];
::sprintf(lat, "%04.2lf", latitude);
char lon[20U];
::sprintf(lon, "%05.2lf", longitude);
std::string server = m_callsign;
size_t pos = server.find_first_of('-');
if (pos == std::string::npos)
server.append("-S");
else
server.append("S");
char output[500U];
::sprintf(output, "%s>APDG03,TCPIP*,qAC,%s:!%s%cD%s%c&/A=%06.0f%s %s",
m_callsign.c_str(), server.c_str(),
lat, (m_latitude < 0.0F) ? 'S' : 'N',
lon, (m_longitude < 0.0F) ? 'W' : 'E',
float(m_height) * 3.28F, band, desc);
m_thread->write(output);
m_idTimer.start();
}

56
APRSWriter.h Normal file
View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2010,2011,2012,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 APRSWriter_H
#define APRSWriter_H
#include "APRSWriterThread.h"
#include "Timer.h"
#include <string>
class CAPRSWriter {
public:
CAPRSWriter(const std::string& callsign, const std::string& suffix, const std::string& password, const std::string& address, unsigned int port);
~CAPRSWriter();
bool open();
void setInfo(unsigned int txFrequency, unsigned int rxFrequency, float latitude, float longitude, int height);
void write(const unsigned char* source, const char* type, unsigned char radio, float latitude, float longitude, int altitude);
void clock(unsigned int ms);
void close();
private:
CAPRSWriterThread* m_thread;
bool m_enabled;
CTimer m_idTimer;
std::string m_callsign;
unsigned int m_txFrequency;
unsigned int m_rxFrequency;
float m_latitude;
float m_longitude;
int m_height;
void sendIdFrames();
};
#endif

248
APRSWriterThread.cpp Normal file
View file

@ -0,0 +1,248 @@
/*
* Copyright (C) 2010-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 "APRSWriterThread.h"
#include "Utils.h"
#include "Log.h"
#include <algorithm>
#include <functional>
#include <cctype>
#include <cstdint>
#include <cstdio>
#include <cassert>
// #define DUMP_TX
const unsigned int CALLSIGN_LENGTH = 8U;
const unsigned int APRS_TIMEOUT = 10U;
CAPRSWriterThread::CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, unsigned int port) :
CThread(),
m_username(callsign),
m_password(password),
m_socket(address, port),
m_queue(20U, "APRS Queue"),
m_exit(false),
m_connected(false),
m_APRSReadCallback(NULL),
m_filter(),
m_clientName("DMRGateway")
{
assert(!callsign.empty());
assert(!password.empty());
assert(!address.empty());
assert(port > 0U);
m_username.resize(CALLSIGN_LENGTH, ' ');
m_username.erase(std::find_if(m_username.rbegin(), m_username.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), m_username.end());
std::transform(m_username.begin(), m_username.end(), m_username.begin(), ::toupper);
}
CAPRSWriterThread::CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, unsigned int port, const std::string& filter, const std::string& clientName) :
CThread(),
m_username(callsign),
m_password(password),
m_socket(address, port),
m_queue(20U, "APRS Queue"),
m_exit(false),
m_connected(false),
m_APRSReadCallback(NULL),
m_filter(filter),
m_clientName(clientName)
{
assert(!callsign.empty());
assert(!password.empty());
assert(!address.empty());
assert(port > 0U);
m_username.resize(CALLSIGN_LENGTH, ' ');
m_username.erase(std::find_if(m_username.rbegin(), m_username.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), m_username.end());
std::transform(m_username.begin(), m_username.end(), m_username.begin(), ::toupper);
}
CAPRSWriterThread::~CAPRSWriterThread()
{
m_username.clear();
}
bool CAPRSWriterThread::start()
{
run();
return true;
}
void CAPRSWriterThread::entry()
{
LogMessage("Starting the APRS Writer thread");
m_connected = connect();
try {
while (!m_exit) {
if (!m_connected) {
m_connected = connect();
if (!m_connected){
LogError("Reconnect attempt to the APRS server has failed");
sleep(10000UL); // 10 secs
}
}
if (m_connected) {
if (!m_queue.isEmpty()){
char* p = NULL;
m_queue.getData(&p, 1U);
LogMessage("APRS ==> %s", p);
::strcat(p, "\r\n");
bool ret = m_socket.write((unsigned char*)p, ::strlen(p));
if (!ret) {
m_connected = false;
m_socket.close();
LogError("Connection to the APRS thread has failed");
}
delete[] p;
}
{
std::string line;
int length = m_socket.readLine(line, APRS_TIMEOUT);
if (length < 0) {
m_connected = false;
m_socket.close();
LogError("Error when reading from the APRS server");
}
if(length > 0 && line.at(0U) != '#'//check if we have something and if that something is an APRS frame
&& m_APRSReadCallback != NULL)//do we have someone wanting an APRS Frame?
{
//wxLogMessage(wxT("Received APRS Frame : ") + line);
m_APRSReadCallback(std::string(line));
}
}
}
}
if (m_connected)
m_socket.close();
while (!m_queue.isEmpty()) {
char* p = NULL;
m_queue.getData(&p, 1U);
delete[] p;
}
}
catch (std::exception& e) {
LogError("Exception raised in the APRS Writer thread - \"%s\"", e.what());
}
catch (...) {
LogError("Unknown exception raised in the APRS Writer thread");
}
LogMessage("Stopping the APRS Writer thread");
}
void CAPRSWriterThread::setReadAPRSCallback(ReadAPRSFrameCallback cb)
{
m_APRSReadCallback = cb;
}
void CAPRSWriterThread::write(const char* data)
{
assert(data != NULL);
if (!m_connected)
return;
unsigned int len = ::strlen(data);
char* p = new char[len + 5U];
::strcpy(p, data);
m_queue.addData(&p, 1U);
}
bool CAPRSWriterThread::isConnected() const
{
return m_connected;
}
void CAPRSWriterThread::stop()
{
m_exit = true;
wait();
}
bool CAPRSWriterThread::connect()
{
bool ret = m_socket.open();
if (!ret)
return false;
//wait for lgin banner
int length;
std::string serverResponse;
length = m_socket.readLine(serverResponse, APRS_TIMEOUT);
if (length == 0) {
LogError("No reply from the APRS server after %u seconds", APRS_TIMEOUT);
m_socket.close();
return false;
}
LogMessage("Received login banner : %s", serverResponse.c_str());
std::string filter(m_filter);
if (filter.length() > 0)
filter.insert(0U, " filter ");
char connectString[200U];
::sprintf(connectString, "user %s pass %s vers %s%s\n", m_username.c_str(), m_password.c_str(), (m_clientName.length() ? m_clientName : "YSFGateway").c_str(), filter.c_str());
ret = m_socket.writeLine(std::string(connectString));
if (!ret) {
m_socket.close();
return false;
}
length = m_socket.readLine(serverResponse, APRS_TIMEOUT);
if (length == 0) {
LogError("No reply from the APRS server after %u seconds", APRS_TIMEOUT);
m_socket.close();
return false;
}
if (length < 0) {
LogError("Error when reading from the APRS server");
m_socket.close();
return false;
}
LogMessage("Response from APRS server: %s", serverResponse.c_str());
LogMessage("Connected to the APRS server");
return true;
}

62
APRSWriterThread.h Normal file
View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2010,2011,2012,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 APRSWriterThread_H
#define APRSWriterThread_H
#include "TCPSocket.h"
#include "RingBuffer.h"
#include "Thread.h"
#include <string>
typedef void (*ReadAPRSFrameCallback)(const std::string&);
class CAPRSWriterThread : public CThread {
public:
CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, unsigned int port);
CAPRSWriterThread(const std::string& callsign, const std::string& password, const std::string& address, unsigned int port, const std::string& filter, const std::string& clientName);
virtual ~CAPRSWriterThread();
virtual bool start();
virtual bool isConnected() const;
virtual void write(const char* data);
virtual void entry();
virtual void stop();
void setReadAPRSCallback(ReadAPRSFrameCallback cb);
private:
std::string m_username;
std::string m_password;
CTCPSocket m_socket;
CRingBuffer<char*> m_queue;
bool m_exit;
bool m_connected;
ReadAPRSFrameCallback m_APRSReadCallback;
std::string m_filter;
std::string m_clientName;
bool connect();
};
#endif

View file

@ -30,6 +30,8 @@ enum SECTION {
SECTION_NONE,
SECTION_GENERAL,
SECTION_LOG,
SECTION_DMRID_LOOKUP,
SECTION_APRS_FI,
SECTION_VOICE,
SECTION_DMR_NETWORK_1,
SECTION_DMR_NETWORK_2,
@ -53,6 +55,12 @@ m_logDisplayLevel(0U),
m_logFileLevel(0U),
m_logFilePath(),
m_logFileRoot(),
m_dmrIdLookupFile(),
m_dmrIdLookupTime(0U),
m_aprsEnabled(false),
m_aprsServer(),
m_aprsPort(0U),
m_aprsPassword(),
m_dmrNetwork1Enabled(false),
m_dmrNetwork1Id(0U),
m_dmrNetwork1Address(),
@ -132,6 +140,10 @@ bool CConf::read()
section = SECTION_GENERAL;
else if (::strncmp(buffer, "[Log]", 5U) == 0)
section = SECTION_LOG;
else if (::strncmp(buffer, "[DMR Id Lookup]", 15U) == 0)
section = SECTION_DMRID_LOOKUP;
else if (::strncmp(buffer, "[aprs.fi]", 5U) == 0)
section = SECTION_APRS_FI;
else if (::strncmp(buffer, "[Voice]", 7U) == 0)
section = SECTION_VOICE;
else if (::strncmp(buffer, "[XLX Network 1]", 15U) == 0)
@ -159,7 +171,17 @@ bool CConf::read()
if (section == SECTION_GENERAL) {
if (::strcmp(key, "Daemon") == 0)
m_daemon = ::atoi(value) == 1;
else if (::strcmp(key, "Timeout") == 0)
if (::strcmp(key, "Callsign") == 0) {
// Convert the callsign to upper case
for (unsigned int i = 0U; value[i] != 0; i++)
value[i] = ::toupper(value[i]);
m_callsign = value;
} else if (::strcmp(key, "Suffix") == 0) {
// Convert the callsign to upper case
for (unsigned int i = 0U; value[i] != 0; i++)
value[i] = ::toupper(value[i]);
m_suffix = value;
}else if (::strcmp(key, "Timeout") == 0)
m_timeout = (unsigned int)::atoi(value);
else if (::strcmp(key, "RptAddress") == 0)
m_rptAddress = value;
@ -180,6 +202,20 @@ bool CConf::read()
m_logFileLevel = (unsigned int)::atoi(value);
else if (::strcmp(key, "DisplayLevel") == 0)
m_logDisplayLevel = (unsigned int)::atoi(value);
} else if (section == SECTION_DMRID_LOOKUP) {
if (::strcmp(key, "File") == 0)
m_dmrIdLookupFile = value;
else if (::strcmp(key, "Time") == 0)
m_dmrIdLookupTime = (unsigned int)::atoi(value);
} else if (section == SECTION_APRS_FI) {
if (::strcmp(key, "Enable") == 0)
m_aprsEnabled = ::atoi(value) == 1;
else if (::strcmp(key, "Server") == 0)
m_aprsServer = value;
else if (::strcmp(key, "Port") == 0)
m_aprsPort = (unsigned int)::atoi(value);
else if (::strcmp(key, "Password") == 0)
m_aprsPassword = value;
} else if (section == SECTION_VOICE) {
if (::strcmp(key, "Enabled") == 0)
m_voiceEnabled = ::atoi(value) == 1;
@ -409,11 +445,22 @@ bool CConf::read()
return true;
}
std::string CConf::getCallsign() const
{
return m_callsign;
}
std::string CConf::getSuffix() const
{
return m_suffix;
}
bool CConf::getDaemon() const
{
return m_daemon;
}
std::string CConf::getRptAddress() const
{
return m_rptAddress;
@ -464,6 +511,37 @@ std::string CConf::getLogFileRoot() const
return m_logFileRoot;
}
std::string CConf::getDMRIdLookupFile() const
{
return m_dmrIdLookupFile;
}
unsigned int CConf::getDMRIdLookupTime() const
{
return m_dmrIdLookupTime;
}
bool CConf::getAPRSEnabled() const
{
return m_aprsEnabled;
}
std::string CConf::getAPRSServer() const
{
return m_aprsServer;
}
unsigned int CConf::getAPRSPort() const
{
return m_aprsPort;
}
std::string CConf::getAPRSPassword() const
{
return m_aprsPassword;
}
bool CConf::getVoiceEnabled() const
{
return m_voiceEnabled;

23
Conf.h
View file

@ -62,6 +62,8 @@ public:
bool read();
// The General section
std::string getCallsign() const;
std::string getSuffix() const;
bool getDaemon() const;
unsigned int getTimeout() const;
std::string getRptAddress() const;
@ -76,6 +78,17 @@ public:
std::string getLogFilePath() const;
std::string getLogFileRoot() const;
// The DMR Id section
std::string getDMRIdLookupFile() const;
unsigned int getDMRIdLookupTime() const;
// The aprs.fi section
bool getAPRSEnabled() const;
std::string getAPRSServer() const;
unsigned int getAPRSPort() const;
std::string getAPRSPassword() const;
// The Voice section
bool getVoiceEnabled() const;
std::string getVoiceLanguage() const;
@ -143,6 +156,8 @@ public:
private:
std::string m_file;
std:: string m_callsign;
std:: string m_suffix;
bool m_daemon;
std::string m_rptAddress;
unsigned int m_rptPort;
@ -160,6 +175,14 @@ private:
std::string m_logFilePath;
std::string m_logFileRoot;
std::string m_dmrIdLookupFile;
unsigned int m_dmrIdLookupTime;
bool m_aprsEnabled;
std::string m_aprsServer;
unsigned int m_aprsPort;
std::string m_aprsPassword;
bool m_dmrNetwork1Enabled;
unsigned int m_dmrNetwork1Id;
std::string m_dmrNetwork1Address;

View file

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* MD-390/RT8 Modifications (C) 2017, John Ronan, EI7IG
*
* 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
@ -32,6 +33,9 @@
#include "Sync.h"
#include "Log.h"
#include "GitVersion.h"
#include "BPTC19696.h"
#include "APRSHelper.h"
#include "DMRLookup.h"
#include <cstdio>
#include <vector>
@ -58,6 +62,8 @@ const unsigned char COLOR_CODE = 3U;
static bool m_killed = false;
static int m_signal = 0;
CDMRLookup* m_lookup = NULL;
#if !defined(_WIN32) && !defined(_WIN64)
static void sigHandler(int signum)
{
@ -126,7 +132,9 @@ int main(int argc, char** argv)
}
CDMRGateway::CDMRGateway(const std::string& confFile) :
m_callsign(),
m_conf(confFile),
m_aprsHelper(NULL),
m_repeater(NULL),
m_dmrNetwork1(NULL),
m_dmrNetwork2(NULL),
@ -153,7 +161,8 @@ m_xlx2Rewrite(NULL),
m_dmr1NetRewrites(),
m_dmr1RFRewrites(),
m_dmr2NetRewrites(),
m_dmr2RFRewrites()
m_dmr2RFRewrites(),
m_lookup(NULL)
{
}
@ -250,6 +259,8 @@ int CDMRGateway::run()
}
}
#endif
m_callsign = m_conf.getCallsign();
m_suffix = m_conf.getSuffix();
LogInfo(HEADER1);
LogInfo(HEADER2);
@ -258,7 +269,21 @@ int CDMRGateway::run()
LogMessage("DMRGateway-%s is starting", VERSION);
LogMessage("Built %s %s (GitID #%.7s)", __TIME__, __DATE__, gitversion);
CDMRGateway::createAPRSHelper();
// For DMR we try to map IDs to callsigns
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();
ret = createMMDVM();
if (!ret)
return 1;
@ -402,6 +427,8 @@ int CDMRGateway::run()
bool ret = m_repeater->read(data);
if (ret) {
extractGPSData(data);
unsigned int slotNo = data.getSlotNo();
unsigned int srcId = data.getSrcId();
unsigned int dstId = data.getDstId();
@ -728,6 +755,12 @@ int CDMRGateway::run()
delete timer[1U];
delete timer[2U];
if (m_aprsHelper != NULL)
m_aprsHelper->close();
if (m_lookup != NULL)
m_lookup->stop();
return 0;
}
@ -1143,3 +1176,74 @@ void CDMRGateway::writeXLXLink(unsigned int srcId, unsigned int dstId, CDMRNetwo
network->write(data);
}
}
void CDMRGateway::createAPRSHelper()
{
if (!m_conf.getAPRSEnabled())
return;
std::string hostname = m_conf.getAPRSServer();
unsigned int port = m_conf.getAPRSPort();
std::string password = m_conf.getAPRSPassword();
m_aprsHelper = new CAPRSHelper(m_callsign, m_suffix, password, hostname, port);
bool ret = m_aprsHelper->open();
if (!ret) {
delete m_aprsHelper;
m_aprsHelper = NULL;
}
}
void CDMRGateway::extractGPSData(const CDMRData& data)
{
unsigned int slotNo = data.getSlotNo();
unsigned int srcId = data.getSrcId();
unsigned int dstId = data.getDstId();
FLCO flco = data.getFLCO();
unsigned char type = data.getDataType();
if (type == DT_RATE_12_DATA) {
int8_t latSign = 0;
int8_t longSign = 0;
LogDebug("1/2 Rate Header slot %d: src is %u, dest is %s%u", slotNo, srcId, flco == FLCO_GROUP ? "TG" : "", dstId);
std::string src = m_lookup->find(srcId);
CBPTC19696 bptc;
unsigned char buffer[DMR_FRAME_LENGTH_BYTES];
data.getData(buffer);
unsigned char payload[12U];
bptc.decode(buffer, payload);
// Have we a GPS fix
if ((payload[0U] & 0x10U) >> 4) {
if ((payload[0U] & 0x40U) >> 6)
latSign = 1;
else
latSign = -1;
if ((payload[0U] & 0x20U) >> 5)
longSign = 1;
else
longSign = -1;
uint8_t latDeg = ((payload[1U] & 0x1F) << 2) + ((payload[2U] & 0xC0) >> 6);
uint8_t latMin = payload[2U] & 0x3F;
float latSec = (payload[3U] << 6) + (payload[4U] & 0xFC);
uint8_t lonDeg = ((payload[4U] & 0x03) << 6) + ((payload[5U] & 0xFC) >> 2);
uint8_t lonMin = ((payload[5U] & 0x03) << 4) + ((payload[6U] & 0xF0) >> 4);
float lonSec = ((payload[6U] & 0x0F) << 10) + (payload[7U] << 2) + ((payload[8U] & 0xC0) >> 6);
int altitude = (((payload[8U] & 0x3F) << 8) + payload[9U]);
float latitude = latDeg + (latMin + latSec / 10000) / 60;
float longitude = lonDeg + (lonMin + lonSec / 10000) / 60;
LogDebug("Sending GPS position from %d", srcId);
m_aprsHelper->send(src.c_str(), latitude * latSign, longitude * longSign, altitude);
}
}
}

View file

@ -25,6 +25,8 @@
#include "RewriteTG.h"
#include "Rewrite.h"
#include "Conf.h"
#include "DMRLookup.h"
#include "APRSHelper.h"
#include <string>
@ -37,7 +39,11 @@ public:
int run();
private:
std::string m_callsign;
std::string m_suffix;
CConf m_conf;
CAPRSHelper* m_aprsHelper;
IRepeaterProtocol* m_repeater;
CDMRNetwork* m_dmrNetwork1;
CDMRNetwork* m_dmrNetwork2;
@ -65,6 +71,7 @@ private:
std::vector<IRewrite*> m_dmr1RFRewrites;
std::vector<IRewrite*> m_dmr2NetRewrites;
std::vector<IRewrite*> m_dmr2RFRewrites;
CDMRLookup* m_lookup;
bool createMMDVM();
bool createDMRNetwork1();
@ -72,6 +79,8 @@ private:
bool createXLXNetwork1();
bool createXLXNetwork2();
void writeXLXLink(unsigned int srcId, unsigned int dstId, CDMRNetwork* network);
void extractGPSData(const CDMRData& data);
void createAPRSHelper();
};
#endif

View file

@ -1,11 +1,12 @@
[General]
Callsign=G9BF-DG
Timeout=10
RptAddress=127.0.0.1
RptPort=62032
LocalAddress=127.0.0.1
LocalPort=62031
Daemon=0
Debug=0
Debug=1
[Log]
# Logging levels, 0=No logging
@ -14,6 +15,19 @@ FileLevel=1
FilePath=.
FileRoot=DMRGateway
[aprs.fi]
Enable=1
# Server=noam.aprs2.net
Server=rotate.aprs2.net
Port=14580
Password=12345
[DMR Id Lookup]
File=DMRIds.dat
Time=24
[Voice]
Enabled=1
Language=en_GB

View file

@ -152,6 +152,9 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="APRSHelper.h" />
<ClInclude Include="APRSWriter.h" />
<ClInclude Include="APRSWriterThread.h" />
<ClInclude Include="BPTC19696.h" />
<ClInclude Include="Conf.h" />
<ClInclude Include="CRC.h" />
@ -162,12 +165,14 @@
<ClInclude Include="DMRFullLC.h" />
<ClInclude Include="DMRGateway.h" />
<ClInclude Include="DMRLC.h" />
<ClInclude Include="DMRLookup.h" />
<ClInclude Include="DMRNetwork.h" />
<ClInclude Include="DMRSlotType.h" />
<ClInclude Include="Golay2087.h" />
<ClInclude Include="Hamming.h" />
<ClInclude Include="Log.h" />
<ClInclude Include="MMDVMNetwork.h" />
<ClInclude Include="Mutex.h" />
<ClInclude Include="PassAllPC.h" />
<ClInclude Include="PassAllTG.h" />
<ClInclude Include="QR1676.h" />
@ -182,6 +187,7 @@
<ClInclude Include="SHA256.h" />
<ClInclude Include="StopWatch.h" />
<ClInclude Include="Sync.h" />
<ClInclude Include="TCPSocket.h" />
<ClInclude Include="Thread.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="UDPSocket.h" />
@ -190,6 +196,9 @@
<ClInclude Include="Voice.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="APRSHelper.cpp" />
<ClCompile Include="APRSWriter.cpp" />
<ClCompile Include="APRSWriterThread.cpp" />
<ClCompile Include="BPTC19696.cpp" />
<ClCompile Include="Conf.cpp" />
<ClCompile Include="CRC.cpp" />
@ -199,12 +208,14 @@
<ClCompile Include="DMRFullLC.cpp" />
<ClCompile Include="DMRGateway.cpp" />
<ClCompile Include="DMRLC.cpp" />
<ClCompile Include="DMRLookup.cpp" />
<ClCompile Include="DMRNetwork.cpp" />
<ClCompile Include="DMRSlotType.cpp" />
<ClCompile Include="Golay2087.cpp" />
<ClCompile Include="Hamming.cpp" />
<ClCompile Include="Log.cpp" />
<ClCompile Include="MMDVMNetwork.cpp" />
<ClCompile Include="Mutex.cpp" />
<ClCompile Include="PassAllPC.cpp" />
<ClCompile Include="PassAllTG.cpp" />
<ClCompile Include="QR1676.cpp" />
@ -218,6 +229,7 @@
<ClCompile Include="SHA256.cpp" />
<ClCompile Include="StopWatch.cpp" />
<ClCompile Include="Sync.cpp" />
<ClCompile Include="TCPSocket.cpp" />
<ClCompile Include="Thread.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="UDPSocket.cpp" />

View file

@ -119,6 +119,24 @@
<ClInclude Include="PassAllTG.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="APRSHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="APRSWriter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="APRSWriterThread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Mutex.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TCPSocket.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRLookup.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Conf.cpp">
@ -220,5 +238,23 @@
<ClCompile Include="PassAllTG.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="APRSHelper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="APRSWriter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="APRSWriterThread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Mutex.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TCPSocket.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRLookup.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

60258
DMRIds.dat Normal file

File diff suppressed because it is too large Load diff

147
DMRLookup.cpp Normal file
View file

@ -0,0 +1,147 @@
/*
* 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 "DMRLookup.h"
#include "Timer.h"
#include "Log.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
CDMRLookup::CDMRLookup(const std::string& filename, unsigned int reloadTime) :
CThread(),
m_filename(filename),
m_reloadTime(reloadTime),
m_table(),
m_mutex(),
m_stop(false)
{
}
CDMRLookup::~CDMRLookup()
{
}
bool CDMRLookup::read()
{
bool ret = load();
if (m_reloadTime > 0U)
run();
return ret;
}
void CDMRLookup::entry()
{
LogInfo("Started the DMR Id lookup reload thread");
CTimer timer(1U, 3600U * m_reloadTime);
timer.start();
while (!m_stop) {
sleep(1000U);
timer.clock();
if (timer.hasExpired()) {
load();
timer.start();
}
}
LogInfo("Stopped the DMR Id lookup reload thread");
}
void CDMRLookup::stop()
{
if (m_reloadTime == 0U) {
delete this;
return;
}
m_stop = true;
wait();
}
std::string CDMRLookup::find(unsigned int id)
{
std::string callsign;
if (id == 0xFFFFFFU)
return std::string("ALL");
m_mutex.lock();
try {
callsign = m_table.at(id);
} catch (...) {
char text[10U];
::sprintf(text, "%u", id);
callsign = std::string(text);
}
m_mutex.unlock();
return callsign;
}
bool CDMRLookup::load()
{
FILE* fp = ::fopen(m_filename.c_str(), "rt");
if (fp == NULL) {
LogWarning("Cannot open the Id lookup file - %s", m_filename.c_str());
return false;
}
m_mutex.lock();
// Remove the old entries
m_table.clear();
char buffer[100U];
while (::fgets(buffer, 100U, 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) {
unsigned int id = (unsigned int)::atoi(p1);
for (char* p = p2; *p != 0x00U; p++)
*p = ::toupper(*p);
m_table[id] = std::string(p2);
}
}
m_mutex.unlock();
::fclose(fp);
size_t size = m_table.size();
if (size == 0U)
return false;
LogInfo("Loaded %u Ids to the callsign lookup table", size);
return true;
}

51
DMRLookup.h Normal file
View file

@ -0,0 +1,51 @@
/*
* 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 DMRLookup_H
#define DMRLookup_H
#include "Thread.h"
#include "Mutex.h"
#include <string>
#include <unordered_map>
class CDMRLookup : public CThread {
public:
CDMRLookup(const std::string& filename, unsigned int reloadTime);
virtual ~CDMRLookup();
bool read();
virtual void entry();
std::string find(unsigned int id);
void stop();
private:
std::string m_filename;
unsigned int m_reloadTime;
std::unordered_map<unsigned int, std::string> m_table;
CMutex m_mutex;
bool m_stop;
bool load();
};
#endif

View file

@ -4,9 +4,9 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread
LIBS = -lpthread
LDFLAGS = -g
OBJECTS = BPTC19696.o Conf.o CRC.o DMRData.o DMREmbeddedData.o DMREMB.o DMRFullLC.o DMRGateway.o DMRLC.o DMRNetwork.o DMRSlotType.o Golay2087.o Hamming.o Log.o \
MMDVMNetwork.o PassAllPC.o PassAllTG.o QR1676.o RepeaterProtocol.o Rewrite.o RewritePC.o RewriteSrc.o RewriteTG.o RewriteType.o RS129.o SHA256.o StopWatch.o \
Sync.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o
OBJECTS = APRSWriter.o APRSWriterThread.o APRSHelper.o BPTC19696.o Conf.o CRC.o DMRData.o DMREmbeddedData.o DMREMB.o DMRFullLC.o DMRGateway.o DMRLC.o DMRLookup.o \
DMRNetwork.o DMRSlotType.o Golay2087.o Hamming.o Log.o MMDVMNetwork.o Mutex.o PassAllPC.o PassAllTG.o QR1676.o RepeaterProtocol.o Rewrite.o RewritePC.o \
RewriteSrc.o RewriteTG.o RewriteType.o RS129.o SHA256.o StopWatch.o Sync.o TCPSocket.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o
all: DMRGateway

65
Mutex.cpp Normal file
View file

@ -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

45
Mutex.h Normal file
View file

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

232
TCPSocket.cpp Normal file
View file

@ -0,0 +1,232 @@
/*
* Copyright (C) 2010-2013,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 "TCPSocket.h"
#include "UDPSocket.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#if defined(_WIN32) || defined(_WIN64)
typedef int ssize_t;
#else
#include <cerrno>
#endif
CTCPSocket::CTCPSocket(const std::string& address, unsigned int port) :
m_address(address),
m_port(port),
m_fd(-1)
{
assert(!address.empty());
assert(port > 0U);
#if defined(_WIN32) || defined(_WIN64)
WSAData data;
int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data);
if (wsaRet != 0)
LogError("Error from WSAStartup");
#endif
}
CTCPSocket::~CTCPSocket()
{
#if defined(_WIN32) || defined(_WIN64)
::WSACleanup();
#endif
}
bool CTCPSocket::open()
{
if (m_fd != -1)
return true;
if (m_address.empty() || m_port == 0U)
return false;
m_fd = ::socket(PF_INET, SOCK_STREAM, 0);
if (m_fd < 0) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Cannot create the TCP client socket, err=%d", ::GetLastError());
#else
LogError("Cannot create the TCP client socket, err=%d", errno);
#endif
return false;
}
struct sockaddr_in addr;
::memset(&addr, 0x00, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(m_port);
addr.sin_addr = CUDPSocket::lookup(m_address);
if (addr.sin_addr.s_addr == INADDR_NONE) {
close();
return false;
}
if (::connect(m_fd, (sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Cannot connect the TCP client socket, err=%d", ::GetLastError());
#else
LogError("Cannot connect the TCP client socket, err=%d", errno);
#endif
close();
return false;
}
int noDelay = 1;
if (::setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&noDelay, sizeof(noDelay)) == -1) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Cannot set the TCP client socket option, err=%d", ::GetLastError());
#else
LogError("Cannot set the TCP client socket option, err=%d", errno);
#endif
close();
return false;
}
return true;
}
int CTCPSocket::read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs)
{
assert(buffer != NULL);
assert(length > 0U);
assert(m_fd != -1);
// Check that the recv() won't block
fd_set readFds;
FD_ZERO(&readFds);
#if defined(_WIN32) || defined(_WIN64)
FD_SET((unsigned int)m_fd, &readFds);
#else
FD_SET(m_fd, &readFds);
#endif
// Return after timeout
timeval tv;
tv.tv_sec = secs;
tv.tv_usec = msecs * 1000;
int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv);
if (ret < 0) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Error returned from TCP client select, err=%d", ::GetLastError());
#else
LogError("Error returned from TCP client select, err=%d", errno);
#endif
return -1;
}
#if defined(_WIN32) || defined(_WIN64)
if (!FD_ISSET((unsigned int)m_fd, &readFds))
return 0;
#else
if (!FD_ISSET(m_fd, &readFds))
return 0;
#endif
ssize_t len = ::recv(m_fd, (char*)buffer, length, 0);
if (len == 0) {
return -2;
} else if (len < 0) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Error returned from recv, err=%d", ::GetLastError());
#else
LogError("Error returned from recv, err=%d", errno);
#endif
return -1;
}
return len;
}
int CTCPSocket::readLine(std::string& line, unsigned int secs)
{
// Maybe there is a better way to do this like reading blocks, pushing them for later calls
// Nevermind, we'll read one char at a time for the time being.
int resultCode;
int len = 0;
line.clear();
char c[2U];
c[1U] = 0x00U;
do
{
resultCode = read((unsigned char*)c, 1U, secs);
if (resultCode == 1){
line.append(c);
len++;
}
} while (c[0U] != '\n' && resultCode == 1);
return resultCode <= 0 ? resultCode : len;
}
bool CTCPSocket::write(const unsigned char* buffer, unsigned int length)
{
assert(buffer != NULL);
assert(length > 0U);
assert(m_fd != -1);
ssize_t ret = ::send(m_fd, (char *)buffer, length, 0);
if (ret != ssize_t(length)) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Error returned from send, err=%d", ::GetLastError());
#else
LogError("Error returned from send, err=%d", errno);
#endif
return false;
}
return true;
}
bool CTCPSocket::writeLine(const std::string& line)
{
std::string lineCopy(line);
if (lineCopy.length() > 0 && lineCopy.at(lineCopy.length() - 1) != '\n')
lineCopy.append("\n");
//stupidly write one char after the other
size_t len = lineCopy.length();
bool result = true;
for (size_t i = 0U; i < len && result; i++){
unsigned char c = lineCopy.at(i);
result = write(&c , 1);
}
return result;
}
void CTCPSocket::close()
{
if (m_fd != -1) {
#if defined(_WIN32) || defined(_WIN64)
::closesocket(m_fd);
#else
::close(m_fd);
#endif
m_fd = -1;
}
}

58
TCPSocket.h Normal file
View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2010,2011,2012,2013,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 TCPSocket_H
#define TCPSocket_H
#if !defined(_WIN32) && !defined(_WIN64)
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#else
#include <winsock.h>
#endif
#include <string>
class CTCPSocket {
public:
CTCPSocket(const std::string& address, unsigned int port);
~CTCPSocket();
bool open();
int read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs = 0U);
int readLine(std::string& line, unsigned int secs);
bool write(const unsigned char* buffer, unsigned int length);
bool writeLine(const std::string& line);
void close();
private:
std::string m_address;
unsigned short m_port;
int m_fd;
};
#endif