2015-07-27 00:35:07 +00:00
|
|
|
//
|
|
|
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
|
|
|
//
|
2021-01-05 00:43:00 +00:00
|
|
|
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2021 John Langner, WB2OSZ
|
2015-07-27 00:35:07 +00:00
|
|
|
//
|
|
|
|
// 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
//
|
|
|
|
|
2016-07-03 22:09:34 +00:00
|
|
|
#define CONFIG_C 1 // influences behavior of aprs_tt.h
|
2015-09-07 23:56:20 +00:00
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
// #define DEBUG 1
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Module: config.c
|
|
|
|
*
|
|
|
|
* Purpose: Read configuration information from a file.
|
|
|
|
*
|
|
|
|
* Description: This started out as a simple little application with a few
|
|
|
|
* command line options. Due to creeping featurism, it's now
|
|
|
|
* time to add a configuration file to specify options.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------------*/
|
|
|
|
|
2016-07-03 22:09:34 +00:00
|
|
|
#include "direwolf.h"
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
2015-07-27 01:17:23 +00:00
|
|
|
#include <math.h>
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
#if ENABLE_GPSD
|
|
|
|
#include <gps.h> /* for DEFAULT_GPSD_PORT (2947) */
|
2015-07-27 00:35:07 +00:00
|
|
|
#endif
|
|
|
|
|
2015-10-27 16:31:02 +00:00
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
#include "ax25_pad.h"
|
|
|
|
#include "textcolor.h"
|
|
|
|
#include "audio.h"
|
|
|
|
#include "digipeater.h"
|
2016-11-20 19:58:51 +00:00
|
|
|
#include "cdigipeater.h"
|
2015-07-27 00:35:07 +00:00
|
|
|
#include "config.h"
|
|
|
|
#include "aprs_tt.h"
|
|
|
|
#include "igate.h"
|
|
|
|
#include "latlong.h"
|
|
|
|
#include "symbols.h"
|
2015-07-27 01:17:23 +00:00
|
|
|
#include "xmit.h"
|
2015-09-07 23:56:20 +00:00
|
|
|
#include "tt_text.h"
|
2016-11-20 19:58:51 +00:00
|
|
|
#include "ax25_link.h"
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2021-02-07 21:19:34 +00:00
|
|
|
#if USE_CM108 // Current Linux or Windows only
|
2017-10-09 22:00:59 +00:00
|
|
|
#include "cm108.h"
|
|
|
|
#endif
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
// geotranz
|
|
|
|
|
|
|
|
#include "utm.h"
|
|
|
|
#include "mgrs.h"
|
|
|
|
#include "usng.h"
|
|
|
|
#include "error_string.h"
|
|
|
|
|
|
|
|
#define D2R(d) ((d) * M_PI / 180.)
|
|
|
|
#define R2D(r) ((r) * 180. / M_PI)
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Conversions from various units to meters.
|
|
|
|
* There is some disagreement about the exact values for some of these.
|
|
|
|
* Close enough for our purposes.
|
|
|
|
* Parsec, light year, and angstrom are probably not useful.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const struct units_s {
|
|
|
|
char *name;
|
|
|
|
float meters;
|
|
|
|
} units[] = {
|
|
|
|
{ "barleycorn", 0.008466667 },
|
|
|
|
{ "inch", 0.0254 },
|
|
|
|
{ "in", 0.0254 },
|
|
|
|
{ "hand", 0.1016 },
|
|
|
|
{ "shaku", 0.3030 },
|
|
|
|
{ "foot", 0.304801 },
|
|
|
|
{ "ft", 0.304801 },
|
|
|
|
{ "cubit", 0.4572 },
|
|
|
|
{ "megalithicyard", 0.8296 },
|
|
|
|
{ "my", 0.8296 },
|
|
|
|
{ "yard", 0.914402 },
|
|
|
|
{ "yd", 0.914402 },
|
|
|
|
{ "m", 1. },
|
|
|
|
{ "meter", 1. },
|
|
|
|
{ "metre", 1. },
|
|
|
|
{ "ell", 1.143 },
|
|
|
|
{ "ken", 1.818 },
|
|
|
|
{ "hiro", 1.818 },
|
|
|
|
{ "fathom", 1.8288 },
|
|
|
|
{ "fath", 1.8288 },
|
|
|
|
{ "toise", 1.949 },
|
|
|
|
{ "jo", 3.030 },
|
|
|
|
{ "twain", 3.6576074 },
|
|
|
|
{ "rod", 5.0292 },
|
|
|
|
{ "rd", 5.0292 },
|
|
|
|
{ "perch", 5.0292 },
|
|
|
|
{ "pole", 5.0292 },
|
|
|
|
{ "rope", 6.096 },
|
|
|
|
{ "dekameter", 10. },
|
|
|
|
{ "dekametre", 10. },
|
|
|
|
{ "dam", 10. },
|
|
|
|
{ "chain", 20.1168 },
|
|
|
|
{ "ch", 20.1168 },
|
|
|
|
{ "actus", 35.47872 },
|
|
|
|
{ "arpent", 58.471 },
|
|
|
|
{ "hectometer", 100. },
|
|
|
|
{ "hectometre", 100. },
|
|
|
|
{ "hm", 100. },
|
|
|
|
{ "cho", 109.1 },
|
|
|
|
{ "furlong", 201.168 },
|
|
|
|
{ "fur", 201.168 },
|
|
|
|
{ "kilometer", 1000. },
|
|
|
|
{ "kilometre", 1000. },
|
|
|
|
{ "km", 1000. },
|
|
|
|
{ "mile", 1609.344 },
|
|
|
|
{ "mi", 1609.344 },
|
|
|
|
{ "ri", 3927. },
|
|
|
|
{ "league", 4828.032 },
|
|
|
|
{ "lea", 4828.032 } };
|
|
|
|
|
2017-01-01 16:49:55 +00:00
|
|
|
#define NUM_UNITS ((int)((sizeof(units) / sizeof(struct units_s))))
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_s *p_audio_config);
|
|
|
|
|
|
|
|
/* Do we have a string of all digits? */
|
|
|
|
|
|
|
|
static int alldigits(char *p)
|
|
|
|
{
|
|
|
|
if (p == NULL) return (0);
|
|
|
|
if (strlen(p) == 0) return (0);
|
|
|
|
while (*p != '\0') {
|
|
|
|
if ( ! isdigit(*p)) return (0);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do we have a string of all letters or + or - ? */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
static int alllettersorpm(char *p)
|
|
|
|
{
|
|
|
|
if (p == NULL) return (0);
|
|
|
|
if (strlen(p) == 0) return (0);
|
|
|
|
while (*p != '\0') {
|
|
|
|
if ( ! isalpha(*p) && *p != '+' && *p != '-') return (0);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
return (1);
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: parse_ll
|
|
|
|
*
|
|
|
|
* Purpose: Parse latitude or longitude from configuration file.
|
|
|
|
*
|
|
|
|
* Inputs: str - String like [-]deg[^min][hemisphere]
|
|
|
|
*
|
|
|
|
* which - LAT or LON for error checking and message.
|
|
|
|
*
|
|
|
|
* line - Line number for use in error message.
|
|
|
|
*
|
|
|
|
* Returns: Coordinate in signed degrees.
|
|
|
|
*
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Acceptable symbols to separate degrees & minutes. */
|
|
|
|
/* Degree symbol is not in ASCII so documentation says to use "^" instead. */
|
|
|
|
/* Some wise guy will try to use degree symbol. */
|
2015-11-08 01:57:02 +00:00
|
|
|
/* UTF-8 is more difficult because it is a two byte sequence, c2 b0. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
#define DEG1 '^'
|
|
|
|
#define DEG2 0xb0 /* ISO Latin1 */
|
|
|
|
#define DEG3 0xf8 /* Microsoft code page 437 */
|
|
|
|
|
|
|
|
|
|
|
|
enum parse_ll_which_e { LAT, LON };
|
|
|
|
|
|
|
|
static double parse_ll (char *str, enum parse_ll_which_e which, int line)
|
|
|
|
{
|
|
|
|
char stemp[40];
|
|
|
|
int sign;
|
|
|
|
double degrees, minutes;
|
|
|
|
char *endptr;
|
|
|
|
char hemi;
|
|
|
|
int limit;
|
|
|
|
unsigned char sep;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove any negative sign.
|
|
|
|
*/
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (stemp, str, sizeof(stemp));
|
2015-07-27 00:35:07 +00:00
|
|
|
sign = +1;
|
|
|
|
if (stemp[0] == '-') {
|
|
|
|
sign = -1;
|
|
|
|
stemp[0] = ' ';
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Process any hemisphere on the end.
|
|
|
|
*/
|
|
|
|
if (strlen(stemp) >= 2) {
|
|
|
|
endptr = stemp + strlen(stemp) - 1;
|
|
|
|
if (isalpha(*endptr)) {
|
|
|
|
|
|
|
|
hemi = *endptr;
|
|
|
|
*endptr = '\0';
|
|
|
|
if (islower(hemi)) {
|
|
|
|
hemi = toupper(hemi);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hemi == 'W' || hemi == 'S') {
|
|
|
|
sign = -sign;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (which == LAT) {
|
|
|
|
if (hemi != 'N' && hemi != 'S') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Latitude hemisphere in \"%s\" is not N or S.\n", line, str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (hemi != 'E' && hemi != 'W') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Longitude hemisphere in \"%s\" is not E or W.\n", line, str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the degrees part.
|
|
|
|
*/
|
|
|
|
degrees = strtod (stemp, &endptr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is there a minutes part?
|
|
|
|
*/
|
|
|
|
sep = *endptr;
|
|
|
|
if (sep != '\0') {
|
|
|
|
|
|
|
|
if (sep == DEG1 || sep == DEG2 || sep == DEG3) {
|
|
|
|
|
|
|
|
minutes = strtod (endptr+1, &endptr);
|
|
|
|
if (*endptr != '\0') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unexpected character '%c' in location \"%s\"\n", line, sep, str);
|
|
|
|
}
|
|
|
|
if (minutes >= 60.0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Number of minutes in \"%s\" is >= 60.\n", line, str);
|
|
|
|
}
|
|
|
|
degrees += minutes / 60.0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unexpected character '%c' in location \"%s\"\n", line, sep, str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
degrees = degrees * sign;
|
|
|
|
|
|
|
|
limit = which == LAT ? 90 : 180;
|
|
|
|
if (degrees < -limit || degrees > limit) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Number of degrees in \"%s\" is out of range for %s\n", line, str,
|
|
|
|
which == LAT ? "latitude" : "longitude");
|
|
|
|
}
|
|
|
|
//dw_printf ("%s = %f\n", str, degrees);
|
|
|
|
return (degrees);
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: parse_utm_zone
|
|
|
|
*
|
|
|
|
* Purpose: Parse UTM zone from configuration file.
|
|
|
|
*
|
|
|
|
* Inputs: szone - String like [-]number[letter]
|
|
|
|
*
|
2015-09-07 23:56:20 +00:00
|
|
|
* Outputs: latband - Latitude band if specified, otherwise space or -.
|
|
|
|
*
|
|
|
|
* hemi - Hemisphere, always one of 'N' or 'S'.
|
2015-07-27 01:17:23 +00:00
|
|
|
*
|
|
|
|
* Returns: Zone as number.
|
|
|
|
* Type is long because Convert_UTM_To_Geodetic expects that.
|
|
|
|
*
|
|
|
|
* Errors: Prints message and return 0.
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* It seems there are multiple conventions for specifying the UTM hemisphere.
|
|
|
|
*
|
|
|
|
* - MGRS latitude band. North if missing or >= 'N'.
|
|
|
|
* - Negative zone for south.
|
|
|
|
* - Separate North or South.
|
|
|
|
*
|
2021-09-19 18:51:18 +00:00
|
|
|
* I'm using the first alternative.
|
2015-07-27 01:17:23 +00:00
|
|
|
* GEOTRANS uses the third.
|
|
|
|
* We will also recognize the second one but I'm not sure if I want to document it.
|
|
|
|
*
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
long parse_utm_zone (char *szone, char *latband, char *hemi)
|
2015-07-27 01:17:23 +00:00
|
|
|
{
|
|
|
|
long lzone;
|
|
|
|
char *zlet;
|
|
|
|
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
*latband = ' ';
|
2015-07-27 01:17:23 +00:00
|
|
|
*hemi = 'N'; /* default */
|
|
|
|
|
|
|
|
lzone = strtol(szone, &zlet, 10);
|
|
|
|
|
|
|
|
if (*zlet == '\0') {
|
|
|
|
/* Number is not followed by letter something else. */
|
|
|
|
/* Allow negative number to mean south. */
|
|
|
|
|
|
|
|
if (lzone < 0) {
|
2015-09-07 23:56:20 +00:00
|
|
|
*latband = '-';
|
2015-07-27 01:17:23 +00:00
|
|
|
*hemi = 'S';
|
|
|
|
lzone = (- lzone);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (islower (*zlet)) {
|
|
|
|
*zlet = toupper(*zlet);
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
*latband = *zlet;
|
2015-07-27 01:17:23 +00:00
|
|
|
if (strchr ("CDEFGHJKLMNPQRSTUVWX", *zlet) != NULL) {
|
|
|
|
if (*zlet < 'N') {
|
|
|
|
*hemi = 'S';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Latitudinal band in \"%s\" must be one of CDEFGHJKLMNPQRSTUVWX.\n", szone);
|
|
|
|
*hemi = '?';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lzone < 1 || lzone > 60) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("UTM Zone number %ld must be in range of 1 to 60.\n", lzone);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return (lzone);
|
2015-09-07 23:56:20 +00:00
|
|
|
|
|
|
|
} /* end parse_utm_zone */
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
#if 0
|
|
|
|
main ()
|
|
|
|
{
|
|
|
|
|
|
|
|
parse_ll ("12.5", LAT);
|
|
|
|
parse_ll ("12.5N", LAT);
|
|
|
|
parse_ll ("12.5E", LAT); // error
|
|
|
|
|
|
|
|
parse_ll ("-12.5", LAT);
|
|
|
|
parse_ll ("12.5S", LAT);
|
|
|
|
parse_ll ("12.5W", LAT); // error
|
|
|
|
|
|
|
|
parse_ll ("12.5", LON);
|
|
|
|
parse_ll ("12.5E", LON);
|
|
|
|
parse_ll ("12.5N", LON); // error
|
|
|
|
|
|
|
|
parse_ll ("-12.5", LON);
|
|
|
|
parse_ll ("12.5W", LON);
|
|
|
|
parse_ll ("12.5S", LON); // error
|
|
|
|
|
|
|
|
parse_ll ("12^30", LAT);
|
2015-09-07 23:56:20 +00:00
|
|
|
parse_ll ("12\xb030", LAT); // ISO Latin-1 degree symbol
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
parse_ll ("91", LAT); // out of range
|
|
|
|
parse_ll ("91", LON);
|
|
|
|
parse_ll ("181", LON); // out of range
|
|
|
|
|
|
|
|
parse_ll ("12&5", LAT); // bad character
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: parse_interval
|
|
|
|
*
|
|
|
|
* Purpose: Parse time interval from configuration file.
|
|
|
|
*
|
|
|
|
* Inputs: str - String like 10 or 9:30
|
|
|
|
*
|
|
|
|
* line - Line number for use in error message.
|
|
|
|
*
|
|
|
|
* Returns: Number of seconds.
|
|
|
|
*
|
|
|
|
* Description: This is used by the BEACON configuration items
|
|
|
|
* for initial delay or time between beacons.
|
|
|
|
*
|
|
|
|
* The format is either minutes or minutes:seconds.
|
|
|
|
*
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
static int parse_interval (char *str, int line)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
int sec;
|
|
|
|
int nc = 0;
|
|
|
|
int bad = 0;
|
|
|
|
|
|
|
|
for (p = str; *p != '\0'; p++) {
|
|
|
|
if (*p == ':') nc++;
|
|
|
|
else if ( ! isdigit(*p)) bad++;
|
|
|
|
}
|
|
|
|
if (bad > 0 || nc > 1) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Time interval must be of the form minutes or minutes:seconds.\n", line);
|
|
|
|
}
|
|
|
|
|
|
|
|
p = strchr (str, ':');
|
|
|
|
|
|
|
|
if (p != NULL) {
|
|
|
|
sec = atoi(str) * 60 + atoi(p+1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sec = atoi(str) * 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (sec);
|
|
|
|
|
|
|
|
} /* end parse_interval */
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-07-03 22:09:34 +00:00
|
|
|
/*------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: check_via_path
|
|
|
|
*
|
|
|
|
* Purpose: Check for valid path in beacons, IGate, and APRStt configuration.
|
|
|
|
*
|
|
|
|
* Inputs: via_path - Zero or more comma separated stations.
|
|
|
|
*
|
|
|
|
* Returns: Maximum number of digipeater hops or -1 for error.
|
|
|
|
*
|
|
|
|
* Description: Beacons and IGate can use via paths such as:
|
|
|
|
*
|
|
|
|
* WIDE1-1,MA3-3
|
|
|
|
* N2GH,RARA-7
|
|
|
|
*
|
|
|
|
* Each part could be a specific station, an alias, or a path
|
|
|
|
* from the "New n-N Paradigm."
|
|
|
|
* In the first example above, the maximum number of digipeater
|
|
|
|
* hops would be 4. In the second example, 2.
|
|
|
|
*
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
// Put something like this in the config file as a quick test.
|
|
|
|
// Not worth adding to "make check" regression tests.
|
|
|
|
//
|
|
|
|
// IBEACON via=
|
|
|
|
// IBEACON via=W2UB
|
|
|
|
// IBEACON via=W2UB-7
|
|
|
|
// IBEACON via=WIDE1-1,WIDE2-2,WIDE3-3
|
|
|
|
// IBEACON via=Lower
|
|
|
|
// IBEACON via=T00LONG
|
|
|
|
// IBEACON via=W2UB-16
|
|
|
|
// IBEACON via=D1,D2,D3,D4,D5,D6,D7,D8
|
|
|
|
// IBEACON via=D1,D2,D3,D4,D5,D6,D7,D8,D9
|
|
|
|
//
|
|
|
|
// Define below and visually check results.
|
|
|
|
|
|
|
|
//#define DEBUG8 1
|
|
|
|
|
|
|
|
|
|
|
|
static int check_via_path (char *via_path)
|
|
|
|
{
|
|
|
|
char stemp[AX25_MAX_REPEATERS * (AX25_MAX_ADDR_LEN + 1)];
|
|
|
|
int num_digi = 0;
|
|
|
|
int max_digi_hops = 0;
|
|
|
|
char *r;
|
|
|
|
char *a;
|
|
|
|
|
|
|
|
#if DEBUG8
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
dw_printf ("check_via_path %s\n", via_path);
|
|
|
|
#endif
|
|
|
|
if (strlen(via_path) == 0) {
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
strlcpy (stemp, via_path, sizeof(stemp));
|
|
|
|
|
|
|
|
r = stemp;
|
|
|
|
while (( a = strsep(&r,",")) != NULL) {
|
2017-06-01 00:07:52 +00:00
|
|
|
int strict = 2;
|
2016-07-03 22:09:34 +00:00
|
|
|
int ok;
|
|
|
|
char addr[AX25_MAX_ADDR_LEN];
|
|
|
|
int ssid;
|
|
|
|
int heard;
|
|
|
|
|
|
|
|
num_digi++;
|
|
|
|
ok = ax25_parse_addr (AX25_REPEATER_1 - 1 + num_digi, a, strict, addr, &ssid, &heard);
|
|
|
|
if ( ! ok) {
|
|
|
|
#if DEBUG8
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
dw_printf ("check_via_path bad address\n");
|
|
|
|
#endif
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Based on assumption that a callsign can't end with a digit. */
|
|
|
|
/* For something of the form xxx9-9, we take the ssid as max hop count. */
|
|
|
|
|
|
|
|
if (ssid > 0 && strlen(addr) >= 2 && isdigit(addr[strlen(addr)-1])) {
|
|
|
|
max_digi_hops += ssid;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
max_digi_hops++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_digi > AX25_MAX_REPEATERS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Maximum of 8 digipeaters has been exceeded.\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if DEBUG8
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
dw_printf ("check_via_path %d addresses, %d max digi hops\n", num_digi, max_digi_hops);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return (max_digi_hops);
|
|
|
|
|
|
|
|
} /* end check_via_path */
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: split
|
|
|
|
*
|
|
|
|
* Purpose: Separate a line into command and parameters.
|
|
|
|
*
|
|
|
|
* Inputs: string - Complete command line to start process.
|
|
|
|
* NULL for subsequent calls.
|
|
|
|
*
|
|
|
|
* rest_of_line - Caller wants remainder of line, not just
|
|
|
|
* the next parameter.
|
|
|
|
*
|
|
|
|
* Returns: Pointer to next part with any quoting removed.
|
|
|
|
*
|
|
|
|
* Description: the configuration file started out very simple and strtok
|
|
|
|
* was used to split up the lines. As more complicated options
|
|
|
|
* were added, there were several different situations where
|
|
|
|
* parameter values might contain spaces. These were handled
|
|
|
|
* inconsistently in different places. In version 1.3, we now
|
|
|
|
* treat them consistently in one place.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
2021-10-22 21:29:20 +00:00
|
|
|
#define MAXCMDLEN 1200
|
2015-09-07 23:56:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
static char *split (char *string, int rest_of_line)
|
|
|
|
{
|
|
|
|
static char cmd[MAXCMDLEN];
|
|
|
|
static char token[MAXCMDLEN];
|
2018-01-02 02:38:50 +00:00
|
|
|
static char shutup[] = " "; // Shut up static analysis which gets upset
|
|
|
|
// over the case where this could be called with
|
|
|
|
// string NULL and c was not yet initialized.
|
|
|
|
static char *c = shutup; // Current position in command line.
|
2015-09-07 23:56:20 +00:00
|
|
|
char *s, *t;
|
|
|
|
int in_quotes;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If string is provided, make a copy.
|
|
|
|
* Drop any CRLF at the end.
|
|
|
|
* Change any tabs to spaces so we don't have to check for it later.
|
|
|
|
*/
|
|
|
|
if (string != NULL) {
|
|
|
|
|
|
|
|
// dw_printf("split in: '%s'\n", string);
|
|
|
|
|
|
|
|
c = cmd;
|
|
|
|
for (s = string; *s != '\0'; s++) {
|
|
|
|
if (*s == '\t') {
|
|
|
|
*c++ = ' ';
|
|
|
|
}
|
|
|
|
else if (*s == '\r' || *s == '\n') {
|
|
|
|
;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*c++ = *s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*c = '\0';
|
|
|
|
c = cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get next part, separated by whitespace, keeping spaces within quotes.
|
|
|
|
* Quotation marks inside need to be doubled.
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (*c == ' ') {
|
|
|
|
c++;
|
|
|
|
};
|
|
|
|
|
|
|
|
t = token;
|
|
|
|
in_quotes = 0;
|
|
|
|
for ( ; *c != '\0'; c++) {
|
|
|
|
|
|
|
|
if (*c == '"') {
|
|
|
|
if (in_quotes) {
|
|
|
|
if (c[1] == '"') {
|
|
|
|
*t++ = *c++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
in_quotes = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
in_quotes = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (*c == ' ') {
|
|
|
|
if (in_quotes || rest_of_line) {
|
|
|
|
*t++ = *c;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*t++ = *c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*t = '\0';
|
|
|
|
|
|
|
|
// dw_printf("split out: '%s'\n", token);
|
|
|
|
|
|
|
|
t = token;
|
|
|
|
if (*t == '\0') {
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (t);
|
|
|
|
|
|
|
|
} /* end split */
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: config_init
|
|
|
|
*
|
|
|
|
* Purpose: Read configuration file when application starts up.
|
|
|
|
*
|
|
|
|
* Inputs: fname - Name of configuration file.
|
|
|
|
*
|
2015-07-27 01:17:23 +00:00
|
|
|
* Outputs: p_audio_config - Radio channel parameters stored here.
|
2015-07-27 00:35:07 +00:00
|
|
|
*
|
2016-11-20 19:58:51 +00:00
|
|
|
* p_digi_config - APRS Digipeater configuration stored here.
|
|
|
|
*
|
|
|
|
* p_cdigi_config - Connected Digipeater configuration stored here.
|
2015-07-27 00:35:07 +00:00
|
|
|
*
|
|
|
|
* p_tt_config - APRStt stuff.
|
|
|
|
*
|
|
|
|
* p_igate_config - Internet Gateway.
|
|
|
|
*
|
|
|
|
* p_misc_config - Everything else. This wasn't thought out well.
|
|
|
|
*
|
|
|
|
* Description: Apply default values for various parameters then read the
|
|
|
|
* the configuration file which can override those values.
|
|
|
|
*
|
|
|
|
* Errors: For invalid input, display line number and message on stdout (not stderr).
|
|
|
|
* In many cases this will result in keeping the default rather than aborting.
|
|
|
|
*
|
|
|
|
* Bugs: Very simple-minded parsing.
|
|
|
|
* Not much error checking. (e.g. atoi() will return 0 for invalid string.)
|
|
|
|
* Not very forgiving about sloppy input.
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
void config_init (char *fname, struct audio_s *p_audio_config,
|
2015-07-27 00:35:07 +00:00
|
|
|
struct digi_config_s *p_digi_config,
|
2016-11-20 19:58:51 +00:00
|
|
|
struct cdigi_config_s *p_cdigi_config,
|
2015-07-27 00:35:07 +00:00
|
|
|
struct tt_config_s *p_tt_config,
|
|
|
|
struct igate_config_s *p_igate_config,
|
|
|
|
struct misc_config_s *p_misc_config)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
2015-09-07 23:56:20 +00:00
|
|
|
char filepath[128];
|
|
|
|
char stuff[MAXCMDLEN];
|
2015-07-27 00:35:07 +00:00
|
|
|
int line;
|
|
|
|
int channel;
|
2015-07-27 01:17:23 +00:00
|
|
|
int adevice;
|
2015-09-07 23:56:20 +00:00
|
|
|
int m;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
dw_printf ("config_init ( %s )\n", fname);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First apply defaults.
|
|
|
|
*/
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
memset (p_audio_config, 0, sizeof(struct audio_s));
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
p_audio_config->igate_vchannel = -1; // none.
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* First audio device is always available with defaults. */
|
|
|
|
/* Others must be explicitly defined before use. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
for (adevice=0; adevice<MAX_ADEVS; adevice++) {
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (p_audio_config->adev[adevice].adevice_in, DEFAULT_ADEVICE, sizeof(p_audio_config->adev[adevice].adevice_in));
|
|
|
|
strlcpy (p_audio_config->adev[adevice].adevice_out, DEFAULT_ADEVICE, sizeof(p_audio_config->adev[adevice].adevice_out));
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
p_audio_config->adev[adevice].defined = 0;
|
|
|
|
p_audio_config->adev[adevice].num_channels = DEFAULT_NUM_CHANNELS; /* -2 stereo */
|
|
|
|
p_audio_config->adev[adevice].samples_per_sec = DEFAULT_SAMPLES_PER_SEC; /* -r option */
|
|
|
|
p_audio_config->adev[adevice].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; /* -8 option for 8 instead of 16 bits */
|
|
|
|
}
|
|
|
|
|
|
|
|
p_audio_config->adev[0].defined = 1;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
for (channel=0; channel<MAX_CHANS; channel++) {
|
2017-04-01 13:26:31 +00:00
|
|
|
int ot, it;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
p_audio_config->chan_medium[channel] = MEDIUM_NONE; /* One or both channels will be */
|
2019-05-13 10:25:12 +00:00
|
|
|
/* set to radio when corresponding */
|
2015-07-27 01:17:23 +00:00
|
|
|
/* audio device is defined. */
|
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_AFSK;
|
2019-05-20 00:57:56 +00:00
|
|
|
p_audio_config->achan[channel].v26_alternative = V26_UNSPECIFIED;
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].mark_freq = DEFAULT_MARK_FREQ; /* -m option */
|
|
|
|
p_audio_config->achan[channel].space_freq = DEFAULT_SPACE_FREQ; /* -s option */
|
|
|
|
p_audio_config->achan[channel].baud = DEFAULT_BAUD; /* -b option */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
/* None. Will set default later based on other factors. */
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (p_audio_config->achan[channel].profiles, "", sizeof(p_audio_config->achan[channel].profiles));
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
p_audio_config->achan[channel].num_freq = 1;
|
|
|
|
p_audio_config->achan[channel].offset = 0;
|
|
|
|
|
2021-10-22 21:29:20 +00:00
|
|
|
p_audio_config->achan[channel].layer2_xmit = LAYER2_AX25;
|
|
|
|
p_audio_config->achan[channel].il2p_max_fec = 1;
|
|
|
|
p_audio_config->achan[channel].il2p_invert_polarity = 0;
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS;
|
|
|
|
p_audio_config->achan[channel].sanity_test = SANITY_APRS;
|
|
|
|
p_audio_config->achan[channel].passall = 0;
|
|
|
|
|
|
|
|
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_NONE;
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, "", sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device));
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line = PTT_LINE_NONE;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line2 = PTT_LINE_NONE;
|
2017-04-01 13:26:31 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].out_gpio_num = 0;
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_lpt_bit = 0;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert2 = 0;
|
|
|
|
}
|
|
|
|
|
2017-04-01 13:26:31 +00:00
|
|
|
for (it = 0; it < NUM_ICTYPES; it++) {
|
|
|
|
p_audio_config->achan[channel].ictrl[it].method = PTT_METHOD_NONE;
|
|
|
|
p_audio_config->achan[channel].ictrl[it].in_gpio_num = 0;
|
|
|
|
p_audio_config->achan[channel].ictrl[it].invert = 0;
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].dwait = DEFAULT_DWAIT;
|
|
|
|
p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME;
|
|
|
|
p_audio_config->achan[channel].persist = DEFAULT_PERSIST;
|
|
|
|
p_audio_config->achan[channel].txdelay = DEFAULT_TXDELAY;
|
|
|
|
p_audio_config->achan[channel].txtail = DEFAULT_TXTAIL;
|
2017-10-19 00:50:59 +00:00
|
|
|
p_audio_config->achan[channel].fulldup = DEFAULT_FULLDUP;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2020-04-19 04:50:18 +00:00
|
|
|
p_audio_config->fx25_auto_enable = AX25_N2_RETRY_DEFAULT / 2;
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* First channel should always be valid. */
|
|
|
|
/* If there is no ADEVICE, it uses default device in mono. */
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
p_audio_config->chan_medium[0] = MEDIUM_RADIO;
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2017-09-29 22:31:19 +00:00
|
|
|
memset (p_digi_config, 0, sizeof(struct digi_config_s)); // APRS digipeater
|
2015-07-27 00:35:07 +00:00
|
|
|
p_digi_config->dedupe_time = DEFAULT_DEDUPE;
|
2017-09-29 22:31:19 +00:00
|
|
|
memset (p_cdigi_config, 0, sizeof(struct cdigi_config_s)); // Connected mode digipeater
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
memset (p_tt_config, 0, sizeof(struct tt_config_s));
|
2015-07-27 01:17:23 +00:00
|
|
|
p_tt_config->gateway_enabled = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
p_tt_config->ttloc_size = 2; /* Start with at least 2. */
|
|
|
|
/* When full, it will be increased by 50 %. */
|
|
|
|
p_tt_config->ttloc_ptr = malloc (sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
|
|
|
p_tt_config->ttloc_len = 0;
|
|
|
|
|
|
|
|
/* Retention time and decay algorithm from 13 Feb 13 version of */
|
|
|
|
/* http://www.aprs.org/aprstt/aprstt-coding24.txt */
|
2015-09-07 23:56:20 +00:00
|
|
|
/* Reduced by transmit count by one. An 8 minute delay in between transmissions seems awful long. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
p_tt_config->retain_time = 80 * 60;
|
2015-09-07 23:56:20 +00:00
|
|
|
p_tt_config->num_xmits = 6;
|
2015-07-27 00:35:07 +00:00
|
|
|
assert (p_tt_config->num_xmits <= TT_MAX_XMITS);
|
|
|
|
p_tt_config->xmit_delay[0] = 3; /* Before initial transmission. */
|
|
|
|
p_tt_config->xmit_delay[1] = 16;
|
|
|
|
p_tt_config->xmit_delay[2] = 32;
|
|
|
|
p_tt_config->xmit_delay[3] = 64;
|
|
|
|
p_tt_config->xmit_delay[4] = 2 * 60;
|
|
|
|
p_tt_config->xmit_delay[5] = 4 * 60;
|
2015-09-07 23:56:20 +00:00
|
|
|
p_tt_config->xmit_delay[6] = 8 * 60; // not currently used.
|
|
|
|
|
|
|
|
strlcpy (p_tt_config->status[0], "", sizeof(p_tt_config->status[0]));
|
|
|
|
strlcpy (p_tt_config->status[1], "/off duty", sizeof(p_tt_config->status[1]));
|
|
|
|
strlcpy (p_tt_config->status[2], "/enroute", sizeof(p_tt_config->status[2]));
|
|
|
|
strlcpy (p_tt_config->status[3], "/in service", sizeof(p_tt_config->status[3]));
|
|
|
|
strlcpy (p_tt_config->status[4], "/returning", sizeof(p_tt_config->status[4]));
|
|
|
|
strlcpy (p_tt_config->status[5], "/committed", sizeof(p_tt_config->status[5]));
|
|
|
|
strlcpy (p_tt_config->status[6], "/special", sizeof(p_tt_config->status[6]));
|
|
|
|
strlcpy (p_tt_config->status[7], "/priority", sizeof(p_tt_config->status[7]));
|
|
|
|
strlcpy (p_tt_config->status[8], "/emergency", sizeof(p_tt_config->status[8]));
|
|
|
|
strlcpy (p_tt_config->status[9], "/custom 1", sizeof(p_tt_config->status[9]));
|
|
|
|
|
|
|
|
for (m = 0; m < TT_ERROR_MAXP1; m++) {
|
|
|
|
strlcpy (p_tt_config->response[m].method, "MORSE", sizeof(p_tt_config->response[m].method));
|
|
|
|
strlcpy (p_tt_config->response[m].mtext, "?", sizeof(p_tt_config->response[m].mtext));
|
|
|
|
}
|
|
|
|
strlcpy (p_tt_config->response[TT_ERROR_OK].mtext, "R", sizeof(p_tt_config->response[TT_ERROR_OK].mtext));
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
memset (p_misc_config, 0, sizeof(struct misc_config_s));
|
|
|
|
p_misc_config->agwpe_port = DEFAULT_AGWPE_PORT;
|
2021-01-05 00:43:00 +00:00
|
|
|
|
|
|
|
for (int i=0; i<MAX_KISS_TCP_PORTS; i++) {
|
|
|
|
p_misc_config->kiss_port[i] = 0; // entry not used.
|
|
|
|
p_misc_config->kiss_chan[i] = -1;
|
|
|
|
}
|
|
|
|
p_misc_config->kiss_port[0] = DEFAULT_KISS_PORT;
|
|
|
|
p_misc_config->kiss_chan[0] = -1; // all channels.
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
p_misc_config->enable_kiss_pt = 0; /* -p option */
|
2019-01-02 00:59:58 +00:00
|
|
|
p_misc_config->kiss_copy = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2020-12-27 13:48:46 +00:00
|
|
|
p_misc_config->dns_sd_enabled = 1;
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/* Defaults from http://info.aprs.net/index.php?title=SmartBeaconing */
|
|
|
|
|
|
|
|
p_misc_config->sb_configured = 0; /* TRUE if SmartBeaconing is configured. */
|
|
|
|
p_misc_config->sb_fast_speed = 60; /* MPH */
|
|
|
|
p_misc_config->sb_fast_rate = 180; /* seconds */
|
|
|
|
p_misc_config->sb_slow_speed = 5; /* MPH */
|
|
|
|
p_misc_config->sb_slow_rate = 1800; /* seconds */
|
|
|
|
p_misc_config->sb_turn_time = 15; /* seconds */
|
|
|
|
p_misc_config->sb_turn_angle = 30; /* degrees */
|
|
|
|
p_misc_config->sb_turn_slope = 255; /* degrees * MPH */
|
|
|
|
|
|
|
|
memset (p_igate_config, 0, sizeof(struct igate_config_s));
|
|
|
|
p_igate_config->t2_server_port = DEFAULT_IGATE_PORT;
|
|
|
|
p_igate_config->tx_chan = -1; /* IS->RF not enabled */
|
2015-11-18 13:09:45 +00:00
|
|
|
p_igate_config->tx_limit_1 = IGATE_TX_LIMIT_1_DEFAULT;
|
|
|
|
p_igate_config->tx_limit_5 = IGATE_TX_LIMIT_5_DEFAULT;
|
2017-01-01 16:49:55 +00:00
|
|
|
p_igate_config->igmsp = 1;
|
2017-10-29 01:30:04 +00:00
|
|
|
p_igate_config->rx2ig_dedupe_time = IGATE_RX2IG_DEDUPE_TIME;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* People find this confusing. */
|
|
|
|
/* Ideally we'd like to figure out if com0com is installed */
|
|
|
|
/* and automatically enable this. */
|
|
|
|
|
2017-05-03 21:41:37 +00:00
|
|
|
strlcpy (p_misc_config->kiss_serial_port, "", sizeof(p_misc_config->kiss_serial_port));
|
|
|
|
p_misc_config->kiss_serial_speed = 0;
|
|
|
|
p_misc_config->kiss_serial_poll = 0;
|
2016-03-20 23:23:09 +00:00
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_misc_config->gpsnmea_port, "", sizeof(p_misc_config->gpsnmea_port));
|
2020-05-27 01:20:37 +00:00
|
|
|
strlcpy (p_misc_config->waypoint_serial_port, "", sizeof(p_misc_config->waypoint_serial_port));
|
2016-03-20 23:23:09 +00:00
|
|
|
|
2017-06-17 23:39:59 +00:00
|
|
|
p_misc_config->log_daily_names = 0;
|
|
|
|
strlcpy (p_misc_config->log_path, "", sizeof(p_misc_config->log_path));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2016-11-20 19:58:51 +00:00
|
|
|
/* connected mode. */
|
|
|
|
|
|
|
|
p_misc_config->frack = AX25_T1V_FRACK_DEFAULT; /* Number of seconds to wait for ack to transmission. */
|
|
|
|
|
|
|
|
p_misc_config->retry = AX25_N2_RETRY_DEFAULT; /* Number of times to retry before giving up. */
|
|
|
|
|
|
|
|
p_misc_config->paclen = AX25_N1_PACLEN_DEFAULT; /* Max number of bytes in information part of frame. */
|
|
|
|
|
|
|
|
p_misc_config->maxframe_basic = AX25_K_MAXFRAME_BASIC_DEFAULT; /* Max frames to send before ACK. mod 8 "Window" size. */
|
|
|
|
|
|
|
|
p_misc_config->maxframe_extended = AX25_K_MAXFRAME_EXTENDED_DEFAULT; /* Max frames to send before ACK. mod 128 "Window" size. */
|
|
|
|
|
2019-10-01 01:55:48 +00:00
|
|
|
p_misc_config->maxv22 = AX25_N2_RETRY_DEFAULT / 3; /* Max SABME before falling back to SABM. */
|
2017-06-01 00:07:52 +00:00
|
|
|
p_misc_config->v20_addrs = NULL; /* Go directly to v2.0 for stations listed. */
|
|
|
|
p_misc_config->v20_count = 0;
|
2018-07-02 16:18:23 +00:00
|
|
|
p_misc_config->noxid_addrs = NULL; /* Don't send XID to these stations. */
|
|
|
|
p_misc_config->noxid_count = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to extract options from a file.
|
|
|
|
*
|
|
|
|
* Windows: File must be in current working directory.
|
|
|
|
*
|
|
|
|
* Linux: Search current directory then home directory.
|
2015-09-07 23:56:20 +00:00
|
|
|
*
|
|
|
|
* Future possibility - Could also search home directory
|
|
|
|
* for Windows by combinting two variables:
|
|
|
|
* HOMEDRIVE=C:
|
|
|
|
* HOMEPATH=\Users\John
|
|
|
|
*
|
|
|
|
* It's not clear if this always points to same location:
|
|
|
|
* USERPROFILE=C:\Users\John
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
channel = 0;
|
2015-07-27 01:17:23 +00:00
|
|
|
adevice = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
// TODO: Would be better to have a search list and loop thru it.
|
|
|
|
|
|
|
|
strlcpy(filepath, fname, sizeof(filepath));
|
|
|
|
|
|
|
|
fp = fopen (filepath, "r");
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
#ifndef __WIN32__
|
|
|
|
if (fp == NULL && strcmp(fname, "direwolf.conf") == 0) {
|
|
|
|
/* Failed to open the default location. Try home dir. */
|
|
|
|
char *p;
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
|
|
|
|
strlcpy (filepath, "", sizeof(filepath));
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
p = getenv("HOME");
|
|
|
|
if (p != NULL) {
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (filepath, p, sizeof(filepath));
|
|
|
|
strlcat (filepath, "/direwolf.conf", sizeof(filepath));
|
|
|
|
fp = fopen (filepath, "r");
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (fp == NULL) {
|
|
|
|
// TODO: not exactly right for all situations.
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-09-07 23:56:20 +00:00
|
|
|
dw_printf ("ERROR - Could not open config file %s\n", filepath);
|
2015-07-27 00:35:07 +00:00
|
|
|
dw_printf ("Try using -c command line option for alternate location.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
dw_printf ("\nReading config file %s\n", filepath);
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
line = 0;
|
|
|
|
while (fgets(stuff, sizeof(stuff), fp) != NULL) {
|
|
|
|
char *t;
|
|
|
|
|
|
|
|
line++;
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(stuff,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
if (t == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*t == '#' || *t == '*') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ==================== Audio device parameters ====================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2015-09-07 23:56:20 +00:00
|
|
|
* ADEVICE[n] - Name of input sound device, and optionally output, if different.
|
|
|
|
*
|
|
|
|
* ADEVICE plughw:1,0 -- same for in and out.
|
|
|
|
* ADEVICE plughw:2,0 plughw:3,0 -- different in/out for a channel or channel pair.
|
2018-10-08 13:49:18 +00:00
|
|
|
* ADEVICE1 udp:7355 default -- from Software defined radio (SDR) via UDP.
|
2015-09-07 23:56:20 +00:00
|
|
|
*
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Note that ALSA name can contain comma such as hw:1,0 */
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if (strncasecmp(t, "ADEVICE", 7) == 0) {
|
2018-10-08 13:49:18 +00:00
|
|
|
/* "ADEVICE" is equivalent to "ADEVICE0". */
|
2015-07-27 01:17:23 +00:00
|
|
|
adevice = 0;
|
2018-10-08 13:49:18 +00:00
|
|
|
if (strlen(t) >= 8) {
|
|
|
|
adevice = atoi(t+7);
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (adevice < 0 || adevice >= MAX_ADEVS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Device number %d out of range for ADEVICE command on line %d.\n", adevice, line);
|
2018-10-08 13:49:18 +00:00
|
|
|
dw_printf ("If you really need more than %d audio devices, increase MAX_ADEVS and recompile.\n", MAX_ADEVS);
|
2015-07-27 01:17:23 +00:00
|
|
|
adevice = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing name of audio device for ADEVICE command on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
p_audio_config->adev[adevice].defined = 1;
|
|
|
|
|
|
|
|
/* First channel of device is valid. */
|
2021-12-10 03:30:31 +00:00
|
|
|
p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO;
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in));
|
|
|
|
strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t != NULL) {
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out));
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* PAIDEVICE[n] input-device
|
|
|
|
* PAODEVICE[n] output-device
|
|
|
|
*
|
|
|
|
* This was submitted by KK5VD for the Mac OS X version. (__APPLE__)
|
|
|
|
*
|
|
|
|
* It looks like device names can contain spaces making it a little
|
|
|
|
* more difficult to put two names on the same line unless we come up with
|
|
|
|
* some other delimiter between them or a quoting scheme to handle
|
|
|
|
* embedded spaces in a name.
|
|
|
|
*
|
|
|
|
* It concerns me that we could have one defined without the other
|
|
|
|
* if we don't put in more error checking later.
|
|
|
|
*
|
|
|
|
* version 1.3 dev snapshot C:
|
|
|
|
*
|
|
|
|
* We now have a general quoting scheme so the original ADEVICE can handle this.
|
|
|
|
* These options will probably be removed before general 1.3 release.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "PAIDEVICE") == 0) {
|
|
|
|
adevice = 0;
|
|
|
|
if (isdigit(t[9])) {
|
|
|
|
adevice = t[9] - '0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adevice < 0 || adevice >= MAX_ADEVS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Device number %d out of range for PADEVICE command on line %d.\n", adevice, line);
|
|
|
|
adevice = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,1);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing name of audio device for PADEVICE command on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
p_audio_config->adev[adevice].defined = 1;
|
|
|
|
|
|
|
|
/* First channel of device is valid. */
|
2021-12-10 03:30:31 +00:00
|
|
|
p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO;
|
2015-09-07 23:56:20 +00:00
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in));
|
2015-09-07 23:56:20 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "PAODEVICE") == 0) {
|
|
|
|
adevice = 0;
|
|
|
|
if (isdigit(t[9])) {
|
|
|
|
adevice = t[9] - '0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adevice < 0 || adevice >= MAX_ADEVS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Device number %d out of range for PADEVICE command on line %d.\n", adevice, line);
|
|
|
|
adevice = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,1);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing name of audio device for PADEVICE command on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
p_audio_config->adev[adevice].defined = 1;
|
|
|
|
|
|
|
|
/* First channel of device is valid. */
|
2021-12-10 03:30:31 +00:00
|
|
|
p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO;
|
2015-09-07 23:56:20 +00:00
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out));
|
2015-09-07 23:56:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* ARATE - Audio samples per second, 11025, 22050, 44100, etc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "ARATE") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing audio sample rate for ARATE command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= MIN_SAMPLES_PER_SEC && n <= MAX_SAMPLES_PER_SEC) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->adev[adevice].samples_per_sec = n;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Use a more reasonable audio sample rate in range of %d - %d.\n",
|
|
|
|
line, MIN_SAMPLES_PER_SEC, MAX_SAMPLES_PER_SEC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-07-27 01:17:23 +00:00
|
|
|
* ACHANNELS - Number of audio channels for current device: 1 or 2
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "ACHANNELS") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing number of audio channels for ACHANNELS command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (n ==1 || n == 2) {
|
|
|
|
p_audio_config->adev[adevice].num_channels = n;
|
|
|
|
|
|
|
|
/* Set valid channels depending on mono or stereo. */
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO;
|
2015-07-27 01:17:23 +00:00
|
|
|
if (n == 2) {
|
2021-12-10 03:30:31 +00:00
|
|
|
p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice) + 1] = MEDIUM_RADIO;
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Number of audio channels must be 1 or 2.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ==================== Radio channel parameters ====================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2021-12-10 03:30:31 +00:00
|
|
|
* CHANNEL n - Set channel for channel-specific commands.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "CHANNEL") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing channel number for CHANNEL command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n < MAX_CHANS) {
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
channel = n;
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[n] != MEDIUM_RADIO) {
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
if ( ! p_audio_config->adev[ACHAN2ADEV(n)].defined) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Channel number %d is not valid because audio device %d is not defined.\n",
|
|
|
|
line, n, ACHAN2ADEV(n));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Channel number %d is not valid because audio device %d is not in stereo.\n",
|
|
|
|
line, n, ACHAN2ADEV(n));
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Line %d: Channel number must in range of 0 to %d.\n", line, MAX_CHANS-1);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
/*
|
|
|
|
* ICHANNEL n - Define IGate virtual channel.
|
|
|
|
*
|
|
|
|
* This allows a client application to talk to to APRS-IS
|
|
|
|
* by using a channel number outside the normal range for modems.
|
|
|
|
* In the future there might be other typs of virtual channels.
|
|
|
|
* This does not change the current channel number used by MODEM, PTT, etc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "ICHANNEL") == 0) {
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing virtual channel number for ICHANNEL command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int ichan = atoi(t);
|
|
|
|
if (ichan >= MAX_CHANS && ichan < MAX_TOTAL_CHANS) {
|
|
|
|
|
|
|
|
if (p_audio_config->chan_medium[ichan] == MEDIUM_NONE) {
|
|
|
|
|
|
|
|
p_audio_config->chan_medium[ichan] = MEDIUM_IGATE;
|
|
|
|
|
|
|
|
// This is redundant but saves the time of searching through all
|
|
|
|
// the channels for each packet.
|
|
|
|
p_audio_config->igate_vchannel = ichan;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: ICHANNEL can't use %d because it is already in use.\n", line, ichan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: ICHANNEL number must in range of %d to %d.\n", line, MAX_CHANS, MAX_TOTAL_CHANS-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* MYCALL station
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "mycall") == 0) {
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing value for MYCALL command on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
2017-06-01 00:07:52 +00:00
|
|
|
char *p;
|
|
|
|
int const strict = 2;
|
|
|
|
char call_no_ssid[AX25_MAX_ADDR_LEN];
|
|
|
|
int ssid, heard;
|
|
|
|
|
|
|
|
for (p = t; *p != '\0'; p++) {
|
|
|
|
if (islower(*p)) {
|
|
|
|
*p = toupper(*p); /* Silently force upper case. */
|
|
|
|
/* Might change to warning someday. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! ax25_parse_addr (-1, t, strict, call_no_ssid, &ssid, &heard)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Invalid value for MYCALL command on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:05:48 +00:00
|
|
|
// Definitely set for current channel.
|
|
|
|
// Set for other channels which have not been set yet.
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:05:48 +00:00
|
|
|
int c;
|
|
|
|
|
|
|
|
for (c = 0; c < MAX_CHANS; c++) {
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if (c == channel ||
|
|
|
|
strlen(p_audio_config->achan[c].mycall) == 0 ||
|
|
|
|
strcasecmp(p_audio_config->achan[c].mycall, "NOCALL") == 0 ||
|
|
|
|
strcasecmp(p_audio_config->achan[c].mycall, "N0CALL") == 0) {
|
2015-07-27 01:05:48 +00:00
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_audio_config->achan[c].mycall, t, sizeof(p_audio_config->achan[c].mycall));
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2015-07-27 01:17:23 +00:00
|
|
|
* MODEM - Set modem properties for current channel.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Old style:
|
|
|
|
* MODEM baud [ mark space [A][B][C][+] [ num-decoders spacing ] ]
|
|
|
|
*
|
|
|
|
* New style, version 1.2:
|
|
|
|
* MODEM speed [ option ] ...
|
2015-07-27 00:35:07 +00:00
|
|
|
*
|
2015-07-27 01:17:23 +00:00
|
|
|
* Options:
|
|
|
|
* mark:space - AFSK tones. Defaults based on speed.
|
|
|
|
* num@offset - Multiple decoders on different frequencies.
|
2019-01-21 16:07:20 +00:00
|
|
|
* /9 - Divide sample rate by specified number.
|
|
|
|
* *9 - Upsample ratio for G3RUH.
|
|
|
|
* [A-Z+-]+ - Letters, plus, minus for the demodulator "profile."
|
|
|
|
* g3ruh - This modem type regardless of default for speed.
|
2019-05-20 00:57:56 +00:00
|
|
|
* v26a or v26b - V.26 alternative. a=original, b=MFJ compatible
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "MODEM") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Line %d: Missing data transmission speed for MODEM command.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-04-19 04:50:18 +00:00
|
|
|
if (strcasecmp(t,"AIS") == 0) {
|
2020-06-21 01:15:44 +00:00
|
|
|
n = MAX_BAUD-1; // Hack - See special case later.
|
2020-04-19 04:50:18 +00:00
|
|
|
}
|
2020-06-14 01:33:10 +00:00
|
|
|
else if (strcasecmp(t,"EAS") == 0) {
|
2020-06-21 01:15:44 +00:00
|
|
|
n = MAX_BAUD-2; // Hack - See special case later.
|
2020-06-14 01:33:10 +00:00
|
|
|
}
|
2020-04-19 04:50:18 +00:00
|
|
|
else {
|
|
|
|
n = atoi(t);
|
|
|
|
}
|
2016-01-30 15:36:15 +00:00
|
|
|
if (n >= MIN_BAUD && n <= MAX_BAUD) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].baud = n;
|
2020-06-21 01:15:44 +00:00
|
|
|
if (n != 300 && n != 1200 && n != 2400 && n != 4800 && n != 9600 && n != 19200 && n != MAX_BAUD-1 && n != MAX_BAUD-2) {
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2016-05-01 22:46:47 +00:00
|
|
|
dw_printf ("Line %d: Warning: Non-standard data rate of %d bits per second. Are you sure?\n", line, n);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].baud = DEFAULT_BAUD;
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2016-05-01 22:46:47 +00:00
|
|
|
dw_printf ("Line %d: Unreasonable data rate. Using %d bits per second.\n",
|
2015-07-27 01:17:23 +00:00
|
|
|
line, p_audio_config->achan[channel].baud);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Set defaults based on speed. */
|
|
|
|
/* Should be same as -B command line option in direwolf.c. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2016-05-01 22:46:47 +00:00
|
|
|
/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
|
|
|
|
/* that need to be kept in sync. Maybe it could be a common function someday. */
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if (p_audio_config->achan[channel].baud < 600) {
|
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_AFSK;
|
|
|
|
p_audio_config->achan[channel].mark_freq = 1600;
|
|
|
|
p_audio_config->achan[channel].space_freq = 1800;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2016-05-01 22:46:47 +00:00
|
|
|
else if (p_audio_config->achan[channel].baud < 1800) {
|
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_AFSK;
|
|
|
|
p_audio_config->achan[channel].mark_freq = DEFAULT_MARK_FREQ;
|
|
|
|
p_audio_config->achan[channel].space_freq = DEFAULT_SPACE_FREQ;
|
|
|
|
}
|
|
|
|
else if (p_audio_config->achan[channel].baud < 3600) {
|
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_QPSK;
|
|
|
|
p_audio_config->achan[channel].mark_freq = 0;
|
|
|
|
p_audio_config->achan[channel].space_freq = 0;
|
|
|
|
}
|
|
|
|
else if (p_audio_config->achan[channel].baud < 7200) {
|
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_8PSK;
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].mark_freq = 0;
|
|
|
|
p_audio_config->achan[channel].space_freq = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2020-06-21 01:15:44 +00:00
|
|
|
else if (p_audio_config->achan[channel].baud == MAX_BAUD-1) {
|
2020-04-19 04:50:18 +00:00
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_AIS;
|
|
|
|
p_audio_config->achan[channel].mark_freq = 0;
|
|
|
|
p_audio_config->achan[channel].space_freq = 0;
|
|
|
|
}
|
2020-06-21 01:15:44 +00:00
|
|
|
else if (p_audio_config->achan[channel].baud == MAX_BAUD-2) {
|
2020-06-14 01:33:10 +00:00
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_EAS;
|
|
|
|
p_audio_config->achan[channel].baud = 521; // Actually 520.83 but we have an integer field here.
|
|
|
|
// Will make more precise in afsk demod init.
|
|
|
|
p_audio_config->achan[channel].mark_freq = 2083; // Actually 2083.3 - logic 1.
|
|
|
|
p_audio_config->achan[channel].space_freq = 1563; // Actually 1562.5 - logic 0.
|
2020-11-28 02:25:35 +00:00
|
|
|
// ? strlcpy (p_audio_config->achan[channel].profiles, "A", sizeof(p_audio_config->achan[channel].profiles));
|
2020-06-14 01:33:10 +00:00
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
else {
|
2016-05-01 22:46:47 +00:00
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
|
|
|
|
p_audio_config->achan[channel].mark_freq = 0;
|
|
|
|
p_audio_config->achan[channel].space_freq = 0;
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
|
2019-01-21 16:07:20 +00:00
|
|
|
/* Get any options. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
2015-07-27 01:17:23 +00:00
|
|
|
/* all done. */
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if (alldigits(t)) {
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* old style */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2019-01-21 16:07:20 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Old style (pre version 1.2) format will no longer be supported in next version.\n", line);
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
n = atoi(t);
|
|
|
|
/* Originally the upper limit was 3000. */
|
|
|
|
/* Version 1.0 increased to 5000 because someone */
|
|
|
|
/* wanted to use 2400/4800 Hz AFSK. */
|
|
|
|
/* Of course the MIC and SPKR connections won't */
|
|
|
|
/* have enough bandwidth so radios must be modified. */
|
|
|
|
if (n >= 300 && n <= 5000) {
|
|
|
|
p_audio_config->achan[channel].mark_freq = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_audio_config->achan[channel].mark_freq = DEFAULT_MARK_FREQ;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable mark tone frequency. Using %d.\n",
|
|
|
|
line, p_audio_config->achan[channel].mark_freq);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get space frequency */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing tone frequency for space.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 300 && n <= 5000) {
|
|
|
|
p_audio_config->achan[channel].space_freq = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_audio_config->achan[channel].space_freq = DEFAULT_SPACE_FREQ;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable space tone frequency. Using %d.\n",
|
|
|
|
line, p_audio_config->achan[channel].space_freq);
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Gently guide users toward new format. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if (p_audio_config->achan[channel].baud == 1200 &&
|
|
|
|
p_audio_config->achan[channel].mark_freq == 1200 &&
|
|
|
|
p_audio_config->achan[channel].space_freq == 2200) {
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: The AFSK frequencies can be omitted when using the 1200 baud default 1200:2200.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
if (p_audio_config->achan[channel].baud == 300 &&
|
|
|
|
p_audio_config->achan[channel].mark_freq == 1600 &&
|
|
|
|
p_audio_config->achan[channel].space_freq == 1800) {
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: The AFSK frequencies can be omitted when using the 300 baud default 1600:1800.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* New feature in 0.9 - Optional filter profile(s). */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
|
|
|
|
/* Look for some combination of letter(s) and + */
|
|
|
|
|
|
|
|
if (isalpha(t[0]) || t[0] == '+') {
|
|
|
|
char *pc;
|
|
|
|
|
|
|
|
/* Here we only catch something other than letters and + mixed in. */
|
|
|
|
/* Later, we check for valid letters and no more than one letter if + specified. */
|
|
|
|
|
|
|
|
for (pc = t; *pc != '\0'; pc++) {
|
2016-01-31 16:42:58 +00:00
|
|
|
if ( ! isalpha(*pc) && ! (*pc == '+')) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Demodulator type can only contain letters and + character.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles));
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (strlen(p_audio_config->achan[channel].profiles) > 1 && t != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Can't combine multiple demodulator types and multiple frequencies.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* New feature in 0.9 - optional number of decoders and frequency offset between. */
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
n = atoi(t);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (n < 1 || n > MAX_SUBCHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Number of demodulators is out of range. Using 3.\n", line);
|
|
|
|
n = 3;
|
|
|
|
}
|
|
|
|
p_audio_config->achan[channel].num_freq = n;
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
n = atoi(t);
|
|
|
|
if (n < 5 || n > abs(p_audio_config->achan[channel].mark_freq - p_audio_config->achan[channel].space_freq)/2) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable value for offset between modems. Using 50 Hz.\n", line);
|
|
|
|
n = 50;
|
|
|
|
}
|
|
|
|
p_audio_config->achan[channel].offset = n;
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Line %d: New style for multiple demodulators is %d@%d\n", line,
|
|
|
|
p_audio_config->achan[channel].num_freq, p_audio_config->achan[channel].offset);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing frequency offset between modems. Using 50 Hz.\n", line);
|
|
|
|
p_audio_config->achan[channel].offset = 50;
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
/* New style in version 1.2. */
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
while (t != NULL) {
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
if ((s = strchr(t, ':')) != NULL) { /* mark:space */
|
|
|
|
|
|
|
|
p_audio_config->achan[channel].mark_freq = atoi(t);
|
|
|
|
p_audio_config->achan[channel].space_freq = atoi(s+1);
|
|
|
|
|
|
|
|
if (p_audio_config->achan[channel].mark_freq == 0 && p_audio_config->achan[channel].space_freq == 0) {
|
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_AFSK;
|
|
|
|
|
|
|
|
if (p_audio_config->achan[channel].mark_freq < 300 || p_audio_config->achan[channel].mark_freq > 5000) {
|
|
|
|
p_audio_config->achan[channel].mark_freq = DEFAULT_MARK_FREQ;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable mark tone frequency. Using %d instead.\n",
|
|
|
|
line, p_audio_config->achan[channel].mark_freq);
|
|
|
|
}
|
|
|
|
if (p_audio_config->achan[channel].space_freq < 300 || p_audio_config->achan[channel].space_freq > 5000) {
|
|
|
|
p_audio_config->achan[channel].space_freq = DEFAULT_SPACE_FREQ;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable space tone frequency. Using %d instead.\n",
|
|
|
|
line, p_audio_config->achan[channel].space_freq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if ((s = strchr(t, '@')) != NULL) { /* num@offset */
|
|
|
|
|
|
|
|
p_audio_config->achan[channel].num_freq = atoi(t);
|
|
|
|
p_audio_config->achan[channel].offset = atoi(s+1);
|
|
|
|
|
|
|
|
if (p_audio_config->achan[channel].num_freq < 1 || p_audio_config->achan[channel].num_freq > MAX_SUBCHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Number of demodulators is out of range. Using 3.\n", line);
|
|
|
|
p_audio_config->achan[channel].num_freq = 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_audio_config->achan[channel].offset < 5 ||
|
|
|
|
p_audio_config->achan[channel].offset > abs(p_audio_config->achan[channel].mark_freq - p_audio_config->achan[channel].space_freq)/2) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Offset between demodulators is unreasonable. Using 50 Hz.\n", line);
|
|
|
|
p_audio_config->achan[channel].offset = 50;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (alllettersorpm(t)) { /* profile of letter(s) + - */
|
|
|
|
|
|
|
|
// Will be validated later.
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_audio_config->achan[channel].profiles, t, sizeof(p_audio_config->achan[channel].profiles));
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (*t == '/') { /* /div */
|
|
|
|
int n = atoi(t+1);
|
|
|
|
|
|
|
|
if (n >= 1 && n <= 8) {
|
|
|
|
p_audio_config->achan[channel].decimate = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Ignoring unreasonable sample rate division factor of %d.\n", line, n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-21 16:07:20 +00:00
|
|
|
else if (*t == '*') { /* *upsample */
|
|
|
|
int n = atoi(t+1);
|
|
|
|
|
|
|
|
if (n >= 1 && n <= 4) {
|
|
|
|
p_audio_config->achan[channel].upsample = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Ignoring unreasonable upsample ratio of %d.\n", line, n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "G3RUH") == 0) { /* Force G3RUH modem regardless of default for speed. New in 1.6. */
|
|
|
|
|
|
|
|
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
|
|
|
|
p_audio_config->achan[channel].mark_freq = 0;
|
|
|
|
p_audio_config->achan[channel].space_freq = 0;
|
|
|
|
}
|
|
|
|
|
2019-05-20 00:57:56 +00:00
|
|
|
else if (strcasecmp(t, "V26A") == 0 || /* Compatible with direwolf versions <= 1.5. New in 1.6. */
|
|
|
|
strcasecmp(t, "V26B") == 0) { /* Compatible with MFJ-2400. New in 1.6. */
|
|
|
|
|
|
|
|
if (p_audio_config->achan[channel].modem_type != MODEM_QPSK ||
|
|
|
|
p_audio_config->achan[channel].baud != 2400) {
|
|
|
|
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: %s option can only be used with 2400 bps PSK.\n", line, t);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p_audio_config->achan[channel].v26_alternative = (strcasecmp(t, "V26A") == 0) ? V26_A : V26_B;
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unrecognized option for MODEM: %s\n", line, t);
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* A later place catches disallowed combination of + and @. */
|
|
|
|
/* A later place sets /n for 300 baud if not specified by user. */
|
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
//dw_printf ("debug: div = %d\n", p_audio_config->achan[channel].decimate);
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* DTMF - Enable DTMF decoder.
|
|
|
|
*
|
|
|
|
* Future possibilities:
|
|
|
|
* Option to determine if it goes to APRStt gateway and/or application.
|
|
|
|
* Disable normal demodulator to reduce CPU requirements.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "DTMF") == 0) {
|
|
|
|
|
|
|
|
p_audio_config->achan[channel].dtmf_decode = DTMF_DECODE_ON;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FIX_BITS n [ APRS | AX25 | NONE ] [ PASSALL ]
|
|
|
|
*
|
2019-10-01 01:55:48 +00:00
|
|
|
* - Attempt to fix frames with bad FCS.
|
|
|
|
* - n is maximum number of bits to attempt fixing.
|
|
|
|
* - Optional sanity check & allow everything even with bad FCS.
|
2015-07-27 01:17:23 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "FIX_BITS") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing value for FIX_BITS command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
2015-11-29 15:44:30 +00:00
|
|
|
if (n >= RETRY_NONE && n < RETRY_MAX) { // MAX is actually last valid +1
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].fix_bits = (retry_t)n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-11-29 15:44:30 +00:00
|
|
|
dw_printf ("Line %d: Invalid value %d for FIX_BITS. Using default of %d.\n",
|
|
|
|
line, n, p_audio_config->achan[channel].fix_bits);
|
|
|
|
}
|
|
|
|
|
2019-10-01 01:55:48 +00:00
|
|
|
if (p_audio_config->achan[channel].fix_bits > DEFAULT_FIX_BITS) {
|
2015-11-29 15:44:30 +00:00
|
|
|
text_color_set(DW_COLOR_INFO);
|
|
|
|
dw_printf ("Line %d: Using a FIX_BITS value greater than %d is not recommended for normal operation.\n",
|
2019-10-01 01:55:48 +00:00
|
|
|
line, DEFAULT_FIX_BITS);
|
2022-03-23 23:07:31 +00:00
|
|
|
dw_printf ("FIX_BITS > 1 was an interesting experiment but turned out to be a bad idea.\n");
|
|
|
|
dw_printf ("Don't be surprised if it takes 100%% CPU, direwolf can't keep up with the audio stream,\n");
|
|
|
|
dw_printf ("and you see messages like \"Audio input device 0 error code -32: Broken pipe\"\n");
|
2015-11-29 15:44:30 +00:00
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
while (t != NULL) {
|
|
|
|
|
|
|
|
// If more than one sanity test, we silently take the last one.
|
|
|
|
|
|
|
|
if (strcasecmp(t, "APRS") == 0) {
|
|
|
|
p_audio_config->achan[channel].sanity_test = SANITY_APRS;
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "AX25") == 0 || strcasecmp(t, "AX.25") == 0) {
|
|
|
|
p_audio_config->achan[channel].sanity_test = SANITY_AX25;
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "NONE") == 0) {
|
|
|
|
p_audio_config->achan[channel].sanity_test = SANITY_NONE;
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "PASSALL") == 0) {
|
|
|
|
p_audio_config->achan[channel].passall = 1;
|
2015-12-06 15:09:27 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: There is an old saying, \"Be careful what you ask for because you might get it.\"\n", line);
|
|
|
|
dw_printf ("The PASSALL option means allow all frames even when they are invalid.\n");
|
|
|
|
dw_printf ("You are asking to receive random trash and you WILL get your wish.\n");
|
|
|
|
dw_printf ("Don't complain when you see all sorts of random garbage. That's what you asked for.\n");
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid option '%s' for FIX_BITS.\n", line, t);
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* PTT - Push To Talk signal line.
|
2015-07-27 01:17:23 +00:00
|
|
|
* DCD - Data Carrier Detect indicator.
|
2016-11-20 19:58:51 +00:00
|
|
|
* CON - Connected to another station indicator.
|
2015-07-27 00:35:07 +00:00
|
|
|
*
|
2015-07-27 01:17:23 +00:00
|
|
|
* xxx serial-port [-]rts-or-dtr [ [-]rts-or-dtr ]
|
|
|
|
* xxx GPIO [-]gpio-num
|
|
|
|
* xxx LPT [-]bit-num
|
2020-10-22 02:07:19 +00:00
|
|
|
* PTT RIG model port [ rate ]
|
|
|
|
* PTT RIG AUTO port [ rate ]
|
2017-10-09 22:00:59 +00:00
|
|
|
* PTT CM108 [ [-]bit-num ] [ hid-device ]
|
2016-02-27 20:42:26 +00:00
|
|
|
*
|
|
|
|
* When model is 2, port would host:port like 127.0.0.1:4532
|
|
|
|
* Otherwise, port would be a serial port like /dev/ttyS0
|
|
|
|
*
|
2015-07-27 01:17:23 +00:00
|
|
|
*
|
|
|
|
* Applies to most recent CHANNEL command.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
2016-11-20 19:58:51 +00:00
|
|
|
else if (strcasecmp(t, "PTT") == 0 || strcasecmp(t, "DCD") == 0 || strcasecmp(t, "CON") == 0) {
|
2015-07-27 01:17:23 +00:00
|
|
|
int ot;
|
|
|
|
char otname[8];
|
|
|
|
|
|
|
|
if (strcasecmp(t, "PTT") == 0) {
|
|
|
|
ot = OCTYPE_PTT;
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (otname, "PTT", sizeof(otname));
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "DCD") == 0) {
|
|
|
|
ot = OCTYPE_DCD;
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (otname, "DCD", sizeof(otname));
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
else {
|
2016-11-20 19:58:51 +00:00
|
|
|
ot = OCTYPE_CON;
|
|
|
|
strlcpy (otname, "CON", sizeof(otname));
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2018-01-04 03:43:38 +00:00
|
|
|
dw_printf ("Config file line %d: Missing output control device for %s command.\n",
|
2015-07-27 01:17:23 +00:00
|
|
|
line, otname);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:05:48 +00:00
|
|
|
if (strcasecmp(t, "GPIO") == 0) {
|
|
|
|
|
|
|
|
/* GPIO case, Linux only. */
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Config file line %d: %s with GPIO is only available on Linux.\n", line, otname);
|
2015-07-27 01:05:48 +00:00
|
|
|
#else
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:05:48 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Config file line %d: Missing GPIO number for %s.\n", line, otname);
|
2015-07-27 01:05:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*t == '-') {
|
2017-04-01 13:26:31 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t+1);
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 1;
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
else {
|
2017-04-01 13:26:31 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t);
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0;
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_GPIO;
|
2015-07-27 01:05:48 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "LPT") == 0) {
|
|
|
|
|
|
|
|
/* Parallel printer case, x86 Linux only. */
|
|
|
|
|
|
|
|
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:05:48 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Config file line %d: Missing LPT bit number for %s.\n", line, otname);
|
2015-07-27 01:05:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*t == '-') {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_lpt_bit = atoi(t+1);
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 1;
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_lpt_bit = atoi(t);
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0;
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_LPT;
|
2015-07-27 01:05:48 +00:00
|
|
|
#else
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Config file line %d: %s with LPT is only available on x86 Linux.\n", line, otname);
|
2015-07-27 01:05:48 +00:00
|
|
|
#endif
|
|
|
|
}
|
2015-12-26 22:08:57 +00:00
|
|
|
else if (strcasecmp(t, "RIG") == 0) {
|
2015-10-27 16:31:02 +00:00
|
|
|
#ifdef USE_HAMLIB
|
|
|
|
|
2016-01-31 22:47:41 +00:00
|
|
|
t = split(NULL,0);
|
2015-10-27 16:31:02 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2016-02-27 20:42:26 +00:00
|
|
|
dw_printf ("Config file line %d: Missing model number for hamlib.\n", line);
|
2015-10-27 16:31:02 +00:00
|
|
|
continue;
|
|
|
|
}
|
2016-02-27 20:42:26 +00:00
|
|
|
if (strcasecmp(t, "AUTO") == 0) {
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_model = -1;
|
|
|
|
}
|
|
|
|
else {
|
2020-01-19 23:42:50 +00:00
|
|
|
if ( ! alldigits(t)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: A rig number, not a name, is required here.\n", line);
|
|
|
|
dw_printf ("For example, if you have a Yaesu FT-847, specify 101.\n");
|
|
|
|
dw_printf ("See https://github.com/Hamlib/Hamlib/wiki/Supported-Radios for more details.\n");
|
|
|
|
continue;
|
|
|
|
}
|
2016-02-27 20:42:26 +00:00
|
|
|
int n = atoi(t);
|
|
|
|
if (n < 1 || n > 9999) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: Unreasonable model number %d for hamlib.\n", line, n);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_model = n;
|
|
|
|
}
|
2015-10-27 16:31:02 +00:00
|
|
|
|
2016-02-27 20:42:26 +00:00
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: Missing port for hamlib.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
strlcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, t, sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device));
|
|
|
|
|
2021-09-19 18:51:18 +00:00
|
|
|
// Optional serial port rate for CAT control PTT.
|
2020-10-22 02:07:19 +00:00
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
if ( ! alldigits(t)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: An optional number is required here for CAT serial port speed: %s\n", line, t);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int n = atoi(t);
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_rate = n;
|
|
|
|
}
|
|
|
|
|
2016-02-27 20:42:26 +00:00
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: %s was not expected after model & port for hamlib.\n", line, t);
|
|
|
|
}
|
|
|
|
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_HAMLIB;
|
2015-10-27 16:31:02 +00:00
|
|
|
|
|
|
|
#else
|
2017-07-01 01:57:04 +00:00
|
|
|
#if __WIN32__
|
2017-10-09 22:00:59 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: Windows version of direwolf does not support HAMLIB.\n", line);
|
|
|
|
exit (EXIT_FAILURE);
|
2017-07-01 01:57:04 +00:00
|
|
|
#else
|
2017-10-09 22:00:59 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: %s with RIG is only available when hamlib support is enabled.\n", line, otname);
|
2017-07-01 01:57:04 +00:00
|
|
|
dw_printf ("You must rebuild direwolf with hamlib support.\n");
|
|
|
|
dw_printf ("See User Guide for details.\n");
|
|
|
|
#endif
|
|
|
|
|
2017-10-09 22:00:59 +00:00
|
|
|
#endif
|
|
|
|
}
|
2018-01-04 03:43:38 +00:00
|
|
|
else if (strcasecmp(t, "CM108") == 0) {
|
2017-10-09 22:00:59 +00:00
|
|
|
|
2021-02-07 21:19:34 +00:00
|
|
|
/* CM108 - GPIO of USB sound card. case, Linux and Windows only. */
|
2017-10-09 22:00:59 +00:00
|
|
|
|
2021-02-07 21:19:34 +00:00
|
|
|
#if USE_CM108
|
2017-10-09 22:00:59 +00:00
|
|
|
|
|
|
|
if (ot != OCTYPE_PTT) {
|
|
|
|
// Future project: Allow DCD and CON via the same device.
|
|
|
|
// This gets more complicated because we can't selectively change a single GPIO bit.
|
|
|
|
// We would need to keep track of what is currently there, change one bit, in our local
|
|
|
|
// copy of the status and then write out the byte for all of the pins.
|
|
|
|
// Let's keep it simple with just PTT for the first stab at this.
|
|
|
|
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: PTT CM108 option is only valid for PTT, not %s.\n", line, otname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
p_audio_config->achan[channel].octrl[ot].out_gpio_num = 3; // All known designs use GPIO 3.
|
|
|
|
// User can override for special cases.
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0; // High for transmit.
|
|
|
|
strcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, "");
|
2017-10-19 00:50:59 +00:00
|
|
|
|
2017-10-09 22:00:59 +00:00
|
|
|
// Try to find PTT device for audio output device.
|
|
|
|
// Simplifiying assumption is that we have one radio per USB Audio Adapter.
|
|
|
|
// Failure at this point is not an error.
|
|
|
|
// See if config file sets it explicitly before complaining.
|
|
|
|
|
2017-10-19 00:50:59 +00:00
|
|
|
cm108_find_ptt (p_audio_config->adev[ACHAN2ADEV(channel)].adevice_out,
|
2017-10-09 22:00:59 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_device,
|
|
|
|
(int)sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device));
|
|
|
|
|
|
|
|
while ((t = split(NULL,0)) != NULL) {
|
|
|
|
if (*t == '-') {
|
|
|
|
p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t+1);
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 1;
|
|
|
|
}
|
|
|
|
else if (isdigit(*t)) {
|
|
|
|
p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t);
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0;
|
|
|
|
}
|
2021-04-11 00:46:13 +00:00
|
|
|
#if __WIN32__
|
|
|
|
else if (*t == '\\') {
|
|
|
|
strlcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, t, sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: Found \"%s\" when expecting GPIO number or device name like \\\\?\\hid#vid_0d8c&... .\n", line, t);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#else
|
2017-10-09 22:00:59 +00:00
|
|
|
else if (*t == '/') {
|
|
|
|
strlcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, t, sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: Found \"%s\" when expecting GPIO number or device name like /dev/hidraw1.\n", line, t);
|
|
|
|
continue;
|
|
|
|
}
|
2021-04-11 00:46:13 +00:00
|
|
|
#endif
|
2017-10-09 22:00:59 +00:00
|
|
|
}
|
|
|
|
if (p_audio_config->achan[channel].octrl[ot].out_gpio_num < 1 || p_audio_config->achan[channel].octrl[ot].out_gpio_num > 8) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: CM108 GPIO number %d is not in range of 1 thru 8.\n", line,
|
|
|
|
p_audio_config->achan[channel].octrl[ot].out_gpio_num);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strlen(p_audio_config->achan[channel].octrl[ot].ptt_device) == 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: Could not determine USB Audio GPIO PTT device for audio output %s.\n", line,
|
|
|
|
p_audio_config->adev[ACHAN2ADEV(channel)].adevice_out);
|
2021-02-07 21:19:34 +00:00
|
|
|
#if __WIN32__
|
|
|
|
dw_printf ("You must explicitly mention a HID path.\n");
|
|
|
|
#else
|
2017-10-09 22:00:59 +00:00
|
|
|
dw_printf ("You must explicitly mention a device name such as /dev/hidraw1.\n");
|
2021-02-07 21:19:34 +00:00
|
|
|
#endif
|
|
|
|
dw_printf ("Run \"cm108\" utility to get a list.\n");
|
|
|
|
dw_printf ("See Interface Guide for details.\n");
|
2017-10-09 22:00:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_CM108;
|
|
|
|
|
|
|
|
#else
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: %s with CM108 is only available when USB Audio GPIO support is enabled.\n", line, otname);
|
|
|
|
dw_printf ("You must rebuild direwolf with CM108 Audio Adapter GPIO PTT support.\n");
|
2021-02-07 21:19:34 +00:00
|
|
|
dw_printf ("See Interface Guide for details.\n");
|
|
|
|
|
2017-10-09 22:00:59 +00:00
|
|
|
exit (EXIT_FAILURE);
|
2015-10-27 16:31:02 +00:00
|
|
|
#endif
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
/* serial port case. */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, t, sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Config file line %d: Missing RTS or DTR after %s device name.\n",
|
|
|
|
line, otname);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp(t, "rts") == 0) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line = PTT_LINE_RTS;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "dtr") == 0) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line = PTT_LINE_DTR;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "-rts") == 0) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line = PTT_LINE_RTS;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 1;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "-dtr") == 0) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line = PTT_LINE_DTR;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert = 1;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Config file line %d: Expected RTS or DTR after %s device name.\n",
|
|
|
|
line, otname);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_SERIAL;
|
|
|
|
|
|
|
|
|
|
|
|
/* In version 1.2, we allow a second one for same serial port. */
|
|
|
|
/* Some interfaces want the two control lines driven with opposite polarity. */
|
|
|
|
/* e.g. PTT COM1 RTS -DTR */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
|
|
|
|
if (strcasecmp(t, "rts") == 0) {
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line2 = PTT_LINE_RTS;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert2 = 0;
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "dtr") == 0) {
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line2 = PTT_LINE_DTR;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert2 = 0;
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "-rts") == 0) {
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line2 = PTT_LINE_RTS;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert2 = 1;
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "-dtr") == 0) {
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_line2 = PTT_LINE_DTR;
|
|
|
|
p_audio_config->achan[channel].octrl[ot].ptt_invert2 = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: Expected RTS or DTR after first RTS or DTR.\n",
|
|
|
|
line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Would not make sense to specify the same one twice. */
|
|
|
|
|
|
|
|
if (p_audio_config->achan[channel].octrl[ot].ptt_line == p_audio_config->achan[channel].octrl[ot].ptt_line2) {
|
|
|
|
dw_printf ("Config file line %d: Doesn't make sense to specify the some control line twice.\n",
|
|
|
|
line);
|
|
|
|
}
|
|
|
|
|
|
|
|
} /* end of second serial port control line. */
|
|
|
|
} /* end of serial port case. */
|
|
|
|
|
2016-11-20 19:58:51 +00:00
|
|
|
} /* end of PTT, DCD, CON */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-11-17 16:27:31 +00:00
|
|
|
/*
|
|
|
|
* INPUTS
|
|
|
|
*
|
|
|
|
* TXINH - TX holdoff input
|
|
|
|
*
|
2017-10-19 00:50:59 +00:00
|
|
|
* TXINH GPIO [-]gpio-num (only type supported so far)
|
2015-11-17 16:27:31 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "TXINH") == 0) {
|
|
|
|
char itname[8];
|
|
|
|
|
|
|
|
strlcpy (itname, "TXINH", sizeof(itname));
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: Missing input type name for %s command.\n", line, itname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp(t, "GPIO") == 0) {
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: %s with GPIO is only available on Linux.\n", line, itname);
|
|
|
|
#else
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file line %d: Missing GPIO number for %s.\n", line, itname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*t == '-') {
|
2017-04-01 13:26:31 +00:00
|
|
|
p_audio_config->achan[channel].ictrl[ICTYPE_TXINH].in_gpio_num = atoi(t+1);
|
2016-03-20 23:23:09 +00:00
|
|
|
p_audio_config->achan[channel].ictrl[ICTYPE_TXINH].invert = 1;
|
2015-11-17 16:27:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2017-04-01 13:26:31 +00:00
|
|
|
p_audio_config->achan[channel].ictrl[ICTYPE_TXINH].in_gpio_num = atoi(t);
|
2016-03-20 23:23:09 +00:00
|
|
|
p_audio_config->achan[channel].ictrl[ICTYPE_TXINH].invert = 0;
|
2015-11-17 16:27:31 +00:00
|
|
|
}
|
2016-03-20 23:23:09 +00:00
|
|
|
p_audio_config->achan[channel].ictrl[ICTYPE_TXINH].method = PTT_METHOD_GPIO;
|
2015-11-17 16:27:31 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-10-27 16:31:02 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/*
|
2017-10-19 00:50:59 +00:00
|
|
|
* DWAIT n - Extra delay for receiver squelch. n = 10 mS units.
|
2015-07-27 01:17:23 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "DWAIT") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing delay time for DWAIT command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n <= 255) {
|
|
|
|
p_audio_config->achan[channel].dwait = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_audio_config->achan[channel].dwait = DEFAULT_DWAIT;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid delay time for DWAIT. Using %d.\n",
|
|
|
|
line, p_audio_config->achan[channel].dwait);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
2017-10-19 00:50:59 +00:00
|
|
|
* SLOTTIME n - For non-digipeat transmit delay timing. n = 10 mS units.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "SLOTTIME") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing delay time for SLOTTIME command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n <= 255) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].slottime = n;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME;
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid delay time for persist algorithm. Using %d.\n",
|
2015-07-27 01:17:23 +00:00
|
|
|
line, p_audio_config->achan[channel].slottime);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PERSIST - For non-digipeat transmit delay timing.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "PERSIST") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing probability for PERSIST command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n <= 255) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].persist = n;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].persist = DEFAULT_PERSIST;
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid probability for persist algorithm. Using %d.\n",
|
2015-07-27 01:17:23 +00:00
|
|
|
line, p_audio_config->achan[channel].persist);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-10-19 00:50:59 +00:00
|
|
|
* TXDELAY n - For transmit delay timing. n = 10 mS units.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "TXDELAY") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing time for TXDELAY command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n <= 255) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].txdelay = n;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].txdelay = DEFAULT_TXDELAY;
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid time for transmit delay. Using %d.\n",
|
2015-07-27 01:17:23 +00:00
|
|
|
line, p_audio_config->achan[channel].txdelay);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-10-19 00:50:59 +00:00
|
|
|
* TXTAIL n - For transmit timing. n = 10 mS units.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "TXTAIL") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing time for TXTAIL command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n <= 255) {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].txtail = n;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-07-27 01:17:23 +00:00
|
|
|
p_audio_config->achan[channel].txtail = DEFAULT_TXTAIL;
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid time for transmit timing. Using %d.\n",
|
2015-07-27 01:17:23 +00:00
|
|
|
line, p_audio_config->achan[channel].txtail);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-19 00:50:59 +00:00
|
|
|
/*
|
|
|
|
* FULLDUP {on|off} - Full Duplex
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "FULLDUP") == 0) {
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing parameter for FULLDUP command. Expecting ON or OFF.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcasecmp(t, "ON") == 0) {
|
|
|
|
p_audio_config->achan[channel].fulldup = 1;
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "OFF") == 0) {
|
|
|
|
p_audio_config->achan[channel].fulldup = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_audio_config->achan[channel].fulldup = 0;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Expected ON or OFF for FULLDUP.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/*
|
|
|
|
* SPEECH script
|
|
|
|
*
|
|
|
|
* Specify script for text-to-speech function.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "SPEECH") == 0) {
|
2015-11-08 01:57:02 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing script for Text-to-Speech function.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See if we can run it. */
|
|
|
|
|
|
|
|
if (xmit_speak_it(t, -1, " ") == 0) {
|
2015-09-07 23:56:20 +00:00
|
|
|
if (strlcpy (p_audio_config->tts_script, t, sizeof(p_audio_config->tts_script)) >= sizeof(p_audio_config->tts_script)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Script for text-to-speech function is too long.\n", line);
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Error trying to run Text-to-Speech function.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-19 04:50:18 +00:00
|
|
|
/*
|
|
|
|
* FX25TX n - Enable FX.25 transmission. Default off.
|
|
|
|
* 0 = off, 1 = auto mode, others are suggestions for testing
|
|
|
|
* or special cases. 16, 32, 64 is number of parity bytes to add.
|
|
|
|
* Also set by "-X n" command line option.
|
2021-10-22 21:29:20 +00:00
|
|
|
* V1.7 changed from global to per-channel setting.
|
2020-04-19 04:50:18 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "FX25TX") == 0) {
|
|
|
|
int n;
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing FEC mode for FX25TX command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n < 200) {
|
2021-10-22 21:29:20 +00:00
|
|
|
p_audio_config->achan[channel].fx25_strength = n;
|
|
|
|
p_audio_config->achan[channel].layer2_xmit = LAYER2_FX25;
|
2020-04-19 04:50:18 +00:00
|
|
|
}
|
|
|
|
else {
|
2021-10-22 21:29:20 +00:00
|
|
|
p_audio_config->achan[channel].fx25_strength = 1;
|
|
|
|
p_audio_config->achan[channel].layer2_xmit = LAYER2_FX25;
|
2020-04-19 04:50:18 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable value for FX.25 transmission mode. Using %d.\n",
|
2021-10-22 21:29:20 +00:00
|
|
|
line, p_audio_config->achan[channel].fx25_strength);
|
2020-04-19 04:50:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FX25AUTO n - Enable Automatic use of FX.25 for connected mode.
|
|
|
|
* Automatically enable, for that session only, when an identical
|
|
|
|
* frame is sent more than this number of times.
|
|
|
|
* Default 5 based on half of default RETRY.
|
|
|
|
* 0 to disable feature.
|
|
|
|
* Current a global setting. Could be per channel someday.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "FX25AUTO") == 0) {
|
|
|
|
int n;
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing count for FX25AUTO command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n < 20) {
|
|
|
|
p_audio_config->fx25_auto_enable = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_audio_config->fx25_auto_enable = AX25_N2_RETRY_DEFAULT / 2;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable count for connected mode automatic FX.25. Using %d.\n",
|
|
|
|
line, p_audio_config->fx25_auto_enable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-22 21:29:20 +00:00
|
|
|
/*
|
|
|
|
* IL2PTX [ + - ] [ 0 1 ] - Enable IL2P transmission. Default off.
|
|
|
|
* "+" means normal polarity. Redundant since it is the default.
|
|
|
|
* (command line -I for first channel)
|
|
|
|
* "-" means inverted polarity. Do not use for 1200 bps.
|
|
|
|
* (command line -i for first channel)
|
|
|
|
* "0" means weak FEC. Not recommended.
|
|
|
|
* "1" means stronger FEC. "Max FEC." Default if not specified.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "IL2PTX") == 0) {
|
|
|
|
|
|
|
|
p_audio_config->achan[channel].layer2_xmit = LAYER2_IL2P;
|
|
|
|
p_audio_config->achan[channel].il2p_max_fec = 1;
|
|
|
|
p_audio_config->achan[channel].il2p_invert_polarity = 0;
|
|
|
|
|
|
|
|
while ((t = split(NULL,0)) != NULL) {
|
|
|
|
for (char *c = t; *t != '\0'; c++) {
|
|
|
|
switch (*c) {
|
|
|
|
case '+':
|
|
|
|
p_audio_config->achan[channel].il2p_invert_polarity = 0;
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
p_audio_config->achan[channel].il2p_invert_polarity = 1;
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
p_audio_config->achan[channel].il2p_max_fec = 0;
|
|
|
|
break;
|
|
|
|
case '1':
|
|
|
|
p_audio_config->achan[channel].il2p_max_fec = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid parameter '%c' fol IL2PTX command.\n", line, *c);
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
2016-11-20 19:58:51 +00:00
|
|
|
* ==================== APRS Digipeater parameters ====================
|
2015-07-27 01:17:23 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2022-02-25 22:29:07 +00:00
|
|
|
* DIGIPEAT from-chan to-chan alias-pattern wide-pattern [ OFF|DROP|MARK|TRACE | ATGP=alias ]
|
2022-01-30 20:00:43 +00:00
|
|
|
*
|
2022-02-25 22:29:07 +00:00
|
|
|
* ATGP is an ugly hack for the specific need of ATGP which needs more that 8 digipeaters.
|
2022-01-30 20:00:43 +00:00
|
|
|
* DO NOT put this in the User Guide. On a need to know basis.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
2016-11-20 19:58:51 +00:00
|
|
|
else if (strcasecmp(t, "DIGIPEAT") == 0 || strcasecmp(t, "DIGIPEATER") == 0) {
|
2015-07-27 00:35:07 +00:00
|
|
|
int from_chan, to_chan;
|
|
|
|
int e;
|
|
|
|
char message[100];
|
|
|
|
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing FROM-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-10-01 01:55:48 +00:00
|
|
|
if ( ! alldigits(t)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: '%s' is not allowed for FROM-channel. It must be a number.\n",
|
|
|
|
line, t);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
from_chan = atoi(t);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (from_chan < 0 || from_chan >= MAX_CHANS) {
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n",
|
2015-07-27 01:17:23 +00:00
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-05-13 10:25:12 +00:00
|
|
|
|
|
|
|
// Channels specified must be radio channels or network TNCs.
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[from_chan] != MEDIUM_RADIO &&
|
|
|
|
p_audio_config->chan_medium[from_chan] != MEDIUM_NETTNC) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: FROM-channel %d is not valid.\n",
|
|
|
|
line, from_chan);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing TO-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-10-01 01:55:48 +00:00
|
|
|
if ( ! alldigits(t)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: '%s' is not allowed for TO-channel. It must be a number.\n",
|
|
|
|
line, t);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
to_chan = atoi(t);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (to_chan < 0 || to_chan >= MAX_CHANS) {
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n",
|
2015-07-27 01:17:23 +00:00
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-05-13 10:25:12 +00:00
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO &&
|
|
|
|
p_audio_config->chan_medium[to_chan] != MEDIUM_NETTNC) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: TO-channel %d is not valid.\n",
|
|
|
|
line, to_chan);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing alias pattern on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
e = regcomp (&(p_digi_config->alias[from_chan][to_chan]), t, REG_EXTENDED|REG_NOSUB);
|
|
|
|
if (e != 0) {
|
|
|
|
regerror (e, &(p_digi_config->alias[from_chan][to_chan]), message, sizeof(message));
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Invalid alias matching pattern on line %d:\n%s\n",
|
|
|
|
line, message);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing wide pattern on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
e = regcomp (&(p_digi_config->wide[from_chan][to_chan]), t, REG_EXTENDED|REG_NOSUB);
|
|
|
|
if (e != 0) {
|
|
|
|
regerror (e, &(p_digi_config->wide[from_chan][to_chan]), message, sizeof(message));
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Invalid wide matching pattern on line %d:\n%s\n",
|
|
|
|
line, message);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
p_digi_config->enabled[from_chan][to_chan] = 1;
|
|
|
|
p_digi_config->preempt[from_chan][to_chan] = PREEMPT_OFF;
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
if (strcasecmp(t, "OFF") == 0) {
|
|
|
|
p_digi_config->preempt[from_chan][to_chan] = PREEMPT_OFF;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "DROP") == 0) {
|
2017-10-29 01:30:04 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Preemptive digipeating DROP option is discouraged.\n", line);
|
|
|
|
dw_printf ("It can create a via path which is misleading about the actual path taken.\n");
|
|
|
|
dw_printf ("TRACE is the best choice for this feature.\n");
|
2015-07-27 00:35:07 +00:00
|
|
|
p_digi_config->preempt[from_chan][to_chan] = PREEMPT_DROP;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "MARK") == 0) {
|
2017-10-29 01:30:04 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Preemptive digipeating MARK option is discouraged.\n", line);
|
|
|
|
dw_printf ("It can create a via path which is misleading about the actual path taken.\n");
|
|
|
|
dw_printf ("TRACE is the best choice for this feature.\n");
|
2015-07-27 00:35:07 +00:00
|
|
|
p_digi_config->preempt[from_chan][to_chan] = PREEMPT_MARK;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "TRACE") == 0) {
|
|
|
|
p_digi_config->preempt[from_chan][to_chan] = PREEMPT_TRACE;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2022-02-25 22:29:07 +00:00
|
|
|
else if (strncasecmp(t, "ATGP=", 5) == 0) {
|
|
|
|
strlcpy (p_digi_config->atgp[from_chan][to_chan], t+5, sizeof(p_digi_config->atgp[from_chan][to_chan]));;
|
2022-01-30 20:00:43 +00:00
|
|
|
t = split(NULL,0);
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Found \"%s\" where end of line was expected.\n", line, t);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-10-01 01:55:48 +00:00
|
|
|
* DEDUPE - Time to suppress digipeating of duplicate APRS packets.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "DEDUPE") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing time for DEDUPE command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n < 600) {
|
|
|
|
p_digi_config->dedupe_time = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_digi_config->dedupe_time = DEFAULT_DEDUPE;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable value for dedupe time. Using %d.\n",
|
|
|
|
line, p_digi_config->dedupe_time);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:05:48 +00:00
|
|
|
/*
|
2015-07-27 01:17:23 +00:00
|
|
|
* REGEN - Signal regeneration.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "regen") == 0) {
|
|
|
|
int from_chan, to_chan;
|
|
|
|
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing FROM-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-10-01 01:55:48 +00:00
|
|
|
if ( ! alldigits(t)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: '%s' is not allowed for FROM-channel. It must be a number.\n",
|
|
|
|
line, t);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
from_chan = atoi(t);
|
|
|
|
if (from_chan < 0 || from_chan >= MAX_CHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n",
|
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-05-13 10:25:12 +00:00
|
|
|
|
|
|
|
// Only radio channels are valid for regenerate.
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[from_chan] != MEDIUM_RADIO) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: FROM-channel %d is not valid.\n",
|
|
|
|
line, from_chan);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing TO-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-10-01 01:55:48 +00:00
|
|
|
if ( ! alldigits(t)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: '%s' is not allowed for TO-channel. It must be a number.\n",
|
|
|
|
line, t);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
to_chan = atoi(t);
|
|
|
|
if (to_chan < 0 || to_chan >= MAX_CHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n",
|
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: TO-channel %d is not valid.\n",
|
|
|
|
line, to_chan);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
p_digi_config->regen[from_chan][to_chan] = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2016-11-20 19:58:51 +00:00
|
|
|
* ==================== Connected Digipeater parameters ====================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CDIGIPEAT from-chan to-chan [ alias-pattern ]
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "CDIGIPEAT") == 0 || strcasecmp(t, "CDIGIPEATER") == 0) {
|
|
|
|
int from_chan, to_chan;
|
|
|
|
int e;
|
|
|
|
char message[100];
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing FROM-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-10-01 01:55:48 +00:00
|
|
|
if ( ! alldigits(t)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: '%s' is not allowed for FROM-channel. It must be a number.\n",
|
|
|
|
line, t);
|
|
|
|
continue;
|
|
|
|
}
|
2016-11-20 19:58:51 +00:00
|
|
|
from_chan = atoi(t);
|
|
|
|
if (from_chan < 0 || from_chan >= MAX_CHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n",
|
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-05-13 10:25:12 +00:00
|
|
|
|
|
|
|
// For connected mode Link layer, only internal modems should be allowed.
|
|
|
|
// A network TNC probably would not provide information about channel status.
|
|
|
|
// There is discussion about this in the document called
|
|
|
|
// Why-is-9600-only-twice-as-fast-as-1200.pdf
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[from_chan] != MEDIUM_RADIO) {
|
2016-11-20 19:58:51 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: FROM-channel %d is not valid.\n",
|
|
|
|
line, from_chan);
|
2019-05-13 10:25:12 +00:00
|
|
|
dw_printf ("Only internal modems can be used for connected mode packet.\n");
|
2016-11-20 19:58:51 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing TO-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-10-01 01:55:48 +00:00
|
|
|
if ( ! alldigits(t)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: '%s' is not allowed for TO-channel. It must be a number.\n",
|
|
|
|
line, t);
|
|
|
|
continue;
|
|
|
|
}
|
2016-11-20 19:58:51 +00:00
|
|
|
to_chan = atoi(t);
|
|
|
|
if (to_chan < 0 || to_chan >= MAX_CHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n",
|
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO) {
|
2016-11-20 19:58:51 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: TO-channel %d is not valid.\n",
|
|
|
|
line, to_chan);
|
2019-05-13 10:25:12 +00:00
|
|
|
dw_printf ("Only internal modems can be used for connected mode packet.\n");
|
2016-11-20 19:58:51 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
e = regcomp (&(p_cdigi_config->alias[from_chan][to_chan]), t, REG_EXTENDED|REG_NOSUB);
|
2017-09-29 22:31:19 +00:00
|
|
|
if (e == 0) {
|
|
|
|
p_cdigi_config->has_alias[from_chan][to_chan] = 1;
|
|
|
|
}
|
|
|
|
else {
|
2016-11-20 19:58:51 +00:00
|
|
|
regerror (e, &(p_cdigi_config->alias[from_chan][to_chan]), message, sizeof(message));
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Invalid alias matching pattern on line %d:\n%s\n",
|
|
|
|
line, message);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
t = split(NULL,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
p_cdigi_config->enabled[from_chan][to_chan] = 1;
|
|
|
|
|
|
|
|
if (t != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Found \"%s\" where end of line was expected.\n", line, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ==================== Packet Filtering for APRS digipeater or IGate ====================
|
2015-07-27 01:17:23 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FILTER from-chan to-chan filter_specification_expression
|
|
|
|
* FILTER from-chan IG filter_specification_expression
|
|
|
|
* FILTER IG to-chan filter_specification_expression
|
2017-09-29 22:31:19 +00:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* Note that we have three different config file filter commands:
|
|
|
|
*
|
|
|
|
* FILTER - Originally for APRS digipeating but later enhanced
|
|
|
|
* to include IGate client side. Maybe it should be
|
|
|
|
* renamed AFILTER to make it clearer after adding CFILTER.
|
|
|
|
*
|
2019-05-13 10:25:12 +00:00
|
|
|
* Both internal modem and NET TNC channels allowed here.
|
|
|
|
* "IG" should be used for the IGate, NOT a virtual channel
|
|
|
|
* assigned to it.
|
|
|
|
*
|
2017-09-29 22:31:19 +00:00
|
|
|
* CFILTER - Similar for connected moded digipeater.
|
|
|
|
*
|
2019-05-13 10:25:12 +00:00
|
|
|
* Only internal modems can be used because they provide
|
|
|
|
* information about radio channel status.
|
|
|
|
* A remote network TNC might not provide the necessary
|
|
|
|
* status for correct operation.
|
|
|
|
* There is discussion about this in the document called
|
|
|
|
* Why-is-9600-only-twice-as-fast-as-1200.pdf
|
|
|
|
*
|
2021-09-19 18:51:18 +00:00
|
|
|
* IGFILTER - APRS-IS (IGate) server side - completely different.
|
2017-09-29 22:31:19 +00:00
|
|
|
* I'm not happy with this name because IG sounds like IGate
|
|
|
|
* which is really the client side. More comments later.
|
2019-05-13 10:25:12 +00:00
|
|
|
* Maybe it should be called subscribe or something like that
|
2021-09-19 18:51:18 +00:00
|
|
|
* because the subscriptions are cumulative.
|
2015-07-27 01:05:48 +00:00
|
|
|
*/
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
else if (strcasecmp(t, "FILTER") == 0) {
|
2015-07-27 01:05:48 +00:00
|
|
|
int from_chan, to_chan;
|
|
|
|
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:05:48 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing FROM-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
if (*t == 'i' || *t == 'I') {
|
|
|
|
from_chan = MAX_CHANS;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
from_chan = isdigit(*t) ? atoi(t) : -999;
|
|
|
|
if (from_chan < 0 || from_chan >= MAX_CHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Filter FROM-channel must be in range of 0 to %d or \"IG\" on line %d.\n",
|
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[from_chan] != MEDIUM_RADIO &&
|
|
|
|
p_audio_config->chan_medium[from_chan] != MEDIUM_NETTNC) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: FROM-channel %d is not valid.\n",
|
|
|
|
line, from_chan);
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[from_chan] == MEDIUM_IGATE) {
|
2019-05-13 10:25:12 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Use 'IG' rather than %d for FROM-channel.\n",
|
|
|
|
line, from_chan);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:05:48 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing TO-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
if (*t == 'i' || *t == 'I') {
|
|
|
|
to_chan = MAX_CHANS;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
to_chan = isdigit(*t) ? atoi(t) : -999;
|
|
|
|
if (to_chan < 0 || to_chan >= MAX_CHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Filter TO-channel must be in range of 0 to %d or \"IG\" on line %d.\n",
|
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO &&
|
|
|
|
p_audio_config->chan_medium[to_chan] != MEDIUM_NETTNC) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: TO-channel %d is not valid.\n",
|
|
|
|
line, to_chan);
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[to_chan] == MEDIUM_IGATE) {
|
2019-05-13 10:25:12 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Use 'IG' rather than %d for TO-channel.\n",
|
|
|
|
line, to_chan);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,1); /* Take rest of line including spaces. */
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
if (t == NULL) {
|
|
|
|
t = " "; /* Empty means permit nothing. */
|
|
|
|
}
|
|
|
|
|
2017-03-05 19:56:34 +00:00
|
|
|
if (p_digi_config->filter_str[from_chan][to_chan] != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Replacing previous filter for same from/to pair:\n %s\n",
|
|
|
|
line, p_digi_config->filter_str[from_chan][to_chan]);
|
|
|
|
free (p_digi_config->filter_str[from_chan][to_chan]);
|
|
|
|
p_digi_config->filter_str[from_chan][to_chan] = NULL;
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
p_digi_config->filter_str[from_chan][to_chan] = strdup(t);
|
|
|
|
|
2017-03-05 19:56:34 +00:00
|
|
|
//TODO: Do a test run to see errors now instead of waiting.
|
2015-07-27 01:05:48 +00:00
|
|
|
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2016-11-20 19:58:51 +00:00
|
|
|
/*
|
|
|
|
* ==================== Packet Filtering for connected digipeater ====================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CFILTER from-chan to-chan filter_specification_expression
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "CFILTER") == 0) {
|
|
|
|
int from_chan, to_chan;
|
|
|
|
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing FROM-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
from_chan = isdigit(*t) ? atoi(t) : -999;
|
|
|
|
if (from_chan < 0 || from_chan >= MAX_CHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Filter FROM-channel must be in range of 0 to %d on line %d.\n",
|
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-05-13 10:25:12 +00:00
|
|
|
// DO NOT allow a network TNC here.
|
|
|
|
// Must be internal modem to have necessary knowledge about channel status.
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[from_chan] != MEDIUM_RADIO) {
|
2016-11-20 19:58:51 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: FROM-channel %d is not valid.\n",
|
|
|
|
line, from_chan);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing TO-channel on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
to_chan = isdigit(*t) ? atoi(t) : -999;
|
|
|
|
if (to_chan < 0 || to_chan >= MAX_CHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Filter TO-channel must be in range of 0 to %d on line %d.\n",
|
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[to_chan] != MEDIUM_RADIO) {
|
2016-11-20 19:58:51 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: TO-channel %d is not valid.\n",
|
|
|
|
line, to_chan);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,1); /* Take rest of line including spaces. */
|
|
|
|
|
|
|
|
if (t == NULL) {
|
|
|
|
t = " "; /* Empty means permit nothing. */
|
|
|
|
}
|
|
|
|
|
2017-09-29 22:31:19 +00:00
|
|
|
p_cdigi_config->cfilter_str[from_chan][to_chan] = strdup(t);
|
2016-11-20 19:58:51 +00:00
|
|
|
|
|
|
|
//TODO1.2: Do a test run to see errors now instead of waiting.
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* ==================== APRStt gateway ====================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TTCORRAL - How to handle unknown positions
|
|
|
|
*
|
|
|
|
* TTCORRAL latitude longitude offset-or-ambiguity
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "TTCORRAL") == 0) {
|
2015-11-08 01:57:02 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing latitude for TTCORRAL command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p_tt_config->corral_lat = parse_ll(t,LAT,line);
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing longitude for TTCORRAL command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p_tt_config->corral_lon = parse_ll(t,LON,line);
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing longitude for TTCORRAL command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p_tt_config->corral_offset = parse_ll(t,LAT,line);
|
|
|
|
if (p_tt_config->corral_offset == 1 ||
|
|
|
|
p_tt_config->corral_offset == 2 ||
|
|
|
|
p_tt_config->corral_offset == 3) {
|
|
|
|
p_tt_config->corral_ambiguity = p_tt_config->corral_offset;
|
|
|
|
p_tt_config->corral_offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//dw_printf ("DEBUG: corral %f %f %f %d\n", p_tt_config->corral_lat,
|
|
|
|
// p_tt_config->corral_lon, p_tt_config->corral_offset, p_tt_config->corral_ambiguity);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TTPOINT - Define a point represented by touch tone sequence.
|
|
|
|
*
|
|
|
|
* TTPOINT pattern latitude longitude
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "TTPOINT") == 0) {
|
|
|
|
|
|
|
|
struct ttloc_s *tl;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
assert (p_tt_config->ttloc_size >= 2);
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
// Should make this a function/macro instead of repeating code.
|
2015-07-27 00:35:07 +00:00
|
|
|
/* Allocate new space, but first, if already full, make larger. */
|
|
|
|
if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
|
|
|
|
p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
|
|
|
|
p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
|
|
|
}
|
|
|
|
p_tt_config->ttloc_len++;
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
|
|
|
|
tl->type = TTLOC_POINT;
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy(tl->pattern, "", sizeof(tl->pattern));
|
2015-07-27 00:35:07 +00:00
|
|
|
tl->point.lat = 0;
|
|
|
|
tl->point.lon = 0;
|
|
|
|
|
|
|
|
/* Pattern: B and digits */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing pattern for TTPOINT command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (tl->pattern, t, sizeof(tl->pattern));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
if (t[0] != 'B') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTPOINT pattern must begin with upper case 'B'.\n", line);
|
|
|
|
}
|
2017-01-01 16:49:55 +00:00
|
|
|
for (j=1; j<(int)(strlen(t)); j++) {
|
2015-07-27 00:35:07 +00:00
|
|
|
if ( ! isdigit(t[j])) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTPOINT pattern must be B and digits only.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Latitude */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing latitude for TTPOINT command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tl->point.lat = parse_ll(t,LAT,line);
|
|
|
|
|
|
|
|
/* Longitude */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing longitude for TTPOINT command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tl->point.lon = parse_ll(t,LON,line);
|
|
|
|
|
|
|
|
/* temp debugging */
|
|
|
|
|
|
|
|
//for (j=0; j<p_tt_config->ttloc_len; j++) {
|
|
|
|
// dw_printf ("debug ttloc %d/%d %s\n", j, p_tt_config->ttloc_size,
|
|
|
|
// p_tt_config->ttloc_ptr[j].pattern);
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TTVECTOR - Touch tone location with bearing and distance.
|
|
|
|
*
|
|
|
|
* TTVECTOR pattern latitude longitude scale unit
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "TTVECTOR") == 0) {
|
|
|
|
|
|
|
|
struct ttloc_s *tl;
|
|
|
|
int j;
|
|
|
|
double scale;
|
|
|
|
double meters;
|
|
|
|
|
|
|
|
assert (p_tt_config->ttloc_size >= 2);
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
/* Allocate new space, but first, if already full, make larger. */
|
|
|
|
if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
|
|
|
|
p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
|
|
|
|
p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
|
|
|
}
|
|
|
|
p_tt_config->ttloc_len++;
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
|
|
|
|
tl->type = TTLOC_VECTOR;
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy(tl->pattern, "", sizeof(tl->pattern));
|
2015-07-27 00:35:07 +00:00
|
|
|
tl->vector.lat = 0;
|
|
|
|
tl->vector.lon = 0;
|
|
|
|
tl->vector.scale = 1;
|
|
|
|
|
|
|
|
/* Pattern: B5bbbd... */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing pattern for TTVECTOR command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (tl->pattern, t, sizeof(tl->pattern));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
if (t[0] != 'B') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTVECTOR pattern must begin with upper case 'B'.\n", line);
|
|
|
|
}
|
|
|
|
if (strncmp(t+1, "5bbb", 4) != 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTVECTOR pattern would normally contain \"5bbb\".\n", line);
|
|
|
|
}
|
2017-01-01 16:49:55 +00:00
|
|
|
for (j=1; j<(int)(strlen(t)); j++) {
|
2015-07-27 00:35:07 +00:00
|
|
|
if ( ! isdigit(t[j]) && t[j] != 'b' && t[j] != 'd') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTVECTOR pattern must contain only B, digits, b, and d.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Latitude */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing latitude for TTVECTOR command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tl->vector.lat = parse_ll(t,LAT,line);
|
|
|
|
|
|
|
|
/* Longitude */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing longitude for TTVECTOR command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tl->vector.lon = parse_ll(t,LON,line);
|
|
|
|
|
|
|
|
/* Longitude */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing scale for TTVECTOR command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
scale = atof(t);
|
|
|
|
|
|
|
|
/* Unit. */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing unit for TTVECTOR command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
meters = 0;
|
|
|
|
for (j=0; j<NUM_UNITS && meters == 0; j++) {
|
|
|
|
if (strcasecmp(units[j].name, t) == 0) {
|
|
|
|
meters = units[j].meters;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (meters == 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unrecognized unit for TTVECTOR command. Using miles.\n", line);
|
|
|
|
meters = 1609.344;
|
|
|
|
}
|
|
|
|
tl->vector.scale = scale * meters;
|
|
|
|
|
|
|
|
//dw_printf ("ttvector: %f meters\n", tl->vector.scale);
|
|
|
|
|
|
|
|
/* temp debugging */
|
|
|
|
|
|
|
|
//for (j=0; j<p_tt_config->ttloc_len; j++) {
|
|
|
|
// dw_printf ("debug ttloc %d/%d %s\n", j, p_tt_config->ttloc_size,
|
|
|
|
// p_tt_config->ttloc_ptr[j].pattern);
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TTGRID - Define a grid for touch tone locations.
|
|
|
|
*
|
|
|
|
* TTGRID pattern min-latitude min-longitude max-latitude max-longitude
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "TTGRID") == 0) {
|
|
|
|
|
|
|
|
struct ttloc_s *tl;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
assert (p_tt_config->ttloc_size >= 2);
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
/* Allocate new space, but first, if already full, make larger. */
|
|
|
|
if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
|
|
|
|
p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
|
|
|
|
p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
|
|
|
}
|
|
|
|
p_tt_config->ttloc_len++;
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
|
|
|
|
tl->type = TTLOC_GRID;
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy(tl->pattern, "", sizeof(tl->pattern));
|
2015-07-27 00:35:07 +00:00
|
|
|
tl->grid.lat0 = 0;
|
|
|
|
tl->grid.lon0 = 0;
|
|
|
|
tl->grid.lat9 = 0;
|
|
|
|
tl->grid.lon9 = 0;
|
|
|
|
|
|
|
|
/* Pattern: B [digit] x... y... */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing pattern for TTGRID command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (tl->pattern, t, sizeof(tl->pattern));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
if (t[0] != 'B') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTGRID pattern must begin with upper case 'B'.\n", line);
|
|
|
|
}
|
2017-01-01 16:49:55 +00:00
|
|
|
for (j=1; j<(int)(strlen(t)); j++) {
|
2015-07-27 00:35:07 +00:00
|
|
|
if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTGRID pattern must be B, optional digit, xxx, yyy.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Minimum Latitude - all zeros in received data */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2022-02-25 22:29:07 +00:00
|
|
|
dw_printf ("Line %d: Missing minimum latitude for TTGRID command.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tl->grid.lat0 = parse_ll(t,LAT,line);
|
|
|
|
|
|
|
|
/* Minimum Longitude - all zeros in received data */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2022-02-25 22:29:07 +00:00
|
|
|
dw_printf ("Line %d: Missing minimum longitude for TTGRID command.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tl->grid.lon0 = parse_ll(t,LON,line);
|
|
|
|
|
|
|
|
/* Maximum Latitude - all nines in received data */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2022-02-25 22:29:07 +00:00
|
|
|
dw_printf ("Line %d: Missing maximum latitude for TTGRID command.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tl->grid.lat9 = parse_ll(t,LAT,line);
|
|
|
|
|
|
|
|
/* Maximum Longitude - all nines in received data */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2022-02-25 22:29:07 +00:00
|
|
|
dw_printf ("Line %d: Missing maximum longitude for TTGRID command.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
2022-02-25 22:29:07 +00:00
|
|
|
tl->grid.lon9 = parse_ll(t,LON,line);
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
/* temp debugging */
|
|
|
|
|
2022-02-25 22:29:07 +00:00
|
|
|
// dw_printf ("CONFIG TTGRID min %f %f\n", tl->grid.lat0, tl->grid.lon0);
|
|
|
|
// dw_printf ("CONFIG TTGRID max %f %f\n", tl->grid.lat9, tl->grid.lon9);
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
//for (j=0; j<p_tt_config->ttloc_len; j++) {
|
|
|
|
// dw_printf ("debug ttloc %d/%d %s\n", j, p_tt_config->ttloc_size,
|
|
|
|
// p_tt_config->ttloc_ptr[j].pattern);
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TTUTM - Specify UTM zone for touch tone locations.
|
|
|
|
*
|
|
|
|
* TTUTM pattern zone [ scale [ x-offset y-offset ] ]
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "TTUTM") == 0) {
|
|
|
|
|
|
|
|
struct ttloc_s *tl;
|
|
|
|
int j;
|
2015-07-27 01:17:23 +00:00
|
|
|
double dlat, dlon;
|
|
|
|
long lerr;
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
assert (p_tt_config->ttloc_size >= 2);
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
/* Allocate new space, but first, if already full, make larger. */
|
|
|
|
if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
|
|
|
|
p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
|
|
|
|
p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
|
|
|
}
|
|
|
|
p_tt_config->ttloc_len++;
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
|
|
|
|
tl->type = TTLOC_UTM;
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy(tl->pattern, "", sizeof(tl->pattern));
|
2015-07-27 01:17:23 +00:00
|
|
|
tl->utm.lzone = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
tl->utm.scale = 1;
|
|
|
|
tl->utm.x_offset = 0;
|
|
|
|
tl->utm.y_offset = 0;
|
|
|
|
|
|
|
|
/* Pattern: B [digit] x... y... */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing pattern for TTUTM command.\n", line);
|
2015-07-27 01:17:23 +00:00
|
|
|
p_tt_config->ttloc_len--;
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (tl->pattern, t, sizeof(tl->pattern));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
if (t[0] != 'B') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTUTM pattern must begin with upper case 'B'.\n", line);
|
2015-07-27 01:17:23 +00:00
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2017-01-01 16:49:55 +00:00
|
|
|
for (j=1; j < (int)(strlen(t)); j++) {
|
2015-07-27 00:35:07 +00:00
|
|
|
if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTUTM pattern must be B, optional digit, xxx, yyy.\n", line);
|
2015-07-27 01:17:23 +00:00
|
|
|
// Bail out somehow. continue would match inner for.
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Zone 1 - 60 and optional latitudinal letter. */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing zone for TTUTM command.\n", line);
|
2015-07-27 01:17:23 +00:00
|
|
|
p_tt_config->ttloc_len--;
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
tl->utm.lzone = parse_utm_zone (t, &(tl->utm.latband), &(tl->utm.hemi));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Optional scale. */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
|
|
|
|
tl->utm.scale = atof(t);
|
|
|
|
|
|
|
|
/* Optional x offset. */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
|
|
|
|
tl->utm.x_offset = atof(t);
|
|
|
|
|
|
|
|
/* Optional y offset. */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
|
|
|
|
tl->utm.y_offset = atof(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Practice run to see if conversion might fail later with actual location. */
|
|
|
|
|
|
|
|
lerr = Convert_UTM_To_Geodetic(tl->utm.lzone, tl->utm.hemi,
|
|
|
|
tl->utm.x_offset + 5 * tl->utm.scale,
|
|
|
|
tl->utm.y_offset + 5 * tl->utm.scale,
|
|
|
|
&dlat, &dlon);
|
|
|
|
|
|
|
|
if (lerr != 0) {
|
|
|
|
char message [300];
|
|
|
|
|
|
|
|
utm_error_string (lerr, message);
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Line %d: Invalid UTM location: \n%s\n", line, message);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TTUSNG, TTMGRS - Specify zone/square for touch tone locations.
|
|
|
|
*
|
|
|
|
* TTUSNG pattern zone_square
|
|
|
|
* TTMGRS pattern zone_square
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "TTUSNG") == 0 || strcasecmp(t, "TTMGRS") == 0) {
|
|
|
|
|
|
|
|
struct ttloc_s *tl;
|
|
|
|
int j;
|
|
|
|
int num_x, num_y;
|
|
|
|
double lat, lon;
|
|
|
|
long lerr;
|
|
|
|
char message[300];
|
|
|
|
|
|
|
|
assert (p_tt_config->ttloc_size >= 2);
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
/* Allocate new space, but first, if already full, make larger. */
|
|
|
|
if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
|
|
|
|
p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
|
|
|
|
p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
|
|
|
}
|
|
|
|
p_tt_config->ttloc_len++;
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
|
|
|
|
|
|
|
|
// TODO1.2: in progress...
|
|
|
|
if (strcasecmp(t, "TTMGRS") == 0) {
|
|
|
|
tl->type = TTLOC_MGRS;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tl->type = TTLOC_USNG;
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy(tl->pattern, "", sizeof(tl->pattern));
|
|
|
|
strlcpy(tl->mgrs.zone, "", sizeof(tl->mgrs.zone));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Pattern: B [digit] x... y... */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing pattern for TTUSNG/TTMGRS command.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (tl->pattern, t, sizeof(tl->pattern));
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
if (t[0] != 'B') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTUSNG/TTMGRS pattern must begin with upper case 'B'.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
num_x = 0;
|
|
|
|
num_y = 0;
|
2017-01-01 16:49:55 +00:00
|
|
|
for (j=1; j<(int)(strlen(t)); j++) {
|
2015-07-27 01:17:23 +00:00
|
|
|
if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTUSNG/TTMGRS pattern must be B, optional digit, xxx, yyy.\n", line);
|
|
|
|
// Bail out somehow. continue would match inner for.
|
|
|
|
}
|
|
|
|
if (t[j] == 'x') num_x++;
|
|
|
|
if (t[j] == 'y') num_y++;
|
|
|
|
}
|
|
|
|
if (num_x < 1 || num_x > 5 || num_x != num_y) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTUSNG/TTMGRS must have 1 to 5 x and same number y.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Zone 1 - 60 and optional latitudinal letter. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing zone & square for TTUSNG/TTMGRS command.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (tl->mgrs.zone, t, sizeof(tl->mgrs.zone));
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
/* Try converting it rather do our own error checking. */
|
|
|
|
|
|
|
|
if (tl->type == TTLOC_MGRS) {
|
|
|
|
lerr = Convert_MGRS_To_Geodetic (tl->mgrs.zone, &lat, &lon);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lerr = Convert_USNG_To_Geodetic (tl->mgrs.zone, &lat, &lon);
|
|
|
|
}
|
|
|
|
if (lerr != 0) {
|
|
|
|
|
|
|
|
mgrs_error_string (lerr, message);
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid USNG/MGRS zone & square: %s\n%s\n", line, tl->mgrs.zone, message);
|
|
|
|
p_tt_config->ttloc_len--;
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Should be the end. */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unexpected stuff at end ignored: %s\n", line, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
/*
|
|
|
|
* TTMHEAD - Define pattern to be used for Maidenhead Locator.
|
|
|
|
*
|
|
|
|
* TTMHEAD pattern [ prefix ]
|
|
|
|
*
|
|
|
|
* Pattern would be B[0-9A-D]xxxx...
|
|
|
|
* Optional prefix is 10, 6, or 4 digits.
|
|
|
|
*
|
|
|
|
* The total number of digts in both must be 4, 6, 10, or 12.
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "TTMHEAD") == 0) {
|
|
|
|
|
|
|
|
// TODO1.3: TTMHEAD needs testing.
|
|
|
|
|
|
|
|
struct ttloc_s *tl;
|
|
|
|
int j;
|
|
|
|
int k;
|
|
|
|
int count_x;
|
|
|
|
int count_other;
|
|
|
|
|
|
|
|
|
|
|
|
assert (p_tt_config->ttloc_size >= 2);
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
/* Allocate new space, but first, if already full, make larger. */
|
|
|
|
if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
|
|
|
|
p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
|
|
|
|
p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
|
|
|
}
|
|
|
|
p_tt_config->ttloc_len++;
|
|
|
|
assert (p_tt_config->ttloc_len > 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
|
|
|
|
tl->type = TTLOC_MHEAD;
|
|
|
|
strlcpy(tl->pattern, "", sizeof(tl->pattern));
|
|
|
|
strlcpy(tl->mhead.prefix, "", sizeof(tl->mhead.prefix));
|
|
|
|
|
|
|
|
/* Pattern: B, optional additional button, some number of xxxx... for matching */
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing pattern for TTMHEAD command.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
strlcpy (tl->pattern, t, sizeof(tl->pattern));
|
|
|
|
|
|
|
|
if (t[0] != 'B') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTMHEAD pattern must begin with upper case 'B'.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Optionally one of 0-9ABCD */
|
|
|
|
|
|
|
|
if (strchr("ABCD", t[1]) != NULL || isdigit(t[1])) {
|
|
|
|
j = 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
j = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
count_x = 0;
|
|
|
|
count_other = 0;
|
2017-01-01 16:49:55 +00:00
|
|
|
for (k = j ; k < (int)(strlen(t)); k++) {
|
2015-09-07 23:56:20 +00:00
|
|
|
if (t[k] == 'x') count_x++;
|
|
|
|
else count_other++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count_other != 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTMHEAD must have only lower case x to match received data.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// optional prefix
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
char mh[30];
|
|
|
|
|
|
|
|
strlcpy(tl->mhead.prefix, t, sizeof(tl->mhead.prefix));
|
|
|
|
|
|
|
|
if (!alldigits(t) || (strlen(t) != 4 && strlen(t) != 6 && strlen(t) != 10)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTMHEAD prefix must be 4, 6, or 10 digits.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
2015-11-08 01:57:02 +00:00
|
|
|
if (tt_mhead_to_text(t, 0, mh, sizeof(mh)) != 0) {
|
2015-09-07 23:56:20 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTMHEAD prefix not a valid DTMF sequence.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
k = strlen(tl->mhead.prefix) + count_x;
|
|
|
|
|
|
|
|
if (k != 4 && k != 6 && k != 10 && k != 12 ) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTMHEAD prefix and user data must have a total of 4, 6, 10, or 12 digits.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* TTSATSQ - Define pattern to be used for Satellite square.
|
|
|
|
*
|
|
|
|
* TTSATSQ pattern
|
|
|
|
*
|
|
|
|
* Pattern would be B[0-9A-D]xxxx
|
2015-09-07 23:56:20 +00:00
|
|
|
*
|
|
|
|
* Must have exactly 4 x.
|
2015-07-27 01:17:23 +00:00
|
|
|
*/
|
2015-09-07 23:56:20 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
else if (strcasecmp(t, "TTSATSQ") == 0) {
|
|
|
|
|
|
|
|
// TODO1.2: TTSATSQ To be continued...
|
|
|
|
|
|
|
|
struct ttloc_s *tl;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
assert (p_tt_config->ttloc_size >= 2);
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
/* Allocate new space, but first, if already full, make larger. */
|
|
|
|
if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
|
|
|
|
p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
|
|
|
|
p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
p_tt_config->ttloc_len++;
|
|
|
|
assert (p_tt_config->ttloc_len > 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
|
|
|
|
tl->type = TTLOC_SATSQ;
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy(tl->pattern, "", sizeof(tl->pattern));
|
2015-07-27 01:17:23 +00:00
|
|
|
tl->point.lat = 0;
|
|
|
|
tl->point.lon = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Pattern: B, optional additional button, exactly xxxx for matching */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing pattern for TTSATSQ command.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (tl->pattern, t, sizeof(tl->pattern));
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
if (t[0] != 'B') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTSATSQ pattern must begin with upper case 'B'.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Optionally one of 0-9ABCD */
|
|
|
|
|
|
|
|
if (strchr("ABCD", t[1]) != NULL || isdigit(t[1])) {
|
|
|
|
j = 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
j = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(t+j, "xxxx") != 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTSATSQ pattern must end with exactly xxxx in lower case.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
/* temp debugging */
|
|
|
|
|
|
|
|
//for (j=0; j<p_tt_config->ttloc_len; j++) {
|
|
|
|
// dw_printf ("debug ttloc %d/%d %s\n", j, p_tt_config->ttloc_size,
|
|
|
|
// p_tt_config->ttloc_ptr[j].pattern);
|
|
|
|
//}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2015-12-06 15:09:27 +00:00
|
|
|
/*
|
|
|
|
* TTAMBIG - Define pattern to be used for Object Location Ambiguity.
|
|
|
|
*
|
|
|
|
* TTAMBIG pattern
|
|
|
|
*
|
|
|
|
* Pattern would be B[0-9A-D]x
|
|
|
|
*
|
|
|
|
* Must have exactly one x.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "TTAMBIG") == 0) {
|
|
|
|
|
|
|
|
// TODO1.3: TTAMBIG To be continued...
|
|
|
|
|
|
|
|
struct ttloc_s *tl;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
assert (p_tt_config->ttloc_size >= 2);
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
/* Allocate new space, but first, if already full, make larger. */
|
|
|
|
if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
|
|
|
|
p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
|
|
|
|
p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
|
|
|
}
|
|
|
|
p_tt_config->ttloc_len++;
|
|
|
|
assert (p_tt_config->ttloc_len > 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
|
|
|
|
tl->type = TTLOC_AMBIG;
|
|
|
|
strlcpy(tl->pattern, "", sizeof(tl->pattern));
|
|
|
|
|
|
|
|
/* Pattern: B, optional additional button, exactly x for matching */
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing pattern for TTAMBIG command.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
strlcpy (tl->pattern, t, sizeof(tl->pattern));
|
|
|
|
|
|
|
|
if (t[0] != 'B') {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTAMBIG pattern must begin with upper case 'B'.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Optionally one of 0-9ABCD */
|
|
|
|
|
|
|
|
if (strchr("ABCD", t[1]) != NULL || isdigit(t[1])) {
|
|
|
|
j = 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
j = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(t+j, "x") != 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTAMBIG pattern must end with exactly one x in lower case.\n", line);
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* temp debugging */
|
|
|
|
|
|
|
|
//for (j=0; j<p_tt_config->ttloc_len; j++) {
|
|
|
|
// dw_printf ("debug ttloc %d/%d %s\n", j, p_tt_config->ttloc_size,
|
|
|
|
// p_tt_config->ttloc_ptr[j].pattern);
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* TTMACRO - Define compact message format with full expansion
|
|
|
|
*
|
|
|
|
* TTMACRO pattern definition
|
2015-07-27 01:17:23 +00:00
|
|
|
*
|
|
|
|
* pattern can contain:
|
|
|
|
* 0-9 which must match exactly.
|
|
|
|
* In version 1.2, also allow A,B,C,D for exact match.
|
|
|
|
* x, y, z which are used for matching of variable fields.
|
|
|
|
*
|
|
|
|
* definition can contain:
|
|
|
|
* 0-9, A, B, C, D, *, #, x, y, z.
|
|
|
|
* Not sure why # was included in there.
|
2015-09-07 23:56:20 +00:00
|
|
|
*
|
|
|
|
* new for version 1.3 - in progress
|
|
|
|
*
|
|
|
|
* AA{objname}
|
|
|
|
* AB{symbol}
|
|
|
|
* AC{call}
|
|
|
|
*
|
|
|
|
* These provide automatic conversion from plain text to the TT encoding.
|
|
|
|
*
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "TTMACRO") == 0) {
|
|
|
|
|
|
|
|
struct ttloc_s *tl;
|
|
|
|
int j;
|
|
|
|
int p_count[3], d_count[3];
|
2015-09-07 23:56:20 +00:00
|
|
|
int tt_error = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
assert (p_tt_config->ttloc_size >= 2);
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
/* Allocate new space, but first, if already full, make larger. */
|
|
|
|
if (p_tt_config->ttloc_len == p_tt_config->ttloc_size) {
|
|
|
|
p_tt_config->ttloc_size += p_tt_config->ttloc_size / 2;
|
|
|
|
p_tt_config->ttloc_ptr = realloc (p_tt_config->ttloc_ptr, sizeof(struct ttloc_s) * p_tt_config->ttloc_size);
|
|
|
|
}
|
|
|
|
p_tt_config->ttloc_len++;
|
|
|
|
assert (p_tt_config->ttloc_len >= 0 && p_tt_config->ttloc_len <= p_tt_config->ttloc_size);
|
|
|
|
|
|
|
|
tl = &(p_tt_config->ttloc_ptr[p_tt_config->ttloc_len-1]);
|
|
|
|
tl->type = TTLOC_MACRO;
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy(tl->pattern, "", sizeof(tl->pattern));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
/* Pattern: Any combination of digits, x, y, and z. */
|
2021-09-19 18:51:18 +00:00
|
|
|
/* Also make note of which letters are used in pattern and definition. */
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Version 1.2: also allow A,B,C,D in the pattern. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing pattern for TTMACRO command.\n", line);
|
2015-09-07 23:56:20 +00:00
|
|
|
p_tt_config->ttloc_len--;
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (tl->pattern, t, sizeof(tl->pattern));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
p_count[0] = p_count[1] = p_count[2] = 0;
|
|
|
|
|
2017-01-01 16:49:55 +00:00
|
|
|
for (j=0; j<(int)(strlen(t)); j++) {
|
2015-07-27 01:17:23 +00:00
|
|
|
if ( strchr ("0123456789ABCDxyz", t[j]) == NULL) {
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Line %d: TTMACRO pattern can contain only digits, A, B, C, D, and lower case x, y, or z.\n", line);
|
2015-09-07 23:56:20 +00:00
|
|
|
p_tt_config->ttloc_len--;
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Count how many x, y, z in the pattern. */
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t[j] >= 'x' && t[j] <= 'z') {
|
|
|
|
p_count[t[j]-'x']++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
//text_color_set(DW_COLOR_DEBUG);
|
|
|
|
//dw_printf ("Line %d: TTMACRO pattern \"%s\" p_count = %d %d %d.\n", line, t, p_count[0], p_count[1], p_count[2]);
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
/* Next we should find the definition. */
|
2015-07-27 00:35:07 +00:00
|
|
|
/* It can contain touch tone characters and lower case x, y, z for substitutions. */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,1);;
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing definition for TTMACRO command.\n", line);
|
|
|
|
tl->macro.definition = ""; /* Don't die on null pointer later. */
|
2015-09-07 23:56:20 +00:00
|
|
|
p_tt_config->ttloc_len--;
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
/* Make a pass over the definition, looking for the xx{...} substitutions. */
|
|
|
|
/* These are done just once when reading the configuration file. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
char *pi;
|
|
|
|
char *ps;
|
|
|
|
char stemp[100]; // text inside of xx{...}
|
|
|
|
char ttemp[300]; // Converted to tone sequences.
|
|
|
|
char otemp[1000]; // Result after any substitutions.
|
|
|
|
char t2[2];
|
|
|
|
|
|
|
|
strlcpy (otemp, "", sizeof(otemp));
|
|
|
|
t2[1] = '\0';
|
|
|
|
pi = t;
|
|
|
|
while (*pi == ' ' || *pi == '\t') {
|
|
|
|
pi++;
|
|
|
|
}
|
|
|
|
for ( ; *pi != '\0'; pi++) {
|
|
|
|
|
|
|
|
if (strncmp(pi, "AC{", 3) == 0) {
|
|
|
|
|
|
|
|
// Convert to fixed length 10 digit callsign.
|
|
|
|
|
|
|
|
pi += 3;
|
|
|
|
ps = stemp;
|
|
|
|
while (*pi != '}' && *pi != '*' && *pi != '\0') {
|
|
|
|
*ps++ = *pi++;
|
|
|
|
}
|
|
|
|
if (*pi == '}') {
|
|
|
|
*ps = '\0';
|
|
|
|
if (tt_text_to_call10 (stemp, 0, ttemp) == 0) {
|
|
|
|
//text_color_set(DW_COLOR_DEBUG);
|
|
|
|
//dw_printf ("DEBUG Line %d: AC{%s} -> AC%s\n", line, stemp, ttemp);
|
|
|
|
strlcat (otemp, "AC", sizeof(otemp));
|
|
|
|
strlcat (otemp, ttemp, sizeof(otemp));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: AC{%s} could not be converted to tones for callsign.\n", line, stemp);
|
|
|
|
tt_error++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: AC{... is missing matching } in TTMACRO definition.\n", line);
|
|
|
|
tt_error++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (strncmp(pi, "AA{", 3) == 0) {
|
|
|
|
|
|
|
|
// Convert to object name.
|
|
|
|
|
|
|
|
pi += 3;
|
|
|
|
ps = stemp;
|
|
|
|
while (*pi != '}' && *pi != '*' && *pi != '\0') {
|
|
|
|
*ps++ = *pi++;
|
|
|
|
}
|
|
|
|
if (*pi == '}') {
|
|
|
|
*ps = '\0';
|
|
|
|
if (strlen(stemp) > 9) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Object name %s has been truncated to 9 characters.\n", line, stemp);
|
|
|
|
stemp[9] = '\0';
|
|
|
|
}
|
|
|
|
if (tt_text_to_two_key (stemp, 0, ttemp) == 0) {
|
|
|
|
//text_color_set(DW_COLOR_DEBUG);
|
|
|
|
//dw_printf ("DEBUG Line %d: AA{%s} -> AA%s\n", line, stemp, ttemp);
|
|
|
|
strlcat (otemp, "AA", sizeof(otemp));
|
|
|
|
strlcat (otemp, ttemp, sizeof(otemp));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: AA{%s} could not be converted to tones for object name.\n", line, stemp);
|
|
|
|
tt_error++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: AA{... is missing matching } in TTMACRO definition.\n", line);
|
|
|
|
tt_error++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (strncmp(pi, "AB{", 3) == 0) {
|
|
|
|
|
|
|
|
// Attempt conversion from description to symbol code.
|
|
|
|
|
|
|
|
pi += 3;
|
|
|
|
ps = stemp;
|
|
|
|
while (*pi != '}' && *pi != '*' && *pi != '\0') {
|
|
|
|
*ps++ = *pi++;
|
|
|
|
}
|
|
|
|
if (*pi == '}') {
|
|
|
|
char symtab;
|
|
|
|
char symbol;
|
|
|
|
|
|
|
|
*ps = '\0';
|
|
|
|
|
|
|
|
// First try to find something matching the description.
|
|
|
|
|
|
|
|
if (symbols_code_from_description (' ', stemp, &symtab, &symbol) == 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Couldn't convert \"%s\" to APRS symbol code. Using default.\n", line, stemp);
|
|
|
|
symtab = '\\'; // Alternate
|
|
|
|
symbol = 'A'; // Box
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert symtab(overlay) & symbol to tone sequence.
|
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
symbols_to_tones (symtab, symbol, ttemp, sizeof(ttemp));
|
2015-09-07 23:56:20 +00:00
|
|
|
|
|
|
|
//text_color_set(DW_COLOR_DEBUG);
|
|
|
|
//dw_printf ("DEBUG config file Line %d: AB{%s} -> %s\n", line, stemp, ttemp);
|
|
|
|
|
|
|
|
strlcat (otemp, ttemp, sizeof(otemp));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: AB{... is missing matching } in TTMACRO definition.\n", line);
|
|
|
|
tt_error++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-06 15:09:27 +00:00
|
|
|
else if (strncmp(pi, "CA{", 3) == 0) {
|
|
|
|
|
|
|
|
// Convert to enhanced comment that can contain any ASCII character.
|
|
|
|
|
|
|
|
pi += 3;
|
|
|
|
ps = stemp;
|
|
|
|
while (*pi != '}' && *pi != '*' && *pi != '\0') {
|
|
|
|
*ps++ = *pi++;
|
|
|
|
}
|
|
|
|
if (*pi == '}') {
|
|
|
|
*ps = '\0';
|
|
|
|
if (tt_text_to_ascii2d (stemp, 0, ttemp) == 0) {
|
|
|
|
//text_color_set(DW_COLOR_DEBUG);
|
|
|
|
//dw_printf ("DEBUG Line %d: CA{%s} -> CA%s\n", line, stemp, ttemp);
|
|
|
|
strlcat (otemp, "CA", sizeof(otemp));
|
|
|
|
strlcat (otemp, ttemp, sizeof(otemp));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: CA{%s} could not be converted to tones for enhanced comment.\n", line, stemp);
|
|
|
|
tt_error++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: CA{... is missing matching } in TTMACRO definition.\n", line);
|
|
|
|
tt_error++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
else if (strchr("0123456789ABCD*#xyz", *pi) != NULL) {
|
|
|
|
t2[0] = *pi;
|
|
|
|
strlcat (otemp, t2, sizeof(otemp));
|
|
|
|
}
|
|
|
|
else {
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: TTMACRO definition can contain only 0-9, A, B, C, D, *, #, x, y, z.\n", line);
|
2015-09-07 23:56:20 +00:00
|
|
|
tt_error++;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure that number of x, y, z, in pattern and definition match. */
|
|
|
|
|
|
|
|
d_count[0] = d_count[1] = d_count[2] = 0;
|
|
|
|
|
2017-01-01 16:49:55 +00:00
|
|
|
for (j=0; j<(int)(strlen(otemp)); j++) {
|
2015-09-07 23:56:20 +00:00
|
|
|
if (otemp[j] >= 'x' && otemp[j] <= 'z') {
|
|
|
|
d_count[otemp[j]-'x']++;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A little validity checking. */
|
|
|
|
|
|
|
|
for (j=0; j<3; j++) {
|
|
|
|
if (p_count[j] > 0 && d_count[j] == 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: '%c' is in TTMACRO pattern but is not used in definition.\n", line, 'x'+j);
|
|
|
|
}
|
|
|
|
if (d_count[j] > 0 && p_count[j] == 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: '%c' is referenced in TTMACRO definition but does not appear in the pattern.\n", line, 'x'+j);
|
|
|
|
}
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
|
|
|
|
//text_color_set(DW_COLOR_DEBUG);
|
|
|
|
//dw_printf ("DEBUG Config Line %d: %s -> %s\n", line, t, otemp);
|
|
|
|
|
|
|
|
if (tt_error == 0) {
|
|
|
|
tl->macro.definition = strdup(otemp);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_tt_config->ttloc_len--;
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TTOBJ - TT Object Report options.
|
|
|
|
*
|
2015-09-07 23:56:20 +00:00
|
|
|
* TTOBJ recv-chan where-to [ via-path ]
|
|
|
|
*
|
|
|
|
* whereto is any combination of transmit channel, APP, IG.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "TTOBJ") == 0) {
|
2015-09-07 23:56:20 +00:00
|
|
|
int r, x = -1;
|
|
|
|
int app = 0;
|
|
|
|
int ig = 0;
|
|
|
|
char *p;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Line %d: Missing DTMF receive channel for TTOBJ command.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
r = atoi(t);
|
|
|
|
if (r < 0 || r > MAX_CHANS-1) {
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Config file: DTMF receive channel must be in range of 0 to %d on line %d.\n",
|
|
|
|
MAX_CHANS-1, line);
|
|
|
|
continue;
|
|
|
|
}
|
2019-05-13 10:25:12 +00:00
|
|
|
|
|
|
|
// I suppose we need internal modem channel here.
|
|
|
|
// otherwise a DTMF decoder would not be available.
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[r] != MEDIUM_RADIO) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: TTOBJ DTMF receive channel %d is not valid.\n",
|
|
|
|
line, r);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Line %d: Missing transmit channel for TTOBJ command.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
// Can have any combination of number, APP, IG.
|
|
|
|
// Would it be easier with strtok?
|
|
|
|
|
|
|
|
for (p = t; *p != '\0'; p++) {
|
|
|
|
|
|
|
|
if (isdigit(*p)) {
|
|
|
|
x = *p - '0';
|
|
|
|
if (x < 0 || x > MAX_CHANS-1) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Transmit channel must be in range of 0 to %d on line %d.\n", MAX_CHANS-1, line);
|
|
|
|
x = -1;
|
|
|
|
}
|
2021-12-10 03:30:31 +00:00
|
|
|
else if (p_audio_config->chan_medium[x] != MEDIUM_RADIO &&
|
|
|
|
p_audio_config->chan_medium[x] != MEDIUM_NETTNC) {
|
2015-09-07 23:56:20 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: TTOBJ transmit channel %d is not valid.\n", line, x);
|
|
|
|
x = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (*p == 'a' || *p == 'A') {
|
|
|
|
app = 1;
|
|
|
|
}
|
|
|
|
else if (*p == 'i' || *p == 'I') {
|
|
|
|
ig = 1;
|
|
|
|
}
|
|
|
|
else if (strchr("pPgG,", *p) != NULL) {
|
|
|
|
;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Expected comma separated list with some combination of transmit channel, APP, and IG.\n", line);
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// This enables the DTMF decoder on the specified channel.
|
|
|
|
// Additional channels can be enabled with the DTMF command.
|
2015-09-07 23:56:20 +00:00
|
|
|
// Note that DTMF command does not enable the APRStt gateway.
|
|
|
|
|
|
|
|
|
|
|
|
//text_color_set(DW_COLOR_DEBUG);
|
|
|
|
//dw_printf ("Debug TTOBJ r=%d, x=%d, app=%d, ig=%d\n", r, x, app, ig);
|
2015-07-27 01:17:23 +00:00
|
|
|
|
|
|
|
p_audio_config->achan[r].dtmf_decode = DTMF_DECODE_ON;
|
|
|
|
p_tt_config->gateway_enabled = 1;
|
|
|
|
p_tt_config->obj_recv_chan = r;
|
|
|
|
p_tt_config->obj_xmit_chan = x;
|
2015-09-07 23:56:20 +00:00
|
|
|
p_tt_config->obj_send_to_app = app;
|
|
|
|
p_tt_config->obj_send_to_ig = ig;
|
2015-07-27 01:17:23 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
|
2016-07-03 22:09:34 +00:00
|
|
|
if (check_via_path(t) >= 0) {
|
|
|
|
strlcpy (p_tt_config->obj_xmit_via, t, sizeof(p_tt_config->obj_xmit_via));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: invalid via path.\n", line);
|
|
|
|
}
|
2015-07-27 01:17:23 +00:00
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
/*
|
|
|
|
* TTERR - TT responses for success or errors.
|
|
|
|
*
|
|
|
|
* TTERR msg_id method text...
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "TTERR") == 0) {
|
|
|
|
int n, msg_num;
|
|
|
|
char *p;
|
|
|
|
char method[AX25_MAX_ADDR_LEN];
|
|
|
|
int ssid;
|
|
|
|
int heard;
|
|
|
|
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing message identifier for TTERR command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg_num = -1;
|
|
|
|
for (n=0; n<TT_ERROR_MAXP1; n++) {
|
|
|
|
if (strcasecmp(t, tt_msg_id[n]) == 0) {
|
|
|
|
msg_num = n;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (msg_num < 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid message identifier for TTERR command.\n", line);
|
|
|
|
// pick one of ...
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing method (SPEECH, MORSE) for TTERR command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (p=t; *p!= '\0'; p++) {
|
|
|
|
if (islower(*p)) *p = toupper(*p);
|
|
|
|
}
|
|
|
|
|
2015-11-18 13:09:45 +00:00
|
|
|
if ( ! ax25_parse_addr(-1, t, 1, method, &ssid, &heard)) {
|
2015-09-07 23:56:20 +00:00
|
|
|
continue; // function above prints any error message
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(method,"MORSE") != 0 && strcmp(method,"SPEECH") != 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Response method of %s must be SPEECH or MORSE for TTERR command.\n", line, method);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,1);;
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing response text for TTERR command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//text_color_set(DW_COLOR_DEBUG);
|
|
|
|
//dw_printf ("Line %d: TTERR debug %d %s-%d \"%s\"\n", line, msg_num, method, ssid, t);
|
|
|
|
|
|
|
|
assert (msg_num >= 0 && msg_num < TT_ERROR_MAXP1);
|
|
|
|
|
|
|
|
strlcpy (p_tt_config->response[msg_num].method, method, sizeof(p_tt_config->response[msg_num].method));
|
|
|
|
|
|
|
|
// TODO1.3: Need SSID too!
|
|
|
|
|
|
|
|
strlcpy (p_tt_config->response[msg_num].mtext, t, sizeof(p_tt_config->response[msg_num].mtext));
|
|
|
|
p_tt_config->response[msg_num].mtext[TT_MTEXT_LEN-1] = '\0';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TTSTATUS - TT custom status messages.
|
|
|
|
*
|
|
|
|
* TTSTATUS status_id text...
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "TTSTATUS") == 0) {
|
|
|
|
int status_num;
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing status number for TTSTATUS command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_num = atoi(t);
|
|
|
|
|
|
|
|
if (status_num < 1 || status_num > 9) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Status number for TTSTATUS command must be in range of 1 to 9.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,1);;
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing status text for TTSTATUS command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//text_color_set(DW_COLOR_DEBUG);
|
|
|
|
//dw_printf ("Line %d: TTSTATUS debug %d \"%s\"\n", line, status_num, t);
|
|
|
|
|
|
|
|
while (*t == ' ' || *t == '\t') t++; // remove leading white space.
|
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_tt_config->status[status_num], t, sizeof(p_tt_config->status[status_num]));
|
2015-09-07 23:56:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TTCMD - Command to run when valid sequence is received.
|
|
|
|
* Any text generated will be sent back to user.
|
|
|
|
*
|
|
|
|
* TTCMD ...
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "TTCMD") == 0) {
|
|
|
|
|
|
|
|
t = split(NULL,1);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing command for TTCMD command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_tt_config->ttcmd, t, sizeof(p_tt_config->ttcmd));
|
2015-09-07 23:56:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* ==================== Internet gateway ====================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IGSERVER - Name of IGate server.
|
|
|
|
*
|
|
|
|
* IGSERVER hostname [ port ] -- original implementation.
|
|
|
|
*
|
|
|
|
* IGSERVER hostname:port -- more in line with usual conventions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "IGSERVER") == 0) {
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing IGate server name for IGSERVER command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_igate_config->t2_server_name, t, sizeof(p_igate_config->t2_server_name));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
/* If there is a : in the name, split it out as the port number. */
|
|
|
|
|
|
|
|
t = strchr (p_igate_config->t2_server_name, ':');
|
|
|
|
if (t != NULL) {
|
|
|
|
*t = '\0';
|
|
|
|
t++;
|
|
|
|
int n = atoi(t);
|
|
|
|
if (n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) {
|
|
|
|
p_igate_config->t2_server_port = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_igate_config->t2_server_port = DEFAULT_IGATE_PORT;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid port number for IGate server. Using default %d.\n",
|
|
|
|
line, p_igate_config->t2_server_port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Alternatively, the port number could be separated by white space. */
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t != NULL) {
|
|
|
|
int n = atoi(t);
|
|
|
|
if (n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) {
|
|
|
|
p_igate_config->t2_server_port = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_igate_config->t2_server_port = DEFAULT_IGATE_PORT;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid port number for IGate server. Using default %d.\n",
|
|
|
|
line, p_igate_config->t2_server_port);
|
|
|
|
}
|
|
|
|
}
|
2015-11-08 01:57:02 +00:00
|
|
|
//dw_printf ("DEBUG server=%s port=%d\n", p_igate_config->t2_server_name, p_igate_config->t2_server_port);
|
2015-07-27 00:35:07 +00:00
|
|
|
//exit (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IGLOGIN - Login callsign and passcode for IGate server
|
|
|
|
*
|
|
|
|
* IGLOGIN callsign passcode
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "IGLOGIN") == 0) {
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing login callsign for IGLOGIN command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// TODO: Wouldn't hurt to do validity checking of format.
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_igate_config->t2_login, t, sizeof(p_igate_config->t2_login));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing passcode for IGLOGIN command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_igate_config->t2_passcode, t, sizeof(p_igate_config->t2_passcode));
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IGTXVIA - Transmit channel and VIA path for messages from IGate server
|
|
|
|
*
|
|
|
|
* IGTXVIA channel [ path ]
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "IGTXVIA") == 0) {
|
|
|
|
int n;
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing transmit channel for IGTXVIA command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = atoi(t);
|
2015-07-27 01:17:23 +00:00
|
|
|
if (n < 0 || n > MAX_CHANS-1) {
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Transmit channel must be in range of 0 to %d on line %d.\n",
|
2015-07-27 01:17:23 +00:00
|
|
|
MAX_CHANS-1, line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p_igate_config->tx_chan = n;
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t != NULL) {
|
2016-07-03 22:09:34 +00:00
|
|
|
|
|
|
|
#if 1 // proper checking
|
|
|
|
|
|
|
|
n = check_via_path(t);
|
|
|
|
if (n >= 0) {
|
|
|
|
p_igate_config->max_digi_hops = n;
|
|
|
|
p_igate_config->tx_via[0] = ',';
|
|
|
|
strlcpy (p_igate_config->tx_via + 1, t, sizeof(p_igate_config->tx_via)-1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: invalid via path.\n", line);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else // previously
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
char *p;
|
|
|
|
p_igate_config->tx_via[0] = ',';
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy (p_igate_config->tx_via + 1, t, sizeof(p_igate_config->tx_via)-1);
|
2015-07-27 00:35:07 +00:00
|
|
|
for (p = p_igate_config->tx_via; *p != '\0'; p++) {
|
|
|
|
if (islower(*p)) {
|
|
|
|
*p = toupper(*p); /* silently force upper case. */
|
|
|
|
}
|
|
|
|
}
|
2016-07-03 22:09:34 +00:00
|
|
|
#endif
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-01-01 16:49:55 +00:00
|
|
|
* IGFILTER - IGate Server side filters.
|
2017-06-01 00:07:52 +00:00
|
|
|
* Is this name too confusing. Too similar to FILTER IG 0 ...
|
|
|
|
* Maybe SSFILTER suggesting Server Side.
|
|
|
|
* SUBSCRIBE might be better because it's not a filter that limits.
|
2015-07-27 00:35:07 +00:00
|
|
|
*
|
|
|
|
* IGFILTER filter-spec ...
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "IGFILTER") == 0) {
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,1); /* Take rest of line as one string. */
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2017-06-01 00:07:52 +00:00
|
|
|
if (p_igate_config->t2_filter != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Warning - Earlier IGFILTER value will be replaced by this one.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t != NULL && strlen(t) > 0) {
|
|
|
|
p_igate_config->t2_filter = strdup (t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IGTXLIMIT - Limit transmissions during 1 and 5 minute intervals.
|
|
|
|
*
|
|
|
|
* IGTXLIMIT one-minute-limit five-minute-limit
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "IGTXLIMIT") == 0) {
|
|
|
|
int n;
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing one minute limit for IGTXLIMIT command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = atoi(t);
|
2015-11-18 13:09:45 +00:00
|
|
|
if (n < 1) {
|
|
|
|
p_igate_config->tx_limit_1 = 1;
|
|
|
|
}
|
|
|
|
else if (n <= IGATE_TX_LIMIT_1_MAX) {
|
2015-07-27 00:35:07 +00:00
|
|
|
p_igate_config->tx_limit_1 = n;
|
|
|
|
}
|
|
|
|
else {
|
2015-11-18 13:09:45 +00:00
|
|
|
p_igate_config->tx_limit_1 = IGATE_TX_LIMIT_1_MAX;
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-11-18 13:09:45 +00:00
|
|
|
dw_printf ("Line %d: One minute transmit limit has been reduced to %d.\n",
|
2015-07-27 00:35:07 +00:00
|
|
|
line, p_igate_config->tx_limit_1);
|
2015-11-18 13:09:45 +00:00
|
|
|
dw_printf ("You won't make friends by setting a limit this high.\n");
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2015-11-18 13:09:45 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing five minute limit for IGTXLIMIT command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = atoi(t);
|
2015-11-18 13:09:45 +00:00
|
|
|
if (n < 1) {
|
|
|
|
p_igate_config->tx_limit_5 = 1;
|
|
|
|
}
|
|
|
|
else if (n <= IGATE_TX_LIMIT_5_MAX) {
|
2015-07-27 00:35:07 +00:00
|
|
|
p_igate_config->tx_limit_5 = n;
|
|
|
|
}
|
|
|
|
else {
|
2015-11-18 13:09:45 +00:00
|
|
|
p_igate_config->tx_limit_5 = IGATE_TX_LIMIT_5_MAX;
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-11-18 13:09:45 +00:00
|
|
|
dw_printf ("Line %d: Five minute transmit limit has been reduced to %d.\n",
|
2015-07-27 00:35:07 +00:00
|
|
|
line, p_igate_config->tx_limit_5);
|
2015-11-18 13:09:45 +00:00
|
|
|
dw_printf ("You won't make friends by setting a limit this high.\n");
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-01 16:49:55 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* IGMSP - Number of times to send position of message sender.
|
|
|
|
*
|
|
|
|
* IGMSP n
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "IGMSP") == 0) {
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
|
|
|
|
int n = atoi(t);
|
|
|
|
if (n >= 0 && n <= 10) {
|
|
|
|
p_igate_config->igmsp = n;
|
|
|
|
}
|
|
|
|
else {
|
2017-10-29 01:30:04 +00:00
|
|
|
p_igate_config->igmsp = 1;
|
2017-01-01 16:49:55 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable number of times for message sender position. Using default 1.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2017-10-29 01:30:04 +00:00
|
|
|
p_igate_config->igmsp = 1;
|
2017-01-01 16:49:55 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing number of times for message sender position. Using default 1.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-30 15:36:15 +00:00
|
|
|
/*
|
|
|
|
* SATGATE - Special SATgate mode to delay packets heard directly.
|
|
|
|
*
|
|
|
|
* SATGATE [ n ]
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "SATGATE") == 0) {
|
|
|
|
|
2017-10-29 01:30:04 +00:00
|
|
|
text_color_set(DW_COLOR_INFO);
|
|
|
|
dw_printf ("Line %d: SATGATE is pretty useless and will be removed in a future version.\n", line);
|
|
|
|
|
2016-01-30 15:36:15 +00:00
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
|
|
|
|
int n = atoi(t);
|
|
|
|
if (n >= MIN_SATGATE_DELAY && n <= MAX_SATGATE_DELAY) {
|
|
|
|
p_igate_config->satgate_delay = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_igate_config->satgate_delay = DEFAULT_SATGATE_DELAY;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unreasonable SATgate delay. Using default.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_igate_config->satgate_delay = DEFAULT_SATGATE_DELAY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* ==================== All the left overs ====================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* AGWPORT - Port number for "AGW TCPIP Socket Interface"
|
2015-07-27 01:17:23 +00:00
|
|
|
*
|
|
|
|
* In version 1.2 we allow 0 to disable listening.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
2021-11-23 02:10:31 +00:00
|
|
|
// FIXME: complain if extra parameter e.g. port as in KISSPORT
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
else if (strcasecmp(t, "AGWPORT") == 0) {
|
|
|
|
int n;
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing port number for AGWPORT command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
2015-07-27 01:17:23 +00:00
|
|
|
if ((n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) || n == 0) {
|
2015-07-27 00:35:07 +00:00
|
|
|
p_misc_config->agwpe_port = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_misc_config->agwpe_port = DEFAULT_AGWPE_PORT;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid port number for AGW TCPIP Socket Interface. Using %d.\n",
|
|
|
|
line, p_misc_config->agwpe_port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-01-05 00:43:00 +00:00
|
|
|
* KISSPORT port [ chan ] - Port number for KISS over IP.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
2021-01-05 00:43:00 +00:00
|
|
|
// Previously we allowed only a single TCP port for KISS.
|
|
|
|
// An increasing number of people want to run multiple radios.
|
|
|
|
// Unfortunately, most applications don't know how to deal with multi-radio TNCs.
|
|
|
|
// They ignore the channel on receive and always transmit to channel 0.
|
|
|
|
// Running multiple instances of direwolf is a work-around but this leads to
|
|
|
|
// more complex configuration and we lose the cross-channel digipeating capability.
|
|
|
|
// In release 1.7 we add a new feature to assign a single radio channel to a TCP port.
|
|
|
|
// e.g.
|
|
|
|
// KISSPORT 8001 # default, all channels. Radio channel = KISS channel.
|
|
|
|
//
|
|
|
|
// KISSPORT 7000 0 # Only radio channel 0 for receive.
|
|
|
|
// # Transmit to radio channel 0, ignoring KISS channel.
|
|
|
|
//
|
|
|
|
// KISSPORT 7001 1 # Only radio channel 1 for receive. KISS channel set to 0.
|
|
|
|
// # Transmit to radio channel 1, ignoring KISS channel.
|
|
|
|
|
|
|
|
// FIXME
|
2015-07-27 00:35:07 +00:00
|
|
|
else if (strcasecmp(t, "KISSPORT") == 0) {
|
|
|
|
int n;
|
2021-01-05 00:43:00 +00:00
|
|
|
int tcp_port = 0;
|
|
|
|
int chan = -1; // optional. default to all if not specified.
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2021-01-05 00:43:00 +00:00
|
|
|
dw_printf ("Line %d: Missing TCP port number for KISSPORT command.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
2015-07-27 01:17:23 +00:00
|
|
|
if ((n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) || n == 0) {
|
2021-01-05 00:43:00 +00:00
|
|
|
tcp_port = n;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2021-01-05 00:43:00 +00:00
|
|
|
dw_printf ("Line %d: Invalid TCP port number for KISS TCPIP Socket Interface.\n", line);
|
|
|
|
dw_printf ("Use something in the range of %d to %d.\n", MIN_IP_PORT_NUMBER, MAX_IP_PORT_NUMBER);
|
|
|
|
continue;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2021-01-05 00:43:00 +00:00
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
chan = atoi(t);
|
|
|
|
if (chan < 0 || chan >= MAX_CHANS) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid channel %d for KISSPORT command. Must be in range 0 thru %d.\n", line, chan, MAX_CHANS-1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// "KISSPORT 0" is used to remove the default entry.
|
|
|
|
|
|
|
|
if (tcp_port == 0) {
|
|
|
|
p_misc_config->kiss_port[0] = 0; // Should all be wiped out?
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Try to find an empty slot.
|
|
|
|
// A duplicate TCP port number will overwrite the previous value.
|
|
|
|
|
|
|
|
int slot = -1;
|
|
|
|
for (int i = 0; i < MAX_KISS_TCP_PORTS && slot == -1; i++) {
|
|
|
|
if (p_misc_config->kiss_port[i] == tcp_port) {
|
|
|
|
slot = i;
|
|
|
|
if ( ! (slot == 0 && tcp_port == DEFAULT_KISS_PORT)) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Warning: Duplicate TCP port %d will overwrite previous value.\n", line, tcp_port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (p_misc_config->kiss_port[i] == 0) {
|
|
|
|
slot = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (slot >= 0) {
|
|
|
|
p_misc_config->kiss_port[slot] = tcp_port;
|
|
|
|
p_misc_config->kiss_chan[slot] = chan;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Too many KISSPORT commands.\n", line);
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-05-03 21:41:37 +00:00
|
|
|
* NULLMODEM name [ speed ] - Device name for serial port or our end of the virtual "null modem"
|
|
|
|
* SERIALKISS name [ speed ]
|
|
|
|
*
|
|
|
|
* Version 1.5: Added SERIALKISS which is equivalent to NULLMODEM.
|
|
|
|
* The original name sort of made sense when it was used only for one end of a virtual
|
|
|
|
* null modem cable on Windows only. Now it is also available for Linux.
|
|
|
|
* TODO1.5: In retrospect, this doesn't seem like such a good name.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "NULLMODEM") == 0 || strcasecmp(t, "SERIALKISS") == 0) {
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing serial port name on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (strlen(p_misc_config->kiss_serial_port) > 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Warning serial port name on line %d replaces earlier value.\n", line);
|
|
|
|
}
|
|
|
|
strlcpy (p_misc_config->kiss_serial_port, t, sizeof(p_misc_config->kiss_serial_port));
|
|
|
|
p_misc_config->kiss_serial_speed = 0;
|
|
|
|
p_misc_config->kiss_serial_poll = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
p_misc_config->kiss_serial_speed = atoi(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SERIALKISSPOLL name - Poll for serial port name that might come and go.
|
|
|
|
* e.g. /dev/rfcomm0 for bluetooth.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
2017-05-03 21:41:37 +00:00
|
|
|
|
|
|
|
else if (strcasecmp(t, "SERIALKISSPOLL") == 0) {
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2017-05-03 21:41:37 +00:00
|
|
|
dw_printf ("Config file: Missing serial port name on line %d.\n", line);
|
2015-07-27 00:35:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
2017-05-03 21:41:37 +00:00
|
|
|
if (strlen(p_misc_config->kiss_serial_port) > 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Warning serial port name on line %d replaces earlier value.\n", line);
|
|
|
|
}
|
|
|
|
strlcpy (p_misc_config->kiss_serial_port, t, sizeof(p_misc_config->kiss_serial_port));
|
|
|
|
p_misc_config->kiss_serial_speed = 0;
|
|
|
|
p_misc_config->kiss_serial_poll = 1; // set polling.
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-02 00:59:58 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* KISSCOPY - Data from network KISS client is copied to all others.
|
2022-01-30 20:00:43 +00:00
|
|
|
* This does not apply to pseudo terminal KISS.
|
2019-01-02 00:59:58 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "KISSCOPY") == 0) {
|
|
|
|
p_misc_config->kiss_copy = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-27 13:48:46 +00:00
|
|
|
/*
|
|
|
|
* DNSSD - Enable or disable (1/0) dns-sd, DNS Service Discovery announcements
|
|
|
|
* DNSSDNAME - Set DNS-SD service name, defaults to "Dire Wolf on <hostname>"
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "DNSSD") == 0) {
|
|
|
|
int n;
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing integer value for DNSSD command.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n == 0 || n == 1) {
|
|
|
|
p_misc_config->dns_sd_enabled = n;
|
|
|
|
} else {
|
|
|
|
p_misc_config->dns_sd_enabled = 0;
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid integer value for DNSSD. Disabling dns-sd.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "DNSSDNAME") == 0) {
|
|
|
|
t = split(NULL, 1);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing service name for DNSSDNAME.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
strlcpy(p_misc_config->dns_sd_name, t, sizeof(p_misc_config->dns_sd_name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-11-08 01:57:02 +00:00
|
|
|
/*
|
2021-12-29 22:30:20 +00:00
|
|
|
* GPSNMEA serial-device [ speed ] - Direct connection to GPS receiver.
|
2015-11-08 01:57:02 +00:00
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "gpsnmea") == 0) {
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Missing serial port name for GPS receiver.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-29 22:30:20 +00:00
|
|
|
strlcpy (p_misc_config->gpsnmea_port, t, sizeof(p_misc_config->gpsnmea_port));
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
int n = atoi(t);
|
|
|
|
p_misc_config->gpsnmea_speed = n;
|
|
|
|
}
|
2015-11-08 01:57:02 +00:00
|
|
|
else {
|
2021-12-29 22:30:20 +00:00
|
|
|
p_misc_config->gpsnmea_speed = 4800; // The standard at one time.
|
2015-11-08 01:57:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GPSD - Use GPSD server.
|
|
|
|
*
|
|
|
|
* GPSD [ host [ port ] ]
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "gpsd") == 0) {
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: The GPSD interface is not available for Windows.\n", line);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
#elif ENABLE_GPSD
|
|
|
|
|
|
|
|
strlcpy (p_misc_config->gpsd_host, "localhost", sizeof(p_misc_config->gpsd_host));
|
|
|
|
p_misc_config->gpsd_port = atoi(DEFAULT_GPSD_PORT);
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
strlcpy (p_misc_config->gpsd_host, t, sizeof(p_misc_config->gpsd_host));
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
|
|
|
|
int n = atoi(t);
|
|
|
|
if ((n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) || n == 0) {
|
|
|
|
p_misc_config->gpsd_port = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p_misc_config->gpsd_port = atoi(DEFAULT_GPSD_PORT);
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid port number for GPSD Socket Interface. Using default of %d.\n",
|
|
|
|
line, p_misc_config->gpsd_port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: The GPSD interface has not been enabled.\n", line);
|
|
|
|
dw_printf ("Install gpsd and libgps-dev packages then rebuild direwolf.\n");
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:05:48 +00:00
|
|
|
/*
|
2020-05-27 01:20:37 +00:00
|
|
|
* WAYPOINT - Generate WPL and AIS NMEA sentences for display on map.
|
2016-03-20 23:23:09 +00:00
|
|
|
*
|
|
|
|
* WAYPOINT serial-device [ formats ]
|
2020-05-27 01:20:37 +00:00
|
|
|
* WAYPOINT host:udpport [ formats ]
|
2016-03-20 23:23:09 +00:00
|
|
|
*
|
2015-07-27 01:05:48 +00:00
|
|
|
*/
|
2016-03-20 23:23:09 +00:00
|
|
|
else if (strcasecmp(t, "waypoint") == 0) {
|
2020-05-27 01:20:37 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:05:48 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2020-05-27 01:20:37 +00:00
|
|
|
dw_printf ("Config file: Missing output device for WAYPOINT on line %d.\n", line);
|
2015-07-27 01:05:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-05-27 01:20:37 +00:00
|
|
|
|
|
|
|
/* If there is a ':' in the name, split it into hostname:udpportnum. */
|
|
|
|
/* Otherwise assume it is serial port name. */
|
|
|
|
|
|
|
|
char *p = strchr (t, ':');
|
|
|
|
if (p != NULL) {
|
|
|
|
*p = '\0';
|
|
|
|
int n = atoi(p+1);
|
|
|
|
if (n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) {
|
|
|
|
strlcpy (p_misc_config->waypoint_udp_hostname, t, sizeof(p_misc_config->waypoint_udp_hostname));
|
|
|
|
if (strlen(p_misc_config->waypoint_udp_hostname) == 0) {
|
|
|
|
strlcpy (p_misc_config->waypoint_udp_hostname, "localhost", sizeof(p_misc_config->waypoint_udp_hostname));
|
|
|
|
}
|
|
|
|
p_misc_config->waypoint_udp_portnum = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid UDP port number %d for sending waypoints.\n", line, n);
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
else {
|
2020-05-27 01:20:37 +00:00
|
|
|
strlcpy (p_misc_config->waypoint_serial_port, t, sizeof(p_misc_config->waypoint_serial_port));
|
2016-03-20 23:23:09 +00:00
|
|
|
}
|
2020-05-27 01:20:37 +00:00
|
|
|
|
2021-09-19 18:51:18 +00:00
|
|
|
/* Anything remaining is the formats to enable. */
|
2020-05-27 01:20:37 +00:00
|
|
|
|
2016-03-20 23:23:09 +00:00
|
|
|
t = split(NULL,1);
|
|
|
|
if (t != NULL) {
|
|
|
|
for ( ; *t != '\0' ; t++ ) {
|
|
|
|
switch (toupper(*t)) {
|
|
|
|
case 'N':
|
2022-02-16 02:42:15 +00:00
|
|
|
p_misc_config->waypoint_formats |= WPL_FORMAT_NMEA_GENERIC;
|
2016-03-20 23:23:09 +00:00
|
|
|
break;
|
|
|
|
case 'G':
|
2022-02-16 02:42:15 +00:00
|
|
|
p_misc_config->waypoint_formats |= WPL_FORMAT_GARMIN;
|
2016-03-20 23:23:09 +00:00
|
|
|
break;
|
|
|
|
case 'M':
|
2022-02-16 02:42:15 +00:00
|
|
|
p_misc_config->waypoint_formats |= WPL_FORMAT_MAGELLAN;
|
2016-03-20 23:23:09 +00:00
|
|
|
break;
|
|
|
|
case 'K':
|
2022-02-16 02:42:15 +00:00
|
|
|
p_misc_config->waypoint_formats |= WPL_FORMAT_KENWOOD;
|
2016-03-20 23:23:09 +00:00
|
|
|
break;
|
2020-05-27 01:20:37 +00:00
|
|
|
case 'A':
|
2022-02-16 02:42:15 +00:00
|
|
|
p_misc_config->waypoint_formats |= WPL_FORMAT_AIS;
|
2020-05-27 01:20:37 +00:00
|
|
|
break;
|
2016-03-20 23:23:09 +00:00
|
|
|
case ' ':
|
|
|
|
case ',':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Invalid output format '%c' for WAYPOINT on line %d.\n", *t, line);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-06-17 23:39:59 +00:00
|
|
|
* LOGDIR - Directory name for automatically named daily log files. Use "." for current working directory.
|
2015-07-27 01:05:48 +00:00
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "logdir") == 0) {
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
2015-07-27 01:05:48 +00:00
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing directory name for LOGDIR on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
2017-06-17 23:39:59 +00:00
|
|
|
if (strlen(p_misc_config->log_path) > 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: LOGDIR on line %d is replacing an earlier LOGDIR or LOGFILE.\n", line);
|
|
|
|
}
|
|
|
|
p_misc_config->log_daily_names = 1;
|
|
|
|
strlcpy (p_misc_config->log_path, t, sizeof(p_misc_config->log_path));
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: LOGDIR on line %d should have directory path and nothing more.\n", line);
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
|
2017-06-17 23:39:59 +00:00
|
|
|
/*
|
|
|
|
* LOGFILE - Log file name, including any directory part.
|
|
|
|
*/
|
|
|
|
else if (strcasecmp(t, "logfile") == 0) {
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Missing file name for LOGFILE on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (strlen(p_misc_config->log_path) > 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: LOGFILE on line %d is replacing an earlier LOGDIR or LOGFILE.\n", line);
|
|
|
|
}
|
|
|
|
p_misc_config->log_daily_names = 0;
|
|
|
|
strlcpy (p_misc_config->log_path, t, sizeof(p_misc_config->log_path));
|
|
|
|
}
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: LOGFILE on line %d should have file name and nothing more.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* BEACON channel delay every message
|
|
|
|
*
|
|
|
|
* Original handcrafted style. Removed in version 1.0.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "BEACON") == 0) {
|
|
|
|
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Old style 'BEACON' has been replaced with new commands.\n", line);
|
2015-07-27 01:05:48 +00:00
|
|
|
dw_printf ("Use PBEACON, OBEACON, or CBEACON instead.\n");
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PBEACON keyword=value ...
|
|
|
|
* OBEACON keyword=value ...
|
|
|
|
* TBEACON keyword=value ...
|
|
|
|
* CBEACON keyword=value ...
|
2016-07-03 22:09:34 +00:00
|
|
|
* IBEACON keyword=value ...
|
2015-07-27 00:35:07 +00:00
|
|
|
*
|
|
|
|
* New style with keywords for options.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "PBEACON") == 0 ||
|
|
|
|
strcasecmp(t, "OBEACON") == 0 ||
|
|
|
|
strcasecmp(t, "TBEACON") == 0 ||
|
2016-07-03 22:09:34 +00:00
|
|
|
strcasecmp(t, "CBEACON") == 0 ||
|
|
|
|
strcasecmp(t, "IBEACON") == 0) {
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
if (p_misc_config->num_beacons < MAX_BEACONS) {
|
|
|
|
|
|
|
|
memset (&(p_misc_config->beacon[p_misc_config->num_beacons]), 0, sizeof(struct beacon_s));
|
|
|
|
if (strcasecmp(t, "PBEACON") == 0) {
|
|
|
|
p_misc_config->beacon[p_misc_config->num_beacons].btype = BEACON_POSITION;
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "OBEACON") == 0) {
|
|
|
|
p_misc_config->beacon[p_misc_config->num_beacons].btype = BEACON_OBJECT;
|
|
|
|
}
|
|
|
|
else if (strcasecmp(t, "TBEACON") == 0) {
|
|
|
|
p_misc_config->beacon[p_misc_config->num_beacons].btype = BEACON_TRACKER;
|
|
|
|
}
|
2016-07-03 22:09:34 +00:00
|
|
|
else if (strcasecmp(t, "IBEACON") == 0) {
|
|
|
|
p_misc_config->beacon[p_misc_config->num_beacons].btype = BEACON_IGATE;
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
else {
|
|
|
|
p_misc_config->beacon[p_misc_config->num_beacons].btype = BEACON_CUSTOM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save line number because some errors will be reported later. */
|
|
|
|
p_misc_config->beacon[p_misc_config->num_beacons].lineno = line;
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if (beacon_options(t + strlen("xBEACON") + 1, &(p_misc_config->beacon[p_misc_config->num_beacons]), line, p_audio_config)) {
|
2015-07-27 00:35:07 +00:00
|
|
|
p_misc_config->num_beacons++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Maximum number of beacons exceeded on line %d.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2015-11-08 01:57:02 +00:00
|
|
|
* SMARTBEACONING [ fast_speed fast_rate slow_speed slow_rate turn_time turn_angle turn_slope ]
|
|
|
|
*
|
|
|
|
* Parameters must be all or nothing.
|
2015-07-27 00:35:07 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "SMARTBEACON") == 0 ||
|
|
|
|
strcasecmp(t, "SMARTBEACONING") == 0) {
|
|
|
|
|
|
|
|
int n;
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
#define SB_NUM(name,sbvar,minn,maxx,unit) \
|
2015-11-08 01:57:02 +00:00
|
|
|
t = split(NULL,0); \
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) { \
|
2015-11-08 01:57:02 +00:00
|
|
|
if (strcmp(name, "fast speed") == 0) { \
|
|
|
|
p_misc_config->sb_configured = 1; \
|
|
|
|
continue; \
|
|
|
|
} \
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR); \
|
|
|
|
dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name); \
|
|
|
|
continue; \
|
|
|
|
} \
|
|
|
|
n = atoi(t); \
|
|
|
|
if (n >= minn && n <= maxx) { \
|
|
|
|
p_misc_config->sbvar = n; \
|
|
|
|
} \
|
|
|
|
else { \
|
|
|
|
text_color_set(DW_COLOR_ERROR); \
|
|
|
|
dw_printf ("Line %d: Invalid %s for SmartBeaconing. Using default %d %s.\n", \
|
|
|
|
line, name, p_misc_config->sbvar, unit); \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SB_TIME(name,sbvar,minn,maxx,unit) \
|
2015-11-08 01:57:02 +00:00
|
|
|
t = split(NULL,0); \
|
2015-07-27 00:35:07 +00:00
|
|
|
if (t == NULL) { \
|
|
|
|
text_color_set(DW_COLOR_ERROR); \
|
|
|
|
dw_printf ("Line %d: Missing %s for SmartBeaconing.\n", line, name); \
|
|
|
|
continue; \
|
|
|
|
} \
|
|
|
|
n = parse_interval(t,line); \
|
|
|
|
if (n >= minn && n <= maxx) { \
|
|
|
|
p_misc_config->sbvar = n; \
|
|
|
|
} \
|
|
|
|
else { \
|
|
|
|
text_color_set(DW_COLOR_ERROR); \
|
|
|
|
dw_printf ("Line %d: Invalid %s for SmartBeaconing. Using default %d %s.\n", \
|
|
|
|
line, name, p_misc_config->sbvar, unit); \
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SB_NUM ("fast speed", sb_fast_speed, 2, 90, "MPH")
|
|
|
|
SB_TIME ("fast rate", sb_fast_rate, 10, 300, "seconds")
|
|
|
|
|
|
|
|
SB_NUM ("slow speed", sb_slow_speed, 1, 30, "MPH")
|
|
|
|
SB_TIME ("slow rate", sb_slow_rate, 30, 3600, "seconds")
|
|
|
|
|
|
|
|
SB_TIME ("turn time", sb_turn_time, 5, 180, "seconds")
|
|
|
|
SB_NUM ("turn angle", sb_turn_angle, 5, 90, "degrees")
|
|
|
|
SB_NUM ("turn slope", sb_turn_slope, 1, 255, "deg*mph")
|
|
|
|
|
|
|
|
/* If I was ambitious, I might allow optional */
|
|
|
|
/* unit at end for miles or km / hour. */
|
|
|
|
|
|
|
|
p_misc_config->sb_configured = 1;
|
|
|
|
}
|
|
|
|
|
2016-11-20 19:58:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ==================== AX.25 connected mode ====================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FRACK n - Number of seconds to wait for ack to transmission.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "FRACK") == 0) {
|
|
|
|
int n;
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing value for FRACK.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= AX25_T1V_FRACK_MIN && n <= AX25_T1V_FRACK_MAX) {
|
|
|
|
p_misc_config->frack = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid FRACK time. Using default %d.\n", line, p_misc_config->frack);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RETRY n - Number of times to retry before giving up.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "RETRY") == 0) {
|
|
|
|
int n;
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing value for RETRY.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= AX25_N2_RETRY_MIN && n <= AX25_N2_RETRY_MAX) {
|
|
|
|
p_misc_config->retry = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid RETRY number. Using default %d.\n", line, p_misc_config->retry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PACLEN n - Maximum number of bytes in information part.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "PACLEN") == 0) {
|
|
|
|
int n;
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing value for PACLEN.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= AX25_N1_PACLEN_MIN && n <= AX25_N1_PACLEN_MAX) {
|
|
|
|
p_misc_config->paclen = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid PACLEN value. Using default %d.\n", line, p_misc_config->paclen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MAXFRAME n - Max frames to send before ACK. mod 8 "Window" size.
|
|
|
|
*
|
|
|
|
* Window size would make more sense but everyone else calls it MAXFRAME.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "MAXFRAME") == 0) {
|
|
|
|
int n;
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing value for MAXFRAME.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= AX25_K_MAXFRAME_BASIC_MIN && n <= AX25_K_MAXFRAME_BASIC_MAX) {
|
|
|
|
p_misc_config->maxframe_basic = n;
|
|
|
|
}
|
|
|
|
else {
|
2019-01-02 00:59:58 +00:00
|
|
|
p_misc_config->maxframe_basic = AX25_K_MAXFRAME_BASIC_DEFAULT;
|
2016-11-20 19:58:51 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid MAXFRAME value outside range of %d to %d. Using default %d.\n",
|
|
|
|
line, AX25_K_MAXFRAME_BASIC_MIN, AX25_K_MAXFRAME_BASIC_MAX, p_misc_config->maxframe_basic);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* EMAXFRAME n - Max frames to send before ACK. mod 128 "Window" size.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "EMAXFRAME") == 0) {
|
|
|
|
int n;
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing value for EMAXFRAME.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= AX25_K_MAXFRAME_EXTENDED_MIN && n <= AX25_K_MAXFRAME_EXTENDED_MAX) {
|
|
|
|
p_misc_config->maxframe_extended = n;
|
|
|
|
}
|
|
|
|
else {
|
2019-01-02 00:59:58 +00:00
|
|
|
p_misc_config->maxframe_extended = AX25_K_MAXFRAME_EXTENDED_DEFAULT;
|
2016-11-20 19:58:51 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid EMAXFRAME value outside of range %d to %d. Using default %d.\n",
|
|
|
|
line, AX25_K_MAXFRAME_EXTENDED_MIN, AX25_K_MAXFRAME_EXTENDED_MAX, p_misc_config->maxframe_extended);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MAXV22 n - Max number of SABME sent before trying SABM.
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "MAXV22") == 0) {
|
|
|
|
int n;
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing value for MAXV22.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
n = atoi(t);
|
|
|
|
if (n >= 0 && n <= AX25_N2_RETRY_MAX) {
|
|
|
|
p_misc_config->maxv22 = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid MAXV22 number. Will use half of RETRY.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-01 00:07:52 +00:00
|
|
|
/*
|
|
|
|
* V20 address [ address ... ] - Stations known to support only AX.25 v2.0.
|
|
|
|
* When connecting to these, skip SABME and go right to SABM.
|
2021-09-19 18:51:18 +00:00
|
|
|
* Possible to have multiple and they are cumulative.
|
2017-06-01 00:07:52 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "V20") == 0) {
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing address(es) for V20.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (t != NULL) {
|
|
|
|
int const strict = 2;
|
|
|
|
char call_no_ssid[AX25_MAX_ADDR_LEN];
|
|
|
|
int ssid, heard;
|
|
|
|
|
|
|
|
if (ax25_parse_addr (AX25_DESTINATION, t, strict, call_no_ssid, &ssid, &heard)) {
|
|
|
|
p_misc_config->v20_addrs = (char**)realloc (p_misc_config->v20_addrs, sizeof(char*) * (p_misc_config->v20_count + 1));
|
|
|
|
p_misc_config->v20_addrs[p_misc_config->v20_count++] = strdup(t);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid station address for V20 command.\n", line);
|
|
|
|
|
|
|
|
// continue processing any others following.
|
|
|
|
}
|
|
|
|
t = split(NULL,0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-02 16:18:23 +00:00
|
|
|
/*
|
|
|
|
* NOXID address [ address ... ] - Stations known not to understand XID.
|
2021-09-19 18:51:18 +00:00
|
|
|
* After connecting to these (with v2.2 obviously), don't try using XID command.
|
2018-07-02 16:18:23 +00:00
|
|
|
* AX.25 for Linux is the one known case so far.
|
2021-09-19 18:51:18 +00:00
|
|
|
* Possible to have multiple and they are cumulative.
|
2018-07-02 16:18:23 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
else if (strcasecmp(t, "NOXID") == 0) {
|
|
|
|
|
|
|
|
t = split(NULL,0);
|
|
|
|
if (t == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Missing address(es) for NOXID.\n", line);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (t != NULL) {
|
|
|
|
int const strict = 2;
|
|
|
|
char call_no_ssid[AX25_MAX_ADDR_LEN];
|
|
|
|
int ssid, heard;
|
|
|
|
|
|
|
|
if (ax25_parse_addr (AX25_DESTINATION, t, strict, call_no_ssid, &ssid, &heard)) {
|
|
|
|
p_misc_config->noxid_addrs = (char**)realloc (p_misc_config->noxid_addrs, sizeof(char*) * (p_misc_config->noxid_count + 1));
|
|
|
|
p_misc_config->noxid_addrs[p_misc_config->noxid_count++] = strdup(t);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Invalid station address for NOXID command.\n", line);
|
|
|
|
|
|
|
|
// continue processing any others following.
|
|
|
|
}
|
|
|
|
t = split(NULL,0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* Invalid command.
|
|
|
|
*/
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Unrecognized command '%s' on line %d.\n", t, line);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose (fp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A little error checking for option interactions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Require that MYCALL be set when digipeating or IGating.
|
|
|
|
*
|
|
|
|
* Suggest that beaconing be enabled when digipeating.
|
|
|
|
*/
|
|
|
|
int i, j, k, b;
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
for (i=0; i<MAX_CHANS; i++) {
|
|
|
|
for (j=0; j<MAX_CHANS; j++) {
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2016-11-20 19:58:51 +00:00
|
|
|
/* APRS digipeating. */
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
if (p_digi_config->enabled[i][j]) {
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if ( strcmp(p_audio_config->achan[i].mycall, "") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[i].mycall, "NOCALL") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[i].mycall, "N0CALL") == 0) {
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: MYCALL must be set for receive channel %d before digipeating is allowed.\n", i);
|
|
|
|
p_digi_config->enabled[i][j] = 0;
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if ( strcmp(p_audio_config->achan[j].mycall, "") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[j].mycall, "NOCALL") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[j].mycall, "N0CALL") == 0) {
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: MYCALL must be set for transmit channel %d before digipeating is allowed.\n", i);
|
|
|
|
p_digi_config->enabled[i][j] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
b = 0;
|
|
|
|
for (k=0; k<p_misc_config->num_beacons; k++) {
|
2015-07-27 01:17:23 +00:00
|
|
|
if (p_misc_config->beacon[k].sendto_chan == j) b++;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
if (b == 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Config file: Beaconing should be configured for channel %d when digipeating is enabled.\n", j);
|
2015-07-27 01:05:48 +00:00
|
|
|
// It's a recommendation, not a requirement.
|
|
|
|
// Was there some good reason to turn it off in earlier version?
|
2015-07-27 01:17:23 +00:00
|
|
|
//p_digi_config->enabled[i][j] = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-20 19:58:51 +00:00
|
|
|
|
|
|
|
/* Connected mode digipeating. */
|
|
|
|
|
|
|
|
if (p_cdigi_config->enabled[i][j]) {
|
|
|
|
|
|
|
|
if ( strcmp(p_audio_config->achan[i].mycall, "") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[i].mycall, "NOCALL") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[i].mycall, "N0CALL") == 0) {
|
|
|
|
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: MYCALL must be set for receive channel %d before digipeating is allowed.\n", i);
|
|
|
|
p_cdigi_config->enabled[i][j] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( strcmp(p_audio_config->achan[j].mycall, "") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[j].mycall, "NOCALL") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[j].mycall, "N0CALL") == 0) {
|
|
|
|
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: MYCALL must be set for transmit channel %d before digipeating is allowed.\n", i);
|
|
|
|
p_cdigi_config->enabled[i][j] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
b = 0;
|
|
|
|
for (k=0; k<p_misc_config->num_beacons; k++) {
|
|
|
|
if (p_misc_config->beacon[k].sendto_chan == j) b++;
|
|
|
|
}
|
|
|
|
if (b == 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Beaconing should be configured for channel %d when digipeating is enabled.\n", j);
|
|
|
|
// It's a recommendation, not a requirement.
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2019-05-13 10:25:12 +00:00
|
|
|
/* When IGate is enabled, all radio channels must have a callsign associated. */
|
|
|
|
|
|
|
|
if (strlen(p_igate_config->t2_login) > 0 &&
|
2021-12-10 03:30:31 +00:00
|
|
|
(p_audio_config->chan_medium[i] == MEDIUM_RADIO || p_audio_config->chan_medium[i] == MEDIUM_NETTNC)) {
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if (strcmp(p_audio_config->achan[i].mycall, "NOCALL") == 0 || strcmp(p_audio_config->achan[i].mycall, "N0CALL") == 0) {
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: MYCALL must be set for receive channel %d before Rx IGate is allowed.\n", i);
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (p_igate_config->t2_login, "", sizeof(p_igate_config->t2_login));
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
2017-01-01 16:49:55 +00:00
|
|
|
// Currently we can have only one transmit channel.
|
|
|
|
// This might be generalized someday to allow more.
|
2015-07-27 00:35:07 +00:00
|
|
|
if (p_igate_config->tx_chan >= 0 &&
|
2015-07-27 01:17:23 +00:00
|
|
|
( strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "NOCALL") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "N0CALL") == 0)) {
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: MYCALL must be set for transmit channel %d before Tx IGate is allowed.\n", i);
|
|
|
|
p_igate_config->tx_chan = -1;
|
|
|
|
}
|
|
|
|
}
|
2017-01-01 16:49:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply default IS>RF IGate filter if none specified. New in 1.4.
|
|
|
|
// This will handle eventual case of multiple transmit channels.
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2019-05-13 10:25:12 +00:00
|
|
|
if (strlen(p_igate_config->t2_login) > 0) {
|
|
|
|
for (j=0; j<MAX_CHANS; j++) {
|
2021-12-10 03:30:31 +00:00
|
|
|
if (p_audio_config->chan_medium[j] == MEDIUM_RADIO || p_audio_config->chan_medium[j] == MEDIUM_NETTNC) {
|
2019-05-13 10:25:12 +00:00
|
|
|
if (p_digi_config->filter_str[MAX_CHANS][j] == NULL) {
|
|
|
|
p_digi_config->filter_str[MAX_CHANS][j] = strdup("i/60");
|
|
|
|
}
|
2017-01-01 16:49:55 +00:00
|
|
|
}
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
|
2017-01-01 16:49:55 +00:00
|
|
|
// Terrible hack. But what can we do?
|
|
|
|
|
2016-11-20 19:58:51 +00:00
|
|
|
if (p_misc_config->maxv22 < 0) {
|
2017-01-01 16:49:55 +00:00
|
|
|
p_misc_config->maxv22 = p_misc_config->retry / 3;
|
2016-11-20 19:58:51 +00:00
|
|
|
}
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
} /* end config_init */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the PBEACON or OBEACON options.
|
|
|
|
* Returns 1 for success, 0 for serious error.
|
|
|
|
*/
|
|
|
|
|
2022-02-16 02:42:15 +00:00
|
|
|
// FIXME: provide error messages when non applicable option is used for particular beacon type.
|
|
|
|
// e.g. IBEACON DELAY=1 EVERY=1 SENDTO=IG OVERLAY=R SYMBOL="igate" LAT=37^44.46N LONG=122^27.19W COMMENT="N1KOL-1 IGATE"
|
|
|
|
// Just ignores overlay, symbol, lat, long, and comment.
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_s *p_audio_config)
|
2015-07-27 00:35:07 +00:00
|
|
|
{
|
|
|
|
char *t;
|
|
|
|
char temp_symbol[100];
|
|
|
|
int ok;
|
2015-07-27 01:05:48 +00:00
|
|
|
char zone[8];
|
|
|
|
double easting = G_UNKNOWN;
|
|
|
|
double northing = G_UNKNOWN;
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (temp_symbol, "", sizeof(temp_symbol));
|
|
|
|
strlcpy (zone, "", sizeof(zone));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-07-27 01:05:48 +00:00
|
|
|
b->sendto_type = SENDTO_XMIT;
|
|
|
|
b->sendto_chan = 0;
|
2015-07-27 00:35:07 +00:00
|
|
|
b->delay = 60;
|
2017-06-11 20:57:43 +00:00
|
|
|
b->slot = G_UNKNOWN;
|
2015-07-27 00:35:07 +00:00
|
|
|
b->every = 600;
|
2015-07-27 01:05:48 +00:00
|
|
|
//b->delay = 6; // temp test.
|
2015-07-27 00:35:07 +00:00
|
|
|
//b->every = 3600;
|
|
|
|
b->lat = G_UNKNOWN;
|
|
|
|
b->lon = G_UNKNOWN;
|
2017-06-15 01:57:34 +00:00
|
|
|
b->ambiguity = 0;
|
2015-07-27 01:05:48 +00:00
|
|
|
b->alt_m = G_UNKNOWN;
|
2015-07-27 00:35:07 +00:00
|
|
|
b->symtab = '/';
|
|
|
|
b->symbol = '-'; /* house */
|
2015-12-24 20:25:13 +00:00
|
|
|
b->freq = G_UNKNOWN;
|
|
|
|
b->tone = G_UNKNOWN;
|
|
|
|
b->offset = G_UNKNOWN;
|
2020-11-07 22:43:47 +00:00
|
|
|
b->source = NULL;
|
|
|
|
b->dest = NULL;
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
while ((t = split(NULL,0)) != NULL) {
|
2015-07-27 00:35:07 +00:00
|
|
|
|
|
|
|
char keyword[20];
|
2021-10-22 21:29:20 +00:00
|
|
|
char value[1000];
|
2015-07-27 00:35:07 +00:00
|
|
|
char *e;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
|
|
e = strchr(t, '=');
|
|
|
|
if (e == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: No = found in, %s, on line %d.\n", t, line);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
*e = '\0';
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (keyword, t, sizeof(keyword));
|
|
|
|
strlcpy (value, e+1, sizeof(value));
|
2015-07-27 00:35:07 +00:00
|
|
|
|
2022-03-23 23:07:31 +00:00
|
|
|
// QUICK TEMP EXPERIMENT, maybe permanent new feature.
|
|
|
|
// Recognize \xnn as hexadecimal value. Handy for UTF-8 in comment.
|
|
|
|
// Maybe recognize the <0xnn> form that we print.
|
|
|
|
//
|
|
|
|
// # Convert between languages here: https://translate.google.com/ then
|
|
|
|
// # Convert to UTF-8 bytes here: https://codebeautify.org/utf8-converter
|
|
|
|
//
|
|
|
|
// pbeacon delay=0:05 every=0:30 sendto=R0 lat=12.5N long=69.97W comment="\xe3\x82\xa2\xe3\x83\x9e\xe3\x83\x81\xe3\x83\xa5\xe3\x82\xa2\xe7\x84\xa1\xe7\xb7\x9a \xce\xa1\xce\xb1\xce\xb4\xce\xb9\xce\xbf\xce\xb5\xcf\x81\xce\xb1\xcf\x83\xce\xb9\xcf\x84\xce\xb5\xcf\x87\xce\xbd\xce\xb9\xcf\x83\xce\xbc\xcf\x8c\xcf\x82"
|
|
|
|
|
|
|
|
char temp[256];
|
|
|
|
int tlen = 0;
|
|
|
|
|
|
|
|
for (char *p = value; *p != '\0'; ) {
|
|
|
|
if (p[0] == '\\' && p[1] == 'x' && strlen(p) >= 4 && isxdigit(p[2]) && isxdigit(p[3])) {
|
|
|
|
int n = 0;
|
|
|
|
for (int i = 2; i < 4; i++) {
|
|
|
|
n = n * 16;
|
|
|
|
if (islower(p[i])) {
|
|
|
|
n += p[i] - 'a' + 10;
|
|
|
|
}
|
|
|
|
else if (isupper(p[i])) {
|
|
|
|
n += p[i] - 'A' + 10;
|
|
|
|
}
|
|
|
|
else { // must be digit due to isxdigit test above.
|
|
|
|
n += p[i] - '0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
temp[tlen++] = n;
|
|
|
|
p += 4;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
temp[tlen++] = *p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
temp[tlen] = '\0';
|
|
|
|
strlcpy (value, temp, sizeof(value));
|
|
|
|
|
|
|
|
// end
|
2015-07-27 00:35:07 +00:00
|
|
|
if (strcasecmp(keyword, "DELAY") == 0) {
|
|
|
|
b->delay = parse_interval(value,line);
|
|
|
|
}
|
2017-06-11 20:57:43 +00:00
|
|
|
else if (strcasecmp(keyword, "SLOT") == 0) {
|
|
|
|
int n = parse_interval(value,line);
|
|
|
|
if ( n < 1 || n > 3600) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Beacon time slot, %d, must be in range of 1 to 3600 seconds.\n", line, n);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
b->slot = n;
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
else if (strcasecmp(keyword, "EVERY") == 0) {
|
|
|
|
b->every = parse_interval(value,line);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "SENDTO") == 0) {
|
|
|
|
if (value[0] == 'i' || value[0] == 'I') {
|
2015-07-27 01:05:48 +00:00
|
|
|
b->sendto_type = SENDTO_IGATE;
|
|
|
|
b->sendto_chan = 0;
|
|
|
|
}
|
|
|
|
else if (value[0] == 'r' || value[0] == 'R') {
|
2015-07-27 01:17:23 +00:00
|
|
|
int n = atoi(value+1);
|
2021-12-10 03:30:31 +00:00
|
|
|
if ( n < 0 || n >= MAX_CHANS || p_audio_config->chan_medium[n] == MEDIUM_NONE) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2017-06-11 20:57:43 +00:00
|
|
|
dw_printf ("Config file, line %d: Simulated receive on channel %d is not valid.\n", line, n);
|
2015-07-27 01:17:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
b->sendto_type = SENDTO_RECV;
|
2015-07-27 01:17:23 +00:00
|
|
|
b->sendto_chan = n;
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
else if (value[0] == 't' || value[0] == 'T' || value[0] == 'x' || value[0] == 'X') {
|
2015-07-27 01:17:23 +00:00
|
|
|
int n = atoi(value+1);
|
2021-12-10 03:30:31 +00:00
|
|
|
if ( n < 0 || n >= MAX_CHANS || p_audio_config->chan_medium[n] == MEDIUM_NONE) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, n);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
b->sendto_type = SENDTO_XMIT;
|
|
|
|
b->sendto_chan = n;
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-07-27 01:17:23 +00:00
|
|
|
int n = atoi(value);
|
2021-12-10 03:30:31 +00:00
|
|
|
if ( n < 0 || n >= MAX_CHANS || p_audio_config->chan_medium[n] == MEDIUM_NONE) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, n);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
b->sendto_type = SENDTO_XMIT;
|
2015-07-27 01:17:23 +00:00
|
|
|
b->sendto_chan = n;
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-07 22:43:47 +00:00
|
|
|
else if (strcasecmp(keyword, "SOURCE") == 0) {
|
|
|
|
b->source = strdup(value);
|
|
|
|
for (p = b->source; *p != '\0'; p++) {
|
|
|
|
if (islower(*p)) {
|
|
|
|
*p = toupper(*p); /* silently force upper case. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (strlen(b->source) > 9) {
|
|
|
|
b->source[9] = '\0';
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
else if (strcasecmp(keyword, "DEST") == 0) {
|
|
|
|
b->dest = strdup(value);
|
|
|
|
for (p = b->dest; *p != '\0'; p++) {
|
|
|
|
if (islower(*p)) {
|
|
|
|
*p = toupper(*p); /* silently force upper case. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (strlen(b->dest) > 9) {
|
|
|
|
b->dest[9] = '\0';
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "VIA") == 0) {
|
2016-07-03 22:09:34 +00:00
|
|
|
|
|
|
|
#if 1 // proper checking
|
|
|
|
|
|
|
|
if (check_via_path(value) >= 0) {
|
|
|
|
b->via = strdup(value);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: invalid via path.\n", line);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else // previously
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
b->via = strdup(value);
|
|
|
|
for (p = b->via; *p != '\0'; p++) {
|
|
|
|
if (islower(*p)) {
|
|
|
|
*p = toupper(*p); /* silently force upper case. */
|
|
|
|
}
|
|
|
|
}
|
2016-07-03 22:09:34 +00:00
|
|
|
#endif
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "INFO") == 0) {
|
|
|
|
b->custom_info = strdup(value);
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
else if (strcasecmp(keyword, "INFOCMD") == 0) {
|
|
|
|
b->custom_infocmd = strdup(value);
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
else if (strcasecmp(keyword, "OBJNAME") == 0) {
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy(b->objname, value, sizeof(b->objname));
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "LAT") == 0) {
|
|
|
|
b->lat = parse_ll (value, LAT, line);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "LONG") == 0 || strcasecmp(keyword, "LON") == 0) {
|
|
|
|
b->lon = parse_ll (value, LON, line);
|
|
|
|
}
|
2017-06-15 01:57:34 +00:00
|
|
|
else if (strcasecmp(keyword, "AMBIGUITY") == 0 || strcasecmp(keyword, "AMBIG") == 0) {
|
|
|
|
int n = atoi(value);
|
|
|
|
if (n >= 0 && n <= 4) {
|
|
|
|
b->ambiguity = n;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Location ambiguity, on line %d, must be in range of 0 to 4.\n", line);
|
|
|
|
}
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
else if (strcasecmp(keyword, "ALT") == 0 || strcasecmp(keyword, "ALTITUDE") == 0) {
|
2022-01-30 20:00:43 +00:00
|
|
|
|
2022-02-16 02:42:15 +00:00
|
|
|
char *unit = strpbrk(value, "abcedfghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
|
|
|
if (unit != NULL) {
|
|
|
|
float meters = 0;
|
|
|
|
for (int j=0; j<NUM_UNITS && meters == 0; j++) {
|
|
|
|
if (strcasecmp(units[j].name, unit) == 0) {
|
|
|
|
meters = units[j].meters;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (meters == 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Line %d: Unrecognized unit '%s' for altitude. Using meter.\n", line, unit);
|
|
|
|
dw_printf ("Try using singular form. e.g. ft or foot rather than feet.\n");
|
|
|
|
b->alt_m = atof(value);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// valid unit
|
|
|
|
b->alt_m = atof(value) * meters;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// no unit specified
|
|
|
|
b->alt_m = atof(value);
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "ZONE") == 0) {
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy(zone, value, sizeof(zone));
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "EAST") == 0 || strcasecmp(keyword, "EASTING") == 0) {
|
|
|
|
easting = atof(value);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "NORTH") == 0 || strcasecmp(keyword, "NORTHING") == 0) {
|
|
|
|
northing = atof(value);
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
else if (strcasecmp(keyword, "SYMBOL") == 0) {
|
|
|
|
/* Defer processing in case overlay appears later. */
|
2015-09-07 23:56:20 +00:00
|
|
|
strlcpy (temp_symbol, value, sizeof(temp_symbol));
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "OVERLAY") == 0) {
|
|
|
|
if (strlen(value) == 1 && (isupper(value[0]) || isdigit(value[0]))) {
|
|
|
|
b->symtab = value[0];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: Overlay must be one character in range of 0-9 or A-Z, upper case only, on line %d.\n", line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "POWER") == 0) {
|
|
|
|
b->power = atoi(value);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "HEIGHT") == 0) {
|
|
|
|
b->height = atoi(value);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "GAIN") == 0) {
|
|
|
|
b->gain = atoi(value);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "DIR") == 0 || strcasecmp(keyword, "DIRECTION") == 0) {
|
2015-11-08 01:57:02 +00:00
|
|
|
strlcpy(b->dir, value, sizeof(b->dir));
|
2015-07-27 00:35:07 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "FREQ") == 0) {
|
|
|
|
b->freq = atof(value);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "TONE") == 0) {
|
|
|
|
b->tone = atof(value);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "OFFSET") == 0 || strcasecmp(keyword, "OFF") == 0) {
|
|
|
|
b->offset = atof(value);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "COMMENT") == 0) {
|
|
|
|
b->comment = strdup(value);
|
|
|
|
}
|
2015-09-07 23:56:20 +00:00
|
|
|
else if (strcasecmp(keyword, "COMMENTCMD") == 0) {
|
|
|
|
b->commentcmd = strdup(value);
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
else if (strcasecmp(keyword, "COMPRESS") == 0 || strcasecmp(keyword, "COMPRESSED") == 0) {
|
|
|
|
b->compress = atoi(value);
|
|
|
|
}
|
2015-07-27 01:05:48 +00:00
|
|
|
else if (strcasecmp(keyword, "MESSAGING") == 0) {
|
|
|
|
b->messaging = atoi(value);
|
|
|
|
}
|
2015-07-27 00:35:07 +00:00
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Invalid option keyword, %s.\n", line, keyword);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
if (b->custom_info != NULL && b->custom_infocmd != NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2017-06-15 01:57:34 +00:00
|
|
|
dw_printf ("Config file, line %d: Can't use both INFO and INFOCMD at the same time.\n", line);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (b->compress && b->ambiguity != 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Position ambiguity can't be used with compressed location format.\n", line);
|
|
|
|
b->ambiguity = 0;
|
2015-09-07 23:56:20 +00:00
|
|
|
}
|
|
|
|
|
2015-07-27 01:05:48 +00:00
|
|
|
/*
|
2021-09-19 18:51:18 +00:00
|
|
|
* Convert UTM coordinates to lat / long.
|
2015-07-27 01:05:48 +00:00
|
|
|
*/
|
|
|
|
if (strlen(zone) > 0 || easting != G_UNKNOWN || northing != G_UNKNOWN) {
|
|
|
|
|
|
|
|
if (strlen(zone) > 0 && easting != G_UNKNOWN && northing != G_UNKNOWN) {
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
long lzone;
|
2015-09-07 23:56:20 +00:00
|
|
|
char latband, hemi;
|
2015-07-27 01:17:23 +00:00
|
|
|
long lerr;
|
|
|
|
double dlat, dlon;
|
2015-07-27 01:05:48 +00:00
|
|
|
|
2015-09-07 23:56:20 +00:00
|
|
|
lzone = parse_utm_zone (zone, &latband, &hemi);
|
2015-07-27 01:05:48 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
lerr = Convert_UTM_To_Geodetic(lzone, hemi, easting, northing, &dlat, &dlon);
|
2015-07-27 01:05:48 +00:00
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
if (lerr == 0) {
|
|
|
|
b->lat = R2D(dlat);
|
|
|
|
b->lon = R2D(dlon);
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-07-27 01:17:23 +00:00
|
|
|
char message [300];
|
|
|
|
|
|
|
|
utm_error_string (lerr, message);
|
2015-07-27 01:05:48 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2015-07-27 01:17:23 +00:00
|
|
|
dw_printf ("Line %d: Invalid UTM location: \n%s\n", line, message);
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
2016-05-29 14:51:54 +00:00
|
|
|
dw_printf ("Config file, line %d: When any of ZONE, EASTING, NORTHING specified, they must all be specified.\n", line);
|
2015-07-27 01:05:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
/*
|
|
|
|
* Process symbol now that we have any later overlay.
|
|
|
|
*/
|
|
|
|
if (strlen(temp_symbol) > 0) {
|
|
|
|
|
|
|
|
if (strlen(temp_symbol) == 2 &&
|
|
|
|
(temp_symbol[0] == '/' || temp_symbol[0] == '\\' || isupper(temp_symbol[0]) || isdigit(temp_symbol[0])) &&
|
|
|
|
temp_symbol[1] >= '!' && temp_symbol[1] <= '~') {
|
|
|
|
|
|
|
|
/* Explicit table and symbol. */
|
|
|
|
|
|
|
|
if (isupper(b->symtab) || isdigit(b->symtab)) {
|
|
|
|
b->symbol = temp_symbol[1];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
b->symtab = temp_symbol[0];
|
|
|
|
b->symbol = temp_symbol[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
/* Try to look up by description. */
|
|
|
|
ok = symbols_code_from_description (b->symtab, temp_symbol, &(b->symtab), &(b->symbol));
|
|
|
|
if (!ok) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Could not find symbol matching %s.\n", line, temp_symbol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 01:17:23 +00:00
|
|
|
/* Check is here because could be using default channel when SENDTO= is not specified. */
|
|
|
|
|
|
|
|
if (b->sendto_type == SENDTO_XMIT) {
|
|
|
|
|
2021-12-10 03:30:31 +00:00
|
|
|
if ( b->sendto_chan < 0 || b->sendto_chan >= MAX_CHANS || p_audio_config->chan_medium[b->sendto_chan] == MEDIUM_NONE) {
|
2015-07-27 01:17:23 +00:00
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file, line %d: Send to channel %d is not valid.\n", line, b->sendto_chan);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( strcmp(p_audio_config->achan[b->sendto_chan].mycall, "") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[b->sendto_chan].mycall, "NOCALL") == 0 ||
|
|
|
|
strcmp(p_audio_config->achan[b->sendto_chan].mycall, "N0CALL") == 0 ) {
|
|
|
|
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Config file: MYCALL must be set for channel %d before beaconing is allowed.\n", b->sendto_chan);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-27 00:35:07 +00:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* end config.c */
|