mirror of
https://github.com/g4klx/ircDDBGateway.git
synced 2025-12-06 05:32:02 +01:00
Initial commit.
This commit is contained in:
commit
12d55cef37
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
*.o
|
||||
*.obj
|
||||
x86
|
||||
Debug
|
||||
Release
|
||||
*.user
|
||||
*~
|
||||
*.bak
|
||||
.vs
|
||||
|
||||
601
APRSTransmit/APRSParser.cpp
Normal file
601
APRSTransmit/APRSParser.cpp
Normal 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
79
APRSTransmit/APRSParser.h
Normal 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
|
||||
174
APRSTransmit/APRSTransmit.cpp
Normal file
174
APRSTransmit/APRSTransmit.cpp
Normal 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;
|
||||
}
|
||||
44
APRSTransmit/APRSTransmit.h
Normal file
44
APRSTransmit/APRSTransmit.h
Normal 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
|
||||
188
APRSTransmit/APRSTransmit.vcxproj
Normal file
188
APRSTransmit/APRSTransmit.vcxproj
Normal 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>
|
||||
32
APRSTransmit/APRSTransmit.vcxproj.filters
Normal file
32
APRSTransmit/APRSTransmit.vcxproj.filters
Normal 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>
|
||||
100
APRSTransmit/APRSTransmitApp.cpp
Normal file
100
APRSTransmit/APRSTransmitApp.cpp
Normal 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;
|
||||
}
|
||||
|
||||
272
APRSTransmit/APRSTransmitAppD.cpp
Normal file
272
APRSTransmit/APRSTransmitAppD.cpp
Normal 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;
|
||||
}
|
||||
|
||||
54
APRSTransmit/APRSTransmitAppD.h
Normal file
54
APRSTransmit/APRSTransmitAppD.h
Normal 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
|
||||
189
APRSTransmit/APRSTransmitD.vcxproj
Normal file
189
APRSTransmit/APRSTransmitD.vcxproj
Normal 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>
|
||||
35
APRSTransmit/APRSTransmitD.vcxproj.filters
Normal file
35
APRSTransmit/APRSTransmitD.vcxproj.filters
Normal 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
1478
CHANGES.txt
Normal file
File diff suppressed because it is too large
Load diff
340
COPYING.txt
Normal file
340
COPYING.txt
Normal 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
629
Common/AMBEData.cpp
Normal 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
108
Common/AMBEData.h
Normal 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
661
Common/APRSCollector.cpp
Normal 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
84
Common/APRSCollector.h
Normal 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
394
Common/APRSWriter.cpp
Normal 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
98
Common/APRSWriter.h
Normal 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
263
Common/APRSWriterThread.cpp
Normal 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
61
Common/APRSWriterThread.h
Normal 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
151
Common/AnnouncementUnit.cpp
Normal 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
60
Common/AnnouncementUnit.h
Normal 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
505
Common/AudioUnit.cpp
Normal 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
120
Common/AudioUnit.h
Normal 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
142
Common/CCITTChecksum.cpp
Normal 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
39
Common/CCITTChecksum.h
Normal 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
44
Common/CCSCallback.h
Normal 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
192
Common/CCSData.cpp
Normal 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
74
Common/CCSData.h
Normal 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
700
Common/CCSHandler.cpp
Normal 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
155
Common/CCSHandler.h
Normal 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
|
||||
235
Common/CCSProtocolHandler.cpp
Normal file
235
Common/CCSProtocolHandler.cpp
Normal 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();
|
||||
}
|
||||
81
Common/CCSProtocolHandler.h
Normal file
81
Common/CCSProtocolHandler.h
Normal 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
128
Common/CacheManager.cpp
Normal 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
158
Common/CacheManager.h
Normal 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
76
Common/CallsignList.cpp
Normal 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
40
Common/CallsignList.h
Normal 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
173
Common/CallsignServer.cpp
Normal 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
54
Common/CallsignServer.h
Normal 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
295
Common/Common.vcxproj
Normal 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>
|
||||
437
Common/Common.vcxproj.filters
Normal file
437
Common/Common.vcxproj.filters
Normal 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
543
Common/ConnectData.cpp
Normal 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
81
Common/ConnectData.h
Normal 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
884
Common/DCSHandler.cpp
Normal 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
140
Common/DCSHandler.h
Normal 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
|
||||
196
Common/DCSProtocolHandler.cpp
Normal file
196
Common/DCSProtocolHandler.cpp
Normal 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();
|
||||
}
|
||||
75
Common/DCSProtocolHandler.h
Normal file
75
Common/DCSProtocolHandler.h
Normal 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
|
||||
132
Common/DCSProtocolHandlerPool.cpp
Normal file
132
Common/DCSProtocolHandlerPool.cpp
Normal 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();
|
||||
}
|
||||
56
Common/DCSProtocolHandlerPool.h
Normal file
56
Common/DCSProtocolHandlerPool.h
Normal 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
313
Common/DDData.cpp
Normal 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
89
Common/DDData.h
Normal 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
394
Common/DDHandler.cpp
Normal 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
73
Common/DDHandler.h
Normal 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
1004
Common/DExtraHandler.cpp
Normal file
File diff suppressed because it is too large
Load diff
138
Common/DExtraHandler.h
Normal file
138
Common/DExtraHandler.h
Normal 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
|
||||
228
Common/DExtraProtocolHandler.cpp
Normal file
228
Common/DExtraProtocolHandler.cpp
Normal 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();
|
||||
}
|
||||
79
Common/DExtraProtocolHandler.h
Normal file
79
Common/DExtraProtocolHandler.h
Normal 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
|
||||
137
Common/DExtraProtocolHandlerPool.cpp
Normal file
137
Common/DExtraProtocolHandlerPool.cpp
Normal 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();
|
||||
}
|
||||
57
Common/DExtraProtocolHandlerPool.h
Normal file
57
Common/DExtraProtocolHandlerPool.h
Normal 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
|
||||
286
Common/DPlusAuthenticator.cpp
Normal file
286
Common/DPlusAuthenticator.cpp
Normal 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;
|
||||
}
|
||||
59
Common/DPlusAuthenticator.h
Normal file
59
Common/DPlusAuthenticator.h
Normal 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
953
Common/DPlusHandler.cpp
Normal 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
147
Common/DPlusHandler.h
Normal 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
|
||||
233
Common/DPlusProtocolHandler.cpp
Normal file
233
Common/DPlusProtocolHandler.cpp
Normal 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();
|
||||
}
|
||||
79
Common/DPlusProtocolHandler.h
Normal file
79
Common/DPlusProtocolHandler.h
Normal 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
|
||||
137
Common/DPlusProtocolHandlerPool.cpp
Normal file
137
Common/DPlusProtocolHandlerPool.cpp
Normal 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();
|
||||
}
|
||||
57
Common/DPlusProtocolHandlerPool.h
Normal file
57
Common/DPlusProtocolHandlerPool.h
Normal 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
375
Common/DRATSServer.cpp
Normal 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
65
Common/DRATSServer.h
Normal 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
177
Common/DStarDefines.h
Normal 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
317
Common/DTMF.cpp
Normal 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
49
Common/DTMF.h
Normal 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
181
Common/DVTOOLFileReader.cpp
Normal 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
61
Common/DVTOOLFileReader.h
Normal 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
143
Common/Defs.h
Normal 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
|
||||
133
Common/DummyRepeaterProtocolHandler.cpp
Normal file
133
Common/DummyRepeaterProtocolHandler.cpp
Normal 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()
|
||||
{
|
||||
}
|
||||
67
Common/DummyRepeaterProtocolHandler.h
Normal file
67
Common/DummyRepeaterProtocolHandler.h
Normal 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
154
Common/EchoUnit.cpp
Normal 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
63
Common/EchoUnit.h
Normal 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
198
Common/G2Handler.cpp
Normal 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
72
Common/G2Handler.h
Normal 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
|
||||
150
Common/G2ProtocolHandler.cpp
Normal file
150
Common/G2ProtocolHandler.cpp
Normal 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();
|
||||
}
|
||||
68
Common/G2ProtocolHandler.h
Normal file
68
Common/G2ProtocolHandler.h
Normal 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
57
Common/GatewayCache.cpp
Normal 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
106
Common/GatewayCache.h
Normal 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
|
||||
285
Common/HBRepeaterProtocolHandler.cpp
Normal file
285
Common/HBRepeaterProtocolHandler.cpp
Normal 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();
|
||||
}
|
||||
76
Common/HBRepeaterProtocolHandler.h
Normal file
76
Common/HBRepeaterProtocolHandler.h
Normal 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
951
Common/HeaderData.cpp
Normal 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
130
Common/HeaderData.h
Normal 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
103
Common/HeaderLogger.cpp
Normal 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
46
Common/HeaderLogger.h
Normal 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
134
Common/HeardData.cpp
Normal 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
61
Common/HeardData.h
Normal 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
107
Common/HostFile.cpp
Normal 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
45
Common/HostFile.h
Normal 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
|
||||
2651
Common/IRCDDBGatewayConfig.cpp
Normal file
2651
Common/IRCDDBGatewayConfig.cpp
Normal file
File diff suppressed because it is too large
Load diff
311
Common/IRCDDBGatewayConfig.h
Normal file
311
Common/IRCDDBGatewayConfig.h
Normal 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
|
||||
650
Common/IcomRepeaterProtocolHandler.cpp
Normal file
650
Common/IcomRepeaterProtocolHandler.cpp
Normal 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
Loading…
Reference in a new issue