diff --git a/sdl1090/README.md b/sdl1090/README.md index 3fdd396..bed3cdb 100644 --- a/sdl1090/README.md +++ b/sdl1090/README.md @@ -1,284 +1,85 @@ -Dump1090 README -=== +#spidr1090 -Dump 1090 is a Mode S decoder specifically designed for RTLSDR devices. +###HARDWARE -The main features are: +####Pi Zero Version +Part | Link | Cost +--- | --- | --- +Raspberry Pi Zero W | | $10 +Adafruit 2.4" PiTFT Hat| (https://www.adafruit.com/product/2455) | $35 +NooElec Nano3 SDR | | $28 +Adafruit GPS Hat | (https://www.adafruit.com/product/2324) | $45 +Antenna | | +USB Jack | | +MicroSD Card | | +| | Total: $118+ -* Robust decoding of weak messages, with mode1090 many users observed - improved range compared to other popular decoders. -* Network support: TCP30003 stream (MSG5...), Raw packets, HTTP. -* Embedded HTTP server that displays the currently detected aircrafts on - Google Map. -* Single bit errors correction using the 24 bit CRC. -* Ability to decode DF11, DF17 messages. -* Ability to decode DF formats like DF0, DF4, DF5, DF16, DF20 and DF21 - where the checksum is xored with the ICAO address by brute forcing the - checksum field using recently seen ICAO addresses. -* Decode raw IQ samples from file (using --ifile command line switch). -* Interactive command-line-interfae mode where aircrafts currently detected - are shown as a list refreshing as more data arrives. -* CPR coordinates decoding and track calculation from velocity. -* TCP server streaming and recceiving raw data to/from connected clients - (using --net). +####Battery Options +(https://en.wikipedia.org/wiki/List_of_battery_sizes) +Portrait Orientation: +18500 Batteries (18mm x 50mm), ~1000-2000mAh ea. +The Pi Zero configuration consumes are 500mAh -Installation ---- +Landscape Orientation +18650 batteries (18mm x 65mm). ~2200-3500mAh ea. +Adafruit pack + PowerBoost Charger, ~$40 +Anker PowerCore 10000, ~$25 -Type "make". +Other options +external recharger (maybe cheaper and smaller?) along with 18500/18650 -Normal usage ---- +Some 18500 / 18650 include current protection inside, such as: +http://www.ebay.com/itm/2-x-Panasonic-NCR-18500-2000mAh-LI-ION-RECHARGEABLE-BATTERY-PCB-button-top-case-/391378348412?epid=1830264923&hash=item5b1ff7a97c:g:ePgAAOSwFqJWswnj, +otherwise would require external protection circuit to be used in parallel. -To capture traffic directly from your RTL device and show the captured traffic -on standard output, just run the program without options at all: - ./dump1090 -To just output hexadecimal messages: +####Pi 3 Version +This gets you a slightly bigger screen, and slightly better performance using the SDR Smart rather than the Nano 3, but requires a ton of annoying port/header clipping and desoldering. - ./dump1090 --raw +Part | Link | Cost +--- | --- | --- +Raspberry Pi 3 | | $35 +Adafruit 2.8" PiTFT Hat| https://www.adafruit.com/product/2423 | $45 +NooElec SDR Smart | | $20 +GlobalSat MicroGPS Dongle | (https://www.amazon.com/GlobalSat-ND-105C-Micro-USB-Receiver/dp/B00N32HKIW/ref=sr_1_3?ie=UTF8&qid=1505829420&sr=8-3&keywords=gps+dongle) | $30 +Antenna | | +USB Jack | | +MicroSD Card | | +| | Total: $130+ -To run the program in interactive mode: - ./dump1090 --interactive +alt screen https://www.amazon.com/3-5inch-RPi-LCD-Directly-pluggable-Displaying/dp/B01N48NOXI/ref=sr_1_26?ie=UTF8&qid=1505871836&sr=8-26&keywords=pi+3.5%22+tft, $30 instead of 45, larger, not cap touch -To run the program in interactive mode, with networking support, and connect -with your browser to http://localhost:8080 to see live traffic: +####Battery Options +18650 batteries (18mm x 65mm). ~2200mAH ea. +Adafruit pack + PowerBoost Charger, ~$40 +http://www.ebay.com/itm/3-7-volts-6400-mAh-1S2P-18650-Li-Ion-Battery-Pack-PCB-protected-Panasonic-Cells-/221923032745?hash=item33aba4bea9:g:0-IAAOSw14xWLSr2 - ./dump1090 --interactive --net +###INSTALLATION -In iteractive mode it is possible to have a less information dense but more -"arcade style" output, where the screen is refreshed every second displaying -all the recently seen aircrafts with some additional information such as -altitude and flight number, extracted from the received Mode S packets. +####On RPI -Using files as source of data ---- +1. Follow [Adafruit PiTFT install guide] (https://learn.adafruit.com/adafruit-2-2-pitft-hat-320-240-primary-display-for-raspberry-pi/easy-install) -To decode data from file, use: +2. Install SDL and RTL-SDR libararies +``` +sudo apt-get install libsdl1.2-dev libsdl-ttf2.0-dev libsdl-gfx1.2-dev wiringpi +``` +3. Download spidr +``` +git clone sdfsdfdfs +``` - ./dump1090 --ifile /path/to/binfile +4. Build spidr with RPI flag set +``` +cd spidr +make RPI=1 +``` -The binary file should be created using `rtl_sdr` like this (or with any other -program that is able to output 8-bit unsigned IQ samples at 2Mhz sample rate). +TODO +make install should set up service? - rtl_sdr -f 1090000000 -s 2000000 -g 50 output.bin -In the example `rtl_sdr` a gain of 50 is used, simply you should use the highest -gain availabe for your tuner. This is not needed when calling Dump1090 itself -as it is able to select the highest gain supported automatically. -It is possible to feed the program with data via standard input using -the --ifile option with "-" as argument. -Additional options ---- - -Dump1090 can be called with other command line options to set a different -gain, frequency, and so forth. For a list of options use: - - ./dump1090 --help - -Everything is not documented here should be obvious, and for most users calling -it without arguments at all is the best thing to do. - -Reliability ---- - -By default Dump1090 checks for decoding errors using the 24-bit CRC checksum, -where available. Messages with errors are discarded. - -The --fix command line switch enables fixing single bit error correction -based on the CRC checksum. Technically, it uses a table of precomputed -checksum differences resulting from single bit errors to look up the -wrong bit position. - -This is indeed able to fix errors and works reliably in my experience, -however if you are interested in very reliable data I suggest to use -the --no-fix command line switch in order to disable error fixing. - -Performances and sensibility of detection ---- - -In my limited experience Dump1090 was able to decode a big number of messages -even in conditions where I encountered problems using other programs, however -no formal test was performed so I can't really claim that this program is -better or worse compared to other similar programs. - -If you can capture traffic that Dump1090 is not able to decode properly, drop -me an email with a download link. I may try to improve the detection during -my free time (this is just an hobby project). - -Network server features ---- - -By enabling the networking support with --net Dump1090 starts listening -for clients connections on port 30002 and 30001 (you can change both the -ports if you want, see --help output). - -Port 30002 ---- - -Connected clients are served with data ASAP as they arrive from the device -(or from file if --ifile is used) in the raw format similar to the following: - - *8D451E8B99019699C00B0A81F36E; - -Every entry is separated by a simple newline (LF character, hex 0x0A). - -Port 30001 ---- - -Port 30001 is the raw input port, and can be used to feed Dump1090 with -data in the same format as specified above, with hex messages starting with -a `*` and ending with a `;` character. - -So for instance if there is another remote Dump1090 instance collecting data -it is possible to sum the output to a local Dump1090 instance doing something -like this: - - nc remote-dump1090.example.net 30002 | nc localhost 30001 - -It is important to note that what is received via port 30001 is also -broadcasted to clients listening to port 30002. - -In general everything received from port 30001 is handled exactly like the -normal traffic from RTL devices or from file when --ifile is used. - -It is possible to use Dump1090 just as an hub using --ifile with /dev/zero -as argument as in the following example: - - ./dump1090 --net-only - -Or alternatively to see what's happening on the screen: - - ./dump1090 --net-only --interactive - -Then you can feed it from different data sources from the internet. - -Port 30003 ---- - -Connected clients are served with messages in SBS1 (BaseStation) format, -similar to: - - MSG,4,,,738065,,,,,,,,420,179,,,0,,0,0,0,0 - MSG,3,,,738065,,,,,,,35000,,,34.81609,34.07810,,,0,0,0,0 - -This can be used to feed data to various sharing sites without the need to use another decoder. - -Antenna ---- - -Mode S messages are transmitted in the 1090 Mhz frequency. If you have a decent -antenna you'll be able to pick up signals from aircrafts pretty far from your -position, especially if you are outdoor and in a position with a good sky view. - -You can easily build a very cheap antenna following the istructions at: - - http://antirez.com/news/46 - -With this trivial antenna I was able to pick up signals of aircrafts 200+ Km -away from me. - -If you are interested in a more serious antenna check the following -resources: - -* http://gnuradio.org/redmine/attachments/download/246/06-foster-adsb.pdf -* http://www.lll.lu/~edward/edward/adsb/antenna/ADSBantenna.html -* http://modesbeast.com/pix/adsb-ant-drawing.gif - -Aggressive mode ---- - -With --aggressive it is possible to activate the *aggressive mode* that is a -modified version of the Mode S packet detection and decoding. -The aggresive mode uses more CPU usually (especially if there are many planes -sending DF17 packets), but can detect a few more messages. - -The algorithm in aggressive mode is modified in the following ways: - -* Up to two demodulation errors are tolerated (adjacent entires in the - magnitude vector with the same eight). Normally only messages without - errors are checked. -* It tries to fix DF17 messages with CRC errors resulting from any two bit - errors. - -The use of aggressive mdoe is only advised in places where there is -low traffic in order to have a chance to capture some more messages. - -Debug mode ---- - -The Debug mode is a visual help to improve the detection algorithm or to -understand why the program is not working for a given input. - -In this mode messages are displayed in an ASCII-art style graphical -representation, where the individial magnitude bars sampled at 2Mhz are -displayed. - -An index shows the sample number, where 0 is the sample where the first -Mode S peak was found. Some additional background noise is also added -before the first peak to provide some context. - -To enable debug mode and check what combinations of packets you can -log, use `mode1090 --help` to obtain a list of available debug flags. - -Debug mode includes an optional javascript output that is used to visualize -packets using a web browser, you can use the file debug.html under the -'tools' directory to load the generated frames.js file. - -How this program works? ---- - -The code is very documented and written in order to be easy to understand. -For the diligent programmer with a Mode S specification on his hands it -should be trivial to understand how it works. - -The algorithms I used were obtained basically looking at many messages -as displayed using a trow-away SDL program, and trying to model the algorithm -based on how the messages look graphically. - -How to test the program? ---- - -If you have an RTLSDR device and you happen to be in an area where there -are aircrafts flying over your head, just run the program and check for signals. - -However if you don't have an RTLSDR device, or if in your area the presence -of aircrafts is very limited, you may want to try the sample file distributed -with the Dump1090 distribution under the "testfiles" directory. - -Just run it like this: - - ./dump1090 --ifile testfiles/modes1.bin - -What is --strip mode? ---- - -It is just a simple filter that will get raw IQ 8 bit samples in input -and will output a file missing all the parts of the file where I and Q -are lower than the specified for more than 32 samples. - -Use it like this: - - cat big.bin | ./dump1090 --snip 25 > small.bin - -I used it in order to create a small test file to include inside this -program source code distribution. - -Contributing ---- - -Dump1090 was written during some free time during xmas 2012, it is an hobby -project so I'll be able to address issues and improve it only during -free time, however you are incouraged to send pull requests in order to -improve the program. A good starting point can be the TODO list included in -the source distribution. - -Credits ---- - -Dump1090 was written by Salvatore Sanfilippo and is -released under the BSD three clause license. diff --git a/sdl1090/TerminusTTF-4.46.0.ttf.REMOVED.git-id b/sdl1090/TerminusTTF-4.46.0.ttf.REMOVED.git-id new file mode 100644 index 0000000..0fe2e2e --- /dev/null +++ b/sdl1090/TerminusTTF-4.46.0.ttf.REMOVED.git-id @@ -0,0 +1 @@ +eafa3a6e408def39bcd002f60a6d9a9810d54234 \ No newline at end of file diff --git a/sdl1090/TerminusTTF-Bold-4.46.0.ttf.REMOVED.git-id b/sdl1090/TerminusTTF-Bold-4.46.0.ttf.REMOVED.git-id new file mode 100644 index 0000000..c1e10e0 --- /dev/null +++ b/sdl1090/TerminusTTF-Bold-4.46.0.ttf.REMOVED.git-id @@ -0,0 +1 @@ +9dc2aed0a76993fd7ba9c28e0f55fce873a8c790 \ No newline at end of file diff --git a/sdl1090/defs.h b/sdl1090/defs.h index 36b8882..b552058 100644 --- a/sdl1090/defs.h +++ b/sdl1090/defs.h @@ -11,4 +11,6 @@ #endif #define SCREEN_WIDTH 320 -#define SCREEN_HEIGHT 240 +#define SCREEN_HEIGHT 480 + +#define UPSCALE 3 \ No newline at end of file diff --git a/sdl1090/draw.c b/sdl1090/draw.c index 5e0cb3c..4f9b6a9 100644 --- a/sdl1090/draw.c +++ b/sdl1090/draw.c @@ -1,294 +1,46 @@ #include "dump1090.h" -#include "draw.h" -#include "parula.h" -#include "magma.h" -#include "SDL/SDL_gfxPrimitives.h" +#include "structs.h" +#include "SDL/SDL_rotozoom.h" -#define LOGMAXDIST 1000.0 -#define MAXDIST 50.0 +static uint64_t mstime(void) { + struct timeval tv; + uint64_t mst; -#define AA 0 - -#define MAGMA 0 - -void CROSSVP(double *v, double *u, double *w) -{ - v[0] = u[1]*w[2] - u[2]*(w)[1]; - v[1] = u[2]*w[0] - u[0]*(w)[2]; - v[2] = u[0]*w[1] - u[1]*(w)[0]; + gettimeofday(&tv, NULL); + mst = ((uint64_t)tv.tv_sec)*1000; + mst += tv.tv_usec/1000; + return mst; } -SDL_Color setColor(uint8_t r, uint8_t g, uint8_t b) { - SDL_Color out; - out.r = r; - out.g = g; - out.b = b; - return out; -} +void draw() { + if ((mstime() - Modes.interactive_last_update) > MODES_INTERACTIVE_REFRESH_TIME) { -SDL_Color signalToColor(int signal) { - SDL_Color planeColor; + Modes.interactive_last_update = mstime(); - if(signal > 127) { - signal = 127; - } + SDL_FillRect(game.screen, NULL, 0); - if(signal < 0) { - planeColor = setColor(96, 96, 96); - } else { - if(MAGMA) { - planeColor = setColor(magma[signal][0], magma[signal][1], magma[signal][2]); - } else { - planeColor = setColor(parula[signal][0], parula[signal][1], parula[signal][2]); - } - } + if (Modes.map) { + drawMap(); + //drawList(3,320); + } else { + drawList(10,0); + } - return planeColor; -} + #ifdef RPI + SDL_Flip(game.screen); + #else + SDL_Rect clip; -int screenDist(double d) { - if(Modes.mapLogDist) { - return round((double)SCREEN_WIDTH * 0.5 * log(1.0+fabs(d)) / log(1.0+LOGMAXDIST)); - } else { - return round((double)SCREEN_WIDTH * 0.5 * fabs(d) / MAXDIST); - } -} + SDL_Surface *temp = SDL_DisplayFormat(zoomSurface(game.screen, UPSCALE, UPSCALE, 0)); -void screenCoords(int *outX, int *outY, double dx, double dy) { - *outX = (SCREEN_WIDTH>>1) + ((dx>0) ? 1 : -1) * screenDist(dx); - *outY = (SCREEN_HEIGHT>>1) + ((dy>0) ? 1 : -1) * screenDist(dy); -} + clip.x = 0; + clip.y = 0; + clip.w = temp->w; + clip.h = temp->h; -int outOfBounds(int x, int y) { - if(x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= SCREEN_HEIGHT ) { - return 1; - } else { - return 0; + SDL_BlitSurface(temp, &clip, game.bigScreen, 0); + + SDL_Flip(game.bigScreen); + #endif } -} - -void drawPlaneHeading(double dx, double dy, double heading, int signal, char *flight) -{ - int x, y; - screenCoords(&x, &y, dx, dy); - - if(outOfBounds(x,y)) { - return; - } - - SDL_Color planeColor = signalToColor(signal); - - double body = 8.0; - double wing = 6.0; - double tail = 3.0; - - double vec[3]; - vec[0] = sin(heading * M_PI / 180); - vec[1] = cos(heading * M_PI / 180); - vec[2] = 0; - - double up[] = {0,0,1}; - - double out[3]; - - CROSSVP(out,vec,up); - - int x1, x2, y1, y2; - - //body - x1 = x + round(-body*vec[0]); - y1 = y + round(-body*vec[1]); - x2 = x + round(body*vec[0]); - y2 = y + round(body*vec[1]); - - if(AA) { - aalineRGBA(game.screen,x1,y1,x2,y2,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - aatrigonRGBA(game.screen, x + round(-wing*.35*out[0]), y + round(-wing*.35*out[1]), x + round(wing*.35*out[0]), y + round(wing*.35*out[1]), x1, y1,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - aacircleRGBA(game.screen, x2,y2,1,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - } else { - thickLineRGBA(game.screen,x,y,x2,y2,2,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - filledTrigonRGBA(game.screen, x + round(-wing*.35*out[0]), y + round(-wing*.35*out[1]), x + round(wing*.35*out[0]), y + round(wing*.35*out[1]), x1, y1,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - filledCircleRGBA(game.screen, x2,y2,1,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - } - - //wing - x1 = x + round(-wing*out[0]); - y1 = y + round(-wing*out[1]); - x2 = x + round(wing*out[0]); - y2 = y + round(wing*out[1]); - - if(AA) { - aatrigonRGBA(game.screen, x1, y1, x2, y2, x+round(body*.35*vec[0]), y+round(body*.35*vec[1]),planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - } else { - filledTrigonRGBA(game.screen, x1, y1, x2, y2, x+round(body*.35*vec[0]), y+round(body*.35*vec[1]),planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - } - - //tail - x1 = x + round(-body*.75*vec[0]) + round(-tail*out[0]); - y1 = y + round(-body*.75*vec[1]) + round(-tail*out[1]); - x2 = x + round(-body*.75*vec[0]) + round(tail*out[0]); - y2 = y + round(-body*.75*vec[1]) + round(tail*out[1]); - - if(AA) { - aatrigonRGBA (game.screen, x1, y1, x2, y2, x+round(-body*.5*vec[0]), y+round(-body*.5*vec[1]),planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - } else { - filledTrigonRGBA (game.screen, x1, y1, x2, y2, x+round(-body*.5*vec[0]), y+round(-body*.5*vec[1]),planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); - } - - drawString(flight, x + 10, y + 10, game.font, planeColor); -} - -void drawPlane(double dx, double dy, int signal) -{ - - int x, y; - screenCoords(&x, &y, dx, dy); - - if(outOfBounds(x,y)) { - return; - } - - SDL_Color planeColor = signalToColor(signal); - - int length = 3.0; - - rectangleRGBA (game.screen, x - length, y - length, x+length, y + length, planeColor.r, planeColor.g, planeColor.b, SDL_ALPHA_OPAQUE); -} - -void drawTrail(double *oldDx, double *oldDy, time_t * oldSeen, int idx) { - - - int currentIdx, prevIdx; - - int currentX, currentY, prevX, prevY; - - time_t now = time(NULL); - - for(int i=0; i < (MODES_INTERACTIVE_TRAIL_LENGTH - 1); i++) { - currentIdx = (idx - i) % MODES_INTERACTIVE_TRAIL_LENGTH; - currentIdx = currentIdx < 0 ? currentIdx + MODES_INTERACTIVE_TRAIL_LENGTH : currentIdx; - prevIdx = (idx - (i + 1)) % MODES_INTERACTIVE_TRAIL_LENGTH; - prevIdx = prevIdx < 0 ? prevIdx + MODES_INTERACTIVE_TRAIL_LENGTH : prevIdx; - - if(oldDx[currentIdx] == 0 || oldDy[currentIdx] == 0) { - continue; - } - - if(oldDx[prevIdx] == 0 || oldDy[prevIdx] == 0) { - continue; - } - - screenCoords(¤tX, ¤tY, oldDx[currentIdx], oldDy[currentIdx]); - - screenCoords(&prevX, &prevY, oldDx[prevIdx], oldDy[prevIdx]); - - if(outOfBounds(currentX,currentY)) { - return; - } - - if(outOfBounds(prevX,prevY)) { - return; - } - - double age = 1.0 - (double)(now - oldSeen[currentIdx]) / MODES_INTERACTIVE_TRAIL_TTL; - - if(age < 0) { - age = 0; - } - - uint8_t colorVal = (uint8_t)floor(127.0 * age); - - if(AA) { - aalineRGBA(game.screen, prevX, prevY, currentX, currentY,colorVal, colorVal, colorVal, SDL_ALPHA_OPAQUE); - } else { - thickLineRGBA(game.screen, prevX, prevY, currentX, currentY, 2, colorVal, colorVal, colorVal, SDL_ALPHA_OPAQUE); - } - } -} - - -void drawGrid() -{ - int p1km = screenDist(1.0); - int p10km = screenDist(10.0); - int p100km = screenDist(100.0); - - hlineRGBA (game.screen, 0, SCREEN_WIDTH, SCREEN_HEIGHT>>1, 127, 127, 127, SDL_ALPHA_OPAQUE); - vlineRGBA (game.screen, SCREEN_WIDTH>>1, 0, SCREEN_HEIGHT, 127, 127, 127, SDL_ALPHA_OPAQUE); - - if(AA) { - aacircleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p1km, 249,38,114, SDL_ALPHA_OPAQUE); - aacircleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p10km, 187,29,86, SDL_ALPHA_OPAQUE); - aacircleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p100km, 125,19,57, SDL_ALPHA_OPAQUE); - } else { - circleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p1km, 249,38,114, SDL_ALPHA_OPAQUE); - circleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p10km, 187,29,86, SDL_ALPHA_OPAQUE); - circleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p100km, 125,19,57, SDL_ALPHA_OPAQUE); - } - - drawString("1km", (SCREEN_WIDTH>>1) + p1km + 5, (SCREEN_HEIGHT>>1) + 5, game.font, setColor(249,38,114)); - drawString("10km", (SCREEN_WIDTH>>1) + p10km + 5, (SCREEN_HEIGHT>>1) + 5, game.font, setColor(187,29,86)); - drawString("100km", (SCREEN_WIDTH>>1) + p100km + 5, (SCREEN_HEIGHT>>1) + 5, game.font, setColor(125,19,57)); -} - -void drawGeography() { - int x1, y1, x2, y2; - for(int i=1; i>1)); - // double y1d = (double) abs(y1 - (SCREEN_HEIGHT>>1)); - - // double colorDist = sqrt(x1d * x1d + y1d * y1d) / (double) SCREEN_HEIGHT; - - // colorDist = (colorDist < 0.0) ? 0.0 : colorDist; - // colorDist = (colorDist > 1.0) ? 1.0 : colorDist; - - // geoColor.r = (uint8_t) (colorDist * 114.0 + (1.0 - colorDist) * 166.0); - // geoColor.g = (uint8_t) (colorDist * 29.0 + (1.0 - colorDist) * 266.0); - // geoColor.b = (uint8_t) (colorDist * 240.0 + (1.0 - colorDist) * 16.0); - - geoColor.r = 100; - geoColor.g = 0; - geoColor.b = 200; - - if(AA) { - aalineRGBA(game.screen, x1, y1, x2, y2,geoColor.r,geoColor.g,geoColor.b, SDL_ALPHA_OPAQUE); - } else { - lineRGBA(game.screen, x1, y1, x2, y2,geoColor.r,geoColor.g,geoColor.b, SDL_ALPHA_OPAQUE); - } - } -} - -void delay(unsigned int frameLimit) -{ - unsigned int ticks = SDL_GetTicks(); - - if (frameLimit < ticks) - { - return; - } - - if (frameLimit > ticks + 16) - { - SDL_Delay(16); - } - - else - { - SDL_Delay(frameLimit - ticks); - } -} +} \ No newline at end of file diff --git a/sdl1090/draw.h b/sdl1090/draw.h deleted file mode 100644 index 23c33a1..0000000 --- a/sdl1090/draw.h +++ /dev/null @@ -1,3 +0,0 @@ -#include "structs.h" - -Game game; diff --git a/sdl1090/init.c b/sdl1090/init.c index 049dcc5..fd9938b 100644 --- a/sdl1090/init.c +++ b/sdl1090/init.c @@ -38,7 +38,15 @@ void init(char *title) SDL_ShowCursor(SDL_DISABLE); - game.screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 16, SDL_HWPALETTE|SDL_DOUBLEBUF); + #ifndef RPI + game.bigScreen = SDL_SetVideoMode(SCREEN_WIDTH * UPSCALE, SCREEN_HEIGHT * UPSCALE, 32, SDL_HWPALETTE|SDL_DOUBLEBUF); + game.screen = SDL_CreateRGBSurface(0, SCREEN_WIDTH, SCREEN_HEIGHT, 32, 0, 0, 0, 0); + #else + game.screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 16, SDL_HWPALETTE|SDL_DOUBLEBUF); + #endif + + + if (game.screen == NULL) { @@ -49,9 +57,9 @@ void init(char *title) /* Load the font */ - game.font = loadFont("Anonymous_Pro_B.ttf", 12); + game.font = loadFont("TerminusTTF-Bold-4.46.0.ttf", 12); - game.listFont = loadFont("Anonymous_Pro_B.ttf", 18); + game.listFont = loadFont("TerminusTTF-Bold-4.46.0.ttf", 18); /* Set the screen title */ diff --git a/sdl1090/list.c b/sdl1090/list.c index 7e52d44..004fbb5 100644 --- a/sdl1090/list.c +++ b/sdl1090/list.c @@ -3,16 +3,6 @@ #include "parula.h" #include "SDL/SDL_gfxPrimitives.h" -static uint64_t mstime(void) { - struct timeval tv; - uint64_t mst; - - gettimeofday(&tv, NULL); - mst = ((uint64_t)tv.tv_sec)*1000; - mst += tv.tv_usec/1000; - return mst; -} - SDL_Color pink = {249,38,114,255}; SDL_Color blue = {102,217,239,255}; SDL_Color green = {166,226,46,255}; @@ -24,24 +14,16 @@ SDL_Color darkGrey = {64,64,64,255}; SDL_Color black = {0,0,0,255}; SDL_Color white = {255,255,255,255}; -void drawList() { +void drawList(int rows, int top) { struct aircraft *a = Modes.aircrafts; time_t now = time(NULL); int count = 0; char progress; char spinner[4] = "|/-\\"; - // Refresh screen every (MODES_INTERACTIVE_REFRESH_TIME) miliseconde - if ((mstime() - Modes.interactive_last_update) < MODES_INTERACTIVE_REFRESH_TIME) - {return;} - - Modes.interactive_last_update = mstime(); - progress = spinner[time(NULL)%4]; - SDL_FillRect(game.screen, NULL, 0); - - drawStringBG(" Flight Alt(m) km/h D(km) H S ", 0, 0, game.listFont, black, white); + drawStringBG(" Flight Alt(m) km/h D(km) H S ", 0, top, game.listFont, black, white); // int xstride = 10; // for(int i = 0; i < 320 / xstride; i++) { @@ -54,7 +36,7 @@ void drawList() { // } int numNoDir = 0; - while(a && (count < 10)) { + while(a && (count < rows)) { if ((now - a->seen) < Modes.interactive_display_ttl) { int msgs = a->messages; @@ -151,7 +133,7 @@ void drawList() { if ((now - a->seen) > 30 ) { - drawString(a->flight, 0, (count + 1) * 20, game.listFont, (SDL_Color){96,96,96,255}); + drawString(a->flight, 0, top + (count + 1) * 20, game.listFont, (SDL_Color){96,96,96,255}); // printf("\x1B[1;30m%-8s%5s %4s %5s %c%c %d", // a->flight, @@ -161,15 +143,15 @@ void drawList() { // cLat, cLon, // (int)((float)signalAverage/25.0f)); } else { - drawString(a->flight, 10, (count + 1) * 20, game.listFont, pink); + drawString(a->flight, 10, top + (count + 1) * 20, game.listFont, pink); - drawString(strFl, 90, (count + 1) * 20, game.listFont, orange); + drawString(strFl, 90, top + (count + 1) * 20, game.listFont, orange); - drawString(strGs, 160, (count + 1) * 20, game.listFont, green); + drawString(strGs, 160, top + (count + 1) * 20, game.listFont, green); - drawString(strD, 210, (count + 1) * 20, game.listFont, blue); + drawString(strD, 210, top + (count + 1) * 20, game.listFont, blue); - drawString(strDir, 270, (count + 1) * 20, game.listFont, yellow); + drawString(strDir, 270, top + (count + 1) * 20, game.listFont, yellow); // drawString(strS, 290, (count + 1) * 20, game.listFont, (SDL_Color){255,9,96,255}); @@ -189,7 +171,4 @@ void drawList() { } a = a->next; } - - SDL_Flip(game.screen); - } diff --git a/sdl1090/maps.c b/sdl1090/maps.c index bc75fcc..758fd1b 100644 --- a/sdl1090/maps.c +++ b/sdl1090/maps.c @@ -1,35 +1,290 @@ #include "dump1090.h" #include "structs.h" +#include "parula.h" +#include "magma.h" +#include "SDL/SDL_gfxPrimitives.h" -// -// ============================= Utility functions ========================== -// -static uint64_t mstime(void) { - struct timeval tv; - uint64_t mst; +#define LOGMAXDIST 1000.0 +#define MAXDIST 50.0 - gettimeofday(&tv, NULL); - mst = ((uint64_t)tv.tv_sec)*1000; - mst += tv.tv_usec/1000; - return mst; +#define AA 0 + +#define MAGMA 0 + +void CROSSVP(double *v, double *u, double *w) +{ + v[0] = u[1]*w[2] - u[2]*(w)[1]; + v[1] = u[2]*w[0] - u[0]*(w)[2]; + v[2] = u[0]*w[1] - u[1]*(w)[0]; +} + +SDL_Color setColor(uint8_t r, uint8_t g, uint8_t b) { + SDL_Color out; + out.r = r; + out.g = g; + out.b = b; + return out; +} + +SDL_Color signalToColor(int signal) { + SDL_Color planeColor; + + if(signal > 127) { + signal = 127; + } + + if(signal < 0) { + planeColor = setColor(96, 96, 96); + } else { + if(MAGMA) { + planeColor = setColor(magma[signal][0], magma[signal][1], magma[signal][2]); + } else { + planeColor = setColor(parula[signal][0], parula[signal][1], parula[signal][2]); + } + } + + return planeColor; +} + +int screenDist(double d) { + + double scale_factor = (SCREEN_WIDTH > SCREEN_HEIGHT) ? SCREEN_WIDTH : SCREEN_HEIGHT; + + if(Modes.mapLogDist) { + return round(scale_factor* 0.5 * log(1.0+fabs(d)) / log(1.0+LOGMAXDIST)); + } else { + return round(scale_factor * 0.5 * fabs(d) / MAXDIST); + } +} + +void screenCoords(int *outX, int *outY, double dx, double dy) { + *outX = (SCREEN_WIDTH>>1) + ((dx>0) ? 1 : -1) * screenDist(dx); + *outY = (SCREEN_HEIGHT>>1) + ((dy>0) ? 1 : -1) * screenDist(dy); +} + +int outOfBounds(int x, int y) { + if(x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= SCREEN_HEIGHT ) { + return 1; + } else { + return 0; + } +} + +void drawPlaneHeading(double dx, double dy, double heading, int signal, char *flight) +{ + int x, y; + screenCoords(&x, &y, dx, dy); + + if(outOfBounds(x,y)) { + return; + } + + SDL_Color planeColor = signalToColor(signal); + + double body = 8.0; + double wing = 6.0; + double tail = 3.0; + + double vec[3]; + vec[0] = sin(heading * M_PI / 180); + vec[1] = cos(heading * M_PI / 180); + vec[2] = 0; + + double up[] = {0,0,1}; + + double out[3]; + + CROSSVP(out,vec,up); + + int x1, x2, y1, y2; + + //body + x1 = x + round(-body*vec[0]); + y1 = y + round(-body*vec[1]); + x2 = x + round(body*vec[0]); + y2 = y + round(body*vec[1]); + + if(AA) { + aalineRGBA(game.screen,x1,y1,x2,y2,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + aatrigonRGBA(game.screen, x + round(-wing*.35*out[0]), y + round(-wing*.35*out[1]), x + round(wing*.35*out[0]), y + round(wing*.35*out[1]), x1, y1,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + aacircleRGBA(game.screen, x2,y2,1,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + } else { + thickLineRGBA(game.screen,x,y,x2,y2,2,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + filledTrigonRGBA(game.screen, x + round(-wing*.35*out[0]), y + round(-wing*.35*out[1]), x + round(wing*.35*out[0]), y + round(wing*.35*out[1]), x1, y1,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + filledCircleRGBA(game.screen, x2,y2,1,planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + } + + //wing + x1 = x + round(-wing*out[0]); + y1 = y + round(-wing*out[1]); + x2 = x + round(wing*out[0]); + y2 = y + round(wing*out[1]); + + if(AA) { + aatrigonRGBA(game.screen, x1, y1, x2, y2, x+round(body*.35*vec[0]), y+round(body*.35*vec[1]),planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + } else { + filledTrigonRGBA(game.screen, x1, y1, x2, y2, x+round(body*.35*vec[0]), y+round(body*.35*vec[1]),planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + } + + //tail + x1 = x + round(-body*.75*vec[0]) + round(-tail*out[0]); + y1 = y + round(-body*.75*vec[1]) + round(-tail*out[1]); + x2 = x + round(-body*.75*vec[0]) + round(tail*out[0]); + y2 = y + round(-body*.75*vec[1]) + round(tail*out[1]); + + if(AA) { + aatrigonRGBA (game.screen, x1, y1, x2, y2, x+round(-body*.5*vec[0]), y+round(-body*.5*vec[1]),planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + } else { + filledTrigonRGBA (game.screen, x1, y1, x2, y2, x+round(-body*.5*vec[0]), y+round(-body*.5*vec[1]),planeColor.r,planeColor.g,planeColor.b,SDL_ALPHA_OPAQUE); + } + + drawString(flight, x + 10, y + 10, game.font, planeColor); +} + +void drawPlane(double dx, double dy, int signal) +{ + + int x, y; + screenCoords(&x, &y, dx, dy); + + if(outOfBounds(x,y)) { + return; + } + + SDL_Color planeColor = signalToColor(signal); + + int length = 3.0; + + rectangleRGBA (game.screen, x - length, y - length, x+length, y + length, planeColor.r, planeColor.g, planeColor.b, SDL_ALPHA_OPAQUE); +} + +void drawTrail(double *oldDx, double *oldDy, time_t * oldSeen, int idx) { + + + int currentIdx, prevIdx; + + int currentX, currentY, prevX, prevY; + + time_t now = time(NULL); + + for(int i=0; i < (MODES_INTERACTIVE_TRAIL_LENGTH - 1); i++) { + currentIdx = (idx - i) % MODES_INTERACTIVE_TRAIL_LENGTH; + currentIdx = currentIdx < 0 ? currentIdx + MODES_INTERACTIVE_TRAIL_LENGTH : currentIdx; + prevIdx = (idx - (i + 1)) % MODES_INTERACTIVE_TRAIL_LENGTH; + prevIdx = prevIdx < 0 ? prevIdx + MODES_INTERACTIVE_TRAIL_LENGTH : prevIdx; + + if(oldDx[currentIdx] == 0 || oldDy[currentIdx] == 0) { + continue; + } + + if(oldDx[prevIdx] == 0 || oldDy[prevIdx] == 0) { + continue; + } + + screenCoords(¤tX, ¤tY, oldDx[currentIdx], oldDy[currentIdx]); + + screenCoords(&prevX, &prevY, oldDx[prevIdx], oldDy[prevIdx]); + + if(outOfBounds(currentX,currentY)) { + return; + } + + if(outOfBounds(prevX,prevY)) { + return; + } + + double age = pow(1.0 - (double)(now - oldSeen[currentIdx]) / MODES_INTERACTIVE_TRAIL_TTL, 2.2); + + if(age < 0) { + age = 0; + } + + uint8_t colorVal = (uint8_t)floor(255.0 * age); + + if(AA) { + aalineRGBA(game.screen, prevX, prevY, currentX, currentY,colorVal, colorVal, colorVal, SDL_ALPHA_OPAQUE); + } else { + //thickLineRGBA(game.screen, prevX, prevY, currentX, currentY, 2, colorVal, colorVal, colorVal, SDL_ALPHA_OPAQUE); + thickLineRGBA(game.screen, prevX, prevY, currentX, currentY, 2, colorVal, colorVal, colorVal, SDL_ALPHA_OPAQUE); + } + } +} + + +void drawGrid() +{ + int p1km = screenDist(1.0); + int p10km = screenDist(10.0); + int p100km = screenDist(100.0); + + hlineRGBA (game.screen, 0, SCREEN_WIDTH, SCREEN_HEIGHT>>1, 127, 127, 127, SDL_ALPHA_OPAQUE); + vlineRGBA (game.screen, SCREEN_WIDTH>>1, 0, SCREEN_HEIGHT, 127, 127, 127, SDL_ALPHA_OPAQUE); + + if(AA) { + aacircleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p1km, 249,38,114, SDL_ALPHA_OPAQUE); + aacircleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p10km, 187,29,86, SDL_ALPHA_OPAQUE); + aacircleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p100km, 125,19,57, SDL_ALPHA_OPAQUE); + } else { + circleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p1km, 249,38,114, SDL_ALPHA_OPAQUE); + circleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p10km, 187,29,86, SDL_ALPHA_OPAQUE); + circleRGBA (game.screen, SCREEN_WIDTH>>1, SCREEN_HEIGHT>>1, p100km, 125,19,57, SDL_ALPHA_OPAQUE); + } + + drawString("1km", (SCREEN_WIDTH>>1) + p1km + 5, (SCREEN_HEIGHT>>1) + 5, game.font, setColor(249,38,114)); + drawString("10km", (SCREEN_WIDTH>>1) + p10km + 5, (SCREEN_HEIGHT>>1) + 5, game.font, setColor(187,29,86)); + drawString("100km", (SCREEN_WIDTH>>1) + p100km + 5, (SCREEN_HEIGHT>>1) + 5, game.font, setColor(125,19,57)); +} + +void drawGeography() { + int x1, y1, x2, y2; + for(int i=1; i>1)); + // double y1d = (double) abs(y1 - (SCREEN_HEIGHT>>1)); + + // double colorDist = sqrt(x1d * x1d + y1d * y1d) / (double) SCREEN_HEIGHT; + + // colorDist = (colorDist < 0.0) ? 0.0 : colorDist; + // colorDist = (colorDist > 1.0) ? 1.0 : colorDist; + + // geoColor.r = (uint8_t) (colorDist * 114.0 + (1.0 - colorDist) * 166.0); + // geoColor.g = (uint8_t) (colorDist * 29.0 + (1.0 - colorDist) * 266.0); + // geoColor.b = (uint8_t) (colorDist * 240.0 + (1.0 - colorDist) * 16.0); + + geoColor.r = 100; + geoColor.g = 0; + geoColor.b = 200; + + if(AA) { + aalineRGBA(game.screen, x1, y1, x2, y2,geoColor.r,geoColor.g,geoColor.b, SDL_ALPHA_OPAQUE); + } else { + lineRGBA(game.screen, x1, y1, x2, y2,geoColor.r,geoColor.g,geoColor.b, SDL_ALPHA_OPAQUE); + } + } } void drawMap(void) { struct aircraft *a = Modes.aircrafts; time_t now = time(NULL); - // Refresh screen every (MODES_INTERACTIVE_REFRESH_TIME) miliseconde - if ((mstime() - Modes.interactive_last_update) < MODES_INTERACTIVE_REFRESH_TIME) - {return;} - - Modes.interactive_last_update = mstime(); - - SDL_FillRect(game.screen, NULL, 0); - drawGeography(); - drawGrid(); + drawGrid(); while(a) { if ((now - a->seen) < Modes.interactive_display_ttl) { @@ -57,6 +312,4 @@ void drawMap(void) { } a = a->next; } - - SDL_Flip(game.screen); } diff --git a/sdl1090/structs.h b/sdl1090/structs.h index 923a75f..d4d23f2 100644 --- a/sdl1090/structs.h +++ b/sdl1090/structs.h @@ -3,6 +3,7 @@ typedef struct Game { SDL_Surface *screen; + SDL_Surface *bigScreen; TTF_Font *font; TTF_Font *listFont; } Game; @@ -24,15 +25,11 @@ void cleanup(void); //input.c void getInput(void); -//draw.c -void drawGeography(); -void drawPlaneHeading(double , double , double, int, char *); -void drawPlane(double , double, int); -void drawTrail(double *, double *, time_t *, int); -void drawGrid(); - //mapdata.c void initMaps(); //list.c -void drawList(); +void drawList(int rows, int top); + +//draw.c +void draw(); \ No newline at end of file diff --git a/sdl1090/view1090.REMOVED.git-id b/sdl1090/view1090.REMOVED.git-id index 5be9935..5f9b358 100644 --- a/sdl1090/view1090.REMOVED.git-id +++ b/sdl1090/view1090.REMOVED.git-id @@ -1 +1 @@ -0539f0c770f684b7ef79f185c195a747d2736a87 \ No newline at end of file +421a8bc720c21c7084c1bd73d4831dae5f24cf9f \ No newline at end of file diff --git a/sdl1090/view1090.c b/sdl1090/view1090.c index 7848477..f3a8d08 100644 --- a/sdl1090/view1090.c +++ b/sdl1090/view1090.c @@ -235,9 +235,6 @@ void showCopyright(void) { //========================================================================= // - - - int main(int argc, char **argv) { int j, fd; struct client *c; @@ -362,12 +359,7 @@ int main(int argc, char **argv) { interactiveShowData(); } - if (Modes.map) { - drawMap(); - } else { - drawList(); - } - + draw(); if ((fd == ANET_ERR) || (recv(c->fd, pk_buf, sizeof(pk_buf), MSG_PEEK | MSG_DONTWAIT) == 0)) { free(c);