Initial commit.

This commit is contained in:
Jonathan Naylor 2018-05-09 19:23:17 +01:00
commit 12d55cef37
430 changed files with 72067 additions and 0 deletions

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
*.o
*.obj
x86
Debug
Release
*.user
*~
*.bak
.vs

601
APRSTransmit/APRSParser.cpp Normal file
View file

@ -0,0 +1,601 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA
*
* 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.
*/
// Most of the code has been borrowed from APRX Software. I just kept the parts I need for my purpose and C++ed it. Hence the copyright text below.
//https://github.com/PhirePhly/aprx/blob/master/parse_aprs.c
/********************************************************************
* APRX -- 2nd generation APRS-i-gate with *
* minimal requirement of esoteric facilities or *
* libraries of any kind beyond UNIX system libc. *
* *
* (c) Matti Aarnio - OH2MQK, 2007-2014 *
* *
********************************************************************/
/*
* Some parts of this code are copied from:
*
* aprsc
*
* (c) Heikki Hannikainen, OH7LZB <hessu@hes.iki.fi>
*
* This program is licensed under the BSD license, which can be found
* in the file LICENSE.
*
*/
#include "APRSParser.h"
#include <wx/log.h>
#include <string.h>
#include <math.h>
CAPRSPacket::CAPRSPacket() :
m_type(APT_Unknown),
m_latitude(0.0F),
m_longitude(0.0F),
m_symbol('\0'),
m_symbolTable('\0'),
m_typeChar('\0'),
m_body(),
m_raw()
{
}
CAPRSPacket::CAPRSPacket(APRSPacketType type, float latitude, float longitude, const wxString& infoText, const wxString raw) :
m_type(type),
m_latitude(latitude),
m_longitude(longitude),
m_symbol('\0'),
m_symbolTable('\0'),
m_typeChar('\0'),
m_body(infoText),
m_raw(raw),
m_destCall(),
m_srcCall()
{
}
float& CAPRSPacket::Longitude(){
return m_longitude;
}
float& CAPRSPacket::Latitude(){
return m_latitude;
}
wxString& CAPRSPacket::Body(){
return m_body;
}
wxString& CAPRSPacket::Raw(){
return m_raw;
}
wxChar& CAPRSPacket::Symbol(){
return m_symbol;
}
wxChar& CAPRSPacket::SymbolTable(){
return m_symbolTable;
}
wxChar& CAPRSPacket::TypeChar(){
return m_typeChar;
}
APRSPacketType& CAPRSPacket::Type(){
return m_type;
}
wxString& CAPRSPacket::SourceCall(){
return m_srcCall;
}
wxString& CAPRSPacket::DestinationCall(){
return m_destCall;
}
/*****************************************************************************
* Parsed an APRS string into an APRS frame
* currently it does nothing but determine if the packet is Icom supported
* I already added provision for conversion of unsupported packet formats to supported formats
******************************************************************************/
bool CAPRSParser::Parse(const wxString& aprsFrame, CAPRSPacket& packet)
{
wxString aprsFrameLocal(aprsFrame);
stripFrame(aprsFrameLocal);
if(!preprocessFrame(packet, aprsFrameLocal))
return false;
switch(packet.TypeChar())
{
case '\'':
case '`' :
{
//Packet is of MicE type
//Icom radios do not support MicE so convert
if(parse_aprs_mice(packet)){
wxLogMessage(wxT("Got MicE format, converting to Icom supported"));
convertToIcomCompatible(packet);
return Parse(packet.Raw(), packet);
}
packet.Type() = APT_Position;
return false;
}
case '!' :
if(packet.Body().GetChar(0) == '!'){
//Ultimeter 2000 weather station
//same procedure as above, ignore for now
return false;
}
case '=' :
case '/' :
case '@' :
{
if(packet.Body().Length() < 10) return false;//enough chars to have a chance to parse it ?
/* Normal or compressed location packet, with or without
* timestamp, with or without messaging capability
*
* ! and / have messaging, / and @ have a prepended timestamp
*/
packet.Type() = APT_Position;
if(packet.TypeChar() == '/' || packet.TypeChar() == '@')//With a prepended timestamp, jump over it.
packet.Body() = packet.Body().Mid(7);
wxChar posChar = packet.Body().GetChar(0);
if(valid_sym_table_compressed(posChar)//Compressed format
&& packet.Body().Length() >= 13){//we need at least 13 char
//icom unsupported, ignore for now
return false;//parse_aprs_compressed(pb, body, body_end);
}
else if(posChar >= '0' && posChar <= '9' //Normal uncompressed format
&& packet.Body().Length() >=19){//we need at least 19 chars for it to be valid
if(ensureIsIcomCompatible(packet))
return Parse(packet.Raw(), packet);
return true;
}
break;
}
case '$' : //NMEA packet
if(packet.Body().Length() > 10){
packet.Type() = APT_NMEA;
return true;
}
break;
case ':' :
//We have an APRS message
packet.Type() = APT_Message;
//Nice future feature would be to add conversion to icom Android App messaging
return false;
case ';' :
//we have an object
if(packet.Body().Length() > 29){
//TODO : Parsing ...
packet.Type() = APT_Object;
return true;
}
break;
case ')' :
//this is an item
if(packet.Body().Length() > 19){
//TODO : Parsing ...
packet.Type() = APT_Item;
return true;
}
break;
case '>' ://APRS status...
case '<' ://stat capa
case '?' ://Query
case 'T' ://Telemetry
return false;//not supported
case '#': /* Peet Bros U-II Weather Station */
case '*': /* Peet Bros U-I Weather Station */
case '_': /* Weather report without position */
packet.Type() = APT_WX;
return true; // AFAIK _ is supported, not sure for the others
case '{' ://custom
return false;
}
return false;
}
void CAPRSParser::convertToIcomCompatible(CAPRSPacket& packet)
{
//first build the coordinate string
float degrees = ::floor(::fabs(packet.Latitude()));
float minutes = (::fabs(packet.Latitude()) - degrees) * 60.0f;
char hemisphere = packet.Latitude() > 0.0f ? 'N' : 'S';
wxString latString = wxString::Format(wxT("%02.0f%05.2f%c"), degrees, minutes, hemisphere);
degrees = ::floor(::fabs(packet.Longitude()));
minutes = (::fabs(packet.Longitude()) - degrees) * 60.0f;
hemisphere = packet.Longitude() > 0.0f ? 'E' : 'W';
wxString longString = wxString::Format(wxT("%03.0f%05.2f%c"), degrees, minutes, hemisphere);
/*packet.Raw() = wxString::Format(wxT("%s>%s:=%s%c%s%c/%s"),
packet.SourceCall().mb_str().data(),
packet.DestinationCall().mb_str().data(),
latString.mb_str().data(),
(char)packet.SymbolTable(),
longString.mb_str().data(),
(char)packet.Symbol(),
packet.Body().mb_str().data());*/
//above code behaved in different manner under wx2.8 let's do it the brutal way !
packet.Raw() = packet.SourceCall()
+ wxT(">")
+ packet.DestinationCall()
+ wxT(":=")
+ latString
+ packet.SymbolTable()
+ longString
+ packet.Symbol()
+ wxT("/")
+ packet.Body();
}
//configures the packet's raw, source call and dest call
bool CAPRSParser::preprocessFrame(CAPRSPacket& packet, const wxString& aprsFrame)
{
wxString addressField = aprsFrame.BeforeFirst(':');
wxString body = aprsFrame.AfterFirst(':');
if(addressField == aprsFrame || body.IsEmpty()){
wxLogWarning(wxT("Failed to find body of aprs frame"));
return false;
}
wxString srcCall = addressField.BeforeFirst('>');
wxString destCall = addressField.AfterFirst('>');
if(srcCall == addressField){
wxLogWarning(wxT("Failed to extract source and destination call"));
return false;
}
packet.TypeChar() = body.GetChar(0);
packet.Body() = body.Mid(1);//strip the type char
packet.SourceCall() = srcCall;
packet.DestinationCall() = destCall;
packet.Raw() = wxString(aprsFrame);
packet.Type() = APT_Unknown;
return true;
}
/* Stupidly icom always expects a / after the symbol char, even if no data extension is present -_-
* This function makes sure the frame is icom compatible. In case the frame is invalid, A COMPLETE aprsFrame is built.
* If no modifications were made true is returned, otherwise false */
bool CAPRSParser::ensureIsIcomCompatible(CAPRSPacket& packet)
{
bool changeMade = false;
wxChar symbol = packet.Body().GetChar(18);
wxChar charAfterSymbol = packet.Body().GetChar(19);
wxString newBody(packet.Body());
wxString aprsFrame(packet.Raw());
if(charAfterSymbol != '/'
&& symbol != '_'){//do not do this for weather packets !
newBody = packet.Body().Mid(0, 19) + wxT("/") + packet.Body().Mid(19);
aprsFrame.Replace(packet.Body(), newBody);
changeMade = true;
}
if(stripFrame(aprsFrame)) changeMade = true;
packet.Body() = newBody;
packet.Raw() = aprsFrame;
return changeMade;
}
bool CAPRSParser::stripFrame(wxString& aprsFrame)
{
const unsigned int maxAprsFrameLen = 64U;
bool changeMade = false;
//Trim the path, only keep from and to. Icom device do not support too long frames.
if(aprsFrame.Length() > maxAprsFrameLen){
wxString dataField = aprsFrame.AfterFirst(':');
wxString addressField = aprsFrame.BeforeFirst(':');
addressField = addressField.BeforeFirst(',');
aprsFrame = addressField + wxT(":") + dataField;
changeMade = true;
}
//Still too long ?
if(aprsFrame.Length() > maxAprsFrameLen){
aprsFrame = aprsFrame.Left(maxAprsFrameLen);
changeMade = true;
}
return changeMade;
}
bool CAPRSParser::parse_aprs_mice(CAPRSPacket& packet)
{
float lat = 0.0, lng = 0.0;
unsigned int lat_deg = 0, lat_min = 0, lat_min_frag = 0, lng_deg = 0, lng_min = 0, lng_min_frag = 0;
//const char *d_start;
char dstcall[7];
char *p;
char sym_table, sym_code;
int posambiguity = 0;
int i;
//code below is just to map wxWidgets stuff to original APRX pointer based logic.
char* body = new char[packet.Body().length()];
for (unsigned int i = 0U; i < packet.Body().length(); i++)
body[i] = packet.Body().GetChar(i);
char* body_end = body + packet.Body().length();
char* d_start = new char[packet.Body().length()];
for (unsigned int i = 0U; i < packet.Body().length(); i++)
d_start[i] = packet.DestinationCall().GetChar(i);
char* dstcall_end_or_ssid = d_start + packet.DestinationCall().length();
//Original APRX code follows.. Just a few minor changes
/* check packet length */
if (body_end - body < 8) {
delete[] body;
delete[] d_start;
return false;
}
/* check that the destination call exists and is of the right size for mic-e */
//d_start = pb->srccall_end+1;
if (dstcall_end_or_ssid - d_start != 6) {
//DEBUG_LOG(".. bad destcall length! ");
delete[] body;
delete[] d_start;
return false; /* eh...? */
}
/* validate destination call:
* A-K characters are not used in the last 3 characters
* and MNO are never used
*/
//if (debug)printf(" destcall='%6.6s'",d_start);
for (i = 0; i < 3; i++)
if (!((d_start[i] >= '0' && d_start[i] <= '9')
|| (d_start[i] >= 'A' && d_start[i] <= 'L')
|| (d_start[i] >= 'P' && d_start[i] <= 'Z'))) {
//DEBUG_LOG(".. bad destcall characters in posits 1..3");
delete[] body;
delete[] d_start;
return false;
}
for (i = 3; i < 6; i++)
if (!((d_start[i] >= '0' && d_start[i] <= '9')
|| (d_start[i] == 'L')
|| (d_start[i] >= 'P' && d_start[i] <= 'Z'))) {
//DEBUG_LOG(".. bad destcall characters in posits 4..6");
delete[] body;
delete[] d_start;
return false;
}
//DEBUG_LOG("\tpassed dstcall format check");
/* validate information field (longitude, course, speed and
* symbol table and code are checked). Not bullet proof..
*
* 0 1 23 4 5 6 7
* /^[\x26-\x7f][\x26-\x61][\x1c-\x7f]{2}[\x1c-\x7d][\x1c-\x7f][\x21-\x7b\x7d][\/\\A-Z0-9]/
*/
if (body[0] < 0x26 || body[0] > 0x7f) {
//DEBUG_LOG("..bad infofield column 1");
delete[] body;
delete[] d_start;
return false;
}
if (body[1] < 0x26 || body[1] > 0x61) {
//DEBUG_LOG("..bad infofield column 2");
delete[] body;
delete[] d_start;
return false;
}
if (body[2] < 0x1c || body[2] > 0x7f) {
//DEBUG_LOG("..bad infofield column 3");
delete[] body;
delete[] d_start;
return false;
}
if (body[3] < 0x1c || body[3] > 0x7f) {
//DEBUG_LOG("..bad infofield column 4");
delete[] body;
delete[] d_start;
return false;
}
if (body[4] < 0x1c || body[4] > 0x7d) {
//DEBUG_LOG("..bad infofield column 5");
//delete[] body;
//delete[] d_start;
//return false;
}
if (body[5] < 0x1c || body[5] > 0x7f) {
//DEBUG_LOG("..bad infofield column 6");
delete[] body;
delete[] d_start;
return false;
}
if ((body[6] < 0x21 || body[6] > 0x7b)
&& body[6] != 0x7d) {
//DEBUG_LOG("..bad infofield column 7");
delete[] body;
delete[] d_start;
return false;
}
if (!valid_sym_table_uncompressed(body[7])) {
//DEBUG_LOG("..bad symbol table entry on column 8");
delete[] body;
delete[] d_start;
return false;
}
//DEBUG_LOG("\tpassed info format check");
/* make a local copy, we're going to modify it */
strncpy(dstcall, d_start, 6);
dstcall[6] = 0;
/* First do the destination callsign
* (latitude, message bits, N/S and W/E indicators and long. offset)
*
* Translate the characters to get the latitude
*/
//fprintf(stderr, "\tuntranslated dstcall: %s\n", dstcall);
for (p = dstcall; *p; p++) {
if (*p >= 'A' && *p <= 'J')
*p -= 'A' - '0';
else if (*p >= 'P' && *p <= 'Y')
*p -= 'P' - '0';
else if (*p == 'K' || *p == 'L' || *p == 'Z')
*p = '_';
}
//fprintf(stderr, "\ttranslated dstcall: %s\n", dstcall);
// position ambiquity is going to get ignored now,
// it's not needed in this application.
if (dstcall[5] == '_') { dstcall[5] = '5'; posambiguity = 1; }
if (dstcall[4] == '_') { dstcall[4] = '5'; posambiguity = 2; }
if (dstcall[3] == '_') { dstcall[3] = '5'; posambiguity = 3; }
if (dstcall[2] == '_') { dstcall[2] = '3'; posambiguity = 4; }
if (dstcall[1] == '_' || dstcall[0] == '_') {
//DEBUG_LOG("..bad pos-ambiguity on destcall");
delete[] body;
delete[] d_start;
return false;
} // cannot use posamb here
// convert to degrees, minutes and decimal degrees,
// and then to a float lat
if (sscanf(dstcall, "%2u%2u%2u",
&lat_deg, &lat_min, &lat_min_frag) != 3) {
//DEBUG_LOG("\tsscanf failed");
delete[] body;
delete[] d_start;
return false;
}
lat = (float)lat_deg + (float)lat_min / 60.0 + (float)lat_min_frag / 6000.0;
// check the north/south direction and correct the latitude if necessary
if (d_start[3] <= 0x4c)
lat = 0 - lat;
/* Decode the longitude, the first three bytes of the body
* after the data type indicator. First longitude degrees,
* remember the longitude offset.
*/
lng_deg = body[0] - 28;
if (d_start[4] >= 0x50)
lng_deg += 100;
if (lng_deg >= 180 && lng_deg <= 189)
lng_deg -= 80;
else if (lng_deg >= 190 && lng_deg <= 199)
lng_deg -= 190;
/* Decode the longitude minutes */
lng_min = body[1] - 28;
if (lng_min >= 60)
lng_min -= 60;
/* ... and minute decimals */
lng_min_frag = body[2] - 28;
/* apply position ambiguity to longitude */
switch (posambiguity) {
case 0:
/* use everything */
lng = (float)lng_deg + (float)lng_min / 60.0
+ (float)lng_min_frag / 6000.0;
break;
case 1:
/* ignore last number of lng_min_frag */
lng = (float)lng_deg + (float)lng_min / 60.0
+ (float)(lng_min_frag - lng_min_frag % 10 + 5) / 6000.0;
break;
case 2:
/* ignore lng_min_frag */
lng = (float)lng_deg + ((float)lng_min + 0.5) / 60.0;
break;
case 3:
/* ignore lng_min_frag and last number of lng_min */
lng = (float)lng_deg + (float)(lng_min - lng_min % 10 + 5) / 60.0;
break;
case 4:
/* minute is unused -> add 0.5 degrees to longitude */
lng = (float)lng_deg + 0.5;
break;
default:
//DEBUG_LOG(".. posambiguity code BUG!");
delete[] body;
delete[] d_start;
return false;
}
/* check the longitude E/W sign */
if (d_start[5] >= 0x50)
lng = 0 - lng;
/* save the symbol table and code */
sym_code = body[6];
sym_table = body[7];
/* ok, we're done */
/*
fprintf(stderr, "\tlat %u %u.%u (%.4f) lng %u %u.%u (%.4f)\n",
lat_deg, lat_min, lat_min_frag, lat,
lng_deg, lng_min, lng_min_frag, lng);
fprintf(stderr, "\tsym '%c' '%c'\n", sym_table, sym_code);
*/
//return pbuf_fill_pos(pb, lat, lng, sym_table, sym_code);
///End of APRX original code
packet.Latitude() = lat;
packet.Longitude() = lng;
packet.Symbol() = sym_code;
packet.SymbolTable() = sym_table;
packet.Body() = packet.Body().Mid(9);//if MicE has additional info like heading it'll be longer than 9, ignore for now
delete[] body;
delete[] d_start;
return true;
}
bool CAPRSParser::valid_sym_table_compressed(wxChar c)
{
return (c == '/' || c == '\\' || (c >= 0x41 && c <= 0x5A)
|| (c >= 0x61 && c <= 0x6A)); /* [\/\\A-Za-j] */
}
bool CAPRSParser::valid_sym_table_uncompressed(wxChar c)
{
return (c == '/' || c == '\\' || (c >= 0x41 && c <= 0x5A)
|| (c >= 0x30 && c <= 0x39)); /* [\/\\A-Z0-9] */
}

79
APRSTransmit/APRSParser.h Normal file
View file

@ -0,0 +1,79 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA
*
* 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 APRSParserAppD_H
#define APRSParserAppD_H
#include <wx/string.h>
enum APRSPacketType {
APT_Unknown,
APT_Position,
APT_WX,
APT_Object,
APT_Item,
APT_Message,
APT_NMEA
};
class CAPRSPacket{
public :
CAPRSPacket();
CAPRSPacket(APRSPacketType type, float latitude, float longitude, const wxString& infoText, const wxString raw);
float& Longitude();
float& Latitude();
wxString& SourceCall();
wxChar& Symbol();
wxChar& SymbolTable();
wxChar& TypeChar();
wxString& DestinationCall();
wxString& Body();
wxString& Raw();
APRSPacketType& Type();
private:
APRSPacketType m_type;
float m_latitude;
float m_longitude;
wxChar m_symbol;
wxChar m_symbolTable;
wxChar m_typeChar;
wxString m_body;
wxString m_raw;//raw string in TNC2 format, including '\r'
wxString m_destCall;
wxString m_srcCall;
};
class CAPRSParser{
public:
static bool Parse(const wxString& aprsString, CAPRSPacket& packet);
private :
static bool valid_sym_table_compressed(wxChar c);
static bool valid_sym_table_uncompressed(wxChar c);
static bool parse_aprs_mice(CAPRSPacket& packet);
static bool ensureIsIcomCompatible(CAPRSPacket& packet);
static bool stripFrame(wxString& aprsFrame);
static bool preprocessFrame(CAPRSPacket& packet, const wxString& aprsFrame);
static void convertToIcomCompatible(CAPRSPacket& packet);
};
#endif

View file

@ -0,0 +1,174 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA
*
* 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 "SlowDataEncoder.h"
#include "DStarDefines.h"
#include "APRSTransmit.h"
#include "APRSParser.h"
CAPRSTransmit::CAPRSTransmit(const wxString& callsign, const wxString& text) :
m_socket(wxEmptyString, 0U),
m_repeaterCallsign(callsign),
m_APRSCallsign(callsign),
m_text(text)
{
int index = m_text.Find(wxT(">"));
if(index != wxNOT_FOUND)
m_APRSCallsign = m_text.Left(index);
}
CAPRSTransmit::~CAPRSTransmit()
{
}
bool CAPRSTransmit::run()
{
//First see if the packet is Icom supported...
CAPRSPacket aprsPacket;
if(!CAPRSParser::Parse(m_text, aprsPacket)){
wxLogWarning(wxT("Unsupported APRS Format, ignoring => ") + m_text.Trim(true));
return false;
}
wxString textWithCRC(aprsPacket.Raw());
wxLogMessage(wxT("Supported APRS Format => ") + textWithCRC.Trim(true));
//add nececessary stuff to text, but keep it the original
textWithCRC.Replace(wxT("\n"), wxEmptyString);
if(!textWithCRC.EndsWith(wxT("\r"))) textWithCRC.Append(wxT("\r"));
wxString crc = wxString::Format(wxT("$$CRC%04X,"), calcCRC(textWithCRC));
textWithCRC.Prepend(crc);
bool opened = m_socket.open();
if (!opened)
return false;
in_addr address = CUDPReaderWriter::lookup(wxT("127.0.0.1"));
unsigned int id = CHeaderData::createId();
wxString callsignG = m_repeaterCallsign.Left(LONG_CALLSIGN_LENGTH - 1U);
callsignG.Append(wxT("G"));
CHeaderData header;
header.setId(id);
header.setMyCall1(m_APRSCallsign);
header.setMyCall2(wxT("APRS"));
header.setRptCall1(callsignG);
header.setRptCall2(m_repeaterCallsign);
header.setYourCall(wxT("CQCQCQ "));
header.setDestination(address, G2_DV_PORT);
sendHeader(header);
CSlowDataEncoder encoder;
encoder.setHeaderData(header);
encoder.setGPSData(textWithCRC);
encoder.setTextData(wxT("APRS to DPRS"));
CAMBEData data;
data.setDestination(address, G2_DV_PORT);
data.setId(id);
wxStopWatch timer;
timer.Start();
unsigned int out = 0U;
unsigned int dataOut = 0U;
unsigned int needed = (encoder.getInterleavedDataLength() / (DATA_FRAME_LENGTH_BYTES)) * 2U;
while (dataOut < needed) {
data.setSeq(out);
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
// Insert sync bytes when the sequence number is zero, slow data otherwise
if (out == 0U) {
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
} else {
encoder.getInterleavedData(buffer + VOICE_FRAME_LENGTH_BYTES);
dataOut++;
}
data.setData(buffer, DV_FRAME_LENGTH_BYTES);
sendData(data);
out++;
if (out == 21U) out = 0U;
}
data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES);
data.setSeq(out >= 21U ? 0U : out);
data.setEnd(true);
sendData(data);
m_socket.close();
return true;
}
bool CAPRSTransmit::sendHeader(const CHeaderData& header)
{
unsigned char buffer[60U];
unsigned int length = header.getG2Data(buffer, 60U, true);
for (unsigned int i = 0U; i < 2U; i++) {
bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort());
if (!res)
return false;
}
return true;
}
bool CAPRSTransmit::sendData(const CAMBEData& data)
{
unsigned char buffer[60U];
unsigned int length = data.getG2Data(buffer, 60U);
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
unsigned int CAPRSTransmit::calcCRC(const wxString& gpsData)
{
size_t length = gpsData.length();
wxASSERT(length > 0U);
unsigned int icomcrc = 0xFFFFU;
for (unsigned int j = 0U; j < length; j++) {
unsigned char ch = gpsData.GetChar(j);
for (unsigned int i = 0U; i < 8U; i++) {
bool xorflag = (((icomcrc ^ ch) & 0x01U) == 0x01U);
icomcrc >>= 1;
if (xorflag)
icomcrc ^= 0x8408U;
ch >>= 1;
}
}
return ~icomcrc & 0xFFFFU;
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2014 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 TextTransmit_H
#define TextTransmit_H
#include "UDPReaderWriter.h"
#include "HeaderData.h"
#include "AMBEData.h"
class CAPRSTransmit {
public:
CAPRSTransmit(const wxString& callsign, const wxString& text);
~CAPRSTransmit();
bool run();
private:
CUDPReaderWriter m_socket;
wxString m_repeaterCallsign;
wxString m_APRSCallsign;
wxString m_text;
bool sendHeader(const CHeaderData& header);
bool sendData(const CAMBEData& data);
unsigned int calcCRC(const wxString& gpsData);
};
#endif

View file

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{F26EA1DB-74CF-4C52-A425-00235C8ABED2}</ProjectGuid>
<RootNamespace>APRSTransmit</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\$(ProjectName)</IntDir>
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\$(ProjectName)</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="APRSParser.cpp" />
<ClCompile Include="APRSTransmit.cpp" />
<ClCompile Include="APRSTransmitApp.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="APRSParser.h" />
<ClInclude Include="APRSTransmit.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.vcxproj">
<Project>{e793cb8e-2ac9-431a-bbfc-3f52537bb3cf}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="APRSParser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="APRSTransmit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="APRSTransmitApp.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="APRSParser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="APRSTransmit.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,100 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "APRSTransmit.h"
#include <wx/textfile.h>
#include <wx/cmdline.h>
const wxChar* REPEATER_PARAM = wxT("Repeater");
const wxChar* FILE_OPTION = wxT("file");
const wxChar* APRS_OPTION = wxT("aprs");
int main(int argc, char** argv)
{
bool res = ::wxInitialize();
if (!res) {
::fprintf(stderr, "aprstransmit: failed to initialise the wxWidgets library, exiting\n");
return 1;
}
wxCmdLineParser parser(argc, argv);
parser.AddParam(REPEATER_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddOption(APRS_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddOption(FILE_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
int cmd = parser.Parse();
if (cmd != 0) {
::wxUninitialize();
return 1;
}
if (parser.GetParamCount() < 1U) {
::fprintf(stderr, "aprstransmit: invalid command line usage: aprstransmit <repeater> -aprs <aprs_tnc2_string>|-file <filename>, exiting\n");
::wxUninitialize();
return 1;
}
wxString text;
bool aprsFound = parser.Found(APRS_OPTION, &text);
wxString filename;
bool fileFound = parser.Found(FILE_OPTION, &filename);
if (!aprsFound && !fileFound) {
::fprintf(stderr, "aprstransmit: invalid command line usage: aprstransmit <repeater> -aprs <text>|-file <filename>, exiting\n");
::wxUninitialize();
return 1;
}
if (aprsFound && fileFound) {
::fprintf(stderr, "aprstransmit: invalid command line usage: aprstransmit <repeater> -aprs <text>|-file <filename>, exiting\n");
::wxUninitialize();
return 1;
}
wxString repeater = parser.GetParam(0U);
repeater.Replace(wxT("_"), wxT(" "));
repeater.resize(LONG_CALLSIGN_LENGTH, wxT(' '));
repeater.MakeUpper();
if (fileFound) {
wxTextFile file;
bool found = file.Open(filename);
if (!found) {
::fprintf(stderr, "aprstransmit: unable to open the file, exiting\n");
::wxUninitialize();
return 1;
}
text = file.GetFirstLine();
file.Close();
}
CAPRSTransmit tt(repeater, text);
bool ret = tt.run();
::wxUninitialize();
return ret ? 0 : 1;
}

View file

@ -0,0 +1,272 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "APRSTransmit.h"
#include "APRSTransmitAppD.h"
#include "APRSWriterThread.h"
#include <wx/textfile.h>
#include <wx/cmdline.h>
#include <signal.h>
#include <iostream>
#if defined(__WINDOWS__)
#include <Windows.h>
#include <wx/filename.h>
#endif
const wxChar* REPEATER_PARAM = wxT("Repeater");
const wxChar* APRS_HOST = wxT("host");
const wxChar* APRS_PORT = wxT("port");
const wxChar* APRS_FILTER = wxT("filter");
const wxChar* DAEMON_SWITCH = wxT("daemon");
static CAPRSTransmitAppD* m_aprsTransmit = NULL;
static void handler(int signum)
{
m_aprsTransmit->kill();
}
static void aprsFrameCallback(const wxString& aprsFrame)
{
//wxLogMessage(wxT("Received APRS Frame : ") + aprsFrame);
m_aprsTransmit->m_aprsFramesQueue->addData(new wxString(aprsFrame.Clone()));
}
int main(int argc, char** argv)
{
bool res = ::wxInitialize();
if (!res) {
::fprintf(stderr, "aprstransmit: failed to initialise the wxWidgets library, exiting\n");
return 1;
}
wxCmdLineParser parser(argc, argv);
parser.AddParam(REPEATER_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddOption(APRS_HOST, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddOption(APRS_PORT, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddOption(APRS_FILTER, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddSwitch(DAEMON_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL);
int cmd = parser.Parse();
if (cmd != 0) {
::wxUninitialize();
return 1;
}
if (parser.GetParamCount() < 1U) {
::fprintf(stderr, "aprstransmitd: invalid command line usage: aprstransmitd <repeater> [-host <aprs_server>] [-port <aprs_port>] [-filter <aprsis_filter1>[;<aprsis_filter2]] [-daemon] exiting\n");
::wxUninitialize();
return 1;
}
wxString repeater = parser.GetParam(0U);
if (repeater.length() != LONG_CALLSIGN_LENGTH) {
wxString callErrorMsg;
callErrorMsg << wxT("Invalid repeater call. ") << repeater << wxT("Call must be ") << LONG_CALLSIGN_LENGTH << wxT(" characters long.\n");
callErrorMsg << wxT("Valid example : A1ABC__B\n");
::fputs(callErrorMsg.c_str(), stderr);
::wxUninitialize();
return 1;
}
repeater.Replace(wxT("_"), wxT(" "));
repeater.MakeUpper();
long aprsPort;
if(!parser.Found(APRS_PORT, &aprsPort))
aprsPort = 14580;
wxString aprsHost;
if(!parser.Found(APRS_HOST, &aprsHost)){
aprsHost = wxT("rotate.aprs2.net");
}
wxString aprsFilter;
if(!parser.Found(APRS_FILTER, &aprsFilter)) {
/* no filter specified on command line,
build one which will tell the APRS server to pass
us all stations 50km around our repeater */
aprsFilter = wxT("m/50");
}
bool daemon = parser.Found(DAEMON_SWITCH);
#if defined(__WINDOWS__)
m_aprsTransmit = new CAPRSTransmitAppD(repeater, aprsHost, aprsPort, aprsFilter, daemon);
if (!m_aprsTransmit->init()) {
::wxUninitialize();
return 1;
}
#else
if (daemon) {
pid_t pid = ::fork();
if (pid < 0) {
::fprintf(stderr, "aprstransmitd: error in fork(), exiting\n");
::wxUninitialize();
return 1;
}
// If this is the parent, exit
if (pid > 0)
return 0;
// We are the child from here onwards
::setsid();
::chdir("/");
::umask(0);
}
//create a pid file
wxString pidFileName = wxT("/var/run/aprstransmit.pid");
FILE* fp = ::fopen(pidFileName.mb_str(), "wt");
if (fp != NULL) {
::fprintf(fp, "%u\n", ::getpid());
::fclose(fp);
}
m_aprsTransmit = new CAPRSTransmitAppD(repeater, aprsHost, aprsPort, aprsFilter, daemon);
if (!m_aprsTransmit->init()) {
::wxUninitialize();
return 1;
}
::signal(SIGUSR1, handler);
::unlink(pidFileName.mb_str());
#endif
m_aprsTransmit->run();
delete m_aprsTransmit;
::wxUninitialize();
return 0;
}
CAPRSTransmitAppD::CAPRSTransmitAppD(const wxString& repeater, const wxString& aprsHost, unsigned int aprsPort, const wxString& aprsFilter, bool daemon) :
m_aprsFramesQueue(NULL),
m_repeater(repeater),
m_aprsHost(aprsHost),
m_aprsFilter(aprsFilter),
m_aprsPort(aprsPort),
m_aprsThread(NULL),
m_run(false),
m_checker(NULL),
m_daemon(daemon)
{
}
CAPRSTransmitAppD::~CAPRSTransmitAppD()
{
cleanup();
}
bool CAPRSTransmitAppD::init()
{
#if defined(__WINDOWS__)
wxString tempPath = wxFileName::GetTempDir();
m_checker = new wxSingleInstanceChecker(wxT("aprstransmit"), tempPath);
#else
m_checker = new wxSingleInstanceChecker(wxT("aprstransmit"), wxT("/tmp"));
#endif
bool ret = m_checker->IsAnotherRunning();
if (ret) {
wxLogError(wxT("Another copy of APRSTransmit is running, exiting"));
return false;
}
#if defined(__WINDOWS__)
wxLog* logger = new wxLogStream(&std::cout);
wxLog::SetActiveTarget(logger);
wxLog::SetLogLevel(wxLOG_Message);
if(m_daemon)
wxLogMessage(wxT("Daemon not supported under Windows, ignoring"));
m_daemon = false;
#else
if(!m_daemon){
wxLog* logger = new wxLogStream(&std::cout);
wxLog::SetActiveTarget(logger);
wxLog::SetLogLevel(wxLOG_Message);
} else {
new wxLogNull;
}
#endif
return true;
}
void CAPRSTransmitAppD::run()
{
if(m_run) return;
m_aprsFramesQueue = new CRingBuffer<wxString*>(30U);
m_aprsThread = new CAPRSWriterThread(m_repeater, wxT("0.0.0.0"), m_aprsHost, m_aprsPort, m_aprsFilter, wxT("APRSTransmit 1.1"));
m_aprsThread->setReadAPRSCallback(aprsFrameCallback);
m_aprsThread->start();
wxString * aprsFrame;
m_run = true;
while(m_run){
wxMilliSleep(10U);
aprsFrame = m_aprsFramesQueue->getData();
if(aprsFrame){
CAPRSTransmit aprsTransmit(m_repeater, wxString(*aprsFrame));
aprsTransmit.run();
delete aprsFrame;
}
}
m_aprsThread->stop();
cleanup();
}
void CAPRSTransmitAppD::cleanup()
{
m_aprsThread->setReadAPRSCallback(NULL);
if(m_aprsFramesQueue)
{
while(m_aprsFramesQueue->peek()) delete m_aprsFramesQueue->getData();
delete m_aprsFramesQueue;
m_aprsFramesQueue = NULL;
}
if(m_checker) {
delete m_checker;
m_checker = NULL;
}
if(m_aprsThread)
{
delete m_aprsThread;
m_aprsThread = NULL;
}
}
void CAPRSTransmitAppD::kill()
{
m_run = false;
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA
*
* 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 APRSTransmitAppD_H
#define APRSTransmitAppD_H
#include "RingBuffer.h"
#include "APRSWriterThread.h"
#include <wx/wx.h>
#include <wx/config.h>
#include <wx/snglinst.h>
class CAPRSTransmitAppD {
public:
CAPRSTransmitAppD(const wxString& repeater, const wxString& aprsHost, unsigned int aprsPort, const wxString& aprsFilter, bool daemon);
~CAPRSTransmitAppD();
CRingBuffer<wxString*> * m_aprsFramesQueue;
bool init();
void run();
void kill();
private:
wxString m_repeater, m_aprsHost, m_aprsFilter;
unsigned int m_aprsPort;
CAPRSWriterThread * m_aprsThread;
bool m_run;
wxSingleInstanceChecker * m_checker;
bool m_daemon;
void cleanup();
};
#endif

View file

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{C706EF5D-3917-4796-8BEB-823498A1B13C}</ProjectGuid>
<RootNamespace>APRSTransmit</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\$(ProjectName)\</IntDir>
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\$(ProjectName)\</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="APRSParser.cpp" />
<ClCompile Include="APRSTransmit.cpp" />
<ClCompile Include="APRSTransmitAppD.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="APRSParser.h" />
<ClInclude Include="APRSTransmit.h" />
<ClInclude Include="APRSTransmitAppD.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.vcxproj">
<Project>{e793cb8e-2ac9-431a-bbfc-3f52537bb3cf}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="APRSParser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="APRSTransmit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="APRSTransmitAppD.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="APRSParser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="APRSTransmit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="APRSTransmitAppD.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

1478
CHANGES.txt Normal file

File diff suppressed because it is too large Load diff

340
COPYING.txt Normal file
View file

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

629
Common/AMBEData.cpp Normal file
View file

@ -0,0 +1,629 @@
/*
* Copyright (C) 2010-2013 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 "AMBEData.h"
#include "DStarDefines.h"
#include "Utils.h"
CAMBEData::CAMBEData() :
m_rptSeq(0U),
m_outSeq(0U),
m_id(0U),
m_band1(0x00U),
m_band2(0x02U),
m_band3(0x01U),
m_data(NULL),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U),
m_errors(0U),
m_text(),
m_header()
{
m_data = new unsigned char[DV_FRAME_LENGTH_BYTES];
}
CAMBEData::CAMBEData(const CAMBEData& data) :
m_rptSeq(data.m_rptSeq),
m_outSeq(data.m_outSeq),
m_id(data.m_id),
m_band1(data.m_band1),
m_band2(data.m_band2),
m_band3(data.m_band3),
m_data(NULL),
m_yourAddress(data.m_yourAddress),
m_yourPort(data.m_yourPort),
m_myPort(data.m_myPort),
m_errors(data.m_errors),
m_text(data.m_text),
m_header(data.m_header)
{
m_data = new unsigned char[DV_FRAME_LENGTH_BYTES];
::memcpy(m_data, data.m_data, DV_FRAME_LENGTH_BYTES);
}
CAMBEData::~CAMBEData()
{
delete[] m_data;
}
bool CAMBEData::setIcomRepeaterData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 29U);
m_rptSeq = data[4] * 256U + data[5];
m_band1 = data[11];
m_band2 = data[12];
m_band3 = data[13];
m_id = data[14] * 256U + data[15];
m_outSeq = data[16];
// A repeater end packet is longer than usual, so we substitute a normal length set of data
if (isEnd()) {
::memset(m_data, 0x00U, DV_FRAME_LENGTH_BYTES);
::memcpy(m_data, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES);
} else {
::memcpy(m_data, data + 17U, DV_FRAME_LENGTH_BYTES);
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
return true;
}
bool CAMBEData::setHBRepeaterData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 21U);
m_id = data[5U] * 256U + data[6U];
m_outSeq = data[7U];
m_errors = data[8U];
// A repeater end packet is longer than usual, so we substitute a normal length set of data
if (isEnd()) {
::memset(m_data, 0x00U, DV_FRAME_LENGTH_BYTES);
::memcpy(m_data, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES);
} else {
::memcpy(m_data, data + 9U, DV_FRAME_LENGTH_BYTES);
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
return true;
}
bool CAMBEData::setG2Data(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 27U);
m_band1 = data[9];
m_band2 = data[10];
m_band3 = data[11];
m_id = data[12] * 256U + data[13];
m_outSeq = data[14];
::memcpy(m_data, data + 15U, DV_FRAME_LENGTH_BYTES);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
return true;
}
bool CAMBEData::setDExtraData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 27U);
m_band1 = data[9];
m_band2 = data[10];
m_band3 = data[11];
m_id = data[12] * 256U + data[13];
m_outSeq = data[14];
::memcpy(m_data, data + 15U, DV_FRAME_LENGTH_BYTES);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CAMBEData::setDPlusData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 29U);
if ((data[0] != 0x1D && data[0] != 0x20) || data[1] != 0x80) {
CUtils::dump(wxT("Invalid AMBE length from D-Plus"), data, length);
return false;
}
m_band1 = data[11];
m_band2 = data[12];
m_band3 = data[13];
m_id = data[14] * 256U + data[15];
m_outSeq = data[16];
if (isEnd()) {
::memset(m_data, 0x00U, DV_FRAME_LENGTH_BYTES);
::memcpy(m_data, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES);
} else {
::memcpy(m_data, data + 17U, DV_FRAME_LENGTH_BYTES);
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CAMBEData::setDCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 100U);
m_header.setDCSData(data, length, yourAddress, yourPort, myPort);
m_id = data[44] * 256U + data[43];
m_outSeq = data[45];
::memcpy(m_data, data + 46U, DV_FRAME_LENGTH_BYTES);
m_rptSeq = data[60] * 65536U + data[59] * 256U + data[58];
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CAMBEData::setCCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 100U);
m_header.setCCSData(data, length, yourAddress, yourPort, myPort);
m_id = data[44] * 256U + data[43];
m_outSeq = data[45];
::memcpy(m_data, data + 46U, DV_FRAME_LENGTH_BYTES);
m_rptSeq = data[60] * 65536U + data[59] * 256U + data[58];
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
unsigned int CAMBEData::getIcomRepeaterData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 32U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'T';
data[3] = 'R';
data[4] = m_rptSeq / 256U; // Packet sequence number
data[5] = m_rptSeq % 256U;
data[6] = 0x73; // Not a response
data[7] = 0x12; // Data type
data[8] = 0x00; // Length MSB
data[9] = 0x13U; // Length LSB
data[10] = 0x20; // AMBE plus Slow Data following
data[11] = m_band1;
data[12] = m_band2;
data[13] = m_band3;
data[14] = m_id / 256U; // Unique session id
data[15] = m_id % 256U;
data[16] = m_outSeq;
::memcpy(data + 17U, m_data, DV_FRAME_LENGTH_BYTES);
return 17U + DV_FRAME_LENGTH_BYTES;
}
unsigned int CAMBEData::getHBRepeaterData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 21U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'R';
data[3] = 'P';
data[4] = 0x21U;
data[5] = m_id / 256U; // Unique session id
data[6] = m_id % 256U;
data[7] = m_outSeq;
data[8] = 0U;
::memcpy(data + 9U, m_data, DV_FRAME_LENGTH_BYTES);
return 9U + DV_FRAME_LENGTH_BYTES;
}
unsigned int CAMBEData::getG2Data(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 30U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'V';
data[3] = 'T';
data[4] = 0x20;
data[5] = 0x00;
data[6] = 0x15;
data[7] = 0x09;
data[8] = 0x20;
data[9] = m_band1;
data[10] = m_band2;
data[11] = m_band3;
data[12] = m_id / 256U; // Unique session id
data[13] = m_id % 256U;
data[14] = m_outSeq;
::memcpy(data + 15U, m_data, DV_FRAME_LENGTH_BYTES);
return 15U + DV_FRAME_LENGTH_BYTES;
}
unsigned int CAMBEData::getDExtraData(unsigned char* data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 30U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'V';
data[3] = 'T';
data[4] = 0x20;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
data[8] = 0x20;
data[9] = m_band1;
data[10] = m_band2;
data[11] = m_band3;
data[12] = m_id % 256U; // Unique session id
data[13] = m_id / 256U;
data[14] = m_outSeq;
::memcpy(data + 15U, m_data, DV_FRAME_LENGTH_BYTES);
return 15U + DV_FRAME_LENGTH_BYTES;
}
unsigned int CAMBEData::getDPlusData(unsigned char* data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 32U);
if (isEnd()) {
data[0] = 0x20;
data[1] = 0x80;
} else {
data[0] = 0x1D;
data[1] = 0x80;
}
data[2] = 'D';
data[3] = 'S';
data[4] = 'V';
data[5] = 'T';
data[6] = 0x20;
data[7] = 0x00;
data[8] = 0x00;
data[9] = 0x00;
data[10] = 0x20;
data[11] = m_band1;
data[12] = m_band2;
data[13] = m_band3;
data[14] = m_id % 256U; // Unique session id
data[15] = m_id / 256U;
data[16] = m_outSeq;
if (isEnd()) {
::memcpy(data + 17U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
::memcpy(data + 26U, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); // Add the end flag
return 17U + DV_FRAME_MAX_LENGTH_BYTES;
} else {
// All other cases, just copy the payload
::memcpy(data + 17U, m_data, DV_FRAME_LENGTH_BYTES);
return 17U + DV_FRAME_LENGTH_BYTES;
}
}
unsigned int CAMBEData::getDCSData(unsigned char* data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 100U);
::memset(data, 0x00U, 100U);
data[0] = '0';
data[1] = '0';
data[2] = '0';
data[3] = '1';
data[43] = m_id % 256U; // Unique session id
data[44] = m_id / 256U;
data[45] = m_outSeq;
::memcpy(data + 46U, m_data, DV_FRAME_LENGTH_BYTES);
if (isEnd()) {
data[55] = 0x55U;
data[56] = 0x55U;
data[57] = 0x55U;
}
data[58] = (m_rptSeq >> 0) & 0xFFU;
data[59] = (m_rptSeq >> 8) & 0xFFU;
data[60] = (m_rptSeq >> 16) & 0xFFU;
data[61] = 0x01U;
data[62] = 0x00U;
data[63] = 0x21U;
for (unsigned int i = 0U; i < m_text.Len(); i++)
data[64 + i] = m_text.GetChar(i);
m_header.getDCSData(data, 100U);
return 100U;
}
unsigned int CAMBEData::getCCSData(unsigned char* data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 100U);
::memset(data, 0x00U, 100U);
data[0] = '0';
data[1] = '0';
data[2] = '0';
data[3] = '1';
data[43] = m_id % 256U; // Unique session id
data[44] = m_id / 256U;
data[45] = m_outSeq;
::memcpy(data + 46U, m_data, DV_FRAME_LENGTH_BYTES);
if (isEnd()) {
data[55] = 0x55U;
data[56] = 0x55U;
data[57] = 0x55U;
}
data[58] = (m_rptSeq >> 0) & 0xFFU;
data[59] = (m_rptSeq >> 8) & 0xFFU;
data[60] = (m_rptSeq >> 16) & 0xFFU;
data[61] = 0x01U;
data[62] = 0x00U;
data[63] = 0x21U;
for (unsigned int i = 0U; i < m_text.Len(); i++)
data[64 + i] = m_text.GetChar(i);
data[93U] = 0x36U;
m_header.getCCSData(data, 100U);
return 100U;
}
unsigned int CAMBEData::getId() const
{
return m_id;
}
void CAMBEData::setId(unsigned int id)
{
m_id = id;
}
unsigned char CAMBEData::getBand1() const
{
return m_band1;
}
unsigned char CAMBEData::getBand2() const
{
return m_band2;
}
unsigned char CAMBEData::getBand3() const
{
return m_band3;
}
void CAMBEData::setBand1(unsigned char band)
{
m_band1 = band;
}
void CAMBEData::setBand2(unsigned char band)
{
m_band2 = band;
}
void CAMBEData::setBand3(unsigned char band)
{
m_band3 = band;
}
unsigned int CAMBEData::getRptSeq() const
{
return m_rptSeq;
}
void CAMBEData::setRptSeq(unsigned int seqNo)
{
m_rptSeq = seqNo;
}
unsigned int CAMBEData::getSeq() const
{
return m_outSeq & 0x1FU;
}
void CAMBEData::setSeq(unsigned int seqNo)
{
m_outSeq = seqNo;
}
bool CAMBEData::isEnd() const
{
return (m_outSeq & 0x40U) == 0x40U;
}
void CAMBEData::setEnd(bool end)
{
if (end)
m_outSeq |= 0x40U;
else
m_outSeq &= ~0x40U;
}
bool CAMBEData::isSync() const
{
return (m_outSeq & 0x1FU) == 0x00U;
}
void CAMBEData::setDestination(const in_addr& address, unsigned int port)
{
m_yourAddress = address;
m_yourPort = port;
}
void CAMBEData::setText(const wxString& text)
{
m_text = text;
}
in_addr CAMBEData::getYourAddress() const
{
return m_yourAddress;
}
unsigned int CAMBEData::getYourPort() const
{
return m_yourPort;
}
unsigned int CAMBEData::getMyPort() const
{
return m_myPort;
}
CHeaderData& CAMBEData::getHeader()
{
return m_header;
}
unsigned int CAMBEData::getErrors() const
{
return m_errors;
}
void CAMBEData::setData(const unsigned char *data, unsigned int length)
{
wxASSERT(data != NULL);
wxASSERT(length >= DV_FRAME_LENGTH_BYTES);
::memcpy(m_data, data, DV_FRAME_LENGTH_BYTES);
}
unsigned int CAMBEData::getData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= DV_FRAME_LENGTH_BYTES);
::memcpy(data, m_data, DV_FRAME_LENGTH_BYTES);
return DV_FRAME_LENGTH_BYTES;
}
CAMBEData& CAMBEData::operator=(const CAMBEData& data)
{
if (&data != this) {
m_rptSeq = data.m_rptSeq;
m_outSeq = data.m_outSeq;
m_id = data.m_id;
m_band1 = data.m_band1;
m_band2 = data.m_band2;
m_band3 = data.m_band3;
m_yourAddress = data.m_yourAddress;
m_yourPort = data.m_yourPort;
m_myPort = data.m_myPort;
m_errors = data.m_errors;
m_text = data.m_text;
m_header = data.m_header;
::memcpy(m_data, data.m_data, DV_FRAME_LENGTH_BYTES);
}
return *this;
}

108
Common/AMBEData.h Normal file
View file

@ -0,0 +1,108 @@
/*
* Copyright (C) 2010-2013 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 AMBEData_H
#define AMBEData_H
#include "HeaderData.h"
#include <wx/wx.h>
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
class CAMBEData {
public:
CAMBEData();
CAMBEData(const CAMBEData& data);
~CAMBEData();
bool setIcomRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
bool setHBRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
bool setG2Data(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
bool setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
unsigned int getIcomRepeaterData(unsigned char* data, unsigned int length) const;
unsigned int getHBRepeaterData(unsigned char* data, unsigned int length) const;
unsigned int getDExtraData(unsigned char* data, unsigned int length) const;
unsigned int getDPlusData(unsigned char* data, unsigned int length) const;
unsigned int getDCSData(unsigned char* data, unsigned int length) const;
unsigned int getCCSData(unsigned char* data, unsigned int length) const;
unsigned int getG2Data(unsigned char* data, unsigned int length) const;
unsigned int getId() const;
void setId(unsigned int id);
unsigned char getBand1() const;
unsigned char getBand2() const;
unsigned char getBand3() const;
void setBand1(unsigned char band);
void setBand2(unsigned char band);
void setBand3(unsigned char band);
unsigned int getRptSeq() const;
void setRptSeq(unsigned int seqNo);
unsigned int getSeq() const;
void setSeq(unsigned int seqNo);
bool isEnd() const;
void setEnd(bool end);
bool isSync() const;
void setData(const unsigned char* data, unsigned int length);
unsigned int getData(unsigned char* data, unsigned int length) const;
void setDestination(const in_addr& address, unsigned int port);
void setText(const wxString& text);
in_addr getYourAddress() const;
unsigned int getYourPort() const;
unsigned int getMyPort() const;
unsigned int getErrors() const;
CHeaderData& getHeader();
CAMBEData& operator=(const CAMBEData& data);
private:
unsigned int m_rptSeq;
unsigned char m_outSeq;
unsigned int m_id;
unsigned char m_band1;
unsigned char m_band2;
unsigned char m_band3;
unsigned char* m_data;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
unsigned int m_errors;
wxString m_text;
CHeaderData m_header;
};
#endif

661
Common/APRSCollector.cpp Normal file
View file

@ -0,0 +1,661 @@
/*
* Copyright (C) 2010,2012,2013,2014 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 "APRSCollector.h"
#include "DStarDefines.h"
#include "Utils.h"
const unsigned int APRS_CSUM_LENGTH = 4U;
const unsigned int APRS_DATA_LENGTH = 300U;
const unsigned int SLOW_DATA_BLOCK_LENGTH = 6U;
CAPRSCollector::CAPRSCollector() :
m_state(AS_NONE),
m_ggaData(NULL),
m_ggaLength(0U),
m_ggaValid(false),
m_rmcData(NULL),
m_rmcLength(0U),
m_rmcValid(false),
m_crcData(NULL),
m_crcLength(0U),
m_crcValid(false),
m_txtData(NULL),
m_txtLength(0U),
m_txtValid(false),
m_buffer(NULL),
m_slowData(SS_FIRST)
{
m_ggaData = new unsigned char[APRS_DATA_LENGTH];
m_rmcData = new unsigned char[APRS_DATA_LENGTH];
m_crcData = new unsigned char[APRS_DATA_LENGTH];
m_txtData = new unsigned char[APRS_DATA_LENGTH];
m_buffer = new unsigned char[SLOW_DATA_BLOCK_LENGTH];
}
CAPRSCollector::~CAPRSCollector()
{
delete[] m_ggaData;
delete[] m_rmcData;
delete[] m_crcData;
delete[] m_txtData;
delete[] m_buffer;
}
bool CAPRSCollector::writeData(const unsigned char* data)
{
wxASSERT(data != NULL);
switch (m_slowData) {
case SS_FIRST:
m_buffer[0U] = data[0U] ^ SCRAMBLER_BYTE1;
m_buffer[1U] = data[1U] ^ SCRAMBLER_BYTE2;
m_buffer[2U] = data[2U] ^ SCRAMBLER_BYTE3;
m_slowData = SS_SECOND;
return false;
case SS_SECOND:
m_buffer[3U] = data[0U] ^ SCRAMBLER_BYTE1;
m_buffer[4U] = data[1U] ^ SCRAMBLER_BYTE2;
m_buffer[5U] = data[2U] ^ SCRAMBLER_BYTE3;
m_slowData = SS_FIRST;
break;
}
// Is it GPS data, or are we collecting data already?
if ((m_buffer[0U] & SLOW_DATA_TYPE_MASK) != SLOW_DATA_TYPE_GPS)
return false;
return addData(m_buffer + 1U);
}
void CAPRSCollector::reset()
{
m_state = AS_NONE;
m_ggaLength = 0U;
m_ggaValid = false;
m_rmcLength = 0U;
m_rmcValid = false;
m_crcLength = 0U;
m_crcValid = false;
m_txtLength = 0U;
m_txtValid = false;
m_slowData = SS_FIRST;
}
void CAPRSCollector::sync()
{
m_slowData = SS_FIRST;
}
bool CAPRSCollector::addData(const unsigned char* data)
{
wxASSERT(data != NULL);
if (::memcmp(data, "$GPGG", 5U) == 0) {
m_state = AS_GGA;
m_ggaLength = 0U;
m_ggaValid = false;
m_rmcLength = 0U;
m_rmcValid = false;
m_txtLength = 0U;
m_txtValid = false;
addGGAData(data);
return false;
} else if (::memcmp(data, "$GPRM", 5U) == 0) {
m_state = AS_RMC;
m_rmcLength = 0U;
m_rmcValid = false;
m_txtLength = 0U;
m_txtValid = false;
addRMCData(data);
return false;
} else if (::memcmp(data, "$$CRC", 5U) == 0) {
m_state = AS_CRC;
m_crcLength = 0U;
m_crcValid = false;
return addCRCData(data);
} else if (m_state == AS_RMC && m_rmcLength == 0U) {
m_state = AS_TXT;
m_txtLength = 0U;
m_txtValid = false;
addTXTData(data);
return false;
} else if (m_state == AS_GGA) {
addGGAData(data);
return false;
} else if (m_state == AS_RMC) {
addRMCData(data);
return false;
} else if (m_state == AS_CRC) {
return addCRCData(data);
} else if (m_state == AS_TXT) {
return addTXTData(data);
}
return false;
}
void CAPRSCollector::addGGAData(const unsigned char* data)
{
for (unsigned int i = 0U; i < 5U; i++) {
unsigned char c = data[i];
m_ggaData[m_ggaLength] = c & 0x7FU;
m_ggaLength++;
if (m_ggaLength >= APRS_DATA_LENGTH) {
// CUtils::dump(wxT("Missed end of $GPGGA data"), m_ggaData, m_ggaLength);
m_ggaLength = 0U;
m_ggaValid = false;
m_state = AS_NONE;
return;
}
if (c == 0x0AU) {
bool ret = checkXOR(m_ggaData + 1U, m_ggaLength - 1U);
if (ret) {
// CUtils::dump(wxT("$GPGGA Valid"), m_ggaData, m_ggaLength);
m_ggaValid = true;
m_state = AS_RMC;
return;
} else {
// CUtils::dump(wxT("$GPGGA Bad checksum"), m_ggaData, m_ggaLength);
m_ggaLength = 0U;
m_ggaValid = false;
m_state = AS_RMC;
return;
}
}
}
}
void CAPRSCollector::addRMCData(const unsigned char* data)
{
for (unsigned int i = 0U; i < 5U; i++) {
unsigned char c = data[i];
m_rmcData[m_rmcLength] = c & 0x7FU;
m_rmcLength++;
if (m_rmcLength >= APRS_DATA_LENGTH) {
// CUtils::dump(wxT("Missed end of $GPRMC data"), m_rmcData, m_rmcLength);
m_rmcLength = 0U;
m_rmcValid = false;
m_state = AS_NONE;
return;
}
if (c == 0x0AU) {
bool ret = checkXOR(m_rmcData + 1U, m_rmcLength - 1U);
if (ret) {
// CUtils::dump(wxT("$GPRMC Valid"), m_rmcData, m_rmcLength);
m_rmcValid = true;
m_state = AS_TXT;
return;
} else {
// CUtils::dump(wxT("$GPRMC Bad checksum"), m_rmcData, m_rmcLength);
m_rmcLength = 0U;
m_rmcValid = false;
m_state = AS_TXT;
return;
}
}
}
}
bool CAPRSCollector::addCRCData(const unsigned char* data)
{
for (unsigned int i = 0U; i < 5U; i++) {
unsigned char c = data[i];
// m_crcData[m_crcLength] = c & 0x7FU; // XXX
m_crcData[m_crcLength] = c;
m_crcLength++;
if (m_crcLength >= APRS_DATA_LENGTH) {
// CUtils::dump(wxT("Missed end of $$CRC data"), m_crcData, m_crcLength);
m_state = AS_NONE;
m_crcLength = 0U;
m_crcValid = false;
return false;
}
if (c == 0x0DU) {
bool ret = checkCRC(m_crcData, m_crcLength);
if (ret) {
// CUtils::dump(wxT("$$CRC Valid"), m_crcData, m_crcLength);
m_state = AS_NONE;
m_crcValid = true;
return true;
} else {
// CUtils::dump(wxT("$$CRC Bad checksum"), m_crcData, m_crcLength);
m_state = AS_NONE;
m_crcLength = 0U;
m_crcValid = false;
return false;
}
}
}
return false;
}
bool CAPRSCollector::addTXTData(const unsigned char* data)
{
for (unsigned int i = 0U; i < 5U; i++) {
unsigned char c = data[i];
m_txtData[m_txtLength] = c & 0x7FU;
m_txtLength++;
if (m_txtLength >= APRS_DATA_LENGTH) {
// CUtils::dump(wxT("Missed end of TEXT data"), m_txtData, m_txtLength);
m_state = AS_NONE;
m_txtLength = 0U;
m_txtValid = false;
return false;
}
if (c == 0x0AU) {
bool ret = checkXOR(m_txtData, m_txtLength);
if (ret) {
// CUtils::dump(wxT("TEXT Valid"), m_txtData, m_txtLength);
m_state = AS_NONE;
m_txtValid = true;
return true;
} else {
// CUtils::dump(wxT("TEXT Bad checksum"), m_txtData, m_txtLength);
m_state = AS_NONE;
m_txtLength = 0U;
m_txtValid = false;
return false;
}
}
}
return false;
}
unsigned int CAPRSCollector::getData(unsigned char* data, unsigned int length)
{
wxASSERT(data != NULL);
// Have we got GPS-A data?
if (m_crcValid) {
unsigned int len = m_crcLength - 10U;
if (len > length)
len = length;
::memcpy(data, m_crcData + 10U, len);
m_crcLength = 0U;
m_crcValid = false;
return len;
}
// Have we got GGA and text data?
if (m_ggaValid && m_txtValid) {
unsigned int len = convertNMEA1(data, length);
m_ggaLength = 0U;
m_rmcLength = 0U;
m_txtLength = 0U;
m_ggaValid = false;
m_rmcValid = false;
m_txtValid = false;
return len;
}
// Have we got RMC and text data?
if (m_rmcValid && m_txtValid) {
unsigned int len = convertNMEA2(data, length);
m_ggaLength = 0U;
m_rmcLength = 0U;
m_txtLength = 0U;
m_ggaValid = false;
m_rmcValid = false;
m_txtValid = false;
return len;
}
return 0U;
}
bool CAPRSCollector::checkXOR(const unsigned char* data, unsigned int length) const
{
unsigned int posStar = 0U;
for (unsigned int i = length - 1U; i > 0U; i--) {
if (data[i] == '*') {
posStar = i;
break;
}
}
if (posStar == 0U)
return false;
unsigned char csum = calcXOR(data, posStar);
char buffer[10U];
::sprintf(buffer, "%02X", csum);
return ::memcmp(buffer, data + posStar + 1U, 2U) == 0;
}
unsigned char CAPRSCollector::calcXOR(const unsigned char* buffer, unsigned int length) const
{
wxASSERT(buffer != NULL);
wxASSERT(length > 0U);
unsigned char res = 0U;
for (unsigned int i = 0U; i < length; i++)
res ^= buffer[i];
return res;
}
bool CAPRSCollector::checkCRC(const unsigned char* data, unsigned int length) const
{
unsigned int csum = calcCRC(data + 10U, length - 10U);
char buffer[10U];
::sprintf(buffer, "%04X", csum);
return ::memcmp(buffer, data + 5U, APRS_CSUM_LENGTH) == 0;
}
unsigned int CAPRSCollector::calcCRC(const unsigned char* buffer, unsigned int length) const
{
wxASSERT(buffer != NULL);
wxASSERT(length > 0U);
unsigned int icomcrc = 0xFFFFU;
for (unsigned int j = 0U; j < length; j++) {
unsigned char ch = buffer[j];
for (unsigned int i = 0U; i < 8U; i++) {
bool xorflag = (((icomcrc ^ ch) & 0x01U) == 0x01U);
icomcrc >>= 1;
if (xorflag)
icomcrc ^= 0x8408U;
ch >>= 1;
}
}
return ~icomcrc & 0xFFFFU;
}
unsigned int CAPRSCollector::convertNMEA1(unsigned char* data, unsigned int)
{
// Parse the $GPGGA string into tokens
char* pGGA[20U];
::memset(pGGA, 0x00U, 20U * sizeof(char*));
unsigned int nGGA = 0U;
char* str = (char*)m_ggaData;
for (;;) {
char* p = mystrsep(&str, ",\r\n");
pGGA[nGGA++] = p;
if (p == NULL)
break;
}
// Is there any position data?
if (pGGA[2U] == NULL || pGGA[3U] == NULL || pGGA[4U] == NULL || pGGA[5U] == NULL || ::strlen(pGGA[2U]) == 0U || ::strlen(pGGA[3U]) == 0U || ::strlen(pGGA[4U]) == 0 || ::strlen(pGGA[5U]) == 0)
return 0U;
// Is it a valid GPS fix?
if (::strcmp(pGGA[6U], "0") == 0)
return 0U;
char callsign[10U];
::memset(callsign, ' ', 10U);
::strncpy(callsign, (char*)m_txtData, 7U);
// This can't fail!
char* p = ::strchr(callsign, ' ');
if (m_txtData[6U] == ' ' && m_txtData[7U] != ' ') {
*p++ = '-';
*p++ = m_txtData[7U];
} else if (m_txtData[6U] != ' ' && m_txtData[7U] != ' ') {
*p++ = m_txtData[7U];
}
*p = '\0';
char symbol, overlay;
getSymbol(m_txtData + 9U, symbol, overlay);
::sprintf((char*)data, "%s>APDPRS,DSTAR*:!%.7s%s%c%.8s%s%c", callsign, pGGA[2U], pGGA[3U], overlay, pGGA[4U], pGGA[5U], symbol);
// Get the bearing and speed from the RMC data
if (m_rmcValid) {
// Parse the $GPRMC string into tokens
char* pRMC[20U];
::memset(pRMC, 0x00U, 20U * sizeof(char*));
unsigned int nRMC = 0U;
str = (char*)m_rmcData;
for (;;) {
p = mystrsep(&str, ",\r\n");
pRMC[nRMC++] = p;
if (p == NULL)
break;
}
// Check that we have a bearing and speed
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((char*)data + ::strlen((char*)data), "%03d/%03d", bearing, speed);
}
}
::strcat((char*)data, " ");
// Insert the message text
unsigned int j = ::strlen((char*)data);
for (unsigned int i = 13U; i < 29U; i++) {
unsigned char c = m_txtData[i];
if (c == '*') {
data[j] = '\0';
break;
}
data[j++] = c;
}
if (pGGA[9U] != NULL && ::strlen(pGGA[9U]) > 0U) {
// Convert altitude from metres to feet
int altitude = ::atoi(pGGA[9U]);
::sprintf((char*)data + ::strlen((char*)data), "/A=%06.0f", float(altitude) * 3.28F);
}
return ::strlen((char*)data);
}
unsigned int CAPRSCollector::convertNMEA2(unsigned char* data, unsigned int)
{
// Parse the $GPRMC string into tokens
char* pRMC[20U];
::memset(pRMC, 0x00U, 20U * sizeof(char*));
unsigned int nRMC = 0U;
char* str = (char*)m_rmcData;
for (;;) {
char* p = mystrsep(&str, ",\r\n");
pRMC[nRMC++] = p;
if (p == NULL)
break;
}
// 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 0U;
// Is it a valid GPS fix?
if (::strcmp(pRMC[2U], "A") != 0)
return 0U;
char callsign[10U];
::memset(callsign, ' ', 10U);
::strncpy(callsign, (char*)m_txtData, 7U);
// This can't fail!
char* p = ::strchr(callsign, ' ');
if (m_txtData[6U] == ' ' && m_txtData[7U] != ' ') {
*p++ = '-';
*p++ = m_txtData[7U];
} else if (m_txtData[6U] != ' ' && m_txtData[7U] != ' ') {
*p++ = m_txtData[7U];
}
*p = '\0';
char symbol, overlay;
getSymbol(m_txtData + 9U, symbol, overlay);
::sprintf((char*)data, "%s>APDPRS,DSTAR*:!%.7s%s%c%.8s%s%c", callsign, pRMC[3U], pRMC[4U], overlay, pRMC[5U], pRMC[6U], symbol);
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((char*)data + ::strlen((char*)data), "%03d/%03d", bearing, speed);
}
if (m_txtData[13U] != '*')
::strcat((char*)data, " ");
// Insert the message text
unsigned int j = ::strlen((char*)data);
for (unsigned int i = 13U; i < 29U; i++) {
unsigned char c = m_txtData[i];
if (c == '*') {
data[j] = '\0';
break;
}
data[j++] = c;
}
return ::strlen((char*)data);
}
// Function taken from DPRSIntf.java from Pete Loveall AE5PL
void CAPRSCollector::getSymbol(const unsigned char* data, char& symbol, char& overlay)
{
symbol = '.';
if (data[3U] == ' ') {
int offset = -1;
switch (data[0U]) {
case 'B':
case 'O':
offset = -33;
break;
case 'P':
case 'A':
offset = 0;
break;
case 'M':
case 'N':
offset = -24;
break;
case 'H':
case 'D':
offset = 8;
break;
case 'L':
case 'S':
offset = 32;
break;
case 'J':
case 'Q':
offset = 74;
break;
default:
break;
}
if (offset != -1 && ::isalnum(data[1U])) {
bool altIcons = false;
// x is valid, lets get y
switch (data[0U]) {
case 'O':
case 'A':
case 'N':
case 'D':
case 'S':
case 'Q':
altIcons = true;
break;
}
symbol = char(data[1U] + offset);
overlay = '/';
if (altIcons) {
if (data[2] == ' ')
overlay = '\\';
else if (::isalnum(data[2]))
overlay = data[2U];
else
overlay = 0;
}
}
}
}
// Source found at <http://unixpapa.com/incnote/string.html>
char* CAPRSCollector::mystrsep(char** sp, const char* sep) const
{
if (sp == NULL || *sp == NULL || **sp == '\0')
return NULL;
char* s = *sp;
char* p = s + ::strcspn(s, sep);
if (*p != '\0')
*p++ = '\0';
*sp = p;
return s;
}

84
Common/APRSCollector.h Normal file
View file

@ -0,0 +1,84 @@
/*
* Copyright (C) 2010,2012 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 APRSCollector_H
#define APRSCollector_H
#include "Defs.h"
#include <wx/wx.h>
enum APRS_STATE {
AS_NONE,
AS_GGA,
AS_RMC,
AS_CRC,
AS_TXT
};
class CAPRSCollector {
public:
CAPRSCollector();
~CAPRSCollector();
bool writeData(const unsigned char* data);
void sync();
void reset();
unsigned int getData(unsigned char* data, unsigned int length);
private:
APRS_STATE m_state;
unsigned char* m_ggaData;
unsigned int m_ggaLength;
bool m_ggaValid;
unsigned char* m_rmcData;
unsigned int m_rmcLength;
bool m_rmcValid;
unsigned char* m_crcData;
unsigned int m_crcLength;
bool m_crcValid;
unsigned char* m_txtData;
unsigned int m_txtLength;
bool m_txtValid;
unsigned char* m_buffer;
SLOWDATA_STATE m_slowData;
bool addData(const unsigned char* data);
void addGGAData(const unsigned char* data);
void addRMCData(const unsigned char* data);
bool addCRCData(const unsigned char* data);
bool addTXTData(const unsigned char* data);
bool checkXOR(const unsigned char* data, unsigned int length) const;
unsigned char calcXOR(const unsigned char* buffer, unsigned int length) const;
bool checkCRC(const unsigned char* data, unsigned int length) const;
unsigned int calcCRC(const unsigned char* buffer, unsigned int length) const;
unsigned int convertNMEA1(unsigned char* data, unsigned int length);
unsigned int convertNMEA2(unsigned char* data, unsigned int length);
void getSymbol(const unsigned char* data, char& symbol, char& overlay);
char* mystrsep(char** sp, const char* sep) const;
};
#endif

394
Common/APRSWriter.cpp Normal file
View file

@ -0,0 +1,394 @@
/*
* Copyright (C) 2010-2014 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 "DStarDefines.h"
#include "Defs.h"
CAPRSEntry::CAPRSEntry(const wxString& callsign, const wxString& band, double frequency, double offset, double range, double latitude, double longitude, double agl) :
m_callsign(callsign),
m_band(band),
m_frequency(frequency),
m_offset(offset),
m_range(range),
m_latitude(latitude),
m_longitude(longitude),
m_agl(agl),
m_timer(1000U, 10U),
m_first(true),
m_collector(NULL)
{
m_callsign.Trim();
m_collector = new CAPRSCollector;
}
CAPRSEntry::~CAPRSEntry()
{
delete m_collector;
}
wxString CAPRSEntry::getCallsign() const
{
return m_callsign;
}
wxString CAPRSEntry::getBand() const
{
return m_band;
}
double CAPRSEntry::getFrequency() const
{
return m_frequency;
}
double CAPRSEntry::getOffset() const
{
return m_offset;
}
double CAPRSEntry::getRange() const
{
return m_range;
}
double CAPRSEntry::getLatitude() const
{
return m_latitude;
}
double CAPRSEntry::getLongitude() const
{
return m_longitude;
}
double CAPRSEntry::getAGL() const
{
return m_agl;
}
CAPRSCollector* CAPRSEntry::getCollector() const
{
return m_collector;
}
void CAPRSEntry::reset()
{
m_first = true;
m_timer.stop();
m_collector->reset();
}
void CAPRSEntry::clock(unsigned int ms)
{
m_timer.clock(ms);
}
bool CAPRSEntry::isOK()
{
if (m_first) {
m_first = false;
m_timer.start();
return true;
}
if (m_timer.hasExpired()) {
m_timer.start();
return true;
} else {
m_timer.start();
return false;
}
}
CAPRSWriter::CAPRSWriter(const wxString& hostname, unsigned int port, const wxString& gateway, const wxString& address) :
m_thread(NULL),
m_enabled(false),
m_idTimer(1000U, 20U * 60U), // 20 minutes
m_gateway(),
m_array()
{
wxASSERT(!hostname.IsEmpty());
wxASSERT(port > 0U);
wxASSERT(!gateway.IsEmpty());
m_thread = new CAPRSWriterThread(gateway, address, hostname, port);
m_gateway = gateway;
m_gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U);
m_gateway.Trim();
}
CAPRSWriter::~CAPRSWriter()
{
for (CEntry_t::iterator it = m_array.begin(); it != m_array.end(); ++it)
delete it->second;
m_array.clear();
}
void CAPRSWriter::setPort(const wxString& callsign, const wxString& band, double frequency, double offset, double range, double latitude, double longitude, double agl)
{
wxString temp = callsign;
temp.resize(LONG_CALLSIGN_LENGTH - 1U, wxT(' '));
temp.Append(band);
m_array[temp] = new CAPRSEntry(callsign, band, frequency, offset, range, latitude, longitude, agl);
}
bool CAPRSWriter::open()
{
return m_thread->start();
}
void CAPRSWriter::writeData(const wxString& callsign, const CAMBEData& data)
{
if (data.isEnd())
return;
CAPRSEntry* entry = m_array[callsign];
if (entry == NULL) {
wxLogError(wxT("Cannot find the callsign \"%s\" in the APRS array"), callsign.c_str());
return;
}
CAPRSCollector* collector = entry->getCollector();
if (data.isSync()) {
collector->sync();
return;
}
unsigned char buffer[400U];
data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES);
bool complete = collector->writeData(buffer + VOICE_FRAME_LENGTH_BYTES);
if (!complete)
return;
if (!m_enabled) {
collector->reset();
return;
}
if (!m_thread->isConnected()) {
collector->reset();
return;
}
// Check the transmission timer
bool ok = entry->isOK();
if (!ok) {
collector->reset();
return;
}
unsigned int length = collector->getData(buffer, 400U);
wxString text((char*)buffer, wxConvLocal, length);
int n = text.Find(wxT(':'));
if (n == wxNOT_FOUND) {
collector->reset();
return;
}
wxString header = text.Left(n);
wxString body = text.Mid(n + 1U);
// If we already have a q-construct, don't send it on
n = header.Find(wxT('q'));
if (n != wxNOT_FOUND)
return;
// Remove the trailing \r
n = body.Find(wxT('\r'));
if (n != wxNOT_FOUND)
body = body.Left(n);
wxString output;
output.Printf(wxT("%s,qAR,%s-%s:%s"), header.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), body.c_str());
char ascii[500U];
::memset(ascii, 0x00, 500U);
for (unsigned int i = 0U; i < output.Len(); i++)
ascii[i] = output.GetChar(i);
m_thread->write(ascii);
collector->reset();
}
void CAPRSWriter::reset(const wxString& callsign)
{
CAPRSEntry* entry = m_array[callsign];
if (entry == NULL) {
wxLogError(wxT("Cannot find the callsign \"%s\" in the APRS array"), callsign.c_str());
return;
}
entry->reset();
}
void CAPRSWriter::setEnabled(bool enabled)
{
m_enabled = enabled;
if (m_enabled) {
sendIdFrames();
m_idTimer.start();
}
}
void CAPRSWriter::clock(unsigned int ms)
{
m_idTimer.clock(ms);
if (m_idTimer.hasExpired()) {
sendIdFrames();
m_idTimer.start();
}
for (CEntry_t::iterator it = m_array.begin(); it != m_array.end(); ++it)
it->second->clock(ms);
}
bool CAPRSWriter::isConnected() const
{
return m_thread->isConnected();
}
void CAPRSWriter::close()
{
m_thread->stop();
}
void CAPRSWriter::sendIdFrames()
{
if (!m_thread->isConnected())
return;
time_t now;
::time(&now);
struct tm* tm = ::gmtime(&now);
for (CEntry_t::iterator it = m_array.begin(); it != m_array.end(); ++it) {
CAPRSEntry* entry = it->second;
if (entry == NULL)
continue;
// Default values aren't passed on
if (entry->getLatitude() == 0.0 && entry->getLongitude() == 0.0)
continue;
wxString desc;
if (entry->getBand().Len() > 1U) {
if (entry->getFrequency() != 0.0)
desc.Printf(wxT("Data %.5lfMHz"), entry->getFrequency());
else
desc = wxT("Data");
} else {
if (entry->getFrequency() != 0.0)
desc.Printf(wxT("Voice %.5lfMHz %c%.4lfMHz"),
entry->getFrequency(),
entry->getOffset() < 0.0 ? wxT('-') : wxT('+'),
::fabs(entry->getOffset()));
else
desc = wxT("Voice");
}
wxString band;
if (entry->getFrequency() >= 1200.0)
band = wxT("1.2");
else if (entry->getFrequency() >= 420.0)
band = wxT("440");
else if (entry->getFrequency() >= 144.0)
band = wxT("2m");
else if (entry->getFrequency() >= 50.0)
band = wxT("6m");
else if (entry->getFrequency() >= 28.0)
band = wxT("10m");
double tempLat = ::fabs(entry->getLatitude());
double tempLong = ::fabs(entry->getLongitude());
double latitude = ::floor(tempLat);
double longitude = ::floor(tempLong);
latitude = (tempLat - latitude) * 60.0 + latitude * 100.0;
longitude = (tempLong - longitude) * 60.0 + longitude * 100.0;
wxString lat;
if (latitude >= 1000.0F)
lat.Printf(wxT("%.2lf"), latitude);
else if (latitude >= 100.0F)
lat.Printf(wxT("0%.2lf"), latitude);
else if (latitude >= 10.0F)
lat.Printf(wxT("00%.2lf"), latitude);
else
lat.Printf(wxT("000%.2lf"), latitude);
wxString lon;
if (longitude >= 10000.0F)
lon.Printf(wxT("%.2lf"), longitude);
else if (longitude >= 1000.0F)
lon.Printf(wxT("0%.2lf"), longitude);
else if (longitude >= 100.0F)
lon.Printf(wxT("00%.2lf"), longitude);
else if (longitude >= 10.0F)
lon.Printf(wxT("000%.2lf"), longitude);
else
lon.Printf(wxT("0000%.2lf"), longitude);
// Convert commas to periods in the latitude and longitude
lat.Replace(wxT(","), wxT("."));
lon.Replace(wxT(","), wxT("."));
wxString output;
output.Printf(wxT("%s-S>APDG01,TCPIP*,qAC,%s-GS:;%-7s%-2s*%02d%02d%02dz%s%cD%s%caRNG%04.0lf %s %s"),
m_gateway.c_str(), m_gateway.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(),
tm->tm_mday, tm->tm_hour, tm->tm_min,
lat.c_str(), (entry->getLatitude() < 0.0F) ? wxT('S') : wxT('N'),
lon.c_str(), (entry->getLongitude() < 0.0F) ? wxT('W') : wxT('E'),
entry->getRange() * 0.6214, band.c_str(), desc.c_str());
char ascii[300U];
::memset(ascii, 0x00, 300U);
for (unsigned int i = 0U; i < output.Len(); i++)
ascii[i] = output.GetChar(i);
m_thread->write(ascii);
if (entry->getBand().Len() == 1U) {
output.Printf(wxT("%s-%s>APDG02,TCPIP*,qAC,%s-%sS:!%s%cD%s%c&RNG%04.0lf %s %s"),
entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(),
lat.c_str(), (entry->getLatitude() < 0.0F) ? wxT('S') : wxT('N'),
lon.c_str(), (entry->getLongitude() < 0.0F) ? wxT('W') : wxT('E'),
entry->getRange() * 0.6214, band.c_str(), desc.c_str());
::memset(ascii, 0x00, 300U);
for (unsigned int i = 0U; i < output.Len(); i++)
ascii[i] = output.GetChar(i);
m_thread->write(ascii);
}
}
m_idTimer.start();
}

98
Common/APRSWriter.h Normal file
View file

@ -0,0 +1,98 @@
/*
* Copyright (C) 2010,2011,2012 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 "APRSCollector.h"
#include "DStarDefines.h"
#include "AMBEData.h"
#include "Timer.h"
#include "Defs.h"
#include <wx/wx.h>
class CAPRSEntry {
public:
CAPRSEntry(const wxString& callsign, const wxString& band, double frequency, double offset, double range, double latitude, double longitude, double agl);
~CAPRSEntry();
wxString getCallsign() const;
wxString getBand() const;
double getFrequency() const;
double getOffset() const;
double getRange() const;
double getLatitude() const;
double getLongitude() const;
double getAGL() const;
CAPRSCollector* getCollector() const;
// Transmission timer
void reset();
void clock(unsigned int ms);
bool isOK();
private:
wxString m_callsign;
wxString m_band;
double m_frequency;
double m_offset;
double m_range;
double m_latitude;
double m_longitude;
double m_agl;
CTimer m_timer;
bool m_first;
CAPRSCollector* m_collector;
};
WX_DECLARE_STRING_HASH_MAP(CAPRSEntry*, CEntry_t);
class CAPRSWriter {
public:
CAPRSWriter(const wxString& hostname, unsigned int port, const wxString& gateway, const wxString& address);
~CAPRSWriter();
bool open();
void setPort(const wxString& callsign, const wxString& band, double frequency, double offset, double range, double latitude, double longitude, double agl);
void writeData(const wxString& callsign, const CAMBEData& data);
void reset(const wxString& callsign);
void setEnabled(bool enable);
bool isConnected() const;
void clock(unsigned int ms);
void close();
private:
CAPRSWriterThread* m_thread;
bool m_enabled;
CTimer m_idTimer;
wxString m_gateway;
CEntry_t m_array;
void sendIdFrames();
};
#endif

263
Common/APRSWriterThread.cpp Normal file
View file

@ -0,0 +1,263 @@
/*
* Copyright (C) 2010-2014 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 "DStarDefines.h"
#include "Utils.h"
#include "Defs.h"
// #define DUMP_TX
const unsigned int APRS_TIMEOUT = 10U;
CAPRSWriterThread::CAPRSWriterThread(const wxString& callsign, const wxString& address, const wxString& hostname, unsigned int port) :
wxThread(wxTHREAD_JOINABLE),
m_username(callsign),
m_ssid(callsign),
m_socket(hostname, port, address),
m_queue(20U),
m_exit(false),
m_connected(false),
m_APRSReadCallback(NULL),
m_filter(wxT("")),
m_clientName(wxT("ircDDBGateway"))
{
wxASSERT(!callsign.IsEmpty());
wxASSERT(!hostname.IsEmpty());
wxASSERT(port > 0U);
m_username.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' '));
m_username.Trim();
m_username.MakeUpper();
m_ssid = m_ssid.SubString(LONG_CALLSIGN_LENGTH - 1U, 1);
}
CAPRSWriterThread::CAPRSWriterThread(const wxString& callsign, const wxString& address, const wxString& hostname, unsigned int port, const wxString& filter, const wxString& clientName) :
wxThread(wxTHREAD_JOINABLE),
m_username(callsign),
m_ssid(callsign),
m_socket(hostname, port, address),
m_queue(20U),
m_exit(false),
m_connected(false),
m_APRSReadCallback(NULL),
m_filter(filter),
m_clientName(clientName)
{
wxASSERT(!callsign.IsEmpty());
wxASSERT(!hostname.IsEmpty());
wxASSERT(port > 0U);
m_username.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' '));
m_username.Trim();
m_username.MakeUpper();
m_ssid = m_ssid.SubString(LONG_CALLSIGN_LENGTH - 1U, 1);
}
CAPRSWriterThread::~CAPRSWriterThread()
{
m_username.Clear();
}
bool CAPRSWriterThread::start()
{
Create();
Run();
return true;
}
void* CAPRSWriterThread::Entry()
{
wxLogMessage(wxT("Starting the APRS Writer thread"));
m_connected = connect();
try {
while (!m_exit) {
if (!m_connected) {
m_connected = connect();
if (!m_connected){
wxLogError(wxT("Reconnect attempt to the APRS server has failed"));
Sleep(10000UL); // 10 secs
}
}
if (m_connected) {
if(!m_queue.isEmpty()){
char* p = m_queue.getData();
wxString text(p, wxConvLocal);
wxLogMessage(wxT("APRS ==> %s"), text.c_str());
::strcat(p, "\r\n");
bool ret = m_socket.write((unsigned char*)p, ::strlen(p));
if (!ret) {
m_connected = false;
m_socket.close();
wxLogError(wxT("Connection to the APRS thread has failed"));
}
delete[] p;
}
{
wxString line;
int length = m_socket.readLine(line, APRS_TIMEOUT);
/*if (length == 0)
wxLogWarning(wxT("No response from the APRS server after %u seconds"), APRS_TIMEOUT);*/
if (length < 0) {
m_connected = false;
m_socket.close();
wxLogError(wxT("Error when reading from the APRS server"));
}
if(length > 0 && line.GetChar(0) != '#'//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(wxString(line));
}
}
}
}
if (m_connected)
m_socket.close();
while (!m_queue.isEmpty()) {
char* p = m_queue.getData();
delete[] p;
}
}
catch (std::exception& e) {
wxString message(e.what(), wxConvLocal);
wxLogError(wxT("Exception raised in the APRS Writer thread - \"%s\""), message.c_str());
}
catch (...) {
wxLogError(wxT("Unknown exception raised in the APRS Writer thread"));
}
wxLogMessage(wxT("Stopping the APRS Writer thread"));
return NULL;
}
void CAPRSWriterThread::setReadAPRSCallback(ReadAPRSFrameCallback cb)
{
m_APRSReadCallback = cb;
}
void CAPRSWriterThread::write(const char* data)
{
wxASSERT(data != NULL);
if (!m_connected)
return;
unsigned int len = ::strlen(data);
char* p = new char[len + 5U];
::strcpy(p, data);
m_queue.addData(p);
}
bool CAPRSWriterThread::isConnected() const
{
return m_connected;
}
void CAPRSWriterThread::stop()
{
m_exit = true;
Wait();
}
bool CAPRSWriterThread::connect()
{
unsigned int password = getAPRSPassword(m_username);
bool ret = m_socket.open();
if (!ret)
return false;
//wait for lgin banner
int length;
wxString serverResponse(wxT(""));
length = m_socket.readLine(serverResponse, APRS_TIMEOUT);
if (length == 0) {
wxLogError(wxT("No reply from the APRS server after %u seconds"), APRS_TIMEOUT);
m_socket.close();
return false;
}
wxLogMessage(wxT("Received login banner : ") + serverResponse);
wxString filter(m_filter);
if (filter.Length() > 0) filter.Prepend(wxT(" filter "));
wxString connectString = wxString::Format(wxT("user %s-%s pass %u vers %s%s\n"), m_username.c_str(), m_ssid.c_str(), password,
(m_clientName.Length() ? m_clientName : wxT("ircDDBGateway")).c_str(),
filter.c_str());
//wxLogMessage(wxT("Connect String : ") + connectString);
ret = m_socket.writeLine(connectString);
if (!ret) {
m_socket.close();
return false;
}
length = m_socket.readLine(serverResponse, APRS_TIMEOUT);
if (length == 0) {
wxLogError(wxT("No reply from the APRS server after %u seconds"), APRS_TIMEOUT);
m_socket.close();
return false;
}
if (length < 0) {
wxLogError(wxT("Error when reading from the APRS server"));
m_socket.close();
return false;
}
wxLogMessage(wxT("Response from APRS server: ") + serverResponse);
wxLogMessage(wxT("Connected to the APRS server"));
return true;
}
unsigned int CAPRSWriterThread::getAPRSPassword(wxString callsign) const
{
unsigned int len = callsign.Length();
wxUint16 hash = 0x73E2U;
for (unsigned int i = 0U; i < len; i += 2U) {
hash ^= (char)callsign.GetChar(i) << 8;
if(i + 1 < len)
hash ^= (char)callsign.GetChar(i + 1);
}
return hash & 0x7FFFU;
}

61
Common/APRSWriterThread.h Normal file
View file

@ -0,0 +1,61 @@
/*
* Copyright (C) 2010,2011,2012 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 "TCPReaderWriterClient.h"
#include "RingBuffer.h"
#include <wx/wx.h>
typedef void (*ReadAPRSFrameCallback)(const wxString&);
class CAPRSWriterThread : public wxThread {
public:
CAPRSWriterThread(const wxString& callsign, const wxString& address, const wxString& hostname, unsigned int port);
CAPRSWriterThread(const wxString& callsign, const wxString& address, const wxString& hostname, unsigned int port, const wxString& filter, const wxString& 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:
wxString m_username;
wxString m_ssid;
CTCPReaderWriterClient m_socket;
CRingBuffer<char*> m_queue;
bool m_exit;
bool m_connected;
ReadAPRSFrameCallback m_APRSReadCallback;
wxString m_filter;
wxString m_clientName;
bool connect();
unsigned int getAPRSPassword(wxString username) const;
};
#endif

151
Common/AnnouncementUnit.cpp Normal file
View file

@ -0,0 +1,151 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AnnouncementUnit.h"
#include <wx/file.h>
CAnnouncementUnit::CAnnouncementUnit(IRepeaterCallback* handler, const wxString& callsign, const wxString& fileName, const wxString& name) :
m_handler(handler),
m_callsign(callsign),
m_fileName(fileName),
m_name(name),
m_reader(NULL),
m_status(NS_IDLE),
m_timer(1000U, REPLY_TIME),
m_out(0U),
m_id(0U),
m_time()
{
wxASSERT(handler != NULL);
m_name.resize(SHORT_CALLSIGN_LENGTH, wxT(' '));
wxLogMessage(wxT("Connected '%s' to %s using file - %s"), name.c_str(), callsign.c_str(), fileName.c_str());
}
CAnnouncementUnit::~CAnnouncementUnit()
{
}
void CAnnouncementUnit::sendAnnouncement()
{
if (m_status != NS_IDLE)
return;
bool ret = wxFile::Exists(m_fileName.c_str());
if (!ret)
return;
m_reader = new CDVTOOLFileReader;
ret = m_reader->open(m_fileName);
if (!ret) {
delete m_reader;
m_reader = NULL;
return;
}
m_status = NS_WAIT;
m_timer.start();
}
void CAnnouncementUnit::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_status == NS_WAIT && m_timer.hasExpired()) {
m_reader->read(); // Pop the first record off the file
m_id = CHeaderData::createId();
CHeaderData header;
header.setMyCall1(m_callsign);
header.setMyCall2(m_name);
header.setYourCall(wxT("CQCQCQ "));
header.setId(m_id);
m_handler->process(header, DIR_INCOMING, AS_INFO);
m_timer.stop();
m_out = 0U;
m_status = NS_TRANSMIT;
m_time.Start();
return;
}
if (m_status == NS_TRANSMIT) {
unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS;
while (m_out < needed) {
DVTFR_TYPE type = m_reader->read();
if (type != DVTFR_DATA) {
m_out = 0U;
m_status = NS_IDLE;
m_timer.stop();
m_reader->close();
delete m_reader;
m_reader = NULL;
return;
}
CAMBEData* data = m_reader->readData();
data->setId(m_id);
m_out++;
m_handler->process(*data, DIR_INCOMING, AS_INFO);
bool end = data->isEnd();
delete data;
if (end) {
m_out = 0U;
m_status = NS_IDLE;
m_reader->close();
delete m_reader;
m_reader = NULL;
m_timer.stop();
return;
}
}
return;
}
}
void CAnnouncementUnit::cancel()
{
m_status = NS_IDLE;
m_out = 0U;
m_id = 0U;
if (m_reader != NULL) {
m_reader->close();
delete m_reader;
m_reader = NULL;
}
m_timer.stop();
}

60
Common/AnnouncementUnit.h Normal file
View file

@ -0,0 +1,60 @@
/*
* Copyright (C) 2014 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 AnnouncementUnit_H
#define AnnouncementUnit_H
#include "DVTOOLFileReader.h"
#include "RepeaterCallback.h"
#include "AMBEData.h"
#include "Timer.h"
#include "Defs.h"
#include <wx/wx.h>
enum ANNOUNCEMENT_STATUS {
NS_IDLE,
NS_WAIT,
NS_TRANSMIT
};
class CAnnouncementUnit {
public:
CAnnouncementUnit(IRepeaterCallback* handler, const wxString& callsign, const wxString& fileName, const wxString& name);
~CAnnouncementUnit();
void sendAnnouncement();
void cancel();
void clock(unsigned int ms);
private:
IRepeaterCallback* m_handler;
wxString m_callsign;
wxString m_fileName;
wxString m_name;
CDVTOOLFileReader* m_reader;
ANNOUNCEMENT_STATUS m_status;
CTimer m_timer;
unsigned int m_out;
unsigned int m_id;
wxStopWatch m_time;
};
#endif

505
Common/AudioUnit.cpp Normal file
View file

@ -0,0 +1,505 @@
/*
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AudioUnit.h"
#include <wx/filename.h>
#include <wx/textfile.h>
#include <wx/tokenzr.h>
#include <wx/ffile.h>
unsigned char* CAudioUnit::m_ambe = NULL;
unsigned int CAudioUnit::m_ambeLength = 0U;
CIndexList_t CAudioUnit::m_index;
TEXT_LANG CAudioUnit::m_language = TL_ENGLISH_UK;
const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC;
const unsigned int SILENCE_LENGTH = 10U;
void CAudioUnit::initialise()
{
}
void CAudioUnit::setLanguage(TEXT_LANG language)
{
m_language = language;
wxString ambeFileName;
wxString indxFileName;
switch (language) {
case TL_DEUTSCH:
ambeFileName = wxT("de_DE.ambe");
indxFileName = wxT("de_DE.indx");
break;
case TL_DANSK:
ambeFileName = wxT("dk_DK.ambe");
indxFileName = wxT("dk_DK.indx");
break;
case TL_ITALIANO:
ambeFileName = wxT("it_IT.ambe");
indxFileName = wxT("it_IT.indx");
break;
case TL_FRANCAIS:
ambeFileName = wxT("fr_FR.ambe");
indxFileName = wxT("fr_FR.indx");
break;
case TL_ESPANOL:
ambeFileName = wxT("es_ES.ambe");
indxFileName = wxT("es_ES.indx");
break;
case TL_SVENSKA:
ambeFileName = wxT("se_SE.ambe");
indxFileName = wxT("se_SE.indx");
break;
case TL_POLSKI:
ambeFileName = wxT("pl_PL.ambe");
indxFileName = wxT("pl_PL.indx");
break;
case TL_ENGLISH_US:
ambeFileName = wxT("en_US.ambe");
indxFileName = wxT("en_US.indx");
break;
case TL_NORSK:
ambeFileName = wxT("no_NO.ambe");
indxFileName = wxT("no_NO.indx");
break;
default:
ambeFileName = wxT("en_GB.ambe");
indxFileName = wxT("en_GB.indx");
break;
}
bool ret = readAMBE(ambeFileName);
if (!ret) {
delete[] m_ambe;
m_ambe = NULL;
return;
}
ret = readIndex(indxFileName);
if (!ret) {
delete[] m_ambe;
m_ambe = NULL;
}
}
void CAudioUnit::finalise()
{
for (CIndexList_t::iterator it = m_index.begin(); it != m_index.end(); ++it)
delete it->second;
delete[] m_ambe;
}
CAudioUnit::CAudioUnit(IRepeaterCallback* handler, const wxString& callsign) :
m_handler(handler),
m_callsign(callsign),
m_encoder(),
m_status(AS_IDLE),
m_linkStatus(LS_NONE),
m_tempLinkStatus(LS_NONE),
m_text(),
m_tempText(),
m_reflector(),
m_tempReflector(),
m_hasTemporary(false),
m_timer(1000U, REPLY_TIME),
m_data(NULL),
m_in(0U),
m_out(0U),
m_seqNo(0U),
m_time()
{
wxASSERT(handler != NULL);
m_data = new CAMBEData*[MAX_FRAMES];
for (unsigned int i = 0U; i < MAX_FRAMES; i++)
m_data[i] = NULL;
}
CAudioUnit::~CAudioUnit()
{
delete[] m_data;
}
void CAudioUnit::sendStatus()
{
if (m_ambe == NULL)
return;
if (m_status != AS_IDLE)
return;
m_status = AS_WAIT;
m_timer.start();
}
void CAudioUnit::setStatus(LINK_STATUS status, const wxString& reflector, const wxString& text)
{
m_linkStatus = status;
m_reflector = reflector;
m_text = text;
}
void CAudioUnit::setTempStatus(LINK_STATUS status, const wxString& reflector, const wxString& text)
{
m_tempLinkStatus = status;
m_tempReflector = reflector;
m_tempText = text;
m_hasTemporary = true;
}
void CAudioUnit::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_status == AS_WAIT && m_timer.hasExpired()) {
if (m_hasTemporary) {
sendStatus(m_tempLinkStatus, m_tempReflector, m_tempText);
m_hasTemporary = false;
} else {
sendStatus(m_linkStatus, m_reflector, m_text);
}
m_timer.stop();
m_out = 0U;
m_seqNo = 0U;
m_status = AS_TRANSMIT;
m_time.Start();
return;
}
if (m_status == AS_TRANSMIT) {
unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS;
while (m_out < needed) {
CAMBEData* data = m_data[m_out];
m_data[m_out] = NULL;
m_out++;
if (m_in == m_out)
data->setEnd(true);
m_handler->process(*data, DIR_INCOMING, AS_INFO);
delete data;
if (m_in == m_out) {
m_in = 0U;
m_out = 0U;
m_status = AS_IDLE;
m_timer.stop();
return;
}
}
return;
}
}
void CAudioUnit::cancel()
{
for (unsigned int i = 0U; i < MAX_FRAMES; i++) {
if (m_data[i] != NULL) {
delete m_data[i];
m_data[i] = NULL;
}
}
m_status = AS_IDLE;
m_out = 0U;
m_in = 0U;
m_timer.stop();
}
bool CAudioUnit::lookup(unsigned int id, const wxString &name)
{
CIndexRecord* info = m_index[name];
if (info == NULL) {
// wxLogError(wxT("Cannot find the AMBE index for *%s*"), name.c_str());
return false;
}
unsigned int start = info->getStart();
unsigned int length = info->getLength();
for (unsigned int i = 0U; i < length; i++) {
unsigned char* dataIn = m_ambe + (start + i) * VOICE_FRAME_LENGTH_BYTES;
CAMBEData* dataOut = new CAMBEData;
dataOut->setSeq(m_seqNo);
dataOut->setId(id);
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
::memcpy(buffer + 0U, dataIn, VOICE_FRAME_LENGTH_BYTES);
// Insert sync bytes when the sequence number is zero, slow data otherwise
if (m_seqNo == 0U) {
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
m_encoder.sync();
} else {
m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES);
}
dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES);
m_seqNo++;
if (m_seqNo == 21U)
m_seqNo = 0U;
m_data[m_in] = dataOut;
m_in++;
}
return true;
}
void CAudioUnit::spellReflector(unsigned int id, const wxString &reflector)
{
unsigned int length = reflector.Len();
for (unsigned int i = 0U; i < (length - 1U); i++) {
wxString c = reflector.Mid(i, 1U);
if (!c.IsSameAs(wxT(" ")))
lookup(id, c);
}
wxChar c = reflector.GetChar(length - 1U);
if (c == wxT(' '))
return;
if (m_linkStatus == LS_LINKING_DCS || m_linkStatus == LS_LINKED_DCS ||
m_linkStatus == LS_LINKING_CCS || m_linkStatus == LS_LINKED_CCS) {
lookup(id, wxString(c));
return;
}
switch (c) {
case wxT('A'):
lookup(id, wxT("alpha"));
break;
case wxT('B'):
lookup(id, wxT("bravo"));
break;
case wxT('C'):
lookup(id, wxT("charlie"));
break;
case wxT('D'):
lookup(id, wxT("delta"));
break;
default:
lookup(id, wxString(c));
break;
}
}
bool CAudioUnit::readAMBE(const wxString& name)
{
wxFileName fileName(wxFileName::GetHomeDir(), name);
if (!fileName.IsFileReadable()) {
wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str());
#if defined(__WINDOWS__)
fileName.Assign(::wxGetCwd(), name);
#else
fileName.Assign(wxT(DATA_DIR), name);
#endif
if (!fileName.IsFileReadable()) {
wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str());
return false;
}
}
wxFFile file;
bool ret = file.Open(fileName.GetFullPath().c_str(), wxT("rb"));
if (!ret) {
wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str());
return false;
}
wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str());
unsigned char buffer[VOICE_FRAME_LENGTH_BYTES];
size_t n = file.Read(buffer, 4U);
if (n != 4U) {
wxLogMessage(wxT("Unable to read the header from %s"), fileName.GetFullPath().c_str());
file.Close();
return false;
}
if (::memcmp(buffer, "AMBE", 4U) != 0) {
wxLogMessage(wxT("Invalid header from %s"), fileName.GetFullPath().c_str());
file.Close();
return false;
}
// Length of the file minus the header
unsigned int length = file.Length() - 4U;
// Hold the file data plus silence at the end
m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES];
m_ambeLength = length / VOICE_FRAME_LENGTH_BYTES;
// Add silence to the beginning of the buffer
unsigned char* p = m_ambe;
for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES)
::memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
n = file.Read(p, length);
if (n != length) {
wxLogMessage(wxT("Unable to read the AMBE data from %s"), fileName.GetFullPath().c_str());
file.Close();
delete[] m_ambe;
m_ambe = NULL;
return false;
}
file.Close();
return true;
}
bool CAudioUnit::readIndex(const wxString& name)
{
wxFileName fileName(wxFileName::GetHomeDir(), name);
if (!fileName.IsFileReadable()) {
wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str());
#if defined(__WINDOWS__)
fileName.Assign(::wxGetCwd(), name);
#else
fileName.Assign(wxT(DATA_DIR), name);
#endif
if (!fileName.IsFileReadable()) {
wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str());
return false;
}
}
wxTextFile file;
bool ret = file.Open(fileName.GetFullPath());
if (!ret) {
wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str());
return false;
}
// Add a silence entry at the beginning
m_index[wxT(" ")] = new CIndexRecord(wxT(" "), 0U, SILENCE_LENGTH);
wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str());
unsigned int nLines = file.GetLineCount();
for (unsigned int i = 0; i < nLines; i++) {
wxString line = file.GetLine(i);
if (line.length() > 0 && line.GetChar(0) != wxT('#')) {
wxStringTokenizer t(line, wxT(" \t\r\n"), wxTOKEN_STRTOK);
wxString name = t.GetNextToken();
wxString startTxt = t.GetNextToken();
wxString lengthTxt = t.GetNextToken();
if (!name.IsEmpty() && !startTxt.IsEmpty() && !lengthTxt.IsEmpty()) {
unsigned long start;
startTxt.ToULong(&start);
unsigned long length;
lengthTxt.ToULong(&length);
if (start >= m_ambeLength || (start + length) >= m_ambeLength)
wxLogError(wxT("The start or end for *%s* is out of range, start: %lu, end: %lu"), name.c_str(), start, start + length);
else
m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length);
}
}
}
file.Close();
return true;
}
void CAudioUnit::sendStatus(LINK_STATUS status, const wxString& reflector, const wxString &text)
{
m_encoder.setTextData(text);
// Create the message
unsigned int id = CHeaderData::createId();
lookup(id, wxT(" "));
lookup(id, wxT(" "));
lookup(id, wxT(" "));
lookup(id, wxT(" "));
bool found;
switch (status) {
case LS_NONE:
lookup(id, wxT("notlinked"));
break;
case LS_LINKED_CCS:
case LS_LINKED_DCS:
case LS_LINKED_DPLUS:
case LS_LINKED_DEXTRA:
case LS_LINKED_LOOPBACK:
found = lookup(id, wxT("linkedto"));
if (!found) {
lookup(id, wxT("linked"));
lookup(id, wxT("2"));
}
spellReflector(id, reflector);
break;
default:
found = lookup(id, wxT("linkingto"));
if (!found) {
lookup(id, wxT("linking"));
lookup(id, wxT("2"));
}
spellReflector(id, reflector);
break;
}
lookup(id, wxT(" "));
lookup(id, wxT(" "));
lookup(id, wxT(" "));
lookup(id, wxT(" "));
// RPT1 and RPT2 will be filled in later
CHeaderData header;
header.setMyCall1(m_callsign);
header.setMyCall2(wxT("INFO"));
header.setYourCall(wxT("CQCQCQ "));
header.setId(id);
m_handler->process(header, DIR_INCOMING, AS_INFO);
}

120
Common/AudioUnit.h Normal file
View file

@ -0,0 +1,120 @@
/*
* Copyright (C) 2011,2012,2013 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 AudioUnit_H
#define AudioUnit_H
#include "RepeaterCallback.h"
#include "SlowDataEncoder.h"
#include "AMBEData.h"
#include "Timer.h"
#include "Defs.h"
#include <wx/wx.h>
class CIndexRecord {
public:
CIndexRecord(const wxString& name, unsigned int start, unsigned int length) :
m_name(name),
m_start(start),
m_length(length)
{
}
wxString getName() const
{
return m_name;
}
unsigned int getStart() const
{
return m_start;
}
unsigned int getLength() const
{
return m_length;
}
private:
wxString m_name;
unsigned int m_start;
unsigned int m_length;
};
WX_DECLARE_STRING_HASH_MAP(CIndexRecord*, CIndexList_t);
enum AUDIO_STATUS {
AS_IDLE,
AS_WAIT,
AS_TRANSMIT
};
class CAudioUnit {
public:
CAudioUnit(IRepeaterCallback* handler, const wxString& callsign);
~CAudioUnit();
void sendStatus();
void setStatus(LINK_STATUS status, const wxString& reflector, const wxString& text);
void setTempStatus(LINK_STATUS status, const wxString& reflector, const wxString& text);
void cancel();
void clock(unsigned int ms);
static void initialise();
static void setLanguage(TEXT_LANG language);
static void finalise();
private:
static unsigned char* m_ambe;
static unsigned int m_ambeLength;
static CIndexList_t m_index;
static TEXT_LANG m_language;
IRepeaterCallback* m_handler;
wxString m_callsign;
CSlowDataEncoder m_encoder;
AUDIO_STATUS m_status;
LINK_STATUS m_linkStatus;
LINK_STATUS m_tempLinkStatus;
wxString m_text;
wxString m_tempText;
wxString m_reflector;
wxString m_tempReflector;
bool m_hasTemporary;
CTimer m_timer;
CAMBEData** m_data;
unsigned int m_in;
unsigned int m_out;
unsigned int m_seqNo;
wxStopWatch m_time;
bool lookup(unsigned int id, const wxString& name);
void spellReflector(unsigned int id, const wxString& reflector);
void sendStatus(LINK_STATUS status, const wxString& reflector, const wxString& text);
static bool readAMBE(const wxString& name);
static bool readIndex(const wxString& name);
};
#endif

142
Common/CCITTChecksum.cpp Normal file
View file

@ -0,0 +1,142 @@
/*
* Copyright (C) 2009 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; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CCITTChecksum.h"
#include "Utils.h"
static const unsigned short ccittTab[] = {
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,
0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,
0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,
0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974,
0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,
0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3,
0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,
0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72,
0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,
0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1,
0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,
0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70,
0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,
0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff,
0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,
0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e,
0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,
0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd,
0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,
0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c,
0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,
0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb,
0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,
0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a,
0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,
0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9,
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,
0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78};
CCCITTChecksum::CCCITTChecksum() :
m_crc(0xFFFF)
{
}
CCCITTChecksum::~CCCITTChecksum()
{
}
void CCCITTChecksum::update(const unsigned char* data, unsigned int length)
{
wxASSERT(data != NULL);
for (unsigned int i = 0U; i < length; i++) {
unsigned short byte = data[i];
unsigned short tmp = (m_crc & 0x00FF) ^ byte;
m_crc = (m_crc >> 8) ^ ccittTab[tmp];
}
}
void CCCITTChecksum::update(const bool* data)
{
wxASSERT(data != NULL);
unsigned short byte = CUtils::bitsToByte(data);
unsigned short tmp = (m_crc & 0x00FF) ^ byte;
m_crc = (m_crc >> 8) ^ ccittTab[tmp];
}
void CCCITTChecksum::result(unsigned char* data) // XX FIXME
{
wxASSERT(data != NULL);
m_crc = ~m_crc;
unsigned short tmp = m_crc;
m_crc = (m_crc << 8) | (tmp >> 8 & 0xFF);
data[0] = (m_crc >> 8) & 0xFF;
data[1] = (m_crc >> 0) & 0xFF;
}
void CCCITTChecksum::result(bool* data)
{
wxASSERT(data != NULL);
m_crc = ~m_crc;
unsigned short tmp = m_crc;
m_crc = (m_crc << 8) | (tmp >> 8 & 0xFF);
unsigned short mask = 0x8000;
for (unsigned int i = 0U; i < 16U; i++, mask >>= 1)
data[i] = (m_crc & mask) ? true : false;
}
bool CCCITTChecksum::check(const unsigned char* data)
{
wxASSERT(data != NULL);
unsigned char sum[2];
result(sum);
return sum[0] == data[0] && sum[1] == data[1];
}
bool CCCITTChecksum::check(const bool* data)
{
wxASSERT(data != NULL);
bool sum[16];
result(sum);
for (unsigned int i = 0U; i < 16U; i++)
if (sum[i] != data[i])
return false;
return true;
}
void CCCITTChecksum::reset()
{
m_crc = 0xFFFF;
}

39
Common/CCITTChecksum.h Normal file
View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2009,2013 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; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CCITTChecksum_H
#define CCITTChecksum_H
#include <wx/wx.h>
class CCCITTChecksum {
public:
CCCITTChecksum();
~CCCITTChecksum();
void update(const unsigned char* data, unsigned int length);
void update(const bool* data);
void result(unsigned char* data);
void result(bool* data);
bool check(const unsigned char* data);
bool check(const bool* data);
void reset();
private:
wxUint16 m_crc;
};
#endif

44
Common/CCSCallback.h Normal file
View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2013 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 CCSCallback_H
#define CCSCallback_H
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Defs.h"
#include <wx/wx.h>
class ICCSCallback {
public:
virtual bool process(CHeaderData& header, DIRECTION direction, AUDIO_SOURCE source) = 0;
virtual bool process(CAMBEData& data, DIRECTION direction, AUDIO_SOURCE source) = 0;
virtual void ccsLinkMade(const wxString& callsign, DIRECTION direction) = 0;
virtual void ccsLinkFailed(const wxString& dtmf, DIRECTION direction) = 0;
virtual void ccsLinkEnded(const wxString& callsign, DIRECTION direction) = 0;
private:
};
#endif

192
Common/CCSData.cpp Normal file
View file

@ -0,0 +1,192 @@
/*
* Copyright (C) 2013 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "CCSData.h"
#include "Utils.h"
CCCSData::CCCSData(const wxString& local, double latitude, double longitude, double frequency, double offset, const wxString& description1, const wxString& description2, const wxString& url, CC_TYPE type) :
m_local(local),
m_remote(),
m_latitude(latitude),
m_longitude(longitude),
m_frequency(frequency),
m_offset(offset),
m_description1(description1),
m_description2(description2),
m_url(url),
m_type(type),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U)
{
}
CCCSData::CCCSData(const wxString& local, const wxString& remote, CC_TYPE type) :
m_local(local),
m_remote(remote),
m_latitude(0.0),
m_longitude(0.0),
m_frequency(0.0),
m_offset(0.0),
m_description1(),
m_description2(),
m_url(),
m_type(type),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U)
{
}
CCCSData::CCCSData() :
m_local(),
m_remote(),
m_latitude(0.0),
m_longitude(0.0),
m_frequency(0.0),
m_offset(0.0),
m_description1(),
m_description2(),
m_url(),
m_type(),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U)
{
}
CCCSData::~CCCSData()
{
}
bool CCCSData::setCCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
switch (length) {
case 100U:
m_remote = wxString((char*)(data + 0U), wxConvLocal, LONG_CALLSIGN_LENGTH);
if (::memcmp(data + 8U, "0001", 4U) == 0) {
m_type = CT_TERMINATE;
} else {
// CUtils::dump(wxT("Invalid CCS packet"), data, length);
return false;
}
m_local = wxString((char*)(data + 12U), wxConvLocal, LONG_CALLSIGN_LENGTH);
break;
case 20U:
if (::memcmp(data + 0U, "DTMF_CALL:", 10U) == 0) {
m_type = CT_DTMFFOUND;
} else {
CUtils::dump(wxT("Invalid CCS packet"), data, length);
return false;
}
m_remote = wxString((char*)(data + 10U), wxConvLocal, LONG_CALLSIGN_LENGTH);
break;
case 17U:
if (::memcmp(data + 0U, "NODTMFCALL", 10U) == 0) {
m_type = CT_DTMFNOTFOUND;
} else {
CUtils::dump(wxT("Invalid CCS packet"), data, length);
return false;
}
break;
default:
CUtils::dump(wxT("Invalid CCS packet"), data, length);
return false;
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
unsigned int CCCSData::getCCSData(unsigned char* data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 133U);
if (m_type == CT_TERMINATE) {
::memset(data, ' ', 38U);
for (unsigned int i = 0U; i < m_remote.Len() && i < LONG_CALLSIGN_LENGTH; i++)
data[i + 0U] = m_remote.GetChar(i);
::memcpy(data + 8U, "0001", 4U);
for (unsigned int i = 0U; i < m_local.Len() && i < LONG_CALLSIGN_LENGTH; i++)
data[i + 12U] = m_local.GetChar(i);
return 38U;
} else if (m_type == CT_INFO) {
wxString buffer;
buffer.Printf(wxT("IRPT%.7s %s%-10.4lf%-10.4lf%-10.4lf%-10.4lf%-20s%-20s%-40s"), m_local.Mid(0U, LONG_CALLSIGN_LENGTH - 1U).c_str(), m_local.Mid(LONG_CALLSIGN_LENGTH - 1U, 1U).c_str(), m_latitude, m_longitude, m_frequency, m_offset, m_description1.c_str(), m_description2.c_str(), m_url.c_str());
for (unsigned int i = 0U; i < buffer.Len() && i < 133U; i++)
data[i] = buffer.GetChar(i);
return 133U;
}
return 0U;
}
wxString CCCSData::getLocal() const
{
return m_local;
}
wxString CCCSData::getRemote() const
{
return m_remote;
}
CC_TYPE CCCSData::getType() const
{
return m_type;
}
void CCCSData::setDestination(const in_addr& address, unsigned int port)
{
m_yourAddress = address;
m_yourPort = port;
}
in_addr CCCSData::getYourAddress() const
{
return m_yourAddress;
}
unsigned int CCCSData::getYourPort() const
{
return m_yourPort;
}
unsigned int CCCSData::getMyPort() const
{
return m_myPort;
}

74
Common/CCSData.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2013 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 CCSData_H
#define CCSData_H
#include <wx/wx.h>
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
enum CC_TYPE {
CT_TERMINATE,
CT_DTMFNOTFOUND,
CT_DTMFFOUND,
CT_INFO
};
class CCCSData {
public:
CCCSData(const wxString& local, double latitude, double longitude, double frequency, double offset, const wxString& description1, const wxString& description2, const wxString& url, CC_TYPE type);
CCCSData(const wxString& local, const wxString& remote, CC_TYPE type);
CCCSData();
~CCCSData();
bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
unsigned int getCCSData(unsigned char* data, unsigned int length) const;
void setDestination(const in_addr& address, unsigned int port);
wxString getLocal() const;
wxString getRemote() const;
CC_TYPE getType() const;
in_addr getYourAddress() const;
unsigned int getYourPort() const;
unsigned int getMyPort() const;
private:
wxString m_local;
wxString m_remote;
double m_latitude;
double m_longitude;
double m_frequency;
double m_offset;
wxString m_description1;
wxString m_description2;
wxString m_url;
CC_TYPE m_type;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
};
#endif

700
Common/CCSHandler.cpp Normal file
View file

@ -0,0 +1,700 @@
/*
* Copyright (C) 2013,2014 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 "RepeaterHandler.h"
#include "DStarDefines.h"
#include "CCSHandler.h"
#include "Utils.h"
CCCSHandler** CCCSHandler::m_handlers = NULL;
unsigned int CCCSHandler::m_count = 0U;
wxString CCCSHandler::m_localAddress;
CHeaderLogger* CCCSHandler::m_headerLogger = NULL;
wxString CCCSHandler::m_ccsHost;
CCCSCache_t CCCSHandler::m_cache;
wxMutex CCCSHandler::m_mutex;
bool CCCSHandler::m_stateChange = false;
void CCCSHandler::initialise(unsigned int count)
{
wxASSERT(count > 0U);
m_count = count;
m_handlers = new CCCSHandler*[m_count];
for (unsigned int i = 0U; i < m_count; i++)
m_handlers[i] = NULL;
}
void CCCSHandler::setLocalAddress(const wxString& address)
{
m_localAddress = address;
}
void CCCSHandler::setHeaderLogger(CHeaderLogger* logger)
{
m_headerLogger = logger;
}
void CCCSHandler::setHost(const wxString& host)
{
m_ccsHost = host;
}
void CCCSHandler::process()
{
for (unsigned int i = 0U; i < m_count; i++) {
if (m_handlers[i] != NULL)
m_handlers[i]->processInt();
}
}
void CCCSHandler::disconnect()
{
for (unsigned int i = 0U; i < m_count; i++) {
if (m_handlers[i] != NULL)
m_handlers[i]->disconnectInt();
}
}
void CCCSHandler::clock(unsigned int ms)
{
for (unsigned int i = 0U; i < m_count; i++) {
if (m_handlers[i] != NULL)
m_handlers[i]->clockInt(ms);
}
}
void CCCSHandler::getInfo(ICCSCallback* handler, CRemoteRepeaterData& data)
{
wxASSERT(handler != NULL);
for (unsigned int i = 0U; i < m_count; i++) {
CCCSHandler* ccs = m_handlers[i];
if (ccs != NULL && ccs->m_handler == handler && ccs->m_state == CS_ACTIVE)
data.addLink(ccs->m_yourCall, PROTO_CCS, true, ccs->m_direction, false);
}
}
wxString CCCSHandler::getIncoming(const wxString& callsign)
{
wxString incoming;
for (unsigned int i = 0U; i < m_count; i++) {
CCCSHandler* handler = m_handlers[i];
if (handler != NULL && handler->m_direction == DIR_INCOMING && handler->m_state == CS_ACTIVE && handler->m_callsign.IsSameAs(callsign)) {
incoming.Append(handler->m_yourCall);
incoming.Append(wxT(" "));
}
}
return incoming;
}
void CCCSHandler::finalise()
{
for (unsigned int i = 0U; i < m_count; i++)
delete m_handlers[i];
delete[] m_handlers;
}
CCCSHandler::CCCSHandler(ICCSCallback* handler, const wxString& callsign, unsigned int delay, double latitude, double longitude, double frequency, double offset, const wxString& description1, const wxString& description2, const wxString& url, unsigned int localPort) :
m_handler(handler),
m_callsign(callsign),
m_reflector(),
m_latitude(latitude),
m_longitude(longitude),
m_frequency(frequency),
m_offset(offset),
m_description1(description1),
m_description2(description2),
m_url(url),
m_ccsAddress(),
m_protocol(localPort, m_localAddress),
m_state(CS_DISABLED),
m_local(),
m_announceTimer(1000U, 20U), // 20 seconds
m_inactivityTimer(1000U, 300U), // 5 minutes
m_pollInactivityTimer(1000U, 60U), // 60 seconds
m_pollTimer(1000U, 10U), // 10 seconds
m_waitTimer(1000U, delay),
m_tryTimer(1000U, 1U), // 1 second
m_tryCount(0U),
m_id(0x00U),
m_seqNo(0U),
m_time(),
m_direction(DIR_OUTGOING),
m_yourCall(),
m_myCall1(),
m_myCall2(),
m_rptCall1()
{
wxASSERT(handler != NULL);
// Add to the global list
for (unsigned int i = 0U; i < m_count; i++) {
if (m_handlers[i] == NULL) {
m_handlers[i] = this;
break;
}
}
}
CCCSHandler::~CCCSHandler()
{
}
void CCCSHandler::setReflector(const wxString& callsign)
{
m_reflector = callsign;
if (m_reflector.IsEmpty())
m_reflector = wxT(" ");
}
void CCCSHandler::processInt()
{
if (m_state == CS_DISABLED)
return;
for (;;) {
CCS_TYPE type = m_protocol.read();
switch (type) {
case CT_DATA: {
CAMBEData* data = m_protocol.readData();
if (data != NULL) {
process(*data);
delete data;
}
}
break;
case CT_POLL: {
CPollData* poll = m_protocol.readPoll();
if (poll != NULL) {
process(*poll);
delete poll;
}
}
break;
case CT_CONNECT: {
CConnectData* connect = m_protocol.readConnect();
if (connect != NULL) {
process(*connect);
delete connect;
}
}
break;
case CT_MISC: {
CCCSData* data = m_protocol.readMisc();
if (data != NULL) {
process(*data);
delete data;
}
}
break;
default:
return;
}
}
}
void CCCSHandler::process(CAMBEData& data)
{
CHeaderData& header = data.getHeader();
wxString myCall1 = header.getMyCall1();
wxString rptCall1 = header.getRptCall1();
wxString yourCall = header.getYourCall();
unsigned int seqNo = data.getSeq();
unsigned int id = data.getId();
if (m_state != CS_CONNECTED && m_state != CS_ACTIVE)
return;
// This is a new incoming CCS call
if (m_state == CS_CONNECTED) {
m_yourCall = myCall1;
m_local = yourCall;
m_rptCall1 = rptCall1;
m_direction = DIR_INCOMING;
m_time = ::time(NULL);
m_state = CS_ACTIVE;
m_stateChange = true;
m_inactivityTimer.start();
m_handler->ccsLinkMade(m_yourCall, m_direction);
wxLogMessage(wxT("CCS: New incoming link to %s from %s @ %s"), m_local.c_str(), m_yourCall.c_str(), m_rptCall1.c_str());
} else {
if (!m_yourCall.IsSameAs(myCall1) && !m_rptCall1.IsSameAs(rptCall1)) {
wxLogMessage(wxT("CCS: Rejecting new incoming CCS link from %s @ %s to %s"), myCall1.c_str(), rptCall1.c_str(), yourCall.c_str());
CCCSData data(yourCall, myCall1, CT_TERMINATE);
data.setDestination(m_ccsAddress, CCS_PORT);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
return;
}
// Allow for the fact that the distant repeater may change during the QSO
if (m_yourCall.IsSameAs(myCall1) && !m_rptCall1.IsSameAs(rptCall1)) {
wxLogMessage(wxT("CCS: %s has moved from repeater %s to %s"), m_yourCall.c_str(), m_rptCall1.c_str(), rptCall1.c_str());
m_rptCall1 = rptCall1;
}
}
m_pollInactivityTimer.start();
m_inactivityTimer.start();
if (m_id != id) {
// Write to Header.log if it's enabled
if (m_headerLogger != NULL)
m_headerLogger->write(wxT("CCS"), header);
header.setCQCQCQ();
m_handler->process(header, DIR_INCOMING, AS_CCS);
m_id = id;
} else if (seqNo == 0U) {
header.setCQCQCQ();
m_handler->process(header, DIR_INCOMING, AS_DUP);
}
m_handler->process(data, DIR_INCOMING, AS_CCS);
}
void CCCSHandler::process(CCCSData& data)
{
CC_TYPE type = data.getType();
switch (type) {
case CT_TERMINATE:
if (m_state == CS_ACTIVE) {
wxLogMessage(wxT("CCS: Link between %s and %s has been terminated"), data.getLocal().c_str(), data.getRemote().c_str());
m_stateChange = true;
m_state = CS_CONNECTED;
m_inactivityTimer.stop();
m_handler->ccsLinkEnded(data.getRemote(), m_direction);
}
break;
case CT_DTMFNOTFOUND:
wxLogMessage(wxT("CCS: Cannot map %s to a callsign"), m_yourCall.c_str());
m_stateChange = true;
m_state = CS_CONNECTED;
m_inactivityTimer.stop();
m_handler->ccsLinkFailed(m_yourCall, m_direction);
break;
case CT_DTMFFOUND:
wxLogMessage(wxT("CCS: Mapped %s to %s, added to the cache"), m_yourCall.c_str(), data.getRemote().c_str());
addToCache(m_yourCall, data.getRemote());
m_stateChange = true;
m_yourCall = data.getRemote();
m_rptCall1 = data.getRemote();
m_handler->ccsLinkMade(m_yourCall, m_direction);
break;
default:
break;
}
}
void CCCSHandler::process(CPollData&)
{
m_pollInactivityTimer.start();
}
void CCCSHandler::process(CConnectData& connect)
{
CD_TYPE type = connect.getType();
if (type == CT_ACK && m_state == CS_CONNECTING) {
wxLogMessage(wxT("CCS: %s connected to server %s"), m_callsign.c_str(), m_ccsHost.c_str());
m_announceTimer.start();
m_pollInactivityTimer.start();
m_pollTimer.start();
m_tryTimer.stop();
// Give our location, frequency, etc
CCCSData data(m_callsign, m_latitude, m_longitude, m_frequency, m_offset, m_description1, m_description2, m_url, CT_INFO);
data.setDestination(m_ccsAddress, CCS_PORT);
m_protocol.writeMisc(data);
m_state = CS_CONNECTED;
return;
}
if (type == CT_NAK && m_state == CS_CONNECTING) {
wxLogMessage(wxT("CCS: Connection refused for %s"), m_callsign.c_str());
m_tryTimer.stop();
m_state = CS_DISABLED;
return;
}
}
bool CCCSHandler::connect()
{
// Is CCS disabled?
if (m_localAddress.IsSameAs(wxT("127.0.0.1")))
return false;
// Can we resolve the CCS server address?
m_ccsAddress = CUDPReaderWriter::lookup(m_ccsHost);
if (m_ccsAddress.s_addr == INADDR_NONE) {
wxLogError(wxT("CCS: Unable to find the IP address for %s"), m_ccsHost.c_str());
return false;
}
bool res = m_protocol.open();
if (!res)
return false;
wxLogMessage(wxT("CCS: Opening UDP port %u for %s"), m_protocol.getPort(), m_callsign.c_str());
m_waitTimer.start();
m_state = CS_CONNECTING;
return true;
}
void CCCSHandler::disconnectInt()
{
if (m_state == CS_CONNECTED || m_state == CS_ACTIVE) {
CConnectData connect(m_callsign, CT_UNLINK, m_ccsAddress, CCS_PORT);
m_protocol.writeConnect(connect);
}
m_announceTimer.stop();
m_pollInactivityTimer.stop();
m_inactivityTimer.stop();
m_pollTimer.stop();
m_tryTimer.stop();
if (m_state != CS_DISABLED)
m_protocol.close();
m_state = CS_DISABLED;
}
void CCCSHandler::startLink(const wxString& dtmf, const wxString& user, const wxString& type)
{
if (m_state != CS_CONNECTED)
return;
wxString callsign = findInCache(dtmf);
if (!callsign.IsEmpty()) {
wxLogMessage(wxT("CCS: New outgoing link to %s/%s via %s by %s"), dtmf.c_str(), callsign.c_str(), type.c_str(), user.c_str());
m_handler->ccsLinkMade(callsign, m_direction);
m_yourCall = callsign;
m_rptCall1 = callsign;
} else {
wxLogMessage(wxT("CCS: New outgoing link to %s via %s by %s"), dtmf.c_str(), type.c_str(), user.c_str());
m_yourCall = dtmf;
m_yourCall.resize(LONG_CALLSIGN_LENGTH, wxT(' '));
m_rptCall1.Clear();
}
m_local = user;
m_seqNo = 0U;
m_time = ::time(NULL);
m_stateChange = true;
m_state = CS_ACTIVE;
m_direction = DIR_OUTGOING;
m_inactivityTimer.start();
}
void CCCSHandler::stopLink(const wxString& user, const wxString& type)
{
if (m_state != CS_ACTIVE)
return;
if (!user.IsEmpty() && !type.IsEmpty())
wxLogMessage(wxT("CCS: Link to %s from %s has been terminated via %s by %s"), m_yourCall.c_str(), m_local.c_str(), type.c_str(), user.c_str());
CCCSData data(m_local, m_yourCall, CT_TERMINATE);
data.setDestination(m_ccsAddress, CCS_PORT);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_stateChange = true;
m_state = CS_CONNECTED;
m_inactivityTimer.stop();
m_handler->ccsLinkEnded(m_yourCall, m_direction);
}
void CCCSHandler::unlink(const wxString& callsign)
{
if (m_state != CS_ACTIVE)
return;
if (!m_yourCall.IsSameAs(callsign))
return;
wxLogMessage(wxT("CCS: Link to %s from %s has been terminated by command"), m_yourCall.c_str(), m_local.c_str());
CCCSData data(m_local, m_yourCall, CT_TERMINATE);
data.setDestination(m_ccsAddress, CCS_PORT);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_stateChange = true;
m_state = CS_CONNECTED;
m_inactivityTimer.stop();
m_handler->ccsLinkEnded(m_yourCall, m_direction);
}
void CCCSHandler::writeHeard(CHeaderData& header)
{
if (m_state != CS_CONNECTED && m_state != CS_ACTIVE)
return;
CHeardData heard(header, m_callsign, m_reflector);
heard.setDestination(m_ccsAddress, CCS_PORT);
m_protocol.writeHeard(heard);
}
void CCCSHandler::writeHeader(CHeaderData& header)
{
m_myCall1 = header.getMyCall1();
m_myCall2 = header.getMyCall2();
m_seqNo = 0U;
}
void CCCSHandler::writeAMBE(CAMBEData& data)
{
if (m_state != CS_ACTIVE)
return;
CAMBEData temp(data);
CHeaderData& header = temp.getHeader();
header.setMyCall1(m_myCall1);
header.setMyCall2(m_myCall2);
header.setYourCall(m_yourCall);
header.setRptCall1(m_callsign);
header.setRptCall2(m_reflector);
temp.setRptSeq(m_seqNo++);
temp.setDestination(m_ccsAddress, CCS_PORT);
m_protocol.writeData(temp);
}
CCS_STATUS CCCSHandler::getStatus() const
{
return m_state;
}
void CCCSHandler::clockInt(unsigned int ms)
{
m_announceTimer.clock(ms);
m_pollInactivityTimer.clock(ms);
m_inactivityTimer.clock(ms);
m_pollTimer.clock(ms);
m_waitTimer.clock(ms);
m_tryTimer.clock(ms);
if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) {
wxLogMessage(wxT("CCS: Connection has failed (poll inactivity) for %s, reconnecting"), m_callsign.c_str());
m_announceTimer.stop();
m_pollInactivityTimer.stop();
m_inactivityTimer.stop();
m_pollTimer.stop();
if (m_state == CS_ACTIVE) {
m_stateChange = true;
m_handler->ccsLinkEnded(m_yourCall, m_direction);
}
m_waitTimer.start();
m_state = CS_CONNECTING;
return;
}
if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) {
CConnectData connect(m_callsign, CT_LINK1, m_ccsAddress, CCS_PORT);
if (m_latitude != 0.0 && m_longitude != 0.0) {
wxString locator = CUtils::latLonToLoc(m_latitude, m_longitude);
connect.setLocator(locator);
}
m_protocol.writeConnect(connect);
unsigned int t = calcBackoff();
m_tryTimer.start(t);
}
if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) {
CPollData poll(m_callsign, m_ccsAddress, CCS_PORT);
m_protocol.writePoll(poll);
m_pollTimer.start();
}
if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) {
wxLogMessage(wxT("CCS: Activity timeout on link for %s"), m_callsign.c_str(), m_callsign.c_str());
CCCSData data(m_local, m_yourCall, CT_TERMINATE);
data.setDestination(m_ccsAddress, CCS_PORT);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_protocol.writeMisc(data);
m_stateChange = true;
m_state = CS_CONNECTED;
m_inactivityTimer.stop();
m_handler->ccsLinkEnded(m_yourCall, m_direction);
}
if (m_waitTimer.isRunning() && m_waitTimer.hasExpired()) {
CConnectData connect(m_callsign, CT_LINK1, m_ccsAddress, CCS_PORT);
if (m_latitude != 0.0 && m_longitude != 0.0) {
wxString locator = CUtils::latLonToLoc(m_latitude, m_longitude);
connect.setLocator(locator);
}
m_protocol.writeConnect(connect);
m_tryTimer.start(1U);
m_tryCount = 1U;
m_waitTimer.stop();
}
if (m_announceTimer.isRunning() && m_announceTimer.hasExpired()) {
CHeaderData header;
header.setMyCall1(m_callsign.Left(LONG_CALLSIGN_LENGTH - 1U));
CHeardData heard(header, m_callsign, wxEmptyString);
heard.setDestination(m_ccsAddress, CCS_PORT);
m_protocol.writeHeard(heard);
m_announceTimer.start(3600U);
}
}
unsigned int CCCSHandler::calcBackoff()
{
if (m_tryCount >= 7U) {
m_tryCount++;
return 60U;
}
unsigned int timeout = 1U;
for (unsigned int i = 0U; i < m_tryCount; i++)
timeout *= 2U;
m_tryCount++;
if (timeout > 60U)
return 60U;
else
return timeout;
}
bool CCCSHandler::stateChange()
{
bool stateChange = m_stateChange;
m_stateChange = false;
return stateChange;
}
void CCCSHandler::writeStatus(wxFFile& file)
{
for (unsigned int i = 0U; i < m_count; i++) {
CCCSHandler* handler = m_handlers[i];
if (handler != NULL) {
struct tm* tm = ::gmtime(&handler->m_time);
switch (handler->m_direction) {
case DIR_OUTGOING:
if (handler->m_state == CS_ACTIVE) {
wxString text;
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: CCS link - Rptr: %s Remote: %s Dir: Outgoing\n"),
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
handler->m_callsign.c_str(), handler->m_yourCall.c_str());
file.Write(text);
}
break;
case DIR_INCOMING:
if (handler->m_state == CS_ACTIVE) {
wxString text;
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: CCS link - Rptr: %s Remote: %s Dir: Incoming\n"),
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
handler->m_callsign.c_str(), handler->m_yourCall.c_str());
file.Write(text);
}
break;
}
}
}
}
void CCCSHandler::addToCache(const wxString& dtmf, const wxString& callsign)
{
wxMutexLocker locker(m_mutex);
m_cache[dtmf] = callsign;
}
wxString CCCSHandler::findInCache(const wxString& dtmf)
{
wxMutexLocker locker(m_mutex);
return m_cache[dtmf];
}

155
Common/CCSHandler.h Normal file
View file

@ -0,0 +1,155 @@
/*
* Copyright (C) 2013 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 CCSHandler_H
#define CCSHandler_H
#include "CCSProtocolHandler.h"
#include "DStarDefines.h"
#include "HeaderLogger.h"
#include "ConnectData.h"
#include "CCSCallback.h"
#include "AMBEData.h"
#include "PollData.h"
#include "Timer.h"
#include "Defs.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
#include <wx/ffile.h>
enum CCS_STATUS {
CS_DISABLED,
CS_CONNECTING,
CS_CONNECTED,
CS_ACTIVE
};
WX_DECLARE_STRING_HASH_MAP(wxString, CCCSCache_t);
class CCCSHandler {
public:
CCCSHandler(ICCSCallback* handler, const wxString& callsign, unsigned int delay, double latitude, double longitude, double frequency, double offset, const wxString& description1, const wxString& description2, const wxString& url, unsigned int localPort);
~CCCSHandler();
bool connect();
void writeHeard(CHeaderData& header);
void writeHeader(CHeaderData& header);
void writeAMBE(CAMBEData& data);
void startLink(const wxString& dtmf, const wxString& user, const wxString& type);
void stopLink(const wxString& user = wxEmptyString, const wxString& type = wxEmptyString);
void unlink(const wxString& callsign);
void setReflector(const wxString& callsign = wxEmptyString);
CCS_STATUS getStatus() const;
static void disconnect();
static void initialise(unsigned int count);
static void process();
static void clock(unsigned int ms);
static void setHeaderLogger(CHeaderLogger* logger);
static void setLocalAddress(const wxString& address);
static void setHost(const wxString& host);
static bool stateChange();
static void writeStatus(wxFFile& file);
static void getInfo(ICCSCallback* handler, CRemoteRepeaterData& data);
static wxString getIncoming(const wxString& callsign);
static void finalise();
protected:
void clockInt(unsigned int ms);
void processInt();
void disconnectInt();
private:
static CCCSHandler** m_handlers;
static unsigned int m_count;
static wxString m_localAddress;
static CHeaderLogger* m_headerLogger;
static wxString m_ccsHost;
static CCCSCache_t m_cache;
static wxMutex m_mutex;
static bool m_stateChange;
ICCSCallback* m_handler;
wxString m_callsign;
wxString m_reflector;
double m_latitude;
double m_longitude;
double m_frequency;
double m_offset;
wxString m_description1;
wxString m_description2;
wxString m_url;
in_addr m_ccsAddress;
CCCSProtocolHandler m_protocol;
CCS_STATUS m_state;
wxString m_local;
CTimer m_announceTimer;
CTimer m_inactivityTimer;
CTimer m_pollInactivityTimer;
CTimer m_pollTimer;
CTimer m_waitTimer;
CTimer m_tryTimer;
unsigned int m_tryCount;
unsigned int m_id;
unsigned int m_seqNo;
time_t m_time;
DIRECTION m_direction;
wxString m_yourCall;
wxString m_myCall1;
wxString m_myCall2;
wxString m_rptCall1;
void process(CAMBEData& header);
void process(CPollData& data);
void process(CConnectData& connect);
void process(CCCSData& data);
unsigned int calcBackoff();
static void addToCache(const wxString& dtmf, const wxString& callsign);
static wxString findInCache(const wxString& dtmf);
};
#endif

View file

@ -0,0 +1,235 @@
/*
* Copyright (C) 2013 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 "CCSProtocolHandler.h"
#include "DStarDefines.h"
#include "Utils.h"
// #define DUMP_TX
const unsigned int BUFFER_LENGTH = 2000U;
CCCSProtocolHandler::CCCSProtocolHandler(unsigned int port, const wxString& addr) :
m_socket(addr, port),
m_type(CT_NONE),
m_buffer(NULL),
m_length(0U),
m_yourAddress(),
m_yourPort(0U),
m_myPort(port)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CCCSProtocolHandler::~CCCSProtocolHandler()
{
delete[] m_buffer;
}
bool CCCSProtocolHandler::open()
{
return m_socket.open();
}
unsigned int CCCSProtocolHandler::getPort() const
{
return m_myPort;
}
bool CCCSProtocolHandler::writeData(const CAMBEData& data)
{
unsigned char buffer[100U];
unsigned int length = data.getCCSData(buffer, 100U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Data"), buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
bool CCCSProtocolHandler::writePoll(const CPollData& poll)
{
unsigned char buffer[30U];
unsigned int length = poll.getCCSData(buffer, 30U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Poll"), buffer, length);
#endif
return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort());
}
bool CCCSProtocolHandler::writeHeard(const CHeardData& heard)
{
unsigned char buffer[100U];
unsigned int length = heard.getCCSData(buffer, 100U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Heard"), buffer, length);
#endif
return m_socket.write(buffer, length, heard.getAddress(), heard.getPort());
}
bool CCCSProtocolHandler::writeConnect(const CConnectData& connect)
{
unsigned char buffer[40U];
unsigned int length = connect.getCCSData(buffer, 40U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Connect"), buffer, length);
#endif
return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort());
}
bool CCCSProtocolHandler::writeMisc(const CCCSData& data)
{
unsigned char buffer[140U];
unsigned int length = data.getCCSData(buffer, 140U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Misc"), buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
CCS_TYPE CCCSProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CCCSProtocolHandler::readPackets()
{
m_type = CT_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort);
if (length <= 0)
return false;
m_length = length;
if (m_buffer[0] == '0' && m_buffer[1] == '0' && m_buffer[2] == '0' && m_buffer[3] == '1') {
m_type = CT_DATA;
return false;
} else if (m_buffer[0] == 'L' && m_buffer[1] == 'L' && m_buffer[2] == 'L') {
return true;
} else {
switch (m_length) {
case 14U:
m_type = CT_CONNECT;
return false;
case 25U:
m_type = CT_POLL;
return false;
case 100U:
case 20U:
case 17U:
m_type = CT_MISC;
return false;
case 39U:
return true;
default:
break;
}
}
// An unknown type
CUtils::dump(wxT("Unknown packet type from CCS"), m_buffer, m_length);
return true;
}
CAMBEData* CCCSProtocolHandler::readData()
{
if (m_type != CT_DATA)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete data;
return NULL;
}
return data;
}
CConnectData* CCCSProtocolHandler::readConnect()
{
if (m_type != CT_CONNECT)
return NULL;
CConnectData* connect = new CConnectData;
bool res = connect->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete connect;
return NULL;
}
return connect;
}
CPollData* CCCSProtocolHandler::readPoll()
{
if (m_type != CT_POLL)
return NULL;
CPollData* poll = new CPollData;
bool res = poll->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete poll;
return NULL;
}
return poll;
}
CCCSData* CCCSProtocolHandler::readMisc()
{
if (m_type != CT_MISC)
return NULL;
CCCSData* data = new CCCSData;
bool res = data->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete data;
return NULL;
}
return data;
}
void CCCSProtocolHandler::close()
{
m_socket.close();
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (C) 2013 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 CCSProtocolHandler_H
#define CCSProtocolHandler_H
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "ConnectData.h"
#include "HeardData.h"
#include "AMBEData.h"
#include "PollData.h"
#include "CCSData.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
enum CCS_TYPE {
CT_NONE,
CT_DATA,
CT_POLL,
CT_CONNECT,
CT_MISC
};
class CCCSProtocolHandler {
public:
CCCSProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString);
~CCCSProtocolHandler();
bool open();
unsigned int getPort() const;
bool writeData(const CAMBEData& data);
bool writeConnect(const CConnectData& connect);
bool writePoll(const CPollData& poll);
bool writeHeard(const CHeardData& heard);
bool writeMisc(const CCCSData& data);
CCS_TYPE read();
CAMBEData* readData();
CPollData* readPoll();
CConnectData* readConnect();
CCCSData* readMisc();
void close();
private:
CUDPReaderWriter m_socket;
CCS_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
bool readPackets();
};
#endif

128
Common/CacheManager.cpp Normal file
View file

@ -0,0 +1,128 @@
/*
* Copyright (C) 2010,2011,2012 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 "CacheManager.h"
#include "DStarDefines.h"
CCacheManager::CCacheManager() :
m_mutex(),
m_userCache(),
m_gatewayCache(),
m_repeaterCache()
{
}
CCacheManager::~CCacheManager()
{
}
CUserData* CCacheManager::findUser(const wxString& user)
{
wxMutexLocker locker(m_mutex);
CUserRecord* ur = m_userCache.find(user);
if (ur == NULL)
return NULL;
CRepeaterRecord* rr = m_repeaterCache.find(ur->getRepeater());
wxString gateway;
if (rr == NULL) {
gateway = ur->getRepeater();
gateway.Append(wxT(" "));
gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U);
gateway.Append(wxT("G"));
} else {
gateway = rr->getGateway();
}
CGatewayRecord* gr = m_gatewayCache.find(gateway);
if (gr == NULL)
return NULL;
return new CUserData(user, ur->getRepeater(), gr->getGateway(), gr->getAddress());
}
CGatewayData* CCacheManager::findGateway(const wxString& gateway)
{
wxMutexLocker locker(m_mutex);
CGatewayRecord* gr = m_gatewayCache.find(gateway);
if (gr == NULL)
return NULL;
return new CGatewayData(gateway, gr->getAddress(), gr->getProtocol());
}
CRepeaterData* CCacheManager::findRepeater(const wxString& repeater)
{
wxMutexLocker locker(m_mutex);
CRepeaterRecord* rr = m_repeaterCache.find(repeater);
wxString gateway;
if (rr == NULL) {
gateway = repeater;
gateway.Append(wxT(" "));
gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U);
gateway.Append(wxT("G"));
} else {
gateway = rr->getGateway();
}
CGatewayRecord* gr = m_gatewayCache.find(gateway);
if (gr == NULL)
return NULL;
return new CRepeaterData(repeater, gr->getGateway(), gr->getAddress(), gr->getProtocol());
}
void CCacheManager::updateUser(const wxString& user, const wxString& repeater, const wxString& gateway, const wxString& address, const wxString& timestamp, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
wxMutexLocker locker(m_mutex);
wxString repeater7 = repeater.Left(LONG_CALLSIGN_LENGTH - 1U);
wxString gateway7 = gateway.Left(LONG_CALLSIGN_LENGTH - 1U);
m_userCache.update(user, repeater, timestamp);
// Only store non-standard repeater-gateway pairs
if (!repeater7.IsSameAs(gateway7))
m_repeaterCache.update(repeater, gateway);
m_gatewayCache.update(gateway, address, protocol, addrLock, protoLock);
}
void CCacheManager::updateRepeater(const wxString& repeater, const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
wxMutexLocker locker(m_mutex);
wxString repeater7 = repeater.Left(LONG_CALLSIGN_LENGTH - 1U);
wxString gateway7 = gateway.Left(LONG_CALLSIGN_LENGTH - 1U);
// Only store non-standard repeater-gateway pairs
if (!repeater7.IsSameAs(gateway7))
m_repeaterCache.update(repeater, gateway);
m_gatewayCache.update(gateway, address, protocol, addrLock, protoLock);
}
void CCacheManager::updateGateway(const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
wxMutexLocker locker(m_mutex);
m_gatewayCache.update(gateway, address, protocol, addrLock, protoLock);
}

158
Common/CacheManager.h Normal file
View file

@ -0,0 +1,158 @@
/*
* Copyright (C) 2010,2011,2012 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 CacheManager_H
#define CacheManager_H
#include <wx/wx.h>
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include "RepeaterCache.h"
#include "GatewayCache.h"
#include "UserCache.h"
class CUserData {
public:
CUserData(const wxString& user, const wxString& repeater, const wxString& gateway, in_addr address) :
m_user(user),
m_repeater(repeater),
m_gateway(gateway),
m_address(address)
{
}
wxString getUser() const
{
return m_user;
}
wxString getRepeater() const
{
return m_repeater;
}
wxString getGateway() const
{
return m_gateway;
}
in_addr getAddress() const
{
return m_address;
}
private:
wxString m_user;
wxString m_repeater;
wxString m_gateway;
in_addr m_address;
};
class CRepeaterData {
public:
CRepeaterData(const wxString& repeater, const wxString& gateway, in_addr address, DSTAR_PROTOCOL protocol) :
m_repeater(repeater),
m_gateway(gateway),
m_address(address),
m_protocol(protocol)
{
}
wxString getRepeater() const
{
return m_repeater;
}
wxString getGateway() const
{
return m_gateway;
}
in_addr getAddress() const
{
return m_address;
}
DSTAR_PROTOCOL getProtocol() const
{
return m_protocol;
}
private:
wxString m_repeater;
wxString m_gateway;
in_addr m_address;
DSTAR_PROTOCOL m_protocol;
};
class CGatewayData {
public:
CGatewayData(const wxString& gateway, in_addr address, DSTAR_PROTOCOL protocol) :
m_gateway(gateway),
m_address(address),
m_protocol(protocol)
{
}
wxString getGateway() const
{
return m_gateway;
}
in_addr getAddress() const
{
return m_address;
}
DSTAR_PROTOCOL getProtocol() const
{
return m_protocol;
}
private:
wxString m_gateway;
in_addr m_address;
DSTAR_PROTOCOL m_protocol;
};
class CCacheManager {
public:
CCacheManager();
~CCacheManager();
CUserData* findUser(const wxString& user);
CGatewayData* findGateway(const wxString& gateway);
CRepeaterData* findRepeater(const wxString& repeater);
void updateUser(const wxString& user, const wxString& repeater, const wxString& gateway, const wxString& address, const wxString& timeStamp, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock);
void updateRepeater(const wxString& repeater, const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock);
void updateGateway(const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock);
private:
wxMutex m_mutex;
CUserCache m_userCache;
CGatewayCache m_gatewayCache;
CRepeaterCache m_repeaterCache;
};
#endif

76
Common/CallsignList.cpp Normal file
View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2011 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 "CallsignList.h"
#include "DStarDefines.h"
#include <wx/textfile.h>
CCallsignList::CCallsignList(const wxString& filename) :
m_filename(filename),
m_callsigns()
{
}
CCallsignList::~CCallsignList()
{
}
bool CCallsignList::load()
{
wxTextFile file;
bool res = file.Open(m_filename);
if (!res)
return false;
unsigned int lines = file.GetLineCount();
if (lines == 0U) {
file.Close();
return true;
}
m_callsigns.Alloc(lines);
wxString callsign = file.GetFirstLine();
while (!file.Eof()) {
callsign.MakeUpper();
callsign.Append(wxT(" "));
callsign.Truncate(LONG_CALLSIGN_LENGTH);
m_callsigns.Add(callsign);
callsign = file.GetNextLine();
}
file.Close();
return true;
}
unsigned int CCallsignList::getCount() const
{
return m_callsigns.GetCount();
}
bool CCallsignList::isInList(const wxString& callsign) const
{
return m_callsigns.Index(callsign) != wxNOT_FOUND;
}

40
Common/CallsignList.h Normal file
View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2011,2013 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 CallsignList_H
#define CallsignList_H
#include <wx/wx.h>
class CCallsignList {
public:
CCallsignList(const wxString& filename);
~CCallsignList();
bool load();
unsigned int getCount() const;
bool isInList(const wxString& callsign) const;
private:
wxString m_filename;
wxArrayString m_callsigns;
};
#endif

173
Common/CallsignServer.cpp Normal file
View file

@ -0,0 +1,173 @@
/*
* Copyright (C) 2012,2013,2014 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 "TCPReaderWriterClient.h"
#include "CallsignServer.h"
#include "DStarDefines.h"
#include "Utils.h"
#include "Defs.h"
const wxString CALLSERVER_HOSTNAME = wxT("dns.xreflector.net");
const unsigned int CALLSERVER_PORT = 20001U;
const unsigned int TCP_TIMEOUT = 10U;
CCallsignServer::CCallsignServer(const wxString& callsign, const wxString& address, CCacheManager* cache) :
wxThread(wxTHREAD_JOINABLE),
m_callsign(callsign),
m_address(address),
m_cache(cache),
m_timer(1U, 1U * 3600U), // 1 hour
m_killed(false)
{
wxASSERT(!callsign.IsEmpty());
wxASSERT(cache != NULL);
}
CCallsignServer::~CCallsignServer()
{
}
void CCallsignServer::start()
{
process(CALLSERVER_HOSTNAME, CALLSERVER_PORT);
Create();
Run();
}
void* CCallsignServer::Entry()
{
wxLogMessage(wxT("Starting the Callsign Server thread"));
m_timer.start();
try {
while (!m_killed) {
if (m_timer.hasExpired()) {
process(CALLSERVER_HOSTNAME, CALLSERVER_PORT);
m_timer.start();
}
Sleep(1000UL);
m_timer.clock();
}
}
catch (std::exception& e) {
wxString message(e.what(), wxConvLocal);
wxLogError(wxT("Exception raised in the Callsign Server thread - \"%s\""), message.c_str());
}
catch (...) {
wxLogError(wxT("Unknown exception raised in the Callsign Server thread"));
}
wxLogMessage(wxT("Stopping the Callsign Server thread"));
return NULL;
}
void CCallsignServer::stop()
{
m_killed = true;
Wait();
}
void CCallsignServer::process(const wxString& hostname, unsigned int port)
{
CTCPReaderWriterClient socket(hostname, port, m_address);
bool ret = socket.open();
if (!ret) {
wxLogMessage(wxT("Cannot connect to %s"), hostname.c_str());
return;
}
// Space for 5000 entries
unsigned int length = 5000U * (6U + 1U + 15U + 1U);
unsigned char* buffer = new unsigned char[length + 1U];
::memset(buffer, ' ', 29U);
for (unsigned int i = 0U; i < m_callsign.Len() && i < LONG_CALLSIGN_LENGTH - 1U; i++)
buffer[i + 0U] = m_callsign.GetChar(i);
::memcpy(buffer + 9U, "ircDDB Gateway", 14U);
socket.write(buffer, 29U);
unsigned int offset = 0U;
int n = socket.read(buffer, length, TCP_TIMEOUT);
if (n >= 0)
offset += n;
while (n >= 0 && offset < length) {
n = socket.read(buffer + offset, length - offset, TCP_TIMEOUT);
if (n == 0)
Sleep(TCP_TIMEOUT * 1000UL);
else if (n > 0)
offset += n;
}
buffer[offset] = 0x00U;
unsigned int count = 0U;
char* p = (char*)buffer;
for (;;) {
// Split into lines
char* p1 = ::strchr(p, 0x0A);
if (p1 != NULL)
*p1 = 0x00;
if (::strncmp(p, "DCS", 3U) == 0) {
char* p2 = ::strtok(p, " \t\r\n");
char* p3 = ::strtok(NULL, " \t\r\n");
if (p2 != NULL && p3 != NULL) {
wxString name = wxString(p2, wxConvLocal);
wxString address = wxString(p3, wxConvLocal);
if (!address.IsSameAs(wxT("0.0.0.0"))) {
wxLogMessage(wxT("DCS: %s\t%s"), name.c_str(), address.c_str());
name.resize(LONG_CALLSIGN_LENGTH - 1U, wxT(' '));
name.Append(wxT("G"));
m_cache->updateGateway(name, address, DP_DCS, false, true);
count++;
}
}
}
if (p1 == NULL)
break;
p = p1 + 1U;
}
wxLogMessage(wxT("Registered with %s using callsign %s"), hostname.c_str(), m_callsign.Left(LONG_CALLSIGN_LENGTH - 1U).c_str());
wxLogMessage(wxT("Loaded %u DCS reflectors from %s"), count, hostname.c_str());
delete[] buffer;
socket.close();
}

54
Common/CallsignServer.h Normal file
View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2012,2014 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 CallsignServer_H
#define CallsignServer_H
#include "CacheManager.h"
#include "Timer.h"
#include <wx/wx.h>
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
class CCallsignServer : public wxThread {
public:
CCallsignServer(const wxString& callsign, const wxString& address, CCacheManager* cache);
virtual ~CCallsignServer();
virtual void start();
virtual void* Entry();
virtual void stop();
private:
wxString m_callsign;
wxString m_address;
CCacheManager* m_cache;
CTimer m_timer;
bool m_killed;
void process(const wxString& hostname, unsigned int port);
};
#endif

295
Common/Common.vcxproj Normal file
View file

@ -0,0 +1,295 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}</ProjectGuid>
<RootNamespace>Common</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<IncludePath>$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<PreBuildEvent>
<Command />
</PreBuildEvent>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;DCS_LINK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PreBuildEvent>
<Command>
</Command>
</PreBuildEvent>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;DCS_LINK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<PreBuildEvent>
<Command />
</PreBuildEvent>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PreBuildEvent>
<Command>
</Command>
</PreBuildEvent>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>$(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="AMBEData.cpp" />
<ClCompile Include="AnnouncementUnit.cpp" />
<ClCompile Include="APRSCollector.cpp" />
<ClCompile Include="APRSWriter.cpp" />
<ClCompile Include="APRSWriterThread.cpp" />
<ClCompile Include="AudioUnit.cpp" />
<ClCompile Include="CacheManager.cpp" />
<ClCompile Include="CallsignList.cpp" />
<ClCompile Include="CallsignServer.cpp" />
<ClCompile Include="CCITTChecksum.cpp" />
<ClCompile Include="CCSData.cpp" />
<ClCompile Include="CCSHandler.cpp" />
<ClCompile Include="CCSProtocolHandler.cpp" />
<ClCompile Include="ConnectData.cpp" />
<ClCompile Include="DCSHandler.cpp" />
<ClCompile Include="DCSProtocolHandler.cpp" />
<ClCompile Include="DCSProtocolHandlerPool.cpp" />
<ClCompile Include="DDData.cpp" />
<ClCompile Include="DDHandler.cpp" />
<ClCompile Include="DExtraHandler.cpp" />
<ClCompile Include="DExtraProtocolHandler.cpp" />
<ClCompile Include="DExtraProtocolHandlerPool.cpp" />
<ClCompile Include="DPlusAuthenticator.cpp" />
<ClCompile Include="DPlusHandler.cpp" />
<ClCompile Include="DPlusProtocolHandler.cpp" />
<ClCompile Include="DPlusProtocolHandlerPool.cpp" />
<ClCompile Include="DRATSServer.cpp" />
<ClCompile Include="DTMF.cpp" />
<ClCompile Include="DummyRepeaterProtocolHandler.cpp" />
<ClCompile Include="DVTOOLFileReader.cpp" />
<ClCompile Include="EchoUnit.cpp" />
<ClCompile Include="G2Handler.cpp" />
<ClCompile Include="G2ProtocolHandler.cpp" />
<ClCompile Include="GatewayCache.cpp" />
<ClCompile Include="HBRepeaterProtocolHandler.cpp" />
<ClCompile Include="HeaderData.cpp" />
<ClCompile Include="HeaderLogger.cpp" />
<ClCompile Include="HeardData.cpp" />
<ClCompile Include="HostFile.cpp" />
<ClCompile Include="IcomRepeaterProtocolHandler.cpp" />
<ClCompile Include="IRCDDBGatewayConfig.cpp" />
<ClCompile Include="LogEvent.cpp" />
<ClCompile Include="Logger.cpp" />
<ClCompile Include="PollData.cpp" />
<ClCompile Include="RemoteHandler.cpp" />
<ClCompile Include="RemoteLinkData.cpp" />
<ClCompile Include="RemoteProtocolHandler.cpp" />
<ClCompile Include="RemoteRepeaterData.cpp" />
<ClCompile Include="RemoteStarNetGroup.cpp" />
<ClCompile Include="RemoteStarNetUser.cpp" />
<ClCompile Include="RepeaterCache.cpp" />
<ClCompile Include="RepeaterHandler.cpp" />
<ClCompile Include="SHA256.cpp" />
<ClCompile Include="SlowDataEncoder.cpp" />
<ClCompile Include="StarNetHandler.cpp" />
<ClCompile Include="StatusData.cpp" />
<ClCompile Include="TCPReaderWriterClient.cpp" />
<ClCompile Include="TCPReaderWriterServer.cpp" />
<ClCompile Include="TextCollector.cpp" />
<ClCompile Include="TextData.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="UDPReaderWriter.cpp" />
<ClCompile Include="UserCache.cpp" />
<ClCompile Include="Utils.cpp" />
<ClCompile Include="VersionUnit.cpp" />
<ClCompile Include="XLXHostsFileDownloader.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="AMBEData.h" />
<ClInclude Include="AnnouncementUnit.h" />
<ClInclude Include="APRSCollector.h" />
<ClInclude Include="APRSWriter.h" />
<ClInclude Include="APRSWriterThread.h" />
<ClInclude Include="AudioUnit.h" />
<ClInclude Include="CacheManager.h" />
<ClInclude Include="CallsignList.h" />
<ClInclude Include="CallsignServer.h" />
<ClInclude Include="CCITTChecksum.h" />
<ClInclude Include="CCSCallback.h" />
<ClInclude Include="CCSData.h" />
<ClInclude Include="CCSHandler.h" />
<ClInclude Include="CCSProtocolHandler.h" />
<ClInclude Include="ConnectData.h" />
<ClInclude Include="DCSHandler.h" />
<ClInclude Include="DCSProtocolHandler.h" />
<ClInclude Include="DCSProtocolHandlerPool.h" />
<ClInclude Include="DDData.h" />
<ClInclude Include="DDHandler.h" />
<ClInclude Include="Defs.h" />
<ClInclude Include="DExtraHandler.h" />
<ClInclude Include="DExtraProtocolHandler.h" />
<ClInclude Include="DExtraProtocolHandlerPool.h" />
<ClInclude Include="DPlusAuthenticator.h" />
<ClInclude Include="DPlusHandler.h" />
<ClInclude Include="DPlusProtocolHandler.h" />
<ClInclude Include="DPlusProtocolHandlerPool.h" />
<ClInclude Include="DRATSServer.h" />
<ClInclude Include="DStarDefines.h" />
<ClInclude Include="DTMF.h" />
<ClInclude Include="DummyRepeaterProtocolHandler.h" />
<ClInclude Include="DVTOOLFileReader.h" />
<ClInclude Include="EchoUnit.h" />
<ClInclude Include="G2Handler.h" />
<ClInclude Include="G2ProtocolHandler.h" />
<ClInclude Include="GatewayCache.h" />
<ClInclude Include="HBRepeaterProtocolHandler.h" />
<ClInclude Include="HeaderData.h" />
<ClInclude Include="HeaderLogger.h" />
<ClInclude Include="HeardData.h" />
<ClInclude Include="HostFile.h" />
<ClInclude Include="IcomRepeaterProtocolHandler.h" />
<ClInclude Include="IRCDDBGatewayConfig.h" />
<ClInclude Include="LogEvent.h" />
<ClInclude Include="Logger.h" />
<ClInclude Include="PollData.h" />
<ClInclude Include="ReflectorCallback.h" />
<ClInclude Include="RemoteHandler.h" />
<ClInclude Include="RemoteLinkData.h" />
<ClInclude Include="RemoteProtocolHandler.h" />
<ClInclude Include="RemoteRepeaterData.h" />
<ClInclude Include="RemoteStarNetGroup.h" />
<ClInclude Include="RemoteStarNetUser.h" />
<ClInclude Include="RepeaterCache.h" />
<ClInclude Include="RepeaterCallback.h" />
<ClInclude Include="RepeaterHandler.h" />
<ClInclude Include="RepeaterProtocolHandler.h" />
<ClInclude Include="RingBuffer.h" />
<ClInclude Include="SHA256.h" />
<ClInclude Include="SlowDataEncoder.h" />
<ClInclude Include="StarNetHandler.h" />
<ClInclude Include="StatusData.h" />
<ClInclude Include="TCPReaderWriterClient.h" />
<ClInclude Include="TCPReaderWriterServer.h" />
<ClInclude Include="TextCollector.h" />
<ClInclude Include="TextData.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="UDPReaderWriter.h" />
<ClInclude Include="UserCache.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="Version.h" />
<ClInclude Include="VersionUnit.h" />
<ClInclude Include="XLXHostsFileDownloader.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,437 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="AMBEData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AnnouncementUnit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="APRSCollector.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="AudioUnit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CacheManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CallsignList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CallsignServer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CCITTChecksum.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CCSData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CCSHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CCSProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ConnectData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DCSHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DCSProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DCSProtocolHandlerPool.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DDData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DDHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DExtraHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DExtraProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DExtraProtocolHandlerPool.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DPlusAuthenticator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DPlusHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DPlusProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DPlusProtocolHandlerPool.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DRATSServer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DTMF.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DummyRepeaterProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DVTOOLFileReader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EchoUnit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="G2Handler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="G2ProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GatewayCache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HBRepeaterProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HeaderData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HeaderLogger.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HeardData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HostFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IcomRepeaterProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IRCDDBGatewayConfig.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LogEvent.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Logger.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PollData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RemoteHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RemoteLinkData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RemoteProtocolHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RemoteRepeaterData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RemoteStarNetGroup.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RemoteStarNetUser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RepeaterCache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RepeaterHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SHA256.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SlowDataEncoder.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="StarNetHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="StatusData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TCPReaderWriterClient.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TCPReaderWriterServer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TextCollector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TextData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Timer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UDPReaderWriter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UserCache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VersionUnit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XLXHostsFileDownloader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="AMBEData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="AnnouncementUnit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="APRSCollector.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="AudioUnit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CacheManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CallsignList.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CallsignServer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CCITTChecksum.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CCSCallback.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CCSData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CCSHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CCSProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ConnectData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DCSHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DCSProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DCSProtocolHandlerPool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DDData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DDHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Defs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DExtraHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DExtraProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DExtraProtocolHandlerPool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DPlusAuthenticator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DPlusHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DPlusProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DPlusProtocolHandlerPool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DRATSServer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarDefines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DTMF.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DummyRepeaterProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DVTOOLFileReader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EchoUnit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="G2Handler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="G2ProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GatewayCache.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HBRepeaterProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HeaderData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HeaderLogger.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HeardData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HostFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IcomRepeaterProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IRCDDBGatewayConfig.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LogEvent.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Logger.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PollData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ReflectorCallback.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RemoteHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RemoteLinkData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RemoteProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RemoteRepeaterData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RemoteStarNetGroup.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RemoteStarNetUser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RepeaterCache.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RepeaterCallback.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RepeaterHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RepeaterProtocolHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RingBuffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SHA256.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SlowDataEncoder.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="StarNetHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="StatusData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TCPReaderWriterClient.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TCPReaderWriterServer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TextCollector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TextData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Timer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UDPReaderWriter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UserCache.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VersionUnit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="XLXHostsFileDownloader.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

543
Common/ConnectData.cpp Normal file
View file

@ -0,0 +1,543 @@
/*
* Copyright (C) 2010,2012,2013 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 "ConnectData.h"
#include "DStarDefines.h"
#include "Version.h"
#include "Utils.h"
const wxChar* HTML = wxT("<table border=\"0\" width=\"95%%\"><tr><td width=\"4%%\"><img border=\"0\" src=%s></td><td width=\"96%%\"><font size=\"2\"><b>%s</b> ircDDB Gateway %s</font></td></tr></table>");
CConnectData::CConnectData(GATEWAY_TYPE gatewayType, const wxString& repeater, const wxString& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(gatewayType),
m_repeater(repeater),
m_reflector(reflector),
m_type(type),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
wxASSERT(yourPort > 0U);
wxASSERT(!repeater.IsEmpty());
wxASSERT(!reflector.IsEmpty());
}
CConnectData::CConnectData(const wxString& repeater, const wxString& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(GT_REPEATER),
m_repeater(repeater),
m_reflector(reflector),
m_type(type),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
wxASSERT(yourPort > 0U);
wxASSERT(!repeater.IsEmpty());
wxASSERT(!reflector.IsEmpty());
}
CConnectData::CConnectData(const wxString& repeater, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(GT_REPEATER),
m_repeater(repeater),
m_reflector(),
m_type(type),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
wxASSERT(yourPort > 0U);
wxASSERT(!repeater.IsEmpty());
}
CConnectData::CConnectData(const wxString& repeater, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(GT_REPEATER),
m_repeater(repeater),
m_reflector(),
m_type(CT_UNLINK),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
wxASSERT(yourPort > 0U);
wxASSERT(!repeater.IsEmpty());
}
CConnectData::CConnectData(CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(GT_REPEATER),
m_repeater(),
m_reflector(),
m_type(type),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
wxASSERT(yourPort > 0U);
}
CConnectData::CConnectData() :
m_gatewayType(GT_REPEATER),
m_repeater(wxT(" ")),
m_reflector(),
m_type(CT_LINK1),
m_locator(),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U)
{
}
CConnectData::~CConnectData()
{
}
bool CConnectData::setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 11U);
wxASSERT(yourPort > 0U);
m_repeater = wxString((const char*)data, wxConvLocal, LONG_CALLSIGN_LENGTH);
m_repeater.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 0U]);
m_reflector = wxT(" ");
m_reflector.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 1U]);
switch (length) {
case 11U:
if (m_reflector.IsSameAs(wxT(" ")))
m_type = CT_UNLINK;
else
m_type = CT_LINK1;
break;
case 14U:
if (data[LONG_CALLSIGN_LENGTH + 2U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'C' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_ACK;
else if (data[LONG_CALLSIGN_LENGTH + 2U] == 'N' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_NAK;
else
return false;
break;
default:
return false;
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CConnectData::setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 11U);
wxASSERT(yourPort > 0U);
m_repeater = wxString((const char*)data, wxConvLocal, LONG_CALLSIGN_LENGTH);
m_repeater.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 0U]);
switch (length) {
case 519U:
m_reflector = wxString((const char*)(data + LONG_CALLSIGN_LENGTH + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH);
m_reflector.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 1U]);
m_type = CT_LINK1;
break;
case 19U:
m_reflector = wxString((const char*)(data + LONG_CALLSIGN_LENGTH + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH);
m_reflector.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 1U]);
m_type = CT_UNLINK;
break;
case 14U:
if (data[LONG_CALLSIGN_LENGTH + 2U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'C' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_ACK;
else if (data[LONG_CALLSIGN_LENGTH + 2U] == 'N' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_NAK;
else
return false;
break;
default:
return false;
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CConnectData::setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 14U);
wxASSERT(yourPort > 0U);
m_repeater = wxString((const char*)data, wxConvLocal, LONG_CALLSIGN_LENGTH);
m_repeater.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 0U]);
if (data[LONG_CALLSIGN_LENGTH + 2U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'C' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_ACK;
else if (data[LONG_CALLSIGN_LENGTH + 2U] == 'N' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_NAK;
else
return false;
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CConnectData::setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 5U);
wxASSERT(yourPort > 0U);
switch (length) {
case 5U:
switch (data[4U]) {
case 0x01:
m_type = CT_LINK1;
break;
case 0x00:
m_type = CT_UNLINK;
break;
}
break;
case 8U: {
wxString reply((const char*)(data + 4U), wxConvLocal, 4U);
wxLogMessage(wxT("D-Plus reply is %.4s"), reply.c_str());
if (::memcmp(data + 4U, "OKRW", 4U) == 0)
m_type = CT_ACK;
else
m_type = CT_NAK;
}
break;
case 28U:
m_repeater = wxString((const char*)(data + 4U), wxConvLocal);
m_type = CT_LINK2;
break;
default:
return false;
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
unsigned int CConnectData::getDExtraData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 11U);
::memset(data, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_repeater.Len() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i] = m_repeater.GetChar(i);
data[LONG_CALLSIGN_LENGTH + 0U] = m_repeater.GetChar(LONG_CALLSIGN_LENGTH - 1U);
switch (m_type) {
case CT_LINK1:
case CT_LINK2:
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 0x00;
return 11U;
case CT_UNLINK:
data[LONG_CALLSIGN_LENGTH + 1U] = ' ';
data[LONG_CALLSIGN_LENGTH + 2U] = 0x00;
return 11U;
case CT_ACK:
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 'A';
data[LONG_CALLSIGN_LENGTH + 3U] = 'C';
data[LONG_CALLSIGN_LENGTH + 4U] = 'K';
data[LONG_CALLSIGN_LENGTH + 5U] = 0x00;
return 14U;
case CT_NAK:
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 'N';
data[LONG_CALLSIGN_LENGTH + 3U] = 'A';
data[LONG_CALLSIGN_LENGTH + 4U] = 'K';
data[LONG_CALLSIGN_LENGTH + 5U] = 0x00;
return 14U;
default:
return 0U;
}
}
unsigned int CConnectData::getDCSData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 519U);
::memset(data, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_repeater.Len() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i] = m_repeater.GetChar(i);
data[LONG_CALLSIGN_LENGTH + 0U] = m_repeater.GetChar(LONG_CALLSIGN_LENGTH - 1U);
switch (m_type) {
case CT_LINK1:
case CT_LINK2: {
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 0x00U;
::memset(data + 11U, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_reflector.Len() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i + 11U] = m_reflector.GetChar(i);
wxString html;
switch (m_gatewayType) {
case GT_HOTSPOT:
html.Printf(HTML, wxT("hotspot.jpg"), wxT("HOTSPOT"), VERSION.c_str());
break;
case GT_DONGLE:
html.Printf(HTML, wxT("dongle.jpg"), wxT("DONGLE"), VERSION.c_str());
break;
case GT_STARNET:
html.Printf(HTML, wxT("hf.jpg"), wxT("STARNET"), VERSION.c_str());
break;
default:
html.Printf(HTML, wxT("hf.jpg"), wxT("REPEATER"), VERSION.c_str());
break;
}
::memset(data + 19U, 0x00U, 500U);
for (unsigned int i = 0U; i < html.Len(); i++)
data[i + 19U] = html.GetChar(i);
}
return 519U;
case CT_UNLINK:
data[LONG_CALLSIGN_LENGTH + 1U] = 0x20U;
data[LONG_CALLSIGN_LENGTH + 2U] = 0x00U;
::memset(data + 11U, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_reflector.Len() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i + 11U] = m_reflector.GetChar(i);
return 19U;
case CT_ACK:
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 'A';
data[LONG_CALLSIGN_LENGTH + 3U] = 'C';
data[LONG_CALLSIGN_LENGTH + 4U] = 'K';
data[LONG_CALLSIGN_LENGTH + 5U] = 0x00;
return 14U;
case CT_NAK:
data[LONG_CALLSIGN_LENGTH + 1U] = 0x20U;
data[LONG_CALLSIGN_LENGTH + 2U] = 'N';
data[LONG_CALLSIGN_LENGTH + 3U] = 'A';
data[LONG_CALLSIGN_LENGTH + 4U] = 'K';
data[LONG_CALLSIGN_LENGTH + 5U] = 0x00;
return 14U;
default:
return 0U;
}
}
unsigned int CConnectData::getCCSData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 39U);
::memset(data, ' ', 39U);
for (unsigned int i = 0U; i < m_repeater.Len() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i] = m_repeater.GetChar(i);
data[LONG_CALLSIGN_LENGTH + 0U] = m_repeater.GetChar(LONG_CALLSIGN_LENGTH - 1U);
switch (m_type) {
case CT_LINK1:
case CT_LINK2: {
data[9U] = 0x41U;
data[10U] = '@';
for (unsigned int i = 0U; i < m_locator.Len(); i++)
data[11U + i] = m_locator.GetChar(i);
data[17U] = 0x20U;
data[18U] = '@';
wxString text;
text.Printf(wxT("ircDDB_GW-%s"), VERSION.Left(8U).c_str());
for (unsigned int i = 0U; i < text.Len(); i++)
data[19U + i] = text.GetChar(i);
}
return 39U;
case CT_UNLINK:
return 19U;
default:
return 0U;
}
}
unsigned int CConnectData::getDPlusData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 28U);
switch (m_type) {
case CT_LINK1:
data[0U] = 0x05;
data[1U] = 0x00;
data[2U] = 0x18;
data[3U] = 0x00;
data[4U] = 0x01;
return 5U;
case CT_LINK2: {
data[0U] = 0x1C;
data[1U] = 0xC0;
data[2U] = 0x04;
data[3U] = 0x00;
for (unsigned int i = 4U; i < 20U; i++)
data[i] = 0x00;
wxString callsign = m_repeater;
callsign.Trim();
for (unsigned int i = 0U; i < callsign.Len(); i++)
data[i + 4U] = callsign.GetChar(i);
data[20U] = 'D';
data[21U] = 'V';
data[22U] = '0';
data[23U] = '1';
data[24U] = '9';
data[25U] = '9';
data[26U] = '9';
data[27U] = '9';
}
return 28U;
case CT_UNLINK:
data[0U] = 0x05;
data[1U] = 0x00;
data[2U] = 0x18;
data[3U] = 0x00;
data[4U] = 0x00;
return 5U;
case CT_ACK:
data[0U] = 0x08;
data[1U] = 0xC0;
data[2U] = 0x04;
data[3U] = 0x00;
data[4U] = 'O';
data[5U] = 'K';
data[6U] = 'R';
data[7U] = 'W';
return 8U;
case CT_NAK:
data[0U] = 0x08;
data[1U] = 0xC0;
data[2U] = 0x04;
data[3U] = 0x00;
data[4U] = 'B';
data[5U] = 'U';
data[6U] = 'S';
data[7U] = 'Y';
return 8U;
default:
return 0U;
}
}
in_addr CConnectData::getYourAddress() const
{
return m_yourAddress;
}
unsigned int CConnectData::getYourPort() const
{
return m_yourPort;
}
unsigned int CConnectData::getMyPort() const
{
return m_myPort;
}
wxString CConnectData::getRepeater() const
{
return m_repeater;
}
wxString CConnectData::getReflector() const
{
return m_reflector;
}
CD_TYPE CConnectData::getType() const
{
return m_type;
}
void CConnectData::setLocator(const wxString& locator)
{
m_locator = locator;
}

81
Common/ConnectData.h Normal file
View file

@ -0,0 +1,81 @@
/*
* Copyright (C) 2010,2012,2013 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 ConnectData_H
#define ConnectData_H
#include "Defs.h"
#include <wx/wx.h>
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
enum CD_TYPE {
CT_LINK1,
CT_LINK2,
CT_UNLINK,
CT_ACK,
CT_NAK
};
class CConnectData {
public:
CConnectData(GATEWAY_TYPE gatewayType, const wxString& repeater, const wxString& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData(const wxString& repeater, const wxString& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData(const wxString& repeater, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData(const wxString& repeater, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData(CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData();
~CConnectData();
bool setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
unsigned int getDExtraData(unsigned char* data, unsigned int length) const;
unsigned int getDPlusData(unsigned char* data, unsigned int length) const;
unsigned int getDCSData(unsigned char* data, unsigned int length) const;
unsigned int getCCSData(unsigned char* data, unsigned int length) const;
wxString getRepeater() const;
wxString getReflector() const;
CD_TYPE getType() const;
in_addr getYourAddress() const;
unsigned int getYourPort() const;
unsigned int getMyPort() const;
void setLocator(const wxString& locator);
private:
GATEWAY_TYPE m_gatewayType;
wxString m_repeater;
wxString m_reflector;
CD_TYPE m_type;
wxString m_locator;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
};
#endif

884
Common/DCSHandler.cpp Normal file
View file

@ -0,0 +1,884 @@
/*
* Copyright (C) 2012-2015 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "RepeaterHandler.h"
#include "DStarDefines.h"
#include "DCSHandler.h"
#include "Utils.h"
unsigned int CDCSHandler::m_maxReflectors = 0U;
CDCSHandler** CDCSHandler::m_reflectors = NULL;
CDCSProtocolHandlerPool* CDCSHandler::m_pool = NULL;
CDCSProtocolHandler* CDCSHandler::m_incoming = NULL;
bool CDCSHandler::m_stateChange = false;
GATEWAY_TYPE CDCSHandler::m_gatewayType = GT_REPEATER;
CHeaderLogger* CDCSHandler::m_headerLogger = NULL;
CCallsignList* CDCSHandler::m_whiteList = NULL;
CCallsignList* CDCSHandler::m_blackList = NULL;
CDCSHandler::CDCSHandler(IReflectorCallback* handler, const wxString& reflector, const wxString& repeater, CDCSProtocolHandler* protoHandler, const in_addr& address, unsigned int port, DIRECTION direction) :
m_reflector(reflector.Clone()),
m_repeater(repeater.Clone()),
m_handler(protoHandler),
m_yourAddress(address),
m_yourPort(port),
m_myPort(0U),
m_direction(direction),
m_linkState(DCS_LINKING),
m_destination(handler),
m_time(),
m_pollTimer(1000U, 5U),
m_pollInactivityTimer(1000U, 60U),
m_tryTimer(1000U, 1U),
m_tryCount(0U),
m_dcsId(0x00U),
m_dcsSeq(0x00U),
m_seqNo(0x00U),
m_inactivityTimer(1000U, NETWORK_TIMEOUT),
m_yourCall(),
m_myCall1(),
m_myCall2(),
m_rptCall1(),
m_rptCall2()
{
wxASSERT(protoHandler != NULL);
wxASSERT(handler != NULL);
wxASSERT(port > 0U);
m_myPort = protoHandler->getPort();
m_pollInactivityTimer.start();
m_time = ::time(NULL);
if (direction == DIR_INCOMING) {
m_pollTimer.start();
m_stateChange = true;
m_linkState = DCS_LINKED;
} else {
m_linkState = DCS_LINKING;
m_tryTimer.start();
}
}
CDCSHandler::~CDCSHandler()
{
if (m_direction == DIR_OUTGOING)
m_pool->release(m_handler);
}
void CDCSHandler::initialise(unsigned int maxReflectors)
{
wxASSERT(maxReflectors > 0U);
m_maxReflectors = maxReflectors;
m_reflectors = new CDCSHandler*[m_maxReflectors];
for (unsigned int i = 0U; i < m_maxReflectors; i++)
m_reflectors[i] = NULL;
}
void CDCSHandler::setDCSProtocolHandlerPool(CDCSProtocolHandlerPool* pool)
{
wxASSERT(pool != NULL);
m_pool = pool;
}
void CDCSHandler::setDCSProtocolIncoming(CDCSProtocolHandler* handler)
{
wxASSERT(handler != NULL);
m_incoming = handler;
}
void CDCSHandler::setHeaderLogger(CHeaderLogger* logger)
{
m_headerLogger = logger;
}
void CDCSHandler::setGatewayType(GATEWAY_TYPE type)
{
m_gatewayType = type;
}
void CDCSHandler::setWhiteList(CCallsignList* list)
{
wxASSERT(list != NULL);
m_whiteList = list;
}
void CDCSHandler::setBlackList(CCallsignList* list)
{
wxASSERT(list != NULL);
m_blackList = list;
}
wxString CDCSHandler::getIncoming(const wxString& callsign)
{
wxString incoming;
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDCSHandler* reflector = m_reflectors[i];
if (reflector != NULL && reflector->m_direction == DIR_INCOMING && reflector->m_repeater.IsSameAs(callsign)) {
incoming.Append(reflector->m_reflector);
incoming.Append(wxT(" "));
}
}
return incoming;
}
void CDCSHandler::getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data)
{
wxASSERT(handler != NULL);
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDCSHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (reflector->m_destination == handler) {
if (reflector->m_direction == DIR_INCOMING && reflector->m_repeater.IsEmpty()) {
if (reflector->m_linkState != DCS_UNLINKING)
data.addLink(reflector->m_reflector, PROTO_DCS, reflector->m_linkState == DCS_LINKED, DIR_INCOMING, true);
} else {
if (reflector->m_linkState != DCS_UNLINKING)
data.addLink(reflector->m_reflector, PROTO_DCS, reflector->m_linkState == DCS_LINKED, reflector->m_direction, false);
}
}
}
}
}
void CDCSHandler::process(CAMBEData& data)
{
in_addr yourAddress = data.getYourAddress();
unsigned int yourPort = data.getYourPort();
unsigned int myPort = data.getMyPort();
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDCSHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (reflector->m_yourAddress.s_addr == yourAddress.s_addr &&
reflector->m_yourPort == yourPort &&
reflector->m_myPort == myPort) {
reflector->processInt(data);
return;
}
}
}
}
void CDCSHandler::process(CPollData& poll)
{
wxString reflector = poll.getData1();
wxString repeater = poll.getData2();
in_addr yourAddress = poll.getYourAddress();
unsigned int yourPort = poll.getYourPort();
unsigned int myPort = poll.getMyPort();
unsigned int length = poll.getLength();
// Check to see if we already have a link
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDCSHandler* handler = m_reflectors[i];
if (handler != NULL) {
if (handler->m_reflector.IsSameAs(reflector) &&
handler->m_repeater.IsSameAs(repeater) &&
handler->m_yourAddress.s_addr == yourAddress.s_addr &&
handler->m_yourPort == yourPort &&
handler->m_myPort == myPort &&
handler->m_direction == DIR_OUTGOING &&
handler->m_linkState == DCS_LINKED &&
length == 22U) {
handler->m_pollInactivityTimer.start();
CPollData reply(handler->m_repeater, handler->m_reflector, handler->m_direction, handler->m_yourAddress, handler->m_yourPort);
handler->m_handler->writePoll(reply);
return;
} else if (handler->m_reflector.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(reflector.Left(LONG_CALLSIGN_LENGTH - 1U)) &&
handler->m_yourAddress.s_addr == yourAddress.s_addr &&
handler->m_yourPort == yourPort &&
handler->m_myPort == myPort &&
handler->m_direction == DIR_INCOMING &&
handler->m_linkState == DCS_LINKED &&
length == 17U) {
handler->m_pollInactivityTimer.start();
return;
}
}
}
wxLogMessage(wxT("Unknown incoming DCS poll from %s"), reflector.c_str());
}
void CDCSHandler::process(CConnectData& connect)
{
CD_TYPE type = connect.getType();
if (type == CT_ACK || type == CT_NAK || type == CT_UNLINK) {
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL) {
bool res = m_reflectors[i]->processInt(connect, type);
if (res) {
delete m_reflectors[i];
m_reflectors[i] = NULL;
}
}
}
return;
}
// else if type == CT_LINK1 or type == CT_LINK2
in_addr yourAddress = connect.getYourAddress();
unsigned int yourPort = connect.getYourPort();
unsigned int myPort = connect.getMyPort();
wxString repeaterCallsign = connect.getRepeater();
wxString reflectorCallsign = connect.getReflector();
// Check that it isn't a duplicate
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL) {
if (m_reflectors[i]->m_direction == DIR_INCOMING &&
m_reflectors[i]->m_yourAddress.s_addr == yourAddress.s_addr &&
m_reflectors[i]->m_yourPort == yourPort &&
m_reflectors[i]->m_myPort == myPort &&
m_reflectors[i]->m_repeater.IsSameAs(reflectorCallsign) &&
m_reflectors[i]->m_reflector.IsSameAs(repeaterCallsign))
return;
}
}
// Check the validity of our repeater callsign
IReflectorCallback* handler = CRepeaterHandler::findDVRepeater(reflectorCallsign);
if (handler == NULL) {
wxLogMessage(wxT("DCS connect to unknown reflector %s from %s"), reflectorCallsign.c_str(), repeaterCallsign.c_str());
CConnectData reply(repeaterCallsign, reflectorCallsign, CT_NAK, connect.getYourAddress(), connect.getYourPort());
m_incoming->writeConnect(reply);
return;
}
// A new connect packet indicates the need for a new entry
wxLogMessage(wxT("New incoming DCS link to %s from %s"), reflectorCallsign.c_str(), repeaterCallsign.c_str());
CDCSHandler* dcs = new CDCSHandler(handler, repeaterCallsign, reflectorCallsign, m_incoming, yourAddress, yourPort, DIR_INCOMING);
bool found = false;
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] == NULL) {
m_reflectors[i] = dcs;
found = true;
break;
}
}
if (found) {
CConnectData reply(repeaterCallsign, reflectorCallsign, CT_ACK, yourAddress, yourPort);
m_incoming->writeConnect(reply);
} else {
CConnectData reply(repeaterCallsign, reflectorCallsign, CT_NAK, yourAddress, yourPort);
m_incoming->writeConnect(reply);
wxLogError(wxT("No space to add new DCS link, ignoring"));
delete dcs;
}
}
void CDCSHandler::link(IReflectorCallback* handler, const wxString& repeater, const wxString &gateway, const in_addr& address)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL) {
if (m_reflectors[i]->m_direction == DIR_OUTGOING && m_reflectors[i]->m_destination == handler && m_reflectors[i]->m_linkState != DCS_UNLINKING)
return;
}
}
CDCSProtocolHandler* protoHandler = m_pool->getHandler();
if (protoHandler == NULL)
return;
CDCSHandler* dcs = new CDCSHandler(handler, gateway, repeater, protoHandler, address, DCS_PORT, DIR_OUTGOING);
bool found = false;
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] == NULL) {
m_reflectors[i] = dcs;
found = true;
break;
}
}
if (found) {
CConnectData reply(m_gatewayType, repeater, gateway, CT_LINK1, address, DCS_PORT);
protoHandler->writeConnect(reply);
} else {
wxLogError(wxT("No space to add new DCS link, ignoring"));
delete dcs;
}
}
void CDCSHandler::unlink(IReflectorCallback* handler, const wxString& callsign, bool exclude)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDCSHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
bool found = false;
if (exclude) {
if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination == handler && !reflector->m_reflector.IsSameAs(callsign)) {
wxLogMessage(wxT("Removing outgoing DCS link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str());
if (reflector->m_linkState == DCS_LINKING || reflector->m_linkState == DCS_LINKED) {
CConnectData connect(reflector->m_repeater, reflector->m_reflector, CT_UNLINK, reflector->m_yourAddress, reflector->m_yourPort);
reflector->m_handler->writeConnect(connect);
reflector->m_linkState = DCS_UNLINKING;
reflector->m_tryTimer.start(1U);
reflector->m_tryCount = 0U;
}
found = true;
}
} else {
if (reflector->m_destination == handler && reflector->m_reflector.IsSameAs(callsign)) {
wxLogMessage(wxT("Removing DCS link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str());
if (reflector->m_linkState == DCS_LINKING || reflector->m_linkState == DCS_LINKED) {
CConnectData connect(reflector->m_repeater, reflector->m_reflector, CT_UNLINK, reflector->m_yourAddress, reflector->m_yourPort);
reflector->m_handler->writeConnect(connect);
reflector->m_linkState = DCS_UNLINKING;
reflector->m_tryTimer.start(1U);
reflector->m_tryCount = 0U;
}
found = true;
}
}
// If an active link with incoming traffic, send an EOT to the repeater
if (found) {
if (reflector->m_dcsId != 0x00U) {
unsigned int seq = reflector->m_dcsSeq + 1U;
if (seq == 21U)
seq = 0U;
CAMBEData data;
data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES);
data.setSeq(seq);
data.setEnd(true);
data.setId(reflector->m_dcsId);
reflector->m_destination->process(data, reflector->m_direction, AS_DCS);
}
m_stateChange = true;
}
}
}
}
void CDCSHandler::unlink()
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDCSHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (!reflector->m_repeater.IsEmpty()) {
wxLogMessage(wxT("Unlinking from DCS reflector %s"), reflector->m_reflector.c_str());
CConnectData connect(reflector->m_repeater, reflector->m_reflector, CT_UNLINK, reflector->m_yourAddress, reflector->m_yourPort);
reflector->m_handler->writeConnect(connect);
reflector->m_linkState = DCS_UNLINKING;
reflector->m_tryTimer.start(1U);
reflector->m_tryCount = 0U;
}
}
}
}
void CDCSHandler::writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL)
m_reflectors[i]->writeHeaderInt(handler, header, direction);
}
}
void CDCSHandler::writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL)
m_reflectors[i]->writeAMBEInt(handler, data, direction);
}
}
void CDCSHandler::gatewayUpdate(const wxString& reflector, const wxString& address)
{
wxString gateway = reflector;
gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U);
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDCSHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (reflector->m_reflector.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(gateway)) {
if (!address.IsEmpty()) {
// A new address, change the value
wxLogMessage(wxT("Changing IP address of DCS gateway or reflector %s to %s"), reflector->m_reflector.c_str(), address.c_str());
reflector->m_yourAddress.s_addr = ::inet_addr(address.mb_str());
} else {
wxLogMessage(wxT("IP address for DCS gateway or reflector %s has been removed"), reflector->m_reflector.c_str());
// No address, this probably shouldn't happen....
if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination != NULL)
reflector->m_destination->linkFailed(DP_DCS, reflector->m_reflector, false);
m_stateChange = true;
delete m_reflectors[i];
m_reflectors[i] = NULL;
}
}
}
}
}
void CDCSHandler::clock(unsigned int ms)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL) {
bool ret = m_reflectors[i]->clockInt(ms);
if (ret) {
delete m_reflectors[i];
m_reflectors[i] = NULL;
}
}
}
}
void CDCSHandler::finalise()
{
for (unsigned int i = 0U; i < m_maxReflectors; i++)
delete m_reflectors[i];
delete[] m_reflectors;
}
void CDCSHandler::processInt(CAMBEData& data)
{
// Make a copy of the AMBE data so that any changes made here don't modify the original
CAMBEData temp(data);
unsigned int id = temp.getId();
CHeaderData& header = temp.getHeader();
unsigned int seqNo = temp.getSeq();
wxString my = header.getMyCall1();
wxString rpt2 = header.getRptCall2();
if (m_whiteList != NULL) {
bool res = m_whiteList->isInList(my);
if (!res) {
wxLogMessage(wxT("%s rejected from DCS as not found in the white list"), my.c_str());
m_dcsId = 0x00U;
return;
}
}
if (m_blackList != NULL) {
bool res = m_blackList->isInList(my);
if (res) {
wxLogMessage(wxT("%s rejected from DCS as found in the black list"), my.c_str());
m_dcsId = 0x00U;
return;
}
}
if (m_linkState != DCS_LINKED)
return;
switch (m_direction) {
case DIR_OUTGOING:
if (!m_reflector.IsSameAs(rpt2))
return;
if (m_dcsId == 0x00U && seqNo != 0U)
return;
if (m_dcsId == 0x00U) { // && seqNo == 0U) {
// Write to Header.log if it's enabled
if (m_headerLogger != NULL)
m_headerLogger->write(wxT("DCS"), header);
m_dcsId = id;
m_dcsSeq = 0x00U;
m_inactivityTimer.start();
header.setCQCQCQ();
header.setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(header, m_direction, AS_DCS);
}
if (id == m_dcsId) {
m_pollInactivityTimer.start();
m_inactivityTimer.start();
m_dcsSeq = seqNo;
if (m_dcsSeq == 0U) {
// Send the header every 21 frames
header.setCQCQCQ();
header.setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(header, m_direction, AS_DUP);
}
m_destination->process(temp, m_direction, AS_DCS);
if (temp.isEnd()) {
m_dcsId = 0x00U;
m_dcsSeq = 0x00U;
m_inactivityTimer.stop();
}
}
break;
case DIR_INCOMING:
if (!m_repeater.IsSameAs(rpt2))
return;
if (m_dcsId == 0x00U && seqNo != 0U)
return;
if (m_dcsId == 0x00U) { // && seqNo == 0U) {
// Write to Header.log if it's enabled
if (m_headerLogger != NULL)
m_headerLogger->write(wxT("DCS"), header);
m_dcsId = id;
m_dcsSeq = 0x00U;
m_inactivityTimer.start();
header.setCQCQCQ();
header.setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(header, m_direction, AS_DCS);
}
if (id == m_dcsId) {
m_pollInactivityTimer.start();
m_inactivityTimer.start();
m_dcsSeq = seqNo;
if (m_dcsSeq == 0U) {
// Send the header every 21 frames
header.setCQCQCQ();
header.setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(header, m_direction, AS_DUP);
}
m_destination->process(temp, m_direction, AS_DCS);
if (temp.isEnd()) {
m_dcsId = 0x00U;
m_dcsSeq = 0x00U;
m_inactivityTimer.stop();
}
}
break;
}
}
bool CDCSHandler::processInt(CConnectData& connect, CD_TYPE type)
{
in_addr yourAddress = connect.getYourAddress();
unsigned int yourPort = connect.getYourPort();
unsigned int myPort = connect.getMyPort();
wxString repeater = connect.getRepeater();
if (m_yourAddress.s_addr != yourAddress.s_addr || m_yourPort != yourPort || m_myPort != myPort)
return false;
switch (type) {
case CT_ACK:
if (!m_repeater.IsSameAs(repeater))
return false;
if (m_linkState == DCS_LINKING) {
wxLogMessage(wxT("DCS ACK message received from %s"), m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkUp(DP_DCS, m_reflector);
m_tryTimer.stop();
m_stateChange = true;
m_linkState = DCS_LINKED;
}
return false;
case CT_NAK:
if (!m_repeater.IsSameAs(repeater))
return false;
if (m_linkState == DCS_LINKING) {
wxLogMessage(wxT("DCS NAK message received from %s"), m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkRefused(DP_DCS, m_reflector);
return true;
}
if (m_linkState == DCS_UNLINKING) {
wxLogMessage(wxT("DCS NAK message received from %s"), m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkFailed(DP_DCS, m_reflector, false);
return true;
}
return false;
case CT_UNLINK:
if (!m_reflector.IsSameAs(repeater))
return false;
if (m_linkState == DCS_LINKED) {
wxLogMessage(wxT("DCS disconnect message received from %s"), m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkFailed(DP_DCS, m_reflector, false);
m_stateChange = true;
}
return true;
default:
return false;
}
}
bool CDCSHandler::clockInt(unsigned int ms)
{
m_pollInactivityTimer.clock(ms);
m_inactivityTimer.clock(ms);
m_pollTimer.clock(ms);
m_tryTimer.clock(ms);
if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) {
m_pollInactivityTimer.start();
m_stateChange = true;
m_dcsId = 0x00U;
m_dcsSeq = 0x00U;
switch (m_linkState) {
case DCS_LINKING:
wxLogMessage(wxT("DCS link to %s has failed to connect"), m_reflector.c_str());
break;
case DCS_LINKED:
wxLogMessage(wxT("DCS link to %s has failed (poll inactivity)"), m_reflector.c_str());
break;
case DCS_UNLINKING:
wxLogMessage(wxT("DCS link to %s has failed to disconnect cleanly"), m_reflector.c_str());
break;
default:
break;
}
if (m_direction == DIR_OUTGOING) {
bool reconnect = m_destination->linkFailed(DP_DCS, m_reflector, true);
if (reconnect) {
CConnectData reply(m_gatewayType, m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort);
m_handler->writeConnect(reply);
m_linkState = DCS_LINKING;
m_tryTimer.start(1U);
m_tryCount = 0U;
return false;
}
}
return true;
}
if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) {
m_dcsId = 0x00U;
m_dcsSeq = 0x00U;
m_inactivityTimer.stop();
}
if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) {
m_pollTimer.start();
CPollData poll(m_repeater, m_reflector, m_direction, m_yourAddress, m_yourPort);
m_handler->writePoll(poll);
}
if (m_linkState == DCS_LINKING) {
if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) {
CConnectData reply(m_gatewayType, m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort);
m_handler->writeConnect(reply);
unsigned int timeout = calcBackoff();
m_tryTimer.start(timeout);
}
}
if (m_linkState == DCS_UNLINKING) {
if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) {
CConnectData connect(m_repeater, m_reflector, CT_UNLINK, m_yourAddress, m_yourPort);
m_handler->writeConnect(connect);
unsigned int timeout = calcBackoff();
m_tryTimer.start(timeout);
}
}
return false;
}
void CDCSHandler::writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction)
{
if (m_linkState != DCS_LINKED)
return;
// Is it link in the right direction
if (m_direction != direction)
return;
if (m_destination != handler)
return;
// Already in use?
if (m_dcsId != 0x00)
return;
m_seqNo = 0U;
m_myCall1 = header.getMyCall1();
m_myCall2 = header.getMyCall2();
m_yourCall = header.getYourCall();
m_rptCall1 = header.getRptCall1();
m_rptCall2 = header.getRptCall2();
}
void CDCSHandler::writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction)
{
if (m_linkState != DCS_LINKED)
return;
// Is it link in the right direction
if (m_direction != direction)
return;
if (m_destination != handler)
return;
// Already in use?
if (m_dcsId != 0x00)
return;
CHeaderData& header = data.getHeader();
header.setMyCall1(m_myCall1);
header.setMyCall2(m_myCall2);
header.setRptCall1(m_rptCall1);
header.setRptCall2(m_rptCall2);
header.setCQCQCQ();
data.setRptSeq(m_seqNo++);
data.setDestination(m_yourAddress, m_yourPort);
m_handler->writeData(data);
}
bool CDCSHandler::stateChange()
{
bool stateChange = m_stateChange;
m_stateChange = false;
return stateChange;
}
void CDCSHandler::writeStatus(wxFFile& file)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDCSHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
struct tm* tm = ::gmtime(&reflector->m_time);
switch (reflector->m_direction) {
case DIR_OUTGOING:
if (reflector->m_linkState == DCS_LINKED) {
wxString text;
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DCS link - Type: Repeater Rptr: %s Refl: %s Dir: Outgoing\n"),
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
reflector->m_repeater.c_str(), reflector->m_reflector.c_str());
file.Write(text);
}
break;
case DIR_INCOMING:
if (reflector->m_linkState == DCS_LINKED) {
wxString text;
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DCS link - Type: Repeater Rptr: %s Refl: %s Dir: Incoming\n"),
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
reflector->m_repeater.c_str(), reflector->m_reflector.c_str());
file.Write(text);
}
break;
}
}
}
}
unsigned int CDCSHandler::calcBackoff()
{
if (m_tryCount >= 7U) {
m_tryCount++;
return 60U;
}
unsigned int timeout = 1U;
for (unsigned int i = 0U; i < m_tryCount; i++)
timeout *= 2U;
m_tryCount++;
if (timeout > 60U)
return 60U;
else
return timeout;
}

140
Common/DCSHandler.h Normal file
View file

@ -0,0 +1,140 @@
/*
* Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DCSHandler_H
#define DCSHandler_H
#include "DCSProtocolHandlerPool.h"
#include "ReflectorCallback.h"
#include "DStarDefines.h"
#include "HeaderLogger.h"
#include "CallsignList.h"
#include "ConnectData.h"
#include "AMBEData.h"
#include "PollData.h"
#include "Timer.h"
#include "Defs.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
#include <wx/ffile.h>
enum DCS_STATE {
DCS_LINKING,
DCS_LINKED,
DCS_UNLINKING
};
class CDCSHandler {
public:
static void initialise(unsigned int maxReflectors);
static void setDCSProtocolHandlerPool(CDCSProtocolHandlerPool* pool);
static void setDCSProtocolIncoming(CDCSProtocolHandler* handler);
static void setHeaderLogger(CHeaderLogger* logger);
static void setGatewayType(GATEWAY_TYPE type);
static void link(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, const in_addr& address);
static void unlink(IReflectorCallback* handler, const wxString& reflector = wxEmptyString, bool exclude = true);
static void unlink();
static void writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction);
static void writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction);
static void process(CAMBEData& header);
static void process(CPollData& data);
static void process(CConnectData& connect);
static void gatewayUpdate(const wxString& reflector, const wxString& address);
static void clock(unsigned int ms);
static bool stateChange();
static void writeStatus(wxFFile& file);
static void setWhiteList(CCallsignList* list);
static void setBlackList(CCallsignList* list);
static void finalise();
static void getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data);
static wxString getIncoming(const wxString& callsign);
protected:
CDCSHandler(IReflectorCallback* handler, const wxString& reflector, const wxString& repeater, CDCSProtocolHandler* protoHandler, const in_addr& address, unsigned int port, DIRECTION direction);
~CDCSHandler();
void processInt(CAMBEData& data);
bool processInt(CConnectData& connect, CD_TYPE type);
void writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction);
void writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction);
bool clockInt(unsigned int ms);
private:
static unsigned int m_maxReflectors;
static CDCSHandler** m_reflectors;
static CDCSProtocolHandlerPool* m_pool;
static CDCSProtocolHandler* m_incoming;
static bool m_stateChange;
static GATEWAY_TYPE m_gatewayType;
static CHeaderLogger* m_headerLogger;
static CCallsignList* m_whiteList;
static CCallsignList* m_blackList;
wxString m_reflector;
wxString m_repeater;
CDCSProtocolHandler* m_handler;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
DIRECTION m_direction;
DCS_STATE m_linkState;
IReflectorCallback* m_destination;
time_t m_time;
CTimer m_pollTimer;
CTimer m_pollInactivityTimer;
CTimer m_tryTimer;
unsigned int m_tryCount;
unsigned int m_dcsId;
unsigned int m_dcsSeq;
unsigned int m_seqNo;
CTimer m_inactivityTimer;
// Header data
wxString m_yourCall;
wxString m_myCall1;
wxString m_myCall2;
wxString m_rptCall1;
wxString m_rptCall2;
unsigned int calcBackoff();
};
#endif

View file

@ -0,0 +1,196 @@
/*
* Copyright (C) 2012,2013 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 "DCSProtocolHandler.h"
#include "DStarDefines.h"
#include "Utils.h"
// #define DUMP_TX
const unsigned int BUFFER_LENGTH = 2000U;
CDCSProtocolHandler::CDCSProtocolHandler(unsigned int port, const wxString& addr) :
m_socket(addr, port),
m_type(DC_NONE),
m_buffer(NULL),
m_length(0U),
m_yourAddress(),
m_yourPort(0U),
m_myPort(port)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CDCSProtocolHandler::~CDCSProtocolHandler()
{
delete[] m_buffer;
}
bool CDCSProtocolHandler::open()
{
return m_socket.open();
}
unsigned int CDCSProtocolHandler::getPort() const
{
return m_myPort;
}
bool CDCSProtocolHandler::writeData(const CAMBEData& data)
{
unsigned char buffer[100U];
unsigned int length = data.getDCSData(buffer, 100U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Data"), buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
bool CDCSProtocolHandler::writePoll(const CPollData& poll)
{
unsigned char buffer[25U];
unsigned int length = poll.getDCSData(buffer, 25U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Poll"), buffer, length);
#endif
return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort());
}
bool CDCSProtocolHandler::writeConnect(const CConnectData& connect)
{
unsigned char buffer[520U];
unsigned int length = connect.getDCSData(buffer, 520U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Connect"), buffer, length);
#endif
return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort());
}
DCS_TYPE CDCSProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CDCSProtocolHandler::readPackets()
{
m_type = DC_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort);
if (length <= 0)
return false;
m_length = length;
if (m_buffer[0] == '0' && m_buffer[1] == '0' && m_buffer[2] == '0' && m_buffer[3] == '1') {
if (m_length == 100U) {
m_type = DC_DATA;
return false;
}
} else if (m_buffer[0] == 'E' && m_buffer[1] == 'E' && m_buffer[2] == 'E' && m_buffer[3] == 'E') {
// CUtils::dump(wxT("Status data"), m_buffer, m_length);
return true;
} else {
switch (m_length) {
case 17U:
case 22U:
m_type = DC_POLL;
return false;
case 14U:
case 19U:
case 519U:
m_type = DC_CONNECT;
return false;
case 35U:
// CUtils::dump(wxT("Status data"), m_buffer, m_length);
return true;
default:
break;
}
}
// An unknown type
// CUtils::dump(wxT("Unknown packet type from DCS"), m_buffer, m_length);
return true;
}
CAMBEData* CDCSProtocolHandler::readData()
{
if (m_type != DC_DATA)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setDCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete data;
return NULL;
}
return data;
}
CPollData* CDCSProtocolHandler::readPoll()
{
if (m_type != DC_POLL)
return NULL;
CPollData* poll = new CPollData;
bool res = poll->setDCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete poll;
return NULL;
}
return poll;
}
CConnectData* CDCSProtocolHandler::readConnect()
{
if (m_type != DC_CONNECT)
return NULL;
CConnectData* connect = new CConnectData;
bool res = connect->setDCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete connect;
return NULL;
}
return connect;
}
void CDCSProtocolHandler::close()
{
m_socket.close();
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (C) 2012,2013 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 DCSProtocolHandler_H
#define DCSProtocolHandler_H
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "ConnectData.h"
#include "AMBEData.h"
#include "PollData.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
enum DCS_TYPE {
DC_NONE,
DC_DATA,
DC_POLL,
DC_CONNECT
};
class CDCSProtocolHandler {
public:
CDCSProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString);
~CDCSProtocolHandler();
bool open();
unsigned int getPort() const;
bool writeData(const CAMBEData& data);
bool writeConnect(const CConnectData& connect);
bool writePoll(const CPollData& poll);
DCS_TYPE read();
CAMBEData* readData();
CPollData* readPoll();
CConnectData* readConnect();
void close();
private:
CUDPReaderWriter m_socket;
DCS_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
bool readPackets();
};
#endif

View file

@ -0,0 +1,132 @@
/*
* Copyright (C) 2012,2013 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 "DCSProtocolHandlerPool.h"
CDCSProtocolHandlerPool::CDCSProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr) :
m_pool(NULL),
m_n(n),
m_index(0U)
{
wxASSERT(port > 0U);
wxASSERT(n > 0U);
m_pool = new CDCSProtocolHandlerEntry[n];
for (unsigned int i = 0U; i < n; i++) {
m_pool[i].m_handler = new CDCSProtocolHandler(port + i, addr);
m_pool[i].m_port = port + i;
m_pool[i].m_inUse = false;
}
wxLogMessage(wxT("Allocated UDP ports %u-%u to DCS"), port, port + n - 1U);
}
CDCSProtocolHandlerPool::~CDCSProtocolHandlerPool()
{
for (unsigned int i = 0U; i < m_n; i++)
delete m_pool[i].m_handler;
delete[] m_pool;
}
bool CDCSProtocolHandlerPool::open()
{
for (unsigned int i = 0U; i < m_n; i++) {
bool ret = m_pool[i].m_handler->open();
if (!ret)
return false;
}
return true;
}
CDCSProtocolHandler* CDCSProtocolHandlerPool::getHandler(unsigned int port)
{
if (port == 0U) {
for (unsigned int i = 0U; i < m_n; i++) {
if (!m_pool[i].m_inUse) {
m_pool[i].m_inUse = true;
return m_pool[i].m_handler;
}
}
} else {
for (unsigned int i = 0U; i < m_n; i++) {
if (m_pool[i].m_port == port) {
m_pool[i].m_inUse = true;
return m_pool[i].m_handler;
}
}
}
wxLogError(wxT("Cannot find a free DCS port in the pool"));
return NULL;
}
void CDCSProtocolHandlerPool::release(CDCSProtocolHandler* handler)
{
wxASSERT(handler != NULL);
for (unsigned int i = 0U; i < m_n; i++) {
if (m_pool[i].m_handler == handler && m_pool[i].m_inUse) {
m_pool[i].m_inUse = false;
return;
}
}
wxLogError(wxT("Trying to release an unused DCS port"));
}
DCS_TYPE CDCSProtocolHandlerPool::read()
{
while (m_index < m_n) {
if (m_pool[m_index].m_inUse) {
DCS_TYPE type = m_pool[m_index].m_handler->read();
if (type != DC_NONE)
return type;
}
m_index++;
}
m_index = 0U;
return DC_NONE;
}
CAMBEData* CDCSProtocolHandlerPool::readData()
{
return m_pool[m_index].m_handler->readData();
}
CPollData* CDCSProtocolHandlerPool::readPoll()
{
return m_pool[m_index].m_handler->readPoll();
}
CConnectData* CDCSProtocolHandlerPool::readConnect()
{
return m_pool[m_index].m_handler->readConnect();
}
void CDCSProtocolHandlerPool::close()
{
for (unsigned int i = 0U; i < m_n; i++)
m_pool[i].m_handler->close();
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2012,2013 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 DCSProtocolHandlerPool_H
#define DCSProtocolHandlerPool_H
#include <wx/wx.h>
#include "DCSProtocolHandler.h"
class CDCSProtocolHandlerEntry {
public:
CDCSProtocolHandler* m_handler;
unsigned int m_port;
bool m_inUse;
};
class CDCSProtocolHandlerPool {
public:
CDCSProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr = wxEmptyString);
~CDCSProtocolHandlerPool();
bool open();
CDCSProtocolHandler* getHandler(unsigned int port = 0U);
void release(CDCSProtocolHandler* handler);
DCS_TYPE read();
CAMBEData* readData();
CPollData* readPoll();
CConnectData* readConnect();
void close();
private:
CDCSProtocolHandlerEntry* m_pool;
unsigned int m_n;
unsigned int m_index;
};
#endif

313
Common/DDData.cpp Normal file
View file

@ -0,0 +1,313 @@
/*
* Copyright (C) 2011,2013 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "DDData.h"
#include "Utils.h"
unsigned int ETHERNET_ADDRESS_LENGTH = 6U;
unsigned int BUFFER_LENGTH = 2500U;
CDDData::CDDData() :
m_header(),
m_length(0U),
m_frame(NULL)
{
m_frame = new unsigned char[BUFFER_LENGTH];
}
CDDData::CDDData(const CDDData& data) :
m_header(data.m_header),
m_length(data.m_length),
m_frame(NULL)
{
m_frame = new unsigned char[BUFFER_LENGTH];
::memcpy(m_frame, data.m_frame, data.m_length);
}
CDDData::~CDDData()
{
delete[] m_frame;
}
bool CDDData::setIcomRepeaterData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 29U);
bool ret = m_header.setIcomRepeaterData(data, length, true, yourAddress, yourPort);
if (!ret)
return false;
m_length = data[59] * 256U + data[58];
if (m_length > BUFFER_LENGTH)
m_length = BUFFER_LENGTH;
::memcpy(m_frame, data + 60U, m_length);
return true;
}
bool CDDData::setHBRepeaterData(const unsigned char *data, unsigned int length, const in_addr&, unsigned int)
{
wxASSERT(data != NULL);
wxASSERT(length >= 60U);
m_header.setData(data, length, false);
m_length = length - 44U;
if (m_length > BUFFER_LENGTH)
m_length = BUFFER_LENGTH;
::memcpy(m_frame, data + 44U, m_length);
return true;
}
unsigned int CDDData::getIcomRepeaterData(unsigned char *data, unsigned int length)
{
wxASSERT(data != NULL);
wxASSERT(length >= 32U);
// This section is used when it's normal data (i.e. not an ack) for an Icom controller
data[0] = 'D';
data[1] = 'S';
data[2] = 'T';
data[3] = 'R';
data[4] = m_header.getRptSeq() / 256U; // Packet sequence number
data[5] = m_header.getRptSeq() % 256U;
data[6] = 0x73; // Not a response
data[7] = 0x11; // DD Data type
unsigned int dataLength = m_length + 50U;
data[8] = dataLength / 256U; // Length
data[9] = dataLength % 256U;
data[10] = 0x40; // DD Data
data[11] = 0xFF;
data[12] = 0xFF;
data[13] = 0xFF;
data[14] = 0x00; // Dummy ID
data[15] = 0x00;
data[16] = 0xC0; // DD Data
m_header.getData(data + 17U, RADIO_HEADER_LENGTH_BYTES, true);
// Another length field
data[58] = m_length % 256U;
data[59] = m_length / 256U;
// Now copy the payload
::memcpy(data + 60U, m_frame, m_length);
return 60U + m_length;
}
unsigned int CDDData::getHBRepeaterData(unsigned char *data, unsigned int length)
{
wxASSERT(data != NULL);
wxASSERT(length >= 1600U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'R';
data[3] = 'P';
data[4] = 0x24U;
m_header.getData(data + 5U, RADIO_HEADER_LENGTH_BYTES, false);
// Now copy the payload
::memcpy(data + 44U, m_frame, m_length);
return 44U + m_length;
}
unsigned int CDDData::getRptSeq() const
{
return m_header.getRptSeq();
}
void CDDData::setRptSeq(unsigned int seqNo)
{
m_header.setRptSeq(seqNo);
}
unsigned char CDDData::getBand1() const
{
return m_header.getBand1();
}
unsigned char CDDData::getBand2() const
{
return m_header.getBand2();
}
unsigned char CDDData::getBand3() const
{
return m_header.getBand3();
}
void CDDData::setBand1(unsigned char band)
{
m_header.setBand1(band);
}
void CDDData::setBand2(unsigned char band)
{
m_header.setBand2(band);
}
void CDDData::setBand3(unsigned char band)
{
m_header.setBand3(band);
}
unsigned char CDDData::getFlag1() const
{
return m_header.getFlag1();
}
unsigned char CDDData::getFlag2() const
{
return m_header.getFlag2();
}
unsigned char CDDData::getFlag3() const
{
return m_header.getFlag3();
}
void CDDData::setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3)
{
m_header.setFlags(flag1, flag2, flag3);
}
wxString CDDData::getMyCall1() const
{
return m_header.getMyCall1();
}
wxString CDDData::getMyCall2() const
{
return m_header.getMyCall2();
}
wxString CDDData::getYourCall() const
{
return m_header.getYourCall();
}
wxString CDDData::getRptCall1() const
{
return m_header.getRptCall1();
}
wxString CDDData::getRptCall2() const
{
return m_header.getRptCall2();
}
void CDDData::setMyCall1(const wxString& callsign)
{
m_header.setMyCall1(callsign);
}
void CDDData::setMyCall2(const wxString& callsign)
{
m_header.setMyCall2(callsign);
}
void CDDData::setYourCall(const wxString& callsign)
{
m_header.setYourCall(callsign);
}
void CDDData::setRptCall1(const wxString& callsign)
{
m_header.setRptCall1(callsign);
}
void CDDData::setRptCall2(const wxString& callsign)
{
m_header.setRptCall2(callsign);
}
void CDDData::setRepeaters(const wxString& rpt1, const wxString& rpt2)
{
m_header.setRepeaters(rpt1, rpt2);
}
void CDDData::setDestination(const in_addr& address, unsigned int port)
{
m_header.setDestination(address, port);
}
in_addr CDDData::getYourAddress() const
{
return m_header.getYourAddress();
}
unsigned int CDDData::getYourPort() const
{
return m_header.getYourPort();
}
void CDDData::setEthernetFrame(const unsigned char *frame, unsigned int length)
{
wxASSERT(frame != NULL);
wxASSERT(length > 0U);
m_length = length;
if (m_length > BUFFER_LENGTH)
m_length = BUFFER_LENGTH;
::memcpy(m_frame, frame, m_length);
}
unsigned int CDDData::getEthernetFrame(unsigned char *frame, unsigned int length) const
{
wxASSERT(frame != NULL);
wxASSERT(length > 0U);
if (length > m_length)
length = m_length;
::memcpy(frame, m_frame, length);
return length;
}
unsigned char* CDDData::getSourceAddress() const
{
return m_frame + ETHERNET_ADDRESS_LENGTH;
}
unsigned char* CDDData::getDestinationAddress() const
{
return m_frame + 0U;
}

89
Common/DDData.h Normal file
View file

@ -0,0 +1,89 @@
/*
* Copyright (C) 2011,2013 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 DDData_H
#define DDData_H
#include "HeaderData.h"
#include <wx/wx.h>
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
class CDDData {
public:
CDDData();
CDDData(const CDDData& data);
~CDDData();
bool setIcomRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
bool setHBRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
unsigned int getIcomRepeaterData(unsigned char* data, unsigned int length);
unsigned int getHBRepeaterData(unsigned char* data, unsigned int length);
unsigned char getBand1() const;
unsigned char getBand2() const;
unsigned char getBand3() const;
void setBand1(unsigned char band);
void setBand2(unsigned char band);
void setBand3(unsigned char band);
unsigned char getFlag1() const;
unsigned char getFlag2() const;
unsigned char getFlag3() const;
void setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3);
wxString getMyCall1() const;
wxString getMyCall2() const;
wxString getYourCall() const;
wxString getRptCall1() const;
wxString getRptCall2() const;
void setMyCall1(const wxString& callsign);
void setMyCall2(const wxString& callsign);
void setYourCall(const wxString& callsign);
void setRptCall1(const wxString& callsign);
void setRptCall2(const wxString& callsign);
unsigned int getRptSeq() const;
void setRptSeq(unsigned int seqNo);
void setEthernetFrame(const unsigned char* frame, unsigned int length);
unsigned int getEthernetFrame(unsigned char* frame, unsigned int length) const;
unsigned char* getSourceAddress() const;
unsigned char* getDestinationAddress() const;
void setRepeaters(const wxString& rpt1, const wxString& rpt2);
void setDestination(const in_addr& address, unsigned int port);
in_addr getYourAddress() const;
unsigned int getYourPort() const;
private:
CHeaderData m_header;
unsigned int m_length;
unsigned char* m_frame;
};
#endif

394
Common/DDHandler.cpp Normal file
View file

@ -0,0 +1,394 @@
/*
* Copyright (C) 2011,2012,2013 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 "RepeaterHandler.h"
#include "DDHandler.h"
#include "Defs.h"
#include <wx/filename.h>
#if !defined(WIN32)
// XXX Check these
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <linux/if_tun.h>
#include <linux/sockios.h>
#include <netinet/in.h>
#endif
const unsigned int ETHERNET_ADDRESS_LENGTH = 6U;
const unsigned int ETHERNET_MTU = 1500U;
const unsigned int BUFFER_LENGTH = 2000U;
const unsigned int MIN_HEARD_TIME_SECS = 120U;
const int MINIMUM_DD_FRAME_LENGTH = 60;
const unsigned char ETHERNET_BROADCAST_ADDRESS[] = {0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU};
// Multicast address '01:00:5E:00:00:01' - IP: '224.0.0.1' (send to all)
const unsigned char TOALL_MULTICAST_ADDRESS[] = {0x01U, 0x00U, 0x5EU, 0x00U, 0x00U, 0x01U};
// Multicast address '01:00:5E:00:00:23' - IP: '224.0.0.35' (DX-Cluster)
const unsigned char DX_MULTICAST_ADDRESS[] = {0x01U, 0x00U, 0x5EU, 0x00U, 0x00U, 0x23U};
CIRCDDB* CDDHandler::m_irc = NULL;
CHeaderLogger* CDDHandler::m_headerLogger = NULL;
int CDDHandler::m_fd = -1;
unsigned int CDDHandler::m_maxRoutes = 0U;
CEthernet** CDDHandler::m_list = NULL;
unsigned char* CDDHandler::m_buffer = NULL;
bool CDDHandler::m_logEnabled = false;
wxString CDDHandler::m_logDir = wxEmptyString;
wxString CDDHandler::m_name = wxEmptyString;
CTimer CDDHandler::m_timer = CTimer(1000U, MIN_HEARD_TIME_SECS);
CEthernet::CEthernet(const unsigned char* address, const wxString& callsign) :
m_address(NULL),
m_callsign(callsign)
{
wxASSERT(address != NULL);
wxASSERT(!callsign.IsEmpty());
m_address = new unsigned char[ETHERNET_ADDRESS_LENGTH];
::memcpy(m_address, address, ETHERNET_ADDRESS_LENGTH);
}
CEthernet::~CEthernet()
{
delete[] m_address;
}
unsigned char* CEthernet::getAddress() const
{
return m_address;
}
wxString CEthernet::getCallsign() const
{
return m_callsign;
}
void CDDHandler::initialise(unsigned int maxRoutes, const wxString& name)
{
wxASSERT(maxRoutes > 0U);
m_maxRoutes = maxRoutes;
m_name = name;
m_buffer = new unsigned char[BUFFER_LENGTH];
m_list = new CEthernet*[maxRoutes];
for (unsigned int i = 0U; i < maxRoutes; i++)
m_list[i] = NULL;
// Add a dummy entry for broadcasts
m_list[0] = new CEthernet(ETHERNET_BROADCAST_ADDRESS, wxT(" "));
// Add a dummy entry for "to all" multicast
m_list[1] = new CEthernet(TOALL_MULTICAST_ADDRESS, wxT("CQCQCQ "));
// Add a dummy entry for "DX-Cluster" multicast
m_list[2] = new CEthernet(DX_MULTICAST_ADDRESS, wxT("CQCQCQ "));
#if !defined(WIN32)
m_fd = ::open("/dev/net/tun", O_RDWR);
if (m_fd < 0) {
wxLogError(wxT("Cannot open /dev/net/tun"));
return;
}
struct ifreq ifr1;
::memset(&ifr1, 0x00, sizeof(struct ifreq));
ifr1.ifr_flags = IFF_TAP | IFF_NO_PI;
::strcpy(ifr1.ifr_name, "tap%d");
if (::ioctl(m_fd, TUNSETIFF, (void *)&ifr1) < 0) {
wxLogError(wxT("TUNSETIFF ioctl failed, closing the tap device"));
::close(m_fd);
m_fd = -1;
return;
}
wxString device = wxString(ifr1.ifr_name, wxConvLocal);
wxLogMessage(wxT("DD mode Tap interface created on %s"), device.c_str());
int fd = ::socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
wxLogError(wxT("Unable to open the config socket, closing the tap device"));
::close(m_fd);
m_fd = -1;
return;
}
struct ifreq ifr2;
::memset(&ifr2, 0x00, sizeof(struct ifreq));
::strcpy(ifr2.ifr_name, ifr1.ifr_name);
ifr2.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST;
if (::ioctl(fd, SIOCSIFFLAGS, (void *)&ifr2) < 0) {
wxLogError(wxT("SIOCSIFFLAGS ioctl failed, closing the tap device"));
::close(m_fd);
m_fd = -1;
return;
}
::close(fd);
#endif
}
void CDDHandler::setLogging(bool enabled, const wxString& dir)
{
m_logDir = dir;
m_logEnabled = enabled;
}
void CDDHandler::setHeaderLogger(CHeaderLogger* logger)
{
m_headerLogger = logger;
}
void CDDHandler::setIRC(CIRCDDB* irc)
{
wxASSERT(irc != NULL);
m_irc = irc;
}
void CDDHandler::process(CDDData& data)
{
// If we're not initialised, return immediately
if (m_maxRoutes == 0U)
return;
unsigned char flag1 = data.getFlag1();
unsigned char flag2 = data.getFlag2();
unsigned char flag3 = data.getFlag3();
wxString myCall1 = data.getMyCall1();
wxString myCall2 = data.getMyCall2();
wxString yourCall = data.getYourCall();
wxString rptCall1 = data.getRptCall1();
wxString rptCall2 = data.getRptCall2();
if (!m_timer.isRunning() || m_timer.hasExpired()) {
// Write to Header.log if it's enabled
if (m_headerLogger != NULL)
m_headerLogger->write(wxT("Repeater"), data);
if (m_irc != NULL) {
m_irc->sendHeardWithTXMsg(myCall1, myCall2, yourCall, rptCall1, rptCall2, flag1, flag2, flag3, wxEmptyString, wxT("Digital Data "));
m_irc->sendHeardWithTXStats(myCall1, myCall2, yourCall, rptCall1, rptCall2, flag1, flag2, flag3, 1, 0, -1);
}
m_timer.start();
}
// Can we continue?
if (m_fd < 0)
return;
unsigned char* address = data.getSourceAddress();
bool found = false;
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
if (m_list[i] != NULL) {
unsigned char* addr = m_list[i]->getAddress();
if (::memcmp(addr, address, ETHERNET_ADDRESS_LENGTH) == 0) {
found = true;
break;
}
}
}
if (!found) {
wxLogMessage(wxT("Adding DD user %s with ethernet address %02X:%02X:%02X:%02X:%02X:%02X"), myCall1.c_str(),
address[0], address[1], address[2], address[3], address[4], address[5]);
CEthernet* ethernet = new CEthernet(address, myCall1);
found = false;
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
if (m_list[i] == NULL) {
m_list[i] = ethernet;
found = true;
if (m_logEnabled)
writeStatus(*ethernet);
break;
}
}
if (!found) {
wxLogError(wxT("No space to add new DD ethernet address"));
delete ethernet;
return;
}
}
#if !defined(WIN32)
unsigned int length = data.getEthernetFrame(m_buffer, BUFFER_LENGTH);
ssize_t len = ::write(m_fd, (char*)m_buffer, length);
if (len != ssize_t(length))
wxLogError(wxT("Error returned from write()"));
#endif
}
CDDData* CDDHandler::read()
{
// If we're not initialised, return immediately
if (m_maxRoutes == 0U)
return NULL;
#if defined(WIN32)
return NULL;
#else
// Check that the read() won't block
fd_set readFds;
FD_ZERO(&readFds);
#if defined(__WINDOWS__)
FD_SET((unsigned int)m_fd, &readFds);
#else
FD_SET(m_fd, &readFds);
#endif
// Return immediately
timeval tv;
tv.tv_sec = 0L;
tv.tv_usec = 0L;
int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv);
if (ret < 0) {
wxLogError(wxT("Error returned from select()"));
return NULL;
}
#if defined(__WINDOWS__)
if (!FD_ISSET((unsigned int)m_fd, &readFds))
return NULL;
#else
if (!FD_ISSET(m_fd, &readFds))
return NULL;
#endif
// Ensure that the minimum length is padded with 0x00s
::memset(m_buffer, 0x00U, MINIMUM_DD_FRAME_LENGTH);
ssize_t len = ::read(m_fd, (char*)m_buffer, BUFFER_LENGTH);
if (len <= 0) {
wxLogError(wxT("Error returned from read()"));
return NULL;
}
// There seems to be a minimum size with DD mode, so pad with zeroes if it's not reached
if (len < MINIMUM_DD_FRAME_LENGTH)
len = MINIMUM_DD_FRAME_LENGTH;
// Point to the destination ethernet address
unsigned char* address = m_buffer + 0U;
// Do destination address to callsign lookup
CEthernet* ethernet = NULL;
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
if (m_list[i] != NULL) {
unsigned char* addr = m_list[i]->getAddress();
if (::memcmp(addr, address, ETHERNET_ADDRESS_LENGTH) == 0) {
ethernet = m_list[i];
break;
}
}
}
if (ethernet == NULL) {
wxLogWarning(wxT("Cannot find the ethernet address of %02X:%02X:%02X:%02X:%02X:%02X in the ethernet list"), address[0], address[1], address[2], address[3], address[4], address[5]);
return NULL;
}
CRepeaterHandler* handler = CRepeaterHandler::findDDRepeater();
if (handler == NULL) {
wxLogWarning(wxT("Incoming DD data to unknown repeater"));
return NULL;
}
// wxLogMessage(wxT("Mapping ethernet address %02X:%02X:%02X:%02X:%02X:%02X to user %s"),
// address[0], address[1], address[2], address[3], address[4], address[5],
// ethernet->getCallsign().c_str());
CDDData* data = new CDDData;
data->setEthernetFrame(m_buffer, len);
data->setYourCall(ethernet->getCallsign());
handler->process(*data);
return data;
#endif
}
void CDDHandler::clock(unsigned int ms)
{
m_timer.clock(ms);
}
void CDDHandler::finalise()
{
#if !defined(WIN32)
if (m_fd >= 0) {
::close(m_fd);
m_fd = -1;
}
#endif
delete[] m_buffer;
for (unsigned int i = 0U; i < m_maxRoutes; i++)
delete m_list[i];
delete[] m_list;
}
void CDDHandler::writeStatus(const CEthernet& ethernet)
{
wxString fullName = DDMODE_BASE_NAME;
if (!m_name.IsEmpty()) {
fullName.Append(wxT("_"));
fullName.Append(m_name);
}
wxFileName fileName(m_logDir, fullName, wxT("log"));
wxFFile file;
bool ret = file.Open(fileName.GetFullPath(), wxT("at"));
if (!ret) {
wxLogError(wxT("Unable to open %s for writing"), fileName.GetFullPath().c_str());
return;
}
wxString callsign = ethernet.getCallsign();
unsigned char* address = ethernet.getAddress();
time_t timeNow = ::time(NULL);
struct tm* tm = ::gmtime(&timeNow);
wxString text;
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: %02X:%02X:%02X:%02X:%02X:%02X %s\n"),
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
address[0], address[1], address[2], address[3], address[4], address[5], callsign.c_str());
file.Write(text);
file.Close();
}

73
Common/DDHandler.h Normal file
View file

@ -0,0 +1,73 @@
/*
* Copyright (C) 2011,2012 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 DDHandler_H
#define DDHandler_H
#include "HeaderLogger.h"
#include "DDData.h"
#include "IRCDDB.h"
#include "Timer.h"
#include <wx/wx.h>
class CEthernet {
public:
CEthernet(const unsigned char* address, const wxString& callsign);
~CEthernet();
unsigned char* getAddress() const;
wxString getCallsign() const;
private:
unsigned char* m_address;
wxString m_callsign;
};
class CDDHandler {
public:
static void initialise(unsigned int maxRoutes, const wxString& name);
static void setLogging(bool enabled, const wxString& dir);
static void setHeaderLogger(CHeaderLogger* logger);
static void setIRC(CIRCDDB* irc);
static void process(CDDData& data);
static CDDData* read();
static void clock(unsigned int ms);
static void finalise();
private:
static CIRCDDB* m_irc;
static CHeaderLogger* m_headerLogger;
static int m_fd;
static unsigned int m_maxRoutes;
static CEthernet** m_list;
static unsigned char* m_buffer;
static bool m_logEnabled;
static wxString m_logDir;
static wxString m_name;
static CTimer m_timer;
static void writeStatus(const CEthernet& ethernet);
};
#endif

1004
Common/DExtraHandler.cpp Normal file

File diff suppressed because it is too large Load diff

138
Common/DExtraHandler.h Normal file
View file

@ -0,0 +1,138 @@
/*
* Copyright (C) 2010-2013,2015 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DExtraHander_H
#define DExtraHander_H
#include "DExtraProtocolHandlerPool.h"
#include "ReflectorCallback.h"
#include "DStarDefines.h"
#include "HeaderLogger.h"
#include "CallsignList.h"
#include "ConnectData.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "PollData.h"
#include "Timer.h"
#include "Defs.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
#include <wx/ffile.h>
enum DEXTRA_STATE {
DEXTRA_LINKING,
DEXTRA_LINKED,
DEXTRA_UNLINKING
};
class CDExtraHandler {
public:
static void initialise(unsigned int maxReflectors);
static void setCallsign(const wxString& callsign);
static void setDExtraProtocolHandlerPool(CDExtraProtocolHandlerPool* pool);
static void setDExtraProtocolIncoming(CDExtraProtocolHandler* handler);
static void setHeaderLogger(CHeaderLogger* logger);
static void setMaxDongles(unsigned int maxDongles);
static void link(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, const in_addr& address);
static void unlink(IReflectorCallback* handler, const wxString& reflector = wxEmptyString, bool exclude = true);
static void unlink();
static void writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction);
static void writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction);
static void process(CHeaderData& header);
static void process(CAMBEData& data);
static void process(const CPollData& poll);
static void process(CConnectData& connect);
static void gatewayUpdate(const wxString& reflector, const wxString& address);
static void clock(unsigned int ms);
static bool stateChange();
static void writeStatus(wxFFile& file);
static void setWhiteList(CCallsignList* list);
static void setBlackList(CCallsignList* list);
static void finalise();
static void getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data);
static wxString getIncoming(const wxString& callsign);
static wxString getDongles();
protected:
CDExtraHandler(IReflectorCallback* handler, const wxString& reflector, const wxString& repeater, CDExtraProtocolHandler* protoHandler, const in_addr& address, unsigned int port, DIRECTION direction);
CDExtraHandler(CDExtraProtocolHandler* protoHandler, const wxString& reflector, const in_addr& address, unsigned int port, DIRECTION direction);
~CDExtraHandler();
void processInt(CHeaderData& header);
void processInt(CAMBEData& data);
bool processInt(CConnectData& connect, CD_TYPE type);
void writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction);
void writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction);
bool clockInt(unsigned int ms);
private:
static unsigned int m_maxReflectors;
static unsigned int m_maxDongles;
static CDExtraHandler** m_reflectors;
static wxString m_callsign;
static CDExtraProtocolHandlerPool* m_pool;
static CDExtraProtocolHandler* m_incoming;
static bool m_stateChange;
static CHeaderLogger* m_headerLogger;
static CCallsignList* m_whiteList;
static CCallsignList* m_blackList;
wxString m_reflector;
wxString m_repeater;
CDExtraProtocolHandler* m_handler;
in_addr m_yourAddress;
unsigned int m_yourPort;
DIRECTION m_direction;
DEXTRA_STATE m_linkState;
IReflectorCallback* m_destination;
time_t m_time;
CTimer m_pollTimer;
CTimer m_pollInactivityTimer;
CTimer m_tryTimer;
unsigned int m_tryCount;
unsigned int m_dExtraId;
unsigned int m_dExtraSeq;
CTimer m_inactivityTimer;
CHeaderData* m_header;
unsigned int calcBackoff();
};
#endif

View file

@ -0,0 +1,228 @@
/*
* Copyright (C) 2010-2013 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 "DExtraProtocolHandler.h"
#include "DStarDefines.h"
#include "Utils.h"
// #define DUMP_TX
const unsigned int BUFFER_LENGTH = 1000U;
CDExtraProtocolHandler::CDExtraProtocolHandler(unsigned int port, const wxString& addr) :
m_socket(addr, port),
m_type(DE_NONE),
m_buffer(NULL),
m_length(0U),
m_yourAddress(),
m_yourPort(0U),
m_myPort(port)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CDExtraProtocolHandler::~CDExtraProtocolHandler()
{
delete[] m_buffer;
}
bool CDExtraProtocolHandler::open()
{
return m_socket.open();
}
unsigned int CDExtraProtocolHandler::getPort() const
{
return m_myPort;
}
bool CDExtraProtocolHandler::writeHeader(const CHeaderData& header)
{
unsigned char buffer[60U];
unsigned int length = header.getDExtraData(buffer, 60U, true);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Header"), buffer, length);
#endif
for (unsigned int i = 0U; i < 5U; i++) {
bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort());
if (!res)
return false;
}
return true;
}
bool CDExtraProtocolHandler::writeAMBE(const CAMBEData& data)
{
unsigned char buffer[40U];
unsigned int length = data.getDExtraData(buffer, 40U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Data"), buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
bool CDExtraProtocolHandler::writePoll(const CPollData& poll)
{
unsigned char buffer[20U];
unsigned int length = poll.getDExtraData(buffer, 20U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Poll"), buffer, length);
#endif
return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort());
}
bool CDExtraProtocolHandler::writeConnect(const CConnectData& connect)
{
unsigned char buffer[20U];
unsigned int length = connect.getDExtraData(buffer, 20U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Connect"), buffer, length);
#endif
for (unsigned int i = 0U; i < 2U; i++) {
bool res = m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort());
if (!res)
return false;
}
return true;
}
DEXTRA_TYPE CDExtraProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CDExtraProtocolHandler::readPackets()
{
m_type = DE_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort);
if (length <= 0)
return false;
m_length = length;
if (m_buffer[0] != 'D' || m_buffer[1] != 'S' || m_buffer[2] != 'V' || m_buffer[3] != 'T') {
switch (m_length) {
case 9U:
m_type = DE_POLL;
return false;
case 11U:
case 14U:
m_type = DE_CONNECT;
return false;
default:
return true;
}
} else {
// Header or data packet type?
if (m_buffer[14] == 0x80)
m_type = DE_HEADER;
else
m_type = DE_AMBE;
return false;
}
}
CHeaderData* CDExtraProtocolHandler::readHeader()
{
if (m_type != DE_HEADER)
return NULL;
CHeaderData* header = new CHeaderData;
// DExtra checksums are unreliable
bool res = header->setDExtraData(m_buffer, m_length, false, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete header;
return NULL;
}
return header;
}
CAMBEData* CDExtraProtocolHandler::readAMBE()
{
if (m_type != DE_AMBE)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setDExtraData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete data;
return NULL;
}
return data;
}
CPollData* CDExtraProtocolHandler::readPoll()
{
if (m_type != DE_POLL)
return NULL;
CPollData* poll = new CPollData;
bool res = poll->setDExtraData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete poll;
return NULL;
}
return poll;
}
CConnectData* CDExtraProtocolHandler::readConnect()
{
if (m_type != DE_CONNECT)
return NULL;
CConnectData* connect = new CConnectData;
bool res = connect->setDExtraData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete connect;
return NULL;
}
return connect;
}
void CDExtraProtocolHandler::close()
{
m_socket.close();
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (C) 2010-2013 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 DExtraProtocolHandler_H
#define DExtraProtocolHandler_H
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "ConnectData.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "PollData.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
enum DEXTRA_TYPE {
DE_NONE,
DE_HEADER,
DE_AMBE,
DE_POLL,
DE_CONNECT
};
class CDExtraProtocolHandler {
public:
CDExtraProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString);
~CDExtraProtocolHandler();
bool open();
unsigned int getPort() const;
bool writeHeader(const CHeaderData& header);
bool writeAMBE(const CAMBEData& data);
bool writeConnect(const CConnectData& connect);
bool writePoll(const CPollData& poll);
DEXTRA_TYPE read();
CHeaderData* readHeader();
CAMBEData* readAMBE();
CPollData* readPoll();
CConnectData* readConnect();
void close();
private:
CUDPReaderWriter m_socket;
DEXTRA_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
bool readPackets();
};
#endif

View file

@ -0,0 +1,137 @@
/*
* Copyright (C) 2012,2013 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 "DExtraProtocolHandlerPool.h"
CDExtraProtocolHandlerPool::CDExtraProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr) :
m_pool(NULL),
m_n(n),
m_index(0U)
{
wxASSERT(port > 0U);
wxASSERT(n > 0U);
m_pool = new CDExtraProtocolHandlerEntry[n];
for (unsigned int i = 0U; i < n; i++) {
m_pool[i].m_handler = new CDExtraProtocolHandler(port + i, addr);
m_pool[i].m_port = port + i;
m_pool[i].m_inUse = false;
}
wxLogMessage(wxT("Allocated UDP ports %u-%u to DExtra"), port, port + n - 1U);
}
CDExtraProtocolHandlerPool::~CDExtraProtocolHandlerPool()
{
for (unsigned int i = 0U; i < m_n; i++)
delete m_pool[i].m_handler;
delete[] m_pool;
}
bool CDExtraProtocolHandlerPool::open()
{
for (unsigned int i = 0U; i < m_n; i++) {
bool ret = m_pool[i].m_handler->open();
if (!ret)
return false;
}
return true;
}
CDExtraProtocolHandler* CDExtraProtocolHandlerPool::getHandler(unsigned int port)
{
if (port == 0U) {
for (unsigned int i = 0U; i < m_n; i++) {
if (!m_pool[i].m_inUse) {
m_pool[i].m_inUse = true;
return m_pool[i].m_handler;
}
}
} else {
for (unsigned int i = 0U; i < m_n; i++) {
if (m_pool[i].m_port == port) {
m_pool[i].m_inUse = true;
return m_pool[i].m_handler;
}
}
}
wxLogError(wxT("Cannot find a free DExtra port in the pool"));
return NULL;
}
void CDExtraProtocolHandlerPool::release(CDExtraProtocolHandler* handler)
{
wxASSERT(handler != NULL);
for (unsigned int i = 0U; i < m_n; i++) {
if (m_pool[i].m_handler == handler && m_pool[i].m_inUse) {
m_pool[i].m_inUse = false;
return;
}
}
wxLogError(wxT("Trying to release an unused DExtra port"));
}
DEXTRA_TYPE CDExtraProtocolHandlerPool::read()
{
while (m_index < m_n) {
if (m_pool[m_index].m_inUse) {
DEXTRA_TYPE type = m_pool[m_index].m_handler->read();
if (type != DE_NONE)
return type;
}
m_index++;
}
m_index = 0U;
return DE_NONE;
}
CHeaderData* CDExtraProtocolHandlerPool::readHeader()
{
return m_pool[m_index].m_handler->readHeader();
}
CAMBEData* CDExtraProtocolHandlerPool::readAMBE()
{
return m_pool[m_index].m_handler->readAMBE();
}
CPollData* CDExtraProtocolHandlerPool::readPoll()
{
return m_pool[m_index].m_handler->readPoll();
}
CConnectData* CDExtraProtocolHandlerPool::readConnect()
{
return m_pool[m_index].m_handler->readConnect();
}
void CDExtraProtocolHandlerPool::close()
{
for (unsigned int i = 0U; i < m_n; i++)
m_pool[i].m_handler->close();
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2012,2013 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 DExtraProtocolHandlerPool_H
#define DExtraProtocolHandlerPool_H
#include <wx/wx.h>
#include "DExtraProtocolHandler.h"
class CDExtraProtocolHandlerEntry {
public:
CDExtraProtocolHandler* m_handler;
unsigned int m_port;
bool m_inUse;
};
class CDExtraProtocolHandlerPool {
public:
CDExtraProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr = wxEmptyString);
~CDExtraProtocolHandlerPool();
bool open();
CDExtraProtocolHandler* getHandler(unsigned int port = 0U);
void release(CDExtraProtocolHandler* handler);
DEXTRA_TYPE read();
CHeaderData* readHeader();
CAMBEData* readAMBE();
CPollData* readPoll();
CConnectData* readConnect();
void close();
private:
CDExtraProtocolHandlerEntry* m_pool;
unsigned int m_n;
unsigned int m_index;
};
#endif

View file

@ -0,0 +1,286 @@
/*
* Copyright (C) 2010-2015 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DPlusAuthenticator.h"
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "Utils.h"
#include "Defs.h"
const wxString OPENDSTAR_HOSTNAME = wxT("opendstar.org");
const unsigned int OPENDSTAR_PORT = 20001U;
const wxString DUTCHSTAR_HOSTNAME = wxT("dpns.dutch-star.eu");
const unsigned int DUTCHSTAR_PORT = 20001U;
const unsigned int TCP_TIMEOUT = 10U;
CDPlusAuthenticator::CDPlusAuthenticator(const wxString& loginCallsign, const wxString& gatewayCallsign, const wxString& address, CCacheManager* cache) :
wxThread(wxTHREAD_JOINABLE),
m_loginCallsign(loginCallsign),
m_gatewayCallsign(gatewayCallsign),
m_address(address),
m_cache(cache),
m_timer(1U, 6U * 3600U), // 6 hours
m_pollTimer(1U, 60U), // 1 minute
m_killed(false)
{
wxASSERT(!loginCallsign.IsEmpty());
wxASSERT(!gatewayCallsign.IsEmpty());
wxASSERT(cache != NULL);
m_gatewayCallsign.Truncate(LONG_CALLSIGN_LENGTH - 1U);
m_loginCallsign.Trim();
m_gatewayCallsign.Trim();
if (m_loginCallsign.IsEmpty())
m_loginCallsign = m_gatewayCallsign;
}
CDPlusAuthenticator::~CDPlusAuthenticator()
{
}
void CDPlusAuthenticator::start()
{
Create();
Run();
}
void* CDPlusAuthenticator::Entry()
{
wxLogMessage(wxT("Starting the D-Plus Authenticator thread"));
authenticate(m_loginCallsign, OPENDSTAR_HOSTNAME, OPENDSTAR_PORT, '2', true);
authenticate(m_gatewayCallsign, DUTCHSTAR_HOSTNAME, DUTCHSTAR_PORT, 'K', false);
m_timer.start();
m_pollTimer.start();
try {
while (!m_killed) {
if (m_pollTimer.hasExpired()) {
poll(m_gatewayCallsign, DUTCHSTAR_HOSTNAME, DUTCHSTAR_PORT, 'K');
m_pollTimer.start();
}
if (m_timer.hasExpired()) {
authenticate(m_loginCallsign, OPENDSTAR_HOSTNAME, OPENDSTAR_PORT, '2', true);
authenticate(m_gatewayCallsign, DUTCHSTAR_HOSTNAME, DUTCHSTAR_PORT, 'K', false);
m_timer.start();
}
Sleep(1000UL);
m_timer.clock();
m_pollTimer.clock();
}
}
catch (std::exception& e) {
wxString message(e.what(), wxConvLocal);
wxLogError(wxT("Exception raised in the D-Plus Authenticator thread - \"%s\""), message.c_str());
}
catch (...) {
wxLogError(wxT("Unknown exception raised in the D-Plus Authenticator thread"));
}
wxLogMessage(wxT("Stopping the D-Plus Authenticator thread"));
return NULL;
}
void CDPlusAuthenticator::stop()
{
m_killed = true;
Wait();
}
bool CDPlusAuthenticator::authenticate(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id, bool writeToCache)
{
CTCPReaderWriterClient socket(hostname, port, m_address);
bool ret = socket.open();
if (!ret)
return false;
unsigned char* buffer = new unsigned char[4096U];
::memset(buffer, ' ', 56U);
buffer[0U] = 0x38U;
buffer[1U] = 0xC0U;
buffer[2U] = 0x01U;
buffer[3U] = 0x00U;
for (unsigned int i = 0U; i < callsign.Len(); i++)
buffer[i + 4U] = callsign.GetChar(i);
buffer[12U] = 'D';
buffer[13U] = 'V';
buffer[14U] = '0';
buffer[15U] = '1';
buffer[16U] = '9';
buffer[17U] = '9';
buffer[18U] = '9';
buffer[19U] = '9';
buffer[28U] = 'W';
buffer[29U] = '7';
buffer[30U] = 'I';
buffer[31U] = 'B';
buffer[32U] = id;
buffer[40U] = 'D';
buffer[41U] = 'H';
buffer[42U] = 'S';
buffer[43U] = '0';
buffer[44U] = '2';
buffer[45U] = '5';
buffer[46U] = '7';
ret = socket.write(buffer, 56U);
if (!ret) {
socket.close();
delete[] buffer;
return false;
}
ret = read(socket, buffer + 0U, 2U);
while (ret) {
unsigned int len = (buffer[1U] & 0x0FU) * 256U + buffer[0U];
// Ensure that we get exactly len - 2U bytes from the TCP stream
ret = read(socket, buffer + 2U, len - 2U);
if (!ret) {
wxLogError(wxT("Short read from %s:%u"), hostname.c_str(), port);
break;
}
if ((buffer[1U] & 0xC0U) != 0xC0U || buffer[2U] != 0x01U) {
wxLogError(wxT("Invalid packet received from %s:%u"), hostname.c_str(), port);
CUtils::dump(wxT("Details:"), buffer, len);
break;
}
for (unsigned int i = 8U; (i + 25U) < len; i += 26U) {
wxString address = wxString((char*)(buffer + i + 0U), wxConvLocal, 16U);
wxString name = wxString((char*)(buffer + i + 16U), wxConvLocal, LONG_CALLSIGN_LENGTH);
address.Trim();
name.Trim();
// Get the active flag
bool active = (buffer[i + 25U] & 0x80U) == 0x80U;
// An empty name or IP address or an inactive gateway/reflector is not written out
if (address.Len() > 0U && name.Len() > 0U && !name.Left(3U).IsSameAs(wxT("XRF")) && active && writeToCache){
if (name.Left(3U).IsSameAs(wxT("REF")))
wxLogMessage(wxT("D-Plus: %s\t%s"), name.c_str(), address.c_str());
name.Append(wxT(" "));
name.Truncate(LONG_CALLSIGN_LENGTH - 1U);
name.Append(wxT("G"));
m_cache->updateGateway(name, address, DP_DPLUS, false, true);
}
}
ret = read(socket, buffer + 0U, 2U);
}
wxLogMessage(wxT("Registered with %s using callsign %s"), hostname.c_str(), callsign.c_str());
socket.close();
delete[] buffer;
return true;
}
bool CDPlusAuthenticator::poll(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id)
{
CUDPReaderWriter socket(m_address, 0U);
bool ret = socket.open();
if (!ret)
return false;
unsigned char buffer[56U];
::memset(buffer, ' ', 56U);
buffer[0U] = 0x38U;
buffer[1U] = 0x20U;
buffer[2U] = 0x01U;
buffer[3U] = 0x01U;
for (unsigned int i = 0U; i < callsign.Len(); i++)
buffer[i + 4U] = callsign.GetChar(i);
buffer[12U] = 'D';
buffer[13U] = 'V';
buffer[14U] = '0';
buffer[15U] = '1';
buffer[16U] = '9';
buffer[17U] = '9';
buffer[18U] = '9';
buffer[19U] = '9';
for (unsigned int i = 0U; i < callsign.Len(); i++)
buffer[i + 20U] = callsign.GetChar(i);
buffer[28U] = 'W';
buffer[29U] = '7';
buffer[30U] = 'I';
buffer[31U] = 'B';
buffer[32U] = id;
buffer[40U] = 'D';
buffer[41U] = 'H';
buffer[42U] = 'S';
buffer[43U] = '0';
buffer[44U] = '2';
buffer[45U] = '5';
buffer[46U] = '7';
in_addr address = socket.lookup(hostname);
if (address.s_addr == INADDR_NONE) {
socket.close();
return false;
}
ret = socket.write(buffer, 56U, address, port);
socket.close();
return ret;
}
bool CDPlusAuthenticator::read(CTCPReaderWriterClient &socket, unsigned char *buffer, unsigned int len) const
{
unsigned int offset = 0U;
do {
int n = socket.read(buffer + offset, len - offset, TCP_TIMEOUT);
if (n < 0)
return false;
offset += n;
} while ((len - offset) > 0U);
return true;
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2010-2013 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 DPlusAuthenticator_H
#define DPlusAuthenticator_H
#include "TCPReaderWriterClient.h"
#include "CacheManager.h"
#include "Timer.h"
#include <wx/wx.h>
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
class CDPlusAuthenticator : public wxThread {
public:
CDPlusAuthenticator(const wxString& loginCallsign, const wxString& gatewayCallsign, const wxString& address, CCacheManager* cache);
virtual ~CDPlusAuthenticator();
virtual void start();
virtual void* Entry();
virtual void stop();
private:
wxString m_loginCallsign;
wxString m_gatewayCallsign;
wxString m_address;
CCacheManager* m_cache;
CTimer m_timer;
CTimer m_pollTimer;
bool m_killed;
bool poll(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id);
bool authenticate(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id, bool writeToCache);
bool read(CTCPReaderWriterClient& socket, unsigned char* buffer, unsigned int len) const;
};
#endif

953
Common/DPlusHandler.cpp Normal file
View file

@ -0,0 +1,953 @@
/*
* Copyright (C) 2010-2015 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "RepeaterHandler.h"
#include "DPlusHandler.h"
#include "DStarDefines.h"
#include "Utils.h"
#include <wx/filename.h>
unsigned int CDPlusHandler::m_maxReflectors = 0U;
unsigned int CDPlusHandler::m_maxDongles = 0U;
CDPlusHandler** CDPlusHandler::m_reflectors = NULL;
wxString CDPlusHandler::m_gatewayCallsign;
wxString CDPlusHandler::m_dplusLogin;
CDPlusProtocolHandlerPool* CDPlusHandler::m_pool = NULL;
CDPlusProtocolHandler* CDPlusHandler::m_incoming = NULL;
bool CDPlusHandler::m_stateChange = false;
CDPlusAuthenticator* CDPlusHandler::m_authenticator = NULL;
CHeaderLogger* CDPlusHandler::m_headerLogger = NULL;
CCallsignList* CDPlusHandler::m_whiteList = NULL;
CCallsignList* CDPlusHandler::m_blackList = NULL;
CDPlusHandler::CDPlusHandler(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port) :
m_repeater(repeater.Clone()),
m_callsign(m_dplusLogin.Clone()),
m_reflector(reflector.Clone()),
m_handler(protoHandler),
m_yourAddress(address),
m_yourPort(port),
m_myPort(0U),
m_direction(DIR_OUTGOING),
m_linkState(DPLUS_LINKING),
m_destination(handler),
m_time(),
m_pollTimer(1000U, 1U), // 1s
m_pollInactivityTimer(1000U, 30U),
m_tryTimer(1000U, 1U),
m_tryCount(0U),
m_dPlusId(0x00U),
m_dPlusSeq(0x00U),
m_inactivityTimer(1000U, NETWORK_TIMEOUT),
m_header(NULL)
{
wxASSERT(protoHandler != NULL);
wxASSERT(handler != NULL);
wxASSERT(port > 0U);
m_myPort = protoHandler->getPort();
m_pollInactivityTimer.start();
m_tryTimer.start();
m_time = ::time(NULL);
m_callsign.resize(LONG_CALLSIGN_LENGTH, ' ');
wxChar band = m_repeater.GetChar(LONG_CALLSIGN_LENGTH - 1U);
m_callsign.SetChar(LONG_CALLSIGN_LENGTH - 1U, band);
}
CDPlusHandler::CDPlusHandler(CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port) :
m_repeater(),
m_callsign(),
m_reflector(),
m_handler(protoHandler),
m_yourAddress(address),
m_yourPort(port),
m_myPort(0U),
m_direction(DIR_INCOMING),
m_linkState(DPLUS_LINKING),
m_destination(NULL),
m_time(),
m_pollTimer(1000U, 1U), // 1s
m_pollInactivityTimer(1000U, 10U), // 10s
m_tryTimer(1000U),
m_tryCount(0U),
m_dPlusId(0x00U),
m_dPlusSeq(0x00U),
m_inactivityTimer(1000U, NETWORK_TIMEOUT),
m_header(NULL)
{
wxASSERT(protoHandler != NULL);
wxASSERT(port > 0U);
m_myPort = protoHandler->getPort();
m_pollTimer.start();
m_pollInactivityTimer.start();
m_time = ::time(NULL);
}
CDPlusHandler::~CDPlusHandler()
{
if (m_direction == DIR_OUTGOING)
m_pool->release(m_handler);
delete m_header;
}
void CDPlusHandler::initialise(unsigned int maxReflectors)
{
wxASSERT(maxReflectors > 0U);
m_maxReflectors = maxReflectors;
m_reflectors = new CDPlusHandler*[m_maxReflectors];
for (unsigned int i = 0U; i < m_maxReflectors; i++)
m_reflectors[i] = NULL;
}
void CDPlusHandler::startAuthenticator(const wxString& address, CCacheManager* cache)
{
wxASSERT(cache != NULL);
m_authenticator = new CDPlusAuthenticator(m_dplusLogin, m_gatewayCallsign, address, cache);
m_authenticator->start();
}
void CDPlusHandler::setCallsign(const wxString& callsign)
{
m_gatewayCallsign = callsign;
}
void CDPlusHandler::setDPlusProtocolHandlerPool(CDPlusProtocolHandlerPool* pool)
{
wxASSERT(pool != NULL);
m_pool = pool;
}
void CDPlusHandler::setDPlusProtocolIncoming(CDPlusProtocolHandler* handler)
{
wxASSERT(handler != NULL);
m_incoming = handler;
}
void CDPlusHandler::setDPlusLogin(const wxString& dplusLogin)
{
m_dplusLogin = dplusLogin;
}
void CDPlusHandler::setHeaderLogger(CHeaderLogger* logger)
{
m_headerLogger = logger;
}
void CDPlusHandler::setMaxDongles(unsigned int maxDongles)
{
m_maxDongles = maxDongles;
}
void CDPlusHandler::setWhiteList(CCallsignList* list)
{
wxASSERT(list != NULL);
m_whiteList = list;
}
void CDPlusHandler::setBlackList(CCallsignList* list)
{
wxASSERT(list != NULL);
m_blackList = list;
}
void CDPlusHandler::getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data)
{
wxASSERT(handler != NULL);
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (reflector->m_destination == handler && reflector->m_linkState != DPLUS_UNLINKING)
data.addLink(reflector->m_reflector, PROTO_DPLUS, reflector->m_linkState == DPLUS_LINKED, reflector->m_direction, true);
}
}
}
wxString CDPlusHandler::getDongles()
{
wxString dongles;
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL && reflector->m_direction == DIR_INCOMING) {
dongles.Append(wxT("P:"));
dongles.Append(reflector->m_reflector);
dongles.Append(wxT(" "));
}
}
return dongles;
}
void CDPlusHandler::process(CHeaderData& header)
{
in_addr yourAddress = header.getYourAddress();
unsigned int yourPort = header.getYourPort();
unsigned int myPort = header.getMyPort();
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (reflector->m_yourAddress.s_addr == yourAddress.s_addr &&
reflector->m_yourPort == yourPort &&
reflector->m_myPort == myPort) {
reflector->processInt(header);
return;
}
}
}
}
void CDPlusHandler::process(CAMBEData& data)
{
in_addr yourAddress = data.getYourAddress();
unsigned int yourPort = data.getYourPort();
unsigned int myPort = data.getMyPort();
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (reflector->m_yourAddress.s_addr == yourAddress.s_addr &&
reflector->m_yourPort == yourPort &&
reflector->m_myPort == myPort) {
reflector->processInt(data);
return;
}
}
}
}
void CDPlusHandler::process(const CPollData& poll)
{
in_addr yourAddress = poll.getYourAddress();
unsigned int yourPort = poll.getYourPort();
unsigned int myPort = poll.getMyPort();
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (reflector->m_yourAddress.s_addr == yourAddress.s_addr &&
reflector->m_yourPort == yourPort &&
reflector->m_myPort == myPort) {
reflector->m_pollInactivityTimer.start();
return;
}
}
}
// If we cannot find an existing link, we ignore the poll
wxLogMessage(wxT("Incoming poll from unknown D-Plus dongle"));
}
void CDPlusHandler::process(CConnectData& connect)
{
CD_TYPE type = connect.getType();
in_addr yourAddress = connect.getYourAddress();
unsigned int yourPort = connect.getYourPort();
unsigned int myPort = connect.getMyPort();
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (reflector->m_yourAddress.s_addr == yourAddress.s_addr &&
reflector->m_yourPort == yourPort &&
reflector->m_myPort == myPort) {
bool res = m_reflectors[i]->processInt(connect, type);
if (res) {
delete m_reflectors[i];
m_reflectors[i] = NULL;
}
}
}
}
// Check that it isn't a duplicate
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (reflector->m_yourAddress.s_addr == yourAddress.s_addr &&
reflector->m_yourPort == yourPort &&
reflector->m_myPort == myPort)
return;
}
}
if (type == CT_UNLINK)
return;
if (type != CT_LINK1) {
wxLogMessage(wxT("Incoming D-Plus message from unknown source"));
return;
}
// Check to see if we are allowed to accept it
unsigned int count = 0U;
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL &&
m_reflectors[i]->m_direction == DIR_INCOMING)
count++;
}
if (count >= m_maxDongles)
return;
CDPlusHandler* dplus = new CDPlusHandler(m_incoming, yourAddress, yourPort);
bool found = false;
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] == NULL) {
m_reflectors[i] = dplus;
found = true;
break;
}
}
if (found) {
CConnectData connect(CT_LINK1, yourAddress, yourPort);
m_incoming->writeConnect(connect);
} else {
wxLogError(wxT("No space to add new D-Plus dongle, ignoring"));
delete dplus;
}
}
void CDPlusHandler::link(IReflectorCallback* handler, const wxString& repeater, const wxString &gateway, const in_addr& address)
{
CDPlusProtocolHandler* protoHandler = m_pool->getHandler();
if (protoHandler == NULL)
return;
CDPlusHandler* dplus = new CDPlusHandler(handler, repeater, gateway, protoHandler, address, DPLUS_PORT);
bool found = false;
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] == NULL) {
m_reflectors[i] = dplus;
found = true;
break;
}
}
if (found) {
CConnectData connect(CT_LINK1, address, DPLUS_PORT);
protoHandler->writeConnect(connect);
m_stateChange = true;
} else {
wxLogError(wxT("No space to add new D-Plus reflector, ignoring"));
delete dplus;
}
}
void CDPlusHandler::relink(IReflectorCallback* handler, const wxString &gateway)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL && m_reflectors[i]->m_direction == DIR_OUTGOING) {
if (m_reflectors[i]->m_destination == handler) {
m_reflectors[i]->m_reflector = gateway;
m_reflectors[i]->m_dPlusId = 0x00U;
m_reflectors[i]->m_dPlusSeq = 0x00U;
m_stateChange = true;
return;
}
}
}
}
void CDPlusHandler::unlink(IReflectorCallback* handler, const wxString& callsign, bool exclude)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
bool found = false;
if (exclude) {
if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination == handler && !reflector->m_reflector.IsSameAs(callsign)) {
wxLogMessage(wxT("Removing outgoing D-Plus link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str());
if (reflector->m_linkState == DPLUS_LINKING || reflector->m_linkState == DPLUS_LINKED) {
CConnectData connect(CT_UNLINK, reflector->m_yourAddress, DPLUS_PORT);
reflector->m_handler->writeConnect(connect);
reflector->m_handler->writeConnect(connect);
reflector->m_linkState = DPLUS_UNLINKING;
reflector->m_tryTimer.start(1U);
reflector->m_pollTimer.stop();
reflector->m_pollInactivityTimer.stop();
reflector->m_tryCount = 0U;
}
found = true;
}
} else {
if (reflector->m_destination == handler && reflector->m_reflector.IsSameAs(callsign)) {
wxLogMessage(wxT("Removing D-Plus link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str());
if (reflector->m_linkState == DPLUS_LINKING || reflector->m_linkState == DPLUS_LINKED) {
CConnectData connect(CT_UNLINK, reflector->m_yourAddress, DPLUS_PORT);
reflector->m_handler->writeConnect(connect);
reflector->m_handler->writeConnect(connect);
reflector->m_linkState = DPLUS_UNLINKING;
reflector->m_tryTimer.start(1U);
reflector->m_pollTimer.stop();
reflector->m_pollInactivityTimer.stop();
reflector->m_tryCount = 0U;
}
found = true;
}
}
// If an active link with incoming traffic, send an EOT to the repeater
if (found) {
if (reflector->m_dPlusId != 0x00U) {
unsigned int seq = reflector->m_dPlusSeq + 1U;
if (seq == 21U)
seq = 0U;
CAMBEData data;
data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES);
data.setSeq(seq);
data.setEnd(true);
data.setId(reflector->m_dPlusId);
reflector->m_destination->process(data, reflector->m_direction, AS_DPLUS);
}
m_stateChange = true;
}
}
}
}
void CDPlusHandler::unlink()
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (!reflector->m_reflector.IsEmpty())
wxLogMessage(wxT("Unlinking from D-Plus reflector or dongle %s"), reflector->m_reflector.c_str());
CConnectData connect(CT_UNLINK, reflector->m_yourAddress, reflector->m_yourPort);
reflector->m_handler->writeConnect(connect);
reflector->m_handler->writeConnect(connect);
reflector->m_tryTimer.start(1U);
reflector->m_pollTimer.stop();
reflector->m_pollInactivityTimer.stop();
reflector->m_tryCount = 0U;
}
}
}
void CDPlusHandler::writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL)
m_reflectors[i]->writeHeaderInt(handler, header, direction);
}
}
void CDPlusHandler::writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL)
m_reflectors[i]->writeAMBEInt(handler, data, direction);
}
}
void CDPlusHandler::gatewayUpdate(const wxString& gateway, const wxString& address)
{
wxString gatewayBase = gateway;
gatewayBase.Truncate(LONG_CALLSIGN_LENGTH - 1U);
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
if (!reflector->m_reflector.IsEmpty() && reflector->m_reflector.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(gatewayBase)) {
if (!address.IsEmpty()) {
// A new address, change the value
wxLogMessage(wxT("Changing IP address of D-Plus gateway or reflector %s to %s"), gatewayBase.c_str(), address.c_str());
reflector->m_yourAddress.s_addr = ::inet_addr(address.mb_str());
} else {
wxLogMessage(wxT("IP address for D-Plus gateway or reflector %s has been removed"), gatewayBase.c_str());
// No address, this probably shouldn't happen....
if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination != NULL)
reflector->m_destination->linkFailed(DP_DPLUS, reflector->m_reflector, false);
m_stateChange = true;
delete m_reflectors[i];
m_reflectors[i] = NULL;
}
}
}
}
}
void CDPlusHandler::clock(unsigned int ms)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
if (m_reflectors[i] != NULL) {
bool ret = m_reflectors[i]->clockInt(ms);
if (ret) {
delete m_reflectors[i];
m_reflectors[i] = NULL;
}
}
}
}
void CDPlusHandler::finalise()
{
if (m_authenticator != NULL)
m_authenticator->stop();
for (unsigned int i = 0U; i < m_maxReflectors; i++)
delete m_reflectors[i];
delete[] m_reflectors;
}
void CDPlusHandler::processInt(CHeaderData& header)
{
wxString my = header.getMyCall1();
wxString rpt1 = header.getRptCall1();
wxString rpt2 = header.getRptCall2();
unsigned int id = header.getId();
if (m_whiteList != NULL) {
bool res = m_whiteList->isInList(my);
if (!res) {
wxLogMessage(wxT("%s rejected from D-Plus as not found in the white list"), my.c_str());
m_dPlusId = 0x00U;
return;
}
}
if (m_blackList != NULL) {
bool res = m_blackList->isInList(my);
if (res) {
wxLogMessage(wxT("%s rejected from D-Plus as found in the black list"), my.c_str());
m_dPlusId = 0x00U;
return;
}
}
if (m_linkState != DPLUS_LINKED)
return;
switch (m_direction) {
case DIR_OUTGOING:
if (m_reflector.IsSameAs(rpt1) || m_reflector.IsSameAs(rpt2)) {
// If we're already processing, ignore the new header
if (m_dPlusId != 0x00U)
return;
// Write to Header.log if it's enabled
if (m_headerLogger != NULL)
m_headerLogger->write(wxT("DPlus"), header);
m_dPlusId = id;
m_dPlusSeq = 0x00U;
m_inactivityTimer.start();
m_pollInactivityTimer.start();
delete m_header;
m_header = new CHeaderData(header);
m_header->setCQCQCQ();
m_header->setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(*m_header, m_direction, AS_DPLUS);
}
break;
case DIR_INCOMING: {
m_destination = CRepeaterHandler::findDVRepeater(rpt1);
if (m_destination == NULL) {
m_destination = CRepeaterHandler::findDVRepeater(rpt2);
if (m_destination == NULL)
return;
}
if (m_dPlusId != 0x00U)
return;
// Write to Header.log if it's enabled
if (m_headerLogger != NULL)
m_headerLogger->write(wxT("DPlus"), header);
m_dPlusId = id;
m_dPlusSeq = 0x00U;
m_inactivityTimer.start();
m_pollInactivityTimer.start();
delete m_header;
m_header = new CHeaderData(header);
m_header->setCQCQCQ();
m_header->setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(*m_header, m_direction, AS_DPLUS);
}
break;
}
}
void CDPlusHandler::processInt(CAMBEData& data)
{
unsigned int id = data.getId();
if (m_dPlusId != id)
return;
m_dPlusSeq = data.getSeq();
// Send the header every 21 frames, if we have it
if (m_dPlusSeq == 0U && m_header != NULL)
m_destination->process(*m_header, m_direction, AS_DUP);
m_inactivityTimer.start();
m_pollInactivityTimer.start();
m_destination->process(data, m_direction, AS_DPLUS);
if (data.isEnd()) {
m_dPlusId = 0x00U;
m_dPlusSeq = 0x00U;
delete m_header;
m_header = NULL;
m_inactivityTimer.stop();
}
}
bool CDPlusHandler::processInt(CConnectData& connect, CD_TYPE type)
{
switch (m_direction) {
case DIR_OUTGOING:
switch (type) {
case CT_ACK:
if (m_linkState == DPLUS_LINKING) {
wxLogMessage(wxT("D-Plus ACK message received from %s"), m_reflector.c_str());
m_destination->linkUp(DP_DPLUS, m_reflector);
m_stateChange = true;
m_linkState = DPLUS_LINKED;
m_tryTimer.stop();
m_pollTimer.start();
m_pollInactivityTimer.start();
}
return false;
case CT_NAK:
if (m_linkState == DPLUS_LINKING) {
wxLogMessage(wxT("D-Plus NAK message received from %s"), m_reflector.c_str());
m_destination->linkRefused(DP_DPLUS, m_reflector);
CConnectData reply(CT_UNLINK, connect.getYourAddress(), connect.getYourPort());
m_handler->writeConnect(reply);
m_tryTimer.stop();
}
return true;
case CT_UNLINK:
if (m_linkState == DPLUS_UNLINKING) {
wxLogMessage(wxT("D-Plus disconnect acknowledgement received from %s"), m_reflector.c_str());
m_destination->linkFailed(DP_DPLUS, m_reflector, false);
m_stateChange = true;
m_tryTimer.stop();
}
return true;
case CT_LINK1: {
CConnectData reply(m_dplusLogin, CT_LINK2, connect.getYourAddress(), connect.getYourPort());
m_handler->writeConnect(reply);
m_tryTimer.stop();
}
return false;
default:
return false;
}
break;
case DIR_INCOMING:
switch (type) {
case CT_LINK2: {
m_reflector = connect.getRepeater();
wxLogMessage(wxT("D-Plus dongle link to %s has started"), m_reflector.c_str());
CConnectData reply(CT_ACK, m_yourAddress, m_yourPort);
m_handler->writeConnect(reply);
m_linkState = DPLUS_LINKED;
m_stateChange = true;
}
return false;
case CT_UNLINK:
if (m_linkState == DPLUS_LINKED) {
wxLogMessage(wxT("D-Plus dongle link to %s has ended (unlinked)"), m_reflector.c_str());
m_stateChange = true;
m_handler->writeConnect(connect);
}
return true;
default:
return false;
}
break;
}
return false;
}
bool CDPlusHandler::clockInt(unsigned int ms)
{
m_tryTimer.clock(ms);
m_pollTimer.clock(ms);
m_inactivityTimer.clock(ms);
m_pollInactivityTimer.clock(ms);
if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) {
m_pollInactivityTimer.start();
delete m_header;
m_header = NULL;
m_stateChange = true;
m_dPlusId = 0x00U;
m_dPlusSeq = 0x00U;
if (!m_reflector.IsEmpty()) {
switch (m_linkState) {
case DPLUS_LINKING:
wxLogMessage(wxT("D-Plus link to %s has failed to connect"), m_reflector.c_str());
break;
case DPLUS_LINKED:
wxLogMessage(wxT("D-Plus link to %s has failed (poll inactivity)"), m_reflector.c_str());
break;
case DPLUS_UNLINKING:
wxLogMessage(wxT("D-Plus link to %s has failed to disconnect cleanly"), m_reflector.c_str());
break;
default:
break;
}
}
if (m_direction == DIR_OUTGOING) {
bool reconnect = m_destination->linkFailed(DP_DPLUS, m_reflector, true);
if (reconnect) {
CConnectData connect(CT_LINK1, m_yourAddress, DPLUS_PORT);
m_handler->writeConnect(connect);
m_linkState = DPLUS_LINKING;
m_tryTimer.start(1U);
m_tryCount = 0U;
return false;
}
}
return true;
}
if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) {
CPollData poll(m_yourAddress, m_yourPort);
m_handler->writePoll(poll);
m_pollTimer.start();
}
if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) {
switch (m_linkState) {
case DPLUS_LINKING: {
CConnectData connect(CT_LINK1, m_yourAddress, DPLUS_PORT);
m_handler->writeConnect(connect);
}
break;
case DPLUS_UNLINKING: {
CConnectData connect(CT_UNLINK, m_yourAddress, m_yourPort);
m_handler->writeConnect(connect);
m_handler->writeConnect(connect);
}
break;
default:
break;
}
unsigned int timeout = calcBackoff();
m_tryTimer.start(timeout);
}
if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) {
delete m_header;
m_header = NULL;
m_dPlusId = 0x00U;
m_dPlusSeq = 0x00U;
m_inactivityTimer.stop();
}
return false;
}
void CDPlusHandler::writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction)
{
wxASSERT(handler != NULL);
if (m_linkState != DPLUS_LINKED)
return;
if (direction != m_direction)
return;
// Already is use?
if (m_dPlusId != 0x00U)
return;
switch (m_direction) {
case DIR_OUTGOING:
if (m_destination == handler) {
header.setRepeaters(m_callsign, m_reflector);
header.setDestination(m_yourAddress, m_yourPort);
m_handler->writeHeader(header);
}
break;
case DIR_INCOMING:
header.setDestination(m_yourAddress, m_yourPort);
m_handler->writeHeader(header);
break;
}
}
void CDPlusHandler::writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction)
{
if (m_linkState != DPLUS_LINKED)
return;
if (direction != m_direction)
return;
// Already in use?
if (m_dPlusId != 0x00U)
return;
switch (m_direction) {
case DIR_OUTGOING:
if (m_destination == handler) {
data.setDestination(m_yourAddress, m_yourPort);
m_handler->writeAMBE(data);
}
break;
case DIR_INCOMING:
data.setDestination(m_yourAddress, m_yourPort);
m_handler->writeAMBE(data);
break;
}
}
bool CDPlusHandler::stateChange()
{
bool stateChange = m_stateChange;
m_stateChange = false;
return stateChange;
}
void CDPlusHandler::writeStatus(wxFFile& file)
{
for (unsigned int i = 0U; i < m_maxReflectors; i++) {
CDPlusHandler* reflector = m_reflectors[i];
if (reflector != NULL) {
wxString text;
struct tm* tm = ::gmtime(&reflector->m_time);
if (reflector->m_linkState == DPLUS_LINKED) {
switch (reflector->m_direction) {
case DIR_OUTGOING:
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DPlus link - Type: Dongle Rptr: %s Refl: %s Dir: Outgoing\n"),
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
reflector->m_repeater.c_str(), reflector->m_reflector.c_str());
break;
case DIR_INCOMING:
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DPlus link - Type: Dongle User: %s Dir: Incoming\n"),
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
reflector->m_reflector.c_str());
break;
}
file.Write(text);
}
}
}
}
unsigned int CDPlusHandler::calcBackoff()
{
if (m_tryCount >= 7U) {
m_tryCount++;
return 60U;
}
unsigned int timeout = 1U;
for (unsigned int i = 0U; i < m_tryCount; i++)
timeout *= 2U;
m_tryCount++;
if (timeout > 60U)
return 60U;
else
return timeout;
}

147
Common/DPlusHandler.h Normal file
View file

@ -0,0 +1,147 @@
/*
* Copyright (C) 2010-2013,2015 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DPlusHandler_H
#define DPlusHandler_H
#include "DPlusProtocolHandlerPool.h"
#include "DPlusAuthenticator.h"
#include "ReflectorCallback.h"
#include "CacheManager.h"
#include "DStarDefines.h"
#include "HeaderLogger.h"
#include "CallsignList.h"
#include "ConnectData.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "PollData.h"
#include "Timer.h"
#include "Defs.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
#include <wx/ffile.h>
enum DPLUS_STATE {
DPLUS_LINKING,
DPLUS_LINKED,
DPLUS_UNLINKING
};
class CDPlusHandler {
public:
static void initialise(unsigned int maxReflectors);
static void setCallsign(const wxString& callsign);
static void setDPlusProtocolHandlerPool(CDPlusProtocolHandlerPool* pool);
static void setDPlusProtocolIncoming(CDPlusProtocolHandler* handler);
static void setDPlusLogin(const wxString& dplusLogin);
static void setHeaderLogger(CHeaderLogger* logger);
static void setMaxDongles(unsigned int maxDongles);
static void startAuthenticator(const wxString& address, CCacheManager* cache);
static void link(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, const in_addr& address);
static void relink(IReflectorCallback* handler, const wxString& reflector);
static void unlink(IReflectorCallback* handler, const wxString& reflector = wxEmptyString, bool exclude = true);
static void unlink();
static void writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction);
static void writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction);
static void process(CHeaderData& header);
static void process(CAMBEData& header);
static void process(const CPollData& header);
static void process(CConnectData& process);
static void gatewayUpdate(const wxString& gateway, const wxString& address);
static void clock(unsigned int ms);
static bool stateChange();
static void writeStatus(wxFFile& file);
static void setWhiteList(CCallsignList* list);
static void setBlackList(CCallsignList* list);
static void finalise();
static void getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data);
static wxString getDongles();
protected:
CDPlusHandler(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port);
CDPlusHandler(CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port);
~CDPlusHandler();
void processInt(CHeaderData& header);
void processInt(CAMBEData& data);
bool processInt(CConnectData& connect, CD_TYPE type);
void writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction);
void writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction);
bool clockInt(unsigned int ms);
private:
static unsigned int m_maxReflectors;
static unsigned int m_maxDongles;
static CDPlusHandler** m_reflectors;
static wxString m_gatewayCallsign;
static wxString m_dplusLogin;
static CDPlusProtocolHandlerPool* m_pool;
static CDPlusProtocolHandler* m_incoming;
static bool m_stateChange;
static CHeaderLogger* m_headerLogger;
static CDPlusAuthenticator* m_authenticator;
static CCallsignList* m_whiteList;
static CCallsignList* m_blackList;
wxString m_repeater;
wxString m_callsign;
wxString m_reflector;
CDPlusProtocolHandler* m_handler;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
DIRECTION m_direction;
DPLUS_STATE m_linkState;
IReflectorCallback* m_destination;
time_t m_time;
CTimer m_pollTimer;
CTimer m_pollInactivityTimer;
CTimer m_tryTimer;
unsigned int m_tryCount;
unsigned int m_dPlusId;
unsigned int m_dPlusSeq;
CTimer m_inactivityTimer;
CHeaderData* m_header;
unsigned int calcBackoff();
};
#endif

View file

@ -0,0 +1,233 @@
/*
* Copyright (C) 2010-2013 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 "DPlusProtocolHandler.h"
#include "DStarDefines.h"
#include "Utils.h"
// #define DUMP_TX
const unsigned int BUFFER_LENGTH = 1000U;
CDPlusProtocolHandler::CDPlusProtocolHandler(unsigned int port, const wxString& addr) :
m_socket(addr, port),
m_type(DP_NONE),
m_buffer(NULL),
m_length(0U),
m_yourAddress(),
m_yourPort(0U),
m_myPort(port)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CDPlusProtocolHandler::~CDPlusProtocolHandler()
{
delete[] m_buffer;
}
bool CDPlusProtocolHandler::open()
{
return m_socket.open();
}
unsigned int CDPlusProtocolHandler::getPort() const
{
return m_myPort;
}
bool CDPlusProtocolHandler::writeHeader(const CHeaderData& header)
{
unsigned char buffer[60U];
unsigned int length = header.getDPlusData(buffer, 60U, true);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Header"), buffer, length);
#endif
for (unsigned int i = 0U; i < 5U; i++) {
bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort());
if (!res)
return false;
}
return true;
}
bool CDPlusProtocolHandler::writeAMBE(const CAMBEData& data)
{
unsigned char buffer[40U];
unsigned int length = data.getDPlusData(buffer, 40U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Data"), buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
bool CDPlusProtocolHandler::writePoll(const CPollData& poll)
{
unsigned char buffer[10U];
unsigned int length = poll.getDPlusData(buffer, 10U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Poll"), buffer, length);
#endif
return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort());
}
bool CDPlusProtocolHandler::writeConnect(const CConnectData& connect)
{
unsigned char buffer[40U];
unsigned int length = connect.getDPlusData(buffer, 40U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Connect"), buffer, length);
#endif
return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort());
}
DPLUS_TYPE CDPlusProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CDPlusProtocolHandler::readPackets()
{
m_type = DP_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort);
if (length <= 0)
return false;
m_length = length;
if (m_buffer[2] != 'D' || m_buffer[3] != 'S' || m_buffer[4] != 'V' || m_buffer[5] != 'T') {
switch (m_length) {
case 3U:
m_type = DP_POLL;
return false;
case 5U:
case 8U:
case 28U:
m_type = DP_CONNECT;
return false;
default:
// An unknown type
// CUtils::dump(wxT("Unknown packet type from D-Plus"), m_buffer, m_length);
return true;
}
} else {
// Header or data packet type?
if (m_buffer[0] == 0x3A && m_buffer[1] == 0x80) {
m_type = DP_HEADER;
return false;
} else if (m_buffer[0] == 0x1D && m_buffer[1] == 0x80) {
m_type = DP_AMBE;
return false;
} else if (m_buffer[0] == 0x20 && m_buffer[1] == 0x80) {
m_type = DP_AMBE;
return false;
} else {
// An unknown type
CUtils::dump(wxT("Unknown packet type from D-Plus"), m_buffer, m_length);
return true;
}
}
}
CHeaderData* CDPlusProtocolHandler::readHeader()
{
if (m_type != DP_HEADER)
return NULL;
CHeaderData* header = new CHeaderData;
// DPlus checksums are unreliable
bool res = header->setDPlusData(m_buffer, m_length, false, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete header;
return NULL;
}
return header;
}
CAMBEData* CDPlusProtocolHandler::readAMBE()
{
if (m_type != DP_AMBE)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setDPlusData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete data;
return NULL;
}
return data;
}
CPollData* CDPlusProtocolHandler::readPoll()
{
if (m_type != DP_POLL)
return NULL;
CPollData* poll = new CPollData;
bool res = poll->setDPlusData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete poll;
return NULL;
}
return poll;
}
CConnectData* CDPlusProtocolHandler::readConnect()
{
if (m_type != DP_CONNECT)
return NULL;
CConnectData* connect = new CConnectData;
bool res = connect->setDPlusData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete connect;
return NULL;
}
return connect;
}
void CDPlusProtocolHandler::close()
{
m_socket.close();
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (C) 2010,2011,2013 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 DPlusProtocolHandler_H
#define DPlusProtocolHandler_H
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "ConnectData.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "PollData.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
enum DPLUS_TYPE {
DP_NONE,
DP_HEADER,
DP_AMBE,
DP_POLL,
DP_CONNECT
};
class CDPlusProtocolHandler {
public:
CDPlusProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString);
~CDPlusProtocolHandler();
bool open();
unsigned int getPort() const;
bool writeHeader(const CHeaderData& header);
bool writeAMBE(const CAMBEData& data);
bool writeConnect(const CConnectData& connect);
bool writePoll(const CPollData& poll);
DPLUS_TYPE read();
CHeaderData* readHeader();
CAMBEData* readAMBE();
CPollData* readPoll();
CConnectData* readConnect();
void close();
private:
CUDPReaderWriter m_socket;
DPLUS_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
bool readPackets();
};
#endif

View file

@ -0,0 +1,137 @@
/*
* Copyright (C) 2012,2013 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 "DPlusProtocolHandlerPool.h"
CDPlusProtocolHandlerPool::CDPlusProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr) :
m_pool(NULL),
m_n(n),
m_index(0U)
{
wxASSERT(port > 0U);
wxASSERT(n > 0U);
m_pool = new CDPlusProtocolHandlerEntry[n];
for (unsigned int i = 0U; i < n; i++) {
m_pool[i].m_handler = new CDPlusProtocolHandler(port + i, addr);
m_pool[i].m_port = port + i;
m_pool[i].m_inUse = false;
}
wxLogMessage(wxT("Allocated UDP ports %u-%u to D-Plus"), port, port + n - 1U);
}
CDPlusProtocolHandlerPool::~CDPlusProtocolHandlerPool()
{
for (unsigned int i = 0U; i < m_n; i++)
delete m_pool[i].m_handler;
delete[] m_pool;
}
bool CDPlusProtocolHandlerPool::open()
{
for (unsigned int i = 0U; i < m_n; i++) {
bool ret = m_pool[i].m_handler->open();
if (!ret)
return false;
}
return true;
}
CDPlusProtocolHandler* CDPlusProtocolHandlerPool::getHandler(unsigned int port)
{
if (port == 0U) {
for (unsigned int i = 0U; i < m_n; i++) {
if (!m_pool[i].m_inUse) {
m_pool[i].m_inUse = true;
return m_pool[i].m_handler;
}
}
} else {
for (unsigned int i = 0U; i < m_n; i++) {
if (m_pool[i].m_port == port) {
m_pool[i].m_inUse = true;
return m_pool[i].m_handler;
}
}
}
wxLogError(wxT("Cannot find a free D-Plus port in the pool"));
return NULL;
}
void CDPlusProtocolHandlerPool::release(CDPlusProtocolHandler* handler)
{
wxASSERT(handler != NULL);
for (unsigned int i = 0U; i < m_n; i++) {
if (m_pool[i].m_handler == handler && m_pool[i].m_inUse) {
m_pool[i].m_inUse = false;
return;
}
}
wxLogError(wxT("Trying to release an unused D-Plus port"));
}
DPLUS_TYPE CDPlusProtocolHandlerPool::read()
{
while (m_index < m_n) {
if (m_pool[m_index].m_inUse) {
DPLUS_TYPE type = m_pool[m_index].m_handler->read();
if (type != DP_NONE)
return type;
}
m_index++;
}
m_index = 0U;
return DP_NONE;
}
CHeaderData* CDPlusProtocolHandlerPool::readHeader()
{
return m_pool[m_index].m_handler->readHeader();
}
CAMBEData* CDPlusProtocolHandlerPool::readAMBE()
{
return m_pool[m_index].m_handler->readAMBE();
}
CPollData* CDPlusProtocolHandlerPool::readPoll()
{
return m_pool[m_index].m_handler->readPoll();
}
CConnectData* CDPlusProtocolHandlerPool::readConnect()
{
return m_pool[m_index].m_handler->readConnect();
}
void CDPlusProtocolHandlerPool::close()
{
for (unsigned int i = 0U; i < m_n; i++)
m_pool[i].m_handler->close();
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2012,2013 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 DPlusProtocolHandlerPool_H
#define DPlusProtocolHandlerPool_H
#include <wx/wx.h>
#include "DPlusProtocolHandler.h"
class CDPlusProtocolHandlerEntry {
public:
CDPlusProtocolHandler* m_handler;
unsigned int m_port;
bool m_inUse;
};
class CDPlusProtocolHandlerPool {
public:
CDPlusProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr = wxEmptyString);
~CDPlusProtocolHandlerPool();
bool open();
CDPlusProtocolHandler* getHandler(unsigned int port = 0U);
void release(CDPlusProtocolHandler* handler);
DPLUS_TYPE read();
CHeaderData* readHeader();
CAMBEData* readAMBE();
CPollData* readPoll();
CConnectData* readConnect();
void close();
private:
CDPlusProtocolHandlerEntry* m_pool;
unsigned int m_n;
unsigned int m_index;
};
#endif

375
Common/DRATSServer.cpp Normal file
View file

@ -0,0 +1,375 @@
/*
* Copyright (C) 2011-2015 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "DRATSServer.h"
#include "Utils.h"
// #define LOOPBACK
const unsigned int BUFFER_LENGTH = 30000U;
CDRATSServer::CDRATSServer(const wxString& address, unsigned int port, const wxString& callsign, IRepeaterCallback* handler) :
wxThread(wxTHREAD_JOINABLE),
m_address(address),
m_port(port),
m_callsign(callsign),
m_handler(handler),
m_socket(NULL),
m_stopped(false),
m_readState(SS_FIRST),
m_readBuffer(NULL),
m_readLength(0U),
m_readPos(0U),
m_readEnd(false),
m_writeText(NULL),
m_writeState(SS_FIRST),
m_writeBuffer(NULL),
m_writeLength(0U)
{
wxASSERT(handler != NULL);
wxASSERT(port > 0U);
m_readBuffer = new unsigned char[BUFFER_LENGTH];
m_writeBuffer = new unsigned char[BUFFER_LENGTH];
m_writeText = new unsigned char[6U];
}
CDRATSServer::~CDRATSServer()
{
delete[] m_readBuffer;
delete[] m_writeBuffer;
delete[] m_writeText;
}
bool CDRATSServer::open()
{
m_socket = new CTCPReaderWriterServer(m_address, m_port);
bool ret = m_socket->start();
if (!ret) {
delete m_socket;
m_socket = NULL;
return false;
}
Create();
Run();
return true;
}
void CDRATSServer::writeHeader(const CHeaderData&)
{
m_writeState = SS_FIRST;
if (m_writeLength > 0U && m_socket != NULL) {
CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength);
m_socket->write(m_writeBuffer, m_writeLength);
}
m_writeLength = 0U;
}
void CDRATSServer::writeData(const CAMBEData& data)
{
// Sync data isn't sent on
if (data.isSync()) {
m_writeState = SS_FIRST;
return;
}
if (data.isEnd()) {
if (m_writeLength > 0U && m_socket != NULL) {
CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength);
m_socket->write(m_writeBuffer, m_writeLength);
}
m_writeLength = 0U;
return;
}
unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES];
unsigned int length = data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES);
if (length != DV_FRAME_LENGTH_BYTES)
return;
unsigned char byte1 = buffer[VOICE_FRAME_LENGTH_BYTES + 0U] ^ SCRAMBLER_BYTE1;
unsigned char byte2 = buffer[VOICE_FRAME_LENGTH_BYTES + 1U] ^ SCRAMBLER_BYTE2;
unsigned char byte3 = buffer[VOICE_FRAME_LENGTH_BYTES + 2U] ^ SCRAMBLER_BYTE3;
switch (m_writeState) {
case SS_FIRST:
m_writeText[0U] = byte1;
m_writeText[1U] = byte2;
m_writeText[2U] = byte3;
m_writeState = SS_SECOND;
return;
case SS_SECOND:
m_writeText[3U] = byte1;
m_writeText[4U] = byte2;
m_writeText[5U] = byte3;
m_writeState = SS_FIRST;
break;
}
if ((m_writeText[0U] & SLOW_DATA_TYPE_MASK) != SLOW_DATA_TYPE_GPS)
return;
length = m_writeText[0U] & 0x07; // Maximum value of 5
if (length > 5U)
length = 5U;
for (unsigned int i = 0U; i < length; i++) {
m_writeBuffer[m_writeLength++] = m_writeText[i + 1U];
// Check for [EOB] in the buffer to signal the end of the D-RATS data.
// To allow strstr() to run correctly
m_writeBuffer[m_writeLength] = 0x00U;
if (::strstr((char*)m_writeBuffer, "[EOB]") != NULL) {
if (m_socket != NULL) {
CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength);
m_socket->write(m_writeBuffer, m_writeLength);
}
m_writeLength = 0U;
}
}
}
void CDRATSServer::writeEnd()
{
if (m_writeLength > 0U && m_socket != NULL) {
CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength);
m_socket->write(m_writeBuffer, m_writeLength);
}
m_writeLength = 0U;
}
void* CDRATSServer::Entry()
{
wxLogMessage(wxT("Starting the D-RATS Server thread for %s"), m_callsign.c_str());
bool sending = false;
unsigned int id = 0U;
unsigned char seqNo = 0U;
unsigned int sent = 0U;
wxStopWatch time;
try {
while (!m_stopped) {
serviceSocket();
if (m_readEnd && !sending) {
id = CHeaderData::createId();
// Write header
CHeaderData header;
header.setMyCall1(m_callsign);
header.setMyCall2(wxT("DATA"));
header.setYourCall(wxT("CQCQCQ "));
header.setId(id);
#if defined(LOOPBACK)
writeHeader(header);
#else
m_handler->process(header, DIR_INCOMING, AS_DRATS);
#endif
m_readState = SS_FIRST;
m_readPos = 0U;
sending = true;
seqNo = 0U;
sent = 0U;
time.Start();
}
if (m_readEnd && sending) {
unsigned int needed = time.Time() / DSTAR_FRAME_TIME_MS;
while (sent < needed && sending) {
// Write AMBE data
CAMBEData data;
data.setId(id);
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
// Insert sync bytes when the sequence number is zero, slow data otherwise
if (seqNo == 0U) {
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
m_readState = SS_FIRST;
} else {
if (m_readState == SS_FIRST) {
unsigned char readText[3U];
::memset(readText, 'f', 3U);
unsigned int length = m_readLength - m_readPos;
unsigned char bytes = 5U;
if (length < 5U)
bytes = length;
readText[0U] = SLOW_DATA_TYPE_GPS | bytes;
for (unsigned int i = 0U; i < 2U && m_readPos < m_readLength; i++)
readText[i + 1U] = m_readBuffer[m_readPos++];
readText[0U] ^= SCRAMBLER_BYTE1;
readText[1U] ^= SCRAMBLER_BYTE2;
readText[2U] ^= SCRAMBLER_BYTE3;
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES);
m_readState = SS_SECOND;
} else {
unsigned char readText[3U];
::memset(readText, 'f', 3U);
for (unsigned int i = 0U; i < 3U && m_readPos < m_readLength; i++)
readText[i] = m_readBuffer[m_readPos++];
readText[0U] ^= SCRAMBLER_BYTE1;
readText[1U] ^= SCRAMBLER_BYTE2;
readText[2U] ^= SCRAMBLER_BYTE3;
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES);
m_readState = SS_FIRST;
}
}
data.setSeq(seqNo);
data.setData(buffer, DV_FRAME_LENGTH_BYTES);
sent++;
#if defined(LOOPBACK)
writeData(data);
#else
m_handler->process(data, DIR_INCOMING, AS_DRATS);
#endif
if (m_readPos == m_readLength) {
if (m_readState == SS_SECOND) {
seqNo++;
if (seqNo == 21U)
seqNo = 0U;
unsigned char readText[3U];
readText[0U] = 'f' ^ SCRAMBLER_BYTE1;
readText[1U] = 'f' ^ SCRAMBLER_BYTE2;
readText[2U] = 'f' ^ SCRAMBLER_BYTE3;
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES);
data.setSeq(seqNo);
data.setData(buffer, DV_FRAME_LENGTH_BYTES);
sent++;
#if defined(LOOPBACK)
writeData(data);
#else
m_handler->process(data, DIR_INCOMING, AS_DRATS);
#endif
}
seqNo++;
if (seqNo == 21U)
seqNo = 0U;
if (seqNo == 0U)
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
else
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, NULL_SLOW_DATA_BYTES, DATA_FRAME_LENGTH_BYTES);
data.setData(buffer, DV_FRAME_LENGTH_BYTES);
data.setSeq(seqNo);
data.setEnd(true);
sent++;
#if defined(LOOPBACK)
writeData(data);
#else
m_handler->process(data, DIR_INCOMING, AS_DRATS);
#endif
m_readLength = 0U;
m_readPos = 0U;
m_readEnd = false;
sending = false;
sent = 0U;
}
seqNo++;
if (seqNo == 21U)
seqNo = 0U;
}
}
// 50ms
Sleep(50UL);
}
if (m_socket != NULL)
m_socket->stop();
}
catch (std::exception& e) {
wxString message(e.what(), wxConvLocal);
wxLogError(wxT("Exception raised in the D-RATS Server thread - \"%s\""), message.c_str());
}
catch (...) {
wxLogError(wxT("Unknown exception raised in the D-RATS Server thread"));
}
wxLogMessage(wxT("Stopping the D-RATS Server thread for %s"), m_callsign.c_str());
return NULL;
}
void CDRATSServer::close()
{
m_stopped = true;
Wait();
}
void CDRATSServer::serviceSocket()
{
if (m_socket == NULL) {
m_readLength = 0U;
m_readPos = 0U;
m_readEnd = false;
return;
}
int len = m_socket->read(m_readBuffer + m_readLength, BUFFER_LENGTH - m_readLength, 0U);
if (len > 0) {
m_readLength += len;
if (!m_readEnd) {
// To allow strstr() to run correctly
m_readBuffer[m_readLength] = 0x00U;
if (::strstr((char*)m_readBuffer, "[EOB]") != NULL) {
CUtils::dump(wxT("To RF"), m_readBuffer, m_readLength);
m_readEnd = true;
}
}
}
}

65
Common/DRATSServer.h Normal file
View file

@ -0,0 +1,65 @@
/*
* Copyright (C) 2011,2012 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 DRATSServer_H
#define DRATSServer_H
#include "TCPReaderWriterServer.h"
#include "RepeaterCallback.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Defs.h"
#include <wx/wx.h>
class CDRATSServer : public wxThread {
public:
CDRATSServer(const wxString& address, unsigned int port, const wxString& callsign, IRepeaterCallback* handler);
virtual ~CDRATSServer();
virtual bool open();
virtual void writeHeader(const CHeaderData& header);
virtual void writeData(const CAMBEData& data);
virtual void writeEnd();
virtual void close();
virtual void* Entry();
private:
wxString m_address;
unsigned int m_port;
wxString m_callsign;
IRepeaterCallback* m_handler;
CTCPReaderWriterServer* m_socket;
bool m_stopped;
SLOWDATA_STATE m_readState;
unsigned char* m_readBuffer;
unsigned int m_readLength;
unsigned int m_readPos;
bool m_readEnd;
unsigned char* m_writeText;
SLOWDATA_STATE m_writeState;
unsigned char* m_writeBuffer;
unsigned int m_writeLength;
void serviceSocket();
};
#endif

177
Common/DStarDefines.h Normal file
View file

@ -0,0 +1,177 @@
/*
* Copyright (C) 2009-2015 by Jonathan Naylor, G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef DStarDefines_H
#define DStarDefines_H
#include <wx/wx.h>
const unsigned int DSTAR_GMSK_SYMBOL_RATE = 4800U;
const float DSTAR_GMSK_BT = 0.5F;
const bool BIT_SYNC_BITS[] = {true, false, true, false};
const unsigned int BIT_SYNC_LENGTH_BITS = 4U;
const bool FRAME_SYNC_BITS[] = {true, true, true, false, true, true, false, false,
true, false, true, false, false, false, false};
const unsigned int FRAME_SYNC_LENGTH_BITS = 15U;
const unsigned char DATA_SYNC_BYTES[] = {0x55, 0x2D, 0x16};
const bool DATA_SYNC_BITS[] = {true, false, true, false, true, false, true, false,
true, false, true, true, false, true, false, false,
false, true, true, false, true, false, false, false};
const unsigned char END_PATTERN_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const bool END_PATTERN_BITS[] = {true, false, true, false, true, false, true, false,
true, false, true, false, true, false, true, false,
true, false, true, false, true, false, true, false,
true, false, true, false, true, false, true, false,
false, false, false, true, false, false, true, true,
false, true, false, true, true, true, true, false};
const unsigned int END_PATTERN_LENGTH_BITS = 48U;
const unsigned int END_PATTERN_LENGTH_BYTES = END_PATTERN_LENGTH_BITS / 8U;
const unsigned char NULL_AMBE_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8};
const bool NULL_AMBE_DATA_BITS[] = {false, true, true, true, true, false, false, true,
true, false, true, true, false, false, false, true,
false, true, false, false, true, true, false, false,
false, false, false, true, false, false, false, true,
false, true, true, false, false, true, false, false,
false, true, false, true, true, false, false, false,
true, true, true, true, true, true, false, false,
true, false, false, false, false, true, true, false,
false, false, false, true, false, true, true, true};
// Note that these are already scrambled, 0x66 0x66 0x66 otherwise
const unsigned char NULL_SLOW_DATA_BYTES[] = {0x16, 0x29, 0xF5};
const bool NULL_SLOW_DATA_BITS[] = {false, true, true, false, true, false, false, false,
true, false, false, true, false, true, false, false,
true, false, true, false, true, true, true, true};
const unsigned int VOICE_FRAME_LENGTH_BITS = 72U;
const unsigned int VOICE_FRAME_LENGTH_BYTES = VOICE_FRAME_LENGTH_BITS / 8U;
const unsigned int DATA_FRAME_LENGTH_BITS = 24U;
const unsigned int DATA_FRAME_LENGTH_BYTES = DATA_FRAME_LENGTH_BITS / 8U;
const unsigned int DV_FRAME_LENGTH_BITS = VOICE_FRAME_LENGTH_BITS + DATA_FRAME_LENGTH_BITS;
const unsigned int DV_FRAME_LENGTH_BYTES = VOICE_FRAME_LENGTH_BYTES + DATA_FRAME_LENGTH_BYTES;
// The length of the end frame, three bytes extra
const unsigned int DV_FRAME_MAX_LENGTH_BITS = DV_FRAME_LENGTH_BITS + 24U;
const unsigned int DV_FRAME_MAX_LENGTH_BYTES = DV_FRAME_MAX_LENGTH_BITS / 8U;
const unsigned int FEC_SECTION_LENGTH_BITS = 660U;
const unsigned int RADIO_HEADER_LENGTH_BITS = 330U;
const unsigned int RADIO_HEADER_LENGTH_BYTES = 41U;
const unsigned int DATA_BLOCK_SIZE_BITS = 21U * DV_FRAME_LENGTH_BITS;
const unsigned int DATA_BLOCK_SIZE_BYTES = 21U * DV_FRAME_LENGTH_BYTES;
const unsigned int LONG_CALLSIGN_LENGTH = 8U;
const unsigned int SHORT_CALLSIGN_LENGTH = 4U;
const unsigned char SLOW_DATA_TYPE_MASK = 0xF0U;
const unsigned char SLOW_DATA_TYPE_GPS = 0x30U;
const unsigned char SLOW_DATA_TYPE_TEXT = 0x40U;
const unsigned char SLOW_DATA_TYPE_HEADER = 0x50U;
const unsigned char DATA_MASK = 0x80U;
const unsigned char REPEATER_MASK = 0x40U;
const unsigned char INTERRUPTED_MASK = 0x20U;
const unsigned char CONTROL_SIGNAL_MASK = 0x10U;
const unsigned char URGENT_MASK = 0x08U;
const unsigned char REPEATER_CONTROL_MASK = 0x07U;
const unsigned char REPEATER_CONTROL = 0x07U;
const unsigned char AUTO_REPLY = 0x06U;
const unsigned char RESEND_REQUESTED = 0x04U;
const unsigned char ACK_FLAG = 0x03U;
const unsigned char NO_RESPONSE = 0x02U;
const unsigned char RELAY_UNAVAILABLE = 0x01U;
const unsigned int DSTAR_FRAME_TIME_MS = 20U;
const unsigned int DSTAR_FRAMES_PER_SEC = 50U;
const unsigned char SCRAMBLER_BYTE1 = 0x70U;
const unsigned char SCRAMBLER_BYTE2 = 0x4FU;
const unsigned char SCRAMBLER_BYTE3 = 0x93U;
const unsigned int DPLUS_PORT = 20001U;
const unsigned int DEXTRA_PORT = 30001U;
const unsigned int DCS_PORT = 30051U;
const unsigned int CCS_PORT = 30062U; // Port for CCS7
const unsigned int G2_DV_PORT = 40000U;
const unsigned int G2_DD_PORT = 40001U;
const unsigned int NETWORK_TIMEOUT = 2U; // Network timeout for G2, CCS, DCS, DExtra, and D-Plus
const unsigned int REPEATER_TIMEOUT = 2U; // Repeater timeout
const unsigned int REPLY_TIME = 2U; // The turnaround time for version, echo, audio prompts
enum DSTAR_PROTOCOL {
DP_UNKNOWN,
DP_LOOPBACK,
DP_DEXTRA,
DP_DPLUS,
DP_DCS
};
enum AUDIO_SOURCE {
AS_G2,
AS_ECHO,
AS_INFO,
AS_XBAND,
AS_DRATS,
AS_DPLUS,
AS_DEXTRA,
AS_DCS,
AS_DUP,
AS_VERSION,
AS_CCS
};
enum DSTAR_RX_STATE {
DSRXS_LISTENING,
DSRXS_PROCESS_HEADER,
DSRXS_PROCESS_DATA,
DSRXS_PROCESS_SLOW_DATA
};
enum DSTAR_RPT_STATE {
DSRS_SHUTDOWN,
DSRS_LISTENING,
DSRS_VALID,
DSRS_VALID_WAIT,
DSRS_INVALID,
DSRS_INVALID_WAIT,
DSRS_TIMEOUT,
DSRS_TIMEOUT_WAIT,
DSRS_NETWORK
};
enum NETWORK_TYPE {
NETWORK_NONE,
NETWORK_HEADER,
NETWORK_DATA,
NETWORK_TEXT
};
enum DSTAR_MODE {
MODE_DUPLEX,
MODE_SIMPLEX,
MODE_GATEWAY
};
#endif

317
Common/DTMF.cpp Normal file
View file

@ -0,0 +1,317 @@
/*
* Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX
* Copyright (C) 2011 by DV Developer Group. DJ0ABR
*
* 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 "DTMF.h"
const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U};
const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const unsigned char DTMF_SYM_MASK[] = {0x10U, 0x40U, 0x08U, 0x20U};
const unsigned char DTMF_SYM0[] = {0x00U, 0x40U, 0x08U, 0x20U};
const unsigned char DTMF_SYM1[] = {0x00U, 0x00U, 0x00U, 0x00U};
const unsigned char DTMF_SYM2[] = {0x00U, 0x40U, 0x00U, 0x00U};
const unsigned char DTMF_SYM3[] = {0x10U, 0x00U, 0x00U, 0x00U};
const unsigned char DTMF_SYM4[] = {0x00U, 0x00U, 0x00U, 0x20U};
const unsigned char DTMF_SYM5[] = {0x00U, 0x40U, 0x00U, 0x20U};
const unsigned char DTMF_SYM6[] = {0x10U, 0x00U, 0x00U, 0x20U};
const unsigned char DTMF_SYM7[] = {0x00U, 0x00U, 0x08U, 0x00U};
const unsigned char DTMF_SYM8[] = {0x00U, 0x40U, 0x08U, 0x00U};
const unsigned char DTMF_SYM9[] = {0x10U, 0x00U, 0x08U, 0x00U};
const unsigned char DTMF_SYMA[] = {0x10U, 0x40U, 0x00U, 0x00U};
const unsigned char DTMF_SYMB[] = {0x10U, 0x40U, 0x00U, 0x20U};
const unsigned char DTMF_SYMC[] = {0x10U, 0x40U, 0x08U, 0x00U};
const unsigned char DTMF_SYMD[] = {0x10U, 0x40U, 0x08U, 0x20U};
const unsigned char DTMF_SYMS[] = {0x00U, 0x00U, 0x08U, 0x20U};
const unsigned char DTMF_SYMH[] = {0x10U, 0x00U, 0x08U, 0x20U};
CDTMF::CDTMF() :
m_data(),
m_command(),
m_pressed(false),
m_releaseCount(0U),
m_pressCount(0U),
m_lastChar(wxT(' '))
{
}
CDTMF::~CDTMF()
{
}
bool CDTMF::decode(const unsigned char* ambe, bool end)
{
// DTMF begins with these byte values
if (!end && (ambe[0] & DTMF_MASK[0]) == DTMF_SIG[0] && (ambe[1] & DTMF_MASK[1]) == DTMF_SIG[1] &&
(ambe[2] & DTMF_MASK[2]) == DTMF_SIG[2] && (ambe[3] & DTMF_MASK[3]) == DTMF_SIG[3] &&
(ambe[4] & DTMF_MASK[4]) == DTMF_SIG[4] && (ambe[5] & DTMF_MASK[5]) == DTMF_SIG[5] &&
(ambe[6] & DTMF_MASK[6]) == DTMF_SIG[6] && (ambe[7] & DTMF_MASK[7]) == DTMF_SIG[7] &&
(ambe[8] & DTMF_MASK[8]) == DTMF_SIG[8]) {
unsigned char sym0 = ambe[4] & DTMF_SYM_MASK[0];
unsigned char sym1 = ambe[5] & DTMF_SYM_MASK[1];
unsigned char sym2 = ambe[7] & DTMF_SYM_MASK[2];
unsigned char sym3 = ambe[8] & DTMF_SYM_MASK[3];
wxChar c = wxT(' ');
if (sym0 == DTMF_SYM0[0] && sym1 == DTMF_SYM0[1] && sym2 == DTMF_SYM0[2] && sym3 == DTMF_SYM0[3])
c = wxT('0');
else if (sym0 == DTMF_SYM1[0] && sym1 == DTMF_SYM1[1] && sym2 == DTMF_SYM1[2] && sym3 == DTMF_SYM1[3])
c = wxT('1');
else if (sym0 == DTMF_SYM2[0] && sym1 == DTMF_SYM2[1] && sym2 == DTMF_SYM2[2] && sym3 == DTMF_SYM2[3])
c = wxT('2');
else if (sym0 == DTMF_SYM3[0] && sym1 == DTMF_SYM3[1] && sym2 == DTMF_SYM3[2] && sym3 == DTMF_SYM3[3])
c = wxT('3');
else if (sym0 == DTMF_SYM4[0] && sym1 == DTMF_SYM4[1] && sym2 == DTMF_SYM4[2] && sym3 == DTMF_SYM4[3])
c = wxT('4');
else if (sym0 == DTMF_SYM5[0] && sym1 == DTMF_SYM5[1] && sym2 == DTMF_SYM5[2] && sym3 == DTMF_SYM5[3])
c = wxT('5');
else if (sym0 == DTMF_SYM6[0] && sym1 == DTMF_SYM6[1] && sym2 == DTMF_SYM6[2] && sym3 == DTMF_SYM6[3])
c = wxT('6');
else if (sym0 == DTMF_SYM7[0] && sym1 == DTMF_SYM7[1] && sym2 == DTMF_SYM7[2] && sym3 == DTMF_SYM7[3])
c = wxT('7');
else if (sym0 == DTMF_SYM8[0] && sym1 == DTMF_SYM8[1] && sym2 == DTMF_SYM8[2] && sym3 == DTMF_SYM8[3])
c = wxT('8');
else if (sym0 == DTMF_SYM9[0] && sym1 == DTMF_SYM9[1] && sym2 == DTMF_SYM9[2] && sym3 == DTMF_SYM9[3])
c = wxT('9');
else if (sym0 == DTMF_SYMA[0] && sym1 == DTMF_SYMA[1] && sym2 == DTMF_SYMA[2] && sym3 == DTMF_SYMA[3])
c = wxT('A');
else if (sym0 == DTMF_SYMB[0] && sym1 == DTMF_SYMB[1] && sym2 == DTMF_SYMB[2] && sym3 == DTMF_SYMB[3])
c = wxT('B');
else if (sym0 == DTMF_SYMC[0] && sym1 == DTMF_SYMC[1] && sym2 == DTMF_SYMC[2] && sym3 == DTMF_SYMC[3])
c = wxT('C');
else if (sym0 == DTMF_SYMD[0] && sym1 == DTMF_SYMD[1] && sym2 == DTMF_SYMD[2] && sym3 == DTMF_SYMD[3])
c = wxT('D');
else if (sym0 == DTMF_SYMS[0] && sym1 == DTMF_SYMS[1] && sym2 == DTMF_SYMS[2] && sym3 == DTMF_SYMS[3])
c = wxT('*');
else if (sym0 == DTMF_SYMH[0] && sym1 == DTMF_SYMH[1] && sym2 == DTMF_SYMH[2] && sym3 == DTMF_SYMH[3])
c = wxT('#');
if (c == m_lastChar) {
m_pressCount++;
} else {
m_lastChar = c;
m_pressCount = 0U;
}
if (c != wxT(' ') && !m_pressed && m_pressCount >= 3U) {
m_data.Append(c);
m_releaseCount = 0U;
m_pressed = true;
}
return c != wxT(' ');
} else {
// If it is not a DTMF Code
if ((end || m_releaseCount >= 100U) && m_data.Len() > 0U) {
m_command = m_data;
m_data.Clear();
m_releaseCount = 0U;
}
m_pressed = false;
m_releaseCount++;
m_pressCount = 0U;
m_lastChar = wxT(' ');
return false;
}
}
bool CDTMF::hasCommand() const
{
return !m_command.IsEmpty();
}
// DTMF to YOUR call command
wxString CDTMF::translate()
{
wxString command = m_command;
m_command.Clear();
if (command.IsEmpty())
return wxEmptyString;
if (command.IsSameAs(wxT("#")))
return wxT(" U");
if (command.IsSameAs(wxT("0")))
return wxT(" I");
if (command.IsSameAs(wxT("A")))
return wxT("CA ");
if (command.IsSameAs(wxT("00")))
return wxT(" I");
if (command.IsSameAs(wxT("**")))
return wxT(" L");
if (command.GetChar(0U) == wxT('*'))
return processReflector(wxT("REF"), command.Mid(1U));
else if (command.GetChar(0U) == wxT('B'))
return processReflector(wxT("XRF"), command.Mid(1U));
else if (command.GetChar(0U) == wxT('D'))
return processReflector(wxT("DCS"), command.Mid(1U));
else
return processCCS(command);
}
void CDTMF::reset()
{
m_data.Clear();
m_command.Clear();
m_pressed = false;
m_pressCount = 0U;
m_releaseCount = 0U;
m_lastChar = wxT(' ');
}
wxString CDTMF::processReflector(const wxString& prefix, const wxString& command) const
{
unsigned int len = command.Len();
wxChar c = command.GetChar(len - 1U);
if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) {
if (len < 2U || len > 4U)
return wxEmptyString;
unsigned long n;
command.Left(len - 1U).ToULong(&n);
if (n == 0UL)
return wxEmptyString;
wxString out;
out.Printf(wxT("%s%03lu%cL"), prefix.c_str(), n, c);
return out;
} else {
if (len < 3U || len > 5U)
return wxEmptyString;
unsigned long n1;
command.Left(len - 2U).ToULong(&n1);
if (n1 == 0UL)
return wxEmptyString;
unsigned long n2;
command.Right(2U).ToULong(&n2);
if (n2 == 0UL || n2 > 26UL)
return wxEmptyString;
c = wxT('A') + n2 - 1UL;
wxString out;
out.Printf(wxT("%s%03lu%cL"), prefix.c_str(), n1, c);
return out;
}
}
wxString CDTMF::processCCS(const wxString& command) const
{
unsigned int len = command.Len();
wxString out = wxEmptyString;
switch (len) {
case 3U: {
// CCS7 for local repeater without band
unsigned long n;
command.ToULong(&n);
if (n == 0UL)
return wxEmptyString;
out.Printf(wxT("C%03lu "), n);
}
break;
case 4U: {
wxChar c = command.GetChar(3U);
if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) {
// CCS7 for local repeater with band
unsigned long n;
command.Mid(0U, 3U).ToULong(&n);
if (n == 0UL)
return wxEmptyString;
out.Printf(wxT("C%03lu%c "), n, c);
} else {
// CCS7 for local user
unsigned long n;
command.ToULong(&n);
if (n == 0UL)
return wxEmptyString;
out.Printf(wxT("C%04lu "), n);
}
}
break;
case 5U: {
wxChar c = command.GetChar(4U);
if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) {
// CCS7 for local hostspot with band
unsigned long n;
command.Mid(0U, 4U).ToULong(&n);
if (n == 0UL)
return wxEmptyString;
out.Printf(wxT("C%04lu%c "), n, c);
}
}
break;
case 6U: {
// CCS7 for full repeater without band
unsigned long n;
command.ToULong(&n);
if (n == 0UL)
return wxEmptyString;
out.Printf(wxT("C%06lu "), n);
}
break;
case 7U: {
wxChar c = command.GetChar(6U);
if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) {
// CCS7 for full repeater with band
unsigned long n;
command.Mid(0U, 6U).ToULong(&n);
if (n == 0UL)
return wxEmptyString;
out.Printf(wxT("C%06lu%c"), n, c);
} else {
// CCS7 for full user or CCS7 for full hostpot without band
unsigned long n;
command.ToULong(&n);
if (n == 0UL)
return wxEmptyString;
out.Printf(wxT("C%07lu"), n);
}
}
break;
case 8U: {
wxChar c = command.GetChar(7U);
if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) {
// CCS7 for full hotspot with band
unsigned long n;
command.Mid(0U, 7U).ToULong(&n);
if (n == 0UL)
return wxEmptyString;
out.Printf(wxT("C%07lu%c"), n, c);
}
}
break;
default:
break;
}
return out;
}

49
Common/DTMF.h Normal file
View file

@ -0,0 +1,49 @@
/*
* Copyright (C) 2012,2013 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 DTMF_H
#define DTMF_H
#include <wx/wx.h>
class CDTMF {
public:
CDTMF();
~CDTMF();
bool decode(const unsigned char* ambe, bool end);
bool hasCommand() const;
wxString translate();
void reset();
private:
wxString m_data;
wxString m_command;
bool m_pressed;
unsigned int m_releaseCount;
unsigned int m_pressCount;
wxChar m_lastChar;
wxString processReflector(const wxString& prefix, const wxString& command) const;
wxString processCCS(const wxString& command) const;
};
#endif

181
Common/DVTOOLFileReader.cpp Normal file
View file

@ -0,0 +1,181 @@
/*
* Copyright (C) 2009,2013,2014 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 "DVTOOLFileReader.h"
#include "DStarDefines.h"
#include <wx/wx.h>
static const char DVTOOL_SIGNATURE[] = "DVTOOL";
static const unsigned int DVTOOL_SIGNATURE_LENGTH = 6U;
static const char DSVT_SIGNATURE[] = "DSVT";
static const unsigned int DSVT_SIGNATURE_LENGTH = 4U;
static const unsigned int FIXED_DATA_LENGTH = 9U;
static const unsigned char HEADER_FLAG = 0x10;
static const unsigned char DATA_FLAG = 0x20;
static const unsigned char HEADER_MASK = 0x80;
static const unsigned char TRAILER_MASK = 0x40;
const unsigned int BUFFER_LENGTH = 255U;
CDVTOOLFileReader::CDVTOOLFileReader() :
m_fileName(),
m_file(),
m_records(0U),
m_type(DVTFR_NONE),
m_buffer(NULL),
m_length(0U),
m_seqNo(0U)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CDVTOOLFileReader::~CDVTOOLFileReader()
{
delete[] m_buffer;
}
wxString CDVTOOLFileReader::getFileName() const
{
return m_fileName;
}
unsigned int CDVTOOLFileReader::getRecords() const
{
return m_records;
}
bool CDVTOOLFileReader::open(const wxString& fileName)
{
m_fileName = fileName;
bool res = m_file.Open(fileName, wxT("rb"));
if (!res)
return false;
unsigned char buffer[DVTOOL_SIGNATURE_LENGTH];
size_t n = m_file.Read(buffer, DVTOOL_SIGNATURE_LENGTH);
if (n != DVTOOL_SIGNATURE_LENGTH) {
m_file.Close();
return false;
}
if (::memcmp(buffer, DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH) != 0) {
m_file.Close();
return false;
}
wxUint32 uint32;
n = m_file.Read(&uint32, sizeof(wxUint32));
if (n != sizeof(wxUint32)) {
m_file.Close();
return false;
}
m_records = wxUINT32_SWAP_ON_LE(uint32);
m_seqNo = 0U;
return true;
}
DVTFR_TYPE CDVTOOLFileReader::read()
{
wxUint16 uint16;
size_t n = m_file.Read(&uint16, sizeof(wxUint16));
if (n != sizeof(wxUint16))
return DVTFR_NONE;
m_length = wxUINT16_SWAP_ON_BE(uint16) - 15U;
unsigned char bytes[FIXED_DATA_LENGTH];
n = m_file.Read(bytes, DSVT_SIGNATURE_LENGTH);
if (n != DSVT_SIGNATURE_LENGTH)
return DVTFR_NONE;
if (::memcmp(bytes, DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH) != 0)
return DVTFR_NONE;
char flag;
n = m_file.Read(&flag, 1U);
if (n != 1U)
return DVTFR_NONE;
m_type = (flag == HEADER_FLAG) ? DVTFR_HEADER : DVTFR_DATA;
n = m_file.Read(bytes, FIXED_DATA_LENGTH);
if (n != FIXED_DATA_LENGTH)
return DVTFR_NONE;
n = m_file.Read(&flag, 1U);
if (n != 1U)
return DVTFR_NONE;
if (m_type == DVTFR_DATA)
m_seqNo = flag;
n = m_file.Read(m_buffer, m_length);
if (n != m_length)
return DVTFR_NONE;
return m_type;
}
CHeaderData* CDVTOOLFileReader::readHeader()
{
if (m_type != DVTFR_HEADER)
return NULL;
CHeaderData* header = new CHeaderData;
if (m_buffer[39U] == 0xFFU && m_buffer[40U] == 0xFFU) {
header->setDVTOOLData(m_buffer, RADIO_HEADER_LENGTH_BYTES, false);
return header;
}
// Header checksum testing is enabled
bool valid = header->setDVTOOLData(m_buffer, RADIO_HEADER_LENGTH_BYTES, true);
if (!valid) {
delete header;
return NULL;
}
return header;
}
CAMBEData* CDVTOOLFileReader::readData()
{
if (m_type != DVTFR_DATA)
return NULL;
CAMBEData* data = new CAMBEData;
data->setData(m_buffer, m_length);
data->setSeq(m_seqNo);
return data;
}
void CDVTOOLFileReader::close()
{
m_file.Close();
}

61
Common/DVTOOLFileReader.h Normal file
View file

@ -0,0 +1,61 @@
/*
* Copyright (C) 2009,2014 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 DVTOOLFileReader_H
#define DVTOOLFileReader_H
#include "HeaderData.h"
#include "AMBEData.h"
enum DVTFR_TYPE {
DVTFR_NONE,
DVTFR_HEADER,
DVTFR_DATA
};
#include <wx/wx.h>
#include <wx/ffile.h>
class CDVTOOLFileReader {
public:
CDVTOOLFileReader();
~CDVTOOLFileReader();
wxString getFileName() const;
unsigned int getRecords() const;
bool open(const wxString& fileName);
DVTFR_TYPE read();
CHeaderData* readHeader();
CAMBEData* readData();
void close();
private:
wxString m_fileName;
wxFFile m_file;
wxUint32 m_records;
DVTFR_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
unsigned char m_seqNo;
};
#endif

143
Common/Defs.h Normal file
View file

@ -0,0 +1,143 @@
/*
* Copyright (C) 2010-2015 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef Defs_H
#define Defs_H
#include <wx/wx.h>
const wxString DEXTRA_HOSTS_FILE_NAME = wxT("DExtra_Hosts.txt");
const wxString DPLUS_HOSTS_FILE_NAME = wxT("DPlus_Hosts.txt");
const wxString DCS_HOSTS_FILE_NAME = wxT("DCS_Hosts.txt");
const wxString CCS_HOSTS_FILE_NAME = wxT("CCS_Hosts.txt");
const wxString GATEWAY_HOSTS_FILE_NAME = wxT("Gateway_Hosts.txt");
const wxString LINKS_BASE_NAME = wxT("Links");
const wxString TEXT_BASE_NAME = wxT("Text");
const wxString USERS_BASE_NAME = wxT("Users");
const wxString STARNET_BASE_NAME = wxT("STARnet");
const wxString HEADERS_BASE_NAME = wxT("Headers");
const wxString DDMODE_BASE_NAME = wxT("DDMode");
enum RECONNECT {
RECONNECT_NEVER,
RECONNECT_FIXED,
RECONNECT_5MINS,
RECONNECT_10MINS,
RECONNECT_15MINS,
RECONNECT_20MINS,
RECONNECT_25MINS,
RECONNECT_30MINS,
RECONNECT_60MINS,
RECONNECT_90MINS,
RECONNECT_120MINS,
RECONNECT_180MINS
};
enum DIRECTION {
DIR_INCOMING,
DIR_OUTGOING
};
enum PROTOCOL {
PROTO_DEXTRA,
PROTO_DPLUS,
PROTO_DCS,
PROTO_CCS
};
enum HW_TYPE {
HW_HOMEBREW,
HW_ICOM,
HW_DUMMY
};
enum TEXT_LANG {
TL_ENGLISH_UK,
TL_DEUTSCH,
TL_DANSK,
TL_FRANCAIS,
TL_ITALIANO,
TL_POLSKI,
TL_ENGLISH_US,
TL_ESPANOL,
TL_SVENSKA,
TL_NEDERLANDS_NL,
TL_NEDERLANDS_BE,
TL_NORSK,
TL_PORTUGUES
};
enum IRCDDB_STATUS {
IS_DISABLED,
IS_DISCONNECTED,
IS_CONNECTING,
IS_CONNECTED
};
enum G2_STATUS {
G2_NONE,
G2_LOCAL,
G2_USER,
G2_REPEATER,
G2_OK,
G2_XBAND,
G2_ECHO,
G2_VERSION,
G2_STARNET
};
enum LINK_STATUS {
LS_NONE,
LS_PENDING_IRCDDB,
LS_LINKING_LOOPBACK,
LS_LINKING_DEXTRA,
LS_LINKING_DPLUS,
LS_LINKING_DCS,
LS_LINKING_CCS,
LS_LINKED_LOOPBACK,
LS_LINKED_DEXTRA,
LS_LINKED_DPLUS,
LS_LINKED_DCS,
LS_LINKED_CCS
};
enum SLOWDATA_STATE {
SS_FIRST,
SS_SECOND
};
enum STARNET_CALLSIGN_SWITCH {
SCS_GROUP_CALLSIGN,
SCS_USER_CALLSIGN
};
enum GATEWAY_TYPE {
GT_REPEATER,
GT_HOTSPOT,
GT_DONGLE,
GT_STARNET
};
const unsigned int TIME_PER_TIC_MS = 5U;
#if defined(__WINDOWS__)
typedef unsigned long in_addr_t;
#endif
#endif

View file

@ -0,0 +1,133 @@
/*
* Copyright (C) 2013 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 "DummyRepeaterProtocolHandler.h"
#include "DStarDefines.h"
#include "Utils.h"
CDummyRepeaterProtocolHandler::CDummyRepeaterProtocolHandler()
{
}
CDummyRepeaterProtocolHandler::~CDummyRepeaterProtocolHandler()
{
}
bool CDummyRepeaterProtocolHandler::open()
{
return true;
}
bool CDummyRepeaterProtocolHandler::writeHeader(CHeaderData& header)
{
unsigned char buffer[50U];
unsigned int length = header.getHBRepeaterData(buffer, 50U, true);
wxLogMessage(wxT("Sending Header to port: %u, id: %04X"), header.getYourPort(), header.getId());
CUtils::dump(wxT("Data"), buffer + 8U, length - 8U);
return true;
}
bool CDummyRepeaterProtocolHandler::writeAMBE(CAMBEData& data)
{
unsigned char buffer[30U];
unsigned int length = data.getHBRepeaterData(buffer, 30U);
wxLogMessage(wxT("Sending AMBE to port: %u, seq: %02X, id: %04X"), data.getYourPort(), data.getSeq(), data.getId());
CUtils::dump(wxT("Data"), buffer + 9U, length - 9U);
return true;
}
bool CDummyRepeaterProtocolHandler::writeDD(CDDData& data)
{
unsigned char buffer[2000U];
unsigned int length = data.getHBRepeaterData(buffer, 2000U);
CUtils::dump(wxT("DD Data"), buffer, length);
return true;
}
bool CDummyRepeaterProtocolHandler::writeText(CTextData& text)
{
unsigned char buffer[40U];
unsigned int length = text.getHBRepeaterData(buffer, 40U);
CUtils::dump(wxT("Sending Text"), buffer, length);
return true;
}
bool CDummyRepeaterProtocolHandler::writeStatus(CStatusData& status)
{
unsigned char buffer[30U];
unsigned int length = status.getHBRepeaterData(buffer, 30U);
CUtils::dump(wxT("Sending Status"), buffer, length);
return true;
}
REPEATER_TYPE CDummyRepeaterProtocolHandler::read()
{
return RT_NONE;
}
CPollData* CDummyRepeaterProtocolHandler::readPoll()
{
return NULL;
}
CHeaderData* CDummyRepeaterProtocolHandler::readHeader()
{
return NULL;
}
CAMBEData* CDummyRepeaterProtocolHandler::readAMBE()
{
return NULL;
}
CHeaderData* CDummyRepeaterProtocolHandler::readBusyHeader()
{
return NULL;
}
CAMBEData* CDummyRepeaterProtocolHandler::readBusyAMBE()
{
return NULL;
}
CHeardData* CDummyRepeaterProtocolHandler::readHeard()
{
return NULL;
}
CDDData* CDummyRepeaterProtocolHandler::readDD()
{
return NULL;
}
void CDummyRepeaterProtocolHandler::close()
{
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (C) 2013 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 DummyRepeaterProtocolHandler_H
#define DummyRepeaterProtocolHandler_H
#include "RepeaterProtocolHandler.h"
#include "DStarDefines.h"
#include "HeaderData.h"
#include "StatusData.h"
#include "HeardData.h"
#include "AMBEData.h"
#include "TextData.h"
#include "PollData.h"
#include "DDData.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
class CDummyRepeaterProtocolHandler : public IRepeaterProtocolHandler {
public:
CDummyRepeaterProtocolHandler();
virtual ~CDummyRepeaterProtocolHandler();
virtual bool open();
virtual bool writeHeader(CHeaderData& header);
virtual bool writeAMBE(CAMBEData& data);
virtual bool writeDD(CDDData& data);
virtual bool writeText(CTextData& text);
virtual bool writeStatus(CStatusData& status);
virtual REPEATER_TYPE read();
virtual CPollData* readPoll();
virtual CHeardData* readHeard();
virtual CHeaderData* readHeader();
virtual CAMBEData* readAMBE();
virtual CDDData* readDD();
virtual CHeaderData* readBusyHeader();
virtual CAMBEData* readBusyAMBE();
virtual void close();
private:
};
#endif

154
Common/EchoUnit.cpp Normal file
View file

@ -0,0 +1,154 @@
/*
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "EchoUnit.h"
#include "Defs.h"
const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC;
CEchoUnit::CEchoUnit(IRepeaterCallback* handler, const wxString& callsign) :
m_handler(handler),
m_callsign(callsign),
m_status(ES_IDLE),
m_timer(1000U, REPLY_TIME),
m_header(NULL),
m_data(NULL),
m_in(0U),
m_out(0U),
m_time()
{
wxASSERT(handler != NULL);
m_data = new CAMBEData*[MAX_FRAMES];
for (unsigned int i = 0U; i < MAX_FRAMES; i++)
m_data[i] = NULL;
}
CEchoUnit::~CEchoUnit()
{
delete[] m_data;
}
void CEchoUnit::writeHeader(const CHeaderData& header)
{
if (m_status != ES_IDLE)
return;
m_header = new CHeaderData(header);
m_in = 0U;
m_status = ES_RECEIVE;
}
void CEchoUnit::writeData(const CAMBEData& data)
{
if (m_status != ES_RECEIVE)
return;
if (m_in < MAX_FRAMES) {
m_data[m_in] = new CAMBEData(data);
m_in++;
}
if (data.isEnd()) {
wxLogMessage(wxT("Received %.1f secs of audio from %s for echoing"), float(m_in) / float(DSTAR_FRAMES_PER_SEC), m_header->getMyCall1().c_str());
m_timer.start();
m_status = ES_WAIT;
}
}
void CEchoUnit::end()
{
if (m_status != ES_RECEIVE)
return;
wxLogMessage(wxT("Received %.1f secs of audio from %s for echoing"), float(m_in) / float(DSTAR_FRAMES_PER_SEC), m_header->getMyCall1().c_str());
m_timer.start();
m_status = ES_WAIT;
}
void CEchoUnit::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_status == ES_WAIT && m_timer.hasExpired()) {
m_timer.stop();
// RPT1 and RPT2 will be filled in later
m_header->setMyCall1(m_callsign);
m_header->setMyCall2(wxT("ECHO"));
m_header->setYourCall(wxT("CQCQCQ "));
m_handler->process(*m_header, DIR_INCOMING, AS_ECHO);
delete m_header;
m_out = 0U;
m_status = ES_TRANSMIT;
m_time.Start();
return;
}
if (m_status == ES_TRANSMIT) {
unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS;
while (m_out < needed) {
CAMBEData* data = m_data[m_out];
m_data[m_out] = NULL;
m_out++;
if (m_in == m_out)
data->setEnd(true);
m_handler->process(*data, DIR_INCOMING, AS_ECHO);
delete data;
if (m_in == m_out) {
m_in = 0U;
m_out = 0U;
m_status = ES_IDLE;
return;
}
}
return;
}
}
void CEchoUnit::cancel()
{
for (unsigned int i = 0U; i < MAX_FRAMES; i++) {
if (m_data[i] != NULL) {
delete m_data[i];
m_data[i] = NULL;
}
}
m_status = ES_IDLE;
m_out = 0U;
m_in = 0U;
m_timer.stop();
}

63
Common/EchoUnit.h Normal file
View file

@ -0,0 +1,63 @@
/*
* Copyright (C) 2011,2012 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 EchoUnit_H
#define EchoUnit_H
#include "RepeaterCallback.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Timer.h"
#include <wx/wx.h>
enum ECHO_STATUS {
ES_IDLE,
ES_RECEIVE,
ES_WAIT,
ES_TRANSMIT
};
class CEchoUnit {
public:
CEchoUnit(IRepeaterCallback* handler, const wxString& callsign);
~CEchoUnit();
void writeHeader(const CHeaderData& header);
void writeData(const CAMBEData& data);
void end();
void cancel();
void clock(unsigned int ms);
private:
IRepeaterCallback* m_handler;
wxString m_callsign;
ECHO_STATUS m_status;
CTimer m_timer;
CHeaderData* m_header;
CAMBEData** m_data;
unsigned int m_in;
unsigned int m_out;
wxStopWatch m_time;
};
#endif

198
Common/G2Handler.cpp Normal file
View file

@ -0,0 +1,198 @@
/*
* Copyright (C) 2010-2014 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 "StarNetHandler.h"
#include "DStarDefines.h"
#include "G2Handler.h"
#include "Utils.h"
#include "Defs.h"
unsigned int CG2Handler::m_maxRoutes = 0U;
CG2Handler** CG2Handler::m_routes = NULL;
CG2ProtocolHandler* CG2Handler::m_handler = NULL;
CHeaderLogger* CG2Handler::m_headerLogger = NULL;
CG2Handler::CG2Handler(CRepeaterHandler* repeater, const in_addr& address, unsigned int id) :
m_repeater(repeater),
m_address(address),
m_id(id),
m_inactivityTimer(1000U, NETWORK_TIMEOUT)
{
m_inactivityTimer.start();
}
CG2Handler::~CG2Handler()
{
}
void CG2Handler::initialise(unsigned int maxRoutes)
{
m_maxRoutes = maxRoutes;
if (maxRoutes == 0U)
return;
m_routes = new CG2Handler*[m_maxRoutes];
for (unsigned int i = 0U; i < m_maxRoutes; i++)
m_routes[i] = NULL;
}
void CG2Handler::setG2ProtocolHandler(CG2ProtocolHandler* handler)
{
wxASSERT(handler != NULL);
m_handler = handler;
}
void CG2Handler::setHeaderLogger(CHeaderLogger* logger)
{
m_headerLogger = logger;
}
void CG2Handler::process(CHeaderData& header)
{
// Is this a busy reply?
unsigned char flag1 = header.getFlag1();
if (flag1 == 0x01) {
// Don't check the incoming stream
// wxLogMessage(wxT("G2 busy message received"));
return;
}
// Check to see if this is for StarNet
CStarNetHandler* handler = CStarNetHandler::findStarNet(header);
if (handler != NULL) {
// Write to Header.log if it's enabled
if (m_headerLogger != NULL)
m_headerLogger->write(wxT("StarNet"), header);
handler->process(header);
return;
}
// No need to go any further
if (m_maxRoutes == 0U)
return;
in_addr address = header.getYourAddress();
unsigned int id = header.getId();
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
CG2Handler* route = m_routes[i];
if (route != NULL) {
// Is this a duplicate header, ignore it
if (route->m_id == id)
return;
}
}
// Find the destination repeater
CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(header.getRptCall2());
if (repeater == NULL) {
wxLogMessage(wxT("Incoming G2 header from %s to unknown repeater - %s"), header.getMyCall1().c_str(), header.getRptCall2().c_str());
return; // Not found, ignore
}
CG2Handler* route = new CG2Handler(repeater, address, id);
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
if (m_routes[i] == NULL) {
m_routes[i] = route;
// Write to Header.log if it's enabled
if (m_headerLogger != NULL)
m_headerLogger->write(wxT("G2"), header);
repeater->process(header, DIR_INCOMING, AS_G2);
return;
}
}
wxLogMessage(wxT("No space to add new G2 route, ignoring"));
delete route;
}
void CG2Handler::process(CAMBEData& data)
{
// Check to see if this is for StarNet
CStarNetHandler* handler = CStarNetHandler::findStarNet(data);
if (handler != NULL) {
handler->process(data);
return;
}
// No need to go any further
if (m_maxRoutes == 0U)
return;
unsigned int id = data.getId();
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
CG2Handler* route = m_routes[i];
if (route != NULL) {
if (route->m_id == id) {
route->m_inactivityTimer.start();
route->m_repeater->process(data, DIR_INCOMING, AS_G2);
if (data.isEnd()) {
delete route;
m_routes[i] = NULL;
}
return;
}
}
}
}
void CG2Handler::clock(unsigned int ms)
{
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
CG2Handler* route = m_routes[i];
if (route != NULL) {
bool ret = route->clockInt(ms);
if (ret) {
delete route;
m_routes[i] = NULL;
}
}
}
}
void CG2Handler::finalise()
{
for (unsigned int i = 0U; i < m_maxRoutes; i++)
delete m_routes[i];
delete[] m_routes;
}
bool CG2Handler::clockInt(unsigned int ms)
{
m_inactivityTimer.clock(ms);
if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) {
wxLogMessage(wxT("Inactivity timeout for a G2 route has expired"));
return true;
}
return false;
}

72
Common/G2Handler.h Normal file
View file

@ -0,0 +1,72 @@
/*
* Copyright (C) 2010,2012 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 G2Handler_H
#define G2Handler_H
#include "G2ProtocolHandler.h"
#include "RepeaterHandler.h"
#include "DStarDefines.h"
#include "HeaderLogger.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Timer.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
class CG2Handler {
public:
static void initialise(unsigned int maxRoutes);
static void setG2ProtocolHandler(CG2ProtocolHandler* handler);
static void setHeaderLogger(CHeaderLogger* logger);
static void process(CHeaderData& header);
static void process(CAMBEData& header);
static void clock(unsigned int ms);
static void finalise();
protected:
CG2Handler(CRepeaterHandler* repeater, const in_addr& address, unsigned int id);
~CG2Handler();
bool clockInt(unsigned int ms);
private:
static unsigned int m_maxRoutes;
static CG2Handler** m_routes;
static CG2ProtocolHandler* m_handler;
static CHeaderLogger* m_headerLogger;
CRepeaterHandler* m_repeater;
in_addr m_address;
unsigned int m_id;
CTimer m_inactivityTimer;
};
#endif

View file

@ -0,0 +1,150 @@
/*
* Copyright (C) 2010,2011,2013 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 "G2ProtocolHandler.h"
#include "DStarDefines.h"
#include "Utils.h"
// #define DUMP_TX
const unsigned int BUFFER_LENGTH = 255U;
CG2ProtocolHandler::CG2ProtocolHandler(unsigned int port, const wxString& addr) :
m_socket(addr, port),
m_type(GT_NONE),
m_buffer(NULL),
m_length(0U),
m_address(),
m_port(0U)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CG2ProtocolHandler::~CG2ProtocolHandler()
{
delete[] m_buffer;
}
bool CG2ProtocolHandler::open()
{
return m_socket.open();
}
bool CG2ProtocolHandler::writeHeader(const CHeaderData& header)
{
unsigned char buffer[60U];
unsigned int length = header.getG2Data(buffer, 60U, true);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Header"), buffer, length);
#endif
for (unsigned int i = 0U; i < 5U; i++) {
bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort());
if (!res)
return false;
}
return true;
}
bool CG2ProtocolHandler::writeAMBE(const CAMBEData& data)
{
unsigned char buffer[40U];
unsigned int length = data.getG2Data(buffer, 40U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Data"), buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
G2_TYPE CG2ProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CG2ProtocolHandler::readPackets()
{
m_type = GT_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_address, m_port);
if (length <= 0)
return false;
m_length = length;
if (m_buffer[0] != 'D' || m_buffer[1] != 'S' || m_buffer[2] != 'V' || m_buffer[3] != 'T') {
return true;
} else {
// Header or data packet type?
if ((m_buffer[14] & 0x80) == 0x80)
m_type = GT_HEADER;
else
m_type = GT_AMBE;
return false;
}
}
CHeaderData* CG2ProtocolHandler::readHeader()
{
if (m_type != GT_HEADER)
return NULL;
CHeaderData* header = new CHeaderData;
// G2 checksums are unreliable
bool res = header->setG2Data(m_buffer, m_length, false, m_address, m_port);
if (!res) {
delete header;
return NULL;
}
return header;
}
CAMBEData* CG2ProtocolHandler::readAMBE()
{
if (m_type != GT_AMBE)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setG2Data(m_buffer, m_length, m_address, m_port);
if (!res) {
delete data;
return NULL;
}
return data;
}
void CG2ProtocolHandler::close()
{
m_socket.close();
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 2010,2011 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 G2ProtocolHandler_H
#define G2ProtocolHandler_H
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AMBEData.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
enum G2_TYPE {
GT_NONE,
GT_HEADER,
GT_AMBE
};
class CG2ProtocolHandler {
public:
CG2ProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString);
~CG2ProtocolHandler();
bool open();
bool writeHeader(const CHeaderData& header);
bool writeAMBE(const CAMBEData& data);
G2_TYPE read();
CHeaderData* readHeader();
CAMBEData* readAMBE();
void close();
private:
CUDPReaderWriter m_socket;
G2_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_address;
unsigned int m_port;
bool readPackets();
};
#endif

57
Common/GatewayCache.cpp Normal file
View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2010,2011,2012 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 "GatewayCache.h"
const unsigned int CACHE_SIZE = 500U;
CGatewayCache::CGatewayCache() :
m_cache(CACHE_SIZE)
{
}
CGatewayCache::~CGatewayCache()
{
for (CGatewayCache_t::iterator it = m_cache.begin(); it != m_cache.end(); ++it)
delete it->second;
}
CGatewayRecord* CGatewayCache::find(const wxString& gateway)
{
return m_cache[gateway];
}
void CGatewayCache::update(const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
CGatewayRecord* rec = m_cache[gateway];
in_addr addr_in;
addr_in.s_addr = ::inet_addr(address.mb_str());
if (rec == NULL)
// A brand new record is needed
m_cache[gateway] = new CGatewayRecord(gateway, addr_in, protocol, addrLock, protoLock);
else
// Update an existing record
rec->setData(addr_in, protocol, addrLock, protoLock);
}
unsigned int CGatewayCache::getCount() const
{
return m_cache.size();
}

106
Common/GatewayCache.h Normal file
View file

@ -0,0 +1,106 @@
/*
* Copyright (C) 2010,2011,2012 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 GatewayCache_H
#define GatewayCache_H
#include "DStarDefines.h"
#include "Defs.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <wx/wx.h>
#include <wx/ffile.h>
class CGatewayRecord {
public:
CGatewayRecord(const wxString& gateway, in_addr address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock) :
m_gateway(gateway),
m_address(address),
m_protocol(DP_UNKNOWN),
m_addrLock(addrLock),
m_protoLock(false)
{
if (protocol != DP_UNKNOWN) {
m_protocol = protocol;
m_protoLock = protoLock;
}
}
wxString getGateway() const
{
return m_gateway;
}
in_addr getAddress() const
{
return m_address;
}
DSTAR_PROTOCOL getProtocol() const
{
return m_protocol;
}
void setData(in_addr address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
if (!m_addrLock) {
m_address = address;
m_addrLock = addrLock;
}
if (!m_protoLock) {
if (protocol != DP_UNKNOWN) {
m_protocol = protocol;
m_protoLock = protoLock;
}
}
}
private:
wxString m_gateway;
in_addr m_address;
DSTAR_PROTOCOL m_protocol;
bool m_addrLock;
bool m_protoLock;
};
WX_DECLARE_STRING_HASH_MAP(CGatewayRecord*, CGatewayCache_t);
class CGatewayCache {
public:
CGatewayCache();
~CGatewayCache();
CGatewayRecord* find(const wxString& gateway);
void update(const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock);
unsigned int getCount() const;
private:
CGatewayCache_t m_cache;
};
#endif

View file

@ -0,0 +1,285 @@
/*
* Copyright (C) 2010-2013 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 "HBRepeaterProtocolHandler.h"
#include "CCITTChecksum.h"
#include "DStarDefines.h"
#include "Utils.h"
const unsigned int BUFFER_LENGTH = 255U;
CHBRepeaterProtocolHandler::CHBRepeaterProtocolHandler(const wxString& address, unsigned int port) :
m_socket(address, port),
m_type(RT_NONE),
m_buffer(NULL),
m_length(0U),
m_address(),
m_port(0U)
{
wxASSERT(!address.IsEmpty());
wxASSERT(port > 0U);
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CHBRepeaterProtocolHandler::~CHBRepeaterProtocolHandler()
{
delete[] m_buffer;
}
bool CHBRepeaterProtocolHandler::open()
{
return m_socket.open();
}
bool CHBRepeaterProtocolHandler::writeHeader(CHeaderData& header)
{
unsigned char buffer[50U];
unsigned int length = header.getHBRepeaterData(buffer, 50U, true);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Header"), buffer, length);
return true;
#else
return m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort());
#endif
}
bool CHBRepeaterProtocolHandler::writeAMBE(CAMBEData& data)
{
unsigned char buffer[30U];
unsigned int length = data.getHBRepeaterData(buffer, 30U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Data"), buffer, length);
return true;
#else
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
#endif
}
bool CHBRepeaterProtocolHandler::writeDD(CDDData& data)
{
unsigned char buffer[2000U];
unsigned int length = data.getHBRepeaterData(buffer, 2000U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending DD Data"), buffer, length);
return true;
#else
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
#endif
}
bool CHBRepeaterProtocolHandler::writeText(CTextData& text)
{
unsigned char buffer[40U];
unsigned int length = text.getHBRepeaterData(buffer, 40U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Text"), buffer, length);
return true;
#else
return m_socket.write(buffer, length, text.getAddress(), text.getPort());
#endif
}
bool CHBRepeaterProtocolHandler::writeStatus(CStatusData& status)
{
unsigned char buffer[30U];
unsigned int length = status.getHBRepeaterData(buffer, 30U);
#if defined(DUMP_TX)
CUtils::dump(wxT("Sending Status"), buffer, length);
return true;
#else
return m_socket.write(buffer, length, status.getAddress(), status.getPort());
#endif
}
REPEATER_TYPE CHBRepeaterProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CHBRepeaterProtocolHandler::readPackets()
{
m_type = RT_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_address, m_port);
if (length <= 0)
return false;
m_length = length;
// Invalid packet type?
if (m_buffer[0] == 'D' && m_buffer[1] == 'S' && m_buffer[2] == 'R' && m_buffer[3] == 'P') {
// Poll data
if (m_buffer[4] == 0x0AU) {
m_type = RT_POLL;
return false;
}
// Header data
else if (m_buffer[4] == 0x20U) {
m_type = RT_HEADER;
return false;
}
// User data
else if (m_buffer[4] == 0x21U) {
m_type = RT_AMBE;
return false;
}
// DD data
else if (m_buffer[4] == 0x24U) {
m_type = RT_DD;
return false;
}
// Busy header data
else if (m_buffer[4] == 0x22U) {
m_type = RT_BUSY_HEADER;
return false;
}
// Busy user data
else if (m_buffer[4] == 0x23U) {
m_type = RT_BUSY_AMBE;
return false;
}
}
CUtils::dump(wxT("Unknown packet from the Repeater"), m_buffer, m_length);
return true;
}
CPollData* CHBRepeaterProtocolHandler::readPoll()
{
if (m_type != RT_POLL)
return NULL;
wxString text = wxString((char*)(m_buffer + 5U), wxConvLocal);
return new CPollData(text, m_address, m_port, m_socket.getPort());
}
CHeaderData* CHBRepeaterProtocolHandler::readHeader()
{
if (m_type != RT_HEADER)
return NULL;
CHeaderData* header = new CHeaderData;
bool res = header->setHBRepeaterData(m_buffer, m_length, true, m_address, m_port);
if (!res) {
wxLogError(wxT("Invalid checksum from the repeater"));
delete header;
return NULL;
}
return header;
}
CAMBEData* CHBRepeaterProtocolHandler::readAMBE()
{
if (m_type != RT_AMBE)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setHBRepeaterData(m_buffer, m_length, m_address, m_port);
if (!res) {
wxLogError(wxT("Invalid AMBE data from the repeater"));
delete data;
return NULL;
}
return data;
}
CHeaderData* CHBRepeaterProtocolHandler::readBusyHeader()
{
if (m_type != RT_BUSY_HEADER)
return NULL;
CHeaderData* header = new CHeaderData;
bool res = header->setHBRepeaterData(m_buffer, m_length, true, m_address, m_port);
if (!res) {
wxLogError(wxT("Invalid checksum from the repeater"));
delete header;
return NULL;
}
return header;
}
CAMBEData* CHBRepeaterProtocolHandler::readBusyAMBE()
{
if (m_type != RT_BUSY_AMBE)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setHBRepeaterData(m_buffer, m_length, m_address, m_port);
if (!res) {
wxLogError(wxT("Invalid AMBE data from the repeater"));
delete data;
return NULL;
}
return data;
}
CHeardData* CHBRepeaterProtocolHandler::readHeard()
{
return NULL;
}
CDDData* CHBRepeaterProtocolHandler::readDD()
{
if (m_type != RT_DD)
return NULL;
CDDData* data = new CDDData;
bool res = data->setHBRepeaterData(m_buffer, m_length, m_address, m_port);
if (!res) {
wxLogError(wxT("Invalid DD data from the repeater"));
delete data;
return NULL;
}
return data;
}
void CHBRepeaterProtocolHandler::close()
{
m_socket.close();
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2010-2013 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 HBRepeaterProtocolHandler_H
#define HBRepeaterProtocolHandler_H
#include "RepeaterProtocolHandler.h"
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "HeaderData.h"
#include "StatusData.h"
#include "HeardData.h"
#include "AMBEData.h"
#include "TextData.h"
#include "PollData.h"
#include "DDData.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
#include <wx/wx.h>
class CHBRepeaterProtocolHandler : public IRepeaterProtocolHandler {
public:
CHBRepeaterProtocolHandler(const wxString& address, unsigned int port);
virtual ~CHBRepeaterProtocolHandler();
virtual bool open();
virtual bool writeHeader(CHeaderData& header);
virtual bool writeAMBE(CAMBEData& data);
virtual bool writeDD(CDDData& data);
virtual bool writeText(CTextData& text);
virtual bool writeStatus(CStatusData& status);
virtual REPEATER_TYPE read();
virtual CPollData* readPoll();
virtual CHeardData* readHeard();
virtual CHeaderData* readHeader();
virtual CAMBEData* readAMBE();
virtual CDDData* readDD();
virtual CHeaderData* readBusyHeader();
virtual CAMBEData* readBusyAMBE();
virtual void close();
private:
CUDPReaderWriter m_socket;
REPEATER_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_address;
unsigned int m_port;
bool readPackets();
};
#endif

951
Common/HeaderData.cpp Normal file
View file

@ -0,0 +1,951 @@
/*
* Copyright (C) 2010-2014 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 "HeaderData.h"
#include <wx/datetime.h>
#include "CCITTChecksum.h"
#include "DStarDefines.h"
#include "Utils.h"
void CHeaderData::initialise()
{
wxDateTime now = wxDateTime::UNow();
::srand(now.GetMillisecond());
}
void CHeaderData::finalise()
{
}
unsigned int CHeaderData::createId()
{
return (::rand() % 65535U) + 1U;
}
CHeaderData::CHeaderData() :
m_rptSeq(0U),
m_id(0U),
m_band1(0x00U),
m_band2(0x02U),
m_band3(0x01U),
m_flag1(0U),
m_flag2(0U),
m_flag3(0U),
m_myCall1(NULL),
m_myCall2(NULL),
m_yourCall(NULL),
m_rptCall1(NULL),
m_rptCall2(NULL),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U),
m_errors(0U)
{
m_myCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_myCall2 = new unsigned char[SHORT_CALLSIGN_LENGTH];
m_yourCall = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall2 = new unsigned char[LONG_CALLSIGN_LENGTH];
::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_yourCall, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_myCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_myCall2, ' ', SHORT_CALLSIGN_LENGTH);
}
CHeaderData::CHeaderData(const CHeaderData& header) :
m_rptSeq(header.m_rptSeq),
m_id(header.m_id),
m_band1(header.m_band1),
m_band2(header.m_band2),
m_band3(header.m_band3),
m_flag1(header.m_flag1),
m_flag2(header.m_flag2),
m_flag3(header.m_flag3),
m_myCall1(NULL),
m_myCall2(NULL),
m_yourCall(NULL),
m_rptCall1(NULL),
m_rptCall2(NULL),
m_yourAddress(header.m_yourAddress),
m_yourPort(header.m_yourPort),
m_myPort(header.m_myPort),
m_errors(header.m_errors)
{
m_myCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_myCall2 = new unsigned char[SHORT_CALLSIGN_LENGTH];
m_yourCall = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall2 = new unsigned char[LONG_CALLSIGN_LENGTH];
::memcpy(m_myCall1, header.m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, header.m_myCall2, SHORT_CALLSIGN_LENGTH);
::memcpy(m_yourCall, header.m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, header.m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall2, header.m_rptCall2, LONG_CALLSIGN_LENGTH);
}
CHeaderData::CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall,
const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1,
unsigned char flag2, unsigned char flag3) :
m_rptSeq(0U),
m_id(0U),
m_band1(0U),
m_band2(0U),
m_band3(0U),
m_flag1(flag1),
m_flag2(flag2),
m_flag3(flag3),
m_myCall1(NULL),
m_myCall2(NULL),
m_yourCall(NULL),
m_rptCall1(NULL),
m_rptCall2(NULL),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U),
m_errors(0U)
{
m_myCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_myCall2 = new unsigned char[SHORT_CALLSIGN_LENGTH];
m_yourCall = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall2 = new unsigned char[LONG_CALLSIGN_LENGTH];
::memset(m_myCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_myCall2, ' ', SHORT_CALLSIGN_LENGTH);
::memset(m_yourCall, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < myCall1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
m_myCall1[i] = myCall1.GetChar(i);
for (unsigned int i = 0U; i < myCall2.Len() && i < SHORT_CALLSIGN_LENGTH; i++)
m_myCall2[i] = myCall2.GetChar(i);
for (unsigned int i = 0U; i < yourCall.Len() && i < LONG_CALLSIGN_LENGTH; i++)
m_yourCall[i] = yourCall.GetChar(i);
for (unsigned int i = 0U; i < rptCall1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
m_rptCall1[i] = rptCall1.GetChar(i);
for (unsigned int i = 0U; i < rptCall2.Len() && i < LONG_CALLSIGN_LENGTH; i++)
m_rptCall2[i] = rptCall2.GetChar(i);
}
CHeaderData::~CHeaderData()
{
delete[] m_myCall1;
delete[] m_myCall2;
delete[] m_yourCall;
delete[] m_rptCall1;
delete[] m_rptCall2;
}
bool CHeaderData::setIcomRepeaterData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 58U);
m_rptSeq = data[4] * 256U + data[5];
m_band1 = data[11];
m_band2 = data[12];
m_band3 = data[13];
m_id = data[14] * 256U + data[15];
m_flag1 = data[17U];
m_flag2 = data[18U];
m_flag3 = data[19U];
::memcpy(m_rptCall2, data + 20U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 28U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 36U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 44U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 52U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 17U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 17U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump(wxT("Header checksum failure from the repeater"), data + 17U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
bool CHeaderData::setHBRepeaterData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 49U);
m_id = data[5U] * 256U + data[6U];
m_errors = data[7U];
m_flag1 = data[8U];
m_flag2 = data[9U];
m_flag3 = data[10U];
::memcpy(m_rptCall2, data + 11U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 19U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 27U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 35U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 43U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 8U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 8U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump(wxT("Header checksum failure from the repeater"), data + 8U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
void CHeaderData::setDCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 100U);
m_id = data[44U] * 256U + data[43U];
m_flag1 = data[4U];
m_flag2 = data[5U];
m_flag3 = data[6U];
::memcpy(m_rptCall2, data + 7U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 15U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 23U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 31U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 39U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
}
void CHeaderData::setCCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 100U);
m_id = data[44U] * 256U + data[43U];
::memcpy(m_rptCall2, data + 7U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 15U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 23U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 31U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 39U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
}
bool CHeaderData::setG2Data(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 56U);
m_band1 = data[9];
m_band2 = data[10];
m_band3 = data[11];
m_id = data[12] * 256U + data[13];
m_flag1 = data[15U];
m_flag2 = data[16U];
m_flag3 = data[17U];
::memcpy(m_rptCall2, data + 18U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 26U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 34U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 42U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 50U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 15U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 15U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump(wxT("Header checksum failure from G2"), data + 15U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
bool CHeaderData::setDExtraData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 56U);
m_band1 = data[9];
m_band2 = data[10];
m_band3 = data[11];
m_id = data[12] * 256U + data[13];
m_flag1 = data[15U];
m_flag2 = data[16U];
m_flag3 = data[17U];
::memcpy(m_rptCall2, data + 18U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 26U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 34U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 42U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 50U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 15U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 15U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump(wxT("Header checksum failure from DExtra"), data + 15U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
bool CHeaderData::setDPlusData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
wxASSERT(data != NULL);
wxASSERT(length >= 58U);
if (data[0] != 0x3A || data[1] != 0x80) {
CUtils::dump(wxT("Invalid header length from D-Plus"), data, length);
return false;
}
m_band1 = data[11];
m_band2 = data[12];
m_band3 = data[13];
m_id = data[14] * 256U + data[15];
m_flag1 = data[17U];
m_flag2 = data[18U];
m_flag3 = data[19U];
::memcpy(m_rptCall2, data + 20U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 28U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 36U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 44U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 52U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 17U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 17U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump(wxT("Header checksum failure from D-Plus"), data + 17U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
bool CHeaderData::setDVTOOLData(const unsigned char* data, unsigned int length, bool check)
{
wxASSERT(data != NULL);
wxASSERT(length >= RADIO_HEADER_LENGTH_BYTES);
m_flag1 = data[0U];
m_flag2 = data[1U];
m_flag3 = data[2U];
::memcpy(m_rptCall2, data + 3U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 11U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 19U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 27U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 35U, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum cksum;
cksum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump(wxT("Header checksum failure from DVTOOL"), data, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
unsigned int CHeaderData::getIcomRepeaterData(unsigned char *data, unsigned int length, bool check) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 58U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'T';
data[3] = 'R';
data[4] = m_rptSeq / 256U; // Packet sequence number
data[5] = m_rptSeq % 256U;
data[6] = 0x73; // Not a response
data[7] = 0x12; // Data type
data[8] = 0x00; // Length of 48 bytes following
data[9] = 0x30;
data[10] = 0x20; // AMBE plus Slow Data following
data[11] = m_band1;
data[12] = m_band2;
data[13] = m_band3;
data[14] = m_id / 256U; // Unique session id
data[15] = m_id % 256U;
data[16] = 0x80;
data[17] = m_flag1; // Flags 1, 2, and 3
data[18] = m_flag2;
data[19] = m_flag3;
::memcpy(data + 20U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 28U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 36U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 44U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 52U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 17, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 56);
} else {
data[56] = 0xFF;
data[57] = 0xFF;
}
return 58U;
}
unsigned int CHeaderData::getHBRepeaterData(unsigned char *data, unsigned int length, bool check) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 49U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'R';
data[3] = 'P';
data[4] = 0x20U;
data[5] = m_id / 256U; // Unique session id
data[6] = m_id % 256U;
data[7] = 0U;
data[8] = m_flag1; // Flags 1, 2, and 3
data[9] = m_flag2;
data[10] = m_flag3;
::memcpy(data + 11U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 19U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 27U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 35U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 43U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 8U, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 47U);
} else {
data[47] = 0xFF;
data[48] = 0xFF;
}
return 49U;
}
void CHeaderData::getDCSData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 100U);
data[4] = m_flag1; // Flags 1, 2, and 3
data[5] = m_flag2;
data[6] = m_flag3;
::memcpy(data + 7U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 15U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 23U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 31U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 39U, m_myCall2, SHORT_CALLSIGN_LENGTH);
}
void CHeaderData::getCCSData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 100U);
::memcpy(data + 7U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 15U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 23U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 31U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 39U, m_myCall2, SHORT_CALLSIGN_LENGTH);
}
unsigned int CHeaderData::getG2Data(unsigned char *data, unsigned int length, bool check) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 56U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'V';
data[3] = 'T';
data[4] = 0x10;
data[5] = 0x00;
data[6] = 0x15;
data[7] = 0x09;
data[8] = 0x20;
data[9] = m_band1;
data[10] = m_band2;
data[11] = m_band3;
data[12] = m_id / 256U; // Unique session id
data[13] = m_id % 256U;
data[14] = 0x80;
data[15] = m_flag1; // Flags 1, 2, and 3
data[16] = m_flag2;
data[17] = m_flag3;
::memcpy(data + 18U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 26U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 34U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 42U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 50U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 15, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 54);
} else {
data[54] = 0xFF;
data[55] = 0xFF;
}
return 56U;
}
unsigned int CHeaderData::getDExtraData(unsigned char* data, unsigned int length, bool check) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 56U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'V';
data[3] = 'T';
data[4] = 0x10;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
data[8] = 0x20;
data[9] = m_band1;
data[10] = m_band2;
data[11] = m_band3;
data[12] = m_id % 256U; // Unique session id
data[13] = m_id / 256U;
data[14] = 0x80;
data[15] = 0x00; // Flags 1, 2, and 3
data[16] = 0x00;
data[17] = 0x00;
::memcpy(data + 18U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 26U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 34U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 42U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 50U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 15, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 54);
} else {
data[54] = 0xFF;
data[55] = 0xFF;
}
return 56U;
}
unsigned int CHeaderData::getDPlusData(unsigned char* data, unsigned int length, bool check) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 58U);
data[0] = 0x3A;
data[1] = 0x80;
data[2] = 'D';
data[3] = 'S';
data[4] = 'V';
data[5] = 'T';
data[6] = 0x10;
data[7] = 0x00;
data[8] = 0x00;
data[9] = 0x00;
data[10] = 0x20;
data[11] = m_band1;
data[12] = m_band2;
data[13] = m_band3;
data[14] = m_id % 256U; // Unique session id
data[15] = m_id / 256U;
data[16] = 0x80;
data[17] = 0x00; // Flags 1, 2, and 3
data[18] = 0x00;
data[19] = 0x00;
::memcpy(data + 20U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 28U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 36U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 44U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 52U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 17, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 56);
} else {
data[56] = 0xFF;
data[57] = 0xFF;
}
return 58U;
}
unsigned int CHeaderData::getId() const
{
return m_id;
}
void CHeaderData::setId(unsigned int id)
{
m_id = id;
}
unsigned char CHeaderData::getBand1() const
{
return m_band1;
}
unsigned char CHeaderData::getBand2() const
{
return m_band2;
}
unsigned char CHeaderData::getBand3() const
{
return m_band3;
}
void CHeaderData::setBand1(unsigned char band)
{
m_band1 = band;
}
void CHeaderData::setBand2(unsigned char band)
{
m_band2 = band;
}
void CHeaderData::setBand3(unsigned char band)
{
m_band3 = band;
}
unsigned int CHeaderData::getRptSeq() const
{
return m_rptSeq;
}
void CHeaderData::setRptSeq(unsigned int seqNo)
{
m_rptSeq = seqNo;
}
unsigned char CHeaderData::getFlag1() const
{
return m_flag1;
}
unsigned char CHeaderData::getFlag2() const
{
return m_flag2;
}
unsigned char CHeaderData::getFlag3() const
{
return m_flag3;
}
void CHeaderData::setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3)
{
m_flag1 = flag1;
m_flag2 = flag2;
m_flag3 = flag3;
}
wxString CHeaderData::getMyCall1() const
{
return wxString((const char*)m_myCall1, wxConvLocal, LONG_CALLSIGN_LENGTH);
}
wxString CHeaderData::getMyCall2() const
{
return wxString((const char*)m_myCall2, wxConvLocal, SHORT_CALLSIGN_LENGTH);
}
wxString CHeaderData::getYourCall() const
{
return wxString((const char*)m_yourCall, wxConvLocal, LONG_CALLSIGN_LENGTH);
}
wxString CHeaderData::getRptCall1() const
{
return wxString((const char*)m_rptCall1, wxConvLocal, LONG_CALLSIGN_LENGTH);
}
wxString CHeaderData::getRptCall2() const
{
return wxString((const char*)m_rptCall2, wxConvLocal, LONG_CALLSIGN_LENGTH);
}
void CHeaderData::setFlag1(unsigned char flag)
{
m_flag1 = flag;
}
void CHeaderData::setFlag2(unsigned char flag)
{
m_flag2 = flag;
}
void CHeaderData::setFlag3(unsigned char flag)
{
m_flag3 = flag;
}
void CHeaderData::setMyCall1(const wxString& my1)
{
::memset(m_myCall1, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < my1.Len(); i++)
m_myCall1[i] = my1.GetChar(i);
}
void CHeaderData::setMyCall2(const wxString& my2)
{
::memset(m_myCall2, ' ', SHORT_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < my2.Len(); i++)
m_myCall2[i] = my2.GetChar(i);
}
void CHeaderData::setYourCall(const wxString& your)
{
::memset(m_yourCall, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < your.Len(); i++)
m_yourCall[i] = your.GetChar(i);
}
void CHeaderData::setRptCall1(const wxString& rpt1)
{
::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < rpt1.Len(); i++)
m_rptCall1[i] = rpt1.GetChar(i);
}
void CHeaderData::setRptCall2(const wxString& rpt2)
{
::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < rpt2.Len(); i++)
m_rptCall2[i] = rpt2.GetChar(i);
}
void CHeaderData::setRepeaters(const wxString& rpt1, const wxString& rpt2)
{
::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < rpt1.Len(); i++)
m_rptCall1[i] = rpt1.GetChar(i);
for (unsigned int i = 0U; i < rpt2.Len(); i++)
m_rptCall2[i] = rpt2.GetChar(i);
}
void CHeaderData::setCQCQCQ()
{
::memcpy(m_yourCall, "CQCQCQ ", LONG_CALLSIGN_LENGTH);
}
bool CHeaderData::setData(const unsigned char *data, unsigned int length, bool check)
{
wxASSERT(data != NULL);
wxASSERT(length >= RADIO_HEADER_LENGTH_BYTES);
m_flag1 = data[0U];
m_flag2 = data[1U];
m_flag3 = data[2U];
::memcpy(m_rptCall2, data + 3U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 11U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 19U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 27U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 35U, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum cksum;
cksum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U);
return cksum.check(data + RADIO_HEADER_LENGTH_BYTES - 2U);
} else {
return true;
}
}
unsigned int CHeaderData::getData(unsigned char *data, unsigned int length, bool check) const
{
wxASSERT(data != NULL);
wxASSERT(length >= RADIO_HEADER_LENGTH_BYTES);
data[0] = m_flag1; // Flags 1, 2, and 3
data[1] = m_flag2;
data[2] = m_flag3;
::memcpy(data + 3U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 11U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 19U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 27U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 35U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U);
csum.result(data + RADIO_HEADER_LENGTH_BYTES - 2U);
return RADIO_HEADER_LENGTH_BYTES;
} else {
return RADIO_HEADER_LENGTH_BYTES - 2U;
}
}
void CHeaderData::setDestination(const in_addr& address, unsigned int port)
{
m_yourAddress = address;
m_yourPort = port;
}
in_addr CHeaderData::getYourAddress() const
{
return m_yourAddress;
}
unsigned int CHeaderData::getYourPort() const
{
return m_yourPort;
}
unsigned int CHeaderData::getMyPort() const
{
return m_myPort;
}
CHeaderData& CHeaderData::operator =(const CHeaderData& header)
{
if (&header != this) {
m_rptSeq = header.m_rptSeq;
m_id = header.m_id;
m_band1 = header.m_band1;
m_band2 = header.m_band2;
m_band3 = header.m_band3;
m_flag1 = header.m_flag1;
m_flag2 = header.m_flag2;
m_flag3 = header.m_flag3;
m_yourAddress = header.m_yourAddress;
m_yourPort = header.m_yourPort;
m_myPort = header.m_myPort;
m_errors = header.m_errors;
::memcpy(m_myCall1, header.m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, header.m_myCall2, SHORT_CALLSIGN_LENGTH);
::memcpy(m_yourCall, header.m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, header.m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall2, header.m_rptCall2, LONG_CALLSIGN_LENGTH);
}
return *this;
}

130
Common/HeaderData.h Normal file
View file

@ -0,0 +1,130 @@
/*
* Copyright (C) 2010-2014 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 HeaderData_H
#define HeaderData_H
#include <wx/wx.h>
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
class CHeaderData {
public:
CHeaderData();
CHeaderData(const CHeaderData& header);
CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall,
const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1 = 0x00,
unsigned char flag2 = 0x00, unsigned char flag3 = 0x00);
~CHeaderData();
bool setIcomRepeaterData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort);
bool setHBRepeaterData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort);
bool setG2Data(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort);
bool setDExtraData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDPlusData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
void setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
void setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
unsigned int getIcomRepeaterData(unsigned char* data, unsigned int length, bool check) const;
unsigned int getHBRepeaterData(unsigned char* data, unsigned int length, bool check) const;
unsigned int getDExtraData(unsigned char* data, unsigned int length, bool check) const;
unsigned int getDPlusData(unsigned char* data, unsigned int length, bool check) const;
unsigned int getG2Data(unsigned char* data, unsigned int length, bool check) const;
void getDCSData(unsigned char* data, unsigned int length) const;
void getCCSData(unsigned char* data, unsigned int length) const;
bool setDVTOOLData(const unsigned char* data, unsigned int length, bool check);
unsigned int getId() const;
void setId(unsigned int id);
unsigned char getBand1() const;
unsigned char getBand2() const;
unsigned char getBand3() const;
void setBand1(unsigned char band);
void setBand2(unsigned char band);
void setBand3(unsigned char band);
unsigned int getRptSeq() const;
void setRptSeq(unsigned int seqNo);
unsigned char getFlag1() const;
unsigned char getFlag2() const;
unsigned char getFlag3() const;
wxString getMyCall1() const;
wxString getMyCall2() const;
wxString getYourCall() const;
wxString getRptCall1() const;
wxString getRptCall2() const;
void setFlag1(unsigned char flag);
void setFlag2(unsigned char flag);
void setFlag3(unsigned char flag);
void setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3);
void setMyCall1(const wxString& callsign);
void setMyCall2(const wxString& callsign);
void setYourCall(const wxString& callsign);
void setRptCall1(const wxString& callsign);
void setRptCall2(const wxString& callsign);
void setCQCQCQ();
void setRepeaters(const wxString& rpt1, const wxString& rpt2);
void setDestination(const in_addr& address, unsigned int port);
bool setData(const unsigned char* data, unsigned int length, bool check);
unsigned int getData(unsigned char* data, unsigned int length, bool check) const;
in_addr getYourAddress() const;
unsigned int getYourPort() const;
unsigned int getMyPort() const;
unsigned int getErrors() const;
static void initialise();
static void finalise();
static unsigned int createId();
CHeaderData& operator=(const CHeaderData& header);
private:
unsigned int m_rptSeq;
unsigned int m_id;
unsigned char m_band1;
unsigned char m_band2;
unsigned char m_band3;
unsigned char m_flag1;
unsigned char m_flag2;
unsigned char m_flag3;
unsigned char* m_myCall1;
unsigned char* m_myCall2;
unsigned char* m_yourCall;
unsigned char* m_rptCall1;
unsigned char* m_rptCall2;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
unsigned int m_errors;
};
#endif

103
Common/HeaderLogger.cpp Normal file
View file

@ -0,0 +1,103 @@
/*
* Copyright (C) 2010,2011,2012,2014 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 "HeaderLogger.h"
#include "Defs.h"
#if !defined(__WXMSW__)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <wx/filename.h>
CHeaderLogger::CHeaderLogger(const wxString& dir, const wxString& name) :
m_dir(dir),
m_name(name),
m_file()
{
}
CHeaderLogger::~CHeaderLogger()
{
}
bool CHeaderLogger::open()
{
wxString fullName = HEADERS_BASE_NAME;
if (!m_name.IsEmpty()) {
fullName.Append(wxT("_"));
fullName.Append(m_name);
}
wxFileName fileName(m_dir, fullName, wxT("log"));
bool ret = m_file.Open(fileName.GetFullPath(), wxT("a+t"));
if (!ret) {
wxLogError(wxT("Cannot open %s file for appending"), fileName.GetFullPath().c_str());
return false;
}
return true;
}
void CHeaderLogger::write(const wxChar* type, const CHeaderData& header)
{
wxASSERT(type != NULL);
time_t timeNow = ::time(NULL);
struct tm* tm = ::gmtime(&timeNow);
char* t = ::inet_ntoa(header.getYourAddress());
wxString address(t, wxConvLocal);
wxString text;
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: %s header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X (%s:%u)\n"),
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, type,
header.getMyCall1().c_str(), header.getMyCall2().c_str(), header.getYourCall().c_str(),
header.getRptCall1().c_str(), header.getRptCall2().c_str(), header.getFlag1(), header.getFlag2(),
header.getFlag3(), address.c_str(), header.getYourPort());
m_file.Write(text);
m_file.Flush();
}
void CHeaderLogger::write(const wxChar* type, const CDDData& data)
{
wxASSERT(type != NULL);
time_t timeNow = ::time(NULL);
struct tm* tm = ::gmtime(&timeNow);
wxString text;
text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: %s header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X\n"),
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, type,
data.getMyCall1().c_str(), data.getMyCall2().c_str(), data.getYourCall().c_str(),
data.getRptCall1().c_str(), data.getRptCall2().c_str(), data.getFlag1(), data.getFlag2(),
data.getFlag3());
m_file.Write(text);
m_file.Flush();
}
void CHeaderLogger::close()
{
m_file.Close();
}

46
Common/HeaderLogger.h Normal file
View file

@ -0,0 +1,46 @@
/*
* Copyright (C) 2010-2013 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 HeaderLogger_H
#define HeaderLogger_H
#include "HeaderData.h"
#include "DDData.h"
#include <wx/wx.h>
#include <wx/ffile.h>
class CHeaderLogger {
public:
CHeaderLogger(const wxString& dir, const wxString& name = wxEmptyString);
~CHeaderLogger();
bool open();
void write(const wxChar* type, const CHeaderData& header);
void write(const wxChar* type, const CDDData& header);
void close();
private:
wxString m_dir;
wxString m_name;
wxFFile m_file;
};
#endif

134
Common/HeardData.cpp Normal file
View file

@ -0,0 +1,134 @@
/*
* Copyright (C) 2012,2013 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 "HeardData.h"
CHeardData::CHeardData() :
m_reflector(),
m_repeater(),
m_user(),
m_ext(),
m_address(),
m_port(0U)
{
}
CHeardData::CHeardData(const CHeardData& data) :
m_reflector(data.m_reflector),
m_repeater(data.m_repeater),
m_user(data.m_user),
m_ext(data.m_ext),
m_address(data.m_address),
m_port(data.m_port)
{
}
CHeardData::CHeardData(const CHeaderData& data, const wxString& repeater, const wxString& reflector) :
m_reflector(reflector),
m_repeater(repeater),
m_user(),
m_ext(),
m_address(),
m_port()
{
m_user = data.getMyCall1();
m_ext = data.getMyCall2();
}
CHeardData::~CHeardData()
{
}
bool CHeardData::setIcomRepeaterData(const unsigned char *data, unsigned int length, const in_addr& address, unsigned int port)
{
wxASSERT(data != NULL);
wxASSERT(length >= 26U);
m_user = wxString((char*)(data + 10U), wxConvLocal, LONG_CALLSIGN_LENGTH);
m_repeater = wxString((char*)(data + 18U), wxConvLocal, LONG_CALLSIGN_LENGTH);
m_address = address;
m_port = port;
return true;
}
unsigned int CHeardData::getCCSData(unsigned char *data, unsigned int length) const
{
wxASSERT(data != NULL);
wxASSERT(length >= 100U);
::memset(data, 0x00U, 100U);
data[0U] = '0';
data[1U] = '0';
data[2U] = '0';
data[3U] = '1';
::memset(data + 7U, ' ', 36U);
for (unsigned int i = 0U; i < m_reflector.Len(); i++)
data[i + 7U] = m_reflector.GetChar(i);
for (unsigned int i = 0U; i < m_repeater.Len(); i++)
data[i + 15U] = m_repeater.GetChar(i);
::memcpy(data + 23U, "CQCQCQ ", LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_user.Len(); i++)
data[i + 31U] = m_user.GetChar(i);
for (unsigned int i = 0U; i < m_ext.Len(); i++)
data[i + 39U] = m_ext.GetChar(i);
data[61U] = 0x01U;
data[63U] = 0x21U;
::memset(data + 64U, ' ', 20U);
data[93U] = 0x36U;
return 100U;
}
wxString CHeardData::getRepeater() const
{
return m_repeater;
}
wxString CHeardData::getUser() const
{
return m_user;
}
void CHeardData::setDestination(const in_addr& address, unsigned int port)
{
m_address = address;
m_port = port;
}
in_addr CHeardData::getAddress() const
{
return m_address;
}
unsigned int CHeardData::getPort() const
{
return m_port;
}

61
Common/HeardData.h Normal file
View file

@ -0,0 +1,61 @@
/*
* Copyright (C) 2012,2013 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 HeardData_H
#define HeardData_H
#include <wx/wx.h>
#include "DStarDefines.h"
#include "HeaderData.h"
#if defined(__WINDOWS__)
#include "Inaddr.h"
#else
#include <netinet/in.h>
#endif
class CHeardData {
public:
CHeardData();
CHeardData(const CHeardData& data);
CHeardData(const CHeaderData& data, const wxString& repeater, const wxString& reflector);
~CHeardData();
bool setIcomRepeaterData(const unsigned char* data, unsigned int length, const in_addr& address, unsigned int port);
unsigned int getCCSData(unsigned char* data, unsigned int length) const;
wxString getRepeater() const;
wxString getUser() const;
void setDestination(const in_addr& address, unsigned int port);
in_addr getAddress() const;
unsigned int getPort() const;
private:
wxString m_reflector;
wxString m_repeater;
wxString m_user;
wxString m_ext;
in_addr m_address;
unsigned int m_port;
};
#endif

107
Common/HostFile.cpp Normal file
View file

@ -0,0 +1,107 @@
/*
* Copyright (C) 2010-2013 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarDefines.h"
#include "HostFile.h"
#include <wx/textfile.h>
#include <wx/tokenzr.h>
#include <wx/file.h>
CHostFile::CHostFile(const wxString& fileName, bool logging) :
m_names(),
m_addresses(),
m_locks()
{
if (!wxFile::Exists(fileName))
return;
wxTextFile file;
bool ret = file.Open(fileName);
if (!ret)
return;
if (logging)
wxLogMessage(wxT("Reading %s"), fileName.c_str());
unsigned int nLines = file.GetLineCount();
for (unsigned int i = 0; i < nLines; i++) {
wxString line = file.GetLine(i);
if (line.length() > 0 && line.GetChar(0) != wxT('#')) {
wxStringTokenizer t(line, wxT(" \t\r\n"), wxTOKEN_STRTOK);
wxString name = t.GetNextToken();
wxString address = t.GetNextToken();
wxString lock = t.GetNextToken();
name.Append(wxT(" "));
name.Truncate(LONG_CALLSIGN_LENGTH);
if (!name.IsEmpty() && !address.IsEmpty()) {
m_names.Add(name);
m_addresses.Add(address);
m_locks.Add(lock.IsEmpty() ? 0 : 1);
}
}
}
file.Close();
}
CHostFile::~CHostFile()
{
}
unsigned int CHostFile::getCount() const
{
return m_names.GetCount();
}
wxString CHostFile::getName(unsigned int n) const
{
return m_names.Item(n);
}
wxString CHostFile::getAddress(unsigned int n) const
{
return m_addresses.Item(n);
}
wxString CHostFile::getAddress(const wxString& host) const
{
wxString name = host;
name.resize(LONG_CALLSIGN_LENGTH, wxT(' '));
int n = m_names.Index(name);
if (n == wxNOT_FOUND)
return wxEmptyString;
return m_addresses.Item(n);
}
bool CHostFile::getLock(unsigned int n) const
{
return m_locks.Item(n) == 1;
}
wxArrayString CHostFile::getNames() const
{
return m_names;
}

45
Common/HostFile.h Normal file
View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2010-2013 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 HostFile_H
#define HostFile_H
#include <wx/wx.h>
WX_DEFINE_ARRAY_INT(int, CArrayInt);
class CHostFile {
public:
CHostFile(const wxString& fileName, bool logging);
~CHostFile();
unsigned int getCount() const;
wxString getName(unsigned int n) const;
wxString getAddress(unsigned int n) const;
wxString getAddress(const wxString& host) const;
bool getLock(unsigned int n) const;
wxArrayString getNames() const;
private:
wxArrayString m_names;
wxArrayString m_addresses;
CArrayInt m_locks;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,311 @@
/*
* Copyright (C) 2010-2014 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 IRCDDBGatewayConfig_H
#define IRCDDBGatewayConfig_H
#include "Defs.h"
#include <wx/wx.h>
#include <wx/config.h>
#include <wx/filename.h>
class CIRCDDBGatewayConfig {
public:
#if defined(__WINDOWS__)
CIRCDDBGatewayConfig(wxConfigBase* config, const wxString& dir, const wxString& configName, const wxString& name);
#else
CIRCDDBGatewayConfig(const wxString& dir, const wxString& configName, const wxString& name);
#endif
~CIRCDDBGatewayConfig();
void getGateway(GATEWAY_TYPE& type, wxString& callsign, wxString& address, wxString& icomAddress, unsigned int& icomPort, wxString& hbAddress, unsigned int& hbPort, double& latitude, double& longitude, wxString& description1, wxString& description2, wxString& url) const;
void setGateway(GATEWAY_TYPE type, const wxString& callsign, const wxString& address, const wxString& icomAddress, unsigned int icomPort, const wxString& hbAddress, unsigned int hbPort, double latitude, double longitude, const wxString& description1, const wxString& description2, const wxString& url);
void getRepeater1(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const;
void setRepeater1(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url);
void getRepeater2(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const;
void setRepeater2(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url);
void getRepeater3(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const;
void setRepeater3(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url);
void getRepeater4(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const;
void setRepeater4(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url);
void getIrcDDB(bool& enabled, wxString& hostname, wxString& username, wxString& password) const;
void getIrcDDB2(bool& enabled, wxString& hostname, wxString& username, wxString& password) const;
void getIrcDDB3(bool& enabled, wxString& hostname, wxString& username, wxString& password) const;
void getIrcDDB4(bool& enabled, wxString& hostname, wxString& username, wxString& password) const;
void setIrcDDB(bool enabled, const wxString& hostname, const wxString& username, const wxString& password);
void setIrcDDB2(bool enabled, const wxString& hostname, const wxString& username, const wxString& password);
void setIrcDDB3(bool enabled, const wxString& hostname, const wxString& username, const wxString& password);
void setIrcDDB4(bool enabled, const wxString& hostname, const wxString& username, const wxString& password);
void getDPRS(bool& enabled, wxString& hostname, unsigned int& port) const;
void setDPRS(bool enabled, const wxString& hostname, unsigned int port);
void getDExtra(bool& enabled, unsigned int& maxDongles) const;
void setDExtra(bool enabled, unsigned int maxDongles);
void getDPlus(bool& enabled, unsigned int& maxDongles, wxString& login) const;
void setDPlus(bool enabled, unsigned int maxDongles, const wxString& login);
void getDCS(bool& dcsEnabled, bool& ccsEnabled, wxString& ccsHost) const;
void setDCS(bool dcsEnabled, bool ccsEnabled, const wxString& ccsHost);
void getXLX(bool& xlxEnabled, bool& xlxOverrideLocal, wxString& xlxHostsFileUrl);
void setXLX(bool xlxEnabled, bool xlxOverrideLocal, wxString xlxHostsFileUrl);
#if defined(DEXTRA_LINK) || defined(DCS_LINK)
void getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const;
void setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector);
void getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const;
void setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector);
void getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const;
void setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector);
void getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const;
void setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector);
void getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const;
void setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector);
#else
void getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const;
void setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch);
void getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const;
void setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch);
void getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const;
void setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch);
void getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const;
void setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch);
void getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const;
void setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch);
#endif
void getRemote(bool& enabled, wxString& password, unsigned int& port) const;
void setRemote(bool enabled, const wxString& password, unsigned int port);
void getMiscellaneous(TEXT_LANG& language, bool& infoEnabled, bool& echoEnabled, bool& logEnabled, bool& dratsEnabled, bool& dtmfEnabled) const;
void setMiscellaneous(TEXT_LANG language, bool infoEnabled, bool echoEnabled, bool logEnabled, bool dratsEnabled, bool dtmfEnabled);
void getPosition(int& x, int& y) const;
void setPosition(int x, int y);
bool write();
private:
#if defined(__WINDOWS__)
wxConfigBase* m_config;
wxString m_name;
#endif
wxFileName m_fileName;
GATEWAY_TYPE m_type;
wxString m_callsign;
wxString m_address;
wxString m_icomAddress;
unsigned int m_icomPort;
wxString m_hbAddress;
unsigned int m_hbPort;
double m_latitude;
double m_longitude;
wxString m_description1;
wxString m_description2;
wxString m_url;
wxString m_repeater1Callsign;
wxString m_repeater1Band;
HW_TYPE m_repeater1Type;
wxString m_repeater1Address;
unsigned int m_repeater1Port;
wxString m_repeater1Reflector;
bool m_repeater1AtStartup;
RECONNECT m_repeater1Reconnect;
double m_repeater1Frequency;
double m_repeater1Offset;
double m_repeater1Range;
double m_repeater1Latitude;
double m_repeater1Longitude;
double m_repeater1Agl;
wxString m_repeater1Description1;
wxString m_repeater1Description2;
wxString m_repeater1URL;
unsigned char m_repeater1Band1;
unsigned char m_repeater1Band2;
unsigned char m_repeater1Band3;
wxString m_repeater2Callsign;
wxString m_repeater2Band;
HW_TYPE m_repeater2Type;
wxString m_repeater2Address;
unsigned int m_repeater2Port;
wxString m_repeater2Reflector;
bool m_repeater2AtStartup;
RECONNECT m_repeater2Reconnect;
double m_repeater2Frequency;
double m_repeater2Offset;
double m_repeater2Range;
double m_repeater2Latitude;
double m_repeater2Longitude;
double m_repeater2Agl;
wxString m_repeater2Description1;
wxString m_repeater2Description2;
wxString m_repeater2URL;
unsigned char m_repeater2Band1;
unsigned char m_repeater2Band2;
unsigned char m_repeater2Band3;
wxString m_repeater3Callsign;
wxString m_repeater3Band;
HW_TYPE m_repeater3Type;
wxString m_repeater3Address;
unsigned int m_repeater3Port;
wxString m_repeater3Reflector;
bool m_repeater3AtStartup;
RECONNECT m_repeater3Reconnect;
double m_repeater3Frequency;
double m_repeater3Offset;
double m_repeater3Range;
double m_repeater3Latitude;
double m_repeater3Longitude;
double m_repeater3Agl;
wxString m_repeater3Description1;
wxString m_repeater3Description2;
wxString m_repeater3URL;
unsigned char m_repeater3Band1;
unsigned char m_repeater3Band2;
unsigned char m_repeater3Band3;
wxString m_repeater4Callsign;
wxString m_repeater4Band;
HW_TYPE m_repeater4Type;
wxString m_repeater4Address;
unsigned int m_repeater4Port;
wxString m_repeater4Reflector;
bool m_repeater4AtStartup;
RECONNECT m_repeater4Reconnect;
double m_repeater4Frequency;
double m_repeater4Offset;
double m_repeater4Range;
double m_repeater4Latitude;
double m_repeater4Longitude;
double m_repeater4Agl;
wxString m_repeater4Description1;
wxString m_repeater4Description2;
wxString m_repeater4URL;
unsigned char m_repeater4Band1;
unsigned char m_repeater4Band2;
unsigned char m_repeater4Band3;
bool m_ircddbEnabled;
wxString m_ircddbHostname;
wxString m_ircddbUsername;
wxString m_ircddbPassword;
bool m_ircddbEnabled2;
wxString m_ircddbHostname2;
wxString m_ircddbUsername2;
wxString m_ircddbPassword2;
bool m_ircddbEnabled3;
wxString m_ircddbHostname3;
wxString m_ircddbUsername3;
wxString m_ircddbPassword3;
bool m_ircddbEnabled4;
wxString m_ircddbHostname4;
wxString m_ircddbUsername4;
wxString m_ircddbPassword4;
bool m_aprsEnabled;
wxString m_aprsHostname;
unsigned int m_aprsPort;
bool m_dextraEnabled;
unsigned int m_dextraMaxDongles;
bool m_dplusEnabled;
unsigned int m_dplusMaxDongles;
wxString m_dplusLogin;
bool m_dcsEnabled;
bool m_ccsEnabled;
wxString m_ccsHost;
bool m_xlxEnabled;
bool m_xlxOverrideLocal;
wxString m_xlxHostsFileUrl;
wxString m_starNet1Band;
wxString m_starNet1Callsign;
wxString m_starNet1Logoff;
wxString m_starNet1Info;
wxString m_starNet1Permanent;
unsigned int m_starNet1UserTimeout;
unsigned int m_starNet1GroupTimeout;
STARNET_CALLSIGN_SWITCH m_starNet1CallsignSwitch;
bool m_starNet1TxMsgSwitch;
wxString m_starNet1Reflector;
wxString m_starNet2Band;
wxString m_starNet2Callsign;
wxString m_starNet2Logoff;
wxString m_starNet2Info;
wxString m_starNet2Permanent;
unsigned int m_starNet2UserTimeout;
unsigned int m_starNet2GroupTimeout;
STARNET_CALLSIGN_SWITCH m_starNet2CallsignSwitch;
bool m_starNet2TxMsgSwitch;
wxString m_starNet2Reflector;
wxString m_starNet3Band;
wxString m_starNet3Callsign;
wxString m_starNet3Logoff;
wxString m_starNet3Info;
wxString m_starNet3Permanent;
unsigned int m_starNet3UserTimeout;
unsigned int m_starNet3GroupTimeout;
STARNET_CALLSIGN_SWITCH m_starNet3CallsignSwitch;
bool m_starNet3TxMsgSwitch;
wxString m_starNet3Reflector;
wxString m_starNet4Band;
wxString m_starNet4Callsign;
wxString m_starNet4Logoff;
wxString m_starNet4Info;
wxString m_starNet4Permanent;
unsigned int m_starNet4UserTimeout;
unsigned int m_starNet4GroupTimeout;
STARNET_CALLSIGN_SWITCH m_starNet4CallsignSwitch;
bool m_starNet4TxMsgSwitch;
wxString m_starNet4Reflector;
wxString m_starNet5Band;
wxString m_starNet5Callsign;
wxString m_starNet5Logoff;
wxString m_starNet5Info;
wxString m_starNet5Permanent;
unsigned int m_starNet5UserTimeout;
unsigned int m_starNet5GroupTimeout;
STARNET_CALLSIGN_SWITCH m_starNet5CallsignSwitch;
bool m_starNet5TxMsgSwitch;
wxString m_starNet5Reflector;
bool m_remoteEnabled;
wxString m_remotePassword;
unsigned int m_remotePort;
TEXT_LANG m_language;
bool m_infoEnabled;
bool m_echoEnabled;
bool m_logEnabled;
bool m_dratsEnabled;
bool m_dtmfEnabled;
int m_x;
int m_y;
};
#endif

View file

@ -0,0 +1,650 @@
/*
* Copyright (C) 2010-2014 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 "IcomRepeaterProtocolHandler.h"
#include <wx/datetime.h>
#include "CCITTChecksum.h"
#include "DStarDefines.h"
#include "Utils.h"
// Allow space for a big DD packet
const unsigned int BUFFER_LENGTH = 2500U;
const unsigned int QUEUE_LENGTH = 50U;
const unsigned int LOOP_DELAY = 5UL;
const unsigned int LOOP_TICKS = 200U;
CIcomRepeaterProtocolHandler::CIcomRepeaterProtocolHandler(const wxString& address, unsigned int port, const wxString& icomAddress, unsigned int icomPort) :
wxThread(wxTHREAD_JOINABLE),
m_socket(address, port),
m_icomAddress(),
m_icomPort(icomPort),
m_over1(false),
m_seqNo(0U),
m_tries(0U),
m_ackQueue(NULL),
m_killed(false),
m_type(RT_NONE),
m_buffer(NULL),
m_rptrQueue(QUEUE_LENGTH),
m_gwyQueue(QUEUE_LENGTH),
m_retryTimer(LOOP_TICKS, 0U, 200U) // 200ms
{
wxASSERT(!icomAddress.IsEmpty());
wxASSERT(!address.IsEmpty());
wxASSERT(icomPort > 0U);
wxASSERT(port > 0U);
m_icomAddress.s_addr = ::inet_addr(icomAddress.mb_str());
m_buffer = new unsigned char[BUFFER_LENGTH];
wxDateTime now = wxDateTime::UNow();
::srand(now.GetMillisecond());
}
CIcomRepeaterProtocolHandler::~CIcomRepeaterProtocolHandler()
{
while (!m_gwyQueue.isEmpty()) {
CDataQueue* dq = m_gwyQueue.getData();
free(dq);
}
while (!m_rptrQueue.isEmpty()) {
CDataQueue* dq = m_rptrQueue.getData();
free(dq);
}
delete[] m_buffer;
}
void CIcomRepeaterProtocolHandler::setCount(unsigned int count)
{
m_over1 = count > 1U;
}
bool CIcomRepeaterProtocolHandler::open()
{
bool ret = m_socket.open();
if (!ret)
return false;
unsigned char buffer[10U];
buffer[0U] = 'I';
buffer[1U] = 'N';
buffer[2U] = 'I';
buffer[3U] = 'T';
buffer[4U] = 0x00;
buffer[5U] = 0x00;
buffer[6U] = 0x73;
buffer[7U] = 0x00;
buffer[8U] = 0x00;
buffer[9U] = 0x00;
ret = m_socket.write(buffer, 10U, m_icomAddress, m_icomPort);
if (!ret) {
m_socket.close();
return false;
}
// Wait for a reply from the RP2C
for (unsigned int i = 0U; i < 10U; i++) {
in_addr address;
unsigned int port;
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port);
if (length == 10 && m_buffer[0U] == 'I' && m_buffer[1U] == 'N' && m_buffer[2U] == 'I' && m_buffer[3U] == 'T' && m_buffer[6U] == 0x72 && m_buffer[7U] == 0x00) {
m_seqNo = m_buffer[4U] * 256U + m_buffer[5U] + 1U;
wxLogMessage(wxT("Initial sequence number from the RP2C is %u"), m_seqNo);
// Start the thread
Create();
Run();
return true;
}
::wxSleep(1);
}
m_socket.close();
wxLogError(wxT("No reply from the RP2C for 10 seconds, aborting"));
return false;
}
void* CIcomRepeaterProtocolHandler::Entry()
{
wxLogMessage(wxT("Starting the Icom Controller thread"));
try {
while (!m_killed) {
sendGwyPackets();
Sleep(LOOP_DELAY);
readIcomPackets();
m_retryTimer.clock();
}
}
catch (std::exception& e) {
wxString message(e.what(), wxConvLocal);
wxLogError(wxT("Exception raised in the Icom Controller thread - \"%s\""), message.c_str());
}
catch (...) {
wxLogError(wxT("Unknown exception raised in the Icom Controller thread"));
}
wxLogMessage(wxT("Stopping the Icom Controller thread"));
m_socket.close();
return NULL;
}
bool CIcomRepeaterProtocolHandler::writeHeader(CHeaderData& header)
{
CDataQueue* dq = new CDataQueue(new CHeaderData(header));
m_gwyQueue.addData(dq);
return true;
}
bool CIcomRepeaterProtocolHandler::writeAMBE(CAMBEData& data)
{
CDataQueue* dq = new CDataQueue(new CAMBEData(data));
m_gwyQueue.addData(dq);
return true;
}
bool CIcomRepeaterProtocolHandler::writeDD(CDDData& data)
{
CDataQueue* dq = new CDataQueue(new CDDData(data));
m_gwyQueue.addData(dq);
return true;
}
bool CIcomRepeaterProtocolHandler::writeText(CTextData&)
{
return true;
}
bool CIcomRepeaterProtocolHandler::writeStatus(CStatusData&)
{
return true;
}
void CIcomRepeaterProtocolHandler::readIcomPackets()
{
for (;;) {
// No more data?
in_addr address;
unsigned int port;
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port);
if (length <= 0)
return;
if (address.s_addr != m_icomAddress.s_addr || port != m_icomPort) {
wxLogError(wxT("Incoming Icom data from an unknown source"));
continue;
}
// Invalid packet type?
if (m_buffer[0] != 'D' || m_buffer[1] != 'S' || m_buffer[2] != 'T' || m_buffer[3] != 'R') {
CUtils::dump(wxT("Missing DSTR from the RP2C"), m_buffer, length);
continue;
}
// An ack
if (length == 10 && m_buffer[6] == 0x72) {
wxUint16 seqNo = m_buffer[4] * 256U + m_buffer[5];
if (seqNo == m_seqNo)
m_seqNo++;
else
m_seqNo = seqNo;
// Free the ack queue
free(m_ackQueue);
m_ackQueue = NULL;
m_retryTimer.stop();
m_tries = 0U;
continue;
}
// Send an ack for all packet types except an ack
wxUint16 seqNo = m_buffer[4] * 256U + m_buffer[5];
sendAck(seqNo);
// Heard data
if (m_buffer[6] == 0x73 && m_buffer[7] == 0x21) {
CHeardData* heard = new CHeardData;
bool ret = heard->setIcomRepeaterData(m_buffer, length, m_icomAddress, m_icomPort);
if (!ret) {
wxLogError(wxT("Invalid heard data from the RP2C"));
delete heard;
continue;
}
m_rptrQueue.addData(new CDataQueue(heard));
continue;
}
// Poll data
if (m_buffer[6] == 0x73 && m_buffer[7] == 0x00) {
m_rptrQueue.addData(new CDataQueue);
continue;
}
// DD Data
if (m_buffer[6] == 0x73 && m_buffer[7] == 0x11) {
CDDData* data = new CDDData;
bool ret = data->setIcomRepeaterData(m_buffer, length, m_icomAddress, m_icomPort);
if (!ret) {
wxLogError(wxT("Invalid DD data from the RP2C"));
delete data;
continue;
}
m_rptrQueue.addData(new CDataQueue(data));
continue;
}
// DV data
if (m_buffer[6] == 0x73 && m_buffer[7] == 0x12 && m_buffer[10] == 0x20) {
if ((m_buffer[16] & 0x80) == 0x80) {
CHeaderData* header = new CHeaderData;
bool ret = header->setIcomRepeaterData(m_buffer, length, true, m_icomAddress, m_icomPort);
if (!ret) {
wxLogError(wxT("Invalid header data or checksum from the RP2C"));
delete header;
continue;
}
if (m_over1)
sendMultiReply(*header);
else
sendSingleReply(*header);
m_rptrQueue.addData(new CDataQueue(header));
continue;
} else {
CAMBEData* data = new CAMBEData;
bool ret = data->setIcomRepeaterData(m_buffer, length, m_icomAddress, m_icomPort);
if (!ret) {
wxLogError(wxT("Invalid AMBE data from the RP2C"));
delete data;
continue;
}
m_rptrQueue.addData(new CDataQueue(data));
continue;
}
}
// An unknown type
CUtils::dump(wxT("Unknown packet type from the RP2C"), m_buffer, length);
}
}
void CIcomRepeaterProtocolHandler::sendGwyPackets()
{
// Anything to send
if (m_gwyQueue.isEmpty() && m_ackQueue == NULL)
return;
if (m_tries > 0U && !m_retryTimer.hasExpired())
return;
if (m_ackQueue == NULL) {
m_ackQueue = m_gwyQueue.getData();
if (m_ackQueue == NULL) {
wxLogError(wxT("getData of a non-empty gateway queue is NULL"));
return;
}
}
unsigned int length = 0U;
switch (m_ackQueue->getType()) {
case RT_HEADER: {
CHeaderData* header = m_ackQueue->getHeader();
header->setRptSeq(m_seqNo);
length = header->getIcomRepeaterData(m_buffer, 60U, true);
}
break;
case RT_AMBE: {
CAMBEData* data = m_ackQueue->getAMBE();
data->setRptSeq(m_seqNo);
length = data->getIcomRepeaterData(m_buffer, 60U);
}
break;
case RT_DD: {
CDDData* data = m_ackQueue->getDD();
data->setRptSeq(m_seqNo);
length = data->getIcomRepeaterData(m_buffer, BUFFER_LENGTH);
}
break;
default:
wxLogError(wxT("Invalid type in the gateway queue"));
break;
}
if (length > 0U) {
m_socket.write(m_buffer, length, m_icomAddress, m_icomPort);
m_tries++;
m_retryTimer.start();
if (m_tries > 0U && (m_tries % 100U) == 0U)
wxLogMessage(wxT("No reply from the RP2C after %u retries"), m_tries);
}
}
REPEATER_TYPE CIcomRepeaterProtocolHandler::read()
{
if (m_rptrQueue.isEmpty()) {
m_type = RT_NONE;
} else {
CDataQueue* dq = m_rptrQueue.peek();
if (dq == NULL) {
wxLogError(wxT("Peek of a non-empty repeater queue is NULL"));
m_type = RT_NONE;
} else {
m_type = dq->getType();
}
}
return m_type;
}
CPollData* CIcomRepeaterProtocolHandler::readPoll()
{
if (m_type != RT_POLL)
return NULL;
CDataQueue* dq = m_rptrQueue.getData();
if (dq == NULL) {
wxLogError(wxT("Missing DataQueue in readPoll"));
return NULL;
}
if (dq->getType() != RT_POLL) {
wxLogError(wxT("Wrong DataQueue type in readPoll"));
delete dq;
return NULL;
}
delete dq;
CPollData* data = new CPollData;
data->setData1(wxT("icom_rp2c"));
return data;
}
CHeaderData* CIcomRepeaterProtocolHandler::readHeader()
{
if (m_type != RT_HEADER)
return NULL;
CDataQueue* dq = m_rptrQueue.getData();
if (dq == NULL) {
wxLogError(wxT("Missing DataQueue in readHeader"));
return NULL;
}
if (dq->getType() != RT_HEADER) {
wxLogError(wxT("Wrong DataQueue type in readHeader"));
delete dq;
return NULL;
}
CHeaderData* header = dq->getHeader();
delete dq;
return header;
}
CAMBEData* CIcomRepeaterProtocolHandler::readAMBE()
{
if (m_type != RT_AMBE)
return NULL;
CDataQueue* dq = m_rptrQueue.getData();
if (dq == NULL) {
wxLogError(wxT("Missing DataQueue in readData"));
return NULL;
}
if (dq->getType() != RT_AMBE) {
wxLogError(wxT("Wrong DataQueue type in readData"));
delete dq;
return NULL;
}
CAMBEData* data = dq->getAMBE();
delete dq;
return data;
}
CHeardData* CIcomRepeaterProtocolHandler::readHeard()
{
if (m_type != RT_HEARD)
return NULL;
CDataQueue* dq = m_rptrQueue.getData();
if (dq == NULL) {
wxLogError(wxT("Missing DataQueue in readHeard"));
return NULL;
}
if (dq->getType() != RT_HEARD) {
wxLogError(wxT("Wrong DataQueue type in readHeard"));
delete dq;
return NULL;
}
CHeardData* heard = dq->getHeard();
delete dq;
return heard;
}
CDDData* CIcomRepeaterProtocolHandler::readDD()
{
if (m_type != RT_DD)
return NULL;
CDataQueue* dq = m_rptrQueue.getData();
if (dq == NULL) {
wxLogError(wxT("Missing DataQueue in readData"));
return NULL;
}
if (dq->getType() != RT_DD) {
wxLogError(wxT("Wrong DataQueue type in readData"));
delete dq;
return NULL;
}
CDDData* data = dq->getDD();
delete dq;
return data;
}
CHeaderData* CIcomRepeaterProtocolHandler::readBusyHeader()
{
return NULL;
}
CAMBEData* CIcomRepeaterProtocolHandler::readBusyAMBE()
{
return NULL;
}
void CIcomRepeaterProtocolHandler::close()
{
m_killed = true;
Wait();
}
bool CIcomRepeaterProtocolHandler::sendAck(wxUint16 seqNo)
{
unsigned char buffer[10U];
buffer[0U] = 'D';
buffer[1U] = 'S';
buffer[2U] = 'T';
buffer[3U] = 'R';
buffer[4U] = seqNo / 256U;
buffer[5U] = seqNo % 256U;
buffer[6U] = 0x72U;
buffer[7U] = 0x00U;
buffer[8U] = 0x00U;
buffer[9U] = 0x00U;
return m_socket.write(buffer, 10U, m_icomAddress, m_icomPort);
}
void CIcomRepeaterProtocolHandler::sendSingleReply(const CHeaderData& header)
{
unsigned int id = ::rand() % 0xFFFF;
CHeaderData replyHdr;
replyHdr.setId(id);
replyHdr.setBand1(0xFFU);
replyHdr.setBand2(0xFFU);
replyHdr.setBand3(0xFFU);
replyHdr.setFlag1(0x01U);
replyHdr.setMyCall1(header.getRptCall2());
replyHdr.setMyCall2(wxT(" "));
replyHdr.setYourCall(header.getMyCall1());
replyHdr.setRptCall1(header.getRptCall2());
replyHdr.setRptCall2(header.getRptCall1());
writeHeader(replyHdr);
unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES];
::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES);
CAMBEData replyData;
replyData.setId(id);
replyData.setBand1(0xFFU);
replyData.setBand2(0xFFU);
replyData.setBand3(0xFFU);
replyData.setSeq(0x40U); // Seq = 0 and end-of-data
replyData.setData(buffer, DV_FRAME_MAX_LENGTH_BYTES);
writeAMBE(replyData);
}
void CIcomRepeaterProtocolHandler::sendMultiReply(const CHeaderData& header)
{
unsigned int id = ::rand() % 0xFFFF;
CHeaderData replyHdr;
replyHdr.setId(id);
replyHdr.setBand1(0xFFU);
replyHdr.setBand2(0xFFU);
replyHdr.setBand3(0xFFU);
replyHdr.setFlag1(0x41U);
replyHdr.setMyCall1(header.getRptCall2());
replyHdr.setMyCall2(wxT(" "));
replyHdr.setYourCall(header.getMyCall1());
replyHdr.setRptCall1(header.getRptCall2());
replyHdr.setRptCall2(header.getRptCall1());
writeHeader(replyHdr);
CAMBEData replyData;
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
replyData.setId(id);
replyData.setBand1(0xFFU);
replyData.setBand2(0xFFU);
replyData.setBand3(0xFFU);
::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES + 0U, 3U);
replyData.setSeq(0x00U); // Seq = 0
replyData.setData(buffer, DV_FRAME_LENGTH_BYTES);
writeAMBE(replyData);
::memset(buffer + 0U, 0x00, DV_FRAME_LENGTH_BYTES);
::memcpy(buffer + 0U, END_PATTERN_BYTES + 3U, 3U);
replyData.setSeq(0x41U); // Seq = 1 and end-of-data
replyData.setData(buffer, DV_FRAME_LENGTH_BYTES);
writeAMBE(replyData);
}
void CIcomRepeaterProtocolHandler::free(CDataQueue* dataQueue)
{
if (dataQueue == NULL)
return;
switch (dataQueue->getType()) {
case RT_HEADER:
delete dataQueue->getHeader();
break;
case RT_HEARD:
delete dataQueue->getHeard();
break;
case RT_AMBE:
delete dataQueue->getAMBE();
break;
case RT_DD:
delete dataQueue->getDD();
break;
default:
break;
}
delete dataQueue;
}

Some files were not shown because too many files have changed in this diff Show more