diff --git a/sdl1090/.gitignore b/sdl1090/.gitignore new file mode 100644 index 0000000..b04dcba --- /dev/null +++ b/sdl1090/.gitignore @@ -0,0 +1,2 @@ +*.o +dump1090 diff --git a/sdl1090/Arial.ttf.REMOVED.git-id b/sdl1090/Arial.ttf.REMOVED.git-id new file mode 100644 index 0000000..98f1e60 --- /dev/null +++ b/sdl1090/Arial.ttf.REMOVED.git-id @@ -0,0 +1 @@ +ab68fb197d4479b3b6dec6e85bd5cbaf433a87c5 \ No newline at end of file diff --git a/sdl1090/Makefile b/sdl1090/Makefile new file mode 100644 index 0000000..4da4694 --- /dev/null +++ b/sdl1090/Makefile @@ -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 `sdl2-config --libs` -lSDL2 -lSDL2_ttf +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 input.o draw.o font.o init.o + $(CC) -g -o view1090 view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o input.o draw.o font.o init.o $(LIBS) $(LDFLAGS) + +clean: + rm -f *.o view1090 diff --git a/sdl1090/README.md b/sdl1090/README.md new file mode 100644 index 0000000..3fdd396 --- /dev/null +++ b/sdl1090/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 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 and is +released under the BSD three clause license. diff --git a/sdl1090/TODO b/sdl1090/TODO new file mode 100644 index 0000000..041ad29 --- /dev/null +++ b/sdl1090/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/sdl1090/anet.c b/sdl1090/anet.c new file mode 100644 index 0000000..859c98c --- /dev/null +++ b/sdl1090/anet.c @@ -0,0 +1,345 @@ +/* anet.c -- Basic TCP socket stuff made a bit less boring + * + * Copyright (c) 2006-2012, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _WIN32 + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#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/sdl1090/anet.h b/sdl1090/anet.h new file mode 100644 index 0000000..6d74af5 --- /dev/null +++ b/sdl1090/anet.h @@ -0,0 +1,59 @@ +/* anet.c -- Basic TCP socket stuff made a bit less boring + * + * Copyright (c) 2006-2012, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANET_H +#define ANET_H + +#define ANET_OK 0 +#define ANET_ERR -1 +#define ANET_ERR_LEN 256 + +#if defined(__sun) +#define AF_LOCAL AF_UNIX +#endif + +int anetTcpConnect(char *err, char *addr, int port); +int anetTcpNonBlockConnect(char *err, char *addr, int port); +int anetUnixConnect(char *err, char *path); +int anetUnixNonBlockConnect(char *err, char *path); +int anetRead(int fd, char *buf, int count); +int anetResolve(char *err, char *host, char *ipbuf); +int anetTcpServer(char *err, int port, char *bindaddr); +int anetUnixServer(char *err, char *path, mode_t perm); +int anetTcpAccept(char *err, int serversock, char *ip, int *port); +int anetUnixAccept(char *err, int serversock); +int anetWrite(int fd, char *buf, int count); +int anetNonBlock(char *err, int fd); +int anetTcpNoDelay(char *err, int fd); +int anetTcpKeepAlive(char *err, int fd); +int anetPeerToString(int fd, char *ip, int *port); +int anetSetSendBuffer(char *err, int fd, int buffsize); + +#endif diff --git a/sdl1090/coaa.h b/sdl1090/coaa.h new file mode 100644 index 0000000..3d07704 --- /dev/null +++ b/sdl1090/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/sdl1090/defs.h b/sdl1090/defs.h new file mode 100644 index 0000000..0bb1727 --- /dev/null +++ b/sdl1090/defs.h @@ -0,0 +1,8 @@ +#include +#include +#include +#include "SDL2/SDL.h" +#include "SDL2/SDL_ttf.h" + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 240 diff --git a/sdl1090/draw.c b/sdl1090/draw.c new file mode 100644 index 0000000..c1ecad5 --- /dev/null +++ b/sdl1090/draw.c @@ -0,0 +1,112 @@ +#include "draw.h" +#include "parula.h" + +extern void drawString(char *, int, int, TTF_Font *, SDL_Color); + +void CROSSVP(double *v, double *u, double *w) +{ + v[0] = u[1]*w[2] - u[2]*(w)[1]; + v[1] = u[2]*w[0] - u[0]*(w)[2]; + v[2] = u[0]*w[1] - u[1]*(w)[0]; +} + +void drawPlaneHeading(int x, int y, double heading, int signal, char *flight) +{ + + if(signal > 127) { + signal = 127; + } + + SDL_SetRenderDrawColor(game.renderer, parula[signal][0], parula[signal][1], parula[signal][2], SDL_ALPHA_OPAQUE); + + double body = 8.0; + double wing = 6.0; + double tail = 3.0; + + double vec[3]; + vec[0] = sin(heading * M_PI / 180); + vec[1] = cos(heading * M_PI / 180); + vec[2] = 0; + + double up[] = {0,0,1}; + + double out[3]; + + CROSSVP(out,vec,up); + + int x1, x2, y1, y2; + + //body + + x1 = x + round(-body*vec[0]); + y1 = y + round(-body*vec[1]); + x2 = x + round(body*vec[0]); + y2 = y + round(body*vec[1]); + + SDL_RenderDrawLine(game.renderer, x1, y1, x2, y2); + + //wing + + x1 = x + round(-wing*out[0]); + y1 = y + round(-wing*out[1]); + x2 = x + round(wing*out[0]); + y2 = y + round(wing*out[1]); + + SDL_RenderDrawLine(game.renderer, x1, y1, x2, y2); + + //tail + + x1 = x + round(-body*vec[0]) + round(-tail*out[0]); + y1 = y + round(-body*vec[1]) + round(-tail*out[1]); + x2 = x + round(-body*vec[0]) + round(tail*out[0]); + y2 = y + round(-body*vec[1]) + round(tail*out[1]); + + SDL_RenderDrawLine(game.renderer, x1, y1, x2, y2); + + + SDL_Color color = { parula[signal][0], parula[signal][1], parula[signal][2]}; + + drawString(flight, x, y - 10, game.font, color); +} + +void drawPlane(int x, int y, int signal) +{ + SDL_SetRenderDrawColor(game.renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); + + int length = 3.0; + + double vec[3]; + + SDL_RenderDrawLine(game.renderer, x-length , y , x+length , y ); + SDL_RenderDrawLine(game.renderer, x , y-length , x , y+length ); +} + + +void drawGrid() +{ + SDL_SetRenderDrawColor(game.renderer, 40, 40, 40, SDL_ALPHA_OPAQUE); + + SDL_RenderDrawLine(game.renderer, 0, 120, 320, 120); + SDL_RenderDrawLine(game.renderer, 160, 0, 160, 240); +} + + +void delay(unsigned int frameLimit) +{ + unsigned int ticks = SDL_GetTicks(); + + if (frameLimit < ticks) + { + return; + } + + if (frameLimit > ticks + 16) + { + SDL_Delay(16); + } + + else + { + SDL_Delay(frameLimit - ticks); + } +} diff --git a/sdl1090/draw.h b/sdl1090/draw.h new file mode 100644 index 0000000..23c33a1 --- /dev/null +++ b/sdl1090/draw.h @@ -0,0 +1,3 @@ +#include "structs.h" + +Game game; diff --git a/sdl1090/dump1090.c b/sdl1090/dump1090.c new file mode 100644 index 0000000..60882fc --- /dev/null +++ b/sdl1090/dump1090.c @@ -0,0 +1,904 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 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 Select RTL device (default: 0)\n" +"--gain Set gain (default: max gain. Use -10 for auto-gain)\n" +"--enable-agc Enable the Automatic Gain Control (default: off)\n" +"--freq Set frequency (default: 1090 Mhz)\n" +"--ifile Read data from file (use '-' for stdin)\n" +"--interactive Interactive mode refreshing data on screen\n" +"--interactive-rows Max number of rows in interactive mode (default: 15)\n" +"--interactive-ttl Remove from list if idle for (default: 60)\n" +"--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-bind-address IP address to bind to (default: Any; Use 127.0.0.1 for private)\n" +"--net-http-port HTTP server port (default: 8080)\n" +"--net-ri-port TCP raw input listen port (default: 30001)\n" +"--net-ro-port TCP raw output listen port (default: 30002)\n" +"--net-sbs-port TCP BaseStation output listen port (default: 30003)\n" +"--net-bi-port TCP Beast input listen port (default: 30004)\n" +"--net-bo-port TCP Beast output listen port (default: 30005)\n" +"--net-ro-size TCP raw output minimum size (default: 0)\n" +"--net-ro-rate TCP raw output memory flush rate (default: 0)\n" +"--net-heartbeat TCP heartbeat rate in seconds (default: 60 sec; 0 to disable)\n" +"--net-buffer TCP buffer size 64Kb * (2^n) (default: n=0, 64Kb)\n" +"--lat Reference/receiver latitude for surface posn (opt)\n" +"--lon 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 Show and reset stats every seconds\n" +"--onlyaddr Show only ICAO addresses (testing purposes)\n" +"--metric Use metric units (meters, km/h, ...)\n" +"--snip Strip IQ file removing samples < level\n" +"--debug Debug mode (verbose), see README for details\n" +"--quiet Disable output to stdout. Use for daemon applications\n" +"--ppm 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 \n" +" Copyright (C) 2014 by Malcolm Robb \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 \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-bind-address") && more) { + Modes.net_bind_address = strdup(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/sdl1090/dump1090.h b/sdl1090/dump1090.h new file mode 100644 index 0000000..9ad4de0 --- /dev/null +++ b/sdl1090/dump1090.h @@ -0,0 +1,466 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 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.10.3010.14" + +// ============================= Include files ========================== + +#ifndef _WIN32 + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include "rtl-sdr.h" + #include "anet.h" +#else + #include "winstubs.h" //Put everything Windows specific in here + #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 + char *net_bind_address; // Bind address + 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/sdl1090/font.c b/sdl1090/font.c new file mode 100755 index 0000000..380bfc9 --- /dev/null +++ b/sdl1090/font.c @@ -0,0 +1,59 @@ +#include "font.h" + +TTF_Font *loadFont(char *name, int size) +{ + /* Use SDL_TTF to load the font at the specified size */ + + TTF_Font *font = TTF_OpenFont(name, size); + + if (font == NULL) + { + printf("Failed to open Font %s: %s\n", name, TTF_GetError()); + + exit(1); + } + + return font; +} + +void closeFont(TTF_Font *font) +{ + /* Close the font once we're done with it */ + + if (font != NULL) + { + TTF_CloseFont(font); + } +} + +void drawString(char * text, int x, int y, TTF_Font *font, SDL_Color color) +{ + SDL_Rect dest; + SDL_Surface *surface; + + // surface = TTF_RenderUTF8_Shaded(font, text, foregroundColor, backgroundColor); + surface = TTF_RenderText_Solid(font, text, color); + + if (surface == NULL) + { + printf("Couldn't create String: %s\n", SDL_GetError()); + + return; + } + + SDL_Texture* Message = SDL_CreateTextureFromSurface(game.renderer, surface); //now you can convert it into a texture + + SDL_Rect Message_rect; //create a rect + Message_rect.x = x; //controls the rect's x coordinate + Message_rect.y = y; // controls the rect's y coordinte + Message_rect.w = surface->w; // controls the width of the rect + Message_rect.h = surface->h; // controls the height of the rect + + SDL_RenderCopy(game.renderer, Message, NULL, &Message_rect); //you put the renderer's name first, the Message, the crop size(you can ignore this if you don't want to dabble with cropping), and the rect which is the size and coordinate of your texture + + + SDL_FreeSurface(surface); + +} + + diff --git a/sdl1090/font.h b/sdl1090/font.h new file mode 100755 index 0000000..4447612 --- /dev/null +++ b/sdl1090/font.h @@ -0,0 +1,3 @@ +#include "structs.h" + +extern Game game; diff --git a/sdl1090/init.c b/sdl1090/init.c new file mode 100644 index 0000000..00884da --- /dev/null +++ b/sdl1090/init.c @@ -0,0 +1,56 @@ +#include "init.h" + +extern void closeFont(TTF_Font *); + + +void init(char *title) +{ + /* Initialise SDL */ + + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_EVENTS) < 0) + { + printf("Could not initialize SDL: %s\n", SDL_GetError()); + + exit(1); + } + + /* Initialise SDL_TTF */ + + if (TTF_Init() < 0) + { + printf("Couldn't initialize SDL TTF: %s\n", SDL_GetError()); + + exit(1); + } + + + if (SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, 0, &(game.window), &(game.renderer)) < 0) + { + printf("Couldn't initialize Renderer: %s\n", SDL_GetError()); + + exit(1); + } + + /* Set the screen title */ + + SDL_SetWindowTitle(game.window,title); +} + +void cleanup() +{ + /* Close the font */ + + closeFont(game.font); + + /* Close SDL_TTF */ + + TTF_Quit(); + + + SDL_DestroyWindow(game.window); + + + /* Shut down SDL */ + + SDL_Quit(); +} diff --git a/sdl1090/init.h b/sdl1090/init.h new file mode 100644 index 0000000..4447612 --- /dev/null +++ b/sdl1090/init.h @@ -0,0 +1,3 @@ +#include "structs.h" + +extern Game game; diff --git a/sdl1090/input.c b/sdl1090/input.c new file mode 100644 index 0000000..30cf050 --- /dev/null +++ b/sdl1090/input.c @@ -0,0 +1,32 @@ +#include "input.h" + +void getInput() +{ + SDL_Event event; + + /* Loop through waiting messages and process them */ + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + /* Closing the Window or pressing Escape will exit the program */ + + case SDL_QUIT: + exit(0); + break; + + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: + exit(0); + break; + + default: + break; + } + break; + } + } +} \ No newline at end of file diff --git a/sdl1090/input.h b/sdl1090/input.h new file mode 100644 index 0000000..31c69c6 --- /dev/null +++ b/sdl1090/input.h @@ -0,0 +1 @@ +#include "defs.h" diff --git a/sdl1090/interactive.c b/sdl1090/interactive.c new file mode 100644 index 0000000..2056f2d --- /dev/null +++ b/sdl1090/interactive.c @@ -0,0 +1,596 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 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" +#include "structs.h" + +Game game; +extern void drawPlaneHeading(int , int , double, int, char *); +extern void drawPlane(int , int, int); +extern void drawGrid(); + + +// +// ============================= 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 dx(km) dy(km) 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"); + SDL_SetRenderDrawColor(game.renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(game.renderer); + drawGrid(); + + 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) { + double dLon = a->lon+87.6651033; + double dLat = a->lat-41.9809263; + + double x = 6371.0 * dLon * M_PI / 180.0f * cos(((a->lat+41.9809263)/2.0f) * M_PI / 180.0f); + double y = 6371.0 * dLat * M_PI / 180.0f; + // d = sqrt(x*x + y*y) * 6371.0; + + snprintf(strLat, 8,"%7.03f", x); + snprintf(strLon, 9,"%8.03f", y); + + int px = round(320.0 * (0.5 + (x / 64.0))); + int py = round(240.0 * (0.5 + (y / 48.0))); + + if(px >= 0 && px < 320 && py >= 0 && py < 240 ) { + + + if(MODES_ACFLAGS_HEADING_VALID) { + drawPlaneHeading(px, py,a->track, signalAverage, a->flight); + } else { + drawPlane(px, py, signalAverage); + } + } + + } + + 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; + } + + SDL_RenderPresent(game.renderer); + +} +// +//========================================================================= +// +// 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/sdl1090/mode_ac.c b/sdl1090/mode_ac.c new file mode 100644 index 0000000..881a428 --- /dev/null +++ b/sdl1090/mode_ac.c @@ -0,0 +1,386 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 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/sdl1090/mode_s.c.REMOVED.git-id b/sdl1090/mode_s.c.REMOVED.git-id new file mode 100644 index 0000000..4263876 --- /dev/null +++ b/sdl1090/mode_s.c.REMOVED.git-id @@ -0,0 +1 @@ +cd129a02b926ab42fbf9f41ab984f4bb2a3e5e59 \ No newline at end of file diff --git a/sdl1090/net_io.c b/sdl1090/net_io.c new file mode 100644 index 0000000..a979883 --- /dev/null +++ b/sdl1090/net_io.c @@ -0,0 +1,1006 @@ +// dump1090, a Mode S messages decoder for RTLSDR devices. +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 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, Modes.net_bind_address); + 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; + } + } + } + + free(c); +} +// +//========================================================================= +// +// Close the client connection and mark it as closed +// +void modesCloseClient(struct client *c) { + 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); + + c->fd = -1; +} +// +//========================================================================= +// +// 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->fd != -1) { + 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) { + modesCloseClient(c); + } + } + } else { + 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; + int statuscode = 500; + 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; + } + keepalive = 0; + + // 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")) { + statuscode = 200; + content = aircraftsToJson(&clen); + //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); + } else { + struct stat sbuf; + int fd = -1; + char *rp, *hrp; + + rp = realpath(getFile, NULL); + hrp = realpath(HTMLPATH, NULL); + hrp = (hrp ? hrp : HTMLPATH); + clen = -1; + content = strdup("Server error occured"); + if (rp && (!strncmp(hrp, rp, strlen(hrp)))) { + if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) { + content = (char *) realloc(content, sbuf.st_size); + if (read(fd, content, sbuf.st_size) != -1) { + clen = sbuf.st_size; + statuscode = 200; + } + } + } else { + errno = ENOENT; + } + + if (clen < 0) { + content = realloc(content, 128); + clen = snprintf(content, 128,"Error opening HTML file: %s", strerror(errno)); + statuscode = 404; + } + + 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 %d \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", + statuscode, + 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 (nread == 0) { + modesCloseClient(c); + return; + } + + // 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 + modesCloseClient(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)) { + modesCloseClient(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. + modesCloseClient(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->fd >= 0) { + 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); + } + } else { + modesFreeClient(c); + } + c = next; + } +} +// +// =============================== Network IO =========================== +// diff --git a/sdl1090/parula.h b/sdl1090/parula.h new file mode 100644 index 0000000..b5bdf41 --- /dev/null +++ b/sdl1090/parula.h @@ -0,0 +1,130 @@ +int parula[128][3] = { + {53, 42, 135}, + {54, 45, 141}, + {54, 48, 147}, + {54, 51, 153}, + {54, 54, 160}, + {54, 58, 166}, + {53, 61, 172}, + {52, 64, 179}, + {50, 67, 185}, + {47, 71, 192}, + {44, 74, 198}, + {39, 78, 205}, + {33, 82, 211}, + {25, 87, 217}, + {16, 91, 221}, + {8, 95, 224}, + {3, 98, 225}, + {1, 101, 225}, + {1, 104, 225}, + {2, 106, 225}, + {4, 108, 224}, + {6, 111, 223}, + {8, 113, 223}, + {10, 115, 222}, + {12, 117, 221}, + {14, 119, 219}, + {16, 120, 218}, + {17, 122, 217}, + {18, 124, 216}, + {19, 126, 215}, + {20, 128, 214}, + {20, 130, 213}, + {20, 132, 212}, + {20, 134, 211}, + {19, 136, 211}, + {18, 139, 210}, + {17, 141, 210}, + {15, 144, 210}, + {13, 146, 210}, + {11, 149, 210}, + {9, 151, 209}, + {8, 154, 209}, + {7, 156, 208}, + {6, 158, 207}, + {6, 160, 205}, + {6, 161, 204}, + {6, 163, 202}, + {6, 165, 201}, + {6, 166, 199}, + {6, 167, 197}, + {6, 169, 195}, + {7, 170, 193}, + {9, 171, 191}, + {11, 173, 188}, + {14, 174, 186}, + {17, 175, 184}, + {20, 176, 181}, + {23, 177, 179}, + {27, 178, 176}, + {31, 179, 173}, + {35, 181, 171}, + {39, 182, 168}, + {44, 183, 165}, + {48, 183, 162}, + {53, 184, 159}, + {58, 185, 156}, + {63, 186, 153}, + {68, 187, 150}, + {74, 188, 147}, + {80, 188, 144}, + {85, 189, 141}, + {91, 190, 138}, + {97, 190, 135}, + {103, 190, 133}, + {109, 191, 130}, + {115, 191, 127}, + {121, 191, 125}, + {126, 191, 122}, + {132, 191, 120}, + {137, 191, 118}, + {142, 191, 116}, + {147, 191, 114}, + {152, 191, 112}, + {157, 190, 110}, + {162, 190, 108}, + {167, 190, 106}, + {171, 190, 104}, + {176, 190, 103}, + {180, 189, 101}, + {185, 189, 99}, + {189, 189, 97}, + {193, 188, 96}, + {197, 188, 94}, + {202, 187, 92}, + {206, 187, 90}, + {210, 187, 89}, + {214, 186, 87}, + {218, 186, 85}, + {222, 186, 83}, + {226, 185, 82}, + {230, 185, 80}, + {234, 185, 78}, + {238, 185, 76}, + {242, 185, 73}, + {245, 186, 70}, + {249, 187, 67}, + {252, 188, 64}, + {254, 190, 61}, + {255, 193, 58}, + {255, 196, 55}, + {254, 198, 52}, + {254, 201, 50}, + {253, 203, 48}, + {252, 206, 46}, + {251, 209, 43}, + {249, 211, 41}, + {248, 214, 39}, + {247, 216, 37}, + {246, 219, 35}, + {245, 222, 33}, + {245, 225, 31}, + {245, 228, 29}, + {244, 232, 26}, + {245, 235, 24}, + {245, 239, 22}, + {246, 243, 19}, + {248, 247, 17}, + {249, 251, 14} +}; \ No newline at end of file diff --git a/sdl1090/pthreads/pthread.h b/sdl1090/pthreads/pthread.h new file mode 100644 index 0000000..b4072f7 --- /dev/null +++ b/sdl1090/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@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 + * + * 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 +#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 +#else /* NEED_FTIME */ +/* use native WIN32 time API */ +#endif /* NEED_FTIME */ + +#if defined(HAVE_SIGNAL_H) +#include +#endif /* HAVE_SIGNAL_H */ + +#include + +/* + * 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 +#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 + +/* + * 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 , 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 , 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 +#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 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/sdl1090/pthreads/sched.h b/sdl1090/pthreads/sched.h new file mode 100644 index 0000000..f36a97a --- /dev/null +++ b/sdl1090/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@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 +#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 +/* Required by Unix 98 */ +# include +# 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/sdl1090/pthreads/semaphore.h b/sdl1090/pthreads/semaphore.h new file mode 100644 index 0000000..c6e9407 --- /dev/null +++ b/sdl1090/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@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 +#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/sdl1090/public_html/config.js b/sdl1090/public_html/config.js new file mode 100644 index 0000000..f7d8e2e --- /dev/null +++ b/sdl1090/public_html/config.js @@ -0,0 +1,34 @@ +// -------------------------------------------------------- +// +// 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)"; +StaleColor = "rgb(190, 190, 190)"; + +// -- 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/sdl1090/public_html/coolclock/coolclock.js b/sdl1090/public_html/coolclock/coolclock.js new file mode 100644 index 0000000..4411974 --- /dev/null +++ b/sdl1090/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 +// 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/sdl1090/public_html/coolclock/excanvas.js b/sdl1090/public_html/coolclock/excanvas.js new file mode 100644 index 0000000..3e1aedf --- /dev/null +++ b/sdl1090/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(' ' , + '', + ''); + + 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(' 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(''); + } else if (aFill) { + lineStr.push(''); + } else { + lineStr.push( + '' + ); + } + + lineStr.push(""); + + 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/sdl1090/public_html/coolclock/moreskins.js b/sdl1090/public_html/coolclock/moreskins.js new file mode 100644 index 0000000..e316181 --- /dev/null +++ b/sdl1090/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/sdl1090/public_html/extension.js b/sdl1090/public_html/extension.js new file mode 100644 index 0000000..f71fe17 --- /dev/null +++ b/sdl1090/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/sdl1090/public_html/gmap.html b/sdl1090/public_html/gmap.html new file mode 100644 index 0000000..9ab2dc2 --- /dev/null +++ b/sdl1090/public_html/gmap.html @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + DUMP1090 + + + +
+
+
+ +
+ Squak 7x00 is reported and shown.
+ This is most likely an error in reciving or decoding.
+ Please do not call the local authorities, they already know about it if it is valid squak. +
+
+ + diff --git a/sdl1090/public_html/options.js b/sdl1090/public_html/options.js new file mode 100644 index 0000000..f4e3627 --- /dev/null +++ b/sdl1090/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/sdl1090/public_html/planeObject.js b/sdl1090/public_html/planeObject.js new file mode 100644 index 0000000..98153fd --- /dev/null +++ b/sdl1090/public_html/planeObject.js @@ -0,0 +1,260 @@ +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() { + this.markerColor = MarkerColor; + // If this marker is selected we should make it lighter than the rest. + if (this.is_selected == true) { + this.markerColor = SelectedColor; + } + + // If we have not seen a recent update, change color + if (this.seen > 15) { + this.markerColor = StaleColor; + } + + // 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/sdl1090/public_html/script.js b/sdl1090/public_html/script.js new file mode 100644 index 0000000..8212e50 --- /dev/null +++ b/sdl1090/public_html/script.js @@ -0,0 +1,658 @@ +// 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