First attempts at NXDN GPS to aprs.fi.
This commit is contained in:
parent
4d09647a7e
commit
9c3673687d
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-2014,2016,2017,2018 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),
|
||||||
|
m_desc()
|
||||||
|
{
|
||||||
|
assert(!callsign.empty());
|
||||||
|
assert(!password.empty());
|
||||||
|
assert(!address.empty());
|
||||||
|
assert(port > 0U);
|
||||||
|
|
||||||
|
if (!suffix.empty()) {
|
||||||
|
m_callsign.append("-");
|
||||||
|
m_callsign.append(suffix.substr(0U, 1U));
|
||||||
|
}
|
||||||
|
|
||||||
|
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, const std::string& desc)
|
||||||
|
{
|
||||||
|
m_txFrequency = txFrequency;
|
||||||
|
m_rxFrequency = rxFrequency;
|
||||||
|
m_latitude = latitude;
|
||||||
|
m_longitude = longitude;
|
||||||
|
m_height = height;
|
||||||
|
m_desc = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAPRSWriter::open()
|
||||||
|
{
|
||||||
|
m_idTimer.start();
|
||||||
|
return m_thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAPRSWriter::write(const char* data)
|
||||||
|
{
|
||||||
|
assert(data != NULL);
|
||||||
|
|
||||||
|
m_thread->write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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[200U];
|
||||||
|
if (m_txFrequency != 0U) {
|
||||||
|
float offset = float(int(m_rxFrequency) - int(m_txFrequency)) / 1000000.0F;
|
||||||
|
::sprintf(desc, "MMDVM Voice %.5LfMHz %c%.4lfMHz%s%s",
|
||||||
|
(long double)(m_txFrequency) / 1000000.0F,
|
||||||
|
offset < 0.0F ? '-' : '+',
|
||||||
|
::fabs(offset), m_desc.empty() ? "" : ", ", m_desc.c_str());
|
||||||
|
} else {
|
||||||
|
::sprintf(desc, "MMDVM Voice%s%s", m_desc.empty() ? "" : ", ", m_desc.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
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, "%07.2lf", latitude);
|
||||||
|
|
||||||
|
char lon[20U];
|
||||||
|
::sprintf(lon, "%08.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>APDG04,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();
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010,2011,2012,2016,2017,2018 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, const std::string& desc);
|
||||||
|
|
||||||
|
void write(const char* data);
|
||||||
|
|
||||||
|
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;
|
||||||
|
std::string m_desc;
|
||||||
|
|
||||||
|
void sendIdFrames();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -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("YSFGateway")
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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
|
|
@ -29,20 +29,31 @@ const int BUFFER_SIZE = 500;
|
||||||
enum SECTION {
|
enum SECTION {
|
||||||
SECTION_NONE,
|
SECTION_NONE,
|
||||||
SECTION_GENERAL,
|
SECTION_GENERAL,
|
||||||
|
SECTION_INFO,
|
||||||
SECTION_ID_LOOKUP,
|
SECTION_ID_LOOKUP,
|
||||||
SECTION_VOICE,
|
SECTION_VOICE,
|
||||||
SECTION_LOG,
|
SECTION_LOG,
|
||||||
|
SECTION_APRS_FI,
|
||||||
SECTION_NETWORK
|
SECTION_NETWORK
|
||||||
};
|
};
|
||||||
|
|
||||||
CConf::CConf(const std::string& file) :
|
CConf::CConf(const std::string& file) :
|
||||||
m_file(file),
|
m_file(file),
|
||||||
m_callsign(),
|
m_callsign(),
|
||||||
|
m_suffix(),
|
||||||
m_rptAddress(),
|
m_rptAddress(),
|
||||||
m_rptPort(0U),
|
m_rptPort(0U),
|
||||||
m_myPort(0U),
|
m_myPort(0U),
|
||||||
m_rptDebug(false),
|
m_rptDebug(false),
|
||||||
m_daemon(false),
|
m_daemon(false),
|
||||||
|
m_rxFrequency(0U),
|
||||||
|
m_txFrequency(0U),
|
||||||
|
m_power(0U),
|
||||||
|
m_latitude(0.0F),
|
||||||
|
m_longitude(0.0F),
|
||||||
|
m_height(0),
|
||||||
|
m_name(),
|
||||||
|
m_description(),
|
||||||
m_lookupName(),
|
m_lookupName(),
|
||||||
m_lookupTime(0U),
|
m_lookupTime(0U),
|
||||||
m_voiceEnabled(true),
|
m_voiceEnabled(true),
|
||||||
|
@ -50,6 +61,11 @@ m_voiceLanguage("en_GB"),
|
||||||
m_voiceDirectory(),
|
m_voiceDirectory(),
|
||||||
m_logFilePath(),
|
m_logFilePath(),
|
||||||
m_logFileRoot(),
|
m_logFileRoot(),
|
||||||
|
m_aprsEnabled(false),
|
||||||
|
m_aprsServer(),
|
||||||
|
m_aprsPort(0U),
|
||||||
|
m_aprsPassword(),
|
||||||
|
m_aprsDescription(),
|
||||||
m_networkPort(0U),
|
m_networkPort(0U),
|
||||||
m_networkHosts(),
|
m_networkHosts(),
|
||||||
m_networkReloadTime(0U),
|
m_networkReloadTime(0U),
|
||||||
|
@ -85,12 +101,16 @@ bool CConf::read()
|
||||||
if (buffer[0U] == '[') {
|
if (buffer[0U] == '[') {
|
||||||
if (::strncmp(buffer, "[General]", 9U) == 0)
|
if (::strncmp(buffer, "[General]", 9U) == 0)
|
||||||
section = SECTION_GENERAL;
|
section = SECTION_GENERAL;
|
||||||
|
else if (::strncmp(buffer, "[Info]", 6U) == 0)
|
||||||
|
section = SECTION_INFO;
|
||||||
else if (::strncmp(buffer, "[Id Lookup]", 11U) == 0)
|
else if (::strncmp(buffer, "[Id Lookup]", 11U) == 0)
|
||||||
section = SECTION_ID_LOOKUP;
|
section = SECTION_ID_LOOKUP;
|
||||||
else if (::strncmp(buffer, "[Voice]", 7U) == 0)
|
else if (::strncmp(buffer, "[Voice]", 7U) == 0)
|
||||||
section = SECTION_VOICE;
|
section = SECTION_VOICE;
|
||||||
else if (::strncmp(buffer, "[Log]", 5U) == 0)
|
else if (::strncmp(buffer, "[Log]", 5U) == 0)
|
||||||
section = SECTION_LOG;
|
section = SECTION_LOG;
|
||||||
|
else if (::strncmp(buffer, "[aprs.fi]", 9U) == 0)
|
||||||
|
section = SECTION_APRS_FI;
|
||||||
else if (::strncmp(buffer, "[Network]", 9U) == 0)
|
else if (::strncmp(buffer, "[Network]", 9U) == 0)
|
||||||
section = SECTION_NETWORK;
|
section = SECTION_NETWORK;
|
||||||
else
|
else
|
||||||
|
@ -110,6 +130,11 @@ bool CConf::read()
|
||||||
for (unsigned int i = 0U; value[i] != 0; i++)
|
for (unsigned int i = 0U; value[i] != 0; i++)
|
||||||
value[i] = ::toupper(value[i]);
|
value[i] = ::toupper(value[i]);
|
||||||
m_callsign = value;
|
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, "RptAddress") == 0)
|
} else if (::strcmp(key, "RptAddress") == 0)
|
||||||
m_rptAddress = value;
|
m_rptAddress = value;
|
||||||
else if (::strcmp(key, "RptPort") == 0)
|
else if (::strcmp(key, "RptPort") == 0)
|
||||||
|
@ -120,6 +145,23 @@ bool CConf::read()
|
||||||
m_rptDebug = ::atoi(value) == 1;
|
m_rptDebug = ::atoi(value) == 1;
|
||||||
else if (::strcmp(key, "Daemon") == 0)
|
else if (::strcmp(key, "Daemon") == 0)
|
||||||
m_daemon = ::atoi(value) == 1;
|
m_daemon = ::atoi(value) == 1;
|
||||||
|
} else if (section == SECTION_INFO) {
|
||||||
|
if (::strcmp(key, "TXFrequency") == 0)
|
||||||
|
m_txFrequency = (unsigned int)::atoi(value);
|
||||||
|
else if (::strcmp(key, "RXFrequency") == 0)
|
||||||
|
m_rxFrequency = (unsigned int)::atoi(value);
|
||||||
|
else if (::strcmp(key, "Power") == 0)
|
||||||
|
m_power = (unsigned int)::atoi(value);
|
||||||
|
else if (::strcmp(key, "Latitude") == 0)
|
||||||
|
m_latitude = float(::atof(value));
|
||||||
|
else if (::strcmp(key, "Longitude") == 0)
|
||||||
|
m_longitude = float(::atof(value));
|
||||||
|
else if (::strcmp(key, "Height") == 0)
|
||||||
|
m_height = ::atoi(value);
|
||||||
|
else if (::strcmp(key, "Name") == 0)
|
||||||
|
m_name = value;
|
||||||
|
else if (::strcmp(key, "Description") == 0)
|
||||||
|
m_description = value;
|
||||||
} else if (section == SECTION_ID_LOOKUP) {
|
} else if (section == SECTION_ID_LOOKUP) {
|
||||||
if (::strcmp(key, "Name") == 0)
|
if (::strcmp(key, "Name") == 0)
|
||||||
m_lookupName = value;
|
m_lookupName = value;
|
||||||
|
@ -137,6 +179,17 @@ bool CConf::read()
|
||||||
m_logFilePath = value;
|
m_logFilePath = value;
|
||||||
else if (::strcmp(key, "FileRoot") == 0)
|
else if (::strcmp(key, "FileRoot") == 0)
|
||||||
m_logFileRoot = value;
|
m_logFileRoot = 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 (::strcmp(key, "Description") == 0)
|
||||||
|
m_aprsDescription = value;
|
||||||
} else if (section == SECTION_NETWORK) {
|
} else if (section == SECTION_NETWORK) {
|
||||||
if (::strcmp(key, "Port") == 0)
|
if (::strcmp(key, "Port") == 0)
|
||||||
m_networkPort = (unsigned int)::atoi(value);
|
m_networkPort = (unsigned int)::atoi(value);
|
||||||
|
@ -171,6 +224,11 @@ std::string CConf::getCallsign() const
|
||||||
return m_callsign;
|
return m_callsign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string CConf::getSuffix() const
|
||||||
|
{
|
||||||
|
return m_suffix;
|
||||||
|
}
|
||||||
|
|
||||||
std::string CConf::getRptAddress() const
|
std::string CConf::getRptAddress() const
|
||||||
{
|
{
|
||||||
return m_rptAddress;
|
return m_rptAddress;
|
||||||
|
@ -196,6 +254,46 @@ bool CConf::getDaemon() const
|
||||||
return m_daemon;
|
return m_daemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int CConf::getRxFrequency() const
|
||||||
|
{
|
||||||
|
return m_rxFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int CConf::getTxFrequency() const
|
||||||
|
{
|
||||||
|
return m_txFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int CConf::getPower() const
|
||||||
|
{
|
||||||
|
return m_power;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CConf::getLatitude() const
|
||||||
|
{
|
||||||
|
return m_latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CConf::getLongitude() const
|
||||||
|
{
|
||||||
|
return m_longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CConf::getHeight() const
|
||||||
|
{
|
||||||
|
return m_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CConf::getName() const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CConf::getDescription() const
|
||||||
|
{
|
||||||
|
return m_description;
|
||||||
|
}
|
||||||
|
|
||||||
std::string CConf::getLookupName() const
|
std::string CConf::getLookupName() const
|
||||||
{
|
{
|
||||||
return m_lookupName;
|
return m_lookupName;
|
||||||
|
@ -226,6 +324,31 @@ std::string CConf::getLogFilePath() const
|
||||||
return m_logFilePath;
|
return m_logFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CConf::getAPRSDescription() const
|
||||||
|
{
|
||||||
|
return m_aprsDescription;
|
||||||
|
}
|
||||||
|
|
||||||
std::string CConf::getLogFileRoot() const
|
std::string CConf::getLogFileRoot() const
|
||||||
{
|
{
|
||||||
return m_logFileRoot;
|
return m_logFileRoot;
|
||||||
|
|
|
@ -32,12 +32,23 @@ public:
|
||||||
|
|
||||||
// The General section
|
// The General section
|
||||||
std::string getCallsign() const;
|
std::string getCallsign() const;
|
||||||
|
std::string getSuffix() const;
|
||||||
std::string getRptAddress() const;
|
std::string getRptAddress() const;
|
||||||
unsigned int getRptPort() const;
|
unsigned int getRptPort() const;
|
||||||
unsigned int getMyPort() const;
|
unsigned int getMyPort() const;
|
||||||
bool getRptDebug() const;
|
bool getRptDebug() const;
|
||||||
bool getDaemon() const;
|
bool getDaemon() const;
|
||||||
|
|
||||||
|
// The Info section
|
||||||
|
unsigned int getRxFrequency() const;
|
||||||
|
unsigned int getTxFrequency() const;
|
||||||
|
unsigned int getPower() const;
|
||||||
|
float getLatitude() const;
|
||||||
|
float getLongitude() const;
|
||||||
|
int getHeight() const;
|
||||||
|
std::string getName() const;
|
||||||
|
std::string getDescription() const;
|
||||||
|
|
||||||
// The Id Lookup section
|
// The Id Lookup section
|
||||||
std::string getLookupName() const;
|
std::string getLookupName() const;
|
||||||
unsigned int getLookupTime() const;
|
unsigned int getLookupTime() const;
|
||||||
|
@ -47,6 +58,13 @@ public:
|
||||||
std::string getVoiceLanguage() const;
|
std::string getVoiceLanguage() const;
|
||||||
std::string getVoiceDirectory() const;
|
std::string getVoiceDirectory() const;
|
||||||
|
|
||||||
|
// The aprs.fi section
|
||||||
|
bool getAPRSEnabled() const;
|
||||||
|
std::string getAPRSServer() const;
|
||||||
|
unsigned int getAPRSPort() const;
|
||||||
|
std::string getAPRSPassword() const;
|
||||||
|
std::string getAPRSDescription() const;
|
||||||
|
|
||||||
// The Log section
|
// The Log section
|
||||||
std::string getLogFilePath() const;
|
std::string getLogFilePath() const;
|
||||||
std::string getLogFileRoot() const;
|
std::string getLogFileRoot() const;
|
||||||
|
@ -66,12 +84,22 @@ public:
|
||||||
private:
|
private:
|
||||||
std::string m_file;
|
std::string m_file;
|
||||||
std::string m_callsign;
|
std::string m_callsign;
|
||||||
|
std::string m_suffix;
|
||||||
std::string m_rptAddress;
|
std::string m_rptAddress;
|
||||||
unsigned int m_rptPort;
|
unsigned int m_rptPort;
|
||||||
unsigned int m_myPort;
|
unsigned int m_myPort;
|
||||||
bool m_rptDebug;
|
bool m_rptDebug;
|
||||||
bool m_daemon;
|
bool m_daemon;
|
||||||
|
|
||||||
|
unsigned int m_rxFrequency;
|
||||||
|
unsigned int m_txFrequency;
|
||||||
|
unsigned int m_power;
|
||||||
|
float m_latitude;
|
||||||
|
float m_longitude;
|
||||||
|
int m_height;
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_description;
|
||||||
|
|
||||||
std::string m_lookupName;
|
std::string m_lookupName;
|
||||||
unsigned int m_lookupTime;
|
unsigned int m_lookupTime;
|
||||||
|
|
||||||
|
@ -82,6 +110,12 @@ private:
|
||||||
std::string m_logFilePath;
|
std::string m_logFilePath;
|
||||||
std::string m_logFileRoot;
|
std::string m_logFileRoot;
|
||||||
|
|
||||||
|
bool m_aprsEnabled;
|
||||||
|
std::string m_aprsServer;
|
||||||
|
unsigned int m_aprsPort;
|
||||||
|
std::string m_aprsPassword;
|
||||||
|
std::string m_aprsDescription;
|
||||||
|
|
||||||
unsigned int m_networkPort;
|
unsigned int m_networkPort;
|
||||||
std::string m_networkHosts;
|
std::string m_networkHosts;
|
||||||
unsigned int m_networkReloadTime;
|
unsigned int m_networkReloadTime;
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 "GPSHandler.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
const unsigned char NXDN_DATA_TYPE_GPS = 0x06U;
|
||||||
|
|
||||||
|
const unsigned int NXDN_DATA_LENGTH = 20U;
|
||||||
|
const unsigned int NXDN_DATA_MAX_LENGTH = 15U * NXDN_DATA_LENGTH;
|
||||||
|
|
||||||
|
CGPSHandler::CGPSHandler(const std::string& callsign, const std::string& suffix, const std::string& password, const std::string& address, unsigned int port) :
|
||||||
|
m_callsign(callsign),
|
||||||
|
m_writer(callsign, suffix, password, address, port),
|
||||||
|
m_data(NULL),
|
||||||
|
m_length(0U),
|
||||||
|
m_source()
|
||||||
|
{
|
||||||
|
assert(!callsign.empty());
|
||||||
|
assert(!password.empty());
|
||||||
|
assert(!address.empty());
|
||||||
|
assert(port > 0U);
|
||||||
|
|
||||||
|
m_data = new char[NXDN_DATA_MAX_LENGTH];
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
CGPSHandler::~CGPSHandler()
|
||||||
|
{
|
||||||
|
delete[] m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGPSHandler::open()
|
||||||
|
{
|
||||||
|
return m_writer.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGPSHandler::setInfo(unsigned int txFrequency, unsigned int rxFrequency, float latitude, float longitude, int height, const std::string& desc)
|
||||||
|
{
|
||||||
|
m_writer.setInfo(txFrequency, rxFrequency, latitude, longitude, height, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGPSHandler::processHeader(const std::string& source)
|
||||||
|
{
|
||||||
|
m_source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGPSHandler::processData(const unsigned char* data)
|
||||||
|
{
|
||||||
|
assert(data != NULL);
|
||||||
|
|
||||||
|
::memcpy(m_data + m_length, data + 1U, NXDN_DATA_LENGTH);
|
||||||
|
m_length += NXDN_DATA_LENGTH;
|
||||||
|
|
||||||
|
if (data[0U] == 0x00U) {
|
||||||
|
processNMEA();
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGPSHandler::processEnd()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGPSHandler::clock(unsigned int ms)
|
||||||
|
{
|
||||||
|
m_writer.clock(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGPSHandler::close()
|
||||||
|
{
|
||||||
|
m_writer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGPSHandler::reset()
|
||||||
|
{
|
||||||
|
::memset(m_data, 0x00U, NXDN_DATA_MAX_LENGTH);
|
||||||
|
m_length = 0U;
|
||||||
|
m_source.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGPSHandler::processNMEA()
|
||||||
|
{
|
||||||
|
if (m_data[0U] != NXDN_DATA_TYPE_GPS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (::memcmp(m_data + 1U, "$G", 2U) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (::strchr(m_data + 1U, '*') == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!checkXOR())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (::memcmp(m_data + 4U, "RMC", 3U) != 0) {
|
||||||
|
CUtils::dump("Unhandled NMEA sentence", (unsigned char*)(m_data + 1U), m_length - 1U);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the $GxRMC string into tokens
|
||||||
|
char* pRMC[20U];
|
||||||
|
::memset(pRMC, 0x00U, 20U * sizeof(char*));
|
||||||
|
unsigned int nRMC = 0U;
|
||||||
|
|
||||||
|
char* p = NULL;
|
||||||
|
char* d = m_data + 1U;
|
||||||
|
while ((p = ::strtok(d, ",\r\n")) != NULL) {
|
||||||
|
pRMC[nRMC++] = p;
|
||||||
|
d = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there any position data?
|
||||||
|
if (pRMC[3U] == NULL || pRMC[4U] == NULL || pRMC[5U] == NULL || pRMC[6U] == NULL || ::strlen(pRMC[3U]) == 0U || ::strlen(pRMC[4U]) == 0U || ::strlen(pRMC[5U]) == 0 || ::strlen(pRMC[6U]) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Is it a valid GPS fix?
|
||||||
|
if (::strcmp(pRMC[2U], "A") != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char output[300U];
|
||||||
|
if (pRMC[7U] != NULL && pRMC[8U] != NULL && ::strlen(pRMC[7U]) > 0U && ::strlen(pRMC[8U]) > 0U) {
|
||||||
|
int bearing = ::atoi(pRMC[8U]);
|
||||||
|
int speed = ::atoi(pRMC[7U]);
|
||||||
|
|
||||||
|
::sprintf(output, "%s-Y>APDPRS,NXDN*,qAR,%s:!%s%s/%s%sr%03d/%03d via MMDVM",
|
||||||
|
m_source.c_str(), m_callsign.c_str(), pRMC[3U], pRMC[4U], pRMC[5U], pRMC[6U], bearing, speed);
|
||||||
|
} else {
|
||||||
|
::sprintf(output, "%s-Y>APDPRS,NXDN*,qAR,%s:!%s%s/%s%sr via MMDVM",
|
||||||
|
m_source.c_str(), m_callsign.c_str(), pRMC[3U], pRMC[4U], pRMC[5U], pRMC[6U]);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_writer.write(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGPSHandler::checkXOR() const
|
||||||
|
{
|
||||||
|
char* p1 = ::strchr(m_data, '$');
|
||||||
|
char* p2 = ::strchr(m_data, '*');
|
||||||
|
|
||||||
|
unsigned char res = 0U;
|
||||||
|
for (char* q = p1; q < p2; q++)
|
||||||
|
res ^= *q;
|
||||||
|
|
||||||
|
char buffer[10U];
|
||||||
|
::sprintf(buffer, "%02X", res);
|
||||||
|
|
||||||
|
return ::memcmp(buffer, p2 + 1U, 2U) == 0;
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 GPSHandler_H
|
||||||
|
#define GPSHandler_H
|
||||||
|
|
||||||
|
#include "APRSWriter.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class CGPSHandler {
|
||||||
|
public:
|
||||||
|
CGPSHandler(const std::string& callsign, const std::string& suffix, const std::string& password, const std::string& address, unsigned int port);
|
||||||
|
~CGPSHandler();
|
||||||
|
|
||||||
|
bool open();
|
||||||
|
|
||||||
|
void setInfo(unsigned int txFrequency, unsigned int rxFrequency, float latitude, float longitude, int height, const std::string& desc);
|
||||||
|
|
||||||
|
void processHeader(const std::string& source);
|
||||||
|
|
||||||
|
void processData(const unsigned char* data);
|
||||||
|
|
||||||
|
void processEnd();
|
||||||
|
|
||||||
|
void clock(unsigned int ms);
|
||||||
|
|
||||||
|
void close();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_callsign;
|
||||||
|
CAPRSWriter m_writer;
|
||||||
|
char* m_data;
|
||||||
|
unsigned int m_length;
|
||||||
|
std::string m_source;
|
||||||
|
|
||||||
|
void processNMEA();
|
||||||
|
bool checkXOR() const;
|
||||||
|
void reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -4,7 +4,8 @@ CFLAGS = -g -O3 -Wall -std=c++0x -pthread
|
||||||
LIBS = -lpthread
|
LIBS = -lpthread
|
||||||
LDFLAGS = -g
|
LDFLAGS = -g
|
||||||
|
|
||||||
OBJECTS = Conf.o IcomNetwork.o Log.o Mutex.o NXDNCRC.o NXDNGateway.o NXDNLookup.o NXDNNetwork.o Reflectors.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o
|
OBJECTS = APRSWriter.o APRSWriterThread.o Conf.o GPSHandler.o IcomNetwork.o Log.o Mutex.o NXDNCRC.o NXDNGateway.o NXDNLookup.o NXDNNetwork.o Reflectors.o \
|
||||||
|
StopWatch.o TCPSocket.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o
|
||||||
|
|
||||||
all: NXDNGateway
|
all: NXDNGateway
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "NXDNGateway.h"
|
#include "NXDNGateway.h"
|
||||||
#include "NXDNLookup.h"
|
#include "NXDNLookup.h"
|
||||||
#include "Reflectors.h"
|
#include "Reflectors.h"
|
||||||
|
#include "GPSHandler.h"
|
||||||
#include "StopWatch.h"
|
#include "StopWatch.h"
|
||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
|
@ -54,6 +55,10 @@ const char* DEFAULT_INI_FILE = "/etc/NXDNGateway.ini";
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
const unsigned char NXDN_TYPE_DCALL_HDR = 0x09U;
|
||||||
|
const unsigned char NXDN_TYPE_DCALL = 0x0BU;
|
||||||
|
const unsigned char NXDN_TYPE_TX_REL = 0x08U;
|
||||||
|
|
||||||
const unsigned short NXDN_VOICE_ID = 9999U;
|
const unsigned short NXDN_VOICE_ID = 9999U;
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
|
@ -166,6 +171,8 @@ void CNXDNGateway::run()
|
||||||
in_addr rptAddr = CUDPSocket::lookup(m_conf.getRptAddress());
|
in_addr rptAddr = CUDPSocket::lookup(m_conf.getRptAddress());
|
||||||
unsigned int rptPort = m_conf.getRptPort();
|
unsigned int rptPort = m_conf.getRptPort();
|
||||||
|
|
||||||
|
createGPS();
|
||||||
|
|
||||||
CIcomNetwork localNetwork(m_conf.getMyPort(), m_conf.getRptDebug());
|
CIcomNetwork localNetwork(m_conf.getMyPort(), m_conf.getRptDebug());
|
||||||
ret = localNetwork.open();
|
ret = localNetwork.open();
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
|
@ -236,7 +243,7 @@ void CNXDNGateway::run()
|
||||||
|
|
||||||
LogMessage("Linked at startup to reflector %u", currentId);
|
LogMessage("Linked at startup to reflector %u", currentId);
|
||||||
} else {
|
} else {
|
||||||
startupId = 9999U;
|
startupId = 9999U;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,10 +281,10 @@ void CNXDNGateway::run()
|
||||||
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && (buffer[5U] == 0x01U || buffer[5U] == 0x08U)) {
|
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && (buffer[5U] == 0x01U || buffer[5U] == 0x08U)) {
|
||||||
grp = (buffer[7U] & 0x20U) == 0x20U;
|
grp = (buffer[7U] & 0x20U) == 0x20U;
|
||||||
|
|
||||||
srcId = (buffer[8U] << 8) & 0xFF00U;
|
srcId = (buffer[8U] << 8) & 0xFF00U;
|
||||||
srcId |= (buffer[9U] << 0) & 0x00FFU;
|
srcId |= (buffer[9U] << 0) & 0x00FFU;
|
||||||
|
|
||||||
dstId = (buffer[10U] << 8) & 0xFF00U;
|
dstId = (buffer[10U] << 8) & 0xFF00U;
|
||||||
dstId |= (buffer[11U] << 0) & 0x00FFU;
|
dstId |= (buffer[11U] << 0) & 0x00FFU;
|
||||||
|
|
||||||
if (dstId != currentId) {
|
if (dstId != currentId) {
|
||||||
|
@ -308,7 +315,7 @@ void CNXDNGateway::run()
|
||||||
|
|
||||||
// Link to the new reflector
|
// Link to the new reflector
|
||||||
if (reflector != NULL) {
|
if (reflector != NULL) {
|
||||||
currentId = dstId;
|
currentId = dstId;
|
||||||
currentAddr = reflector->m_address;
|
currentAddr = reflector->m_address;
|
||||||
currentPort = reflector->m_port;
|
currentPort = reflector->m_port;
|
||||||
|
|
||||||
|
@ -335,6 +342,29 @@ void CNXDNGateway::run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_gps != NULL) {
|
||||||
|
if ((buffer[0U] & 0xF0U) == 0x90U) {
|
||||||
|
switch (buffer[2U] & 0x3FU) {
|
||||||
|
case NXDN_TYPE_DCALL_HDR: {
|
||||||
|
unsigned short srcId = 0U;
|
||||||
|
srcId |= (buffer[5U] << 8) & 0xFF00U;
|
||||||
|
srcId |= (buffer[6U] << 0) & 0x00FFU;
|
||||||
|
std::string callsign = lookup->find(srcId);
|
||||||
|
m_gps->processHeader(callsign);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NXDN_TYPE_DCALL:
|
||||||
|
m_gps->processData(buffer + 3U);
|
||||||
|
break;
|
||||||
|
case NXDN_TYPE_TX_REL:
|
||||||
|
m_gps->processEnd();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we're linked and we have a network, send it on
|
// If we're linked and we have a network, send it on
|
||||||
if (currentId != 9999U) {
|
if (currentId != 9999U) {
|
||||||
remoteNetwork.writeData(buffer, len, srcId, dstId, grp, currentAddr, currentPort);
|
remoteNetwork.writeData(buffer, len, srcId, dstId, grp, currentAddr, currentPort);
|
||||||
|
@ -424,6 +454,9 @@ void CNXDNGateway::run()
|
||||||
lostTimer.stop();
|
lostTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_gps != NULL)
|
||||||
|
m_gps->clock(ms);
|
||||||
|
|
||||||
if (ms < 5U)
|
if (ms < 5U)
|
||||||
CThread::sleep(5U);
|
CThread::sleep(5U);
|
||||||
}
|
}
|
||||||
|
@ -436,5 +469,39 @@ void CNXDNGateway::run()
|
||||||
|
|
||||||
lookup->stop();
|
lookup->stop();
|
||||||
|
|
||||||
|
if (m_gps != NULL) {
|
||||||
|
m_gps->close();
|
||||||
|
delete m_gps;
|
||||||
|
}
|
||||||
|
|
||||||
::LogFinalise();
|
::LogFinalise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CNXDNGateway::createGPS()
|
||||||
|
{
|
||||||
|
if (!m_conf.getAPRSEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string callsign = m_conf.getCallsign();
|
||||||
|
std::string suffix = m_conf.getSuffix();
|
||||||
|
std::string hostname = m_conf.getAPRSServer();
|
||||||
|
unsigned int port = m_conf.getAPRSPort();
|
||||||
|
std::string password = m_conf.getAPRSPassword();
|
||||||
|
std::string desc = m_conf.getAPRSDescription();
|
||||||
|
|
||||||
|
m_gps = new CGPSHandler(callsign, suffix, password, hostname, port);
|
||||||
|
|
||||||
|
unsigned int txFrequency = m_conf.getTxFrequency();
|
||||||
|
unsigned int rxFrequency = m_conf.getRxFrequency();
|
||||||
|
float latitude = m_conf.getLatitude();
|
||||||
|
float longitude = m_conf.getLongitude();
|
||||||
|
int height = m_conf.getHeight();
|
||||||
|
|
||||||
|
m_gps->setInfo(txFrequency, rxFrequency, latitude, longitude, height, desc);
|
||||||
|
|
||||||
|
bool ret = m_gps->open();
|
||||||
|
if (!ret) {
|
||||||
|
delete m_gps;
|
||||||
|
m_gps = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#if !defined(NXDNGateway_H)
|
#if !defined(NXDNGateway_H)
|
||||||
#define NXDNGateway_H
|
#define NXDNGateway_H
|
||||||
|
|
||||||
|
#include "GPSHandler.h"
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
#include "Conf.h"
|
#include "Conf.h"
|
||||||
|
|
||||||
|
@ -47,7 +48,10 @@ public:
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CConf m_conf;
|
CConf m_conf;
|
||||||
|
CGPSHandler* m_gps;
|
||||||
|
|
||||||
|
void createGPS();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,16 +1,35 @@
|
||||||
[General]
|
[General]
|
||||||
Callsign=G4KLX
|
Callsign=G4KLX
|
||||||
|
Suffix=N
|
||||||
RptAddress=127.0.0.1
|
RptAddress=127.0.0.1
|
||||||
RptPort=14021
|
RptPort=14021
|
||||||
LocalPort=14020
|
LocalPort=14020
|
||||||
Debug=0
|
Debug=0
|
||||||
Daemon=1
|
Daemon=1
|
||||||
|
|
||||||
|
[Info]
|
||||||
|
RXFrequency=430475000
|
||||||
|
TXFrequency=439475000
|
||||||
|
Power=1
|
||||||
|
Latitude=0.0
|
||||||
|
Longitude=0.0
|
||||||
|
Height=0
|
||||||
|
Name=Nowhere
|
||||||
|
Description=Multi-Mode Repeater
|
||||||
|
|
||||||
[Voice]
|
[Voice]
|
||||||
Enabled=1
|
Enabled=1
|
||||||
Language=en_GB
|
Language=en_GB
|
||||||
Directory=./Audio
|
Directory=./Audio
|
||||||
|
|
||||||
|
[aprs.fi]
|
||||||
|
Enable=0
|
||||||
|
# Server=noam.aprs2.net
|
||||||
|
Server=euro.aprs2.net
|
||||||
|
Port=14580
|
||||||
|
Password=9999
|
||||||
|
Description=APRS Description
|
||||||
|
|
||||||
[Id Lookup]
|
[Id Lookup]
|
||||||
Name=NXDN.csv
|
Name=NXDN.csv
|
||||||
Time=24
|
Time=24
|
||||||
|
|
|
@ -146,7 +146,10 @@
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="APRSWriter.h" />
|
||||||
|
<ClInclude Include="APRSWriterThread.h" />
|
||||||
<ClInclude Include="Conf.h" />
|
<ClInclude Include="Conf.h" />
|
||||||
|
<ClInclude Include="GPSHandler.h" />
|
||||||
<ClInclude Include="IcomNetwork.h" />
|
<ClInclude Include="IcomNetwork.h" />
|
||||||
<ClInclude Include="Log.h" />
|
<ClInclude Include="Log.h" />
|
||||||
<ClInclude Include="Mutex.h" />
|
<ClInclude Include="Mutex.h" />
|
||||||
|
@ -155,7 +158,9 @@
|
||||||
<ClInclude Include="NXDNLookup.h" />
|
<ClInclude Include="NXDNLookup.h" />
|
||||||
<ClInclude Include="NXDNNetwork.h" />
|
<ClInclude Include="NXDNNetwork.h" />
|
||||||
<ClInclude Include="Reflectors.h" />
|
<ClInclude Include="Reflectors.h" />
|
||||||
|
<ClInclude Include="RingBuffer.h" />
|
||||||
<ClInclude Include="StopWatch.h" />
|
<ClInclude Include="StopWatch.h" />
|
||||||
|
<ClInclude Include="TCPSocket.h" />
|
||||||
<ClInclude Include="Thread.h" />
|
<ClInclude Include="Thread.h" />
|
||||||
<ClInclude Include="Timer.h" />
|
<ClInclude Include="Timer.h" />
|
||||||
<ClInclude Include="UDPSocket.h" />
|
<ClInclude Include="UDPSocket.h" />
|
||||||
|
@ -164,7 +169,10 @@
|
||||||
<ClInclude Include="Voice.h" />
|
<ClInclude Include="Voice.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="APRSWriter.cpp" />
|
||||||
|
<ClCompile Include="APRSWriterThread.cpp" />
|
||||||
<ClCompile Include="Conf.cpp" />
|
<ClCompile Include="Conf.cpp" />
|
||||||
|
<ClCompile Include="GPSHandler.cpp" />
|
||||||
<ClCompile Include="IcomNetwork.cpp" />
|
<ClCompile Include="IcomNetwork.cpp" />
|
||||||
<ClCompile Include="Log.cpp" />
|
<ClCompile Include="Log.cpp" />
|
||||||
<ClCompile Include="Mutex.cpp" />
|
<ClCompile Include="Mutex.cpp" />
|
||||||
|
@ -174,6 +182,7 @@
|
||||||
<ClCompile Include="NXDNNetwork.cpp" />
|
<ClCompile Include="NXDNNetwork.cpp" />
|
||||||
<ClCompile Include="Reflectors.cpp" />
|
<ClCompile Include="Reflectors.cpp" />
|
||||||
<ClCompile Include="StopWatch.cpp" />
|
<ClCompile Include="StopWatch.cpp" />
|
||||||
|
<ClCompile Include="TCPSocket.cpp" />
|
||||||
<ClCompile Include="Thread.cpp" />
|
<ClCompile Include="Thread.cpp" />
|
||||||
<ClCompile Include="Timer.cpp" />
|
<ClCompile Include="Timer.cpp" />
|
||||||
<ClCompile Include="UDPSocket.cpp" />
|
<ClCompile Include="UDPSocket.cpp" />
|
||||||
|
|
|
@ -59,6 +59,21 @@
|
||||||
<ClInclude Include="NXDNCRC.h">
|
<ClInclude Include="NXDNCRC.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="APRSWriter.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="APRSWriterThread.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="TCPSocket.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="RingBuffer.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="GPSHandler.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="Conf.cpp">
|
<ClCompile Include="Conf.cpp">
|
||||||
|
@ -106,5 +121,17 @@
|
||||||
<ClCompile Include="NXDNCRC.cpp">
|
<ClCompile Include="NXDNCRC.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="APRSWriter.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="APRSWriterThread.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="TCPSocket.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="GPSHandler.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -71,7 +71,7 @@ bool CNXDNNetwork::writeData(const unsigned char* data, unsigned int length, uns
|
||||||
buffer[9U] |= data[5U] == 0x01U ? 0x04U : 0x00U;
|
buffer[9U] |= data[5U] == 0x01U ? 0x04U : 0x00U;
|
||||||
buffer[9U] |= data[5U] == 0x08U ? 0x08U : 0x00U;
|
buffer[9U] |= data[5U] == 0x08U ? 0x08U : 0x00U;
|
||||||
} else if ((data[0U] & 0xF0U) == 0x90U) {
|
} else if ((data[0U] & 0xF0U) == 0x90U) {
|
||||||
// This if data.
|
// This is data.
|
||||||
buffer[9U] |= 0x02U;
|
buffer[9U] |= 0x02U;
|
||||||
if (data[0U] == 0x90U || data[0U] == 0x92U || data[0U] == 0x9CU || data[0U] == 0x9EU) {
|
if (data[0U] == 0x90U || data[0U] == 0x92U || data[0U] == 0x9CU || data[0U] == 0x9EU) {
|
||||||
// This is data header or trailer.
|
// This is data header or trailer.
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2006-2009,2012,2013,2015,2016 by Jonathan Naylor G4KLX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RingBuffer_H
|
||||||
|
#define RingBuffer_H
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
template<class T> class CRingBuffer {
|
||||||
|
public:
|
||||||
|
CRingBuffer(unsigned int length, const char* name) :
|
||||||
|
m_length(length),
|
||||||
|
m_name(name),
|
||||||
|
m_buffer(NULL),
|
||||||
|
m_iPtr(0U),
|
||||||
|
m_oPtr(0U)
|
||||||
|
{
|
||||||
|
assert(length > 0U);
|
||||||
|
assert(name != NULL);
|
||||||
|
|
||||||
|
m_buffer = new T[length];
|
||||||
|
|
||||||
|
::memset(m_buffer, 0x00, m_length * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
~CRingBuffer()
|
||||||
|
{
|
||||||
|
delete[] m_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addData(const T* buffer, unsigned int nSamples)
|
||||||
|
{
|
||||||
|
if (nSamples >= freeSpace()) {
|
||||||
|
LogError("**** Overflow in %s ring buffer, %u >= %u", m_name, nSamples, freeSpace());
|
||||||
|
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
|
||||||
|
{
|
||||||
|
if (m_oPtr == m_iPtr)
|
||||||
|
return m_length;
|
||||||
|
|
||||||
|
if (m_oPtr > m_iPtr)
|
||||||
|
return m_oPtr - m_iPtr;
|
||||||
|
|
||||||
|
return (m_length + m_oPtr) - m_iPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int dataSize() const
|
||||||
|
{
|
||||||
|
return m_length - freeSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasSpace(unsigned int length) const
|
||||||
|
{
|
||||||
|
return freeSpace() > length;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasData() const
|
||||||
|
{
|
||||||
|
return m_oPtr != m_iPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEmpty() const
|
||||||
|
{
|
||||||
|
return m_oPtr == m_iPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int m_length;
|
||||||
|
const char* m_name;
|
||||||
|
T* m_buffer;
|
||||||
|
unsigned int m_iPtr;
|
||||||
|
unsigned int m_oPtr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
Loading…
Reference in New Issue