APRStt enhancements including new 5 digit suffix format.

This commit is contained in:
WB2OSZ 2015-12-24 15:25:13 -05:00
parent d653a534c4
commit d6bf8102bf
19 changed files with 510 additions and 140 deletions

View File

@ -9,6 +9,9 @@
- New experimental demodulator. More details later.
- APRStt enhancements including new 5 digit callsign suffix abbreviation and
position ambiguity for latitude and longitude in object reports.
### Bugs Fixed: ###
- "INTERNAL ERROR: dlq_append NULL packet pointer." when using PASSALL.

View File

@ -127,7 +127,7 @@ tocalls-symbols :
# Separate application to decode raw data.
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o regex.a misc.a geotranz.a
decode_aprs : decode_aprs.c dwgpsnmea.o dwgps.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.c regex.a misc.a geotranz.a
$(CC) $(CFLAGS) -DDECAMAIN -o decode_aprs $^

View File

@ -1,7 +1,7 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2013 John Langner, WB2OSZ
// Copyright (C) 2013, 2015 John Langner, WB2OSZ
//
// 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
@ -28,11 +28,20 @@
* Description: Establish connection with multiple servers and
* compare results side by side.
*
* Usage: aclients 8000=AGWPE 8002=DireWolf COM1=D710A
* Usage: aclients port1=name1 port2=name2 ...
*
* Example: aclients 8000=AGWPE 192.168.1.64:8002=DireWolf COM1=D710A
*
* This will connect to multiple physical or virtual
* TNCs, read packets from them, and display results.
*
* Each port can have the following forms:
*
* * host-name:tcp-port
* * ip-addr:tcp-port
* * tcp-port
* * serial port name (e.g. COM1, /dev/ttyS0)
*
*---------------------------------------------------------------*/
@ -40,7 +49,6 @@
/*
* Native Windows: Use the Winsock interface.
* Linux: Use the BSD socket interface.
* Cygwin: Can use either one.
*/
@ -156,14 +164,7 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
* Purpose: Start up multiple client threads listening to different
* TNCs. Print packets. Tally up statistics.
*
* Usage: aclients 8000=AGWPE 8002=DireWolf COM1=D710A
*
* Each command line argument is TCP port number or a
* serial port name. Follow by = and a text description
* of what is connected.
*
* For now, everything is assumed to be on localhost.
* Maybe someday we might recognize host:port=description.
* Usage: Described above.
*
*---------------------------------------------------------------*/
@ -173,9 +174,17 @@ static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t S
static int num_clients;
static char hostname[MAX_CLIENTS][50];
static char port[MAX_CLIENTS][30];
static char description[MAX_CLIENTS][50];
static char hostname[MAX_CLIENTS][50]; /* DNS host name or IPv4 address. */
/* Some of the code is there for IPv6 but */
/* needs more work. */
/* Defaults to "localhost" if not specified. */
static char port[MAX_CLIENTS][30]; /* If it begins with a digit, it is considered */
/* a TCP port number at the hostname. */
/* Otherwise, we treat it as a serial port name. */
static char description[MAX_CLIENTS][50]; /* Name used in the output. */
#if __WIN32__
static HANDLE client_th[MAX_CLIENTS];
@ -223,6 +232,8 @@ int main (int argc, char *argv[])
char stemp[100];
char *p;
/* Each command line argument should be of the form "port=description." */
strlcpy (stemp, argv[j+1], sizeof(stemp));
p = strtok (stemp, "=");
if (p == NULL) {
@ -237,6 +248,22 @@ int main (int argc, char *argv[])
exit (1);
}
strlcpy (description[j], p, sizeof(description[j]));
/* If the port contains ":" split it into hostname (or addr) and port number. */
/* Haven't thought about IPv6 yet. */
strlcpy (stemp, port[j], sizeof(stemp));
char *h;
h = strtok (stemp, ":");
if (h != NULL) {
p = strtok (NULL, ":");
if (p != NULL) {
strlcpy (hostname[j], h, sizeof(hostname[j]));
strlcpy (port[j], p, sizeof(port[j]));
}
}
}
//printf ("_WIN32_WINNT = %04x\n", _WIN32_WINNT);
@ -253,6 +280,10 @@ int main (int argc, char *argv[])
for (j=0; j<num_clients; j++) {
/* If port begins with digit, consider it to be TCP. */
/* Otherwise, treat as serial port name. */
#if __WIN32__
if (isdigit(port[j][0])) {
client_th[j] = (HANDLE)_beginthreadex (NULL, 0, client_thread_net, (void *)j, 0, NULL);

227
aprs_tt.c
View File

@ -43,7 +43,7 @@
// TODO: clean up terminolgy.
// "Message" has a specific meaning in APRS and this is not it.
// Touch Tone sequence should be appropriate.
// What do we call the parts separated by * key? Entry? Field?
// What do we call the parts separated by * key? Field.
#include <stdlib.h>
@ -101,6 +101,7 @@ static int parse_fields (char *msg);
static int parse_callsign (char *e);
static int parse_object_name (char *e);
static int parse_symbol (char *e);
static int parse_aprstt3_call (char *e);
static int parse_location (char *e);
static int parse_comment (char *e);
static int expand_macro (char *e);
@ -324,6 +325,7 @@ static double m_latitude; // Set to G_UNKNOWN if not defined.
static int m_ambiguity;
static char m_comment[200];
static char m_freq[12];
static char m_ctcss[8];
static char m_mic_e;
static char m_dao[6];
static int m_ssid; // Default 12 for APRStt user.
@ -351,14 +353,15 @@ void aprs_tt_sequence (int chan, char *msg)
* The parse functions will fill these in.
*/
strlcpy (m_callsign, "", sizeof(m_callsign));
m_symtab_or_overlay = '\\';
m_symbol_code = 'A';
m_symtab_or_overlay = APRSTT_DEFAULT_SYMTAB;
m_symbol_code = APRSTT_DEFAULT_SYMBOL;
strlcpy (m_loc_text, "", sizeof(m_loc_text));
m_longitude = G_UNKNOWN;
m_latitude = G_UNKNOWN;
m_ambiguity = 0;
strlcpy (m_comment, "", sizeof(m_comment));
strlcpy (m_freq, "", sizeof(m_freq));
strlcpy (m_ctcss, "", sizeof(m_ctcss));
m_mic_e = ' ';
strlcpy (m_dao, "!T !", sizeof(m_dao)); /* start out unknown */
m_ssid = 12;
@ -370,8 +373,8 @@ void aprs_tt_sequence (int chan, char *msg)
#if defined(DEBUG)
text_color_set(DW_COLOR_DEBUG);
dw_printf ("callsign=\"%s\", ssid=%d, symbol=\"%c%c\", freq=\"%s\", comment=\"%s\", lat=%.4f, lon=%.4f, dao=\"%s\"\n",
m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_comment, m_latitude, m_longitude, m_dao);
dw_printf ("callsign=\"%s\", ssid=%d, symbol=\"%c%c\", freq=\"%s\", ctcss=\"%s\", comment=\"%s\", lat=%.4f, lon=%.4f, dao=\"%s\"\n",
m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code, m_freq, m_ctcss, m_comment, m_latitude, m_longitude, m_dao);
#endif
#if TT_MAIN
@ -386,20 +389,22 @@ void aprs_tt_sequence (int chan, char *msg)
err = tt_user_heard (m_callsign, m_ssid, m_symtab_or_overlay, m_symbol_code,
m_loc_text, m_latitude, m_longitude, m_ambiguity,
m_freq, m_comment, m_mic_e, m_dao);
m_freq, m_ctcss, m_comment, m_mic_e, m_dao);
}
/*
* If a command / script was supplied, run it now.
* This can do additional processing and provide a custom audible response.
* This is done even for the error case.
* This is done only for the success case.
* It might be useful to run it for error cases as well but we currently
* don't pass in the success / failure code to know the difference.
*/
char script_response[1000];
strlcpy (script_response, "", sizeof(script_response));
if (strlen(tt_config.ttcmd) > 0) {
if (err == 0 && strlen(tt_config.ttcmd) > 0) {
dw_run_cmd (tt_config.ttcmd, 1, script_response, sizeof(script_response));
@ -409,6 +414,8 @@ void aprs_tt_sequence (int chan, char *msg)
* Send response to user by constructing packet with SPEECH or MORSE as destination.
* Source shouldn't matter because it doesn't get transmitted as AX.25 frame.
* Use high priority queue for consistent timing.
*
* Anything from script, above, will override other predefined responses.
*/
char audible_response[1000];
@ -484,25 +491,26 @@ static int parse_fields (char *msg)
case 'A':
switch (e[1]) {
case 'A':
case 'A': /* AA object-name */
err = parse_object_name (e);
if (err != 0) return (err);
break;
case 'B':
case 'B': /* AB symbol */
err = parse_symbol (e);
if (err != 0) return (err);
break;
case 'C':
/*
* New in 1.2: test for 10 digit callsign.
*/
if (tt_call10_to_text(e+2,1,stemp) == 0) {
strlcpy(m_callsign, stemp, sizeof(m_callsign));
}
// TODO1.3: else return (?)
case 'C': /* AC new-style-callsign */
err = parse_aprstt3_call (e);
if (err != 0) return (err);
break;
default:
default: /* Traditional style call or suffix */
err = parse_callsign (e);
if (err != 0) return (err);
break;
}
break;
@ -674,7 +682,7 @@ static int expand_macro (char *e)
*
* Name: parse_callsign
*
* Purpose: Extract callsign or object name from touch tone message.
* Purpose: Extract traditional format callsign or object name from touch tone sequence.
*
* Inputs: e - An "entry" extracted from a complete
* APRStt messsage.
@ -685,6 +693,10 @@ static int expand_macro (char *e)
* m_symtab_or_overlay - Set to 0-9 or A-Z if specified.
*
* m_symbol_code - Always set to 'A'.
* NO! This should be applied only if we
* have the default value at this point.
* The symbol might have been explicitly
* set already and we don't want to overwrite that.
*
* Returns: 0 for success or one of the TT_ERROR_... codes.
*
@ -734,7 +746,6 @@ static int checksum_not_ok (char *str, int len, char found)
static int parse_callsign (char *e)
{
int len;
//int c_length;
char tttemp[40], stemp[30];
assert (*e == 'A');
@ -772,11 +783,11 @@ static int parse_callsign (char *e)
tttemp[1] = e[len-2];
tttemp[2] = '\0';
tt_two_key_to_text (tttemp, 0, stemp);
m_symbol_code = 'A';
m_symbol_code = APRSTT_DEFAULT_SYMBOL;
m_symtab_or_overlay = stemp[0];
}
else {
m_symbol_code = 'A';
m_symbol_code = APRSTT_DEFAULT_SYMBOL;
m_symtab_or_overlay = e[len-2];
}
return (0);
@ -794,7 +805,6 @@ static int parse_callsign (char *e)
return (cs_err);
}
if (isupper(e[len-2])) {
strncpy (tttemp, e+1, len-4);
tttemp[len-4] = '\0';
@ -804,7 +814,7 @@ static int parse_callsign (char *e)
tttemp[1] = e[len-2];
tttemp[2] = '\0';
tt_two_key_to_text (tttemp, 0, stemp);
m_symbol_code = 'A';
m_symbol_code = APRSTT_DEFAULT_SYMBOL;
m_symtab_or_overlay = stemp[0];
}
else {
@ -812,7 +822,7 @@ static int parse_callsign (char *e)
tttemp[len-3] = '\0';
tt_two_key_to_text (tttemp, 0, m_callsign);
m_symbol_code = 'A';
m_symbol_code = APRSTT_DEFAULT_SYMBOL;
m_symtab_or_overlay = e[len-2];
}
return (0);
@ -823,11 +833,12 @@ static int parse_callsign (char *e)
return (TT_ERROR_INVALID_CALL);
}
/*------------------------------------------------------------------
*
* Name: parse_object_name
*
* Purpose: Extract object name from touch tone message.
* Purpose: Extract object name from touch tone sequence.
*
* Inputs: e - An "entry" extracted from a complete
* APRStt messsage.
@ -883,7 +894,7 @@ static int parse_object_name (char *e)
*
* Name: parse_symbol
*
* Purpose: Extract symbol from touch tone message.
* Purpose: Extract symbol from touch tone sequence.
*
* Inputs: e - An "entry" extracted from a complete
* APRStt messsage.
@ -973,11 +984,89 @@ static int parse_symbol (char *e)
} /* end parse_oject_name */
/*------------------------------------------------------------------
*
* Name: parse_aprstt3_call
*
* Purpose: Extract QIKcom-2 / APRStt 3 ten digit call or five digit suffix.
*
* Inputs: e - An "entry" extracted from a complete
* APRStt messsage.
* In this case, it should start with "AC".
*
* Outputs: m_callsign
*
* Returns: 0 for success or one of the TT_ERROR_... codes.
*
* Description: We recognize 3 different formats:
*
* ACxxxxxxxxxx - 10 digit full callsign.
*
* ACxxxxx - 5 digit suffix. If we can find a corresponding full
* callsign, that will be substituted.
* Error condition is returned if we can't find one.
*
*----------------------------------------------------------------*/
static int parse_aprstt3_call (char *e)
{
assert (e[0] == 'A');
assert (e[1] == 'C');
if (strlen(e) == 2+10) {
char call[12];
if (tt_call10_to_text(e+2,1,call) == 0) {
strlcpy(m_callsign, call, sizeof(m_callsign));
}
else {
return (TT_ERROR_INVALID_CALL); /* Could not convert to text */
}
}
else if (strlen(e) == 2+5) {
char suffix[8];
if (tt_call5_suffix_to_text(e+2,1,suffix) == 0) {
#if TT_MAIN
/* For unit test, use suffix rather than trying lookup. */
strlcpy (m_callsign, suffix, sizeof(m_callsign));
#else
char call[12];
/* In normal operation, try to find full callsign for the suffix received. */
if (tt_3char_suffix_search (suffix, call) >= 0) {
text_color_set(DW_COLOR_INFO);
dw_printf ("Suffix \"%s\" was converted to full callsign \"%s\"\n", suffix, call);
strlcpy(m_callsign, call, sizeof(m_callsign));
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Couldn't find full callsign for suffix \"%s\"\n", suffix);
return (TT_ERROR_SUFFIX_NO_CALL); /* Don't know this user. */
}
#endif
}
else {
return (TT_ERROR_INVALID_CALL); /* Could not convert to text */
}
}
else {
return (TT_ERROR_INVALID_CALL); /* Invalid length, not 2+ (10 ir 5) */
}
return (0);
} /* end parse_aprstt3_call */
/*------------------------------------------------------------------
*
* Name: parse_location
*
* Purpose: Extract location from touch tone message.
* Purpose: Extract location from touch tone sequence.
*
* Inputs: e - An "entry" extracted from a complete
* APRStt messsage.
@ -985,7 +1074,16 @@ static int parse_symbol (char *e)
*
* Outputs: m_latitude
* m_longitude
* m_dao
*
* m_dao It should previously be "!T !" to mean unknown or none.
* We generally take the first two tones of the field.
* For example, "!TB5!" for the standard bearing & range.
* The point type is an exception where we use "!Tn !" for
* one of ten positions or "!Tnn" for one of a hundred.
* If this ever changes, be sure to update corresponding
* section in process_comment() in decode_aprs.c
*
* m_ambiguity
*
* Returns: 0 for success or one of the TT_ERROR_... codes.
*
@ -1001,6 +1099,10 @@ static int parse_symbol (char *e)
* * utm
* * usng / mgrs
*
* Position ambiguity is also handled here.
* Latitude, Longitude, and DAO should not be touched in this case.
* We only record a position ambiguity value.
*
*----------------------------------------------------------------*/
/* Average radius of earth in meters. */
@ -1009,7 +1111,6 @@ static int parse_symbol (char *e)
static int parse_location (char *e)
{
//int len;
int ipat;
char xstr[VALSTRSIZE], ystr[VALSTRSIZE], zstr[VALSTRSIZE], bstr[VALSTRSIZE], dstr[VALSTRSIZE];
double x, y, dist, bearing;
@ -1023,14 +1124,6 @@ static int parse_location (char *e)
assert (*e == 'B');
m_dao[2] = e[0];
m_dao[3] = e[1]; /* Type of location. e.g. !TB6! */
/* Will be changed by point types. */
/* If this ever changes, be sure to update corresponding */
/* section in process_comment() in decode_aprs.c */
//len = strlen(e);
ipat = find_ttloc_match (e, xstr, ystr, zstr, bstr, dstr, VALSTRSIZE);
if (ipat >= 0) {
@ -1047,6 +1140,9 @@ static int parse_location (char *e)
/* It's not hardwired to always be B0n or B9nn. */
/* This is a pretty good approximation. */
m_dao[2] = e[0];
m_dao[3] = e[1];
if (strlen(e) == 3) { /* probably B0n --> !Tn ! */
m_dao[2] = e[2];
m_dao[3] = ' ';
@ -1083,6 +1179,10 @@ static int parse_location (char *e)
m_longitude = R2D(lon0 + atan2(sin(bearing) * sin(dist/R) * cos(lat0),
cos(dist/R) - sin(lat0) * sin(D2R(m_latitude))));
m_dao[2] = e[0];
m_dao[3] = e[1];
break;
case TTLOC_GRID:
@ -1108,6 +1208,9 @@ static int parse_location (char *e)
x = atof(xstr);
m_longitude = lon0 + x * (lon9-lon0) / (pow(10., strlen(xstr)) - 1.);
m_dao[2] = e[0];
m_dao[3] = e[1];
break;
case TTLOC_UTM:
@ -1158,6 +1261,9 @@ static int parse_location (char *e)
dw_printf ("Conversion from UTM failed:\n%s\n\n", message);
}
m_dao[2] = e[0];
m_dao[3] = e[1];
break;
@ -1207,6 +1313,10 @@ static int parse_location (char *e)
mgrs_error_string (lerr, message);
dw_printf ("Conversion from MGRS/USNG failed:\n%s\n\n", message);
}
m_dao[2] = e[0];
m_dao[3] = e[1];
break;
case TTLOC_MHEAD:
@ -1235,6 +1345,10 @@ static int parse_location (char *e)
ll_from_grid_square (mh, &m_latitude, &m_longitude);
}
m_dao[2] = e[0];
m_dao[3] = e[1];
break;
case TTLOC_SATSQ:
@ -1253,6 +1367,10 @@ static int parse_location (char *e)
ll_from_grid_square (mh, &m_latitude, &m_longitude);
}
m_dao[2] = e[0];
m_dao[3] = e[1];
break;
case TTLOC_AMBIG:
@ -1267,7 +1385,6 @@ static int parse_location (char *e)
break;
default:
assert (0);
}
@ -1462,6 +1579,9 @@ static int find_ttloc_match (char *e, char *xstr, char *ystr, char *zstr, char *
*
* Cnnnnnn - Six digit frequency reformatted as nnn.nnnMHz
*
* Cnnn - Three digit are for CTCSS tone. Use only integer part
* and leading 0 if necessary to make exactly 3 digits.
*
* Cttt...tttt - General comment in Multi-press encoding.
*
* CAttt...tttt - New enhanced comment format that can handle all ASCII characters.
@ -1501,6 +1621,11 @@ static int parse_comment (char *e)
return (0);
}
if (len == 4 && isdigit(e[1]) && isdigit(e[2]) && isdigit(e[3])) {
strlcpy (m_ctcss, e+1, sizeof(m_ctcss));
return (0);
}
tt_multipress_to_text (e+1, 0, m_comment);
return (0);
}
@ -1705,9 +1830,6 @@ int dw_run_cmd (char *cmd, int oneline, char *result, size_t resultsiz)
*----------------------------------------------------------------*/
// TODO: add this to "make check"
#if TT_MAIN
/*
@ -1715,6 +1837,9 @@ int dw_run_cmd (char *cmd, int oneline, char *result, size_t resultsiz)
* It does not maintain any history so abbreviation will not invoke previous full call.
*/
/* Some examples are derived from http://www.aprs.org/aprstt/aprstt-coding24.txt */
static const struct {
char *toneseq; /* Tone sequence in. */
@ -1728,16 +1853,17 @@ static const struct {
char *dao;
} testcases[] = {
/* Callsigns & abbreviations. */
/* Callsigns & abbreviations, traditional */
{ "A9A2B42A7A7C71#", "WB4APR", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* WB4APR/7 */
{ "A27773#", "277", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* abbreviated form */
/* Example in http://www.aprs.org/aprstt/aprstt-coding24.txt has a bad checksum! */
/* Bad checksum for "2777". Expected 3 but received 6. */
/* Intentionally wrong - Has 6 for checksum when it should be 3. */
{ "A27776#", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Expect error message. */
/* Bad checksum for "2A7A7C7". E xpected 5 but received 1. */
/* Example in spec is wrong. checksum should be 5 in this case. */
{ "A2A7A7C71#", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Spelled suffix, overlay, checksum */
{ "A2A7A7C75#", "APR", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Spelled suffix, overlay, checksum */
{ "A27773#", "277", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Suffix digits, overlay, checksum */
{ "A9A2B26C7D9D71#", "WB2OSZ", "12", "7A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* WB2OSZ/7 numeric overlay */
@ -1748,6 +1874,11 @@ static const struct {
{ "A277#", "277", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" }, /* Tactical call "277" no overlay and no checksum */
/* QIKcom-2 style 10 digit call & 5 digit suffix */
{ "AC9242771558#", "WB4APR", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" },
{ "AC27722#", "APR", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" },
/* Locations */
{ "B01*A67979#", "679", "12", "7A", "", "", "12.2500", "56.2500", "!T1 !" },
@ -1761,6 +1892,8 @@ static const struct {
{ "B21234*A67979#", "679", "12", "7A", "", "", "12.3400", "56.1200", "!TB2!" },
{ "B533686*A67979#", "679", "12", "7A", "", "", "37.9222", "81.1143", "!TB5!" },
// TODO: should test other coordinate systems.
/* Comments */
{ "C1", "", "12", "\\A", "", "", "-999999.0000", "-999999.0000", "!T !" },

View File

@ -80,8 +80,9 @@ struct ttloc_s {
#define TT_ERROR_NO_CALL 9 /* No call or object name included. */
#define TT_ERROR_INVALID_MHEAD 10 /* Invalid Maidenhead Locator. */
#define TT_ERROR_INVALID_SATSQ 11 /* Satellite square must be 4 digits. */
#define TT_ERROR_SUFFIX_NO_CALL 12 /* No known callsign for suffix. */
#define TT_ERROR_MAXP1 12 /* Number of items above. i.e. Last number plus 1. */
#define TT_ERROR_MAXP1 13 /* Number of items above. i.e. Last number plus 1. */
#if CONFIG_C /* Is this being included from config.c? */
@ -100,7 +101,8 @@ static const char *tt_msg_id[TT_ERROR_MAXP1] = {
"INVALID_LOC",
"NO_CALL",
"INVALID_MHEAD",
"INVALID_SATSQ"
"INVALID_SATSQ",
"SUFFIX_NO_CALL"
};
#endif
@ -173,6 +175,10 @@ void aprs_tt_button (int chan, char button);
#define APRSTT_LOC_DESC_LEN 32 /* Need at least 26 */
#define APRSTT_DEFAULT_SYMTAB '\\'
#define APRSTT_DEFAULT_SYMBOL 'A'
void aprs_tt_dao_to_desc (char *dao, char *str);
void aprs_tt_sequence (int chan, char *msg);

View File

@ -4021,7 +4021,9 @@ static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_
b->alt_m = G_UNKNOWN;
b->symtab = '/';
b->symbol = '-'; /* house */
b->freq = G_UNKNOWN;
b->tone = G_UNKNOWN;
b->offset = G_UNKNOWN;
while ((t = split(NULL,0)) != NULL) {

Binary file not shown.

Binary file not shown.

View File

@ -28,6 +28,11 @@ These dive into more detail for specialized topics or typical usage scenarios.
This example illustrates how APRStt can be integrated with other applications such as SARTrack, APRSISCE/32, YAAC, or Xastir.
- [APRStt Listening Example](APRStt-Listening-Example.pdf)
WB4APR described a useful application for the [QIKCOM-2 Satallite Transponder](http://www.tapr.org/pipermail/aprssig/2015-November/045035.html).
Dont have your own QIKCOM-2 Satellite Transponder? No Problem. You can do the same thing with an ordinary computer and the APRStt gateway built into Dire Wolf. Heres how.
- [Raspberry Pi SDR IGate](Raspberry-Pi-SDR-IGate.pdf)

Binary file not shown.

View File

@ -411,62 +411,57 @@ static int cse_spd_data_extension (int course, int speed, char *presult)
*
* Offset must always be preceded by tone.
*
* Resulting formats are all fixed width and have a trailing space:
*
* "999.999MHz "
* "T999 "
* "+999 " (10 kHz units)
*
* Reference: http://www.aprs.org/info/freqspec.txt
*
*----------------------------------------------------------------*/
typedef struct freq_s {
char f[7]; /* format 999.999 */
char mhz[3];
char space;
} freq_t;
typedef struct to_s {
char T;
char ttt[3]; /* format 999 (drop fraction) or 'off'. */
char space1;
char oooo[4]; /* leading sign, 3 digits, tens of KHz. */
char space2;
} to_t;
static int frequency_spec (float freq, float tone, float offset, char *presult)
{
int result_len = 0;
int result_size = 24; // TODO: add as parameter.
if (freq != 0) {
freq_t *f = (freq_t*)presult;
char stemp[12]; /* Frequency should be exactly 7 characters: 999.999 */
/* Offset shouldbe exactly 4 characters: +999 */
*presult = '\0';
if (freq > 0) {
char stemp[16];
/* TODO: Should use letters for > 999.999. */
snprintf (stemp, sizeof(stemp), "%07.3f", freq);
memcpy (f->f, stemp, 7);
memcpy (f->mhz, "MHz", 3);
f->space = ' ';
result_len = sizeof (freq_t);
/* For now, just be sure we have proper field width. */
if (freq > 999.999) freq = 999.999;
snprintf (stemp, sizeof(stemp), "%07.3fMHz ", freq);
strlcpy (presult, stemp, result_size);
}
if (tone != 0 || offset != 0) {
to_t *to = (to_t*)(presult + result_len);
if (tone != G_UNKNOWN) {
char stemp[12];
to->T = 'T';
if (tone == 0) {
memcpy(to->ttt, "off", 3);
strlcpy (stemp, "Toff ", sizeof (stemp));
}
else {
snprintf (stemp, sizeof(stemp), "%03d", (int)tone);
memcpy (to->ttt, stemp, 3);
snprintf (stemp, sizeof(stemp), "T%03d ", (int)tone);
}
to->space1 = ' ';
strlcat (presult, stemp, result_size);
}
if (offset != G_UNKNOWN) {
char stemp[12];
snprintf (stemp, sizeof(stemp), "%+04d ", (int)round(offset * 100));
memcpy (to->oooo, stemp, 4);
to->space2 = ' ';
result_len += sizeof (to_t);
strlcat (presult, stemp, result_size);
}
return (result_len);
return (strlen(presult));
}
@ -800,13 +795,23 @@ int main (int argc, char *argv[])
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&PHG7368") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* with freq. */
/* with freq & tone. minus offset, no offset, explict simplex. */
encode_position (0, 0, 42+34.61/60, -(71+26.47/60), 0, G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, G_UNKNOWN, 0, 146.955, 74.4, -0.6, NULL, result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 -060 ") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
encode_position (0, 0, 42+34.61/60, -(71+26.47/60), 0, G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, G_UNKNOWN, 0, 146.955, 74.4, G_UNKNOWN, NULL, result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 ") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
encode_position (0, 0, 42+34.61/60, -(71+26.47/60), 0, G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, G_UNKNOWN, 0, 146.955, 74.4, 0, NULL, result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&146.955MHz T074 +000 ") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* with course/speed, freq, and comment! */
encode_position (0, 0, 42+34.61/60, -(71+26.47/60), 0, G_UNKNOWN, 'D', '&',
@ -817,12 +822,17 @@ int main (int argc, char *argv[])
/* Course speed, no tone, + offset */
encode_position (0, 0, 42+34.61/60, -(71+26.47/60), 0, G_UNKNOWN, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result, sizeof(result));
0, 0, 0, NULL, 180, 55, 146.955, G_UNKNOWN, 0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz Toff +060 River flooding") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz +060 River flooding") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
/* Course speed, no tone, + offset + altitude */
encode_position (0, 0, 42+34.61/60, -(71+26.47/60), 0, 12345, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, G_UNKNOWN, 0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result);
if (strcmp(result, "!4234.61ND07126.47W&180/055146.955MHz +060 /A=012345River flooding") != 0) { dw_printf ("ERROR! line %d\n", __LINE__); errors++; }
encode_position (0, 0, 42+34.61/60, -(71+26.47/60), 0, 12345, 'D', '&',
0, 0, 0, NULL, 180, 55, 146.955, 0, 0.6, "River flooding", result, sizeof(result));
dw_printf ("%s\n", result);

View File

@ -569,4 +569,5 @@ C#TTERR INVALID_SYMBOL SPEECH Invalid symbol.
C#TTERR INVALID_LOC SPEECH Invalid location.
C#TTERR NO_CALL SPEECH No call or object name.
C#TTERR SATSQ SPEECH Satellite square must be 4 digits.
C#TTERR SUFFIX_NO_CALL SPEECH Send full call before using suffix.
C

View File

@ -9,12 +9,13 @@ aclients \- Test program for side-by-side TNC performance comparison.
.I tnc ...
.RS
.P
Each \fItnc\fR reference is either a serial port or TCP network port followed by = and a comment.
Each \fItnc\fR reference is a port, =, and a description.
.P
.RE
.SH DESCRIPTION
\fBaclients\fR is used to compare how well different TNCs decode AS.25 frames.
The port can be a serial port name, host_name:tcp_port, ip_addr:port, or simply tcp_port.
.P
@ -26,7 +27,7 @@ None.
.SH EXAMPLES
.B aclients /dev/ttyS0=KPC3+ /dev/ttyUSB0=D710A 8000=DireWolf
.B aclients /dev/ttyS0=KPC3+ /dev/ttyUSB0=D710A 8000=DireWolf 192.168.1.64:8002=other
.P
Serial port /dev/ttyS0 is connected to a KPC3+ with monitor mode turned on.
.P
@ -34,6 +35,8 @@ Serial port /dev/ttyUSB0 is connected to a TM-D710A with monitor mode turned on.
.P
The Dire Wolf software TNC is available on network port 8000.
.P
Some other software TNC is available on network port 8002 on host 192.168.1.64.
.P
Packets from each are displayed in columns so it is easy to see how well each decodes
the received signals.
.P

View File

@ -652,7 +652,7 @@ static void pick_best_candidate (int chan)
/* Clear in preparation for next time. */
memset (candidate, 0, sizeof(candidate));
memset (candidate[chan], 0, sizeof(candidate[chan]));
} /* end pick_best_candidate */

4
rrbb.h
View File

@ -51,7 +51,7 @@ rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram
void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram);
static __attribute__((always_inline)) void rrbb_append_bit (rrbb_t b, const unsigned char val)
static inline /*__attribute__((always_inline))*/ void rrbb_append_bit (rrbb_t b, const unsigned char val)
{
if (b->len >= MAX_NUM_BITS) {
return; /* Silently discard if full. */
@ -60,7 +60,7 @@ static __attribute__((always_inline)) void rrbb_append_bit (rrbb_t b, const unsi
b->len++;
}
static __attribute__((always_inline)) unsigned char rrbb_get_bit (const rrbb_t b, const int ind)
static inline /*__attribute__((always_inline))*/ unsigned char rrbb_get_bit (const rrbb_t b, const int ind)
{
return (b->fdata[ind]);
}

View File

@ -19,7 +19,7 @@
/*------------------------------------------------------------------
*
* Module: tt-text.c
* Module: tt_text.c
*
* Purpose: Translate between text and touch tone representation.
*
@ -1079,6 +1079,102 @@ int tt_call10_to_text (const char *buttons, int quiet, char *text)
} /* end tt_call10_to_text */
/*------------------------------------------------------------------
*
* Name: tt_call5_suffix_to_text
*
* Purpose: Convert the 5 digit APRStt 3 style callsign suffix
* representation to text.
*
* Inputs: buttons - Input string.
* Should contain exactly 5 digits.
*
* quiet - True to suppress error messages.
*
* Outputs: text - Converted to 3 upper case letters and/or digits.
*
* Returns: Number of errors detected.
*
*----------------------------------------------------------------*/
int tt_call5_suffix_to_text (const char *buttons, int quiet, char *text)
{
const char *b;
char *t;
char c;
int packed; /* from last 4 digits */
int row, col;
int errors = 0;
int k;
t = text;
*t = '\0'; /* result */
/* Validity check. */
if (strlen(buttons) != 5) {
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Callsign 3+2 suffix to text: Encoded Callsign \"%s\" must be exactly 5 digits.\n", buttons);
}
errors++;
return (errors);
}
for (b = buttons; *b != '\0'; b++) {
if (! isdigit(*b)) {
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Callsign 3+2 suffix to text: Encoded Callsign \"%s\" can contain only digits.\n", buttons);
}
errors++;
return (errors);
}
}
packed = atoi(buttons+3);
for (k = 0; k < 3; k++) {
c = buttons[k];
row = c - '0';
col = (packed >> ((2 - k) * 2)) & 3;
if (row < 0 || row > 9 || col < 0 || col > 3) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Callsign 3+2 suffix to text: INTERNAL ERROR %d %d. Should not be here.\n", row, col);
errors++;
row = 0;
col = 1;
}
if (call10encoding[row][col] != 0) {
*t++ = call10encoding[row][col];
*t = '\0';
}
else {
errors++;
if (! quiet) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Callsign 3+2 suffix to text: Invalid combination: button %d, position %d.\n", row, col);
}
}
}
if (errors > 0) {
strcpy (text, "");
return (errors);
}
return (errors);
} /* end tt_call5_suffix_to_text */
/*------------------------------------------------------------------
*
* Name: tt_mhead_to_text

View File

@ -25,6 +25,8 @@ int tt_two_key_to_text (const char *buttons, int quiet, char *text);
int tt_call10_to_text (const char *buttons, int quiet, char *text);
int tt_call5_suffix_to_text (const char *buttons, int quiet, char *text);
int tt_mhead_to_text (const char *buttons, int quiet, char *text, size_t textsiz);
int tt_satsq_to_text (const char *buttons, int quiet, char *text);

122
tt_user.c
View File

@ -141,6 +141,9 @@ static struct tt_user_s {
char freq[12]; /* Frequency in format 999.999MHz */
char ctcss[5]; /* CTCSS tone. Exactly 3 digits for integer part. */
/* For example 74.4 Hz becomes "074". */
char comment[MAX_COMMENT_LEN+1]; /* Free form comment from user. */
/* Comment sent in final object report includes */
/* other information besides this. */
@ -215,13 +218,14 @@ void tt_user_init (struct audio_s *p_audio_config, struct tt_config_s *p_tt_conf
}
}
/*------------------------------------------------------------------
*
* Name: tt_user_search
*
* Purpose: Search for user in recent history.
*
* Inputs: callsign - full or a suffix abbreviation
* Inputs: callsign - full or a old style 3 DIGIT suffix abbreviation
* overlay
*
* Returns: Handle for refering to table position or -1 if not found.
@ -273,6 +277,51 @@ int tt_user_search (char *callsign, char overlay)
} /* end tt_user_search */
/*------------------------------------------------------------------
*
* Name: tt_3char_suffix_search
*
* Purpose: Search for new style 3 CHARACTER (vs. 3 digit) suffix in recent history.
*
* Inputs: suffix - full or a old style 3 DIGIT suffix abbreviation
*
* Outputs: callsign - corresponding full callsign or empty string.
*
* Returns: Handle for refering to table position (>= 0) or -1 if not found.
* This happens to be an index into an array but
* the implementation could change so the caller should
* not make any assumptions.
*
*----------------------------------------------------------------*/
int tt_3char_suffix_search (char *suffix, char *callsign)
{
int i;
/*
* Look for suffix in list of known calls.
*/
for (i=0; i<MAX_TT_USERS; i++) {
int len = strlen(tt_user[i].callsign);
if (len >= 3 && len <= 6 && strcmp(tt_user[i].callsign + len - 3, suffix) == 0) {
strlcpy (callsign, tt_user[i].callsign, MAX_CALLSIGN_LEN+1);
return (i);
}
}
/*
* Not found.
*/
strlcpy (callsign, "", MAX_CALLSIGN_LEN+1);
return (-1);
} /* end tt_3char_suffix_search */
/*------------------------------------------------------------------
*
* Name: clear_user
@ -413,6 +462,7 @@ static void digit_suffix (char *callsign, char *suffix)
* longitude
* ambiguity
* freq
* ctcss
* comment
* mic_e
* dao
@ -426,7 +476,7 @@ static void digit_suffix (char *callsign, char *suffix)
*----------------------------------------------------------------*/
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude,
double longitude, int ambiguity, char *freq, char *comment, char mic_e, char *dao)
double longitude, int ambiguity, char *freq, char *ctcss, char *comment, char mic_e, char *dao)
{
int i;
@ -458,8 +508,7 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *lo
i = find_avail ();
assert (i >= 0 && i < MAX_TT_USERS);
strncpy (tt_user[i].callsign, callsign, MAX_CALLSIGN_LEN);
tt_user[i].callsign[MAX_CALLSIGN_LEN] = '\0';
strlcpy (tt_user[i].callsign, callsign, sizeof(tt_user[i].callsign));
tt_user[i].count = 1;
tt_user[i].ssid = ssid;
tt_user[i].overlay = overlay;
@ -481,14 +530,15 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *lo
tt_user[i].ambiguity = ambiguity;
strlcpy (tt_user[i].freq, freq, sizeof(tt_user[i].freq));
strncpy (tt_user[i].comment, comment, MAX_COMMENT_LEN);
tt_user[i].comment[MAX_COMMENT_LEN] = '\0';
strlcpy (tt_user[i].ctcss, ctcss, sizeof(tt_user[i].ctcss));
strlcpy (tt_user[i].comment, comment, sizeof(tt_user[i].comment));
tt_user[i].mic_e = mic_e;
strncpy(tt_user[i].dao, dao, 6);
strlcpy(tt_user[i].dao, dao, sizeof(tt_user[i].dao));
}
else {
/*
* Known user. Update with any new information.
* Keep any old values where not being updated.
*/
assert (i >= 0 && i < MAX_TT_USERS);
@ -496,6 +546,13 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *lo
/* Any reason to look at ssid here? */
/* Update the symbol if not the default. */
if (overlay != APRSTT_DEFAULT_SYMTAB || symbol != APRSTT_DEFAULT_SYMBOL) {
tt_user[i].overlay = overlay;
tt_user[i].symbol = symbol;
}
if (strlen(loc_text) > 0) {
strlcpy (tt_user[i].loc_text, loc_text, sizeof(tt_user[i].loc_text));
}
@ -515,17 +572,21 @@ int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *lo
strlcpy (tt_user[i].freq, freq, sizeof(tt_user[i].freq));
}
if (ctcss[0] != '\0') {
strlcpy (tt_user[i].ctcss, ctcss, sizeof(tt_user[i].ctcss));
}
if (comment[0] != '\0') {
strncpy (tt_user[i].comment, comment, MAX_COMMENT_LEN);
strlcpy (tt_user[i].comment, comment, MAX_COMMENT_LEN);
tt_user[i].comment[MAX_COMMENT_LEN] = '\0';
}
if (mic_e != ' ') {
tt_user[i].mic_e = mic_e;
}
if (strlen(dao) > 0) {
strncpy(tt_user[i].dao, dao, 6);
tt_user[i].dao[5] = '\0';
strlcpy(tt_user[i].dao, dao, sizeof(tt_user[i].dao));
}
}
@ -782,7 +843,10 @@ static void xmit_object_report (int i, int first_time)
encode_object (object_name, 0, tt_user[i].last_heard, olat, olong, oambig,
tt_user[i].overlay, tt_user[i].symbol,
0,0,0,NULL, G_UNKNOWN, G_UNKNOWN, /* PHGD, Course/Speed */
atof(tt_user[i].freq), 0, 0, info_comment, object_info, sizeof(object_info));
strlen(tt_user[i].freq) > 0 ? atof(tt_user[i].freq) : G_UNKNOWN,
strlen(tt_user[i].ctcss) > 0 ? atof(tt_user[i].ctcss) : G_UNKNOWN,
G_UNKNOWN, /* CTCSS */
info_comment, object_info, sizeof(object_info));
strlcat (stemp, object_info, sizeof(stemp));
@ -972,6 +1036,13 @@ static void tt_setenv (int i)
setenv ("TTFREQ", tt_user[i].freq, 1);
// TODO: Should convert to actual frequency. e.g. 074 becomes 74.4
// There is some code for this in decode_aprs.c but not broken out
// into a function that we could use from here.
// TODO: Document this environment variable after converting.
setenv ("TTCTCSS", tt_user[i].ctcss, 1);
setenv ("TTCOMMENT", tt_user[i].comment, 1);
setenv ("TTLOC", tt_user[i].loc_text, 1);
@ -1006,10 +1077,10 @@ void tt_user_dump (void)
int i;
time_t now = time(NULL);
printf ("call ov suf lsthrd xmit nxt cor lat long freq m comment\n");
printf ("call ov suf lsthrd xmit nxt cor lat long freq ctcss m comment\n");
for (i=0; i<MAX_TT_USERS; i++) {
if (tt_user[i].callsign[0] != '\0') {
printf ("%-6s %c%c %-3s %6d %d %+6d %d %6.2f %7.2f %-10s %c %s\n",
printf ("%-6s %c%c %-3s %6d %d %+6d %d %6.2f %7.2f %-10s %-3s %c %s\n",
tt_user[i].callsign,
tt_user[i].overlay,
tt_user[i].symbol,
@ -1021,6 +1092,7 @@ void tt_user_dump (void)
tt_user[i].latitude,
tt_user[i].longitude,
tt_user[i].freq,
tt_user[i].ctcss,
tt_user[i].mic_e,
tt_user[i].comment);
}
@ -1037,7 +1109,7 @@ void tt_user_dump (void)
*
* Description: Just a smattering, not an organized test.
*
* $ rm a.exe ; gcc -DTT_MAIN -Iregex tt_user.c tt_text.c encode_aprs.c latlong.c textcolor.c ; ./a.exe
* $ rm a.exe ; gcc -DTT_MAIN -Iregex tt_user.c tt_text.c encode_aprs.c latlong.c textcolor.c misc.a ; ./a.exe
*
*----------------------------------------------------------------*/
@ -1081,21 +1153,25 @@ int main (int argc, char *argv[])
tt_user_init(&my_audio_config, &my_tt_config);
tt_user_heard ("TEST1", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
// tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude,
// double longitude, int ambiguity, char *freq, char *ctcss, char *comment, char mic_e, char *dao);
tt_user_heard ("TEST1", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
SLEEP_SEC (1);
tt_user_heard ("TEST2", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
tt_user_heard ("TEST2", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
SLEEP_SEC (1);
tt_user_heard ("TEST3", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
tt_user_heard ("TEST3", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
SLEEP_SEC (1);
tt_user_heard ("TEST4", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
tt_user_heard ("TEST4", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
SLEEP_SEC (1);
tt_user_heard ("WB2OSZ", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
tt_user_heard ("K2H", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "", ' ', "!T99!");
tt_user_heard ("WB2OSZ", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
tt_user_heard ("K2H", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
tt_user_dump ();
tt_user_heard ("679", 12, 'J', 'A', 37.25, -71.75, " ", " ", ' ', "!T99!");
tt_user_heard ("WB2OSZ", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "146.520MHz", "", ' ', "!T99!");
tt_user_heard ("679", 12, 'J', 'A', G_UNKNOWN, G_UNKNOWN, "", "Hello, world", '9', "!T99!");
tt_user_heard ("679", 12, 'J', 'A', "", 37.25, -71.75, 0, "", " ", " ", ' ', "!T99!");
tt_user_heard ("WB2OSZ", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "146.520MHz", "", "", ' ', "!T99!");
tt_user_heard ("WB1GOF", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "146.955MHz", "074", "", ' ', "!T99!");
tt_user_heard ("679", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "Hello, world", '9', "!T99!");
tt_user_dump ();
for (n=0; n<30; n++) {

View File

@ -7,7 +7,9 @@
void tt_user_init (struct audio_s *p_audio_config, struct tt_config_s *p);
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude,
double longitude, int ambiguity, char *freq, char *comment, char mic_e, char *dao);
double longitude, int ambiguity, char *freq, char *ctcss, char *comment, char mic_e, char *dao);
int tt_3char_suffix_search (char *suffix, char *callsign);
void tt_user_background (void);
void tt_user_dump (void);