├── .gitignore ├── Makefile ├── README.md ├── TODO ├── anet.c ├── anet.h ├── dump1090.c ├── gmap.html ├── testfiles └── modes1.bin └── tools └── debug.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | dump1090 3 | testfiles/*.bin 4 | misc 5 | frames.js 6 | .*.swp 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS?=-O2 -g -Wall -W $(shell pkg-config --cflags librtlsdr) 2 | LDLIBS+=$(shell pkg-config --libs librtlsdr) -lpthread -lm 3 | CC?=gcc 4 | PROGNAME=dump1090 5 | 6 | all: dump1090 7 | 8 | %.o: %.c 9 | $(CC) $(CFLAGS) -c $< 10 | 11 | dump1090: dump1090.o anet.o 12 | $(CC) -g -o dump1090 dump1090.o anet.o $(LDFLAGS) $(LDLIBS) 13 | 14 | clean: 15 | rm -f *.o dump1090 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Dump1090 README 2 | === 3 | 4 | Dump 1090 is a Mode S decoder specifically designed for RTLSDR devices. 5 | 6 | The main features are: 7 | 8 | * Robust decoding of weak messages, with mode1090 many users observed 9 | improved range compared to other popular decoders. 10 | * Network support: TCP30003 stream (MSG5...), Raw packets, HTTP. 11 | * Embedded HTTP server that displays the currently detected aircrafts on 12 | Google Map. 13 | * Single bit errors correction using the 24 bit CRC. 14 | * Ability to decode DF11, DF17 messages. 15 | * Ability to decode DF formats like DF0, DF4, DF5, DF16, DF20 and DF21 16 | where the checksum is xored with the ICAO address by brute forcing the 17 | checksum field using recently seen ICAO addresses. 18 | * Decode raw IQ samples from file (using --ifile command line switch). 19 | * Interactive command-line-interfae mode where aircrafts currently detected 20 | are shown as a list refreshing as more data arrives. 21 | * CPR coordinates decoding and track calculation from velocity. 22 | * TCP server streaming and receiving raw data to/from connected clients 23 | (using --net). 24 | 25 | While from time to time I still add / fix stuff in my fork, I target 26 | minimalism of the implementation. However there is a 27 | [much more feature complete fork](https://github.com/MalcolmRobb/dump1090) 28 | available, developed by MalcolmRobb. 29 | 30 | Installation 31 | --- 32 | 33 | Type "make". 34 | 35 | Normal usage 36 | --- 37 | 38 | To capture traffic directly from your RTL device and show the captured traffic 39 | on standard output, just run the program without options at all: 40 | 41 | ./dump1090 42 | 43 | To just output hexadecimal messages: 44 | 45 | ./dump1090 --raw 46 | 47 | To run the program in interactive mode: 48 | 49 | ./dump1090 --interactive 50 | 51 | To run the program in interactive mode, with networking support, and connect 52 | with your browser to http://localhost:8080 to see live traffic: 53 | 54 | ./dump1090 --interactive --net 55 | 56 | In interactive mode it is possible to have a less information dense but more 57 | "arcade style" output, where the screen is refreshed every second displaying 58 | all the recently seen aircrafts with some additional information such as 59 | altitude and flight number, extracted from the received Mode S packets. 60 | 61 | Using files as source of data 62 | --- 63 | 64 | To decode data from file, use: 65 | 66 | ./dump1090 --ifile /path/to/binfile 67 | 68 | The binary file should be created using `rtl_sdr` like this (or with any other 69 | program that is able to output 8-bit unsigned IQ samples at 2Mhz sample rate). 70 | 71 | rtl_sdr -f 1090000000 -s 2000000 -g 50 output.bin 72 | 73 | In the example `rtl_sdr` a gain of 50 is used, simply you should use the highest 74 | gain availabe for your tuner. This is not needed when calling Dump1090 itself 75 | as it is able to select the highest gain supported automatically. 76 | 77 | It is possible to feed the program with data via standard input using 78 | the --ifile option with "-" as argument. 79 | 80 | Additional options 81 | --- 82 | 83 | Dump1090 can be called with other command line options to set a different 84 | gain, frequency, and so forth. For a list of options use: 85 | 86 | ./dump1090 --help 87 | 88 | Everything is not documented here should be obvious, and for most users calling 89 | it without arguments at all is the best thing to do. 90 | 91 | Reliability 92 | --- 93 | 94 | By default Dump1090 tries to fix single bit errors using the checksum. 95 | Basically the program will try to flip every bit of the message and check if 96 | the checksum of the resulting message matches. 97 | 98 | This is indeed able to fix errors and works reliably in my experience, 99 | however if you are interested in very reliable data I suggest to use 100 | the --no-fix command line switch in order to disable error fixing. 101 | 102 | Performances and sensibility of detection 103 | --- 104 | 105 | In my limited experience Dump1090 was able to decode a big number of messages 106 | even in conditions where I encountered problems using other programs, however 107 | no formal test was performed so I can't really claim that this program is 108 | better or worse compared to other similar programs. 109 | 110 | If you can capture traffic that Dump1090 is not able to decode properly, drop 111 | me an email with a download link. I may try to improve the detection during 112 | my free time (this is just an hobby project). 113 | 114 | Network server features 115 | --- 116 | 117 | By enabling the networking support with --net Dump1090 starts listening 118 | for clients connections on port 30002 and 30001 (you can change both the 119 | ports if you want, see --help output). 120 | 121 | Port 30002 122 | --- 123 | 124 | Connected clients are served with data ASAP as they arrive from the device 125 | (or from file if --ifile is used) in the raw format similar to the following: 126 | 127 | *8D451E8B99019699C00B0A81F36E; 128 | 129 | Every entry is separated by a simple newline (LF character, hex 0x0A). 130 | 131 | Port 30001 132 | --- 133 | 134 | Port 30001 is the raw input port, and can be used to feed Dump1090 with 135 | data in the same format as specified above, with hex messages starting with 136 | a `*` and ending with a `;` character. 137 | 138 | So for instance if there is another remote Dump1090 instance collecting data 139 | it is possible to sum the output to a local Dump1090 instance doing something 140 | like this: 141 | 142 | nc remote-dump1090.example.net 30002 | nc localhost 30001 143 | 144 | It is important to note that what is received via port 30001 is also 145 | broadcasted to clients listening to port 30002. 146 | 147 | In general everything received from port 30001 is handled exactly like the 148 | normal traffic from RTL devices or from file when --ifile is used. 149 | 150 | It is possible to use Dump1090 just as an hub using --ifile with /dev/zero 151 | as argument as in the following example: 152 | 153 | ./dump1090 --net-only 154 | 155 | Or alternatively to see what's happening on the screen: 156 | 157 | ./dump1090 --net-only --interactive 158 | 159 | Then you can feed it from different data sources from the internet. 160 | 161 | Port 30003 162 | --- 163 | 164 | Connected clients are served with messages in SBS1 (BaseStation) format, 165 | similar to: 166 | 167 | MSG,4,,,738065,,,,,,,,420,179,,,0,,0,0,0,0 168 | MSG,3,,,738065,,,,,,,35000,,,34.81609,34.07810,,,0,0,0,0 169 | 170 | This can be used to feed data to various sharing sites without the need to use another decoder. 171 | 172 | Antenna 173 | --- 174 | 175 | Mode S messages are transmitted in the 1090 Mhz frequency. If you have a decent 176 | antenna you'll be able to pick up signals from aircrafts pretty far from your 177 | position, especially if you are outdoor and in a position with a good sky view. 178 | 179 | You can easily build a very cheap antenna following the istructions at: 180 | 181 | http://antirez.com/news/46 182 | 183 | With this trivial antenna I was able to pick up signals of aircrafts 200+ Km 184 | away from me. 185 | 186 | If you are interested in a more serious antenna check the following 187 | resources: 188 | 189 | * http://gnuradio.org/data/grcon11/06-foster-adsb.pdf 190 | * http://www.lll.lu/~edward/edward/adsb/antenna/ADSBantenna.html 191 | * http://modesbeast.com/pix/adsb-ant-drawing.gif 192 | 193 | Aggressive mode 194 | --- 195 | 196 | With --aggressive it is possible to activate the *aggressive mode* that is a 197 | modified version of the Mode S packet detection and decoding. 198 | THe aggresive mode uses more CPU usually (especially if there are many planes 199 | sending DF17 packets), but can detect a few more messages. 200 | 201 | The algorithm in aggressive mode is modified in the following ways: 202 | 203 | * Up to two demodulation errors are tolerated (adjacent entires in the magnitude 204 | vector with the same eight). Normally only messages without errors are 205 | checked. 206 | * It tries to fix DF17 messages trying every two bits combination. 207 | 208 | The use of aggressive mdoe is only advised in places where there is low traffic 209 | in order to have a chance to capture some more messages. 210 | 211 | Debug mode 212 | --- 213 | 214 | The Debug mode is a visual help to improve the detection algorithm or to 215 | understand why the program is not working for a given input. 216 | 217 | In this mode messages are displayed in an ASCII-art style graphical 218 | representation, where the individial magnitude bars sampled at 2Mhz are 219 | displayed. 220 | 221 | An index shows the sample number, where 0 is the sample where the first 222 | Mode S peak was found. Some additional background noise is also added 223 | before the first peak to provide some context. 224 | 225 | To enable debug mode and check what combinations of packets you can 226 | log, use `mode1090 --help` to obtain a list of available debug flags. 227 | 228 | Debug mode includes an optional javascript output that is used to visualize 229 | packets using a web browser, you can use the file debug.html under the 230 | 'tools' directory to load the generated frames.js file. 231 | 232 | How this program works? 233 | --- 234 | 235 | The code is very documented and written in order to be easy to understand. 236 | For the diligent programmer with a Mode S specification on his hands it 237 | should be trivial to understand how it works. 238 | 239 | The algorithms I used were obtained basically looking at many messages 240 | as displayed using a trow-away SDL program, and trying to model the algorithm 241 | based on how the messages look graphically. 242 | 243 | How to test the program? 244 | --- 245 | 246 | If you have an RTLSDR device and you happen to be in an area where there 247 | are aircrafts flying over your head, just run the program and check for signals. 248 | 249 | However if you don't have an RTLSDR device, or if in your area the presence 250 | of aircrafts is very limited, you may want to try the sample file distributed 251 | with the Dump1090 distribution under the "testfiles" directory. 252 | 253 | Just run it like this: 254 | 255 | ./dump1090 --ifile testfiles/modes1.bin 256 | 257 | What is --strip mode? 258 | --- 259 | 260 | It is just a simple filter that will get raw IQ 8 bit samples in input 261 | and will output a file missing all the parts of the file where I and Q 262 | are lower than the specified for more than 32 samples. 263 | 264 | Use it like this: 265 | 266 | cat big.bin | ./dump1090 --snip 25 > small.bin 267 | 268 | I used it in order to create a small test file to include inside this 269 | program source code distribution. 270 | 271 | Contributing 272 | --- 273 | 274 | Dump1090 was written during some free time during xmas 2012, it is an hobby 275 | project so I'll be able to address issues and improve it only during 276 | free time, however you are incouraged to send pull requests in order to 277 | improve the program. A good starting point can be the TODO list included in 278 | the source distribution. 279 | 280 | Credits 281 | --- 282 | 283 | Dump1090 was written by Salvatore Sanfilippo and is 284 | released under the BSD three clause license. 285 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dump1090.c: -------------------------------------------------------------------------------- 1 | /* Mode1090, a Mode S messages decoder for RTLSDR devices. 2 | * 3 | * Copyright (C) 2012 by Salvatore Sanfilippo 4 | * 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are 9 | * met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * * Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE 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 | #include 46 | #include "rtl-sdr.h" 47 | #include "anet.h" 48 | 49 | #define MODES_DEFAULT_RATE 2000000 50 | #define MODES_DEFAULT_FREQ 1090000000 51 | #define MODES_DEFAULT_WIDTH 1000 52 | #define MODES_DEFAULT_HEIGHT 700 53 | #define MODES_ASYNC_BUF_NUMBER 12 54 | #define MODES_DATA_LEN (16*16384) /* 256k */ 55 | #define MODES_AUTO_GAIN -100 /* Use automatic gain. */ 56 | #define MODES_MAX_GAIN 999999 /* Use max available gain. */ 57 | 58 | #define MODES_PREAMBLE_US 8 /* microseconds */ 59 | #define MODES_LONG_MSG_BITS 112 60 | #define MODES_SHORT_MSG_BITS 56 61 | #define MODES_FULL_LEN (MODES_PREAMBLE_US+MODES_LONG_MSG_BITS) 62 | #define MODES_LONG_MSG_BYTES (112/8) 63 | #define MODES_SHORT_MSG_BYTES (56/8) 64 | 65 | #define MODES_ICAO_CACHE_LEN 1024 /* Power of two required. */ 66 | #define MODES_ICAO_CACHE_TTL 60 /* Time to live of cached addresses. */ 67 | #define MODES_UNIT_FEET 0 68 | #define MODES_UNIT_METERS 1 69 | 70 | #define MODES_DEBUG_DEMOD (1<<0) 71 | #define MODES_DEBUG_DEMODERR (1<<1) 72 | #define MODES_DEBUG_BADCRC (1<<2) 73 | #define MODES_DEBUG_GOODCRC (1<<3) 74 | #define MODES_DEBUG_NOPREAMBLE (1<<4) 75 | #define MODES_DEBUG_NET (1<<5) 76 | #define MODES_DEBUG_JS (1<<6) 77 | 78 | /* When debug is set to MODES_DEBUG_NOPREAMBLE, the first sample must be 79 | * at least greater than a given level for us to dump the signal. */ 80 | #define MODES_DEBUG_NOPREAMBLE_LEVEL 25 81 | 82 | #define MODES_INTERACTIVE_REFRESH_TIME 250 /* Milliseconds */ 83 | #define MODES_INTERACTIVE_ROWS 15 /* Rows on screen */ 84 | #define MODES_INTERACTIVE_TTL 60 /* TTL before being removed */ 85 | 86 | #define MODES_NET_MAX_FD 1024 87 | #define MODES_NET_OUTPUT_SBS_PORT 30003 88 | #define MODES_NET_OUTPUT_RAW_PORT 30002 89 | #define MODES_NET_INPUT_RAW_PORT 30001 90 | #define MODES_NET_HTTP_PORT 8080 91 | #define MODES_CLIENT_BUF_SIZE 1024 92 | #define MODES_NET_SNDBUF_SIZE (1024*64) 93 | 94 | #define MODES_NOTUSED(V) ((void) V) 95 | 96 | /* Structure used to describe a networking client. */ 97 | struct client { 98 | int fd; /* File descriptor. */ 99 | int service; /* TCP port the client is connected to. */ 100 | char buf[MODES_CLIENT_BUF_SIZE+1]; /* Read buffer. */ 101 | int buflen; /* Amount of data on buffer. */ 102 | }; 103 | 104 | /* Structure used to describe an aircraft in iteractive mode. */ 105 | struct aircraft { 106 | uint32_t addr; /* ICAO address */ 107 | char hexaddr[7]; /* Printable ICAO address */ 108 | char flight[9]; /* Flight number */ 109 | int altitude; /* Altitude */ 110 | int speed; /* Velocity computed from EW and NS components. */ 111 | int track; /* Angle of flight. */ 112 | time_t seen; /* Time at which the last packet was received. */ 113 | long messages; /* Number of Mode S messages received. */ 114 | /* Encoded latitude and longitude as extracted by odd and even 115 | * CPR encoded messages. */ 116 | int odd_cprlat; 117 | int odd_cprlon; 118 | int even_cprlat; 119 | int even_cprlon; 120 | double lat, lon; /* Coordinated obtained from CPR encoded data. */ 121 | long long odd_cprtime, even_cprtime; 122 | struct aircraft *next; /* Next aircraft in our linked list. */ 123 | }; 124 | 125 | /* Program global state. */ 126 | struct { 127 | /* Internal state */ 128 | pthread_t reader_thread; 129 | pthread_mutex_t data_mutex; /* Mutex to synchronize buffer access. */ 130 | pthread_cond_t data_cond; /* Conditional variable associated. */ 131 | unsigned char *data; /* Raw IQ samples buffer */ 132 | uint16_t *magnitude; /* Magnitude vector */ 133 | uint32_t data_len; /* Buffer length. */ 134 | int fd; /* --ifile option file descriptor. */ 135 | int data_ready; /* Data ready to be processed. */ 136 | uint32_t *icao_cache; /* Recently seen ICAO addresses cache. */ 137 | uint16_t *maglut; /* I/Q -> Magnitude lookup table. */ 138 | int exit; /* Exit from the main loop when true. */ 139 | 140 | /* RTLSDR */ 141 | int dev_index; 142 | int gain; 143 | int enable_agc; 144 | rtlsdr_dev_t *dev; 145 | int freq; 146 | 147 | /* Networking */ 148 | char aneterr[ANET_ERR_LEN]; 149 | struct client *clients[MODES_NET_MAX_FD]; /* Our clients. */ 150 | int maxfd; /* Greatest fd currently active. */ 151 | int sbsos; /* SBS output listening socket. */ 152 | int ros; /* Raw output listening socket. */ 153 | int ris; /* Raw input listening socket. */ 154 | int https; /* HTTP listening socket. */ 155 | 156 | /* Configuration */ 157 | char *filename; /* Input form file, --ifile option. */ 158 | int loop; /* Read input file again and again. */ 159 | int fix_errors; /* Single bit error correction if true. */ 160 | int check_crc; /* Only display messages with good CRC. */ 161 | int raw; /* Raw output format. */ 162 | int debug; /* Debugging mode. */ 163 | int net; /* Enable networking. */ 164 | int net_only; /* Enable just networking. */ 165 | int interactive; /* Interactive mode */ 166 | int interactive_rows; /* Interactive mode: max number of rows. */ 167 | int interactive_ttl; /* Interactive mode: TTL before deletion. */ 168 | int stats; /* Print stats at exit in --ifile mode. */ 169 | int onlyaddr; /* Print only ICAO addresses. */ 170 | int metric; /* Use metric units. */ 171 | int aggressive; /* Aggressive detection algorithm. */ 172 | 173 | /* Interactive mode */ 174 | struct aircraft *aircrafts; 175 | long long interactive_last_update; /* Last screen update in milliseconds */ 176 | 177 | /* Statistics */ 178 | long long stat_valid_preamble; 179 | long long stat_demodulated; 180 | long long stat_goodcrc; 181 | long long stat_badcrc; 182 | long long stat_fixed; 183 | long long stat_single_bit_fix; 184 | long long stat_two_bits_fix; 185 | long long stat_http_requests; 186 | long long stat_sbs_connections; 187 | long long stat_out_of_phase; 188 | } Modes; 189 | 190 | /* The struct we use to store information about a decoded message. */ 191 | struct modesMessage { 192 | /* Generic fields */ 193 | unsigned char msg[MODES_LONG_MSG_BYTES]; /* Binary message. */ 194 | int msgbits; /* Number of bits in message */ 195 | int msgtype; /* Downlink format # */ 196 | int crcok; /* True if CRC was valid */ 197 | uint32_t crc; /* Message CRC */ 198 | int errorbit; /* Bit corrected. -1 if no bit corrected. */ 199 | int aa1, aa2, aa3; /* ICAO Address bytes 1 2 and 3 */ 200 | int phase_corrected; /* True if phase correction was applied. */ 201 | 202 | /* DF 11 */ 203 | int ca; /* Responder capabilities. */ 204 | 205 | /* DF 17 */ 206 | int metype; /* Extended squitter message type. */ 207 | int mesub; /* Extended squitter message subtype. */ 208 | int heading_is_valid; 209 | int heading; 210 | int aircraft_type; 211 | int fflag; /* 1 = Odd, 0 = Even CPR message. */ 212 | int tflag; /* UTC synchronized? */ 213 | int raw_latitude; /* Non decoded latitude */ 214 | int raw_longitude; /* Non decoded longitude */ 215 | char flight[9]; /* 8 chars flight number. */ 216 | int ew_dir; /* 0 = East, 1 = West. */ 217 | int ew_velocity; /* E/W velocity. */ 218 | int ns_dir; /* 0 = North, 1 = South. */ 219 | int ns_velocity; /* N/S velocity. */ 220 | int vert_rate_source; /* Vertical rate source. */ 221 | int vert_rate_sign; /* Vertical rate sign. */ 222 | int vert_rate; /* Vertical rate. */ 223 | int velocity; /* Computed from EW and NS velocity. */ 224 | 225 | /* DF4, DF5, DF20, DF21 */ 226 | int fs; /* Flight status for DF4,5,20,21 */ 227 | int dr; /* Request extraction of downlink request. */ 228 | int um; /* Request extraction of downlink request. */ 229 | int identity; /* 13 bits identity (Squawk). */ 230 | 231 | /* Fields used by multiple message types. */ 232 | int altitude, unit; 233 | }; 234 | 235 | void interactiveShowData(void); 236 | struct aircraft* interactiveReceiveData(struct modesMessage *mm); 237 | void modesSendRawOutput(struct modesMessage *mm); 238 | void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a); 239 | void useModesMessage(struct modesMessage *mm); 240 | int fixSingleBitErrors(unsigned char *msg, int bits); 241 | int fixTwoBitsErrors(unsigned char *msg, int bits); 242 | int modesMessageLenByType(int type); 243 | void sigWinchCallback(); 244 | int getTermRows(); 245 | 246 | /* ============================= Utility functions ========================== */ 247 | 248 | static long long mstime(void) { 249 | struct timeval tv; 250 | long long mst; 251 | 252 | gettimeofday(&tv, NULL); 253 | mst = ((long long)tv.tv_sec)*1000; 254 | mst += tv.tv_usec/1000; 255 | return mst; 256 | } 257 | 258 | /* =============================== Initialization =========================== */ 259 | 260 | void modesInitConfig(void) { 261 | Modes.gain = MODES_MAX_GAIN; 262 | Modes.dev_index = 0; 263 | Modes.enable_agc = 0; 264 | Modes.freq = MODES_DEFAULT_FREQ; 265 | Modes.filename = NULL; 266 | Modes.fix_errors = 1; 267 | Modes.check_crc = 1; 268 | Modes.raw = 0; 269 | Modes.net = 0; 270 | Modes.net_only = 0; 271 | Modes.onlyaddr = 0; 272 | Modes.debug = 0; 273 | Modes.interactive = 0; 274 | Modes.interactive_rows = MODES_INTERACTIVE_ROWS; 275 | Modes.interactive_ttl = MODES_INTERACTIVE_TTL; 276 | Modes.aggressive = 0; 277 | Modes.interactive_rows = getTermRows(); 278 | Modes.loop = 0; 279 | } 280 | 281 | void modesInit(void) { 282 | int i, q; 283 | 284 | pthread_mutex_init(&Modes.data_mutex,NULL); 285 | pthread_cond_init(&Modes.data_cond,NULL); 286 | /* We add a full message minus a final bit to the length, so that we 287 | * can carry the remaining part of the buffer that we can't process 288 | * in the message detection loop, back at the start of the next data 289 | * to process. This way we are able to also detect messages crossing 290 | * two reads. */ 291 | Modes.data_len = MODES_DATA_LEN + (MODES_FULL_LEN-1)*4; 292 | Modes.data_ready = 0; 293 | /* Allocate the ICAO address cache. We use two uint32_t for every 294 | * entry because it's a addr / timestamp pair for every entry. */ 295 | Modes.icao_cache = malloc(sizeof(uint32_t)*MODES_ICAO_CACHE_LEN*2); 296 | memset(Modes.icao_cache,0,sizeof(uint32_t)*MODES_ICAO_CACHE_LEN*2); 297 | Modes.aircrafts = NULL; 298 | Modes.interactive_last_update = 0; 299 | if ((Modes.data = malloc(Modes.data_len)) == NULL || 300 | (Modes.magnitude = malloc(Modes.data_len*2)) == NULL) { 301 | fprintf(stderr, "Out of memory allocating data buffer.\n"); 302 | exit(1); 303 | } 304 | memset(Modes.data,127,Modes.data_len); 305 | 306 | /* Populate the I/Q -> Magnitude lookup table. It is used because 307 | * sqrt or round may be expensive and performance may vary a lot 308 | * depending on the libc used. 309 | * 310 | * Note that we don't need to fill the table for negative values, as 311 | * we square both i and q to take the magnitude. So the maximum absolute 312 | * value of i and q is 128, thus the maximum magnitude we get is: 313 | * 314 | * sqrt(128*128+128*128) = ~181.02 315 | * 316 | * Then, to retain the full resolution and be able to distinguish among 317 | * every pair of I/Q values, we scale this range from the float range 318 | * 0-181 to the uint16_t range of 0-65536 by multiplying for 360. */ 319 | Modes.maglut = malloc(129*129*2); 320 | for (i = 0; i <= 128; i++) { 321 | for (q = 0; q <= 128; q++) { 322 | Modes.maglut[i*129+q] = round(sqrt(i*i+q*q)*360); 323 | } 324 | } 325 | 326 | /* Statistics */ 327 | Modes.stat_valid_preamble = 0; 328 | Modes.stat_demodulated = 0; 329 | Modes.stat_goodcrc = 0; 330 | Modes.stat_badcrc = 0; 331 | Modes.stat_fixed = 0; 332 | Modes.stat_single_bit_fix = 0; 333 | Modes.stat_two_bits_fix = 0; 334 | Modes.stat_http_requests = 0; 335 | Modes.stat_sbs_connections = 0; 336 | Modes.stat_out_of_phase = 0; 337 | Modes.exit = 0; 338 | } 339 | 340 | /* =============================== RTLSDR handling ========================== */ 341 | 342 | void modesInitRTLSDR(void) { 343 | int j; 344 | int device_count; 345 | int ppm_error = 0; 346 | char vendor[256], product[256], serial[256]; 347 | 348 | device_count = rtlsdr_get_device_count(); 349 | if (!device_count) { 350 | fprintf(stderr, "No supported RTLSDR devices found.\n"); 351 | exit(1); 352 | } 353 | 354 | fprintf(stderr, "Found %d device(s):\n", device_count); 355 | for (j = 0; j < device_count; j++) { 356 | rtlsdr_get_device_usb_strings(j, vendor, product, serial); 357 | fprintf(stderr, "%d: %s, %s, SN: %s %s\n", j, vendor, product, serial, 358 | (j == Modes.dev_index) ? "(currently selected)" : ""); 359 | } 360 | 361 | if (rtlsdr_open(&Modes.dev, Modes.dev_index) < 0) { 362 | fprintf(stderr, "Error opening the RTLSDR device: %s\n", 363 | strerror(errno)); 364 | exit(1); 365 | } 366 | 367 | /* Set gain, frequency, sample rate, and reset the device. */ 368 | rtlsdr_set_tuner_gain_mode(Modes.dev, 369 | (Modes.gain == MODES_AUTO_GAIN) ? 0 : 1); 370 | if (Modes.gain != MODES_AUTO_GAIN) { 371 | if (Modes.gain == MODES_MAX_GAIN) { 372 | /* Find the maximum gain available. */ 373 | int numgains; 374 | int gains[100]; 375 | 376 | numgains = rtlsdr_get_tuner_gains(Modes.dev, gains); 377 | Modes.gain = gains[numgains-1]; 378 | fprintf(stderr, "Max available gain is: %.2f\n", Modes.gain/10.0); 379 | } 380 | rtlsdr_set_tuner_gain(Modes.dev, Modes.gain); 381 | fprintf(stderr, "Setting gain to: %.2f\n", Modes.gain/10.0); 382 | } else { 383 | fprintf(stderr, "Using automatic gain control.\n"); 384 | } 385 | rtlsdr_set_freq_correction(Modes.dev, ppm_error); 386 | if (Modes.enable_agc) rtlsdr_set_agc_mode(Modes.dev, 1); 387 | rtlsdr_set_center_freq(Modes.dev, Modes.freq); 388 | rtlsdr_set_sample_rate(Modes.dev, MODES_DEFAULT_RATE); 389 | rtlsdr_reset_buffer(Modes.dev); 390 | fprintf(stderr, "Gain reported by device: %.2f\n", 391 | rtlsdr_get_tuner_gain(Modes.dev)/10.0); 392 | } 393 | 394 | /* We use a thread reading data in background, while the main thread 395 | * handles decoding and visualization of data to the user. 396 | * 397 | * The reading thread calls the RTLSDR API to read data asynchronously, and 398 | * uses a callback to populate the data buffer. 399 | * A Mutex is used to avoid races with the decoding thread. */ 400 | void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) { 401 | MODES_NOTUSED(ctx); 402 | 403 | pthread_mutex_lock(&Modes.data_mutex); 404 | if (len > MODES_DATA_LEN) len = MODES_DATA_LEN; 405 | /* Move the last part of the previous buffer, that was not processed, 406 | * on the start of the new buffer. */ 407 | memcpy(Modes.data, Modes.data+MODES_DATA_LEN, (MODES_FULL_LEN-1)*4); 408 | /* Read the new data. */ 409 | memcpy(Modes.data+(MODES_FULL_LEN-1)*4, buf, len); 410 | Modes.data_ready = 1; 411 | /* Signal to the other thread that new data is ready */ 412 | pthread_cond_signal(&Modes.data_cond); 413 | pthread_mutex_unlock(&Modes.data_mutex); 414 | } 415 | 416 | /* This is used when --ifile is specified in order to read data from file 417 | * instead of using an RTLSDR device. */ 418 | void readDataFromFile(void) { 419 | pthread_mutex_lock(&Modes.data_mutex); 420 | while(1) { 421 | ssize_t nread, toread; 422 | unsigned char *p; 423 | 424 | if (Modes.data_ready) { 425 | pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex); 426 | continue; 427 | } 428 | 429 | if (Modes.interactive) { 430 | /* When --ifile and --interactive are used together, slow down 431 | * playing at the natural rate of the RTLSDR received. */ 432 | pthread_mutex_unlock(&Modes.data_mutex); 433 | usleep(5000); 434 | pthread_mutex_lock(&Modes.data_mutex); 435 | } 436 | 437 | /* Move the last part of the previous buffer, that was not processed, 438 | * on the start of the new buffer. */ 439 | memcpy(Modes.data, Modes.data+MODES_DATA_LEN, (MODES_FULL_LEN-1)*4); 440 | toread = MODES_DATA_LEN; 441 | p = Modes.data+(MODES_FULL_LEN-1)*4; 442 | while(toread) { 443 | nread = read(Modes.fd, p, toread); 444 | /* In --file mode, seek the file again from the start 445 | * and re-play it if --loop was given. */ 446 | if (nread == 0 && 447 | Modes.filename != NULL && 448 | Modes.fd != STDIN_FILENO && 449 | Modes.loop) 450 | { 451 | if (lseek(Modes.fd,0,SEEK_SET) != -1) continue; 452 | } 453 | 454 | if (nread <= 0) { 455 | Modes.exit = 1; /* Signal the other thread to exit. */ 456 | break; 457 | } 458 | p += nread; 459 | toread -= nread; 460 | } 461 | if (toread) { 462 | /* Not enough data on file to fill the buffer? Pad with 463 | * no signal. */ 464 | memset(p,127,toread); 465 | } 466 | Modes.data_ready = 1; 467 | /* Signal to the other thread that new data is ready */ 468 | pthread_cond_signal(&Modes.data_cond); 469 | } 470 | } 471 | 472 | /* We read data using a thread, so the main thread only handles decoding 473 | * without caring about data acquisition. */ 474 | void *readerThreadEntryPoint(void *arg) { 475 | MODES_NOTUSED(arg); 476 | 477 | if (Modes.filename == NULL) { 478 | rtlsdr_read_async(Modes.dev, rtlsdrCallback, NULL, 479 | MODES_ASYNC_BUF_NUMBER, 480 | MODES_DATA_LEN); 481 | } else { 482 | readDataFromFile(); 483 | } 484 | return NULL; 485 | } 486 | 487 | /* ============================== Debugging ================================= */ 488 | 489 | /* Helper function for dumpMagnitudeVector(). 490 | * It prints a single bar used to display raw signals. 491 | * 492 | * Since every magnitude sample is between 0-255, the function uses 493 | * up to 63 characters for every bar. Every character represents 494 | * a length of 4, 3, 2, 1, specifically: 495 | * 496 | * "O" is 4 497 | * "o" is 3 498 | * "-" is 2 499 | * "." is 1 500 | */ 501 | void dumpMagnitudeBar(int index, int magnitude) { 502 | char *set = " .-o"; 503 | char buf[256]; 504 | int div = magnitude / 256 / 4; 505 | int rem = magnitude / 256 % 4; 506 | 507 | memset(buf,'O',div); 508 | buf[div] = set[rem]; 509 | buf[div+1] = '\0'; 510 | 511 | if (index >= 0) { 512 | int markchar = ']'; 513 | 514 | /* preamble peaks are marked with ">" */ 515 | if (index == 0 || index == 2 || index == 7 || index == 9) 516 | markchar = '>'; 517 | /* Data peaks are marked to distinguish pairs of bits. */ 518 | if (index >= 16) markchar = ((index-16)/2 & 1) ? '|' : ')'; 519 | printf("[%.3d%c |%-66s %d\n", index, markchar, buf, magnitude); 520 | } else { 521 | printf("[%.2d] |%-66s %d\n", index, buf, magnitude); 522 | } 523 | } 524 | 525 | /* Display an ASCII-art alike graphical representation of the undecoded 526 | * message as a magnitude signal. 527 | * 528 | * The message starts at the specified offset in the "m" buffer. 529 | * The function will display enough data to cover a short 56 bit message. 530 | * 531 | * If possible a few samples before the start of the messsage are included 532 | * for context. */ 533 | 534 | void dumpMagnitudeVector(uint16_t *m, uint32_t offset) { 535 | uint32_t padding = 5; /* Show a few samples before the actual start. */ 536 | uint32_t start = (offset < padding) ? 0 : offset-padding; 537 | uint32_t end = offset + (MODES_PREAMBLE_US*2)+(MODES_SHORT_MSG_BITS*2) - 1; 538 | uint32_t j; 539 | 540 | for (j = start; j <= end; j++) { 541 | dumpMagnitudeBar(j-offset, m[j]); 542 | } 543 | } 544 | 545 | /* Produce a raw representation of the message as a Javascript file 546 | * loadable by debug.html. */ 547 | void dumpRawMessageJS(char *descr, unsigned char *msg, 548 | uint16_t *m, uint32_t offset, int fixable) 549 | { 550 | int padding = 5; /* Show a few samples before the actual start. */ 551 | int start = offset - padding; 552 | int end = offset + (MODES_PREAMBLE_US*2)+(MODES_LONG_MSG_BITS*2) - 1; 553 | FILE *fp; 554 | int j, fix1 = -1, fix2 = -1; 555 | 556 | if (fixable != -1) { 557 | fix1 = fixable & 0xff; 558 | if (fixable > 255) fix2 = fixable >> 8; 559 | } 560 | 561 | if ((fp = fopen("frames.js","a")) == NULL) { 562 | fprintf(stderr, "Error opening frames.js: %s\n", strerror(errno)); 563 | exit(1); 564 | } 565 | 566 | fprintf(fp,"frames.push({\"descr\": \"%s\", \"mag\": [", descr); 567 | for (j = start; j <= end; j++) { 568 | fprintf(fp,"%d", j < 0 ? 0 : m[j]); 569 | if (j != end) fprintf(fp,","); 570 | } 571 | fprintf(fp,"], \"fix1\": %d, \"fix2\": %d, \"bits\": %d, \"hex\": \"", 572 | fix1, fix2, modesMessageLenByType(msg[0]>>3)); 573 | for (j = 0; j < MODES_LONG_MSG_BYTES; j++) 574 | fprintf(fp,"\\x%02x",msg[j]); 575 | fprintf(fp,"\"});\n"); 576 | fclose(fp); 577 | } 578 | 579 | /* This is a wrapper for dumpMagnitudeVector() that also show the message 580 | * in hex format with an additional description. 581 | * 582 | * descr is the additional message to show to describe the dump. 583 | * msg points to the decoded message 584 | * m is the original magnitude vector 585 | * offset is the offset where the message starts 586 | * 587 | * The function also produces the Javascript file used by debug.html to 588 | * display packets in a graphical format if the Javascript output was 589 | * enabled. 590 | */ 591 | void dumpRawMessage(char *descr, unsigned char *msg, 592 | uint16_t *m, uint32_t offset) 593 | { 594 | int j; 595 | int msgtype = msg[0]>>3; 596 | int fixable = -1; 597 | 598 | if (msgtype == 11 || msgtype == 17) { 599 | int msgbits = (msgtype == 11) ? MODES_SHORT_MSG_BITS : 600 | MODES_LONG_MSG_BITS; 601 | fixable = fixSingleBitErrors(msg,msgbits); 602 | if (fixable == -1) 603 | fixable = fixTwoBitsErrors(msg,msgbits); 604 | } 605 | 606 | if (Modes.debug & MODES_DEBUG_JS) { 607 | dumpRawMessageJS(descr, msg, m, offset, fixable); 608 | return; 609 | } 610 | 611 | printf("\n--- %s\n ", descr); 612 | for (j = 0; j < MODES_LONG_MSG_BYTES; j++) { 613 | printf("%02x",msg[j]); 614 | if (j == MODES_SHORT_MSG_BYTES-1) printf(" ... "); 615 | } 616 | printf(" (DF %d, Fixable: %d)\n", msgtype, fixable); 617 | dumpMagnitudeVector(m,offset); 618 | printf("---\n\n"); 619 | } 620 | 621 | /* ===================== Mode S detection and decoding ===================== */ 622 | 623 | /* Parity table for MODE S Messages. 624 | * The table contains 112 elements, every element corresponds to a bit set 625 | * in the message, starting from the first bit of actual data after the 626 | * preamble. 627 | * 628 | * For messages of 112 bit, the whole table is used. 629 | * For messages of 56 bits only the last 56 elements are used. 630 | * 631 | * The algorithm is as simple as xoring all the elements in this table 632 | * for which the corresponding bit on the message is set to 1. 633 | * 634 | * The latest 24 elements in this table are set to 0 as the checksum at the 635 | * end of the message should not affect the computation. 636 | * 637 | * Note: this function can be used with DF11 and DF17, other modes have 638 | * the CRC xored with the sender address as they are reply to interrogations, 639 | * but a casual listener can't split the address from the checksum. 640 | */ 641 | uint32_t modes_checksum_table[112] = { 642 | 0x3935ea, 0x1c9af5, 0xf1b77e, 0x78dbbf, 0xc397db, 0x9e31e9, 0xb0e2f0, 0x587178, 643 | 0x2c38bc, 0x161c5e, 0x0b0e2f, 0xfa7d13, 0x82c48d, 0xbe9842, 0x5f4c21, 0xd05c14, 644 | 0x682e0a, 0x341705, 0xe5f186, 0x72f8c3, 0xc68665, 0x9cb936, 0x4e5c9b, 0xd8d449, 645 | 0x939020, 0x49c810, 0x24e408, 0x127204, 0x093902, 0x049c81, 0xfdb444, 0x7eda22, 646 | 0x3f6d11, 0xe04c8c, 0x702646, 0x381323, 0xe3f395, 0x8e03ce, 0x4701e7, 0xdc7af7, 647 | 0x91c77f, 0xb719bb, 0xa476d9, 0xadc168, 0x56e0b4, 0x2b705a, 0x15b82d, 0xf52612, 648 | 0x7a9309, 0xc2b380, 0x6159c0, 0x30ace0, 0x185670, 0x0c2b38, 0x06159c, 0x030ace, 649 | 0x018567, 0xff38b7, 0x80665f, 0xbfc92b, 0xa01e91, 0xaff54c, 0x57faa6, 0x2bfd53, 650 | 0xea04ad, 0x8af852, 0x457c29, 0xdd4410, 0x6ea208, 0x375104, 0x1ba882, 0x0dd441, 651 | 0xf91024, 0x7c8812, 0x3e4409, 0xe0d800, 0x706c00, 0x383600, 0x1c1b00, 0x0e0d80, 652 | 0x0706c0, 0x038360, 0x01c1b0, 0x00e0d8, 0x00706c, 0x003836, 0x001c1b, 0xfff409, 653 | 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 654 | 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 655 | 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 656 | }; 657 | 658 | uint32_t modesChecksum(unsigned char *msg, int bits) { 659 | uint32_t crc = 0; 660 | int offset = (bits == 112) ? 0 : (112-56); 661 | int j; 662 | 663 | for(j = 0; j < bits; j++) { 664 | int byte = j/8; 665 | int bit = j%8; 666 | int bitmask = 1 << (7-bit); 667 | 668 | /* If bit is set, xor with corresponding table entry. */ 669 | if (msg[byte] & bitmask) 670 | crc ^= modes_checksum_table[j+offset]; 671 | } 672 | return crc; /* 24 bit checksum. */ 673 | } 674 | 675 | /* Given the Downlink Format (DF) of the message, return the message length 676 | * in bits. */ 677 | int modesMessageLenByType(int type) { 678 | if (type == 16 || type == 17 || 679 | type == 19 || type == 20 || 680 | type == 21) 681 | return MODES_LONG_MSG_BITS; 682 | else 683 | return MODES_SHORT_MSG_BITS; 684 | } 685 | 686 | /* Try to fix single bit errors using the checksum. On success modifies 687 | * the original buffer with the fixed version, and returns the position 688 | * of the error bit. Otherwise if fixing failed -1 is returned. */ 689 | int fixSingleBitErrors(unsigned char *msg, int bits) { 690 | int j; 691 | unsigned char aux[MODES_LONG_MSG_BITS/8]; 692 | 693 | for (j = 0; j < bits; j++) { 694 | int byte = j/8; 695 | int bitmask = 1 << (7-(j%8)); 696 | uint32_t crc1, crc2; 697 | 698 | memcpy(aux,msg,bits/8); 699 | aux[byte] ^= bitmask; /* Flip j-th bit. */ 700 | 701 | crc1 = ((uint32_t)aux[(bits/8)-3] << 16) | 702 | ((uint32_t)aux[(bits/8)-2] << 8) | 703 | (uint32_t)aux[(bits/8)-1]; 704 | crc2 = modesChecksum(aux,bits); 705 | 706 | if (crc1 == crc2) { 707 | /* The error is fixed. Overwrite the original buffer with 708 | * the corrected sequence, and returns the error bit 709 | * position. */ 710 | memcpy(msg,aux,bits/8); 711 | return j; 712 | } 713 | } 714 | return -1; 715 | } 716 | 717 | /* Similar to fixSingleBitErrors() but try every possible two bit combination. 718 | * This is very slow and should be tried only against DF17 messages that 719 | * don't pass the checksum, and only in Aggressive Mode. */ 720 | int fixTwoBitsErrors(unsigned char *msg, int bits) { 721 | int j, i; 722 | unsigned char aux[MODES_LONG_MSG_BITS/8]; 723 | 724 | for (j = 0; j < bits; j++) { 725 | int byte1 = j/8; 726 | int bitmask1 = 1 << (7-(j%8)); 727 | 728 | /* Don't check the same pairs multiple times, so i starts from j+1 */ 729 | for (i = j+1; i < bits; i++) { 730 | int byte2 = i/8; 731 | int bitmask2 = 1 << (7-(i%8)); 732 | uint32_t crc1, crc2; 733 | 734 | memcpy(aux,msg,bits/8); 735 | 736 | aux[byte1] ^= bitmask1; /* Flip j-th bit. */ 737 | aux[byte2] ^= bitmask2; /* Flip i-th bit. */ 738 | 739 | crc1 = ((uint32_t)aux[(bits/8)-3] << 16) | 740 | ((uint32_t)aux[(bits/8)-2] << 8) | 741 | (uint32_t)aux[(bits/8)-1]; 742 | crc2 = modesChecksum(aux,bits); 743 | 744 | if (crc1 == crc2) { 745 | /* The error is fixed. Overwrite the original buffer with 746 | * the corrected sequence, and returns the error bit 747 | * position. */ 748 | memcpy(msg,aux,bits/8); 749 | /* We return the two bits as a 16 bit integer by shifting 750 | * 'i' on the left. This is possible since 'i' will always 751 | * be non-zero because i starts from j+1. */ 752 | return j | (i<<8); 753 | } 754 | } 755 | } 756 | return -1; 757 | } 758 | 759 | /* Hash the ICAO address to index our cache of MODES_ICAO_CACHE_LEN 760 | * elements, that is assumed to be a power of two. */ 761 | uint32_t ICAOCacheHashAddress(uint32_t a) { 762 | /* The following three rounds wil make sure that every bit affects 763 | * every output bit with ~ 50% of probability. */ 764 | a = ((a >> 16) ^ a) * 0x45d9f3b; 765 | a = ((a >> 16) ^ a) * 0x45d9f3b; 766 | a = ((a >> 16) ^ a); 767 | return a & (MODES_ICAO_CACHE_LEN-1); 768 | } 769 | 770 | /* Add the specified entry to the cache of recently seen ICAO addresses. 771 | * Note that we also add a timestamp so that we can make sure that the 772 | * entry is only valid for MODES_ICAO_CACHE_TTL seconds. */ 773 | void addRecentlySeenICAOAddr(uint32_t addr) { 774 | uint32_t h = ICAOCacheHashAddress(addr); 775 | Modes.icao_cache[h*2] = addr; 776 | Modes.icao_cache[h*2+1] = (uint32_t) time(NULL); 777 | } 778 | 779 | /* Returns 1 if the specified ICAO address was seen in a DF format with 780 | * proper checksum (not xored with address) no more than * MODES_ICAO_CACHE_TTL 781 | * seconds ago. Otherwise returns 0. */ 782 | int ICAOAddressWasRecentlySeen(uint32_t addr) { 783 | uint32_t h = ICAOCacheHashAddress(addr); 784 | uint32_t a = Modes.icao_cache[h*2]; 785 | uint32_t t = Modes.icao_cache[h*2+1]; 786 | 787 | return a && a == addr && time(NULL)-t <= MODES_ICAO_CACHE_TTL; 788 | } 789 | 790 | /* If the message type has the checksum xored with the ICAO address, try to 791 | * brute force it using a list of recently seen ICAO addresses. 792 | * 793 | * Do this in a brute-force fashion by xoring the predicted CRC with 794 | * the address XOR checksum field in the message. This will recover the 795 | * address: if we found it in our cache, we can assume the message is ok. 796 | * 797 | * This function expects mm->msgtype and mm->msgbits to be correctly 798 | * populated by the caller. 799 | * 800 | * On success the correct ICAO address is stored in the modesMessage 801 | * structure in the aa3, aa2, and aa1 fiedls. 802 | * 803 | * If the function successfully recovers a message with a correct checksum 804 | * it returns 1. Otherwise 0 is returned. */ 805 | int bruteForceAP(unsigned char *msg, struct modesMessage *mm) { 806 | unsigned char aux[MODES_LONG_MSG_BYTES]; 807 | int msgtype = mm->msgtype; 808 | int msgbits = mm->msgbits; 809 | 810 | if (msgtype == 0 || /* Short air surveillance */ 811 | msgtype == 4 || /* Surveillance, altitude reply */ 812 | msgtype == 5 || /* Surveillance, identity reply */ 813 | msgtype == 16 || /* Long Air-Air survillance */ 814 | msgtype == 20 || /* Comm-A, altitude request */ 815 | msgtype == 21 || /* Comm-A, identity request */ 816 | msgtype == 24) /* Comm-C ELM */ 817 | { 818 | uint32_t addr; 819 | uint32_t crc; 820 | int lastbyte = (msgbits/8)-1; 821 | 822 | /* Work on a copy. */ 823 | memcpy(aux,msg,msgbits/8); 824 | 825 | /* Compute the CRC of the message and XOR it with the AP field 826 | * so that we recover the address, because: 827 | * 828 | * (ADDR xor CRC) xor CRC = ADDR. */ 829 | crc = modesChecksum(aux,msgbits); 830 | aux[lastbyte] ^= crc & 0xff; 831 | aux[lastbyte-1] ^= (crc >> 8) & 0xff; 832 | aux[lastbyte-2] ^= (crc >> 16) & 0xff; 833 | 834 | /* If the obtained address exists in our cache we consider 835 | * the message valid. */ 836 | addr = aux[lastbyte] | (aux[lastbyte-1] << 8) | (aux[lastbyte-2] << 16); 837 | if (ICAOAddressWasRecentlySeen(addr)) { 838 | mm->aa1 = aux[lastbyte-2]; 839 | mm->aa2 = aux[lastbyte-1]; 840 | mm->aa3 = aux[lastbyte]; 841 | return 1; 842 | } 843 | } 844 | return 0; 845 | } 846 | 847 | /* Decode the 13 bit AC altitude field (in DF 20 and others). 848 | * Returns the altitude, and set 'unit' to either MODES_UNIT_METERS 849 | * or MDOES_UNIT_FEETS. */ 850 | int decodeAC13Field(unsigned char *msg, int *unit) { 851 | int m_bit = msg[3] & (1<<6); 852 | int q_bit = msg[3] & (1<<4); 853 | 854 | if (!m_bit) { 855 | *unit = MODES_UNIT_FEET; 856 | if (q_bit) { 857 | /* N is the 11 bit integer resulting from the removal of bit 858 | * Q and M */ 859 | int n = ((msg[2]&31)<<6) | 860 | ((msg[3]&0x80)>>2) | 861 | ((msg[3]&0x20)>>1) | 862 | (msg[3]&15); 863 | /* The final altitude is due to the resulting number multiplied 864 | * by 25, minus 1000. */ 865 | return n*25-1000; 866 | } else { 867 | /* TODO: Implement altitude where Q=0 and M=0 */ 868 | } 869 | } else { 870 | *unit = MODES_UNIT_METERS; 871 | /* TODO: Implement altitude when meter unit is selected. */ 872 | } 873 | return 0; 874 | } 875 | 876 | /* Decode the 12 bit AC altitude field (in DF 17 and others). 877 | * Returns the altitude or 0 if it can't be decoded. */ 878 | int decodeAC12Field(unsigned char *msg, int *unit) { 879 | int q_bit = msg[5] & 1; 880 | 881 | if (q_bit) { 882 | /* N is the 11 bit integer resulting from the removal of bit 883 | * Q */ 884 | *unit = MODES_UNIT_FEET; 885 | int n = ((msg[5]>>1)<<4) | ((msg[6]&0xF0) >> 4); 886 | /* The final altitude is due to the resulting number multiplied 887 | * by 25, minus 1000. */ 888 | return n*25-1000; 889 | } else { 890 | return 0; 891 | } 892 | } 893 | 894 | /* Capability table. */ 895 | char *ca_str[8] = { 896 | /* 0 */ "Level 1 (Survillance Only)", 897 | /* 1 */ "Level 2 (DF0,4,5,11)", 898 | /* 2 */ "Level 3 (DF0,4,5,11,20,21)", 899 | /* 3 */ "Level 4 (DF0,4,5,11,20,21,24)", 900 | /* 4 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on ground)", 901 | /* 5 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7 - is on airborne)", 902 | /* 6 */ "Level 2+3+4 (DF0,4,5,11,20,21,24,code7)", 903 | /* 7 */ "Level 7 ???" 904 | }; 905 | 906 | /* Flight status table. */ 907 | char *fs_str[8] = { 908 | /* 0 */ "Normal, Airborne", 909 | /* 1 */ "Normal, On the ground", 910 | /* 2 */ "ALERT, Airborne", 911 | /* 3 */ "ALERT, On the ground", 912 | /* 4 */ "ALERT & Special Position Identification. Airborne or Ground", 913 | /* 5 */ "Special Position Identification. Airborne or Ground", 914 | /* 6 */ "Value 6 is not assigned", 915 | /* 7 */ "Value 7 is not assigned" 916 | }; 917 | 918 | /* ME message type to description table. */ 919 | char *me_str[] = { 920 | }; 921 | 922 | char *getMEDescription(int metype, int mesub) { 923 | char *mename = "Unknown"; 924 | 925 | if (metype >= 1 && metype <= 4) 926 | mename = "Aircraft Identification and Category"; 927 | else if (metype >= 5 && metype <= 8) 928 | mename = "Surface Position"; 929 | else if (metype >= 9 && metype <= 18) 930 | mename = "Airborne Position (Baro Altitude)"; 931 | else if (metype == 19 && mesub >=1 && mesub <= 4) 932 | mename = "Airborne Velocity"; 933 | else if (metype >= 20 && metype <= 22) 934 | mename = "Airborne Position (GNSS Height)"; 935 | else if (metype == 23 && mesub == 0) 936 | mename = "Test Message"; 937 | else if (metype == 24 && mesub == 1) 938 | mename = "Surface System Status"; 939 | else if (metype == 28 && mesub == 1) 940 | mename = "Extended Squitter Aircraft Status (Emergency)"; 941 | else if (metype == 28 && mesub == 2) 942 | mename = "Extended Squitter Aircraft Status (1090ES TCAS RA)"; 943 | else if (metype == 29 && (mesub == 0 || mesub == 1)) 944 | mename = "Target State and Status Message"; 945 | else if (metype == 31 && (mesub == 0 || mesub == 1)) 946 | mename = "Aircraft Operational Status Message"; 947 | return mename; 948 | } 949 | 950 | /* Decode a raw Mode S message demodulated as a stream of bytes by 951 | * detectModeS(), and split it into fields populating a modesMessage 952 | * structure. */ 953 | void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) { 954 | uint32_t crc2; /* Computed CRC, used to verify the message CRC. */ 955 | char *ais_charset = "?ABCDEFGHIJKLMNOPQRSTUVWXYZ????? ???????????????0123456789??????"; 956 | 957 | /* Work on our local copy */ 958 | memcpy(mm->msg,msg,MODES_LONG_MSG_BYTES); 959 | msg = mm->msg; 960 | 961 | /* Get the message type ASAP as other operations depend on this */ 962 | mm->msgtype = msg[0]>>3; /* Downlink Format */ 963 | mm->msgbits = modesMessageLenByType(mm->msgtype); 964 | 965 | /* CRC is always the last three bytes. */ 966 | mm->crc = ((uint32_t)msg[(mm->msgbits/8)-3] << 16) | 967 | ((uint32_t)msg[(mm->msgbits/8)-2] << 8) | 968 | (uint32_t)msg[(mm->msgbits/8)-1]; 969 | crc2 = modesChecksum(msg,mm->msgbits); 970 | 971 | /* Check CRC and fix single bit errors using the CRC when 972 | * possible (DF 11 and 17). */ 973 | mm->errorbit = -1; /* No error */ 974 | mm->crcok = (mm->crc == crc2); 975 | 976 | if (!mm->crcok && Modes.fix_errors && 977 | (mm->msgtype == 11 || mm->msgtype == 17)) 978 | { 979 | if ((mm->errorbit = fixSingleBitErrors(msg,mm->msgbits)) != -1) { 980 | mm->crc = modesChecksum(msg,mm->msgbits); 981 | mm->crcok = 1; 982 | } else if (Modes.aggressive && mm->msgtype == 17 && 983 | (mm->errorbit = fixTwoBitsErrors(msg,mm->msgbits)) != -1) 984 | { 985 | mm->crc = modesChecksum(msg,mm->msgbits); 986 | mm->crcok = 1; 987 | } 988 | } 989 | 990 | /* Note that most of the other computation happens *after* we fix 991 | * the single bit errors, otherwise we would need to recompute the 992 | * fields again. */ 993 | mm->ca = msg[0] & 7; /* Responder capabilities. */ 994 | 995 | /* ICAO address */ 996 | mm->aa1 = msg[1]; 997 | mm->aa2 = msg[2]; 998 | mm->aa3 = msg[3]; 999 | 1000 | /* DF 17 type (assuming this is a DF17, otherwise not used) */ 1001 | mm->metype = msg[4] >> 3; /* Extended squitter message type. */ 1002 | mm->mesub = msg[4] & 7; /* Extended squitter message subtype. */ 1003 | 1004 | /* Fields for DF4,5,20,21 */ 1005 | mm->fs = msg[0] & 7; /* Flight status for DF4,5,20,21 */ 1006 | mm->dr = msg[1] >> 3 & 31; /* Request extraction of downlink request. */ 1007 | mm->um = ((msg[1] & 7)<<3)| /* Request extraction of downlink request. */ 1008 | msg[2]>>5; 1009 | 1010 | /* In the squawk (identity) field bits are interleaved like that 1011 | * (message bit 20 to bit 32): 1012 | * 1013 | * C1-A1-C2-A2-C4-A4-ZERO-B1-D1-B2-D2-B4-D4 1014 | * 1015 | * So every group of three bits A, B, C, D represent an integer 1016 | * from 0 to 7. 1017 | * 1018 | * The actual meaning is just 4 octal numbers, but we convert it 1019 | * into a base ten number tha happens to represent the four 1020 | * octal numbers. 1021 | * 1022 | * For more info: http://en.wikipedia.org/wiki/Gillham_code */ 1023 | { 1024 | int a,b,c,d; 1025 | 1026 | a = ((msg[3] & 0x80) >> 5) | 1027 | ((msg[2] & 0x02) >> 0) | 1028 | ((msg[2] & 0x08) >> 3); 1029 | b = ((msg[3] & 0x02) << 1) | 1030 | ((msg[3] & 0x08) >> 2) | 1031 | ((msg[3] & 0x20) >> 5); 1032 | c = ((msg[2] & 0x01) << 2) | 1033 | ((msg[2] & 0x04) >> 1) | 1034 | ((msg[2] & 0x10) >> 4); 1035 | d = ((msg[3] & 0x01) << 2) | 1036 | ((msg[3] & 0x04) >> 1) | 1037 | ((msg[3] & 0x10) >> 4); 1038 | mm->identity = a*1000 + b*100 + c*10 + d; 1039 | } 1040 | 1041 | /* DF 11 & 17: try to populate our ICAO addresses whitelist. 1042 | * DFs with an AP field (xored addr and crc), try to decode it. */ 1043 | if (mm->msgtype != 11 && mm->msgtype != 17) { 1044 | /* Check if we can check the checksum for the Downlink Formats where 1045 | * the checksum is xored with the aircraft ICAO address. We try to 1046 | * brute force it using a list of recently seen aircraft addresses. */ 1047 | if (bruteForceAP(msg,mm)) { 1048 | /* We recovered the message, mark the checksum as valid. */ 1049 | mm->crcok = 1; 1050 | } else { 1051 | mm->crcok = 0; 1052 | } 1053 | } else { 1054 | /* If this is DF 11 or DF 17 and the checksum was ok, 1055 | * we can add this address to the list of recently seen 1056 | * addresses. */ 1057 | if (mm->crcok && mm->errorbit == -1) { 1058 | uint32_t addr = (mm->aa1 << 16) | (mm->aa2 << 8) | mm->aa3; 1059 | addRecentlySeenICAOAddr(addr); 1060 | } 1061 | } 1062 | 1063 | /* Decode 13 bit altitude for DF0, DF4, DF16, DF20 */ 1064 | if (mm->msgtype == 0 || mm->msgtype == 4 || 1065 | mm->msgtype == 16 || mm->msgtype == 20) { 1066 | mm->altitude = decodeAC13Field(msg, &mm->unit); 1067 | } 1068 | 1069 | /* Decode extended squitter specific stuff. */ 1070 | if (mm->msgtype == 17) { 1071 | /* Decode the extended squitter message. */ 1072 | 1073 | if (mm->metype >= 1 && mm->metype <= 4) { 1074 | /* Aircraft Identification and Category */ 1075 | mm->aircraft_type = mm->metype-1; 1076 | mm->flight[0] = ais_charset[msg[5]>>2]; 1077 | mm->flight[1] = ais_charset[((msg[5]&3)<<4)|(msg[6]>>4)]; 1078 | mm->flight[2] = ais_charset[((msg[6]&15)<<2)|(msg[7]>>6)]; 1079 | mm->flight[3] = ais_charset[msg[7]&63]; 1080 | mm->flight[4] = ais_charset[msg[8]>>2]; 1081 | mm->flight[5] = ais_charset[((msg[8]&3)<<4)|(msg[9]>>4)]; 1082 | mm->flight[6] = ais_charset[((msg[9]&15)<<2)|(msg[10]>>6)]; 1083 | mm->flight[7] = ais_charset[msg[10]&63]; 1084 | mm->flight[8] = '\0'; 1085 | } else if (mm->metype >= 9 && mm->metype <= 18) { 1086 | /* Airborne position Message */ 1087 | mm->fflag = msg[6] & (1<<2); 1088 | mm->tflag = msg[6] & (1<<3); 1089 | mm->altitude = decodeAC12Field(msg,&mm->unit); 1090 | mm->raw_latitude = ((msg[6] & 3) << 15) | 1091 | (msg[7] << 7) | 1092 | (msg[8] >> 1); 1093 | mm->raw_longitude = ((msg[8]&1) << 16) | 1094 | (msg[9] << 8) | 1095 | msg[10]; 1096 | } else if (mm->metype == 19 && mm->mesub >= 1 && mm->mesub <= 4) { 1097 | /* Airborne Velocity Message */ 1098 | if (mm->mesub == 1 || mm->mesub == 2) { 1099 | mm->ew_dir = (msg[5]&4) >> 2; 1100 | mm->ew_velocity = ((msg[5]&3) << 8) | msg[6]; 1101 | mm->ns_dir = (msg[7]&0x80) >> 7; 1102 | mm->ns_velocity = ((msg[7]&0x7f) << 3) | ((msg[8]&0xe0) >> 5); 1103 | mm->vert_rate_source = (msg[8]&0x10) >> 4; 1104 | mm->vert_rate_sign = (msg[8]&0x8) >> 3; 1105 | mm->vert_rate = ((msg[8]&7) << 6) | ((msg[9]&0xfc) >> 2); 1106 | /* Compute velocity and angle from the two speed 1107 | * components. */ 1108 | mm->velocity = sqrt(mm->ns_velocity*mm->ns_velocity+ 1109 | mm->ew_velocity*mm->ew_velocity); 1110 | if (mm->velocity) { 1111 | int ewv = mm->ew_velocity; 1112 | int nsv = mm->ns_velocity; 1113 | double heading; 1114 | 1115 | if (mm->ew_dir) ewv *= -1; 1116 | if (mm->ns_dir) nsv *= -1; 1117 | heading = atan2(ewv,nsv); 1118 | 1119 | /* Convert to degrees. */ 1120 | mm->heading = heading * 360 / (M_PI*2); 1121 | /* We don't want negative values but a 0-360 scale. */ 1122 | if (mm->heading < 0) mm->heading += 360; 1123 | } else { 1124 | mm->heading = 0; 1125 | } 1126 | } else if (mm->mesub == 3 || mm->mesub == 4) { 1127 | mm->heading_is_valid = msg[5] & (1<<2); 1128 | mm->heading = (360.0/128) * (((msg[5] & 3) << 5) | 1129 | (msg[6] >> 3)); 1130 | } 1131 | } 1132 | } 1133 | mm->phase_corrected = 0; /* Set to 1 by the caller if needed. */ 1134 | } 1135 | 1136 | /* This function gets a decoded Mode S Message and prints it on the screen 1137 | * in a human readable format. */ 1138 | void displayModesMessage(struct modesMessage *mm) { 1139 | int j; 1140 | 1141 | /* Handle only addresses mode first. */ 1142 | if (Modes.onlyaddr) { 1143 | printf("%02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1144 | return; 1145 | } 1146 | 1147 | /* Show the raw message. */ 1148 | printf("*"); 1149 | for (j = 0; j < mm->msgbits/8; j++) printf("%02x", mm->msg[j]); 1150 | printf(";\n"); 1151 | 1152 | if (Modes.raw) { 1153 | fflush(stdout); /* Provide data to the reader ASAP. */ 1154 | return; /* Enough for --raw mode */ 1155 | } 1156 | 1157 | printf("CRC: %06x (%s)\n", (int)mm->crc, mm->crcok ? "ok" : "wrong"); 1158 | if (mm->errorbit != -1) 1159 | printf("Single bit error fixed, bit %d\n", mm->errorbit); 1160 | 1161 | if (mm->msgtype == 0) { 1162 | /* DF 0 */ 1163 | printf("DF 0: Short Air-Air Surveillance.\n"); 1164 | printf(" Altitude : %d %s\n", mm->altitude, 1165 | (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); 1166 | printf(" ICAO Address : %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1167 | } else if (mm->msgtype == 4 || mm->msgtype == 20) { 1168 | printf("DF %d: %s, Altitude Reply.\n", mm->msgtype, 1169 | (mm->msgtype == 4) ? "Surveillance" : "Comm-B"); 1170 | printf(" Flight Status : %s\n", fs_str[mm->fs]); 1171 | printf(" DR : %d\n", mm->dr); 1172 | printf(" UM : %d\n", mm->um); 1173 | printf(" Altitude : %d %s\n", mm->altitude, 1174 | (mm->unit == MODES_UNIT_METERS) ? "meters" : "feet"); 1175 | printf(" ICAO Address : %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1176 | 1177 | if (mm->msgtype == 20) { 1178 | /* TODO: 56 bits DF20 MB additional field. */ 1179 | } 1180 | } else if (mm->msgtype == 5 || mm->msgtype == 21) { 1181 | printf("DF %d: %s, Identity Reply.\n", mm->msgtype, 1182 | (mm->msgtype == 5) ? "Surveillance" : "Comm-B"); 1183 | printf(" Flight Status : %s\n", fs_str[mm->fs]); 1184 | printf(" DR : %d\n", mm->dr); 1185 | printf(" UM : %d\n", mm->um); 1186 | printf(" Squawk : %d\n", mm->identity); 1187 | printf(" ICAO Address : %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1188 | 1189 | if (mm->msgtype == 21) { 1190 | /* TODO: 56 bits DF21 MB additional field. */ 1191 | } 1192 | } else if (mm->msgtype == 11) { 1193 | /* DF 11 */ 1194 | printf("DF 11: All Call Reply.\n"); 1195 | printf(" Capability : %s\n", ca_str[mm->ca]); 1196 | printf(" ICAO Address: %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1197 | } else if (mm->msgtype == 17) { 1198 | /* DF 17 */ 1199 | printf("DF 17: ADS-B message.\n"); 1200 | printf(" Capability : %d (%s)\n", mm->ca, ca_str[mm->ca]); 1201 | printf(" ICAO Address : %02x%02x%02x\n", mm->aa1, mm->aa2, mm->aa3); 1202 | printf(" Extended Squitter Type: %d\n", mm->metype); 1203 | printf(" Extended Squitter Sub : %d\n", mm->mesub); 1204 | printf(" Extended Squitter Name: %s\n", 1205 | getMEDescription(mm->metype,mm->mesub)); 1206 | 1207 | /* Decode the extended squitter message. */ 1208 | if (mm->metype >= 1 && mm->metype <= 4) { 1209 | /* Aircraft identification. */ 1210 | char *ac_type_str[4] = { 1211 | "Aircraft Type D", 1212 | "Aircraft Type C", 1213 | "Aircraft Type B", 1214 | "Aircraft Type A" 1215 | }; 1216 | 1217 | printf(" Aircraft Type : %s\n", ac_type_str[mm->aircraft_type]); 1218 | printf(" Identification : %s\n", mm->flight); 1219 | } else if (mm->metype >= 9 && mm->metype <= 18) { 1220 | printf(" F flag : %s\n", mm->fflag ? "odd" : "even"); 1221 | printf(" T flag : %s\n", mm->tflag ? "UTC" : "non-UTC"); 1222 | printf(" Altitude : %d feet\n", mm->altitude); 1223 | printf(" Latitude : %d (not decoded)\n", mm->raw_latitude); 1224 | printf(" Longitude: %d (not decoded)\n", mm->raw_longitude); 1225 | } else if (mm->metype == 19 && mm->mesub >= 1 && mm->mesub <= 4) { 1226 | if (mm->mesub == 1 || mm->mesub == 2) { 1227 | /* Velocity */ 1228 | printf(" EW direction : %d\n", mm->ew_dir); 1229 | printf(" EW velocity : %d\n", mm->ew_velocity); 1230 | printf(" NS direction : %d\n", mm->ns_dir); 1231 | printf(" NS velocity : %d\n", mm->ns_velocity); 1232 | printf(" Vertical rate src : %d\n", mm->vert_rate_source); 1233 | printf(" Vertical rate sign: %d\n", mm->vert_rate_sign); 1234 | printf(" Vertical rate : %d\n", mm->vert_rate); 1235 | } else if (mm->mesub == 3 || mm->mesub == 4) { 1236 | printf(" Heading status: %d", mm->heading_is_valid); 1237 | printf(" Heading: %d", mm->heading); 1238 | } 1239 | } else { 1240 | printf(" Unrecognized ME type: %d subtype: %d\n", 1241 | mm->metype, mm->mesub); 1242 | } 1243 | } else { 1244 | if (Modes.check_crc) 1245 | printf("DF %d with good CRC received " 1246 | "(decoding still not implemented).\n", 1247 | mm->msgtype); 1248 | } 1249 | } 1250 | 1251 | /* Turn I/Q samples pointed by Modes.data into the magnitude vector 1252 | * pointed by Modes.magnitude. */ 1253 | void computeMagnitudeVector(void) { 1254 | uint16_t *m = Modes.magnitude; 1255 | unsigned char *p = Modes.data; 1256 | uint32_t j; 1257 | 1258 | /* Compute the magnitudo vector. It's just SQRT(I^2 + Q^2), but 1259 | * we rescale to the 0-255 range to exploit the full resolution. */ 1260 | for (j = 0; j < Modes.data_len; j += 2) { 1261 | int i = p[j]-127; 1262 | int q = p[j+1]-127; 1263 | 1264 | if (i < 0) i = -i; 1265 | if (q < 0) q = -q; 1266 | m[j/2] = Modes.maglut[i*129+q]; 1267 | } 1268 | } 1269 | 1270 | /* Return -1 if the message is out of fase left-side 1271 | * Return 1 if the message is out of fase right-size 1272 | * Return 0 if the message is not particularly out of phase. 1273 | * 1274 | * Note: this function will access m[-1], so the caller should make sure to 1275 | * call it only if we are not at the start of the current buffer. */ 1276 | int detectOutOfPhase(uint16_t *m) { 1277 | if (m[3] > m[2]/3) return 1; 1278 | if (m[10] > m[9]/3) return 1; 1279 | if (m[6] > m[7]/3) return -1; 1280 | if (m[-1] > m[1]/3) return -1; 1281 | return 0; 1282 | } 1283 | 1284 | /* This function does not really correct the phase of the message, it just 1285 | * applies a transformation to the first sample representing a given bit: 1286 | * 1287 | * If the previous bit was one, we amplify it a bit. 1288 | * If the previous bit was zero, we decrease it a bit. 1289 | * 1290 | * This simple transformation makes the message a bit more likely to be 1291 | * correctly decoded for out of phase messages: 1292 | * 1293 | * When messages are out of phase there is more uncertainty in 1294 | * sequences of the same bit multiple times, since 11111 will be 1295 | * transmitted as continuously altering magnitude (high, low, high, low...) 1296 | * 1297 | * However because the message is out of phase some part of the high 1298 | * is mixed in the low part, so that it is hard to distinguish if it is 1299 | * a zero or a one. 1300 | * 1301 | * However when the message is out of phase passing from 0 to 1 or from 1302 | * 1 to 0 happens in a very recognizable way, for instance in the 0 -> 1 1303 | * transition, magnitude goes low, high, high, low, and one of of the 1304 | * two middle samples the high will be *very* high as part of the previous 1305 | * or next high signal will be mixed there. 1306 | * 1307 | * Applying our simple transformation we make more likely if the current 1308 | * bit is a zero, to detect another zero. Symmetrically if it is a one 1309 | * it will be more likely to detect a one because of the transformation. 1310 | * In this way similar levels will be interpreted more likely in the 1311 | * correct way. */ 1312 | void applyPhaseCorrection(uint16_t *m) { 1313 | int j; 1314 | 1315 | m += 16; /* Skip preamble. */ 1316 | for (j = 0; j < (MODES_LONG_MSG_BITS-1)*2; j += 2) { 1317 | if (m[j] > m[j+1]) { 1318 | /* One */ 1319 | m[j+2] = (m[j+2] * 5) / 4; 1320 | } else { 1321 | /* Zero */ 1322 | m[j+2] = (m[j+2] * 4) / 5; 1323 | } 1324 | } 1325 | } 1326 | 1327 | /* Detect a Mode S messages inside the magnitude buffer pointed by 'm' and of 1328 | * size 'mlen' bytes. Every detected Mode S message is convert it into a 1329 | * stream of bits and passed to the function to display it. */ 1330 | void detectModeS(uint16_t *m, uint32_t mlen) { 1331 | unsigned char bits[MODES_LONG_MSG_BITS]; 1332 | unsigned char msg[MODES_LONG_MSG_BITS/2]; 1333 | uint16_t aux[MODES_LONG_MSG_BITS*2]; 1334 | uint32_t j; 1335 | int use_correction = 0; 1336 | 1337 | /* The Mode S preamble is made of impulses of 0.5 microseconds at 1338 | * the following time offsets: 1339 | * 1340 | * 0 - 0.5 usec: first impulse. 1341 | * 1.0 - 1.5 usec: second impulse. 1342 | * 3.5 - 4 usec: third impulse. 1343 | * 4.5 - 5 usec: last impulse. 1344 | * 1345 | * Since we are sampling at 2 Mhz every sample in our magnitude vector 1346 | * is 0.5 usec, so the preamble will look like this, assuming there is 1347 | * an impulse at offset 0 in the array: 1348 | * 1349 | * 0 ----------------- 1350 | * 1 - 1351 | * 2 ------------------ 1352 | * 3 -- 1353 | * 4 - 1354 | * 5 -- 1355 | * 6 - 1356 | * 7 ------------------ 1357 | * 8 -- 1358 | * 9 ------------------- 1359 | */ 1360 | for (j = 0; j < mlen - MODES_FULL_LEN*2; j++) { 1361 | int low, high, delta, i, errors; 1362 | int good_message = 0; 1363 | 1364 | if (use_correction) goto good_preamble; /* We already checked it. */ 1365 | 1366 | /* First check of relations between the first 10 samples 1367 | * representing a valid preamble. We don't even investigate further 1368 | * if this simple test is not passed. */ 1369 | if (!(m[j] > m[j+1] && 1370 | m[j+1] < m[j+2] && 1371 | m[j+2] > m[j+3] && 1372 | m[j+3] < m[j] && 1373 | m[j+4] < m[j] && 1374 | m[j+5] < m[j] && 1375 | m[j+6] < m[j] && 1376 | m[j+7] > m[j+8] && 1377 | m[j+8] < m[j+9] && 1378 | m[j+9] > m[j+6])) 1379 | { 1380 | if (Modes.debug & MODES_DEBUG_NOPREAMBLE && 1381 | m[j] > MODES_DEBUG_NOPREAMBLE_LEVEL) 1382 | dumpRawMessage("Unexpected ratio among first 10 samples", 1383 | msg, m, j); 1384 | continue; 1385 | } 1386 | 1387 | /* The samples between the two spikes must be < than the average 1388 | * of the high spikes level. We don't test bits too near to 1389 | * the high levels as signals can be out of phase so part of the 1390 | * energy can be in the near samples. */ 1391 | high = (m[j]+m[j+2]+m[j+7]+m[j+9])/6; 1392 | if (m[j+4] >= high || 1393 | m[j+5] >= high) 1394 | { 1395 | if (Modes.debug & MODES_DEBUG_NOPREAMBLE && 1396 | m[j] > MODES_DEBUG_NOPREAMBLE_LEVEL) 1397 | dumpRawMessage( 1398 | "Too high level in samples between 3 and 6", 1399 | msg, m, j); 1400 | continue; 1401 | } 1402 | 1403 | /* Similarly samples in the range 11-14 must be low, as it is the 1404 | * space between the preamble and real data. Again we don't test 1405 | * bits too near to high levels, see above. */ 1406 | if (m[j+11] >= high || 1407 | m[j+12] >= high || 1408 | m[j+13] >= high || 1409 | m[j+14] >= high) 1410 | { 1411 | if (Modes.debug & MODES_DEBUG_NOPREAMBLE && 1412 | m[j] > MODES_DEBUG_NOPREAMBLE_LEVEL) 1413 | dumpRawMessage( 1414 | "Too high level in samples between 10 and 15", 1415 | msg, m, j); 1416 | continue; 1417 | } 1418 | Modes.stat_valid_preamble++; 1419 | 1420 | good_preamble: 1421 | /* If the previous attempt with this message failed, retry using 1422 | * magnitude correction. */ 1423 | if (use_correction) { 1424 | memcpy(aux,m+j+MODES_PREAMBLE_US*2,sizeof(aux)); 1425 | if (j && detectOutOfPhase(m+j)) { 1426 | applyPhaseCorrection(m+j); 1427 | Modes.stat_out_of_phase++; 1428 | } 1429 | /* TODO ... apply other kind of corrections. */ 1430 | } 1431 | 1432 | /* Decode all the next 112 bits, regardless of the actual message 1433 | * size. We'll check the actual message type later. */ 1434 | errors = 0; 1435 | for (i = 0; i < MODES_LONG_MSG_BITS*2; i += 2) { 1436 | low = m[j+i+MODES_PREAMBLE_US*2]; 1437 | high = m[j+i+MODES_PREAMBLE_US*2+1]; 1438 | delta = low-high; 1439 | if (delta < 0) delta = -delta; 1440 | 1441 | if (i > 0 && delta < 256) { 1442 | bits[i/2] = bits[i/2-1]; 1443 | } else if (low == high) { 1444 | /* Checking if two adiacent samples have the same magnitude 1445 | * is an effective way to detect if it's just random noise 1446 | * that was detected as a valid preamble. */ 1447 | bits[i/2] = 2; /* error */ 1448 | if (i < MODES_SHORT_MSG_BITS*2) errors++; 1449 | } else if (low > high) { 1450 | bits[i/2] = 1; 1451 | } else { 1452 | /* (low < high) for exclusion */ 1453 | bits[i/2] = 0; 1454 | } 1455 | } 1456 | 1457 | /* Restore the original message if we used magnitude correction. */ 1458 | if (use_correction) 1459 | memcpy(m+j+MODES_PREAMBLE_US*2,aux,sizeof(aux)); 1460 | 1461 | /* Pack bits into bytes */ 1462 | for (i = 0; i < MODES_LONG_MSG_BITS; i += 8) { 1463 | msg[i/8] = 1464 | bits[i]<<7 | 1465 | bits[i+1]<<6 | 1466 | bits[i+2]<<5 | 1467 | bits[i+3]<<4 | 1468 | bits[i+4]<<3 | 1469 | bits[i+5]<<2 | 1470 | bits[i+6]<<1 | 1471 | bits[i+7]; 1472 | } 1473 | 1474 | int msgtype = msg[0]>>3; 1475 | int msglen = modesMessageLenByType(msgtype)/8; 1476 | 1477 | /* Last check, high and low bits are different enough in magnitude 1478 | * to mark this as real message and not just noise? */ 1479 | delta = 0; 1480 | for (i = 0; i < msglen*8*2; i += 2) { 1481 | delta += abs(m[j+i+MODES_PREAMBLE_US*2]- 1482 | m[j+i+MODES_PREAMBLE_US*2+1]); 1483 | } 1484 | delta /= msglen*4; 1485 | 1486 | /* Filter for an average delta of three is small enough to let almost 1487 | * every kind of message to pass, but high enough to filter some 1488 | * random noise. */ 1489 | if (delta < 10*255) { 1490 | use_correction = 0; 1491 | continue; 1492 | } 1493 | 1494 | /* If we reached this point, and error is zero, we are very likely 1495 | * with a Mode S message in our hands, but it may still be broken 1496 | * and CRC may not be correct. This is handled by the next layer. */ 1497 | if (errors == 0 || (Modes.aggressive && errors < 3)) { 1498 | struct modesMessage mm; 1499 | 1500 | /* Decode the received message and update statistics */ 1501 | decodeModesMessage(&mm,msg); 1502 | 1503 | /* Update statistics. */ 1504 | if (mm.crcok || use_correction) { 1505 | if (errors == 0) Modes.stat_demodulated++; 1506 | if (mm.errorbit == -1) { 1507 | if (mm.crcok) 1508 | Modes.stat_goodcrc++; 1509 | else 1510 | Modes.stat_badcrc++; 1511 | } else { 1512 | Modes.stat_badcrc++; 1513 | Modes.stat_fixed++; 1514 | if (mm.errorbit < MODES_LONG_MSG_BITS) 1515 | Modes.stat_single_bit_fix++; 1516 | else 1517 | Modes.stat_two_bits_fix++; 1518 | } 1519 | } 1520 | 1521 | /* Output debug mode info if needed. */ 1522 | if (use_correction == 0) { 1523 | if (Modes.debug & MODES_DEBUG_DEMOD) 1524 | dumpRawMessage("Demodulated with 0 errors", msg, m, j); 1525 | else if (Modes.debug & MODES_DEBUG_BADCRC && 1526 | mm.msgtype == 17 && 1527 | (!mm.crcok || mm.errorbit != -1)) 1528 | dumpRawMessage("Decoded with bad CRC", msg, m, j); 1529 | else if (Modes.debug & MODES_DEBUG_GOODCRC && mm.crcok && 1530 | mm.errorbit == -1) 1531 | dumpRawMessage("Decoded with good CRC", msg, m, j); 1532 | } 1533 | 1534 | /* Skip this message if we are sure it's fine. */ 1535 | if (mm.crcok) { 1536 | j += (MODES_PREAMBLE_US+(msglen*8))*2; 1537 | good_message = 1; 1538 | if (use_correction) 1539 | mm.phase_corrected = 1; 1540 | } 1541 | 1542 | /* Pass data to the next layer */ 1543 | useModesMessage(&mm); 1544 | } else { 1545 | if (Modes.debug & MODES_DEBUG_DEMODERR && use_correction) { 1546 | printf("The following message has %d demod errors\n", errors); 1547 | dumpRawMessage("Demodulated with errors", msg, m, j); 1548 | } 1549 | } 1550 | 1551 | /* Retry with phase correction if possible. */ 1552 | if (!good_message && !use_correction) { 1553 | j--; 1554 | use_correction = 1; 1555 | } else { 1556 | use_correction = 0; 1557 | } 1558 | } 1559 | } 1560 | 1561 | /* When a new message is available, because it was decoded from the 1562 | * RTL device, file, or received in the TCP input port, or any other 1563 | * way we can receive a decoded message, we call this function in order 1564 | * to use the message. 1565 | * 1566 | * Basically this function passes a raw message to the upper layers for 1567 | * further processing and visualization. */ 1568 | void useModesMessage(struct modesMessage *mm) { 1569 | if (!Modes.stats && (Modes.check_crc == 0 || mm->crcok)) { 1570 | /* Track aircrafts in interactive mode or if the HTTP 1571 | * interface is enabled. */ 1572 | if (Modes.interactive || Modes.stat_http_requests > 0 || Modes.stat_sbs_connections > 0) { 1573 | struct aircraft *a = interactiveReceiveData(mm); 1574 | if (a && Modes.stat_sbs_connections > 0) modesSendSBSOutput(mm, a); /* Feed SBS output clients. */ 1575 | } 1576 | /* In non-interactive way, display messages on standard output. */ 1577 | if (!Modes.interactive) { 1578 | displayModesMessage(mm); 1579 | if (!Modes.raw && !Modes.onlyaddr) printf("\n"); 1580 | } 1581 | /* Send data to connected clients. */ 1582 | if (Modes.net) { 1583 | modesSendRawOutput(mm); /* Feed raw output clients. */ 1584 | } 1585 | } 1586 | } 1587 | 1588 | /* ========================= Interactive mode =============================== */ 1589 | 1590 | /* Return a new aircraft structure for the interactive mode linked list 1591 | * of aircrafts. */ 1592 | struct aircraft *interactiveCreateAircraft(uint32_t addr) { 1593 | struct aircraft *a = malloc(sizeof(*a)); 1594 | 1595 | a->addr = addr; 1596 | snprintf(a->hexaddr,sizeof(a->hexaddr),"%06x",(int)addr); 1597 | a->flight[0] = '\0'; 1598 | a->altitude = 0; 1599 | a->speed = 0; 1600 | a->track = 0; 1601 | a->odd_cprlat = 0; 1602 | a->odd_cprlon = 0; 1603 | a->odd_cprtime = 0; 1604 | a->even_cprlat = 0; 1605 | a->even_cprlon = 0; 1606 | a->even_cprtime = 0; 1607 | a->lat = 0; 1608 | a->lon = 0; 1609 | a->seen = time(NULL); 1610 | a->messages = 0; 1611 | a->next = NULL; 1612 | return a; 1613 | } 1614 | 1615 | /* Return the aircraft with the specified address, or NULL if no aircraft 1616 | * exists with this address. */ 1617 | struct aircraft *interactiveFindAircraft(uint32_t addr) { 1618 | struct aircraft *a = Modes.aircrafts; 1619 | 1620 | while(a) { 1621 | if (a->addr == addr) return a; 1622 | a = a->next; 1623 | } 1624 | return NULL; 1625 | } 1626 | 1627 | /* Always positive MOD operation, used for CPR decoding. */ 1628 | int cprModFunction(int a, int b) { 1629 | int res = a % b; 1630 | if (res < 0) res += b; 1631 | return res; 1632 | } 1633 | 1634 | /* The NL function uses the precomputed table from 1090-WP-9-14 */ 1635 | int cprNLFunction(double lat) { 1636 | if (lat < 0) lat = -lat; /* Table is simmetric about the equator. */ 1637 | if (lat < 10.47047130) return 59; 1638 | if (lat < 14.82817437) return 58; 1639 | if (lat < 18.18626357) return 57; 1640 | if (lat < 21.02939493) return 56; 1641 | if (lat < 23.54504487) return 55; 1642 | if (lat < 25.82924707) return 54; 1643 | if (lat < 27.93898710) return 53; 1644 | if (lat < 29.91135686) return 52; 1645 | if (lat < 31.77209708) return 51; 1646 | if (lat < 33.53993436) return 50; 1647 | if (lat < 35.22899598) return 49; 1648 | if (lat < 36.85025108) return 48; 1649 | if (lat < 38.41241892) return 47; 1650 | if (lat < 39.92256684) return 46; 1651 | if (lat < 41.38651832) return 45; 1652 | if (lat < 42.80914012) return 44; 1653 | if (lat < 44.19454951) return 43; 1654 | if (lat < 45.54626723) return 42; 1655 | if (lat < 46.86733252) return 41; 1656 | if (lat < 48.16039128) return 40; 1657 | if (lat < 49.42776439) return 39; 1658 | if (lat < 50.67150166) return 38; 1659 | if (lat < 51.89342469) return 37; 1660 | if (lat < 53.09516153) return 36; 1661 | if (lat < 54.27817472) return 35; 1662 | if (lat < 55.44378444) return 34; 1663 | if (lat < 56.59318756) return 33; 1664 | if (lat < 57.72747354) return 32; 1665 | if (lat < 58.84763776) return 31; 1666 | if (lat < 59.95459277) return 30; 1667 | if (lat < 61.04917774) return 29; 1668 | if (lat < 62.13216659) return 28; 1669 | if (lat < 63.20427479) return 27; 1670 | if (lat < 64.26616523) return 26; 1671 | if (lat < 65.31845310) return 25; 1672 | if (lat < 66.36171008) return 24; 1673 | if (lat < 67.39646774) return 23; 1674 | if (lat < 68.42322022) return 22; 1675 | if (lat < 69.44242631) return 21; 1676 | if (lat < 70.45451075) return 20; 1677 | if (lat < 71.45986473) return 19; 1678 | if (lat < 72.45884545) return 18; 1679 | if (lat < 73.45177442) return 17; 1680 | if (lat < 74.43893416) return 16; 1681 | if (lat < 75.42056257) return 15; 1682 | if (lat < 76.39684391) return 14; 1683 | if (lat < 77.36789461) return 13; 1684 | if (lat < 78.33374083) return 12; 1685 | if (lat < 79.29428225) return 11; 1686 | if (lat < 80.24923213) return 10; 1687 | if (lat < 81.19801349) return 9; 1688 | if (lat < 82.13956981) return 8; 1689 | if (lat < 83.07199445) return 7; 1690 | if (lat < 83.99173563) return 6; 1691 | if (lat < 84.89166191) return 5; 1692 | if (lat < 85.75541621) return 4; 1693 | if (lat < 86.53536998) return 3; 1694 | if (lat < 87.00000000) return 2; 1695 | else return 1; 1696 | } 1697 | 1698 | int cprNFunction(double lat, int isodd) { 1699 | int nl = cprNLFunction(lat) - isodd; 1700 | if (nl < 1) nl = 1; 1701 | return nl; 1702 | } 1703 | 1704 | double cprDlonFunction(double lat, int isodd) { 1705 | return 360.0 / cprNFunction(lat, isodd); 1706 | } 1707 | 1708 | /* This algorithm comes from: 1709 | * http://www.lll.lu/~edward/edward/adsb/DecodingADSBposition.html. 1710 | * 1711 | * 1712 | * A few remarks: 1713 | * 1) 131072 is 2^17 since CPR latitude and longitude are encoded in 17 bits. 1714 | * 2) We assume that we always received the odd packet as last packet for 1715 | * simplicity. This may provide a position that is less fresh of a few 1716 | * seconds. 1717 | */ 1718 | void decodeCPR(struct aircraft *a) { 1719 | const double AirDlat0 = 360.0 / 60; 1720 | const double AirDlat1 = 360.0 / 59; 1721 | double lat0 = a->even_cprlat; 1722 | double lat1 = a->odd_cprlat; 1723 | double lon0 = a->even_cprlon; 1724 | double lon1 = a->odd_cprlon; 1725 | 1726 | /* Compute the Latitude Index "j" */ 1727 | int j = floor(((59*lat0 - 60*lat1) / 131072) + 0.5); 1728 | double rlat0 = AirDlat0 * (cprModFunction(j,60) + lat0 / 131072); 1729 | double rlat1 = AirDlat1 * (cprModFunction(j,59) + lat1 / 131072); 1730 | 1731 | if (rlat0 >= 270) rlat0 -= 360; 1732 | if (rlat1 >= 270) rlat1 -= 360; 1733 | 1734 | /* Check that both are in the same latitude zone, or abort. */ 1735 | if (cprNLFunction(rlat0) != cprNLFunction(rlat1)) return; 1736 | 1737 | /* Compute ni and the longitude index m */ 1738 | if (a->even_cprtime > a->odd_cprtime) { 1739 | /* Use even packet. */ 1740 | int ni = cprNFunction(rlat0,0); 1741 | int m = floor((((lon0 * (cprNLFunction(rlat0)-1)) - 1742 | (lon1 * cprNLFunction(rlat0))) / 131072) + 0.5); 1743 | a->lon = cprDlonFunction(rlat0,0) * (cprModFunction(m,ni)+lon0/131072); 1744 | a->lat = rlat0; 1745 | } else { 1746 | /* Use odd packet. */ 1747 | int ni = cprNFunction(rlat1,1); 1748 | int m = floor((((lon0 * (cprNLFunction(rlat1)-1)) - 1749 | (lon1 * cprNLFunction(rlat1))) / 131072.0) + 0.5); 1750 | a->lon = cprDlonFunction(rlat1,1) * (cprModFunction(m,ni)+lon1/131072); 1751 | a->lat = rlat1; 1752 | } 1753 | if (a->lon > 180) a->lon -= 360; 1754 | } 1755 | 1756 | /* Receive new messages and populate the interactive mode with more info. */ 1757 | struct aircraft *interactiveReceiveData(struct modesMessage *mm) { 1758 | uint32_t addr; 1759 | struct aircraft *a, *aux; 1760 | 1761 | if (Modes.check_crc && mm->crcok == 0) return NULL; 1762 | addr = (mm->aa1 << 16) | (mm->aa2 << 8) | mm->aa3; 1763 | 1764 | /* Loookup our aircraft or create a new one. */ 1765 | a = interactiveFindAircraft(addr); 1766 | if (!a) { 1767 | a = interactiveCreateAircraft(addr); 1768 | a->next = Modes.aircrafts; 1769 | Modes.aircrafts = a; 1770 | } else { 1771 | /* If it is an already known aircraft, move it on head 1772 | * so we keep aircrafts ordered by received message time. 1773 | * 1774 | * However move it on head only if at least one second elapsed 1775 | * since the aircraft that is currently on head sent a message, 1776 | * othewise with multiple aircrafts at the same time we have an 1777 | * useless shuffle of positions on the screen. */ 1778 | if (0 && Modes.aircrafts != a && (time(NULL) - a->seen) >= 1) { 1779 | aux = Modes.aircrafts; 1780 | while(aux->next != a) aux = aux->next; 1781 | /* Now we are a node before the aircraft to remove. */ 1782 | aux->next = aux->next->next; /* removed. */ 1783 | /* Add on head */ 1784 | a->next = Modes.aircrafts; 1785 | Modes.aircrafts = a; 1786 | } 1787 | } 1788 | 1789 | a->seen = time(NULL); 1790 | a->messages++; 1791 | 1792 | if (mm->msgtype == 0 || mm->msgtype == 4 || mm->msgtype == 20) { 1793 | a->altitude = mm->altitude; 1794 | } else if (mm->msgtype == 17) { 1795 | if (mm->metype >= 1 && mm->metype <= 4) { 1796 | memcpy(a->flight, mm->flight, sizeof(a->flight)); 1797 | } else if (mm->metype >= 9 && mm->metype <= 18) { 1798 | a->altitude = mm->altitude; 1799 | if (mm->fflag) { 1800 | a->odd_cprlat = mm->raw_latitude; 1801 | a->odd_cprlon = mm->raw_longitude; 1802 | a->odd_cprtime = mstime(); 1803 | } else { 1804 | a->even_cprlat = mm->raw_latitude; 1805 | a->even_cprlon = mm->raw_longitude; 1806 | a->even_cprtime = mstime(); 1807 | } 1808 | /* If the two data is less than 10 seconds apart, compute 1809 | * the position. */ 1810 | if (llabs(a->even_cprtime - a->odd_cprtime) <= 10000) { 1811 | decodeCPR(a); 1812 | } 1813 | } else if (mm->metype == 19) { 1814 | if (mm->mesub == 1 || mm->mesub == 2) { 1815 | a->speed = mm->velocity; 1816 | a->track = mm->heading; 1817 | } 1818 | } 1819 | } 1820 | return a; 1821 | } 1822 | 1823 | /* Show the currently captured interactive data on screen. */ 1824 | void interactiveShowData(void) { 1825 | struct aircraft *a = Modes.aircrafts; 1826 | time_t now = time(NULL); 1827 | char progress[4]; 1828 | int count = 0; 1829 | 1830 | memset(progress,' ',3); 1831 | progress[time(NULL)%3] = '.'; 1832 | progress[3] = '\0'; 1833 | 1834 | printf("\x1b[H\x1b[2J"); /* Clear the screen */ 1835 | printf( 1836 | "Hex Flight Altitude Speed Lat Lon Track Messages Seen %s\n" 1837 | "--------------------------------------------------------------------------------\n", 1838 | progress); 1839 | 1840 | while(a && count < Modes.interactive_rows) { 1841 | int altitude = a->altitude, speed = a->speed; 1842 | 1843 | /* Convert units to metric if --metric was specified. */ 1844 | if (Modes.metric) { 1845 | altitude /= 3.2828; 1846 | speed *= 1.852; 1847 | } 1848 | 1849 | printf("%-6s %-8s %-9d %-7d %-7.03f %-7.03f %-3d %-9ld %d sec\n", 1850 | a->hexaddr, a->flight, altitude, speed, 1851 | a->lat, a->lon, a->track, a->messages, 1852 | (int)(now - a->seen)); 1853 | a = a->next; 1854 | count++; 1855 | } 1856 | } 1857 | 1858 | /* When in interactive mode If we don't receive new nessages within 1859 | * MODES_INTERACTIVE_TTL seconds we remove the aircraft from the list. */ 1860 | void interactiveRemoveStaleAircrafts(void) { 1861 | struct aircraft *a = Modes.aircrafts; 1862 | struct aircraft *prev = NULL; 1863 | time_t now = time(NULL); 1864 | 1865 | while(a) { 1866 | if ((now - a->seen) > Modes.interactive_ttl) { 1867 | struct aircraft *next = a->next; 1868 | /* Remove the element from the linked list, with care 1869 | * if we are removing the first element. */ 1870 | free(a); 1871 | if (!prev) 1872 | Modes.aircrafts = next; 1873 | else 1874 | prev->next = next; 1875 | a = next; 1876 | } else { 1877 | prev = a; 1878 | a = a->next; 1879 | } 1880 | } 1881 | } 1882 | 1883 | /* ============================== Snip mode ================================= */ 1884 | 1885 | /* Get raw IQ samples and filter everything is < than the specified level 1886 | * for more than 256 samples in order to reduce example file size. */ 1887 | void snipMode(int level) { 1888 | int i, q; 1889 | long long c = 0; 1890 | 1891 | while ((i = getchar()) != EOF && (q = getchar()) != EOF) { 1892 | if (abs(i-127) < level && abs(q-127) < level) { 1893 | c++; 1894 | if (c > MODES_PREAMBLE_US*4) continue; 1895 | } else { 1896 | c = 0; 1897 | } 1898 | putchar(i); 1899 | putchar(q); 1900 | } 1901 | } 1902 | 1903 | /* ============================= Networking ================================= 1904 | * Note: here we risregard any kind of good coding practice in favor of 1905 | * extreme simplicity, that is: 1906 | * 1907 | * 1) We only rely on the kernel buffers for our I/O without any kind of 1908 | * user space buffering. 1909 | * 2) We don't register any kind of event handler, from time to time a 1910 | * function gets called and we accept new connections. All the rest is 1911 | * handled via non-blocking I/O and manually pulling clients to see if 1912 | * they have something new to share with us when reading is needed. 1913 | */ 1914 | 1915 | #define MODES_NET_SERVICE_RAWO 0 1916 | #define MODES_NET_SERVICE_RAWI 1 1917 | #define MODES_NET_SERVICE_HTTP 2 1918 | #define MODES_NET_SERVICE_SBS 3 1919 | #define MODES_NET_SERVICES_NUM 4 1920 | struct { 1921 | char *descr; 1922 | int *socket; 1923 | int port; 1924 | } modesNetServices[MODES_NET_SERVICES_NUM] = { 1925 | {"Raw TCP output", &Modes.ros, MODES_NET_OUTPUT_RAW_PORT}, 1926 | {"Raw TCP input", &Modes.ris, MODES_NET_INPUT_RAW_PORT}, 1927 | {"HTTP server", &Modes.https, MODES_NET_HTTP_PORT}, 1928 | {"Basestation TCP output", &Modes.sbsos, MODES_NET_OUTPUT_SBS_PORT} 1929 | }; 1930 | 1931 | /* Networking "stack" initialization. */ 1932 | void modesInitNet(void) { 1933 | int j; 1934 | 1935 | memset(Modes.clients,0,sizeof(Modes.clients)); 1936 | Modes.maxfd = -1; 1937 | 1938 | for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { 1939 | int s = anetTcpServer(Modes.aneterr, modesNetServices[j].port, NULL); 1940 | if (s == -1) { 1941 | fprintf(stderr, "Error opening the listening port %d (%s): %s\n", 1942 | modesNetServices[j].port, 1943 | modesNetServices[j].descr, 1944 | strerror(errno)); 1945 | exit(1); 1946 | } 1947 | anetNonBlock(Modes.aneterr, s); 1948 | *modesNetServices[j].socket = s; 1949 | } 1950 | 1951 | signal(SIGPIPE, SIG_IGN); 1952 | } 1953 | 1954 | /* This function gets called from time to time when the decoding thread is 1955 | * awakened by new data arriving. This usually happens a few times every 1956 | * second. */ 1957 | void modesAcceptClients(void) { 1958 | int fd, port; 1959 | unsigned int j; 1960 | struct client *c; 1961 | 1962 | for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { 1963 | fd = anetTcpAccept(Modes.aneterr, *modesNetServices[j].socket, 1964 | NULL, &port); 1965 | if (fd == -1) { 1966 | if (Modes.debug & MODES_DEBUG_NET && errno != EAGAIN) 1967 | printf("Accept %d: %s\n", *modesNetServices[j].socket, 1968 | strerror(errno)); 1969 | continue; 1970 | } 1971 | 1972 | if (fd >= MODES_NET_MAX_FD) { 1973 | close(fd); 1974 | return; /* Max number of clients reached. */ 1975 | } 1976 | 1977 | anetNonBlock(Modes.aneterr, fd); 1978 | c = malloc(sizeof(*c)); 1979 | c->service = *modesNetServices[j].socket; 1980 | c->fd = fd; 1981 | c->buflen = 0; 1982 | Modes.clients[fd] = c; 1983 | anetSetSendBuffer(Modes.aneterr,fd,MODES_NET_SNDBUF_SIZE); 1984 | 1985 | if (Modes.maxfd < fd) Modes.maxfd = fd; 1986 | if (*modesNetServices[j].socket == Modes.sbsos) 1987 | Modes.stat_sbs_connections++; 1988 | 1989 | j--; /* Try again with the same listening port. */ 1990 | 1991 | if (Modes.debug & MODES_DEBUG_NET) 1992 | printf("Created new client %d\n", fd); 1993 | } 1994 | } 1995 | 1996 | /* On error free the client, collect the structure, adjust maxfd if needed. */ 1997 | void modesFreeClient(int fd) { 1998 | close(fd); 1999 | free(Modes.clients[fd]); 2000 | Modes.clients[fd] = NULL; 2001 | 2002 | if (Modes.debug & MODES_DEBUG_NET) 2003 | printf("Closing client %d\n", fd); 2004 | 2005 | /* If this was our maxfd, scan the clients array to find the new max. 2006 | * Note that we are sure there is no active fd greater than the closed 2007 | * fd, so we scan from fd-1 to 0. */ 2008 | if (Modes.maxfd == fd) { 2009 | int j; 2010 | 2011 | Modes.maxfd = -1; 2012 | for (j = fd-1; j >= 0; j--) { 2013 | if (Modes.clients[j]) { 2014 | Modes.maxfd = j; 2015 | break; 2016 | } 2017 | } 2018 | } 2019 | } 2020 | 2021 | /* Send the specified message to all clients listening for a given service. */ 2022 | void modesSendAllClients(int service, void *msg, int len) { 2023 | int j; 2024 | struct client *c; 2025 | 2026 | for (j = 0; j <= Modes.maxfd; j++) { 2027 | c = Modes.clients[j]; 2028 | if (c && c->service == service) { 2029 | int nwritten = write(j, msg, len); 2030 | if (nwritten != len) { 2031 | modesFreeClient(j); 2032 | } 2033 | } 2034 | } 2035 | } 2036 | 2037 | /* Write raw output to TCP clients. */ 2038 | void modesSendRawOutput(struct modesMessage *mm) { 2039 | char msg[128], *p = msg; 2040 | int j; 2041 | 2042 | *p++ = '*'; 2043 | for (j = 0; j < mm->msgbits/8; j++) { 2044 | sprintf(p, "%02X", mm->msg[j]); 2045 | p += 2; 2046 | } 2047 | *p++ = ';'; 2048 | *p++ = '\n'; 2049 | modesSendAllClients(Modes.ros, msg, p-msg); 2050 | } 2051 | 2052 | 2053 | /* Write SBS output to TCP clients. */ 2054 | void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) { 2055 | char msg[256], *p = msg; 2056 | int emergency = 0, ground = 0, alert = 0, spi = 0; 2057 | 2058 | if (mm->msgtype == 4 || mm->msgtype == 5 || mm->msgtype == 21) { 2059 | /* Node: identity is calculated/kept in base10 but is actually 2060 | * octal (07500 is represented as 7500) */ 2061 | if (mm->identity == 7500 || mm->identity == 7600 || 2062 | mm->identity == 7700) emergency = -1; 2063 | if (mm->fs == 1 || mm->fs == 3) ground = -1; 2064 | if (mm->fs == 2 || mm->fs == 3 || mm->fs == 4) alert = -1; 2065 | if (mm->fs == 4 || mm->fs == 5) spi = -1; 2066 | } 2067 | 2068 | if (mm->msgtype == 0) { 2069 | p += sprintf(p, "MSG,5,,,%02X%02X%02X,,,,,,,%d,,,,,,,,,,", 2070 | mm->aa1, mm->aa2, mm->aa3, mm->altitude); 2071 | } else if (mm->msgtype == 4) { 2072 | p += sprintf(p, "MSG,5,,,%02X%02X%02X,,,,,,,%d,,,,,,,%d,%d,%d,%d", 2073 | mm->aa1, mm->aa2, mm->aa3, mm->altitude, alert, emergency, spi, ground); 2074 | } else if (mm->msgtype == 5) { 2075 | p += sprintf(p, "MSG,6,,,%02X%02X%02X,,,,,,,,,,,,,%d,%d,%d,%d,%d", 2076 | mm->aa1, mm->aa2, mm->aa3, mm->identity, alert, emergency, spi, ground); 2077 | } else if (mm->msgtype == 11) { 2078 | p += sprintf(p, "MSG,8,,,%02X%02X%02X,,,,,,,,,,,,,,,,,", 2079 | mm->aa1, mm->aa2, mm->aa3); 2080 | } else if (mm->msgtype == 17 && mm->metype == 4) { 2081 | p += sprintf(p, "MSG,1,,,%02X%02X%02X,,,,,,%s,,,,,,,,0,0,0,0", 2082 | mm->aa1, mm->aa2, mm->aa3, mm->flight); 2083 | } else if (mm->msgtype == 17 && mm->metype >= 9 && mm->metype <= 18) { 2084 | if (a->lat == 0 && a->lon == 0) 2085 | p += sprintf(p, "MSG,3,,,%02X%02X%02X,,,,,,,%d,,,,,,,0,0,0,0", 2086 | mm->aa1, mm->aa2, mm->aa3, mm->altitude); 2087 | else 2088 | p += sprintf(p, "MSG,3,,,%02X%02X%02X,,,,,,,%d,,,%1.5f,%1.5f,,," 2089 | "0,0,0,0", 2090 | mm->aa1, mm->aa2, mm->aa3, mm->altitude, a->lat, a->lon); 2091 | } else if (mm->msgtype == 17 && mm->metype == 19 && mm->mesub == 1) { 2092 | int vr = (mm->vert_rate_sign==0?1:-1) * (mm->vert_rate-1) * 64; 2093 | 2094 | p += sprintf(p, "MSG,4,,,%02X%02X%02X,,,,,,,,%d,%d,,,%i,,0,0,0,0", 2095 | mm->aa1, mm->aa2, mm->aa3, a->speed, a->track, vr); 2096 | } else if (mm->msgtype == 21) { 2097 | p += sprintf(p, "MSG,6,,,%02X%02X%02X,,,,,,,,,,,,,%d,%d,%d,%d,%d", 2098 | mm->aa1, mm->aa2, mm->aa3, mm->identity, alert, emergency, spi, ground); 2099 | } else { 2100 | return; 2101 | } 2102 | 2103 | *p++ = '\n'; 2104 | modesSendAllClients(Modes.sbsos, msg, p-msg); 2105 | } 2106 | 2107 | /* Turn an hex digit into its 4 bit decimal value. 2108 | * Returns -1 if the digit is not in the 0-F range. */ 2109 | int hexDigitVal(int c) { 2110 | c = tolower(c); 2111 | if (c >= '0' && c <= '9') return c-'0'; 2112 | else if (c >= 'a' && c <= 'f') return c-'a'+10; 2113 | else return -1; 2114 | } 2115 | 2116 | /* This function decodes a string representing a Mode S message in 2117 | * raw hex format like: *8D4B969699155600E87406F5B69F; 2118 | * The string is supposed to be at the start of the client buffer 2119 | * and null-terminated. 2120 | * 2121 | * The message is passed to the higher level layers, so it feeds 2122 | * the selected screen output, the network output and so forth. 2123 | * 2124 | * If the message looks invalid is silently discarded. 2125 | * 2126 | * The function always returns 0 (success) to the caller as there is 2127 | * no case where we want broken messages here to close the client 2128 | * connection. */ 2129 | int decodeHexMessage(struct client *c) { 2130 | char *hex = c->buf; 2131 | int l = strlen(hex), j; 2132 | unsigned char msg[MODES_LONG_MSG_BYTES]; 2133 | struct modesMessage mm; 2134 | 2135 | /* Remove spaces on the left and on the right. */ 2136 | while(l && isspace(hex[l-1])) { 2137 | hex[l-1] = '\0'; 2138 | l--; 2139 | } 2140 | while(isspace(*hex)) { 2141 | hex++; 2142 | l--; 2143 | } 2144 | 2145 | /* Turn the message into binary. */ 2146 | if (l < 2 || hex[0] != '*' || hex[l-1] != ';') return 0; 2147 | hex++; l-=2; /* Skip * and ; */ 2148 | if (l > MODES_LONG_MSG_BYTES*2) return 0; /* Too long message... broken. */ 2149 | for (j = 0; j < l; j += 2) { 2150 | int high = hexDigitVal(hex[j]); 2151 | int low = hexDigitVal(hex[j+1]); 2152 | 2153 | if (high == -1 || low == -1) return 0; 2154 | msg[j/2] = (high<<4) | low; 2155 | } 2156 | decodeModesMessage(&mm,msg); 2157 | useModesMessage(&mm); 2158 | return 0; 2159 | } 2160 | 2161 | /* Return a description of planes in json. */ 2162 | char *aircraftsToJson(int *len) { 2163 | struct aircraft *a = Modes.aircrafts; 2164 | int buflen = 1024; /* The initial buffer is incremented as needed. */ 2165 | char *buf = malloc(buflen), *p = buf; 2166 | int l; 2167 | 2168 | l = snprintf(p,buflen,"[\n"); 2169 | p += l; buflen -= l; 2170 | while(a) { 2171 | int altitude = a->altitude, speed = a->speed; 2172 | 2173 | /* Convert units to metric if --metric was specified. */ 2174 | if (Modes.metric) { 2175 | altitude /= 3.2828; 2176 | speed *= 1.852; 2177 | } 2178 | 2179 | if (a->lat != 0 && a->lon != 0) { 2180 | l = snprintf(p,buflen, 2181 | "{\"hex\":\"%s\", \"flight\":\"%s\", \"lat\":%f, " 2182 | "\"lon\":%f, \"altitude\":%d, \"track\":%d, " 2183 | "\"speed\":%d},\n", 2184 | a->hexaddr, a->flight, a->lat, a->lon, altitude, a->track, 2185 | speed); 2186 | p += l; buflen -= l; 2187 | /* Resize if needed. */ 2188 | if (buflen < 256) { 2189 | int used = p-buf; 2190 | buflen += 1024; /* Our increment. */ 2191 | buf = realloc(buf,used+buflen); 2192 | p = buf+used; 2193 | } 2194 | } 2195 | a = a->next; 2196 | } 2197 | /* Remove the final comma if any, and closes the json array. */ 2198 | if (*(p-2) == ',') { 2199 | *(p-2) = '\n'; 2200 | p--; 2201 | buflen++; 2202 | } 2203 | l = snprintf(p,buflen,"]\n"); 2204 | p += l; buflen -= l; 2205 | 2206 | *len = p-buf; 2207 | return buf; 2208 | } 2209 | 2210 | #define MODES_CONTENT_TYPE_HTML "text/html;charset=utf-8" 2211 | #define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8" 2212 | 2213 | /* Get an HTTP request header and write the response to the client. 2214 | * Again here we assume that the socket buffer is enough without doing 2215 | * any kind of userspace buffering. 2216 | * 2217 | * Returns 1 on error to signal the caller the client connection should 2218 | * be closed. */ 2219 | int handleHTTPRequest(struct client *c) { 2220 | char hdr[512]; 2221 | int clen, hdrlen; 2222 | int httpver, keepalive; 2223 | char *p, *url, *content; 2224 | char *ctype; 2225 | 2226 | if (Modes.debug & MODES_DEBUG_NET) 2227 | printf("\nHTTP request: %s\n", c->buf); 2228 | 2229 | /* Minimally parse the request. */ 2230 | httpver = (strstr(c->buf, "HTTP/1.1") != NULL) ? 11 : 10; 2231 | if (httpver == 10) { 2232 | /* HTTP 1.0 defaults to close, unless otherwise specified. */ 2233 | keepalive = strstr(c->buf, "Connection: keep-alive") != NULL; 2234 | } else if (httpver == 11) { 2235 | /* HTTP 1.1 defaults to keep-alive, unless close is specified. */ 2236 | keepalive = strstr(c->buf, "Connection: close") == NULL; 2237 | } 2238 | 2239 | /* Identify he URL. */ 2240 | p = strchr(c->buf,' '); 2241 | if (!p) return 1; /* There should be the method and a space... */ 2242 | url = ++p; /* Now this should point to the requested URL. */ 2243 | p = strchr(p, ' '); 2244 | if (!p) return 1; /* There should be a space before HTTP/... */ 2245 | *p = '\0'; 2246 | 2247 | if (Modes.debug & MODES_DEBUG_NET) { 2248 | printf("\nHTTP keep alive: %d\n", keepalive); 2249 | printf("HTTP requested URL: %s\n\n", url); 2250 | } 2251 | 2252 | /* Select the content to send, we have just two so far: 2253 | * "/" -> Our google map application. 2254 | * "/data.json" -> Our ajax request to update planes. */ 2255 | if (strstr(url, "/data.json")) { 2256 | content = aircraftsToJson(&clen); 2257 | ctype = MODES_CONTENT_TYPE_JSON; 2258 | } else { 2259 | struct stat sbuf; 2260 | int fd = -1; 2261 | 2262 | if (stat("gmap.html",&sbuf) != -1 && 2263 | (fd = open("gmap.html",O_RDONLY)) != -1) 2264 | { 2265 | content = malloc(sbuf.st_size); 2266 | if (read(fd,content,sbuf.st_size) == -1) { 2267 | snprintf(content,sbuf.st_size,"Error reading from file: %s", 2268 | strerror(errno)); 2269 | } 2270 | clen = sbuf.st_size; 2271 | } else { 2272 | char buf[128]; 2273 | 2274 | clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s", 2275 | strerror(errno)); 2276 | content = strdup(buf); 2277 | } 2278 | if (fd != -1) close(fd); 2279 | ctype = MODES_CONTENT_TYPE_HTML; 2280 | } 2281 | 2282 | /* Create the header and send the reply. */ 2283 | hdrlen = snprintf(hdr, sizeof(hdr), 2284 | "HTTP/1.1 200 OK\r\n" 2285 | "Server: Dump1090\r\n" 2286 | "Content-Type: %s\r\n" 2287 | "Connection: %s\r\n" 2288 | "Content-Length: %d\r\n" 2289 | "Access-Control-Allow-Origin: *\r\n" 2290 | "\r\n", 2291 | ctype, 2292 | keepalive ? "keep-alive" : "close", 2293 | clen); 2294 | 2295 | if (Modes.debug & MODES_DEBUG_NET) 2296 | printf("HTTP Reply header:\n%s", hdr); 2297 | 2298 | /* Send header and content. */ 2299 | if (write(c->fd, hdr, hdrlen) != hdrlen || 2300 | write(c->fd, content, clen) != clen) 2301 | { 2302 | free(content); 2303 | return 1; 2304 | } 2305 | free(content); 2306 | Modes.stat_http_requests++; 2307 | return !keepalive; 2308 | } 2309 | 2310 | /* This function polls the clients using read() in order to receive new 2311 | * messages from the net. 2312 | * 2313 | * The message is supposed to be separated by the next message by the 2314 | * separator 'sep', that is a null-terminated C string. 2315 | * 2316 | * Every full message received is decoded and passed to the higher layers 2317 | * calling the function 'handler'. 2318 | * 2319 | * The handelr returns 0 on success, or 1 to signal this function we 2320 | * should close the connection with the client in case of non-recoverable 2321 | * errors. */ 2322 | void modesReadFromClient(struct client *c, char *sep, 2323 | int(*handler)(struct client *)) 2324 | { 2325 | while(1) { 2326 | int left = MODES_CLIENT_BUF_SIZE - c->buflen; 2327 | int nread = read(c->fd, c->buf+c->buflen, left); 2328 | int fullmsg = 0; 2329 | int i; 2330 | char *p; 2331 | 2332 | if (nread <= 0) { 2333 | if (nread == 0 || errno != EAGAIN) { 2334 | /* Error, or end of file. */ 2335 | modesFreeClient(c->fd); 2336 | } 2337 | break; /* Serve next client. */ 2338 | } 2339 | c->buflen += nread; 2340 | 2341 | /* Always null-term so we are free to use strstr() */ 2342 | c->buf[c->buflen] = '\0'; 2343 | 2344 | /* If there is a complete message there must be the separator 'sep' 2345 | * in the buffer, note that we full-scan the buffer at every read 2346 | * for simplicity. */ 2347 | while ((p = strstr(c->buf, sep)) != NULL) { 2348 | i = p - c->buf; /* Turn it as an index inside the buffer. */ 2349 | c->buf[i] = '\0'; /* Te handler expects null terminated strings. */ 2350 | /* Call the function to process the message. It returns 1 2351 | * on error to signal we should close the client connection. */ 2352 | if (handler(c)) { 2353 | modesFreeClient(c->fd); 2354 | return; 2355 | } 2356 | /* Move what's left at the start of the buffer. */ 2357 | i += strlen(sep); /* The separator is part of the previous msg. */ 2358 | memmove(c->buf,c->buf+i,c->buflen-i); 2359 | c->buflen -= i; 2360 | c->buf[c->buflen] = '\0'; 2361 | /* Maybe there are more messages inside the buffer. 2362 | * Start looping from the start again. */ 2363 | fullmsg = 1; 2364 | } 2365 | /* If our buffer is full discard it, this is some badly 2366 | * formatted shit. */ 2367 | if (c->buflen == MODES_CLIENT_BUF_SIZE) { 2368 | c->buflen = 0; 2369 | /* If there is garbage, read more to discard it ASAP. */ 2370 | continue; 2371 | } 2372 | /* If no message was decoded process the next client, otherwise 2373 | * read more data from the same client. */ 2374 | if (!fullmsg) break; 2375 | } 2376 | } 2377 | 2378 | /* Read data from clients. This function actually delegates a lower-level 2379 | * function that depends on the kind of service (raw, http, ...). */ 2380 | void modesReadFromClients(void) { 2381 | int j; 2382 | struct client *c; 2383 | 2384 | for (j = 0; j <= Modes.maxfd; j++) { 2385 | if ((c = Modes.clients[j]) == NULL) continue; 2386 | if (c->service == Modes.ris) 2387 | modesReadFromClient(c,"\n",decodeHexMessage); 2388 | else if (c->service == Modes.https) 2389 | modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest); 2390 | } 2391 | } 2392 | 2393 | /* This function is used when "net only" mode is enabled to know when there 2394 | * is at least a new client to serve. Note that the dump1090 networking model 2395 | * is extremely trivial and a function takes care of handling all the clients 2396 | * that have something to serve, without a proper event library, so the 2397 | * function here returns as long as there is a single client ready, or 2398 | * when the specified timeout in milliesconds elapsed, without specifying to 2399 | * the caller what client requires to be served. */ 2400 | void modesWaitReadableClients(int timeout_ms) { 2401 | struct timeval tv; 2402 | fd_set fds; 2403 | int j, maxfd = Modes.maxfd; 2404 | 2405 | FD_ZERO(&fds); 2406 | 2407 | /* Set client FDs */ 2408 | for (j = 0; j <= Modes.maxfd; j++) { 2409 | if (Modes.clients[j]) FD_SET(j,&fds); 2410 | } 2411 | 2412 | /* Set listening sockets to accept new clients ASAP. */ 2413 | for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { 2414 | int s = *modesNetServices[j].socket; 2415 | FD_SET(s,&fds); 2416 | if (s > maxfd) maxfd = s; 2417 | } 2418 | 2419 | tv.tv_sec = timeout_ms/1000; 2420 | tv.tv_usec = (timeout_ms%1000)*1000; 2421 | /* We don't care why select returned here, timeout, error, or 2422 | * FDs ready are all conditions for which we just return. */ 2423 | select(maxfd+1,&fds,NULL,NULL,&tv); 2424 | } 2425 | 2426 | /* ============================ Terminal handling ========================== */ 2427 | 2428 | /* Handle resizing terminal. */ 2429 | void sigWinchCallback() { 2430 | signal(SIGWINCH, SIG_IGN); 2431 | Modes.interactive_rows = getTermRows(); 2432 | interactiveShowData(); 2433 | signal(SIGWINCH, sigWinchCallback); 2434 | } 2435 | 2436 | /* Get the number of rows after the terminal changes size. */ 2437 | int getTermRows() { 2438 | struct winsize w; 2439 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 2440 | return w.ws_row; 2441 | } 2442 | 2443 | /* ================================ Main ==================================== */ 2444 | 2445 | void showHelp(void) { 2446 | printf( 2447 | "--device-index Select RTL device (default: 0).\n" 2448 | "--gain Set gain (default: max gain. Use -100 for auto-gain).\n" 2449 | "--enable-agc Enable the Automatic Gain Control (default: off).\n" 2450 | "--freq Set frequency (default: 1090 Mhz).\n" 2451 | "--ifile Read data from file (use '-' for stdin).\n" 2452 | "--loop With --ifile, read the same file in a loop.\n" 2453 | "--interactive Interactive mode refreshing data on screen.\n" 2454 | "--interactive-rows Max number of rows in interactive mode (default: 15).\n" 2455 | "--interactive-ttl Remove from list if idle for (default: 60).\n" 2456 | "--raw Show only messages hex values.\n" 2457 | "--net Enable networking.\n" 2458 | "--net-only Enable just networking, no RTL device or file used.\n" 2459 | "--net-ro-port TCP listening port for raw output (default: 30002).\n" 2460 | "--net-ri-port TCP listening port for raw input (default: 30001).\n" 2461 | "--net-http-port HTTP server port (default: 8080).\n" 2462 | "--net-sbs-port TCP listening port for BaseStation format output (default: 30003).\n" 2463 | "--no-fix Disable single-bits error correction using CRC.\n" 2464 | "--no-crc-check Disable messages with broken CRC (discouraged).\n" 2465 | "--aggressive More CPU for more messages (two bits fixes, ...).\n" 2466 | "--stats With --ifile print stats at exit. No other output.\n" 2467 | "--onlyaddr Show only ICAO addresses (testing purposes).\n" 2468 | "--metric Use metric units (meters, km/h, ...).\n" 2469 | "--snip Strip IQ file removing samples < level.\n" 2470 | "--debug Debug mode (verbose), see README for details.\n" 2471 | "--help Show this help.\n" 2472 | "\n" 2473 | "Debug mode flags: d = Log frames decoded with errors\n" 2474 | " D = Log frames decoded with zero errors\n" 2475 | " c = Log frames with bad CRC\n" 2476 | " C = Log frames with good CRC\n" 2477 | " p = Log frames with bad preamble\n" 2478 | " n = Log network debugging info\n" 2479 | " j = Log frames to frames.js, loadable by debug.html.\n" 2480 | ); 2481 | } 2482 | 2483 | /* This function is called a few times every second by main in order to 2484 | * perform tasks we need to do continuously, like accepting new clients 2485 | * from the net, refreshing the screen in interactive mode, and so forth. */ 2486 | void backgroundTasks(void) { 2487 | if (Modes.net) { 2488 | modesAcceptClients(); 2489 | modesReadFromClients(); 2490 | interactiveRemoveStaleAircrafts(); 2491 | } 2492 | 2493 | /* Refresh screen when in interactive mode. */ 2494 | if (Modes.interactive && 2495 | (mstime() - Modes.interactive_last_update) > 2496 | MODES_INTERACTIVE_REFRESH_TIME) 2497 | { 2498 | interactiveRemoveStaleAircrafts(); 2499 | interactiveShowData(); 2500 | Modes.interactive_last_update = mstime(); 2501 | } 2502 | } 2503 | 2504 | int main(int argc, char **argv) { 2505 | int j; 2506 | 2507 | /* Set sane defaults. */ 2508 | modesInitConfig(); 2509 | 2510 | /* Parse the command line options */ 2511 | for (j = 1; j < argc; j++) { 2512 | int more = j+1 < argc; /* There are more arguments. */ 2513 | 2514 | if (!strcmp(argv[j],"--device-index") && more) { 2515 | Modes.dev_index = atoi(argv[++j]); 2516 | } else if (!strcmp(argv[j],"--gain") && more) { 2517 | Modes.gain = atof(argv[++j])*10; /* Gain is in tens of DBs */ 2518 | } else if (!strcmp(argv[j],"--enable-agc")) { 2519 | Modes.enable_agc++; 2520 | } else if (!strcmp(argv[j],"--freq") && more) { 2521 | Modes.freq = strtoll(argv[++j],NULL,10); 2522 | } else if (!strcmp(argv[j],"--ifile") && more) { 2523 | Modes.filename = strdup(argv[++j]); 2524 | } else if (!strcmp(argv[j],"--loop")) { 2525 | Modes.loop = 1; 2526 | } else if (!strcmp(argv[j],"--no-fix")) { 2527 | Modes.fix_errors = 0; 2528 | } else if (!strcmp(argv[j],"--no-crc-check")) { 2529 | Modes.check_crc = 0; 2530 | } else if (!strcmp(argv[j],"--raw")) { 2531 | Modes.raw = 1; 2532 | } else if (!strcmp(argv[j],"--net")) { 2533 | Modes.net = 1; 2534 | } else if (!strcmp(argv[j],"--net-only")) { 2535 | Modes.net = 1; 2536 | Modes.net_only = 1; 2537 | } else if (!strcmp(argv[j],"--net-ro-port") && more) { 2538 | modesNetServices[MODES_NET_SERVICE_RAWO].port = atoi(argv[++j]); 2539 | } else if (!strcmp(argv[j],"--net-ri-port") && more) { 2540 | modesNetServices[MODES_NET_SERVICE_RAWI].port = atoi(argv[++j]); 2541 | } else if (!strcmp(argv[j],"--net-http-port") && more) { 2542 | modesNetServices[MODES_NET_SERVICE_HTTP].port = atoi(argv[++j]); 2543 | } else if (!strcmp(argv[j],"--net-sbs-port") && more) { 2544 | modesNetServices[MODES_NET_SERVICE_SBS].port = atoi(argv[++j]); 2545 | } else if (!strcmp(argv[j],"--onlyaddr")) { 2546 | Modes.onlyaddr = 1; 2547 | } else if (!strcmp(argv[j],"--metric")) { 2548 | Modes.metric = 1; 2549 | } else if (!strcmp(argv[j],"--aggressive")) { 2550 | Modes.aggressive++; 2551 | } else if (!strcmp(argv[j],"--interactive")) { 2552 | Modes.interactive = 1; 2553 | } else if (!strcmp(argv[j],"--interactive-rows")) { 2554 | Modes.interactive_rows = atoi(argv[++j]); 2555 | } else if (!strcmp(argv[j],"--interactive-ttl")) { 2556 | Modes.interactive_ttl = atoi(argv[++j]); 2557 | } else if (!strcmp(argv[j],"--debug") && more) { 2558 | char *f = argv[++j]; 2559 | while(*f) { 2560 | switch(*f) { 2561 | case 'D': Modes.debug |= MODES_DEBUG_DEMOD; break; 2562 | case 'd': Modes.debug |= MODES_DEBUG_DEMODERR; break; 2563 | case 'C': Modes.debug |= MODES_DEBUG_GOODCRC; break; 2564 | case 'c': Modes.debug |= MODES_DEBUG_BADCRC; break; 2565 | case 'p': Modes.debug |= MODES_DEBUG_NOPREAMBLE; break; 2566 | case 'n': Modes.debug |= MODES_DEBUG_NET; break; 2567 | case 'j': Modes.debug |= MODES_DEBUG_JS; break; 2568 | default: 2569 | fprintf(stderr, "Unknown debugging flag: %c\n", *f); 2570 | exit(1); 2571 | break; 2572 | } 2573 | f++; 2574 | } 2575 | } else if (!strcmp(argv[j],"--stats")) { 2576 | Modes.stats = 1; 2577 | } else if (!strcmp(argv[j],"--snip") && more) { 2578 | snipMode(atoi(argv[++j])); 2579 | exit(0); 2580 | } else if (!strcmp(argv[j],"--help")) { 2581 | showHelp(); 2582 | exit(0); 2583 | } else { 2584 | fprintf(stderr, 2585 | "Unknown or not enough arguments for option '%s'.\n\n", 2586 | argv[j]); 2587 | showHelp(); 2588 | exit(1); 2589 | } 2590 | } 2591 | 2592 | /* Setup for SIGWINCH for handling lines */ 2593 | if (Modes.interactive == 1) signal(SIGWINCH, sigWinchCallback); 2594 | 2595 | /* Initialization */ 2596 | modesInit(); 2597 | if (Modes.net_only) { 2598 | fprintf(stderr,"Net-only mode, no RTL device or file open.\n"); 2599 | } else if (Modes.filename == NULL) { 2600 | modesInitRTLSDR(); 2601 | } else { 2602 | if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') { 2603 | Modes.fd = STDIN_FILENO; 2604 | } else if ((Modes.fd = open(Modes.filename,O_RDONLY)) == -1) { 2605 | perror("Opening data file"); 2606 | exit(1); 2607 | } 2608 | } 2609 | if (Modes.net) modesInitNet(); 2610 | 2611 | /* If the user specifies --net-only, just run in order to serve network 2612 | * clients without reading data from the RTL device. */ 2613 | while (Modes.net_only) { 2614 | backgroundTasks(); 2615 | modesWaitReadableClients(100); 2616 | } 2617 | 2618 | /* Create the thread that will read the data from the device. */ 2619 | pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL); 2620 | 2621 | pthread_mutex_lock(&Modes.data_mutex); 2622 | while(1) { 2623 | if (!Modes.data_ready) { 2624 | pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex); 2625 | continue; 2626 | } 2627 | computeMagnitudeVector(); 2628 | 2629 | /* Signal to the other thread that we processed the available data 2630 | * and we want more (useful for --ifile). */ 2631 | Modes.data_ready = 0; 2632 | pthread_cond_signal(&Modes.data_cond); 2633 | 2634 | /* Process data after releasing the lock, so that the capturing 2635 | * thread can read data while we perform computationally expensive 2636 | * stuff * at the same time. (This should only be useful with very 2637 | * slow processors). */ 2638 | pthread_mutex_unlock(&Modes.data_mutex); 2639 | detectModeS(Modes.magnitude, Modes.data_len/2); 2640 | backgroundTasks(); 2641 | pthread_mutex_lock(&Modes.data_mutex); 2642 | if (Modes.exit) break; 2643 | } 2644 | 2645 | /* If --ifile and --stats were given, print statistics. */ 2646 | if (Modes.stats && Modes.filename) { 2647 | printf("%lld valid preambles\n", Modes.stat_valid_preamble); 2648 | printf("%lld demodulated again after phase correction\n", 2649 | Modes.stat_out_of_phase); 2650 | printf("%lld demodulated with zero errors\n", 2651 | Modes.stat_demodulated); 2652 | printf("%lld with good crc\n", Modes.stat_goodcrc); 2653 | printf("%lld with bad crc\n", Modes.stat_badcrc); 2654 | printf("%lld errors corrected\n", Modes.stat_fixed); 2655 | printf("%lld single bit errors\n", Modes.stat_single_bit_fix); 2656 | printf("%lld two bits errors\n", Modes.stat_two_bits_fix); 2657 | printf("%lld total usable messages\n", 2658 | Modes.stat_goodcrc + Modes.stat_fixed); 2659 | } 2660 | 2661 | rtlsdr_close(Modes.dev); 2662 | return 0; 2663 | } 2664 | 2665 | 2666 | -------------------------------------------------------------------------------- /gmap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 40 | 41 | 42 | 188 | 189 | 190 |
191 |
192 |
193 |

Dump1090

194 |

195 |

Click on a plane for info.

196 |
197 |
198 | 199 | 200 | -------------------------------------------------------------------------------- /testfiles/modes1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antirez/dump1090/0c3bb23eb447d4ae47c7013346fa6fa97482bb1d/testfiles/modes1.bin -------------------------------------------------------------------------------- /tools/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 150 | 151 | 184 | 185 |
186 |
187 |
188 | 
189 | 190 | 191 | 192 | 193 | 194 | --------------------------------------------------------------------------------