Initial commit.

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

601
APRSTransmit/APRSParser.cpp Normal file
View file

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

79
APRSTransmit/APRSParser.h Normal file
View file

@ -0,0 +1,79 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef APRSParserAppD_H
#define APRSParserAppD_H
#include <wx/string.h>
enum APRSPacketType {
APT_Unknown,
APT_Position,
APT_WX,
APT_Object,
APT_Item,
APT_Message,
APT_NMEA
};
class CAPRSPacket{
public :
CAPRSPacket();
CAPRSPacket(APRSPacketType type, float latitude, float longitude, const wxString& infoText, const wxString raw);
float& Longitude();
float& Latitude();
wxString& SourceCall();
wxChar& Symbol();
wxChar& SymbolTable();
wxChar& TypeChar();
wxString& DestinationCall();
wxString& Body();
wxString& Raw();
APRSPacketType& Type();
private:
APRSPacketType m_type;
float m_latitude;
float m_longitude;
wxChar m_symbol;
wxChar m_symbolTable;
wxChar m_typeChar;
wxString m_body;
wxString m_raw;//raw string in TNC2 format, including '\r'
wxString m_destCall;
wxString m_srcCall;
};
class CAPRSParser{
public:
static bool Parse(const wxString& aprsString, CAPRSPacket& packet);
private :
static bool valid_sym_table_compressed(wxChar c);
static bool valid_sym_table_uncompressed(wxChar c);
static bool parse_aprs_mice(CAPRSPacket& packet);
static bool ensureIsIcomCompatible(CAPRSPacket& packet);
static bool stripFrame(wxString& aprsFrame);
static bool preprocessFrame(CAPRSPacket& packet, const wxString& aprsFrame);
static void convertToIcomCompatible(CAPRSPacket& packet);
};
#endif

View file

@ -0,0 +1,174 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "SlowDataEncoder.h"
#include "DStarDefines.h"
#include "APRSTransmit.h"
#include "APRSParser.h"
CAPRSTransmit::CAPRSTransmit(const wxString& callsign, const wxString& text) :
m_socket(wxEmptyString, 0U),
m_repeaterCallsign(callsign),
m_APRSCallsign(callsign),
m_text(text)
{
int index = m_text.Find(wxT(">"));
if(index != wxNOT_FOUND)
m_APRSCallsign = m_text.Left(index);
}
CAPRSTransmit::~CAPRSTransmit()
{
}
bool CAPRSTransmit::run()
{
//First see if the packet is Icom supported...
CAPRSPacket aprsPacket;
if(!CAPRSParser::Parse(m_text, aprsPacket)){
wxLogWarning(wxT("Unsupported APRS Format, ignoring => ") + m_text.Trim(true));
return false;
}
wxString textWithCRC(aprsPacket.Raw());
wxLogMessage(wxT("Supported APRS Format => ") + textWithCRC.Trim(true));
//add nececessary stuff to text, but keep it the original
textWithCRC.Replace(wxT("\n"), wxEmptyString);
if(!textWithCRC.EndsWith(wxT("\r"))) textWithCRC.Append(wxT("\r"));
wxString crc = wxString::Format(wxT("$$CRC%04X,"), calcCRC(textWithCRC));
textWithCRC.Prepend(crc);
bool opened = m_socket.open();
if (!opened)
return false;
in_addr address = CUDPReaderWriter::lookup(wxT("127.0.0.1"));
unsigned int id = CHeaderData::createId();
wxString callsignG = m_repeaterCallsign.Left(LONG_CALLSIGN_LENGTH - 1U);
callsignG.Append(wxT("G"));
CHeaderData header;
header.setId(id);
header.setMyCall1(m_APRSCallsign);
header.setMyCall2(wxT("APRS"));
header.setRptCall1(callsignG);
header.setRptCall2(m_repeaterCallsign);
header.setYourCall(wxT("CQCQCQ "));
header.setDestination(address, G2_DV_PORT);
sendHeader(header);
CSlowDataEncoder encoder;
encoder.setHeaderData(header);
encoder.setGPSData(textWithCRC);
encoder.setTextData(wxT("APRS to DPRS"));
CAMBEData data;
data.setDestination(address, G2_DV_PORT);
data.setId(id);
wxStopWatch timer;
timer.Start();
unsigned int out = 0U;
unsigned int dataOut = 0U;
unsigned int needed = (encoder.getInterleavedDataLength() / (DATA_FRAME_LENGTH_BYTES)) * 2U;
while (dataOut < needed) {
data.setSeq(out);
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
// Insert sync bytes when the sequence number is zero, slow data otherwise
if (out == 0U) {
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
} else {
encoder.getInterleavedData(buffer + VOICE_FRAME_LENGTH_BYTES);
dataOut++;
}
data.setData(buffer, DV_FRAME_LENGTH_BYTES);
sendData(data);
out++;
if (out == 21U) out = 0U;
}
data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES);
data.setSeq(out >= 21U ? 0U : out);
data.setEnd(true);
sendData(data);
m_socket.close();
return true;
}
bool CAPRSTransmit::sendHeader(const CHeaderData& header)
{
unsigned char buffer[60U];
unsigned int length = header.getG2Data(buffer, 60U, true);
for (unsigned int i = 0U; i < 2U; i++) {
bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort());
if (!res)
return false;
}
return true;
}
bool CAPRSTransmit::sendData(const CAMBEData& data)
{
unsigned char buffer[60U];
unsigned int length = data.getG2Data(buffer, 60U);
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
unsigned int CAPRSTransmit::calcCRC(const wxString& gpsData)
{
size_t length = gpsData.length();
wxASSERT(length > 0U);
unsigned int icomcrc = 0xFFFFU;
for (unsigned int j = 0U; j < length; j++) {
unsigned char ch = gpsData.GetChar(j);
for (unsigned int i = 0U; i < 8U; i++) {
bool xorflag = (((icomcrc ^ ch) & 0x01U) == 0x01U);
icomcrc >>= 1;
if (xorflag)
icomcrc ^= 0x8408U;
ch >>= 1;
}
}
return ~icomcrc & 0xFFFFU;
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef TextTransmit_H
#define TextTransmit_H
#include "UDPReaderWriter.h"
#include "HeaderData.h"
#include "AMBEData.h"
class CAPRSTransmit {
public:
CAPRSTransmit(const wxString& callsign, const wxString& text);
~CAPRSTransmit();
bool run();
private:
CUDPReaderWriter m_socket;
wxString m_repeaterCallsign;
wxString m_APRSCallsign;
wxString m_text;
bool sendHeader(const CHeaderData& header);
bool sendData(const CAMBEData& data);
unsigned int calcCRC(const wxString& gpsData);
};
#endif

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef APRSTransmitAppD_H
#define APRSTransmitAppD_H
#include "RingBuffer.h"
#include "APRSWriterThread.h"
#include <wx/wx.h>
#include <wx/config.h>
#include <wx/snglinst.h>
class CAPRSTransmitAppD {
public:
CAPRSTransmitAppD(const wxString& repeater, const wxString& aprsHost, unsigned int aprsPort, const wxString& aprsFilter, bool daemon);
~CAPRSTransmitAppD();
CRingBuffer<wxString*> * m_aprsFramesQueue;
bool init();
void run();
void kill();
private:
wxString m_repeater, m_aprsHost, m_aprsFilter;
unsigned int m_aprsPort;
CAPRSWriterThread * m_aprsThread;
bool m_run;
wxSingleInstanceChecker * m_checker;
bool m_daemon;
void cleanup();
};
#endif

View file

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

View file

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