diff --git a/Makefile b/Makefile index 8a14b55..c6084a0 100644 --- a/Makefile +++ b/Makefile @@ -5,11 +5,11 @@ PROGNAME=dump1090 all: dump1090 -dump1090.o: dump1090.c - $(CC) $(CFLAGS) dump1090.c -c -o dump1090.o +%.o: %.c + $(CC) $(CFLAGS) -c $< -dump1090: dump1090.o - $(CC) -g -o dump1090 dump1090.o $(LIBS) +dump1090: dump1090.o anet.o + $(CC) -g -o dump1090 dump1090.o anet.o $(LIBS) clean: rm -f *.o dump1090 diff --git a/README b/README index 12281f3..70a807c 100644 --- a/README +++ b/README @@ -5,7 +5,8 @@ Dump 1090 is a Mode S decoder specifically designed for RTLSDR devices. The main features are: -* Robust decoding of weak messages. +* Robust decoding of weak messages, with mode1090 many users observed + improved range compared to other popular decoders. * Single bit errors correction using the 24 bit CRC. * Ability to decode DF11, DF17 messages. * Ability to decode DF formats like DF0, DF4, DF5, DF16, DF20 and DF21 @@ -14,6 +15,8 @@ The main features are: * Decode raw IQ samples from file (using --ifile command line switch). * Interactive mode where aircrafts currently detected are shown as a list refreshing as more data arrives. +* CPR coordinates decoding and track calculation from velocity. +* TCP server streaming raw data to connected clients (use --net). Installation --- @@ -94,6 +97,20 @@ If you can capture traffic that Dump1090 is not able to decode properly, drop me an email with a download link. I may try to improve the detection during my free time (this is just an hobby project). +Network server features +--- + +By enabling the networking support with --net Dump1090 starts listening +for clients connections on port 30002 (you can change the port using +the --net-ro-port option). + +Connected clients are served with data ASAP as they arrive from the device +(or from file if --ifile is used) in the raw format similar to the following: + +*8D451E8B99019699C00B0A81F36E; + +Every entry is separated by a simple newline (LF character, hex 0x0A). + Antenna --- diff --git a/anet.c b/anet.c new file mode 100644 index 0000000..8f8a904 --- /dev/null +++ b/anet.c @@ -0,0 +1,382 @@ +/* anet.c -- Basic TCP socket stuff made a bit less boring + * + * Copyright (c) 2006-2012, Salvatore Sanfilippo + * 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. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * 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 OWNER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "anet.h" + +static void anetSetError(char *err, const char *fmt, ...) +{ + va_list ap; + + if (!err) return; + va_start(ap, fmt); + vsnprintf(err, ANET_ERR_LEN, fmt, ap); + va_end(ap); +} + +int anetNonBlock(char *err, int fd) +{ + int flags; + + /* Set the socket nonblocking. + * Note that fcntl(2) for F_GETFL and F_SETFL can't be + * interrupted by a signal. */ + if ((flags = fcntl(fd, F_GETFL)) == -1) { + anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno)); + return ANET_ERR; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); + return ANET_ERR; + } + return ANET_OK; +} + +int anetTcpNoDelay(char *err, int fd) +{ + int yes = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) + { + anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); + return ANET_ERR; + } + return ANET_OK; +} + +int anetSetSendBuffer(char *err, int fd, int buffsize) +{ + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1) + { + anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno)); + return ANET_ERR; + } + return ANET_OK; +} + +int anetTcpKeepAlive(char *err, int fd) +{ + int yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) { + anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); + return ANET_ERR; + } + return ANET_OK; +} + +int anetResolve(char *err, char *host, char *ipbuf) +{ + struct sockaddr_in sa; + + sa.sin_family = AF_INET; + if (inet_aton(host, &sa.sin_addr) == 0) { + struct hostent *he; + + he = gethostbyname(host); + if (he == NULL) { + anetSetError(err, "can't resolve: %s", host); + return ANET_ERR; + } + memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); + } + strcpy(ipbuf,inet_ntoa(sa.sin_addr)); + return ANET_OK; +} + +static int anetCreateSocket(char *err, int domain) { + int s, on = 1; + if ((s = socket(domain, SOCK_STREAM, 0)) == -1) { + anetSetError(err, "creating socket: %s", strerror(errno)); + return ANET_ERR; + } + + /* Make sure connection-intensive things like the redis benckmark + * will be able to close/open sockets a zillion of times */ + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { + anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno)); + return ANET_ERR; + } + return s; +} + +#define ANET_CONNECT_NONE 0 +#define ANET_CONNECT_NONBLOCK 1 +static int anetTcpGenericConnect(char *err, char *addr, int port, int flags) +{ + int s; + struct sockaddr_in sa; + + if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) + return ANET_ERR; + + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + if (inet_aton(addr, &sa.sin_addr) == 0) { + struct hostent *he; + + he = gethostbyname(addr); + if (he == NULL) { + anetSetError(err, "can't resolve: %s", addr); + close(s); + return ANET_ERR; + } + memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); + } + if (flags & ANET_CONNECT_NONBLOCK) { + if (anetNonBlock(err,s) != ANET_OK) + return ANET_ERR; + } + if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { + if (errno == EINPROGRESS && + flags & ANET_CONNECT_NONBLOCK) + return s; + + anetSetError(err, "connect: %s", strerror(errno)); + close(s); + return ANET_ERR; + } + return s; +} + +int anetTcpConnect(char *err, char *addr, int port) +{ + return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE); +} + +int anetTcpNonBlockConnect(char *err, char *addr, int port) +{ + return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK); +} + +int anetUnixGenericConnect(char *err, char *path, int flags) +{ + int s; + struct sockaddr_un sa; + + if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) + return ANET_ERR; + + sa.sun_family = AF_LOCAL; + strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); + if (flags & ANET_CONNECT_NONBLOCK) { + if (anetNonBlock(err,s) != ANET_OK) + return ANET_ERR; + } + if (connect(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) { + if (errno == EINPROGRESS && + flags & ANET_CONNECT_NONBLOCK) + return s; + + anetSetError(err, "connect: %s", strerror(errno)); + close(s); + return ANET_ERR; + } + return s; +} + +int anetUnixConnect(char *err, char *path) +{ + return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE); +} + +int anetUnixNonBlockConnect(char *err, char *path) +{ + return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK); +} + +/* Like read(2) but make sure 'count' is read before to return + * (unless error or EOF condition is encountered) */ +int anetRead(int fd, char *buf, int count) +{ + int nread, totlen = 0; + while(totlen != count) { + nread = read(fd,buf,count-totlen); + if (nread == 0) return totlen; + if (nread == -1) return -1; + totlen += nread; + buf += nread; + } + return totlen; +} + +/* Like write(2) but make sure 'count' is read before to return + * (unless error is encountered) */ +int anetWrite(int fd, char *buf, int count) +{ + int nwritten, totlen = 0; + while(totlen != count) { + nwritten = write(fd,buf,count-totlen); + if (nwritten == 0) return totlen; + if (nwritten == -1) return -1; + totlen += nwritten; + buf += nwritten; + } + return totlen; +} + +static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) { + if (bind(s,sa,len) == -1) { + anetSetError(err, "bind: %s", strerror(errno)); + close(s); + return ANET_ERR; + } + + /* Use a backlog of 512 entries. We pass 511 to the listen() call because + * the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1); + * which will thus give us a backlog of 512 entries */ + if (listen(s, 511) == -1) { + anetSetError(err, "listen: %s", strerror(errno)); + close(s); + return ANET_ERR; + } + return ANET_OK; +} + +int anetTcpServer(char *err, int port, char *bindaddr) +{ + int s; + struct sockaddr_in sa; + + if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) + return ANET_ERR; + + memset(&sa,0,sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr.s_addr = htonl(INADDR_ANY); + if (bindaddr && inet_aton(bindaddr, &sa.sin_addr) == 0) { + anetSetError(err, "invalid bind address"); + close(s); + return ANET_ERR; + } + if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR) + return ANET_ERR; + return s; +} + +int anetUnixServer(char *err, char *path, mode_t perm) +{ + int s; + struct sockaddr_un sa; + + if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) + return ANET_ERR; + + memset(&sa,0,sizeof(sa)); + sa.sun_family = AF_LOCAL; + strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); + if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR) + return ANET_ERR; + if (perm) + chmod(sa.sun_path, perm); + return s; +} + +static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) { + int fd; + while(1) { + fd = accept(s,sa,len); + if (fd == -1) { + if (errno == EINTR) + continue; + else { + anetSetError(err, "accept: %s", strerror(errno)); + return ANET_ERR; + } + } + break; + } + return fd; +} + +int anetTcpAccept(char *err, int s, char *ip, int *port) { + int fd; + struct sockaddr_in sa; + socklen_t salen = sizeof(sa); + if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) + return ANET_ERR; + + if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); + if (port) *port = ntohs(sa.sin_port); + return fd; +} + +int anetUnixAccept(char *err, int s) { + int fd; + struct sockaddr_un sa; + socklen_t salen = sizeof(sa); + if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) + return ANET_ERR; + + return fd; +} + +int anetPeerToString(int fd, char *ip, int *port) { + struct sockaddr_in sa; + socklen_t salen = sizeof(sa); + + if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) { + *port = 0; + ip[0] = '?'; + ip[1] = '\0'; + return -1; + } + if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); + if (port) *port = ntohs(sa.sin_port); + return 0; +} + +int anetSockName(int fd, char *ip, int *port) { + struct sockaddr_in sa; + socklen_t salen = sizeof(sa); + + if (getsockname(fd,(struct sockaddr*)&sa,&salen) == -1) { + *port = 0; + ip[0] = '?'; + ip[1] = '\0'; + return -1; + } + if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); + if (port) *port = ntohs(sa.sin_port); + return 0; +} diff --git a/anet.h b/anet.h new file mode 100644 index 0000000..062b22c --- /dev/null +++ b/anet.h @@ -0,0 +1,58 @@ +/* anet.c -- Basic TCP socket stuff made a bit less boring + * + * Copyright (c) 2006-2012, Salvatore Sanfilippo + * 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. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * 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 OWNER 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 ANET_H +#define ANET_H + +#define ANET_OK 0 +#define ANET_ERR -1 +#define ANET_ERR_LEN 256 + +#if defined(__sun) +#define AF_LOCAL AF_UNIX +#endif + +int anetTcpConnect(char *err, char *addr, int port); +int anetTcpNonBlockConnect(char *err, char *addr, int port); +int anetUnixConnect(char *err, char *path); +int anetUnixNonBlockConnect(char *err, char *path); +int anetRead(int fd, char *buf, int count); +int anetResolve(char *err, char *host, char *ipbuf); +int anetTcpServer(char *err, int port, char *bindaddr); +int anetUnixServer(char *err, char *path, mode_t perm); +int anetTcpAccept(char *err, int serversock, char *ip, int *port); +int anetUnixAccept(char *err, int serversock); +int anetWrite(int fd, char *buf, int count); +int anetNonBlock(char *err, int fd); +int anetTcpNoDelay(char *err, int fd); +int anetTcpKeepAlive(char *err, int fd); +int anetPeerToString(int fd, char *ip, int *port); + +#endif diff --git a/dump1090.c b/dump1090.c index 14ce277..03a0f58 100644 --- a/dump1090.c +++ b/dump1090.c @@ -37,9 +37,11 @@ #include #include #include +#include #include #include #include "rtl-sdr.h" +#include "anet.h" #define MODES_DEFAULT_RATE 2000000 #define MODES_DEFAULT_FREQ 1090000000 @@ -76,8 +78,17 @@ #define MODES_INTERACTIVE_ROWS 15 /* Rows on screen */ #define MODES_INTERACTIVE_TTL 60 /* TTL before being removed */ +#define MODES_NET_MAX_FD 1024 +#define MODES_NET_OUTPUT_RAW_PORT 30002 + #define MODES_NOTUSED(V) ((void) V) +/* Structure used to describe a networking client. */ +struct client { + int fd; /* File descriptor. */ + int service; /* TCP port the client is connected to. */ +}; + /* Structure used to describe an aircraft in iteractive mode. */ struct aircraft { uint32_t addr; /* ICAO address */ @@ -121,15 +132,23 @@ struct { rtlsdr_dev_t *dev; int freq; + /* Networking */ + char aneterr[ANET_ERR_LEN]; + struct client *clients[MODES_NET_MAX_FD]; /* Our clients. */ + int maxfd; /* Greatest fd currently active. */ + int ros; /* Raw output listening socket. */ + /* Configuration */ char *filename; /* Input form file, --ifile option. */ int fix_errors; /* Single bit error correction if true. */ int check_crc; /* Only display messages with good CRC. */ - int raw; /* Raw output format */ - int debug; /* Debugging mode */ + int raw; /* Raw output format. */ + int debug; /* Debugging mode. */ + int net; /* Enable networking. */ + int net_output_raw_port; /* Raw output TCP port. */ int interactive; /* Interactive mode */ - int interactive_rows; /* Interactive mode: max number of rows */ - int interactive_ttl; /* Interactive mode: TTL before deletion */ + int interactive_rows; /* Interactive mode: max number of rows. */ + int interactive_ttl; /* Interactive mode: TTL before deletion. */ int stats; /* Print stats at exit in --ifile mode. */ int onlyaddr; /* Print only ICAO addresses. */ int metric; /* Use metric units. */ @@ -192,6 +211,7 @@ struct modesMessage { void interactiveShowData(void); void interactiveReceiveData(struct modesMessage *mm); +void modesSendRawOutput(struct modesMessage *mm); /* ============================= Utility functions ========================== */ @@ -216,6 +236,8 @@ void modesInitConfig(void) { Modes.fix_errors = 1; Modes.check_crc = 1; Modes.raw = 0; + Modes.net = 0; + Modes.net_output_raw_port = MODES_NET_OUTPUT_RAW_PORT; Modes.onlyaddr = 0; Modes.debug = 0; Modes.interactive = 0; @@ -1212,6 +1234,10 @@ void detectModeS(uint16_t *m, uint32_t mlen) { displayModesMessage(&mm); if (!Modes.raw && !Modes.onlyaddr) printf("\n"); } + /* Send data to connected clients. */ + if (Modes.net) { + modesSendRawOutput(&mm); /* Feed raw output clients. */ + } } /* Skip this message if we are sure it's fine. */ @@ -1533,19 +1559,120 @@ void snipMode(int level) { } } +/* ============================= Networking ================================= + * Note: here we risregard any kind of good coding practice in favor of + * extreme simplicity, that is: + * + * 1) We only rely on the kernel buffers for our I/O without any kind of + * user space buffering. + * 2) We don't register any kind of event handler, from time to time a + * function gets called and we accept new connections. All the rest is + * handled via non-blocking I/O and manually pullign clients to see if + * they have something new to share with us when reading is needed. + */ + +/* Networking "stack" initialization. */ +void modesInitNet(void) { + memset(Modes.clients,0,sizeof(Modes.clients)); + Modes.maxfd = -1; + Modes.ros = anetTcpServer(Modes.aneterr, Modes.net_output_raw_port, NULL); + anetNonBlock(Modes.aneterr, Modes.ros); + if (Modes.ros == -1) { + fprintf(stderr, "Error opening TCP port %d: %s\n", + Modes.net_output_raw_port, Modes.aneterr); + exit(1); + } + signal(SIGPIPE, SIG_IGN); +} + +/* This function gets called from time to time when the decoding thread is + * awakened by new data arriving. This usually happens a few times every + * second. */ +void modesAcceptClients(void) { + int fd, port; + struct client *c; + + while(1) { + fd = anetTcpAccept(Modes.aneterr, Modes.ros, NULL, &port); + if (fd == -1) return; + if (fd >= MODES_NET_MAX_FD) { + close(fd); + return; /* Max number of clients reached. */ + } + + c = malloc(sizeof(*c)); + c->service = Modes.ros; + c->fd = fd; + Modes.clients[fd] = c; + + if (Modes.maxfd < fd) Modes.maxfd = fd; + } +} + +/* On error free the client, collect the structure, adjust maxfd if needed. */ +void modesFreeClient(int fd) { + close(fd); + free(Modes.clients[fd]); + Modes.clients[fd] = NULL; + + /* If this was our maxfd, rescan the full clients array to check what's + * the new max. */ + if (Modes.maxfd == fd) { + int j; + + Modes.maxfd = -1; + for (j = 0; j < MODES_NET_MAX_FD; j++) { + if (Modes.clients[j]) Modes.maxfd = j; + } + } +} + +/* Send the specified message to all clients listening for a given service. */ +void modesSendAllClients(int service, void *msg, int len) { + int j; + struct client *c; + + for (j = 0; j <= Modes.maxfd; j++) { + c = Modes.clients[j]; + if (c && c->service == service) { + int nwritten = write(j, msg, len); + if (nwritten != len) { + modesFreeClient(j); + } + } + } +} + +/* Write raw output to TCP clients. */ +void modesSendRawOutput(struct modesMessage *mm) { + char msg[128], *p = msg; + int j; + + *p++ = '*'; + for (j = 0; j < mm->msgbits/8; j++) { + sprintf(p, "%02X", mm->msg[j]); + p += 2; + } + *p++ = ';'; + *p++ = '\n'; + modesSendAllClients(Modes.ros, msg, p-msg); +} + /* ================================ Main ==================================== */ void showHelp(void) { printf( "--device-index Select RTL device (default: 0).\n" "--gain Set gain (default: max gain. Use -100 for auto-gain).\n" -"--enable-agc Enable the Automatic Gain Control (default: off).\n" +"--enable-agc> Enable the Automatic Gain Control (default: off).\n" "--freq Set frequency (default: 1090 Mhz).\n" "--ifile Read data from file (use '-' for stdin).\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" "--raw Show only messages hex values.\n" +"--net Enable networking.\n" +"--net-ro-port TCP listening port for raw output (default: 30002).\n" "--no-fix Disable single-bits error correction using CRC.\n" "--no-crc-check Disable messages with broken CRC (discouraged).\n" "--stats With --ifile print stats at exit. No other output.\n" @@ -1583,6 +1710,10 @@ int main(int argc, char **argv) { Modes.check_crc = 0; } else if (!strcmp(argv[j],"--raw")) { Modes.raw = 1; + } else if (!strcmp(argv[j],"--net")) { + Modes.net = 1; + } else if (!strcmp(argv[j],"--net-ro-port") && more) { + Modes.net_output_raw_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--onlyaddr")) { Modes.onlyaddr = 1; } else if (!strcmp(argv[j],"--metric")) { @@ -1612,6 +1743,7 @@ int main(int argc, char **argv) { } } + /* Initialization */ modesInit(); if (Modes.filename == NULL) { modesInitRTLSDR(); @@ -1623,6 +1755,7 @@ int main(int argc, char **argv) { exit(1); } } + if (Modes.net) modesInitNet(); /* Create the thread that will read the data from the device. */ pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL); @@ -1646,6 +1779,7 @@ int main(int argc, char **argv) { * slow processors). */ pthread_mutex_unlock(&Modes.data_mutex); detectModeS(Modes.magnitude, Modes.data_len/2); + if (Modes.net) modesAcceptClients(); /* Refresh screen when in interactive mode. */ if (Modes.interactive &&