From 0430323014682e25668ba6fae05d3a09f478727b Mon Sep 17 00:00:00 2001 From: Malcolm Robb Date: Tue, 24 Sep 2013 18:37:54 +0100 Subject: [PATCH] Implement a remote interactive screen No changes to dump1090, (except the version number) Include a sample Linux batch start file called dump1090.sh for use when running dump1090 headless. This file needs to be copied to the /etc/init.d/ subdirectory on your raspberry pi, and marked as executable. Then when you re-start your RPi, dump1090 will start-up auto-magically and run as a sort of server to allow both local and remote connection to it's various internet ports. Modified the Makefile to build a new headless helper application called view1090 Added view1090. This is an executable that allows you to connect to dump1090 when it is running and 'see' the interactive screen display. The default is to try and connect to dump1090 on IP address 127.0.0.1 port 30005. This should work if you are running on the same RPi as dump1090 and using the default dump1090 port settings. However, if you're running on a different machine you will have to specify the IP address of the RPi running dump1090 using the --net-bo-ipaddr switch. Something like "view1090 --net-bo-ipaddr 192.168.2.65" . You may also have to sudo it, depending on your privilige settings. I've also compiled view1090 as a Wiin32 exe, so you should be able to run it under any 32 bit version of Microsoft Windows - i.e. Win95, Win 2K, Win XP, Win 7 etc. It may work on Win 8 and 64 bit Windows, but I haven't tried it. The Win32 version is compiled from the same source, so takes all the same command line switches. --- Makefile | 7 +- Release/view1090.exe | Bin 0 -> 86016 bytes dump1090.h | 5 +- dump1090.sh | 74 ++++++++++++++ view1090.c | 227 +++++++++++++++++++++++++++++++++++++++++++ view1090.h | 82 ++++++++++++++++ 6 files changed, 392 insertions(+), 3 deletions(-) create mode 100644 Release/view1090.exe create mode 100644 dump1090.sh create mode 100644 view1090.c create mode 100644 view1090.h diff --git a/Makefile b/Makefile index 51a7756..1827026 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ LIBS=`pkg-config --libs librtlsdr` -lpthread -lm CC=gcc -all: dump1090 +all: dump1090 view1090 %.o: %.c $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $< @@ -23,5 +23,8 @@ all: dump1090 dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(CC) -g -o dump1090 dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) +view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o + $(CC) -g -o view1090 view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) + clean: - rm -f *.o dump1090 + rm -f *.o dump1090 view1090 diff --git a/Release/view1090.exe b/Release/view1090.exe new file mode 100644 index 0000000000000000000000000000000000000000..186552d9b5f8b316c624c57d67ee6bd1c46880df GIT binary patch literal 86016 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 r{q&2 + exit 1 + else + ## Change from /dev/null to something like /var/log/$PROG if you want to save output. + cd $PROG_PATH + ./$PROG $PROG_ARGS 2>&1 >/dev/null & + echo "$PROG started" + touch $PIDFILE + fi +} + +stop() { + if [ -e $PIDFILE ]; then + ## Program is running, so stop it + echo "$PROG is running" + killall $PROG + rm -f $PIDFILE + echo "$PROG stopped" + else + ## Program is not running, exit with error. + echo "Error! $PROG not started!" 1>&2 + exit 1 + fi +} + +## Check to see if we are running as root first. +## Found at http://www.cyberciti.biz/tips/shell-root-user-check-script.html +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" 1>&2 + exit 1 +fi + +case "$1" in + start) + start + exit 0 + ;; + stop) + stop + exit 0 + ;; + reload|restart|force-reload) + stop + start + exit 0 + ;; + **) + echo "Usage: $0 {start|stop|reload}" 1>&2 + exit 1 + ;; +esac +# + diff --git a/view1090.c b/view1090.c new file mode 100644 index 0000000..6673baf --- /dev/null +++ b/view1090.c @@ -0,0 +1,227 @@ +// view1090, a Mode S messages viewer for dump1090 devices. +// +// Copyright (C) 2013 by Malcolm Robb +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#include "view1090.h" +// +// ============================= Utility functions ========================== +// +void sigintHandler(int dummy) { + NOTUSED(dummy); + signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety + Modes.exit = 1; // Signal to threads that we are done +} +// +// =============================== Initialization =========================== +// +void view1090InitConfig(void) { + // Default everything to zero/NULL + memset(&Modes, 0, sizeof(Modes)); + memset(&View1090, 0, sizeof(View1090)); + + // Now initialise things that should not be 0/NULL to their defaults + Modes.check_crc = 1; + strcpy(View1090.net_input_beast_ipaddr,VIEW1090_NET_OUTPUT_IP_ADDRESS); + Modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT; + Modes.interactive_rows = MODES_INTERACTIVE_ROWS; + Modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL; + Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; + Modes.fUserLat = MODES_USER_LATITUDE_DFLT; + Modes.fUserLon = MODES_USER_LONGITUDE_DFLT; + + Modes.interactive = 1; +} +// +//========================================================================= +// +void view1090Init(void) { + + // Allocate the various buffers used by Modes + if ( NULL == (Modes.icao_cache = (uint32_t *) malloc(sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2))) + { + fprintf(stderr, "Out of memory allocating data buffer.\n"); + exit(1); + } + + // Clear the buffers that have just been allocated, just in-case + memset(Modes.icao_cache, 0, sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2); + + // Validate the users Lat/Lon home location inputs + if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90 + || (Modes.fUserLat < -90.0) // and + || (Modes.fUserLon > 360.0) // Longitude must be -180 to +360 + || (Modes.fUserLon < -180.0) ) { + Modes.fUserLat = Modes.fUserLon = 0.0; + } else if (Modes.fUserLon > 180.0) { // If Longitude is +180 to +360, make it -180 to 0 + Modes.fUserLon -= 360.0; + } + // If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the + // Atlantic ocean off the west coast of Africa. This is unlikely to be correct. + // Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian + // is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both. + // Testing the flag at runtime will be much quicker than ((fLon != 0.0) || (fLat != 0.0)) + Modes.bUserFlags &= ~MODES_USER_LATLON_VALID; + if ((Modes.fUserLat != 0.0) || (Modes.fUserLon != 0.0)) { + Modes.bUserFlags |= MODES_USER_LATLON_VALID; + } + + // Prepare error correction tables + modesInitErrorInfo(); +} +// +// ================================ Main ==================================== +// +void showHelp(void) { + printf( +"-----------------------------------------------------------------------------\n" +"| view1090 dump1090 Viewer Ver : "MODES_DUMP1090_VERSION " |\n" +"-----------------------------------------------------------------------------\n" + "--interactive Interactive mode refreshing data on screen\n" + "--interactive-rows Max number of rows in interactive mode (default: 15)\n" + "--interactive-ttl Remove from list if idle for (default: 60)\n" + "--interactive-rtl1090 Display flight table in RTL1090 format\n" + "--modeac Enable decoding of SSR modes 3/A & 3/C\n" + "--net-bo-ipaddr TCP Beast output listen IPv4 (default: 127.0.0.1)\n" + "--net-bo-port TCP Beast output listen port (default: 30005)\n" + "--lat Reference/receiver latitide for surface posn (opt)\n" + "--lon Reference/receiver longitude for surface posn (opt)\n" + "--no-crc-check Disable messages with broken CRC (discouraged)\n" + "--no-fix Disable single-bits error correction using CRC\n" + "--fix Enable single-bits error correction using CRC\n" + "--aggressive More CPU for more messages (two bits fixes, ...)\n" + "--metric Use metric units (meters, km/h, ...)\n" + "--help Show this help\n" + ); +} +// +//========================================================================= +// +int main(int argc, char **argv) { + int j, fd; + struct client *c; + + // Set sane defaults + + view1090InitConfig(); + signal(SIGINT, sigintHandler); // Define Ctrl/C handler (exit program) + + // Parse the command line options + for (j = 1; j < argc; j++) { + int more = ((j + 1) < argc); // There are more arguments + + if (!strcmp(argv[j],"--net-bo-port") && more) { + Modes.net_input_beast_port = atoi(argv[++j]); + } else if (!strcmp(argv[j],"--net-bo-ipaddr") && more) { + strcpy(View1090.net_input_beast_ipaddr, argv[++j]); + } else if (!strcmp(argv[j],"--modeac")) { + Modes.mode_ac = 1; + } else if (!strcmp(argv[j],"--interactive-rows") && more) { + Modes.interactive_rows = atoi(argv[++j]); + } else if (!strcmp(argv[j],"--interactive")) { + Modes.interactive = 1; + } else if (!strcmp(argv[j],"--interactive-ttl") && more) { + Modes.interactive_display_ttl = atoi(argv[++j]); + } else if (!strcmp(argv[j],"--interactive-rtl1090")) { + Modes.interactive = 1; + Modes.interactive_rtl1090 = 1; + } else if (!strcmp(argv[j],"--lat") && more) { + Modes.fUserLat = atof(argv[++j]); + } else if (!strcmp(argv[j],"--lon") && more) { + Modes.fUserLon = atof(argv[++j]); + } else if (!strcmp(argv[j],"--metric")) { + Modes.metric = 1; + } else if (!strcmp(argv[j],"--no-crc-check")) { + Modes.check_crc = 0; + } else if (!strcmp(argv[j],"--fix")) { + Modes.nfix_crc = 1; + } else if (!strcmp(argv[j],"--no-fix")) { + Modes.nfix_crc = 0; + } else if (!strcmp(argv[j],"--aggressive")) { + Modes.nfix_crc = MODES_MAX_BITERRORS; + } else if (!strcmp(argv[j],"--help")) { + showHelp(); + exit(0); + } else { + fprintf(stderr, "Unknown or not enough arguments for option '%s'.\n\n", argv[j]); + showHelp(); + exit(1); + } + } + + // Initialization + view1090Init(); + + // Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here. + if ((fd = anetTcpConnect(Modes.aneterr, View1090.net_input_beast_ipaddr, Modes.net_input_beast_port)) == ANET_ERR) { + fprintf(stderr, "Failed to connect to %s:%d\n", View1090.net_input_beast_ipaddr, Modes.net_input_beast_port); + exit(1); + } + // + // Setup a service callback client structure for a beast binary input (from dump1090) + // This is a bit dodgy under Windows. The fd parameter is a handle to the internet + // socket on which we are receiving data. Under Linux, these seem to start at 0 and + // count upwards. However, Windows uses "HANDLES" and these don't nececeriy start at 0. + // dump1090 limits fd to values less than 1024, and then uses the fd parameter to + // index into an array of clients. This is ok-ish if handles are allocated up from 0. + // However, there is no gaurantee that Windows will behave like this, and if Windows + // allocates a handle greater than 1024, then dump1090 won't like it. On my test machine, + // the first Windows handle is usually in the 0x54 (84 decimal) region. + + if (fd >= MODES_NET_MAX_FD) { // Max number of clients reached + close(fd); + exit(1); + } + + c = (struct client *) malloc(sizeof(*c)); + c->buflen = 0; + c->fd = + c->service = + Modes.bis = fd; + Modes.clients[fd] = c; + if (Modes.maxfd < fd) { + Modes.maxfd = fd; + } + + // Keep going till the user does something that stops us + while (!Modes.exit) { + modesReadFromClient(c,"",decodeBinMessage); + interactiveRemoveStaleAircrafts(); + interactiveShowData(); + } + + // The user has stopped us, so close any socket we opened + if (fd != ANET_ERR) + {close(fd);} + + pthread_exit(0); + + return (0); +} +// +//========================================================================= +// diff --git a/view1090.h b/view1090.h new file mode 100644 index 0000000..c4c97fe --- /dev/null +++ b/view1090.h @@ -0,0 +1,82 @@ +// view1090, a Mode S messages viewer for dump1090 devices. +// +// Copyright (C) 2013 by Malcolm Robb +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#ifndef __VIEW1090_H +#define __VIEW1090_H + +// ============================= Include files ========================== + +#include "dump1090.h" + +#ifndef _WIN32 + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include "rtl-sdr.h" + #include "anet.h" +#else + #include "winstubs.h" //Put everything Windows specific in here +#endif + +// ============================= #defines =============================== + +#define VIEW1090_NET_OUTPUT_IP_ADDRESS "127.0.0.1" + +#define NOTUSED(V) ((void) V) + +// ======================== structure declarations ======================== + +// Program global state +struct { // Internal state + // Networking + char net_input_beast_ipaddr[32]; // IPv4 address or network name of server/RPi +} View1090; + +// ======================== function declarations ========================= + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __VIEW1090_H