mirror of https://github.com/wb2osz/direwolf.git
AIS Reception enhancements.
This commit is contained in:
parent
0edb44efc3
commit
45136a91eb
|
@ -41,6 +41,7 @@ list(APPEND direwolf_SOURCES
|
|||
dtime_now.c
|
||||
dtmf.c
|
||||
dwgps.c
|
||||
dwsock.c
|
||||
encode_aprs.c
|
||||
encode_aprs.c
|
||||
fcs_calc.c
|
||||
|
|
192
src/ais.c
192
src/ais.c
|
@ -87,6 +87,10 @@ static const struct {
|
|||
{ 96, 168 } // 27 96 or 168, not range
|
||||
};
|
||||
|
||||
static void save_ship_data(char *mssi, char *shipname, char *callsign, char *destination);
|
||||
static void get_ship_data(char *mssi, char *comment, int comment_size);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Functions to get and set element of a bit vector.
|
||||
|
@ -144,14 +148,34 @@ static int get_field_signed (unsigned char *base, unsigned int start, unsigned i
|
|||
return (result);
|
||||
}
|
||||
|
||||
static double get_field_latlon (unsigned char *base, unsigned int start, unsigned int len)
|
||||
static double get_field_lat (unsigned char *base, unsigned int start, unsigned int len)
|
||||
{
|
||||
// Latitude of 0x3412140 (91 deg) means not available.
|
||||
// Longitude of 0x6791AC0 (181 deg) means not available.
|
||||
return ((double)get_field_signed(base, start, len) / 600000.0);
|
||||
|
||||
// Message type 27 uses lower resolution, 17 & 18 bits rather than 27 & 28.
|
||||
// Message type 27 uses lower resolution, 17 bits rather than 27.
|
||||
// It encodes minutes/10 rather than normal minutes/10000.
|
||||
|
||||
int n = get_field_signed(base, start, len);
|
||||
if (len == 17) {
|
||||
return ((n == 91*600) ? G_UNKNOWN : (double)n / 600.0);
|
||||
}
|
||||
else {
|
||||
return ((n == 91*600000) ? G_UNKNOWN : (double)n / 600000.0);
|
||||
}
|
||||
}
|
||||
|
||||
static double get_field_lon (unsigned char *base, unsigned int start, unsigned int len)
|
||||
{
|
||||
// Longitude of 0x6791AC0 (181 deg) means not available.
|
||||
// Message type 27 uses lower resolution, 18 bits rather than 28.
|
||||
// It encodes minutes/10 rather than normal minutes/10000.
|
||||
|
||||
int n = get_field_signed(base, start, len);
|
||||
if (len == 18) {
|
||||
return ((n == 181*600) ? G_UNKNOWN : (double)n / 600.0);
|
||||
}
|
||||
else {
|
||||
return ((n == 181*600000) ? G_UNKNOWN : (double)n / 600000.0);
|
||||
}
|
||||
}
|
||||
|
||||
static float get_field_speed (unsigned char *base, unsigned int start, unsigned int len)
|
||||
|
@ -159,14 +183,33 @@ static float get_field_speed (unsigned char *base, unsigned int start, unsigned
|
|||
// Raw 1023 means not available.
|
||||
// Multiply by 0.1 to get knots.
|
||||
// For aircraft it is knots, not deciknots.
|
||||
return ((float)get_field(base, start, len) * 0.1);
|
||||
|
||||
// Message type 27 uses lower resolution, 6 bits rather than 10.
|
||||
// It encodes minutes/10 rather than normal minutes/10000.
|
||||
|
||||
int n = get_field(base, start, len);
|
||||
if (len == 6) {
|
||||
return ((n == 63) ? G_UNKNOWN : (float)n);
|
||||
}
|
||||
else {
|
||||
return ((n == 1023) ? G_UNKNOWN : (float)n * 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
static float get_field_course (unsigned char *base, unsigned int start, unsigned int len)
|
||||
{
|
||||
// Raw 3600 means not available.
|
||||
// Multiply by 0.1 to get degrees
|
||||
return ((float)get_field(base, start, len) * 0.1);
|
||||
// Message type 27 uses lower resolution, 9 bits rather than 12.
|
||||
// It encodes degrees rather than normal degrees/10.
|
||||
|
||||
int n = get_field(base, start, len);
|
||||
if (len == 9) {
|
||||
return ((n == 360) ? G_UNKNOWN : (float)n);
|
||||
}
|
||||
else {
|
||||
return ((n == 3600) ? G_UNKNOWN : (float)n * 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_field_ascii (unsigned char *base, unsigned int start, unsigned int len)
|
||||
|
@ -428,7 +471,7 @@ int ais_parse (char *sentence, int quiet, char *descr, int descr_size, char *mss
|
|||
int type = get_field(ais, 0, 6);
|
||||
|
||||
if (type >= 1 && type <= 27) {
|
||||
snprintf (mssi, mssi_size, "%d", get_field(ais, 8, 30));
|
||||
snprintf (mssi, mssi_size, "%09d", get_field(ais, 8, 30));
|
||||
}
|
||||
switch (type) {
|
||||
|
||||
|
@ -439,10 +482,11 @@ int ais_parse (char *sentence, int quiet, char *descr, int descr_size, char *mss
|
|||
snprintf (descr, descr_size, "AIS %d: Position Report Class A", type);
|
||||
*symtab = '/';
|
||||
*symbol = 's'; // Power boat (ship) side view
|
||||
*odlon = get_field_latlon(ais, 61, 28);
|
||||
*odlat = get_field_latlon(ais, 89, 27);
|
||||
*odlon = get_field_lon(ais, 61, 28);
|
||||
*odlat = get_field_lat(ais, 89, 27);
|
||||
*ofknots = get_field_speed(ais, 50, 10);
|
||||
*ofcourse = get_field_course(ais, 116, 12);
|
||||
get_ship_data(mssi, comment, comment_size);
|
||||
break;
|
||||
|
||||
case 4: // Base Station Report
|
||||
|
@ -456,8 +500,10 @@ int ais_parse (char *sentence, int quiet, char *descr, int descr_size, char *mss
|
|||
//hour = get_field(ais, 61, 5);
|
||||
//minute = get_field(ais, 66, 6);
|
||||
//second = get_field(ais, 72, 6);
|
||||
*odlon = get_field_latlon(ais, 79, 28);
|
||||
*odlat = get_field_latlon(ais, 107, 27);
|
||||
*odlon = get_field_lon(ais, 79, 28);
|
||||
*odlat = get_field_lat(ais, 107, 27);
|
||||
// Is this suitable or not? Doesn't hurt, I suppose.
|
||||
get_ship_data(mssi, comment, comment_size);
|
||||
break;
|
||||
|
||||
case 5: // Static and Voyage Related Data
|
||||
|
@ -472,13 +518,8 @@ int ais_parse (char *sentence, int quiet, char *descr, int descr_size, char *mss
|
|||
get_field_string(ais, 70, 42, callsign);
|
||||
get_field_string(ais, 112, 120, shipname);
|
||||
get_field_string(ais, 302, 120, destination);
|
||||
|
||||
if (strlen(destination) > 0) {
|
||||
snprintf (comment, comment_size, "%s, %s, dest. %s", shipname, callsign, destination);
|
||||
}
|
||||
else {
|
||||
snprintf (comment, comment_size, "%s, %s", shipname, callsign);
|
||||
}
|
||||
save_ship_data(mssi, shipname, callsign, destination);
|
||||
get_ship_data(mssi, comment, comment_size);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -489,10 +530,12 @@ int ais_parse (char *sentence, int quiet, char *descr, int descr_size, char *mss
|
|||
*symtab = '/';
|
||||
*symbol = '\''; // Small AIRCRAFT
|
||||
*ofalt_m = get_field(ais, 38, 12); // meters, 4095 means not available
|
||||
*odlon = get_field_latlon(ais, 61, 28);
|
||||
*odlat = get_field_latlon(ais, 89, 27);
|
||||
*ofknots = get_field_speed(ais, 50, 10) * 10.0; // plane is knots, not knots/10
|
||||
*odlon = get_field_lon(ais, 61, 28);
|
||||
*odlat = get_field_lat(ais, 89, 27);
|
||||
*ofknots = get_field_speed(ais, 50, 10); // plane is knots, not knots/10
|
||||
if (*ofknots != G_UNKNOWN) *ofknots = *ofknots * 10.0;
|
||||
*ofcourse = get_field_course(ais, 116, 12);
|
||||
get_ship_data(mssi, comment, comment_size);
|
||||
break;
|
||||
|
||||
case 18: // Standard Class B CS Position Report
|
||||
|
@ -501,8 +544,9 @@ int ais_parse (char *sentence, int quiet, char *descr, int descr_size, char *mss
|
|||
snprintf (descr, descr_size, "AIS %d: Standard Class B CS Position Report", type);
|
||||
*symtab = '/';
|
||||
*symbol = 'Y'; // YACHT (sail)
|
||||
*odlon = get_field_latlon(ais, 57, 28);
|
||||
*odlat = get_field_latlon(ais, 85, 27);
|
||||
*odlon = get_field_lon(ais, 57, 28);
|
||||
*odlat = get_field_lat(ais, 85, 27);
|
||||
get_ship_data(mssi, comment, comment_size);
|
||||
break;
|
||||
|
||||
case 19: // Extended Class B CS Position Report
|
||||
|
@ -510,8 +554,9 @@ int ais_parse (char *sentence, int quiet, char *descr, int descr_size, char *mss
|
|||
snprintf (descr, descr_size, "AIS %d: Extended Class B CS Position Report", type);
|
||||
*symtab = '/';
|
||||
*symbol = 'Y'; // YACHT (sail)
|
||||
*odlon = get_field_latlon(ais, 57, 28);
|
||||
*odlat = get_field_latlon(ais, 85, 27);
|
||||
*odlon = get_field_lon(ais, 57, 28);
|
||||
*odlat = get_field_lat(ais, 85, 27);
|
||||
get_ship_data(mssi, comment, comment_size);
|
||||
break;
|
||||
|
||||
case 27: // Long Range AIS Broadcast message
|
||||
|
@ -519,10 +564,11 @@ int ais_parse (char *sentence, int quiet, char *descr, int descr_size, char *mss
|
|||
snprintf (descr, descr_size, "AIS %d: Long Range AIS Broadcast message", type);
|
||||
*symtab = '\\';
|
||||
*symbol = 's'; // OVERLAY SHIP/boat (top view)
|
||||
*odlon = get_field_latlon(ais, 44, 18) * 1000; // Note: minutes/10 rather than usual /10000.
|
||||
*odlat = get_field_latlon(ais, 62, 17) * 1000;
|
||||
*ofknots = get_field_speed(ais, 79, 6) * 10; // Note: knots, not deciknots.
|
||||
*ofcourse = get_field_course(ais, 85, 9) * 10; // Note: degrees, not decidegrees.
|
||||
*odlon = get_field_lon(ais, 44, 18); // Note: minutes/10 rather than usual /10000.
|
||||
*odlat = get_field_lat(ais, 62, 17);
|
||||
*ofknots = get_field_speed(ais, 79, 6); // Note: knots, not deciknots.
|
||||
*ofcourse = get_field_course(ais, 85, 9); // Note: degrees, not decidegrees.
|
||||
get_ship_data(mssi, comment, comment_size);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -575,4 +621,90 @@ int ais_check_length (int type, int length)
|
|||
} // end ais_check_length
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: save_ship_data
|
||||
*
|
||||
* Purpose: Save shipname, etc., from "Static and Voyage Related Data"
|
||||
* so it can be combined later with the position reports.
|
||||
*
|
||||
* Inputs: mssi
|
||||
* shipname
|
||||
* callsign
|
||||
* destination
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
struct ship_data_s {
|
||||
struct ship_data_s *pnext;
|
||||
char mssi[9+1];
|
||||
char shipname[20+1];
|
||||
char callsign[7+1];
|
||||
char destination[20+1];
|
||||
};
|
||||
|
||||
// Just use a single linked list for now.
|
||||
// If I get ambitious, I might use a hash table.
|
||||
// I don't think we need a critical region because all channels
|
||||
// should be serialized thru the receive queue.
|
||||
|
||||
static struct ship_data_s *ships = NULL;
|
||||
|
||||
|
||||
static void save_ship_data(char *mssi, char *shipname, char *callsign, char *destination)
|
||||
{
|
||||
// Get list node, either existing or new.
|
||||
struct ship_data_s *p = ships;
|
||||
while (p != NULL) {
|
||||
if (strcmp(mssi, p->mssi) == 0) {
|
||||
break;
|
||||
}
|
||||
p = p->pnext;
|
||||
}
|
||||
if (p == NULL) {
|
||||
p = calloc(sizeof(struct ship_data_s),1);
|
||||
p->pnext = ships;
|
||||
ships = p;
|
||||
}
|
||||
|
||||
strlcpy (p->mssi, mssi, sizeof(p->mssi));
|
||||
strlcpy (p->shipname, shipname, sizeof(p->shipname));
|
||||
strlcpy (p->callsign, callsign, sizeof(p->callsign));
|
||||
strlcpy (p->destination, destination, sizeof(p->destination));
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: save_ship_data
|
||||
*
|
||||
* Purpose: Get ship data for specified mssi.
|
||||
*
|
||||
* Inputs: mssi
|
||||
*
|
||||
* Outputs: comment - If mssi is found, return in single string here,
|
||||
* suitable for the comment field.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
static void get_ship_data(char *mssi, char *comment, int comment_size)
|
||||
{
|
||||
struct ship_data_s *p = ships;
|
||||
while (p != NULL) {
|
||||
if (strcmp(mssi, p->mssi) == 0) {
|
||||
break;
|
||||
}
|
||||
p = p->pnext;
|
||||
}
|
||||
if (p != NULL) {
|
||||
if (strlen(p->destination) > 0) {
|
||||
snprintf (comment, comment_size, "%s, %s, dest. %s", p->shipname, p->callsign, p->destination);
|
||||
}
|
||||
else {
|
||||
snprintf (comment, comment_size, "%s, %s", p->shipname, p->callsign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// end ais.c
|
||||
|
|
38
src/config.c
38
src/config.c
|
@ -885,7 +885,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
p_misc_config->kiss_serial_poll = 0;
|
||||
|
||||
strlcpy (p_misc_config->gpsnmea_port, "", sizeof(p_misc_config->gpsnmea_port));
|
||||
strlcpy (p_misc_config->waypoint_port, "", sizeof(p_misc_config->waypoint_port));
|
||||
strlcpy (p_misc_config->waypoint_serial_port, "", sizeof(p_misc_config->waypoint_serial_port));
|
||||
|
||||
p_misc_config->log_daily_names = 0;
|
||||
strlcpy (p_misc_config->log_path, "", sizeof(p_misc_config->log_path));
|
||||
|
@ -4602,21 +4602,46 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
|
||||
/*
|
||||
* WAYPOINT - Generate WPL NMEA sentences for display on map.
|
||||
* WAYPOINT - Generate WPL and AIS NMEA sentences for display on map.
|
||||
*
|
||||
* WAYPOINT serial-device [ formats ]
|
||||
* WAYPOINT host:udpport [ formats ]
|
||||
*
|
||||
*/
|
||||
else if (strcasecmp(t, "waypoint") == 0) {
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Missing device name for WAYPOINT on line %d.\n", line);
|
||||
dw_printf ("Config file: Missing output device for WAYPOINT on line %d.\n", line);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
strlcpy (p_misc_config->waypoint_port, t, sizeof(p_misc_config->waypoint_port));
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
strlcpy (p_misc_config->waypoint_serial_port, t, sizeof(p_misc_config->waypoint_serial_port));
|
||||
}
|
||||
|
||||
/* Anthing remaining is the formats to enable. */
|
||||
|
||||
t = split(NULL,1);
|
||||
if (t != NULL) {
|
||||
for ( ; *t != '\0' ; t++ ) {
|
||||
|
@ -4633,6 +4658,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
case 'K':
|
||||
p_misc_config->waypoint_formats |= WPT_FORMAT_KENWOOD;
|
||||
break;
|
||||
case 'A':
|
||||
p_misc_config->waypoint_formats |= WPT_FORMAT_AIS;
|
||||
break;
|
||||
case ' ':
|
||||
case ',':
|
||||
break;
|
||||
|
|
|
@ -65,10 +65,15 @@ struct misc_config_s {
|
|||
/* Default is 2947. */
|
||||
|
||||
|
||||
char waypoint_port[20]; /* Serial port name for sending NMEA waypoint sentences */
|
||||
char waypoint_serial_port[20]; /* Serial port name for sending NMEA waypoint sentences */
|
||||
/* to a GPS map display or other mapping application. */
|
||||
/* e.g. COM22, /dev/ttyACM0 */
|
||||
/* Currently no option for setting non-standard speed. */
|
||||
/* This was done in 2014 and no one has complained yet. */
|
||||
|
||||
char waypoint_udp_hostname[80]; /* Destination host when using UDP. */
|
||||
|
||||
int waypoint_udp_portnum; /* UDP port. */
|
||||
|
||||
int waypoint_formats; /* Which sentence formats should be generated? */
|
||||
|
||||
|
@ -76,6 +81,7 @@ struct misc_config_s {
|
|||
#define WPT_FORMAT_GARMIN 0x02 /* G $PGRMW */
|
||||
#define WPT_FORMAT_MAGELLAN 0x04 /* M $PMGNWPL */
|
||||
#define WPT_FORMAT_KENWOOD 0x08 /* K $PKWDWPL */
|
||||
#define WPT_FORMAT_AIS 0x10 /* A !AIVDM */
|
||||
|
||||
|
||||
int log_daily_names; /* True to generate new log file each day. */
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
#include "ax25_pad.h"
|
||||
#include "xid.h"
|
||||
#include "decode_aprs.h"
|
||||
#include "encode_aprs.h"
|
||||
#include "textcolor.h"
|
||||
#include "server.h"
|
||||
#include "kiss.h"
|
||||
|
@ -123,6 +124,7 @@
|
|||
#include "ax25_link.h"
|
||||
#include "dtime_now.h"
|
||||
#include "fx25.h"
|
||||
#include "dwsock.h"
|
||||
|
||||
|
||||
//static int idx_decoded = 0;
|
||||
|
@ -182,6 +184,8 @@ static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */
|
|||
static int q_h_opt = 0; /* "-q h" Quiet, suppress the "heard" line with audio level. */
|
||||
static int q_d_opt = 0; /* "-q d" Quiet, suppress the printing of decoded of APRS packets. */
|
||||
|
||||
static int A_opt_ais_to_obj = 0; /* "-A" Convert received AIS to APRS "Object Report." */
|
||||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
|
@ -284,7 +288,7 @@ int main (int argc, char *argv[])
|
|||
text_color_init(t_opt);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
||||
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "E", __DATE__);
|
||||
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "F", __DATE__);
|
||||
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||
|
||||
|
||||
|
@ -321,22 +325,32 @@ int main (int argc, char *argv[])
|
|||
* Apple computers with Intel processors started with P6. Since the
|
||||
* cpu test code was giving Clang compiler grief it has been excluded.
|
||||
*
|
||||
* Now, where can I find a Pentium 2 or earlier to test this?
|
||||
* Version 1.6: Newer compiler with i686, rather than i386 target.
|
||||
* This is running about 10% faster for the same hardware so it would
|
||||
* appear the compiler is using newer, more efficient, instructions.
|
||||
*
|
||||
* According to https://en.wikipedia.org/wiki/P6_(microarchitecture)
|
||||
* and https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
|
||||
* the Pentium III still seems to be the minimum required because
|
||||
* it has the P6 microarchitecture and SSE instructions.
|
||||
*
|
||||
* I've never heard any complaints about anyone getting the message below.
|
||||
*/
|
||||
|
||||
#if defined(__SSE__) && !defined(__APPLE__)
|
||||
int cpuinfo[4];
|
||||
int cpuinfo[4]; // EAX, EBX, ECX, EDX
|
||||
__cpuid (cpuinfo, 0);
|
||||
if (cpuinfo[0] >= 1) {
|
||||
__cpuid (cpuinfo, 1);
|
||||
//dw_printf ("debug: cpuinfo = %x, %x, %x, %x\n", cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]);
|
||||
// https://en.wikipedia.org/wiki/CPUID
|
||||
if ( ! ( cpuinfo[3] & (1 << 25))) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("------------------------------------------------------------------\n");
|
||||
dw_printf ("This version requires a minimum of a Pentium 3 or equivalent.\n");
|
||||
dw_printf ("If you are seeing this message, you are probably using a computer\n");
|
||||
dw_printf ("from the previous century. See comments in Makefile.win for\n");
|
||||
dw_printf ("information on how you can recompile it for use with your antique.\n");
|
||||
dw_printf ("from the previous Century. See instructions in User Guide for\n");
|
||||
dw_printf ("information on how you can compile it for use with your antique.\n");
|
||||
dw_printf ("------------------------------------------------------------------\n");
|
||||
}
|
||||
}
|
||||
|
@ -373,7 +387,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
/* ':' following option character means arg is required. */
|
||||
|
||||
c = getopt_long(argc, argv, "hP:B:gjJD:U:c:pxr:b:n:d:q:t:ul:L:Sa:E:T:e:X:",
|
||||
c = getopt_long(argc, argv, "hP:B:gjJD:U:c:pxr:b:n:d:q:t:ul:L:Sa:E:T:e:X:A",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -636,6 +650,11 @@ int main (int argc, char *argv[])
|
|||
X_fx25_xmit_enable = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'A': // -A convert AIS to APRS object
|
||||
|
||||
A_opt_ais_to_obj = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/* Should not be here. */
|
||||
|
@ -670,6 +689,8 @@ int main (int argc, char *argv[])
|
|||
|
||||
symbols_init ();
|
||||
|
||||
(void)dwsock_init();
|
||||
|
||||
config_init (config_file, &audio_config, &digi_config, &cdigi_config, &tt_config, &igate_config, &misc_config);
|
||||
|
||||
if (r_opt != 0) {
|
||||
|
@ -1205,6 +1226,8 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
*
|
||||
* Suppress printed decoding if "-q d" option used.
|
||||
*/
|
||||
char ais_obj_packet[300];
|
||||
strcpy (ais_obj_packet, "");
|
||||
|
||||
if (ax25_is_aprs(pp)) {
|
||||
|
||||
|
@ -1239,6 +1262,37 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
mheard_save_rf (chan, &A, pp, alevel, retries);
|
||||
|
||||
// For AIS, we have an option to convert the NMEA format, in User Defined data,
|
||||
// into an APRS "Object Report" and send that to the clients as well.
|
||||
|
||||
// FIXME: partial implementation.
|
||||
|
||||
static const char user_def_da[4] = { '{', USER_DEF_USER_ID, USER_DEF_TYPE_AIS, '\0' };
|
||||
|
||||
if (strncmp((char*)pinfo, user_def_da, 3) == 0) {
|
||||
|
||||
waypoint_send_ais((char*)pinfo + 3);
|
||||
|
||||
if (A_opt_ais_to_obj && A.g_lat != G_UNKNOWN && A.g_lon != G_UNKNOWN) {
|
||||
|
||||
char ais_obj_info[256];
|
||||
(void)encode_object (A.g_name, 0, time(NULL),
|
||||
A.g_lat, A.g_lon, 0, // no ambiguity
|
||||
A.g_symbol_table, A.g_symbol_code,
|
||||
0, 0, 0, "", // power, height, gain, direction.
|
||||
// Unknown not handled properly.
|
||||
// Should encode_object take floating point here?
|
||||
(int)(A.g_course+0.5), (int)(DW_MPH_TO_KNOTS(A.g_speed_mph)+0.5),
|
||||
0, 0, 0, A.g_comment, // freq, tone, offset
|
||||
ais_obj_info, sizeof(ais_obj_info));
|
||||
|
||||
snprintf (ais_obj_packet, sizeof(ais_obj_packet), "%s>%s%1d%1d:%s", A.g_src, APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, ais_obj_info);
|
||||
|
||||
dw_printf ("[%d.AIS] %s\n", chan, ais_obj_packet);
|
||||
|
||||
// This will be sent to client apps after the User Defined Data representation.
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to NMEA waypoint sentence if we have a location.
|
||||
|
||||
|
@ -1265,6 +1319,20 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
kissserial_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1); // KISS serial port
|
||||
kisspt_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1); // KISS pseudo terminal
|
||||
|
||||
if (A_opt_ais_to_obj && strlen(ais_obj_packet) != 0) {
|
||||
packet_t ao_pp = ax25_from_text (ais_obj_packet, 1);
|
||||
if (ao_pp != NULL) {
|
||||
unsigned char ao_fbuf[AX25_MAX_PACKET_LEN];
|
||||
int ao_flen = ax25_pack(ao_pp, ao_fbuf);
|
||||
|
||||
server_send_rec_packet (chan, ao_pp, ao_fbuf, ao_flen);
|
||||
kissnet_send_rec_packet (chan, KISS_CMD_DATA_FRAME, ao_fbuf, ao_flen, -1);
|
||||
kissserial_send_rec_packet (chan, KISS_CMD_DATA_FRAME, ao_fbuf, ao_flen, -1);
|
||||
kisspt_send_rec_packet (chan, KISS_CMD_DATA_FRAME, ao_fbuf, ao_flen, -1);
|
||||
ax25_delete (ao_pp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If it came from DTMF decoder, send it to APRStt gateway.
|
||||
* Otherwise, it is a candidate for IGate and digipeater.
|
||||
|
@ -1302,6 +1370,9 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
* Use only those with correct CRC; We don't want to spread corrupted data!
|
||||
*/
|
||||
|
||||
// TODO: Should also use anything received with FX.25 because it is known to be good.
|
||||
// Our earlier "fix bits" hack could allow corrupted information to get thru.
|
||||
|
||||
if (ax25_is_aprs(pp) && retries == RETRY_NONE) {
|
||||
|
||||
digipeater (chan, pp);
|
||||
|
@ -1384,8 +1455,9 @@ static void usage (char **argv)
|
|||
dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n");
|
||||
dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\n");
|
||||
dw_printf (" -P xxx Modem Profiles.\n");
|
||||
dw_printf (" -A Convert AIS positions to APRS Object Reports.\n");
|
||||
dw_printf (" -D n Divide audio sample rate by n for channel 0.\n");
|
||||
dw_printf (" -X n Enable FX.25 transmit. Specify number of check bytes: 16, 32, or 64.\n");
|
||||
dw_printf (" -X n 1 to enable FX.25 transmit.\n");
|
||||
dw_printf (" -d Debug options:\n");
|
||||
dw_printf (" a a = AGWPE network protocol client.\n");
|
||||
dw_printf (" k k = KISS serial port or pseudo terminal client.\n");
|
||||
|
|
|
@ -215,6 +215,7 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets)
|
|||
|
||||
case MODEM_BASEBAND:
|
||||
case MODEM_SCRAMBLE:
|
||||
case MODEM_AIS:
|
||||
|
||||
// Tone is half baud.
|
||||
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5);
|
||||
|
@ -398,6 +399,7 @@ void tone_gen_put_bit (int chan, int dat)
|
|||
|
||||
case MODEM_BASEBAND:
|
||||
case MODEM_SCRAMBLE:
|
||||
case MODEM_AIS:
|
||||
|
||||
if (dat != prev_dat[chan]) {
|
||||
tone_phase[chan] += f1_change_per_sample[chan];
|
||||
|
|
161
src/waypoint.c
161
src/waypoint.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2014, 2015, 2016 John Langner, WB2OSZ
|
||||
// Copyright (C) 2014, 2015, 2016, 2020 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
@ -32,16 +32,21 @@
|
|||
|
||||
#include "direwolf.h" // should be first
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if __WIN32__
|
||||
#include <stdlib.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
//#include <arpa/inet.h>
|
||||
#include <netdb.h> // gethostbyname
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
@ -57,13 +62,16 @@
|
|||
#include "mgn_icon.h" /* Magellan icons */
|
||||
#include "dwgpsnmea.h"
|
||||
#include "serial_port.h"
|
||||
#include "dwsock.h"
|
||||
|
||||
|
||||
static MYFDTYPE s_waypoint_port_fd = MYFDERROR;
|
||||
static MYFDTYPE s_waypoint_serial_port_fd = MYFDERROR;
|
||||
static int s_waypoint_udp_sock_fd = -1; // ideally INVALID_SOCKET for Windows.
|
||||
static struct sockaddr_in s_udp_dest_addr;
|
||||
|
||||
static int s_waypoint_formats = 0; /* which formats should we generate? */
|
||||
|
||||
static int s_waypoint_debug = 0; /* Print information flowing to attached device. */
|
||||
static int s_waypoint_debug = 0; /* Print information flowing to attached device. */
|
||||
|
||||
|
||||
|
||||
|
@ -86,19 +94,24 @@ void waypoint_set_debug (int n)
|
|||
*
|
||||
* Inputs: mc - Pointer to configuration options.
|
||||
*
|
||||
* ->waypoint_port - Name of serial port. COM1, /dev/ttyS0, etc.
|
||||
* ->waypoint_serial_port - Name of serial port. COM1, /dev/ttyS0, etc.
|
||||
*
|
||||
* ->waypoint_udp_hostname - Destination host when using UDP.
|
||||
*
|
||||
* ->waypoint_udp_portnum - UDP port number.
|
||||
*
|
||||
* (currently none) - speed, baud. Default 4800 if not set
|
||||
*
|
||||
*
|
||||
* ->waypoint_formats - Set of formats enabled.
|
||||
* If none set, default to generic & Kenwood.
|
||||
* If none set, default to generic & Kenwood here.
|
||||
*
|
||||
* Global output: s_waypoint_port_fd
|
||||
* Global output: s_waypoint_serial_port_fd
|
||||
* s_waypoint_udp_sock_fd
|
||||
*
|
||||
* Description: First to see if this is shared with GPS input.
|
||||
* If not, open serial port.
|
||||
* In version 1.6 UDP is added. It is possible to use both.
|
||||
*
|
||||
* Restriction: MUST be done after GPS init because we might be sharing the
|
||||
* same serial port device.
|
||||
|
@ -111,46 +124,79 @@ void waypoint_init (struct misc_config_s *mc)
|
|||
|
||||
#if DEBUG
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("waypoint_init() device=%s formats=%d\n", mc->waypoint_port, mc->waypoint_formats);
|
||||
dw_printf ("waypoint_init() serial device=%s formats=%02x\n", mc->waypoint_serial_port, mc->waypoint_formats);
|
||||
dw_printf ("waypoint_init() destination hostname=%s UDP port=%d\n", mc->waypoint_udp_hostname, mc->waypoint_udp_portnum);
|
||||
#endif
|
||||
|
||||
|
||||
s_waypoint_udp_sock_fd = -1;
|
||||
|
||||
if (mc->waypoint_udp_portnum > 0) {
|
||||
|
||||
s_waypoint_udp_sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s_waypoint_udp_sock_fd != -1) {
|
||||
|
||||
// Not thread-safe. Should use getaddrinfo instead.
|
||||
struct hostent *hp = gethostbyname(mc->waypoint_udp_hostname);
|
||||
|
||||
if (hp != NULL) {
|
||||
memset ((char *)&s_udp_dest_addr, 0, sizeof(s_udp_dest_addr));
|
||||
s_udp_dest_addr.sin_family = AF_INET;
|
||||
memcpy ((char *)&s_udp_dest_addr.sin_addr, (char *)hp->h_addr, hp->h_length);
|
||||
s_udp_dest_addr.sin_port = htons(mc->waypoint_udp_portnum);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Waypoint: Couldn't get address for %s\n", mc->waypoint_udp_hostname);
|
||||
close (s_waypoint_udp_sock_fd);
|
||||
s_waypoint_udp_sock_fd = -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Couldn't create socket for waypoint send to %s\n", mc->waypoint_udp_hostname);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Are we sharing with GPS input?
|
||||
* First try to get fd if they have same device name.
|
||||
* If that fails, do own serial port open.
|
||||
*/
|
||||
if (strlen(mc->waypoint_port) > 0) {
|
||||
s_waypoint_serial_port_fd = MYFDERROR;
|
||||
|
||||
s_waypoint_port_fd = dwgpsnmea_get_fd (mc->waypoint_port, 4800);
|
||||
if (strlen(mc->waypoint_serial_port) > 0) {
|
||||
|
||||
if (s_waypoint_port_fd == MYFDERROR) {
|
||||
s_waypoint_port_fd = serial_port_open (mc->waypoint_port, 4800);
|
||||
s_waypoint_serial_port_fd = dwgpsnmea_get_fd (mc->waypoint_serial_port, 4800);
|
||||
|
||||
if (s_waypoint_serial_port_fd == MYFDERROR) {
|
||||
s_waypoint_serial_port_fd = serial_port_open (mc->waypoint_serial_port, 4800);
|
||||
}
|
||||
else {
|
||||
text_color_set (DW_COLOR_INFO);
|
||||
dw_printf ("Note: Sharing same port for GPS input and waypoint output.\n");
|
||||
}
|
||||
|
||||
if (s_waypoint_port_fd == MYFDERROR) {
|
||||
if (s_waypoint_serial_port_fd == MYFDERROR) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Unable to open %s for waypoint output.\n", mc->waypoint_port);
|
||||
return;
|
||||
}
|
||||
|
||||
s_waypoint_formats = mc->waypoint_formats;
|
||||
if (s_waypoint_formats == 0) {
|
||||
s_waypoint_formats = WPT_FORMAT_NMEA_GENERIC | WPT_FORMAT_KENWOOD;
|
||||
}
|
||||
if (s_waypoint_formats & WPT_FORMAT_GARMIN) {
|
||||
s_waypoint_formats |= WPT_FORMAT_NMEA_GENERIC; /* See explanation below. */
|
||||
dw_printf ("Unable to open serial port %s for waypoint output.\n", mc->waypoint_serial_port);
|
||||
}
|
||||
}
|
||||
|
||||
// Set default formats if user did not specify any.
|
||||
|
||||
s_waypoint_formats = mc->waypoint_formats;
|
||||
if (s_waypoint_formats == 0) {
|
||||
s_waypoint_formats = WPT_FORMAT_NMEA_GENERIC | WPT_FORMAT_KENWOOD;
|
||||
}
|
||||
if (s_waypoint_formats & WPT_FORMAT_GARMIN) {
|
||||
s_waypoint_formats |= WPT_FORMAT_NMEA_GENERIC; /* See explanation below. */
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("end of waypoint_init: s_waypoint_port_fd = %d\n", s_waypoint_port_fd);
|
||||
dw_printf ("end of waypoint_init: s_waypoint_serial_port_fd = %d\n", s_waypoint_serial_port_fd);
|
||||
dw_printf ("end of waypoint_init: s_waypoint_udp_sock_fd = %d\n", s_waypoint_udp_sock_fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -252,6 +298,12 @@ void waypoint_send_sentence (char *name_in, double dlat, double dlong, char symt
|
|||
dw_printf ("waypoint_send_sentence (\"%s\", \"%c%c\")\n", name_in, symtab, symbol);
|
||||
#endif
|
||||
|
||||
// Don't waste time if no destintations specified.
|
||||
|
||||
if (s_waypoint_serial_port_fd == MYFDERROR &&
|
||||
s_waypoint_udp_sock_fd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to remove any , or * from name, symbol, or comment because they are field delimiters.
|
||||
|
@ -583,29 +635,59 @@ void waypoint_send_sentence (char *name_in, double dlat, double dlong, char symt
|
|||
} /* end waypoint_send_sentence */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: nema_send_ais
|
||||
*
|
||||
* Purpose: Send NMEA AIS sentence to GPS display or other mapping application.
|
||||
*
|
||||
* Inputs: sentence - should look something like this, with checksum, and no CR LF.
|
||||
*
|
||||
* !AIVDM,1,1,,A,35NO=dPOiAJriVDH@94E84AJ0000,0*4B
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
void waypoint_send_ais (char *sentence)
|
||||
{
|
||||
if (s_waypoint_serial_port_fd == MYFDERROR &&
|
||||
s_waypoint_udp_sock_fd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_waypoint_formats & WPT_FORMAT_AIS) {
|
||||
send_sentence (sentence);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Append CR LF and send it.
|
||||
*/
|
||||
|
||||
static void send_sentence (char *sent)
|
||||
{
|
||||
|
||||
char final[256];
|
||||
|
||||
|
||||
if (s_waypoint_port_fd == MYFDERROR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_waypoint_debug) {
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("%s\n", sent);
|
||||
dw_printf ("waypoint send sentence: \"%s\"\n", sent);
|
||||
}
|
||||
|
||||
strlcpy (final, sent, sizeof(final));
|
||||
strlcat (final, "\r\n", sizeof(final));
|
||||
int final_len = strlen(final);
|
||||
|
||||
serial_port_write (s_waypoint_port_fd, final, strlen(final));
|
||||
if (s_waypoint_serial_port_fd != MYFDERROR) {
|
||||
serial_port_write (s_waypoint_serial_port_fd, final, final_len);
|
||||
}
|
||||
|
||||
if (s_waypoint_udp_sock_fd != -1) {
|
||||
int n = sendto(s_waypoint_udp_sock_fd, final, final_len, 0, (struct sockaddr*)(&s_udp_dest_addr), sizeof(struct sockaddr_in));
|
||||
if (n != final_len) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Failed to send waypoint via UDP, errno=%d\n", errno);
|
||||
}
|
||||
}
|
||||
|
||||
} /* send_sentence */
|
||||
|
||||
|
@ -613,10 +695,13 @@ static void send_sentence (char *sent)
|
|||
|
||||
void waypoint_term (void)
|
||||
{
|
||||
|
||||
if (s_waypoint_port_fd != MYFDERROR) {
|
||||
if (s_waypoint_serial_port_fd != MYFDERROR) {
|
||||
//serial_port_close (s_waypoint_port_fd);
|
||||
s_waypoint_port_fd = MYFDERROR;
|
||||
s_waypoint_serial_port_fd = MYFDERROR;
|
||||
}
|
||||
if (s_waypoint_udp_sock_fd != -1) {
|
||||
close (s_waypoint_udp_sock_fd);
|
||||
s_waypoint_udp_sock_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ void waypoint_set_debug (int n);
|
|||
void waypoint_send_sentence (char *wname_in, double dlat, double dlong, char symtab, char symbol,
|
||||
float alt, float course, float speed, char *comment_in);
|
||||
|
||||
void waypoint_send_ais (char *sentence);
|
||||
|
||||
void waypoint_term ();
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue