[hamradio-commits] [dump1090] 01/02: Imported Upstream version 1.09.1007.14+dfsg
Matthew Ernisse
mernisse-guest at moszumanska.debian.org
Fri Oct 24 14:29:00 UTC 2014
This is an automated email from the git hooks/post-receive script.
mernisse-guest pushed a commit to branch master
in repository dump1090.
commit 8e9c659264ea04cc9b2511ca2be79946fdd3120b
Author: Matthew Ernisse <mernisse at ub3rgeek.net>
Date: Fri Oct 24 10:01:38 2014 -0400
Imported Upstream version 1.09.1007.14+dfsg
---
.gitignore | 10 +
Makefile | 30 +
README.md | 284 +++++
TODO | 5 +
anet.c | 345 ++++++
anet.h | 59 +
coaa.h | 6 +
dump1090-win.1.09.0608.14.zip | Bin 0 -> 614488 bytes
dump1090.c | 901 +++++++++++++++
dump1090.dsp | 148 +++
dump1090.dsw | 41 +
dump1090.h | 465 ++++++++
dump1090.sh | 74 ++
interactive.c | 561 ++++++++++
makedump1090 | 27 +
makeppup1090 | 27 +
makeview1090 | 27 +
mode_ac.c | 386 +++++++
mode_s.c | 2172 ++++++++++++++++++++++++++++++++++++
net_io.c | 971 ++++++++++++++++
ppup1090.c | 261 +++++
ppup1090.h | 110 ++
ppup1090.sh | 85 ++
pthreads/pthread.h | 1368 +++++++++++++++++++++++
pthreads/sched.h | 183 +++
pthreads/semaphore.h | 169 +++
public_html/config.js | 33 +
public_html/coolclock/coolclock.js | 318 ++++++
public_html/coolclock/excanvas.js | 785 +++++++++++++
public_html/coolclock/moreskins.js | 212 ++++
public_html/extension.js | 19 +
public_html/gmap.html | 63 ++
public_html/options.js | 17 +
public_html/planeObject.js | 254 +++++
public_html/script.js | 630 +++++++++++
public_html/style.css | 32 +
rtlsdr/rtl-sdr.h | 366 ++++++
rtlsdr/rtl-sdr_export.h | 47 +
testfiles/modes1.bin | 13 +
tools/debug.html | 193 ++++
view1090.c | 321 ++++++
view1090.dsp | 149 +++
view1090.h | 84 ++
winstubs.h | 110 ++
44 files changed, 12361 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..67495f9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.o
+dump1090
+testfiles/*.bin
+misc
+frames.js
+.*.swp
+*~
+*.rej
+*.orig
+untrackedDeveloperSettings.js
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6bfe9ee
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,30 @@
+#
+# When building a package or installing otherwise in the system, make
+# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
+#
+PROGNAME=dump1090
+
+ifdef PREFIX
+BINDIR=$(PREFIX)/bin
+SHAREDIR=$(PREFIX)/share/$(PROGNAME)
+EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\"
+endif
+
+CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr`
+LIBS=`pkg-config --libs librtlsdr` -lpthread -lm
+CC=gcc
+
+
+all: dump1090 view1090
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
+
+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) $(LDFLAGS)
+
+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) $(LDFLAGS)
+
+clean:
+ rm -f *.o dump1090 view1090
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3fdd396
--- /dev/null
+++ b/README.md
@@ -0,0 +1,284 @@
+Dump1090 README
+===
+
+Dump 1090 is a Mode S decoder specifically designed for RTLSDR devices.
+
+The main features are:
+
+* Robust decoding of weak messages, with mode1090 many users observed
+ improved range compared to other popular decoders.
+* Network support: TCP30003 stream (MSG5...), Raw packets, HTTP.
+* Embedded HTTP server that displays the currently detected aircrafts on
+ Google Map.
+* 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
+ where the checksum is xored with the ICAO address by brute forcing the
+ checksum field using recently seen ICAO addresses.
+* Decode raw IQ samples from file (using --ifile command line switch).
+* Interactive command-line-interfae 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 and recceiving raw data to/from connected clients
+ (using --net).
+
+Installation
+---
+
+Type "make".
+
+Normal usage
+---
+
+To capture traffic directly from your RTL device and show the captured traffic
+on standard output, just run the program without options at all:
+
+ ./dump1090
+
+To just output hexadecimal messages:
+
+ ./dump1090 --raw
+
+To run the program in interactive mode:
+
+ ./dump1090 --interactive
+
+To run the program in interactive mode, with networking support, and connect
+with your browser to http://localhost:8080 to see live traffic:
+
+ ./dump1090 --interactive --net
+
+In iteractive mode it is possible to have a less information dense but more
+"arcade style" output, where the screen is refreshed every second displaying
+all the recently seen aircrafts with some additional information such as
+altitude and flight number, extracted from the received Mode S packets.
+
+Using files as source of data
+---
+
+To decode data from file, use:
+
+ ./dump1090 --ifile /path/to/binfile
+
+The binary file should be created using `rtl_sdr` like this (or with any other
+program that is able to output 8-bit unsigned IQ samples at 2Mhz sample rate).
+
+ rtl_sdr -f 1090000000 -s 2000000 -g 50 output.bin
+
+In the example `rtl_sdr` a gain of 50 is used, simply you should use the highest
+gain availabe for your tuner. This is not needed when calling Dump1090 itself
+as it is able to select the highest gain supported automatically.
+
+It is possible to feed the program with data via standard input using
+the --ifile option with "-" as argument.
+
+Additional options
+---
+
+Dump1090 can be called with other command line options to set a different
+gain, frequency, and so forth. For a list of options use:
+
+ ./dump1090 --help
+
+Everything is not documented here should be obvious, and for most users calling
+it without arguments at all is the best thing to do.
+
+Reliability
+---
+
+By default Dump1090 checks for decoding errors using the 24-bit CRC checksum,
+where available. Messages with errors are discarded.
+
+The --fix command line switch enables fixing single bit error correction
+based on the CRC checksum. Technically, it uses a table of precomputed
+checksum differences resulting from single bit errors to look up the
+wrong bit position.
+
+This is indeed able to fix errors and works reliably in my experience,
+however if you are interested in very reliable data I suggest to use
+the --no-fix command line switch in order to disable error fixing.
+
+Performances and sensibility of detection
+---
+
+In my limited experience Dump1090 was able to decode a big number of messages
+even in conditions where I encountered problems using other programs, however
+no formal test was performed so I can't really claim that this program is
+better or worse compared to other similar programs.
+
+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 and 30001 (you can change both the
+ports if you want, see --help output).
+
+Port 30002
+---
+
+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).
+
+Port 30001
+---
+
+Port 30001 is the raw input port, and can be used to feed Dump1090 with
+data in the same format as specified above, with hex messages starting with
+a `*` and ending with a `;` character.
+
+So for instance if there is another remote Dump1090 instance collecting data
+it is possible to sum the output to a local Dump1090 instance doing something
+like this:
+
+ nc remote-dump1090.example.net 30002 | nc localhost 30001
+
+It is important to note that what is received via port 30001 is also
+broadcasted to clients listening to port 30002.
+
+In general everything received from port 30001 is handled exactly like the
+normal traffic from RTL devices or from file when --ifile is used.
+
+It is possible to use Dump1090 just as an hub using --ifile with /dev/zero
+as argument as in the following example:
+
+ ./dump1090 --net-only
+
+Or alternatively to see what's happening on the screen:
+
+ ./dump1090 --net-only --interactive
+
+Then you can feed it from different data sources from the internet.
+
+Port 30003
+---
+
+Connected clients are served with messages in SBS1 (BaseStation) format,
+similar to:
+
+ MSG,4,,,738065,,,,,,,,420,179,,,0,,0,0,0,0
+ MSG,3,,,738065,,,,,,,35000,,,34.81609,34.07810,,,0,0,0,0
+
+This can be used to feed data to various sharing sites without the need to use another decoder.
+
+Antenna
+---
+
+Mode S messages are transmitted in the 1090 Mhz frequency. If you have a decent
+antenna you'll be able to pick up signals from aircrafts pretty far from your
+position, especially if you are outdoor and in a position with a good sky view.
+
+You can easily build a very cheap antenna following the istructions at:
+
+ http://antirez.com/news/46
+
+With this trivial antenna I was able to pick up signals of aircrafts 200+ Km
+away from me.
+
+If you are interested in a more serious antenna check the following
+resources:
+
+* http://gnuradio.org/redmine/attachments/download/246/06-foster-adsb.pdf
+* http://www.lll.lu/~edward/edward/adsb/antenna/ADSBantenna.html
+* http://modesbeast.com/pix/adsb-ant-drawing.gif
+
+Aggressive mode
+---
+
+With --aggressive it is possible to activate the *aggressive mode* that is a
+modified version of the Mode S packet detection and decoding.
+The aggresive mode uses more CPU usually (especially if there are many planes
+sending DF17 packets), but can detect a few more messages.
+
+The algorithm in aggressive mode is modified in the following ways:
+
+* Up to two demodulation errors are tolerated (adjacent entires in the
+ magnitude vector with the same eight). Normally only messages without
+ errors are checked.
+* It tries to fix DF17 messages with CRC errors resulting from any two bit
+ errors.
+
+The use of aggressive mdoe is only advised in places where there is
+low traffic in order to have a chance to capture some more messages.
+
+Debug mode
+---
+
+The Debug mode is a visual help to improve the detection algorithm or to
+understand why the program is not working for a given input.
+
+In this mode messages are displayed in an ASCII-art style graphical
+representation, where the individial magnitude bars sampled at 2Mhz are
+displayed.
+
+An index shows the sample number, where 0 is the sample where the first
+Mode S peak was found. Some additional background noise is also added
+before the first peak to provide some context.
+
+To enable debug mode and check what combinations of packets you can
+log, use `mode1090 --help` to obtain a list of available debug flags.
+
+Debug mode includes an optional javascript output that is used to visualize
+packets using a web browser, you can use the file debug.html under the
+'tools' directory to load the generated frames.js file.
+
+How this program works?
+---
+
+The code is very documented and written in order to be easy to understand.
+For the diligent programmer with a Mode S specification on his hands it
+should be trivial to understand how it works.
+
+The algorithms I used were obtained basically looking at many messages
+as displayed using a trow-away SDL program, and trying to model the algorithm
+based on how the messages look graphically.
+
+How to test the program?
+---
+
+If you have an RTLSDR device and you happen to be in an area where there
+are aircrafts flying over your head, just run the program and check for signals.
+
+However if you don't have an RTLSDR device, or if in your area the presence
+of aircrafts is very limited, you may want to try the sample file distributed
+with the Dump1090 distribution under the "testfiles" directory.
+
+Just run it like this:
+
+ ./dump1090 --ifile testfiles/modes1.bin
+
+What is --strip mode?
+---
+
+It is just a simple filter that will get raw IQ 8 bit samples in input
+and will output a file missing all the parts of the file where I and Q
+are lower than the specified <level> for more than 32 samples.
+
+Use it like this:
+
+ cat big.bin | ./dump1090 --snip 25 > small.bin
+
+I used it in order to create a small test file to include inside this
+program source code distribution.
+
+Contributing
+---
+
+Dump1090 was written during some free time during xmas 2012, it is an hobby
+project so I'll be able to address issues and improve it only during
+free time, however you are incouraged to send pull requests in order to
+improve the program. A good starting point can be the TODO list included in
+the source distribution.
+
+Credits
+---
+
+Dump1090 was written by Salvatore Sanfilippo <antirez at gmail.com> and is
+released under the BSD three clause license.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..041ad29
--- /dev/null
+++ b/TODO
@@ -0,0 +1,5 @@
+TODO
+
+* Extract more information from captured Mode S messages.
+* Improve the web interface gmap.html.
+* Enhance the algorithm to reliably decode more messages.
diff --git a/anet.c b/anet.c
new file mode 100644
index 0000000..859c98c
--- /dev/null
+++ b/anet.c
@@ -0,0 +1,345 @@
+/* anet.c -- Basic TCP socket stuff made a bit less boring
+ *
+ * Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * 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 _WIN32
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/stat.h>
+ #include <sys/un.h>
+ #include <netinet/in.h>
+ #include <netinet/tcp.h>
+ #include <arpa/inet.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <string.h>
+ #include <netdb.h>
+ #include <errno.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+#else
+ #include "winstubs.h" //Put everything Windows specific in here
+ #include "dump1090.h"
+#endif
+
+#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;
+#ifndef _WIN32
+ /* 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;
+ }
+#else
+ flags = 1;
+ if (ioctlsocket(fd, FIONBIO, &flags)) {
+ errno = WSAGetLastError();
+ anetSetError(err, "ioctlsocket(FIONBIO): %s", strerror(errno));
+ return ANET_ERR;
+ }
+#endif
+ return ANET_OK;
+}
+
+int anetTcpNoDelay(char *err, int fd)
+{
+ int yes = 1;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&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, (void*)&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, (void*)&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, (void*)&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) {
+#ifdef _WIN32
+ errno = WSAGetLastError();
+#endif
+ 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, (void*)&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;
+
+ memset(&sa,0,sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons((uint16_t)port);
+ if (inet_aton(addr, (void*)&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);
+}
+
+/* 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) {
+#ifdef _WIN32
+ errno = WSAGetLastError();
+#endif
+ 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) {
+#ifdef _WIN32
+ errno = WSAGetLastError();
+#endif
+ 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((uint16_t)port);
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bindaddr && inet_aton(bindaddr, (void*)&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;
+}
+
+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) {
+#ifndef _WIN32
+ if (errno == EINTR) {
+ continue;
+#else
+ errno = WSAGetLastError();
+ if (errno == WSAEWOULDBLOCK) {
+#endif
+ } else {
+ anetSetError(err, "accept: %s", strerror(errno));
+ }
+ }
+ 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 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..6d74af5
--- /dev/null
+++ b/anet.h
@@ -0,0 +1,59 @@
+/* anet.c -- Basic TCP socket stuff made a bit less boring
+ *
+ * Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * 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);
+int anetSetSendBuffer(char *err, int fd, int buffsize);
+
+#endif
diff --git a/coaa.h b/coaa.h
new file mode 100644
index 0000000..3d07704
--- /dev/null
+++ b/coaa.h
@@ -0,0 +1,6 @@
+// coaa.h configuration file for Plane Plotter Uploader
+//
+// You MUST apply via the COAA website for your own personal version of this file
+// Do not disclose the contents of this file to anyone thereafter as it uniquely
+// identifies you to the PlanePlotter system
+//
diff --git a/dump1090-win.1.09.0608.14.zip b/dump1090-win.1.09.0608.14.zip
new file mode 100644
index 0000000..5bd3d84
Binary files /dev/null and b/dump1090-win.1.09.0608.14.zip differ
diff --git a/dump1090.c b/dump1090.c
new file mode 100644
index 0000000..e8096a3
--- /dev/null
+++ b/dump1090.c
@@ -0,0 +1,901 @@
+// dump1090, a Mode S messages decoder for RTLSDR devices.
+//
+// Copyright (C) 2012 by Salvatore Sanfilippo <antirez at gmail.com>
+//
+// 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 "coaa.h"
+#include "dump1090.h"
+//
+// ============================= Utility functions ==========================
+//
+void sigintHandler(int dummy) {
+ MODES_NOTUSED(dummy);
+ signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety
+ Modes.exit = 1; // Signal to threads that we are done
+}
+//
+// =============================== Terminal handling ========================
+//
+#ifndef _WIN32
+// Get the number of rows after the terminal changes size.
+int getTermRows() {
+ struct winsize w;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+ return (w.ws_row);
+}
+
+// Handle resizing terminal
+void sigWinchCallback() {
+ signal(SIGWINCH, SIG_IGN);
+ Modes.interactive_rows = getTermRows();
+ interactiveShowData();
+ signal(SIGWINCH, sigWinchCallback);
+}
+#else
+int getTermRows() { return MODES_INTERACTIVE_ROWS;}
+#endif
+//
+// =============================== Initialization ===========================
+//
+void modesInitConfig(void) {
+ // Default everything to zero/NULL
+ memset(&Modes, 0, sizeof(Modes));
+
+ // Now initialise things that should not be 0/NULL to their defaults
+ Modes.gain = MODES_MAX_GAIN;
+ Modes.freq = MODES_DEFAULT_FREQ;
+ Modes.ppm_error = MODES_DEFAULT_PPM;
+ Modes.check_crc = 1;
+ Modes.net_heartbeat_rate = MODES_NET_HEARTBEAT_RATE;
+ Modes.net_output_sbs_port = MODES_NET_OUTPUT_SBS_PORT;
+ Modes.net_output_raw_port = MODES_NET_OUTPUT_RAW_PORT;
+ Modes.net_input_raw_port = MODES_NET_INPUT_RAW_PORT;
+ Modes.net_output_beast_port = MODES_NET_OUTPUT_BEAST_PORT;
+ Modes.net_input_beast_port = MODES_NET_INPUT_BEAST_PORT;
+ Modes.net_http_port = MODES_NET_HTTP_PORT;
+ Modes.interactive_rows = getTermRows();
+ 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;
+}
+//
+//=========================================================================
+//
+void modesInit(void) {
+ int i, q;
+
+ pthread_mutex_init(&Modes.pDF_mutex,NULL);
+ pthread_mutex_init(&Modes.data_mutex,NULL);
+ pthread_cond_init(&Modes.data_cond,NULL);
+
+ // Allocate the various buffers used by Modes
+ if ( ((Modes.icao_cache = (uint32_t *) malloc(sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2) ) == NULL) ||
+ ((Modes.pFileData = (uint16_t *) malloc(MODES_ASYNC_BUF_SIZE) ) == NULL) ||
+ ((Modes.magnitude = (uint16_t *) malloc(MODES_ASYNC_BUF_SIZE+MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE) ) == NULL) ||
+ ((Modes.maglut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) ||
+ ((Modes.beastOut = (char *) malloc(MODES_RAWOUT_BUF_SIZE) ) == NULL) ||
+ ((Modes.rawOut = (char *) malloc(MODES_RAWOUT_BUF_SIZE) ) == NULL) )
+ {
+ 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);
+ memset(Modes.pFileData,127, MODES_ASYNC_BUF_SIZE);
+ memset(Modes.magnitude, 0, MODES_ASYNC_BUF_SIZE+MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE);
+
+ // 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;
+ }
+
+ // Limit the maximum requested raw output size to less than one Ethernet Block
+ if (Modes.net_output_raw_size > (MODES_RAWOUT_BUF_FLUSH))
+ {Modes.net_output_raw_size = MODES_RAWOUT_BUF_FLUSH;}
+ if (Modes.net_output_raw_rate > (MODES_RAWOUT_BUF_RATE))
+ {Modes.net_output_raw_rate = MODES_RAWOUT_BUF_RATE;}
+ if (Modes.net_sndbuf_size > (MODES_NET_SNDBUF_MAX))
+ {Modes.net_sndbuf_size = MODES_NET_SNDBUF_MAX;}
+
+ // Initialise the Block Timers to something half sensible
+ ftime(&Modes.stSystemTimeBlk);
+ for (i = 0; i < MODES_ASYNC_BUF_NUMBER; i++)
+ {Modes.stSystemTimeRTL[i] = Modes.stSystemTimeBlk;}
+
+ // Each I and Q value varies from 0 to 255, which represents a range from -1 to +1. To get from the
+ // unsigned (0-255) range you therefore subtract 127 (or 128 or 127.5) from each I and Q, giving you
+ // a range from -127 to +128 (or -128 to +127, or -127.5 to +127.5)..
+ //
+ // To decode the AM signal, you need the magnitude of the waveform, which is given by sqrt((I^2)+(Q^2))
+ // The most this could be is if I&Q are both 128 (or 127 or 127.5), so you could end up with a magnitude
+ // of 181.019 (or 179.605, or 180.312)
+ //
+ // However, in reality the magnitude of the signal should never exceed the range -1 to +1, because the
+ // values are I = rCos(w) and Q = rSin(w). Therefore the integer computed magnitude should (can?) never
+ // exceed 128 (or 127, or 127.5 or whatever)
+ //
+ // If we scale up the results so that they range from 0 to 65535 (16 bits) then we need to multiply
+ // by 511.99, (or 516.02 or 514). antirez's original code multiplies by 360, presumably because he's
+ // assuming the maximim calculated amplitude is 181.019, and (181.019 * 360) = 65166.
+ //
+ // So lets see if we can improve things by subtracting 127.5, Well in integer arithmatic we can't
+ // subtract half, so, we'll double everything up and subtract one, and then compensate for the doubling
+ // in the multiplier at the end.
+ //
+ // If we do this we can never have I or Q equal to 0 - they can only be as small as +/- 1.
+ // This gives us a minimum magnitude of root 2 (0.707), so the dynamic range becomes (1.414-255). This
+ // also affects our scaling value, which is now 65535/(255 - 1.414), or 258.433254
+ //
+ // The sums then become mag = 258.433254 * (sqrt((I*2-255)^2 + (Q*2-255)^2) - 1.414)
+ // or mag = (258.433254 * sqrt((I*2-255)^2 + (Q*2-255)^2)) - 365.4798
+ //
+ // We also need to clip mag just incaes any rogue I/Q values somehow do have a magnitude greater than 255.
+ //
+
+ for (i = 0; i <= 255; i++) {
+ for (q = 0; q <= 255; q++) {
+ int mag, mag_i, mag_q;
+
+ mag_i = (i * 2) - 255;
+ mag_q = (q * 2) - 255;
+
+ mag = (int) round((sqrt((mag_i*mag_i)+(mag_q*mag_q)) * 258.433254) - 365.4798);
+
+ Modes.maglut[(i*256)+q] = (uint16_t) ((mag < 65535) ? mag : 65535);
+ }
+ }
+
+ // Prepare error correction tables
+ modesInitErrorInfo();
+}
+//
+// =============================== RTLSDR handling ==========================
+//
+void modesInitRTLSDR(void) {
+ int j;
+ int device_count;
+ char vendor[256], product[256], serial[256];
+
+ device_count = rtlsdr_get_device_count();
+ if (!device_count) {
+ fprintf(stderr, "No supported RTLSDR devices found.\n");
+ exit(1);
+ }
+
+ fprintf(stderr, "Found %d device(s):\n", device_count);
+ for (j = 0; j < device_count; j++) {
+ rtlsdr_get_device_usb_strings(j, vendor, product, serial);
+ fprintf(stderr, "%d: %s, %s, SN: %s %s\n", j, vendor, product, serial,
+ (j == Modes.dev_index) ? "(currently selected)" : "");
+ }
+
+ if (rtlsdr_open(&Modes.dev, Modes.dev_index) < 0) {
+ fprintf(stderr, "Error opening the RTLSDR device: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ // Set gain, frequency, sample rate, and reset the device
+ rtlsdr_set_tuner_gain_mode(Modes.dev,
+ (Modes.gain == MODES_AUTO_GAIN) ? 0 : 1);
+ if (Modes.gain != MODES_AUTO_GAIN) {
+ if (Modes.gain == MODES_MAX_GAIN) {
+ // Find the maximum gain available
+ int numgains;
+ int gains[100];
+
+ numgains = rtlsdr_get_tuner_gains(Modes.dev, gains);
+ Modes.gain = gains[numgains-1];
+ fprintf(stderr, "Max available gain is: %.2f\n", Modes.gain/10.0);
+ }
+ rtlsdr_set_tuner_gain(Modes.dev, Modes.gain);
+ fprintf(stderr, "Setting gain to: %.2f\n", Modes.gain/10.0);
+ } else {
+ fprintf(stderr, "Using automatic gain control.\n");
+ }
+ rtlsdr_set_freq_correction(Modes.dev, Modes.ppm_error);
+ if (Modes.enable_agc) rtlsdr_set_agc_mode(Modes.dev, 1);
+ rtlsdr_set_center_freq(Modes.dev, Modes.freq);
+ rtlsdr_set_sample_rate(Modes.dev, MODES_DEFAULT_RATE);
+ rtlsdr_reset_buffer(Modes.dev);
+ fprintf(stderr, "Gain reported by device: %.2f\n",
+ rtlsdr_get_tuner_gain(Modes.dev)/10.0);
+}
+//
+//=========================================================================
+//
+// We use a thread reading data in background, while the main thread
+// handles decoding and visualization of data to the user.
+//
+// The reading thread calls the RTLSDR API to read data asynchronously, and
+// uses a callback to populate the data buffer.
+//
+// A Mutex is used to avoid races with the decoding thread.
+//
+void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) {
+
+ MODES_NOTUSED(ctx);
+
+ // Lock the data buffer variables before accessing them
+ pthread_mutex_lock(&Modes.data_mutex);
+
+ Modes.iDataIn &= (MODES_ASYNC_BUF_NUMBER-1); // Just incase!!!
+
+ // Get the system time for this block
+ ftime(&Modes.stSystemTimeRTL[Modes.iDataIn]);
+
+ if (len > MODES_ASYNC_BUF_SIZE) {len = MODES_ASYNC_BUF_SIZE;}
+
+ // Queue the new data
+ Modes.pData[Modes.iDataIn] = (uint16_t *) buf;
+ Modes.iDataIn = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn + 1);
+ Modes.iDataReady = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn - Modes.iDataOut);
+
+ if (Modes.iDataReady == 0) {
+ // Ooooops. We've just received the MODES_ASYNC_BUF_NUMBER'th outstanding buffer
+ // This means that RTLSDR is currently overwriting the MODES_ASYNC_BUF_NUMBER+1
+ // buffer, but we havent yet processed it, so we're going to lose it. There
+ // isn't much we can do to recover the lost data, but we can correct things to
+ // avoid any additional problems.
+ Modes.iDataOut = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataOut+1);
+ Modes.iDataReady = (MODES_ASYNC_BUF_NUMBER-1);
+ Modes.iDataLost++;
+ }
+
+ // Signal to the other thread that new data is ready, and unlock
+ pthread_cond_signal(&Modes.data_cond);
+ pthread_mutex_unlock(&Modes.data_mutex);
+}
+//
+//=========================================================================
+//
+// This is used when --ifile is specified in order to read data from file
+// instead of using an RTLSDR device
+//
+void readDataFromFile(void) {
+ pthread_mutex_lock(&Modes.data_mutex);
+ while(Modes.exit == 0) {
+ ssize_t nread, toread;
+ unsigned char *p;
+
+ if (Modes.iDataReady) {
+ pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex);
+ continue;
+ }
+
+ if (Modes.interactive) {
+ // When --ifile and --interactive are used together, slow down
+ // playing at the natural rate of the RTLSDR received.
+ pthread_mutex_unlock(&Modes.data_mutex);
+ usleep(64000);
+ pthread_mutex_lock(&Modes.data_mutex);
+ }
+
+ toread = MODES_ASYNC_BUF_SIZE;
+ p = (unsigned char *) Modes.pFileData;
+ while(toread) {
+ nread = read(Modes.fd, p, toread);
+ if (nread <= 0) {
+ Modes.exit = 1; // Signal the other threads to exit.
+ break;
+ }
+ p += nread;
+ toread -= nread;
+ }
+ if (toread) {
+ // Not enough data on file to fill the buffer? Pad with no signal.
+ memset(p,127,toread);
+ }
+
+ Modes.iDataIn &= (MODES_ASYNC_BUF_NUMBER-1); // Just incase!!!
+
+ // Get the system time for this block
+ ftime(&Modes.stSystemTimeRTL[Modes.iDataIn]);
+
+ // Queue the new data
+ Modes.pData[Modes.iDataIn] = Modes.pFileData;
+ Modes.iDataIn = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn + 1);
+ Modes.iDataReady = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn - Modes.iDataOut);
+
+ // Signal to the other thread that new data is ready
+ pthread_cond_signal(&Modes.data_cond);
+ }
+}
+//
+//=========================================================================
+//
+// We read data using a thread, so the main thread only handles decoding
+// without caring about data acquisition
+//
+void *readerThreadEntryPoint(void *arg) {
+ MODES_NOTUSED(arg);
+
+ if (Modes.filename == NULL) {
+ rtlsdr_read_async(Modes.dev, rtlsdrCallback, NULL,
+ MODES_ASYNC_BUF_NUMBER,
+ MODES_ASYNC_BUF_SIZE);
+ } else {
+ readDataFromFile();
+ }
+ // Signal to the other thread that new data is ready - dummy really so threads don't mutually lock
+ pthread_cond_signal(&Modes.data_cond);
+ pthread_mutex_unlock(&Modes.data_mutex);
+#ifndef _WIN32
+ pthread_exit(NULL);
+#else
+ return NULL;
+#endif
+}
+//
+// ============================== Snip mode =================================
+//
+// Get raw IQ samples and filter everything is < than the specified level
+// for more than 256 samples in order to reduce example file size
+//
+void snipMode(int level) {
+ int i, q;
+ uint64_t c = 0;
+
+ while ((i = getchar()) != EOF && (q = getchar()) != EOF) {
+ if (abs(i-127) < level && abs(q-127) < level) {
+ c++;
+ if (c > MODES_PREAMBLE_SIZE) continue;
+ } else {
+ c = 0;
+ }
+ putchar(i);
+ putchar(q);
+ }
+}
+//
+// ================================ Main ====================================
+//
+void showHelp(void) {
+ printf(
+"-----------------------------------------------------------------------------\n"
+"| dump1090 ModeS Receiver Ver : " MODES_DUMP1090_VERSION " |\n"
+"-----------------------------------------------------------------------------\n"
+"--device-index <index> Select RTL device (default: 0)\n"
+"--gain <db> Set gain (default: max gain. Use -10 for auto-gain)\n"
+"--enable-agc Enable the Automatic Gain Control (default: off)\n"
+"--freq <hz> Set frequency (default: 1090 Mhz)\n"
+"--ifile <filename> Read data from file (use '-' for stdin)\n"
+"--interactive Interactive mode refreshing data on screen\n"
+"--interactive-rows <num> Max number of rows in interactive mode (default: 15)\n"
+"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60)\n"
+"--interactive-rtl1090 Display flight table in RTL1090 format\n"
+"--raw Show only messages hex values\n"
+"--net Enable networking\n"
+"--modeac Enable decoding of SSR Modes 3/A & 3/C\n"
+"--net-beast TCP raw output in Beast binary format\n"
+"--net-only Enable just networking, no RTL device or file used\n"
+"--net-http-port <port> HTTP server port (default: 8080)\n"
+"--net-ri-port <port> TCP raw input listen port (default: 30001)\n"
+"--net-ro-port <port> TCP raw output listen port (default: 30002)\n"
+"--net-sbs-port <port> TCP BaseStation output listen port (default: 30003)\n"
+"--net-bi-port <port> TCP Beast input listen port (default: 30004)\n"
+"--net-bo-port <port> TCP Beast output listen port (default: 30005)\n"
+"--net-ro-size <size> TCP raw output minimum size (default: 0)\n"
+"--net-ro-rate <rate> TCP raw output memory flush rate (default: 0)\n"
+"--net-heartbeat <rate> TCP heartbeat rate in seconds (default: 60 sec; 0 to disable)\n"
+"--net-buffer <n> TCP buffer size 64Kb * (2^n) (default: n=0, 64Kb)\n"
+"--lat <latitude> Reference/receiver latitude for surface posn (opt)\n"
+"--lon <longitude> Reference/receiver longitude for surface posn (opt)\n"
+"--fix Enable single-bits error correction using CRC\n"
+"--no-fix Disable single-bits error correction using CRC\n"
+"--no-crc-check Disable messages with broken CRC (discouraged)\n"
+"--phase-enhance Enable phase enhancement\n"
+"--aggressive More CPU for more messages (two bits fixes, ...)\n"
+"--mlat display raw messages in Beast ascii mode\n"
+"--stats With --ifile print stats at exit. No other output\n"
+"--stats-every <seconds> Show and reset stats every <seconds> seconds\n"
+"--onlyaddr Show only ICAO addresses (testing purposes)\n"
+"--metric Use metric units (meters, km/h, ...)\n"
+"--snip <level> Strip IQ file removing samples < level\n"
+"--debug <flags> Debug mode (verbose), see README for details\n"
+"--quiet Disable output to stdout. Use for daemon applications\n"
+"--ppm <error> Set receiver error in parts per million (default 0)\n"
+"--help Show this help\n"
+"\n"
+"Debug mode flags: d = Log frames decoded with errors\n"
+" D = Log frames decoded with zero errors\n"
+" c = Log frames with bad CRC\n"
+" C = Log frames with good CRC\n"
+" p = Log frames with bad preamble\n"
+" n = Log network debugging info\n"
+" j = Log frames to frames.js, loadable by debug.html\n"
+ );
+}
+
+#ifdef _WIN32
+void showCopyright(void) {
+ uint64_t llTime = time(NULL) + 1;
+
+ printf(
+"-----------------------------------------------------------------------------\n"
+"| dump1090 ModeS Receiver Ver : " MODES_DUMP1090_VERSION " |\n"
+"-----------------------------------------------------------------------------\n"
+"\n"
+" Copyright (C) 2012 by Salvatore Sanfilippo <antirez at gmail.com>\n"
+" Copyright (C) 2014 by Malcolm Robb <support at attavionics.com>\n"
+"\n"
+" All rights reserved.\n"
+"\n"
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+" ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n"
+" For further details refer to <https://github.com/MalcolmRobb/dump1090>\n"
+"\n"
+ );
+
+ // delay for 1 second to give the user a chance to read the copyright
+ while (llTime >= time(NULL)) {}
+}
+#endif
+
+
+static void display_stats(void) {
+ int j;
+ time_t now = time(NULL);
+
+ printf("\n\n");
+ if (Modes.interactive)
+ interactiveShowData();
+
+ printf("Statistics as at %s", ctime(&now));
+
+ printf("%d sample blocks processed\n", Modes.stat_blocks_processed);
+ printf("%d sample blocks dropped\n", Modes.stat_blocks_dropped);
+
+ printf("%d ModeA/C detected\n", Modes.stat_ModeAC);
+ printf("%d valid Mode-S preambles\n", Modes.stat_valid_preamble);
+ printf("%d DF-?? fields corrected for length\n", Modes.stat_DF_Len_Corrected);
+ printf("%d DF-?? fields corrected for type\n", Modes.stat_DF_Type_Corrected);
+ printf("%d demodulated with 0 errors\n", Modes.stat_demodulated0);
+ printf("%d demodulated with 1 error\n", Modes.stat_demodulated1);
+ printf("%d demodulated with 2 errors\n", Modes.stat_demodulated2);
+ printf("%d demodulated with > 2 errors\n", Modes.stat_demodulated3);
+ printf("%d with good crc\n", Modes.stat_goodcrc);
+ printf("%d with bad crc\n", Modes.stat_badcrc);
+ printf("%d errors corrected\n", Modes.stat_fixed);
+
+ for (j = 0; j < MODES_MAX_BITERRORS; j++) {
+ printf(" %d with %d bit %s\n", Modes.stat_bit_fix[j], j+1, (j==0)?"error":"errors");
+ }
+
+ if (Modes.phase_enhance) {
+ printf("%d phase enhancement attempts\n", Modes.stat_out_of_phase);
+ printf("%d phase enhanced demodulated with 0 errors\n", Modes.stat_ph_demodulated0);
+ printf("%d phase enhanced demodulated with 1 error\n", Modes.stat_ph_demodulated1);
+ printf("%d phase enhanced demodulated with 2 errors\n", Modes.stat_ph_demodulated2);
+ printf("%d phase enhanced demodulated with > 2 errors\n", Modes.stat_ph_demodulated3);
+ printf("%d phase enhanced with good crc\n", Modes.stat_ph_goodcrc);
+ printf("%d phase enhanced with bad crc\n", Modes.stat_ph_badcrc);
+ printf("%d phase enhanced errors corrected\n", Modes.stat_ph_fixed);
+
+ for (j = 0; j < MODES_MAX_BITERRORS; j++) {
+ printf(" %d with %d bit %s\n", Modes.stat_ph_bit_fix[j], j+1, (j==0)?"error":"errors");
+ }
+ }
+
+ printf("%d total usable messages\n", Modes.stat_goodcrc + Modes.stat_ph_goodcrc + Modes.stat_fixed + Modes.stat_ph_fixed);
+ fflush(stdout);
+
+ Modes.stat_blocks_processed =
+ Modes.stat_blocks_dropped = 0;
+
+ Modes.stat_ModeAC =
+ Modes.stat_valid_preamble =
+ Modes.stat_DF_Len_Corrected =
+ Modes.stat_DF_Type_Corrected =
+ Modes.stat_demodulated0 =
+ Modes.stat_demodulated1 =
+ Modes.stat_demodulated2 =
+ Modes.stat_demodulated3 =
+ Modes.stat_goodcrc =
+ Modes.stat_badcrc =
+ Modes.stat_fixed = 0;
+
+ Modes.stat_out_of_phase =
+ Modes.stat_ph_demodulated0 =
+ Modes.stat_ph_demodulated1 =
+ Modes.stat_ph_demodulated2 =
+ Modes.stat_ph_demodulated3 =
+ Modes.stat_ph_goodcrc =
+ Modes.stat_ph_badcrc =
+ Modes.stat_ph_fixed = 0;
+
+ for (j = 0; j < MODES_MAX_BITERRORS; j++) {
+ Modes.stat_ph_bit_fix[j] = 0;
+ Modes.stat_bit_fix[j] = 0;
+ }
+}
+
+
+//
+//=========================================================================
+//
+// This function is called a few times every second by main in order to
+// perform tasks we need to do continuously, like accepting new clients
+// from the net, refreshing the screen in interactive mode, and so forth
+//
+void backgroundTasks(void) {
+ static time_t next_stats;
+
+ if (Modes.net) {
+ modesReadFromClients();
+ }
+
+ // If Modes.aircrafts is not NULL, remove any stale aircraft
+ if (Modes.aircrafts) {
+ interactiveRemoveStaleAircrafts();
+ }
+
+ // Refresh screen when in interactive mode
+ if (Modes.interactive) {
+ interactiveShowData();
+ }
+
+ if (Modes.stats > 0) {
+ time_t now = time(NULL);
+ if (now > next_stats) {
+ if (next_stats != 0)
+ display_stats();
+ next_stats = now + Modes.stats;
+ }
+ }
+}
+//
+//=========================================================================
+//
+int verbose_device_search(char *s)
+{
+ int i, device_count, device, offset;
+ char *s2;
+ char vendor[256], product[256], serial[256];
+ device_count = rtlsdr_get_device_count();
+ if (!device_count) {
+ fprintf(stderr, "No supported devices found.\n");
+ return -1;
+ }
+ fprintf(stderr, "Found %d device(s):\n", device_count);
+ for (i = 0; i < device_count; i++) {
+ rtlsdr_get_device_usb_strings(i, vendor, product, serial);
+ fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial);
+ }
+ fprintf(stderr, "\n");
+ /* does string look like raw id number */
+ device = (int)strtol(s, &s2, 0);
+ if (s2[0] == '\0' && device >= 0 && device < device_count) {
+ fprintf(stderr, "Using device %d: %s\n",
+ device, rtlsdr_get_device_name((uint32_t)device));
+ return device;
+ }
+ /* does string exact match a serial */
+ for (i = 0; i < device_count; i++) {
+ rtlsdr_get_device_usb_strings(i, vendor, product, serial);
+ if (strcmp(s, serial) != 0) {
+ continue;}
+ device = i;
+ fprintf(stderr, "Using device %d: %s\n",
+ device, rtlsdr_get_device_name((uint32_t)device));
+ return device;
+ }
+ /* does string prefix match a serial */
+ for (i = 0; i < device_count; i++) {
+ rtlsdr_get_device_usb_strings(i, vendor, product, serial);
+ if (strncmp(s, serial, strlen(s)) != 0) {
+ continue;}
+ device = i;
+ fprintf(stderr, "Using device %d: %s\n",
+ device, rtlsdr_get_device_name((uint32_t)device));
+ return device;
+ }
+ /* does string suffix match a serial */
+ for (i = 0; i < device_count; i++) {
+ rtlsdr_get_device_usb_strings(i, vendor, product, serial);
+ offset = strlen(serial) - strlen(s);
+ if (offset < 0) {
+ continue;}
+ if (strncmp(s, serial+offset, strlen(s)) != 0) {
+ continue;}
+ device = i;
+ fprintf(stderr, "Using device %d: %s\n",
+ device, rtlsdr_get_device_name((uint32_t)device));
+ return device;
+ }
+ fprintf(stderr, "No matching devices found.\n");
+ return -1;
+}
+//
+//=========================================================================
+//
+int main(int argc, char **argv) {
+ int j;
+
+ // Set sane defaults
+ modesInitConfig();
+ 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],"--device-index") && more) {
+ Modes.dev_index = verbose_device_search(argv[++j]);
+ } else if (!strcmp(argv[j],"--gain") && more) {
+ Modes.gain = (int) (atof(argv[++j])*10); // Gain is in tens of DBs
+ } else if (!strcmp(argv[j],"--enable-agc")) {
+ Modes.enable_agc++;
+ } else if (!strcmp(argv[j],"--freq") && more) {
+ Modes.freq = (int) strtoll(argv[++j],NULL,10);
+ } else if (!strcmp(argv[j],"--ifile") && more) {
+ Modes.filename = strdup(argv[++j]);
+ } 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],"--no-crc-check")) {
+ Modes.check_crc = 0;
+ } else if (!strcmp(argv[j],"--phase-enhance")) {
+ Modes.phase_enhance = 1;
+ } else if (!strcmp(argv[j],"--raw")) {
+ Modes.raw = 1;
+ } else if (!strcmp(argv[j],"--net")) {
+ Modes.net = 1;
+ } else if (!strcmp(argv[j],"--modeac")) {
+ Modes.mode_ac = 1;
+ } else if (!strcmp(argv[j],"--net-beast")) {
+ Modes.beast = 1;
+ } else if (!strcmp(argv[j],"--net-only")) {
+ Modes.net = 1;
+ Modes.net_only = 1;
+ } else if (!strcmp(argv[j],"--net-heartbeat") && more) {
+ Modes.net_heartbeat_rate = atoi(argv[++j]) * 15;
+ } else if (!strcmp(argv[j],"--net-ro-size") && more) {
+ Modes.net_output_raw_size = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--net-ro-rate") && more) {
+ Modes.net_output_raw_rate = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--net-ro-port") && more) {
+ if (Modes.beast) // Required for legacy backward compatibility
+ {Modes.net_output_beast_port = atoi(argv[++j]);;}
+ else
+ {Modes.net_output_raw_port = atoi(argv[++j]);}
+ } else if (!strcmp(argv[j],"--net-ri-port") && more) {
+ Modes.net_input_raw_port = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--net-bo-port") && more) {
+ Modes.net_output_beast_port = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--net-bi-port") && more) {
+ Modes.net_input_beast_port = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--net-http-port") && more) {
+ Modes.net_http_port = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--net-sbs-port") && more) {
+ Modes.net_output_sbs_port = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--net-buffer") && more) {
+ Modes.net_sndbuf_size = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--onlyaddr")) {
+ Modes.onlyaddr = 1;
+ } else if (!strcmp(argv[j],"--metric")) {
+ Modes.metric = 1;
+ } else if (!strcmp(argv[j],"--aggressive")) {
+ Modes.nfix_crc = MODES_MAX_BITERRORS;
+ } else if (!strcmp(argv[j],"--interactive")) {
+ Modes.interactive = 1;
+ } else if (!strcmp(argv[j],"--interactive-rows") && more) {
+ Modes.interactive_rows = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--interactive-ttl") && more) {
+ Modes.interactive_display_ttl = atoi(argv[++j]);
+ } 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],"--debug") && more) {
+ char *f = argv[++j];
+ while(*f) {
+ switch(*f) {
+ case 'D': Modes.debug |= MODES_DEBUG_DEMOD; break;
+ case 'd': Modes.debug |= MODES_DEBUG_DEMODERR; break;
+ case 'C': Modes.debug |= MODES_DEBUG_GOODCRC; break;
+ case 'c': Modes.debug |= MODES_DEBUG_BADCRC; break;
+ case 'p': Modes.debug |= MODES_DEBUG_NOPREAMBLE; break;
+ case 'n': Modes.debug |= MODES_DEBUG_NET; break;
+ case 'j': Modes.debug |= MODES_DEBUG_JS; break;
+ default:
+ fprintf(stderr, "Unknown debugging flag: %c\n", *f);
+ exit(1);
+ break;
+ }
+ f++;
+ }
+ } else if (!strcmp(argv[j],"--stats")) {
+ Modes.stats = -1;
+ } else if (!strcmp(argv[j],"--stats-every") && more) {
+ Modes.stats = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--snip") && more) {
+ snipMode(atoi(argv[++j]));
+ exit(0);
+ } else if (!strcmp(argv[j],"--help")) {
+ showHelp();
+ exit(0);
+ } else if (!strcmp(argv[j],"--ppm") && more) {
+ Modes.ppm_error = atoi(argv[++j]);
+ } else if (!strcmp(argv[j],"--quiet")) {
+ Modes.quiet = 1;
+ } else if (!strcmp(argv[j],"--mlat")) {
+ Modes.mlat = 1;
+ } else if (!strcmp(argv[j],"--interactive-rtl1090")) {
+ Modes.interactive = 1;
+ Modes.interactive_rtl1090 = 1;
+ } else {
+ fprintf(stderr,
+ "Unknown or not enough arguments for option '%s'.\n\n",
+ argv[j]);
+ showHelp();
+ exit(1);
+ }
+ }
+
+#ifdef _WIN32
+ // Try to comply with the Copyright license conditions for binary distribution
+ if (!Modes.quiet) {showCopyright();}
+#endif
+
+#ifndef _WIN32
+ // Setup for SIGWINCH for handling lines
+ if (Modes.interactive) {signal(SIGWINCH, sigWinchCallback);}
+#endif
+
+ // Initialization
+ modesInit();
+
+ if (Modes.net_only) {
+ fprintf(stderr,"Net-only mode, no RTL device or file open.\n");
+ } else if (Modes.filename == NULL) {
+ modesInitRTLSDR();
+ } else {
+ if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') {
+ Modes.fd = STDIN_FILENO;
+ } else if ((Modes.fd = open(Modes.filename,
+#ifdef _WIN32
+ (O_RDONLY | O_BINARY)
+#else
+ (O_RDONLY)
+#endif
+ )) == -1) {
+ perror("Opening data file");
+ exit(1);
+ }
+ }
+ if (Modes.net) modesInitNet();
+
+ // If the user specifies --net-only, just run in order to serve network
+ // clients without reading data from the RTL device
+ while (Modes.net_only) {
+ if (Modes.exit) exit(0); // If we exit net_only nothing further in main()
+ backgroundTasks();
+ usleep(100000);
+ }
+
+ // Create the thread that will read the data from the device.
+ pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL);
+ pthread_mutex_lock(&Modes.data_mutex);
+
+ while (Modes.exit == 0) {
+
+ if (Modes.iDataReady == 0) {
+ pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex); // This unlocks Modes.data_mutex, and waits for Modes.data_cond
+ continue; // Once (Modes.data_cond) occurs, it locks Modes.data_mutex
+ }
+
+ // Modes.data_mutex is Locked, and (Modes.iDataReady != 0)
+ if (Modes.iDataReady) { // Check we have new data, just in case!!
+
+ Modes.iDataOut &= (MODES_ASYNC_BUF_NUMBER-1); // Just incase
+
+ // Translate the next lot of I/Q samples into Modes.magnitude
+ computeMagnitudeVector(Modes.pData[Modes.iDataOut]);
+
+ Modes.stSystemTimeBlk = Modes.stSystemTimeRTL[Modes.iDataOut];
+
+ // Update the input buffer pointer queue
+ Modes.iDataOut = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataOut + 1);
+ Modes.iDataReady = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn - Modes.iDataOut);
+
+ // If we lost some blocks, correct the timestamp
+ if (Modes.iDataLost) {
+ Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES * 6 * Modes.iDataLost);
+ Modes.stat_blocks_dropped += Modes.iDataLost;
+ Modes.iDataLost = 0;
+ }
+
+ // It's safe to release the lock now
+ pthread_cond_signal (&Modes.data_cond);
+ pthread_mutex_unlock(&Modes.data_mutex);
+
+ // Process data after releasing the lock, so that the capturing
+ // thread can read data while we perform computationally expensive
+ // stuff at the same time.
+ detectModeS(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES);
+
+ // Update the timestamp ready for the next block
+ Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*6);
+ Modes.stat_blocks_processed++;
+ } else {
+ pthread_cond_signal (&Modes.data_cond);
+ pthread_mutex_unlock(&Modes.data_mutex);
+ }
+
+ backgroundTasks();
+ pthread_mutex_lock(&Modes.data_mutex);
+ }
+
+ // If --stats were given, print statistics
+ if (Modes.stats) {
+ display_stats();
+ }
+
+ if (Modes.filename == NULL) {
+ rtlsdr_cancel_async(Modes.dev); // Cancel rtlsdr_read_async will cause data input thread to terminate cleanly
+ rtlsdr_close(Modes.dev);
+ }
+ pthread_cond_destroy(&Modes.data_cond); // Thread cleanup
+ pthread_mutex_destroy(&Modes.data_mutex);
+ pthread_join(Modes.reader_thread,NULL); // Wait on reader thread exit
+#ifndef _WIN32
+ pthread_exit(0);
+#else
+ return (0);
+#endif
+}
+//
+//=========================================================================
+//
diff --git a/dump1090.dsp b/dump1090.dsp
new file mode 100644
index 0000000..d64d0d1
--- /dev/null
+++ b/dump1090.dsp
@@ -0,0 +1,148 @@
+# Microsoft Developer Studio Project File - Name="dump1090" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=dump1090 - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "dump1090.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "dump1090.mak" CFG="dump1090 - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "dump1090 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "dump1090 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "dump1090 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 ws2_32.lib /nologo /subsystem:console /machine:I386 /out:"./dump1090.exe"
+
+!ELSEIF "$(CFG)" == "dump1090 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "dump1090 - Win32 Release"
+# Name "dump1090 - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\anet.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\dump1090.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\interactive.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mode_ac.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mode_s.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\net_io.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\anet.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dump1090.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\winstubs.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Group "Library Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\pthreads\pthreadVC2.lib
+# End Source File
+# Begin Source File
+
+SOURCE=.\rtlsdr\rtlsdr.lib
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/dump1090.dsw b/dump1090.dsw
new file mode 100644
index 0000000..c29f0f1
--- /dev/null
+++ b/dump1090.dsw
@@ -0,0 +1,41 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "dump1090"=.\dump1090.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "view1090"=.\view1090.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/dump1090.h b/dump1090.h
new file mode 100644
index 0000000..b7adae3
--- /dev/null
+++ b/dump1090.h
@@ -0,0 +1,465 @@
+// dump1090, a Mode S messages decoder for RTLSDR devices.
+//
+// Copyright (C) 2012 by Salvatore Sanfilippo <antirez at gmail.com>
+//
+// 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 __DUMP1090_H
+#define __DUMP1090_H
+
+// File Version number
+// ====================
+// Format is : MajorVer.MinorVer.DayMonth.Year"
+// MajorVer changes only with significant changes
+// MinorVer changes when additional features are added, but not for bug fixes (range 00-99)
+// DayDate & Year changes for all changes, including for bug fixes. It represent the release date of the update
+//
+#define MODES_DUMP1090_VERSION "1.09.0608.14"
+
+// ============================= Include files ==========================
+
+#ifndef _WIN32
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <pthread.h>
+ #include <stdint.h>
+ #include <errno.h>
+ #include <unistd.h>
+ #include <math.h>
+ #include <sys/time.h>
+ #include <sys/timeb.h>
+ #include <signal.h>
+ #include <fcntl.h>
+ #include <ctype.h>
+ #include <sys/stat.h>
+ #include <sys/ioctl.h>
+ #include "rtl-sdr.h"
+ #include "anet.h"
+#else
+ #include "winstubs.h" //Put everything Windows specific in here
+ #include "rtl-sdr.h"
+ #include "anet.h"
+#endif
+
+// ============================= #defines ===============================
+//
+// If you have a valid coaa.h, these values will come from it. If not,
+// then you can enter your own values in the #else section here
+//
+#ifdef USER_LATITUDE
+ #define MODES_USER_LATITUDE_DFLT (USER_LATITUDE)
+ #define MODES_USER_LONGITUDE_DFLT (USER_LONGITUDE)
+#else
+ #define MODES_USER_LATITUDE_DFLT (0.0)
+ #define MODES_USER_LONGITUDE_DFLT (0.0)
+#endif
+
+#define MODES_DEFAULT_PPM 52
+#define MODES_DEFAULT_RATE 2000000
+#define MODES_DEFAULT_FREQ 1090000000
+#define MODES_DEFAULT_WIDTH 1000
+#define MODES_DEFAULT_HEIGHT 700
+#define MODES_ASYNC_BUF_NUMBER 16
+#define MODES_ASYNC_BUF_SIZE (16*16384) // 256k
+#define MODES_ASYNC_BUF_SAMPLES (MODES_ASYNC_BUF_SIZE / 2) // Each sample is 2 bytes
+#define MODES_AUTO_GAIN -100 // Use automatic gain
+#define MODES_MAX_GAIN 999999 // Use max available gain
+#define MODES_MSG_SQUELCH_LEVEL 0x02FF // Average signal strength limit
+#define MODES_MSG_ENCODER_ERRS 3 // Maximum number of encoding errors
+
+// When changing, change also fixBitErrors() and modesInitErrorTable() !!
+#define MODES_MAX_BITERRORS 2 // Global max for fixable bit erros
+
+#define MODEAC_MSG_SAMPLES (25 * 2) // include up to the SPI bit
+#define MODEAC_MSG_BYTES 2
+#define MODEAC_MSG_SQUELCH_LEVEL 0x07FF // Average signal strength limit
+#define MODEAC_MSG_FLAG (1<<0)
+#define MODEAC_MSG_MODES_HIT (1<<1)
+#define MODEAC_MSG_MODEA_HIT (1<<2)
+#define MODEAC_MSG_MODEC_HIT (1<<3)
+#define MODEAC_MSG_MODEA_ONLY (1<<4)
+#define MODEAC_MSG_MODEC_OLD (1<<5)
+
+#define MODES_PREAMBLE_US 8 // microseconds = bits
+#define MODES_PREAMBLE_SAMPLES (MODES_PREAMBLE_US * 2)
+#define MODES_PREAMBLE_SIZE (MODES_PREAMBLE_SAMPLES * sizeof(uint16_t))
+#define MODES_LONG_MSG_BYTES 14
+#define MODES_SHORT_MSG_BYTES 7
+#define MODES_LONG_MSG_BITS (MODES_LONG_MSG_BYTES * 8)
+#define MODES_SHORT_MSG_BITS (MODES_SHORT_MSG_BYTES * 8)
+#define MODES_LONG_MSG_SAMPLES (MODES_LONG_MSG_BITS * 2)
+#define MODES_SHORT_MSG_SAMPLES (MODES_SHORT_MSG_BITS * 2)
+#define MODES_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t))
+#define MODES_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t))
+
+#define MODES_RAWOUT_BUF_SIZE (1500)
+#define MODES_RAWOUT_BUF_FLUSH (MODES_RAWOUT_BUF_SIZE - 200)
+#define MODES_RAWOUT_BUF_RATE (1000) // 1000 * 64mS = 1 Min approx
+
+#define MODES_ICAO_CACHE_LEN 1024 // Power of two required
+#define MODES_ICAO_CACHE_TTL 60 // Time to live of cached addresses
+#define MODES_UNIT_FEET 0
+#define MODES_UNIT_METERS 1
+
+#define MODES_USER_LATLON_VALID (1<<0)
+
+#define MODES_ACFLAGS_LATLON_VALID (1<<0) // Aircraft Lat/Lon is decoded
+#define MODES_ACFLAGS_ALTITUDE_VALID (1<<1) // Aircraft altitude is known
+#define MODES_ACFLAGS_HEADING_VALID (1<<2) // Aircraft heading is known
+#define MODES_ACFLAGS_SPEED_VALID (1<<3) // Aircraft speed is known
+#define MODES_ACFLAGS_VERTRATE_VALID (1<<4) // Aircraft vertical rate is known
+#define MODES_ACFLAGS_SQUAWK_VALID (1<<5) // Aircraft Mode A Squawk is known
+#define MODES_ACFLAGS_CALLSIGN_VALID (1<<6) // Aircraft Callsign Identity
+#define MODES_ACFLAGS_EWSPEED_VALID (1<<7) // Aircraft East West Speed is known
+#define MODES_ACFLAGS_NSSPEED_VALID (1<<8) // Aircraft North South Speed is known
+#define MODES_ACFLAGS_AOG (1<<9) // Aircraft is On the Ground
+#define MODES_ACFLAGS_LLEVEN_VALID (1<<10) // Aircraft Even Lot/Lon is known
+#define MODES_ACFLAGS_LLODD_VALID (1<<11) // Aircraft Odd Lot/Lon is known
+#define MODES_ACFLAGS_AOG_VALID (1<<12) // MODES_ACFLAGS_AOG is valid
+#define MODES_ACFLAGS_FS_VALID (1<<13) // Aircraft Flight Status is known
+#define MODES_ACFLAGS_NSEWSPD_VALID (1<<14) // Aircraft EW and NS Speed is known
+#define MODES_ACFLAGS_LATLON_REL_OK (1<<15) // Indicates it's OK to do a relative CPR
+
+#define MODES_ACFLAGS_LLEITHER_VALID (MODES_ACFLAGS_LLEVEN_VALID | MODES_ACFLAGS_LLODD_VALID)
+#define MODES_ACFLAGS_LLBOTH_VALID (MODES_ACFLAGS_LLEVEN_VALID | MODES_ACFLAGS_LLODD_VALID)
+#define MODES_ACFLAGS_AOG_GROUND (MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG)
+
+#define MODES_DEBUG_DEMOD (1<<0)
+#define MODES_DEBUG_DEMODERR (1<<1)
+#define MODES_DEBUG_BADCRC (1<<2)
+#define MODES_DEBUG_GOODCRC (1<<3)
+#define MODES_DEBUG_NOPREAMBLE (1<<4)
+#define MODES_DEBUG_NET (1<<5)
+#define MODES_DEBUG_JS (1<<6)
+
+// When debug is set to MODES_DEBUG_NOPREAMBLE, the first sample must be
+// at least greater than a given level for us to dump the signal.
+#define MODES_DEBUG_NOPREAMBLE_LEVEL 25
+
+#define MODES_INTERACTIVE_REFRESH_TIME 250 // Milliseconds
+#define MODES_INTERACTIVE_ROWS 22 // Rows on screen
+#define MODES_INTERACTIVE_DELETE_TTL 300 // Delete from the list after 300 seconds
+#define MODES_INTERACTIVE_DISPLAY_TTL 60 // Delete from display after 60 seconds
+
+#define MODES_NET_HEARTBEAT_RATE 900 // Each block is approx 65mS - default is > 1 min
+
+#define MODES_NET_SERVICES_NUM 6
+#define MODES_NET_INPUT_RAW_PORT 30001
+#define MODES_NET_OUTPUT_RAW_PORT 30002
+#define MODES_NET_OUTPUT_SBS_PORT 30003
+#define MODES_NET_INPUT_BEAST_PORT 30004
+#define MODES_NET_OUTPUT_BEAST_PORT 30005
+#define MODES_NET_HTTP_PORT 8080
+#define MODES_CLIENT_BUF_SIZE 1024
+#define MODES_NET_SNDBUF_SIZE (1024*64)
+#define MODES_NET_SNDBUF_MAX (7)
+
+#ifndef HTMLPATH
+#define HTMLPATH "./public_html" // default path for gmap.html etc
+#endif
+
+#define MODES_NOTUSED(V) ((void) V)
+
+//======================== structure declarations =========================
+
+// Structure used to describe a networking client
+struct client {
+ struct client* next; // Pointer to next client
+ int fd; // File descriptor
+ int service; // TCP port the client is connected to
+ int buflen; // Amount of data on buffer
+ char buf[MODES_CLIENT_BUF_SIZE+1]; // Read buffer
+};
+
+// Structure used to describe an aircraft in iteractive mode
+struct aircraft {
+ uint32_t addr; // ICAO address
+ char flight[16]; // Flight number
+ unsigned char signalLevel[8]; // Last 8 Signal Amplitudes
+ int altitude; // Altitude
+ int speed; // Velocity
+ int track; // Angle of flight
+ int vert_rate; // Vertical rate.
+ time_t seen; // Time at which the last packet was received
+ time_t seenLatLon; // Time at which the last lat long was calculated
+ uint64_t timestamp; // Timestamp at which the last packet was received
+ uint64_t timestampLatLon;// Timestamp at which the last lat long was calculated
+ long messages; // Number of Mode S messages received
+ int modeA; // Squawk
+ int modeC; // Altitude
+ long modeAcount; // Mode A Squawk hit Count
+ long modeCcount; // Mode C Altitude hit Count
+ int modeACflags; // Flags for mode A/C recognition
+
+ // Encoded latitude and longitude as extracted by odd and even CPR encoded messages
+ int odd_cprlat;
+ int odd_cprlon;
+ int even_cprlat;
+ int even_cprlon;
+ uint64_t odd_cprtime;
+ uint64_t even_cprtime;
+ double lat, lon; // Coordinated obtained from CPR encoded data
+ int bFlags; // Flags related to valid fields in this structure
+ struct aircraft *next; // Next aircraft in our linked list
+};
+
+struct stDF {
+ struct stDF *pNext; // Pointer to next item in the linked list
+ struct stDF *pPrev; // Pointer to previous item in the linked list
+ struct aircraft *pAircraft; // Pointer to the Aircraft structure for this DF
+ time_t seen; // Dos/UNIX Time at which the this packet was received
+ uint64_t llTimestamp; // Timestamp at which the this packet was received
+ uint32_t addr; // Timestamp at which the this packet was received
+ unsigned char msg[MODES_LONG_MSG_BYTES]; // the binary
+} tDF;
+
+// Program global state
+struct { // Internal state
+ pthread_t reader_thread;
+
+ pthread_mutex_t data_mutex; // Mutex to synchronize buffer access
+ pthread_cond_t data_cond; // Conditional variable associated
+ uint16_t *pData [MODES_ASYNC_BUF_NUMBER]; // Raw IQ sample buffers from RTL
+ struct timeb stSystemTimeRTL[MODES_ASYNC_BUF_NUMBER]; // System time when RTL passed us this block
+ int iDataIn; // Fifo input pointer
+ int iDataOut; // Fifo output pointer
+ int iDataReady; // Fifo content count
+ int iDataLost; // Count of missed buffers
+
+ uint16_t *pFileData; // Raw IQ samples buffer (from a File)
+ uint16_t *magnitude; // Magnitude vector
+ uint64_t timestampBlk; // Timestamp of the start of the current block
+ struct timeb stSystemTimeBlk; // System time when RTL passed us currently processing this block
+ int fd; // --ifile option file descriptor
+ uint32_t *icao_cache; // Recently seen ICAO addresses cache
+ uint16_t *maglut; // I/Q -> Magnitude lookup table
+ int exit; // Exit from the main loop when true
+
+ // RTLSDR
+ int dev_index;
+ int gain;
+ int enable_agc;
+ rtlsdr_dev_t *dev;
+ int freq;
+ int ppm_error;
+
+ // Networking
+ char aneterr[ANET_ERR_LEN];
+ struct client *clients; // Our clients
+ int sbsos; // SBS output listening socket
+ int ros; // Raw output listening socket
+ int ris; // Raw input listening socket
+ int bos; // Beast output listening socket
+ int bis; // Beast input listening socket
+ int https; // HTTP listening socket
+ char *rawOut; // Buffer for building raw output data
+ int rawOutUsed; // How much of the buffer is currently used
+ char *beastOut; // Buffer for building beast output data
+ int beastOutUsed; // How much if the buffer is currently used
+#ifdef _WIN32
+ WSADATA wsaData; // Windows socket initialisation
+#endif
+
+ // Configuration
+ char *filename; // Input form file, --ifile option
+ int phase_enhance; // Enable phase enhancement if true
+ int nfix_crc; // Number of crc bit error(s) to correct
+ int check_crc; // Only display messages with good CRC
+ int raw; // Raw output format
+ int beast; // Beast binary format output
+ int mode_ac; // Enable decoding of SSR Modes A & C
+ int debug; // Debugging mode
+ int net; // Enable networking
+ int net_only; // Enable just networking
+ int net_heartbeat_count; // TCP heartbeat counter
+ int net_heartbeat_rate; // TCP heartbeat rate
+ int net_output_sbs_port; // SBS output TCP port
+ int net_output_raw_size; // Minimum Size of the output raw data
+ int net_output_raw_rate; // Rate (in 64mS increments) of output raw data
+ int net_output_raw_rate_count; // Rate (in 64mS increments) of output raw data
+ int net_output_raw_port; // Raw output TCP port
+ int net_input_raw_port; // Raw input TCP port
+ int net_output_beast_port; // Beast output TCP port
+ int net_input_beast_port; // Beast input TCP port
+ int net_http_port; // HTTP port
+ int net_sndbuf_size; // TCP output buffer size (64Kb * 2^n)
+ int quiet; // Suppress stdout
+ int interactive; // Interactive mode
+ int interactive_rows; // Interactive mode: max number of rows
+ int interactive_display_ttl; // Interactive mode: TTL display
+ int interactive_delete_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
+ int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...;
+ int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090
+
+ // User details
+ double fUserLat; // Users receiver/antenna lat/lon needed for initial surface location
+ double fUserLon; // Users receiver/antenna lat/lon needed for initial surface location
+ int bUserFlags; // Flags relating to the user details
+
+ // Interactive mode
+ struct aircraft *aircrafts;
+ uint64_t interactive_last_update; // Last screen update in milliseconds
+ time_t last_cleanup_time; // Last cleanup time in seconds
+
+ // DF List mode
+ int bEnableDFLogging; // Set to enable DF Logging
+ pthread_mutex_t pDF_mutex; // Mutex to synchronize pDF access
+ struct stDF *pDF; // Pointer to DF list
+
+ // Statistics
+ unsigned int stat_valid_preamble;
+ unsigned int stat_demodulated0;
+ unsigned int stat_demodulated1;
+ unsigned int stat_demodulated2;
+ unsigned int stat_demodulated3;
+ unsigned int stat_goodcrc;
+ unsigned int stat_badcrc;
+ unsigned int stat_fixed;
+
+ // Histogram of fixed bit errors: index 0 for single bit erros,
+ // index 1 for double bit errors etc.
+ unsigned int stat_bit_fix[MODES_MAX_BITERRORS];
+
+ unsigned int stat_http_requests;
+ unsigned int stat_sbs_connections;
+ unsigned int stat_raw_connections;
+ unsigned int stat_beast_connections;
+ unsigned int stat_out_of_phase;
+ unsigned int stat_ph_demodulated0;
+ unsigned int stat_ph_demodulated1;
+ unsigned int stat_ph_demodulated2;
+ unsigned int stat_ph_demodulated3;
+ unsigned int stat_ph_goodcrc;
+ unsigned int stat_ph_badcrc;
+ unsigned int stat_ph_fixed;
+ // Histogram of fixed bit errors: index 0 for single bit erros,
+ // index 1 for double bit errors etc.
+ unsigned int stat_ph_bit_fix[MODES_MAX_BITERRORS];
+
+ unsigned int stat_DF_Len_Corrected;
+ unsigned int stat_DF_Type_Corrected;
+ unsigned int stat_ModeAC;
+
+ unsigned int stat_blocks_processed;
+ unsigned int stat_blocks_dropped;
+} Modes;
+
+// The struct we use to store information about a decoded message.
+struct modesMessage {
+ // Generic fields
+ unsigned char msg[MODES_LONG_MSG_BYTES]; // Binary message.
+ int msgbits; // Number of bits in message
+ int msgtype; // Downlink format #
+ int crcok; // True if CRC was valid
+ uint32_t crc; // Message CRC
+ int correctedbits; // No. of bits corrected
+ char corrected[MODES_MAX_BITERRORS]; // corrected bit positions
+ uint32_t addr; // ICAO Address from bytes 1 2 and 3
+ int phase_corrected; // True if phase correction was applied
+ uint64_t timestampMsg; // Timestamp of the message
+ int remote; // If set this message is from a remote station
+ unsigned char signalLevel; // Signal Amplitude
+
+ // DF 11
+ int ca; // Responder capabilities
+ int iid;
+
+ // DF 17, DF 18
+ int metype; // Extended squitter message type.
+ int mesub; // Extended squitter message subtype.
+ int heading; // Reported by aircraft, or computed from from EW and NS velocity
+ int raw_latitude; // Non decoded latitude.
+ int raw_longitude; // Non decoded longitude.
+ double fLat; // Coordinates obtained from CPR encoded data if/when decoded
+ double fLon; // Coordinates obtained from CPR encoded data if/when decoded
+ char flight[16]; // 8 chars flight number.
+ int ew_velocity; // E/W velocity.
+ int ns_velocity; // N/S velocity.
+ int vert_rate; // Vertical rate.
+ int velocity; // Reported by aircraft, or computed from from EW and NS velocity
+
+ // DF4, DF5, DF20, DF21
+ int fs; // Flight status for DF4,5,20,21
+ int modeA; // 13 bits identity (Squawk).
+
+ // Fields used by multiple message types.
+ int altitude;
+ int unit;
+ int bFlags; // Flags related to fields in this structure
+};
+
+// ======================== function declarations =========================
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Functions exported from mode_ac.c
+//
+int detectModeA (uint16_t *m, struct modesMessage *mm);
+void decodeModeAMessage(struct modesMessage *mm, int ModeA);
+int ModeAToModeC (unsigned int ModeA);
+
+//
+// Functions exported from mode_s.c
+//
+void detectModeS (uint16_t *m, uint32_t mlen);
+void decodeModesMessage (struct modesMessage *mm, unsigned char *msg);
+void displayModesMessage(struct modesMessage *mm);
+void useModesMessage (struct modesMessage *mm);
+void computeMagnitudeVector(uint16_t *pData);
+int decodeCPR (struct aircraft *a, int fflag, int surface);
+int decodeCPRrelative (struct aircraft *a, int fflag, int surface);
+void modesInitErrorInfo ();
+//
+// Functions exported from interactive.c
+//
+struct aircraft* interactiveReceiveData(struct modesMessage *mm);
+void interactiveShowData(void);
+void interactiveRemoveStaleAircrafts(void);
+int decodeBinMessage (struct client *c, char *p);
+struct aircraft *interactiveFindAircraft(uint32_t addr);
+struct stDF *interactiveFindDF (uint32_t addr);
+
+//
+// Functions exported from net_io.c
+//
+void modesInitNet (void);
+void modesReadFromClients (void);
+void modesSendAllClients (int service, void *msg, int len);
+void modesQueueOutput (struct modesMessage *mm);
+void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __DUMP1090_H
diff --git a/dump1090.sh b/dump1090.sh
new file mode 100644
index 0000000..d1b63e5
--- /dev/null
+++ b/dump1090.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+### BEGIN INIT INFO
+#
+# Provides: dump1090
+# Required-Start: $remote_fs
+# Required-Stop: $remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: dump1090 initscript
+
+#
+### END INIT INFO
+## Fill in name of program here.
+PROG="dump1090"
+PROG_PATH="/home/pi/dump1090"
+PROG_ARGS="--quiet --net --net-ro-size 500 --net-ro-rate 5 --net-buffer 5"
+PIDFILE="/var/run/dump1090.pid"
+
+start() {
+ if [ -e $PIDFILE ]; then
+ ## Program is running, exit with error.
+ echo "Error! $PROG is currently running!" 1>&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/interactive.c b/interactive.c
new file mode 100644
index 0000000..4f7e029
--- /dev/null
+++ b/interactive.c
@@ -0,0 +1,561 @@
+// dump1090, a Mode S messages decoder for RTLSDR devices.
+//
+// Copyright (C) 2012 by Salvatore Sanfilippo <antirez at gmail.com>
+//
+// 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 "dump1090.h"
+//
+// ============================= Utility functions ==========================
+//
+static uint64_t mstime(void) {
+ struct timeval tv;
+ uint64_t mst;
+
+ gettimeofday(&tv, NULL);
+ mst = ((uint64_t)tv.tv_sec)*1000;
+ mst += tv.tv_usec/1000;
+ return mst;
+}
+//
+//=========================================================================
+//
+// Add a new DF structure to the interactive mode linked list
+//
+void interactiveCreateDF(struct aircraft *a, struct modesMessage *mm) {
+ struct stDF *pDF = (struct stDF *) malloc(sizeof(*pDF));
+
+ if (pDF) {
+ // Default everything to zero/NULL
+ memset(pDF, 0, sizeof(*pDF));
+
+ // Now initialise things
+ pDF->seen = a->seen;
+ pDF->llTimestamp = mm->timestampMsg;
+ pDF->addr = mm->addr;
+ pDF->pAircraft = a;
+ memcpy(pDF->msg, mm->msg, MODES_LONG_MSG_BYTES);
+
+ if (!pthread_mutex_lock(&Modes.pDF_mutex)) {
+ if ((pDF->pNext = Modes.pDF)) {
+ Modes.pDF->pPrev = pDF;
+ }
+ Modes.pDF = pDF;
+ pthread_mutex_unlock(&Modes.pDF_mutex);
+ } else {
+ free(pDF);
+ }
+ }
+}
+//
+// Remove stale DF's from the interactive mode linked list
+//
+void interactiveRemoveStaleDF(time_t now) {
+ struct stDF *pDF = NULL;
+ struct stDF *prev = NULL;
+
+ // Only fiddle with the DF list if we gain possession of the mutex
+ // If we fail to get the mutex we'll get another chance to tidy the
+ // DF list in a second or so.
+ if (!pthread_mutex_trylock(&Modes.pDF_mutex)) {
+ pDF = Modes.pDF;
+ while(pDF) {
+ if ((now - pDF->seen) > Modes.interactive_delete_ttl) {
+ if (Modes.pDF == pDF) {
+ Modes.pDF = NULL;
+ } else {
+ prev->pNext = NULL;
+ }
+
+ // All DF's in the list from here onwards will be time
+ // expired, so delete them all
+ while (pDF) {
+ prev = pDF; pDF = pDF->pNext;
+ free(prev);
+ }
+
+ } else {
+ prev = pDF; pDF = pDF->pNext;
+ }
+ }
+ pthread_mutex_unlock (&Modes.pDF_mutex);
+ }
+}
+
+struct stDF *interactiveFindDF(uint32_t addr) {
+ struct stDF *pDF = NULL;
+
+ if (!pthread_mutex_lock(&Modes.pDF_mutex)) {
+ pDF = Modes.pDF;
+ while(pDF) {
+ if (pDF->addr == addr) {
+ pthread_mutex_unlock (&Modes.pDF_mutex);
+ return (pDF);
+ }
+ pDF = pDF->pNext;
+ }
+ pthread_mutex_unlock (&Modes.pDF_mutex);
+ }
+ return (NULL);
+}
+//
+//========================= Interactive mode ===============================
+//
+// Return a new aircraft structure for the interactive mode linked list
+// of aircraft
+//
+struct aircraft *interactiveCreateAircraft(struct modesMessage *mm) {
+ struct aircraft *a = (struct aircraft *) malloc(sizeof(*a));
+
+ // Default everything to zero/NULL
+ memset(a, 0, sizeof(*a));
+
+ // Now initialise things that should not be 0/NULL to their defaults
+ a->addr = mm->addr;
+ a->lat = a->lon = 0.0;
+ memset(a->signalLevel, mm->signalLevel, 8); // First time, initialise everything
+ // to the first signal strength
+
+ // mm->msgtype 32 is used to represent Mode A/C. These values can never change, so
+ // set them once here during initialisation, and don't bother to set them every
+ // time this ModeA/C is received again in the future
+ if (mm->msgtype == 32) {
+ int modeC = ModeAToModeC(mm->modeA | mm->fs);
+ a->modeACflags = MODEAC_MSG_FLAG;
+ if (modeC < -12) {
+ a->modeACflags |= MODEAC_MSG_MODEA_ONLY;
+ } else {
+ mm->altitude = modeC * 100;
+ mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID;
+ }
+ }
+ return (a);
+}
+//
+//=========================================================================
+//
+// Return the aircraft with the specified address, or NULL if no aircraft
+// exists with this address.
+//
+struct aircraft *interactiveFindAircraft(uint32_t addr) {
+ struct aircraft *a = Modes.aircrafts;
+
+ while(a) {
+ if (a->addr == addr) return (a);
+ a = a->next;
+ }
+ return (NULL);
+}
+//
+//=========================================================================
+//
+// We have received a Mode A or C response.
+//
+// Search through the list of known Mode-S aircraft and tag them if this Mode A/C
+// matches their known Mode S Squawks or Altitudes(+/- 50feet).
+//
+// A Mode S equipped aircraft may also respond to Mode A and Mode C SSR interrogations.
+// We can't tell if this is a Mode A or C, so scan through the entire aircraft list
+// looking for matches on Mode A (squawk) and Mode C (altitude). Flag in the Mode S
+// records that we have had a potential Mode A or Mode C response from this aircraft.
+//
+// If an aircraft responds to Mode A then it's highly likely to be responding to mode C
+// too, and vice verca. Therefore, once the mode S record is tagged with both a Mode A
+// and a Mode C flag, we can be fairly confident that this Mode A/C frame relates to that
+// Mode S aircraft.
+//
+// Mode C's are more likely to clash than Mode A's; There could be several aircraft
+// cruising at FL370, but it's less likely (though not impossible) that there are two
+// aircraft on the same squawk. Therefore, give precidence to Mode A record matches
+//
+// Note : It's theoretically possible for an aircraft to have the same value for Mode A
+// and Mode C. Therefore we have to check BOTH A AND C for EVERY S.
+//
+void interactiveUpdateAircraftModeA(struct aircraft *a) {
+ struct aircraft *b = Modes.aircrafts;
+
+ while(b) {
+ if ((b->modeACflags & MODEAC_MSG_FLAG) == 0) {// skip any fudged ICAO records
+
+ // If both (a) and (b) have valid squawks...
+ if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) {
+ // ...check for Mode-A == Mode-S Squawk matches
+ if (a->modeA == b->modeA) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk
+ b->modeAcount = a->messages;
+ b->modeACflags |= MODEAC_MSG_MODEA_HIT;
+ a->modeACflags |= MODEAC_MSG_MODEA_HIT;
+ if ( (b->modeAcount > 0) &&
+ ( (b->modeCcount > 1)
+ || (a->modeACflags & MODEAC_MSG_MODEA_ONLY)) ) // Allow Mode-A only matches if this Mode-A is invalid Mode-C
+ {a->modeACflags |= MODEAC_MSG_MODES_HIT;} // flag this ModeA/C probably belongs to a known Mode S
+ }
+ }
+
+ // If both (a) and (b) have valid altitudes...
+ if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) {
+ // ... check for Mode-C == Mode-S Altitude matches
+ if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude
+ || (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft
+ || (a->modeC + 1 == b->modeC ) ) { // or this Mode-C + 100 ft
+ b->modeCcount = a->messages;
+ b->modeACflags |= MODEAC_MSG_MODEC_HIT;
+ a->modeACflags |= MODEAC_MSG_MODEC_HIT;
+ if ( (b->modeAcount > 0) &&
+ (b->modeCcount > 1) )
+ {a->modeACflags |= (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD);} // flag this ModeA/C probably belongs to a known Mode S
+ }
+ }
+ }
+ b = b->next;
+ }
+}
+//
+//=========================================================================
+//
+void interactiveUpdateAircraftModeS() {
+ struct aircraft *a = Modes.aircrafts;
+
+ while(a) {
+ int flags = a->modeACflags;
+ if (flags & MODEAC_MSG_FLAG) { // find any fudged ICAO records
+
+ // clear the current A,C and S hit bits ready for this attempt
+ a->modeACflags = flags & ~(MODEAC_MSG_MODEA_HIT | MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODES_HIT);
+
+ interactiveUpdateAircraftModeA(a); // and attempt to match them with Mode-S
+ }
+ a = a->next;
+ }
+}
+//
+//=========================================================================
+//
+// Receive new messages and populate the interactive mode with more info
+//
+struct aircraft *interactiveReceiveData(struct modesMessage *mm) {
+ struct aircraft *a, *aux;
+
+ // Return if (checking crc) AND (not crcok) AND (not fixed)
+ if (Modes.check_crc && (mm->crcok == 0) && (mm->correctedbits == 0))
+ return NULL;
+
+ // Lookup our aircraft or create a new one
+ a = interactiveFindAircraft(mm->addr);
+ if (!a) { // If it's a currently unknown aircraft....
+ a = interactiveCreateAircraft(mm); // ., create a new record for it,
+ a->next = Modes.aircrafts; // .. and put it at the head of the list
+ Modes.aircrafts = a;
+ } else {
+ /* If it is an already known aircraft, move it on head
+ * so we keep aircrafts ordered by received message time.
+ *
+ * However move it on head only if at least one second elapsed
+ * since the aircraft that is currently on head sent a message,
+ * othewise with multiple aircrafts at the same time we have an
+ * useless shuffle of positions on the screen. */
+ if (0 && Modes.aircrafts != a && (time(NULL) - a->seen) >= 1) {
+ aux = Modes.aircrafts;
+ while(aux->next != a) aux = aux->next;
+ /* Now we are a node before the aircraft to remove. */
+ aux->next = aux->next->next; /* removed. */
+ /* Add on head */
+ a->next = Modes.aircrafts;
+ Modes.aircrafts = a;
+ }
+ }
+
+ a->signalLevel[a->messages & 7] = mm->signalLevel;// replace the 8th oldest signal strength
+ a->seen = time(NULL);
+ a->timestamp = mm->timestampMsg;
+ a->messages++;
+
+ // If a (new) CALLSIGN has been received, copy it to the aircraft structure
+ if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {
+ memcpy(a->flight, mm->flight, sizeof(a->flight));
+ }
+
+ // If a (new) ALTITUDE has been received, copy it to the aircraft structure
+ if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
+ if ( (a->modeCcount) // if we've a modeCcount already
+ && (a->altitude != mm->altitude ) ) // and Altitude has changed
+// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet
+// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet
+ {
+ a->modeCcount = 0; //....zero the hit count
+ a->modeACflags &= ~MODEAC_MSG_MODEC_HIT;
+ }
+ a->altitude = mm->altitude;
+ a->modeC = (mm->altitude + 49) / 100;
+ }
+
+ // If a (new) SQUAWK has been received, copy it to the aircraft structure
+ if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
+ if (a->modeA != mm->modeA) {
+ a->modeAcount = 0; // Squawk has changed, so zero the hit count
+ a->modeACflags &= ~MODEAC_MSG_MODEA_HIT;
+ }
+ a->modeA = mm->modeA;
+ }
+
+ // If a (new) HEADING has been received, copy it to the aircraft structure
+ if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) {
+ a->track = mm->heading;
+ }
+
+ // If a (new) SPEED has been received, copy it to the aircraft structure
+ if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) {
+ a->speed = mm->velocity;
+ }
+
+ // If a (new) Vertical Descent rate has been received, copy it to the aircraft structure
+ if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {
+ a->vert_rate = mm->vert_rate;
+ }
+
+ // if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags
+ if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) {
+ a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG);
+ }
+
+ // If we've got a new cprlat or cprlon
+ if (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) {
+ int location_ok = 0;
+
+ if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) {
+ a->odd_cprlat = mm->raw_latitude;
+ a->odd_cprlon = mm->raw_longitude;
+ a->odd_cprtime = mstime();
+ } else {
+ a->even_cprlat = mm->raw_latitude;
+ a->even_cprlon = mm->raw_longitude;
+ a->even_cprtime = mstime();
+ }
+
+ // If we have enough recent data, try global CPR
+ if (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID && abs((int)(a->even_cprtime - a->odd_cprtime)) <= 10000) {
+ if (decodeCPR(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) {
+ location_ok = 1;
+ }
+ }
+
+ // Otherwise try relative CPR.
+ if (!location_ok && decodeCPRrelative(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) {
+ location_ok = 1;
+ }
+
+ //If we sucessfully decoded, back copy the results to mm so that we can print them in list output
+ if (location_ok) {
+ mm->bFlags |= MODES_ACFLAGS_LATLON_VALID;
+ mm->fLat = a->lat;
+ mm->fLon = a->lon;
+ }
+ }
+
+ // Update the aircrafts a->bFlags to reflect the newly received mm->bFlags;
+ a->bFlags |= mm->bFlags;
+
+ if (mm->msgtype == 32) {
+ int flags = a->modeACflags;
+ if ((flags & (MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODEC_OLD)) == MODEAC_MSG_MODEC_OLD) {
+ //
+ // This Mode-C doesn't currently hit any known Mode-S, but it used to because MODEAC_MSG_MODEC_OLD is
+ // set So the aircraft it used to match has either changed altitude, or gone out of our receiver range
+ //
+ // We've now received this Mode-A/C again, so it must be a new aircraft. It could be another aircraft
+ // at the same Mode-C altitude, or it could be a new airctraft with a new Mods-A squawk.
+ //
+ // To avoid masking this aircraft from the interactive display, clear the MODEAC_MSG_MODES_OLD flag
+ // and set messages to 1;
+ //
+ a->modeACflags = flags & ~MODEAC_MSG_MODEC_OLD;
+ a->messages = 1;
+ }
+ }
+
+ // If we are Logging DF's, and it's not a Mode A/C
+ if ((Modes.bEnableDFLogging) && (mm->msgtype < 32)) {
+ interactiveCreateDF(a,mm);
+ }
+
+ return (a);
+}
+//
+//=========================================================================
+//
+// Show the currently captured interactive data on screen.
+//
+void interactiveShowData(void) {
+ struct aircraft *a = Modes.aircrafts;
+ time_t now = time(NULL);
+ int count = 0;
+ char progress;
+ char spinner[4] = "|/-\\";
+
+ // Refresh screen every (MODES_INTERACTIVE_REFRESH_TIME) miliseconde
+ if ((mstime() - Modes.interactive_last_update) < MODES_INTERACTIVE_REFRESH_TIME)
+ {return;}
+
+ Modes.interactive_last_update = mstime();
+
+ // Attempt to reconsile any ModeA/C with known Mode-S
+ // We can't condition on Modes.modeac because ModeA/C could be comming
+ // in from a raw input port which we can't turn off.
+ interactiveUpdateAircraftModeS();
+
+ progress = spinner[time(NULL)%4];
+
+#ifndef _WIN32
+ printf("\x1b[H\x1b[2J"); // Clear the screen
+#else
+ cls();
+#endif
+
+ if (Modes.interactive_rtl1090 == 0) {
+ printf (
+"Hex Mode Sqwk Flight Alt Spd Hdg Lat Long Sig Msgs Ti%c\n", progress);
+ } else {
+ printf (
+"Hex Flight Alt V/S GS TT SSR G*456^ Msgs Seen %c\n", progress);
+ }
+ printf(
+"-------------------------------------------------------------------------------\n");
+
+ while(a && (count < Modes.interactive_rows)) {
+
+ if ((now - a->seen) < Modes.interactive_display_ttl)
+ {
+ int msgs = a->messages;
+ int flags = a->modeACflags;
+
+ if ( (((flags & (MODEAC_MSG_FLAG )) == 0 ) )
+ || (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEA_ONLY)) == MODEAC_MSG_MODEA_ONLY) && (msgs > 4 ) )
+ || (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD )) == 0 ) && (msgs > 127) )
+ ) {
+ int altitude = a->altitude, speed = a->speed;
+ char strSquawk[5] = " ";
+ char strFl[6] = " ";
+ char strTt[5] = " ";
+ char strGs[5] = " ";
+
+ // Convert units to metric if --metric was specified
+ if (Modes.metric) {
+ altitude = (int) (altitude / 3.2828);
+ speed = (int) (speed * 1.852);
+ }
+
+ if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
+ snprintf(strSquawk,5,"%04x", a->modeA);}
+
+ if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) {
+ snprintf (strGs, 5,"%3d", speed);}
+
+ if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) {
+ snprintf (strTt, 5,"%03d", a->track);}
+
+ if (msgs > 99999) {
+ msgs = 99999;}
+
+ if (Modes.interactive_rtl1090) { // RTL1090 display mode
+
+ if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
+ snprintf(strFl,6,"F%03d",(altitude/100));
+ }
+ printf("%06x %-8s %-4s %-3s %-3s %4s %-6d %-2d\n",
+ a->addr, a->flight, strFl, strGs, strTt, strSquawk, msgs, (int)(now - a->seen));
+
+ } else { // Dump1090 display mode
+ char strMode[5] = " ";
+ char strLat[8] = " ";
+ char strLon[9] = " ";
+ unsigned char * pSig = a->signalLevel;
+ unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] +
+ pSig[4] + pSig[5] + pSig[6] + pSig[7] + 3) >> 3;
+
+ if ((flags & MODEAC_MSG_FLAG) == 0) {
+ strMode[0] = 'S';
+ } else if (flags & MODEAC_MSG_MODEA_ONLY) {
+ strMode[0] = 'A';
+ }
+ if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';}
+ if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';}
+
+ if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) {
+ snprintf(strLat, 8,"%7.03f", a->lat);
+ snprintf(strLon, 9,"%8.03f", a->lon);
+ }
+
+ if (a->bFlags & MODES_ACFLAGS_AOG) {
+ snprintf(strFl, 6," grnd");
+ } else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
+ snprintf(strFl, 6, "%5d", altitude);
+ }
+
+ printf("%06X %-4s %-4s %-8s %5s %3s %3s %7s %8s %3d %5d %2d\n",
+ a->addr, strMode, strSquawk, a->flight, strFl, strGs, strTt,
+ strLat, strLon, signalAverage, msgs, (int)(now - a->seen));
+ }
+ count++;
+ }
+ }
+ a = a->next;
+ }
+}
+//
+//=========================================================================
+//
+// When in interactive mode If we don't receive new nessages within
+// MODES_INTERACTIVE_DELETE_TTL seconds we remove the aircraft from the list.
+//
+void interactiveRemoveStaleAircrafts(void) {
+ struct aircraft *a = Modes.aircrafts;
+ struct aircraft *prev = NULL;
+ time_t now = time(NULL);
+
+ // Only do cleanup once per second
+ if (Modes.last_cleanup_time != now) {
+ Modes.last_cleanup_time = now;
+
+ interactiveRemoveStaleDF(now);
+
+ while(a) {
+ if ((now - a->seen) > Modes.interactive_delete_ttl) {
+ // Remove the element from the linked list, with care
+ // if we are removing the first element
+ if (!prev) {
+ Modes.aircrafts = a->next; free(a); a = Modes.aircrafts;
+ } else {
+ prev->next = a->next; free(a); a = prev->next;
+ }
+ } else {
+ prev = a; a = a->next;
+ }
+ }
+ }
+}
+//
+//=========================================================================
+//
diff --git a/makedump1090 b/makedump1090
new file mode 100644
index 0000000..4d3fccb
--- /dev/null
+++ b/makedump1090
@@ -0,0 +1,27 @@
+#
+# When building a package or installing otherwise in the system, make
+# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
+#
+PROGNAME=dump1090
+
+ifdef PREFIX
+BINDIR=$(PREFIX)/bin
+SHAREDIR=$(PREFIX)/share/$(PROGNAME)
+EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\"
+endif
+
+CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr`
+LIBS=`pkg-config --libs librtlsdr` -lpthread -lm
+CC=gcc
+
+
+all: dump1090
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
+
+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) $(LDFLAGS)
+
+clean:
+ rm -f *.o dump1090
diff --git a/makeppup1090 b/makeppup1090
new file mode 100644
index 0000000..9df60aa
--- /dev/null
+++ b/makeppup1090
@@ -0,0 +1,27 @@
+#
+# When building a package or installing otherwise in the system, make
+# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
+#
+PROGNAME=ppup1090
+
+ifdef PREFIX
+BINDIR=$(PREFIX)/bin
+SHAREDIR=$(PREFIX)/share/$(PROGNAME)
+EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\"
+endif
+
+CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr`
+LIBS=`pkg-config --libs librtlsdr` -lpthread -lm
+CC=gcc
+
+
+all: ppup1090
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
+
+ppup1090: ppup1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o
+ $(CC) -g -o ppup1090 ppup1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o coaa1090.obj $(LIBS) $(LDFLAGS)
+
+clean:
+ rm -f *.o ppup1090
diff --git a/makeview1090 b/makeview1090
new file mode 100644
index 0000000..6cb76af
--- /dev/null
+++ b/makeview1090
@@ -0,0 +1,27 @@
+#
+# When building a package or installing otherwise in the system, make
+# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
+#
+PROGNAME=view1090
+
+ifdef PREFIX
+BINDIR=$(PREFIX)/bin
+SHAREDIR=$(PREFIX)/share/$(PROGNAME)
+EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\"
+endif
+
+CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr`
+LIBS=`pkg-config --libs librtlsdr` -lpthread -lm
+CC=gcc
+
+
+all: view1090
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
+
+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) $(LDFLAGS)
+
+clean:
+ rm -f *.o view1090
diff --git a/mode_ac.c b/mode_ac.c
new file mode 100644
index 0000000..881a428
--- /dev/null
+++ b/mode_ac.c
@@ -0,0 +1,386 @@
+// dump1090, a Mode S messages decoder for RTLSDR devices.
+//
+// Copyright (C) 2012 by Salvatore Sanfilippo <antirez at gmail.com>
+//
+// 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 "dump1090.h"
+//
+// ===================== Mode A/C detection and decoding ===================
+//
+//
+// This table is used to build the Mode A/C variable called ModeABits.Each
+// bit period is inspected, and if it's value exceeds the threshold limit,
+// then the value in this table is or-ed into ModeABits.
+//
+// At the end of message processing, ModeABits will be the decoded ModeA value.
+//
+// We can also flag noise in bits that should be zeros - the xx bits. Noise in
+// these bits cause bits (31-16) in ModeABits to be set. Then at the end of message
+// processing we can test for errors by looking at these bits.
+//
+uint32_t ModeABitTable[24] = {
+0x00000000, // F1 = 1
+0x00000010, // C1
+0x00001000, // A1
+0x00000020, // C2
+0x00002000, // A2
+0x00000040, // C4
+0x00004000, // A4
+0x40000000, // xx = 0 Set bit 30 if we see this high
+0x00000100, // B1
+0x00000001, // D1
+0x00000200, // B2
+0x00000002, // D2
+0x00000400, // B4
+0x00000004, // D4
+0x00000000, // F2 = 1
+0x08000000, // xx = 0 Set bit 27 if we see this high
+0x04000000, // xx = 0 Set bit 26 if we see this high
+0x00000080, // SPI
+0x02000000, // xx = 0 Set bit 25 if we see this high
+0x01000000, // xx = 0 Set bit 24 if we see this high
+0x00800000, // xx = 0 Set bit 23 if we see this high
+0x00400000, // xx = 0 Set bit 22 if we see this high
+0x00200000, // xx = 0 Set bit 21 if we see this high
+0x00100000, // xx = 0 Set bit 20 if we see this high
+};
+//
+// This table is used to produce an error variable called ModeAErrs.Each
+// inter-bit period is inspected, and if it's value falls outside of the
+// expected range, then the value in this table is or-ed into ModeAErrs.
+//
+// At the end of message processing, ModeAErrs will indicate if we saw
+// any inter-bit anomolies, and the bits that are set will show which
+// bits had them.
+//
+uint32_t ModeAMidTable[24] = {
+0x80000000, // F1 = 1 Set bit 31 if we see F1_C1 error
+0x00000010, // C1 Set bit 4 if we see C1_A1 error
+0x00001000, // A1 Set bit 12 if we see A1_C2 error
+0x00000020, // C2 Set bit 5 if we see C2_A2 error
+0x00002000, // A2 Set bit 13 if we see A2_C4 error
+0x00000040, // C4 Set bit 6 if we see C3_A4 error
+0x00004000, // A4 Set bit 14 if we see A4_xx error
+0x40000000, // xx = 0 Set bit 30 if we see xx_B1 error
+0x00000100, // B1 Set bit 8 if we see B1_D1 error
+0x00000001, // D1 Set bit 0 if we see D1_B2 error
+0x00000200, // B2 Set bit 9 if we see B2_D2 error
+0x00000002, // D2 Set bit 1 if we see D2_B4 error
+0x00000400, // B4 Set bit 10 if we see B4_D4 error
+0x00000004, // D4 Set bit 2 if we see D4_F2 error
+0x20000000, // F2 = 1 Set bit 29 if we see F2_xx error
+0x08000000, // xx = 0 Set bit 27 if we see xx_xx error
+0x04000000, // xx = 0 Set bit 26 if we see xx_SPI error
+0x00000080, // SPI Set bit 15 if we see SPI_xx error
+0x02000000, // xx = 0 Set bit 25 if we see xx_xx error
+0x01000000, // xx = 0 Set bit 24 if we see xx_xx error
+0x00800000, // xx = 0 Set bit 23 if we see xx_xx error
+0x00400000, // xx = 0 Set bit 22 if we see xx_xx error
+0x00200000, // xx = 0 Set bit 21 if we see xx_xx error
+0x00100000, // xx = 0 Set bit 20 if we see xx_xx error
+};
+//
+// The "off air" format is,,
+// _F1_C1_A1_C2_A2_C4_A4_xx_B1_D1_B2_D2_B4_D4_F2_xx_xx_SPI_
+//
+// Bit spacing is 1.45uS, with 0.45uS high, and 1.00us low. This is a problem
+// because we ase sampling at 2Mhz (500nS) so we are below Nyquist.
+//
+// The bit spacings are..
+// F1 : 0.00,
+// 1.45, 2.90, 4.35, 5.80, 7.25, 8.70,
+// X : 10.15,
+// : 11.60, 13.05, 14.50, 15.95, 17.40, 18.85,
+// F2 : 20.30,
+// X : 21.75, 23.20, 24.65
+//
+// This equates to the following sample point centers at 2Mhz.
+// [ 0.0],
+// [ 2.9], [ 5.8], [ 8.7], [11.6], [14.5], [17.4],
+// [20.3],
+// [23.2], [26.1], [29.0], [31.9], [34.8], [37.7]
+// [40.6]
+// [43.5], [46.4], [49.3]
+//
+// We know that this is a supposed to be a binary stream, so the signal
+// should either be a 1 or a 0. Therefore, any energy above the noise level
+// in two adjacent samples must be from the same pulse, so we can simply
+// add the values together..
+//
+int detectModeA(uint16_t *m, struct modesMessage *mm)
+ {
+ int j, lastBitWasOne;
+ int ModeABits = 0;
+ int ModeAErrs = 0;
+ int byte, bit;
+ int thisSample, lastBit, lastSpace = 0;
+ int m0, m1, m2, m3, mPhase;
+ int n0, n1, n2 ,n3;
+ int F1_sig, F1_noise;
+ int F2_sig, F2_noise;
+ int fSig, fNoise, fLevel, fLoLo;
+
+ // m[0] contains the energy from 0 -> 499 nS
+ // m[1] contains the energy from 500 -> 999 nS
+ // m[2] contains the energy from 1000 -> 1499 nS
+ // m[3] contains the energy from 1500 -> 1999 nS
+ //
+ // We are looking for a Frame bit (F1) whose width is 450nS, followed by
+ // 1000nS of quiet.
+ //
+ // The width of the frame bit is 450nS, which is 90% of our sample rate.
+ // Therefore, in an ideal world, all the energy for the frame bit will be
+ // in a single sample, preceeded by (at least) one zero, and followed by
+ // two zeros, Best case we can look for ...
+ //
+ // 0 - 1 - 0 - 0
+ //
+ // However, our samples are not phase aligned, so some of the energy from
+ // each bit could be spread over two consecutive samples. Worst case is
+ // that we sample half in one bit, and half in the next. In that case,
+ // we're looking for
+ //
+ // 0 - 0.5 - 0.5 - 0.
+
+ m0 = m[0]; m1 = m[1];
+
+ if (m0 >= m1) // m1 *must* be bigger than m0 for this to be F1
+ {return (0);}
+
+ m2 = m[2]; m3 = m[3];
+
+ //
+ // if (m2 <= m0), then assume the sample bob on (Phase == 0), so don't look at m3
+ if ((m2 <= m0) || (m2 < m3))
+ {m3 = m2; m2 = m0;}
+
+ if ( (m3 >= m1) // m1 must be bigger than m3
+ || (m0 > m2) // m2 can be equal to m0 if ( 0,1,0,0 )
+ || (m3 > m2) ) // m2 can be equal to m3 if ( 0,1,0,0 )
+ {return (0);}
+
+ // m0 = noise
+ // m1 = noise + (signal * X))
+ // m2 = noise + (signal * (1-X))
+ // m3 = noise
+ //
+ // Hence, assuming all 4 samples have similar amounts of noise in them
+ // signal = (m1 + m2) - ((m0 + m3) * 2)
+ // noise = (m0 + m3) / 2
+ //
+ F1_sig = (m1 + m2) - ((m0 + m3) << 1);
+ F1_noise = (m0 + m3) >> 1;
+
+ if ( (F1_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F1 signal amplitude
+ || (F1_sig < (F1_noise << 2)) ) // minimum allowable Sig/Noise ratio 4:1
+ {return (0);}
+
+ // If we get here then we have a potential F1, so look for an equally valid F2 20.3uS later
+ //
+ // Our F1 is centered somewhere between samples m[1] and m[2]. We can guestimate where F2 is
+ // by comparing the ratio of m1 and m2, and adding on 20.3 uS (40.6 samples)
+ //
+ mPhase = ((m2 * 20) / (m1 + m2));
+ byte = (mPhase + 812) / 20;
+ n0 = m[byte++]; n1 = m[byte++];
+
+ if (n0 >= n1) // n1 *must* be bigger than n0 for this to be F2
+ {return (0);}
+
+ n2 = m[byte++];
+ //
+ // if the sample bob on (Phase == 0), don't look at n3
+ //
+ if ((mPhase + 812) % 20)
+ {n3 = m[byte++];}
+ else
+ {n3 = n2; n2 = n0;}
+
+ if ( (n3 >= n1) // n1 must be bigger than n3
+ || (n0 > n2) // n2 can be equal to n0 ( 0,1,0,0 )
+ || (n3 > n2) ) // n2 can be equal to n3 ( 0,1,0,0 )
+ {return (0);}
+
+ F2_sig = (n1 + n2) - ((n0 + n3) << 1);
+ F2_noise = (n0 + n3) >> 1;
+
+ if ( (F2_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F2 signal amplitude
+ || (F2_sig < (F2_noise << 2)) ) // maximum allowable Sig/Noise ratio 4:1
+ {return (0);}
+
+ fSig = (F1_sig + F2_sig) >> 1;
+ fNoise = (F1_noise + F2_noise) >> 1;
+ fLoLo = fNoise + (fSig >> 2); // 1/2
+ fLevel = fNoise + (fSig >> 1);
+ lastBitWasOne = 1;
+ lastBit = F1_sig;
+ //
+ // Now step by a half ModeA bit, 0.725nS, which is 1.45 samples, which is 29/20
+ // No need to do bit 0 because we've already selected it as a valid F1
+ // Do several bits past the SPI to increase error rejection
+ //
+ for (j = 1, mPhase += 29; j < 48; mPhase += 29, j ++)
+ {
+ byte = 1 + (mPhase / 20);
+
+ thisSample = m[byte] - fNoise;
+ if (mPhase % 20) // If the bit is split over two samples...
+ {thisSample += (m[byte+1] - fNoise);} // add in the second sample's energy
+
+ // If we're calculating a space value
+ if (j & 1)
+ {lastSpace = thisSample;}
+
+ else
+ {// We're calculating a new bit value
+ bit = j >> 1;
+ if (thisSample >= fLevel)
+ {// We're calculating a new bit value, and its a one
+ ModeABits |= ModeABitTable[bit--]; // or in the correct bit
+
+ if (lastBitWasOne)
+ { // This bit is one, last bit was one, so check the last space is somewhere less than one
+ if ( (lastSpace >= (thisSample>>1)) || (lastSpace >= lastBit) )
+ {ModeAErrs |= ModeAMidTable[bit];}
+ }
+
+ else
+ {// This bit,is one, last bit was zero, so check the last space is somewhere less than one
+ if (lastSpace >= (thisSample >> 1))
+ {ModeAErrs |= ModeAMidTable[bit];}
+ }
+
+ lastBitWasOne = 1;
+ }
+
+
+ else
+ {// We're calculating a new bit value, and its a zero
+ if (lastBitWasOne)
+ { // This bit is zero, last bit was one, so check the last space is somewhere in between
+ if (lastSpace >= lastBit)
+ {ModeAErrs |= ModeAMidTable[bit];}
+ }
+
+ else
+ {// This bit,is zero, last bit was zero, so check the last space is zero too
+ if (lastSpace >= fLoLo)
+ {ModeAErrs |= ModeAMidTable[bit];}
+ }
+
+ lastBitWasOne = 0;
+ }
+
+ lastBit = (thisSample >> 1);
+ }
+ }
+
+ //
+ // Output format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1
+ //
+ if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) )
+ {return (ModeABits = 0);}
+
+ fSig = (fSig + 0x7F) >> 8;
+ mm->signalLevel = ((fSig < 255) ? fSig : 255);
+
+ return ModeABits;
+ }
+//
+//=========================================================================
+//
+// Input format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1
+//
+int ModeAToModeC(unsigned int ModeA)
+ {
+ unsigned int FiveHundreds = 0;
+ unsigned int OneHundreds = 0;
+
+ if ( (ModeA & 0xFFFF888B) // D1 set is illegal. D2 set is > 62700ft which is unlikely
+ || ((ModeA & 0x000000F0) == 0) ) // C1,,C4 cannot be Zero
+ {return -9999;}
+
+ if (ModeA & 0x0010) {OneHundreds ^= 0x007;} // C1
+ if (ModeA & 0x0020) {OneHundreds ^= 0x003;} // C2
+ if (ModeA & 0x0040) {OneHundreds ^= 0x001;} // C4
+
+ // Remove 7s from OneHundreds (Make 7->5, snd 5->7).
+ if ((OneHundreds & 5) == 5) {OneHundreds ^= 2;}
+
+ // Check for invalid codes, only 1 to 5 are valid
+ if (OneHundreds > 5)
+ {return -9999;}
+
+//if (ModeA & 0x0001) {FiveHundreds ^= 0x1FF;} // D1 never used for altitude
+ if (ModeA & 0x0002) {FiveHundreds ^= 0x0FF;} // D2
+ if (ModeA & 0x0004) {FiveHundreds ^= 0x07F;} // D4
+
+ if (ModeA & 0x1000) {FiveHundreds ^= 0x03F;} // A1
+ if (ModeA & 0x2000) {FiveHundreds ^= 0x01F;} // A2
+ if (ModeA & 0x4000) {FiveHundreds ^= 0x00F;} // A4
+
+ if (ModeA & 0x0100) {FiveHundreds ^= 0x007;} // B1
+ if (ModeA & 0x0200) {FiveHundreds ^= 0x003;} // B2
+ if (ModeA & 0x0400) {FiveHundreds ^= 0x001;} // B4
+
+ // Correct order of OneHundreds.
+ if (FiveHundreds & 1) {OneHundreds = 6 - OneHundreds;}
+
+ return ((FiveHundreds * 5) + OneHundreds - 13);
+ }
+//
+//=========================================================================
+//
+void decodeModeAMessage(struct modesMessage *mm, int ModeA)
+ {
+ mm->msgtype = 32; // Valid Mode S DF's are DF-00 to DF-31.
+ // so use 32 to indicate Mode A/C
+
+ mm->msgbits = 16; // Fudge up a Mode S style data stream
+ mm->msg[0] = (ModeA >> 8);
+ mm->msg[1] = (ModeA);
+
+ // Fudge an ICAO address based on Mode A (remove the Ident bit)
+ // Use an upper address byte of FF, since this is ICAO unallocated
+ mm->addr = 0x00FF0000 | (ModeA & 0x0000FF7F);
+
+ // Set the Identity field to ModeA
+ mm->modeA = ModeA & 0x7777;
+ mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID;
+
+ // Flag ident in flight status
+ mm->fs = ModeA & 0x0080;
+
+ // Not much else we can tell from a Mode A/C reply.
+ // Just fudge up a few bits to keep other code happy
+ mm->crcok = 1;
+ mm->correctedbits = 0;
+ }
+//
+// ===================== Mode A/C detection and decoding ===================
+//
\ No newline at end of file
diff --git a/mode_s.c b/mode_s.c
new file mode 100644
index 0000000..bb0da2c
--- /dev/null
+++ b/mode_s.c
@@ -0,0 +1,2172 @@
+// dump1090, a Mode S messages decoder for RTLSDR devices.
+//
+// Copyright (C) 2012 by Salvatore Sanfilippo <antirez at gmail.com>
+//
+// 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 "dump1090.h"
+//
+// ===================== Mode S detection and decoding ===================
+//
+// Parity table for MODE S Messages.
+// The table contains 112 elements, every element corresponds to a bit set
+// in the message, starting from the first bit of actual data after the
+// preamble.
+//
+// For messages of 112 bit, the whole table is used.
+// For messages of 56 bits only the last 56 elements are used.
+//
+// The algorithm is as simple as xoring all the elements in this table
+// for which the corresponding bit on the message is set to 1.
+//
+// The latest 24 elements in this table are set to 0 as the checksum at the
+// end of the message should not affect the computation.
+//
+// Note: this function can be used with DF11 and DF17, other modes have
+// the CRC xored with the sender address as they are reply to interrogations,
+// but a casual listener can't split the address from the checksum.
+//
+uint32_t modes_checksum_table[112] = {
+0x3935ea, 0x1c9af5, 0xf1b77e, 0x78dbbf, 0xc397db, 0x9e31e9, 0xb0e2f0, 0x587178,
+0x2c38bc, 0x161c5e, 0x0b0e2f, 0xfa7d13, 0x82c48d, 0xbe9842, 0x5f4c21, 0xd05c14,
+0x682e0a, 0x341705, 0xe5f186, 0x72f8c3, 0xc68665, 0x9cb936, 0x4e5c9b, 0xd8d449,
+0x939020, 0x49c810, 0x24e408, 0x127204, 0x093902, 0x049c81, 0xfdb444, 0x7eda22,
+0x3f6d11, 0xe04c8c, 0x702646, 0x381323, 0xe3f395, 0x8e03ce, 0x4701e7, 0xdc7af7,
+0x91c77f, 0xb719bb, 0xa476d9, 0xadc168, 0x56e0b4, 0x2b705a, 0x15b82d, 0xf52612,
+0x7a9309, 0xc2b380, 0x6159c0, 0x30ace0, 0x185670, 0x0c2b38, 0x06159c, 0x030ace,
+0x018567, 0xff38b7, 0x80665f, 0xbfc92b, 0xa01e91, 0xaff54c, 0x57faa6, 0x2bfd53,
+0xea04ad, 0x8af852, 0x457c29, 0xdd4410, 0x6ea208, 0x375104, 0x1ba882, 0x0dd441,
+0xf91024, 0x7c8812, 0x3e4409, 0xe0d800, 0x706c00, 0x383600, 0x1c1b00, 0x0e0d80,
+0x0706c0, 0x038360, 0x01c1b0, 0x00e0d8, 0x00706c, 0x003836, 0x001c1b, 0xfff409,
+0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
+0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
+0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000
+};
+
+uint32_t modesChecksum(unsigned char *msg, int bits) {
+ uint32_t crc = 0;
+ uint32_t rem = 0;
+ int offset = (bits == 112) ? 0 : (112-56);
+ uint8_t theByte = *msg;
+ uint32_t * pCRCTable = &modes_checksum_table[offset];
+ int j;
+
+ // We don't really need to include the checksum itself
+ bits -= 24;
+ for(j = 0; j < bits; j++) {
+ if ((j & 7) == 0)
+ theByte = *msg++;
+
+ // If bit is set, xor with corresponding table entry.
+ if (theByte & 0x80) {crc ^= *pCRCTable;}
+ pCRCTable++;
+ theByte = theByte << 1;
+ }
+
+ rem = (msg[0] << 16) | (msg[1] << 8) | msg[2]; // message checksum
+ return ((crc ^ rem) & 0x00FFFFFF); // 24 bit checksum syndrome.
+}
+//
+//=========================================================================
+//
+// Given the Downlink Format (DF) of the message, return the message length in bits.
+//
+// All known DF's 16 or greater are long. All known DF's 15 or less are short.
+// There are lots of unused codes in both category, so we can assume ICAO will stick to
+// these rules, meaning that the most significant bit of the DF indicates the length.
+//
+int modesMessageLenByType(int type) {
+ return (type & 0x10) ? MODES_LONG_MSG_BITS : MODES_SHORT_MSG_BITS ;
+}
+//
+//=========================================================================
+//
+// Try to fix single bit errors using the checksum. On success modifies
+// the original buffer with the fixed version, and returns the position
+// of the error bit. Otherwise if fixing failed -1 is returned.
+/*
+int fixSingleBitErrors(unsigned char *msg, int bits) {
+ int j;
+ unsigned char aux[MODES_LONG_MSG_BYTES];
+
+ memcpy(aux, msg, bits/8);
+
+ // Do not attempt to error correct Bits 0-4. These contain the DF, and must
+ // be correct because we can only error correct DF17
+ for (j = 5; j < bits; j++) {
+ int byte = j/8;
+ int bitmask = 1 << (7 - (j & 7));
+
+ aux[byte] ^= bitmask; // Flip j-th bit
+
+ if (0 == modesChecksum(aux, bits)) {
+ // The error is fixed. Overwrite the original buffer with the
+ // corrected sequence, and returns the error bit position
+ msg[byte] = aux[byte];
+ return (j);
+ }
+
+ aux[byte] ^= bitmask; // Flip j-th bit back again
+ }
+ return (-1);
+}
+*/
+//=========================================================================
+//
+// Similar to fixSingleBitErrors() but try every possible two bit combination.
+// This is very slow and should be tried only against DF17 messages that
+// don't pass the checksum, and only in Aggressive Mode.
+/*
+int fixTwoBitsErrors(unsigned char *msg, int bits) {
+ int j, i;
+ unsigned char aux[MODES_LONG_MSG_BYTES];
+
+ memcpy(aux, msg, bits/8);
+
+ // Do not attempt to error correct Bits 0-4. These contain the DF, and must
+ // be correct because we can only error correct DF17
+ for (j = 5; j < bits; j++) {
+ int byte1 = j/8;
+ int bitmask1 = 1 << (7 - (j & 7));
+ aux[byte1] ^= bitmask1; // Flip j-th bit
+
+ // Don't check the same pairs multiple times, so i starts from j+1
+ for (i = j+1; i < bits; i++) {
+ int byte2 = i/8;
+ int bitmask2 = 1 << (7 - (i & 7));
+
+ aux[byte2] ^= bitmask2; // Flip i-th bit
+
+ if (0 == modesChecksum(aux, bits)) {
+ // The error is fixed. Overwrite the original buffer with
+ // the corrected sequence, and returns the error bit position
+ msg[byte1] = aux[byte1];
+ msg[byte2] = aux[byte2];
+
+ // We return the two bits as a 16 bit integer by shifting
+ // 'i' on the left. This is possible since 'i' will always
+ // be non-zero because i starts from j+1
+ return (j | (i << 8));
+
+ aux[byte2] ^= bitmask2; // Flip i-th bit back
+ }
+
+ aux[byte1] ^= bitmask1; // Flip j-th bit back
+ }
+ }
+ return (-1);
+}
+*/
+//
+//=========================================================================
+//
+// Code for introducing a less CPU-intensive method of correcting
+// single bit errors.
+//
+// Makes use of the fact that the crc checksum is linear with respect to
+// the bitwise xor operation, i.e.
+// crc(m^e) = (crc(m)^crc(e)
+// where m and e are the message resp. error bit vectors.
+//
+// Call crc(e) the syndrome.
+//
+// The code below works by precomputing a table of (crc(e), e) for all
+// possible error vectors e (here only single bit and double bit errors),
+// search for the syndrome in the table, and correct the then known error.
+// The error vector e is represented by one or two bit positions that are
+// changed. If a second bit position is not used, it is -1.
+//
+// Run-time is binary search in a sorted table, plus some constant overhead,
+// instead of running through all possible bit positions (resp. pairs of
+// bit positions).
+//
+struct errorinfo {
+ uint32_t syndrome; // CRC syndrome
+ int bits; // Number of bit positions to fix
+ int pos[MODES_MAX_BITERRORS]; // Bit positions corrected by this syndrome
+};
+
+#define NERRORINFO \
+ (MODES_LONG_MSG_BITS+MODES_LONG_MSG_BITS*(MODES_LONG_MSG_BITS-1)/2)
+struct errorinfo bitErrorTable[NERRORINFO];
+
+// Compare function as needed for stdlib's qsort and bsearch functions
+int cmpErrorInfo(const void *p0, const void *p1) {
+ struct errorinfo *e0 = (struct errorinfo*)p0;
+ struct errorinfo *e1 = (struct errorinfo*)p1;
+ if (e0->syndrome == e1->syndrome) {
+ return 0;
+ } else if (e0->syndrome < e1->syndrome) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+//
+//=========================================================================
+//
+// Compute the table of all syndromes for 1-bit and 2-bit error vectors
+void modesInitErrorInfo() {
+ unsigned char msg[MODES_LONG_MSG_BYTES];
+ int i, j, n;
+ uint32_t crc;
+ n = 0;
+ memset(bitErrorTable, 0, sizeof(bitErrorTable));
+ memset(msg, 0, MODES_LONG_MSG_BYTES);
+ // Add all possible single and double bit errors
+ // don't include errors in first 5 bits (DF type)
+ for (i = 5; i < MODES_LONG_MSG_BITS; i++) {
+ int bytepos0 = (i >> 3);
+ int mask0 = 1 << (7 - (i & 7));
+ msg[bytepos0] ^= mask0; // create error0
+ crc = modesChecksum(msg, MODES_LONG_MSG_BITS);
+ bitErrorTable[n].syndrome = crc; // single bit error case
+ bitErrorTable[n].bits = 1;
+ bitErrorTable[n].pos[0] = i;
+ bitErrorTable[n].pos[1] = -1;
+ n += 1;
+
+ if (Modes.nfix_crc > 1) {
+ for (j = i+1; j < MODES_LONG_MSG_BITS; j++) {
+ int bytepos1 = (j >> 3);
+ int mask1 = 1 << (7 - (j & 7));
+ msg[bytepos1] ^= mask1; // create error1
+ crc = modesChecksum(msg, MODES_LONG_MSG_BITS);
+ if (n >= NERRORINFO) {
+ //fprintf(stderr, "Internal error, too many entries, fix NERRORINFO\n");
+ break;
+ }
+ bitErrorTable[n].syndrome = crc; // two bit error case
+ bitErrorTable[n].bits = 2;
+ bitErrorTable[n].pos[0] = i;
+ bitErrorTable[n].pos[1] = j;
+ n += 1;
+ msg[bytepos1] ^= mask1; // revert error1
+ }
+ }
+ msg[bytepos0] ^= mask0; // revert error0
+ }
+ qsort(bitErrorTable, NERRORINFO, sizeof(struct errorinfo), cmpErrorInfo);
+
+ // Test code: report if any syndrome appears at least twice. In this
+ // case the correction cannot be done without ambiguity.
+ // Tried it, does not happen for 1- and 2-bit errors.
+ /*
+ for (i = 1; i < NERRORINFO; i++) {
+ if (bitErrorTable[i-1].syndrome == bitErrorTable[i].syndrome) {
+ fprintf(stderr, "modesInitErrorInfo: Collision for syndrome %06x\n",
+ (int)bitErrorTable[i].syndrome);
+ }
+ }
+
+ for (i = 0; i < NERRORINFO; i++) {
+ printf("syndrome %06x bit0 %3d bit1 %3d\n",
+ bitErrorTable[i].syndrome,
+ bitErrorTable[i].pos0, bitErrorTable[i].pos1);
+ }
+ */
+}
+//
+//=========================================================================
+//
+// Search for syndrome in table and if an entry is found, flip the necessary
+// bits. Make sure the indices fit into the array
+// Additional parameter: fix only less than maxcorrected bits, and record
+// fixed bit positions in corrected[]. This array can be NULL, otherwise
+// must be of length at least maxcorrected.
+// Return number of fixed bits.
+//
+int fixBitErrors(unsigned char *msg, int bits, int maxfix, char *fixedbits) {
+ struct errorinfo *pei;
+ struct errorinfo ei;
+ int bitpos, offset, res, i;
+ memset(&ei, 0, sizeof(struct errorinfo));
+ ei.syndrome = modesChecksum(msg, bits);
+ pei = bsearch(&ei, bitErrorTable, NERRORINFO,
+ sizeof(struct errorinfo), cmpErrorInfo);
+ if (pei == NULL) {
+ return 0; // No syndrome found
+ }
+
+ // Check if the syndrome fixes more bits than we allow
+ if (maxfix < pei->bits) {
+ return 0;
+ }
+
+ // Check that all bit positions lie inside the message length
+ offset = MODES_LONG_MSG_BITS-bits;
+ for (i = 0; i < pei->bits; i++) {
+ bitpos = pei->pos[i] - offset;
+ if ((bitpos < 0) || (bitpos >= bits)) {
+ return 0;
+ }
+ }
+
+ // Fix the bits
+ for (i = res = 0; i < pei->bits; i++) {
+ bitpos = pei->pos[i] - offset;
+ msg[bitpos >> 3] ^= (1 << (7 - (bitpos & 7)));
+ if (fixedbits) {
+ fixedbits[res++] = bitpos;
+ }
+ }
+ return res;
+}
+//
+// ============================== Debugging =================================
+//
+// Helper function for dumpMagnitudeVector().
+// It prints a single bar used to display raw signals.
+//
+// Since every magnitude sample is between 0-255, the function uses
+// up to 63 characters for every bar. Every character represents
+// a length of 4, 3, 2, 1, specifically:
+//
+// "O" is 4
+// "o" is 3
+// "-" is 2
+// "." is 1
+//
+void dumpMagnitudeBar(int index, int magnitude) {
+ char *set = " .-o";
+ char buf[256];
+ int div = magnitude / 256 / 4;
+ int rem = magnitude / 256 % 4;
+
+ memset(buf,'O',div);
+ buf[div] = set[rem];
+ buf[div+1] = '\0';
+
+ if (index >= 0)
+ printf("[%.3d] |%-66s 0x%04X\n", index, buf, magnitude);
+ else
+ printf("[%.2d] |%-66s 0x%04X\n", index, buf, magnitude);
+}
+//
+//=========================================================================
+//
+// Display an ASCII-art alike graphical representation of the undecoded
+// message as a magnitude signal.
+//
+// The message starts at the specified offset in the "m" buffer.
+// The function will display enough data to cover a short 56 bit message.
+//
+// If possible a few samples before the start of the messsage are included
+// for context.
+//
+void dumpMagnitudeVector(uint16_t *m, uint32_t offset) {
+ uint32_t padding = 5; // Show a few samples before the actual start.
+ uint32_t start = (offset < padding) ? 0 : offset-padding;
+ uint32_t end = offset + (MODES_PREAMBLE_SAMPLES)+(MODES_SHORT_MSG_SAMPLES) - 1;
+ uint32_t j;
+
+ for (j = start; j <= end; j++) {
+ dumpMagnitudeBar(j-offset, m[j]);
+ }
+}
+//
+//=========================================================================
+//
+// Produce a raw representation of the message as a Javascript file
+// loadable by debug.html.
+//
+void dumpRawMessageJS(char *descr, unsigned char *msg,
+ uint16_t *m, uint32_t offset, int fixable, char *bitpos)
+{
+ int padding = 5; // Show a few samples before the actual start.
+ int start = offset - padding;
+ int end = offset + (MODES_PREAMBLE_SAMPLES)+(MODES_LONG_MSG_SAMPLES) - 1;
+ FILE *fp;
+ int j;
+
+ MODES_NOTUSED(fixable);
+ if ((fp = fopen("frames.js","a")) == NULL) {
+ fprintf(stderr, "Error opening frames.js: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ fprintf(fp,"frames.push({\"descr\": \"%s\", \"mag\": [", descr);
+ for (j = start; j <= end; j++) {
+ fprintf(fp,"%d", j < 0 ? 0 : m[j]);
+ if (j != end) fprintf(fp,",");
+ }
+ fprintf(fp,"], \"fix1\": %d, \"fix2\": %d, \"bits\": %d, \"hex\": \"",
+ bitpos[0], bitpos[1] , modesMessageLenByType(msg[0]>>3));
+ for (j = 0; j < MODES_LONG_MSG_BYTES; j++)
+ fprintf(fp,"\\x%02x",msg[j]);
+ fprintf(fp,"\"});\n");
+ fclose(fp);
+}
+//
+//=========================================================================
+//
+// This is a wrapper for dumpMagnitudeVector() that also show the message
+// in hex format with an additional description.
+//
+// descr is the additional message to show to describe the dump.
+// msg points to the decoded message
+// m is the original magnitude vector
+// offset is the offset where the message starts
+//
+// The function also produces the Javascript file used by debug.html to
+// display packets in a graphical format if the Javascript output was
+// enabled.
+//
+void dumpRawMessage(char *descr, unsigned char *msg, uint16_t *m, uint32_t offset) {
+ int j;
+ int msgtype = msg[0] >> 3;
+ int fixable = 0;
+ char bitpos[MODES_MAX_BITERRORS];
+
+ for (j = 0; j < MODES_MAX_BITERRORS; j++) {
+ bitpos[j] = -1;
+ }
+ if (msgtype == 17) {
+ fixable = fixBitErrors(msg, MODES_LONG_MSG_BITS, MODES_MAX_BITERRORS, bitpos);
+ }
+
+ if (Modes.debug & MODES_DEBUG_JS) {
+ dumpRawMessageJS(descr, msg, m, offset, fixable, bitpos);
+ return;
+ }
+
+ printf("\n--- %s\n ", descr);
+ for (j = 0; j < MODES_LONG_MSG_BYTES; j++) {
+ printf("%02x",msg[j]);
+ if (j == MODES_SHORT_MSG_BYTES-1) printf(" ... ");
+ }
+ printf(" (DF %d, Fixable: %d)\n", msgtype, fixable);
+ dumpMagnitudeVector(m,offset);
+ printf("---\n\n");
+}
+//
+//=========================================================================
+//
+// Code for testing the timing: run all possible 1- and 2-bit error
+// the test message by all 1-bit errors. Run the old code against
+// all of them, and new the code.
+//
+// Example measurements:
+// Timing old vs. new crc correction code:
+// Old code: 1-bit errors on 112 msgs: 3934 usecs
+// New code: 1-bit errors on 112 msgs: 104 usecs
+// Old code: 2-bit errors on 6216 msgs: 407743 usecs
+// New code: 2-bit errors on 6216 msgs: 5176 usecs
+// indicating a 37-fold resp. 78-fold improvement in speed for 1-bit resp.
+// 2-bit error.
+/*
+unsigned char tmsg0[MODES_LONG_MSG_BYTES] = {
+ // Test data: first ADS-B message from testfiles/modes1.bin
+ 0x8f, 0x4d, 0x20, 0x23, 0x58, 0x7f, 0x34, 0x5e,
+ 0x35, 0x83, 0x7e, 0x22, 0x18, 0xb2
+};
+#define NTWOBITS (MODES_LONG_MSG_BITS*(MODES_LONG_MSG_BITS-1)/2)
+unsigned char tmsg1[MODES_LONG_MSG_BITS][MODES_LONG_MSG_BYTES];
+unsigned char tmsg2[NTWOBITS][MODES_LONG_MSG_BYTES];
+// Init an array of cloned messages with all possible 1-bit errors present,
+// applied to each message at the respective position
+//
+void inittmsg1() {
+ int i, bytepos, mask;
+ for (i = 0; i < MODES_LONG_MSG_BITS; i++) {
+ bytepos = i >> 3;
+ mask = 1 << (7 - (i & 7));
+ memcpy(&tmsg1[i][0], tmsg0, MODES_LONG_MSG_BYTES);
+ tmsg1[i][bytepos] ^= mask;
+ }
+}
+
+// Run sanity check on all but first 5 messages / bits, as those bits
+// are not corrected.
+//
+void checktmsg1(FILE *out) {
+ int i, k;
+ uint32_t crc;
+ for (i = 5; i < MODES_LONG_MSG_BITS; i++) {
+ crc = modesChecksum(&tmsg1[i][0], MODES_LONG_MSG_BITS);
+ if (crc != 0) {
+ fprintf(out, "CRC not fixed for "
+ "positon %d\n", i);
+ fprintf(out, " MSG ");
+ for (k = 0; k < MODES_LONG_MSG_BYTES; k++) {
+ fprintf(out, "%02x", tmsg1[i][k]);
+ }
+ fprintf(out, "\n");
+ }
+ }
+}
+
+void inittmsg2() {
+ int i, j, n, bytepos0, bytepos1, mask0, mask1;
+ n = 0;
+ for (i = 0; i < MODES_LONG_MSG_BITS; i++) {
+ bytepos0 = i >> 3;
+ mask0 = 1 << (7 - (i & 7));
+ for (j = i+1; j < MODES_LONG_MSG_BITS; j++) {
+ bytepos1 = j >> 3;
+ mask1 = 1 << (7 - (j & 7));
+ memcpy(&tmsg2[n][0], tmsg0, MODES_LONG_MSG_BYTES);
+ tmsg2[n][bytepos0] ^= mask0;
+ tmsg2[n][bytepos1] ^= mask1;
+ n += 1;
+ }
+ }
+}
+
+long difftvusec(struct timeval *t0, struct timeval *t1) {
+ long res = 0;
+ res = t1->tv_usec-t0->tv_usec;
+ res += (t1->tv_sec-t0->tv_sec)*1000000L;
+ return res;
+}
+
+// the actual test code
+void testAndTimeBitCorrection() {
+ struct timeval starttv, endtv;
+ int i;
+ // Run timing on 1-bit errors
+ printf("Timing old vs. new crc correction code:\n");
+ inittmsg1();
+ gettimeofday(&starttv, NULL);
+ for (i = 0; i < MODES_LONG_MSG_BITS; i++) {
+ fixSingleBitErrors(&tmsg1[i][0], MODES_LONG_MSG_BITS);
+ }
+ gettimeofday(&endtv, NULL);
+ printf(" Old code: 1-bit errors on %d msgs: %ld usecs\n",
+ MODES_LONG_MSG_BITS, difftvusec(&starttv, &endtv));
+ checktmsg1(stdout);
+ // Re-init
+ inittmsg1();
+ gettimeofday(&starttv, NULL);
+ for (i = 0; i < MODES_LONG_MSG_BITS; i++) {
+ fixBitErrors(&tmsg1[i][0], MODES_LONG_MSG_BITS, MODES_MAX_BITERRORS, NULL);
+ }
+ gettimeofday(&endtv, NULL);
+ printf(" New code: 1-bit errors on %d msgs: %ld usecs\n",
+ MODES_LONG_MSG_BITS, difftvusec(&starttv, &endtv));
+ checktmsg1(stdout);
+ // Run timing on 2-bit errors
+ inittmsg2();
+ gettimeofday(&starttv, NULL);
+ for (i = 0; i < NTWOBITS; i++) {
+ fixSingleBitErrors(&tmsg2[i][0], MODES_LONG_MSG_BITS);
+ }
+ gettimeofday(&endtv, NULL);
+ printf(" Old code: 2-bit errors on %d msgs: %ld usecs\n",
+ NTWOBITS, difftvusec(&starttv, &endtv));
+ // Re-init
+ inittmsg2();
+ gettimeofday(&starttv, NULL);
+ for (i = 0; i < NTWOBITS; i++) {
+ fixBitErrors(&tmsg2[i][0], MODES_LONG_MSG_BITS, MODES_MAX_BITERRORS, NULL);
+ }
+ gettimeofday(&endtv, NULL);
+ printf(" New code: 2-bit errors on %d msgs: %ld usecs\n",
+ NTWOBITS, difftvusec(&starttv, &endtv));
+}
+*/
+//=========================================================================
+//
+// Hash the ICAO address to index our cache of MODES_ICAO_CACHE_LEN
+// elements, that is assumed to be a power of two
+//
+uint32_t ICAOCacheHashAddress(uint32_t a) {
+ // The following three rounds wil make sure that every bit affects
+ // every output bit with ~ 50% of probability.
+ a = ((a >> 16) ^ a) * 0x45d9f3b;
+ a = ((a >> 16) ^ a) * 0x45d9f3b;
+ a = ((a >> 16) ^ a);
+ return a & (MODES_ICAO_CACHE_LEN-1);
+}
+//
+//=========================================================================
+//
+// Add the specified entry to the cache of recently seen ICAO addresses.
+// Note that we also add a timestamp so that we can make sure that the
+// entry is only valid for MODES_ICAO_CACHE_TTL seconds.
+//
+void addRecentlySeenICAOAddr(uint32_t addr) {
+ uint32_t h = ICAOCacheHashAddress(addr);
+ Modes.icao_cache[h*2] = addr;
+ Modes.icao_cache[h*2+1] = (uint32_t) time(NULL);
+}
+//
+//=========================================================================
+//
+// Returns 1 if the specified ICAO address was seen in a DF format with
+// proper checksum (not xored with address) no more than * MODES_ICAO_CACHE_TTL
+// seconds ago. Otherwise returns 0.
+//
+int ICAOAddressWasRecentlySeen(uint32_t addr) {
+ uint32_t h = ICAOCacheHashAddress(addr);
+ uint32_t a = Modes.icao_cache[h*2];
+ uint32_t t = Modes.icao_cache[h*2+1];
+ uint64_t tn = time(NULL);
+
+ return ( (a) && (a == addr) && ( (tn - t) <= MODES_ICAO_CACHE_TTL) );
+}
+//
+//=========================================================================
+//
+// In the squawk (identity) field bits are interleaved as follows in
+// (message bit 20 to bit 32):
+//
+// C1-A1-C2-A2-C4-A4-ZERO-B1-D1-B2-D2-B4-D4
+//
+// So every group of three bits A, B, C, D represent an integer from 0 to 7.
+//
+// The actual meaning is just 4 octal numbers, but we convert it into a hex
+// number tha happens to represent the four octal numbers.
+//
+// For more info: http://en.wikipedia.org/wiki/Gillham_code
+//
+int decodeID13Field(int ID13Field) {
+ int hexGillham = 0;
+
+ if (ID13Field & 0x1000) {hexGillham |= 0x0010;} // Bit 12 = C1
+ if (ID13Field & 0x0800) {hexGillham |= 0x1000;} // Bit 11 = A1
+ if (ID13Field & 0x0400) {hexGillham |= 0x0020;} // Bit 10 = C2
+ if (ID13Field & 0x0200) {hexGillham |= 0x2000;} // Bit 9 = A2
+ if (ID13Field & 0x0100) {hexGillham |= 0x0040;} // Bit 8 = C4
+ if (ID13Field & 0x0080) {hexGillham |= 0x4000;} // Bit 7 = A4
+ //if (ID13Field & 0x0040) {hexGillham |= 0x0800;} // Bit 6 = X or M
+ if (ID13Field & 0x0020) {hexGillham |= 0x0100;} // Bit 5 = B1
+ if (ID13Field & 0x0010) {hexGillham |= 0x0001;} // Bit 4 = D1 or Q
+ if (ID13Field & 0x0008) {hexGillham |= 0x0200;} // Bit 3 = B2
+ if (ID13Field & 0x0004) {hexGillham |= 0x0002;} // Bit 2 = D2
+ if (ID13Field & 0x0002) {hexGillham |= 0x0400;} // Bit 1 = B4
+ if (ID13Field & 0x0001) {hexGillham |= 0x0004;} // Bit 0 = D4
+
+ return (hexGillham);
+ }
+//
+//=========================================================================
+//
+// Decode the 13 bit AC altitude field (in DF 20 and others).
+// Returns the altitude, and set 'unit' to either MODES_UNIT_METERS or MDOES_UNIT_FEETS.
+//
+int decodeAC13Field(int AC13Field, int *unit) {
+ int m_bit = AC13Field & 0x0040; // set = meters, clear = feet
+ int q_bit = AC13Field & 0x0010; // set = 25 ft encoding, clear = Gillham Mode C encoding
+
+ if (!m_bit) {
+ *unit = MODES_UNIT_FEET;
+ if (q_bit) {
+ // N is the 11 bit integer resulting from the removal of bit Q and M
+ int n = ((AC13Field & 0x1F80) >> 2) |
+ ((AC13Field & 0x0020) >> 1) |
+ (AC13Field & 0x000F);
+ // The final altitude is resulting number multiplied by 25, minus 1000.
+ return ((n * 25) - 1000);
+ } else {
+ // N is an 11 bit Gillham coded altitude
+ int n = ModeAToModeC(decodeID13Field(AC13Field));
+ if (n < -12) {n = 0;}
+
+ return (100 * n);
+ }
+ } else {
+ *unit = MODES_UNIT_METERS;
+ // TODO: Implement altitude when meter unit is selected
+ }
+ return 0;
+}
+//
+//=========================================================================
+//
+// Decode the 12 bit AC altitude field (in DF 17 and others).
+//
+int decodeAC12Field(int AC12Field, int *unit) {
+ int q_bit = AC12Field & 0x10; // Bit 48 = Q
+
+ *unit = MODES_UNIT_FEET;
+ if (q_bit) {
+ /// N is the 11 bit integer resulting from the removal of bit Q at bit 4
+ int n = ((AC12Field & 0x0FE0) >> 1) |
+ (AC12Field & 0x000F);
+ // The final altitude is the resulting number multiplied by 25, minus 1000.
+ return ((n * 25) - 1000);
+ } else {
+ // Make N a 13 bit Gillham coded altitude by inserting M=0 at bit 6
+ int n = ((AC12Field & 0x0FC0) << 1) |
+ (AC12Field & 0x003F);
+ n = ModeAToModeC(decodeID13Field(n));
+ if (n < -12) {n = 0;}
+
+ return (100 * n);
+ }
+}
+//
+//=========================================================================
+//
+// Decode the 7 bit ground movement field PWL exponential style scale
+//
+int decodeMovementField(int movement) {
+ int gspeed;
+
+ // Note : movement codes 0,125,126,127 are all invalid, but they are
+ // trapped for before this function is called.
+
+ if (movement > 123) gspeed = 199; // > 175kt
+ else if (movement > 108) gspeed = ((movement - 108) * 5) + 100;
+ else if (movement > 93) gspeed = ((movement - 93) * 2) + 70;
+ else if (movement > 38) gspeed = ((movement - 38) ) + 15;
+ else if (movement > 12) gspeed = ((movement - 11) >> 1) + 2;
+ else if (movement > 8) gspeed = ((movement - 6) >> 2) + 1;
+ else gspeed = 0;
+
+ return (gspeed);
+}
+//
+//=========================================================================
+//
+// Capability table
+char *ca_str[8] = {
+ /* 0 */ "Level 1 (Surveillance Only)",
+ /* 1 */ "Level 2 (DF0,4,5,11)",
+ /* 2 */ "Level 3 (DF0,4,5,11,20,21)",
+ /* 3 */ "Level 4 (DF0,4,5,11,20,21,24)",
+ /* 4 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on ground)",
+ /* 5 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is airborne)",
+ /* 6 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7)",
+ /* 7 */ "Level 7 ???"
+};
+
+// DF 18 Control field table.
+char *cf_str[8] = {
+ /* 0 */ "ADS-B ES/NT device with ICAO 24-bit address",
+ /* 1 */ "ADS-B ES/NT device with other address",
+ /* 2 */ "Fine format TIS-B",
+ /* 3 */ "Coarse format TIS-B",
+ /* 4 */ "TIS-B management message",
+ /* 5 */ "TIS-B relay of ADS-B message with other address",
+ /* 6 */ "ADS-B rebroadcast using DF-17 message format",
+ /* 7 */ "Reserved"
+};
+
+// Flight status table
+char *fs_str[8] = {
+ /* 0 */ "Normal, Airborne",
+ /* 1 */ "Normal, On the ground",
+ /* 2 */ "ALERT, Airborne",
+ /* 3 */ "ALERT, On the ground",
+ /* 4 */ "ALERT & Special Position Identification. Airborne or Ground",
+ /* 5 */ "Special Position Identification. Airborne or Ground",
+ /* 6 */ "Value 6 is not assigned",
+ /* 7 */ "Value 7 is not assigned"
+};
+
+// Emergency state table
+// from https://www.ll.mit.edu/mission/aviation/publications/publication-files/atc-reports/Grappel_2007_ATC-334_WW-15318.pdf
+// and 1090-DO-260B_FRAC
+char *es_str[8] = {
+ /* 0 */ "No emergency",
+ /* 1 */ "General emergency (squawk 7700)",
+ /* 2 */ "Lifeguard/Medical",
+ /* 3 */ "Minimum fuel",
+ /* 4 */ "No communications (squawk 7600)",
+ /* 5 */ "Unlawful interference (squawk 7500)",
+ /* 6 */ "Downed Aircraft",
+ /* 7 */ "Reserved"
+};
+//
+//=========================================================================
+//
+char *getMEDescription(int metype, int mesub) {
+ char *mename = "Unknown";
+
+ if (metype >= 1 && metype <= 4)
+ mename = "Aircraft Identification and Category";
+ else if (metype >= 5 && metype <= 8)
+ mename = "Surface Position";
+ else if (metype >= 9 && metype <= 18)
+ mename = "Airborne Position (Baro Altitude)";
+ else if (metype == 19 && mesub >=1 && mesub <= 4)
+ mename = "Airborne Velocity";
+ else if (metype >= 20 && metype <= 22)
+ mename = "Airborne Position (GNSS Height)";
+ else if (metype == 23 && mesub == 0)
+ mename = "Test Message";
+ else if (metype == 23 && mesub == 7)
+ mename = "Test Message -- Squawk";
+ else if (metype == 24 && mesub == 1)
+ mename = "Surface System Status";
+ else if (metype == 28 && mesub == 1)
+ mename = "Extended Squitter Aircraft Status (Emergency)";
+ else if (metype == 28 && mesub == 2)
+ mename = "Extended Squitter Aircraft Status (1090ES TCAS RA)";
+ else if (metype == 29 && (mesub == 0 || mesub == 1))
+ mename = "Target State and Status Message";
+ else if (metype == 31 && (mesub == 0 || mesub == 1))
+ mename = "Aircraft Operational Status Message";
+ return mename;
+}
+//
+//=========================================================================
+//
+// Decode a raw Mode S message demodulated as a stream of bytes by detectModeS(),
+// and split it into fields populating a modesMessage structure.
+//
+void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) {
+ char *ais_charset = "?ABCDEFGHIJKLMNOPQRSTUVWXYZ????? ???????????????0123456789??????";
+
+ // Work on our local copy
+ memcpy(mm->msg, msg, MODES_LONG_MSG_BYTES);
+ msg = mm->msg;
+
+ // Get the message type ASAP as other operations depend on this
+ mm->msgtype = msg[0] >> 3; // Downlink Format
+ mm->msgbits = modesMessageLenByType(mm->msgtype);
+ mm->crc = modesChecksum(msg, mm->msgbits);
+
+ if ((mm->crc) && (Modes.nfix_crc) && ((mm->msgtype == 17) || (mm->msgtype == 18))) {
+// if ((mm->crc) && (Modes.nfix_crc) && ((mm->msgtype == 11) || (mm->msgtype == 17))) {
+ //
+ // Fixing single bit errors in DF-11 is a bit dodgy because we have no way to
+ // know for sure if the crc is supposed to be 0 or not - it could be any value
+ // less than 80. Therefore, attempting to fix DF-11 errors can result in a
+ // multitude of possible crc solutions, only one of which is correct.
+ //
+ // We should probably perform some sanity checks on corrected DF-11's before
+ // using the results. Perhaps check the ICAO against known aircraft, and check
+ // IID against known good IID's. That's a TODO.
+ //
+ mm->correctedbits = fixBitErrors(msg, mm->msgbits, Modes.nfix_crc, mm->corrected);
+
+ // If we correct, validate ICAO addr to help filter birthday paradox solutions.
+ if (mm->correctedbits) {
+ uint32_t ulAddr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
+ if (!ICAOAddressWasRecentlySeen(ulAddr))
+ mm->correctedbits = 0;
+ }
+ }
+ //
+ // Note that most of the other computation happens *after* we fix the
+ // single/two bit errors, otherwise we would need to recompute the fields again.
+ //
+ if (mm->msgtype == 11) { // DF 11
+ mm->iid = mm->crc;
+ mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
+ mm->ca = (msg[0] & 0x07); // Responder capabilities
+
+ if ((mm->crcok = (0 == mm->crc))) {
+ // DF 11 : if crc == 0 try to populate our ICAO addresses whitelist.
+ addRecentlySeenICAOAddr(mm->addr);
+ } else if (mm->crc < 80) {
+ mm->crcok = ICAOAddressWasRecentlySeen(mm->addr);
+ if (mm->crcok) {
+ addRecentlySeenICAOAddr(mm->addr);
+ }
+ }
+
+ } else if (mm->msgtype == 17) { // DF 17
+ mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
+ mm->ca = (msg[0] & 0x07); // Responder capabilities
+
+ if ((mm->crcok = (0 == mm->crc))) {
+ // DF 17 : if crc == 0 try to populate our ICAO addresses whitelist.
+ addRecentlySeenICAOAddr(mm->addr);
+ }
+
+ } else if (mm->msgtype == 18) { // DF 18
+ mm->addr = (msg[1] << 16) | (msg[2] << 8) | (msg[3]);
+ mm->ca = (msg[0] & 0x07); // Control Field
+
+ if ((mm->crcok = (0 == mm->crc))) {
+ // DF 18 : if crc == 0 try to populate our ICAO addresses whitelist.
+ addRecentlySeenICAOAddr(mm->addr);
+ }
+
+ } else { // All other DF's
+ // Compare the checksum with the whitelist of recently seen ICAO
+ // addresses. If it matches one, then declare the message as valid
+ mm->crcok = ICAOAddressWasRecentlySeen(mm->addr = mm->crc);
+ }
+
+ // If we're checking CRC and the CRC is invalid, then we can't trust any
+ // of the data contents, so save time and give up now.
+ if ((Modes.check_crc) && (!mm->crcok) && (!mm->correctedbits)) { return;}
+
+ // Fields for DF0, DF16
+ if (mm->msgtype == 0 || mm->msgtype == 16) {
+ if (msg[0] & 0x04) { // VS Bit
+ mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG;
+ } else {
+ mm->bFlags |= MODES_ACFLAGS_AOG_VALID;
+ }
+ }
+
+ // Fields for DF11, DF17
+ if (mm->msgtype == 11 || mm->msgtype == 17) {
+ if (mm->ca == 4) {
+ mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG;
+ } else if (mm->ca == 5) {
+ mm->bFlags |= MODES_ACFLAGS_AOG_VALID;
+ }
+ }
+
+ // Fields for DF5, DF21 = Gillham encoded Squawk
+ if (mm->msgtype == 5 || mm->msgtype == 21) {
+ int ID13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF;
+ if (ID13Field) {
+ mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID;
+ mm->modeA = decodeID13Field(ID13Field);
+ }
+ }
+
+ // Fields for DF0, DF4, DF16, DF20 13 bit altitude
+ if (mm->msgtype == 0 || mm->msgtype == 4 ||
+ mm->msgtype == 16 || mm->msgtype == 20) {
+ int AC13Field = ((msg[2] << 8) | msg[3]) & 0x1FFF;
+ if (AC13Field) { // Only attempt to decode if a valid (non zero) altitude is present
+ mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID;
+ mm->altitude = decodeAC13Field(AC13Field, &mm->unit);
+ }
+ }
+
+ // Fields for DF4, DF5, DF20, DF21
+ if ((mm->msgtype == 4) || (mm->msgtype == 20) ||
+ (mm->msgtype == 5) || (mm->msgtype == 21)) {
+ mm->bFlags |= MODES_ACFLAGS_FS_VALID;
+ mm->fs = msg[0] & 7; // Flight status for DF4,5,20,21
+ if (mm->fs <= 3) {
+ mm->bFlags |= MODES_ACFLAGS_AOG_VALID;
+ if (mm->fs & 1)
+ {mm->bFlags |= MODES_ACFLAGS_AOG;}
+ }
+ }
+
+ // Fields for DF17, DF18_CF0, DF18_CF1, DF18_CF6 squitters
+ if ( (mm->msgtype == 17)
+ || ((mm->msgtype == 18) && ((mm->ca == 0) || (mm->ca == 1) || (mm->ca == 6)) )) {
+ int metype = mm->metype = msg[4] >> 3; // Extended squitter message type
+ int mesub = mm->mesub = (metype == 29 ? ((msg[4]&6)>>1) : (msg[4] & 7)); // Extended squitter message subtype
+
+ // Decode the extended squitter message
+
+ if (metype >= 1 && metype <= 4) { // Aircraft Identification and Category
+ uint32_t chars;
+ mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID;
+
+ chars = (msg[5] << 16) | (msg[6] << 8) | (msg[7]);
+ mm->flight[3] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[2] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[1] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[0] = ais_charset[chars & 0x3F];
+
+ chars = (msg[8] << 16) | (msg[9] << 8) | (msg[10]);
+ mm->flight[7] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[6] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[5] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[4] = ais_charset[chars & 0x3F];
+
+ mm->flight[8] = '\0';
+
+ } else if (metype == 19) { // Airborne Velocity Message
+
+ // Presumably airborne if we get an Airborne Velocity Message
+ mm->bFlags |= MODES_ACFLAGS_AOG_VALID;
+
+ if ( (mesub >= 1) && (mesub <= 4) ) {
+ int vert_rate = ((msg[8] & 0x07) << 6) | (msg[9] >> 2);
+ if (vert_rate) {
+ --vert_rate;
+ if (msg[8] & 0x08)
+ {vert_rate = 0 - vert_rate;}
+ mm->vert_rate = vert_rate * 64;
+ mm->bFlags |= MODES_ACFLAGS_VERTRATE_VALID;
+ }
+ }
+
+ if ((mesub == 1) || (mesub == 2)) {
+ int ew_raw = ((msg[5] & 0x03) << 8) | msg[6];
+ int ew_vel = ew_raw - 1;
+ int ns_raw = ((msg[7] & 0x7F) << 3) | (msg[8] >> 5);
+ int ns_vel = ns_raw - 1;
+
+ if (mesub == 2) { // If (supersonic) unit is 4 kts
+ ns_vel = ns_vel << 2;
+ ew_vel = ew_vel << 2;
+ }
+
+ if (ew_raw) { // Do East/West
+ mm->bFlags |= MODES_ACFLAGS_EWSPEED_VALID;
+ if (msg[5] & 0x04)
+ {ew_vel = 0 - ew_vel;}
+ mm->ew_velocity = ew_vel;
+ }
+
+ if (ns_raw) { // Do North/South
+ mm->bFlags |= MODES_ACFLAGS_NSSPEED_VALID;
+ if (msg[7] & 0x80)
+ {ns_vel = 0 - ns_vel;}
+ mm->ns_velocity = ns_vel;
+ }
+
+ if (ew_raw && ns_raw) {
+ // Compute velocity and angle from the two speed components
+ mm->bFlags |= (MODES_ACFLAGS_SPEED_VALID | MODES_ACFLAGS_HEADING_VALID | MODES_ACFLAGS_NSEWSPD_VALID);
+ mm->velocity = (int) sqrt((ns_vel * ns_vel) + (ew_vel * ew_vel));
+
+ if (mm->velocity) {
+ mm->heading = (int) (atan2(ew_vel, ns_vel) * 180.0 / M_PI);
+ // We don't want negative values but a 0-360 scale
+ if (mm->heading < 0) mm->heading += 360;
+ }
+ }
+
+ } else if (mesub == 3 || mesub == 4) {
+ int airspeed = ((msg[7] & 0x7f) << 3) | (msg[8] >> 5);
+ if (airspeed) {
+ mm->bFlags |= MODES_ACFLAGS_SPEED_VALID;
+ --airspeed;
+ if (mesub == 4) // If (supersonic) unit is 4 kts
+ {airspeed = airspeed << 2;}
+ mm->velocity = airspeed;
+ }
+
+ if (msg[5] & 0x04) {
+ mm->bFlags |= MODES_ACFLAGS_HEADING_VALID;
+ mm->heading = ((((msg[5] & 0x03) << 8) | msg[6]) * 45) >> 7;
+ }
+ }
+
+ } else if (metype >= 5 && metype <= 22) { // Position Message
+ mm->raw_latitude = ((msg[6] & 3) << 15) | (msg[7] << 7) | (msg[8] >> 1);
+ mm->raw_longitude = ((msg[8] & 1) << 16) | (msg[9] << 8) | (msg[10]);
+ mm->bFlags |= (mm->msg[6] & 0x04) ? MODES_ACFLAGS_LLODD_VALID
+ : MODES_ACFLAGS_LLEVEN_VALID;
+ if (metype >= 9) { // Airborne
+ int AC12Field = ((msg[5] << 4) | (msg[6] >> 4)) & 0x0FFF;
+ mm->bFlags |= MODES_ACFLAGS_AOG_VALID;
+ if (AC12Field) {// Only attempt to decode if a valid (non zero) altitude is present
+ mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID;
+ mm->altitude = decodeAC12Field(AC12Field, &mm->unit);
+ }
+ } else { // Ground
+ int movement = ((msg[4] << 4) | (msg[5] >> 4)) & 0x007F;
+ mm->bFlags |= MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG;
+ if ((movement) && (movement < 125)) {
+ mm->bFlags |= MODES_ACFLAGS_SPEED_VALID;
+ mm->velocity = decodeMovementField(movement);
+ }
+
+ if (msg[5] & 0x08) {
+ mm->bFlags |= MODES_ACFLAGS_HEADING_VALID;
+ mm->heading = ((((msg[5] << 4) | (msg[6] >> 4)) & 0x007F) * 45) >> 4;
+ }
+ }
+
+ } else if (metype == 23) { // Test metype squawk field
+ if (mesub == 7) { // (see 1090-WP-15-20)
+ int ID13Field = (((msg[5] << 8) | msg[6]) & 0xFFF1)>>3;
+ if (ID13Field) {
+ mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID;
+ mm->modeA = decodeID13Field(ID13Field);
+ }
+ }
+
+ } else if (metype == 24) { // Reserved for Surface System Status
+
+ } else if (metype == 28) { // Extended Squitter Aircraft Status
+ if (mesub == 1) { // Emergency status squawk field
+ int ID13Field = (((msg[5] << 8) | msg[6]) & 0x1FFF);
+ if (ID13Field) {
+ mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID;
+ mm->modeA = decodeID13Field(ID13Field);
+ }
+ }
+
+ } else if (metype == 29) { // Aircraft Trajectory Intent
+
+ } else if (metype == 30) { // Aircraft Operational Coordination
+
+ } else if (metype == 31) { // Aircraft Operational Status
+
+ } else { // Other metypes
+
+ }
+ }
+
+ // Fields for DF20, DF21 Comm-B
+ if ((mm->msgtype == 20) || (mm->msgtype == 21)){
+
+ if (msg[4] == 0x20) { // Aircraft Identification
+ uint32_t chars;
+ mm->bFlags |= MODES_ACFLAGS_CALLSIGN_VALID;
+
+ chars = (msg[5] << 16) | (msg[6] << 8) | (msg[7]);
+ mm->flight[3] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[2] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[1] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[0] = ais_charset[chars & 0x3F];
+
+ chars = (msg[8] << 16) | (msg[9] << 8) | (msg[10]);
+ mm->flight[7] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[6] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[5] = ais_charset[chars & 0x3F]; chars = chars >> 6;
+ mm->flight[4] = ais_charset[chars & 0x3F];
+
+ mm->flight[8] = '\0';
+ } else {
+ }
+ }
+}
+//
+//=========================================================================
+//
+// This function gets a decoded Mode S Message and prints it on the screen
+// in a human readable format.
+//
+void displayModesMessage(struct modesMessage *mm) {
+ int j;
+ unsigned char * pTimeStamp;
+
+ // Handle only addresses mode first.
+ if (Modes.onlyaddr) {
+ printf("%06x\n", mm->addr);
+ return; // Enough for --onlyaddr mode
+ }
+
+ // Show the raw message.
+ if (Modes.mlat && mm->timestampMsg) {
+ printf("@");
+ pTimeStamp = (unsigned char *) &mm->timestampMsg;
+ for (j=5; j>=0;j--) {
+ printf("%02X",pTimeStamp[j]);
+ }
+ } else
+ printf("*");
+
+ for (j = 0; j < mm->msgbits/8; j++) printf("%02x", mm->msg[j]);
+ printf(";\n");
+
+ if (Modes.raw) {
+ fflush(stdout); // Provide data to the reader ASAP
+ return; // Enough for --raw mode
+ }
+
+ if (mm->msgtype < 32)
+ printf("CRC: %06x (%s)\n", (int)mm->crc, mm->crcok ? "ok" : "wrong");
+
+ if (mm->correctedbits != 0)
+ printf("No. of bit errors fixed: %d\n", mm->correctedbits);
+
+ if (mm->msgtype == 0) { // DF 0
+ printf("DF 0: Short Air-Air Surveillance.\n");
+ printf(" VS : %s\n", (mm->msg[0] & 0x04) ? "Ground" : "Airborne");
+ printf(" CC : %d\n", ((mm->msg[0] & 0x02) >> 1));
+ printf(" SL : %d\n", ((mm->msg[1] & 0xE0) >> 5));
+ printf(" Altitude : %d %s\n", mm->altitude,
+ (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet");
+ printf(" ICAO Address : %06x\n", mm->addr);
+
+ } else if (mm->msgtype == 4 || mm->msgtype == 20) {
+ printf("DF %d: %s, Altitude Reply.\n", mm->msgtype,
+ (mm->msgtype == 4) ? "Surveillance" : "Comm-B");
+ printf(" Flight Status : %s\n", fs_str[mm->fs]);
+ printf(" DR : %d\n", ((mm->msg[1] >> 3) & 0x1F));
+ printf(" UM : %d\n", (((mm->msg[1] & 7) << 3) | (mm->msg[2] >> 5)));
+ printf(" Altitude : %d %s\n", mm->altitude,
+ (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet");
+ printf(" ICAO Address : %06x\n", mm->addr);
+
+ if (mm->msgtype == 20) {
+ printf(" Comm-B BDS : %x\n", mm->msg[4]);
+
+ // Decode the extended squitter message
+ if ( mm->msg[4] == 0x20) { // BDS 2,0 Aircraft identification
+ printf(" BDS 2,0 Aircraft Identification : %s\n", mm->flight);
+/*
+ } else if ( mm->msg[4] == 0x10) { // BDS 1,0 Datalink Capability report
+ printf(" BDS 1,0 Datalink Capability report\n");
+
+ } else if ( mm->msg[4] == 0x30) { // BDS 3,0 ACAS Active Resolution Advisory
+ printf(" BDS 3,0 ACAS Active Resolution Advisory\n");
+
+ } else if ((mm->msg[4] >> 3) == 28) { // BDS 6,1 Extended Squitter Emergency/Priority Status
+ printf(" BDS 6,1 Emergency/Priority Status\n");
+
+ } else if ((mm->msg[4] >> 3) == 29) { // BDS 6,2 Target State and Status
+ printf(" BDS 6,2 Target State and Status\n");
+
+ } else if ((mm->msg[4] >> 3) == 31) { // BDS 6,5 Extended Squitter Aircraft Operational Status
+ printf(" BDS 6,5 Aircraft Operational Status\n");
+*/
+ }
+ }
+
+ } else if (mm->msgtype == 5 || mm->msgtype == 21) {
+ printf("DF %d: %s, Identity Reply.\n", mm->msgtype,
+ (mm->msgtype == 5) ? "Surveillance" : "Comm-B");
+ printf(" Flight Status : %s\n", fs_str[mm->fs]);
+ printf(" DR : %d\n", ((mm->msg[1] >> 3) & 0x1F));
+ printf(" UM : %d\n", (((mm->msg[1] & 7) << 3) | (mm->msg[2] >> 5)));
+ printf(" Squawk : %04x\n", mm->modeA);
+ printf(" ICAO Address : %06x\n", mm->addr);
+
+ if (mm->msgtype == 21) {
+ printf(" Comm-B BDS : %x\n", mm->msg[4]);
+
+ // Decode the extended squitter message
+ if ( mm->msg[4] == 0x20) { // BDS 2,0 Aircraft identification
+ printf(" BDS 2,0 Aircraft Identification : %s\n", mm->flight);
+/*
+ } else if ( mm->msg[4] == 0x10) { // BDS 1,0 Datalink Capability report
+ printf(" BDS 1,0 Datalink Capability report\n");
+
+ } else if ( mm->msg[4] == 0x30) { // BDS 3,0 ACAS Active Resolution Advisory
+ printf(" BDS 3,0 ACAS Active Resolution Advisory\n");
+
+ } else if ((mm->msg[4] >> 3) == 28) { // BDS 6,1 Extended Squitter Emergency/Priority Status
+ printf(" BDS 6,1 Emergency/Priority Status\n");
+
+ } else if ((mm->msg[4] >> 3) == 29) { // BDS 6,2 Target State and Status
+ printf(" BDS 6,2 Target State and Status\n");
+
+ } else if ((mm->msg[4] >> 3) == 31) { // BDS 6,5 Extended Squitter Aircraft Operational Status
+ printf(" BDS 6,5 Aircraft Operational Status\n");
+*/
+ }
+ }
+
+ } else if (mm->msgtype == 11) { // DF 11
+ printf("DF 11: All Call Reply.\n");
+ printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]);
+ printf(" ICAO Address: %06x\n", mm->addr);
+ if (mm->iid > 16)
+ {printf(" IID : SI-%02d\n", mm->iid-16);}
+ else
+ {printf(" IID : II-%02d\n", mm->iid);}
+
+ } else if (mm->msgtype == 16) { // DF 16
+ printf("DF 16: Long Air to Air ACAS\n");
+ printf(" VS : %s\n", (mm->msg[0] & 0x04) ? "Ground" : "Airborne");
+ printf(" CC : %d\n", ((mm->msg[0] & 0x02) >> 1));
+ printf(" SL : %d\n", ((mm->msg[1] & 0xE0) >> 5));
+ printf(" Altitude : %d %s\n", mm->altitude,
+ (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet");
+ printf(" ICAO Address : %06x\n", mm->addr);
+
+ } else if (mm->msgtype == 17) { // DF 17
+ printf("DF 17: ADS-B message.\n");
+ printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]);
+ printf(" ICAO Address : %06x\n", mm->addr);
+ printf(" Extended Squitter Type: %d\n", mm->metype);
+ printf(" Extended Squitter Sub : %d\n", mm->mesub);
+ printf(" Extended Squitter Name: %s\n", getMEDescription(mm->metype, mm->mesub));
+
+ // Decode the extended squitter message
+ if (mm->metype >= 1 && mm->metype <= 4) { // Aircraft identification
+ printf(" Aircraft Type : %c%d\n", ('A' + 4 - mm->metype), mm->mesub);
+ printf(" Identification : %s\n", mm->flight);
+
+ } else if (mm->metype == 19) { // Airborne Velocity
+ if (mm->mesub == 1 || mm->mesub == 2) {
+ printf(" EW status : %s\n", (mm->bFlags & MODES_ACFLAGS_EWSPEED_VALID) ? "Valid" : "Unavailable");
+ printf(" EW velocity : %d\n", mm->ew_velocity);
+ printf(" NS status : %s\n", (mm->bFlags & MODES_ACFLAGS_NSSPEED_VALID) ? "Valid" : "Unavailable");
+ printf(" NS velocity : %d\n", mm->ns_velocity);
+ printf(" Vertical status : %s\n", (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) ? "Valid" : "Unavailable");
+ printf(" Vertical rate src : %d\n", ((mm->msg[8] >> 4) & 1));
+ printf(" Vertical rate : %d\n", mm->vert_rate);
+
+ } else if (mm->mesub == 3 || mm->mesub == 4) {
+ printf(" Heading status : %s\n", (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) ? "Valid" : "Unavailable");
+ printf(" Heading : %d\n", mm->heading);
+ printf(" Airspeed status : %s\n", (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) ? "Valid" : "Unavailable");
+ printf(" Airspeed : %d\n", mm->velocity);
+ printf(" Vertical status : %s\n", (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) ? "Valid" : "Unavailable");
+ printf(" Vertical rate src : %d\n", ((mm->msg[8] >> 4) & 1));
+ printf(" Vertical rate : %d\n", mm->vert_rate);
+
+ } else {
+ printf(" Unrecognized ME subtype: %d subtype: %d\n", mm->metype, mm->mesub);
+ }
+
+ } else if (mm->metype >= 5 && mm->metype <= 22) { // Airborne position Baro
+ printf(" F flag : %s\n", (mm->msg[6] & 0x04) ? "odd" : "even");
+ printf(" T flag : %s\n", (mm->msg[6] & 0x08) ? "UTC" : "non-UTC");
+ printf(" Altitude : %d feet\n", mm->altitude);
+ if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {
+ printf(" Latitude : %f\n", mm->fLat);
+ printf(" Longitude: %f\n", mm->fLon);
+ } else {
+ printf(" Latitude : %d (not decoded)\n", mm->raw_latitude);
+ printf(" Longitude: %d (not decoded)\n", mm->raw_longitude);
+ }
+
+ } else if (mm->metype == 28) { // Extended Squitter Aircraft Status
+ if (mm->mesub == 1) {
+ printf(" Emergency State: %s\n", es_str[(mm->msg[5] & 0xE0) >> 5]);
+ printf(" Squawk: %04x\n", mm->modeA);
+ } else {
+ printf(" Unrecognized ME subtype: %d subtype: %d\n", mm->metype, mm->mesub);
+ }
+
+ } else if (mm->metype == 23) { // Test Message
+ if (mm->mesub == 7) {
+ printf(" Squawk: %04x\n", mm->modeA);
+ } else {
+ printf(" Unrecognized ME subtype: %d subtype: %d\n", mm->metype, mm->mesub);
+ }
+ } else {
+ printf(" Unrecognized ME type: %d subtype: %d\n", mm->metype, mm->mesub);
+ }
+
+ } else if (mm->msgtype == 18) { // DF 18
+ printf("DF 18: Extended Squitter.\n");
+ printf(" Control Field : %d (%s)\n", mm->ca, cf_str[mm->ca]);
+ if ((mm->ca == 0) || (mm->ca == 1) || (mm->ca == 6)) {
+ if (mm->ca == 1) {
+ printf(" Other Address : %06x\n", mm->addr);
+ } else {
+ printf(" ICAO Address : %06x\n", mm->addr);
+ }
+ printf(" Extended Squitter Type: %d\n", mm->metype);
+ printf(" Extended Squitter Sub : %d\n", mm->mesub);
+ printf(" Extended Squitter Name: %s\n", getMEDescription(mm->metype, mm->mesub));
+
+ // Decode the extended squitter message
+ if (mm->metype >= 1 && mm->metype <= 4) { // Aircraft identification
+ printf(" Aircraft Type : %c%d\n", ('A' + 4 - mm->metype), mm->mesub);
+ printf(" Identification : %s\n", mm->flight);
+
+ } else if (mm->metype == 19) { // Airborne Velocity
+ if (mm->mesub == 1 || mm->mesub == 2) {
+ printf(" EW status : %s\n", (mm->bFlags & MODES_ACFLAGS_EWSPEED_VALID) ? "Valid" : "Unavailable");
+ printf(" EW velocity : %d\n", mm->ew_velocity);
+ printf(" NS status : %s\n", (mm->bFlags & MODES_ACFLAGS_NSSPEED_VALID) ? "Valid" : "Unavailable");
+ printf(" NS velocity : %d\n", mm->ns_velocity);
+ printf(" Vertical status : %s\n", (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) ? "Valid" : "Unavailable");
+ printf(" Vertical rate src : %d\n", ((mm->msg[8] >> 4) & 1));
+ printf(" Vertical rate : %d\n", mm->vert_rate);
+
+ } else if (mm->mesub == 3 || mm->mesub == 4) {
+ printf(" Heading status : %s\n", (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) ? "Valid" : "Unavailable");
+ printf(" Heading : %d\n", mm->heading);
+ printf(" Airspeed status : %s\n", (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) ? "Valid" : "Unavailable");
+ printf(" Airspeed : %d\n", mm->velocity);
+ printf(" Vertical status : %s\n", (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) ? "Valid" : "Unavailable");
+ printf(" Vertical rate src : %d\n", ((mm->msg[8] >> 4) & 1));
+ printf(" Vertical rate : %d\n", mm->vert_rate);
+
+ } else {
+ printf(" Unrecognized ME subtype: %d subtype: %d\n", mm->metype, mm->mesub);
+ }
+
+ } else if (mm->metype >= 5 && mm->metype <= 22) { // Ground or Airborne position, Baro or GNSS
+ printf(" F flag : %s\n", (mm->msg[6] & 0x04) ? "odd" : "even");
+ printf(" T flag : %s\n", (mm->msg[6] & 0x08) ? "UTC" : "non-UTC");
+ printf(" Altitude : %d feet\n", mm->altitude);
+ if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {
+ printf(" Latitude : %f\n", mm->fLat);
+ printf(" Longitude: %f\n", mm->fLon);
+ } else {
+ printf(" Latitude : %d (not decoded)\n", mm->raw_latitude);
+ printf(" Longitude: %d (not decoded)\n", mm->raw_longitude);
+ }
+
+ } else {
+ printf(" Unrecognized ME type: %d subtype: %d\n", mm->metype, mm->mesub);
+ }
+ }
+
+ } else if (mm->msgtype == 19) { // DF 19
+ printf("DF 19: Military Extended Squitter.\n");
+
+ } else if (mm->msgtype == 22) { // DF 22
+ printf("DF 22: Military Use.\n");
+
+ } else if (mm->msgtype == 24) { // DF 24
+ printf("DF 24: Comm D Extended Length Message.\n");
+
+ } else if (mm->msgtype == 32) { // DF 32 is special code we use for Mode A/C
+ printf("SSR : Mode A/C Reply.\n");
+ if (mm->fs & 0x0080) {
+ printf(" Mode A : %04x IDENT\n", mm->modeA);
+ } else {
+ printf(" Mode A : %04x\n", mm->modeA);
+ if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID)
+ {printf(" Mode C : %d feet\n", mm->altitude);}
+ }
+
+ } else {
+ printf("DF %d: Unknown DF Format.\n", mm->msgtype);
+ }
+
+ printf("\n");
+}
+//
+//=========================================================================
+//
+// Turn I/Q samples pointed by Modes.data into the magnitude vector
+// pointed by Modes.magnitude.
+//
+void computeMagnitudeVector(uint16_t *p) {
+ uint16_t *m = &Modes.magnitude[MODES_PREAMBLE_SAMPLES+MODES_LONG_MSG_SAMPLES];
+ uint32_t j;
+
+ memcpy(Modes.magnitude,&Modes.magnitude[MODES_ASYNC_BUF_SAMPLES], MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE);
+
+ // Compute the magnitudo vector. It's just SQRT(I^2 + Q^2), but
+ // we rescale to the 0-255 range to exploit the full resolution.
+ for (j = 0; j < MODES_ASYNC_BUF_SAMPLES; j ++) {
+ *m++ = Modes.maglut[*p++];
+ }
+}
+//
+//=========================================================================
+//
+// Return -1 if the message is out of fase left-side
+// Return 1 if the message is out of fase right-size
+// Return 0 if the message is not particularly out of phase.
+//
+// Note: this function will access pPreamble[-1], so the caller should make sure to
+// call it only if we are not at the start of the current buffer
+//
+int detectOutOfPhase(uint16_t *pPreamble) {
+ if (pPreamble[ 3] > pPreamble[2]/3) return 1;
+ if (pPreamble[10] > pPreamble[9]/3) return 1;
+ if (pPreamble[ 6] > pPreamble[7]/3) return -1;
+ if (pPreamble[-1] > pPreamble[1]/3) return -1;
+ return 0;
+}
+//
+//=========================================================================
+//
+// This function does not really correct the phase of the message, it just
+// applies a transformation to the first sample representing a given bit:
+//
+// If the previous bit was one, we amplify it a bit.
+// If the previous bit was zero, we decrease it a bit.
+//
+// This simple transformation makes the message a bit more likely to be
+// correctly decoded for out of phase messages:
+//
+// When messages are out of phase there is more uncertainty in
+// sequences of the same bit multiple times, since 11111 will be
+// transmitted as continuously altering magnitude (high, low, high, low...)
+//
+// However because the message is out of phase some part of the high
+// is mixed in the low part, so that it is hard to distinguish if it is
+// a zero or a one.
+//
+// However when the message is out of phase passing from 0 to 1 or from
+// 1 to 0 happens in a very recognizable way, for instance in the 0 -> 1
+// transition, magnitude goes low, high, high, low, and one of of the
+// two middle samples the high will be *very* high as part of the previous
+// or next high signal will be mixed there.
+//
+// Applying our simple transformation we make more likely if the current
+// bit is a zero, to detect another zero. Symmetrically if it is a one
+// it will be more likely to detect a one because of the transformation.
+// In this way similar levels will be interpreted more likely in the
+// correct way.
+//
+void applyPhaseCorrection(uint16_t *pPayload) {
+ int j;
+
+ for (j = 0; j < MODES_LONG_MSG_SAMPLES; j += 2, pPayload += 2) {
+ if (pPayload[0] > pPayload[1]) { // One
+ pPayload[2] = (pPayload[2] * 5) / 4;
+ } else { // Zero
+ pPayload[2] = (pPayload[2] * 4) / 5;
+ }
+ }
+}
+//
+//=========================================================================
+//
+// Detect a Mode S messages inside the magnitude buffer pointed by 'm' and of
+// size 'mlen' bytes. Every detected Mode S message is convert it into a
+// stream of bits and passed to the function to display it.
+//
+void detectModeS(uint16_t *m, uint32_t mlen) {
+ struct modesMessage mm;
+ unsigned char msg[MODES_LONG_MSG_BYTES], *pMsg;
+ uint16_t aux[MODES_LONG_MSG_SAMPLES];
+ uint32_t j;
+ int use_correction = 0;
+
+ memset(&mm, 0, sizeof(mm));
+
+ // The Mode S preamble is made of impulses of 0.5 microseconds at
+ // the following time offsets:
+ //
+ // 0 - 0.5 usec: first impulse.
+ // 1.0 - 1.5 usec: second impulse.
+ // 3.5 - 4 usec: third impulse.
+ // 4.5 - 5 usec: last impulse.
+ //
+ // Since we are sampling at 2 Mhz every sample in our magnitude vector
+ // is 0.5 usec, so the preamble will look like this, assuming there is
+ // an impulse at offset 0 in the array:
+ //
+ // 0 -----------------
+ // 1 -
+ // 2 ------------------
+ // 3 --
+ // 4 -
+ // 5 --
+ // 6 -
+ // 7 ------------------
+ // 8 --
+ // 9 -------------------
+ //
+ for (j = 0; j < mlen; j++) {
+ int high, i, errors, errors56, errorsTy;
+ uint16_t *pPreamble, *pPayload, *pPtr;
+ uint8_t theByte, theErrs;
+ int msglen, scanlen, sigStrength;
+
+ pPreamble = &m[j];
+ pPayload = &m[j+MODES_PREAMBLE_SAMPLES];
+
+ // Rather than clear the whole mm structure, just clear the parts which are required. The clear
+ // is required for every bit of the input stream, and we don't want to be memset-ing the whole
+ // modesMessage structure two million times per second if we don't have to..
+ mm.bFlags =
+ mm.crcok =
+ mm.correctedbits = 0;
+
+ if (!use_correction) // This is not a re-try with phase correction
+ { // so try to find a new preamble
+
+ if (Modes.mode_ac)
+ {
+ int ModeA = detectModeA(pPreamble, &mm);
+
+ if (ModeA) // We have found a valid ModeA/C in the data
+ {
+ mm.timestampMsg = Modes.timestampBlk + ((j+1) * 6);
+
+ // Decode the received message
+ decodeModeAMessage(&mm, ModeA);
+
+ // Pass data to the next layer
+ useModesMessage(&mm);
+
+ j += MODEAC_MSG_SAMPLES;
+ Modes.stat_ModeAC++;
+ continue;
+ }
+ }
+
+ // First check of relations between the first 10 samples
+ // representing a valid preamble. We don't even investigate further
+ // if this simple test is not passed
+ if (!(pPreamble[0] > pPreamble[1] &&
+ pPreamble[1] < pPreamble[2] &&
+ pPreamble[2] > pPreamble[3] &&
+ pPreamble[3] < pPreamble[0] &&
+ pPreamble[4] < pPreamble[0] &&
+ pPreamble[5] < pPreamble[0] &&
+ pPreamble[6] < pPreamble[0] &&
+ pPreamble[7] > pPreamble[8] &&
+ pPreamble[8] < pPreamble[9] &&
+ pPreamble[9] > pPreamble[6]))
+ {
+ if (Modes.debug & MODES_DEBUG_NOPREAMBLE &&
+ *pPreamble > MODES_DEBUG_NOPREAMBLE_LEVEL)
+ dumpRawMessage("Unexpected ratio among first 10 samples", msg, m, j);
+ continue;
+ }
+
+ // The samples between the two spikes must be < than the average
+ // of the high spikes level. We don't test bits too near to
+ // the high levels as signals can be out of phase so part of the
+ // energy can be in the near samples
+ high = (pPreamble[0] + pPreamble[2] + pPreamble[7] + pPreamble[9]) / 6;
+ if (pPreamble[4] >= high ||
+ pPreamble[5] >= high)
+ {
+ if (Modes.debug & MODES_DEBUG_NOPREAMBLE &&
+ *pPreamble > MODES_DEBUG_NOPREAMBLE_LEVEL)
+ dumpRawMessage("Too high level in samples between 3 and 6", msg, m, j);
+ continue;
+ }
+
+ // Similarly samples in the range 11-14 must be low, as it is the
+ // space between the preamble and real data. Again we don't test
+ // bits too near to high levels, see above
+ if (pPreamble[11] >= high ||
+ pPreamble[12] >= high ||
+ pPreamble[13] >= high ||
+ pPreamble[14] >= high)
+ {
+ if (Modes.debug & MODES_DEBUG_NOPREAMBLE &&
+ *pPreamble > MODES_DEBUG_NOPREAMBLE_LEVEL)
+ dumpRawMessage("Too high level in samples between 10 and 15", msg, m, j);
+ continue;
+ }
+ Modes.stat_valid_preamble++;
+ }
+
+ else {
+ // If the previous attempt with this message failed, retry using
+ // magnitude correction
+ // Make a copy of the Payload, and phase correct the copy
+ memcpy(aux, pPayload, sizeof(aux));
+ applyPhaseCorrection(aux);
+ Modes.stat_out_of_phase++;
+ pPayload = aux;
+ // TODO ... apply other kind of corrections
+ }
+
+ // Decode all the next 112 bits, regardless of the actual message
+ // size. We'll check the actual message type later
+ pMsg = &msg[0];
+ pPtr = pPayload;
+ theByte = 0;
+ theErrs = 0; errorsTy = 0;
+ errors = 0; errors56 = 0;
+
+ // We should have 4 'bits' of 0/1 and 1/0 samples in the preamble,
+ // so include these in the signal strength
+ sigStrength = (pPreamble[0]-pPreamble[1])
+ + (pPreamble[2]-pPreamble[3])
+ + (pPreamble[7]-pPreamble[6])
+ + (pPreamble[9]-pPreamble[8]);
+
+ msglen = scanlen = MODES_LONG_MSG_BITS;
+ for (i = 0; i < scanlen; i++) {
+ uint32_t a = *pPtr++;
+ uint32_t b = *pPtr++;
+
+ if (a > b)
+ {theByte |= 1; if (i < 56) {sigStrength += (a-b);}}
+ else if (a < b)
+ {/*theByte |= 0;*/ if (i < 56) {sigStrength += (b-a);}}
+ else if (i >= MODES_SHORT_MSG_BITS) //(a == b), and we're in the long part of a frame
+ {errors++; /*theByte |= 0;*/}
+ else if (i >= 5) //(a == b), and we're in the short part of a frame
+ {scanlen = MODES_LONG_MSG_BITS; errors56 = ++errors;/*theByte |= 0;*/}
+ else if (i) //(a == b), and we're in the message type part of a frame
+ {errorsTy = errors56 = ++errors; theErrs |= 1; /*theByte |= 0;*/}
+ else //(a == b), and we're in the first bit of the message type part of a frame
+ {errorsTy = errors56 = ++errors; theErrs |= 1; theByte |= 1;}
+
+ if ((i & 7) == 7)
+ {*pMsg++ = theByte;}
+ else if (i == 4) {
+ msglen = modesMessageLenByType(theByte);
+ if (errors == 0)
+ {scanlen = msglen;}
+ }
+
+ theByte = theByte << 1;
+ if (i < 7)
+ {theErrs = theErrs << 1;}
+
+ // If we've exceeded the permissible number of encoding errors, abandon ship now
+ if (errors > MODES_MSG_ENCODER_ERRS) {
+
+ if (i < MODES_SHORT_MSG_BITS) {
+ msglen = 0;
+
+ } else if ((errorsTy == 1) && (theErrs == 0x80)) {
+ // If we only saw one error in the first bit of the byte of the frame, then it's possible
+ // we guessed wrongly about the value of the bit. We may be able to correct it by guessing
+ // the other way.
+ //
+ // We guessed a '1' at bit 7, which is the DF length bit == 112 Bits.
+ // Inverting bit 7 will change the message type from a long to a short.
+ // Invert the bit, cross your fingers and carry on.
+ msglen = MODES_SHORT_MSG_BITS;
+ msg[0] ^= theErrs; errorsTy = 0;
+ errors = errors56; // revert to the number of errors prior to bit 56
+ Modes.stat_DF_Len_Corrected++;
+
+ } else if (i < MODES_LONG_MSG_BITS) {
+ msglen = MODES_SHORT_MSG_BITS;
+ errors = errors56;
+
+ } else {
+ msglen = MODES_LONG_MSG_BITS;
+ }
+
+ break;
+ }
+ }
+
+ // Ensure msglen is consistent with the DF type
+ i = modesMessageLenByType(msg[0] >> 3);
+ if (msglen > i) {msglen = i;}
+ else if (msglen < i) {msglen = 0;}
+
+ //
+ // If we guessed at any of the bits in the DF type field, then look to see if our guess was sensible.
+ // Do this by looking to see if the original guess results in the DF type being one of the ICAO defined
+ // message types. If it isn't then toggle the guessed bit and see if this new value is ICAO defined.
+ // if the new value is ICAO defined, then update it in our message.
+ if ((msglen) && (errorsTy == 1) && (theErrs & 0x78)) {
+ // We guessed at one (and only one) of the message type bits. See if our guess is "likely"
+ // to be correct by comparing the DF against a list of known good DF's
+ int thisDF = ((theByte = msg[0]) >> 3) & 0x1f;
+ uint32_t validDFbits = 0x017F0831; // One bit per 32 possible DF's. Set bits 0,4,5,11,16.17.18.19,20,21,22,24
+ uint32_t thisDFbit = (1 << thisDF);
+ if (0 == (validDFbits & thisDFbit)) {
+ // The current DF is not ICAO defined, so is probably an errors.
+ // Toggle the bit we guessed at and see if the resultant DF is more likely
+ theByte ^= theErrs;
+ thisDF = (theByte >> 3) & 0x1f;
+ thisDFbit = (1 << thisDF);
+ // if this DF any more likely?
+ if (validDFbits & thisDFbit) {
+ // Yep, more likely, so update the main message
+ msg[0] = theByte;
+ Modes.stat_DF_Type_Corrected++;
+ errors--; // decrease the error count so we attempt to use the modified DF.
+ }
+ }
+ }
+
+ // We measured signal strength over the first 56 bits. Don't forget to add 4
+ // for the preamble samples, so round up and divide by 60.
+ sigStrength = (sigStrength + 29) / 60;
+
+ // When we reach this point, if error is small, and the signal strength is large enough
+ // we may have a Mode S message on our hands. It may still be broken and the CRC may not
+ // be correct, but this can be handled by the next layer.
+ if ( (msglen)
+ && (sigStrength > MODES_MSG_SQUELCH_LEVEL)
+ && (errors <= MODES_MSG_ENCODER_ERRS) ) {
+
+ // Set initial mm structure details
+ mm.timestampMsg = Modes.timestampBlk + (j*6);
+ sigStrength = (sigStrength + 0x7F) >> 8;
+ mm.signalLevel = ((sigStrength < 255) ? sigStrength : 255);
+ mm.phase_corrected = use_correction;
+
+ // Decode the received message
+ decodeModesMessage(&mm, msg);
+
+ // Update statistics
+ if (Modes.stats) {
+ if (mm.crcok || use_correction || mm.correctedbits) {
+
+ if (use_correction) {
+ switch (errors) {
+ case 0: {Modes.stat_ph_demodulated0++; break;}
+ case 1: {Modes.stat_ph_demodulated1++; break;}
+ case 2: {Modes.stat_ph_demodulated2++; break;}
+ default:{Modes.stat_ph_demodulated3++; break;}
+ }
+ } else {
+ switch (errors) {
+ case 0: {Modes.stat_demodulated0++; break;}
+ case 1: {Modes.stat_demodulated1++; break;}
+ case 2: {Modes.stat_demodulated2++; break;}
+ default:{Modes.stat_demodulated3++; break;}
+ }
+ }
+
+ if (mm.correctedbits == 0) {
+ if (use_correction) {
+ if (mm.crcok) {Modes.stat_ph_goodcrc++;}
+ else {Modes.stat_ph_badcrc++;}
+ } else {
+ if (mm.crcok) {Modes.stat_goodcrc++;}
+ else {Modes.stat_badcrc++;}
+ }
+
+ } else if (use_correction) {
+ Modes.stat_ph_badcrc++;
+ Modes.stat_ph_fixed++;
+ if ( (mm.correctedbits)
+ && (mm.correctedbits <= MODES_MAX_BITERRORS) ) {
+ Modes.stat_ph_bit_fix[mm.correctedbits-1] += 1;
+ }
+
+ } else {
+ Modes.stat_badcrc++;
+ Modes.stat_fixed++;
+ if ( (mm.correctedbits)
+ && (mm.correctedbits <= MODES_MAX_BITERRORS) ) {
+ Modes.stat_bit_fix[mm.correctedbits-1] += 1;
+ }
+ }
+ }
+ }
+
+ // Output debug mode info if needed
+ if (use_correction) {
+ if (Modes.debug & MODES_DEBUG_DEMOD)
+ dumpRawMessage("Demodulated with 0 errors", msg, m, j);
+ else if (Modes.debug & MODES_DEBUG_BADCRC &&
+ mm.msgtype == 17 &&
+ (!mm.crcok || mm.correctedbits != 0))
+ dumpRawMessage("Decoded with bad CRC", msg, m, j);
+ else if (Modes.debug & MODES_DEBUG_GOODCRC && mm.crcok &&
+ mm.correctedbits == 0)
+ dumpRawMessage("Decoded with good CRC", msg, m, j);
+ }
+
+ // Skip this message if we are sure it's fine
+ if (mm.crcok) {
+ j += (MODES_PREAMBLE_US+msglen)*2 - 1;
+ }
+
+ // Pass data to the next layer
+ useModesMessage(&mm);
+
+ } else {
+ if (Modes.debug & MODES_DEBUG_DEMODERR && use_correction) {
+ printf("The following message has %d demod errors\n", errors);
+ dumpRawMessage("Demodulated with errors", msg, m, j);
+ }
+ }
+
+ // Retry with phase correction if enabled, necessary and possible.
+ if (Modes.phase_enhance && !mm.crcok && !mm.correctedbits && !use_correction && j && detectOutOfPhase(pPreamble)) {
+ use_correction = 1; j--;
+ } else {
+ use_correction = 0;
+ }
+ }
+
+ //Send any remaining partial raw buffers now
+ if (Modes.rawOutUsed || Modes.beastOutUsed)
+ {
+ Modes.net_output_raw_rate_count++;
+ if (Modes.net_output_raw_rate_count > Modes.net_output_raw_rate)
+ {
+ if (Modes.rawOutUsed) {
+ modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed);
+ Modes.rawOutUsed = 0;
+ }
+ if (Modes.beastOutUsed) {
+ modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed);
+ Modes.beastOutUsed = 0;
+ }
+ Modes.net_output_raw_rate_count = 0;
+ }
+ }
+ else if ( (Modes.net)
+ && (Modes.net_heartbeat_rate)
+ && ((++Modes.net_heartbeat_count) > Modes.net_heartbeat_rate) ) {
+ //
+ // We haven't received any Mode A/C/S messages for some time. To try and keep any TCP
+ // links alive, send a null frame. This will help stop any routers discarding our TCP
+ // link which will cause an un-recoverable link error if/when a real frame arrives.
+ //
+ // Fudge up a null message
+ memset(&mm, 0, sizeof(mm));
+ mm.msgbits = MODES_SHORT_MSG_BITS;
+ mm.timestampMsg = Modes.timestampBlk;
+
+ // Feed output clients
+ modesQueueOutput(&mm);
+
+ // Reset the heartbeat counter
+ Modes.net_heartbeat_count = 0;
+ }
+}
+//
+//=========================================================================
+//
+// When a new message is available, because it was decoded from the RTL device,
+// file, or received in the TCP input port, or any other way we can receive a
+// decoded message, we call this function in order to use the message.
+//
+// Basically this function passes a raw message to the upper layers for further
+// processing and visualization
+//
+void useModesMessage(struct modesMessage *mm) {
+ if ((Modes.check_crc == 0) || (mm->crcok) || (mm->correctedbits)) { // not checking, ok or fixed
+
+ // Always track aircraft
+ interactiveReceiveData(mm);
+
+ // In non-interactive non-quiet mode, display messages on standard output
+ if (!Modes.interactive && !Modes.quiet) {
+ displayModesMessage(mm);
+ }
+
+ // Feed output clients
+ if (Modes.net) {modesQueueOutput(mm);}
+
+ // Heartbeat not required whilst we're seeing real messages
+ Modes.net_heartbeat_count = 0;
+ }
+}
+//
+//=========================================================================
+//
+// Always positive MOD operation, used for CPR decoding.
+//
+int cprModFunction(int a, int b) {
+ int res = a % b;
+ if (res < 0) res += b;
+ return res;
+}
+//
+//=========================================================================
+//
+// The NL function uses the precomputed table from 1090-WP-9-14
+//
+int cprNLFunction(double lat) {
+ if (lat < 0) lat = -lat; // Table is simmetric about the equator
+ if (lat < 10.47047130) return 59;
+ if (lat < 14.82817437) return 58;
+ if (lat < 18.18626357) return 57;
+ if (lat < 21.02939493) return 56;
+ if (lat < 23.54504487) return 55;
+ if (lat < 25.82924707) return 54;
+ if (lat < 27.93898710) return 53;
+ if (lat < 29.91135686) return 52;
+ if (lat < 31.77209708) return 51;
+ if (lat < 33.53993436) return 50;
+ if (lat < 35.22899598) return 49;
+ if (lat < 36.85025108) return 48;
+ if (lat < 38.41241892) return 47;
+ if (lat < 39.92256684) return 46;
+ if (lat < 41.38651832) return 45;
+ if (lat < 42.80914012) return 44;
+ if (lat < 44.19454951) return 43;
+ if (lat < 45.54626723) return 42;
+ if (lat < 46.86733252) return 41;
+ if (lat < 48.16039128) return 40;
+ if (lat < 49.42776439) return 39;
+ if (lat < 50.67150166) return 38;
+ if (lat < 51.89342469) return 37;
+ if (lat < 53.09516153) return 36;
+ if (lat < 54.27817472) return 35;
+ if (lat < 55.44378444) return 34;
+ if (lat < 56.59318756) return 33;
+ if (lat < 57.72747354) return 32;
+ if (lat < 58.84763776) return 31;
+ if (lat < 59.95459277) return 30;
+ if (lat < 61.04917774) return 29;
+ if (lat < 62.13216659) return 28;
+ if (lat < 63.20427479) return 27;
+ if (lat < 64.26616523) return 26;
+ if (lat < 65.31845310) return 25;
+ if (lat < 66.36171008) return 24;
+ if (lat < 67.39646774) return 23;
+ if (lat < 68.42322022) return 22;
+ if (lat < 69.44242631) return 21;
+ if (lat < 70.45451075) return 20;
+ if (lat < 71.45986473) return 19;
+ if (lat < 72.45884545) return 18;
+ if (lat < 73.45177442) return 17;
+ if (lat < 74.43893416) return 16;
+ if (lat < 75.42056257) return 15;
+ if (lat < 76.39684391) return 14;
+ if (lat < 77.36789461) return 13;
+ if (lat < 78.33374083) return 12;
+ if (lat < 79.29428225) return 11;
+ if (lat < 80.24923213) return 10;
+ if (lat < 81.19801349) return 9;
+ if (lat < 82.13956981) return 8;
+ if (lat < 83.07199445) return 7;
+ if (lat < 83.99173563) return 6;
+ if (lat < 84.89166191) return 5;
+ if (lat < 85.75541621) return 4;
+ if (lat < 86.53536998) return 3;
+ if (lat < 87.00000000) return 2;
+ else return 1;
+}
+//
+//=========================================================================
+//
+int cprNFunction(double lat, int fflag) {
+ int nl = cprNLFunction(lat) - (fflag ? 1 : 0);
+ if (nl < 1) nl = 1;
+ return nl;
+}
+//
+//=========================================================================
+//
+double cprDlonFunction(double lat, int fflag, int surface) {
+ return (surface ? 90.0 : 360.0) / cprNFunction(lat, fflag);
+}
+//
+//=========================================================================
+//
+// This algorithm comes from:
+// http://www.lll.lu/~edward/edward/adsb/DecodingADSBposition.html.
+//
+// A few remarks:
+// 1) 131072 is 2^17 since CPR latitude and longitude are encoded in 17 bits.
+//
+int decodeCPR(struct aircraft *a, int fflag, int surface) {
+ double AirDlat0 = (surface ? 90.0 : 360.0) / 60.0;
+ double AirDlat1 = (surface ? 90.0 : 360.0) / 59.0;
+ double lat0 = a->even_cprlat;
+ double lat1 = a->odd_cprlat;
+ double lon0 = a->even_cprlon;
+ double lon1 = a->odd_cprlon;
+
+ // Compute the Latitude Index "j"
+ int j = (int) floor(((59*lat0 - 60*lat1) / 131072) + 0.5);
+ double rlat0 = AirDlat0 * (cprModFunction(j,60) + lat0 / 131072);
+ double rlat1 = AirDlat1 * (cprModFunction(j,59) + lat1 / 131072);
+
+ time_t now = time(NULL);
+ double surface_rlat = MODES_USER_LATITUDE_DFLT;
+ double surface_rlon = MODES_USER_LONGITUDE_DFLT;
+
+ if (surface) {
+ // If we're on the ground, make sure we have a (likely) valid Lat/Lon
+ if ((a->bFlags & MODES_ACFLAGS_LATLON_VALID) && (((int)(now - a->seenLatLon)) < Modes.interactive_display_ttl)) {
+ surface_rlat = a->lat;
+ surface_rlon = a->lon;
+ } else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) {
+ surface_rlat = Modes.fUserLat;
+ surface_rlon = Modes.fUserLon;
+ } else {
+ // No local reference, give up
+ return (-1);
+ }
+ rlat0 += floor(surface_rlat / 90.0) * 90.0; // Move from 1st quadrant to our quadrant
+ rlat1 += floor(surface_rlat / 90.0) * 90.0;
+ } else {
+ if (rlat0 >= 270) rlat0 -= 360;
+ if (rlat1 >= 270) rlat1 -= 360;
+ }
+
+ // Check to see that the latitude is in range: -90 .. +90
+ if (rlat0 < -90 || rlat0 > 90 || rlat1 < -90 || rlat1 > 90)
+ return (-1);
+
+ // Check that both are in the same latitude zone, or abort.
+ if (cprNLFunction(rlat0) != cprNLFunction(rlat1))
+ return (-1);
+
+ // Compute ni and the Longitude Index "m"
+ if (fflag) { // Use odd packet.
+ int ni = cprNFunction(rlat1,1);
+ int m = (int) floor((((lon0 * (cprNLFunction(rlat1)-1)) -
+ (lon1 * cprNLFunction(rlat1))) / 131072.0) + 0.5);
+ a->lon = cprDlonFunction(rlat1, 1, surface) * (cprModFunction(m, ni)+lon1/131072);
+ a->lat = rlat1;
+ } else { // Use even packet.
+ int ni = cprNFunction(rlat0,0);
+ int m = (int) floor((((lon0 * (cprNLFunction(rlat0)-1)) -
+ (lon1 * cprNLFunction(rlat0))) / 131072) + 0.5);
+ a->lon = cprDlonFunction(rlat0, 0, surface) * (cprModFunction(m, ni)+lon0/131072);
+ a->lat = rlat0;
+ }
+
+ if (surface) {
+ a->lon += floor(surface_rlon / 90.0) * 90.0; // Move from 1st quadrant to our quadrant
+ } else if (a->lon > 180) {
+ a->lon -= 360;
+ }
+
+ a->seenLatLon = a->seen;
+ a->timestampLatLon = a->timestamp;
+ a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK);
+
+ return 0;
+}
+//
+//=========================================================================
+//
+// This algorithm comes from:
+// 1090-WP29-07-Draft_CPR101 (which also defines decodeCPR() )
+//
+// There is an error in this document related to CPR relative decode.
+// Should use trunc() rather than the floor() function in Eq 38 and related for deltaZI.
+// floor() returns integer less than argument
+// trunc() returns integer closer to zero than argument.
+// Note: text of document describes trunc() functionality for deltaZI calculation
+// but the formulae use floor().
+//
+int decodeCPRrelative(struct aircraft *a, int fflag, int surface) {
+ double AirDlat;
+ double AirDlon;
+ double lat;
+ double lon;
+ double lonr, latr;
+ double rlon, rlat;
+ int j,m;
+
+ if (a->bFlags & MODES_ACFLAGS_LATLON_REL_OK) { // Ok to try aircraft relative first
+ latr = a->lat;
+ lonr = a->lon;
+ } else if (Modes.bUserFlags & MODES_USER_LATLON_VALID) { // Try ground station relative next
+ latr = Modes.fUserLat;
+ lonr = Modes.fUserLon;
+ } else {
+ return (-1); // Exit with error - can't do relative if we don't have ref.
+ }
+
+ if (fflag) { // odd
+ AirDlat = (surface ? 90.0 : 360.0) / 59.0;
+ lat = a->odd_cprlat;
+ lon = a->odd_cprlon;
+ } else { // even
+ AirDlat = (surface ? 90.0 : 360.0) / 60.0;
+ lat = a->even_cprlat;
+ lon = a->even_cprlon;
+ }
+
+ // Compute the Latitude Index "j"
+ j = (int) (floor(latr/AirDlat) +
+ trunc(0.5 + cprModFunction((int)latr, (int)AirDlat)/AirDlat - lat/131072));
+ rlat = AirDlat * (j + lat/131072);
+ if (rlat >= 270) rlat -= 360;
+
+ // Check to see that the latitude is in range: -90 .. +90
+ if (rlat < -90 || rlat > 90) {
+ a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done
+ return (-1); // Time to give up - Latitude error
+ }
+
+ // Check to see that answer is reasonable - ie no more than 1/2 cell away
+ if (fabs(rlat - a->lat) > (AirDlat/2)) {
+ a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done
+ return (-1); // Time to give up - Latitude error
+ }
+
+ // Compute the Longitude Index "m"
+ AirDlon = cprDlonFunction(rlat, fflag, surface);
+ m = (int) (floor(lonr/AirDlon) +
+ trunc(0.5 + cprModFunction((int)lonr, (int)AirDlon)/AirDlon - lon/131072));
+ rlon = AirDlon * (m + lon/131072);
+ if (rlon > 180) rlon -= 360;
+
+ // Check to see that answer is reasonable - ie no more than 1/2 cell away
+ if (fabs(rlon - a->lon) > (AirDlon/2)) {
+ a->bFlags &= ~MODES_ACFLAGS_LATLON_REL_OK; // This will cause a quick exit next time if no global has been done
+ return (-1); // Time to give up - Longitude error
+ }
+
+ a->lat = rlat;
+ a->lon = rlon;
+
+ a->seenLatLon = a->seen;
+ a->timestampLatLon = a->timestamp;
+ a->bFlags |= (MODES_ACFLAGS_LATLON_VALID | MODES_ACFLAGS_LATLON_REL_OK);
+ return (0);
+}
+//
+// ===================== Mode S detection and decoding ===================
+//
diff --git a/net_io.c b/net_io.c
new file mode 100644
index 0000000..f3a8397
--- /dev/null
+++ b/net_io.c
@@ -0,0 +1,971 @@
+// dump1090, a Mode S messages decoder for RTLSDR devices.
+//
+// Copyright (C) 2012 by Salvatore Sanfilippo <antirez at gmail.com>
+//
+// 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 "dump1090.h"
+//
+// ============================= Networking =============================
+//
+// Note: here we disregard 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 polling clients to see if
+// they have something new to share with us when reading is needed.
+//
+//=========================================================================
+//
+// Networking "stack" initialization
+//
+struct service {
+ char *descr;
+ int *socket;
+ int port;
+ int enabled;
+};
+
+struct service services[MODES_NET_SERVICES_NUM];
+
+void modesInitNet(void) {
+ int j;
+
+ struct service svc[MODES_NET_SERVICES_NUM] = {
+ {"Raw TCP output", &Modes.ros, Modes.net_output_raw_port, 1},
+ {"Raw TCP input", &Modes.ris, Modes.net_input_raw_port, 1},
+ {"Beast TCP output", &Modes.bos, Modes.net_output_beast_port, 1},
+ {"Beast TCP input", &Modes.bis, Modes.net_input_beast_port, 1},
+ {"HTTP server", &Modes.https, Modes.net_http_port, 1},
+ {"Basestation TCP output", &Modes.sbsos, Modes.net_output_sbs_port, 1}
+ };
+
+ memcpy(&services, &svc, sizeof(svc));//services = svc;
+
+ Modes.clients = NULL;
+
+#ifdef _WIN32
+ if ( (!Modes.wsaData.wVersion)
+ && (!Modes.wsaData.wHighVersion) ) {
+ // Try to start the windows socket support
+ if (WSAStartup(MAKEWORD(2,1),&Modes.wsaData) != 0)
+ {
+ fprintf(stderr, "WSAStartup returned Error\n");
+ }
+ }
+#endif
+
+ for (j = 0; j < MODES_NET_SERVICES_NUM; j++) {
+ services[j].enabled = (services[j].port != 0);
+ if (services[j].enabled) {
+ int s = anetTcpServer(Modes.aneterr, services[j].port, NULL);
+ if (s == -1) {
+ fprintf(stderr, "Error opening the listening port %d (%s): %s\n",
+ services[j].port, services[j].descr, Modes.aneterr);
+ exit(1);
+ }
+ anetNonBlock(Modes.aneterr, s);
+ *services[j].socket = s;
+ } else {
+ if (Modes.debug & MODES_DEBUG_NET) printf("%s port is disabled\n", services[j].descr);
+ }
+ }
+
+#ifndef _WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+}
+//
+//=========================================================================
+//
+// 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
+//
+struct client * modesAcceptClients(void) {
+ int fd, port;
+ unsigned int j;
+ struct client *c;
+
+ for (j = 0; j < MODES_NET_SERVICES_NUM; j++) {
+ if (services[j].enabled) {
+ fd = anetTcpAccept(Modes.aneterr, *services[j].socket, NULL, &port);
+ if (fd == -1) continue;
+
+ anetNonBlock(Modes.aneterr, fd);
+ c = (struct client *) malloc(sizeof(*c));
+ c->service = *services[j].socket;
+ c->next = Modes.clients;
+ c->fd = fd;
+ c->buflen = 0;
+ Modes.clients = c;
+ anetSetSendBuffer(Modes.aneterr,fd, (MODES_NET_SNDBUF_SIZE << Modes.net_sndbuf_size));
+
+ if (*services[j].socket == Modes.sbsos) Modes.stat_sbs_connections++;
+ if (*services[j].socket == Modes.ros) Modes.stat_raw_connections++;
+ if (*services[j].socket == Modes.bos) Modes.stat_beast_connections++;
+
+ j--; // Try again with the same listening port
+
+ if (Modes.debug & MODES_DEBUG_NET)
+ printf("Created new client %d\n", fd);
+ }
+ }
+ return Modes.clients;
+}
+//
+//=========================================================================
+//
+// On error free the client, collect the structure, adjust maxfd if needed.
+//
+void modesFreeClient(struct client *c) {
+
+ // Unhook this client from the linked list of clients
+ struct client *p = Modes.clients;
+ if (p) {
+ if (p == c) {
+ Modes.clients = c->next;
+ } else {
+ while ((p) && (p->next != c)) {
+ p = p->next;
+ }
+ if (p) {
+ p->next = c->next;
+ }
+ }
+ }
+
+ // It's now safe to remove this client
+ close(c->fd);
+ if (c->service == Modes.sbsos) {
+ if (Modes.stat_sbs_connections) Modes.stat_sbs_connections--;
+ } else if (c->service == Modes.ros) {
+ if (Modes.stat_raw_connections) Modes.stat_raw_connections--;
+ } else if (c->service == Modes.bos) {
+ if (Modes.stat_beast_connections) Modes.stat_beast_connections--;
+ }
+
+ if (Modes.debug & MODES_DEBUG_NET)
+ printf("Closing client %d\n", c->fd);
+
+ free(c);
+}
+//
+//=========================================================================
+//
+// Send the specified message to all clients listening for a given service
+//
+void modesSendAllClients(int service, void *msg, int len) {
+ struct client *c = Modes.clients;
+
+ while (c) {
+ // Read next before servicing client incase the service routine deletes the client!
+ struct client *next = c->next;
+
+ if (c->service == service) {
+#ifndef _WIN32
+ int nwritten = write(c->fd, msg, len);
+#else
+ int nwritten = send(c->fd, msg, len, 0 );
+#endif
+ if (nwritten != len) {
+ modesFreeClient(c);
+ }
+ }
+ c = next;
+ }
+}
+//
+//=========================================================================
+//
+// Write raw output in Beast Binary format with Timestamp to TCP clients
+//
+void modesSendBeastOutput(struct modesMessage *mm) {
+ char *p = &Modes.beastOut[Modes.beastOutUsed];
+ int msgLen = mm->msgbits / 8;
+ char * pTimeStamp;
+ char ch;
+ int j;
+ int iOutLen = msgLen + 9; // Escape, msgtype, timestamp, sigLevel, msg
+
+ *p++ = 0x1a;
+ if (msgLen == MODES_SHORT_MSG_BYTES)
+ {*p++ = '2';}
+ else if (msgLen == MODES_LONG_MSG_BYTES)
+ {*p++ = '3';}
+ else if (msgLen == MODEAC_MSG_BYTES)
+ {*p++ = '1';}
+ else
+ {return;}
+
+ pTimeStamp = (char *) &mm->timestampMsg;
+ for (j = 5; j >= 0; j--) {
+ *p++ = (ch = pTimeStamp[j]);
+ if (0x1A == ch) {*p++ = ch; iOutLen++;}
+ }
+
+ *p++ = (ch = mm->signalLevel);
+ if (0x1A == ch) {*p++ = ch; iOutLen++;}
+
+ for (j = 0; j < msgLen; j++) {
+ *p++ = (ch = mm->msg[j]);
+ if (0x1A == ch) {*p++ = ch; iOutLen++;}
+ }
+
+ Modes.beastOutUsed += iOutLen;
+ if (Modes.beastOutUsed >= Modes.net_output_raw_size)
+ {
+ modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed);
+ Modes.beastOutUsed = 0;
+ Modes.net_output_raw_rate_count = 0;
+ }
+}
+//
+//=========================================================================
+//
+// Write raw output to TCP clients
+//
+void modesSendRawOutput(struct modesMessage *mm) {
+ char *p = &Modes.rawOut[Modes.rawOutUsed];
+ int msgLen = mm->msgbits / 8;
+ int j;
+ unsigned char * pTimeStamp;
+
+ if (Modes.mlat && mm->timestampMsg) {
+ *p++ = '@';
+ pTimeStamp = (unsigned char *) &mm->timestampMsg;
+ for (j = 5; j >= 0; j--) {
+ sprintf(p, "%02X", pTimeStamp[j]);
+ p += 2;
+ }
+ Modes.rawOutUsed += 12; // additional 12 characters for timestamp
+ } else
+ *p++ = '*';
+
+ for (j = 0; j < msgLen; j++) {
+ sprintf(p, "%02X", mm->msg[j]);
+ p += 2;
+ }
+
+ *p++ = ';';
+ *p++ = '\n';
+
+ Modes.rawOutUsed += ((msgLen*2) + 3);
+ if (Modes.rawOutUsed >= Modes.net_output_raw_size)
+ {
+ modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed);
+ Modes.rawOutUsed = 0;
+ Modes.net_output_raw_rate_count = 0;
+ }
+}
+//
+//=========================================================================
+//
+// Write SBS output to TCP clients
+// The message structure mm->bFlags tells us what has been updated by this message
+//
+void modesSendSBSOutput(struct modesMessage *mm) {
+ char msg[256], *p = msg;
+ uint32_t offset;
+ struct timeb epocTime_receive, epocTime_now;
+ struct tm stTime_receive, stTime_now;
+ int msgType;
+
+ //
+ // SBS BS style output checked against the following reference
+ // http://www.homepages.mcb.net/bones/SBS/Article/Barebones42_Socket_Data.htm - seems comprehensive
+ //
+
+ // Decide on the basic SBS Message Type
+ if ((mm->msgtype == 4) || (mm->msgtype == 20)) {
+ msgType = 5;
+ } else if ((mm->msgtype == 5) || (mm->msgtype == 21)) {
+ msgType = 6;
+ } else if ((mm->msgtype == 0) || (mm->msgtype == 16)) {
+ msgType = 7;
+ } else if (mm->msgtype == 11) {
+ msgType = 8;
+ } else if ((mm->msgtype != 17) && (mm->msgtype != 18)) {
+ return;
+ } else if ((mm->metype >= 1) && (mm->metype <= 4)) {
+ msgType = 1;
+ } else if ((mm->metype >= 5) && (mm->metype <= 8)) {
+ if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID)
+ {msgType = 2;}
+ else
+ {msgType = 7;}
+ } else if ((mm->metype >= 9) && (mm->metype <= 18)) {
+ if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID)
+ {msgType = 3;}
+ else
+ {msgType = 7;}
+ } else if (mm->metype != 19) {
+ return;
+ } else if ((mm->mesub == 1) || (mm->mesub == 2)) {
+ msgType = 4;
+ } else {
+ return;
+ }
+
+ // Fields 1 to 6 : SBS message type and ICAO address of the aircraft and some other stuff
+ p += sprintf(p, "MSG,%d,111,11111,%06X,111111,", msgType, mm->addr);
+
+ // Find current system time
+ ftime(&epocTime_now); // get the current system time & date
+ stTime_now = *localtime(&epocTime_now.time);
+
+ // Find message reception time
+ if (mm->timestampMsg && !mm->remote) { // Make sure the records' timestamp is valid before using it
+ epocTime_receive = Modes.stSystemTimeBlk; // This is the time of the start of the Block we're processing
+ offset = (int) (mm->timestampMsg - Modes.timestampBlk); // This is the time (in 12Mhz ticks) into the Block
+ offset = offset / 12000; // convert to milliseconds
+ epocTime_receive.millitm += offset; // add on the offset time to the Block start time
+ if (epocTime_receive.millitm > 999) { // if we've caused an overflow into the next second...
+ epocTime_receive.millitm -= 1000;
+ epocTime_receive.time ++; // ..correct the overflow
+ }
+ stTime_receive = *localtime(&epocTime_receive.time);
+ } else {
+ epocTime_receive = epocTime_now; // We don't have a usable reception time; use the current system time
+ stTime_receive = stTime_now;
+ }
+
+ // Fields 7 & 8 are the message reception time and date
+ p += sprintf(p, "%04d/%02d/%02d,", (stTime_receive.tm_year+1900),(stTime_receive.tm_mon+1), stTime_receive.tm_mday);
+ p += sprintf(p, "%02d:%02d:%02d.%03d,", stTime_receive.tm_hour, stTime_receive.tm_min, stTime_receive.tm_sec, epocTime_receive.millitm);
+
+ // Fields 9 & 10 are the current time and date
+ p += sprintf(p, "%04d/%02d/%02d,", (stTime_now.tm_year+1900),(stTime_now.tm_mon+1), stTime_now.tm_mday);
+ p += sprintf(p, "%02d:%02d:%02d.%03d", stTime_now.tm_hour, stTime_now.tm_min, stTime_now.tm_sec, epocTime_now.millitm);
+
+ // Field 11 is the callsign (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {p += sprintf(p, ",%s", mm->flight);}
+ else {p += sprintf(p, ",");}
+
+ // Field 12 is the altitude (if we have it) - force to zero if we're on the ground
+ if ((mm->bFlags & MODES_ACFLAGS_AOG_GROUND) == MODES_ACFLAGS_AOG_GROUND) {
+ p += sprintf(p, ",0");
+ } else if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
+ p += sprintf(p, ",%d", mm->altitude);
+ } else {
+ p += sprintf(p, ",");
+ }
+
+ // Field 13 is the ground Speed (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) {
+ p += sprintf(p, ",%d", mm->velocity);
+ } else {
+ p += sprintf(p, ",");
+ }
+
+ // Field 14 is the ground Heading (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) {
+ p += sprintf(p, ",%d", mm->heading);
+ } else {
+ p += sprintf(p, ",");
+ }
+
+ // Fields 15 and 16 are the Lat/Lon (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_LATLON_VALID) {p += sprintf(p, ",%1.5f,%1.5f", mm->fLat, mm->fLon);}
+ else {p += sprintf(p, ",,");}
+
+ // Field 17 is the VerticalRate (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {p += sprintf(p, ",%d", mm->vert_rate);}
+ else {p += sprintf(p, ",");}
+
+ // Field 18 is the Squawk (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {p += sprintf(p, ",%x", mm->modeA);}
+ else {p += sprintf(p, ",");}
+
+ // Field 19 is the Squawk Changing Alert flag (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_FS_VALID) {
+ if ((mm->fs >= 2) && (mm->fs <= 4)) {
+ p += sprintf(p, ",-1");
+ } else {
+ p += sprintf(p, ",0");
+ }
+ } else {
+ p += sprintf(p, ",");
+ }
+
+ // Field 20 is the Squawk Emergency flag (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
+ if ((mm->modeA == 0x7500) || (mm->modeA == 0x7600) || (mm->modeA == 0x7700)) {
+ p += sprintf(p, ",-1");
+ } else {
+ p += sprintf(p, ",0");
+ }
+ } else {
+ p += sprintf(p, ",");
+ }
+
+ // Field 21 is the Squawk Ident flag (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_FS_VALID) {
+ if ((mm->fs >= 4) && (mm->fs <= 5)) {
+ p += sprintf(p, ",-1");
+ } else {
+ p += sprintf(p, ",0");
+ }
+ } else {
+ p += sprintf(p, ",");
+ }
+
+ // Field 22 is the OnTheGround flag (if we have it)
+ if (mm->bFlags & MODES_ACFLAGS_AOG_VALID) {
+ if (mm->bFlags & MODES_ACFLAGS_AOG) {
+ p += sprintf(p, ",-1");
+ } else {
+ p += sprintf(p, ",0");
+ }
+ } else {
+ p += sprintf(p, ",");
+ }
+
+ p += sprintf(p, "\r\n");
+ modesSendAllClients(Modes.sbsos, msg, p-msg);
+}
+//
+//=========================================================================
+//
+void modesQueueOutput(struct modesMessage *mm) {
+ if (Modes.stat_sbs_connections) {modesSendSBSOutput(mm);}
+ if (Modes.stat_beast_connections) {modesSendBeastOutput(mm);}
+ if (Modes.stat_raw_connections) {modesSendRawOutput(mm);}
+}
+//
+//=========================================================================
+//
+// This function decodes a Beast binary format message
+//
+// The message is passed to the higher level layers, so it feeds
+// the selected screen output, the network output and so forth.
+//
+// If the message looks invalid it is silently discarded.
+//
+// The function always returns 0 (success) to the caller as there is no
+// case where we want broken messages here to close the client connection.
+//
+int decodeBinMessage(struct client *c, char *p) {
+ int msgLen = 0;
+ int j;
+ char ch;
+ char * ptr;
+ unsigned char msg[MODES_LONG_MSG_BYTES];
+ struct modesMessage mm;
+ MODES_NOTUSED(c);
+ memset(&mm, 0, sizeof(mm));
+
+ ch = *p++; /// Get the message type
+ if (0x1A == ch) {p++;}
+
+ if ((ch == '1') && (Modes.mode_ac)) { // skip ModeA/C unless user enables --modes-ac
+ msgLen = MODEAC_MSG_BYTES;
+ } else if (ch == '2') {
+ msgLen = MODES_SHORT_MSG_BYTES;
+ } else if (ch == '3') {
+ msgLen = MODES_LONG_MSG_BYTES;
+ }
+
+ if (msgLen) {
+ // Mark messages received over the internet as remote so that we don't try to
+ // pass them off as being received by this instance when forwarding them
+ mm.remote = 1;
+
+ ptr = (char*) &mm.timestampMsg;
+ for (j = 0; j < 6; j++) { // Grab the timestamp (big endian format)
+ ptr[5-j] = ch = *p++;
+ if (0x1A == ch) {p++;}
+ }
+
+ mm.signalLevel = ch = *p++; // Grab the signal level
+ if (0x1A == ch) {p++;}
+
+ for (j = 0; j < msgLen; j++) { // and the data
+ msg[j] = ch = *p++;
+ if (0x1A == ch) {p++;}
+ }
+
+ if (msgLen == MODEAC_MSG_BYTES) { // ModeA or ModeC
+ decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1]));
+ } else {
+ decodeModesMessage(&mm, msg);
+ }
+
+ useModesMessage(&mm);
+ }
+ return (0);
+}
+//
+//=========================================================================
+//
+// Turn an hex digit into its 4 bit decimal value.
+// Returns -1 if the digit is not in the 0-F range.
+//
+int hexDigitVal(int c) {
+ c = tolower(c);
+ if (c >= '0' && c <= '9') return c-'0';
+ else if (c >= 'a' && c <= 'f') return c-'a'+10;
+ else return -1;
+}
+//
+//=========================================================================
+//
+// This function decodes a string representing message in raw hex format
+// like: *8D4B969699155600E87406F5B69F; The string is null-terminated.
+//
+// The message is passed to the higher level layers, so it feeds
+// the selected screen output, the network output and so forth.
+//
+// If the message looks invalid it is silently discarded.
+//
+// The function always returns 0 (success) to the caller as there is no
+// case where we want broken messages here to close the client connection.
+//
+int decodeHexMessage(struct client *c, char *hex) {
+ int l = strlen(hex), j;
+ unsigned char msg[MODES_LONG_MSG_BYTES];
+ struct modesMessage mm;
+ MODES_NOTUSED(c);
+ memset(&mm, 0, sizeof(mm));
+
+ // Mark messages received over the internet as remote so that we don't try to
+ // pass them off as being received by this instance when forwarding them
+ mm.remote = 1;
+ mm.signalLevel = 0xFF;
+
+ // Remove spaces on the left and on the right
+ while(l && isspace(hex[l-1])) {
+ hex[l-1] = '\0'; l--;
+ }
+ while(isspace(*hex)) {
+ hex++; l--;
+ }
+
+ // Turn the message into binary.
+ // Accept *-AVR raw @-AVR/BEAST timeS+raw %-AVR timeS+raw (CRC good) <-BEAST timeS+sigL+raw
+ // and some AVR records that we can understand
+ if (hex[l-1] != ';') {return (0);} // not complete - abort
+
+ switch(hex[0]) {
+ case '<': {
+ mm.signalLevel = (hexDigitVal(hex[13])<<4) | hexDigitVal(hex[14]);
+ hex += 15; l -= 16; // Skip <, timestamp and siglevel, and ;
+ break;}
+
+ case '@': // No CRC check
+ case '%': { // CRC is OK
+ hex += 13; l -= 14; // Skip @,%, and timestamp, and ;
+ break;}
+
+ case '*':
+ case ':': {
+ hex++; l-=2; // Skip * and ;
+ break;}
+
+ default: {
+ return (0); // We don't know what this is, so abort
+ break;}
+ }
+
+ if ( (l != (MODEAC_MSG_BYTES * 2))
+ && (l != (MODES_SHORT_MSG_BYTES * 2))
+ && (l != (MODES_LONG_MSG_BYTES * 2)) )
+ {return (0);} // Too short or long message... broken
+
+ if ( (0 == Modes.mode_ac)
+ && (l == (MODEAC_MSG_BYTES * 2)) )
+ {return (0);} // Right length for ModeA/C, but not enabled
+
+ for (j = 0; j < l; j += 2) {
+ int high = hexDigitVal(hex[j]);
+ int low = hexDigitVal(hex[j+1]);
+
+ if (high == -1 || low == -1) return 0;
+ msg[j/2] = (high << 4) | low;
+ }
+
+ if (l == (MODEAC_MSG_BYTES * 2)) { // ModeA or ModeC
+ decodeModeAMessage(&mm, ((msg[0] << 8) | msg[1]));
+ } else { // Assume ModeS
+ decodeModesMessage(&mm, msg);
+ }
+
+ useModesMessage(&mm);
+ return (0);
+}
+//
+//=========================================================================
+//
+// Return a description of planes in json. No metric conversion
+//
+char *aircraftsToJson(int *len) {
+ time_t now = time(NULL);
+ struct aircraft *a = Modes.aircrafts;
+ int buflen = 1024; // The initial buffer is incremented as needed
+ char *buf = (char *) malloc(buflen), *p = buf;
+ int l;
+
+ l = snprintf(p,buflen,"[\n");
+ p += l; buflen -= l;
+ while(a) {
+ int position = 0;
+ int track = 0;
+
+ if (a->modeACflags & MODEAC_MSG_FLAG) { // skip any fudged ICAO records Mode A/C
+ a = a->next;
+ continue;
+ }
+
+ if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) {
+ position = 1;
+ }
+
+ if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) {
+ track = 1;
+ }
+
+ // No metric conversion
+ l = snprintf(p,buflen,
+ "{\"hex\":\"%06x\", \"squawk\":\"%04x\", \"flight\":\"%s\", \"lat\":%f, "
+ "\"lon\":%f, \"validposition\":%d, \"altitude\":%d, \"vert_rate\":%d,\"track\":%d, \"validtrack\":%d,"
+ "\"speed\":%d, \"messages\":%ld, \"seen\":%d},\n",
+ a->addr, a->modeA, a->flight, a->lat, a->lon, position, a->altitude, a->vert_rate, a->track, track,
+ a->speed, a->messages, (int)(now - a->seen));
+ p += l; buflen -= l;
+
+ //Resize if needed
+ if (buflen < 256) {
+ int used = p-buf;
+ buflen += 1024; // Our increment.
+ buf = (char *) realloc(buf,used+buflen);
+ p = buf+used;
+ }
+
+ a = a->next;
+ }
+
+ //Remove the final comma if any, and closes the json array.
+ if (*(p-2) == ',') {
+ *(p-2) = '\n';
+ p--;
+ buflen++;
+ }
+
+ l = snprintf(p,buflen,"]\n");
+ p += l; buflen -= l;
+
+ *len = p-buf;
+ return buf;
+}
+//
+//=========================================================================
+//
+#define MODES_CONTENT_TYPE_HTML "text/html;charset=utf-8"
+#define MODES_CONTENT_TYPE_CSS "text/css;charset=utf-8"
+#define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8"
+#define MODES_CONTENT_TYPE_JS "application/javascript;charset=utf-8"
+//
+// Get an HTTP request header and write the response to the client.
+// gain here we assume that the socket buffer is enough without doing
+// any kind of userspace buffering.
+//
+// Returns 1 on error to signal the caller the client connection should
+// be closed.
+//
+int handleHTTPRequest(struct client *c, char *p) {
+ char hdr[512];
+ int clen, hdrlen;
+ int httpver, keepalive;
+ char *url, *content;
+ char ctype[48];
+ char getFile[1024];
+ char *ext;
+
+ if (Modes.debug & MODES_DEBUG_NET)
+ printf("\nHTTP request: %s\n", c->buf);
+
+ // Minimally parse the request.
+ httpver = (strstr(p, "HTTP/1.1") != NULL) ? 11 : 10;
+ if (httpver == 10) {
+ // HTTP 1.0 defaults to close, unless otherwise specified.
+ keepalive = strstr(p, "Connection: keep-alive") != NULL;
+ } else if (httpver == 11) {
+ // HTTP 1.1 defaults to keep-alive, unless close is specified.
+ keepalive = strstr(p, "Connection: close") == NULL;
+ }
+
+ // Identify he URL.
+ p = strchr(p,' ');
+ if (!p) return 1; // There should be the method and a space
+ url = ++p; // Now this should point to the requested URL
+ p = strchr(p, ' ');
+ if (!p) return 1; // There should be a space before HTTP/
+ *p = '\0';
+
+ if (Modes.debug & MODES_DEBUG_NET) {
+ printf("\nHTTP keep alive: %d\n", keepalive);
+ printf("HTTP requested URL: %s\n\n", url);
+ }
+
+ if (strlen(url) < 2) {
+ snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file
+ } else {
+ snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url);
+ }
+
+ // Select the content to send, we have just two so far:
+ // "/" -> Our google map application.
+ // "/data.json" -> Our ajax request to update planes.
+ if (strstr(url, "/data.json")) {
+ content = aircraftsToJson(&clen);
+ //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON);
+ } else {
+ struct stat sbuf;
+ int fd = -1;
+
+ if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) {
+ content = (char *) malloc(sbuf.st_size);
+ if (read(fd, content, sbuf.st_size) == -1) {
+ snprintf(content, sbuf.st_size, "Error reading from file: %s", strerror(errno));
+ }
+ clen = sbuf.st_size;
+ } else {
+ char buf[128];
+ clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s", strerror(errno));
+ content = strdup(buf);
+ }
+
+ if (fd != -1) {
+ close(fd);
+ }
+ }
+
+ // Get file extension and content type
+ snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_HTML); // Default content type
+ ext = strrchr(getFile, '.');
+
+ if (strlen(ext) > 0) {
+ if (strstr(ext, ".json")) {
+ snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON);
+ } else if (strstr(ext, ".css")) {
+ snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_CSS);
+ } else if (strstr(ext, ".js")) {
+ snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JS);
+ }
+ }
+
+ // Create the header and send the reply
+ hdrlen = snprintf(hdr, sizeof(hdr),
+ "HTTP/1.1 200 OK\r\n"
+ "Server: Dump1090\r\n"
+ "Content-Type: %s\r\n"
+ "Connection: %s\r\n"
+ "Content-Length: %d\r\n"
+ "Cache-Control: no-cache, must-revalidate\r\n"
+ "Expires: Sat, 26 Jul 1997 05:00:00 GMT\r\n"
+ "\r\n",
+ ctype,
+ keepalive ? "keep-alive" : "close",
+ clen);
+
+ if (Modes.debug & MODES_DEBUG_NET) {
+ printf("HTTP Reply header:\n%s", hdr);
+ }
+
+ // Send header and content.
+#ifndef _WIN32
+ if ( (write(c->fd, hdr, hdrlen) != hdrlen)
+ || (write(c->fd, content, clen) != clen) ) {
+#else
+ if ( (send(c->fd, hdr, hdrlen, 0) != hdrlen)
+ || (send(c->fd, content, clen, 0) != clen) ) {
+#endif
+ free(content);
+ return 1;
+ }
+ free(content);
+ Modes.stat_http_requests++;
+ return !keepalive;
+}
+//
+//=========================================================================
+//
+// This function polls the clients using read() in order to receive new
+// messages from the net.
+//
+// The message is supposed to be separated from the next message by the
+// separator 'sep', which is a null-terminated C string.
+//
+// Every full message received is decoded and passed to the higher layers
+// calling the function's 'handler'.
+//
+// The handler returns 0 on success, or 1 to signal this function we should
+// close the connection with the client in case of non-recoverable errors.
+//
+void modesReadFromClient(struct client *c, char *sep,
+ int(*handler)(struct client *, char *)) {
+ int left;
+ int nread;
+ int fullmsg;
+ int bContinue = 1;
+ char *s, *e, *p;
+
+ while(bContinue) {
+
+ fullmsg = 0;
+ left = MODES_CLIENT_BUF_SIZE - c->buflen;
+ // If our buffer is full discard it, this is some badly formatted shit
+ if (left <= 0) {
+ c->buflen = 0;
+ left = MODES_CLIENT_BUF_SIZE;
+ // If there is garbage, read more to discard it ASAP
+ }
+#ifndef _WIN32
+ nread = read(c->fd, c->buf+c->buflen, left);
+#else
+ nread = recv(c->fd, c->buf+c->buflen, left, 0);
+ if (nread < 0) {errno = WSAGetLastError();}
+#endif
+
+ // If we didn't get all the data we asked for, then return once we've processed what we did get.
+ if (nread != left) {
+ bContinue = 0;
+ }
+#ifndef _WIN32
+ if ( (nread < 0 && errno != EAGAIN && errno != EWOULDBLOCK) || nread == 0 ) { // Error, or end of file
+#else
+ if ( (nread < 0) && (errno != EWOULDBLOCK)) { // Error, or end of file
+#endif
+ modesFreeClient(c);
+ return;
+ }
+ if (nread <= 0) {
+ break; // Serve next client
+ }
+ c->buflen += nread;
+
+ // Always null-term so we are free to use strstr() (it won't affect binary case)
+ c->buf[c->buflen] = '\0';
+
+ e = s = c->buf; // Start with the start of buffer, first message
+
+ if (c->service == Modes.bis) {
+ // This is the Beast Binary scanning case.
+ // If there is a complete message still in the buffer, there must be the separator 'sep'
+ // in the buffer, note that we full-scan the buffer at every read for simplicity.
+
+ left = c->buflen; // Length of valid search for memchr()
+ while (left && ((s = memchr(e, (char) 0x1a, left)) != NULL)) { // The first byte of buffer 'should' be 0x1a
+ s++; // skip the 0x1a
+ if (*s == '1') {
+ e = s + MODEAC_MSG_BYTES + 8; // point past remainder of message
+ } else if (*s == '2') {
+ e = s + MODES_SHORT_MSG_BYTES + 8;
+ } else if (*s == '3') {
+ e = s + MODES_LONG_MSG_BYTES + 8;
+ } else {
+ e = s; // Not a valid beast message, skip
+ left = &(c->buf[c->buflen]) - e;
+ continue;
+ }
+ // we need to be careful of double escape characters in the message body
+ for (p = s; p < e; p++) {
+ if (0x1A == *p) {
+ p++; e++;
+ if (e > &(c->buf[c->buflen])) {
+ break;
+ }
+ }
+ }
+ left = &(c->buf[c->buflen]) - e;
+ if (left < 0) { // Incomplete message in buffer
+ e = s - 1; // point back at last found 0x1a.
+ break;
+ }
+ // Have a 0x1a followed by 1, 2 or 3 - pass message less 0x1a to handler.
+ if (handler(c, s)) {
+ modesFreeClient(c);
+ return;
+ }
+ fullmsg = 1;
+ }
+ s = e; // For the buffer remainder below
+
+ } else {
+ //
+ // This is the ASCII scanning case, AVR RAW or HTTP at present
+ // If there is a complete message still in the buffer, there must be the separator 'sep'
+ // in the buffer, note that we full-scan the buffer at every read for simplicity.
+ //
+ while ((e = strstr(s, sep)) != NULL) { // end of first message if found
+ *e = '\0'; // The handler expects null terminated strings
+ if (handler(c, s)) { // Pass message to handler.
+ modesFreeClient(c); // Handler returns 1 on error to signal we .
+ return; // should close the client connection
+ }
+ s = e + strlen(sep); // Move to start of next message
+ fullmsg = 1;
+ }
+ }
+
+ if (fullmsg) { // We processed something - so
+ c->buflen = &(c->buf[c->buflen]) - s; // Update the unprocessed buffer length
+ memmove(c->buf, s, c->buflen); // Move what's remaining to the start of the buffer
+ } else { // If no message was decoded process the next client
+ break;
+ }
+ }
+}
+//
+//=========================================================================
+//
+// Read data from clients. This function actually delegates a lower-level
+// function that depends on the kind of service (raw, http, ...).
+//
+void modesReadFromClients(void) {
+
+ struct client *c = modesAcceptClients();
+
+ while (c) {
+ // Read next before servicing client incase the service routine deletes the client!
+ struct client *next = c->next;
+
+ if (c->service == Modes.ris) {
+ modesReadFromClient(c,"\n",decodeHexMessage);
+ } else if (c->service == Modes.bis) {
+ modesReadFromClient(c,"",decodeBinMessage);
+ } else if (c->service == Modes.https) {
+ modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest);
+ }
+ c = next;
+ }
+}
+//
+// =============================== Network IO ===========================
+//
diff --git a/ppup1090.c b/ppup1090.c
new file mode 100644
index 0000000..afbbe2f
--- /dev/null
+++ b/ppup1090.c
@@ -0,0 +1,261 @@
+// ppup1090, a Mode S PlanePlotter Uploader for dump1090 devices.
+//
+// Copyright (C) 2013 by Malcolm Robb <Support at ATTAvionics.com>
+//
+// 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 "coaa.h"
+#include "ppup1090.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 ppup1090InitConfig(void) {
+
+ int iErr;
+
+ // Default everything to zero/NULL
+ memset(&Modes, 0, sizeof(Modes));
+ memset(&ppup1090, 0, sizeof(ppup1090));
+
+ // Now initialise things that should not be 0/NULL to their defaults
+ Modes.check_crc = 1;
+ Modes.quiet = 1;
+ Modes.bEnableDFLogging = 1;
+ strcpy(ppup1090.net_input_beast_ipaddr,PPUP1090_NET_OUTPUT_IP_ADDRESS);
+ Modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT;
+ 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;
+
+ if ((iErr = openCOAA()))
+ {
+ fprintf(stderr, "Error 0x%X initialising uploader\n", iErr);
+ exit(1);
+ }
+}
+//
+//=========================================================================
+//
+void ppup1090Init(void) {
+
+ int iErr;
+
+ pthread_mutex_init(&Modes.pDF_mutex,NULL);
+ pthread_mutex_init(&Modes.data_mutex,NULL);
+ pthread_cond_init(&Modes.data_cond,NULL);
+
+ // 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();
+
+ // Setup the uploader - read the user paramaters from the coaa.h header file
+ coaa1090.ppIPAddr = ppup1090.net_pp_ipaddr;
+ coaa1090.fUserLat = MODES_USER_LATITUDE_DFLT;
+ coaa1090.fUserLon = MODES_USER_LONGITUDE_DFLT;
+ strcpy(coaa1090.strAuthCode,STR(USER_AUTHCODE));
+ strcpy(coaa1090.strRegNo, STR(USER_REGNO));
+ strcpy(coaa1090.strVersion, MODES_DUMP1090_VERSION);
+
+ if ((iErr = initCOAA (coaa1090)))
+ {
+ fprintf(stderr, "Error 0x%X initialising uploader\n", iErr);
+ exit(1);
+ }
+}
+//
+// ================================ Main ====================================
+//
+void showHelp(void) {
+ printf(
+"-----------------------------------------------------------------------------\n"
+"| ppup1090 RPi Uploader for COAA Planeplotter Ver : "MODES_DUMP1090_VERSION " |\n"
+"-----------------------------------------------------------------------------\n"
+ "--net-bo-ipaddr <IPv4> TCP Beast output listen IPv4 (default: 127.0.0.1)\n"
+ "--net-bo-port <port> TCP Beast output listen port (default: 30005)\n"
+ "--net-pp-ipaddr <IPv4> Plane Plotter LAN IPv4 Address (default: 0.0.0.0)\n"
+ "--quiet Disable output to stdout. Use for daemon applications\n"
+ "--help Show this help\n"
+ );
+}
+
+#ifdef _WIN32
+void showCopyright(void) {
+ uint64_t llTime = time(NULL) + 1;
+
+ printf(
+"-----------------------------------------------------------------------------\n"
+"| ppup1090 RPi Uploader for COAA Planeplotter Ver : "MODES_DUMP1090_VERSION " |\n"
+"-----------------------------------------------------------------------------\n"
+"\n"
+" Copyright (C) 2012 by Salvatore Sanfilippo <antirez at gmail.com>\n"
+" Copyright (C) 2014 by Malcolm Robb <support at attavionics.com>\n"
+"\n"
+" All rights reserved.\n"
+"\n"
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+" ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n"
+" For further details refer to <https://github.com/MalcolmRobb/dump1090>\n"
+"\n"
+ );
+
+ // delay for 1 second to give the user a chance to read the copyright
+ while (llTime >= time(NULL)) {}
+}
+#endif
+//
+//=========================================================================
+//
+int main(int argc, char **argv) {
+ int j, fd;
+ struct client *c;
+
+ // Set sane defaults
+
+ ppup1090InitConfig();
+ 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(ppup1090.net_input_beast_ipaddr, argv[++j]);
+ } else if (!strcmp(argv[j],"--net-pp-ipaddr") && more) {
+ inet_aton(argv[++j], (void *)&ppup1090.net_pp_ipaddr);
+ } else if (!strcmp(argv[j],"--quiet")) {
+ ppup1090.quiet = 1;
+ } 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);
+ }
+ }
+
+#ifdef _WIN32
+ // Try to comply with the Copyright license conditions for binary distribution
+ if (!ppup1090.quiet) {showCopyright();}
+#endif
+
+ // Initialization
+ ppup1090Init();
+
+ // 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, ppup1090.net_input_beast_ipaddr, Modes.net_input_beast_port)) == ANET_ERR) {
+ fprintf(stderr, "Failed to connect to %s:%d\n", ppup1090.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.
+
+ c = (struct client *) malloc(sizeof(*c));
+ c->next = NULL;
+ c->buflen = 0;
+ c->fd =
+ c->service =
+ Modes.bis = fd;
+ Modes.clients = c;
+
+ // Keep going till the user does something that stops us
+ while (!Modes.exit) {
+ modesReadFromClient(c,"",decodeBinMessage);
+ interactiveRemoveStaleAircrafts();
+ postCOAA ();
+ }
+
+ // The user has stopped us, so close any socket we opened
+ if (fd != ANET_ERR)
+ {close(fd);}
+
+ closeCOAA ();
+#ifndef _WIN32
+ pthread_exit(0);
+#else
+ return (0);
+#endif
+}
+//
+//=========================================================================
+//
diff --git a/ppup1090.h b/ppup1090.h
new file mode 100644
index 0000000..6d0756d
--- /dev/null
+++ b/ppup1090.h
@@ -0,0 +1,110 @@
+// ppup1090, a Mode S PlanePlotter Uploader for dump1090 devices.
+//
+// Copyright (C) 2013 by Malcolm Robb <Support at ATTAvionics.com>
+//
+// 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 __PPUP1090_H
+#define __PPUP1090_H
+
+// ============================= Include files ==========================
+
+#include "dump1090.h"
+
+#ifndef _WIN32
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <pthread.h>
+ #include <stdint.h>
+ #include <errno.h>
+ #include <unistd.h>
+ #include <math.h>
+ #include <sys/time.h>
+ #include <sys/timeb.h>
+ #include <netinet/in.h>
+ #include <netinet/tcp.h>
+ #include <arpa/inet.h>
+ #include <signal.h>
+ #include <fcntl.h>
+ #include <ctype.h>
+ #include <sys/stat.h>
+ #include "rtl-sdr.h"
+ #include "anet.h"
+ #include <netdb.h>
+#else
+ #include "winstubs.h" //Put everything Windows specific in here
+#endif
+
+// ============================= #defines ===============================
+
+#define PPUP1090_NET_OUTPUT_IP_ADDRESS "127.0.0.1"
+
+#define NOTUSED(V) ((void) V)
+
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+
+// ======================== structure declarations ========================
+
+// Program global state
+struct { // Internal state
+ int quiet;
+ // Networking
+ uint32_t net_pp_ipaddr; // IPv4 address of PP instance
+ char net_input_beast_ipaddr[32]; // IPv4 address or network name of server/RPi
+} ppup1090;
+
+
+// COAA Initialisation structure
+struct _coaa1090 {
+ uint32_t ppIPAddr;
+ double fUserLat;
+ double fUserLon;
+ char strAuthCode[16];
+ char strRegNo[16];
+ char strVersion[16];
+} coaa1090;
+
+// ======================== function declarations =========================
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Functions exported from coaa1090.c
+//
+int openCOAA (void);
+int closeCOAA (void);
+int initCOAA (struct _coaa1090 coaa1090);
+void postCOAA (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __PPUP1090_H
diff --git a/ppup1090.sh b/ppup1090.sh
new file mode 100644
index 0000000..8ed4ad3
--- /dev/null
+++ b/ppup1090.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+### BEGIN INIT INFO
+#
+# Provides: dump1090
+# Required-Start: $remote_fs
+# Required-Stop: $remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: dump1090 initscript
+
+#
+### END INIT INFO
+## Fill in name of program here.
+PROG="dump1090"
+PROG_PATH="/home/pi/dump1090"
+PROG_ARGS="--quiet --net --net-ro-size 500 --net-ro-rate 5 --net-buffer 5"
+PIDFILE="/var/run/dump1090.pid"
+PROG2="ppup1090"
+PROG2_ARGS="--quiet --net-pp-addr 192.168.1.64"
+PIDFILE2="/var/run/$PROG2.pid"
+DELAY=5
+
+start() {
+ if [ -e $PIDFILE ]; then
+ ## Program is running, exit with error.
+ echo "Error! $PROG is currently running!" 1>&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, waiting $DELAY seconds"
+ touch $PIDFILE
+ sleep $DELAY
+ echo "Attempting to start $PROG2.."
+ ./$PROG2 $PROG2_ARGS 2>1 >/dev/null &
+ echo "$PROG2 started"
+ touch $PIDFILE2
+ fi
+}
+
+stop() {
+ if [ -e $PIDFILE ]; then
+ ## Program is running, so stop it
+ echo "$PROG is running"
+ killall $PROG2
+ killall $PROG
+ rm -f $PIDFILE2
+ 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/pthreads/pthread.h b/pthreads/pthread.h
new file mode 100644
index 0000000..b4072f7
--- /dev/null
+++ b/pthreads/pthread.h
@@ -0,0 +1,1368 @@
+/* This is an implementation of the threads API of POSIX 1003.1-2001.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Pthreads-win32 - POSIX Threads Library for Win32
+ * Copyright(C) 1998 John E. Bossom
+ * Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *
+ * Contact Email: rpj at callisto.canberra.edu.au
+ *
+ * The current list of contributors is contained
+ * in the file CONTRIBUTORS included with the source
+ * code distribution. The list can also be seen at the
+ * following World Wide Web location:
+ * http://sources.redhat.com/pthreads-win32/contributors.html
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#if !defined( PTHREAD_H )
+#define PTHREAD_H
+
+/*
+ * See the README file for an explanation of the pthreads-win32 version
+ * numbering scheme and how the DLL is named etc.
+ */
+#define PTW32_VERSION 2,9,1,0
+#define PTW32_VERSION_STRING "2, 9, 1, 0\0"
+
+/* There are three implementations of cancel cleanup.
+ * Note that pthread.h is included in both application
+ * compilation units and also internally for the library.
+ * The code here and within the library aims to work
+ * for all reasonable combinations of environments.
+ *
+ * The three implementations are:
+ *
+ * WIN32 SEH
+ * C
+ * C++
+ *
+ * Please note that exiting a push/pop block via
+ * "return", "exit", "break", or "continue" will
+ * lead to different behaviour amongst applications
+ * depending upon whether the library was built
+ * using SEH, C++, or C. For example, a library built
+ * with SEH will call the cleanup routine, while both
+ * C++ and C built versions will not.
+ */
+
+/*
+ * Define defaults for cleanup code.
+ * Note: Unless the build explicitly defines one of the following, then
+ * we default to standard C style cleanup. This style uses setjmp/longjmp
+ * in the cancelation and thread exit implementations and therefore won't
+ * do stack unwinding if linked to applications that have it (e.g.
+ * C++ apps). This is currently consistent with most/all commercial Unix
+ * POSIX threads implementations.
+ */
+#if !defined( __CLEANUP_SEH ) && !defined( __CLEANUP_CXX ) && !defined( __CLEANUP_C )
+# define __CLEANUP_C
+#endif
+
+#if defined( __CLEANUP_SEH ) && ( !defined( _MSC_VER ) && !defined(PTW32_RC_MSC))
+#error ERROR [__FILE__, line __LINE__]: SEH is not supported for this compiler.
+#endif
+
+/*
+ * Stop here if we are being included by the resource compiler.
+ */
+#if !defined(RC_INVOKED)
+
+#undef PTW32_LEVEL
+
+#if defined(_POSIX_SOURCE)
+#define PTW32_LEVEL 0
+/* Early POSIX */
+#endif
+
+#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
+#undef PTW32_LEVEL
+#define PTW32_LEVEL 1
+/* Include 1b, 1c and 1d */
+#endif
+
+#if defined(INCLUDE_NP)
+#undef PTW32_LEVEL
+#define PTW32_LEVEL 2
+/* Include Non-Portable extensions */
+#endif
+
+#define PTW32_LEVEL_MAX 3
+
+#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_LEVEL)
+#define PTW32_LEVEL PTW32_LEVEL_MAX
+/* Include everything */
+#endif
+
+#if defined(_UWIN)
+# define HAVE_STRUCT_TIMESPEC 1
+# define HAVE_SIGNAL_H 1
+# undef HAVE_PTW32_CONFIG_H
+# pragma comment(lib, "pthread")
+#endif
+
+/*
+ * -------------------------------------------------------------
+ *
+ *
+ * Module: pthread.h
+ *
+ * Purpose:
+ * Provides an implementation of PThreads based upon the
+ * standard:
+ *
+ * POSIX 1003.1-2001
+ * and
+ * The Single Unix Specification version 3
+ *
+ * (these two are equivalent)
+ *
+ * in order to enhance code portability between Windows,
+ * various commercial Unix implementations, and Linux.
+ *
+ * See the ANNOUNCE file for a full list of conforming
+ * routines and defined constants, and a list of missing
+ * routines and constants not defined in this implementation.
+ *
+ * Authors:
+ * There have been many contributors to this library.
+ * The initial implementation was contributed by
+ * John Bossom, and several others have provided major
+ * sections or revisions of parts of the implementation.
+ * Often significant effort has been contributed to
+ * find and fix important bugs and other problems to
+ * improve the reliability of the library, which sometimes
+ * is not reflected in the amount of code which changed as
+ * result.
+ * As much as possible, the contributors are acknowledged
+ * in the ChangeLog file in the source code distribution
+ * where their changes are noted in detail.
+ *
+ * Contributors are listed in the CONTRIBUTORS file.
+ *
+ * As usual, all bouquets go to the contributors, and all
+ * brickbats go to the project maintainer.
+ *
+ * Maintainer:
+ * The code base for this project is coordinated and
+ * eventually pre-tested, packaged, and made available by
+ *
+ * Ross Johnson <rpj at callisto.canberra.edu.au>
+ *
+ * QA Testers:
+ * Ultimately, the library is tested in the real world by
+ * a host of competent and demanding scientists and
+ * engineers who report bugs and/or provide solutions
+ * which are then fixed or incorporated into subsequent
+ * versions of the library. Each time a bug is fixed, a
+ * test case is written to prove the fix and ensure
+ * that later changes to the code don't reintroduce the
+ * same error. The number of test cases is slowly growing
+ * and therefore so is the code reliability.
+ *
+ * Compliance:
+ * See the file ANNOUNCE for the list of implemented
+ * and not-implemented routines and defined options.
+ * Of course, these are all defined is this file as well.
+ *
+ * Web site:
+ * The source code and other information about this library
+ * are available from
+ *
+ * http://sources.redhat.com/pthreads-win32/
+ *
+ * -------------------------------------------------------------
+ */
+
+/* Try to avoid including windows.h */
+#if (defined(__MINGW64__) || defined(__MINGW32__)) && defined(__cplusplus)
+#define PTW32_INCLUDE_WINDOWS_H
+#endif
+
+#if defined(PTW32_INCLUDE_WINDOWS_H)
+#include <windows.h>
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1300 || defined(__DMC__)
+/*
+ * VC++6.0 or early compiler's header has no DWORD_PTR type.
+ */
+typedef unsigned long DWORD_PTR;
+typedef unsigned long ULONG_PTR;
+#endif
+/*
+ * -----------------
+ * autoconf switches
+ * -----------------
+ */
+
+#if defined(HAVE_PTW32_CONFIG_H)
+#include "config.h"
+#endif /* HAVE_PTW32_CONFIG_H */
+
+#if !defined(NEED_FTIME)
+#include <time.h>
+#else /* NEED_FTIME */
+/* use native WIN32 time API */
+#endif /* NEED_FTIME */
+
+#if defined(HAVE_SIGNAL_H)
+#include <signal.h>
+#endif /* HAVE_SIGNAL_H */
+
+#include <limits.h>
+
+/*
+ * Boolean values to make us independent of system includes.
+ */
+enum {
+ PTW32_FALSE = 0,
+ PTW32_TRUE = (! PTW32_FALSE)
+};
+
+/*
+ * This is a duplicate of what is in the autoconf config.h,
+ * which is only used when building the pthread-win32 libraries.
+ */
+
+#if !defined(PTW32_CONFIG_H)
+# if defined(WINCE)
+# define NEED_ERRNO
+# define NEED_SEM
+# endif
+# if defined(__MINGW64__)
+# define HAVE_STRUCT_TIMESPEC
+# define HAVE_MODE_T
+# elif defined(_UWIN) || defined(__MINGW32__)
+# define HAVE_MODE_T
+# endif
+#endif
+
+/*
+ *
+ */
+
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX
+#if defined(NEED_ERRNO)
+#include "need_errno.h"
+#else
+#include <errno.h>
+#endif
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
+
+/*
+ * Several systems don't define some error numbers.
+ */
+#if !defined(ENOTSUP)
+# define ENOTSUP 48 /* This is the value in Solaris. */
+#endif
+
+#if !defined(ETIMEDOUT)
+# define ETIMEDOUT 10060 /* Same as WSAETIMEDOUT */
+#endif
+
+#if !defined(ENOSYS)
+# define ENOSYS 140 /* Semi-arbitrary value */
+#endif
+
+#if !defined(EDEADLK)
+# if defined(EDEADLOCK)
+# define EDEADLK EDEADLOCK
+# else
+# define EDEADLK 36 /* This is the value in MSVC. */
+# endif
+#endif
+
+/* POSIX 2008 - related to robust mutexes */
+#if !defined(EOWNERDEAD)
+# define EOWNERDEAD 43
+#endif
+#if !defined(ENOTRECOVERABLE)
+# define ENOTRECOVERABLE 44
+#endif
+
+#include <sched.h>
+
+/*
+ * To avoid including windows.h we define only those things that we
+ * actually need from it.
+ */
+#if !defined(PTW32_INCLUDE_WINDOWS_H)
+#if !defined(HANDLE)
+# define PTW32__HANDLE_DEF
+# define HANDLE void *
+#endif
+#if !defined(DWORD)
+# define PTW32__DWORD_DEF
+# define DWORD unsigned long
+#endif
+#endif
+
+#if !defined(HAVE_STRUCT_TIMESPEC)
+#define HAVE_STRUCT_TIMESPEC
+#if !defined(_TIMESPEC_DEFINED)
+#define _TIMESPEC_DEFINED
+struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+#endif /* _TIMESPEC_DEFINED */
+#endif /* HAVE_STRUCT_TIMESPEC */
+
+#if !defined(SIG_BLOCK)
+#define SIG_BLOCK 0
+#endif /* SIG_BLOCK */
+
+#if !defined(SIG_UNBLOCK)
+#define SIG_UNBLOCK 1
+#endif /* SIG_UNBLOCK */
+
+#if !defined(SIG_SETMASK)
+#define SIG_SETMASK 2
+#endif /* SIG_SETMASK */
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * -------------------------------------------------------------
+ *
+ * POSIX 1003.1-2001 Options
+ * =========================
+ *
+ * Options are normally set in <unistd.h>, which is not provided
+ * with pthreads-win32.
+ *
+ * For conformance with the Single Unix Specification (version 3), all of the
+ * options below are defined, and have a value of either -1 (not supported)
+ * or 200112L (supported).
+ *
+ * These options can neither be left undefined nor have a value of 0, because
+ * either indicates that sysconf(), which is not implemented, may be used at
+ * runtime to check the status of the option.
+ *
+ * _POSIX_THREADS (== 200112L)
+ * If == 200112L, you can use threads
+ *
+ * _POSIX_THREAD_ATTR_STACKSIZE (== 200112L)
+ * If == 200112L, you can control the size of a thread's
+ * stack
+ * pthread_attr_getstacksize
+ * pthread_attr_setstacksize
+ *
+ * _POSIX_THREAD_ATTR_STACKADDR (== -1)
+ * If == 200112L, you can allocate and control a thread's
+ * stack. If not supported, the following functions
+ * will return ENOSYS, indicating they are not
+ * supported:
+ * pthread_attr_getstackaddr
+ * pthread_attr_setstackaddr
+ *
+ * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1)
+ * If == 200112L, you can use realtime scheduling.
+ * This option indicates that the behaviour of some
+ * implemented functions conforms to the additional TPS
+ * requirements in the standard. E.g. rwlocks favour
+ * writers over readers when threads have equal priority.
+ *
+ * _POSIX_THREAD_PRIO_INHERIT (== -1)
+ * If == 200112L, you can create priority inheritance
+ * mutexes.
+ * pthread_mutexattr_getprotocol +
+ * pthread_mutexattr_setprotocol +
+ *
+ * _POSIX_THREAD_PRIO_PROTECT (== -1)
+ * If == 200112L, you can create priority ceiling mutexes
+ * Indicates the availability of:
+ * pthread_mutex_getprioceiling
+ * pthread_mutex_setprioceiling
+ * pthread_mutexattr_getprioceiling
+ * pthread_mutexattr_getprotocol +
+ * pthread_mutexattr_setprioceiling
+ * pthread_mutexattr_setprotocol +
+ *
+ * _POSIX_THREAD_PROCESS_SHARED (== -1)
+ * If set, you can create mutexes and condition
+ * variables that can be shared with another
+ * process.If set, indicates the availability
+ * of:
+ * pthread_mutexattr_getpshared
+ * pthread_mutexattr_setpshared
+ * pthread_condattr_getpshared
+ * pthread_condattr_setpshared
+ *
+ * _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L)
+ * If == 200112L you can use the special *_r library
+ * functions that provide thread-safe behaviour
+ *
+ * _POSIX_READER_WRITER_LOCKS (== 200112L)
+ * If == 200112L, you can use read/write locks
+ *
+ * _POSIX_SPIN_LOCKS (== 200112L)
+ * If == 200112L, you can use spin locks
+ *
+ * _POSIX_BARRIERS (== 200112L)
+ * If == 200112L, you can use barriers
+ *
+ * + These functions provide both 'inherit' and/or
+ * 'protect' protocol, based upon these macro
+ * settings.
+ *
+ * -------------------------------------------------------------
+ */
+
+/*
+ * POSIX Options
+ */
+#undef _POSIX_THREADS
+#define _POSIX_THREADS 200809L
+
+#undef _POSIX_READER_WRITER_LOCKS
+#define _POSIX_READER_WRITER_LOCKS 200809L
+
+#undef _POSIX_SPIN_LOCKS
+#define _POSIX_SPIN_LOCKS 200809L
+
+#undef _POSIX_BARRIERS
+#define _POSIX_BARRIERS 200809L
+
+#undef _POSIX_THREAD_SAFE_FUNCTIONS
+#define _POSIX_THREAD_SAFE_FUNCTIONS 200809L
+
+#undef _POSIX_THREAD_ATTR_STACKSIZE
+#define _POSIX_THREAD_ATTR_STACKSIZE 200809L
+
+/*
+ * The following options are not supported
+ */
+#undef _POSIX_THREAD_ATTR_STACKADDR
+#define _POSIX_THREAD_ATTR_STACKADDR -1
+
+#undef _POSIX_THREAD_PRIO_INHERIT
+#define _POSIX_THREAD_PRIO_INHERIT -1
+
+#undef _POSIX_THREAD_PRIO_PROTECT
+#define _POSIX_THREAD_PRIO_PROTECT -1
+
+/* TPS is not fully supported. */
+#undef _POSIX_THREAD_PRIORITY_SCHEDULING
+#define _POSIX_THREAD_PRIORITY_SCHEDULING -1
+
+#undef _POSIX_THREAD_PROCESS_SHARED
+#define _POSIX_THREAD_PROCESS_SHARED -1
+
+
+/*
+ * POSIX 1003.1-2001 Limits
+ * ===========================
+ *
+ * These limits are normally set in <limits.h>, which is not provided with
+ * pthreads-win32.
+ *
+ * PTHREAD_DESTRUCTOR_ITERATIONS
+ * Maximum number of attempts to destroy
+ * a thread's thread-specific data on
+ * termination (must be at least 4)
+ *
+ * PTHREAD_KEYS_MAX
+ * Maximum number of thread-specific data keys
+ * available per process (must be at least 128)
+ *
+ * PTHREAD_STACK_MIN
+ * Minimum supported stack size for a thread
+ *
+ * PTHREAD_THREADS_MAX
+ * Maximum number of threads supported per
+ * process (must be at least 64).
+ *
+ * SEM_NSEMS_MAX
+ * The maximum number of semaphores a process can have.
+ * (must be at least 256)
+ *
+ * SEM_VALUE_MAX
+ * The maximum value a semaphore can have.
+ * (must be at least 32767)
+ *
+ */
+#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS
+#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4
+
+#undef PTHREAD_DESTRUCTOR_ITERATIONS
+#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS
+
+#undef _POSIX_THREAD_KEYS_MAX
+#define _POSIX_THREAD_KEYS_MAX 128
+
+#undef PTHREAD_KEYS_MAX
+#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX
+
+#undef PTHREAD_STACK_MIN
+#define PTHREAD_STACK_MIN 0
+
+#undef _POSIX_THREAD_THREADS_MAX
+#define _POSIX_THREAD_THREADS_MAX 64
+
+ /* Arbitrary value */
+#undef PTHREAD_THREADS_MAX
+#define PTHREAD_THREADS_MAX 2019
+
+#undef _POSIX_SEM_NSEMS_MAX
+#define _POSIX_SEM_NSEMS_MAX 256
+
+ /* Arbitrary value */
+#undef SEM_NSEMS_MAX
+#define SEM_NSEMS_MAX 1024
+
+#undef _POSIX_SEM_VALUE_MAX
+#define _POSIX_SEM_VALUE_MAX 32767
+
+#undef SEM_VALUE_MAX
+#define SEM_VALUE_MAX INT_MAX
+
+
+#if defined(__GNUC__) && !defined(__declspec)
+# error Please upgrade your GNU compiler to one that supports __declspec.
+#endif
+
+/*
+ * When building the library, you should define PTW32_BUILD so that
+ * the variables/functions are exported correctly. When using the library,
+ * do NOT define PTW32_BUILD, and then the variables/functions will
+ * be imported correctly.
+ */
+#if !defined(PTW32_STATIC_LIB)
+# if defined(PTW32_BUILD)
+# define PTW32_DLLPORT __declspec (dllexport)
+# else
+# define PTW32_DLLPORT __declspec (dllimport)
+# endif
+#else
+# define PTW32_DLLPORT
+#endif
+
+/*
+ * The Open Watcom C/C++ compiler uses a non-standard calling convention
+ * that passes function args in registers unless __cdecl is explicitly specified
+ * in exposed function prototypes.
+ *
+ * We force all calls to cdecl even though this could slow Watcom code down
+ * slightly. If you know that the Watcom compiler will be used to build both
+ * the DLL and application, then you can probably define this as a null string.
+ * Remember that pthread.h (this file) is used for both the DLL and application builds.
+ */
+#define PTW32_CDECL __cdecl
+
+#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX
+# include <sys/types.h>
+#else
+/*
+ * Generic handle type - intended to extend uniqueness beyond
+ * that available with a simple pointer. It should scale for either
+ * IA-32 or IA-64.
+ */
+typedef struct {
+ void * p; /* Pointer to actual object */
+ unsigned int x; /* Extra information - reuse count etc */
+} ptw32_handle_t;
+
+typedef ptw32_handle_t pthread_t;
+typedef struct pthread_attr_t_ * pthread_attr_t;
+typedef struct pthread_once_t_ pthread_once_t;
+typedef struct pthread_key_t_ * pthread_key_t;
+typedef struct pthread_mutex_t_ * pthread_mutex_t;
+typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t;
+typedef struct pthread_cond_t_ * pthread_cond_t;
+typedef struct pthread_condattr_t_ * pthread_condattr_t;
+#endif
+typedef struct pthread_rwlock_t_ * pthread_rwlock_t;
+typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t;
+typedef struct pthread_spinlock_t_ * pthread_spinlock_t;
+typedef struct pthread_barrier_t_ * pthread_barrier_t;
+typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t;
+
+/*
+ * ====================
+ * ====================
+ * POSIX Threads
+ * ====================
+ * ====================
+ */
+
+enum {
+/*
+ * pthread_attr_{get,set}detachstate
+ */
+ PTHREAD_CREATE_JOINABLE = 0, /* Default */
+ PTHREAD_CREATE_DETACHED = 1,
+
+/*
+ * pthread_attr_{get,set}inheritsched
+ */
+ PTHREAD_INHERIT_SCHED = 0,
+ PTHREAD_EXPLICIT_SCHED = 1, /* Default */
+
+/*
+ * pthread_{get,set}scope
+ */
+ PTHREAD_SCOPE_PROCESS = 0,
+ PTHREAD_SCOPE_SYSTEM = 1, /* Default */
+
+/*
+ * pthread_setcancelstate paramters
+ */
+ PTHREAD_CANCEL_ENABLE = 0, /* Default */
+ PTHREAD_CANCEL_DISABLE = 1,
+
+/*
+ * pthread_setcanceltype parameters
+ */
+ PTHREAD_CANCEL_ASYNCHRONOUS = 0,
+ PTHREAD_CANCEL_DEFERRED = 1, /* Default */
+
+/*
+ * pthread_mutexattr_{get,set}pshared
+ * pthread_condattr_{get,set}pshared
+ */
+ PTHREAD_PROCESS_PRIVATE = 0,
+ PTHREAD_PROCESS_SHARED = 1,
+
+/*
+ * pthread_mutexattr_{get,set}robust
+ */
+ PTHREAD_MUTEX_STALLED = 0, /* Default */
+ PTHREAD_MUTEX_ROBUST = 1,
+
+/*
+ * pthread_barrier_wait
+ */
+ PTHREAD_BARRIER_SERIAL_THREAD = -1
+};
+
+/*
+ * ====================
+ * ====================
+ * Cancelation
+ * ====================
+ * ====================
+ */
+#define PTHREAD_CANCELED ((void *)(size_t) -1)
+
+
+/*
+ * ====================
+ * ====================
+ * Once Key
+ * ====================
+ * ====================
+ */
+#define PTHREAD_ONCE_INIT { PTW32_FALSE, 0, 0, 0}
+
+struct pthread_once_t_
+{
+ int done; /* indicates if user function has been executed */
+ void * lock;
+ int reserved1;
+ int reserved2;
+};
+
+
+/*
+ * ====================
+ * ====================
+ * Object initialisers
+ * ====================
+ * ====================
+ */
+#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -1)
+#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -2)
+#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -3)
+
+/*
+ * Compatibility with LinuxThreads
+ */
+#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER
+#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
+
+#define PTHREAD_COND_INITIALIZER ((pthread_cond_t)(size_t) -1)
+
+#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t)(size_t) -1)
+
+#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t)(size_t) -1)
+
+
+/*
+ * Mutex types.
+ */
+enum
+{
+ /* Compatibility with LinuxThreads */
+ PTHREAD_MUTEX_FAST_NP,
+ PTHREAD_MUTEX_RECURSIVE_NP,
+ PTHREAD_MUTEX_ERRORCHECK_NP,
+ PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP,
+ PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP,
+ /* For compatibility with POSIX */
+ PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP,
+ PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,
+ PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,
+ PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
+};
+
+
+typedef struct ptw32_cleanup_t ptw32_cleanup_t;
+
+#if defined(_MSC_VER)
+/* Disable MSVC 'anachronism used' warning */
+#pragma warning( disable : 4229 )
+#endif
+
+typedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *);
+
+#if defined(_MSC_VER)
+#pragma warning( default : 4229 )
+#endif
+
+struct ptw32_cleanup_t
+{
+ ptw32_cleanup_callback_t routine;
+ void *arg;
+ struct ptw32_cleanup_t *prev;
+};
+
+#if defined(__CLEANUP_SEH)
+ /*
+ * WIN32 SEH version of cancel cleanup.
+ */
+
+#define pthread_cleanup_push( _rout, _arg ) \
+ { \
+ ptw32_cleanup_t _cleanup; \
+ \
+ _cleanup.routine = (ptw32_cleanup_callback_t)(_rout); \
+ _cleanup.arg = (_arg); \
+ __try \
+ { \
+
+#define pthread_cleanup_pop( _execute ) \
+ } \
+ __finally \
+ { \
+ if( _execute || AbnormalTermination()) \
+ { \
+ (*(_cleanup.routine))( _cleanup.arg ); \
+ } \
+ } \
+ }
+
+#else /* __CLEANUP_SEH */
+
+#if defined(__CLEANUP_C)
+
+ /*
+ * C implementation of PThreads cancel cleanup
+ */
+
+#define pthread_cleanup_push( _rout, _arg ) \
+ { \
+ ptw32_cleanup_t _cleanup; \
+ \
+ ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \
+
+#define pthread_cleanup_pop( _execute ) \
+ (void) ptw32_pop_cleanup( _execute ); \
+ }
+
+#else /* __CLEANUP_C */
+
+#if defined(__CLEANUP_CXX)
+
+ /*
+ * C++ version of cancel cleanup.
+ * - John E. Bossom.
+ */
+
+ class PThreadCleanup {
+ /*
+ * PThreadCleanup
+ *
+ * Purpose
+ * This class is a C++ helper class that is
+ * used to implement pthread_cleanup_push/
+ * pthread_cleanup_pop.
+ * The destructor of this class automatically
+ * pops the pushed cleanup routine regardless
+ * of how the code exits the scope
+ * (i.e. such as by an exception)
+ */
+ ptw32_cleanup_callback_t cleanUpRout;
+ void * obj;
+ int executeIt;
+
+ public:
+ PThreadCleanup() :
+ cleanUpRout( 0 ),
+ obj( 0 ),
+ executeIt( 0 )
+ /*
+ * No cleanup performed
+ */
+ {
+ }
+
+ PThreadCleanup(
+ ptw32_cleanup_callback_t routine,
+ void * arg ) :
+ cleanUpRout( routine ),
+ obj( arg ),
+ executeIt( 1 )
+ /*
+ * Registers a cleanup routine for 'arg'
+ */
+ {
+ }
+
+ ~PThreadCleanup()
+ {
+ if ( executeIt && ((void *) cleanUpRout != (void *) 0) )
+ {
+ (void) (*cleanUpRout)( obj );
+ }
+ }
+
+ void execute( int exec )
+ {
+ executeIt = exec;
+ }
+ };
+
+ /*
+ * C++ implementation of PThreads cancel cleanup;
+ * This implementation takes advantage of a helper
+ * class who's destructor automatically calls the
+ * cleanup routine if we exit our scope weirdly
+ */
+#define pthread_cleanup_push( _rout, _arg ) \
+ { \
+ PThreadCleanup cleanup((ptw32_cleanup_callback_t)(_rout), \
+ (void *) (_arg) );
+
+#define pthread_cleanup_pop( _execute ) \
+ cleanup.execute( _execute ); \
+ }
+
+#else
+
+#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined.
+
+#endif /* __CLEANUP_CXX */
+
+#endif /* __CLEANUP_C */
+
+#endif /* __CLEANUP_SEH */
+
+/*
+ * ===============
+ * ===============
+ * Methods
+ * ===============
+ * ===============
+ */
+
+/*
+ * PThread Attribute Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr,
+ int *detachstate);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr,
+ void **stackaddr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr,
+ size_t * stacksize);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr,
+ int detachstate);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr,
+ void *stackaddr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr,
+ size_t stacksize);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr,
+ struct sched_param *param);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr,
+ const struct sched_param *param);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *,
+ int);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (const pthread_attr_t *,
+ int *);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr,
+ int inheritsched);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(const pthread_attr_t * attr,
+ int * inheritsched);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *,
+ int);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *,
+ int *);
+
+/*
+ * PThread Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid,
+ const pthread_attr_t * attr,
+ void *(PTW32_CDECL *start) (void *),
+ void *arg);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1,
+ pthread_t t2);
+
+PTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread,
+ void **value_ptr);
+
+PTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state,
+ int *oldstate);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type,
+ int *oldtype);
+
+PTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control,
+ void (PTW32_CDECL *init_routine) (void));
+
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX
+PTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute);
+
+PTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup,
+ ptw32_cleanup_callback_t routine,
+ void *arg);
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
+
+/*
+ * Thread Specific Data Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key,
+ void (PTW32_CDECL *destructor) (void *));
+
+PTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key,
+ const void *value);
+
+PTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key);
+
+
+/*
+ * Mutex Attribute Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t
+ * attr,
+ int *pshared);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,
+ int pshared);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind);
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (const pthread_mutexattr_t * attr, int *kind);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setrobust(
+ pthread_mutexattr_t *attr,
+ int robust);
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getrobust(
+ const pthread_mutexattr_t * attr,
+ int * robust);
+
+/*
+ * Barrier Attribute Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t
+ * attr,
+ int *pshared);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr,
+ int pshared);
+
+/*
+ * Mutex Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex,
+ const pthread_mutexattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t * mutex,
+ const struct timespec *abstime);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_consistent (pthread_mutex_t * mutex);
+
+/*
+ * Spinlock Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock);
+
+/*
+ * Barrier Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier,
+ const pthread_barrierattr_t * attr,
+ unsigned int count);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier);
+
+/*
+ * Condition Variable Attribute Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr,
+ int *pshared);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr,
+ int pshared);
+
+/*
+ * Condition Variable Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond,
+ const pthread_condattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond,
+ pthread_mutex_t * mutex);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond,
+ pthread_mutex_t * mutex,
+ const struct timespec *abstime);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond);
+
+/*
+ * Scheduling
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread,
+ int policy,
+ const struct sched_param *param);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread,
+ int *policy,
+ struct sched_param *param);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void);
+
+/*
+ * Read-Write Lock Functions
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock,
+ const pthread_rwlockattr_t *attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,
+ const struct timespec *abstime);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,
+ const struct timespec *abstime);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,
+ int *pshared);
+
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr,
+ int pshared);
+
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1
+
+/*
+ * Signal Functions. Should be defined in <signal.h> but MSVC and MinGW32
+ * already have signal.h that don't define these.
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig);
+
+/*
+ * Non-portable functions
+ */
+
+/*
+ * Compatibility with Linux.
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr,
+ int kind);
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr,
+ int *kind);
+
+/*
+ * Possibly supported by other POSIX threads implementations
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval);
+PTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void);
+PTW32_DLLPORT unsigned __int64 PTW32_CDECL pthread_getunique_np(pthread_t thread);
+
+/*
+ * Useful if an application wants to statically link
+ * the lib rather than load the DLL at run-time.
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void);
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void);
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void);
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void);
+
+/*
+ * Features that are auto-detected at load/run time.
+ */
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int);
+enum ptw32_features {
+ PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */
+ PTW32_ALERTABLE_ASYNC_CANCEL = 0x0002 /* Can cancel blocked threads. */
+};
+
+/*
+ * Register a system time change with the library.
+ * Causes the library to perform various functions
+ * in response to the change. Should be called whenever
+ * the application's top level window receives a
+ * WM_TIMECHANGE message. It can be passed directly to
+ * pthread_create() as a new thread if desired.
+ */
+PTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *);
+
+#endif /*PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */
+
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX
+
+/*
+ * Returns the Win32 HANDLE for the POSIX thread.
+ */
+PTW32_DLLPORT HANDLE PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread);
+/*
+ * Returns the win32 thread ID for POSIX thread.
+ */
+PTW32_DLLPORT DWORD PTW32_CDECL pthread_getw32threadid_np (pthread_t thread);
+
+
+/*
+ * Protected Methods
+ *
+ * This function blocks until the given WIN32 handle
+ * is signaled or pthread_cancel had been called.
+ * This function allows the caller to hook into the
+ * PThreads cancel mechanism. It is implemented using
+ *
+ * WaitForMultipleObjects
+ *
+ * on 'waitHandle' and a manually reset WIN32 Event
+ * used to implement pthread_cancel. The 'timeout'
+ * argument to TimedWait is simply passed to
+ * WaitForMultipleObjects.
+ */
+PTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (HANDLE waitHandle);
+PTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (HANDLE waitHandle,
+ DWORD timeout);
+
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
+
+/*
+ * Thread-Safe C Runtime Library Mappings.
+ */
+#if !defined(_UWIN)
+# if defined(NEED_ERRNO)
+ PTW32_DLLPORT int * PTW32_CDECL _errno( void );
+# else
+# if !defined(errno)
+# if (defined(_MT) || defined(_DLL))
+ __declspec(dllimport) extern int * __cdecl _errno(void);
+# define errno (*_errno())
+# endif
+# endif
+# endif
+#endif
+
+/*
+ * Some compiler environments don't define some things.
+ */
+#if defined(__BORLANDC__)
+# define _ftime ftime
+# define _timeb timeb
+#endif
+
+#if defined(__cplusplus)
+
+/*
+ * Internal exceptions
+ */
+class ptw32_exception {};
+class ptw32_exception_cancel : public ptw32_exception {};
+class ptw32_exception_exit : public ptw32_exception {};
+
+#endif
+
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX
+
+/* FIXME: This is only required if the library was built using SEH */
+/*
+ * Get internal SEH tag
+ */
+PTW32_DLLPORT DWORD PTW32_CDECL ptw32_get_exception_services_code(void);
+
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */
+
+#if !defined(PTW32_BUILD)
+
+#if defined(__CLEANUP_SEH)
+
+/*
+ * Redefine the SEH __except keyword to ensure that applications
+ * propagate our internal exceptions up to the library's internal handlers.
+ */
+#define __except( E ) \
+ __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \
+ ? EXCEPTION_CONTINUE_SEARCH : ( E ) )
+
+#endif /* __CLEANUP_SEH */
+
+#if defined(__CLEANUP_CXX)
+
+/*
+ * Redefine the C++ catch keyword to ensure that applications
+ * propagate our internal exceptions up to the library's internal handlers.
+ */
+#if defined(_MSC_VER)
+ /*
+ * WARNING: Replace any 'catch( ... )' with 'PtW32CatchAll'
+ * if you want Pthread-Win32 cancelation and pthread_exit to work.
+ */
+
+#if !defined(PtW32NoCatchWarn)
+
+#pragma message("Specify \"/DPtW32NoCatchWarn\" compiler flag to skip this message.")
+#pragma message("------------------------------------------------------------------")
+#pragma message("When compiling applications with MSVC++ and C++ exception handling:")
+#pragma message(" Replace any 'catch( ... )' in routines called from POSIX threads")
+#pragma message(" with 'PtW32CatchAll' or 'CATCHALL' if you want POSIX thread")
+#pragma message(" cancelation and pthread_exit to work. For example:")
+#pragma message("")
+#pragma message(" #if defined(PtW32CatchAll)")
+#pragma message(" PtW32CatchAll")
+#pragma message(" #else")
+#pragma message(" catch(...)")
+#pragma message(" #endif")
+#pragma message(" {")
+#pragma message(" /* Catchall block processing */")
+#pragma message(" }")
+#pragma message("------------------------------------------------------------------")
+
+#endif
+
+#define PtW32CatchAll \
+ catch( ptw32_exception & ) { throw; } \
+ catch( ... )
+
+#else /* _MSC_VER */
+
+#define catch( E ) \
+ catch( ptw32_exception & ) { throw; } \
+ catch( E )
+
+#endif /* _MSC_VER */
+
+#endif /* __CLEANUP_CXX */
+
+#endif /* ! PTW32_BUILD */
+
+#if defined(__cplusplus)
+} /* End of extern "C" */
+#endif /* __cplusplus */
+
+#if defined(PTW32__HANDLE_DEF)
+# undef HANDLE
+#endif
+#if defined(PTW32__DWORD_DEF)
+# undef DWORD
+#endif
+
+#undef PTW32_LEVEL
+#undef PTW32_LEVEL_MAX
+
+#endif /* ! RC_INVOKED */
+
+#endif /* PTHREAD_H */
diff --git a/pthreads/sched.h b/pthreads/sched.h
new file mode 100644
index 0000000..f36a97a
--- /dev/null
+++ b/pthreads/sched.h
@@ -0,0 +1,183 @@
+/*
+ * Module: sched.h
+ *
+ * Purpose:
+ * Provides an implementation of POSIX realtime extensions
+ * as defined in
+ *
+ * POSIX 1003.1b-1993 (POSIX.1b)
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Pthreads-win32 - POSIX Threads Library for Win32
+ * Copyright(C) 1998 John E. Bossom
+ * Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *
+ * Contact Email: rpj at callisto.canberra.edu.au
+ *
+ * The current list of contributors is contained
+ * in the file CONTRIBUTORS included with the source
+ * code distribution. The list can also be seen at the
+ * following World Wide Web location:
+ * http://sources.redhat.com/pthreads-win32/contributors.html
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+#if !defined(_SCHED_H)
+#define _SCHED_H
+
+#undef PTW32_SCHED_LEVEL
+
+#if defined(_POSIX_SOURCE)
+#define PTW32_SCHED_LEVEL 0
+/* Early POSIX */
+#endif
+
+#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
+#undef PTW32_SCHED_LEVEL
+#define PTW32_SCHED_LEVEL 1
+/* Include 1b, 1c and 1d */
+#endif
+
+#if defined(INCLUDE_NP)
+#undef PTW32_SCHED_LEVEL
+#define PTW32_SCHED_LEVEL 2
+/* Include Non-Portable extensions */
+#endif
+
+#define PTW32_SCHED_LEVEL_MAX 3
+
+#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_SCHED_LEVEL)
+#define PTW32_SCHED_LEVEL PTW32_SCHED_LEVEL_MAX
+/* Include everything */
+#endif
+
+
+#if defined(__GNUC__) && !defined(__declspec)
+# error Please upgrade your GNU compiler to one that supports __declspec.
+#endif
+
+/*
+ * When building the library, you should define PTW32_BUILD so that
+ * the variables/functions are exported correctly. When using the library,
+ * do NOT define PTW32_BUILD, and then the variables/functions will
+ * be imported correctly.
+ */
+#if !defined(PTW32_STATIC_LIB)
+# if defined(PTW32_BUILD)
+# define PTW32_DLLPORT __declspec (dllexport)
+# else
+# define PTW32_DLLPORT __declspec (dllimport)
+# endif
+#else
+# define PTW32_DLLPORT
+#endif
+
+/*
+ * This is a duplicate of what is in the autoconf config.h,
+ * which is only used when building the pthread-win32 libraries.
+ */
+
+#if !defined(PTW32_CONFIG_H)
+# if defined(WINCE)
+# define NEED_ERRNO
+# define NEED_SEM
+# endif
+# if defined(__MINGW64__)
+# define HAVE_STRUCT_TIMESPEC
+# define HAVE_MODE_T
+# elif defined(_UWIN) || defined(__MINGW32__)
+# define HAVE_MODE_T
+# endif
+#endif
+
+/*
+ *
+ */
+
+#if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX
+#if defined(NEED_ERRNO)
+#include "need_errno.h"
+#else
+#include <errno.h>
+#endif
+#endif /* PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX */
+
+#if (defined(__MINGW64__) || defined(__MINGW32__)) || defined(_UWIN)
+# if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX
+/* For pid_t */
+# include <sys/types.h>
+/* Required by Unix 98 */
+# include <time.h>
+# else
+ typedef int pid_t;
+# endif
+#else
+ typedef int pid_t;
+#endif
+
+/* Thread scheduling policies */
+
+enum {
+ SCHED_OTHER = 0,
+ SCHED_FIFO,
+ SCHED_RR,
+ SCHED_MIN = SCHED_OTHER,
+ SCHED_MAX = SCHED_RR
+};
+
+struct sched_param {
+ int sched_priority;
+};
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif /* __cplusplus */
+
+PTW32_DLLPORT int __cdecl sched_yield (void);
+
+PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy);
+
+PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy);
+
+PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy);
+
+PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid);
+
+/*
+ * Note that this macro returns ENOTSUP rather than
+ * ENOSYS as might be expected. However, returning ENOSYS
+ * should mean that sched_get_priority_{min,max} are
+ * not implemented as well as sched_rr_get_interval.
+ * This is not the case, since we just don't support
+ * round-robin scheduling. Therefore I have chosen to
+ * return the same value as sched_setscheduler when
+ * SCHED_RR is passed to it.
+ */
+#define sched_rr_get_interval(_pid, _interval) \
+ ( errno = ENOTSUP, (int) -1 )
+
+
+#if defined(__cplusplus)
+} /* End of extern "C" */
+#endif /* __cplusplus */
+
+#undef PTW32_SCHED_LEVEL
+#undef PTW32_SCHED_LEVEL_MAX
+
+#endif /* !_SCHED_H */
+
diff --git a/pthreads/semaphore.h b/pthreads/semaphore.h
new file mode 100644
index 0000000..c6e9407
--- /dev/null
+++ b/pthreads/semaphore.h
@@ -0,0 +1,169 @@
+/*
+ * Module: semaphore.h
+ *
+ * Purpose:
+ * Semaphores aren't actually part of the PThreads standard.
+ * They are defined by the POSIX Standard:
+ *
+ * POSIX 1003.1b-1993 (POSIX.1b)
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Pthreads-win32 - POSIX Threads Library for Win32
+ * Copyright(C) 1998 John E. Bossom
+ * Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *
+ * Contact Email: rpj at callisto.canberra.edu.au
+ *
+ * The current list of contributors is contained
+ * in the file CONTRIBUTORS included with the source
+ * code distribution. The list can also be seen at the
+ * following World Wide Web location:
+ * http://sources.redhat.com/pthreads-win32/contributors.html
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+#if !defined( SEMAPHORE_H )
+#define SEMAPHORE_H
+
+#undef PTW32_SEMAPHORE_LEVEL
+
+#if defined(_POSIX_SOURCE)
+#define PTW32_SEMAPHORE_LEVEL 0
+/* Early POSIX */
+#endif
+
+#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
+#undef PTW32_SEMAPHORE_LEVEL
+#define PTW32_SEMAPHORE_LEVEL 1
+/* Include 1b, 1c and 1d */
+#endif
+
+#if defined(INCLUDE_NP)
+#undef PTW32_SEMAPHORE_LEVEL
+#define PTW32_SEMAPHORE_LEVEL 2
+/* Include Non-Portable extensions */
+#endif
+
+#define PTW32_SEMAPHORE_LEVEL_MAX 3
+
+#if !defined(PTW32_SEMAPHORE_LEVEL)
+#define PTW32_SEMAPHORE_LEVEL PTW32_SEMAPHORE_LEVEL_MAX
+/* Include everything */
+#endif
+
+#if defined(__GNUC__) && ! defined (__declspec)
+# error Please upgrade your GNU compiler to one that supports __declspec.
+#endif
+
+/*
+ * When building the library, you should define PTW32_BUILD so that
+ * the variables/functions are exported correctly. When using the library,
+ * do NOT define PTW32_BUILD, and then the variables/functions will
+ * be imported correctly.
+ */
+#if !defined(PTW32_STATIC_LIB)
+# if defined(PTW32_BUILD)
+# define PTW32_DLLPORT __declspec (dllexport)
+# else
+# define PTW32_DLLPORT __declspec (dllimport)
+# endif
+#else
+# define PTW32_DLLPORT
+#endif
+
+/*
+ * This is a duplicate of what is in the autoconf config.h,
+ * which is only used when building the pthread-win32 libraries.
+ */
+
+#if !defined(PTW32_CONFIG_H)
+# if defined(WINCE)
+# define NEED_ERRNO
+# define NEED_SEM
+# endif
+# if defined(__MINGW64__)
+# define HAVE_STRUCT_TIMESPEC
+# define HAVE_MODE_T
+# elif defined(_UWIN) || defined(__MINGW32__)
+# define HAVE_MODE_T
+# endif
+#endif
+
+/*
+ *
+ */
+
+#if PTW32_SEMAPHORE_LEVEL >= PTW32_SEMAPHORE_LEVEL_MAX
+#if defined(NEED_ERRNO)
+#include "need_errno.h"
+#else
+#include <errno.h>
+#endif
+#endif /* PTW32_SEMAPHORE_LEVEL >= PTW32_SEMAPHORE_LEVEL_MAX */
+
+#define _POSIX_SEMAPHORES
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif /* __cplusplus */
+
+#if !defined(HAVE_MODE_T)
+typedef unsigned int mode_t;
+#endif
+
+
+typedef struct sem_t_ * sem_t;
+
+PTW32_DLLPORT int __cdecl sem_init (sem_t * sem,
+ int pshared,
+ unsigned int value);
+
+PTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem);
+
+PTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem);
+
+PTW32_DLLPORT int __cdecl sem_wait (sem_t * sem);
+
+PTW32_DLLPORT int __cdecl sem_timedwait (sem_t * sem,
+ const struct timespec * abstime);
+
+PTW32_DLLPORT int __cdecl sem_post (sem_t * sem);
+
+PTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem,
+ int count);
+
+PTW32_DLLPORT int __cdecl sem_open (const char * name,
+ int oflag,
+ mode_t mode,
+ unsigned int value);
+
+PTW32_DLLPORT int __cdecl sem_close (sem_t * sem);
+
+PTW32_DLLPORT int __cdecl sem_unlink (const char * name);
+
+PTW32_DLLPORT int __cdecl sem_getvalue (sem_t * sem,
+ int * sval);
+
+#if defined(__cplusplus)
+} /* End of extern "C" */
+#endif /* __cplusplus */
+
+#undef PTW32_SEMAPHORE_LEVEL
+#undef PTW32_SEMAPHORE_LEVEL_MAX
+
+#endif /* !SEMAPHORE_H */
diff --git a/public_html/config.js b/public_html/config.js
new file mode 100644
index 0000000..c4d7ecd
--- /dev/null
+++ b/public_html/config.js
@@ -0,0 +1,33 @@
+// --------------------------------------------------------
+//
+// This file is to configure the configurable settings.
+// Load this file before script.js file at gmap.html.
+//
+// --------------------------------------------------------
+
+// -- Output Settings -------------------------------------
+// Show metric values
+Metric = false; // true or false
+
+// -- Map settings ----------------------------------------
+// The Latitude and Longitude in decimal format
+CONST_CENTERLAT = 45.0;
+CONST_CENTERLON = 9.0;
+// The google maps zoom level, 0 - 16, lower is further out
+CONST_ZOOMLVL = 5;
+
+// -- Marker settings -------------------------------------
+// The default marker color
+MarkerColor = "rgb(127, 127, 127)";
+SelectedColor = "rgb(225, 225, 225)";
+
+// -- Site Settings ---------------------------------------
+SiteShow = false; // true or false
+// The Latitude and Longitude in decimal format
+SiteLat = 45.0;
+SiteLon = 9.0;
+
+SiteCircles = true; // true or false (Only shown if SiteShow is true)
+// In nautical miles or km (depending settings value 'Metric')
+SiteCirclesDistances = new Array(100,150,200);
+
diff --git a/public_html/coolclock/coolclock.js b/public_html/coolclock/coolclock.js
new file mode 100644
index 0000000..4411974
--- /dev/null
+++ b/public_html/coolclock/coolclock.js
@@ -0,0 +1,318 @@
+/**
+ * CoolClock 2.1.4
+ * Copyright 2010, Simon Baird
+ * Released under the BSD License.
+ *
+ * Display an analog clock using canvas.
+ * http://randomibis.com/coolclock/
+ *
+ */
+
+// Constructor for CoolClock objects
+window.CoolClock = function(options) {
+ return this.init(options);
+}
+
+// Config contains some defaults, and clock skins
+CoolClock.config = {
+ tickDelay: 1000,
+ longTickDelay: 15000,
+ defaultRadius: 85,
+ renderRadius: 100,
+ defaultSkin: "chunkySwiss",
+ // Should be in skin probably...
+ // (TODO: allow skinning of digital display)
+ showSecs: true,
+ showAmPm: true,
+
+ skins: {
+ // There are more skins in moreskins.js
+ // Try making your own skin by copy/pasting one of these and tweaking it
+ swissRail: {
+ outerBorder: { lineWidth: 2, radius:95, color: "black", alpha: 1 },
+ smallIndicator: { lineWidth: 2, startAt: 88, endAt: 92, color: "black", alpha: 1 },
+ largeIndicator: { lineWidth: 4, startAt: 79, endAt: 92, color: "black", alpha: 1 },
+ hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 },
+ secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 }
+ },
+ chunkySwiss: {
+ outerBorder: { lineWidth: 4, radius:97, color: "black", alpha: 1 },
+ smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 },
+ largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 },
+ hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
+ secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
+ },
+ chunkySwissOnBlack: {
+ outerBorder: { lineWidth: 4, radius:97, color: "white", alpha: 1 },
+ smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "white", alpha: 1 },
+ largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "white", alpha: 1 },
+ hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "white", alpha: 1 },
+ minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "white", alpha: 1 },
+ secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
+ secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
+ }
+
+ },
+
+ // Test for IE so we can nurse excanvas in a couple of places
+ isIE: !!document.all,
+
+ // Will store (a reference to) each clock here, indexed by the id of the canvas element
+ clockTracker: {},
+
+ // For giving a unique id to coolclock canvases with no id
+ noIdCount: 0
+};
+
+// Define the CoolClock object's methods
+CoolClock.prototype = {
+
+ // Initialise using the parameters parsed from the colon delimited class
+ init: function(options) {
+ // Parse and store the options
+ this.canvasId = options.canvasId;
+ this.skinId = options.skinId || CoolClock.config.defaultSkin;
+ this.displayRadius = options.displayRadius || CoolClock.config.defaultRadius;
+ this.showSecondHand = typeof options.showSecondHand == "boolean" ? options.showSecondHand : true;
+ this.gmtOffset = (options.gmtOffset != null && options.gmtOffset != '') ? parseFloat(options.gmtOffset) : null;
+ this.showDigital = typeof options.showDigital == "boolean" ? options.showDigital : false;
+ this.logClock = typeof options.logClock == "boolean" ? options.logClock : false;
+ this.logClockRev = typeof options.logClock == "boolean" ? options.logClockRev : false;
+
+ this.tickDelay = CoolClock.config[ this.showSecondHand ? "tickDelay" : "longTickDelay" ];
+
+ // Get the canvas element
+ this.canvas = document.getElementById(this.canvasId);
+
+ // Make the canvas the requested size. It's always square.
+ this.canvas.setAttribute("width",this.displayRadius*2);
+ this.canvas.setAttribute("height",this.displayRadius*2);
+ this.canvas.style.width = this.displayRadius*2 + "px";
+ this.canvas.style.height = this.displayRadius*2 + "px";
+
+ // Explain me please...?
+ this.renderRadius = CoolClock.config.renderRadius;
+ this.scale = this.displayRadius / this.renderRadius;
+
+ // Initialise canvas context
+ this.ctx = this.canvas.getContext("2d");
+ this.ctx.scale(this.scale,this.scale);
+
+ // Keep track of this object
+ CoolClock.config.clockTracker[this.canvasId] = this;
+
+ // Start the clock going
+ this.tick();
+
+ return this;
+ },
+
+ // Draw a circle at point x,y with params as defined in skin
+ fullCircleAt: function(x,y,skin) {
+ this.ctx.save();
+ this.ctx.globalAlpha = skin.alpha;
+ this.ctx.lineWidth = skin.lineWidth;
+
+ if (!CoolClock.config.isIE) {
+ this.ctx.beginPath();
+ }
+
+ if (CoolClock.config.isIE) {
+ // excanvas doesn't scale line width so we will do it here
+ this.ctx.lineWidth = this.ctx.lineWidth * this.scale;
+ }
+
+ this.ctx.arc(x, y, skin.radius, 0, 2*Math.PI, false);
+
+ if (CoolClock.config.isIE) {
+ // excanvas doesn't close the circle so let's fill in the tiny gap
+ this.ctx.arc(x, y, skin.radius, -0.1, 0.1, false);
+ }
+
+ if (skin.fillColor) {
+ this.ctx.fillStyle = skin.fillColor
+ this.ctx.fill();
+ }
+ else {
+ // XXX why not stroke and fill
+ this.ctx.strokeStyle = skin.color;
+ this.ctx.stroke();
+ }
+ this.ctx.restore();
+ },
+
+ // Draw some text centered vertically and horizontally
+ drawTextAt: function(theText,x,y) {
+ this.ctx.save();
+ this.ctx.font = '15px sans-serif';
+ var tSize = this.ctx.measureText(theText);
+ if (!tSize.height) tSize.height = 15; // no height in firefox.. :(
+ this.ctx.fillText(theText,x - tSize.width/2,y - tSize.height/2);
+ this.ctx.restore();
+ },
+
+ lpad2: function(num) {
+ return (num < 10 ? '0' : '') + num;
+ },
+
+ tickAngle: function(second) {
+ // Log algorithm by David Bradshaw
+ var tweak = 3; // If it's lower the one second mark looks wrong (?)
+ if (this.logClock) {
+ return second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak));
+ }
+ else if (this.logClockRev) {
+ // Flip the seconds then flip the angle (trickiness)
+ second = (60 - second) % 60;
+ return 1.0 - (second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak)));
+ }
+ else {
+ return second/60.0;
+ }
+ },
+
+ timeText: function(hour,min,sec) {
+ var c = CoolClock.config;
+ return '' +
+ (c.showAmPm ? ((hour%12)==0 ? 12 : (hour%12)) : hour) + ':' +
+ this.lpad2(min) +
+ (c.showSecs ? ':' + this.lpad2(sec) : '') +
+ (c.showAmPm ? (hour < 12 ? ' am' : ' pm') : '')
+ ;
+ },
+
+ // Draw a radial line by rotating then drawing a straight line
+ // Ha ha, I think I've accidentally used Taus, (see http://tauday.com/)
+ radialLineAtAngle: function(angleFraction,skin) {
+ this.ctx.save();
+ this.ctx.translate(this.renderRadius,this.renderRadius);
+ this.ctx.rotate(Math.PI * (2.0 * angleFraction - 0.5));
+ this.ctx.globalAlpha = skin.alpha;
+ this.ctx.strokeStyle = skin.color;
+ this.ctx.lineWidth = skin.lineWidth;
+
+ if (CoolClock.config.isIE)
+ // excanvas doesn't scale line width so we will do it here
+ this.ctx.lineWidth = this.ctx.lineWidth * this.scale;
+
+ if (skin.radius) {
+ this.fullCircleAt(skin.startAt,0,skin)
+ }
+ else {
+ this.ctx.beginPath();
+ this.ctx.moveTo(skin.startAt,0)
+ this.ctx.lineTo(skin.endAt,0);
+ this.ctx.stroke();
+ }
+ this.ctx.restore();
+ },
+
+ render: function(hour,min,sec) {
+ // Get the skin
+ var skin = CoolClock.config.skins[this.skinId];
+ if (!skin) skin = CoolClock.config.skins[CoolClock.config.defaultSkin];
+
+ // Clear
+ this.ctx.clearRect(0,0,this.renderRadius*2,this.renderRadius*2);
+
+ // Draw the outer edge of the clock
+ if (skin.outerBorder)
+ this.fullCircleAt(this.renderRadius,this.renderRadius,skin.outerBorder);
+
+ // Draw the tick marks. Every 5th one is a big one
+ for (var i=0;i<60;i++) {
+ (i%5) && skin.smallIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.smallIndicator);
+ !(i%5) && skin.largeIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.largeIndicator);
+ }
+
+ // Write the time
+ if (this.showDigital) {
+ this.drawTextAt(
+ this.timeText(hour,min,sec),
+ this.renderRadius,
+ this.renderRadius+this.renderRadius/2
+ );
+ }
+
+ // Draw the hands
+ if (skin.hourHand)
+ this.radialLineAtAngle(this.tickAngle(((hour%12)*5 + min/12.0)),skin.hourHand);
+
+ if (skin.minuteHand)
+ this.radialLineAtAngle(this.tickAngle((min + sec/60.0)),skin.minuteHand);
+
+ if (this.showSecondHand && skin.secondHand)
+ this.radialLineAtAngle(this.tickAngle(sec),skin.secondHand);
+
+ // Second hand decoration doesn't render right in IE so lets turn it off
+ if (!CoolClock.config.isIE && this.showSecondHand && skin.secondDecoration)
+ this.radialLineAtAngle(this.tickAngle(sec),skin.secondDecoration);
+ },
+
+ // Check the time and display the clock
+ refreshDisplay: function() {
+ var now = new Date();
+ if (this.gmtOffset != null) {
+ // Use GMT + gmtOffset
+ var offsetNow = new Date(now.valueOf() + (this.gmtOffset * 1000 * 60 * 60));
+ this.render(offsetNow.getUTCHours(),offsetNow.getUTCMinutes(),offsetNow.getUTCSeconds());
+ }
+ else {
+ // Use local time
+ this.render(now.getHours(),now.getMinutes(),now.getSeconds());
+ }
+ },
+
+ // Set timeout to trigger a tick in the future
+ nextTick: function() {
+ setTimeout("CoolClock.config.clockTracker['"+this.canvasId+"'].tick()",this.tickDelay);
+ },
+
+ // Check the canvas element hasn't been removed
+ stillHere: function() {
+ return document.getElementById(this.canvasId) != null;
+ },
+
+ // Main tick handler. Refresh the clock then setup the next tick
+ tick: function() {
+ if (this.stillHere()) {
+ this.refreshDisplay()
+ this.nextTick();
+ }
+ }
+};
+
+// Find all canvas elements that have the CoolClock class and turns them into clocks
+CoolClock.findAndCreateClocks = function() {
+ // (Let's not use a jQuery selector here so it's easier to use frameworks other than jQuery)
+ var canvases = document.getElementsByTagName("canvas");
+ for (var i=0;i<canvases.length;i++) {
+ // Pull out the fields from the class. Example "CoolClock:chunkySwissOnBlack:1000"
+ var fields = canvases[i].className.split(" ")[0].split(":");
+ if (fields[0] == "CoolClock") {
+ if (!canvases[i].id) {
+ // If there's no id on this canvas element then give it one
+ canvases[i].id = '_coolclock_auto_id_' + CoolClock.config.noIdCount++;
+ }
+ // Create a clock object for this element
+ new CoolClock({
+ canvasId: canvases[i].id,
+ skinId: fields[1],
+ displayRadius: fields[2],
+ showSecondHand: fields[3]!='noSeconds',
+ gmtOffset: fields[4],
+ showDigital: fields[5]=='showDigital',
+ logClock: fields[6]=='logClock',
+ logClockRev: fields[6]=='logClockRev'
+ });
+ }
+ }
+};
+
+// If you don't have jQuery then you need a body onload like this: <body onload="CoolClock.findAndCreateClocks()">
+// If you do have jQuery and it's loaded already then we can do it right now
+if (window.jQuery) jQuery(document).ready(CoolClock.findAndCreateClocks);
diff --git a/public_html/coolclock/excanvas.js b/public_html/coolclock/excanvas.js
new file mode 100644
index 0000000..3e1aedf
--- /dev/null
+++ b/public_html/coolclock/excanvas.js
@@ -0,0 +1,785 @@
+// Copyright 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+// Known Issues:
+//
+// * Patterns are not implemented.
+// * Radial gradient are not implemented. The VML version of these look very
+// different from the canvas one.
+// * Clipping paths are not implemented.
+// * Coordsize. The width and height attribute have higher priority than the
+// width and height style values which isn't correct.
+// * Painting mode isn't implemented.
+// * Canvas width/height should is using content-box by default. IE in
+// Quirks mode will draw the canvas using border-box. Either change your
+// doctype to HTML5
+// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
+// or use Box Sizing Behavior from WebFX
+// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
+// * Optimize. There is always room for speed improvements.
+
+// only add this code if we do not already have a canvas implementation
+if (!window.CanvasRenderingContext2D) {
+
+(function () {
+
+ // alias some functions to make (compiled) code shorter
+ var m = Math;
+ var mr = m.round;
+ var ms = m.sin;
+ var mc = m.cos;
+
+ // this is used for sub pixel precision
+ var Z = 10;
+ var Z2 = Z / 2;
+
+ var G_vmlCanvasManager_ = {
+ init: function (opt_doc) {
+ var doc = opt_doc || document;
+ if (/MSIE/.test(navigator.userAgent) && !window.opera) {
+ var self = this;
+ doc.attachEvent("onreadystatechange", function () {
+ self.init_(doc);
+ });
+ }
+ },
+
+ init_: function (doc) {
+ if (doc.readyState == "complete") {
+ // create xmlns
+ if (!doc.namespaces["g_vml_"]) {
+ doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
+ }
+
+ // setup default css
+ var ss = doc.createStyleSheet();
+ ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
+ // default size is 300x150 in Gecko and Opera
+ "text-align:left;width:300px;height:150px}" +
+ "g_vml_\\:*{behavior:url(#default#VML)}";
+
+ // find all canvas elements
+ var els = doc.getElementsByTagName("canvas");
+ for (var i = 0; i < els.length; i++) {
+ if (!els[i].getContext) {
+ this.initElement(els[i]);
+ }
+ }
+ }
+ },
+
+ fixElement_: function (el) {
+ // in IE before version 5.5 we would need to add HTML: to the tag name
+ // but we do not care about IE before version 6
+ var outerHTML = el.outerHTML;
+
+ var newEl = el.ownerDocument.createElement(outerHTML);
+ // if the tag is still open IE has created the children as siblings and
+ // it has also created a tag with the name "/FOO"
+ if (outerHTML.slice(-2) != "/>") {
+ var tagName = "/" + el.tagName;
+ var ns;
+ // remove content
+ while ((ns = el.nextSibling) && ns.tagName != tagName) {
+ ns.removeNode();
+ }
+ // remove the incorrect closing tag
+ if (ns) {
+ ns.removeNode();
+ }
+ }
+ el.parentNode.replaceChild(newEl, el);
+ return newEl;
+ },
+
+ /**
+ * Public initializes a canvas element so that it can be used as canvas
+ * element from now on. This is called automatically before the page is
+ * loaded but if you are creating elements using createElement you need to
+ * make sure this is called on the element.
+ * @param {HTMLElement} el The canvas element to initialize.
+ * @return {HTMLElement} the element that was created.
+ */
+ initElement: function (el) {
+ el = this.fixElement_(el);
+ el.getContext = function () {
+ if (this.context_) {
+ return this.context_;
+ }
+ return this.context_ = new CanvasRenderingContext2D_(this);
+ };
+
+ // do not use inline function because that will leak memory
+ el.attachEvent('onpropertychange', onPropertyChange);
+ el.attachEvent('onresize', onResize);
+
+ var attrs = el.attributes;
+ if (attrs.width && attrs.width.specified) {
+ // TODO: use runtimeStyle and coordsize
+ // el.getContext().setWidth_(attrs.width.nodeValue);
+ el.style.width = attrs.width.nodeValue + "px";
+ } else {
+ el.width = el.clientWidth;
+ }
+ if (attrs.height && attrs.height.specified) {
+ // TODO: use runtimeStyle and coordsize
+ // el.getContext().setHeight_(attrs.height.nodeValue);
+ el.style.height = attrs.height.nodeValue + "px";
+ } else {
+ el.height = el.clientHeight;
+ }
+ //el.getContext().setCoordsize_()
+ return el;
+ }
+ };
+
+ function onPropertyChange(e) {
+ var el = e.srcElement;
+
+ switch (e.propertyName) {
+ case 'width':
+ el.style.width = el.attributes.width.nodeValue + "px";
+ el.getContext().clearRect();
+ break;
+ case 'height':
+ el.style.height = el.attributes.height.nodeValue + "px";
+ el.getContext().clearRect();
+ break;
+ }
+ }
+
+ function onResize(e) {
+ var el = e.srcElement;
+ if (el.firstChild) {
+ el.firstChild.style.width = el.clientWidth + 'px';
+ el.firstChild.style.height = el.clientHeight + 'px';
+ }
+ }
+
+ G_vmlCanvasManager_.init();
+
+ // precompute "00" to "FF"
+ var dec2hex = [];
+ for (var i = 0; i < 16; i++) {
+ for (var j = 0; j < 16; j++) {
+ dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
+ }
+ }
+
+ function createMatrixIdentity() {
+ return [
+ [1, 0, 0],
+ [0, 1, 0],
+ [0, 0, 1]
+ ];
+ }
+
+ function matrixMultiply(m1, m2) {
+ var result = createMatrixIdentity();
+
+ for (var x = 0; x < 3; x++) {
+ for (var y = 0; y < 3; y++) {
+ var sum = 0;
+
+ for (var z = 0; z < 3; z++) {
+ sum += m1[x][z] * m2[z][y];
+ }
+
+ result[x][y] = sum;
+ }
+ }
+ return result;
+ }
+
+ function copyState(o1, o2) {
+ o2.fillStyle = o1.fillStyle;
+ o2.lineCap = o1.lineCap;
+ o2.lineJoin = o1.lineJoin;
+ o2.lineWidth = o1.lineWidth;
+ o2.miterLimit = o1.miterLimit;
+ o2.shadowBlur = o1.shadowBlur;
+ o2.shadowColor = o1.shadowColor;
+ o2.shadowOffsetX = o1.shadowOffsetX;
+ o2.shadowOffsetY = o1.shadowOffsetY;
+ o2.strokeStyle = o1.strokeStyle;
+ o2.arcScaleX_ = o1.arcScaleX_;
+ o2.arcScaleY_ = o1.arcScaleY_;
+ }
+
+ function processStyle(styleString) {
+ var str, alpha = 1;
+
+ styleString = String(styleString);
+ if (styleString.substring(0, 3) == "rgb") {
+ var start = styleString.indexOf("(", 3);
+ var end = styleString.indexOf(")", start + 1);
+ var guts = styleString.substring(start + 1, end).split(",");
+
+ str = "#";
+ for (var i = 0; i < 3; i++) {
+ str += dec2hex[Number(guts[i])];
+ }
+
+ if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
+ alpha = guts[3];
+ }
+ } else {
+ str = styleString;
+ }
+
+ return [str, alpha];
+ }
+
+ function processLineCap(lineCap) {
+ switch (lineCap) {
+ case "butt":
+ return "flat";
+ case "round":
+ return "round";
+ case "square":
+ default:
+ return "square";
+ }
+ }
+
+ /**
+ * This class implements CanvasRenderingContext2D interface as described by
+ * the WHATWG.
+ * @param {HTMLElement} surfaceElement The element that the 2D context should
+ * be associated with
+ */
+ function CanvasRenderingContext2D_(surfaceElement) {
+ this.m_ = createMatrixIdentity();
+
+ this.mStack_ = [];
+ this.aStack_ = [];
+ this.currentPath_ = [];
+
+ // Canvas context properties
+ this.strokeStyle = "#000";
+ this.fillStyle = "#000";
+
+ this.lineWidth = 1;
+ this.lineJoin = "miter";
+ this.lineCap = "butt";
+ this.miterLimit = Z * 1;
+ this.globalAlpha = 1;
+ this.canvas = surfaceElement;
+
+ var el = surfaceElement.ownerDocument.createElement('div');
+ el.style.width = surfaceElement.clientWidth + 'px';
+ el.style.height = surfaceElement.clientHeight + 'px';
+ el.style.overflow = 'hidden';
+ el.style.position = 'absolute';
+ surfaceElement.appendChild(el);
+
+ this.element_ = el;
+ this.arcScaleX_ = 1;
+ this.arcScaleY_ = 1;
+ };
+
+ var contextPrototype = CanvasRenderingContext2D_.prototype;
+ contextPrototype.clearRect = function() {
+ this.element_.innerHTML = "";
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.beginPath = function() {
+ // TODO: Branch current matrix so that save/restore has no effect
+ // as per safari docs.
+
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.moveTo = function(aX, aY) {
+ this.currentPath_.push({type: "moveTo", x: aX, y: aY});
+ this.currentX_ = aX;
+ this.currentY_ = aY;
+ };
+
+ contextPrototype.lineTo = function(aX, aY) {
+ this.currentPath_.push({type: "lineTo", x: aX, y: aY});
+ this.currentX_ = aX;
+ this.currentY_ = aY;
+ };
+
+ contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
+ aCP2x, aCP2y,
+ aX, aY) {
+ this.currentPath_.push({type: "bezierCurveTo",
+ cp1x: aCP1x,
+ cp1y: aCP1y,
+ cp2x: aCP2x,
+ cp2y: aCP2y,
+ x: aX,
+ y: aY});
+ this.currentX_ = aX;
+ this.currentY_ = aY;
+ };
+
+ contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
+ // the following is lifted almost directly from
+ // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
+ var cp1x = this.currentX_ + 2.0 / 3.0 * (aCPx - this.currentX_);
+ var cp1y = this.currentY_ + 2.0 / 3.0 * (aCPy - this.currentY_);
+ var cp2x = cp1x + (aX - this.currentX_) / 3.0;
+ var cp2y = cp1y + (aY - this.currentY_) / 3.0;
+ this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, aX, aY);
+ };
+
+ contextPrototype.arc = function(aX, aY, aRadius,
+ aStartAngle, aEndAngle, aClockwise) {
+ aRadius *= Z;
+ var arcType = aClockwise ? "at" : "wa";
+
+ var xStart = aX + (mc(aStartAngle) * aRadius) - Z2;
+ var yStart = aY + (ms(aStartAngle) * aRadius) - Z2;
+
+ var xEnd = aX + (mc(aEndAngle) * aRadius) - Z2;
+ var yEnd = aY + (ms(aEndAngle) * aRadius) - Z2;
+
+ // IE won't render arches drawn counter clockwise if xStart == xEnd.
+ if (xStart == xEnd && !aClockwise) {
+ xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
+ // that can be represented in binary
+ }
+
+ this.currentPath_.push({type: arcType,
+ x: aX,
+ y: aY,
+ radius: aRadius,
+ xStart: xStart,
+ yStart: yStart,
+ xEnd: xEnd,
+ yEnd: yEnd});
+
+ };
+
+ contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ };
+
+ contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
+ // Will destroy any existing path (same as FF behaviour)
+ this.beginPath();
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ this.stroke();
+ };
+
+ contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
+ // Will destroy any existing path (same as FF behaviour)
+ this.beginPath();
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ this.fill();
+ };
+
+ contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
+ var gradient = new CanvasGradient_("gradient");
+ return gradient;
+ };
+
+ contextPrototype.createRadialGradient = function(aX0, aY0,
+ aR0, aX1,
+ aY1, aR1) {
+ var gradient = new CanvasGradient_("gradientradial");
+ gradient.radius1_ = aR0;
+ gradient.radius2_ = aR1;
+ gradient.focus_.x = aX0;
+ gradient.focus_.y = aY0;
+ return gradient;
+ };
+
+ contextPrototype.drawImage = function (image, var_args) {
+ var dx, dy, dw, dh, sx, sy, sw, sh;
+
+ // to find the original width we overide the width and height
+ var oldRuntimeWidth = image.runtimeStyle.width;
+ var oldRuntimeHeight = image.runtimeStyle.height;
+ image.runtimeStyle.width = 'auto';
+ image.runtimeStyle.height = 'auto';
+
+ // get the original size
+ var w = image.width;
+ var h = image.height;
+
+ // and remove overides
+ image.runtimeStyle.width = oldRuntimeWidth;
+ image.runtimeStyle.height = oldRuntimeHeight;
+
+ if (arguments.length == 3) {
+ dx = arguments[1];
+ dy = arguments[2];
+ sx = sy = 0;
+ sw = dw = w;
+ sh = dh = h;
+ } else if (arguments.length == 5) {
+ dx = arguments[1];
+ dy = arguments[2];
+ dw = arguments[3];
+ dh = arguments[4];
+ sx = sy = 0;
+ sw = w;
+ sh = h;
+ } else if (arguments.length == 9) {
+ sx = arguments[1];
+ sy = arguments[2];
+ sw = arguments[3];
+ sh = arguments[4];
+ dx = arguments[5];
+ dy = arguments[6];
+ dw = arguments[7];
+ dh = arguments[8];
+ } else {
+ throw "Invalid number of arguments";
+ }
+
+ var d = this.getCoords_(dx, dy);
+
+ var w2 = sw / 2;
+ var h2 = sh / 2;
+
+ var vmlStr = [];
+
+ var W = 10;
+ var H = 10;
+
+ // For some reason that I've now forgotten, using divs didn't work
+ vmlStr.push(' <g_vml_:group',
+ ' coordsize="', Z * W, ',', Z * H, '"',
+ ' coordorigin="0,0"' ,
+ ' style="width:', W, ';height:', H, ';position:absolute;');
+
+ // If filters are necessary (rotation exists), create them
+ // filters are bog-slow, so only create them if abbsolutely necessary
+ // The following check doesn't account for skews (which don't exist
+ // in the canvas spec (yet) anyway.
+
+ if (this.m_[0][0] != 1 || this.m_[0][1]) {
+ var filter = [];
+
+ // Note the 12/21 reversal
+ filter.push("M11='", this.m_[0][0], "',",
+ "M12='", this.m_[1][0], "',",
+ "M21='", this.m_[0][1], "',",
+ "M22='", this.m_[1][1], "',",
+ "Dx='", mr(d.x / Z), "',",
+ "Dy='", mr(d.y / Z), "'");
+
+ // Bounding box calculation (need to minimize displayed area so that
+ // filters don't waste time on unused pixels.
+ var max = d;
+ var c2 = this.getCoords_(dx + dw, dy);
+ var c3 = this.getCoords_(dx, dy + dh);
+ var c4 = this.getCoords_(dx + dw, dy + dh);
+
+ max.x = Math.max(max.x, c2.x, c3.x, c4.x);
+ max.y = Math.max(max.y, c2.y, c3.y, c4.y);
+
+ vmlStr.push("padding:0 ", mr(max.x / Z), "px ", mr(max.y / Z),
+ "px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
+ filter.join(""), ", sizingmethod='clip');")
+ } else {
+ vmlStr.push("top:", mr(d.y / Z), "px;left:", mr(d.x / Z), "px;")
+ }
+
+ vmlStr.push(' ">' ,
+ '<g_vml_:image src="', image.src, '"',
+ ' style="width:', Z * dw, ';',
+ ' height:', Z * dh, ';"',
+ ' cropleft="', sx / w, '"',
+ ' croptop="', sy / h, '"',
+ ' cropright="', (w - sx - sw) / w, '"',
+ ' cropbottom="', (h - sy - sh) / h, '"',
+ ' />',
+ '</g_vml_:group>');
+
+ this.element_.insertAdjacentHTML("BeforeEnd",
+ vmlStr.join(""));
+ };
+
+ contextPrototype.stroke = function(aFill) {
+ var lineStr = [];
+ var lineOpen = false;
+ var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
+ var color = a[0];
+ var opacity = a[1] * this.globalAlpha;
+
+ var W = 10;
+ var H = 10;
+
+ lineStr.push('<g_vml_:shape',
+ ' fillcolor="', color, '"',
+ ' filled="', Boolean(aFill), '"',
+ ' style="position:absolute;width:', W, ';height:', H, ';"',
+ ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
+ ' stroked="', !aFill, '"',
+ ' strokeweight="', this.lineWidth, '"',
+ ' strokecolor="', color, '"',
+ ' path="');
+
+ var newSeq = false;
+ var min = {x: null, y: null};
+ var max = {x: null, y: null};
+
+ for (var i = 0; i < this.currentPath_.length; i++) {
+ var p = this.currentPath_[i];
+
+ if (p.type == "moveTo") {
+ lineStr.push(" m ");
+ var c = this.getCoords_(p.x, p.y);
+ lineStr.push(mr(c.x), ",", mr(c.y));
+ } else if (p.type == "lineTo") {
+ lineStr.push(" l ");
+ var c = this.getCoords_(p.x, p.y);
+ lineStr.push(mr(c.x), ",", mr(c.y));
+ } else if (p.type == "close") {
+ lineStr.push(" x ");
+ } else if (p.type == "bezierCurveTo") {
+ lineStr.push(" c ");
+ var c = this.getCoords_(p.x, p.y);
+ var c1 = this.getCoords_(p.cp1x, p.cp1y);
+ var c2 = this.getCoords_(p.cp2x, p.cp2y);
+ lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
+ mr(c2.x), ",", mr(c2.y), ",",
+ mr(c.x), ",", mr(c.y));
+ } else if (p.type == "at" || p.type == "wa") {
+ lineStr.push(" ", p.type, " ");
+ var c = this.getCoords_(p.x, p.y);
+ var cStart = this.getCoords_(p.xStart, p.yStart);
+ var cEnd = this.getCoords_(p.xEnd, p.yEnd);
+
+ lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
+ mr(c.y - this.arcScaleY_ * p.radius), " ",
+ mr(c.x + this.arcScaleX_ * p.radius), ",",
+ mr(c.y + this.arcScaleY_ * p.radius), " ",
+ mr(cStart.x), ",", mr(cStart.y), " ",
+ mr(cEnd.x), ",", mr(cEnd.y));
+ }
+
+
+ // TODO: Following is broken for curves due to
+ // move to proper paths.
+
+ // Figure out dimensions so we can do gradient fills
+ // properly
+ if(c) {
+ if (min.x == null || c.x < min.x) {
+ min.x = c.x;
+ }
+ if (max.x == null || c.x > max.x) {
+ max.x = c.x;
+ }
+ if (min.y == null || c.y < min.y) {
+ min.y = c.y;
+ }
+ if (max.y == null || c.y > max.y) {
+ max.y = c.y;
+ }
+ }
+ }
+ lineStr.push(' ">');
+
+ if (typeof this.fillStyle == "object") {
+ var focus = {x: "50%", y: "50%"};
+ var width = (max.x - min.x);
+ var height = (max.y - min.y);
+ var dimension = (width > height) ? width : height;
+
+ focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
+ focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
+
+ var colors = [];
+
+ // inside radius (%)
+ if (this.fillStyle.type_ == "gradientradial") {
+ var inside = (this.fillStyle.radius1_ / dimension * 100);
+
+ // percentage that outside radius exceeds inside radius
+ var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
+ } else {
+ var inside = 0;
+ var expansion = 100;
+ }
+
+ var insidecolor = {offset: null, color: null};
+ var outsidecolor = {offset: null, color: null};
+
+ // We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
+ // won't interpret it correctly
+ this.fillStyle.colors_.sort(function (cs1, cs2) {
+ return cs1.offset - cs2.offset;
+ });
+
+ for (var i = 0; i < this.fillStyle.colors_.length; i++) {
+ var fs = this.fillStyle.colors_[i];
+
+ colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
+
+ if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
+ insidecolor.offset = fs.offset;
+ insidecolor.color = fs.color;
+ }
+
+ if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
+ outsidecolor.offset = fs.offset;
+ outsidecolor.color = fs.color;
+ }
+ }
+ colors.pop();
+
+ lineStr.push('<g_vml_:fill',
+ ' color="', outsidecolor.color, '"',
+ ' color2="', insidecolor.color, '"',
+ ' type="', this.fillStyle.type_, '"',
+ ' focusposition="', focus.x, ', ', focus.y, '"',
+ ' colors="', colors.join(""), '"',
+ ' opacity="', opacity, '" />');
+ } else if (aFill) {
+ lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
+ } else {
+ lineStr.push(
+ '<g_vml_:stroke',
+ ' opacity="', opacity,'"',
+ ' joinstyle="', this.lineJoin, '"',
+ ' miterlimit="', this.miterLimit, '"',
+ ' endcap="', processLineCap(this.lineCap) ,'"',
+ ' weight="', this.lineWidth, 'px"',
+ ' color="', color,'" />'
+ );
+ }
+
+ lineStr.push("</g_vml_:shape>");
+
+ this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
+
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.fill = function() {
+ this.stroke(true);
+ }
+
+ contextPrototype.closePath = function() {
+ this.currentPath_.push({type: "close"});
+ };
+
+ /**
+ * @private
+ */
+ contextPrototype.getCoords_ = function(aX, aY) {
+ return {
+ x: Z * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - Z2,
+ y: Z * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - Z2
+ }
+ };
+
+ contextPrototype.save = function() {
+ var o = {};
+ copyState(this, o);
+ this.aStack_.push(o);
+ this.mStack_.push(this.m_);
+ this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
+ };
+
+ contextPrototype.restore = function() {
+ copyState(this.aStack_.pop(), this);
+ this.m_ = this.mStack_.pop();
+ };
+
+ contextPrototype.translate = function(aX, aY) {
+ var m1 = [
+ [1, 0, 0],
+ [0, 1, 0],
+ [aX, aY, 1]
+ ];
+
+ this.m_ = matrixMultiply(m1, this.m_);
+ };
+
+ contextPrototype.rotate = function(aRot) {
+ var c = mc(aRot);
+ var s = ms(aRot);
+
+ var m1 = [
+ [c, s, 0],
+ [-s, c, 0],
+ [0, 0, 1]
+ ];
+
+ this.m_ = matrixMultiply(m1, this.m_);
+ };
+
+ contextPrototype.scale = function(aX, aY) {
+ this.arcScaleX_ *= aX;
+ this.arcScaleY_ *= aY;
+ var m1 = [
+ [aX, 0, 0],
+ [0, aY, 0],
+ [0, 0, 1]
+ ];
+
+ this.m_ = matrixMultiply(m1, this.m_);
+ };
+
+ /******** STUBS ********/
+ contextPrototype.clip = function() {
+ // TODO: Implement
+ };
+
+ contextPrototype.arcTo = function() {
+ // TODO: Implement
+ };
+
+ contextPrototype.createPattern = function() {
+ return new CanvasPattern_;
+ };
+
+ // Gradient / Pattern Stubs
+ function CanvasGradient_(aType) {
+ this.type_ = aType;
+ this.radius1_ = 0;
+ this.radius2_ = 0;
+ this.colors_ = [];
+ this.focus_ = {x: 0, y: 0};
+ }
+
+ CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
+ aColor = processStyle(aColor);
+ this.colors_.push({offset: 1-aOffset, color: aColor});
+ };
+
+ function CanvasPattern_() {}
+
+ // set up externs
+ G_vmlCanvasManager = G_vmlCanvasManager_;
+ CanvasRenderingContext2D = CanvasRenderingContext2D_;
+ CanvasGradient = CanvasGradient_;
+ CanvasPattern = CanvasPattern_;
+
+})();
+
+} // if
diff --git a/public_html/coolclock/moreskins.js b/public_html/coolclock/moreskins.js
new file mode 100644
index 0000000..e316181
--- /dev/null
+++ b/public_html/coolclock/moreskins.js
@@ -0,0 +1,212 @@
+CoolClock.config.skins = {
+
+ swissRail: {
+ outerBorder: { lineWidth: 2, radius: 95, color: "black", alpha: 1 },
+ smallIndicator: { lineWidth: 2, startAt: 88, endAt: 92, color: "black", alpha: 1 },
+ largeIndicator: { lineWidth: 4, startAt: 79, endAt: 92, color: "black", alpha: 1 },
+ hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 },
+ secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 }
+ },
+
+ chunkySwiss: {
+ outerBorder: { lineWidth: 4, radius: 97, color: "black", alpha: 1 },
+ smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 },
+ largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 },
+ hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
+ secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
+ },
+
+ chunkySwissOnBlack: {
+ outerBorder: { lineWidth: 4, radius: 97, color: "white", alpha: 1 },
+ smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "white", alpha: 1 },
+ largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "white", alpha: 1 },
+ hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "white", alpha: 1 },
+ minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "white", alpha: 1 },
+ secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
+ secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
+ },
+
+ fancy: {
+ outerBorder: { lineWidth: 5, radius: 95, color: "green", alpha: 0.7 },
+ smallIndicator: { lineWidth: 1, startAt: 80, endAt: 93, color: "black", alpha: 0.4 },
+ largeIndicator: { lineWidth: 1, startAt: 30, endAt: 93, color: "black", alpha: 0.5 },
+ hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "blue", alpha: 0.7 },
+ minuteHand: { lineWidth: 7, startAt: -15, endAt: 92, color: "red", alpha: 0.7 },
+ secondHand: { lineWidth: 10, startAt: 80, endAt: 85, color: "blue", alpha: 0.3 },
+ secondDecoration: { lineWidth: 1, startAt: 30, radius: 50, fillColor: "blue", color: "red", alpha: 0.15 }
+ },
+
+ machine: {
+ outerBorder: { lineWidth: 60, radius: 55, color: "#dd6655", alpha: 1 },
+ smallIndicator: { lineWidth: 4, startAt: 80, endAt: 95, color: "white", alpha: 1 },
+ largeIndicator: { lineWidth: 14, startAt: 77, endAt: 92, color: "#dd6655", alpha: 1 },
+ hourHand: { lineWidth: 18, startAt: -15, endAt: 40, color: "white", alpha: 1 },
+ minuteHand: { lineWidth: 14, startAt: 24, endAt: 100, color: "#771100", alpha: 0.5 },
+ secondHand: { lineWidth: 3, startAt: 22, endAt: 83, color: "green", alpha: 0 },
+ secondDecoration: { lineWidth: 1, startAt: 52, radius: 26, fillColor: "#ffcccc", color: "red", alpha: 0.5 }
+ },
+
+ simonbaird_com: {
+ hourHand: { lineWidth: 80, startAt: -15, endAt: 35, color: 'magenta', alpha: 0.5 },
+ minuteHand: { lineWidth: 80, startAt: -15, endAt: 65, color: 'cyan', alpha: 0.5 },
+ secondDecoration: { lineWidth: 1, startAt: 40, radius: 40, color: "#fff", fillColor: 'yellow', alpha: 0.5 }
+ },
+
+ // by bonstio, http://bonstio.net
+ classic/*was gIG*/: {
+ outerBorder: { lineWidth: 185, radius: 1, color: "#E5ECF9", alpha: 1 },
+ smallIndicator: { lineWidth: 2, startAt: 89, endAt: 94, color: "#3366CC", alpha: 1 },
+ largeIndicator: { lineWidth: 4, startAt: 83, endAt: 94, color: "#3366CC", alpha: 1 },
+ hourHand: { lineWidth: 5, startAt: 0, endAt: 60, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 4, startAt: 0, endAt: 80, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: .85 },
+ secondDecoration: { lineWidth: 3, startAt: 0, radius: 2, fillColor: "black", color: "black", alpha: 1 }
+ },
+
+ modern/*was gIG2*/: {
+ outerBorder: { lineWidth: 185, radius: 1, color: "#E5ECF9", alpha: 1 },
+ smallIndicator: { lineWidth: 5, startAt: 88, endAt: 94, color: "#3366CC", alpha: 1 },
+ largeIndicator: { lineWidth: 5, startAt: 88, endAt: 94, color: "#3366CC", alpha: 1 },
+ hourHand: { lineWidth: 8, startAt: 0, endAt: 60, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 8, startAt: 0, endAt: 80, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 5, startAt: 80, endAt: 85, color: "red", alpha: .85 },
+ secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
+ },
+
+ simple/*was gIG3*/: {
+ outerBorder: { lineWidth: 185, radius: 1, color: "#E5ECF9", alpha: 1 },
+ smallIndicator: { lineWidth: 10, startAt: 90, endAt: 94, color: "#3366CC", alpha: 1 },
+ largeIndicator: { lineWidth: 10, startAt: 90, endAt: 94, color: "#3366CC", alpha: 1 },
+ hourHand: { lineWidth: 8, startAt: 0, endAt: 60, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 8, startAt: 0, endAt: 80, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 5, startAt: 80, endAt: 85, color: "red", alpha: .85 },
+ secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
+ },
+
+ // by securephp
+ securephp: {
+ outerBorder: { lineWidth: 100, radius: 0.45, color: "#669900", alpha: 0.3 },
+ smallIndicator: { lineWidth: 2, startAt: 80, endAt: 90 , color: "green", alpha: 1 },
+ largeIndicator: { lineWidth: 8.5, startAt: 20, endAt: 40 , color: "green", alpha: 0.4 },
+ hourHand: { lineWidth: 3, startAt: 0, endAt: 60, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 2, startAt: 0, endAt: 75, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 1, startAt: -10, endAt: 80, color: "blue", alpha: 0.8 },
+ secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "blue", color: "red", alpha: 1 }
+ },
+
+ Tes2: {
+ outerBorder: { lineWidth: 4, radius: 95, color: "black", alpha: 0.5 },
+ smallIndicator: { lineWidth: 1, startAt: 10, endAt: 50 , color: "#66CCFF", alpha: 1 },
+ largeIndicator: { lineWidth: 8.5, startAt: 60, endAt: 70, color: "#6699FF", alpha: 1 },
+ hourHand: { lineWidth: 5, startAt: -15, endAt: 60, color: "black", alpha: 0.7 },
+ minuteHand: { lineWidth: 3, startAt: -25, endAt: 75, color: "black", alpha: 0.7 },
+ secondHand: { lineWidth: 1.5, startAt: -20, endAt: 88, color: "red", alpha: 1 },
+ secondDecoration: { lineWidth: 1, startAt: 20, radius: 4, fillColor: "blue", color: "red", alpha: 1 }
+ },
+
+
+ Lev: {
+ outerBorder: { lineWidth: 10, radius: 95, color: "#CCFF33", alpha: 0.65 },
+ smallIndicator: { lineWidth: 5, startAt: 84, endAt: 90, color: "#996600", alpha: 1 },
+ largeIndicator: { lineWidth: 40, startAt: 25, endAt: 95, color: "#336600", alpha: 0.55 },
+ hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 0.9 },
+ minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 0.85 },
+ secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 1 },
+ secondDecoration: { lineWidth: 2, startAt: 5, radius: 10, fillColor: "black", color: "black", alpha: 1 }
+ },
+
+ Sand: {
+ outerBorder: { lineWidth: 1, radius: 70, color: "black", alpha: 0.5 },
+ smallIndicator: { lineWidth: 3, startAt: 50, endAt: 70, color: "#0066FF", alpha: 0.5 },
+ largeIndicator: { lineWidth: 200, startAt: 80, endAt: 95, color: "#996600", alpha: 0.75 },
+ hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 0.9 },
+ minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 0.85 },
+ secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 1 },
+ secondDecoration: { lineWidth: 2, startAt: 5, radius: 10, fillColor: "black", color: "black", alpha: 1 }
+ },
+
+ Sun: {
+ outerBorder: { lineWidth: 100, radius: 140, color: "#99FFFF", alpha: 0.2 },
+ smallIndicator: { lineWidth: 300, startAt: 50, endAt: 70, color: "black", alpha: 0.1 },
+ largeIndicator: { lineWidth: 5, startAt: 80, endAt: 95, color: "black", alpha: 0.65 },
+ hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 0.9 },
+ minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 0.85 },
+ secondHand: { lineWidth: 1, startAt: 0, endAt: 90, color: "black", alpha: 1 },
+ secondDecoration: { lineWidth: 2, startAt: 5, radius: 10, fillColor: "black", color: "black", alpha: 1 }
+ },
+
+ Tor: {
+ outerBorder: { lineWidth: 10, radius: 88, color: "#996600", alpha: 0.9 },
+ smallIndicator: { lineWidth: 6, startAt: -10, endAt: 73, color: "green", alpha: 0.3 },
+ largeIndicator: { lineWidth: 6, startAt: 73, endAt: 100, color: "black", alpha: 0.65 },
+ hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 1, startAt: -73, endAt: 73, color: "black", alpha: 0.8 },
+ secondDecoration: { lineWidth: 2, startAt: 5, radius: 10, fillColor: "black", color: "black", alpha: 1 }
+ },
+
+ Cold: {
+ outerBorder: { lineWidth: 15, radius: 90, color: "black", alpha: 0.3 },
+ smallIndicator: { lineWidth: 15, startAt: -10, endAt: 95, color: "blue", alpha: 0.1 },
+ largeIndicator: { lineWidth: 3, startAt: 80, endAt: 95, color: "blue", alpha: 0.65 },
+ hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 0.8 },
+ secondDecoration: { lineWidth: 5, startAt: 30, radius: 10, fillColor: "black", color: "black", alpha: 1 }
+ },
+
+ Babosa: {
+ outerBorder: { lineWidth: 100, radius: 25, color: "blue", alpha: 0.25 },
+ smallIndicator: { lineWidth: 3, startAt: 90, endAt: 95, color: "#3366CC", alpha: 1 },
+ largeIndicator: { lineWidth: 4, startAt: 75, endAt: 95, color: "#3366CC", alpha: 1 },
+ hourHand: { lineWidth: 4, startAt: 0, endAt: 60, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 3, startAt: 0, endAt: 85, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 12, startAt: 75, endAt: 90, color: "red", alpha: 0.8 },
+ secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
+ },
+
+ Tumb: {
+ outerBorder: { lineWidth: 105, radius: 5, color: "green", alpha: 0.4 },
+ smallIndicator: { lineWidth: 1, startAt: 93, endAt: 98, color: "green", alpha: 1 },
+ largeIndicator: { lineWidth: 50, startAt: 0, endAt: 89, color: "red", alpha: 0.14 },
+ hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 0.8 },
+ secondDecoration: { lineWidth: 5, startAt: 50, radius: 90, fillColor: "black", color: "black", alpha: 0.05 }
+ },
+
+ Stone: {
+ outerBorder: { lineWidth: 15, radius: 80, color: "#339933", alpha: 0.5 },
+ smallIndicator: { lineWidth: 2, startAt: 70, endAt: 90, color: "#FF3300", alpha: 0.7 },
+ largeIndicator: { lineWidth: 15, startAt: 0, endAt: 29, color: "#FF6600", alpha: 0.3 },
+ hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 3, startAt: 0, endAt: 75, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 0.8 },
+ secondDecoration: { lineWidth: 5, startAt: 50, radius: 90, fillColor: "black", color: "black", alpha: 0.05 }
+ },
+
+ Disc: {
+ outerBorder: { lineWidth: 105, radius: 1, color: "#666600", alpha: 0.2 },
+ smallIndicator: { lineWidth: 1, startAt: 58, endAt: 95, color: "#669900", alpha: 0.8 },
+ largeIndicator: { lineWidth: 6, startAt: 25, endAt: 35, color: "#666600", alpha: 1 },
+ hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
+ minuteHand: { lineWidth: 3, startAt: 0, endAt: 75, color: "black", alpha: 1 },
+ secondHand: { lineWidth: 1, startAt: -75, endAt: 75, color: "#99CC00", alpha: 0.8 },
+ secondDecoration: { lineWidth: 5, startAt: 50, radius: 90, fillColor: "#00FF00", color: "green", alpha: 0.05 }
+ },
+
+ // By Yoo Nhe
+ watermelon: {
+ outerBorder: { lineWidth: 100, radius: 1.7, color: "#d93d04", alpha: 5 },
+ smallIndicator: { lineWidth: 2, startAt: 50, endAt: 70, color: "#d93d04", alpha: 5 },
+ largeIndicator: { lineWidth: 2, startAt: 45, endAt: 94, color: "#a9bf04", alpha: 1 },
+ hourHand: { lineWidth: 5, startAt: -20, endAt: 80, color: "#8c0d17", alpha: 1 },
+ minuteHand: { lineWidth: 2, startAt: -20, endAt: 80, color: "#7c8c03", alpha: .9 },
+ secondHand: { lineWidth: 2, startAt: 70, endAt: 94, color: "#d93d04", alpha: .85 },
+ secondDecoration: { lineWidth: 1, startAt: 70, radius: 3, fillColor: "red", color: "black", alpha: .7 }
+ }
+};
diff --git a/public_html/extension.js b/public_html/extension.js
new file mode 100644
index 0000000..f71fe17
--- /dev/null
+++ b/public_html/extension.js
@@ -0,0 +1,19 @@
+// -----------------------------------------------------
+//
+// This file is so users can modify how the page acts
+// without diving to deep in the code. This way we can
+// also try out or hold custom code for ourselves and
+// not check it into the repo.
+//
+// There is a div id'ed as plane_extension for use with
+// this javascript file.
+// -----------------------------------------------------
+
+function extendedInitalize() {
+ // Write your initalization here
+ // Gets called just before the 1-sec function call loop is setup
+}
+
+function extendedPulse() {
+ // This will get called every second after all the main functions
+}
diff --git a/public_html/gmap.html b/public_html/gmap.html
new file mode 100644
index 0000000..96f412a
--- /dev/null
+++ b/public_html/gmap.html
@@ -0,0 +1,63 @@
+<html>
+ <head>
+ <link rel="stylesheet" type="text/css" href="style.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
+ <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
+ <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false&libraries=geometry"></script>
+ <script type="text/javascript" src="config.js"></script>
+ <script type="text/javascript" src="planeObject.js"></script>
+ <script type="text/javascript" src="options.js"></script>
+ <script type="text/javascript" src="extension.js"></script>
+ <script type="text/javascript" src="script.js"></script>
+ <script type="text/javascript" src="coolclock/excanvas.js"></script>
+ <script type="text/javascript" src="coolclock/coolclock.js"></script>
+ <script type="text/javascript" src="coolclock/moreskins.js"></script>
+ </head>
+ <body onload="initialize()">
+ <div id="dialog-modal" title="Basic modal dialog" style="display:none;">
+ <p>The settings feature is coming soon. Keep checking GitHub.</p>
+ </div>
+ <div id="map_container">
+ <div id="map_canvas"></div>
+ </div>
+ <div id="sidebar_container">
+ <div id="sidebar_canvas">
+ <div id="timestamps" style="align: center">
+ <table width="100%"><tr>
+ <td>Local Time</td>
+ <td>
+ <canvas id="localclock" class="CoolClock:classic:40"></canvas>
+ </td>
+ <td>UTC Time</td>
+ <td>
+ <canvas id="gmtclock" class="CoolClock:classic:40::0"></canvas>
+ </td>
+ </tr></table>
+ </div>
+ <div id="sudo_buttons">
+ <table width="100%"><tr>
+ <td width="150" style="text-align: center;" class="pointer">
+ [ <span onclick="resetMap();">Reset Map</span> ]
+ </td>
+ <td> </td>
+ <td width="150" style="text-align: center;" id="setings_button" class="pointer">
+ [ <span onclick="optionsModal();">Settings</span> ]
+ </td>
+ </tr></table>
+ </div>
+
+ <div id="plane_detail"></div>
+ <div id="options"></div>
+ <div id="planes_table"></div>
+ <div id="plane_extension"></div>
+ </div>
+ </div>
+ <div id="SpecialSquawkWarning">
+ <b>Squak 7x00 is reported and shown.</b><br>
+ This is most likely an error in reciving or decoding.<br>
+ Please do not call the local authorities, they already know about it if it is valid squak.
+ </div>
+ <div id="container_splitter"></div>
+ </body>
+</html>
diff --git a/public_html/options.js b/public_html/options.js
new file mode 100644
index 0000000..f4e3627
--- /dev/null
+++ b/public_html/options.js
@@ -0,0 +1,17 @@
+var listKMLType = ['Approch', 'Departure', 'Transit', 'Custom1', 'Custom2'];
+var listKMLs = localStorage['listKMLs'] || [];
+
+function optionsInitalize() {
+ // Write your initalization here
+ // Gets called just before the 1-sec function call loop is setup
+ $( "#dialog-modal" ).dialog({
+ height: 140,
+ modal: true,
+ autoOpen: false,
+ closeOnEscape: false
+ });
+}
+
+function optionsModal() {
+ $( "#dialog-modal" ).dialog( "open");
+}
diff --git a/public_html/planeObject.js b/public_html/planeObject.js
new file mode 100644
index 0000000..dfb0130
--- /dev/null
+++ b/public_html/planeObject.js
@@ -0,0 +1,254 @@
+var planeObject = {
+ oldlat : null,
+ oldlon : null,
+ oldalt : null,
+
+ // Basic location information
+ altitude : null,
+ speed : null,
+ track : null,
+ latitude : null,
+ longitude : null,
+
+ // Info about the plane
+ flight : null,
+ squawk : null,
+ icao : null,
+ is_selected : false,
+
+ // Data packet numbers
+ messages : null,
+ seen : null,
+
+ // Vaild...
+ vPosition : false,
+ vTrack : false,
+
+ // GMap Details
+ marker : null,
+ markerColor : MarkerColor,
+ lines : [],
+ trackdata : new Array(),
+ trackline : new Array(),
+
+ // When was this last updated?
+ updated : null,
+ reapable : false,
+
+ // Appends data to the running track so we can get a visual tail on the plane
+ // Only useful for a long running browser session.
+ funcAddToTrack : function(){
+ // TODO: Write this function out
+ this.trackdata.push([this.latitude, this.longitude, this.altitude, this.track, this.speed]);
+ this.trackline.push(new google.maps.LatLng(this.latitude, this.longitude));
+ },
+
+ // This is to remove the line from the screen if we deselect the plane
+ funcClearLine : function() {
+ if (this.line) {
+ this.line.setMap(null);
+ this.line = null;
+ }
+ },
+
+ // Should create an icon for us to use on the map...
+ funcGetIcon : function() {
+ // If this marker is selected we should make it lighter than the rest.
+ if (this.is_selected == true) {
+ this.markerColor = SelectedColor;
+ }
+
+ // Plane marker
+ var baseSvg = {
+ planeData : "M 1.9565564,41.694305 C 1.7174505,40.497708 1.6419973,38.448747 " +
+ "1.8096508,37.70494 1.8936398,37.332056 2.0796653,36.88191 2.222907,36.70461 " +
+ "2.4497603,36.423844 4.087816,35.47248 14.917931,29.331528 l 12.434577," +
+ "-7.050718 -0.04295,-7.613412 c -0.03657,-6.4844888 -0.01164,-7.7625804 " +
+ "0.168134,-8.6194061 0.276129,-1.3160905 0.762276,-2.5869575 1.347875," +
+ "-3.5235502 l 0.472298,-0.7553719 1.083746,-0.6085497 c 1.194146,-0.67053522 " +
+ "1.399524,-0.71738842 2.146113,-0.48960552 1.077005,0.3285939 2.06344," +
+ "1.41299352 2.797602,3.07543322 0.462378,1.0469993 0.978731,2.7738408 " +
+ "1.047635,3.5036272 0.02421,0.2570284 0.06357,3.78334 0.08732,7.836246 0.02375," +
+ "4.052905 0.0658,7.409251 0.09345,7.458546 0.02764,0.04929 5.600384,3.561772 " +
+ "12.38386,7.805502 l 12.333598,7.715871 0.537584,0.959688 c 0.626485,1.118378 " +
+ "0.651686,1.311286 0.459287,3.516442 -0.175469,2.011604 -0.608966,2.863924 " +
+ "-1.590344,3.127136 -0.748529,0.200763 -1.293144,0.03637 -10.184829,-3.07436 " +
+ "C 48.007733,41.72562 44.793806,40.60197 43.35084,40.098045 l -2.623567," +
+ "-0.916227 -1.981212,-0.06614 c -1.089663,-0.03638 -1.985079,-0.05089 -1.989804," +
+ "-0.03225 -0.0052,0.01863 -0.02396,2.421278 -0.04267,5.339183 -0.0395,6.147742 " +
+ "-0.143635,7.215456 -0.862956,8.845475 l -0.300457,0.680872 2.91906,1.361455 " +
+ "c 2.929379,1.366269 3.714195,1.835385 4.04589,2.41841 0.368292,0.647353 " +
+ "0.594634,2.901439 0.395779,3.941627 -0.0705,0.368571 -0.106308,0.404853 " +
+ "-0.765159,0.773916 L 41.4545,62.83158 39.259237,62.80426 c -6.030106,-0.07507 " +
+ "-16.19508,-0.495041 -16.870991,-0.697033 -0.359409,-0.107405 -0.523792," +
+ "-0.227482 -0.741884,-0.541926 -0.250591,-0.361297 -0.28386,-0.522402 -0.315075," +
+ "-1.52589 -0.06327,-2.03378 0.23288,-3.033615 1.077963,-3.639283 0.307525," +
+ "-0.2204 4.818478,-2.133627 6.017853,-2.552345 0.247872,-0.08654 0.247455," +
+ "-0.102501 -0.01855,-0.711959 -0.330395,-0.756986 -0.708622,-2.221756 -0.832676," +
+ "-3.224748 -0.05031,-0.406952 -0.133825,-3.078805 -0.185533,-5.937448 -0.0517," +
+ "-2.858644 -0.145909,-5.208974 -0.209316,-5.222958 -0.06341,-0.01399 -0.974464," +
+ "-0.0493 -2.024551,-0.07845 L 23.247235,38.61921 18.831373,39.8906 C 4.9432155," +
+ "43.88916 4.2929558,44.057819 3.4954426,43.86823 2.7487826,43.690732 2.2007966," +
+ "42.916622 1.9565564,41.694305 z"
+ };
+
+ // If the squawk code is one of the international emergency codes,
+ // match the info window alert color.
+ if (this.squawk == 7500) {
+ this.markerColor = "rgb(255, 85, 85)";
+ }
+ if (this.squawk == 7600) {
+ this.markerColor = "rgb(0, 255, 255)";
+ }
+ if (this.squawk == 7700) {
+ this.markerColor = "rgb(255, 255, 0)";
+ }
+
+ // If we have not overwritten color by now, an extension still could but
+ // just keep on trucking. :)
+
+ return {
+ strokeWeight: (this.is_selected ? 2 : 1),
+ path: "M 0,0 "+ baseSvg["planeData"],
+ scale: 0.4,
+ fillColor: this.markerColor,
+ fillOpacity: 0.9,
+ anchor: new google.maps.Point(32, 32), // Set anchor to middle of plane.
+ rotation: this.track
+ };
+ },
+
+ // TODO: Trigger actions of a selecting a plane
+ funcSelectPlane : function(selectedPlane){
+ selectPlaneByHex(this.icao);
+ },
+
+ // Update our data
+ funcUpdateData : function(data){
+ // So we can find out if we moved
+ var oldlat = this.latitude;
+ var oldlon = this.longitude;
+ var oldalt = this.altitude;
+
+ // Update all of our data
+ this.updated = new Date().getTime();
+ this.altitude = data.altitude;
+ this.speed = data.speed;
+ this.track = data.track;
+ this.latitude = data.lat;
+ this.longitude = data.lon;
+ this.flight = data.flight;
+ this.squawk = data.squawk;
+ this.icao = data.hex;
+ this.messages = data.messages;
+ this.seen = data.seen;
+
+ // If no packet in over 58 seconds, consider the plane reapable
+ // This way we can hold it, but not show it just in case the plane comes back
+ if (this.seen > 58) {
+ this.reapable = true;
+ if (this.marker) {
+ this.marker.setMap(null);
+ this.marker = null;
+ }
+ if (this.line) {
+ this.line.setMap(null);
+ this.line = null;
+ }
+ if (SelectedPlane == this.icao) {
+ if (this.is_selected) {
+ this.is_selected = false;
+ }
+ SelectedPlane = null;
+ }
+ } else {
+ if (this.reapable == true) {
+ }
+ this.reapable = false;
+ }
+
+ // Is the position valid?
+ if ((data.validposition == 1) && (this.reapable == false)) {
+ this.vPosition = true;
+
+ // Detech if the plane has moved
+ changeLat = false;
+ changeLon = false;
+ changeAlt = false;
+ if (oldlat != this.latitude) {
+ changeLat = true;
+ }
+ if (oldlon != this.longitude) {
+ changeLon = true;
+ }
+ if (oldalt != this.altitude) {
+ changeAlt = true;
+ }
+ // Right now we only care about lat/long, if alt is updated only, oh well
+ if ((changeLat == true) || (changeLon == true)) {
+ this.funcAddToTrack();
+ if (this.is_selected) {
+ this.line = this.funcUpdateLines();
+ }
+ }
+ this.marker = this.funcUpdateMarker();
+ PlanesOnMap++;
+ } else {
+ this.vPosition = false;
+ }
+
+ // Do we have a valid track for the plane?
+ if (data.validtrack == 1)
+ this.vTrack = true;
+ else
+ this.vTrack = false;
+ },
+
+ // Update our marker on the map
+ funcUpdateMarker: function() {
+ if (this.marker) {
+ this.marker.setPosition(new google.maps.LatLng(this.latitude, this.longitude));
+ this.marker.setIcon(this.funcGetIcon());
+ } else {
+ this.marker = new google.maps.Marker({
+ position: new google.maps.LatLng(this.latitude, this.longitude),
+ map: GoogleMap,
+ icon: this.funcGetIcon(),
+ visable: true
+ });
+
+ // This is so we can match icao address
+ this.marker.icao = this.icao;
+
+ // Trap clicks for this marker.
+ google.maps.event.addListener(this.marker, 'click', this.funcSelectPlane);
+ }
+
+ // Setting the marker title
+ if (this.flight.length == 0) {
+ this.marker.setTitle(this.hex);
+ } else {
+ this.marker.setTitle(this.flight+' ('+this.icao+')');
+ }
+ return this.marker;
+ },
+
+ // Update our planes tail line,
+ // TODO: Make this multi colored based on options
+ // altitude (default) or speed
+ funcUpdateLines: function() {
+ if (this.line) {
+ var path = this.line.getPath();
+ path.push(new google.maps.LatLng(this.latitude, this.longitude));
+ } else {
+ this.line = new google.maps.Polyline({
+ strokeColor: '#000000',
+ strokeOpacity: 1.0,
+ strokeWeight: 3,
+ map: GoogleMap,
+ path: this.trackline
+ });
+ }
+ return this.line;
+ }
+};
diff --git a/public_html/script.js b/public_html/script.js
new file mode 100644
index 0000000..ed2a4dc
--- /dev/null
+++ b/public_html/script.js
@@ -0,0 +1,630 @@
+// Define our global variables
+var GoogleMap = null;
+var Planes = {};
+var PlanesOnMap = 0;
+var PlanesOnTable = 0;
+var PlanesToReap = 0;
+var SelectedPlane = null;
+var SpecialSquawk = false;
+
+var iSortCol=-1;
+var bSortASC=true;
+var bDefaultSortASC=true;
+var iDefaultSortCol=3;
+
+// Get current map settings
+CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT;
+CenterLon = Number(localStorage['CenterLon']) || CONST_CENTERLON;
+ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL;
+
+function fetchData() {
+ $.getJSON('/dump1090/data.json', function(data) {
+ PlanesOnMap = 0
+ SpecialSquawk = false;
+
+ // Loop through all the planes in the data packet
+ for (var j=0; j < data.length; j++) {
+ // Do we already have this plane object in Planes?
+ // If not make it.
+ if (Planes[data[j].hex]) {
+ var plane = Planes[data[j].hex];
+ } else {
+ var plane = jQuery.extend(true, {}, planeObject);
+ }
+
+ /* For special squawk tests
+ if (data[j].hex == '48413x') {
+ data[j].squawk = '7700';
+ } //*/
+
+ // Set SpecialSquawk-value
+ if (data[j].squawk == '7500' || data[j].squawk == '7600' || data[j].squawk == '7700') {
+ SpecialSquawk = true;
+ }
+
+ // Call the function update
+ plane.funcUpdateData(data[j]);
+
+ // Copy the plane into Planes
+ Planes[plane.icao] = plane;
+ }
+
+ PlanesOnTable = data.length;
+ });
+}
+
+// Initalizes the map and starts up our timers to call various functions
+function initialize() {
+ // Make a list of all the available map IDs
+ var mapTypeIds = [];
+ for(var type in google.maps.MapTypeId) {
+ mapTypeIds.push(google.maps.MapTypeId[type]);
+ }
+ // Push OSM on to the end
+ mapTypeIds.push("OSM");
+ mapTypeIds.push("dark_map");
+
+ // Styled Map to outline airports and highways
+ var styles = [
+ {
+ "featureType": "administrative",
+ "stylers": [
+ { "visibility": "off" }
+ ]
+ },{
+ "featureType": "landscape",
+ "stylers": [
+ { "visibility": "off" }
+ ]
+ },{
+ "featureType": "poi",
+ "stylers": [
+ { "visibility": "off" }
+ ]
+ },{
+ "featureType": "road",
+ "stylers": [
+ { "visibility": "off" }
+ ]
+ },{
+ "featureType": "transit",
+ "stylers": [
+ { "visibility": "off" }
+ ]
+ },{
+ "featureType": "landscape",
+ "stylers": [
+ { "visibility": "on" },
+ { "weight": 8 },
+ { "color": "#000000" }
+ ]
+ },{
+ "featureType": "water",
+ "stylers": [
+ { "lightness": -74 }
+ ]
+ },{
+ "featureType": "transit.station.airport",
+ "stylers": [
+ { "visibility": "on" },
+ { "weight": 8 },
+ { "invert_lightness": true },
+ { "lightness": 27 }
+ ]
+ },{
+ "featureType": "road.highway",
+ "stylers": [
+ { "visibility": "simplified" },
+ { "invert_lightness": true },
+ { "gamma": 0.3 }
+ ]
+ },{
+ "featureType": "road",
+ "elementType": "labels",
+ "stylers": [
+ { "visibility": "off" }
+ ]
+ }
+ ]
+
+ // Add our styled map
+ var styledMap = new google.maps.StyledMapType(styles, {name: "Dark Map"});
+
+ // Define the Google Map
+ var mapOptions = {
+ center: new google.maps.LatLng(CenterLat, CenterLon),
+ zoom: ZoomLvl,
+ mapTypeId: google.maps.MapTypeId.ROADMAP,
+ mapTypeControl: true,
+ streetViewControl: false,
+ mapTypeControlOptions: {
+ mapTypeIds: mapTypeIds,
+ position: google.maps.ControlPosition.TOP_LEFT,
+ style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
+ }
+ };
+
+ GoogleMap = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
+
+ //Define OSM map type pointing at the OpenStreetMap tile server
+ GoogleMap.mapTypes.set("OSM", new google.maps.ImageMapType({
+ getTileUrl: function(coord, zoom) {
+ return "http://tile.openstreetmap.org/" + zoom + "/" + coord.x + "/" + coord.y + ".png";
+ },
+ tileSize: new google.maps.Size(256, 256),
+ name: "OpenStreetMap",
+ maxZoom: 18
+ }));
+
+ GoogleMap.mapTypes.set("dark_map", styledMap);
+
+ // Listeners for newly created Map
+ google.maps.event.addListener(GoogleMap, 'center_changed', function() {
+ localStorage['CenterLat'] = GoogleMap.getCenter().lat();
+ localStorage['CenterLon'] = GoogleMap.getCenter().lng();
+ });
+
+ google.maps.event.addListener(GoogleMap, 'zoom_changed', function() {
+ localStorage['ZoomLvl'] = GoogleMap.getZoom();
+ });
+
+ // Add home marker if requested
+ if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) {
+ var siteMarker = new google.maps.LatLng(SiteLat, SiteLon);
+ var markerImage = new google.maps.MarkerImage(
+ 'http://maps.google.com/mapfiles/kml/pal4/icon57.png',
+ new google.maps.Size(32, 32), // Image size
+ new google.maps.Point(0, 0), // Origin point of image
+ new google.maps.Point(16, 16)); // Position where marker should point
+ var marker = new google.maps.Marker({
+ position: siteMarker,
+ map: GoogleMap,
+ icon: markerImage,
+ title: 'My Radar Site',
+ zIndex: -99999
+ });
+
+ if (SiteCircles) {
+ for (var i=0;i<SiteCirclesDistances.length;i++) {
+ drawCircle(marker, SiteCirclesDistances[i]); // in meters
+ }
+ }
+ }
+
+ // These will run after page is complitely loaded
+ $(window).load(function() {
+ $('#dialog-modal').css('display', 'inline'); // Show hidden settings-windows content
+ });
+
+ // Load up our options page
+ optionsInitalize();
+
+ // Did our crafty user need some setup?
+ extendedInitalize();
+
+ // Setup our timer to poll from the server.
+ window.setInterval(function() {
+ fetchData();
+ refreshTableInfo();
+ refreshSelected();
+ reaper();
+ extendedPulse();
+ }, 1000);
+}
+
+// This looks for planes to reap out of the master Planes variable
+function reaper() {
+ PlanesToReap = 0;
+ // When did the reaper start?
+ reaptime = new Date().getTime();
+ // Loop the planes
+ for (var reap in Planes) {
+ // Is this plane possibly reapable?
+ if (Planes[reap].reapable == true) {
+ // Has it not been seen for 5 minutes?
+ // This way we still have it if it returns before then
+ // Due to loss of signal or other reasons
+ if ((reaptime - Planes[reap].updated) > 300000) {
+ // Reap it.
+ delete Planes[reap];
+ }
+ PlanesToReap++;
+ }
+ };
+}
+
+// Refresh the detail window about the plane
+function refreshSelected() {
+ var selected = false;
+ if (typeof SelectedPlane !== 'undefined' && SelectedPlane != "ICAO" && SelectedPlane != null) {
+ selected = Planes[SelectedPlane];
+ }
+
+ var columns = 2;
+ var html = '';
+
+ if (selected) {
+ html += '<table id="selectedinfo" width="100%">';
+ } else {
+ html += '<table id="selectedinfo" class="dim" width="100%">';
+ }
+
+ // Flight header line including squawk if needed
+ if (selected && selected.flight == "") {
+ html += '<tr><td colspan="' + columns + '" id="selectedinfotitle"><b>N/A (' +
+ selected.icao + ')</b>';
+ } else if (selected && selected.flight != "") {
+ html += '<tr><td colspan="' + columns + '" id="selectedinfotitle"><b>' +
+ selected.flight + '</b>';
+ } else {
+ html += '<tr><td colspan="' + columns + '" id="selectedinfotitle"><b>DUMP1090</b>';
+ }
+
+ if (selected && selected.squawk == 7500) { // Lets hope we never see this... Aircraft Hijacking
+ html += ' <span class="squawk7500"> Squawking: Aircraft Hijacking </span>';
+ } else if (selected && selected.squawk == 7600) { // Radio Failure
+ html += ' <span class="squawk7600"> Squawking: Radio Failure </span>';
+ } else if (selected && selected.squawk == 7700) { // General Emergency
+ html += ' <span class="squawk7700"> Squawking: General Emergency </span>';
+ } else if (selected && selected.flight != '') {
+ html += ' <a href="http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?';
+ html += 'flightNumber='+selected.flight+'" target="_blank">[FlightStats]</a>';
+ }
+ html += '<td></tr>';
+
+ if (selected) {
+ if (Metric) {
+ html += '<tr><td>Altitude: ' + Math.round(selected.altitude / 3.2828) + ' m</td>';
+ } else {
+ html += '<tr><td>Altitude: ' + selected.altitude + ' ft</td>';
+ }
+ } else {
+ html += '<tr><td>Altitude: n/a</td>';
+ }
+
+ if (selected && selected.squawk != '0000') {
+ html += '<td>Squawk: ' + selected.squawk + '</td></tr>';
+ } else {
+ html += '<td>Squawk: n/a</td></tr>';
+ }
+
+ html += '<tr><td>Speed: '
+ if (selected) {
+ if (Metric) {
+ html += Math.round(selected.speed * 1.852) + ' km/h';
+ } else {
+ html += selected.speed + ' kt';
+ }
+ } else {
+ html += 'n/a';
+ }
+ html += '</td>';
+
+ if (selected) {
+ html += '<td>ICAO (hex): ' + selected.icao + '</td></tr>';
+ } else {
+ html += '<td>ICAO (hex): n/a</td></tr>'; // Something is wrong if we are here
+ }
+
+ html += '<tr><td>Track: '
+ if (selected && selected.vTrack) {
+ html += selected.track + '°' + ' (' + normalizeTrack(selected.track, selected.vTrack)[1] +')';
+ } else {
+ html += 'n/a';
+ }
+ html += '</td><td> </td></tr>';
+
+ html += '<tr><td colspan="' + columns + '" align="center">Lat/Long: ';
+ if (selected && selected.vPosition) {
+ html += selected.latitude + ', ' + selected.longitude + '</td></tr>';
+
+ // Let's show some extra data if we have site coordinates
+ if (SiteShow) {
+ var siteLatLon = new google.maps.LatLng(SiteLat, SiteLon);
+ var planeLatLon = new google.maps.LatLng(selected.latitude, selected.longitude);
+ var dist = google.maps.geometry.spherical.computeDistanceBetween (siteLatLon, planeLatLon);
+
+ if (Metric) {
+ dist /= 1000;
+ } else {
+ dist /= 1852;
+ }
+ dist = (Math.round((dist)*10)/10).toFixed(1);
+ html += '<tr><td colspan="' + columns + '">Distance from Site: ' + dist +
+ (Metric ? ' km' : ' NM') + '</td></tr>';
+ } // End of SiteShow
+ } else {
+ if (SiteShow) {
+ html += '<tr><td colspan="' + columns + '">Distance from Site: n/a ' +
+ (Metric ? ' km' : ' NM') + '</td></tr>';
+ } else {
+ html += 'n/a</td></tr>';
+ }
+ }
+
+ html += '</table>';
+
+ document.getElementById('plane_detail').innerHTML = html;
+}
+
+// Right now we have no means to validate the speed is good
+// Want to return (n/a) when we dont have it
+// TODO: Edit C code to add a valid speed flag
+// TODO: Edit js code to use said flag
+function normalizeSpeed(speed, valid) {
+ return speed
+}
+
+// Returns back a long string, short string, and the track if we have a vaild track path
+function normalizeTrack(track, valid){
+ x = []
+ if ((track > -1) && (track < 22.5)) {
+ x = ["North", "N", track]
+ }
+ if ((track > 22.5) && (track < 67.5)) {
+ x = ["North East", "NE", track]
+ }
+ if ((track > 67.5) && (track < 112.5)) {
+ x = ["East", "E", track]
+ }
+ if ((track > 112.5) && (track < 157.5)) {
+ x = ["South East", "SE", track]
+ }
+ if ((track > 157.5) && (track < 202.5)) {
+ x = ["South", "S", track]
+ }
+ if ((track > 202.5) && (track < 247.5)) {
+ x = ["South West", "SW", track]
+ }
+ if ((track > 247.5) && (track < 292.5)) {
+ x = ["West", "W", track]
+ }
+ if ((track > 292.5) && (track < 337.5)) {
+ x = ["North West", "NW", track]
+ }
+ if ((track > 337.5) && (track < 361)) {
+ x = ["North", "N", track]
+ }
+ if (!valid) {
+ x = [" ", "n/a", ""]
+ }
+ return x
+}
+
+// Refeshes the larger table of all the planes
+function refreshTableInfo() {
+ var html = '<table id="tableinfo" width="100%">';
+ html += '<thead style="background-color: #BBBBBB; cursor: pointer;">';
+ html += '<td onclick="setASC_DESC(\'0\');sortTable(\'tableinfo\',\'0\');">ICAO</td>';
+ html += '<td onclick="setASC_DESC(\'1\');sortTable(\'tableinfo\',\'1\');">Flight</td>';
+ html += '<td onclick="setASC_DESC(\'2\');sortTable(\'tableinfo\',\'2\');" ' +
+ 'align="right">Squawk</td>';
+ html += '<td onclick="setASC_DESC(\'3\');sortTable(\'tableinfo\',\'3\');" ' +
+ 'align="right">Altitude</td>';
+ html += '<td onclick="setASC_DESC(\'4\');sortTable(\'tableinfo\',\'4\');" ' +
+ 'align="right">Speed</td>';
+ html += '<td onclick="setASC_DESC(\'5\');sortTable(\'tableinfo\',\'5\');" ' +
+ 'align="right">Track</td>';
+ html += '<td onclick="setASC_DESC(\'6\');sortTable(\'tableinfo\',\'6\');" ' +
+ 'align="right">Msgs</td>';
+ html += '<td onclick="setASC_DESC(\'7\');sortTable(\'tableinfo\',\'7\');" ' +
+ 'align="right">Seen</td></thead><tbody>';
+ for (var tablep in Planes) {
+ var tableplane = Planes[tablep]
+ if (!tableplane.reapable) {
+ var specialStyle = "";
+ // Is this the plane we selected?
+ if (tableplane.icao == SelectedPlane) {
+ specialStyle += " selected";
+ }
+ // Lets hope we never see this... Aircraft Hijacking
+ if (tableplane.squawk == 7500) {
+ specialStyle += " squawk7500";
+ }
+ // Radio Failure
+ if (tableplane.squawk == 7600) {
+ specialStyle += " squawk7600";
+ }
+ // Emergancy
+ if (tableplane.squawk == 7700) {
+ specialStyle += " squawk7700";
+ }
+
+ if (tableplane.vPosition == true) {
+ html += '<tr class="plane_table_row vPosition' + specialStyle + '">';
+ } else {
+ html += '<tr class="plane_table_row ' + specialStyle + '">';
+ }
+
+ html += '<td>' + tableplane.icao + '</td>';
+ html += '<td>' + tableplane.flight + '</td>';
+ if (tableplane.squawk != '0000' ) {
+ html += '<td align="right">' + tableplane.squawk + '</td>';
+ } else {
+ html += '<td align="right"> </td>';
+ }
+
+ if (Metric) {
+ html += '<td align="right">' + Math.round(tableplane.altitude / 3.2828) + '</td>';
+ html += '<td align="right">' + Math.round(tableplane.speed * 1.852) + '</td>';
+ } else {
+ html += '<td align="right">' + tableplane.altitude + '</td>';
+ html += '<td align="right">' + tableplane.speed + '</td>';
+ }
+
+ html += '<td align="right">';
+ if (tableplane.vTrack) {
+ html += normalizeTrack(tableplane.track, tableplane.vTrack)[2];
+ // html += ' (' + normalizeTrack(tableplane.track, tableplane.vTrack)[1] + ')';
+ } else {
+ html += ' ';
+ }
+ html += '</td>';
+ html += '<td align="right">' + tableplane.messages + '</td>';
+ html += '<td align="right">' + tableplane.seen + '</td>';
+ html += '</tr>';
+ }
+ }
+ html += '</tbody></table>';
+
+ document.getElementById('planes_table').innerHTML = html;
+
+ if (SpecialSquawk) {
+ $('#SpecialSquawkWarning').css('display', 'inline');
+ } else {
+ $('#SpecialSquawkWarning').css('display', 'none');
+ }
+
+ // Click event for table
+ $('#planes_table').find('tr').click( function(){
+ var hex = $(this).find('td:first').text();
+ if (hex != "ICAO") {
+ selectPlaneByHex(hex);
+ refreshTableInfo();
+ refreshSelected();
+ }
+ });
+
+ sortTable("tableinfo");
+}
+
+// Credit goes to a co-worker that needed a similar functions for something else
+// we get a copy of it free ;)
+function setASC_DESC(iCol) {
+ if(iSortCol==iCol) {
+ bSortASC=!bSortASC;
+ } else {
+ bSortASC=bDefaultSortASC;
+ }
+}
+
+function sortTable(szTableID,iCol) {
+ //if iCol was not provided, and iSortCol is not set, assign default value
+ if (typeof iCol==='undefined'){
+ if(iSortCol!=-1){
+ var iCol=iSortCol;
+ } else {
+ var iCol=iDefaultSortCol;
+ }
+ }
+
+ //retrieve passed table element
+ var oTbl=document.getElementById(szTableID).tBodies[0];
+ var aStore=[];
+
+ //If supplied col # is greater than the actual number of cols, set sel col = to last col
+ if (typeof oTbl.rows[0] !== 'undefined' && oTbl.rows[0].cells.length <= iCol) {
+ iCol=(oTbl.rows[0].cells.length-1);
+ }
+
+ //store the col #
+ iSortCol=iCol;
+
+ //determine if we are delaing with numerical, or alphanumeric content
+ var bNumeric = false;
+ if ((typeof oTbl.rows[0] !== 'undefined') &&
+ (!isNaN(parseFloat(oTbl.rows[0].cells[iSortCol].textContent ||
+ oTbl.rows[0].cells[iSortCol].innerText)))) {
+ bNumeric = true;
+ }
+
+ //loop through the rows, storing each one inro aStore
+ for (var i=0,iLen=oTbl.rows.length;i<iLen;i++){
+ var oRow=oTbl.rows[i];
+ vColData=bNumeric?parseFloat(oRow.cells[iSortCol].textContent||oRow.cells[iSortCol].innerText):String(oRow.cells[iSortCol].textContent||oRow.cells[iSortCol].innerText);
+ aStore.push([vColData,oRow]);
+ }
+
+ //sort aStore ASC/DESC based on value of bSortASC
+ if (bNumeric) { //numerical sort
+ aStore.sort(function(x,y){return bSortASC?x[0]-y[0]:y[0]-x[0];});
+ } else { //alpha sort
+ aStore.sort();
+ if(!bSortASC) {
+ aStore.reverse();
+ }
+ }
+
+ //rewrite the table rows to the passed table element
+ for(var i=0,iLen=aStore.length;i<iLen;i++){
+ oTbl.appendChild(aStore[i][1]);
+ }
+ aStore=null;
+}
+
+function selectPlaneByHex(hex) {
+ // If SelectedPlane has something in it, clear out the selected
+ if (SelectedPlane != null) {
+ Planes[SelectedPlane].is_selected = false;
+ Planes[SelectedPlane].funcClearLine();
+ Planes[SelectedPlane].markerColor = MarkerColor;
+ // If the selected has a marker, make it not stand out
+ if (Planes[SelectedPlane].marker) {
+ Planes[SelectedPlane].marker.setIcon(Planes[SelectedPlane].funcGetIcon());
+ }
+ }
+
+ // If we are clicking the same plane, we are deselected it.
+ if (String(SelectedPlane) != String(hex)) {
+ // Assign the new selected
+ SelectedPlane = hex;
+ Planes[SelectedPlane].is_selected = true;
+ // If the selected has a marker, make it stand out
+ if (Planes[SelectedPlane].marker) {
+ Planes[SelectedPlane].funcUpdateLines();
+ Planes[SelectedPlane].marker.setIcon(Planes[SelectedPlane].funcGetIcon());
+ }
+ } else {
+ SelectedPlane = null;
+ }
+ refreshSelected();
+ refreshTableInfo();
+}
+
+function resetMap() {
+ // Reset localStorage values
+ localStorage['CenterLat'] = CONST_CENTERLAT;
+ localStorage['CenterLon'] = CONST_CENTERLON;
+ localStorage['ZoomLvl'] = CONST_ZOOMLVL;
+
+ // Try to read values from localStorage else use CONST_s
+ CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT;
+ CenterLon = Number(localStorage['CenterLon']) || CONST_CENTERLON;
+ ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL;
+
+ // Set and refresh
+ GoogleMap.setZoom(parseInt(ZoomLvl));
+ GoogleMap.setCenter(new google.maps.LatLng(parseFloat(CenterLat), parseFloat(CenterLon)));
+
+ if (SelectedPlane) {
+ selectPlaneByHex(SelectedPlane);
+ }
+
+ refreshSelected();
+ refreshTableInfo();
+}
+
+function drawCircle(marker, distance) {
+ if (typeof distance === 'undefined') {
+ return false;
+
+ if (!(!isNaN(parseFloat(distance)) && isFinite(distance)) || distance < 0) {
+ return false;
+ }
+ }
+
+ distance *= 1000.0;
+ if (!Metric) {
+ distance *= 1.852;
+ }
+
+ // Add circle overlay and bind to marker
+ var circle = new google.maps.Circle({
+ map: GoogleMap,
+ radius: distance, // In meters
+ fillOpacity: 0.0,
+ strokeWeight: 1,
+ strokeOpacity: 0.3
+ });
+ circle.bindTo('center', marker, 'position');
+}
diff --git a/public_html/style.css b/public_html/style.css
new file mode 100644
index 0000000..86cb053
--- /dev/null
+++ b/public_html/style.css
@@ -0,0 +1,32 @@
+html, body {
+ margin: 0; padding: 0; background-color: #ffffff; font-family: Tahoma, Sans-Serif;
+ font-size: 10pt; overflow: auto;
+}
+div#map_container { float: left; width: 100%; height: 100%; }
+div#map_canvas { height: 100%; margin-right: 420px; }
+
+div#sidebar_container { float: left; width: 410px; margin-left: -410px; height: 100%; overflow: auto; }
+
+div#SpecialSquawkWarning { position: absolute; bottom: 25px; right: 430px; border: 2px solid red;
+ background-color: #FFFFA3; opacity: 0.75; filter:alpha(opacity=75); padding: 5px;
+ display: none; text-align: center; }
+
+table#optionsTabs { width: 100%; font-size: small; font-family: monospace; background-color: #ddd;
+ border: 1px; border-color: #000000;}
+
+#tableinfo, #sudo_buttons { font-size: x-small; font-family: monospace; }
+
+.vPosition { font-weight: bold; background-color: #d5ffd5; }
+.squawk7500 { font-weight: bold; background-color: #ff5555; }
+.squawk7600 { font-weight: bold; background-color: #00ffff; }
+.squawk7700 { font-weight: bold; background-color: #ffff00; }
+.selected { background-color: #dddddd; }
+.plane_table_row { cursor: pointer; }
+
+#selectedinfotitle { font-size: larger; }
+#selectedinfo { font-size: small; }
+#selectedinfo a { text-decoration: none; color: blue; font-size: x-small;}
+#selectedinfo.dim { opacity: 0.3; filter:alpha(opacity=30); /* For IE8 and earlier */ }
+
+.pointer { cursor: pointer; }
+
diff --git a/rtlsdr/rtl-sdr.h b/rtlsdr/rtl-sdr.h
new file mode 100644
index 0000000..bb028fe
--- /dev/null
+++ b/rtlsdr/rtl-sdr.h
@@ -0,0 +1,366 @@
+/*
+ * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
+ * Copyright (C) 2012 by Steve Markgraf <steve at steve-m.de>
+ * Copyright (C) 2012 by Dimitri Stolnikov <horiz0n at gmx.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __RTL_SDR_H
+#define __RTL_SDR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//#include <stdint.h>
+#include "rtl-sdr_export.h"
+
+typedef struct rtlsdr_dev rtlsdr_dev_t;
+
+RTLSDR_API uint32_t rtlsdr_get_device_count(void);
+
+RTLSDR_API const char* rtlsdr_get_device_name(uint32_t index);
+
+/*!
+ * Get USB device strings.
+ *
+ * NOTE: The string arguments must provide space for up to 256 bytes.
+ *
+ * \param index the device index
+ * \param manufact manufacturer name, may be NULL
+ * \param product product name, may be NULL
+ * \param serial serial number, may be NULL
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_get_device_usb_strings(uint32_t index,
+ char *manufact,
+ char *product,
+ char *serial);
+
+/*!
+ * Get device index by USB serial string descriptor.
+ *
+ * \param serial serial string of the device
+ * \return device index of first device where the name matched
+ * \return -1 if name is NULL
+ * \return -2 if no devices were found at all
+ * \return -3 if devices were found, but none with matching name
+ */
+RTLSDR_API int rtlsdr_get_index_by_serial(const char *serial);
+
+RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index);
+
+RTLSDR_API int rtlsdr_close(rtlsdr_dev_t *dev);
+
+/* configuration functions */
+
+/*!
+ * Set crystal oscillator frequencies used for the RTL2832 and the tuner IC.
+ *
+ * Usually both ICs use the same clock. Changing the clock may make sense if
+ * you are applying an external clock to the tuner or to compensate the
+ * frequency (and samplerate) error caused by the original (cheap) crystal.
+ *
+ * NOTE: Call this function only if you fully understand the implications.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param rtl_freq frequency value used to clock the RTL2832 in Hz
+ * \param tuner_freq frequency value used to clock the tuner IC in Hz
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq,
+ uint32_t tuner_freq);
+
+/*!
+ * Get crystal oscillator frequencies used for the RTL2832 and the tuner IC.
+ *
+ * Usually both ICs use the same clock.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param rtl_freq frequency value used to clock the RTL2832 in Hz
+ * \param tuner_freq frequency value used to clock the tuner IC in Hz
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq,
+ uint32_t *tuner_freq);
+
+/*!
+ * Get USB device strings.
+ *
+ * NOTE: The string arguments must provide space for up to 256 bytes.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param manufact manufacturer name, may be NULL
+ * \param product product name, may be NULL
+ * \param serial serial number, may be NULL
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact,
+ char *product, char *serial);
+
+/*!
+ * Write the device EEPROM
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param data buffer of data to be written
+ * \param offset address where the data should be written
+ * \param len length of the data
+ * \return 0 on success
+ * \return -1 if device handle is invalid
+ * \return -2 if EEPROM size is exceeded
+ * \return -3 if no EEPROM was found
+ */
+
+RTLSDR_API int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data,
+ uint8_t offset, uint16_t len);
+
+/*!
+ * Read the device EEPROM
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param data buffer where the data should be written
+ * \param offset address where the data should be read from
+ * \param len length of the data
+ * \return 0 on success
+ * \return -1 if device handle is invalid
+ * \return -2 if EEPROM size is exceeded
+ * \return -3 if no EEPROM was found
+ */
+
+RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data,
+ uint8_t offset, uint16_t len);
+
+RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq);
+
+/*!
+ * Get actual frequency the device is tuned to.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \return 0 on error, frequency in Hz otherwise
+ */
+RTLSDR_API uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev);
+
+/*!
+ * Set the frequency correction value for the device.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param ppm correction value in parts per million (ppm)
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm);
+
+/*!
+ * Get actual frequency correction value of the device.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \return correction value in parts per million (ppm)
+ */
+RTLSDR_API int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev);
+
+enum rtlsdr_tuner {
+ RTLSDR_TUNER_UNKNOWN = 0,
+ RTLSDR_TUNER_E4000,
+ RTLSDR_TUNER_FC0012,
+ RTLSDR_TUNER_FC0013,
+ RTLSDR_TUNER_FC2580,
+ RTLSDR_TUNER_R820T
+};
+
+/*!
+ * Get the tuner type.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \return RTLSDR_TUNER_UNKNOWN on error, tuner type otherwise
+ */
+RTLSDR_API enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev);
+
+/*!
+ * Get a list of gains supported by the tuner.
+ *
+ * NOTE: The gains argument must be preallocated by the caller. If NULL is
+ * being given instead, the number of available gain values will be returned.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param gains array of gain values. In tenths of a dB, 115 means 11.5 dB.
+ * \return <= 0 on error, number of available (returned) gain values otherwise
+ */
+RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains);
+
+/*!
+ * Set the gain for the device.
+ * Manual gain mode must be enabled for this to work.
+ *
+ * Valid gain values (in tenths of a dB) for the E4000 tuner:
+ * -10, 15, 40, 65, 90, 115, 140, 165, 190,
+ * 215, 240, 290, 340, 420, 430, 450, 470, 490
+ *
+ * Valid gain values may be queried with \ref rtlsdr_get_tuner_gains function.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param gain in tenths of a dB, 115 means 11.5 dB.
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain);
+
+/*!
+ * Get actual gain the device is configured to.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \return 0 on error, gain in tenths of a dB, 115 means 11.5 dB.
+ */
+RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev);
+
+/*!
+ * Set the intermediate frequency gain for the device.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param stage intermediate frequency gain stage number (1 to 6 for E4000)
+ * \param gain in tenths of a dB, -30 means -3.0 dB.
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain);
+
+/*!
+ * Set the gain mode (automatic/manual) for the device.
+ * Manual gain mode must be enabled for the gain setter function to work.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param manual gain mode, 1 means manual gain mode shall be enabled.
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual);
+
+/* this will select the baseband filters according to the requested sample rate */
+RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate);
+
+/*!
+ * Get actual sample rate the device is configured to.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \return 0 on error, sample rate in Hz otherwise
+ */
+RTLSDR_API uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev);
+
+/*!
+ * Enable test mode that returns an 8 bit counter instead of the samples.
+ * The counter is generated inside the RTL2832.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param test mode, 1 means enabled, 0 disabled
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on);
+
+/*!
+ * Enable or disable the internal digital AGC of the RTL2832.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param digital AGC mode, 1 means enabled, 0 disabled
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on);
+
+/*!
+ * Enable or disable the direct sampling mode. When enabled, the IF mode
+ * of the RTL2832 is activated, and rtlsdr_set_center_freq() will control
+ * the IF-frequency of the DDC, which can be used to tune from 0 to 28.8 MHz
+ * (xtal frequency of the RTL2832).
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on);
+
+/*!
+ * Get state of the direct sampling mode
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \return -1 on error, 0 means disabled, 1 I-ADC input enabled
+ * 2 Q-ADC input enabled
+ */
+RTLSDR_API int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev);
+
+/*!
+ * Enable or disable offset tuning for zero-IF tuners, which allows to avoid
+ * problems caused by the DC offset of the ADCs and 1/f noise.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param on 0 means disabled, 1 enabled
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on);
+
+/*!
+ * Get state of the offset tuning mode
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \return -1 on error, 0 means disabled, 1 enabled
+ */
+RTLSDR_API int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev);
+
+/* streaming functions */
+
+RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev);
+
+RTLSDR_API int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read);
+
+typedef void(*rtlsdr_read_async_cb_t)(unsigned char *buf, uint32_t len, void *ctx);
+
+/*!
+ * Read samples from the device asynchronously. This function will block until
+ * it is being canceled using rtlsdr_cancel_async()
+ *
+ * NOTE: This function is deprecated and is subject for removal.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param cb callback function to return received samples
+ * \param ctx user specific context to pass via the callback function
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx);
+
+/*!
+ * Read samples from the device asynchronously. This function will block until
+ * it is being canceled using rtlsdr_cancel_async()
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \param cb callback function to return received samples
+ * \param ctx user specific context to pass via the callback function
+ * \param buf_num optional buffer count, buf_num * buf_len = overall buffer size
+ * set to 0 for default buffer count (32)
+ * \param buf_len optional buffer length, must be multiple of 512,
+ * set to 0 for default buffer length (16 * 32 * 512)
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev,
+ rtlsdr_read_async_cb_t cb,
+ void *ctx,
+ uint32_t buf_num,
+ uint32_t buf_len);
+
+/*!
+ * Cancel all pending asynchronous operations on the device.
+ *
+ * \param dev the device handle given by rtlsdr_open()
+ * \return 0 on success
+ */
+RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RTL_SDR_H */
diff --git a/rtlsdr/rtl-sdr_export.h b/rtlsdr/rtl-sdr_export.h
new file mode 100644
index 0000000..69e178d
--- /dev/null
+++ b/rtlsdr/rtl-sdr_export.h
@@ -0,0 +1,47 @@
+/*
+ * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
+ * Copyright (C) 2012 by Hoernchen <la at tfc-server.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RTLSDR_EXPORT_H
+#define RTLSDR_EXPORT_H
+
+#if defined __GNUC__
+# if __GNUC__ >= 4
+# define __SDR_EXPORT __attribute__((visibility("default")))
+# define __SDR_IMPORT __attribute__((visibility("default")))
+# else
+# define __SDR_EXPORT
+# define __SDR_IMPORT
+# endif
+#elif _MSC_VER
+# define __SDR_EXPORT __declspec(dllexport)
+# define __SDR_IMPORT __declspec(dllimport)
+#else
+# define __SDR_EXPORT
+# define __SDR_IMPORT
+#endif
+
+#ifndef rtlsdr_STATIC
+# ifdef rtlsdr_EXPORTS
+# define RTLSDR_API __SDR_EXPORT
+# else
+# define RTLSDR_API __SDR_IMPORT
+# endif
+#else
+#define RTLSDR_API
+#endif
+#endif /* RTLSDR_EXPORT_H */
diff --git a/testfiles/modes1.bin b/testfiles/modes1.bin
new file mode 100644
index 0000000..62f7f97
--- /dev/null
+++ b/testfiles/modes1.bin
@@ -0,0 +1,13 @@
+�~}}�~�~����~�~�������~~~�~~~���~�~����~�~}�~�}��x�n{�qzw{t|syvu|��u|v�x�p�|�x�{��������������������~�|�{~�f�t�x�v�~}�����}���������~}��{�w�z�t�v�r}y{rzyvuy{qzy~f�p�}x�y�~������������|����|�~�t�s��y�g~s}xwr��{ygvr~x�o��y�q�|�~~�������������������}��~���}��}pxp��}~�����v���}}�������}������~~~���������}�l�s��~�|���}}�����~��xf~}~�hp�{e~~���~�~���~|����||��~�~��~~~~~�wh}|���~���~�}�~�c��y�l��� [...]
�X�q�m�h���ɦ�����ט�}�e�Qu��_� �8zK{(dWY4We*H��Ws �8�[�.Îvi�d��ʔ��ӧ�����͞�x�x�h�Ep��J�.�`n g7^VK:Pl5WM�*{Y�#��s]�W�|�}���vn������̞�y�x�s�"�I�Q�0{N�+h[M&��R^*Y��|}}~~~���~���������������������~�~����$�b�
}_�~{~}%Mfs
y\���{����~�m聜y�䐛̿������{�_�p�;�e�"�\~�l? ms�Lex{ �l�)�d��yR�o���}~�䐗������|��i�t�B�g�*�^�
�Z[$q]6;fi!Y hj��|
�f�F�l�g�y����ݑ�˾��y}�}�_�4�o�)�`�xzbQ'ju�~c`|
�]�+��x�|t끡���Ę�ݨ���y��L�n�/� i��jucB.is
c]v
�]��|+�5�}�{������̟�t|��y�s�� [...]
+�h�.�i�K�{�}���Ȕ�֨�����o�N�n�;�f�#~l}a>u{xu'Vaz!�e���y�z�\�������ϔ�ѫ����q�x�P�n�}��wr�yR0%p{|y^ �h�*�f�B�w�|�tᄜ�ӎ�����ߨ���v��N�3�j�*�`�&|a[1kd>Een%dby$�o�x�9�u�V�p������َ����|���m�v�@�$�h�&�bp"wn|y9=gp&df|�
+�u�z7�W�}����~����Ȍ���ሞ|�X�t�3��k�|l~yP*ot��}}�~����}���������������~��~~���~�~�~~|~}�~��>�JmKd>��wy��=sKwJ�;��}z����~t����um�����������d�m�V�X�I�H~Kw3��gI"Ec��6sBU� �[�\�ZŇpt�����sr���������so��\�\�K�J�E�BkGkDUWVQHjAgS| �H��tK�]�s�r�������������ˑi|�o�f�e��R�G�/��e;^Ibc
+,?n��5�F�R�J�f�9�w�o�ȑ������������q�_r��L�L�U� s?��^?XL\f @Ov?�?�Q�?��u^�U���u��������ɝox���X�Xt��D�L�F�@kLg at e]Be��7{E�M�F�^�V�q�j����������������x�mk��R�W�H�E|G{=bN\E`b2=q��9�C�^��f�m�p�k�����ٯ���Ѓj��b�b�O�L�H�AoJk>i]ReHo/r�;�I�^�U�p�n����Ó�������Ζoy�|�J�Ov��B�H{H{:sZ6OU��||}����������������������~~}~�~~~}~~~�}|��~����������yy��z��x�x�j�ex��}�~|~��jWee��RgdvU�]�_�5�q�j�r�}�}�����zv��̣�~t��v�t�m�=�k�X�X�xWT6fo��Pjb{^*�f��v_�s�y�z�����������������u�V�g}��R� [...]
��o�0�r���^�r�}������Ԏ�Ӣ���~��R��u�,�p~rynN2ol3Sju%{qw{�.�n�M�s��||�����~~����~~~�����~�~~����}��~~~������~{�{��z��|~��Ι��~�|{��e�@��P�5��bNeJ��{~��~~���}�������}~���~����~�{x��k�Q{��}�~�}�����~��~��~���~����������~�~��Kz��W�9��|��~�������~��{|��ů��}}���~~�|}��[�=~��|���{���<yTv��y~��~~�~��~��~~��~�}����}~��}~�~�qbe5jn>C�~y��{`�2�l�?��{~�~��||����ѩy��|�d�j�g�G�b�2|b~-shR-��ms
I&j�{\�/�r�+�O�z�u�{w�����u��Փ�~�_�Y{��_�;�`�+tbe-jjG at cv4]`�(�n��7��tx�c͆��ׅ��緾��ܞs��s�h�v�&�4~��hv t/ngD3��hwa.sf�&��zq�=�`�~|�����ʗ�����Ж�~�jz��e�@�o��-saj.lhJ?js&\�z^�-�l�8�|}~��~~�����������~���~~�~��~��}}�~~��x~��n�J~��}���~�}����~��~~�����~�͂�~z��~{��r�U��������|{��Y]VS��z|�~�~~�~~�������������~~�����~��~�����t|���}y��F�]y��z~�M<vh��+_vuy�)�y}�}�����~~����������~��~������a5zh��{�~~�~� [...]
+iq}(�e��|@�H�z���t���ҍ�{~��w�{�M�q�6�k�)�g}�T(yk5H@ry(|e}4�f�H�j��zs�{��Є�|w�~~~~��������~�~��~~~��{������~��������|u���~}��|��z�|��v�)��wmg*��flCM��~�~�~��~��~�~�~~����}~��u�7�}}�~����~�~��~~��~��~�~~~����~�}��]{��f�9��~����~������~���~��~�~~�����ņ�{{�������{x��k�B}��}����z~��IhLf��z|�}}~�������~�~��������~�~~~~�~���~��3�i� �}~|�~ms7Jkz!o{{}|~~��}|�y���~���������ړ���l�t [...]
+S]y� �g�%�d��uX�����x~�嗗���}~��`�r�7�a�
�W�~UT mY.9^gW kd��x�b�E�j�f�����旡t|鹟���e�p|��/� �h�}R��P
mmNVu�{� �k��x:�N�|��셪�ٓ���w��}�|�P�j�,�[��WmwVD*g\%J[hoW~�}x}~}~�}~~~~~}�~�~���������������~���~}���Ex/{Q�&��{x��qv�yЎ���|t�����~s��z�bj��T�E�N�)�]f d:\TI=Nl2ZQ��~S��Mko�؉�������شqt���o�Vp��T�6�`� t!��cES;Rc8ONy+oN�+�W�;�i�X�{����vn������֚���g�`�c�5q��E�)z^Y J.��NX6WM}(vO�,�[�?�m�`Є��֘��Ϧ�ö��ܓ�~�[�Mt��O�0�b� n1kTF'��^b M �zB�4�^�F�r�iځ����Ʀ�����ў�{� [...]
+e6��xV�D�j�W�z�u�������wy���ȉz��|�I�\�`�7���Q};kabAblETiwp?�\�=�k�J��wv�����������ȭs���{�w�?�J��S�@x\s:hcZE_m at Zjzy5��|b�0�[�y�oą���y{����Ɩx��z�v�j�W�`�B�i�o4��oa(0LYWx=q_�9�g�B��ss�mǁ���������q}��dž�|�J�]�`�F�a�-��xbD
JK�VsAk\�9�e�F�m�\�x�{���ux�����������y�hz��m��E�^z/��ua.)OW]s<ib�.��x]�M�u�^�x�����sw������œ�w�p�v�.�N�\�;~ct1���zz�~}|~~~~~~��~~�~����������~�~��<�\fLmQ��zw�}9uXyL�O��{x�|~�xx����xp���������{�f�u�R�e�E�U}F}G��`G.'\h�~7uTyJ�
�f�X�]��pw� [...]
?dq>|QyN�I��wY�W�|�|y���됝����n~ˀ��j�5�l�D�XG~D��_E++[h�8wVwF�Q�Y�Z�k�`�y���������������~q��W�i�J�Y}E�QgHrQ^\
3Vn�|5�T�R��h�c�j�}n�ˉ����ږ������r�{�X�k�N�L~�x3�V_PlPNbZ`Rvy\~H�P�_�Z��rx���zz���Ƭ�x����e�l{��R��Q��p;yX\U[S��{y~~~}~}�~}~��~�������~�����������~~����~�;��yoa>��}~�����������~��~���}�Ā�z��~~~��}mm<�|~}~}�{��{Z�O��|{��~������~�����~���~~��|��~����}�|��~�����~Cqfy��{��|�^�{�|~�~|����u|��Ďr��v�jx���~��������� [...]
�\��nk�{�������os���������i�h�U�R�H�EzGw:n[/HW��:gFtH�;�]�
�d�l�i�����ui�����������t�ch��J�I�\� r=��^>\NKeB^Zx }>��uC�T�m�e����ƌ�����luŚ���{�\�Wq��E�F�Y� sOcK`FWd7U��It �C��zQ�/�p�x���zg�����������{�{�g�On��T� {<��h<dIT_NTJq;o[� �T�V�O�p�h���������po�����|Á�g�d�T�I�^� s<��`AXKbj :MsI~-��yE�Q�m�O�{����¢����������}g��_�]�Q�H�K�9y[F `TQ\IWPw.w��M��T��sc�j���{s���꼡sx���}�fn��T�T�R�6y��z~������������~�~�~�~~�~~~~��~�������bd��_t]}��}������~~��~~��}|��v [...]
+��n�?�y�{�{䂒���၀��ۤ���u�{�E�-�~�n�!�fk&rdC1qoF
`}~n� �o�+�?��}���~����֓�ԫ��ဉ�~�:�$�z�v�swmH'~xty"Xnz��l�-�w�x�a�|����и��ߊ���]�B�}�q�'�g~$wbY+nh8Cfq%ec}%�c�0�i�K�r�s�}���ꄁ����|�U�v�&��|�qr|o@ &*x}zwm�k�*�g�H�s�l�~��ڇ��χ������{�`�q�@�j�"�t�{_Isp.Cxxv{|m��$�s�M�y�|��ㄌ}}����}����������~�}���}�������������~�~��A�n~�
�n~��{�~�������~���������uz����vx��~��}��|�&�o}�{}~{��^w(u��{~��~~�~�~����~�}��������������� [...]
|ps>l[��GKesIl
se��z@�l�_�r�v�~�zw���������}�}�l�C�g{��7�`}F`lk��ASetGp
+~k�D�b�\�m��rtč퐛|x��ܼ�����{p��^�l�R��l�>vW��V at jnPaYa��}7�g�U�j�i�g�}x�Ï����������o�w�V�g�I�`�C�\hErbQTciLkh`��~@�+�s��uo�}����閑yy�������u�px��U�#�j�>�_kGs^TRggGiatE�V��}D�5�t�l�y���������wt��}�p�kr��F�d�L�
ylfDg]��CX_u��{~��������������~�����~~�~���~�}}|}~���~~����M^��~|��|^�Z��qk�y�n����}w���~~����}z����������~~~�~����~~�����~w���~x����~���������~~���������~������������\��rx����x���~�~��~~����������wyCb|�~�������xu�t��x~������x���|v��~�~~~~��~��}~���~���}�~��~�~����������Z��z~���{x����st����o}�y�vv��}����?~p���}~����������}����������~~�~��~x���{x���~��������~���~�~ [...]
gl)?bomm�y��z�x�T�z����눌��⾗����t�w�H�u�u��e� ] xyzp-3aq_]{�e�
�
�p�U�z��鉡�ܓ������y�V�l�2�a��ctQ vv~x. Qv�r|�h��+�}�x�z�ᑝ�ȕ������|�}�C�q�� |s�{b @pi(7swr|w^�
�h�%�8���z�������~��������~�|�z�9�l�� ~r���|�~��~}~�~��~���~�}�������~������~�~�d�C�l��x��}��䑟�ğ�vz���~�}���&�`�
�Wm
vWD+g_'K_nn �g��{�#�s�h�y��接zy�М�����|�a�ly��"�_�
�Wj
tV?-hb%G Ne|�Y�)�]��vI�b���{{�ꓛ���Ζ�눫xs��H��f����\l> km��I\s~U�"�Y�:�g�`�s��慨������߲��r|�t�E�j��� �^��Wf
4 hm��H `c��Y�0�`��tS�p���yz�䗗wr��}�{�}~���~�~���}��~�|��~����� [...]
�c��od�{�����������ns�����lq��N�Z�R� |I��d.kTMRU[AjLo;�H�D�O�X�a�p�n���wo�ȟ����������s�`�Sn��4�K�Kn bR��UG1[t<s8��=��i�a�pĄj�ŵ���uu�������v�t�V�`�L�>{��)tLlP&^eBYOh=�GF�B��qP�i�y�~��r��~�����~~�����}�~~���~�~~�~�����}~�~�~��O�E��pU�i��t}��~��~����~~~����~��}{��saQ>��|~����{��xP�^��v|����~���|�Ȫ��x}��~��������������������~����~~~�������\�{�~�݈���ɺ�}��ڂx��x�Rz���~{���Np>��}y����~����~~����~�����~��~x��~�hy~��~��~}~ [...]
�v�}{G�b�}��܍�y{��뿒�ی�z{��W�,�n����zj\-nd>Gil(ed��} �i�6�8�w�l�|��ԉ��ƕ�y�ܝ���t�vy�����~���~����������~��~�~~~���������~[Ccd��/`b�}(�p�}Eɀ�z�{�}~��x|��ɘzy��~�~������~�������}��{w�hт����~�~�~����~�������~~���������tՁ���ʁ|~���~����~��~~����~���~-�h��}z����z��Ѓ~}�~��~��}u��k�Xu��~���}��~�~���~�����~�����~~~}��~~��;Mhw%l~�|��{l�F�s�dځ�~~�~���}�����x~z��9�4�d�,av(s`S3mm/ [...]
+AW��2r9yS� �F��qZ�_���zĝ��ű������x�|�l�*�Fv��3�AoEr5g[ UYGl)f��C� �H��v]�N�u�����ui���������u�w�l�#�X�C�?|H|'��dJ
AW��Fr �8��tG�L�l�R�w�����tj�������r�t�j�$�By��H� |FgJP4��O] NHuD�*��yP�#�d�y�rǑ���wm���̵��|Ƅ�a�e�O�L�K�+��j6g<c`N`Es't��H� �L��mi�pń�����������Ԛf~�l�h�f��>|��3�6oY5 JJ��Lb ]4��|K��O��si�r���uo���������}ͅ�s�9�_�I�E�F�7oMb,��XT 18l�}6�=�R�A�f�Y�{�~���tp����vu��}�}�~~~~~}~�~���~�~~}~}�}~��~|~~z|��~�~���E}7yT�-��zy�rw�rȏ���~u���~u��x�ii��R�M�L�1^k l [...]
+?<��Wo,ii� �-��wd�R�y�nҋ��ҙ��ġ�ȫ��؆�w�P�Hy��Y�3|[y'vk=J at an'U��a} �+��|j�8�a�}���|r���¡�ǫ��؇�x�L�S�^�:�_�{�xeB
?8��er ]
�|[�5�o�7�c�t���ї��Ý�ǫ��ن�z�N�H{��h� �.y^b"��nh26]^y~��d��EɅvy�vՂ����ŗ�зzy���y�l�j�P�d�({�}Vt+ul7
ED`n$\��c�.��~o�E�l�u���͖�����̠�~��o�S�p��2�_z w�vg)=K]r0cb����h�#�OӁt|��ׇ�������ɨ��ԉ�p�c�f�E�e�#}�wXk0gfL=as0Xh} ~"��i�&�U�|�tӈ��ڂ}���}�}}�}�~}��}~������~��~�������~�������DkHjM�5��v��vf�e����ˀq���~�r}��̀i��a�c�Q�J�\� �MnL_5��W[?<q��J� [...]
gfD�S��vN�h�q�q��������������s|�}�Y�u�U�Yw��B�o[��UHlcDc[eNy
+�Y��|R�:�p��s|Ń�����������𢚅�r�z�[�k�M�_�D�YmFxX]S]Y��EdlfE�R��vQ�i�t�s�����������pyȎ���n�v�b�0�a��7�[tOOqlQYVe��8y[J�Y�[�8�q��vx��xv��ݿ��t~���e�u�[�]v��=�^rG{X]Pm[J`]fNtxf�H�Q��wX�W�y�����������������p~�p�K�fy��J�}[��c<n^]]<_r��{}�~��~�������~������~�}�}��~~~}��~���������GSjp?q_y��z��}J�l�k�p�~~��|x��{~����t~Ȃ���g�u�Q�'�l�;�^��c:?(ip��7euj��z;�h�Y�N�y�ā�~t���ϗ�x~Α���t�Z�p{��>�h�=�`k at y_TLpcDabp<}`|C��m��x^�u�����������������u��_�t�O��n�9�\��[:qiNX
Jg}�2�d� [...]
kL��g](:Ik��U{�K��{`�?�o�|�~�q���خ�rz�����z�]�m�b�Mw��N�E}�{~~~�}~�~~��~~������~~���}}�~~�~��������w|�}x��[�d�V�Mz�z��|}��U^Nd��Nw�S��vS�`�q�j������������������y�~�f�k�V�]�O�RwP}LdViTUdZ_NwRsP�O�X�S�i�H�s����}p���՜�����n�s�r�i�0�V|��C�TkRmQhe'0`lOsBv�~E�R�f�4�l�x�t�����zq��ھ��u}���k�gx��]� �P��yDzSaXfWUfXbQ{PrP�O�Z�V�i�f�|�v�����������������l�n�i�%�a�P�B��kFpV[aY\_r e\~O�K�\�X�n�k��q�������ੜr������m�~�g�g�V�W�O�NpQuN`\cXSlUdNMwS�Q�_�Y�o�i���|��������� [...]
+�]�Y�S��||��~���~����~�~������}~~�~�~~~~���������~����}�w}���}w~z���|y�{�|}���{~���~��~�d�|��||�����y}����|~�~�����|��M�Q~�{~~���~��~�}�~���}��~�~~��~~����~��}��e�|��ˁ��������Ɉ�}��~|��t�2}v�yd%R2{}zy,R'k}�r�7�o�M�q�d�{��dž���������Ɨ���x�y�]�o�H�j�:�fy9xhZ>ur9G)Wz�o|3�l�C�o�W�w�q���ƅ������~��͊�}�h�S�~�v�9�n�'u"�}ztPEmt>ahx8}j�<�k�K�q�c�z��ɂ��ѷ˄���נۈ�}��V�H�p�?�j�3�xyuZ<ppCVjv9qg~7�n�6�=��|�pڈ܀�������ϩښ�~���c�Q�q�D�i�:�iq3~w [...]
\ No newline at end of file
diff --git a/tools/debug.html b/tools/debug.html
new file mode 100644
index 0000000..4d56d34
--- /dev/null
+++ b/tools/debug.html
@@ -0,0 +1,193 @@
+<!DOCTYPE html>
+<html>
+<body>
+<head>
+<script>
+var frames = [];
+var currentFrame = 0;
+
+var modes_checksum_table = [
+0x3935ea, 0x1c9af5, 0xf1b77e, 0x78dbbf, 0xc397db, 0x9e31e9, 0xb0e2f0, 0x587178,
+0x2c38bc, 0x161c5e, 0x0b0e2f, 0xfa7d13, 0x82c48d, 0xbe9842, 0x5f4c21, 0xd05c14,
+0x682e0a, 0x341705, 0xe5f186, 0x72f8c3, 0xc68665, 0x9cb936, 0x4e5c9b, 0xd8d449,
+0x939020, 0x49c810, 0x24e408, 0x127204, 0x093902, 0x049c81, 0xfdb444, 0x7eda22,
+0x3f6d11, 0xe04c8c, 0x702646, 0x381323, 0xe3f395, 0x8e03ce, 0x4701e7, 0xdc7af7,
+0x91c77f, 0xb719bb, 0xa476d9, 0xadc168, 0x56e0b4, 0x2b705a, 0x15b82d, 0xf52612,
+0x7a9309, 0xc2b380, 0x6159c0, 0x30ace0, 0x185670, 0x0c2b38, 0x06159c, 0x030ace,
+0x018567, 0xff38b7, 0x80665f, 0xbfc92b, 0xa01e91, 0xaff54c, 0x57faa6, 0x2bfd53,
+0xea04ad, 0x8af852, 0x457c29, 0xdd4410, 0x6ea208, 0x375104, 0x1ba882, 0x0dd441,
+0xf91024, 0x7c8812, 0x3e4409, 0xe0d800, 0x706c00, 0x383600, 0x1c1b00, 0x0e0d80,
+0x0706c0, 0x038360, 0x01c1b0, 0x00e0d8, 0x00706c, 0x003836, 0x001c1b, 0xfff409,
+0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
+0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
+0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000
+];
+
+function modesChecksum(frame) {
+ var crc = 0;
+ var bits = frame.bits;
+ var offset = (bits == 112) ? 0 : (112-56);
+
+ for(var j = 0; j < bits; j++) {
+ var byte = j/8;
+ var bit = j%8;
+ var bitmask = 1 << (7-bit);
+
+ /* If bit is set, xor with corresponding table entry. */
+ if (frame.hex.charCodeAt(byte) & bitmask)
+ crc ^= modes_checksum_table[j+offset];
+ }
+ return crc; /* 24 bit checksum. */
+}
+
+function getFrameChecksum(frame) {
+ var res = "";
+ for (j = 0; j < frame.hex.length; j++) {
+ var val = frame.hex.charCodeAt(j);
+ var h = val.toString(16);
+ if (h.length == 1) h = "0"+h;
+ res += h;
+ }
+ return res;
+}
+
+function displayFrame(i) {
+ var div = document.getElementById("frame");
+ var msgbits = 8+112;
+ var frame = frames[i];
+ var padding = frame.mag.length - msgbits*2;
+
+ /* Remove the old representation. */
+ var nodes = div.childNodes.length;
+ for(var j = 0; j < nodes; j++) {
+ div.removeChild(div.firstChild);
+ }
+
+ /* Display the new one. */
+ for (var j = -padding; j < msgbits*2+padding; j++) {
+ var m = frame.mag[j+padding];
+ var type;
+
+ if (j < 0) type = "noise";
+ if (j >= 0 && j < 16) type = "pre";
+ if (j >= 16) {
+ if (!(j % 2)) {
+ var next = frame.mag[j+padding+1];
+ if (m > next)
+ type = "one";
+ else
+ type = "zero";
+ }
+ var bit = (j-16)/2;
+ if (bit == frame.fix1 ||
+ bit == frame.fix2)
+ type = "err";
+ }
+ var sample = document.createElement("div");
+ sample.setAttribute("class","sample "+type);
+ sample.setAttribute("title","sample "+j+" ("+m+")");
+ sample.style.left = ""+((j+padding)*4)+"px";
+ sample.style.height = ""+(m/256)+"px";
+ div.appendChild(sample);
+ }
+ document.getElementById("info").innerHTML =
+ "#"+currentFrame+" "+frame.descr+"<br>"+
+ "Bits:"+frame.bits+"<br>"+
+ "DF : "+(frame.hex.charCodeAt(0) >> 3)+"<br>"+
+ "fix1: "+frame.fix1+"<br>"+
+ "fix2: "+frame.fix2+"<br>"+
+ "hex : "+getFrameChecksum(frame)+"<br>"+
+ "crc (computed): "+modesChecksum(frame).toString(16)+"<br>";
+}
+
+function recomputeHex(frame) {
+ var padding = frame.mag.length - (112+8)*2;
+ var b = [];
+ var hex = "";
+
+ /* Get bits */
+ for (var j = 0; j < frame.bits*2; j += 2) {
+ var bit;
+ var l = frame.mag[padding+j+16];
+ var r = frame.mag[padding+j+1+16];
+ if (l > r)
+ bit = 1;
+ else
+ bit = 0;
+ b.push(bit);
+ }
+ /* Pack into bytes */
+ for (j = 0; j < frame.bits; j+= 8) {
+ hex += String.fromCharCode(
+ b[j]<<7 |
+ b[j+1]<<6 |
+ b[j+2]<<5 |
+ b[j+3]<<4 |
+ b[j+4]<<3 |
+ b[j+5]<<2 |
+ b[j+6]<<1 |
+ b[j+7]);
+ }
+ frame.hex = hex;
+}
+
+window.onload = function() {
+ document.getElementById("next").onclick = function() {
+ if (currentFrame != frames.length-1) currentFrame++;
+ displayFrame(currentFrame);
+ }
+ document.getElementById("prev").onclick = function() {
+ if (currentFrame != 0) currentFrame--;
+ displayFrame(currentFrame);
+ }
+ document.getElementById("re").onclick = function() {
+ recomputeHex(frames[currentFrame]);
+ displayFrame(currentFrame);
+ }
+ displayFrame(currentFrame);
+}
+</script>
+<script src="frames.js"></script>
+<style>
+#frame {
+ width: 1024px;
+ height: 255px;
+ border: 1px #aaa solid;
+ position: relative;
+}
+.sample {
+ position: absolute;
+ bottom: 0px;
+}
+.pre {
+ width:4px;
+ background-color: orange;
+}
+.one {
+ width:4px;
+ background-color: #0000cc;
+}
+.zero {
+ width:4px;
+ background-color: #aaaaaa;
+}
+.err {
+ width:4px;
+ background-color: #cc6666;
+}
+.noise {
+ width:2px;
+ background-color: #ffffff;
+ border: 1px #aaa dotted;
+}
+</style>
+</head>
+<div id="frame">
+</div>
+<pre id="info">
+</pre>
+<input type="button" id="prev" value="Prev frame">
+<input type="button" id="next" value="Next frame">
+<input type="button" id="re" value="Recompute Hex">
+</body>
+</html>
diff --git a/view1090.c b/view1090.c
new file mode 100644
index 0000000..0b9b8b9
--- /dev/null
+++ b/view1090.c
@@ -0,0 +1,321 @@
+// view1090, a Mode S messages viewer for dump1090 devices.
+//
+// Copyright (C) 2014 by Malcolm Robb <Support at ATTAvionics.com>
+//
+// 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 "coaa.h"
+#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
+}
+//
+// =============================== Terminal handling ========================
+//
+#ifndef _WIN32
+// Get the number of rows after the terminal changes size.
+int getTermRows() {
+ struct winsize w;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+ return (w.ws_row);
+}
+
+// Handle resizing terminal
+void sigWinchCallback() {
+ signal(SIGWINCH, SIG_IGN);
+ Modes.interactive_rows = getTermRows();
+ interactiveShowData();
+ signal(SIGWINCH, sigWinchCallback);
+}
+#else
+int getTermRows() { return MODES_INTERACTIVE_ROWS;}
+#endif
+//
+// =============================== 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 = getTermRows();
+ 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) {
+
+ pthread_mutex_init(&Modes.pDF_mutex,NULL);
+ pthread_mutex_init(&Modes.data_mutex,NULL);
+ pthread_cond_init(&Modes.data_cond,NULL);
+
+#ifdef _WIN32
+ if ( (!Modes.wsaData.wVersion)
+ && (!Modes.wsaData.wHighVersion) ) {
+ // Try to start the windows socket support
+ if (WSAStartup(MAKEWORD(2,1),&Modes.wsaData) != 0)
+ {
+ fprintf(stderr, "WSAStartup returned Error\n");
+ }
+ }
+#endif
+
+ // 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();
+}
+
+// Set up data connection
+int setupConnection(struct client *c) {
+ int fd;
+
+ // 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) {
+ anetNonBlock(Modes.aneterr, fd);
+ //
+ // 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.
+
+ c->next = NULL;
+ c->buflen = 0;
+ c->fd =
+ c->service =
+ Modes.bis = fd;
+ Modes.clients = c;
+ }
+ return fd;
+}
+//
+// ================================ 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 <num> Max number of rows in interactive mode (default: 15)\n"
+ "--interactive-ttl <sec> Remove from list if idle for <sec> (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 <IPv4> TCP Beast output listen IPv4 (default: 127.0.0.1)\n"
+ "--net-bo-port <port> TCP Beast output listen port (default: 30005)\n"
+ "--lat <latitude> Reference/receiver latitide for surface posn (opt)\n"
+ "--lon <longitude> 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"
+ );
+}
+
+#ifdef _WIN32
+void showCopyright(void) {
+ uint64_t llTime = time(NULL) + 1;
+
+ printf(
+"-----------------------------------------------------------------------------\n"
+"| view1090 ModeS Viewer Ver : " MODES_DUMP1090_VERSION " |\n"
+"-----------------------------------------------------------------------------\n"
+"\n"
+" Copyright (C) 2012 by Salvatore Sanfilippo <antirez at gmail.com>\n"
+" Copyright (C) 2014 by Malcolm Robb <support at attavionics.com>\n"
+"\n"
+" All rights reserved.\n"
+"\n"
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+" ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n"
+" For further details refer to <https://github.com/MalcolmRobb/dump1090>\n"
+"\n"
+ );
+
+ // delay for 1 second to give the user a chance to read the copyright
+ while (llTime >= time(NULL)) {}
+}
+#endif
+//
+//=========================================================================
+//
+int main(int argc, char **argv) {
+ int j, fd;
+ struct client *c;
+ char pk_buf[8];
+
+ // 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);
+ }
+ }
+
+#ifdef _WIN32
+ // Try to comply with the Copyright license conditions for binary distribution
+ if (!Modes.quiet) {showCopyright();}
+#define MSG_DONTWAIT 0
+#endif
+
+#ifndef _WIN32
+ // Setup for SIGWINCH for handling lines
+ if (Modes.interactive) {signal(SIGWINCH, sigWinchCallback);}
+#endif
+
+ // Initialization
+ view1090Init();
+
+ // Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here.
+ c = (struct client *) malloc(sizeof(*c));
+ if ((fd = setupConnection(c)) == ANET_ERR) {
+ fprintf(stderr, "Failed to connect to %s:%d\n", View1090.net_input_beast_ipaddr, Modes.net_input_beast_port);
+ exit(1);
+ }
+
+ // Keep going till the user does something that stops us
+ while (!Modes.exit) {
+ interactiveRemoveStaleAircrafts();
+ interactiveShowData();
+ if ((fd == ANET_ERR) || (recv(c->fd, pk_buf, sizeof(pk_buf), MSG_PEEK | MSG_DONTWAIT) == 0)) {
+ free(c);
+ usleep(1000000);
+ c = (struct client *) malloc(sizeof(*c));
+ fd = setupConnection(c);
+ continue;
+ }
+ modesReadFromClient(c,"",decodeBinMessage);
+ usleep(100000);
+ }
+
+ // The user has stopped us, so close any socket we opened
+ if (fd != ANET_ERR)
+ {close(fd);}
+
+ return (0);
+}
+//
+//=========================================================================
+//
diff --git a/view1090.dsp b/view1090.dsp
new file mode 100644
index 0000000..de97af8
--- /dev/null
+++ b/view1090.dsp
@@ -0,0 +1,149 @@
+# Microsoft Developer Studio Project File - Name="view1090" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=view1090 - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "view1090.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "view1090.mak" CFG="view1090 - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "view1090 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "view1090 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "view1090 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /machine:I386
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "view1090 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "view1090 - Win32 Release"
+# Name "view1090 - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\anet.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\interactive.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mode_ac.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mode_s.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\net_io.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\view1090.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\dump1090.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\view1090.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\winstubs.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Group "Library Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\pthreads\pthreadVC2.lib
+# End Source File
+# Begin Source File
+
+SOURCE=.\rtlsdr\rtlsdr.lib
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/view1090.h b/view1090.h
new file mode 100644
index 0000000..03ad96d
--- /dev/null
+++ b/view1090.h
@@ -0,0 +1,84 @@
+// view1090, a Mode S messages viewer for dump1090 devices.
+//
+// Copyright (C) 2013 by Malcolm Robb <Support at ATTAvionics.com>
+//
+// 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 <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <pthread.h>
+ #include <stdint.h>
+ #include <errno.h>
+ #include <unistd.h>
+ #include <math.h>
+ #include <sys/time.h>
+ #include <sys/timeb.h>
+ #include <signal.h>
+ #include <fcntl.h>
+ #include <ctype.h>
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #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
diff --git a/winstubs.h b/winstubs.h
new file mode 100644
index 0000000..f416668
--- /dev/null
+++ b/winstubs.h
@@ -0,0 +1,110 @@
+// dump1090, a Mode S messages decoder for RTLSDR devices.
+//
+// Copyright (C) 2014 by Malcolm Robb <support at attavionics.com>
+//
+// 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.
+//
+// This file provides basic Windows implementation of Linux specific functions
+// used in the dump1090 project. This allows dump1090 to be compiled and debugged
+// using Microsoft Visual C++ 6.0
+//
+// Note that not all functions actually provide equivalent functionality to their
+// Linux equivalents. They are simply stubs to allow the project to compile.
+//
+#ifndef __WINSTUBS_H
+#define __WINSTUBS_H
+
+#include <winsock2.h>
+#include <windows.h>
+#include <basetsd.h>
+
+typedef UCHAR uint8_t;
+typedef USHORT uint16_t;
+typedef UINT32 uint32_t;
+typedef UINT64 uint64_t;
+typedef UINT32 mode_t;
+typedef long ssize_t;
+typedef int socklen_t;
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/timeb.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <io.h>
+#include <fcntl.h>
+
+#define M_PI 3.14159265358979323846
+#include <math.h>
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//Functions not included in the MSVC maths library. This will do for our use.
+_inline double round(double d) {return floor(d + 0.5);}
+_inline double trunc(double d) {return (d>0) ? floor(d):ceil(d) ;}
+
+//usleep works in microseconds, and isn't supported in Windows. This will do for our use.
+_inline void usleep(UINT32 ulSleep) {Sleep(ulSleep/1000);}
+_inline uint64_t strtoll(const char *p, void *e, UINT32 base) {return _atoi64(p);}
+_inline int inet_aton(const char * cp, DWORD * ulAddr) { *ulAddr = inet_addr(cp); return 0;}
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+
+_inline void cls() {
+ HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ COORD coord = {0, 0};
+ DWORD count;
+
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ GetConsoleScreenBufferInfo(hStdOut, &csbi);
+
+ FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
+
+ SetConsoleCursorPosition(hStdOut, coord);
+}
+
+_inline int gettimeofday(struct timeval *tv, struct timezone *tz) {
+ SYSTEMTIME stSystemTime;
+ GetLocalTime(&stSystemTime);
+
+ tv->tv_sec = stSystemTime.wSecond + (60 * (stSystemTime.wMinute + (60 * stSystemTime.wHour)));
+ tv->tv_usec = stSystemTime.wMilliseconds * 1000;
+
+ return 0;
+ }
+
+#define STDIN_FILENO 0
+#define EINPROGRESS WSAEINPROGRESS
+#define EWOULDBLOCK WSAEWOULDBLOCK
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __WINSTUBS_H
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-hamradio/dump1090.git
More information about the pkg-hamradio-commits
mailing list