├── .gitignore ├── Makefile ├── README.md ├── TODO ├── anet.c ├── anet.h ├── build_libs.txt ├── dump1090.c ├── gmap.html ├── images └── dump1090_sdrplus.png ├── testfiles └── modes1.bin └── tools └── debug.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | aircraft_log.csv 3 | dump1090 4 | testfiles/*.bin 5 | misc 6 | frames.js 7 | .*.swp 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS?=-O2 -g -Wall -W $(shell pkg-config --cflags librtlsdr libhackrf libairspy soxr) 2 | LDLIBS+=$(shell pkg-config --libs librtlsdr libhackrf libairspy soxr) -lpthread -lm 3 | 4 | ifeq ($(NoSDRplay),1) 5 | CFLAGS+= -DNoSDRplay 6 | else 7 | LDLIBS+= -lmirsdrapi-rsp 8 | endif 9 | 10 | CC?=gcc 11 | PROGNAME=dump1090 12 | 13 | all: $(PROGNAME) 14 | 15 | %.o: %.c 16 | $(CC) $(CFLAGS) -c $< 17 | 18 | $(PROGNAME): $(PROGNAME).o anet.o 19 | $(CC) -g -o $@ $^ $(LDFLAGS) $(LDLIBS) 20 | 21 | clean: 22 | rm -f *.o $(PROGNAME) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Dump1090\_sdrplus 2 | 3 | 4 | 5 | Dump1090\_sdrplus is a Mode S decoder (see [ADS-B](https://en.wikipedia.org/wiki/ADS-B)) specifically designed for SDR devices including [RTLSDR](http://www.rtl-sdr.com/), [HackRF One](https://greatscottgadgets.com/hackrf/), [Airspy](http://airspy.com/) and [SDRplay](http://www.sdrplay.com/). It is a fork of Salvatore Sanfilippo's original [dump1090](https://github.com/antirez/dump1090) with support for additional SDR devices. 6 | 7 | The main features are: 8 | 9 | * Robust decoding of weak messages, with dump1090 many users observed 10 | improved range compared to other popular decoders. 11 | * Network support: TCP30003 stream (MSG5...), Raw packets, HTTP. 12 | * Embedded HTTP server that displays currently detected aircrafts on 13 | Google Maps. Remember to replace `YOURGOOGLEKEY` in `gmap.html` with 14 | your `Maps Javascript API`-enabled [key](https://console.cloud.google.com/apis/credentials) to view it in browser. 15 | * Single bit errors correction using the 24 bit CRC. 16 | * Ability to decode DF11, DF17 messages. 17 | * Ability to decode DF formats like DF0, DF4, DF5, DF16, DF20 and DF21 18 | where the checksum is xored with the ICAO address by brute forcing the 19 | checksum field using recently seen ICAO addresses. 20 | * Decode raw IQ samples from file (using --ifile command line switch). 21 | * Interactive command-line-interfae mode where aircrafts currently detected 22 | are shown as a list refreshing as more data arrives. 23 | * CPR coordinates decoding and track calculation from velocity. 24 | * TCP server streaming and receiving raw data to/from connected clients 25 | (using --net). 26 | 27 | ### Installation 28 | 29 | Type in the shell 30 | 31 | sudo apt-get install librtlsdr0 librtlsdr-dev libhackrf-dev libairspy-dev libsoxr-dev 32 | 33 | If you want to enable SDRPlay, download libraries from http://www.sdrplay.com/linuxdl.php and 34 | 35 | chmod 755 SDRplay_RSP_API-Linux-2.13.1.run 36 | ./SDRplay_RSP_API-Linux-2.13.1.run 37 | sudo ldconfig 38 | make 39 | 40 | else 41 | 42 | make NoSDRplay=1 43 | 44 | ### Normal usage 45 | 46 | To capture traffic directly from your RTL device and show the captured traffic 47 | on standard output, just run the program without options at all: 48 | 49 | ./dump1090 50 | 51 | To just output hexadecimal messages: 52 | 53 | ./dump1090 --raw 54 | 55 | To run the program in interactive mode: 56 | 57 | ./dump1090 --interactive 58 | 59 | To run the program in interactive mode, with networking support, and connect 60 | with your browser to http://localhost:8080 to see live traffic: 61 | 62 | ./dump1090 --interactive --net 63 | 64 | In iteractive mode it is possible to have a less information dense but more 65 | "arcade style" output, where the screen is refreshed every second displaying 66 | all recently seen aircrafts with some additional information such as 67 | altitude and flight number, extracted from received Mode S packets. 68 | 69 | ### Using files as source of data 70 | 71 | To decode data from a file, use: 72 | 73 | ./dump1090 --ifile /path/to/binfile 74 | 75 | The binary file can be created using `rtl_sdr` or `hackrf_transfer` (requires 76 | conversion). File contents need to be 8-bit unsigned IQ samples at 2 Mhz sample rate. 77 | 78 | Example with rtl\_sdr: 79 | 80 | rtl_sdr -f 1090000000 -s 2000000 -g 50 output.bin 81 | 82 | In the example a gain of 50 is used, simply you should use the highest gain availabe 83 | for your tuner. This is not needed when calling dump1090 itself as it is able to 84 | select the highest gain supported automatically. 85 | 86 | Example with hackrf\_transfer: 87 | 88 | hackrf_transfer -r output.sb -f 1090000000 -s 2000000 -p 0 -a 0 -l 32 -g 48 89 | sox -r 2000000 -c 1 output.sb output.ub 90 | 91 | In the example RX/TX RF amplifier is disabled, IF gain is set to 32dB and baseband 92 | gain is set to 48dB (80% of maximum). As opposed to RTL SDR devices, HackRF returns 93 | signed IQ values, so [SoX](http://sox.sourceforge.net/) is used to convert them to 94 | unsigned IQ values. 95 | 96 | It is possible to feed the program with data via standard input using 97 | the --ifile option with "-" as argument. 98 | 99 | ### Additional options 100 | 101 | dump1090 can be called with other command line options to set a different 102 | gain, frequency, and so forth. For a list of options use: 103 | 104 | ./dump1090 --help 105 | 106 | ### Reliability 107 | 108 | By default dump1090 tries to fix single bit errors using the checksum. 109 | Basically the program will try to flip every bit of the message and check if 110 | the checksum of the resulting message matches. 111 | 112 | This is indeed able to fix errors and works reliably in my experience, 113 | however if you are interested in very reliable data I suggest to use 114 | the --no-fix command line switch in order to disable error fixing. 115 | 116 | ### Performances and sensibility of detection 117 | 118 | In my limited experience dump1090 was able to decode a big number of messages 119 | even in conditions where I encountered problems using other programs, however 120 | no formal test was performed so I can't really claim that this program is 121 | better or worse compared to other similar programs. 122 | 123 | If you can capture traffic that dump1090 is not able to decode properly, drop 124 | me an email with a download link. I may try to improve the detection during 125 | my free time (this is just a hobby project). 126 | 127 | ### Network server features 128 | 129 | By enabling the networking support with --net dump1090 starts listening 130 | for client connections on port 30002 and 30001 (you can change both 131 | ports if you want, see --help output). 132 | 133 | ### Port 30002 134 | 135 | Connected clients are served with data ASAP as they arrive from the device 136 | (or from file if --ifile is used) in the raw format similar to the following: 137 | 138 | *8D451E8B99019699C00B0A81F36E; 139 | 140 | Every entry is separated by a simple newline (LF character, hex 0x0A). 141 | 142 | ### Port 30001 143 | 144 | Port 30001 is the raw input port, and can be used to feed dump1090 with 145 | data in the same format as specified above, with hex messages starting with 146 | a `*` and ending with a `;` character. 147 | 148 | So for instance if there is another remote dump1090 instance collecting data 149 | it is possible to accumulate the output to a local dump1090 instance doing something 150 | like this: 151 | 152 | nc remote-dump1090.example.net 30002 | nc localhost 30001 153 | 154 | It is important to note that what is received via port 30001 is also 155 | broadcasted to clients listening to port 30002. 156 | 157 | In general everything received from port 30001 is handled exactly like the 158 | normal traffic from RTL devices or from file when --ifile is used. 159 | 160 | It is possible to use dump1090 just as a hub using --ifile with /dev/zero 161 | as argument as in the following example: 162 | 163 | ./dump1090 --net-only 164 | 165 | Or alternatively to see what's happening on the screen: 166 | 167 | ./dump1090 --net-only --interactive 168 | 169 | Then you can feed it from different data sources from the internet. 170 | 171 | ### Port 30003 172 | 173 | Connected clients are served with messages in SBS1 (BaseStation) format, 174 | similar to: 175 | 176 | MSG,4,,,738065,,,,,,,,420,179,,,0,,0,0,0,0 177 | MSG,3,,,738065,,,,,,,35000,,,34.81609,34.07810,,,0,0,0,0 178 | 179 | This can be used to feed data to various sharing sites without the need to use another decoder. 180 | 181 | ### Antenna 182 | 183 | Mode S messages are transmitted at 1090 Mhz. If you have a decent 184 | antenna you'll be able to pick up signals from aircrafts pretty far from your 185 | position, especially if you are outdoor and in a position with a good sky view. 186 | 187 | You can easily build a very cheap antenna following the istructions at: 188 | 189 | http://antirez.com/news/46 190 | 191 | With this trivial antenna I was able to pick up signals of aircrafts 200+ Km 192 | away from me. 193 | 194 | If you are interested in a more serious antenna check the following 195 | resources: 196 | 197 | * http://gnuradio.org/redmine/attachments/download/246/06-foster-adsb.pdf 198 | * http://www.lll.lu/~edward/edward/adsb/antenna/ADSBantenna.html 199 | * http://modesbeast.com/pix/adsb-ant-drawing.gif 200 | 201 | ### Aggressive mode 202 | 203 | With --aggressive it is possible to activate the *aggressive mode* that is a 204 | modified version of the Mode S packet detection and decoding. 205 | THe aggresive mode uses more CPU usually (especially if there are many planes 206 | sending DF17 packets), but can detect a few more messages. 207 | 208 | The algorithm in aggressive mode is modified in the following ways: 209 | 210 | * Up to two demodulation errors are tolerated (adjacent entires in the magnitude 211 | vector with the same eight). Normally only messages without errors are 212 | checked. 213 | * It tries to fix DF17 messages trying every two bits combination. 214 | 215 | The use of aggressive mode is only advised in places where there is low traffic 216 | in order to have a chance to capture some more messages. 217 | 218 | ### Debug mode 219 | 220 | The Debug mode is a visual help to improve the detection algorithm or to 221 | understand why the program is not working for a given input. 222 | 223 | In this mode messages are displayed in an ASCII-art style graphical 224 | representation, where the individial magnitude bars sampled at 2 Mhz are 225 | displayed. 226 | 227 | An index shows the sample number, where 0 is the sample where the first 228 | Mode S peak was found. Some additional background noise is also added 229 | before the first peak to provide some context. 230 | 231 | To enable debug mode and check what combinations of packets you can 232 | log, use --help to obtain a list of available debug flags. 233 | 234 | Debug mode includes an optional javascript output that is used to visualize 235 | packets using a web browser, you can use the file debug.html under the 236 | 'tools' directory to load the generated frames.js file. 237 | 238 | ### How this program works? 239 | 240 | The code is clearly commented in order to be easy to understand. 241 | For the diligent programmer with a Mode S specification on his hands it 242 | should be trivial to understand how it works. 243 | 244 | The algorithms I used were obtained basically looking at many messages 245 | as displayed using a throw-away SDL program, and trying to model the algorithm 246 | based on how the messages look graphically. 247 | 248 | ### How to test the program? 249 | 250 | If you have an RTLSDR device and you happen to be in an area where there 251 | are aircrafts flying over your head, just run the program and check for signals. 252 | 253 | However if you don't have an RTLSDR device, or if the presence of aircrafts 254 | is very limited in your area, you may want to try the sample file distributed 255 | with the dump1090 distribution under the "testfiles" directory. 256 | 257 | Just run it like this: 258 | 259 | ./dump1090 --ifile testfiles/modes1.bin 260 | 261 | ### What is --strip mode? 262 | 263 | It is just a simple filter that will get raw IQ 8 bit samples in input 264 | and will output a file missing all the parts of the file where I and Q 265 | are lower than the specified level for more than 32 samples. 266 | 267 | Use it like this: 268 | 269 | cat big.bin | ./dump1090 --snip 25 > small.bin 270 | 271 | I used it in order to create a small test file to include inside this 272 | program source code distribution. 273 | 274 | ### Contributing 275 | 276 | dump1090 was written during some free time during xmas 2012, it is a hobby 277 | project so I'll be able to address issues and improve it only during 278 | free time, however you are encouraged to send pull requests in order to 279 | improve the program. A good starting point can be the TODO list included in 280 | the source distribution. 281 | 282 | ### Credits 283 | 284 | dump1090 was written by [Salvatore Sanfilippo](https://github.com/antirez) and 285 | is released under the BSD three clause license. HackRF One support was added by 286 | [Ilker Temir](https://github.com/itemir). AirSpy support was added by 287 | [Chris Kuethe](https://github.com/ckuethe). SDRplay support was added by 288 | the [SDRplay](https://github.com/SDRplay) team. 289 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | TODO 2 | 3 | * Extract more information from captured Mode S messages. 4 | * Improve the web interface gmap.html. 5 | * Enhance the algorithm to reliably decode more messages. 6 | * Add support for more receiver types 7 | -------------------------------------------------------------------------------- /anet.c: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "anet.h" 47 | 48 | static void anetSetError(char *err, const char *fmt, ...) 49 | { 50 | va_list ap; 51 | 52 | if (!err) return; 53 | va_start(ap, fmt); 54 | vsnprintf(err, ANET_ERR_LEN, fmt, ap); 55 | va_end(ap); 56 | } 57 | 58 | int anetNonBlock(char *err, int fd) 59 | { 60 | int flags; 61 | 62 | /* Set the socket nonblocking. 63 | * Note that fcntl(2) for F_GETFL and F_SETFL can't be 64 | * interrupted by a signal. */ 65 | if ((flags = fcntl(fd, F_GETFL)) == -1) { 66 | anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno)); 67 | return ANET_ERR; 68 | } 69 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { 70 | anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); 71 | return ANET_ERR; 72 | } 73 | return ANET_OK; 74 | } 75 | 76 | int anetTcpNoDelay(char *err, int fd) 77 | { 78 | int yes = 1; 79 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) 80 | { 81 | anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); 82 | return ANET_ERR; 83 | } 84 | return ANET_OK; 85 | } 86 | 87 | int anetSetSendBuffer(char *err, int fd, int buffsize) 88 | { 89 | if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1) 90 | { 91 | anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno)); 92 | return ANET_ERR; 93 | } 94 | return ANET_OK; 95 | } 96 | 97 | int anetTcpKeepAlive(char *err, int fd) 98 | { 99 | int yes = 1; 100 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) { 101 | anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); 102 | return ANET_ERR; 103 | } 104 | return ANET_OK; 105 | } 106 | 107 | int anetResolve(char *err, char *host, char *ipbuf) 108 | { 109 | struct sockaddr_in sa; 110 | 111 | sa.sin_family = AF_INET; 112 | if (inet_aton(host, &sa.sin_addr) == 0) { 113 | struct hostent *he; 114 | 115 | he = gethostbyname(host); 116 | if (he == NULL) { 117 | anetSetError(err, "can't resolve: %s", host); 118 | return ANET_ERR; 119 | } 120 | memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); 121 | } 122 | strcpy(ipbuf,inet_ntoa(sa.sin_addr)); 123 | return ANET_OK; 124 | } 125 | 126 | static int anetCreateSocket(char *err, int domain) { 127 | int s, on = 1; 128 | if ((s = socket(domain, SOCK_STREAM, 0)) == -1) { 129 | anetSetError(err, "creating socket: %s", strerror(errno)); 130 | return ANET_ERR; 131 | } 132 | 133 | /* Make sure connection-intensive things like the redis benckmark 134 | * will be able to close/open sockets a zillion of times */ 135 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { 136 | anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno)); 137 | return ANET_ERR; 138 | } 139 | return s; 140 | } 141 | 142 | #define ANET_CONNECT_NONE 0 143 | #define ANET_CONNECT_NONBLOCK 1 144 | static int anetTcpGenericConnect(char *err, char *addr, int port, int flags) 145 | { 146 | int s; 147 | struct sockaddr_in sa; 148 | 149 | if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) 150 | return ANET_ERR; 151 | 152 | sa.sin_family = AF_INET; 153 | sa.sin_port = htons(port); 154 | if (inet_aton(addr, &sa.sin_addr) == 0) { 155 | struct hostent *he; 156 | 157 | he = gethostbyname(addr); 158 | if (he == NULL) { 159 | anetSetError(err, "can't resolve: %s", addr); 160 | close(s); 161 | return ANET_ERR; 162 | } 163 | memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); 164 | } 165 | if (flags & ANET_CONNECT_NONBLOCK) { 166 | if (anetNonBlock(err,s) != ANET_OK) 167 | return ANET_ERR; 168 | } 169 | if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { 170 | if (errno == EINPROGRESS && 171 | flags & ANET_CONNECT_NONBLOCK) 172 | return s; 173 | 174 | anetSetError(err, "connect: %s", strerror(errno)); 175 | close(s); 176 | return ANET_ERR; 177 | } 178 | return s; 179 | } 180 | 181 | int anetTcpConnect(char *err, char *addr, int port) 182 | { 183 | return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE); 184 | } 185 | 186 | int anetTcpNonBlockConnect(char *err, char *addr, int port) 187 | { 188 | return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK); 189 | } 190 | 191 | int anetUnixGenericConnect(char *err, char *path, int flags) 192 | { 193 | int s; 194 | struct sockaddr_un sa; 195 | 196 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) 197 | return ANET_ERR; 198 | 199 | sa.sun_family = AF_LOCAL; 200 | strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); 201 | if (flags & ANET_CONNECT_NONBLOCK) { 202 | if (anetNonBlock(err,s) != ANET_OK) 203 | return ANET_ERR; 204 | } 205 | if (connect(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) { 206 | if (errno == EINPROGRESS && 207 | flags & ANET_CONNECT_NONBLOCK) 208 | return s; 209 | 210 | anetSetError(err, "connect: %s", strerror(errno)); 211 | close(s); 212 | return ANET_ERR; 213 | } 214 | return s; 215 | } 216 | 217 | int anetUnixConnect(char *err, char *path) 218 | { 219 | return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE); 220 | } 221 | 222 | int anetUnixNonBlockConnect(char *err, char *path) 223 | { 224 | return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK); 225 | } 226 | 227 | /* Like read(2) but make sure 'count' is read before to return 228 | * (unless error or EOF condition is encountered) */ 229 | int anetRead(int fd, char *buf, int count) 230 | { 231 | int nread, totlen = 0; 232 | while(totlen != count) { 233 | nread = read(fd,buf,count-totlen); 234 | if (nread == 0) return totlen; 235 | if (nread == -1) return -1; 236 | totlen += nread; 237 | buf += nread; 238 | } 239 | return totlen; 240 | } 241 | 242 | /* Like write(2) but make sure 'count' is read before to return 243 | * (unless error is encountered) */ 244 | int anetWrite(int fd, char *buf, int count) 245 | { 246 | int nwritten, totlen = 0; 247 | while(totlen != count) { 248 | nwritten = write(fd,buf,count-totlen); 249 | if (nwritten == 0) return totlen; 250 | if (nwritten == -1) return -1; 251 | totlen += nwritten; 252 | buf += nwritten; 253 | } 254 | return totlen; 255 | } 256 | 257 | static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) { 258 | if (bind(s,sa,len) == -1) { 259 | anetSetError(err, "bind: %s", strerror(errno)); 260 | close(s); 261 | return ANET_ERR; 262 | } 263 | 264 | /* Use a backlog of 512 entries. We pass 511 to the listen() call because 265 | * the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1); 266 | * which will thus give us a backlog of 512 entries */ 267 | if (listen(s, 511) == -1) { 268 | anetSetError(err, "listen: %s", strerror(errno)); 269 | close(s); 270 | return ANET_ERR; 271 | } 272 | return ANET_OK; 273 | } 274 | 275 | int anetTcpServer(char *err, int port, char *bindaddr) 276 | { 277 | int s; 278 | struct sockaddr_in sa; 279 | 280 | if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) 281 | return ANET_ERR; 282 | 283 | memset(&sa,0,sizeof(sa)); 284 | sa.sin_family = AF_INET; 285 | sa.sin_port = htons(port); 286 | sa.sin_addr.s_addr = htonl(INADDR_ANY); 287 | if (bindaddr && inet_aton(bindaddr, &sa.sin_addr) == 0) { 288 | anetSetError(err, "invalid bind address"); 289 | close(s); 290 | return ANET_ERR; 291 | } 292 | if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR) 293 | return ANET_ERR; 294 | return s; 295 | } 296 | 297 | int anetUnixServer(char *err, char *path, mode_t perm) 298 | { 299 | int s; 300 | struct sockaddr_un sa; 301 | 302 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) 303 | return ANET_ERR; 304 | 305 | memset(&sa,0,sizeof(sa)); 306 | sa.sun_family = AF_LOCAL; 307 | strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); 308 | if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR) 309 | return ANET_ERR; 310 | if (perm) 311 | chmod(sa.sun_path, perm); 312 | return s; 313 | } 314 | 315 | static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) { 316 | int fd; 317 | while(1) { 318 | fd = accept(s,sa,len); 319 | if (fd == -1) { 320 | if (errno == EINTR) 321 | continue; 322 | else { 323 | anetSetError(err, "accept: %s", strerror(errno)); 324 | return ANET_ERR; 325 | } 326 | } 327 | break; 328 | } 329 | return fd; 330 | } 331 | 332 | int anetTcpAccept(char *err, int s, char *ip, int *port) { 333 | int fd; 334 | struct sockaddr_in sa; 335 | socklen_t salen = sizeof(sa); 336 | if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) 337 | return ANET_ERR; 338 | 339 | if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); 340 | if (port) *port = ntohs(sa.sin_port); 341 | return fd; 342 | } 343 | 344 | int anetUnixAccept(char *err, int s) { 345 | int fd; 346 | struct sockaddr_un sa; 347 | socklen_t salen = sizeof(sa); 348 | if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) 349 | return ANET_ERR; 350 | 351 | return fd; 352 | } 353 | 354 | int anetPeerToString(int fd, char *ip, int *port) { 355 | struct sockaddr_in sa; 356 | socklen_t salen = sizeof(sa); 357 | 358 | if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) { 359 | *port = 0; 360 | ip[0] = '?'; 361 | ip[1] = '\0'; 362 | return -1; 363 | } 364 | if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); 365 | if (port) *port = ntohs(sa.sin_port); 366 | return 0; 367 | } 368 | 369 | int anetSockName(int fd, char *ip, int *port) { 370 | struct sockaddr_in sa; 371 | socklen_t salen = sizeof(sa); 372 | 373 | if (getsockname(fd,(struct sockaddr*)&sa,&salen) == -1) { 374 | *port = 0; 375 | ip[0] = '?'; 376 | ip[1] = '\0'; 377 | return -1; 378 | } 379 | if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); 380 | if (port) *port = ntohs(sa.sin_port); 381 | return 0; 382 | } 383 | -------------------------------------------------------------------------------- /anet.h: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef ANET_H 32 | #define ANET_H 33 | 34 | #define ANET_OK 0 35 | #define ANET_ERR -1 36 | #define ANET_ERR_LEN 256 37 | 38 | #if defined(__sun) 39 | #define AF_LOCAL AF_UNIX 40 | #endif 41 | 42 | int anetTcpConnect(char *err, char *addr, int port); 43 | int anetTcpNonBlockConnect(char *err, char *addr, int port); 44 | int anetUnixConnect(char *err, char *path); 45 | int anetUnixNonBlockConnect(char *err, char *path); 46 | int anetRead(int fd, char *buf, int count); 47 | int anetResolve(char *err, char *host, char *ipbuf); 48 | int anetTcpServer(char *err, int port, char *bindaddr); 49 | int anetUnixServer(char *err, char *path, mode_t perm); 50 | int anetTcpAccept(char *err, int serversock, char *ip, int *port); 51 | int anetUnixAccept(char *err, int serversock); 52 | int anetWrite(int fd, char *buf, int count); 53 | int anetNonBlock(char *err, int fd); 54 | int anetTcpNoDelay(char *err, int fd); 55 | int anetTcpKeepAlive(char *err, int fd); 56 | int anetPeerToString(int fd, char *ip, int *port); 57 | int anetSetSendBuffer(char *err, int fd, int buffsize); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /build_libs.txt: -------------------------------------------------------------------------------- 1 | To build RTL-SDR, HackRF and Airspy librares use the relevant git statements below and then the same build sequence of... 2 | 3 | cd 4 | mkdir build 5 | cd build 6 | cmake ../ 7 | make 8 | sudo make install 9 | sudo ldconfig 10 | 11 | RTL-SDR: git clone git//git.osmocom.org/rtl-sdr.git ./rtl-sdr 12 | HACKRF: git clone https://github.com/mossmann/hackrf/host.git ./hackrf 13 | AIRSPY: git clone https://github.com/airspy/host.git ./airspy 14 | 15 | For Soxr: 16 | 17 | git clone https://github.com/uklauer/soxr.git ./soxr 18 | 19 | then... 20 | 21 | cd soxr 22 | ./go 23 | cd Release 24 | sudo make install 25 | 26 | wget https://www.sdrplay.com/software/SDRplay_RSP_API-RPi-2.13.1.run 27 | sh ./SDRplay_RSP_API-RPi-2.13.1.run 28 | -------------------------------------------------------------------------------- /dump1090.c: -------------------------------------------------------------------------------- 1 | /* Mode1090, a Mode S messages decoder for RTLSDR devices. 2 | * 3 | * Copyright (C) 2012 by Salvatore Sanfilippo 4 | * 5 | * HackRF One support added by Ilker Temir 6 | * AirSpy support added by Chris Kuethe 7 | * 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are 12 | * met: 13 | * 14 | * * Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 17 | * * Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include "rtl-sdr.h" 51 | #include "libhackrf/hackrf.h" 52 | #include "libairspy/airspy.h" 53 | #ifndef NoSDRplay 54 | #include "mirsdrapi-rsp.h" 55 | #endif 56 | #include "soxr.h" 57 | #include "anet.h" 58 | 59 | #define MODES_DEFAULT_RATE 2000000 60 | #define MODES_DEFAULT_FREQ 1090000000 61 | #define MODES_DEFAULT_WIDTH 1000 62 | #define MODES_DEFAULT_HEIGHT 700 63 | #define MODES_ASYNC_BUF_NUMBER 12 64 | #define MODES_DATA_LEN (16*16384) /* 256k */ 65 | #define MODES_AUTO_GAIN -100 /* Use automatic gain. */ 66 | #define MODES_MAX_GAIN 999999 /* Use max available gain. */ 67 | /* HackRF One Defaults */ 68 | #define HACKRF_RF_GAIN 0 69 | #define HACKRF_LNA_GAIN 32 70 | #define HACKRF_VGA_GAIN 48 71 | /* AirSpy defaults */ 72 | #define AIRSPY_RF_GAIN 11 73 | #define AIRSPY_LNA_GAIN 11 74 | #define AIRSPY_VGA_GAIN 11 75 | 76 | #define MODES_PREAMBLE_US 8 /* microseconds */ 77 | #define MODES_LONG_MSG_BITS 112 78 | #define MODES_SHORT_MSG_BITS 56 79 | #define MODES_FULL_LEN (MODES_PREAMBLE_US+MODES_LONG_MSG_BITS) 80 | #define MODES_LONG_MSG_BYTES (112/8) 81 | #define MODES_SHORT_MSG_BYTES (56/8) 82 | 83 | #define MODES_ICAO_CACHE_LEN 1024 /* Power of two required. */ 84 | #define MODES_ICAO_CACHE_TTL 60 /* Time to live of cached addresses. */ 85 | #define MODES_UNIT_FEET 0 86 | #define MODES_UNIT_METERS 1 87 | 88 | #define MODES_DEBUG_DEMOD (1<<0) 89 | #define MODES_DEBUG_DEMODERR (1<<1) 90 | #define MODES_DEBUG_BADCRC (1<<2) 91 | #define MODES_DEBUG_GOODCRC (1<<3) 92 | #define MODES_DEBUG_NOPREAMBLE (1<<4) 93 | #define MODES_DEBUG_NET (1<<5) 94 | #define MODES_DEBUG_JS (1<<6) 95 | 96 | /* When debug is set to MODES_DEBUG_NOPREAMBLE, the first sample must be 97 | * at least greater than a given level for us to dump the signal. */ 98 | #define MODES_DEBUG_NOPREAMBLE_LEVEL 25 99 | 100 | #define MODES_INTERACTIVE_REFRESH_TIME 250 /* Milliseconds */ 101 | #define MODES_INTERACTIVE_ROWS 15 /* Rows on screen */ 102 | #define MODES_INTERACTIVE_TTL 60 /* TTL before being removed */ 103 | 104 | #define MODES_NET_MAX_FD 1024 105 | #define MODES_NET_OUTPUT_SBS_PORT 30003 106 | #define MODES_NET_OUTPUT_RAW_PORT 30002 107 | #define MODES_NET_INPUT_RAW_PORT 30001 108 | #define MODES_NET_HTTP_PORT 8080 109 | #define MODES_CLIENT_BUF_SIZE 1024 110 | #define MODES_NET_SNDBUF_SIZE (1024*64) 111 | 112 | #define MODES_NOTUSED(V) ((void) V) 113 | 114 | /* Structure used to describe a networking client. */ 115 | struct client { 116 | int fd; /* File descriptor. */ 117 | int service; /* TCP port the client is connected to. */ 118 | char buf[MODES_CLIENT_BUF_SIZE+1]; /* Read buffer. */ 119 | int buflen; /* Amount of data on buffer. */ 120 | }; 121 | 122 | /* Structure used to describe an aircraft in iteractive mode. */ 123 | struct aircraft { 124 | uint32_t addr; /* ICAO address */ 125 | char hexaddr[7]; /* Printable ICAO address */ 126 | char flight[9]; /* Flight number */ 127 | int altitude; /* Altitude */ 128 | int speed; /* Velocity computed from EW and NS components. */ 129 | int track; /* Angle of flight. */ 130 | time_t seen; /* Time at which the last packet was received. */ 131 | long messages; /* Number of Mode S messages received. */ 132 | /* Encoded latitude and longitude as extracted by odd and even 133 | * CPR encoded messages. */ 134 | int odd_cprlat; 135 | int odd_cprlon; 136 | int even_cprlat; 137 | int even_cprlon; 138 | int csv_logged; /* Value is 1 if already logged. */ 139 | double lat, lon; /* Coordinated obtained from CPR encoded data. */ 140 | long long odd_cprtime, even_cprtime; 141 | struct aircraft *next; /* Next aircraft in our linked list. */ 142 | }; 143 | 144 | /* Program global state. */ 145 | struct { 146 | /* Internal state */ 147 | pthread_t reader_thread; 148 | pthread_mutex_t data_mutex; /* Mutex to synchronize buffer access. */ 149 | pthread_cond_t data_cond; /* Conditional variable associated. */ 150 | unsigned char *data; /* Raw IQ samples buffer */ 151 | uint16_t *magnitude; /* Magnitude vector */ 152 | uint32_t data_len; /* Buffer length. */ 153 | int fd; /* --ifile option file descriptor. */ 154 | int data_ready; /* Data ready to be processed. */ 155 | uint32_t *icao_cache; /* Recently seen ICAO addresses cache. */ 156 | uint16_t *maglut; /* I/Q -> Magnitude lookup table. */ 157 | int exit; /* Exit from the main loop when true. */ 158 | 159 | /* Drivers */ 160 | int prefer_airspy; 161 | int prefer_hackrf; 162 | int prefer_rtlsdr; 163 | #ifndef NoSDRplay 164 | int prefer_sdrplay; 165 | #endif 166 | 167 | /* RTLSDR */ 168 | int rtl_enabled; 169 | int dev_index; 170 | int gain; 171 | int enable_agc; 172 | rtlsdr_dev_t *dev; 173 | 174 | /* HackRF One and Airspy are very similar... */ 175 | int hackrf_enabled; 176 | int rf_gain; 177 | int lna_gain; 178 | int vga_gain; 179 | int power_antenna; 180 | hackrf_device *hackrf; 181 | 182 | /* ... but AirSpy needs to be resampled */ 183 | int airspy_enabled; 184 | struct airspy_device *airspy; 185 | soxr_t resampler; 186 | char *airspy_bytes, *airspy_scratch; 187 | int support_10MSPS; 188 | 189 | #ifndef NoSDRplay 190 | /* SDRplay */ 191 | int sdrplay_enabled; 192 | int sdrplaySamplesPerPacket; 193 | short *sdrplay_i; 194 | short *sdrplay_q; 195 | #endif 196 | 197 | /* SDR Common */ 198 | int freq; 199 | 200 | /* Networking */ 201 | char aneterr[ANET_ERR_LEN]; 202 | struct client *clients[MODES_NET_MAX_FD]; /* Our clients. */ 203 | int maxfd; /* Greatest fd currently active. */ 204 | int sbsos; /* SBS output listening socket. */ 205 | int ros; /* Raw output listening socket. */ 206 | int ris; /* Raw input listening socket. */ 207 | int https; /* HTTP listening socket. */ 208 | 209 | /* Configuration */ 210 | char *filename; /* Input form file, --ifile option. */ 211 | int fix_errors; /* Single bit error correction if true. */ 212 | int check_crc; /* Only display messages with good CRC. */ 213 | int raw; /* Raw output format. */ 214 | int debug; /* Debugging mode. */ 215 | int net; /* Enable networking. */ 216 | int net_only; /* Enable just networking. */ 217 | int interactive; /* Interactive mode */ 218 | int interactive_rows; /* Interactive mode: max number of rows. */ 219 | int interactive_ttl; /* Interactive mode: TTL before deletion. */ 220 | int csv_log; /* Log aircraft detection to CSV file. */ 221 | int stats; /* Print stats at exit in --ifile mode. */ 222 | int onlyaddr; /* Print only ICAO addresses. */ 223 | int metric; /* Use metric units. */ 224 | int aggressive; /* Aggressive detection algorithm. */ 225 | 226 | /* Interactive mode */ 227 | struct aircraft *aircrafts; 228 | long long interactive_last_update; /* Last screen update in milliseconds */ 229 | 230 | /* Statistics */ 231 | long long stat_valid_preamble; 232 | long long stat_demodulated; 233 | long long stat_goodcrc; 234 | long long stat_badcrc; 235 | long long stat_fixed; 236 | long long stat_single_bit_fix; 237 | long long stat_two_bits_fix; 238 | long long stat_http_requests; 239 | long long stat_sbs_connections; 240 | long long stat_out_of_phase; 241 | } Modes; 242 | 243 | /* The struct we use to store information about a decoded message. */ 244 | struct modesMessage { 245 | /* Generic fields */ 246 | unsigned char msg[MODES_LONG_MSG_BYTES]; /* Binary message. */ 247 | int msgbits; /* Number of bits in message */ 248 | int msgtype; /* Downlink format # */ 249 | int crcok; /* True if CRC was valid */ 250 | uint32_t crc; /* Message CRC */ 251 | int errorbit; /* Bit corrected. -1 if no bit corrected. */ 252 | int aa1, aa2, aa3; /* ICAO Address bytes 1 2 and 3 */ 253 | int phase_corrected; /* True if phase correction was applied. */ 254 | 255 | /* DF 11 */ 256 | int ca; /* Responder capabilities. */ 257 | 258 | /* DF 17 */ 259 | int metype; /* Extended squitter message type. */ 260 | int mesub; /* Extended squitter message subtype. */ 261 | int heading_is_valid; 262 | int heading; 263 | int aircraft_type; 264 | int fflag; /* 1 = Odd, 0 = Even CPR message. */ 265 | int tflag; /* UTC synchronized? */ 266 | int raw_latitude; /* Non decoded latitude */ 267 | int raw_longitude; /* Non decoded longitude */ 268 | char flight[9]; /* 8 chars flight number. */ 269 | int ew_dir; /* 0 = East, 1 = West. */ 270 | int ew_velocity; /* E/W velocity. */ 271 | int ns_dir; /* 0 = North, 1 = South. */ 272 | int ns_velocity; /* N/S velocity. */ 273 | int vert_rate_source; /* Vertical rate source. */ 274 | int vert_rate_sign; /* Vertical rate sign. */ 275 | int vert_rate; /* Vertical rate. */ 276 | int velocity; /* Computed from EW and NS velocity. */ 277 | 278 | /* DF4, DF5, DF20, DF21 */ 279 | int fs; /* Flight status for DF4,5,20,21 */ 280 | int dr; /* Request extraction of downlink request. */ 281 | int um; /* Request extraction of downlink request. */ 282 | int identity; /* 13 bits identity (Squawk). */ 283 | 284 | /* Fields used by multiple message types. */ 285 | int altitude, unit; 286 | }; 287 | 288 | void interactiveShowData(void); 289 | struct aircraft* interactiveReceiveData(struct modesMessage *mm); 290 | void modesSendRawOutput(struct modesMessage *mm); 291 | void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a); 292 | void useModesMessage(struct modesMessage *mm); 293 | int fixSingleBitErrors(unsigned char *msg, int bits); 294 | int fixTwoBitsErrors(unsigned char *msg, int bits); 295 | int modesMessageLenByType(int type); 296 | void sigWinchCallback(); 297 | int getTermRows(); 298 | 299 | /* ============================= Utility functions ========================== */ 300 | 301 | static long long mstime(void) { 302 | struct timeval tv; 303 | long long mst; 304 | 305 | gettimeofday(&tv, NULL); 306 | mst = ((long long)tv.tv_sec)*1000; 307 | mst += tv.tv_usec/1000; 308 | return mst; 309 | } 310 | 311 | /* =============================== Initialization =========================== */ 312 | 313 | void modesInitConfig(void) { 314 | Modes.gain = MODES_MAX_GAIN; 315 | Modes.dev_index = 0; 316 | Modes.enable_agc = 0; 317 | Modes.rf_gain = 0; 318 | Modes.lna_gain = 0; 319 | Modes.vga_gain = 0; 320 | Modes.power_antenna = 0; 321 | Modes.freq = MODES_DEFAULT_FREQ; 322 | Modes.filename = NULL; 323 | Modes.fix_errors = 1; 324 | Modes.check_crc = 1; 325 | Modes.raw = 0; 326 | Modes.net = 0; 327 | Modes.net_only = 0; 328 | Modes.onlyaddr = 0; 329 | Modes.debug = 0; 330 | Modes.interactive = 0; 331 | Modes.interactive_rows = MODES_INTERACTIVE_ROWS; 332 | Modes.interactive_ttl = MODES_INTERACTIVE_TTL; 333 | Modes.csv_log = 0; 334 | Modes.aggressive = 0; 335 | Modes.interactive_rows = getTermRows(); 336 | Modes.support_10MSPS = 0; 337 | } 338 | 339 | void modesInit(void) { 340 | int i, q; 341 | 342 | pthread_mutex_init(&Modes.data_mutex,NULL); 343 | pthread_cond_init(&Modes.data_cond,NULL); 344 | /* We add a full message minus a final bit to the length, so that we 345 | * can carry the remaining part of the buffer that we can't process 346 | * in the message detection loop, back at the start of the next data 347 | * to process. This way we are able to also detect messages crossing 348 | * two reads. */ 349 | Modes.data_len = MODES_DATA_LEN + (MODES_FULL_LEN-1)*4; 350 | Modes.data_ready = 0; 351 | /* Allocate the ICAO address cache. We use two uint32_t for every 352 | * entry because it's a addr / timestamp pair for every entry. */ 353 | Modes.icao_cache = malloc(sizeof(uint32_t)*MODES_ICAO_CACHE_LEN*2); 354 | memset(Modes.icao_cache,0,sizeof(uint32_t)*MODES_ICAO_CACHE_LEN*2); 355 | Modes.aircrafts = NULL; 356 | Modes.interactive_last_update = 0; 357 | if ((Modes.data = malloc(Modes.data_len)) == NULL || 358 | (Modes.magnitude = malloc(Modes.data_len*2)) == NULL) { 359 | fprintf(stderr, "Out of memory allocating data buffer.\n"); 360 | exit(1); 361 | } 362 | memset(Modes.data,127,Modes.data_len); 363 | 364 | /* Populate the I/Q -> Magnitude lookup table. It is used because 365 | * sqrt or round may be expensive and may vary a lot depending on 366 | * the libc used. 367 | * 368 | * We scale to 0-255 range multiplying by 1.4 in order to ensure that 369 | * every different I/Q pair will result in a different magnitude value, 370 | * not losing any resolution. */ 371 | Modes.maglut = malloc(129*129*2); 372 | for (i = 0; i <= 128; i++) { 373 | for (q = 0; q <= 128; q++) { 374 | Modes.maglut[i*129+q] = round(sqrt(i*i+q*q)*360); 375 | } 376 | } 377 | 378 | /* Statistics */ 379 | Modes.stat_valid_preamble = 0; 380 | Modes.stat_demodulated = 0; 381 | Modes.stat_goodcrc = 0; 382 | Modes.stat_badcrc = 0; 383 | Modes.stat_fixed = 0; 384 | Modes.stat_single_bit_fix = 0; 385 | Modes.stat_two_bits_fix = 0; 386 | Modes.stat_http_requests = 0; 387 | Modes.stat_sbs_connections = 0; 388 | Modes.stat_out_of_phase = 0; 389 | Modes.exit = 0; 390 | } 391 | 392 | /* =============================== RTLSDR handling ========================== */ 393 | 394 | int modesInitRTLSDR(void) { 395 | int j; 396 | int device_count; 397 | int ppm_error = 0; 398 | char vendor[256], product[256], serial[256]; 399 | 400 | device_count = rtlsdr_get_device_count(); 401 | if (!device_count) { 402 | fprintf(stderr, "No supported RTLSDR devices found.\n"); 403 | return(1); 404 | } 405 | 406 | fprintf(stderr, "Found %d device(s):\n", device_count); 407 | for (j = 0; j < device_count; j++) { 408 | rtlsdr_get_device_usb_strings(j, vendor, product, serial); 409 | fprintf(stderr, "%d: %s, %s, SN: %s %s\n", j, vendor, product, serial, 410 | (j == Modes.dev_index) ? "(currently selected)" : ""); 411 | } 412 | 413 | if (rtlsdr_open(&Modes.dev, Modes.dev_index) < 0) { 414 | fprintf(stderr, "Error opening the RTLSDR device: %s\n", 415 | strerror(errno)); 416 | return(1); 417 | } 418 | 419 | /* Set gain, frequency, sample rate, and reset the device. */ 420 | rtlsdr_set_tuner_gain_mode(Modes.dev, 421 | (Modes.gain == MODES_AUTO_GAIN) ? 0 : 1); 422 | if (Modes.gain != MODES_AUTO_GAIN) { 423 | if (Modes.gain == MODES_MAX_GAIN) { 424 | /* Find the maximum gain available. */ 425 | int numgains; 426 | int gains[100]; 427 | 428 | numgains = rtlsdr_get_tuner_gains(Modes.dev, gains); 429 | Modes.gain = gains[numgains-1]; 430 | fprintf(stderr, "Max available gain is: %.2f\n", Modes.gain/10.0); 431 | } 432 | rtlsdr_set_tuner_gain(Modes.dev, Modes.gain); 433 | fprintf(stderr, "Setting gain to: %.2f\n", Modes.gain/10.0); 434 | } else { 435 | fprintf(stderr, "Using automatic gain control.\n"); 436 | } 437 | rtlsdr_set_freq_correction(Modes.dev, ppm_error); 438 | if (Modes.enable_agc) rtlsdr_set_agc_mode(Modes.dev, 1); 439 | rtlsdr_set_center_freq(Modes.dev, Modes.freq); 440 | rtlsdr_set_sample_rate(Modes.dev, MODES_DEFAULT_RATE); 441 | rtlsdr_reset_buffer(Modes.dev); 442 | fprintf(stderr, "Gain reported by device: %.2f\n", 443 | rtlsdr_get_tuner_gain(Modes.dev)/10.0); 444 | Modes.rtl_enabled = 1; 445 | Modes.hackrf_enabled = 0; 446 | Modes.airspy_enabled = 0; 447 | #ifndef NoSDRplay 448 | Modes.sdrplay_enabled = 0; 449 | #endif 450 | return (0); 451 | } 452 | 453 | /* =============================== AirSpy handling ========================== */ 454 | int modesInitAirSpy(void) { 455 | #define AIRSPY_STATUS(status, message) \ 456 | if (status != 0) { \ 457 | fprintf(stderr, "%s\n", message); \ 458 | airspy_close(Modes.airspy); \ 459 | airspy_exit(); \ 460 | return (1); \ 461 | } \ 462 | 463 | int status; 464 | soxr_error_t sox_err = NULL; 465 | soxr_io_spec_t ios; 466 | soxr_quality_spec_t qts; 467 | soxr_runtime_spec_t rts; 468 | 469 | Modes.airspy_scratch = calloc(2*MODES_DATA_LEN, sizeof(int16_t)); 470 | Modes.airspy_bytes = malloc(2*MODES_DATA_LEN); 471 | if ((Modes.airspy_bytes == NULL) || (Modes.airspy_scratch == NULL)) 472 | err(1, NULL); 473 | 474 | ios = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I); 475 | qts = soxr_quality_spec(SOXR_MQ, 0); 476 | rts = soxr_runtime_spec(2); 477 | 478 | status = airspy_init(); 479 | AIRSPY_STATUS(status, "airspy_init failed."); 480 | 481 | status = airspy_open(&Modes.airspy); 482 | AIRSPY_STATUS(status, "No AirSpy compatible devices found."); 483 | 484 | // The initial airspy mini doesnot support 10MSPS, 485 | // its supported samplerate is 6Msps, 3Msps 486 | uint32_t count=0; 487 | airspy_get_samplerates(Modes.airspy, &count, 0); 488 | uint32_t supported_samplerates[10]={0}; //10 is enough 489 | airspy_get_samplerates(Modes.airspy, supported_samplerates, count); 490 | for(uint32_t i=0;i MODES_DATA_LEN) len = MODES_DATA_LEN; 680 | /* Move the last part of the previous buffer, that was not processed, 681 | * on the start of the new buffer. */ 682 | memcpy(Modes.data, Modes.data+MODES_DATA_LEN, (MODES_FULL_LEN-1)*4); 683 | /* Read the new data. */ 684 | memcpy(Modes.data+(MODES_FULL_LEN-1)*4, buf, len); 685 | Modes.data_ready = 1; 686 | /* Signal to the other thread that new data is ready */ 687 | pthread_cond_signal(&Modes.data_cond); 688 | pthread_mutex_unlock(&Modes.data_mutex); 689 | } 690 | 691 | int hackrfCallback (hackrf_transfer *transfer) { 692 | uint32_t i; 693 | pthread_mutex_lock(&Modes.data_mutex); 694 | uint32_t len = transfer-> buffer_length; 695 | /* HackRF One returns signed IQ values, convert them to unsigned */ 696 | for (i = 0; i < len; i++) { 697 | transfer->buffer[i] ^= (uint8_t)0x80; 698 | } 699 | if (len > MODES_DATA_LEN) len = MODES_DATA_LEN; 700 | /* Move the last part of the previous buffer, that was not processed, 701 | * on the start of the new buffer. */ 702 | memcpy(Modes.data, Modes.data+MODES_DATA_LEN, (MODES_FULL_LEN-1)*4); 703 | /* Read the new data. */ 704 | memcpy(Modes.data+(MODES_FULL_LEN-1)*4, transfer->buffer, len); 705 | Modes.data_ready = 1; 706 | /* Signal to the other thread that new data is ready */ 707 | pthread_cond_signal(&Modes.data_cond); 708 | pthread_mutex_unlock(&Modes.data_mutex); 709 | return (0); 710 | } 711 | 712 | int airspyCallback (airspy_transfer *transfer) { 713 | pthread_mutex_lock(&Modes.data_mutex); 714 | int16_t *inptr = (int16_t *)transfer->samples; 715 | int16_t *outptr = (int16_t *)Modes.airspy_scratch; 716 | size_t i, i_done, o_done, i_len, len; 717 | 718 | i_len = transfer->sample_count; 719 | if(Modes.support_10MSPS) 720 | { 721 | len = 4 * i_len / 5; // downsample from 2.5Msps to 2Msps 722 | } 723 | else 724 | { 725 | len = 2 * i_len / 3; // downsample from 3Msps to 2Msps 726 | } 727 | 728 | soxr_process(Modes.resampler, inptr, i_len, &i_done, outptr, len, &o_done); 729 | for(i = 0; i < o_done; i++) 730 | Modes.airspy_bytes[i] = (int8_t)(outptr[i]>>4)+127; 731 | len = o_done; 732 | if (len > MODES_DATA_LEN) len = MODES_DATA_LEN; 733 | /* Move the last part of the previous buffer, that was not processed, 734 | * on the start of the new buffer. */ 735 | memcpy(Modes.data, Modes.data+MODES_DATA_LEN, (MODES_FULL_LEN-1)*4); 736 | /* Read the new data. */ 737 | memcpy(Modes.data+(MODES_FULL_LEN-1)*4, Modes.airspy_bytes, len); 738 | Modes.data_ready = 1; 739 | /* Signal to the other thread that new data is ready */ 740 | pthread_cond_signal(&Modes.data_cond); 741 | pthread_mutex_unlock(&Modes.data_mutex); 742 | return (0); 743 | } 744 | 745 | /* This is used when --ifile is specified in order to read data from file 746 | * instead of using an RTLSDR device. */ 747 | void readDataFromFile(void) { 748 | pthread_mutex_lock(&Modes.data_mutex); 749 | while(1) { 750 | ssize_t nread, toread; 751 | unsigned char *p; 752 | 753 | if (Modes.data_ready) { 754 | pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex); 755 | continue; 756 | } 757 | 758 | if (Modes.interactive) { 759 | /* When --ifile and --interactive are used together, slow down 760 | * playing at the natural rate of the RTLSDR received. */ 761 | pthread_mutex_unlock(&Modes.data_mutex); 762 | usleep(5000); 763 | pthread_mutex_lock(&Modes.data_mutex); 764 | } 765 | 766 | /* Move the last part of the previous buffer, that was not processed, 767 | * on the start of the new buffer. */ 768 | memcpy(Modes.data, Modes.data+MODES_DATA_LEN, (MODES_FULL_LEN-1)*4); 769 | toread = MODES_DATA_LEN; 770 | p = Modes.data+(MODES_FULL_LEN-1)*4; 771 | while(toread) { 772 | nread = read(Modes.fd, p, toread); 773 | if (nread <= 0) { 774 | Modes.exit = 1; /* Signal the other thread to exit. */ 775 | break; 776 | } 777 | p += nread; 778 | toread -= nread; 779 | } 780 | if (toread) { 781 | /* Not enough data on file to fill the buffer? Pad with 782 | * no signal. */ 783 | memset(p,127,toread); 784 | } 785 | Modes.data_ready = 1; 786 | /* Signal to the other thread that new data is ready */ 787 | pthread_cond_signal(&Modes.data_cond); 788 | } 789 | } 790 | 791 | #ifndef NoSDRplay 792 | int sdrplay_start_rx(void) { 793 | unsigned int data_index, firstSampleNum; 794 | int grChanged, rfChanged, fsChanged; 795 | int input_index = Modes.sdrplaySamplesPerPacket; 796 | mir_sdr_ErrT err = 0; 797 | 798 | pthread_mutex_lock(&Modes.data_mutex); 799 | while(1) 800 | { 801 | 802 | if (Modes.data_ready) { 803 | pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex); 804 | continue; 805 | } 806 | 807 | /* Move the last part of the previous buffer, that was not processed, 808 | * on the start of the new buffer. */ 809 | 810 | memcpy(Modes.magnitude, Modes.magnitude+MODES_DATA_LEN, (MODES_FULL_LEN-1)*4); 811 | 812 | /* now read new data buffer */ 813 | 814 | data_index = (MODES_FULL_LEN-1)*2; 815 | while (data_index < ((MODES_DATA_LEN/2) + (MODES_FULL_LEN-1)*2)) 816 | { 817 | /* copy available data into buffer */ 818 | 819 | while ((data_index < (MODES_DATA_LEN/2 + (MODES_FULL_LEN-1)*2)) && (input_index < Modes.sdrplaySamplesPerPacket)) 820 | { 821 | int sum = abs(Modes.sdrplay_i[input_index++]); 822 | sum += abs(Modes.sdrplay_i[input_index++]); 823 | sum += abs(Modes.sdrplay_i[input_index++]); 824 | sum += abs(Modes.sdrplay_i[input_index++]); 825 | sum = sum >> 2; 826 | if (sum > 32767) sum = 32767; 827 | Modes.magnitude[data_index++] = sum; 828 | } 829 | 830 | if (input_index > Modes.sdrplaySamplesPerPacket) { 831 | fprintf(stderr, "ERROR packet size not divisible by 4\n"); 832 | Modes.exit = 1; /* Signal the other thread to exit. */ 833 | break; 834 | } 835 | 836 | 837 | if (input_index == Modes.sdrplaySamplesPerPacket) 838 | { 839 | input_index = 0; 840 | err = mir_sdr_ReadPacket (Modes.sdrplay_i, Modes.sdrplay_q, 841 | &firstSampleNum, &grChanged, &rfChanged, &fsChanged); 842 | 843 | if (err){ 844 | fprintf(stderr, "sdrplay data read failed\n"); 845 | Modes.exit = 1; /* Signal the other thread to exit. */ 846 | break; 847 | } 848 | } 849 | } 850 | 851 | Modes.data_ready = 1; 852 | /* Signal to the other thread that new data is ready */ 853 | pthread_cond_signal(&Modes.data_cond); 854 | } 855 | return (err)? 1 : 0; 856 | } 857 | #endif 858 | 859 | /* We read data using a thread, so the main thread only handles decoding 860 | * without caring about data acquisition. */ 861 | void *readerThreadEntryPoint(void *arg) { 862 | MODES_NOTUSED(arg); 863 | 864 | if (Modes.filename == NULL) { 865 | if (Modes.rtl_enabled) { 866 | rtlsdr_read_async(Modes.dev, rtlsdrCallback, NULL, 867 | MODES_ASYNC_BUF_NUMBER, 868 | MODES_DATA_LEN); 869 | } else if (Modes.hackrf_enabled) { 870 | int status = hackrf_start_rx(Modes.hackrf, hackrfCallback, NULL); 871 | if (status != 0) { 872 | fprintf(stderr, "hackrf_start_rx failed"); 873 | hackrf_close(Modes.hackrf); 874 | hackrf_exit(); 875 | exit (1); 876 | } 877 | } else if (Modes.airspy_enabled) { 878 | int status = airspy_start_rx(Modes.airspy, airspyCallback, NULL); 879 | if (status != 0) { 880 | fprintf(stderr, "airspy_start_rx failed"); 881 | airspy_close(Modes.airspy); 882 | airspy_exit(); 883 | exit (1); 884 | } 885 | } 886 | #ifndef NoSDRplay 887 | else if (Modes.sdrplay_enabled) { 888 | int status = sdrplay_start_rx(); 889 | if (status != 0) { 890 | fprintf(stderr, "sdrplay_start_rx failed"); 891 | mir_sdr_Uninit(); 892 | exit (1); 893 | } 894 | } 895 | #endif 896 | } else { 897 | readDataFromFile(); 898 | } 899 | return NULL; 900 | } 901 | 902 | /* ============================== Debugging ================================= */ 903 | 904 | /* Helper function for dumpMagnitudeVector(). 905 | * It prints a single bar used to display raw signals. 906 | * 907 | * Since every magnitude sample is between 0-255, the function uses 908 | * up to 63 characters for every bar. Every character represents 909 | * a length of 4, 3, 2, 1, specifically: 910 | * 911 | * "O" is 4 912 | * "o" is 3 913 | * "-" is 2 914 | * "." is 1 915 | */ 916 | void dumpMagnitudeBar(int index, int magnitude) { 917 | char *set = " .-o"; 918 | char buf[256]; 919 | int div = magnitude / 256 / 4; 920 | int rem = magnitude / 256 % 4; 921 | 922 | memset(buf,'O',div); 923 | buf[div] = set[rem]; 924 | buf[div+1] = '\0'; 925 | 926 | if (index >= 0) 927 | printf("[%.3d] |%-66s %d\n", index, buf, magnitude); 928 | else 929 | printf("[%.2d] |%-66s %d\n", index, buf, magnitude); 930 | } 931 | 932 | /* Display an ASCII-art alike graphical representation of the undecoded 933 | * message as a magnitude signal. 934 | * 935 | * The message starts at the specified offset in the "m" buffer. 936 | * The function will display enough data to cover a short 56 bit message. 937 | * 938 | * If possible a few samples before the start of the messsage are included 939 | * for context. */ 940 | 941 | void dumpMagnitudeVector(uint16_t *m, uint32_t offset) { 942 | uint32_t padding = 5; /* Show a few samples before the actual start. */ 943 | uint32_t start = (offset < padding) ? 0 : offset-padding; 944 | uint32_t end = offset + (MODES_PREAMBLE_US*2)+(MODES_SHORT_MSG_BITS*2) - 1; 945 | uint32_t j; 946 | 947 | for (j = start; j <= end; j++) { 948 | dumpMagnitudeBar(j-offset, m[j]); 949 | } 950 | } 951 | 952 | /* Produce a raw representation of the message as a Javascript file 953 | * loadable by debug.html. */ 954 | void dumpRawMessageJS(char *descr, unsigned char *msg, 955 | uint16_t *m, uint32_t offset, int fixable) 956 | { 957 | int padding = 5; /* Show a few samples before the actual start. */ 958 | int start = offset - padding; 959 | int end = offset + (MODES_PREAMBLE_US*2)+(MODES_LONG_MSG_BITS*2) - 1; 960 | FILE *fp; 961 | int j, fix1 = -1, fix2 = -1; 962 | 963 | if (fixable != -1) { 964 | fix1 = fixable & 0xff; 965 | if (fixable > 255) fix2 = fixable >> 8; 966 | } 967 | 968 | if ((fp = fopen("frames.js","a")) == NULL) { 969 | fprintf(stderr, "Error opening frames.js: %s\n", strerror(errno)); 970 | exit(1); 971 | } 972 | 973 | fprintf(fp,"frames.push({\"descr\": \"%s\", \"mag\": [", descr); 974 | for (j = start; j <= end; j++) { 975 | fprintf(fp,"%d", j < 0 ? 0 : m[j]); 976 | if (j != end) fprintf(fp,","); 977 | } 978 | fprintf(fp,"], \"fix1\": %d, \"fix2\": %d, \"bits\": %d, \"hex\": \"", 979 | fix1, fix2, modesMessageLenByType(msg[0]>>3)); 980 | for (j = 0; j < MODES_LONG_MSG_BYTES; j++) 981 | fprintf(fp,"\\x%02x",msg[j]); 982 | fprintf(fp,"\"});\n"); 983 | fclose(fp); 984 | } 985 | 986 | /* This is a wrapper for dumpMagnitudeVector() that also show the message 987 | * in hex format with an additional description. 988 | * 989 | * descr is the additional message to show to describe the dump. 990 | * msg points to the decoded message 991 | * m is the original magnitude vector 992 | * offset is the offset where the message starts 993 | * 994 | * The function also produces the Javascript file used by debug.html to 995 | * display packets in a graphical format if the Javascript output was 996 | * enabled. 997 | */ 998 | void dumpRawMessage(char *descr, unsigned char *msg, 999 | uint16_t *m, uint32_t offset) 1000 | { 1001 | int j; 1002 | int msgtype = msg[0]>>3; 1003 | int fixable = -1; 1004 | 1005 | if (msgtype == 11 || msgtype == 17) { 1006 | int msgbits = (msgtype == 11) ? MODES_SHORT_MSG_BITS : 1007 | MODES_LONG_MSG_BITS; 1008 | fixable = fixSingleBitErrors(msg,msgbits); 1009 | if (fixable == -1) 1010 | fixable = fixTwoBitsErrors(msg,msgbits); 1011 | } 1012 | 1013 | if (Modes.debug & MODES_DEBUG_JS) { 1014 | dumpRawMessageJS(descr, msg, m, offset, fixable); 1015 | return; 1016 | } 1017 | 1018 | printf("\n--- %s\n ", descr); 1019 | for (j = 0; j < MODES_LONG_MSG_BYTES; j++) { 1020 | printf("%02x",msg[j]); 1021 | if (j == MODES_SHORT_MSG_BYTES-1) printf(" ... "); 1022 | } 1023 | printf(" (DF %d, Fixable: %d)\n", msgtype, fixable); 1024 | dumpMagnitudeVector(m,offset); 1025 | printf("---\n\n"); 1026 | } 1027 | 1028 | /* ===================== Mode S detection and decoding ===================== */ 1029 | 1030 | /* Parity table for MODE S Messages. 1031 | * The table contains 112 elements, every element corresponds to a bit set 1032 | * in the message, starting from the first bit of actual data after the 1033 | * preamble. 1034 | * 1035 | * For messages of 112 bit, the whole table is used. 1036 | * For messages of 56 bits only the last 56 elements are used. 1037 | * 1038 | * The algorithm is as simple as xoring all the elements in this table 1039 | * for which the corresponding bit on the message is set to 1. 1040 | * 1041 | * The latest 24 elements in this table are set to 0 as the checksum at the 1042 | * end of the message should not affect the computation. 1043 | * 1044 | * Note: this function can be used with DF11 and DF17, other modes have 1045 | * the CRC xored with the sender address as they are reply to interrogations, 1046 | * but a casual listener can't split the address from the checksum. 1047 | */ 1048 | uint32_t modes_checksum_table[112] = { 1049 | 0x3935ea, 0x1c9af5, 0xf1b77e, 0x78dbbf, 0xc397db, 0x9e31e9, 0xb0e2f0, 0x587178, 1050 | 0x2c38bc, 0x161c5e, 0x0b0e2f, 0xfa7d13, 0x82c48d, 0xbe9842, 0x5f4c21, 0xd05c14, 1051 | 0x682e0a, 0x341705, 0xe5f186, 0x72f8c3, 0xc68665, 0x9cb936, 0x4e5c9b, 0xd8d449, 1052 | 0x939020, 0x49c810, 0x24e408, 0x127204, 0x093902, 0x049c81, 0xfdb444, 0x7eda22, 1053 | 0x3f6d11, 0xe04c8c, 0x702646, 0x381323, 0xe3f395, 0x8e03ce, 0x4701e7, 0xdc7af7, 1054 | 0x91c77f, 0xb719bb, 0xa476d9, 0xadc168, 0x56e0b4, 0x2b705a, 0x15b82d, 0xf52612, 1055 | 0x7a9309, 0xc2b380, 0x6159c0, 0x30ace0, 0x185670, 0x0c2b38, 0x06159c, 0x030ace, 1056 | 0x018567, 0xff38b7, 0x80665f, 0xbfc92b, 0xa01e91, 0xaff54c, 0x57faa6, 0x2bfd53, 1057 | 0xea04ad, 0x8af852, 0x457c29, 0xdd4410, 0x6ea208, 0x375104, 0x1ba882, 0x0dd441, 1058 | 0xf91024, 0x7c8812, 0x3e4409, 0xe0d800, 0x706c00, 0x383600, 0x1c1b00, 0x0e0d80, 1059 | 0x0706c0, 0x038360, 0x01c1b0, 0x00e0d8, 0x00706c, 0x003836, 0x001c1b, 0xfff409, 1060 | 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 1061 | 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 1062 | 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 1063 | }; 1064 | 1065 | uint32_t modesChecksum(unsigned char *msg, int bits) { 1066 | uint32_t crc = 0; 1067 | int offset = (bits == 112) ? 0 : (112-56); 1068 | int j; 1069 | 1070 | for(j = 0; j < bits; j++) { 1071 | int byte = j/8; 1072 | int bit = j%8; 1073 | int bitmask = 1 << (7-bit); 1074 | 1075 | /* If bit is set, xor with corresponding table entry. */ 1076 | if (msg[byte] & bitmask) 1077 | crc ^= modes_checksum_table[j+offset]; 1078 | } 1079 | return crc; /* 24 bit checksum. */ 1080 | } 1081 | 1082 | /* Given the Downlink Format (DF) of the message, return the message length 1083 | * in bits. */ 1084 | int modesMessageLenByType(int type) { 1085 | if (type == 16 || type == 17 || 1086 | type == 19 || type == 20 || 1087 | type == 21) 1088 | return MODES_LONG_MSG_BITS; 1089 | else 1090 | return MODES_SHORT_MSG_BITS; 1091 | } 1092 | 1093 | /* Try to fix single bit errors using the checksum. On success modifies 1094 | * the original buffer with the fixed version, and returns the position 1095 | * of the error bit. Otherwise if fixing failed -1 is returned. */ 1096 | int fixSingleBitErrors(unsigned char *msg, int bits) { 1097 | int j; 1098 | unsigned char aux[MODES_LONG_MSG_BITS/8]; 1099 | 1100 | for (j = 0; j < bits; j++) { 1101 | int byte = j/8; 1102 | int bitmask = 1 << (7-(j%8)); 1103 | uint32_t crc1, crc2; 1104 | 1105 | memcpy(aux,msg,bits/8); 1106 | aux[byte] ^= bitmask; /* Flip j-th bit. */ 1107 | 1108 | crc1 = ((uint32_t)aux[(bits/8)-3] << 16) | 1109 | ((uint32_t)aux[(bits/8)-2] << 8) | 1110 | (uint32_t)aux[(bits/8)-1]; 1111 | crc2 = modesChecksum(aux,bits); 1112 | 1113 | if (crc1 == crc2) { 1114 | /* The error is fixed. Overwrite the original buffer with 1115 | * the corrected sequence, and returns the error bit 1116 | * position. */ 1117 | memcpy(msg,aux,bits/8); 1118 | return j; 1119 | } 1120 | } 1121 | return -1; 1122 | } 1123 | 1124 | /* Similar to fixSingleBitErrors() but try every possible two bit combination. 1125 | * This is very slow and should be tried only against DF17 messages that 1126 | * don't pass the checksum, and only in Aggressive Mode. */ 1127 | int fixTwoBitsErrors(unsigned char *msg, int bits) { 1128 | int j, i; 1129 | unsigned char aux[MODES_LONG_MSG_BITS/8]; 1130 | 1131 | for (j = 0; j < bits; j++) { 1132 | int byte1 = j/8; 1133 | int bitmask1 = 1 << (7-(j%8)); 1134 | 1135 | /* Don't check the same pairs multiple times, so i starts from j+1 */ 1136 | for (i = j+1; i < bits; i++) { 1137 | int byte2 = i/8; 1138 | int bitmask2 = 1 << (7-(i%8)); 1139 | uint32_t crc1, crc2; 1140 | 1141 | memcpy(aux,msg,bits/8); 1142 | 1143 | aux[byte1] ^= bitmask1; /* Flip j-th bit. */ 1144 | aux[byte2] ^= bitmask2; /* Flip i-th bit. */ 1145 | 1146 | crc1 = ((uint32_t)aux[(bits/8)-3] << 16) | 1147 | ((uint32_t)aux[(bits/8)-2] << 8) | 1148 | (uint32_t)aux[(bits/8)-1]; 1149 | crc2 = modesChecksum(aux,bits); 1150 | 1151 | if (crc1 == crc2) { 1152 | /* The error is fixed. Overwrite the original buffer with 1153 | * the corrected sequence, and returns the error bit 1154 | * position. */ 1155 | memcpy(msg,aux,bits/8); 1156 | /* We return the two bits as a 16 bit integer by shifting 1157 | * 'i' on the left. This is possible since 'i' will always 1158 | * be non-zero because i starts from j+1. */ 1159 | return j | (i<<8); 1160 | } 1161 | } 1162 | } 1163 | return -1; 1164 | } 1165 | 1166 | /* Hash the ICAO address to index our cache of MODES_ICAO_CACHE_LEN 1167 | * elements, that is assumed to be a power of two. */ 1168 | uint32_t ICAOCacheHashAddress(uint32_t a) { 1169 | /* The following three rounds wil make sure that every bit affects 1170 | * every output bit with ~ 50% of probability. */ 1171 | a = ((a >> 16) ^ a) * 0x45d9f3b; 1172 | a = ((a >> 16) ^ a) * 0x45d9f3b; 1173 | a = ((a >> 16) ^ a); 1174 | return a & (MODES_ICAO_CACHE_LEN-1); 1175 | } 1176 | 1177 | /* Add the specified entry to the cache of recently seen ICAO addresses. 1178 | * Note that we also add a timestamp so that we can make sure that the 1179 | * entry is only valid for MODES_ICAO_CACHE_TTL seconds. */ 1180 | void addRecentlySeenICAOAddr(uint32_t addr) { 1181 | uint32_t h = ICAOCacheHashAddress(addr); 1182 | Modes.icao_cache[h*2] = addr; 1183 | Modes.icao_cache[h*2+1] = (uint32_t) time(NULL); 1184 | } 1185 | 1186 | /* Returns 1 if the specified ICAO address was seen in a DF format with 1187 | * proper checksum (not xored with address) no more than * MODES_ICAO_CACHE_TTL 1188 | * seconds ago. Otherwise returns 0. */ 1189 | int ICAOAddressWasRecentlySeen(uint32_t addr) { 1190 | uint32_t h = ICAOCacheHashAddress(addr); 1191 | uint32_t a = Modes.icao_cache[h*2]; 1192 | uint32_t t = Modes.icao_cache[h*2+1]; 1193 | 1194 | return a && a == addr && time(NULL)-t <= MODES_ICAO_CACHE_TTL; 1195 | } 1196 | 1197 | /* If the message type has the checksum xored with the ICAO address, try to 1198 | * brute force it using a list of recently seen ICAO addresses. 1199 | * 1200 | * Do this in a brute-force fashion by xoring the predicted CRC with 1201 | * the address XOR checksum field in the message. This will recover the 1202 | * address: if we found it in our cache, we can assume the message is ok. 1203 | * 1204 | * This function expects mm->msgtype and mm->msgbits to be correctly 1205 | * populated by the caller. 1206 | * 1207 | * On success the correct ICAO address is stored in the modesMessage 1208 | * structure in the aa3, aa2, and aa1 fiedls. 1209 | * 1210 | * If the function successfully recovers a message with a correct checksum 1211 | * it returns 1. Otherwise 0 is returned. */ 1212 | int bruteForceAP(unsigned char *msg, struct modesMessage *mm) { 1213 | unsigned char aux[MODES_LONG_MSG_BYTES]; 1214 | int msgtype = mm->msgtype; 1215 | int msgbits = mm->msgbits; 1216 | 1217 | if (msgtype == 0 || /* Short air surveillance */ 1218 | msgtype == 4 || /* Surveillance, altitude reply */ 1219 | msgtype == 5 || /* Surveillance, identity reply */ 1220 | msgtype == 16 || /* Long Air-Air survillance */ 1221 | msgtype == 20 || /* Comm-A, altitude request */ 1222 | msgtype == 21 || /* Comm-A, identity request */ 1223 | msgtype == 24) /* Comm-C ELM */ 1224 | { 1225 | uint32_t addr; 1226 | uint32_t crc; 1227 | int lastbyte = (msgbits/8)-1; 1228 | 1229 | /* Work on a copy. */ 1230 | memcpy(aux,msg,msgbits/8); 1231 | 1232 | /* Compute the CRC of the message and XOR it with the AP field 1233 | * so that we recover the address, because: 1234 | * 1235 | * (ADDR xor CRC) xor CRC = ADDR. */ 1236 | crc = modesChecksum(aux,msgbits); 1237 | aux[lastbyte] ^= crc & 0xff; 1238 | aux[lastbyte-1] ^= (crc >> 8) & 0xff; 1239 | aux[lastbyte-2] ^= (crc >> 16) & 0xff; 1240 | 1241 | /* If the obtained address exists in our cache we consider 1242 | * the message valid. */ 1243 | addr = aux[lastbyte] | (aux[lastbyte-1] << 8) | (aux[lastbyte-2] << 16); 1244 | if (ICAOAddressWasRecentlySeen(addr)) { 1245 | mm->aa1 = aux[lastbyte-2]; 1246 | mm->aa2 = aux[lastbyte-1]; 1247 | mm->aa3 = aux[lastbyte]; 1248 | return 1; 1249 | } 1250 | } 1251 | return 0; 1252 | } 1253 | 1254 | /* Decode the 13 bit AC altitude field (in DF 20 and others). 1255 | * Returns the altitude, and set 'unit' to either MODES_UNIT_METERS 1256 | * or MDOES_UNIT_FEETS. */ 1257 | int decodeAC13Field(unsigned char *msg, int *unit) { 1258 | int m_bit = msg[3] & (1<<6); 1259 | int q_bit = msg[3] & (1<<4); 1260 | 1261 | if (!m_bit) { 1262 | *unit = MODES_UNIT_FEET; 1263 | if (q_bit) { 1264 | /* N is the 11 bit integer resulting from the removal of bit 1265 | * Q and M */ 1266 | int n = ((msg[2]&31)<<6) | 1267 | ((msg[3]&0x80)>>2) | 1268 | ((msg[3]&0x20)>>1) | 1269 | (msg[3]&15); 1270 | /* The final altitude is due to the resulting number multiplied 1271 | * by 25, minus 1000. */ 1272 | return n*25-1000; 1273 | } else { 1274 | /* TODO: Implement altitude where Q=0 and M=0 */ 1275 | } 1276 | } else { 1277 | *unit = MODES_UNIT_METERS; 1278 | /* TODO: Implement altitude when meter unit is selected. */ 1279 | } 1280 | return 0; 1281 | } 1282 | 1283 | /* Decode the 12 bit AC altitude field (in DF 17 and others). 1284 | * Returns the altitude or 0 if it can't be decoded. */ 1285 | int decodeAC12Field(unsigned char *msg, int *unit) { 1286 | int q_bit = msg[5] & 1; 1287 | 1288 | if (q_bit) { 1289 | /* N is the 11 bit integer resulting from the removal of bit 1290 | * Q */ 1291 | *unit = MODES_UNIT_FEET; 1292 | int n = ((msg[5]>>1)<<4) | ((msg[6]&0xF0) >> 4); 1293 | /* The final altitude is due to the resulting number multiplied 1294 | * by 25, minus 1000. */ 1295 | return n*25-1000; 1296 | } else { 1297 | return 0; 1298 | } 1299 | } 1300 | 1301 | /* Capability table. */ 1302 | char *ca_str[8] = { 1303 | /* 0 */ "Level 1 (Survillance Only)", 1304 | /* 1 */ "Level 2 (DF0,4,5,11)", 1305 | /* 2 */ "Level 3 (DF0,4,5,11,20,21)", 1306 | /* 3 */ "Level 4 (DF0,4,5,11,20,21,24)", 1307 | /* 4 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on ground)", 1308 | /* 5 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on airborne)", 1309 | /* 6 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7)", 1310 | /* 7 */ "Level 7 ???" 1311 | }; 1312 | 1313 | /* Flight status table. */ 1314 | char *fs_str[8] = { 1315 | /* 0 */ "Normal, Airborne", 1316 | /* 1 */ "Normal, On the ground", 1317 | /* 2 */ "ALERT, Airborne", 1318 | /* 3 */ "ALERT, On the ground", 1319 | /* 4 */ "ALERT & Special Position Identification. Airborne or Ground", 1320 | /* 5 */ "Special Position Identification. Airborne or Ground", 1321 | /* 6 */ "Value 6 is not assigned", 1322 | /* 7 */ "Value 7 is not assigned" 1323 | }; 1324 | 1325 | /* ME message type to description table. */ 1326 | char *me_str[] = { 1327 | }; 1328 | 1329 | char *getMEDescription(int metype, int mesub) { 1330 | char *mename = "Unknown"; 1331 | 1332 | if (metype >= 1 && metype <= 4) 1333 | mename = "Aircraft Identification and Category"; 1334 | else if (metype >= 5 && metype <= 8) 1335 | mename = "Surface Position"; 1336 | else if (metype >= 9 && metype <= 18) 1337 | mename = "Airborne Position (Baro Altitude)"; 1338 | else if (metype == 19 && mesub >=1 && mesub <= 4) 1339 | mename = "Airborne Velocity"; 1340 | else if (metype >= 20 && metype <= 22) 1341 | mename = "Airborne Position (GNSS Height)"; 1342 | else if (metype == 23 && mesub == 0) 1343 | mename = "Test Message"; 1344 | else if (metype == 24 && mesub == 1) 1345 | mename = "Surface System Status"; 1346 | else if (metype == 28 && mesub == 1) 1347 | mename = "Extended Squitter Aircraft Status (Emergency)"; 1348 | else if (metype == 28 && mesub == 2) 1349 | mename = "Extended Squitter Aircraft Status (1090ES TCAS RA)"; 1350 | else if (metype == 29 && (mesub == 0 || mesub == 1)) 1351 | mename = "Target State and Status Message"; 1352 | else if (metype == 31 && (mesub == 0 || mesub == 1)) 1353 | mename = "Aircraft Operational Status Message"; 1354 | return mename; 1355 | } 1356 | 1357 | /* Decode a raw Mode S message demodulated as a stream of bytes by 1358 | * detectModeS(), and split it into fields populating a modesMessage 1359 | * structure. */ 1360 | void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) { 1361 | uint32_t crc2; /* Computed CRC, used to verify the message CRC. */ 1362 | char *ais_charset = "?ABCDEFGHIJKLMNOPQRSTUVWXYZ????? ???????????????0123456789??????"; 1363 | 1364 | /* Work on our local copy */ 1365 | memcpy(mm->msg,msg,MODES_LONG_MSG_BYTES); 1366 | msg = mm->msg; 1367 | 1368 | /* Get the message type ASAP as other operations depend on this */ 1369 | mm->msgtype = msg[0]>>3; /* Downlink Format */ 1370 | mm->msgbits = modesMessageLenByType(mm->msgtype); 1371 | 1372 | /* CRC is always the last three bytes. */ 1373 | mm->crc = ((uint32_t)msg[(mm->msgbits/8)-3] << 16) | 1374 | ((uint32_t)msg[(mm->msgbits/8)-2] << 8) | 1375 | (uint32_t)msg[(mm->msgbits/8)-1]; 1376 | crc2 = modesChecksum(msg,mm->msgbits); 1377 | 1378 | /* Check CRC and fix single bit errors using the CRC when 1379 | * possible (DF 11 and 17). */ 1380 | mm->errorbit = -1; /* No error */ 1381 | mm->crcok = (mm->crc == crc2); 1382 | 1383 | if (!mm->crcok && Modes.fix_errors && 1384 | (mm->msgtype == 11 || mm->msgtype == 17)) 1385 | { 1386 | if ((mm->errorbit = fixSingleBitErrors(msg,mm->msgbits)) != -1) { 1387 | mm->crc = modesChecksum(msg,mm->msgbits); 1388 | mm->crcok = 1; 1389 | } else if (Modes.aggressive && mm->msgtype == 17 && 1390 | (mm->errorbit = fixTwoBitsErrors(msg,mm->msgbits)) != -1) 1391 | { 1392 | mm->crc = modesChecksum(msg,mm->msgbits); 1393 | mm->crcok = 1; 1394 | } 1395 | } 1396 | 1397 | /* Note that most of the other computation happens *after* we fix 1398 | * the single bit errors, otherwise we would need to recompute the 1399 | * fields again. */ 1400 | mm->ca = msg[0] & 7; /* Responder capabilities. */ 1401 | 1402 | /* ICAO address */ 1403 | mm->aa1 = msg[1]; 1404 | mm->aa2 = msg[2]; 1405 | mm->aa3 = msg[3]; 1406 | 1407 | /* DF 17 type (assuming this is a DF17, otherwise not used) */ 1408 | mm->metype = msg[4] >> 3; /* Extended squitter message type. */ 1409 | mm->mesub = msg[4] & 7; /* Extended squitter message subtype. */ 1410 | 1411 | /* Fields for DF4,5,20,21 */ 1412 | mm->fs = msg[0] & 7; /* Flight status for DF4,5,20,21 */ 1413 | mm->dr = msg[1] >> 3 & 31; /* Request extraction of downlink request. */ 1414 | mm->um = ((msg[1] & 7)<<3)| /* Request extraction of downlink request. */ 1415 | msg[2]>>5; 1416 | 1417 | /* In the squawk (identity) field bits are interleaved like that 1418 | * (message bit 20 to bit 32): 1419 | * 1420 | * C1-A1-C2-A2-C4-A4-ZERO-B1-D1-B2-D2-B4-D4 1421 | * 1422 | * So every group of three bits A, B, C, D represent an integer 1423 | * from 0 to 7. 1424 | * 1425 | * The actual meaning is just 4 octal numbers, but we convert it 1426 | * into a base ten number tha happens to represent the four 1427 | * octal numbers. 1428 | * 1429 | * For more info: http://en.wikipedia.org/wiki/Gillham_code */ 1430 | { 1431 | int a,b,c,d; 1432 | 1433 | a = ((msg[3] & 0x80) >> 5) | 1434 | ((msg[2] & 0x02) >> 0) | 1435 | ((msg[2] & 0x08) >> 3); 1436 | b = ((msg[3] & 0x02) << 1) | 1437 | ((msg[3] & 0x08) >> 2) | 1438 | ((msg[3] & 0x20) >> 5); 1439 | c = ((msg[2] & 0x01) << 2) | 1440 | ((msg[2] & 0x04) >> 1) | 1441 | ((msg[2] & 0x10) >> 4); 1442 | d = ((msg[3] & 0x01) << 2) | 1443 | ((msg[3] & 0x04) >> 1) | 1444 | ((msg[3] & 0x10) >> 4); 1445 | mm->identity = a*1000 + b*100 + c*10 + d; 1446 | } 1447 | 1448 | /* DF 11 & 17: try to populate our ICAO addresses whitelist. 1449 | * DFs with an AP field (xored addr and crc), try to decode it. */ 1450 | if (mm->msgtype != 11 && mm->msgtype != 17) { 1451 | /* Check if we can check the checksum for the Downlink Formats where 1452 | * the checksum is xored with the aircraft ICAO address. We try to 1453 | * brute force it using a list of recently seen aircraft addresses. */ 1454 | if (bruteForceAP(msg,mm)) { 1455 | /* We recovered the message, mark the checksum as valid. */ 1456 | mm->crcok = 1; 1457 | } else { 1458 | mm->crcok = 0; 1459 | } 1460 | } else { 1461 | /* If this is DF 11 or DF 17 and the checksum was ok, 1462 | * we can add this address to the list of recently seen 1463 | * addresses. */ 1464 | if (mm->crcok && mm->errorbit == -1) { 1465 | uint32_t addr = (mm->aa1 << 16) | (mm->aa2 << 8) | mm->aa3; 1466 | addRecentlySeenICAOAddr(addr); 1467 | } 1468 | } 1469 | 1470 | /* Decode 13 bit altitude for DF0, DF4, DF16, DF20 */ 1471 | if (mm->msgtype == 0 || mm->msgtype == 4 || 1472 | mm->msgtype == 16 || mm->msgtype == 20) { 1473 | mm->altitude = decodeAC13Field(msg, &mm->unit); 1474 | } 1475 | 1476 | /* Decode extended squitter specific stuff. */ 1477 | if (mm->msgtype == 17) { 1478 | /* Decode the extended squitter message. */ 1479 | 1480 | if (mm->metype >= 1 && mm->metype <= 4) { 1481 | /* Aircraft Identification and Category */ 1482 | mm->aircraft_type = mm->metype-1; 1483 | mm->flight[0] = ais_charset[msg[5]>>2]; 1484 | mm->flight[1] = ais_charset[((msg[5]&3)<<4)|(msg[6]>>4)]; 1485 | mm->flight[2] = ais_charset[((msg[6]&15)<<2)|(msg[7]>>6)]; 1486 | mm->flight[3] = ais_charset[msg[7]&63]; 1487 | mm->flight[4] = ais_charset[msg[8]>>2]; 1488 | mm->flight[5] = ais_charset[((msg[8]&3)<<4)|(msg[9]>>4)]; 1489 | mm->flight[6] = ais_charset[((msg[9]&15)<<2)|(msg[10]>>6)]; 1490 | mm->flight[7] = ais_charset[msg[10]&63]; 1491 | mm->flight[8] = '\0'; 1492 | } else if (mm->metype >= 9 && mm->metype <= 18) { 1493 | /* Airborne position Message */ 1494 | mm->fflag = msg[6] & (1<<2); 1495 | mm->tflag = msg[6] & (1<<3); 1496 | mm->altitude = decodeAC12Field(msg,&mm->unit); 1497 | mm->raw_latitude = ((msg[6] & 3) << 15) | 1498 | (msg[7] << 7) | 1499 | (msg[8] >> 1); 1500 | mm->raw_longitude = ((msg[8]&1) << 16) | 1501 | (msg[9] << 8) | 1502 | msg[10]; 1503 | } else if (mm->metype == 19 && mm->mesub >= 1 && mm->mesub <= 4) { 1504 | /* Airborne Velocity Message */ 1505 | if (mm->mesub == 1 || mm->mesub == 2) { 1506 | mm->ew_dir = (msg[5]&4) >> 2; 1507 | mm->ew_velocity = ((msg[5]&3) << 8) | msg[6]; 1508 | mm->ns_dir = (msg[7]&0x80) >> 7; 1509 | mm->ns_velocity = ((msg[7]&0x7f) << 3) | ((msg[8]&0xe0) >> 5); 1510 | mm->vert_rate_source = (msg[8]&0x10) >> 4; 1511 | mm->vert_rate_sign = (msg[8]&0x8) >> 3; 1512 | mm->vert_rate = ((msg[8]&7) << 6) | ((msg[9]&0xfc) >> 2); 1513 | /* Compute velocity and angle from the two speed 1514 | * components. */ 1515 | mm->velocity = sqrt(mm->ns_velocity*mm->ns_velocity+ 1516 | mm->ew_velocity*mm->ew_velocity); 1517 | if (mm->velocity) { 1518 | int ewv = mm->ew_velocity; 1519 | int nsv = mm->ns_velocity; 1520 | double heading; 1521 | 1522 | if (mm->ew_dir) ewv *= -1; 1523 | if (mm->ns_dir) nsv *= -1; 1524 | heading = atan2(ewv,nsv); 1525 | 1526 | /* Convert to degrees. */ 1527 | mm->heading = heading * 360 / (M_PI*2); 1528 | /* We don't want negative values but a 0-360 scale. */ 1529 | if (mm->heading < 0) mm->heading += 360; 1530 | } else { 1531 | mm->heading = 0; 1532 | } 1533 | } else if (mm->mesub == 3 || mm->mesub == 4) { 1534 | mm->heading_is_valid = msg[5] & (1<<2); 1535 | mm->heading = (360.0/128) * (((msg[5] & 3) << 5) | 1536 | (msg[6] >> 3)); 1537 | } 1538 | } 1539 | } 1540 | mm->phase_corrected = 0; /* Set to 1 by the caller if needed. */ 1541 | } 1542 | 1543 | /* This function gets a decoded Mode S Message and prints it on the screen 1544 | * in a human readable format. */ 1545 | void displayModesMessage(struct modesMessage *mm) { 1546 | int j; 1547 | 1548 | /* Handle only addresses mode first. */ 1549 | if (Modes.onlyaddr) { 1550 | printf("%02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1551 | return; 1552 | } 1553 | 1554 | /* Show the raw message. */ 1555 | printf("*"); 1556 | for (j = 0; j < mm->msgbits/8; j++) printf("%02x", mm->msg[j]); 1557 | printf(";\n"); 1558 | 1559 | if (Modes.raw) { 1560 | fflush(stdout); /* Provide data to the reader ASAP. */ 1561 | return; /* Enough for --raw mode */ 1562 | } 1563 | 1564 | printf("CRC: %06x (%s)\n", (int)mm->crc, mm->crcok ? "ok" : "wrong"); 1565 | if (mm->errorbit != -1) 1566 | printf("Single bit error fixed, bit %d\n", mm->errorbit); 1567 | 1568 | if (mm->msgtype == 0) { 1569 | /* DF 0 */ 1570 | printf("DF 0: Short Air-Air Surveillance.\n"); 1571 | printf(" Altitude : %d %s\n", mm->altitude, 1572 | (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); 1573 | printf(" ICAO Address : %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1574 | } else if (mm->msgtype == 4 || mm->msgtype == 20) { 1575 | printf("DF %d: %s, Altitude Reply.\n", mm->msgtype, 1576 | (mm->msgtype == 4) ? "Surveillance" : "Comm-B"); 1577 | printf(" Flight Status : %s\n", fs_str[mm->fs]); 1578 | printf(" DR : %d\n", mm->dr); 1579 | printf(" UM : %d\n", mm->um); 1580 | printf(" Altitude : %d %s\n", mm->altitude, 1581 | (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); 1582 | printf(" ICAO Address : %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1583 | 1584 | if (mm->msgtype == 20) { 1585 | /* TODO: 56 bits DF20 MB additional field. */ 1586 | } 1587 | } else if (mm->msgtype == 5 || mm->msgtype == 21) { 1588 | printf("DF %d: %s, Identity Reply.\n", mm->msgtype, 1589 | (mm->msgtype == 5) ? "Surveillance" : "Comm-B"); 1590 | printf(" Flight Status : %s\n", fs_str[mm->fs]); 1591 | printf(" DR : %d\n", mm->dr); 1592 | printf(" UM : %d\n", mm->um); 1593 | printf(" Squawk : %d\n", mm->identity); 1594 | printf(" ICAO Address : %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1595 | 1596 | if (mm->msgtype == 21) { 1597 | /* TODO: 56 bits DF21 MB additional field. */ 1598 | } 1599 | } else if (mm->msgtype == 11) { 1600 | /* DF 11 */ 1601 | printf("DF 11: All Call Reply.\n"); 1602 | printf(" Capability : %s\n", ca_str[mm->ca]); 1603 | printf(" ICAO Address: %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1604 | } else if (mm->msgtype == 17) { 1605 | /* DF 17 */ 1606 | printf("DF 17: ADS-B message.\n"); 1607 | printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]); 1608 | printf(" ICAO Address : %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1609 | printf(" Extended Squitter Type: %d\n", mm->metype); 1610 | printf(" Extended Squitter Sub : %d\n", mm->mesub); 1611 | printf(" Extended Squitter Name: %s\n", 1612 | getMEDescription(mm->metype,mm->mesub)); 1613 | 1614 | /* Decode the extended squitter message. */ 1615 | if (mm->metype >= 1 && mm->metype <= 4) { 1616 | /* Aircraft identification. */ 1617 | char *ac_type_str[4] = { 1618 | "Aircraft Type D", 1619 | "Aircraft Type C", 1620 | "Aircraft Type B", 1621 | "Aircraft Type A" 1622 | }; 1623 | 1624 | printf(" Aircraft Type : %s\n", ac_type_str[mm->aircraft_type]); 1625 | printf(" Identification : %s\n", mm->flight); 1626 | } else if (mm->metype >= 9 && mm->metype <= 18) { 1627 | printf(" F flag : %s\n", mm->fflag ? "odd" : "even"); 1628 | printf(" T flag : %s\n", mm->tflag ? "UTC" : "non-UTC"); 1629 | printf(" Altitude : %d feet\n", mm->altitude); 1630 | printf(" Latitude : %d (not decoded)\n", mm->raw_latitude); 1631 | printf(" Longitude: %d (not decoded)\n", mm->raw_longitude); 1632 | } else if (mm->metype == 19 && mm->mesub >= 1 && mm->mesub <= 4) { 1633 | if (mm->mesub == 1 || mm->mesub == 2) { 1634 | /* Velocity */ 1635 | printf(" EW direction : %d\n", mm->ew_dir); 1636 | printf(" EW velocity : %d\n", mm->ew_velocity); 1637 | printf(" NS direction : %d\n", mm->ns_dir); 1638 | printf(" NS velocity : %d\n", mm->ns_velocity); 1639 | printf(" Vertical rate src : %d\n", mm->vert_rate_source); 1640 | printf(" Vertical rate sign: %d\n", mm->vert_rate_sign); 1641 | printf(" Vertical rate : %d\n", mm->vert_rate); 1642 | } else if (mm->mesub == 3 || mm->mesub == 4) { 1643 | printf(" Heading status: %d", mm->heading_is_valid); 1644 | printf(" Heading: %d", mm->heading); 1645 | } 1646 | } else { 1647 | printf(" Unrecognized ME type: %d subtype: %d\n", 1648 | mm->metype, mm->mesub); 1649 | } 1650 | } else { 1651 | if (Modes.check_crc) 1652 | printf("DF %d with good CRC received " 1653 | "(decoding still not implemented).\n", 1654 | mm->msgtype); 1655 | } 1656 | } 1657 | 1658 | float buf[16384]; 1659 | 1660 | /* Turn I/Q samples pointed by Modes.data into the magnitude vector 1661 | * pointed by Modes.magnitude. */ 1662 | void computeMagnitudeVector(void) { 1663 | uint16_t *m = Modes.magnitude; 1664 | unsigned char *p = Modes.data; 1665 | uint32_t j; 1666 | 1667 | /* Compute the magnitudo vector. It's just SQRT(I^2 + Q^2), but 1668 | * we rescale to the 0-255 range to exploit the full resolution. */ 1669 | for (j = 0; j < Modes.data_len; j += 2) { 1670 | int i = p[j]-127; 1671 | int q = p[j+1]-127; 1672 | 1673 | if (i < 0) i = -i; 1674 | if (q < 0) q = -q; 1675 | m[j/2] = Modes.maglut[i*129+q]; 1676 | } 1677 | 1678 | #ifdef XX 1679 | { 1680 | static FILE *fdump = NULL; int i; 1681 | if (fdump == NULL) fdump = fopen ("fdump", "w"); 1682 | for (i = 0; i < 16384; i++) { buf[i] = m[i]; } 1683 | fwrite (buf, sizeof(float), 16384, fdump); 1684 | } 1685 | #endif 1686 | } 1687 | 1688 | /* Return -1 if the message is out of fase left-side 1689 | * Return 1 if the message is out of fase right-size 1690 | * Return 0 if the message is not particularly out of phase. 1691 | * 1692 | * Note: this function will access m[-1], so the caller should make sure to 1693 | * call it only if we are not at the start of the current buffer. */ 1694 | int detectOutOfPhase(uint16_t *m) { 1695 | if (m[3] > m[2]/3) return 1; 1696 | if (m[10] > m[9]/3) return 1; 1697 | if (m[6] > m[7]/3) return -1; 1698 | if (m[-1] > m[1]/3) return -1; 1699 | return 0; 1700 | } 1701 | 1702 | /* This function does not really correct the phase of the message, it just 1703 | * applies a transformation to the first sample representing a given bit: 1704 | * 1705 | * If the previous bit was one, we amplify it a bit. 1706 | * If the previous bit was zero, we decrease it a bit. 1707 | * 1708 | * This simple transformation makes the message a bit more likely to be 1709 | * correctly decoded for out of phase messages: 1710 | * 1711 | * When messages are out of phase there is more uncertainty in 1712 | * sequences of the same bit multiple times, since 11111 will be 1713 | * transmitted as continuously altering magnitude (high, low, high, low...) 1714 | * 1715 | * However because the message is out of phase some part of the high 1716 | * is mixed in the low part, so that it is hard to distinguish if it is 1717 | * a zero or a one. 1718 | * 1719 | * However when the message is out of phase passing from 0 to 1 or from 1720 | * 1 to 0 happens in a very recognizable way, for instance in the 0 -> 1 1721 | * transition, magnitude goes low, high, high, low, and one of of the 1722 | * two middle samples the high will be *very* high as part of the previous 1723 | * or next high signal will be mixed there. 1724 | * 1725 | * Applying our simple transformation we make more likely if the current 1726 | * bit is a zero, to detect another zero. Symmetrically if it is a one 1727 | * it will be more likely to detect a one because of the transformation. 1728 | * In this way similar levels will be interpreted more likely in the 1729 | * correct way. */ 1730 | void applyPhaseCorrection(uint16_t *m) { 1731 | int j; 1732 | 1733 | m += 16; /* Skip preamble. */ 1734 | for (j = 0; j < (MODES_LONG_MSG_BITS-1)*2; j += 2) { 1735 | if (m[j] > m[j+1]) { 1736 | /* One */ 1737 | m[j+2] = (m[j+2] * 5) / 4; 1738 | } else { 1739 | /* Zero */ 1740 | m[j+2] = (m[j+2] * 4) / 5; 1741 | } 1742 | } 1743 | } 1744 | 1745 | /* Detect a Mode S messages inside the magnitude buffer pointed by 'm' and of 1746 | * size 'mlen' bytes. Every detected Mode S message is convert it into a 1747 | * stream of bits and passed to the function to display it. */ 1748 | void detectModeS(uint16_t *m, uint32_t mlen) { 1749 | unsigned char bits[MODES_LONG_MSG_BITS]; 1750 | unsigned char msg[MODES_LONG_MSG_BITS/2]; 1751 | uint16_t aux[MODES_LONG_MSG_BITS*2]; 1752 | uint32_t j; 1753 | int use_correction = 0; 1754 | 1755 | /* The Mode S preamble is made of impulses of 0.5 microseconds at 1756 | * the following time offsets: 1757 | * 1758 | * 0 - 0.5 usec: first impulse. 1759 | * 1.0 - 1.5 usec: second impulse. 1760 | * 3.5 - 4 usec: third impulse. 1761 | * 4.5 - 5 usec: last impulse. 1762 | * 1763 | * Since we are sampling at 2 Mhz every sample in our magnitude vector 1764 | * is 0.5 usec, so the preamble will look like this, assuming there is 1765 | * an impulse at offset 0 in the array: 1766 | * 1767 | * 0 ----------------- 1768 | * 1 - 1769 | * 2 ------------------ 1770 | * 3 -- 1771 | * 4 - 1772 | * 5 -- 1773 | * 6 - 1774 | * 7 ------------------ 1775 | * 8 -- 1776 | * 9 ------------------- 1777 | */ 1778 | for (j = 0; j < mlen - MODES_FULL_LEN*2; j++) { 1779 | int low, high, delta, i, errors; 1780 | int good_message = 0; 1781 | 1782 | if (use_correction) goto good_preamble; /* We already checked it. */ 1783 | 1784 | /* First check of relations between the first 10 samples 1785 | * representing a valid preamble. We don't even investigate further 1786 | * if this simple test is not passed. */ 1787 | if (!(m[j] > m[j+1] && 1788 | m[j+1] < m[j+2] && 1789 | m[j+2] > m[j+3] && 1790 | m[j+3] < m[j] && 1791 | m[j+4] < m[j] && 1792 | m[j+5] < m[j] && 1793 | m[j+6] < m[j] && 1794 | m[j+7] > m[j+8] && 1795 | m[j+8] < m[j+9] && 1796 | m[j+9] > m[j+6])) 1797 | { 1798 | if (Modes.debug & MODES_DEBUG_NOPREAMBLE && 1799 | m[j] > MODES_DEBUG_NOPREAMBLE_LEVEL) 1800 | dumpRawMessage("Unexpected ratio among first 10 samples", 1801 | msg, m, j); 1802 | continue; 1803 | } 1804 | 1805 | /* The samples between the two spikes must be < than the average 1806 | * of the high spikes level. We don't test bits too near to 1807 | * the high levels as signals can be out of phase so part of the 1808 | * energy can be in the near samples. */ 1809 | high = (m[j]+m[j+2]+m[j+7]+m[j+9])/6; 1810 | if (m[j+4] >= high || 1811 | m[j+5] >= high) 1812 | { 1813 | if (Modes.debug & MODES_DEBUG_NOPREAMBLE && 1814 | m[j] > MODES_DEBUG_NOPREAMBLE_LEVEL) 1815 | dumpRawMessage( 1816 | "Too high level in samples between 3 and 6", 1817 | msg, m, j); 1818 | continue; 1819 | } 1820 | 1821 | /* Similarly samples in the range 11-14 must be low, as it is the 1822 | * space between the preamble and real data. Again we don't test 1823 | * bits too near to high levels, see above. */ 1824 | if (m[j+11] >= high || 1825 | m[j+12] >= high || 1826 | m[j+13] >= high || 1827 | m[j+14] >= high) 1828 | { 1829 | if (Modes.debug & MODES_DEBUG_NOPREAMBLE && 1830 | m[j] > MODES_DEBUG_NOPREAMBLE_LEVEL) 1831 | dumpRawMessage( 1832 | "Too high level in samples between 10 and 15", 1833 | msg, m, j); 1834 | continue; 1835 | } 1836 | Modes.stat_valid_preamble++; 1837 | 1838 | good_preamble: 1839 | /* If the previous attempt with this message failed, retry using 1840 | * magnitude correction. */ 1841 | if (use_correction) { 1842 | memcpy(aux,m+j+MODES_PREAMBLE_US*2,sizeof(aux)); 1843 | if (j && detectOutOfPhase(m+j)) { 1844 | applyPhaseCorrection(m+j); 1845 | Modes.stat_out_of_phase++; 1846 | } 1847 | /* TODO ... apply other kind of corrections. */ 1848 | } 1849 | 1850 | /* Decode all the next 112 bits, regardless of the actual message 1851 | * size. We'll check the actual message type later. */ 1852 | errors = 0; 1853 | for (i = 0; i < MODES_LONG_MSG_BITS*2; i += 2) { 1854 | low = m[j+i+MODES_PREAMBLE_US*2]; 1855 | high = m[j+i+MODES_PREAMBLE_US*2+1]; 1856 | delta = low-high; 1857 | if (delta < 0) delta = -delta; 1858 | 1859 | if (i > 0 && delta < 256) { 1860 | bits[i/2] = bits[i/2-1]; 1861 | } else if (low == high) { 1862 | /* Checking if two adiacent samples have the same magnitude 1863 | * is an effective way to detect if it's just random noise 1864 | * that was detected as a valid preamble. */ 1865 | bits[i/2] = 2; /* error */ 1866 | if (i < MODES_SHORT_MSG_BITS*2) errors++; 1867 | } else if (low > high) { 1868 | bits[i/2] = 1; 1869 | } else { 1870 | /* (low < high) for exclusion */ 1871 | bits[i/2] = 0; 1872 | } 1873 | } 1874 | 1875 | /* Restore the original message if we used magnitude correction. */ 1876 | if (use_correction) 1877 | memcpy(m+j+MODES_PREAMBLE_US*2,aux,sizeof(aux)); 1878 | 1879 | /* Pack bits into bytes */ 1880 | for (i = 0; i < MODES_LONG_MSG_BITS; i += 8) { 1881 | msg[i/8] = 1882 | bits[i]<<7 | 1883 | bits[i+1]<<6 | 1884 | bits[i+2]<<5 | 1885 | bits[i+3]<<4 | 1886 | bits[i+4]<<3 | 1887 | bits[i+5]<<2 | 1888 | bits[i+6]<<1 | 1889 | bits[i+7]; 1890 | } 1891 | 1892 | int msgtype = msg[0]>>3; 1893 | int msglen = modesMessageLenByType(msgtype)/8; 1894 | 1895 | /* Last check, high and low bits are different enough in magnitude 1896 | * to mark this as real message and not just noise? */ 1897 | delta = 0; 1898 | for (i = 0; i < msglen*8*2; i += 2) { 1899 | delta += abs(m[j+i+MODES_PREAMBLE_US*2]- 1900 | m[j+i+MODES_PREAMBLE_US*2+1]); 1901 | } 1902 | delta /= msglen*4; 1903 | 1904 | /* Filter for an average delta of three is small enough to let almost 1905 | * every kind of message to pass, but high enough to filter some 1906 | * random noise. */ 1907 | if (delta < 10*255) { 1908 | use_correction = 0; 1909 | continue; 1910 | } 1911 | 1912 | /* If we reached this point, and error is zero, we are very likely 1913 | * with a Mode S message in our hands, but it may still be broken 1914 | * and CRC may not be correct. This is handled by the next layer. */ 1915 | if (errors == 0 || (Modes.aggressive && errors < 3)) { 1916 | struct modesMessage mm; 1917 | 1918 | /* Decode the received message and update statistics */ 1919 | decodeModesMessage(&mm,msg); 1920 | 1921 | /* Update statistics. */ 1922 | if (mm.crcok || use_correction) { 1923 | if (errors == 0) Modes.stat_demodulated++; 1924 | if (mm.errorbit == -1) { 1925 | if (mm.crcok) 1926 | Modes.stat_goodcrc++; 1927 | else 1928 | Modes.stat_badcrc++; 1929 | } else { 1930 | Modes.stat_badcrc++; 1931 | Modes.stat_fixed++; 1932 | if (mm.errorbit < MODES_LONG_MSG_BITS) 1933 | Modes.stat_single_bit_fix++; 1934 | else 1935 | Modes.stat_two_bits_fix++; 1936 | } 1937 | } 1938 | 1939 | /* Output debug mode info if needed. */ 1940 | if (use_correction == 0) { 1941 | if (Modes.debug & MODES_DEBUG_DEMOD) 1942 | dumpRawMessage("Demodulated with 0 errors", msg, m, j); 1943 | else if (Modes.debug & MODES_DEBUG_BADCRC && 1944 | mm.msgtype == 17 && 1945 | (!mm.crcok || mm.errorbit != -1)) 1946 | dumpRawMessage("Decoded with bad CRC", msg, m, j); 1947 | else if (Modes.debug & MODES_DEBUG_GOODCRC && mm.crcok && 1948 | mm.errorbit == -1) 1949 | dumpRawMessage("Decoded with good CRC", msg, m, j); 1950 | } 1951 | 1952 | /* Skip this message if we are sure it's fine. */ 1953 | if (mm.crcok) { 1954 | j += (MODES_PREAMBLE_US+(msglen*8))*2; 1955 | good_message = 1; 1956 | if (use_correction) 1957 | mm.phase_corrected = 1; 1958 | } 1959 | 1960 | /* Pass data to the next layer */ 1961 | useModesMessage(&mm); 1962 | } else { 1963 | if (Modes.debug & MODES_DEBUG_DEMODERR && use_correction) { 1964 | printf("The following message has %d demod errors\n", errors); 1965 | dumpRawMessage("Demodulated with errors", msg, m, j); 1966 | } 1967 | } 1968 | 1969 | /* Retry with phase correction if possible. */ 1970 | if (!good_message && !use_correction) { 1971 | j--; 1972 | use_correction = 1; 1973 | } else { 1974 | use_correction = 0; 1975 | } 1976 | } 1977 | } 1978 | 1979 | /* When a new message is available, because it was decoded from the 1980 | * RTL device, file, or received in the TCP input port, or any other 1981 | * way we can receive a decoded message, we call this function in order 1982 | * to use the message. 1983 | * 1984 | * Basically this function passes a raw message to the upper layers for 1985 | * further processing and visualization. */ 1986 | void useModesMessage(struct modesMessage *mm) { 1987 | if (!Modes.stats && (Modes.check_crc == 0 || mm->crcok)) { 1988 | /* Track aircrafts in interactive mode or if the HTTP 1989 | * interface is enabled. */ 1990 | if (Modes.interactive || Modes.stat_http_requests > 0 || Modes.stat_sbs_connections > 0) { 1991 | struct aircraft *a = interactiveReceiveData(mm); 1992 | if (a && Modes.stat_sbs_connections > 0) modesSendSBSOutput(mm, a); /* Feed SBS output clients. */ 1993 | } 1994 | /* In non-interactive way, display messages on standard output. */ 1995 | if (!Modes.interactive) { 1996 | displayModesMessage(mm); 1997 | if (!Modes.raw && !Modes.onlyaddr) printf("\n"); 1998 | } 1999 | /* Send data to connected clients. */ 2000 | if (Modes.net) { 2001 | modesSendRawOutput(mm); /* Feed raw output clients. */ 2002 | } 2003 | } 2004 | } 2005 | 2006 | /* ========================= Interactive mode =============================== */ 2007 | 2008 | /* Return a new aircraft structure for the interactive mode linked list 2009 | * of aircrafts. */ 2010 | struct aircraft *interactiveCreateAircraft(uint32_t addr) { 2011 | struct aircraft *a = malloc(sizeof(*a)); 2012 | 2013 | a->addr = addr; 2014 | snprintf(a->hexaddr,sizeof(a->hexaddr),"%06x",(int)addr); 2015 | a->flight[0] = '\0'; 2016 | a->altitude = 0; 2017 | a->speed = 0; 2018 | a->track = 0; 2019 | a->odd_cprlat = 0; 2020 | a->odd_cprlon = 0; 2021 | a->odd_cprtime = 0; 2022 | a->even_cprlat = 0; 2023 | a->even_cprlon = 0; 2024 | a->even_cprtime = 0; 2025 | a->lat = 0; 2026 | a->lon = 0; 2027 | a->seen = time(NULL); 2028 | a->messages = 0; 2029 | a->next = NULL; 2030 | return a; 2031 | } 2032 | 2033 | /* Return the aircraft with the specified address, or NULL if no aircraft 2034 | * exists with this address. */ 2035 | struct aircraft *interactiveFindAircraft(uint32_t addr) { 2036 | struct aircraft *a = Modes.aircrafts; 2037 | 2038 | while(a) { 2039 | if (a->addr == addr) return a; 2040 | a = a->next; 2041 | } 2042 | return NULL; 2043 | } 2044 | 2045 | /* Always positive MOD operation, used for CPR decoding. */ 2046 | int cprModFunction(int a, int b) { 2047 | int res = a % b; 2048 | if (res < 0) res += b; 2049 | return res; 2050 | } 2051 | 2052 | /* The NL function uses the precomputed table from 1090-WP-9-14 */ 2053 | int cprNLFunction(double lat) { 2054 | if (lat < 0) lat = -lat; /* Table is simmetric about the equator. */ 2055 | if (lat < 10.47047130) return 59; 2056 | if (lat < 14.82817437) return 58; 2057 | if (lat < 18.18626357) return 57; 2058 | if (lat < 21.02939493) return 56; 2059 | if (lat < 23.54504487) return 55; 2060 | if (lat < 25.82924707) return 54; 2061 | if (lat < 27.93898710) return 53; 2062 | if (lat < 29.91135686) return 52; 2063 | if (lat < 31.77209708) return 51; 2064 | if (lat < 33.53993436) return 50; 2065 | if (lat < 35.22899598) return 49; 2066 | if (lat < 36.85025108) return 48; 2067 | if (lat < 38.41241892) return 47; 2068 | if (lat < 39.92256684) return 46; 2069 | if (lat < 41.38651832) return 45; 2070 | if (lat < 42.80914012) return 44; 2071 | if (lat < 44.19454951) return 43; 2072 | if (lat < 45.54626723) return 42; 2073 | if (lat < 46.86733252) return 41; 2074 | if (lat < 48.16039128) return 40; 2075 | if (lat < 49.42776439) return 39; 2076 | if (lat < 50.67150166) return 38; 2077 | if (lat < 51.89342469) return 37; 2078 | if (lat < 53.09516153) return 36; 2079 | if (lat < 54.27817472) return 35; 2080 | if (lat < 55.44378444) return 34; 2081 | if (lat < 56.59318756) return 33; 2082 | if (lat < 57.72747354) return 32; 2083 | if (lat < 58.84763776) return 31; 2084 | if (lat < 59.95459277) return 30; 2085 | if (lat < 61.04917774) return 29; 2086 | if (lat < 62.13216659) return 28; 2087 | if (lat < 63.20427479) return 27; 2088 | if (lat < 64.26616523) return 26; 2089 | if (lat < 65.31845310) return 25; 2090 | if (lat < 66.36171008) return 24; 2091 | if (lat < 67.39646774) return 23; 2092 | if (lat < 68.42322022) return 22; 2093 | if (lat < 69.44242631) return 21; 2094 | if (lat < 70.45451075) return 20; 2095 | if (lat < 71.45986473) return 19; 2096 | if (lat < 72.45884545) return 18; 2097 | if (lat < 73.45177442) return 17; 2098 | if (lat < 74.43893416) return 16; 2099 | if (lat < 75.42056257) return 15; 2100 | if (lat < 76.39684391) return 14; 2101 | if (lat < 77.36789461) return 13; 2102 | if (lat < 78.33374083) return 12; 2103 | if (lat < 79.29428225) return 11; 2104 | if (lat < 80.24923213) return 10; 2105 | if (lat < 81.19801349) return 9; 2106 | if (lat < 82.13956981) return 8; 2107 | if (lat < 83.07199445) return 7; 2108 | if (lat < 83.99173563) return 6; 2109 | if (lat < 84.89166191) return 5; 2110 | if (lat < 85.75541621) return 4; 2111 | if (lat < 86.53536998) return 3; 2112 | if (lat < 87.00000000) return 2; 2113 | else return 1; 2114 | } 2115 | 2116 | int cprNFunction(double lat, int isodd) { 2117 | int nl = cprNLFunction(lat) - isodd; 2118 | if (nl < 1) nl = 1; 2119 | return nl; 2120 | } 2121 | 2122 | double cprDlonFunction(double lat, int isodd) { 2123 | return 360.0 / cprNFunction(lat, isodd); 2124 | } 2125 | 2126 | /* This algorithm comes from: 2127 | * http://www.lll.lu/~edward/edward/adsb/DecodingADSBposition.html. 2128 | * 2129 | * 2130 | * A few remarks: 2131 | * 1) 131072 is 2^17 since CPR latitude and longitude are encoded in 17 bits. 2132 | * 2) We assume that we always received the odd packet as last packet for 2133 | * simplicity. This may provide a position that is less fresh of a few 2134 | * seconds. 2135 | */ 2136 | void decodeCPR(struct aircraft *a) { 2137 | const double AirDlat0 = 360.0 / 60; 2138 | const double AirDlat1 = 360.0 / 59; 2139 | double lat0 = a->even_cprlat; 2140 | double lat1 = a->odd_cprlat; 2141 | double lon0 = a->even_cprlon; 2142 | double lon1 = a->odd_cprlon; 2143 | 2144 | /* Compute the Latitude Index "j" */ 2145 | int j = floor(((59*lat0 - 60*lat1) / 131072) + 0.5); 2146 | double rlat0 = AirDlat0 * (cprModFunction(j,60) + lat0 / 131072); 2147 | double rlat1 = AirDlat1 * (cprModFunction(j,59) + lat1 / 131072); 2148 | 2149 | if (rlat0 >= 270) rlat0 -= 360; 2150 | if (rlat1 >= 270) rlat1 -= 360; 2151 | 2152 | /* Check that both are in the same latitude zone, or abort. */ 2153 | if (cprNLFunction(rlat0) != cprNLFunction(rlat1)) return; 2154 | 2155 | /* Compute ni and the longitude index m */ 2156 | if (a->even_cprtime > a->odd_cprtime) { 2157 | /* Use even packet. */ 2158 | int ni = cprNFunction(rlat0,0); 2159 | int m = floor((((lon0 * (cprNLFunction(rlat0)-1)) - 2160 | (lon1 * cprNLFunction(rlat0))) / 131072) + 0.5); 2161 | a->lon = cprDlonFunction(rlat0,0) * (cprModFunction(m,ni)+lon0/131072); 2162 | a->lat = rlat0; 2163 | } else { 2164 | /* Use odd packet. */ 2165 | int ni = cprNFunction(rlat1,1); 2166 | int m = floor((((lon0 * (cprNLFunction(rlat1)-1)) - 2167 | (lon1 * cprNLFunction(rlat1))) / 131072.0) + 0.5); 2168 | a->lon = cprDlonFunction(rlat1,1) * (cprModFunction(m,ni)+lon1/131072); 2169 | a->lat = rlat1; 2170 | } 2171 | if (a->lon > 180) a->lon -= 360; 2172 | } 2173 | 2174 | /* Receive new messages and populate the interactive mode with more info. */ 2175 | struct aircraft *interactiveReceiveData(struct modesMessage *mm) { 2176 | uint32_t addr; 2177 | struct aircraft *a, *aux; 2178 | 2179 | if (Modes.check_crc && mm->crcok == 0) return NULL; 2180 | addr = (mm->aa1 << 16) | (mm->aa2 << 8) | mm->aa3; 2181 | 2182 | /* Loookup our aircraft or create a new one. */ 2183 | a = interactiveFindAircraft(addr); 2184 | if (!a) { 2185 | a = interactiveCreateAircraft(addr); 2186 | a->next = Modes.aircrafts; 2187 | Modes.aircrafts = a; 2188 | } else { 2189 | /* If it is an already known aircraft, move it on head 2190 | * so we keep aircrafts ordered by received message time. 2191 | * 2192 | * However move it on head only if at least one second elapsed 2193 | * since the aircraft that is currently on head sent a message, 2194 | * othewise with multiple aircrafts at the same time we have an 2195 | * useless shuffle of positions on the screen. */ 2196 | if (0 && Modes.aircrafts != a && (time(NULL) - a->seen) >= 1) { 2197 | aux = Modes.aircrafts; 2198 | while(aux->next != a) aux = aux->next; 2199 | /* Now we are a node before the aircraft to remove. */ 2200 | aux->next = aux->next->next; /* removed. */ 2201 | /* Add on head */ 2202 | a->next = Modes.aircrafts; 2203 | Modes.aircrafts = a; 2204 | } 2205 | } 2206 | 2207 | a->seen = time(NULL); 2208 | a->messages++; 2209 | a->csv_logged = 0; 2210 | 2211 | if (mm->msgtype == 0 || mm->msgtype == 4 || mm->msgtype == 20) { 2212 | a->altitude = mm->altitude; 2213 | } else if (mm->msgtype == 17) { 2214 | if (mm->metype >= 1 && mm->metype <= 4) { 2215 | memcpy(a->flight, mm->flight, sizeof(a->flight)); 2216 | } else if (mm->metype >= 9 && mm->metype <= 18) { 2217 | a->altitude = mm->altitude; 2218 | if (mm->fflag) { 2219 | a->odd_cprlat = mm->raw_latitude; 2220 | a->odd_cprlon = mm->raw_longitude; 2221 | a->odd_cprtime = mstime(); 2222 | } else { 2223 | a->even_cprlat = mm->raw_latitude; 2224 | a->even_cprlon = mm->raw_longitude; 2225 | a->even_cprtime = mstime(); 2226 | } 2227 | /* If the two data is less than 10 seconds apart, compute 2228 | * the position. */ 2229 | int x = a->even_cprtime - a->odd_cprtime; 2230 | if (-10000 <= x && x <= 10000) { 2231 | decodeCPR(a); 2232 | } 2233 | } else if (mm->metype == 19) { 2234 | if (mm->mesub == 1 || mm->mesub == 2) { 2235 | a->speed = mm->velocity; 2236 | a->track = mm->heading; 2237 | } 2238 | } 2239 | } 2240 | return a; 2241 | } 2242 | 2243 | /* Show the currently captured interactive data on screen. */ 2244 | void interactiveShowData(void) { 2245 | struct aircraft *a = Modes.aircrafts; 2246 | time_t now = time(NULL); 2247 | char progress[4]; 2248 | int count = 0; 2249 | 2250 | memset(progress,' ',3); 2251 | progress[time(NULL)%3] = '.'; 2252 | progress[3] = '\0'; 2253 | 2254 | printf("\x1b[H\x1b[2J"); /* Clear the screen */ 2255 | printf( 2256 | "Hex Flight Altitude Speed Lat Lon Track Messages Seen %s\n" 2257 | "--------------------------------------------------------------------------------\n", 2258 | progress); 2259 | 2260 | while(a && count < Modes.interactive_rows) { 2261 | int altitude = a->altitude, speed = a->speed; 2262 | 2263 | /* Convert units to metric if --metric was specified. */ 2264 | if (Modes.metric) { 2265 | altitude /= 3.2828; 2266 | speed *= 1.852; 2267 | } 2268 | 2269 | printf("%-6s %-8s %-9d %-7d %-7.03f %-7.03f %-3d %-9ld %d sec\n", 2270 | a->hexaddr, a->flight, altitude, speed, 2271 | a->lat, a->lon, a->track, a->messages, 2272 | (int)(now - a->seen)); 2273 | a = a->next; 2274 | count++; 2275 | } 2276 | } 2277 | 2278 | /* Write aircraft detection logs as CSV file. */ 2279 | void writeCSVLog(void) { 2280 | struct aircraft *a = Modes.aircrafts; 2281 | FILE *log_file; 2282 | 2283 | if (NULL == (log_file = fopen("aircraft_log.csv", "a+"))) { 2284 | printf("Error opening aircraft_log.csv"); 2285 | return; 2286 | } 2287 | 2288 | fseek(log_file, 0, SEEK_END); 2289 | 2290 | if (ftell(log_file) == 0) { 2291 | fprintf(log_file, "Hex,Flight,Altitude,Speed,Lat,Lon,Track,Messages,Seen\n"); 2292 | } 2293 | 2294 | while(a) { 2295 | int altitude = a->altitude, speed = a->speed; 2296 | 2297 | /* Convert units to metric if --metric was specified. */ 2298 | if (Modes.metric) { 2299 | altitude /= 3.2828; 2300 | speed *= 1.852; 2301 | } 2302 | 2303 | if (a->csv_logged == 0) { 2304 | fprintf(log_file, "%s,%s,%d,%d,%f,%f,%d,%ld,%ld\n", 2305 | a->hexaddr, a->flight, altitude, speed, 2306 | a->lat, a->lon, a->track, a->messages, 2307 | a->seen); 2308 | a->csv_logged = 1; 2309 | } 2310 | a = a->next; 2311 | } 2312 | 2313 | fclose(log_file); 2314 | } 2315 | 2316 | /* When in interactive mode If we don't receive new nessages within 2317 | * MODES_INTERACTIVE_TTL seconds we remove the aircraft from the list. */ 2318 | void interactiveRemoveStaleAircrafts(void) { 2319 | struct aircraft *a = Modes.aircrafts; 2320 | struct aircraft *prev = NULL; 2321 | time_t now = time(NULL); 2322 | 2323 | while(a) { 2324 | if ((now - a->seen) > Modes.interactive_ttl) { 2325 | struct aircraft *next = a->next; 2326 | /* Remove the element from the linked list, with care 2327 | * if we are removing the first element. */ 2328 | free(a); 2329 | if (!prev) 2330 | Modes.aircrafts = next; 2331 | else 2332 | prev->next = next; 2333 | a = next; 2334 | } else { 2335 | prev = a; 2336 | a = a->next; 2337 | } 2338 | } 2339 | } 2340 | 2341 | /* ============================== Snip mode ================================= */ 2342 | 2343 | /* Get raw IQ samples and filter everything is < than the specified level 2344 | * for more than 256 samples in order to reduce example file size. */ 2345 | void snipMode(int level) { 2346 | int i, q; 2347 | long long c = 0; 2348 | 2349 | while ((i = getchar()) != EOF && (q = getchar()) != EOF) { 2350 | if (abs(i-127) < level && abs(q-127) < level) { 2351 | c++; 2352 | if (c > MODES_PREAMBLE_US*4) continue; 2353 | } else { 2354 | c = 0; 2355 | } 2356 | putchar(i); 2357 | putchar(q); 2358 | } 2359 | } 2360 | 2361 | /* ============================= Networking ================================= 2362 | * Note: here we risregard any kind of good coding practice in favor of 2363 | * extreme simplicity, that is: 2364 | * 2365 | * 1) We only rely on the kernel buffers for our I/O without any kind of 2366 | * user space buffering. 2367 | * 2) We don't register any kind of event handler, from time to time a 2368 | * function gets called and we accept new connections. All the rest is 2369 | * handled via non-blocking I/O and manually pulling clients to see if 2370 | * they have something new to share with us when reading is needed. 2371 | */ 2372 | 2373 | #define MODES_NET_SERVICE_RAWO 0 2374 | #define MODES_NET_SERVICE_RAWI 1 2375 | #define MODES_NET_SERVICE_HTTP 2 2376 | #define MODES_NET_SERVICE_SBS 3 2377 | #define MODES_NET_SERVICES_NUM 4 2378 | struct { 2379 | char *descr; 2380 | int *socket; 2381 | int port; 2382 | } modesNetServices[MODES_NET_SERVICES_NUM] = { 2383 | {"Raw TCP output", &Modes.ros, MODES_NET_OUTPUT_RAW_PORT}, 2384 | {"Raw TCP input", &Modes.ris, MODES_NET_INPUT_RAW_PORT}, 2385 | {"HTTP server", &Modes.https, MODES_NET_HTTP_PORT}, 2386 | {"Basestation TCP output", &Modes.sbsos, MODES_NET_OUTPUT_SBS_PORT} 2387 | }; 2388 | 2389 | /* Networking "stack" initialization. */ 2390 | void modesInitNet(void) { 2391 | int j; 2392 | 2393 | memset(Modes.clients,0,sizeof(Modes.clients)); 2394 | Modes.maxfd = -1; 2395 | 2396 | for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { 2397 | int s = anetTcpServer(Modes.aneterr, modesNetServices[j].port, NULL); 2398 | if (s == -1) { 2399 | fprintf(stderr, "Error opening the listening port %d (%s): %s\n", 2400 | modesNetServices[j].port, 2401 | modesNetServices[j].descr, 2402 | strerror(errno)); 2403 | exit(1); 2404 | } 2405 | anetNonBlock(Modes.aneterr, s); 2406 | *modesNetServices[j].socket = s; 2407 | } 2408 | 2409 | signal(SIGPIPE, SIG_IGN); 2410 | } 2411 | 2412 | /* This function gets called from time to time when the decoding thread is 2413 | * awakened by new data arriving. This usually happens a few times every 2414 | * second. */ 2415 | void modesAcceptClients(void) { 2416 | int fd, port; 2417 | unsigned int j; 2418 | struct client *c; 2419 | 2420 | for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { 2421 | fd = anetTcpAccept(Modes.aneterr, *modesNetServices[j].socket, 2422 | NULL, &port); 2423 | if (fd == -1) { 2424 | if (Modes.debug & MODES_DEBUG_NET && errno != EAGAIN) 2425 | printf("Accept %d: %s\n", *modesNetServices[j].socket, 2426 | strerror(errno)); 2427 | continue; 2428 | } 2429 | 2430 | if (fd >= MODES_NET_MAX_FD) { 2431 | close(fd); 2432 | return; /* Max number of clients reached. */ 2433 | } 2434 | 2435 | anetNonBlock(Modes.aneterr, fd); 2436 | c = malloc(sizeof(*c)); 2437 | c->service = *modesNetServices[j].socket; 2438 | c->fd = fd; 2439 | c->buflen = 0; 2440 | Modes.clients[fd] = c; 2441 | anetSetSendBuffer(Modes.aneterr,fd,MODES_NET_SNDBUF_SIZE); 2442 | 2443 | if (Modes.maxfd < fd) Modes.maxfd = fd; 2444 | if (*modesNetServices[j].socket == Modes.sbsos) 2445 | Modes.stat_sbs_connections++; 2446 | 2447 | j--; /* Try again with the same listening port. */ 2448 | 2449 | if (Modes.debug & MODES_DEBUG_NET) 2450 | printf("Created new client %d\n", fd); 2451 | } 2452 | } 2453 | 2454 | /* On error free the client, collect the structure, adjust maxfd if needed. */ 2455 | void modesFreeClient(int fd) { 2456 | close(fd); 2457 | free(Modes.clients[fd]); 2458 | Modes.clients[fd] = NULL; 2459 | 2460 | if (Modes.debug & MODES_DEBUG_NET) 2461 | printf("Closing client %d\n", fd); 2462 | 2463 | /* If this was our maxfd, scan the clients array to find the new max. 2464 | * Note that we are sure there is no active fd greater than the closed 2465 | * fd, so we scan from fd-1 to 0. */ 2466 | if (Modes.maxfd == fd) { 2467 | int j; 2468 | 2469 | Modes.maxfd = -1; 2470 | for (j = fd-1; j >= 0; j--) { 2471 | if (Modes.clients[j]) { 2472 | Modes.maxfd = j; 2473 | break; 2474 | } 2475 | } 2476 | } 2477 | } 2478 | 2479 | /* Send the specified message to all clients listening for a given service. */ 2480 | void modesSendAllClients(int service, void *msg, int len) { 2481 | int j; 2482 | struct client *c; 2483 | 2484 | for (j = 0; j <= Modes.maxfd; j++) { 2485 | c = Modes.clients[j]; 2486 | if (c && c->service == service) { 2487 | int nwritten = write(j, msg, len); 2488 | if (nwritten != len) { 2489 | modesFreeClient(j); 2490 | } 2491 | } 2492 | } 2493 | } 2494 | 2495 | /* Write raw output to TCP clients. */ 2496 | void modesSendRawOutput(struct modesMessage *mm) { 2497 | char msg[128], *p = msg; 2498 | int j; 2499 | 2500 | *p++ = '*'; 2501 | for (j = 0; j < mm->msgbits/8; j++) { 2502 | sprintf(p, "%02X", mm->msg[j]); 2503 | p += 2; 2504 | } 2505 | *p++ = ';'; 2506 | *p++ = '\n'; 2507 | modesSendAllClients(Modes.ros, msg, p-msg); 2508 | } 2509 | 2510 | 2511 | /* Write SBS output to TCP clients. */ 2512 | void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { 2513 | char msg[256], *p = msg; 2514 | int emergency = 0, ground = 0, alert = 0, spi = 0; 2515 | 2516 | if (mm->msgtype == 4 || mm->msgtype == 5 || mm->msgtype == 21) { 2517 | /* Node: identity is calculated/kept in base10 but is actually 2518 | * octal (07500 is represented as 7500) */ 2519 | if (mm->identity == 7500 || mm->identity == 7600 || 2520 | mm->identity == 7700) emergency = -1; 2521 | if (mm->fs == 1 || mm->fs == 3) ground = -1; 2522 | if (mm->fs == 2 || mm->fs == 3 || mm->fs == 4) alert = -1; 2523 | if (mm->fs == 4 || mm->fs == 5) spi = -1; 2524 | } 2525 | 2526 | if (mm->msgtype == 0) { 2527 | p += sprintf(p, "MSG,5,,,%02X%02X%02X,,,,,,,%d,,,,,,,,,,", 2528 | mm->aa1, mm->aa2, mm->aa3, mm->altitude); 2529 | } else if (mm->msgtype == 4) { 2530 | p += sprintf(p, "MSG,5,,,%02X%02X%02X,,,,,,,%d,,,,,,,%d,%d,%d,%d", 2531 | mm->aa1, mm->aa2, mm->aa3, mm->altitude, alert, emergency, spi, ground); 2532 | } else if (mm->msgtype == 5) { 2533 | p += sprintf(p, "MSG,6,,,%02X%02X%02X,,,,,,,,,,,,,%d,%d,%d,%d,%d", 2534 | mm->aa1, mm->aa2, mm->aa3, mm->identity, alert, emergency, spi, ground); 2535 | } else if (mm->msgtype == 11) { 2536 | p += sprintf(p, "MSG,8,,,%02X%02X%02X,,,,,,,,,,,,,,,,,", 2537 | mm->aa1, mm->aa2, mm->aa3); 2538 | } else if (mm->msgtype == 17 && mm->metype == 4) { 2539 | p += sprintf(p, "MSG,1,,,%02X%02X%02X,,,,,,%s,,,,,,,,0,0,0,0", 2540 | mm->aa1, mm->aa2, mm->aa3, mm->flight); 2541 | } else if (mm->msgtype == 17 && mm->metype >= 9 && mm->metype <= 18) { 2542 | if (a->lat == 0 && a->lon == 0) 2543 | p += sprintf(p, "MSG,3,,,%02X%02X%02X,,,,,,,%d,,,,,,,0,0,0,0", 2544 | mm->aa1, mm->aa2, mm->aa3, mm->altitude); 2545 | else 2546 | p += sprintf(p, "MSG,3,,,%02X%02X%02X,,,,,,,%d,,,%1.5f,%1.5f,,," 2547 | "0,0,0,0", 2548 | mm->aa1, mm->aa2, mm->aa3, mm->altitude, a->lat, a->lon); 2549 | } else if (mm->msgtype == 17 && mm->metype == 19 && mm->mesub == 1) { 2550 | int vr = (mm->vert_rate_sign==0?1:-1) * (mm->vert_rate-1) * 64; 2551 | 2552 | p += sprintf(p, "MSG,4,,,%02X%02X%02X,,,,,,,,%d,%d,,,%i,,0,0,0,0", 2553 | mm->aa1, mm->aa2, mm->aa3, a->speed, a->track, vr); 2554 | } else if (mm->msgtype == 21) { 2555 | p += sprintf(p, "MSG,6,,,%02X%02X%02X,,,,,,,,,,,,,%d,%d,%d,%d,%d", 2556 | mm->aa1, mm->aa2, mm->aa3, mm->identity, alert, emergency, spi, ground); 2557 | } else { 2558 | return; 2559 | } 2560 | 2561 | *p++ = '\n'; 2562 | modesSendAllClients(Modes.sbsos, msg, p-msg); 2563 | } 2564 | 2565 | /* Turn an hex digit into its 4 bit decimal value. 2566 | * Returns -1 if the digit is not in the 0-F range. */ 2567 | int hexDigitVal(int c) { 2568 | c = tolower(c); 2569 | if (c >= '0' && c <= '9') return c-'0'; 2570 | else if (c >= 'a' && c <= 'f') return c-'a'+10; 2571 | else return -1; 2572 | } 2573 | 2574 | /* This function decodes a string representing a Mode S message in 2575 | * raw hex format like: *8D4B969699155600E87406F5B69F; 2576 | * The string is supposed to be at the start of the client buffer 2577 | * and null-terminated. 2578 | * 2579 | * The message is passed to the higher level layers, so it feeds 2580 | * the selected screen output, the network output and so forth. 2581 | * 2582 | * If the message looks invalid is silently discarded. 2583 | * 2584 | * The function always returns 0 (success) to the caller as there is 2585 | * no case where we want broken messages here to close the client 2586 | * connection. */ 2587 | int decodeHexMessage(struct client *c) { 2588 | char *hex = c->buf; 2589 | int l = strlen(hex), j; 2590 | unsigned char msg[MODES_LONG_MSG_BYTES]; 2591 | struct modesMessage mm; 2592 | 2593 | /* Remove spaces on the left and on the right. */ 2594 | while(l && isspace(hex[l-1])) { 2595 | hex[l-1] = '\0'; 2596 | l--; 2597 | } 2598 | while(isspace(*hex)) { 2599 | hex++; 2600 | l--; 2601 | } 2602 | 2603 | /* Turn the message into binary. */ 2604 | if (l < 2 || hex[0] != '*' || hex[l-1] != ';') return 0; 2605 | hex++; l-=2; /* Skip * and ; */ 2606 | if (l > MODES_LONG_MSG_BYTES*2) return 0; /* Too long message... broken. */ 2607 | for (j = 0; j < l; j += 2) { 2608 | int high = hexDigitVal(hex[j]); 2609 | int low = hexDigitVal(hex[j+1]); 2610 | 2611 | if (high == -1 || low == -1) return 0; 2612 | msg[j/2] = (high<<4) | low; 2613 | } 2614 | decodeModesMessage(&mm,msg); 2615 | useModesMessage(&mm); 2616 | return 0; 2617 | } 2618 | 2619 | /* Return a description of planes in json. */ 2620 | char *aircraftsToJson(int *len) { 2621 | struct aircraft *a = Modes.aircrafts; 2622 | int buflen = 1024; /* The initial buffer is incremented as needed. */ 2623 | char *buf = malloc(buflen), *p = buf; 2624 | int l; 2625 | 2626 | l = snprintf(p,buflen,"[\n"); 2627 | p += l; buflen -= l; 2628 | while(a) { 2629 | int altitude = a->altitude, speed = a->speed; 2630 | 2631 | /* Convert units to metric if --metric was specified. */ 2632 | if (Modes.metric) { 2633 | altitude /= 3.2828; 2634 | speed *= 1.852; 2635 | } 2636 | 2637 | if (a->lat != 0 && a->lon != 0) { 2638 | l = snprintf(p,buflen, 2639 | "{\"hex\":\"%s\", \"flight\":\"%s\", \"lat\":%f, " 2640 | "\"lon\":%f, \"altitude\":%d, \"track\":%d, " 2641 | "\"speed\":%d},\n", 2642 | a->hexaddr, a->flight, a->lat, a->lon, a->altitude, a->track, 2643 | a->speed); 2644 | p += l; buflen -= l; 2645 | /* Resize if needed. */ 2646 | if (buflen < 256) { 2647 | int used = p-buf; 2648 | buflen += 1024; /* Our increment. */ 2649 | buf = realloc(buf,used+buflen); 2650 | p = buf+used; 2651 | } 2652 | } 2653 | a = a->next; 2654 | } 2655 | /* Remove the final comma if any, and closes the json array. */ 2656 | if (*(p-2) == ',') { 2657 | *(p-2) = '\n'; 2658 | p--; 2659 | buflen++; 2660 | } 2661 | l = snprintf(p,buflen,"]\n"); 2662 | p += l; buflen -= l; 2663 | 2664 | *len = p-buf; 2665 | return buf; 2666 | } 2667 | 2668 | #define MODES_CONTENT_TYPE_HTML "text/html;charset=utf-8" 2669 | #define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8" 2670 | 2671 | /* Get an HTTP request header and write the response to the client. 2672 | * Again here we assume that the socket buffer is enough without doing 2673 | * any kind of userspace buffering. 2674 | * 2675 | * Returns 1 on error to signal the caller the client connection should 2676 | * be closed. */ 2677 | int handleHTTPRequest(struct client *c) { 2678 | char hdr[512]; 2679 | int clen, hdrlen; 2680 | int httpver, keepalive; 2681 | char *p, *url, *content; 2682 | char *ctype; 2683 | 2684 | if (Modes.debug & MODES_DEBUG_NET) 2685 | printf("\nHTTP request: %s\n", c->buf); 2686 | 2687 | /* Minimally parse the request. */ 2688 | httpver = (strstr(c->buf, "HTTP/1.1") != NULL) ? 11 : 10; 2689 | if (httpver == 10) { 2690 | /* HTTP 1.0 defaults to close, unless otherwise specified. */ 2691 | keepalive = strstr(c->buf, "Connection: keep-alive") != NULL; 2692 | } else if (httpver == 11) { 2693 | /* HTTP 1.1 defaults to keep-alive, unless close is specified. */ 2694 | keepalive = strstr(c->buf, "Connection: close") == NULL; 2695 | } 2696 | 2697 | /* Identify he URL. */ 2698 | p = strchr(c->buf,' '); 2699 | if (!p) return 1; /* There should be the method and a space... */ 2700 | url = ++p; /* Now this should point to the requested URL. */ 2701 | p = strchr(p, ' '); 2702 | if (!p) return 1; /* There should be a space before HTTP/... */ 2703 | *p = '\0'; 2704 | 2705 | if (Modes.debug & MODES_DEBUG_NET) { 2706 | printf("\nHTTP keep alive: %d\n", keepalive); 2707 | printf("HTTP requested URL: %s\n\n", url); 2708 | } 2709 | 2710 | /* Select the content to send, we have just two so far: 2711 | * "/" -> Our google map application. 2712 | * "/data.json" -> Our ajax request to update planes. */ 2713 | if (strstr(url, "/data.json")) { 2714 | content = aircraftsToJson(&clen); 2715 | ctype = MODES_CONTENT_TYPE_JSON; 2716 | } else { 2717 | struct stat sbuf; 2718 | int fd = -1; 2719 | 2720 | if (stat("gmap.html",&sbuf) != -1 && 2721 | (fd = open("gmap.html",O_RDONLY)) != -1) 2722 | { 2723 | content = malloc(sbuf.st_size); 2724 | if (read(fd,content,sbuf.st_size) == -1) { 2725 | snprintf(content,sbuf.st_size,"Error reading from file: %s", 2726 | strerror(errno)); 2727 | } 2728 | clen = sbuf.st_size; 2729 | } else { 2730 | char buf[128]; 2731 | 2732 | clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s", 2733 | strerror(errno)); 2734 | content = strdup(buf); 2735 | } 2736 | if (fd != -1) close(fd); 2737 | ctype = MODES_CONTENT_TYPE_HTML; 2738 | } 2739 | 2740 | /* Create the header and send the reply. */ 2741 | hdrlen = snprintf(hdr, sizeof(hdr), 2742 | "HTTP/1.1 200 OK\r\n" 2743 | "Server: Dump1090\r\n" 2744 | "Content-Type: %s\r\n" 2745 | "Connection: %s\r\n" 2746 | "Content-Length: %d\r\n" 2747 | "Access-Control-Allow-Origin: *\r\n" 2748 | "\r\n", 2749 | ctype, 2750 | keepalive ? "keep-alive" : "close", 2751 | clen); 2752 | 2753 | if (Modes.debug & MODES_DEBUG_NET) 2754 | printf("HTTP Reply header:\n%s", hdr); 2755 | 2756 | /* Send header and content. */ 2757 | if (write(c->fd, hdr, hdrlen) != hdrlen || 2758 | write(c->fd, content, clen) != clen) 2759 | { 2760 | free(content); 2761 | return 1; 2762 | } 2763 | free(content); 2764 | Modes.stat_http_requests++; 2765 | return !keepalive; 2766 | } 2767 | 2768 | /* This function polls the clients using read() in order to receive new 2769 | * messages from the net. 2770 | * 2771 | * The message is supposed to be separated by the next message by the 2772 | * separator 'sep', that is a null-terminated C string. 2773 | * 2774 | * Every full message received is decoded and passed to the higher layers 2775 | * calling the function 'handler'. 2776 | * 2777 | * The handelr returns 0 on success, or 1 to signal this function we 2778 | * should close the connection with the client in case of non-recoverable 2779 | * errors. */ 2780 | void modesReadFromClient(struct client *c, char *sep, 2781 | int(*handler)(struct client *)) 2782 | { 2783 | while(1) { 2784 | int left = MODES_CLIENT_BUF_SIZE - c->buflen; 2785 | int nread = read(c->fd, c->buf+c->buflen, left); 2786 | int fullmsg = 0; 2787 | int i; 2788 | char *p; 2789 | 2790 | if (nread <= 0) { 2791 | if (nread == 0 || errno != EAGAIN) { 2792 | /* Error, or end of file. */ 2793 | modesFreeClient(c->fd); 2794 | } 2795 | break; /* Serve next client. */ 2796 | } 2797 | c->buflen += nread; 2798 | 2799 | /* Always null-term so we are free to use strstr() */ 2800 | c->buf[c->buflen] = '\0'; 2801 | 2802 | /* If there is a complete message there must be the separator 'sep' 2803 | * in the buffer, note that we full-scan the buffer at every read 2804 | * for simplicity. */ 2805 | while ((p = strstr(c->buf, sep)) != NULL) { 2806 | i = p - c->buf; /* Turn it as an index inside the buffer. */ 2807 | c->buf[i] = '\0'; /* Te handler expects null terminated strings. */ 2808 | /* Call the function to process the message. It returns 1 2809 | * on error to signal we should close the client connection. */ 2810 | if (handler(c)) { 2811 | modesFreeClient(c->fd); 2812 | return; 2813 | } 2814 | /* Move what's left at the start of the buffer. */ 2815 | i += strlen(sep); /* The separator is part of the previous msg. */ 2816 | memmove(c->buf,c->buf+i,c->buflen-i); 2817 | c->buflen -= i; 2818 | c->buf[c->buflen] = '\0'; 2819 | /* Maybe there are more messages inside the buffer. 2820 | * Start looping from the start again. */ 2821 | fullmsg = 1; 2822 | } 2823 | /* If our buffer is full discard it, this is some badly 2824 | * formatted shit. */ 2825 | if (c->buflen == MODES_CLIENT_BUF_SIZE) { 2826 | c->buflen = 0; 2827 | /* If there is garbage, read more to discard it ASAP. */ 2828 | continue; 2829 | } 2830 | /* If no message was decoded process the next client, otherwise 2831 | * read more data from the same client. */ 2832 | if (!fullmsg) break; 2833 | } 2834 | } 2835 | 2836 | /* Read data from clients. This function actually delegates a lower-level 2837 | * function that depends on the kind of service (raw, http, ...). */ 2838 | void modesReadFromClients(void) { 2839 | int j; 2840 | struct client *c; 2841 | 2842 | for (j = 0; j <= Modes.maxfd; j++) { 2843 | if ((c = Modes.clients[j]) == NULL) continue; 2844 | if (c->service == Modes.ris) 2845 | modesReadFromClient(c,"\n",decodeHexMessage); 2846 | else if (c->service == Modes.https) 2847 | modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest); 2848 | } 2849 | } 2850 | 2851 | /* This function is used when "net only" mode is enabled to know when there 2852 | * is at least a new client to serve. Note that the dump1090 networking model 2853 | * is extremely trivial and a function takes care of handling all the clients 2854 | * that have something to serve, without a proper event library, so the 2855 | * function here returns as long as there is a single client ready, or 2856 | * when the specified timeout in milliesconds elapsed, without specifying to 2857 | * the caller what client requires to be served. */ 2858 | void modesWaitReadableClients(int timeout_ms) { 2859 | struct timeval tv; 2860 | fd_set fds; 2861 | int j, maxfd = Modes.maxfd; 2862 | 2863 | FD_ZERO(&fds); 2864 | 2865 | /* Set client FDs */ 2866 | for (j = 0; j <= Modes.maxfd; j++) { 2867 | if (Modes.clients[j]) FD_SET(j,&fds); 2868 | } 2869 | 2870 | /* Set listening sockets to accept new clients ASAP. */ 2871 | for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { 2872 | int s = *modesNetServices[j].socket; 2873 | FD_SET(s,&fds); 2874 | if (s > maxfd) maxfd = s; 2875 | } 2876 | 2877 | tv.tv_sec = timeout_ms/1000; 2878 | tv.tv_usec = (timeout_ms%1000)*1000; 2879 | /* We don't care why select returned here, timeout, error, or 2880 | * FDs ready are all conditions for which we just return. */ 2881 | select(maxfd+1,&fds,NULL,NULL,&tv); 2882 | } 2883 | 2884 | /* ============================ Terminal handling ========================== */ 2885 | 2886 | /* Handle resizing terminal. */ 2887 | void sigWinchCallback() { 2888 | signal(SIGWINCH, SIG_IGN); 2889 | Modes.interactive_rows = getTermRows(); 2890 | interactiveShowData(); 2891 | signal(SIGWINCH, sigWinchCallback); 2892 | } 2893 | 2894 | /* Get the number of rows after the terminal changes size. */ 2895 | int getTermRows() { 2896 | struct winsize w; 2897 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 2898 | return w.ws_row; 2899 | } 2900 | 2901 | /* ================================ Main ==================================== */ 2902 | 2903 | void showHelp(void) { 2904 | printf( 2905 | "--device-index Select RTL device (default: 0).\n" 2906 | "--dev-rtl use RTLSDR device.\n" 2907 | "--dev-hackrf use HackRF device.\n" 2908 | "--dev-airspy use AirSpy device.\n" 2909 | #ifndef NoSDRplay 2910 | "--dev-sdrplay use RSP device.\n" 2911 | #endif 2912 | "--gain Set RTLSDR gain (default: max gain. Use -100 for auto-gain).\n" 2913 | "--enable-agc Enable RTLSDR Automatic Gain Control (default: off).\n" 2914 | "--enable-amp Enable HackRF RX/TX RF amplifier (default: off).\n" 2915 | "--enable-antenna Enable HackRF antenna port power (default: off)\n" 2916 | "--rf-gain Set RX AMP (RF) gain\n" 2917 | " HackRF 0 or 14, default 0\n" 2918 | " AirSpy 0-14, step 1, default 11\n" 2919 | "--lna-gain Set RX LNA (IF) gain\n" 2920 | " HackRF 0-40, step 8, default: 40\n" 2921 | " AirSpy 0-14, step 1, default: 11\n" 2922 | "--vga-gain Set RX VGA (baseband) gain\n" 2923 | " HackRF 0-62, step 2, default: 62\n" 2924 | " AirSpy 0-14, step 1, default: 11\n" 2925 | "--freq Set frequency (default: 1090 Mhz).\n" 2926 | "--ifile Read data from file (use '-' for stdin).\n" 2927 | "--csv-log Log data to aircraft_log.csv for later analysis.\n" 2928 | "--interactive Interactive mode refreshing data on screen.\n" 2929 | "--interactive-rows Max number of rows in interactive mode (default: 15).\n" 2930 | "--interactive-ttl Remove from list if idle for (default: 60).\n" 2931 | "--raw Show only messages hex values.\n" 2932 | "--net Enable networking.\n" 2933 | "--net-only Enable just networking, no RTL device or file used.\n" 2934 | "--net-ro-port TCP listening port for raw output (default: 30002).\n" 2935 | "--net-ri-port TCP listening port for raw input (default: 30001).\n" 2936 | "--net-http-port HTTP server port (default: 8080).\n" 2937 | "--net-sbs-port TCP listening port for BaseStation format output (default: 30003).\n" 2938 | "--no-fix Disable single-bits error correction using CRC.\n" 2939 | "--no-crc-check Disable messages with broken CRC (discouraged).\n" 2940 | "--aggressive More CPU for more messages (two bits fixes, ...).\n" 2941 | "--stats With --ifile print stats at exit. No other output.\n" 2942 | "--onlyaddr Show only ICAO addresses (testing purposes).\n" 2943 | "--metric Use metric units (meters, km/h, ...).\n" 2944 | "--snip Strip IQ file removing samples < level.\n" 2945 | "--debug Debug mode (verbose), see README for details.\n" 2946 | "--help Show this help.\n" 2947 | "\n" 2948 | "Debug mode flags: d = Log frames decoded with errors\n" 2949 | " D = Log frames decoded with zero errors\n" 2950 | " c = Log frames with bad CRC\n" 2951 | " C = Log frames with good CRC\n" 2952 | " p = Log frames with bad preamble\n" 2953 | " n = Log network debugging info\n" 2954 | " j = Log frames to frames.js, loadable by debug.html.\n" 2955 | ); 2956 | } 2957 | 2958 | /* This function is called a few times every second by main in order to 2959 | * perform tasks we need to do continuously, like accepting new clients 2960 | * from the net, refreshing the screen in interactive mode, and so forth. */ 2961 | void backgroundTasks(void) { 2962 | if (Modes.net) { 2963 | modesAcceptClients(); 2964 | modesReadFromClients(); 2965 | interactiveRemoveStaleAircrafts(); 2966 | } 2967 | 2968 | /* Log to CSV if CSV logging is activated. */ 2969 | if (Modes.csv_log == 1) { 2970 | writeCSVLog(); 2971 | } 2972 | 2973 | /* Refresh screen when in interactive mode. */ 2974 | if (Modes.interactive && 2975 | (mstime() - Modes.interactive_last_update) > 2976 | MODES_INTERACTIVE_REFRESH_TIME) 2977 | { 2978 | interactiveRemoveStaleAircrafts(); 2979 | interactiveShowData(); 2980 | Modes.interactive_last_update = mstime(); 2981 | } 2982 | } 2983 | 2984 | void INTHandler(int sig) 2985 | { 2986 | signal(sig, SIG_IGN); 2987 | #ifndef NoSDRplay 2988 | mir_sdr_Uninit(); 2989 | #endif 2990 | exit(0); 2991 | } 2992 | 2993 | int main(int argc, char **argv) { 2994 | int j; 2995 | 2996 | signal(SIGINT, INTHandler); 2997 | 2998 | /* Set sane defaults. */ 2999 | modesInitConfig(); 3000 | 3001 | /* Parse the command line options */ 3002 | for (j = 1; j < argc; j++) { 3003 | int more = j+1 < argc; /* There are more arguments. */ 3004 | 3005 | if (!strcmp(argv[j],"--device-index") && more) { 3006 | Modes.dev_index = atoi(argv[++j]); 3007 | } 3008 | #ifndef NoSDRplay 3009 | else if (!strcmp(argv[j],"--dev-sdrplay")) { 3010 | Modes.prefer_sdrplay = 1; 3011 | } 3012 | #endif 3013 | else if (!strcmp(argv[j],"--dev-airspy")) { 3014 | Modes.rf_gain = AIRSPY_RF_GAIN; 3015 | Modes.lna_gain = AIRSPY_LNA_GAIN; 3016 | Modes.vga_gain = AIRSPY_VGA_GAIN; 3017 | Modes.prefer_airspy = 1; 3018 | } else if (!strcmp(argv[j],"--dev-hackrf")) { 3019 | Modes.rf_gain = HACKRF_RF_GAIN; 3020 | Modes.lna_gain = HACKRF_LNA_GAIN; 3021 | Modes.vga_gain = HACKRF_VGA_GAIN; 3022 | Modes.prefer_hackrf = 1; 3023 | } else if (!strcmp(argv[j],"--dev-rtlsdr")) { 3024 | Modes.prefer_rtlsdr = 1; 3025 | } else if (!strcmp(argv[j],"--gain") && more) { 3026 | Modes.gain = atof(argv[++j])*10; /* Gain is in tens of DBs */ 3027 | } else if (!strcmp(argv[j],"--enable-agc")) { 3028 | Modes.enable_agc++; 3029 | } else if (!strcmp(argv[j],"--enable-amp")) { 3030 | Modes.rf_gain = 14; 3031 | } else if (!strcmp(argv[j],"--enable-antenna")) { 3032 | Modes.power_antenna = 1; 3033 | } else if (!strcmp(argv[j],"--rf-gain")) { 3034 | Modes.rf_gain = atoi(argv[++j]); 3035 | } else if (!strcmp(argv[j],"--lna-gain")) { 3036 | Modes.lna_gain = atoi(argv[++j]); 3037 | } else if (!strcmp(argv[j],"--vga-gain")) { 3038 | Modes.vga_gain = atoi(argv[++j]); 3039 | } else if (!strcmp(argv[j],"--freq") && more) { 3040 | Modes.freq = strtoll(argv[++j],NULL,10); 3041 | } else if (!strcmp(argv[j],"--ifile") && more) { 3042 | Modes.filename = strdup(argv[++j]); 3043 | } else if (!strcmp(argv[j],"--no-fix")) { 3044 | Modes.fix_errors = 0; 3045 | } else if (!strcmp(argv[j],"--no-crc-check")) { 3046 | Modes.check_crc = 0; 3047 | } else if (!strcmp(argv[j],"--raw")) { 3048 | Modes.raw = 1; 3049 | } else if (!strcmp(argv[j],"--net")) { 3050 | Modes.net = 1; 3051 | } else if (!strcmp(argv[j],"--net-only")) { 3052 | Modes.net = 1; 3053 | Modes.net_only = 1; 3054 | } else if (!strcmp(argv[j],"--net-ro-port") && more) { 3055 | modesNetServices[MODES_NET_SERVICE_RAWO].port = atoi(argv[++j]); 3056 | } else if (!strcmp(argv[j],"--net-ri-port") && more) { 3057 | modesNetServices[MODES_NET_SERVICE_RAWI].port = atoi(argv[++j]); 3058 | } else if (!strcmp(argv[j],"--net-http-port") && more) { 3059 | modesNetServices[MODES_NET_SERVICE_HTTP].port = atoi(argv[++j]); 3060 | } else if (!strcmp(argv[j],"--net-sbs-port") && more) { 3061 | modesNetServices[MODES_NET_SERVICE_SBS].port = atoi(argv[++j]); 3062 | } else if (!strcmp(argv[j],"--onlyaddr")) { 3063 | Modes.onlyaddr = 1; 3064 | } else if (!strcmp(argv[j],"--metric")) { 3065 | Modes.metric = 1; 3066 | } else if (!strcmp(argv[j],"--aggressive")) { 3067 | Modes.aggressive++; 3068 | } else if (!strcmp(argv[j],"--csv-log")) { 3069 | Modes.csv_log = 1; 3070 | } else if (!strcmp(argv[j],"--interactive")) { 3071 | Modes.interactive = 1; 3072 | } else if (!strcmp(argv[j],"--interactive-rows")) { 3073 | Modes.interactive_rows = atoi(argv[++j]); 3074 | } else if (!strcmp(argv[j],"--interactive-ttl")) { 3075 | Modes.interactive_ttl = atoi(argv[++j]); 3076 | } else if (!strcmp(argv[j],"--debug") && more) { 3077 | char *f = argv[++j]; 3078 | while(*f) { 3079 | switch(*f) { 3080 | case 'D': Modes.debug |= MODES_DEBUG_DEMOD; break; 3081 | case 'd': Modes.debug |= MODES_DEBUG_DEMODERR; break; 3082 | case 'C': Modes.debug |= MODES_DEBUG_GOODCRC; break; 3083 | case 'c': Modes.debug |= MODES_DEBUG_BADCRC; break; 3084 | case 'p': Modes.debug |= MODES_DEBUG_NOPREAMBLE; break; 3085 | case 'n': Modes.debug |= MODES_DEBUG_NET; break; 3086 | case 'j': Modes.debug |= MODES_DEBUG_JS; break; 3087 | default: 3088 | fprintf(stderr, "Unknown debugging flag: %c\n", *f); 3089 | exit(1); 3090 | break; 3091 | } 3092 | f++; 3093 | } 3094 | } else if (!strcmp(argv[j],"--stats")) { 3095 | Modes.stats = 1; 3096 | } else if (!strcmp(argv[j],"--snip") && more) { 3097 | snipMode(atoi(argv[++j])); 3098 | exit(0); 3099 | } else if (!strcmp(argv[j],"--help")) { 3100 | showHelp(); 3101 | exit(0); 3102 | } else { 3103 | fprintf(stderr, 3104 | "Unknown or not enough arguments for option '%s'.\n\n", 3105 | argv[j]); 3106 | showHelp(); 3107 | exit(1); 3108 | } 3109 | } 3110 | 3111 | if ((Modes.prefer_airspy + Modes.prefer_hackrf + Modes.prefer_rtlsdr 3112 | #ifndef NoSDRplay 3113 | + Modes.prefer_sdrplay 3114 | #endif 3115 | ) > 1) { 3116 | showHelp(); 3117 | fprintf(stderr, "\n\nError: dev-{" 3118 | #ifndef NoSDRplay 3119 | "sdrplay," 3120 | #endif 3121 | "airspy,hackrf,rtlsdr} are mutually exclusive.\n"); 3122 | exit(1); 3123 | } 3124 | 3125 | /* Setup for SIGWINCH for handling lines */ 3126 | if (Modes.interactive == 1) signal(SIGWINCH, sigWinchCallback); 3127 | 3128 | /* Initialization */ 3129 | modesInit(); 3130 | if (Modes.net_only) { 3131 | fprintf(stderr,"Net-only mode, no RTL device or file open.\n"); 3132 | } else if (Modes.filename == NULL) { 3133 | if ((Modes.prefer_airspy + Modes.prefer_hackrf 3134 | #ifndef NoSDRplay 3135 | + Modes.prefer_sdrplay 3136 | #endif 3137 | ) == 0 && modesInitRTLSDR() == 0) { 3138 | Modes.rtl_enabled = 1; 3139 | } 3140 | #ifndef NoSDRplay 3141 | else if ((Modes.prefer_hackrf + Modes.prefer_airspy + Modes.prefer_rtlsdr ) == 0 && modesInitSDRplay() == 0 ) { 3142 | Modes.sdrplay_enabled = 1; 3143 | } 3144 | #endif 3145 | else if (( 3146 | #ifndef NoSDRplay 3147 | Modes.prefer_sdrplay + 3148 | #endif 3149 | Modes.prefer_airspy + Modes.prefer_rtlsdr ) == 0 && modesInitHackRF() == 0 ) { 3150 | Modes.hackrf_enabled = 1; 3151 | } else if (( 3152 | #ifndef NoSDRplay 3153 | Modes.prefer_sdrplay + 3154 | #endif 3155 | Modes.prefer_rtlsdr + Modes.prefer_hackrf ) == 0 && modesInitAirSpy() == 0 ) { 3156 | Modes.airspy_enabled = 1; 3157 | } else { 3158 | fprintf(stderr,"No compatible SDR device found.\n"); 3159 | exit (1); 3160 | } 3161 | } else { 3162 | if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') { 3163 | Modes.fd = STDIN_FILENO; 3164 | } else if ((Modes.fd = open(Modes.filename,O_RDONLY)) == -1) { 3165 | perror("Opening data file"); 3166 | exit(1); 3167 | } 3168 | } 3169 | if (Modes.net) modesInitNet(); 3170 | 3171 | /* If the user specifies --net-only, just run in order to serve network 3172 | * clients without reading data from the RTL device. */ 3173 | while (Modes.net_only) { 3174 | backgroundTasks(); 3175 | modesWaitReadableClients(100); 3176 | } 3177 | 3178 | /* Create the thread that will read the data from the device. */ 3179 | pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL); 3180 | 3181 | pthread_mutex_lock(&Modes.data_mutex); 3182 | while(1) { 3183 | if (!Modes.data_ready) { 3184 | pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex); 3185 | continue; 3186 | } 3187 | computeMagnitudeVector(); 3188 | 3189 | /* Signal to the other thread that we processed the available data 3190 | * and we want more (useful for --ifile). */ 3191 | Modes.data_ready = 0; 3192 | pthread_cond_signal(&Modes.data_cond); 3193 | 3194 | /* Process data after releasing the lock, so that the capturing 3195 | * thread can read data while we perform computationally expensive 3196 | * stuff * at the same time. (This should only be useful with very 3197 | * slow processors). */ 3198 | pthread_mutex_unlock(&Modes.data_mutex); 3199 | detectModeS(Modes.magnitude, Modes.data_len/2); 3200 | backgroundTasks(); 3201 | pthread_mutex_lock(&Modes.data_mutex); 3202 | if (Modes.exit) break; 3203 | } 3204 | 3205 | /* If --ifile and --stats were given, print statistics. */ 3206 | if (Modes.stats && Modes.filename) { 3207 | printf("%lld valid preambles\n", Modes.stat_valid_preamble); 3208 | printf("%lld demodulated again after phase correction\n", 3209 | Modes.stat_out_of_phase); 3210 | printf("%lld demodulated with zero errors\n", 3211 | Modes.stat_demodulated); 3212 | printf("%lld with good crc\n", Modes.stat_goodcrc); 3213 | printf("%lld with bad crc\n", Modes.stat_badcrc); 3214 | printf("%lld errors corrected\n", Modes.stat_fixed); 3215 | printf("%lld single bit errors\n", Modes.stat_single_bit_fix); 3216 | printf("%lld two bits errors\n", Modes.stat_two_bits_fix); 3217 | printf("%lld total usable messages\n", 3218 | Modes.stat_goodcrc + Modes.stat_fixed); 3219 | } 3220 | 3221 | rtlsdr_close(Modes.dev); 3222 | return 0; 3223 | } 3224 | 3225 | -------------------------------------------------------------------------------- /gmap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 35 | 39 | 40 | 41 | 42 | 43 | 44 | Dump1090_sdrplus 45 | 46 | Click on a plane for info. 47 | 48 | 49 | 178 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /images/dump1090_sdrplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itemir/dump1090_sdrplus/a21346b5570b7dee49ca590ddde112705ddd3c45/images/dump1090_sdrplus.png -------------------------------------------------------------------------------- /testfiles/modes1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itemir/dump1090_sdrplus/a21346b5570b7dee49ca590ddde112705ddd3c45/testfiles/modes1.bin -------------------------------------------------------------------------------- /tools/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 150 | 151 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | --------------------------------------------------------------------------------
Click on a plane for info.
188 |