diff --git a/Makefile.linux b/Makefile.linux index ca02ffa..a81542f 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -432,7 +432,7 @@ aclients : aclients.c ax25_pad.c fcs_calc.c textcolor.o misc.a # Note: kiss_frame.c has conditional compilation on KISSUTIL. kissutil : kissutil.c kiss_frame.c ax25_pad.o fcs_calc.o textcolor.o serial_port.o dtime_now.o sock.o misc.a - $(CC) $(CFLAGS) -g -DKISSUTIL -o $@ $^ + $(CC) $(CFLAGS) -g -DKISSUTIL -o $@ $^ $(LDFLAGS) # Touch Tone to Speech sample application. @@ -637,6 +637,7 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon $(INSTALL) -D --mode=644 man1/decode_aprs.1 $(INSTALLDIR)/man/man1/decode_aprs.1 $(INSTALL) -D --mode=644 man1/direwolf.1 $(INSTALLDIR)/man/man1/direwolf.1 $(INSTALL) -D --mode=644 man1/gen_packets.1 $(INSTALLDIR)/man/man1/gen_packets.1 + $(INSTALL) -D --mode=644 man1/kissutil.1 $(INSTALLDIR)/man/man1/kissutil.1 $(INSTALL) -D --mode=644 man1/ll2utm.1 $(INSTALLDIR)/man/man1/ll2utm.1 $(INSTALL) -D --mode=644 man1/log2gpx.1 $(INSTALLDIR)/man/man1/log2gpx.1 $(INSTALL) -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1 diff --git a/Makefile.macosx b/Makefile.macosx index f01f218..1c111c6 100644 --- a/Makefile.macosx +++ b/Makefile.macosx @@ -430,9 +430,9 @@ install-conf : direwolf.conf # Separate application to decode raw data. -# First two use .c rather than .o because they depend on DECAMAIN definition. +# First three use .c rather than .o because they depend on DECAMAIN definition. -decode_aprs : decode_aprs.c kiss_frame.c dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o symbols.o ax25_pad.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o +decode_aprs : decode_aprs.c kiss_frame.c ax25_pad.c dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o symbols.o textcolor.o fcs_calc.o latlong.o log.o telemetry.o tt_text.o $(CC) $(CFLAGS) -DDECAMAIN -o $@ $^ -lm # Convert between text and touch tone representation. diff --git a/doc/User-Guide.pdf b/doc/User-Guide.pdf index 0a672bf..5e337cc 100644 Binary files a/doc/User-Guide.pdf and b/doc/User-Guide.pdf differ diff --git a/dtime_now.c b/dtime_now.c index c78fba8..ac7f238 100644 --- a/dtime_now.c +++ b/dtime_now.c @@ -1,5 +1,8 @@ #include "direwolf.h" + +#include + #include "textcolor.h" #include "dtime_now.h" @@ -18,7 +21,25 @@ #endif - +/*------------------------------------------------------------------ + * + * Name: dtime_now + * + * Purpose: Return current time as double precision. + * + * Input: none + * + * Returns: Unix time, as double precision, so we can get resolution + * finer than one second. + * + * Description: Normal unix time is in seconds since 1/1/1970 00:00:00 UTC. + * Sometimes we want resolution finer than a second. + * Rather than having a separate variable for the fractional + * part of a second, and having extra calculations everywhere, + * simply use double precision floating point to make usage + * easier. + * + *---------------------------------------------------------------*/ double dtime_now (void) @@ -59,3 +80,148 @@ double dtime_now (void) return (result); } + + +#if __WIN32__ + +/* + * Windows doesn't have localtime_r. + * It should have the equivalent localtime_s, with opposite parameter + * order, but I get undefined reference when trying to use it. + */ + +static struct tm *localtime_r(time_t *clock, struct tm *res) +{ + struct tm *tm; + + tm = localtime (clock); + memcpy (res, tm, sizeof(struct tm)); + return (res); +} + +#endif + + +/*------------------------------------------------------------------ + * + * Name: timestamp_now + * + * Purpose: Convert local time to one of these formats for debug output. + * + * HH:MM:SS + * HH:MM:SS.mmm + * + * Input: result_size - Size of result location. + * Should be at least 9 or 13. + * + * show_ms - True to display milliseconds. + * + * Output: result - Result is placed here. + * + *---------------------------------------------------------------*/ + +void timestamp_now (char *result, int result_size, int show_ms) +{ + double now = dtime_now(); + time_t t = (int)now; + struct tm tm; + + localtime_r (&t, &tm); + strftime (result, result_size, "%H:%M:%S", &tm); + + if (show_ms) { + int ms = (now - (int)t) * 1000; + char strms[16]; + + if (ms == 1000) ms = 999; + sprintf (strms, ".%03d", ms); + strlcat (result, strms, result_size); + } + +} /* end timestamp_now */ + + + +/*------------------------------------------------------------------ + * + * Name: timestamp_user_format + * + * Purpose: Convert local time user-specified format. e.g. + * + * HH:MM:SS + * mm/dd/YYYY HH:MM:SS + * dd/mm/YYYY HH:MM:SS + * + * Input: result_size - Size of result location. + * + * user_format - See strftime documentation. + * + * https://linux.die.net/man/3/strftime + * https://msdn.microsoft.com/en-us/library/aa272978(v=vs.60).aspx + * + * Note that Windows does not support all of the Linux formats. + * For example, Linux has %T which is equivalent to %H:%M:%S + * + * Output: result - Result is placed here. + * + *---------------------------------------------------------------*/ + +void timestamp_user_format (char *result, int result_size, char *user_format) +{ + double now = dtime_now(); + time_t t = (int)now; + struct tm tm; + + localtime_r (&t, &tm); + strftime (result, result_size, user_format, &tm); + +} /* end timestamp_user_format */ + + +/*------------------------------------------------------------------ + * + * Name: timestamp_filename + * + * Purpose: Generate unique file name based on the current time. + * The format will be: + * + * YYYYMMDD-HHMMSS-mmm + * + * Input: result_size - Size of result location. + * Should be at least 20. + * + * Output: result - Result is placed here. + * + * Description: This is for the kissutil "-r" option which places + * each received frame in a new file. It is possible to + * have two packets arrive in less than a second so we + * need more than one second resolution. + * + * What if someone wants UTC, rather than local time? + * You can simply set an environment variable like this: + * + * TZ=UTC direwolf + * + * so it's probably not worth the effort to add another + * option. + * + *---------------------------------------------------------------*/ + +void timestamp_filename (char *result, int result_size) +{ + double now = dtime_now(); + time_t t = (int)now; + struct tm tm; + + localtime_r (&t, &tm); + strftime (result, result_size, "%Y%m%d-%H%M%S", &tm); + + int ms = (now - (int)t) * 1000; + char strms[16]; + + if (ms == 1000) ms = 999; + sprintf (strms, "-%03d", ms); + strlcat (result, strms, result_size); + +} /* end timestamp_filename */ + diff --git a/dtime_now.h b/dtime_now.h index 5d7c39a..ac22f7d 100644 --- a/dtime_now.h +++ b/dtime_now.h @@ -1,3 +1,9 @@ -extern double dtime_now (void); \ No newline at end of file +extern double dtime_now (void); + +void timestamp_now (char *result, int result_size, int show_ms); + +void timestamp_user_format (char *result, int result_size, char *user_format); + +void timestamp_filename (char *result, int result_size); \ No newline at end of file diff --git a/kissutil.c b/kissutil.c index f91f263..7d05cc7 100644 --- a/kissutil.c +++ b/kissutil.c @@ -35,8 +35,6 @@ * See the "usage" functions at the bottom for details. * * FIXME: This is a rough prototype that needs more work. - * TODO: Prepare the "man" page. - * TODO: Add to User Guide. * *---------------------------------------------------------------*/ @@ -137,16 +135,20 @@ static int serial_speed = 9600; /* -s option. */ static int verbose = 0; /* -v option. */ /* Display the KISS protocol in hexadecimal for troubleshooting. */ -static char transmit_queue[120] = ""; /* -t option */ +static char transmit_from[120] = ""; /* -f option */ /* When specified, files are read from this directory */ /* rather than using stdin. Each file is one or more */ /* lines in the standard monitoring format. */ -static char receive_queue[120] = ""; /* -r option */ +static char receive_output[120] = ""; /* -o option */ /* When specified, each received frame is stored as a file */ /* with a unique name here. */ /* Directory must already exist; we won't create it. */ +static char timestamp_format[60] = ""; /* -T option */ + /* Precede received frames with timestamp. */ + /* Command line option uses "strftime" format string. */ + #if __WIN32__ #define THREAD_F unsigned __stdcall @@ -213,7 +215,7 @@ int main (int argc, char *argv[]) /* ':' following option character means arg is required. */ - c = getopt_long(argc, argv, "h:p:s:vt:r:", + c = getopt_long(argc, argv, "h:p:s:vf:o:T:", long_options, &option_index); if (c == -1) break; @@ -236,12 +238,16 @@ int main (int argc, char *argv[]) verbose++; break; - case 't': /* -t for transmit queue directory. */ - strlcpy (transmit_queue, optarg, sizeof(transmit_queue)); + case 'f': /* -f for transmit files directory. */ + strlcpy (transmit_from, optarg, sizeof(transmit_from)); break; - case 'r': /* -t for receive queue directory. */ - strlcpy (receive_queue, optarg, sizeof(receive_queue)); + case 'o': /* -o for receive output directory. */ + strlcpy (receive_output, optarg, sizeof(receive_output)); + break; + + case 'T': /* -T for receive timestamp. */ + strlcpy (timestamp_format, optarg, sizeof(timestamp_format)); break; case '?': @@ -265,9 +271,9 @@ int main (int argc, char *argv[]) /* * If receive queue directory was specified, make sure that it exists. */ - if (strlen(receive_queue) > 0) { - + if (strlen(receive_output) > 0) { +// TODO } /* If port begins with digit, consider it to be TCP. */ @@ -304,7 +310,7 @@ int main (int argc, char *argv[]) */ char stuff[1000]; - if (strlen(transmit_queue) > 0) { + if (strlen(transmit_from) > 0) { /* * Process and delete all files in specified directory. * When done, sleep for a second and try again. @@ -318,7 +324,7 @@ int main (int argc, char *argv[]) //text_color_set(DW_COLOR_DEBUG); //dw_printf("Get directory listing...\n"); - dp = opendir (transmit_queue); + dp = opendir (transmit_from); if (dp != NULL) { while ((ep = readdir(dp)) != NULL) { char path [300]; @@ -328,7 +334,7 @@ int main (int argc, char *argv[]) text_color_set(DW_COLOR_DEBUG); dw_printf ("Processing %s for transmit...\n", ep->d_name); - strlcpy (path, transmit_queue, sizeof(path)); + strlcpy (path, transmit_from, sizeof(path)); strlcat (path, DIR_CHAR, sizeof(path)); strlcat (path, ep->d_name, sizeof(path)); fp = fopen (path, "r"); @@ -352,7 +358,7 @@ int main (int argc, char *argv[]) } else { text_color_set(DW_COLOR_ERROR); - dw_printf("Can't access transmit queue directory %s. Quitting.\n", transmit_queue); + dw_printf("Can't access transmit queue directory %s. Quitting.\n", transmit_from); exit (EXIT_FAILURE); } SLEEP_SEC (1); @@ -367,6 +373,8 @@ int main (int argc, char *argv[]) } } + return (EXIT_SUCCESS); + } /* end main */ @@ -572,6 +580,9 @@ static THREAD_F tnc_listen_net (void *arg) /* * Connect to network KISS TNC. */ + // For the IGate we would loop around and try to reconnect if the TNC + // goes away. We should probably do the same here. + server_sock = sock_connect (hostname, port, "TCP KISS TNC", allow_ipv6, debug, ipaddr_str); if (server_sock == -1) { @@ -609,9 +620,6 @@ static THREAD_F tnc_listen_net (void *arg) dw_printf ("Read error from TCP KISS TNC. Terminating.\n"); exit (EXIT_FAILURE); - // For the IGate we would loop around and try to reconnect but - // it doesn't seem appropriate here. Just quit. - } /* end tnc_listen_net */ @@ -711,21 +719,29 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli printf ("ERROR - Invalid KISS data frame from TNC.\n"); } else { - char addrs[500]; + char prefix[100]; // Channel and optional timestamp. + // Like [0] or [2 12:34:56] + + char addrs[AX25_MAX_ADDRS*AX25_MAX_ADDR_LEN]; // Like source>dest,digi,...,digi: unsigned char *pinfo; int info_len; + if (strlen(timestamp_format) > 0) { + char ts[100]; + timestamp_user_format (ts, sizeof(ts), timestamp_format); + snprintf (prefix, sizeof(prefix), "[%d %s]", chan, ts); + } + else { + snprintf (prefix, sizeof(prefix), "[%d]", chan); + } + ax25_format_addrs (pp, addrs); info_len = ax25_get_info (pp, &pinfo); text_color_set(DW_COLOR_REC); - if (chan != 0) { - dw_printf ("[%d] ", chan); - } - - dw_printf ("%s", addrs); /* stations followed by : */ + dw_printf ("%s %s", prefix, addrs); // [channel] Addresses followed by : // Safe print will replace any unprintable characters with // hexadecimal representation. @@ -735,24 +751,27 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli /* * Add to receive queue directory if specified. - * File name will be based on time to 0.01 sec resolution. ??? TBD + * File name will be based on current local time. + * If you want UTC, just set an environment variable like this: + * + * TZ=UTC kissutil ... */ - if (strlen(receive_queue) > 0) { + if (strlen(receive_output) > 0) { char fname [30]; char path [300]; FILE *fp; - snprintf (fname, sizeof(fname), "%.0f", dtime_now() * 100.); + timestamp_filename (fname, (int)sizeof(fname)); - strlcpy (path, receive_queue, sizeof(path)); + strlcpy (path, receive_output, sizeof(path)); strlcat (path, DIR_CHAR, sizeof(path)); strlcat (path, fname, sizeof(path)); text_color_set(DW_COLOR_DEBUG); - dw_printf ("save received frame to %s\n", path); + dw_printf ("Save received frame to %s\n", path); fp = fopen (path, "w"); if (fp != NULL) { - fprintf (fp, "%s%s\n", addrs, pinfo); + fprintf (fp, "%s %s%s\n", prefix, addrs, pinfo); fclose (fp); } else { @@ -769,10 +788,14 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli kiss_msg[kiss_len] = '\0'; text_color_set(DW_COLOR_REC); - // TODO: Just precede by "h " for in/out symmetry? - printf ("KISS protocol set hardware \"%s\", channel %d\n", (char*)(kiss_msg+1), chan); + // Display as "h ..." for in/out symmetry. + // Use safe print here? + dw_printf ("[%d] h %s\n", chan, (char*)(kiss_msg+1)); break; +/* + * The rest should only go TO the TNC and not come FROM it. + */ case KISS_CMD_TXDELAY: /* 1 = TXDELAY */ case KISS_CMD_PERSISTENCE: /* 2 = Persistence */ case KISS_CMD_SLOTTIME: /* 3 = SlotTime */ @@ -833,8 +856,9 @@ static void usage(void) dw_printf (" a serial port. e.g. /dev/ttyAMA0 or COM3.\n"); dw_printf (" -s Serial port speed, default 9600.\n"); dw_printf (" -v Verbose. Show the KISS frame contents.\n"); - dw_printf (" -t Transmit queue directory. Processs and delete files here.\n"); - dw_printf (" -r Receive queue directory. Store received frames here.\n"); + dw_printf (" -f Transmit files directory. Processs and delete files here.\n"); + dw_printf (" -o Receive output queue directory. Store received frames here.\n"); + dw_printf (" -T Precede received frames with 'strftime' format time stamp.\n"); usage2(); exit (EXIT_SUCCESS); } diff --git a/man1/aclients.1 b/man1/aclients.1 index 578c1f3..71dab11 100644 --- a/man1/aclients.1 +++ b/man1/aclients.1 @@ -48,5 +48,5 @@ of how to set up tests and the results. .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll diff --git a/man1/atest.1 b/man1/atest.1 index 83fba0f..9470496 100644 --- a/man1/atest.1 +++ b/man1/atest.1 @@ -87,5 +87,5 @@ Try different combinations of options to find the best decoding performance. .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll diff --git a/man1/direwolf.1 b/man1/direwolf.1 index fe45702..700790d 100644 --- a/man1/direwolf.1 +++ b/man1/direwolf.1 @@ -152,5 +152,5 @@ rtl_fm \-f 144.39M \-o 4 \- | direwolf \-n 1 \-r 24000 \-b 16 \- .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll diff --git a/man1/kissutil.1 b/man1/kissutil.1 new file mode 100644 index 0000000..09eb12c --- /dev/null +++ b/man1/kissutil.1 @@ -0,0 +1,60 @@ +.TH KISSUTIL 1 + +.SH NAME +kissutil \- KISS TNC troubleshooting and Application Interface. + + +.SH SYNOPSIS +.B kissutil +[ \fIoptions\fR ] + + + +.SH DESCRIPTION +\fBkissutil\fR can be used interactively for troubleshooting a KISS TNC. +It is usable with direwolf and other generic KISS TNCs connected to a serial port. +It can also be used as an application interface where each side places files in a +directory for the other to process. +See User Guide for more details. + + +.SH OPTIONS +.TP +.BI "-h " "host" +Hostname or IP address for a TCP KISS TNC. Default is localhost. + +.TP +.BI "-p " "port" +A number may be specified for a TCP port other than the default 8001. +If not a number, it is considered to be a serial port name such as /dev/ttyS0 or COM3. + +.TP +.BI "-s " "speed" +Speed for serial port. e.g. 9600. + +.TP +.BI "-o " "rec-directory" +For each received frame, a new file is created here. +It is expected that some other application will process files in this directory then delete them. + +.TP +.BI "-T " "format" +Each received frame will be preceded by a timestamp in the specified format. +See strftime documentation for a description of the format string. +Example: %H:%M:%S for current time in hours, minutes, seconds. + +.TP +.BI "-f " "xmit-directory" +Files in this directory are transmited and deleted. +Another application places a file here when it wants something to be transmitted. + +.TP +.BI "-v " +Verbose - Display the KISS frames going to and from the TNC. + + +.SH SEE ALSO +More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. + +Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll + diff --git a/man1/ll2utm.1 b/man1/ll2utm.1 index e29e0b6..10f37eb 100644 --- a/man1/ll2utm.1 +++ b/man1/ll2utm.1 @@ -30,5 +30,5 @@ zone = 19T, easting = 307504, northing = 4721177 .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll diff --git a/man1/log2gpx.1 b/man1/log2gpx.1 index f94b56b..094aa03 100644 --- a/man1/log2gpx.1 +++ b/man1/log2gpx.1 @@ -36,5 +36,5 @@ None. .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll diff --git a/man1/text2tt.1 b/man1/text2tt.1 index 43a9a58..2551d5c 100644 --- a/man1/text2tt.1 +++ b/man1/text2tt.1 @@ -60,5 +60,5 @@ Push buttons for two-key method: .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll diff --git a/man1/tt2text.1 b/man1/tt2text.1 index 84159c5..b3c3266 100644 --- a/man1/tt2text.1 +++ b/man1/tt2text.1 @@ -62,5 +62,5 @@ Decoded text from two-key method: .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll diff --git a/man1/utm2ll.1 b/man1/utm2ll.1 index 6d420bf..9bc3ef8 100644 --- a/man1/utm2ll.1 +++ b/man1/utm2ll.1 @@ -36,5 +36,5 @@ latitude = 42.662139, longitude = -71.365553 .SH SEE ALSO More detailed information is in the pdf files in /usr/local/share/doc/direwolf, or possibly /usr/share/doc/direwolf, depending on installation location. -Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, ll2utm, log2gpx, text2tt, tt2text, utm2ll +Applications in this package: aclients, atest, decode_aprs, direwolf, gen_packets, kissutil, ll2utm, log2gpx, text2tt, tt2text, utm2ll