more Readme edits and file cleanup
Former-commit-id: 6953975a19bc00dd58b4ec8881df6f8788a374d4 Former-commit-id: 81667ba89b5356b649b7e74b07636fe49b19814e
This commit is contained in:
parent
bf3e7d42d7
commit
dba4babbd2
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -2,9 +2,8 @@
|
|||
*.bin
|
||||
*.svg
|
||||
|
||||
view1090
|
||||
map1090
|
||||
|
||||
*.swp
|
||||
.~
|
||||
thumbs.db
|
||||
|
||||
viz1090
|
8
Makefile
8
Makefile
|
@ -7,13 +7,13 @@ CFLAGS=-O2 -g -Wno-write-strings
|
|||
LIBS=-lm -lSDL2 -lSDL2_ttf -lSDL2_gfx
|
||||
CC=g++
|
||||
|
||||
all: map1090
|
||||
all: viz1090
|
||||
|
||||
%.o: %.c %.cpp
|
||||
$(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
|
||||
|
||||
map1090: map1090.o AppData.o AircraftList.o Aircraft.o anet.o interactive.o mode_ac.o mode_s.o net_io.o Input.o View.o Map.o parula.o monokai.o
|
||||
$(CC) -g -o map1090 map1090.o AppData.o AircraftList.o Aircraft.o anet.o interactive.o mode_ac.o mode_s.o net_io.o Input.o View.o Map.o parula.o monokai.o $(LIBS) $(LDFLAGS)
|
||||
viz1090: viz1090.o AppData.o AircraftList.o Aircraft.o anet.o interactive.o mode_ac.o mode_s.o net_io.o Input.o View.o Map.o parula.o monokai.o
|
||||
$(CC) -g -o viz1090 viz1090.o AppData.o AircraftList.o Aircraft.o anet.o interactive.o mode_ac.o mode_s.o net_io.o Input.o View.o Map.o parula.o monokai.o $(LIBS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o map1090
|
||||
rm -f *.o viz1090
|
||||
|
|
53
README.md
53
README.md
|
@ -1,5 +1,12 @@
|
|||
# viz1090
|
||||
|
||||
**This is work in progress**
|
||||
There are a lot of missing pieces in this implementation so far:
|
||||
* A proper map system yet. Eventually map data should be pulled from Mapbox or similar.
|
||||
* In-application menus or configuration yet.
|
||||
* Theming/colormaps (important as this is primarily intended to be eye candy!)
|
||||
* Integration with handheld features like GPS, battery monitors, buttons/dials, etc.
|
||||
* Android build is currently broken
|
||||
|
||||
### BUILDING
|
||||
|
||||
|
@ -26,9 +33,9 @@ make clean; make
|
|||
```
|
||||
|
||||
3. Download and process map data
|
||||
Until more comprehensive map source (e.g., Mapbox) is integrated, map1090 uses the lat/lon SVG files from https://www.mccurley.org
|
||||
Until more comprehensive map source (e.g., Mapbox) is integrated, viz1090 uses the lat/lon SVG files from https://www.mccurley.org
|
||||
|
||||
The getmap.sh pulls the svg file for the contiguous 48 US states and produces a binary file for map1090 to read.
|
||||
The getmap.sh pulls the svg file for the contiguous 48 US states and produces a binary file for viz1090 to read.
|
||||
|
||||
```
|
||||
sudo apt install python3 python3-pip
|
||||
|
@ -40,7 +47,14 @@ pip3 install lxml numpy
|
|||
|
||||
As WSL does not have an X server built in, you will need to install a 3rd party X server, such as https://sourceforge.net/projects/vcxsrv/
|
||||
|
||||
When running vcxsrv Xlaunch, make sure to **uncheck "Use Native openGL"**
|
||||
* run Xlaunch from the start menu
|
||||
* Uncheck "Use Native openGL"
|
||||
* Open the Ubuntu WSL terminal
|
||||
* Specify the X display to use
|
||||
```
|
||||
export DISPLAY=:0
|
||||
```
|
||||
* Start viz1090 as described below.
|
||||
|
||||
### RUNNING
|
||||
|
||||
|
@ -49,17 +63,17 @@ When running vcxsrv Xlaunch, make sure to **uncheck "Use Native openGL"**
|
|||
dump1090 --net
|
||||
```
|
||||
|
||||
2. Run map1090
|
||||
2. Run viz1090
|
||||
```
|
||||
./view1090 --fullsceen --lat [your latitude] --lon [your longitude]
|
||||
./viz1090 --fullsceen --lat [your latitude] --lon [your longitude]
|
||||
```
|
||||
|
||||
map1090 will open an SDL window set to the resolution of your screen.
|
||||
viz1090 will open an SDL window set to the resolution of your screen.
|
||||
|
||||
### RUNTIME OPTIONS
|
||||
|
||||
--server [domain name or ip] Specify a dump1090 server. Renamed from the view1090 "--net-bo-ip-addr" argument
|
||||
--port [port number] Specify dump1090 server port. Renamed from the view1090 "--net-bo-port" argument
|
||||
--server [domain name or ip] Specify a dump1090 server. Renamed from the viz1090 "--net-bo-ip-addr" argument
|
||||
--port [port number] Specify dump1090 server port. Renamed from the viz1090 "--net-bo-port" argument
|
||||
--metric Display metric units rather than imperial.
|
||||
|
||||
--lat Specify your latitude in degrees
|
||||
|
@ -71,27 +85,28 @@ map1090 will open an SDL window set to the resolution of your screen.
|
|||
|
||||
### HARDWARE NOTES
|
||||
|
||||
map1090 is designed to be portable and work on a variety of systems, however it is intended to be used on a handheld device.
|
||||
|
||||
The software was originally develped for Raspberry Pi devices, and it is currently optimized for the Raspberry Pi 4 with the following configuration:
|
||||
This software was originally intended for Raspberry Pi devices, and it is currently optimized for the Raspberry Pi 4 with the following configuration:
|
||||
|
||||
* Raspberry Pi 4
|
||||
* A display:
|
||||
* [Pimoroni HyperPixel 4.0 Display] (https://shop.pimoroni.com/products/hyperpixel-4) \*best overall, but requires some rework to use battery monitoring features of the PiJuice mentioned below
|
||||
* [Waveshare 5.5" AMOLED] (https://www.waveshare.com/5.5inch-hdmi-amoled.htm) \*this is very good screen but the Google Pixel 2 phone mentioned below has a very similar display for the same price (along with everything else you need in a nice package)
|
||||
* [Waveshare 4.3" HDMI(B)] (https://www.waveshare.com/wiki/4.3inch_HDMI_LCD_(B))
|
||||
* [Adafruit 2.8" Capacitive Touch] (https://www.adafruit.com/product/2423)
|
||||
* [Pimoroni HyperPixel 4.0 Display](https://shop.pimoroni.com/products/hyperpixel-4) \*best overall, but requires some rework to use battery monitoring features of the PiJuice mentioned below
|
||||
* [Waveshare 5.5" AMOLED](https://www.waveshare.com/5.5inch-hdmi-amoled.htm) \*this is very good screen but the Google Pixel 2 phone mentioned below has a very similar display for the same price (along with everything else you need in a nice package)
|
||||
* [Waveshare 4.3" HDMI(B)](https://www.waveshare.com/wiki/4.3inch_HDMI_LCD_(B))
|
||||
* [Adafruit 2.8" Capacitive Touch](https://www.adafruit.com/product/2423)
|
||||
* A battery hat, such as:
|
||||
* [PiJuice Battery Hat] (https://uk.pi-supply.com/products/pijuice-standard) \*I2C pins must be reworked to connect to the Hyperpixel nonstandard I2C breakout pins, unfortunately
|
||||
* [MakerFocus UPS Hat] (https://www.amazon.com/Makerfocus-Raspberry-2500mAh-Lithium-Battery/dp/B01MQYX4UX)
|
||||
* [PiJuice Battery Hat](https://uk.pi-supply.com/products/pijuice-standard) \*I2C pins must be reworked to connect to the Hyperpixel nonstandard I2C breakout pins, unfortunately
|
||||
* [MakerFocus UPS Hat](https://www.amazon.com/Makerfocus-Raspberry-2500mAh-Lithium-Battery/dp/B01MQYX4UX)
|
||||
* Any USB SDR receiver:
|
||||
* [Noelec Nano V3] (https://www.nooelec.com/store/nesdr-nano-three.html)
|
||||
* [Noelec Nano V3](https://www.nooelec.com/store/nesdr-nano-three.html)
|
||||
* Stratux V2 \*very low power but hard to find
|
||||
|
||||
|
||||
If running as a front end only, with a separate dump1090 server, the best option is to use an Android phone, such as the Pixel 2, which significantly outperforms a Raspberry Pi 4.
|
||||
|
||||
map1090 has been tested on other boards such as the UP Core and UP Squared, but these boards have significantly poorer performance than the Raspberry Pi 4 with less software and peripheral support, so they are not recommended. With low resolution maps the software will run on these boards or even a Raspberry Pi Zero, so these remain options with some tradeoffs.
|
||||
viz1090 has been tested on other boards such as the UP Core and UP Squared, but these boards have poor performance compared to a Raspberry Pi 4, along with worse software and peripheral support, so they are not recommended. viz1090 with a low resolution map will run on these boards or even a Raspberry Pi Zero, so these remain options with some tradeoffs.
|
||||
|
||||
Of course, a variety of other devices work well for this purpose - all of the development so far has been done on a touchscreen Dell XPS laptop.
|
||||
|
||||
### Credits
|
||||
|
||||
viz1090 is largely based on [dump1090](https://github.com/MalcolmRobb/dump1090) (Malcom Robb, Salvatore Sanfilippo)
|
||||
|
|
37
TODO.md
37
TODO.md
|
@ -1,37 +0,0 @@
|
|||
todo
|
||||
|
||||
|
||||
|
||||
|
||||
UI stuff
|
||||
+tap on plane sets plane to active
|
||||
+animated recenter to active
|
||||
+toggle follow and orient (and other popup info?)
|
||||
double tap to zoom
|
||||
animated label show and hide
|
||||
+draw map to texture and only redraw on move
|
||||
color themes (requires settings load)
|
||||
settings (button popups or panes, sliders, etc)
|
||||
battery level (need separate linux/win/android)
|
||||
|
||||
|
||||
Utility stuff
|
||||
+map load (and write from python) --> start by reducing lat/lon precision to 4 or 5 decimal places and change storage to float instead of double
|
||||
libconfig load settings
|
||||
|
||||
Platforms
|
||||
migrate Android project into main repo
|
||||
SDL_AndroidGetJNIEnv() to access parameters, system info, etc
|
||||
windows
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
naming
|
||||
|
||||
viz1090
|
||||
map1090
|
||||
sdl1090
|
|
@ -1,21 +0,0 @@
|
|||
Android build notes
|
||||
|
||||
This example works:
|
||||
https://github.com/pvallet/hello-sdl2-android
|
||||
|
||||
the only JDK that seems to work is oracle-java8-installer, discontinued but available as of 2/18/2020 from
|
||||
sudo add-apt-repository ppa:ts.sch.gr/ppa
|
||||
sudo apt-get update
|
||||
sudo apt-get install oracle-java8-installer
|
||||
per https://askubuntu.com/questions/1139387/update-to-latest-version-of-java-after-ppa-is-discontinued
|
||||
|
||||
SDL2, SDL2_gfx, SDL2_ttf official need to go in external
|
||||
|
||||
SDL2_gfx official doesn't have Android.mk, but this one does:
|
||||
|
||||
https://github.com/keera-studios/SDL2_gfx
|
||||
|
||||
|
||||
App parameters are in android/app/src/main/java/[pvallet/com/github/hellosdl2/helloSDL2Activity.java] (network access, fullscreen immersive mode"
|
||||
Resources (fonts etc) need to go in android/app/src/main/res
|
||||
build settings (arch, android version) are in android/AndroidManifest.xml AND android/SDL2/src/main/AndroidManifest.xml (not sure if the second one needs to be changed)
|
|
@ -1 +0,0 @@
|
|||
6c9ac166c74d8d6e7d80eaa3f318cc32e1c83140
|
Binary file not shown.
Before Width: | Height: | Size: 20 KiB |
|
@ -1 +0,0 @@
|
|||
c0d04ee4985a78dfc389debfb93114ff8e33e7bb
|
|
@ -1 +0,0 @@
|
|||
gcc -I /usr/include/libusb-1.0 -I ./convenience/ -I ./getopt -shared -Wl,-soname,rtl_fm_python -o rtl_fm_python.so -fPIC rtl_fm_python.c convenience/convenience.c getopt/getopt.c -lrtlsdr
|
|
@ -1,92 +0,0 @@
|
|||
#include "DigiKeyboard.h"
|
||||
|
||||
#define encoder0PinA 0
|
||||
#define encoder0PinB 2
|
||||
|
||||
#define aIn //5 is 0 on the digistump
|
||||
|
||||
#define avgPeriod 100
|
||||
|
||||
static boolean rotating=false;
|
||||
|
||||
float avgRead = 0;
|
||||
float lastRead = 0;
|
||||
unsigned char avgCount = 0;
|
||||
|
||||
float buttonState = 0;
|
||||
|
||||
bool lastButtonState = 0;
|
||||
|
||||
int rotIncrement = 0;
|
||||
|
||||
void setup() {
|
||||
|
||||
pinMode(encoder0PinA, INPUT);
|
||||
digitalWrite(encoder0PinA, HIGH);
|
||||
pinMode(encoder0PinB, INPUT);
|
||||
digitalWrite(encoder0PinB, HIGH);
|
||||
|
||||
pinMode(1, INPUT);
|
||||
digitalWrite(1, HIGH);
|
||||
|
||||
attachInterrupt(0, rotEncoder, CHANGE);
|
||||
}
|
||||
|
||||
void rotEncoder(){
|
||||
rotating=true;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
DigiKeyboard.update();
|
||||
|
||||
avgRead += (float)analogRead(0) / avgPeriod;
|
||||
avgCount++;
|
||||
|
||||
buttonState += (float)digitalRead(1) / avgPeriod;
|
||||
|
||||
if(avgCount >= avgPeriod) {
|
||||
if(abs(avgRead - lastRead) >= 1) {
|
||||
DigiKeyboard.print(String((int)avgRead, HEX));
|
||||
DigiKeyboard.println("");
|
||||
lastRead = avgRead;
|
||||
}
|
||||
|
||||
if((buttonState > .5) != lastButtonState) {
|
||||
lastButtonState = (buttonState > .5);
|
||||
|
||||
if(lastButtonState) {
|
||||
DigiKeyboard.print("n");
|
||||
} else {
|
||||
DigiKeyboard.print("m");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
avgRead = 0;
|
||||
avgCount = 0;
|
||||
buttonState = 0;
|
||||
|
||||
}
|
||||
|
||||
while(rotating) {
|
||||
delay(2);
|
||||
|
||||
if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
|
||||
rotIncrement++;
|
||||
}
|
||||
else {
|
||||
rotIncrement--;
|
||||
}
|
||||
rotating=false; // Reset the flag back to false
|
||||
}
|
||||
|
||||
if(rotIncrement >= 2) {
|
||||
DigiKeyboard.print("z");
|
||||
rotIncrement = 0;
|
||||
}
|
||||
|
||||
if(rotIncrement <= -2) {
|
||||
DigiKeyboard.print("x");
|
||||
rotIncrement = 0;
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
#include "DigiKeyboard.h"
|
||||
|
||||
#define encoderPinA 0
|
||||
#define encoderPinB 2
|
||||
#define selectPin 1
|
||||
#define sqlPin 0 //5 is 0 on the digistump
|
||||
|
||||
#define bufferPeriod 100
|
||||
|
||||
float encoderPinABuffer = 0;
|
||||
float encoderPinBBuffer = 0;
|
||||
float selectPinBuffer = 0;
|
||||
float sqlPinBuffer = 0;
|
||||
|
||||
int encoderCounter = 0;
|
||||
|
||||
bool encoderPinALast = 0;
|
||||
bool encoderPinBLast = 0;
|
||||
bool selectPinLast = 0;
|
||||
float sqlPinLast = 0;
|
||||
|
||||
int bufferCounter = 0;
|
||||
|
||||
int8_t lookup_table[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
|
||||
|
||||
uint8_t index = 0;
|
||||
|
||||
void setup() {
|
||||
|
||||
pinMode(encoderPinA, INPUT);
|
||||
digitalWrite(encoderPinA, HIGH);
|
||||
pinMode(encoderPinB, INPUT);
|
||||
digitalWrite(encoderPinB, HIGH);
|
||||
|
||||
pinMode(selectPin, INPUT);
|
||||
digitalWrite(selectPin, HIGH);
|
||||
|
||||
pinMode(sqlPin, INPUT);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
//update buffers
|
||||
|
||||
//encoderPinABuffer += (float)digitalRead(encoderPinA) / bufferPeriod;
|
||||
//encoderPinBBuffer += (float)digitalRead(encoderPinB) / bufferPeriod;
|
||||
selectPinBuffer += (float)digitalRead(selectPin) / bufferPeriod;
|
||||
sqlPinBuffer += (float)analogRead(sqlPin) / bufferPeriod;
|
||||
|
||||
bufferCounter++;
|
||||
|
||||
bool encoderPinAState = (bool)digitalRead(encoderPinA);
|
||||
bool encoderPinBState = (bool)digitalRead(encoderPinB);
|
||||
|
||||
if((encoderPinAState != encoderPinALast) | (encoderPinBState != encoderPinBLast)) {
|
||||
index = (index << 2) & 0b1100;
|
||||
index = index | ((encoderPinAState << 1) + encoderPinBState);
|
||||
|
||||
encoderCounter += lookup_table[index & 0b1111];
|
||||
|
||||
if(encoderCounter > 2) {
|
||||
DigiKeyboard.print("z");
|
||||
encoderCounter = 0;
|
||||
}
|
||||
|
||||
if(encoderCounter < -2) {
|
||||
DigiKeyboard.print("x");
|
||||
encoderCounter = 0;
|
||||
}
|
||||
|
||||
encoderPinALast = encoderPinAState;
|
||||
encoderPinBLast = encoderPinBState;
|
||||
}
|
||||
|
||||
//check pin values
|
||||
if(bufferCounter > bufferPeriod) {
|
||||
//usb keepalive
|
||||
DigiKeyboard.update();
|
||||
|
||||
//bool encoderPinAState = encoderPinABuffer > .5;
|
||||
//bool encoderPinBState = encoderPinBBuffer > .5;
|
||||
bool selectPinState = selectPinBuffer > .5;
|
||||
|
||||
//check encoder pins nd transmit
|
||||
// if(encoderPinAState != encoderPinALast || encoderPinBState != encoderPinBLast) {
|
||||
//
|
||||
// uint8_t index = (encoderPinALast << 4) + (encoderPinBLast << 3) + (encoderPinAState << 1) + encoderPinBState;
|
||||
//
|
||||
// //encoderDetentCounter += lookup_table[index];
|
||||
//
|
||||
// if(lookup_table[index] > 0) {
|
||||
// DigiKeyboard.print("z");
|
||||
// } else if (lookup_table[index] < 0) {
|
||||
// DigiKeyboard.print("x");
|
||||
// }
|
||||
//
|
||||
// encoderPinALast = encoderPinAState;
|
||||
// encoderPinBLast = encoderPinBState;
|
||||
// }
|
||||
|
||||
//check select pin and transmit
|
||||
if(selectPinState != selectPinLast) {
|
||||
selectPinLast = selectPinState;
|
||||
if(selectPinLast) {
|
||||
DigiKeyboard.print("n");
|
||||
} else {
|
||||
DigiKeyboard.print("m");
|
||||
}
|
||||
}
|
||||
|
||||
//check squelch knob and transmit
|
||||
if(abs(sqlPinBuffer - sqlPinLast) >= 1) {
|
||||
DigiKeyboard.print(String((int)sqlPinBuffer, HEX));
|
||||
DigiKeyboard.println("");
|
||||
sqlPinLast = sqlPinBuffer;
|
||||
}
|
||||
|
||||
// reset buffers
|
||||
encoderPinABuffer = 0;
|
||||
encoderPinBBuffer = 0;
|
||||
selectPinBuffer = 0;
|
||||
sqlPinBuffer = 0;
|
||||
bufferCounter = 0;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
For information on installing libraries, see: http://www.arduino.cc/en/Guide/Libraries
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
gpio -g mode 18 pwm
|
||||
gpio -g pwm 18 100
|
2
old_hw_build/dump1090/.gitignore
vendored
2
old_hw_build/dump1090/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
*.o
|
||||
dump1090
|
|
@ -1,30 +0,0 @@
|
|||
#
|
||||
# When building a package or installing otherwise in the system, make
|
||||
# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
|
||||
#
|
||||
PROGNAME=dump1090
|
||||
|
||||
ifdef PREFIX
|
||||
BINDIR=$(PREFIX)/bin
|
||||
SHAREDIR=$(PREFIX)/share/$(PROGNAME)
|
||||
EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\"
|
||||
endif
|
||||
|
||||
CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr`
|
||||
LIBS=`pkg-config --libs librtlsdr` -lpthread -lm
|
||||
CC=gcc
|
||||
|
||||
|
||||
all: dump1090 view1090
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
|
||||
|
||||
dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o
|
||||
$(CC) -g -o dump1090 dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LDFLAGS)
|
||||
|
||||
view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o
|
||||
$(CC) -g -o view1090 view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o dump1090 view1090
|
|
@ -1,284 +0,0 @@
|
|||
Dump1090 README
|
||||
===
|
||||
|
||||
Dump 1090 is a Mode S decoder specifically designed for RTLSDR devices.
|
||||
|
||||
The main features are:
|
||||
|
||||
* Robust decoding of weak messages, with mode1090 many users observed
|
||||
improved range compared to other popular decoders.
|
||||
* Network support: TCP30003 stream (MSG5...), Raw packets, HTTP.
|
||||
* Embedded HTTP server that displays the currently detected aircrafts on
|
||||
Google Map.
|
||||
* Single bit errors correction using the 24 bit CRC.
|
||||
* Ability to decode DF11, DF17 messages.
|
||||
* Ability to decode DF formats like DF0, DF4, DF5, DF16, DF20 and DF21
|
||||
where the checksum is xored with the ICAO address by brute forcing the
|
||||
checksum field using recently seen ICAO addresses.
|
||||
* Decode raw IQ samples from file (using --ifile command line switch).
|
||||
* Interactive command-line-interfae mode where aircrafts currently detected
|
||||
are shown as a list refreshing as more data arrives.
|
||||
* CPR coordinates decoding and track calculation from velocity.
|
||||
* TCP server streaming and recceiving raw data to/from connected clients
|
||||
(using --net).
|
||||
|
||||
Installation
|
||||
---
|
||||
|
||||
Type "make".
|
||||
|
||||
Normal usage
|
||||
---
|
||||
|
||||
To capture traffic directly from your RTL device and show the captured traffic
|
||||
on standard output, just run the program without options at all:
|
||||
|
||||
./dump1090
|
||||
|
||||
To just output hexadecimal messages:
|
||||
|
||||
./dump1090 --raw
|
||||
|
||||
To run the program in interactive mode:
|
||||
|
||||
./dump1090 --interactive
|
||||
|
||||
To run the program in interactive mode, with networking support, and connect
|
||||
with your browser to http://localhost:8080 to see live traffic:
|
||||
|
||||
./dump1090 --interactive --net
|
||||
|
||||
In iteractive mode it is possible to have a less information dense but more
|
||||
"arcade style" output, where the screen is refreshed every second displaying
|
||||
all the recently seen aircrafts with some additional information such as
|
||||
altitude and flight number, extracted from the received Mode S packets.
|
||||
|
||||
Using files as source of data
|
||||
---
|
||||
|
||||
To decode data from file, use:
|
||||
|
||||
./dump1090 --ifile /path/to/binfile
|
||||
|
||||
The binary file should be created using `rtl_sdr` like this (or with any other
|
||||
program that is able to output 8-bit unsigned IQ samples at 2Mhz sample rate).
|
||||
|
||||
rtl_sdr -f 1090000000 -s 2000000 -g 50 output.bin
|
||||
|
||||
In the example `rtl_sdr` a gain of 50 is used, simply you should use the highest
|
||||
gain availabe for your tuner. This is not needed when calling Dump1090 itself
|
||||
as it is able to select the highest gain supported automatically.
|
||||
|
||||
It is possible to feed the program with data via standard input using
|
||||
the --ifile option with "-" as argument.
|
||||
|
||||
Additional options
|
||||
---
|
||||
|
||||
Dump1090 can be called with other command line options to set a different
|
||||
gain, frequency, and so forth. For a list of options use:
|
||||
|
||||
./dump1090 --help
|
||||
|
||||
Everything is not documented here should be obvious, and for most users calling
|
||||
it without arguments at all is the best thing to do.
|
||||
|
||||
Reliability
|
||||
---
|
||||
|
||||
By default Dump1090 checks for decoding errors using the 24-bit CRC checksum,
|
||||
where available. Messages with errors are discarded.
|
||||
|
||||
The --fix command line switch enables fixing single bit error correction
|
||||
based on the CRC checksum. Technically, it uses a table of precomputed
|
||||
checksum differences resulting from single bit errors to look up the
|
||||
wrong bit position.
|
||||
|
||||
This is indeed able to fix errors and works reliably in my experience,
|
||||
however if you are interested in very reliable data I suggest to use
|
||||
the --no-fix command line switch in order to disable error fixing.
|
||||
|
||||
Performances and sensibility of detection
|
||||
---
|
||||
|
||||
In my limited experience Dump1090 was able to decode a big number of messages
|
||||
even in conditions where I encountered problems using other programs, however
|
||||
no formal test was performed so I can't really claim that this program is
|
||||
better or worse compared to other similar programs.
|
||||
|
||||
If you can capture traffic that Dump1090 is not able to decode properly, drop
|
||||
me an email with a download link. I may try to improve the detection during
|
||||
my free time (this is just an hobby project).
|
||||
|
||||
Network server features
|
||||
---
|
||||
|
||||
By enabling the networking support with --net Dump1090 starts listening
|
||||
for clients connections on port 30002 and 30001 (you can change both the
|
||||
ports if you want, see --help output).
|
||||
|
||||
Port 30002
|
||||
---
|
||||
|
||||
Connected clients are served with data ASAP as they arrive from the device
|
||||
(or from file if --ifile is used) in the raw format similar to the following:
|
||||
|
||||
*8D451E8B99019699C00B0A81F36E;
|
||||
|
||||
Every entry is separated by a simple newline (LF character, hex 0x0A).
|
||||
|
||||
Port 30001
|
||||
---
|
||||
|
||||
Port 30001 is the raw input port, and can be used to feed Dump1090 with
|
||||
data in the same format as specified above, with hex messages starting with
|
||||
a `*` and ending with a `;` character.
|
||||
|
||||
So for instance if there is another remote Dump1090 instance collecting data
|
||||
it is possible to sum the output to a local Dump1090 instance doing something
|
||||
like this:
|
||||
|
||||
nc remote-dump1090.example.net 30002 | nc localhost 30001
|
||||
|
||||
It is important to note that what is received via port 30001 is also
|
||||
broadcasted to clients listening to port 30002.
|
||||
|
||||
In general everything received from port 30001 is handled exactly like the
|
||||
normal traffic from RTL devices or from file when --ifile is used.
|
||||
|
||||
It is possible to use Dump1090 just as an hub using --ifile with /dev/zero
|
||||
as argument as in the following example:
|
||||
|
||||
./dump1090 --net-only
|
||||
|
||||
Or alternatively to see what's happening on the screen:
|
||||
|
||||
./dump1090 --net-only --interactive
|
||||
|
||||
Then you can feed it from different data sources from the internet.
|
||||
|
||||
Port 30003
|
||||
---
|
||||
|
||||
Connected clients are served with messages in SBS1 (BaseStation) format,
|
||||
similar to:
|
||||
|
||||
MSG,4,,,738065,,,,,,,,420,179,,,0,,0,0,0,0
|
||||
MSG,3,,,738065,,,,,,,35000,,,34.81609,34.07810,,,0,0,0,0
|
||||
|
||||
This can be used to feed data to various sharing sites without the need to use another decoder.
|
||||
|
||||
Antenna
|
||||
---
|
||||
|
||||
Mode S messages are transmitted in the 1090 Mhz frequency. If you have a decent
|
||||
antenna you'll be able to pick up signals from aircrafts pretty far from your
|
||||
position, especially if you are outdoor and in a position with a good sky view.
|
||||
|
||||
You can easily build a very cheap antenna following the istructions at:
|
||||
|
||||
http://antirez.com/news/46
|
||||
|
||||
With this trivial antenna I was able to pick up signals of aircrafts 200+ Km
|
||||
away from me.
|
||||
|
||||
If you are interested in a more serious antenna check the following
|
||||
resources:
|
||||
|
||||
* http://gnuradio.org/redmine/attachments/download/246/06-foster-adsb.pdf
|
||||
* http://www.lll.lu/~edward/edward/adsb/antenna/ADSBantenna.html
|
||||
* http://modesbeast.com/pix/adsb-ant-drawing.gif
|
||||
|
||||
Aggressive mode
|
||||
---
|
||||
|
||||
With --aggressive it is possible to activate the *aggressive mode* that is a
|
||||
modified version of the Mode S packet detection and decoding.
|
||||
The aggresive mode uses more CPU usually (especially if there are many planes
|
||||
sending DF17 packets), but can detect a few more messages.
|
||||
|
||||
The algorithm in aggressive mode is modified in the following ways:
|
||||
|
||||
* Up to two demodulation errors are tolerated (adjacent entires in the
|
||||
magnitude vector with the same eight). Normally only messages without
|
||||
errors are checked.
|
||||
* It tries to fix DF17 messages with CRC errors resulting from any two bit
|
||||
errors.
|
||||
|
||||
The use of aggressive mdoe is only advised in places where there is
|
||||
low traffic in order to have a chance to capture some more messages.
|
||||
|
||||
Debug mode
|
||||
---
|
||||
|
||||
The Debug mode is a visual help to improve the detection algorithm or to
|
||||
understand why the program is not working for a given input.
|
||||
|
||||
In this mode messages are displayed in an ASCII-art style graphical
|
||||
representation, where the individial magnitude bars sampled at 2Mhz are
|
||||
displayed.
|
||||
|
||||
An index shows the sample number, where 0 is the sample where the first
|
||||
Mode S peak was found. Some additional background noise is also added
|
||||
before the first peak to provide some context.
|
||||
|
||||
To enable debug mode and check what combinations of packets you can
|
||||
log, use `mode1090 --help` to obtain a list of available debug flags.
|
||||
|
||||
Debug mode includes an optional javascript output that is used to visualize
|
||||
packets using a web browser, you can use the file debug.html under the
|
||||
'tools' directory to load the generated frames.js file.
|
||||
|
||||
How this program works?
|
||||
---
|
||||
|
||||
The code is very documented and written in order to be easy to understand.
|
||||
For the diligent programmer with a Mode S specification on his hands it
|
||||
should be trivial to understand how it works.
|
||||
|
||||
The algorithms I used were obtained basically looking at many messages
|
||||
as displayed using a trow-away SDL program, and trying to model the algorithm
|
||||
based on how the messages look graphically.
|
||||
|
||||
How to test the program?
|
||||
---
|
||||
|
||||
If you have an RTLSDR device and you happen to be in an area where there
|
||||
are aircrafts flying over your head, just run the program and check for signals.
|
||||
|
||||
However if you don't have an RTLSDR device, or if in your area the presence
|
||||
of aircrafts is very limited, you may want to try the sample file distributed
|
||||
with the Dump1090 distribution under the "testfiles" directory.
|
||||
|
||||
Just run it like this:
|
||||
|
||||
./dump1090 --ifile testfiles/modes1.bin
|
||||
|
||||
What is --strip mode?
|
||||
---
|
||||
|
||||
It is just a simple filter that will get raw IQ 8 bit samples in input
|
||||
and will output a file missing all the parts of the file where I and Q
|
||||
are lower than the specified <level> for more than 32 samples.
|
||||
|
||||
Use it like this:
|
||||
|
||||
cat big.bin | ./dump1090 --snip 25 > small.bin
|
||||
|
||||
I used it in order to create a small test file to include inside this
|
||||
program source code distribution.
|
||||
|
||||
Contributing
|
||||
---
|
||||
|
||||
Dump1090 was written during some free time during xmas 2012, it is an hobby
|
||||
project so I'll be able to address issues and improve it only during
|
||||
free time, however you are incouraged to send pull requests in order to
|
||||
improve the program. A good starting point can be the TODO list included in
|
||||
the source distribution.
|
||||
|
||||
Credits
|
||||
---
|
||||
|
||||
Dump1090 was written by Salvatore Sanfilippo <antirez@gmail.com> and is
|
||||
released under the BSD three clause license.
|
|
@ -1,5 +0,0 @@
|
|||
TODO
|
||||
|
||||
* Extract more information from captured Mode S messages.
|
||||
* Improve the web interface gmap.html.
|
||||
* Enhance the algorithm to reliably decode more messages.
|
|
@ -1,345 +0,0 @@
|
|||
/* anet.c -- Basic TCP socket stuff made a bit less boring
|
||||
*
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include "winstubs.h" //Put everything Windows specific in here
|
||||
#include "dump1090.h"
|
||||
#endif
|
||||
|
||||
#include "anet.h"
|
||||
|
||||
static void anetSetError(char *err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!err) return;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(err, ANET_ERR_LEN, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int anetNonBlock(char *err, int fd)
|
||||
{
|
||||
int flags;
|
||||
#ifndef _WIN32
|
||||
/* Set the socket nonblocking.
|
||||
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
||||
* interrupted by a signal. */
|
||||
if ((flags = fcntl(fd, F_GETFL)) == -1) {
|
||||
anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#else
|
||||
flags = 1;
|
||||
if (ioctlsocket(fd, FIONBIO, &flags)) {
|
||||
errno = WSAGetLastError();
|
||||
anetSetError(err, "ioctlsocket(FIONBIO): %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#endif
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetTcpNoDelay(char *err, int fd)
|
||||
{
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(yes)) == -1)
|
||||
{
|
||||
anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetSetSendBuffer(char *err, int fd, int buffsize)
|
||||
{
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&buffsize, sizeof(buffsize)) == -1)
|
||||
{
|
||||
anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetTcpKeepAlive(char *err, int fd)
|
||||
{
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&yes, sizeof(yes)) == -1) {
|
||||
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetResolve(char *err, char *host, char *ipbuf)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
if (inet_aton(host, (void*)&sa.sin_addr) == 0) {
|
||||
struct hostent *he;
|
||||
|
||||
he = gethostbyname(host);
|
||||
if (he == NULL) {
|
||||
anetSetError(err, "can't resolve: %s", host);
|
||||
return ANET_ERR;
|
||||
}
|
||||
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
|
||||
}
|
||||
strcpy(ipbuf,inet_ntoa(sa.sin_addr));
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
static int anetCreateSocket(char *err, int domain) {
|
||||
int s, on = 1;
|
||||
if ((s = socket(domain, SOCK_STREAM, 0)) == -1) {
|
||||
#ifdef _WIN32
|
||||
errno = WSAGetLastError();
|
||||
#endif
|
||||
anetSetError(err, "creating socket: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Make sure connection-intensive things like the redis benckmark
|
||||
* will be able to close/open sockets a zillion of times */
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) == -1) {
|
||||
anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#define ANET_CONNECT_NONE 0
|
||||
#define ANET_CONNECT_NONBLOCK 1
|
||||
static int anetTcpGenericConnect(char *err, char *addr, int port, int flags)
|
||||
{
|
||||
int s;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
|
||||
memset(&sa,0,sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons((uint16_t)port);
|
||||
if (inet_aton(addr, (void*)&sa.sin_addr) == 0) {
|
||||
struct hostent *he;
|
||||
|
||||
he = gethostbyname(addr);
|
||||
if (he == NULL) {
|
||||
anetSetError(err, "can't resolve: %s", addr);
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
|
||||
}
|
||||
if (flags & ANET_CONNECT_NONBLOCK) {
|
||||
if (anetNonBlock(err,s) != ANET_OK)
|
||||
return ANET_ERR;
|
||||
}
|
||||
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
||||
if (errno == EINPROGRESS &&
|
||||
flags & ANET_CONNECT_NONBLOCK)
|
||||
return s;
|
||||
|
||||
anetSetError(err, "connect: %s", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int anetTcpConnect(char *err, char *addr, int port)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE);
|
||||
}
|
||||
|
||||
int anetTcpNonBlockConnect(char *err, char *addr, int port)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK);
|
||||
}
|
||||
|
||||
/* Like read(2) but make sure 'count' is read before to return
|
||||
* (unless error or EOF condition is encountered) */
|
||||
int anetRead(int fd, char *buf, int count)
|
||||
{
|
||||
int nread, totlen = 0;
|
||||
while(totlen != count) {
|
||||
nread = read(fd,buf,count-totlen);
|
||||
if (nread == 0) return totlen;
|
||||
if (nread == -1) return -1;
|
||||
totlen += nread;
|
||||
buf += nread;
|
||||
}
|
||||
return totlen;
|
||||
}
|
||||
|
||||
/* Like write(2) but make sure 'count' is read before to return
|
||||
* (unless error is encountered) */
|
||||
int anetWrite(int fd, char *buf, int count)
|
||||
{
|
||||
int nwritten, totlen = 0;
|
||||
while(totlen != count) {
|
||||
nwritten = write(fd,buf,count-totlen);
|
||||
if (nwritten == 0) return totlen;
|
||||
if (nwritten == -1) return -1;
|
||||
totlen += nwritten;
|
||||
buf += nwritten;
|
||||
}
|
||||
return totlen;
|
||||
}
|
||||
|
||||
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) {
|
||||
if (bind(s,sa,len) == -1) {
|
||||
#ifdef _WIN32
|
||||
errno = WSAGetLastError();
|
||||
#endif
|
||||
anetSetError(err, "bind: %s", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Use a backlog of 512 entries. We pass 511 to the listen() call because
|
||||
* the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1);
|
||||
* which will thus give us a backlog of 512 entries */
|
||||
if (listen(s, 511) == -1) {
|
||||
#ifdef _WIN32
|
||||
errno = WSAGetLastError();
|
||||
#endif
|
||||
anetSetError(err, "listen: %s", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetTcpServer(char *err, int port, char *bindaddr)
|
||||
{
|
||||
int s;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
|
||||
memset(&sa,0,sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons((uint16_t)port);
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bindaddr && inet_aton(bindaddr, (void*)&sa.sin_addr) == 0) {
|
||||
anetSetError(err, "invalid bind address");
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
return s;
|
||||
}
|
||||
|
||||
static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) {
|
||||
int fd;
|
||||
while(1) {
|
||||
fd = accept(s,sa,len);
|
||||
if (fd == -1) {
|
||||
#ifndef _WIN32
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
#else
|
||||
errno = WSAGetLastError();
|
||||
if (errno == WSAEWOULDBLOCK) {
|
||||
#endif
|
||||
} else {
|
||||
anetSetError(err, "accept: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int anetTcpAccept(char *err, int s, char *ip, int *port) {
|
||||
int fd;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
|
||||
if (ip) strcpy(ip,inet_ntoa(sa.sin_addr));
|
||||
if (port) *port = ntohs(sa.sin_port);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int anetPeerToString(int fd, char *ip, int *port) {
|
||||
struct sockaddr_in sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
|
||||
if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) {
|
||||
*port = 0;
|
||||
ip[0] = '?';
|
||||
ip[1] = '\0';
|
||||
return -1;
|
||||
}
|
||||
if (ip) strcpy(ip,inet_ntoa(sa.sin_addr));
|
||||
if (port) *port = ntohs(sa.sin_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int anetSockName(int fd, char *ip, int *port) {
|
||||
struct sockaddr_in sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
|
||||
if (getsockname(fd,(struct sockaddr*)&sa,&salen) == -1) {
|
||||
*port = 0;
|
||||
ip[0] = '?';
|
||||
ip[1] = '\0';
|
||||
return -1;
|
||||
}
|
||||
if (ip) strcpy(ip,inet_ntoa(sa.sin_addr));
|
||||
if (port) *port = ntohs(sa.sin_port);
|
||||
return 0;
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/* anet.c -- Basic TCP socket stuff made a bit less boring
|
||||
*
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ANET_H
|
||||
#define ANET_H
|
||||
|
||||
#define ANET_OK 0
|
||||
#define ANET_ERR -1
|
||||
#define ANET_ERR_LEN 256
|
||||
|
||||
#if defined(__sun)
|
||||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
|
||||
int anetTcpConnect(char *err, char *addr, int port);
|
||||
int anetTcpNonBlockConnect(char *err, char *addr, int port);
|
||||
int anetUnixConnect(char *err, char *path);
|
||||
int anetUnixNonBlockConnect(char *err, char *path);
|
||||
int anetRead(int fd, char *buf, int count);
|
||||
int anetResolve(char *err, char *host, char *ipbuf);
|
||||
int anetTcpServer(char *err, int port, char *bindaddr);
|
||||
int anetUnixServer(char *err, char *path, mode_t perm);
|
||||
int anetTcpAccept(char *err, int serversock, char *ip, int *port);
|
||||
int anetUnixAccept(char *err, int serversock);
|
||||
int anetWrite(int fd, char *buf, int count);
|
||||
int anetNonBlock(char *err, int fd);
|
||||
int anetTcpNoDelay(char *err, int fd);
|
||||
int anetTcpKeepAlive(char *err, int fd);
|
||||
int anetPeerToString(int fd, char *ip, int *port);
|
||||
int anetSetSendBuffer(char *err, int fd, int buffsize);
|
||||
|
||||
#endif
|
|
@ -1,6 +0,0 @@
|
|||
// coaa.h configuration file for Plane Plotter Uploader
|
||||
//
|
||||
// You MUST apply via the COAA website for your own personal version of this file
|
||||
// Do not disclose the contents of this file to anyone thereafter as it uniquely
|
||||
// identifies you to the PlanePlotter system
|
||||
//
|
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
670d4181c3a6710f0becf3d4c232b386ee904bf1
|
|
@ -1,904 +0,0 @@
|
|||
// dump1090, a Mode S messages decoder for RTLSDR devices.
|
||||
//
|
||||
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#include "coaa.h"
|
||||
#include "dump1090.h"
|
||||
//
|
||||
// ============================= Utility functions ==========================
|
||||
//
|
||||
void sigintHandler(int dummy) {
|
||||
MODES_NOTUSED(dummy);
|
||||
signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety
|
||||
Modes.exit = 1; // Signal to threads that we are done
|
||||
}
|
||||
//
|
||||
// =============================== Terminal handling ========================
|
||||
//
|
||||
#ifndef _WIN32
|
||||
// Get the number of rows after the terminal changes size.
|
||||
int getTermRows() {
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
return (w.ws_row);
|
||||
}
|
||||
|
||||
// Handle resizing terminal
|
||||
void sigWinchCallback() {
|
||||
signal(SIGWINCH, SIG_IGN);
|
||||
Modes.interactive_rows = getTermRows();
|
||||
interactiveShowData();
|
||||
signal(SIGWINCH, sigWinchCallback);
|
||||
}
|
||||
#else
|
||||
int getTermRows() { return MODES_INTERACTIVE_ROWS;}
|
||||
#endif
|
||||
//
|
||||
// =============================== Initialization ===========================
|
||||
//
|
||||
void modesInitConfig(void) {
|
||||
// Default everything to zero/NULL
|
||||
memset(&Modes, 0, sizeof(Modes));
|
||||
|
||||
// Now initialise things that should not be 0/NULL to their defaults
|
||||
Modes.gain = MODES_MAX_GAIN;
|
||||
Modes.freq = MODES_DEFAULT_FREQ;
|
||||
Modes.ppm_error = MODES_DEFAULT_PPM;
|
||||
Modes.check_crc = 1;
|
||||
Modes.net_heartbeat_rate = MODES_NET_HEARTBEAT_RATE;
|
||||
Modes.net_output_sbs_port = MODES_NET_OUTPUT_SBS_PORT;
|
||||
Modes.net_output_raw_port = MODES_NET_OUTPUT_RAW_PORT;
|
||||
Modes.net_input_raw_port = MODES_NET_INPUT_RAW_PORT;
|
||||
Modes.net_output_beast_port = MODES_NET_OUTPUT_BEAST_PORT;
|
||||
Modes.net_input_beast_port = MODES_NET_INPUT_BEAST_PORT;
|
||||
Modes.net_http_port = MODES_NET_HTTP_PORT;
|
||||
Modes.interactive_rows = getTermRows();
|
||||
Modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL;
|
||||
Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL;
|
||||
Modes.fUserLat = MODES_USER_LATITUDE_DFLT;
|
||||
Modes.fUserLon = MODES_USER_LONGITUDE_DFLT;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
void modesInit(void) {
|
||||
int i, q;
|
||||
|
||||
pthread_mutex_init(&Modes.pDF_mutex,NULL);
|
||||
pthread_mutex_init(&Modes.data_mutex,NULL);
|
||||
pthread_cond_init(&Modes.data_cond,NULL);
|
||||
|
||||
// Allocate the various buffers used by Modes
|
||||
if ( ((Modes.icao_cache = (uint32_t *) malloc(sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2) ) == NULL) ||
|
||||
((Modes.pFileData = (uint16_t *) malloc(MODES_ASYNC_BUF_SIZE) ) == NULL) ||
|
||||
((Modes.magnitude = (uint16_t *) malloc(MODES_ASYNC_BUF_SIZE+MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE) ) == NULL) ||
|
||||
((Modes.maglut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) ||
|
||||
((Modes.beastOut = (char *) malloc(MODES_RAWOUT_BUF_SIZE) ) == NULL) ||
|
||||
((Modes.rawOut = (char *) malloc(MODES_RAWOUT_BUF_SIZE) ) == NULL) )
|
||||
{
|
||||
fprintf(stderr, "Out of memory allocating data buffer.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Clear the buffers that have just been allocated, just in-case
|
||||
memset(Modes.icao_cache, 0, sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2);
|
||||
memset(Modes.pFileData,127, MODES_ASYNC_BUF_SIZE);
|
||||
memset(Modes.magnitude, 0, MODES_ASYNC_BUF_SIZE+MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE);
|
||||
|
||||
// Validate the users Lat/Lon home location inputs
|
||||
if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90
|
||||
|| (Modes.fUserLat < -90.0) // and
|
||||
|| (Modes.fUserLon > 360.0) // Longitude must be -180 to +360
|
||||
|| (Modes.fUserLon < -180.0) ) {
|
||||
Modes.fUserLat = Modes.fUserLon = 0.0;
|
||||
} else if (Modes.fUserLon > 180.0) { // If Longitude is +180 to +360, make it -180 to 0
|
||||
Modes.fUserLon -= 360.0;
|
||||
}
|
||||
// If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the
|
||||
// Atlantic ocean off the west coast of Africa. This is unlikely to be correct.
|
||||
// Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian
|
||||
// is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both.
|
||||
// Testing the flag at runtime will be much quicker than ((fLon != 0.0) || (fLat != 0.0))
|
||||
Modes.bUserFlags &= ~MODES_USER_LATLON_VALID;
|
||||
if ((Modes.fUserLat != 0.0) || (Modes.fUserLon != 0.0)) {
|
||||
Modes.bUserFlags |= MODES_USER_LATLON_VALID;
|
||||
}
|
||||
|
||||
// Limit the maximum requested raw output size to less than one Ethernet Block
|
||||
if (Modes.net_output_raw_size > (MODES_RAWOUT_BUF_FLUSH))
|
||||
{Modes.net_output_raw_size = MODES_RAWOUT_BUF_FLUSH;}
|
||||
if (Modes.net_output_raw_rate > (MODES_RAWOUT_BUF_RATE))
|
||||
{Modes.net_output_raw_rate = MODES_RAWOUT_BUF_RATE;}
|
||||
if (Modes.net_sndbuf_size > (MODES_NET_SNDBUF_MAX))
|
||||
{Modes.net_sndbuf_size = MODES_NET_SNDBUF_MAX;}
|
||||
|
||||
// Initialise the Block Timers to something half sensible
|
||||
ftime(&Modes.stSystemTimeBlk);
|
||||
for (i = 0; i < MODES_ASYNC_BUF_NUMBER; i++)
|
||||
{Modes.stSystemTimeRTL[i] = Modes.stSystemTimeBlk;}
|
||||
|
||||
// Each I and Q value varies from 0 to 255, which represents a range from -1 to +1. To get from the
|
||||
// unsigned (0-255) range you therefore subtract 127 (or 128 or 127.5) from each I and Q, giving you
|
||||
// a range from -127 to +128 (or -128 to +127, or -127.5 to +127.5)..
|
||||
//
|
||||
// To decode the AM signal, you need the magnitude of the waveform, which is given by sqrt((I^2)+(Q^2))
|
||||
// The most this could be is if I&Q are both 128 (or 127 or 127.5), so you could end up with a magnitude
|
||||
// of 181.019 (or 179.605, or 180.312)
|
||||
//
|
||||
// However, in reality the magnitude of the signal should never exceed the range -1 to +1, because the
|
||||
// values are I = rCos(w) and Q = rSin(w). Therefore the integer computed magnitude should (can?) never
|
||||
// exceed 128 (or 127, or 127.5 or whatever)
|
||||
//
|
||||
// If we scale up the results so that they range from 0 to 65535 (16 bits) then we need to multiply
|
||||
// by 511.99, (or 516.02 or 514). antirez's original code multiplies by 360, presumably because he's
|
||||
// assuming the maximim calculated amplitude is 181.019, and (181.019 * 360) = 65166.
|
||||
//
|
||||
// So lets see if we can improve things by subtracting 127.5, Well in integer arithmatic we can't
|
||||
// subtract half, so, we'll double everything up and subtract one, and then compensate for the doubling
|
||||
// in the multiplier at the end.
|
||||
//
|
||||
// If we do this we can never have I or Q equal to 0 - they can only be as small as +/- 1.
|
||||
// This gives us a minimum magnitude of root 2 (0.707), so the dynamic range becomes (1.414-255). This
|
||||
// also affects our scaling value, which is now 65535/(255 - 1.414), or 258.433254
|
||||
//
|
||||
// The sums then become mag = 258.433254 * (sqrt((I*2-255)^2 + (Q*2-255)^2) - 1.414)
|
||||
// or mag = (258.433254 * sqrt((I*2-255)^2 + (Q*2-255)^2)) - 365.4798
|
||||
//
|
||||
// We also need to clip mag just incaes any rogue I/Q values somehow do have a magnitude greater than 255.
|
||||
//
|
||||
|
||||
for (i = 0; i <= 255; i++) {
|
||||
for (q = 0; q <= 255; q++) {
|
||||
int mag, mag_i, mag_q;
|
||||
|
||||
mag_i = (i * 2) - 255;
|
||||
mag_q = (q * 2) - 255;
|
||||
|
||||
mag = (int) round((sqrt((mag_i*mag_i)+(mag_q*mag_q)) * 258.433254) - 365.4798);
|
||||
|
||||
Modes.maglut[(i*256)+q] = (uint16_t) ((mag < 65535) ? mag : 65535);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare error correction tables
|
||||
modesInitErrorInfo();
|
||||
}
|
||||
//
|
||||
// =============================== RTLSDR handling ==========================
|
||||
//
|
||||
void modesInitRTLSDR(void) {
|
||||
int j;
|
||||
int device_count;
|
||||
char vendor[256], product[256], serial[256];
|
||||
|
||||
device_count = rtlsdr_get_device_count();
|
||||
if (!device_count) {
|
||||
fprintf(stderr, "No supported RTLSDR devices found.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Found %d device(s):\n", device_count);
|
||||
for (j = 0; j < device_count; j++) {
|
||||
rtlsdr_get_device_usb_strings(j, vendor, product, serial);
|
||||
fprintf(stderr, "%d: %s, %s, SN: %s %s\n", j, vendor, product, serial,
|
||||
(j == Modes.dev_index) ? "(currently selected)" : "");
|
||||
}
|
||||
|
||||
if (rtlsdr_open(&Modes.dev, Modes.dev_index) < 0) {
|
||||
fprintf(stderr, "Error opening the RTLSDR device: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set gain, frequency, sample rate, and reset the device
|
||||
rtlsdr_set_tuner_gain_mode(Modes.dev,
|
||||
(Modes.gain == MODES_AUTO_GAIN) ? 0 : 1);
|
||||
if (Modes.gain != MODES_AUTO_GAIN) {
|
||||
if (Modes.gain == MODES_MAX_GAIN) {
|
||||
// Find the maximum gain available
|
||||
int numgains;
|
||||
int gains[100];
|
||||
|
||||
numgains = rtlsdr_get_tuner_gains(Modes.dev, gains);
|
||||
Modes.gain = gains[numgains-1];
|
||||
fprintf(stderr, "Max available gain is: %.2f\n", Modes.gain/10.0);
|
||||
}
|
||||
rtlsdr_set_tuner_gain(Modes.dev, Modes.gain);
|
||||
fprintf(stderr, "Setting gain to: %.2f\n", Modes.gain/10.0);
|
||||
} else {
|
||||
fprintf(stderr, "Using automatic gain control.\n");
|
||||
}
|
||||
rtlsdr_set_freq_correction(Modes.dev, Modes.ppm_error);
|
||||
if (Modes.enable_agc) rtlsdr_set_agc_mode(Modes.dev, 1);
|
||||
rtlsdr_set_center_freq(Modes.dev, Modes.freq);
|
||||
rtlsdr_set_sample_rate(Modes.dev, MODES_DEFAULT_RATE);
|
||||
rtlsdr_reset_buffer(Modes.dev);
|
||||
fprintf(stderr, "Gain reported by device: %.2f\n",
|
||||
rtlsdr_get_tuner_gain(Modes.dev)/10.0);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// We use a thread reading data in background, while the main thread
|
||||
// handles decoding and visualization of data to the user.
|
||||
//
|
||||
// The reading thread calls the RTLSDR API to read data asynchronously, and
|
||||
// uses a callback to populate the data buffer.
|
||||
//
|
||||
// A Mutex is used to avoid races with the decoding thread.
|
||||
//
|
||||
void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) {
|
||||
|
||||
MODES_NOTUSED(ctx);
|
||||
|
||||
// Lock the data buffer variables before accessing them
|
||||
pthread_mutex_lock(&Modes.data_mutex);
|
||||
|
||||
Modes.iDataIn &= (MODES_ASYNC_BUF_NUMBER-1); // Just incase!!!
|
||||
|
||||
// Get the system time for this block
|
||||
ftime(&Modes.stSystemTimeRTL[Modes.iDataIn]);
|
||||
|
||||
if (len > MODES_ASYNC_BUF_SIZE) {len = MODES_ASYNC_BUF_SIZE;}
|
||||
|
||||
// Queue the new data
|
||||
Modes.pData[Modes.iDataIn] = (uint16_t *) buf;
|
||||
Modes.iDataIn = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn + 1);
|
||||
Modes.iDataReady = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn - Modes.iDataOut);
|
||||
|
||||
if (Modes.iDataReady == 0) {
|
||||
// Ooooops. We've just received the MODES_ASYNC_BUF_NUMBER'th outstanding buffer
|
||||
// This means that RTLSDR is currently overwriting the MODES_ASYNC_BUF_NUMBER+1
|
||||
// buffer, but we havent yet processed it, so we're going to lose it. There
|
||||
// isn't much we can do to recover the lost data, but we can correct things to
|
||||
// avoid any additional problems.
|
||||
Modes.iDataOut = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataOut+1);
|
||||
Modes.iDataReady = (MODES_ASYNC_BUF_NUMBER-1);
|
||||
Modes.iDataLost++;
|
||||
}
|
||||
|
||||
// Signal to the other thread that new data is ready, and unlock
|
||||
pthread_cond_signal(&Modes.data_cond);
|
||||
pthread_mutex_unlock(&Modes.data_mutex);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// This is used when --ifile is specified in order to read data from file
|
||||
// instead of using an RTLSDR device
|
||||
//
|
||||
void readDataFromFile(void) {
|
||||
pthread_mutex_lock(&Modes.data_mutex);
|
||||
while(Modes.exit == 0) {
|
||||
ssize_t nread, toread;
|
||||
unsigned char *p;
|
||||
|
||||
if (Modes.iDataReady) {
|
||||
pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Modes.interactive) {
|
||||
// When --ifile and --interactive are used together, slow down
|
||||
// playing at the natural rate of the RTLSDR received.
|
||||
pthread_mutex_unlock(&Modes.data_mutex);
|
||||
usleep(64000);
|
||||
pthread_mutex_lock(&Modes.data_mutex);
|
||||
}
|
||||
|
||||
toread = MODES_ASYNC_BUF_SIZE;
|
||||
p = (unsigned char *) Modes.pFileData;
|
||||
while(toread) {
|
||||
nread = read(Modes.fd, p, toread);
|
||||
if (nread <= 0) {
|
||||
Modes.exit = 1; // Signal the other threads to exit.
|
||||
break;
|
||||
}
|
||||
p += nread;
|
||||
toread -= nread;
|
||||
}
|
||||
if (toread) {
|
||||
// Not enough data on file to fill the buffer? Pad with no signal.
|
||||
memset(p,127,toread);
|
||||
}
|
||||
|
||||
Modes.iDataIn &= (MODES_ASYNC_BUF_NUMBER-1); // Just incase!!!
|
||||
|
||||
// Get the system time for this block
|
||||
ftime(&Modes.stSystemTimeRTL[Modes.iDataIn]);
|
||||
|
||||
// Queue the new data
|
||||
Modes.pData[Modes.iDataIn] = Modes.pFileData;
|
||||
Modes.iDataIn = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn + 1);
|
||||
Modes.iDataReady = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn - Modes.iDataOut);
|
||||
|
||||
// Signal to the other thread that new data is ready
|
||||
pthread_cond_signal(&Modes.data_cond);
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// We read data using a thread, so the main thread only handles decoding
|
||||
// without caring about data acquisition
|
||||
//
|
||||
void *readerThreadEntryPoint(void *arg) {
|
||||
MODES_NOTUSED(arg);
|
||||
|
||||
if (Modes.filename == NULL) {
|
||||
rtlsdr_read_async(Modes.dev, rtlsdrCallback, NULL,
|
||||
MODES_ASYNC_BUF_NUMBER,
|
||||
MODES_ASYNC_BUF_SIZE);
|
||||
} else {
|
||||
readDataFromFile();
|
||||
}
|
||||
// Signal to the other thread that new data is ready - dummy really so threads don't mutually lock
|
||||
pthread_cond_signal(&Modes.data_cond);
|
||||
pthread_mutex_unlock(&Modes.data_mutex);
|
||||
#ifndef _WIN32
|
||||
pthread_exit(NULL);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
//
|
||||
// ============================== Snip mode =================================
|
||||
//
|
||||
// Get raw IQ samples and filter everything is < than the specified level
|
||||
// for more than 256 samples in order to reduce example file size
|
||||
//
|
||||
void snipMode(int level) {
|
||||
int i, q;
|
||||
uint64_t c = 0;
|
||||
|
||||
while ((i = getchar()) != EOF && (q = getchar()) != EOF) {
|
||||
if (abs(i-127) < level && abs(q-127) < level) {
|
||||
c++;
|
||||
if (c > MODES_PREAMBLE_SIZE) continue;
|
||||
} else {
|
||||
c = 0;
|
||||
}
|
||||
putchar(i);
|
||||
putchar(q);
|
||||
}
|
||||
}
|
||||
//
|
||||
// ================================ Main ====================================
|
||||
//
|
||||
void showHelp(void) {
|
||||
printf(
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"| dump1090 ModeS Receiver Ver : " MODES_DUMP1090_VERSION " |\n"
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"--device-index <index> Select RTL device (default: 0)\n"
|
||||
"--gain <db> Set gain (default: max gain. Use -10 for auto-gain)\n"
|
||||
"--enable-agc Enable the Automatic Gain Control (default: off)\n"
|
||||
"--freq <hz> Set frequency (default: 1090 Mhz)\n"
|
||||
"--ifile <filename> Read data from file (use '-' for stdin)\n"
|
||||
"--interactive Interactive mode refreshing data on screen\n"
|
||||
"--interactive-rows <num> Max number of rows in interactive mode (default: 15)\n"
|
||||
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60)\n"
|
||||
"--interactive-rtl1090 Display flight table in RTL1090 format\n"
|
||||
"--raw Show only messages hex values\n"
|
||||
"--net Enable networking\n"
|
||||
"--modeac Enable decoding of SSR Modes 3/A & 3/C\n"
|
||||
"--net-beast TCP raw output in Beast binary format\n"
|
||||
"--net-only Enable just networking, no RTL device or file used\n"
|
||||
"--net-bind-address <ip> IP address to bind to (default: Any; Use 127.0.0.1 for private)\n"
|
||||
"--net-http-port <port> HTTP server port (default: 8080)\n"
|
||||
"--net-ri-port <port> TCP raw input listen port (default: 30001)\n"
|
||||
"--net-ro-port <port> TCP raw output listen port (default: 30002)\n"
|
||||
"--net-sbs-port <port> TCP BaseStation output listen port (default: 30003)\n"
|
||||
"--net-bi-port <port> TCP Beast input listen port (default: 30004)\n"
|
||||
"--net-bo-port <port> TCP Beast output listen port (default: 30005)\n"
|
||||
"--net-ro-size <size> TCP raw output minimum size (default: 0)\n"
|
||||
"--net-ro-rate <rate> TCP raw output memory flush rate (default: 0)\n"
|
||||
"--net-heartbeat <rate> TCP heartbeat rate in seconds (default: 60 sec; 0 to disable)\n"
|
||||
"--net-buffer <n> TCP buffer size 64Kb * (2^n) (default: n=0, 64Kb)\n"
|
||||
"--lat <latitude> Reference/receiver latitude for surface posn (opt)\n"
|
||||
"--lon <longitude> Reference/receiver longitude for surface posn (opt)\n"
|
||||
"--fix Enable single-bits error correction using CRC\n"
|
||||
"--no-fix Disable single-bits error correction using CRC\n"
|
||||
"--no-crc-check Disable messages with broken CRC (discouraged)\n"
|
||||
"--phase-enhance Enable phase enhancement\n"
|
||||
"--aggressive More CPU for more messages (two bits fixes, ...)\n"
|
||||
"--mlat display raw messages in Beast ascii mode\n"
|
||||
"--stats With --ifile print stats at exit. No other output\n"
|
||||
"--stats-every <seconds> Show and reset stats every <seconds> seconds\n"
|
||||
"--onlyaddr Show only ICAO addresses (testing purposes)\n"
|
||||
"--metric Use metric units (meters, km/h, ...)\n"
|
||||
"--snip <level> Strip IQ file removing samples < level\n"
|
||||
"--debug <flags> Debug mode (verbose), see README for details\n"
|
||||
"--quiet Disable output to stdout. Use for daemon applications\n"
|
||||
"--ppm <error> Set receiver error in parts per million (default 0)\n"
|
||||
"--help Show this help\n"
|
||||
"\n"
|
||||
"Debug mode flags: d = Log frames decoded with errors\n"
|
||||
" D = Log frames decoded with zero errors\n"
|
||||
" c = Log frames with bad CRC\n"
|
||||
" C = Log frames with good CRC\n"
|
||||
" p = Log frames with bad preamble\n"
|
||||
" n = Log network debugging info\n"
|
||||
" j = Log frames to frames.js, loadable by debug.html\n"
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void showCopyright(void) {
|
||||
uint64_t llTime = time(NULL) + 1;
|
||||
|
||||
printf(
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"| dump1090 ModeS Receiver Ver : " MODES_DUMP1090_VERSION " |\n"
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"\n"
|
||||
" Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>\n"
|
||||
" Copyright (C) 2014 by Malcolm Robb <support@attavionics.com>\n"
|
||||
"\n"
|
||||
" All rights reserved.\n"
|
||||
"\n"
|
||||
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
|
||||
" ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
|
||||
" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
|
||||
" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
|
||||
" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
|
||||
" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
|
||||
" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
|
||||
" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
|
||||
" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
|
||||
" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
|
||||
" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
"\n"
|
||||
" For further details refer to <https://github.com/MalcolmRobb/dump1090>\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
// delay for 1 second to give the user a chance to read the copyright
|
||||
while (llTime >= time(NULL)) {}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void display_stats(void) {
|
||||
int j;
|
||||
time_t now = time(NULL);
|
||||
|
||||
printf("\n\n");
|
||||
if (Modes.interactive)
|
||||
interactiveShowData();
|
||||
|
||||
printf("Statistics as at %s", ctime(&now));
|
||||
|
||||
printf("%d sample blocks processed\n", Modes.stat_blocks_processed);
|
||||
printf("%d sample blocks dropped\n", Modes.stat_blocks_dropped);
|
||||
|
||||
printf("%d ModeA/C detected\n", Modes.stat_ModeAC);
|
||||
printf("%d valid Mode-S preambles\n", Modes.stat_valid_preamble);
|
||||
printf("%d DF-?? fields corrected for length\n", Modes.stat_DF_Len_Corrected);
|
||||
printf("%d DF-?? fields corrected for type\n", Modes.stat_DF_Type_Corrected);
|
||||
printf("%d demodulated with 0 errors\n", Modes.stat_demodulated0);
|
||||
printf("%d demodulated with 1 error\n", Modes.stat_demodulated1);
|
||||
printf("%d demodulated with 2 errors\n", Modes.stat_demodulated2);
|
||||
printf("%d demodulated with > 2 errors\n", Modes.stat_demodulated3);
|
||||
printf("%d with good crc\n", Modes.stat_goodcrc);
|
||||
printf("%d with bad crc\n", Modes.stat_badcrc);
|
||||
printf("%d errors corrected\n", Modes.stat_fixed);
|
||||
|
||||
for (j = 0; j < MODES_MAX_BITERRORS; j++) {
|
||||
printf(" %d with %d bit %s\n", Modes.stat_bit_fix[j], j+1, (j==0)?"error":"errors");
|
||||
}
|
||||
|
||||
if (Modes.phase_enhance) {
|
||||
printf("%d phase enhancement attempts\n", Modes.stat_out_of_phase);
|
||||
printf("%d phase enhanced demodulated with 0 errors\n", Modes.stat_ph_demodulated0);
|
||||
printf("%d phase enhanced demodulated with 1 error\n", Modes.stat_ph_demodulated1);
|
||||
printf("%d phase enhanced demodulated with 2 errors\n", Modes.stat_ph_demodulated2);
|
||||
printf("%d phase enhanced demodulated with > 2 errors\n", Modes.stat_ph_demodulated3);
|
||||
printf("%d phase enhanced with good crc\n", Modes.stat_ph_goodcrc);
|
||||
printf("%d phase enhanced with bad crc\n", Modes.stat_ph_badcrc);
|
||||
printf("%d phase enhanced errors corrected\n", Modes.stat_ph_fixed);
|
||||
|
||||
for (j = 0; j < MODES_MAX_BITERRORS; j++) {
|
||||
printf(" %d with %d bit %s\n", Modes.stat_ph_bit_fix[j], j+1, (j==0)?"error":"errors");
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d total usable messages\n", Modes.stat_goodcrc + Modes.stat_ph_goodcrc + Modes.stat_fixed + Modes.stat_ph_fixed);
|
||||
fflush(stdout);
|
||||
|
||||
Modes.stat_blocks_processed =
|
||||
Modes.stat_blocks_dropped = 0;
|
||||
|
||||
Modes.stat_ModeAC =
|
||||
Modes.stat_valid_preamble =
|
||||
Modes.stat_DF_Len_Corrected =
|
||||
Modes.stat_DF_Type_Corrected =
|
||||
Modes.stat_demodulated0 =
|
||||
Modes.stat_demodulated1 =
|
||||
Modes.stat_demodulated2 =
|
||||
Modes.stat_demodulated3 =
|
||||
Modes.stat_goodcrc =
|
||||
Modes.stat_badcrc =
|
||||
Modes.stat_fixed = 0;
|
||||
|
||||
Modes.stat_out_of_phase =
|
||||
Modes.stat_ph_demodulated0 =
|
||||
Modes.stat_ph_demodulated1 =
|
||||
Modes.stat_ph_demodulated2 =
|
||||
Modes.stat_ph_demodulated3 =
|
||||
Modes.stat_ph_goodcrc =
|
||||
Modes.stat_ph_badcrc =
|
||||
Modes.stat_ph_fixed = 0;
|
||||
|
||||
for (j = 0; j < MODES_MAX_BITERRORS; j++) {
|
||||
Modes.stat_ph_bit_fix[j] = 0;
|
||||
Modes.stat_bit_fix[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// This function is called a few times every second by main in order to
|
||||
// perform tasks we need to do continuously, like accepting new clients
|
||||
// from the net, refreshing the screen in interactive mode, and so forth
|
||||
//
|
||||
void backgroundTasks(void) {
|
||||
static time_t next_stats;
|
||||
|
||||
if (Modes.net) {
|
||||
modesReadFromClients();
|
||||
}
|
||||
|
||||
// If Modes.aircrafts is not NULL, remove any stale aircraft
|
||||
if (Modes.aircrafts) {
|
||||
interactiveRemoveStaleAircrafts();
|
||||
}
|
||||
|
||||
// Refresh screen when in interactive mode
|
||||
if (Modes.interactive) {
|
||||
interactiveShowData();
|
||||
}
|
||||
|
||||
if (Modes.stats > 0) {
|
||||
time_t now = time(NULL);
|
||||
if (now > next_stats) {
|
||||
if (next_stats != 0)
|
||||
display_stats();
|
||||
next_stats = now + Modes.stats;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
int verbose_device_search(char *s)
|
||||
{
|
||||
int i, device_count, device, offset;
|
||||
char *s2;
|
||||
char vendor[256], product[256], serial[256];
|
||||
device_count = rtlsdr_get_device_count();
|
||||
if (!device_count) {
|
||||
fprintf(stderr, "No supported devices found.\n");
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "Found %d device(s):\n", device_count);
|
||||
for (i = 0; i < device_count; i++) {
|
||||
rtlsdr_get_device_usb_strings(i, vendor, product, serial);
|
||||
fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
/* does string look like raw id number */
|
||||
device = (int)strtol(s, &s2, 0);
|
||||
if (s2[0] == '\0' && device >= 0 && device < device_count) {
|
||||
fprintf(stderr, "Using device %d: %s\n",
|
||||
device, rtlsdr_get_device_name((uint32_t)device));
|
||||
return device;
|
||||
}
|
||||
/* does string exact match a serial */
|
||||
for (i = 0; i < device_count; i++) {
|
||||
rtlsdr_get_device_usb_strings(i, vendor, product, serial);
|
||||
if (strcmp(s, serial) != 0) {
|
||||
continue;}
|
||||
device = i;
|
||||
fprintf(stderr, "Using device %d: %s\n",
|
||||
device, rtlsdr_get_device_name((uint32_t)device));
|
||||
return device;
|
||||
}
|
||||
/* does string prefix match a serial */
|
||||
for (i = 0; i < device_count; i++) {
|
||||
rtlsdr_get_device_usb_strings(i, vendor, product, serial);
|
||||
if (strncmp(s, serial, strlen(s)) != 0) {
|
||||
continue;}
|
||||
device = i;
|
||||
fprintf(stderr, "Using device %d: %s\n",
|
||||
device, rtlsdr_get_device_name((uint32_t)device));
|
||||
return device;
|
||||
}
|
||||
/* does string suffix match a serial */
|
||||
for (i = 0; i < device_count; i++) {
|
||||
rtlsdr_get_device_usb_strings(i, vendor, product, serial);
|
||||
offset = strlen(serial) - strlen(s);
|
||||
if (offset < 0) {
|
||||
continue;}
|
||||
if (strncmp(s, serial+offset, strlen(s)) != 0) {
|
||||
continue;}
|
||||
device = i;
|
||||
fprintf(stderr, "Using device %d: %s\n",
|
||||
device, rtlsdr_get_device_name((uint32_t)device));
|
||||
return device;
|
||||
}
|
||||
fprintf(stderr, "No matching devices found.\n");
|
||||
return -1;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
int main(int argc, char **argv) {
|
||||
int j;
|
||||
|
||||
// Set sane defaults
|
||||
modesInitConfig();
|
||||
signal(SIGINT, sigintHandler); // Define Ctrl/C handler (exit program)
|
||||
|
||||
// Parse the command line options
|
||||
for (j = 1; j < argc; j++) {
|
||||
int more = j+1 < argc; // There are more arguments
|
||||
|
||||
if (!strcmp(argv[j],"--device-index") && more) {
|
||||
Modes.dev_index = verbose_device_search(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--gain") && more) {
|
||||
Modes.gain = (int) (atof(argv[++j])*10); // Gain is in tens of DBs
|
||||
} else if (!strcmp(argv[j],"--enable-agc")) {
|
||||
Modes.enable_agc++;
|
||||
} else if (!strcmp(argv[j],"--freq") && more) {
|
||||
Modes.freq = (int) strtoll(argv[++j],NULL,10);
|
||||
} else if (!strcmp(argv[j],"--ifile") && more) {
|
||||
Modes.filename = strdup(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--fix")) {
|
||||
Modes.nfix_crc = 1;
|
||||
} else if (!strcmp(argv[j],"--no-fix")) {
|
||||
Modes.nfix_crc = 0;
|
||||
} else if (!strcmp(argv[j],"--no-crc-check")) {
|
||||
Modes.check_crc = 0;
|
||||
} else if (!strcmp(argv[j],"--phase-enhance")) {
|
||||
Modes.phase_enhance = 1;
|
||||
} else if (!strcmp(argv[j],"--raw")) {
|
||||
Modes.raw = 1;
|
||||
} else if (!strcmp(argv[j],"--net")) {
|
||||
Modes.net = 1;
|
||||
} else if (!strcmp(argv[j],"--modeac")) {
|
||||
Modes.mode_ac = 1;
|
||||
} else if (!strcmp(argv[j],"--net-beast")) {
|
||||
Modes.beast = 1;
|
||||
} else if (!strcmp(argv[j],"--net-only")) {
|
||||
Modes.net = 1;
|
||||
Modes.net_only = 1;
|
||||
} else if (!strcmp(argv[j],"--net-heartbeat") && more) {
|
||||
Modes.net_heartbeat_rate = atoi(argv[++j]) * 15;
|
||||
} else if (!strcmp(argv[j],"--net-ro-size") && more) {
|
||||
Modes.net_output_raw_size = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-ro-rate") && more) {
|
||||
Modes.net_output_raw_rate = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-ro-port") && more) {
|
||||
if (Modes.beast) // Required for legacy backward compatibility
|
||||
{Modes.net_output_beast_port = atoi(argv[++j]);;}
|
||||
else
|
||||
{Modes.net_output_raw_port = atoi(argv[++j]);}
|
||||
} else if (!strcmp(argv[j],"--net-ri-port") && more) {
|
||||
Modes.net_input_raw_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-bo-port") && more) {
|
||||
Modes.net_output_beast_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-bi-port") && more) {
|
||||
Modes.net_input_beast_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-bind-address") && more) {
|
||||
Modes.net_bind_address = strdup(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-http-port") && more) {
|
||||
Modes.net_http_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-sbs-port") && more) {
|
||||
Modes.net_output_sbs_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-buffer") && more) {
|
||||
Modes.net_sndbuf_size = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--onlyaddr")) {
|
||||
Modes.onlyaddr = 1;
|
||||
} else if (!strcmp(argv[j],"--metric")) {
|
||||
Modes.metric = 1;
|
||||
} else if (!strcmp(argv[j],"--aggressive")) {
|
||||
Modes.nfix_crc = MODES_MAX_BITERRORS;
|
||||
} else if (!strcmp(argv[j],"--interactive")) {
|
||||
Modes.interactive = 1;
|
||||
} else if (!strcmp(argv[j],"--interactive-rows") && more) {
|
||||
Modes.interactive_rows = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--interactive-ttl") && more) {
|
||||
Modes.interactive_display_ttl = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--lat") && more) {
|
||||
Modes.fUserLat = atof(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--lon") && more) {
|
||||
Modes.fUserLon = atof(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--debug") && more) {
|
||||
char *f = argv[++j];
|
||||
while(*f) {
|
||||
switch(*f) {
|
||||
case 'D': Modes.debug |= MODES_DEBUG_DEMOD; break;
|
||||
case 'd': Modes.debug |= MODES_DEBUG_DEMODERR; break;
|
||||
case 'C': Modes.debug |= MODES_DEBUG_GOODCRC; break;
|
||||
case 'c': Modes.debug |= MODES_DEBUG_BADCRC; break;
|
||||
case 'p': Modes.debug |= MODES_DEBUG_NOPREAMBLE; break;
|
||||
case 'n': Modes.debug |= MODES_DEBUG_NET; break;
|
||||
case 'j': Modes.debug |= MODES_DEBUG_JS; break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown debugging flag: %c\n", *f);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
f++;
|
||||
}
|
||||
} else if (!strcmp(argv[j],"--stats")) {
|
||||
Modes.stats = -1;
|
||||
} else if (!strcmp(argv[j],"--stats-every") && more) {
|
||||
Modes.stats = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--snip") && more) {
|
||||
snipMode(atoi(argv[++j]));
|
||||
exit(0);
|
||||
} else if (!strcmp(argv[j],"--help")) {
|
||||
showHelp();
|
||||
exit(0);
|
||||
} else if (!strcmp(argv[j],"--ppm") && more) {
|
||||
Modes.ppm_error = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--quiet")) {
|
||||
Modes.quiet = 1;
|
||||
} else if (!strcmp(argv[j],"--mlat")) {
|
||||
Modes.mlat = 1;
|
||||
} else if (!strcmp(argv[j],"--interactive-rtl1090")) {
|
||||
Modes.interactive = 1;
|
||||
Modes.interactive_rtl1090 = 1;
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Unknown or not enough arguments for option '%s'.\n\n",
|
||||
argv[j]);
|
||||
showHelp();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Try to comply with the Copyright license conditions for binary distribution
|
||||
if (!Modes.quiet) {showCopyright();}
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
// Setup for SIGWINCH for handling lines
|
||||
if (Modes.interactive) {signal(SIGWINCH, sigWinchCallback);}
|
||||
#endif
|
||||
|
||||
// Initialization
|
||||
modesInit();
|
||||
|
||||
if (Modes.net_only) {
|
||||
fprintf(stderr,"Net-only mode, no RTL device or file open.\n");
|
||||
} else if (Modes.filename == NULL) {
|
||||
modesInitRTLSDR();
|
||||
} else {
|
||||
if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') {
|
||||
Modes.fd = STDIN_FILENO;
|
||||
} else if ((Modes.fd = open(Modes.filename,
|
||||
#ifdef _WIN32
|
||||
(O_RDONLY | O_BINARY)
|
||||
#else
|
||||
(O_RDONLY)
|
||||
#endif
|
||||
)) == -1) {
|
||||
perror("Opening data file");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (Modes.net) modesInitNet();
|
||||
|
||||
// If the user specifies --net-only, just run in order to serve network
|
||||
// clients without reading data from the RTL device
|
||||
while (Modes.net_only) {
|
||||
if (Modes.exit) exit(0); // If we exit net_only nothing further in main()
|
||||
backgroundTasks();
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
// Create the thread that will read the data from the device.
|
||||
pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL);
|
||||
pthread_mutex_lock(&Modes.data_mutex);
|
||||
|
||||
while (Modes.exit == 0) {
|
||||
|
||||
if (Modes.iDataReady == 0) {
|
||||
pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex); // This unlocks Modes.data_mutex, and waits for Modes.data_cond
|
||||
continue; // Once (Modes.data_cond) occurs, it locks Modes.data_mutex
|
||||
}
|
||||
|
||||
// Modes.data_mutex is Locked, and (Modes.iDataReady != 0)
|
||||
if (Modes.iDataReady) { // Check we have new data, just in case!!
|
||||
|
||||
Modes.iDataOut &= (MODES_ASYNC_BUF_NUMBER-1); // Just incase
|
||||
|
||||
// Translate the next lot of I/Q samples into Modes.magnitude
|
||||
computeMagnitudeVector(Modes.pData[Modes.iDataOut]);
|
||||
|
||||
Modes.stSystemTimeBlk = Modes.stSystemTimeRTL[Modes.iDataOut];
|
||||
|
||||
// Update the input buffer pointer queue
|
||||
Modes.iDataOut = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataOut + 1);
|
||||
Modes.iDataReady = (MODES_ASYNC_BUF_NUMBER-1) & (Modes.iDataIn - Modes.iDataOut);
|
||||
|
||||
// If we lost some blocks, correct the timestamp
|
||||
if (Modes.iDataLost) {
|
||||
Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES * 6 * Modes.iDataLost);
|
||||
Modes.stat_blocks_dropped += Modes.iDataLost;
|
||||
Modes.iDataLost = 0;
|
||||
}
|
||||
|
||||
// It's safe to release the lock now
|
||||
pthread_cond_signal (&Modes.data_cond);
|
||||
pthread_mutex_unlock(&Modes.data_mutex);
|
||||
|
||||
// Process data after releasing the lock, so that the capturing
|
||||
// thread can read data while we perform computationally expensive
|
||||
// stuff at the same time.
|
||||
detectModeS(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES);
|
||||
|
||||
// Update the timestamp ready for the next block
|
||||
Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*6);
|
||||
Modes.stat_blocks_processed++;
|
||||
} else {
|
||||
pthread_cond_signal (&Modes.data_cond);
|
||||
pthread_mutex_unlock(&Modes.data_mutex);
|
||||
}
|
||||
|
||||
backgroundTasks();
|
||||
pthread_mutex_lock(&Modes.data_mutex);
|
||||
}
|
||||
|
||||
// If --stats were given, print statistics
|
||||
if (Modes.stats) {
|
||||
display_stats();
|
||||
}
|
||||
|
||||
if (Modes.filename == NULL) {
|
||||
rtlsdr_cancel_async(Modes.dev); // Cancel rtlsdr_read_async will cause data input thread to terminate cleanly
|
||||
rtlsdr_close(Modes.dev);
|
||||
}
|
||||
pthread_cond_destroy(&Modes.data_cond); // Thread cleanup
|
||||
pthread_mutex_destroy(&Modes.data_mutex);
|
||||
pthread_join(Modes.reader_thread,NULL); // Wait on reader thread exit
|
||||
#ifndef _WIN32
|
||||
pthread_exit(0);
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
|
@ -1,152 +0,0 @@
|
|||
# Microsoft Developer Studio Project File - Name="dump1090" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=dump1090 - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "dump1090.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "dump1090.mak" CFG="dump1090 - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "dump1090 - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "dump1090 - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "dump1090 - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
|
||||
# ADD CPP /nologo /W3 /GX /O2 /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
|
||||
# SUBTRACT CPP /YX /Yc /Yu
|
||||
# ADD BASE RSC /l 0x809 /d "NDEBUG"
|
||||
# ADD RSC /l 0x809 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 ws2_32.lib /nologo /subsystem:console /machine:I386 /out:"./dump1090.exe"
|
||||
|
||||
!ELSEIF "$(CFG)" == "dump1090 - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
|
||||
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
|
||||
# SUBTRACT CPP /YX /Yc /Yu
|
||||
# ADD BASE RSC /l 0x809 /d "_DEBUG"
|
||||
# ADD RSC /l 0x809 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "dump1090 - Win32 Release"
|
||||
# Name "dump1090 - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\anet.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\dump1090.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\interactive.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\mode_ac.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\mode_s.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\net_io.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\anet.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\dump1090.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\winstubs.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\Dump1090.rc
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Library Files"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pthreads\pthreadVC2.lib
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\rtlsdr\rtlsdr.lib
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
|
@ -1,41 +0,0 @@
|
|||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "dump1090"=.\dump1090.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "view1090"=.\view1090.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
|
@ -1,466 +0,0 @@
|
|||
// dump1090, a Mode S messages decoder for RTLSDR devices.
|
||||
//
|
||||
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#ifndef __DUMP1090_H
|
||||
#define __DUMP1090_H
|
||||
|
||||
// File Version number
|
||||
// ====================
|
||||
// Format is : MajorVer.MinorVer.DayMonth.Year"
|
||||
// MajorVer changes only with significant changes
|
||||
// MinorVer changes when additional features are added, but not for bug fixes (range 00-99)
|
||||
// DayDate & Year changes for all changes, including for bug fixes. It represent the release date of the update
|
||||
//
|
||||
#define MODES_DUMP1090_VERSION "1.10.3010.14"
|
||||
|
||||
// ============================= Include files ==========================
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "rtl-sdr.h"
|
||||
#include "anet.h"
|
||||
#else
|
||||
#include "winstubs.h" //Put everything Windows specific in here
|
||||
#include "rtl-sdr.h"
|
||||
#include "anet.h"
|
||||
#endif
|
||||
|
||||
// ============================= #defines ===============================
|
||||
//
|
||||
// If you have a valid coaa.h, these values will come from it. If not,
|
||||
// then you can enter your own values in the #else section here
|
||||
//
|
||||
#ifdef USER_LATITUDE
|
||||
#define MODES_USER_LATITUDE_DFLT (USER_LATITUDE)
|
||||
#define MODES_USER_LONGITUDE_DFLT (USER_LONGITUDE)
|
||||
#else
|
||||
#define MODES_USER_LATITUDE_DFLT (0.0)
|
||||
#define MODES_USER_LONGITUDE_DFLT (0.0)
|
||||
#endif
|
||||
|
||||
#define MODES_DEFAULT_PPM 52
|
||||
#define MODES_DEFAULT_RATE 2000000
|
||||
#define MODES_DEFAULT_FREQ 1090000000
|
||||
#define MODES_DEFAULT_WIDTH 1000
|
||||
#define MODES_DEFAULT_HEIGHT 700
|
||||
#define MODES_ASYNC_BUF_NUMBER 16
|
||||
#define MODES_ASYNC_BUF_SIZE (16*16384) // 256k
|
||||
#define MODES_ASYNC_BUF_SAMPLES (MODES_ASYNC_BUF_SIZE / 2) // Each sample is 2 bytes
|
||||
#define MODES_AUTO_GAIN -100 // Use automatic gain
|
||||
#define MODES_MAX_GAIN 999999 // Use max available gain
|
||||
#define MODES_MSG_SQUELCH_LEVEL 0x02FF // Average signal strength limit
|
||||
#define MODES_MSG_ENCODER_ERRS 3 // Maximum number of encoding errors
|
||||
|
||||
// When changing, change also fixBitErrors() and modesInitErrorTable() !!
|
||||
#define MODES_MAX_BITERRORS 2 // Global max for fixable bit erros
|
||||
|
||||
#define MODEAC_MSG_SAMPLES (25 * 2) // include up to the SPI bit
|
||||
#define MODEAC_MSG_BYTES 2
|
||||
#define MODEAC_MSG_SQUELCH_LEVEL 0x07FF // Average signal strength limit
|
||||
#define MODEAC_MSG_FLAG (1<<0)
|
||||
#define MODEAC_MSG_MODES_HIT (1<<1)
|
||||
#define MODEAC_MSG_MODEA_HIT (1<<2)
|
||||
#define MODEAC_MSG_MODEC_HIT (1<<3)
|
||||
#define MODEAC_MSG_MODEA_ONLY (1<<4)
|
||||
#define MODEAC_MSG_MODEC_OLD (1<<5)
|
||||
|
||||
#define MODES_PREAMBLE_US 8 // microseconds = bits
|
||||
#define MODES_PREAMBLE_SAMPLES (MODES_PREAMBLE_US * 2)
|
||||
#define MODES_PREAMBLE_SIZE (MODES_PREAMBLE_SAMPLES * sizeof(uint16_t))
|
||||
#define MODES_LONG_MSG_BYTES 14
|
||||
#define MODES_SHORT_MSG_BYTES 7
|
||||
#define MODES_LONG_MSG_BITS (MODES_LONG_MSG_BYTES * 8)
|
||||
#define MODES_SHORT_MSG_BITS (MODES_SHORT_MSG_BYTES * 8)
|
||||
#define MODES_LONG_MSG_SAMPLES (MODES_LONG_MSG_BITS * 2)
|
||||
#define MODES_SHORT_MSG_SAMPLES (MODES_SHORT_MSG_BITS * 2)
|
||||
#define MODES_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t))
|
||||
#define MODES_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t))
|
||||
|
||||
#define MODES_RAWOUT_BUF_SIZE (1500)
|
||||
#define MODES_RAWOUT_BUF_FLUSH (MODES_RAWOUT_BUF_SIZE - 200)
|
||||
#define MODES_RAWOUT_BUF_RATE (1000) // 1000 * 64mS = 1 Min approx
|
||||
|
||||
#define MODES_ICAO_CACHE_LEN 1024 // Power of two required
|
||||
#define MODES_ICAO_CACHE_TTL 60 // Time to live of cached addresses
|
||||
#define MODES_UNIT_FEET 0
|
||||
#define MODES_UNIT_METERS 1
|
||||
|
||||
#define MODES_USER_LATLON_VALID (1<<0)
|
||||
|
||||
#define MODES_ACFLAGS_LATLON_VALID (1<<0) // Aircraft Lat/Lon is decoded
|
||||
#define MODES_ACFLAGS_ALTITUDE_VALID (1<<1) // Aircraft altitude is known
|
||||
#define MODES_ACFLAGS_HEADING_VALID (1<<2) // Aircraft heading is known
|
||||
#define MODES_ACFLAGS_SPEED_VALID (1<<3) // Aircraft speed is known
|
||||
#define MODES_ACFLAGS_VERTRATE_VALID (1<<4) // Aircraft vertical rate is known
|
||||
#define MODES_ACFLAGS_SQUAWK_VALID (1<<5) // Aircraft Mode A Squawk is known
|
||||
#define MODES_ACFLAGS_CALLSIGN_VALID (1<<6) // Aircraft Callsign Identity
|
||||
#define MODES_ACFLAGS_EWSPEED_VALID (1<<7) // Aircraft East West Speed is known
|
||||
#define MODES_ACFLAGS_NSSPEED_VALID (1<<8) // Aircraft North South Speed is known
|
||||
#define MODES_ACFLAGS_AOG (1<<9) // Aircraft is On the Ground
|
||||
#define MODES_ACFLAGS_LLEVEN_VALID (1<<10) // Aircraft Even Lot/Lon is known
|
||||
#define MODES_ACFLAGS_LLODD_VALID (1<<11) // Aircraft Odd Lot/Lon is known
|
||||
#define MODES_ACFLAGS_AOG_VALID (1<<12) // MODES_ACFLAGS_AOG is valid
|
||||
#define MODES_ACFLAGS_FS_VALID (1<<13) // Aircraft Flight Status is known
|
||||
#define MODES_ACFLAGS_NSEWSPD_VALID (1<<14) // Aircraft EW and NS Speed is known
|
||||
#define MODES_ACFLAGS_LATLON_REL_OK (1<<15) // Indicates it's OK to do a relative CPR
|
||||
|
||||
#define MODES_ACFLAGS_LLEITHER_VALID (MODES_ACFLAGS_LLEVEN_VALID | MODES_ACFLAGS_LLODD_VALID)
|
||||
#define MODES_ACFLAGS_LLBOTH_VALID (MODES_ACFLAGS_LLEVEN_VALID | MODES_ACFLAGS_LLODD_VALID)
|
||||
#define MODES_ACFLAGS_AOG_GROUND (MODES_ACFLAGS_AOG_VALID | MODES_ACFLAGS_AOG)
|
||||
|
||||
#define MODES_DEBUG_DEMOD (1<<0)
|
||||
#define MODES_DEBUG_DEMODERR (1<<1)
|
||||
#define MODES_DEBUG_BADCRC (1<<2)
|
||||
#define MODES_DEBUG_GOODCRC (1<<3)
|
||||
#define MODES_DEBUG_NOPREAMBLE (1<<4)
|
||||
#define MODES_DEBUG_NET (1<<5)
|
||||
#define MODES_DEBUG_JS (1<<6)
|
||||
|
||||
// When debug is set to MODES_DEBUG_NOPREAMBLE, the first sample must be
|
||||
// at least greater than a given level for us to dump the signal.
|
||||
#define MODES_DEBUG_NOPREAMBLE_LEVEL 25
|
||||
|
||||
#define MODES_INTERACTIVE_REFRESH_TIME 250 // Milliseconds
|
||||
#define MODES_INTERACTIVE_ROWS 22 // Rows on screen
|
||||
#define MODES_INTERACTIVE_DELETE_TTL 300 // Delete from the list after 300 seconds
|
||||
#define MODES_INTERACTIVE_DISPLAY_TTL 60 // Delete from display after 60 seconds
|
||||
|
||||
#define MODES_NET_HEARTBEAT_RATE 900 // Each block is approx 65mS - default is > 1 min
|
||||
|
||||
#define MODES_NET_SERVICES_NUM 6
|
||||
#define MODES_NET_INPUT_RAW_PORT 30001
|
||||
#define MODES_NET_OUTPUT_RAW_PORT 30002
|
||||
#define MODES_NET_OUTPUT_SBS_PORT 30003
|
||||
#define MODES_NET_INPUT_BEAST_PORT 30004
|
||||
#define MODES_NET_OUTPUT_BEAST_PORT 30005
|
||||
#define MODES_NET_HTTP_PORT 8080
|
||||
#define MODES_CLIENT_BUF_SIZE 1024
|
||||
#define MODES_NET_SNDBUF_SIZE (1024*64)
|
||||
#define MODES_NET_SNDBUF_MAX (7)
|
||||
|
||||
#ifndef HTMLPATH
|
||||
#define HTMLPATH "./public_html" // default path for gmap.html etc
|
||||
#endif
|
||||
|
||||
#define MODES_NOTUSED(V) ((void) V)
|
||||
|
||||
//======================== structure declarations =========================
|
||||
|
||||
// Structure used to describe a networking client
|
||||
struct client {
|
||||
struct client* next; // Pointer to next client
|
||||
int fd; // File descriptor
|
||||
int service; // TCP port the client is connected to
|
||||
int buflen; // Amount of data on buffer
|
||||
char buf[MODES_CLIENT_BUF_SIZE+1]; // Read buffer
|
||||
};
|
||||
|
||||
// Structure used to describe an aircraft in iteractive mode
|
||||
struct aircraft {
|
||||
uint32_t addr; // ICAO address
|
||||
char flight[16]; // Flight number
|
||||
unsigned char signalLevel[8]; // Last 8 Signal Amplitudes
|
||||
int altitude; // Altitude
|
||||
int speed; // Velocity
|
||||
int track; // Angle of flight
|
||||
int vert_rate; // Vertical rate.
|
||||
time_t seen; // Time at which the last packet was received
|
||||
time_t seenLatLon; // Time at which the last lat long was calculated
|
||||
uint64_t timestamp; // Timestamp at which the last packet was received
|
||||
uint64_t timestampLatLon;// Timestamp at which the last lat long was calculated
|
||||
long messages; // Number of Mode S messages received
|
||||
int modeA; // Squawk
|
||||
int modeC; // Altitude
|
||||
long modeAcount; // Mode A Squawk hit Count
|
||||
long modeCcount; // Mode C Altitude hit Count
|
||||
int modeACflags; // Flags for mode A/C recognition
|
||||
|
||||
// Encoded latitude and longitude as extracted by odd and even CPR encoded messages
|
||||
int odd_cprlat;
|
||||
int odd_cprlon;
|
||||
int even_cprlat;
|
||||
int even_cprlon;
|
||||
uint64_t odd_cprtime;
|
||||
uint64_t even_cprtime;
|
||||
double lat, lon; // Coordinated obtained from CPR encoded data
|
||||
int bFlags; // Flags related to valid fields in this structure
|
||||
struct aircraft *next; // Next aircraft in our linked list
|
||||
};
|
||||
|
||||
struct stDF {
|
||||
struct stDF *pNext; // Pointer to next item in the linked list
|
||||
struct stDF *pPrev; // Pointer to previous item in the linked list
|
||||
struct aircraft *pAircraft; // Pointer to the Aircraft structure for this DF
|
||||
time_t seen; // Dos/UNIX Time at which the this packet was received
|
||||
uint64_t llTimestamp; // Timestamp at which the this packet was received
|
||||
uint32_t addr; // Timestamp at which the this packet was received
|
||||
unsigned char msg[MODES_LONG_MSG_BYTES]; // the binary
|
||||
} tDF;
|
||||
|
||||
// Program global state
|
||||
struct { // Internal state
|
||||
pthread_t reader_thread;
|
||||
|
||||
pthread_mutex_t data_mutex; // Mutex to synchronize buffer access
|
||||
pthread_cond_t data_cond; // Conditional variable associated
|
||||
uint16_t *pData [MODES_ASYNC_BUF_NUMBER]; // Raw IQ sample buffers from RTL
|
||||
struct timeb stSystemTimeRTL[MODES_ASYNC_BUF_NUMBER]; // System time when RTL passed us this block
|
||||
int iDataIn; // Fifo input pointer
|
||||
int iDataOut; // Fifo output pointer
|
||||
int iDataReady; // Fifo content count
|
||||
int iDataLost; // Count of missed buffers
|
||||
|
||||
uint16_t *pFileData; // Raw IQ samples buffer (from a File)
|
||||
uint16_t *magnitude; // Magnitude vector
|
||||
uint64_t timestampBlk; // Timestamp of the start of the current block
|
||||
struct timeb stSystemTimeBlk; // System time when RTL passed us currently processing this block
|
||||
int fd; // --ifile option file descriptor
|
||||
uint32_t *icao_cache; // Recently seen ICAO addresses cache
|
||||
uint16_t *maglut; // I/Q -> Magnitude lookup table
|
||||
int exit; // Exit from the main loop when true
|
||||
|
||||
// RTLSDR
|
||||
int dev_index;
|
||||
int gain;
|
||||
int enable_agc;
|
||||
rtlsdr_dev_t *dev;
|
||||
int freq;
|
||||
int ppm_error;
|
||||
|
||||
// Networking
|
||||
char aneterr[ANET_ERR_LEN];
|
||||
struct client *clients; // Our clients
|
||||
int sbsos; // SBS output listening socket
|
||||
int ros; // Raw output listening socket
|
||||
int ris; // Raw input listening socket
|
||||
int bos; // Beast output listening socket
|
||||
int bis; // Beast input listening socket
|
||||
int https; // HTTP listening socket
|
||||
char *rawOut; // Buffer for building raw output data
|
||||
int rawOutUsed; // How much of the buffer is currently used
|
||||
char *beastOut; // Buffer for building beast output data
|
||||
int beastOutUsed; // How much if the buffer is currently used
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData; // Windows socket initialisation
|
||||
#endif
|
||||
|
||||
// Configuration
|
||||
char *filename; // Input form file, --ifile option
|
||||
int phase_enhance; // Enable phase enhancement if true
|
||||
int nfix_crc; // Number of crc bit error(s) to correct
|
||||
int check_crc; // Only display messages with good CRC
|
||||
int raw; // Raw output format
|
||||
int beast; // Beast binary format output
|
||||
int mode_ac; // Enable decoding of SSR Modes A & C
|
||||
int debug; // Debugging mode
|
||||
int net; // Enable networking
|
||||
int net_only; // Enable just networking
|
||||
int net_heartbeat_count; // TCP heartbeat counter
|
||||
int net_heartbeat_rate; // TCP heartbeat rate
|
||||
int net_output_sbs_port; // SBS output TCP port
|
||||
int net_output_raw_size; // Minimum Size of the output raw data
|
||||
int net_output_raw_rate; // Rate (in 64mS increments) of output raw data
|
||||
int net_output_raw_rate_count; // Rate (in 64mS increments) of output raw data
|
||||
int net_output_raw_port; // Raw output TCP port
|
||||
int net_input_raw_port; // Raw input TCP port
|
||||
int net_output_beast_port; // Beast output TCP port
|
||||
int net_input_beast_port; // Beast input TCP port
|
||||
char *net_bind_address; // Bind address
|
||||
int net_http_port; // HTTP port
|
||||
int net_sndbuf_size; // TCP output buffer size (64Kb * 2^n)
|
||||
int quiet; // Suppress stdout
|
||||
int interactive; // Interactive mode
|
||||
int interactive_rows; // Interactive mode: max number of rows
|
||||
int interactive_display_ttl; // Interactive mode: TTL display
|
||||
int interactive_delete_ttl; // Interactive mode: TTL before deletion
|
||||
int stats; // Print stats at exit in --ifile mode
|
||||
int onlyaddr; // Print only ICAO addresses
|
||||
int metric; // Use metric units
|
||||
int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...;
|
||||
int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090
|
||||
|
||||
// User details
|
||||
double fUserLat; // Users receiver/antenna lat/lon needed for initial surface location
|
||||
double fUserLon; // Users receiver/antenna lat/lon needed for initial surface location
|
||||
int bUserFlags; // Flags relating to the user details
|
||||
|
||||
// Interactive mode
|
||||
struct aircraft *aircrafts;
|
||||
uint64_t interactive_last_update; // Last screen update in milliseconds
|
||||
time_t last_cleanup_time; // Last cleanup time in seconds
|
||||
|
||||
// DF List mode
|
||||
int bEnableDFLogging; // Set to enable DF Logging
|
||||
pthread_mutex_t pDF_mutex; // Mutex to synchronize pDF access
|
||||
struct stDF *pDF; // Pointer to DF list
|
||||
|
||||
// Statistics
|
||||
unsigned int stat_valid_preamble;
|
||||
unsigned int stat_demodulated0;
|
||||
unsigned int stat_demodulated1;
|
||||
unsigned int stat_demodulated2;
|
||||
unsigned int stat_demodulated3;
|
||||
unsigned int stat_goodcrc;
|
||||
unsigned int stat_badcrc;
|
||||
unsigned int stat_fixed;
|
||||
|
||||
// Histogram of fixed bit errors: index 0 for single bit erros,
|
||||
// index 1 for double bit errors etc.
|
||||
unsigned int stat_bit_fix[MODES_MAX_BITERRORS];
|
||||
|
||||
unsigned int stat_http_requests;
|
||||
unsigned int stat_sbs_connections;
|
||||
unsigned int stat_raw_connections;
|
||||
unsigned int stat_beast_connections;
|
||||
unsigned int stat_out_of_phase;
|
||||
unsigned int stat_ph_demodulated0;
|
||||
unsigned int stat_ph_demodulated1;
|
||||
unsigned int stat_ph_demodulated2;
|
||||
unsigned int stat_ph_demodulated3;
|
||||
unsigned int stat_ph_goodcrc;
|
||||
unsigned int stat_ph_badcrc;
|
||||
unsigned int stat_ph_fixed;
|
||||
// Histogram of fixed bit errors: index 0 for single bit erros,
|
||||
// index 1 for double bit errors etc.
|
||||
unsigned int stat_ph_bit_fix[MODES_MAX_BITERRORS];
|
||||
|
||||
unsigned int stat_DF_Len_Corrected;
|
||||
unsigned int stat_DF_Type_Corrected;
|
||||
unsigned int stat_ModeAC;
|
||||
|
||||
unsigned int stat_blocks_processed;
|
||||
unsigned int stat_blocks_dropped;
|
||||
} Modes;
|
||||
|
||||
// The struct we use to store information about a decoded message.
|
||||
struct modesMessage {
|
||||
// Generic fields
|
||||
unsigned char msg[MODES_LONG_MSG_BYTES]; // Binary message.
|
||||
int msgbits; // Number of bits in message
|
||||
int msgtype; // Downlink format #
|
||||
int crcok; // True if CRC was valid
|
||||
uint32_t crc; // Message CRC
|
||||
int correctedbits; // No. of bits corrected
|
||||
char corrected[MODES_MAX_BITERRORS]; // corrected bit positions
|
||||
uint32_t addr; // ICAO Address from bytes 1 2 and 3
|
||||
int phase_corrected; // True if phase correction was applied
|
||||
uint64_t timestampMsg; // Timestamp of the message
|
||||
int remote; // If set this message is from a remote station
|
||||
unsigned char signalLevel; // Signal Amplitude
|
||||
|
||||
// DF 11
|
||||
int ca; // Responder capabilities
|
||||
int iid;
|
||||
|
||||
// DF 17, DF 18
|
||||
int metype; // Extended squitter message type.
|
||||
int mesub; // Extended squitter message subtype.
|
||||
int heading; // Reported by aircraft, or computed from from EW and NS velocity
|
||||
int raw_latitude; // Non decoded latitude.
|
||||
int raw_longitude; // Non decoded longitude.
|
||||
double fLat; // Coordinates obtained from CPR encoded data if/when decoded
|
||||
double fLon; // Coordinates obtained from CPR encoded data if/when decoded
|
||||
char flight[16]; // 8 chars flight number.
|
||||
int ew_velocity; // E/W velocity.
|
||||
int ns_velocity; // N/S velocity.
|
||||
int vert_rate; // Vertical rate.
|
||||
int velocity; // Reported by aircraft, or computed from from EW and NS velocity
|
||||
|
||||
// DF4, DF5, DF20, DF21
|
||||
int fs; // Flight status for DF4,5,20,21
|
||||
int modeA; // 13 bits identity (Squawk).
|
||||
|
||||
// Fields used by multiple message types.
|
||||
int altitude;
|
||||
int unit;
|
||||
int bFlags; // Flags related to fields in this structure
|
||||
};
|
||||
|
||||
// ======================== function declarations =========================
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//
|
||||
// Functions exported from mode_ac.c
|
||||
//
|
||||
int detectModeA (uint16_t *m, struct modesMessage *mm);
|
||||
void decodeModeAMessage(struct modesMessage *mm, int ModeA);
|
||||
int ModeAToModeC (unsigned int ModeA);
|
||||
|
||||
//
|
||||
// Functions exported from mode_s.c
|
||||
//
|
||||
void detectModeS (uint16_t *m, uint32_t mlen);
|
||||
void decodeModesMessage (struct modesMessage *mm, unsigned char *msg);
|
||||
void displayModesMessage(struct modesMessage *mm);
|
||||
void useModesMessage (struct modesMessage *mm);
|
||||
void computeMagnitudeVector(uint16_t *pData);
|
||||
int decodeCPR (struct aircraft *a, int fflag, int surface);
|
||||
int decodeCPRrelative (struct aircraft *a, int fflag, int surface);
|
||||
void modesInitErrorInfo ();
|
||||
//
|
||||
// Functions exported from interactive.c
|
||||
//
|
||||
struct aircraft* interactiveReceiveData(struct modesMessage *mm);
|
||||
void interactiveShowData(void);
|
||||
void interactiveRemoveStaleAircrafts(void);
|
||||
int decodeBinMessage (struct client *c, char *p);
|
||||
struct aircraft *interactiveFindAircraft(uint32_t addr);
|
||||
struct stDF *interactiveFindDF (uint32_t addr);
|
||||
|
||||
//
|
||||
// Functions exported from net_io.c
|
||||
//
|
||||
void modesInitNet (void);
|
||||
void modesReadFromClients (void);
|
||||
void modesSendAllClients (int service, void *msg, int len);
|
||||
void modesQueueOutput (struct modesMessage *mm);
|
||||
void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __DUMP1090_H
|
|
@ -1,115 +0,0 @@
|
|||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#define APSTUDIO_HIDDEN_SYMBOLS
|
||||
#include "windows.h"
|
||||
#undef APSTUDIO_HIDDEN_SYMBOLS
|
||||
#include "ntverp.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifndef _MAC
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,10,2910,14
|
||||
PRODUCTVERSION 1,10,2910,14
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x3L
|
||||
FILESUBTYPE 0x7L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "Dump1090 for Win32 \0"
|
||||
VALUE "CompanyName", "\0"
|
||||
VALUE "FileDescription", "Mode A/C/S decoder for RTL Dongles\0"
|
||||
VALUE "FileVersion", "1, 10, 2910, 14\0"
|
||||
VALUE "InternalName", "DUMP1090.EXE\0"
|
||||
VALUE "LegalCopyright", "Copyright © 2012 by Salvatore Sanfilippo <antirez@gmail.com>\r\nCopyright © 2014 by Malcolm Robb <support@attavionics.com>\0"
|
||||
VALUE "LegalTrademarks", "\0"
|
||||
VALUE "OriginalFilename", "DUMP1090.EXE\0"
|
||||
VALUE "PrivateBuild", "\0"
|
||||
VALUE "ProductName", "DUMP1090\0"
|
||||
VALUE "ProductVersion", "1, 10, 2910, 14\0"
|
||||
VALUE "SpecialBuild", "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // !_MAC
|
||||
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
|
||||
"#include ""windows.h""\r\n"
|
||||
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
|
||||
"#include ""ntverp.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
#
|
||||
# Provides: dump1090
|
||||
# Required-Start: $remote_fs
|
||||
# Required-Stop: $remote_fs
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: dump1090 initscript
|
||||
|
||||
#
|
||||
### END INIT INFO
|
||||
## Fill in name of program here.
|
||||
PROG="dump1090"
|
||||
PROG_PATH="/home/pi/dump1090"
|
||||
PROG_ARGS="--quiet --net --net-ro-size 500 --net-ro-rate 5 --net-buffer 5"
|
||||
PIDFILE="/var/run/dump1090.pid"
|
||||
|
||||
start() {
|
||||
if [ -e $PIDFILE ]; then
|
||||
## Program is running, exit with error.
|
||||
echo "Error! $PROG is currently running!" 1>&2
|
||||
exit 1
|
||||
else
|
||||
## Change from /dev/null to something like /var/log/$PROG if you want to save output.
|
||||
cd $PROG_PATH
|
||||
./$PROG $PROG_ARGS 2>&1 >/dev/null &
|
||||
echo "$PROG started"
|
||||
touch $PIDFILE
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
if [ -e $PIDFILE ]; then
|
||||
## Program is running, so stop it
|
||||
echo "$PROG is running"
|
||||
killall $PROG
|
||||
rm -f $PIDFILE
|
||||
echo "$PROG stopped"
|
||||
else
|
||||
## Program is not running, exit with error.
|
||||
echo "Error! $PROG not started!" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
## Check to see if we are running as root first.
|
||||
## Found at http://www.cyberciti.biz/tips/shell-root-user-check-script.html
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "This script must be run as root" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
exit 0
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
exit 0
|
||||
;;
|
||||
reload|restart|force-reload)
|
||||
stop
|
||||
start
|
||||
exit 0
|
||||
;;
|
||||
**)
|
||||
echo "Usage: $0 {start|stop|reload}" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
#
|
||||
|
|
@ -1,649 +0,0 @@
|
|||
// dump1090, a Mode S messages decoder for RTLSDR devices.
|
||||
//
|
||||
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
#include "dump1090.h"
|
||||
#include <math.h>
|
||||
#include <locale.h>
|
||||
#include <wchar.h>
|
||||
|
||||
//
|
||||
// ============================= Utility functions ==========================
|
||||
//
|
||||
static uint64_t mstime(void) {
|
||||
struct timeval tv;
|
||||
uint64_t mst;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
mst = ((uint64_t)tv.tv_sec)*1000;
|
||||
mst += tv.tv_usec/1000;
|
||||
return mst;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Add a new DF structure to the interactive mode linked list
|
||||
//
|
||||
void interactiveCreateDF(struct aircraft *a, struct modesMessage *mm) {
|
||||
struct stDF *pDF = (struct stDF *) malloc(sizeof(*pDF));
|
||||
|
||||
if (pDF) {
|
||||
// Default everything to zero/NULL
|
||||
memset(pDF, 0, sizeof(*pDF));
|
||||
|
||||
// Now initialise things
|
||||
pDF->seen = a->seen;
|
||||
pDF->llTimestamp = mm->timestampMsg;
|
||||
pDF->addr = mm->addr;
|
||||
pDF->pAircraft = a;
|
||||
memcpy(pDF->msg, mm->msg, MODES_LONG_MSG_BYTES);
|
||||
|
||||
if (!pthread_mutex_lock(&Modes.pDF_mutex)) {
|
||||
if ((pDF->pNext = Modes.pDF)) {
|
||||
Modes.pDF->pPrev = pDF;
|
||||
}
|
||||
Modes.pDF = pDF;
|
||||
pthread_mutex_unlock(&Modes.pDF_mutex);
|
||||
} else {
|
||||
free(pDF);
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// Remove stale DF's from the interactive mode linked list
|
||||
//
|
||||
void interactiveRemoveStaleDF(time_t now) {
|
||||
struct stDF *pDF = NULL;
|
||||
struct stDF *prev = NULL;
|
||||
|
||||
// Only fiddle with the DF list if we gain possession of the mutex
|
||||
// If we fail to get the mutex we'll get another chance to tidy the
|
||||
// DF list in a second or so.
|
||||
if (!pthread_mutex_trylock(&Modes.pDF_mutex)) {
|
||||
pDF = Modes.pDF;
|
||||
while(pDF) {
|
||||
if ((now - pDF->seen) > Modes.interactive_delete_ttl) {
|
||||
if (Modes.pDF == pDF) {
|
||||
Modes.pDF = NULL;
|
||||
} else {
|
||||
prev->pNext = NULL;
|
||||
}
|
||||
|
||||
// All DF's in the list from here onwards will be time
|
||||
// expired, so delete them all
|
||||
while (pDF) {
|
||||
prev = pDF; pDF = pDF->pNext;
|
||||
free(prev);
|
||||
}
|
||||
|
||||
} else {
|
||||
prev = pDF; pDF = pDF->pNext;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock (&Modes.pDF_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
struct stDF *interactiveFindDF(uint32_t addr) {
|
||||
struct stDF *pDF = NULL;
|
||||
|
||||
if (!pthread_mutex_lock(&Modes.pDF_mutex)) {
|
||||
pDF = Modes.pDF;
|
||||
while(pDF) {
|
||||
if (pDF->addr == addr) {
|
||||
pthread_mutex_unlock (&Modes.pDF_mutex);
|
||||
return (pDF);
|
||||
}
|
||||
pDF = pDF->pNext;
|
||||
}
|
||||
pthread_mutex_unlock (&Modes.pDF_mutex);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
//
|
||||
//========================= Interactive mode ===============================
|
||||
//
|
||||
// Return a new aircraft structure for the interactive mode linked list
|
||||
// of aircraft
|
||||
//
|
||||
struct aircraft *interactiveCreateAircraft(struct modesMessage *mm) {
|
||||
struct aircraft *a = (struct aircraft *) malloc(sizeof(*a));
|
||||
|
||||
// Default everything to zero/NULL
|
||||
memset(a, 0, sizeof(*a));
|
||||
|
||||
// Now initialise things that should not be 0/NULL to their defaults
|
||||
a->addr = mm->addr;
|
||||
a->lat = a->lon = 0.0;
|
||||
memset(a->signalLevel, mm->signalLevel, 8); // First time, initialise everything
|
||||
// to the first signal strength
|
||||
|
||||
// mm->msgtype 32 is used to represent Mode A/C. These values can never change, so
|
||||
// set them once here during initialisation, and don't bother to set them every
|
||||
// time this ModeA/C is received again in the future
|
||||
if (mm->msgtype == 32) {
|
||||
int modeC = ModeAToModeC(mm->modeA | mm->fs);
|
||||
a->modeACflags = MODEAC_MSG_FLAG;
|
||||
if (modeC < -12) {
|
||||
a->modeACflags |= MODEAC_MSG_MODEA_ONLY;
|
||||
} else {
|
||||
mm->altitude = modeC * 100;
|
||||
mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID;
|
||||
}
|
||||
}
|
||||
return (a);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Return the aircraft with the specified address, or NULL if no aircraft
|
||||
// exists with this address.
|
||||
//
|
||||
struct aircraft *interactiveFindAircraft(uint32_t addr) {
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
|
||||
while(a) {
|
||||
if (a->addr == addr) return (a);
|
||||
a = a->next;
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// We have received a Mode A or C response.
|
||||
//
|
||||
// Search through the list of known Mode-S aircraft and tag them if this Mode A/C
|
||||
// matches their known Mode S Squawks or Altitudes(+/- 50feet).
|
||||
//
|
||||
// A Mode S equipped aircraft may also respond to Mode A and Mode C SSR interrogations.
|
||||
// We can't tell if this is a Mode A or C, so scan through the entire aircraft list
|
||||
// looking for matches on Mode A (squawk) and Mode C (altitude). Flag in the Mode S
|
||||
// records that we have had a potential Mode A or Mode C response from this aircraft.
|
||||
//
|
||||
// If an aircraft responds to Mode A then it's highly likely to be responding to mode C
|
||||
// too, and vice verca. Therefore, once the mode S record is tagged with both a Mode A
|
||||
// and a Mode C flag, we can be fairly confident that this Mode A/C frame relates to that
|
||||
// Mode S aircraft.
|
||||
//
|
||||
// Mode C's are more likely to clash than Mode A's; There could be several aircraft
|
||||
// cruising at FL370, but it's less likely (though not impossible) that there are two
|
||||
// aircraft on the same squawk. Therefore, give precidence to Mode A record matches
|
||||
//
|
||||
// Note : It's theoretically possible for an aircraft to have the same value for Mode A
|
||||
// and Mode C. Therefore we have to check BOTH A AND C for EVERY S.
|
||||
//
|
||||
void interactiveUpdateAircraftModeA(struct aircraft *a) {
|
||||
struct aircraft *b = Modes.aircrafts;
|
||||
|
||||
while(b) {
|
||||
if ((b->modeACflags & MODEAC_MSG_FLAG) == 0) {// skip any fudged ICAO records
|
||||
|
||||
// If both (a) and (b) have valid squawks...
|
||||
if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) {
|
||||
// ...check for Mode-A == Mode-S Squawk matches
|
||||
if (a->modeA == b->modeA) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk
|
||||
b->modeAcount = a->messages;
|
||||
b->modeACflags |= MODEAC_MSG_MODEA_HIT;
|
||||
a->modeACflags |= MODEAC_MSG_MODEA_HIT;
|
||||
if ( (b->modeAcount > 0) &&
|
||||
( (b->modeCcount > 1)
|
||||
|| (a->modeACflags & MODEAC_MSG_MODEA_ONLY)) ) // Allow Mode-A only matches if this Mode-A is invalid Mode-C
|
||||
{a->modeACflags |= MODEAC_MSG_MODES_HIT;} // flag this ModeA/C probably belongs to a known Mode S
|
||||
}
|
||||
}
|
||||
|
||||
// If both (a) and (b) have valid altitudes...
|
||||
if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) {
|
||||
// ... check for Mode-C == Mode-S Altitude matches
|
||||
if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude
|
||||
|| (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft
|
||||
|| (a->modeC + 1 == b->modeC ) ) { // or this Mode-C + 100 ft
|
||||
b->modeCcount = a->messages;
|
||||
b->modeACflags |= MODEAC_MSG_MODEC_HIT;
|
||||
a->modeACflags |= MODEAC_MSG_MODEC_HIT;
|
||||
if ( (b->modeAcount > 0) &&
|
||||
(b->modeCcount > 1) )
|
||||
{a->modeACflags |= (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD);} // flag this ModeA/C probably belongs to a known Mode S
|
||||
}
|
||||
}
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
void interactiveUpdateAircraftModeS() {
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
|
||||
while(a) {
|
||||
int flags = a->modeACflags;
|
||||
if (flags & MODEAC_MSG_FLAG) { // find any fudged ICAO records
|
||||
|
||||
// clear the current A,C and S hit bits ready for this attempt
|
||||
a->modeACflags = flags & ~(MODEAC_MSG_MODEA_HIT | MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODES_HIT);
|
||||
|
||||
interactiveUpdateAircraftModeA(a); // and attempt to match them with Mode-S
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Receive new messages and populate the interactive mode with more info
|
||||
//
|
||||
struct aircraft *interactiveReceiveData(struct modesMessage *mm) {
|
||||
struct aircraft *a, *aux;
|
||||
|
||||
// Return if (checking crc) AND (not crcok) AND (not fixed)
|
||||
if (Modes.check_crc && (mm->crcok == 0) && (mm->correctedbits == 0))
|
||||
return NULL;
|
||||
|
||||
// Lookup our aircraft or create a new one
|
||||
a = interactiveFindAircraft(mm->addr);
|
||||
if (!a) { // If it's a currently unknown aircraft....
|
||||
a = interactiveCreateAircraft(mm); // ., create a new record for it,
|
||||
a->next = Modes.aircrafts; // .. and put it at the head of the list
|
||||
Modes.aircrafts = a;
|
||||
} else {
|
||||
/* If it is an already known aircraft, move it on head
|
||||
* so we keep aircrafts ordered by received message time.
|
||||
*
|
||||
* However move it on head only if at least one second elapsed
|
||||
* since the aircraft that is currently on head sent a message,
|
||||
* othewise with multiple aircrafts at the same time we have an
|
||||
* useless shuffle of positions on the screen. */
|
||||
if (0 && Modes.aircrafts != a && (time(NULL) - a->seen) >= 1) {
|
||||
aux = Modes.aircrafts;
|
||||
while(aux->next != a) aux = aux->next;
|
||||
/* Now we are a node before the aircraft to remove. */
|
||||
aux->next = aux->next->next; /* removed. */
|
||||
/* Add on head */
|
||||
a->next = Modes.aircrafts;
|
||||
Modes.aircrafts = a;
|
||||
}
|
||||
}
|
||||
|
||||
a->signalLevel[a->messages & 7] = mm->signalLevel;// replace the 8th oldest signal strength
|
||||
a->seen = time(NULL);
|
||||
a->timestamp = mm->timestampMsg;
|
||||
a->messages++;
|
||||
|
||||
// If a (new) CALLSIGN has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {
|
||||
memcpy(a->flight, mm->flight, sizeof(a->flight));
|
||||
}
|
||||
|
||||
// If a (new) ALTITUDE has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
||||
if ( (a->modeCcount) // if we've a modeCcount already
|
||||
&& (a->altitude != mm->altitude ) ) // and Altitude has changed
|
||||
// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet
|
||||
// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet
|
||||
{
|
||||
a->modeCcount = 0; //....zero the hit count
|
||||
a->modeACflags &= ~MODEAC_MSG_MODEC_HIT;
|
||||
}
|
||||
a->altitude = mm->altitude;
|
||||
a->modeC = (mm->altitude + 49) / 100;
|
||||
}
|
||||
|
||||
// If a (new) SQUAWK has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
|
||||
if (a->modeA != mm->modeA) {
|
||||
a->modeAcount = 0; // Squawk has changed, so zero the hit count
|
||||
a->modeACflags &= ~MODEAC_MSG_MODEA_HIT;
|
||||
}
|
||||
a->modeA = mm->modeA;
|
||||
}
|
||||
|
||||
// If a (new) HEADING has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) {
|
||||
a->track = mm->heading;
|
||||
}
|
||||
|
||||
// If a (new) SPEED has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) {
|
||||
a->speed = mm->velocity;
|
||||
}
|
||||
|
||||
// If a (new) Vertical Descent rate has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {
|
||||
a->vert_rate = mm->vert_rate;
|
||||
}
|
||||
|
||||
// if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags
|
||||
if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) {
|
||||
a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG);
|
||||
}
|
||||
|
||||
// If we've got a new cprlat or cprlon
|
||||
if (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) {
|
||||
int location_ok = 0;
|
||||
|
||||
if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) {
|
||||
a->odd_cprlat = mm->raw_latitude;
|
||||
a->odd_cprlon = mm->raw_longitude;
|
||||
a->odd_cprtime = mstime();
|
||||
} else {
|
||||
a->even_cprlat = mm->raw_latitude;
|
||||
a->even_cprlon = mm->raw_longitude;
|
||||
a->even_cprtime = mstime();
|
||||
}
|
||||
|
||||
// If we have enough recent data, try global CPR
|
||||
if (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID && abs((int)(a->even_cprtime - a->odd_cprtime)) <= 10000) {
|
||||
if (decodeCPR(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) {
|
||||
location_ok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise try relative CPR.
|
||||
if (!location_ok && decodeCPRrelative(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) {
|
||||
location_ok = 1;
|
||||
}
|
||||
|
||||
//If we sucessfully decoded, back copy the results to mm so that we can print them in list output
|
||||
if (location_ok) {
|
||||
mm->bFlags |= MODES_ACFLAGS_LATLON_VALID;
|
||||
mm->fLat = a->lat;
|
||||
mm->fLon = a->lon;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the aircrafts a->bFlags to reflect the newly received mm->bFlags;
|
||||
a->bFlags |= mm->bFlags;
|
||||
|
||||
if (mm->msgtype == 32) {
|
||||
int flags = a->modeACflags;
|
||||
if ((flags & (MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODEC_OLD)) == MODEAC_MSG_MODEC_OLD) {
|
||||
//
|
||||
// This Mode-C doesn't currently hit any known Mode-S, but it used to because MODEAC_MSG_MODEC_OLD is
|
||||
// set So the aircraft it used to match has either changed altitude, or gone out of our receiver range
|
||||
//
|
||||
// We've now received this Mode-A/C again, so it must be a new aircraft. It could be another aircraft
|
||||
// at the same Mode-C altitude, or it could be a new airctraft with a new Mods-A squawk.
|
||||
//
|
||||
// To avoid masking this aircraft from the interactive display, clear the MODEAC_MSG_MODES_OLD flag
|
||||
// and set messages to 1;
|
||||
//
|
||||
a->modeACflags = flags & ~MODEAC_MSG_MODEC_OLD;
|
||||
a->messages = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are Logging DF's, and it's not a Mode A/C
|
||||
if ((Modes.bEnableDFLogging) && (mm->msgtype < 32)) {
|
||||
interactiveCreateDF(a,mm);
|
||||
}
|
||||
|
||||
return (a);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Show the currently captured interactive data on screen.
|
||||
//
|
||||
void interactiveShowData(void) {
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
time_t now = time(NULL);
|
||||
int count = 0;
|
||||
char progress;
|
||||
char spinner[4] = "|/-\\";
|
||||
|
||||
// Refresh screen every (MODES_INTERACTIVE_REFRESH_TIME) miliseconde
|
||||
if ((mstime() - Modes.interactive_last_update) < MODES_INTERACTIVE_REFRESH_TIME)
|
||||
{return;}
|
||||
|
||||
Modes.interactive_last_update = mstime();
|
||||
|
||||
// Attempt to reconsile any ModeA/C with known Mode-S
|
||||
// We can't condition on Modes.modeac because ModeA/C could be comming
|
||||
// in from a raw input port which we can't turn off.
|
||||
interactiveUpdateAircraftModeS();
|
||||
|
||||
progress = spinner[time(NULL)%4];
|
||||
|
||||
#ifndef _WIN32
|
||||
printf("\x1b[H\x1b[2J"); // Clear the screen
|
||||
#else
|
||||
cls();
|
||||
#endif
|
||||
setlocale(LC_ALL,"");
|
||||
|
||||
//\xiB[30;47m
|
||||
if (Modes.interactive_rtl1090 == 0) {
|
||||
printf (
|
||||
// original version "Hex Mode Sqwk Flight Alt Spd Hdg Lat Long Sig Msgs Ti%c\n", progress);
|
||||
// pitft version "\x1B[30;47m\e[1mFlight Alt Spd Lat Long \n", progress);
|
||||
"\x1B[30;47m\e[1m Flight Alt(m) km/h D(km) H S ");
|
||||
} else {
|
||||
printf (
|
||||
"Hex Flight Alt V/S GS TT SSR G*456^ Msgs Seen %c\n", progress);
|
||||
}
|
||||
// printf(
|
||||
//"---------------------------------------");
|
||||
printf("\x1B[37;40m");
|
||||
int numNoDir = 0;
|
||||
// while(a && (count < 10)) {
|
||||
while(a && (count < Modes.interactive_rows)) {
|
||||
if ((now - a->seen) < Modes.interactive_display_ttl)
|
||||
{
|
||||
int msgs = a->messages;
|
||||
int flags = a->modeACflags;
|
||||
|
||||
if ( (((flags & (MODEAC_MSG_FLAG )) == 0 ) )
|
||||
|| (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEA_ONLY)) == MODEAC_MSG_MODEA_ONLY) && (msgs > 4 ) )
|
||||
|| (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD )) == 0 ) && (msgs > 127) )
|
||||
) {
|
||||
int altitude = a->altitude, speed = a->speed;
|
||||
char strSquawk[5] = " ";
|
||||
char strFl[6] = " ";
|
||||
char strTt[5] = " ";
|
||||
char strGs[5] = " ";
|
||||
|
||||
// Convert units to metric if --metric was specified
|
||||
//if (Modes.metric) {
|
||||
altitude = (int) (altitude / 3.2828);
|
||||
speed = (int) (speed * 1.852);
|
||||
//}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
|
||||
snprintf(strSquawk,5,"%04x", a->modeA);}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) {
|
||||
snprintf (strGs, 5,"%3d", speed);}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) {
|
||||
snprintf (strTt, 5,"%03d", a->track);}
|
||||
|
||||
if (msgs > 99999) {
|
||||
msgs = 99999;}
|
||||
|
||||
// Dump1090 display mode
|
||||
char strMode[5] = " ";
|
||||
char strLat[8] = " ";
|
||||
char strLon[9] = " ";
|
||||
|
||||
char strD[5] = " ";
|
||||
char cLat = ' ';
|
||||
char cLon = ' ';
|
||||
|
||||
double d;
|
||||
|
||||
unsigned char * pSig = a->signalLevel;
|
||||
unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] +
|
||||
pSig[4] + pSig[5] + pSig[6] + pSig[7] + 3) >> 3;
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_AOG) {
|
||||
snprintf(strFl, 6," grnd");
|
||||
} else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
||||
snprintf(strFl, 6, "%5d", altitude);
|
||||
}
|
||||
|
||||
|
||||
if ((flags & MODEAC_MSG_FLAG) == 0) {
|
||||
strMode[0] = 'S';
|
||||
} else if (flags & MODEAC_MSG_MODEA_ONLY) {
|
||||
strMode[0] = 'A';
|
||||
}
|
||||
if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';}
|
||||
if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) {
|
||||
|
||||
double dLon = a->lon+87.6651033;
|
||||
double dLat = a->lat-41.9809263;
|
||||
|
||||
snprintf(strLat, 8,"%7.03f", dLat);
|
||||
snprintf(strLon, 9,"%8.03f", dLon);
|
||||
|
||||
double x = dLon * M_PI / 180.0f * cos(((a->lat+41.9809263)/2.0f) * M_PI / 180.0f);
|
||||
double y = dLat * M_PI / 180.0f;
|
||||
d = sqrt(x*x + y*y) * 6371.0;
|
||||
|
||||
if(fabs(dLon) < .01 && fabs(dLat) > fabs(dLon)) {
|
||||
cLon = ' ';
|
||||
} else {
|
||||
if(dLon < 0) {
|
||||
cLon = 'W';
|
||||
} else {
|
||||
cLon = 'E';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(fabs(dLat) < .01 && fabs(dLon) > fabs(dLat)) {
|
||||
cLat = ' ';
|
||||
} else {
|
||||
if(dLat < 0) {
|
||||
cLat = 'S';
|
||||
} else {
|
||||
cLat = 'N';
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(strD, 5,"%4.01f", d);
|
||||
|
||||
/*
|
||||
//formatted for Unifont 8x16, rotated 90
|
||||
printf("\n\x1B[%d;31m%-8s\x1B[%d;32m%5s \x1B[%d;33m%3s \x1B[%d;34m%6s \x1B[%d;36m%c%c \x1B[%d;37m%lc",
|
||||
count%2, a->flight,
|
||||
count%2, strFl,
|
||||
count%2, strGs,
|
||||
count%2, strD,
|
||||
count%2, cLat, cLon,
|
||||
count%2, 0x2581 + (wint_t) (2*signalAverage));
|
||||
*/
|
||||
|
||||
//formatted for terminusBold 10x20, no unicode
|
||||
|
||||
if(d<5) {
|
||||
printf("%ls",L"\n\x1B[1;37m┌──────────────────────────────┐\n│");
|
||||
} else {
|
||||
printf("\n ");
|
||||
}
|
||||
|
||||
if ((now - a->seen) > 30 ) {
|
||||
printf("\x1B[1;30m%-8s%5s %4s %5s %c%c %d",
|
||||
a->flight,
|
||||
strFl,
|
||||
strGs,
|
||||
strD,
|
||||
cLat, cLon,
|
||||
(int)((float)signalAverage/25.0f));
|
||||
} else {
|
||||
printf("\x1B[1;31m%-8s\x1B[1;32m%5s \x1B[1;33m%4s \x1B[1;34m%5s \x1B[1;36m%c%c \x1B[1;35m%d",
|
||||
a->flight,
|
||||
strFl,
|
||||
strGs,
|
||||
strD,
|
||||
cLat, cLon,
|
||||
(int)((float)signalAverage/25.0f));
|
||||
}
|
||||
|
||||
if(d<5) {
|
||||
printf("%ls",L"\x1B[1;37m│\n└──────────────────────────────┘");
|
||||
count+=2;
|
||||
}
|
||||
|
||||
count++;
|
||||
} else {
|
||||
numNoDir++;
|
||||
}
|
||||
}
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
|
||||
while(count < 10) {
|
||||
printf("\n");
|
||||
count++;
|
||||
}
|
||||
|
||||
printf("\x1B[30;47m\e[1m\n%+3d %c \x1B[37;40m",numNoDir,progress);
|
||||
fflush(stdout);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// When in interactive mode If we don't receive new nessages within
|
||||
// MODES_INTERACTIVE_DELETE_TTL seconds we remove the aircraft from the list.
|
||||
//
|
||||
void interactiveRemoveStaleAircrafts(void) {
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
struct aircraft *prev = NULL;
|
||||
time_t now = time(NULL);
|
||||
|
||||
// Only do cleanup once per second
|
||||
if (Modes.last_cleanup_time != now) {
|
||||
Modes.last_cleanup_time = now;
|
||||
|
||||
interactiveRemoveStaleDF(now);
|
||||
|
||||
while(a) {
|
||||
if ((now - a->seen) > Modes.interactive_delete_ttl) {
|
||||
// Remove the element from the linked list, with care
|
||||
// if we are removing the first element
|
||||
if (!prev) {
|
||||
Modes.aircrafts = a->next; free(a); a = Modes.aircrafts;
|
||||
} else {
|
||||
prev->next = a->next; free(a); a = prev->next;
|
||||
}
|
||||
} else {
|
||||
prev = a; a = a->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
|
@ -1,561 +0,0 @@
|
|||
// dump1090, a Mode S messages decoder for RTLSDR devices.
|
||||
//
|
||||
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
#include "dump1090.h"
|
||||
//
|
||||
// ============================= Utility functions ==========================
|
||||
//
|
||||
static uint64_t mstime(void) {
|
||||
struct timeval tv;
|
||||
uint64_t mst;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
mst = ((uint64_t)tv.tv_sec)*1000;
|
||||
mst += tv.tv_usec/1000;
|
||||
return mst;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Add a new DF structure to the interactive mode linked list
|
||||
//
|
||||
void interactiveCreateDF(struct aircraft *a, struct modesMessage *mm) {
|
||||
struct stDF *pDF = (struct stDF *) malloc(sizeof(*pDF));
|
||||
|
||||
if (pDF) {
|
||||
// Default everything to zero/NULL
|
||||
memset(pDF, 0, sizeof(*pDF));
|
||||
|
||||
// Now initialise things
|
||||
pDF->seen = a->seen;
|
||||
pDF->llTimestamp = mm->timestampMsg;
|
||||
pDF->addr = mm->addr;
|
||||
pDF->pAircraft = a;
|
||||
memcpy(pDF->msg, mm->msg, MODES_LONG_MSG_BYTES);
|
||||
|
||||
if (!pthread_mutex_lock(&Modes.pDF_mutex)) {
|
||||
if ((pDF->pNext = Modes.pDF)) {
|
||||
Modes.pDF->pPrev = pDF;
|
||||
}
|
||||
Modes.pDF = pDF;
|
||||
pthread_mutex_unlock(&Modes.pDF_mutex);
|
||||
} else {
|
||||
free(pDF);
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// Remove stale DF's from the interactive mode linked list
|
||||
//
|
||||
void interactiveRemoveStaleDF(time_t now) {
|
||||
struct stDF *pDF = NULL;
|
||||
struct stDF *prev = NULL;
|
||||
|
||||
// Only fiddle with the DF list if we gain possession of the mutex
|
||||
// If we fail to get the mutex we'll get another chance to tidy the
|
||||
// DF list in a second or so.
|
||||
if (!pthread_mutex_trylock(&Modes.pDF_mutex)) {
|
||||
pDF = Modes.pDF;
|
||||
while(pDF) {
|
||||
if ((now - pDF->seen) > Modes.interactive_delete_ttl) {
|
||||
if (Modes.pDF == pDF) {
|
||||
Modes.pDF = NULL;
|
||||
} else {
|
||||
prev->pNext = NULL;
|
||||
}
|
||||
|
||||
// All DF's in the list from here onwards will be time
|
||||
// expired, so delete them all
|
||||
while (pDF) {
|
||||
prev = pDF; pDF = pDF->pNext;
|
||||
free(prev);
|
||||
}
|
||||
|
||||
} else {
|
||||
prev = pDF; pDF = pDF->pNext;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock (&Modes.pDF_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
struct stDF *interactiveFindDF(uint32_t addr) {
|
||||
struct stDF *pDF = NULL;
|
||||
|
||||
if (!pthread_mutex_lock(&Modes.pDF_mutex)) {
|
||||
pDF = Modes.pDF;
|
||||
while(pDF) {
|
||||
if (pDF->addr == addr) {
|
||||
pthread_mutex_unlock (&Modes.pDF_mutex);
|
||||
return (pDF);
|
||||
}
|
||||
pDF = pDF->pNext;
|
||||
}
|
||||
pthread_mutex_unlock (&Modes.pDF_mutex);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
//
|
||||
//========================= Interactive mode ===============================
|
||||
//
|
||||
// Return a new aircraft structure for the interactive mode linked list
|
||||
// of aircraft
|
||||
//
|
||||
struct aircraft *interactiveCreateAircraft(struct modesMessage *mm) {
|
||||
struct aircraft *a = (struct aircraft *) malloc(sizeof(*a));
|
||||
|
||||
// Default everything to zero/NULL
|
||||
memset(a, 0, sizeof(*a));
|
||||
|
||||
// Now initialise things that should not be 0/NULL to their defaults
|
||||
a->addr = mm->addr;
|
||||
a->lat = a->lon = 0.0;
|
||||
memset(a->signalLevel, mm->signalLevel, 8); // First time, initialise everything
|
||||
// to the first signal strength
|
||||
|
||||
// mm->msgtype 32 is used to represent Mode A/C. These values can never change, so
|
||||
// set them once here during initialisation, and don't bother to set them every
|
||||
// time this ModeA/C is received again in the future
|
||||
if (mm->msgtype == 32) {
|
||||
int modeC = ModeAToModeC(mm->modeA | mm->fs);
|
||||
a->modeACflags = MODEAC_MSG_FLAG;
|
||||
if (modeC < -12) {
|
||||
a->modeACflags |= MODEAC_MSG_MODEA_ONLY;
|
||||
} else {
|
||||
mm->altitude = modeC * 100;
|
||||
mm->bFlags |= MODES_ACFLAGS_ALTITUDE_VALID;
|
||||
}
|
||||
}
|
||||
return (a);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Return the aircraft with the specified address, or NULL if no aircraft
|
||||
// exists with this address.
|
||||
//
|
||||
struct aircraft *interactiveFindAircraft(uint32_t addr) {
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
|
||||
while(a) {
|
||||
if (a->addr == addr) return (a);
|
||||
a = a->next;
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// We have received a Mode A or C response.
|
||||
//
|
||||
// Search through the list of known Mode-S aircraft and tag them if this Mode A/C
|
||||
// matches their known Mode S Squawks or Altitudes(+/- 50feet).
|
||||
//
|
||||
// A Mode S equipped aircraft may also respond to Mode A and Mode C SSR interrogations.
|
||||
// We can't tell if this is a Mode A or C, so scan through the entire aircraft list
|
||||
// looking for matches on Mode A (squawk) and Mode C (altitude). Flag in the Mode S
|
||||
// records that we have had a potential Mode A or Mode C response from this aircraft.
|
||||
//
|
||||
// If an aircraft responds to Mode A then it's highly likely to be responding to mode C
|
||||
// too, and vice verca. Therefore, once the mode S record is tagged with both a Mode A
|
||||
// and a Mode C flag, we can be fairly confident that this Mode A/C frame relates to that
|
||||
// Mode S aircraft.
|
||||
//
|
||||
// Mode C's are more likely to clash than Mode A's; There could be several aircraft
|
||||
// cruising at FL370, but it's less likely (though not impossible) that there are two
|
||||
// aircraft on the same squawk. Therefore, give precidence to Mode A record matches
|
||||
//
|
||||
// Note : It's theoretically possible for an aircraft to have the same value for Mode A
|
||||
// and Mode C. Therefore we have to check BOTH A AND C for EVERY S.
|
||||
//
|
||||
void interactiveUpdateAircraftModeA(struct aircraft *a) {
|
||||
struct aircraft *b = Modes.aircrafts;
|
||||
|
||||
while(b) {
|
||||
if ((b->modeACflags & MODEAC_MSG_FLAG) == 0) {// skip any fudged ICAO records
|
||||
|
||||
// If both (a) and (b) have valid squawks...
|
||||
if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_SQUAWK_VALID) {
|
||||
// ...check for Mode-A == Mode-S Squawk matches
|
||||
if (a->modeA == b->modeA) { // If a 'real' Mode-S ICAO exists using this Mode-A Squawk
|
||||
b->modeAcount = a->messages;
|
||||
b->modeACflags |= MODEAC_MSG_MODEA_HIT;
|
||||
a->modeACflags |= MODEAC_MSG_MODEA_HIT;
|
||||
if ( (b->modeAcount > 0) &&
|
||||
( (b->modeCcount > 1)
|
||||
|| (a->modeACflags & MODEAC_MSG_MODEA_ONLY)) ) // Allow Mode-A only matches if this Mode-A is invalid Mode-C
|
||||
{a->modeACflags |= MODEAC_MSG_MODES_HIT;} // flag this ModeA/C probably belongs to a known Mode S
|
||||
}
|
||||
}
|
||||
|
||||
// If both (a) and (b) have valid altitudes...
|
||||
if ((a->bFlags & b->bFlags) & MODES_ACFLAGS_ALTITUDE_VALID) {
|
||||
// ... check for Mode-C == Mode-S Altitude matches
|
||||
if ( (a->modeC == b->modeC ) // If a 'real' Mode-S ICAO exists at this Mode-C Altitude
|
||||
|| (a->modeC == b->modeC + 1) // or this Mode-C - 100 ft
|
||||
|| (a->modeC + 1 == b->modeC ) ) { // or this Mode-C + 100 ft
|
||||
b->modeCcount = a->messages;
|
||||
b->modeACflags |= MODEAC_MSG_MODEC_HIT;
|
||||
a->modeACflags |= MODEAC_MSG_MODEC_HIT;
|
||||
if ( (b->modeAcount > 0) &&
|
||||
(b->modeCcount > 1) )
|
||||
{a->modeACflags |= (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD);} // flag this ModeA/C probably belongs to a known Mode S
|
||||
}
|
||||
}
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
void interactiveUpdateAircraftModeS() {
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
|
||||
while(a) {
|
||||
int flags = a->modeACflags;
|
||||
if (flags & MODEAC_MSG_FLAG) { // find any fudged ICAO records
|
||||
|
||||
// clear the current A,C and S hit bits ready for this attempt
|
||||
a->modeACflags = flags & ~(MODEAC_MSG_MODEA_HIT | MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODES_HIT);
|
||||
|
||||
interactiveUpdateAircraftModeA(a); // and attempt to match them with Mode-S
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Receive new messages and populate the interactive mode with more info
|
||||
//
|
||||
struct aircraft *interactiveReceiveData(struct modesMessage *mm) {
|
||||
struct aircraft *a, *aux;
|
||||
|
||||
// Return if (checking crc) AND (not crcok) AND (not fixed)
|
||||
if (Modes.check_crc && (mm->crcok == 0) && (mm->correctedbits == 0))
|
||||
return NULL;
|
||||
|
||||
// Lookup our aircraft or create a new one
|
||||
a = interactiveFindAircraft(mm->addr);
|
||||
if (!a) { // If it's a currently unknown aircraft....
|
||||
a = interactiveCreateAircraft(mm); // ., create a new record for it,
|
||||
a->next = Modes.aircrafts; // .. and put it at the head of the list
|
||||
Modes.aircrafts = a;
|
||||
} else {
|
||||
/* If it is an already known aircraft, move it on head
|
||||
* so we keep aircrafts ordered by received message time.
|
||||
*
|
||||
* However move it on head only if at least one second elapsed
|
||||
* since the aircraft that is currently on head sent a message,
|
||||
* othewise with multiple aircrafts at the same time we have an
|
||||
* useless shuffle of positions on the screen. */
|
||||
if (0 && Modes.aircrafts != a && (time(NULL) - a->seen) >= 1) {
|
||||
aux = Modes.aircrafts;
|
||||
while(aux->next != a) aux = aux->next;
|
||||
/* Now we are a node before the aircraft to remove. */
|
||||
aux->next = aux->next->next; /* removed. */
|
||||
/* Add on head */
|
||||
a->next = Modes.aircrafts;
|
||||
Modes.aircrafts = a;
|
||||
}
|
||||
}
|
||||
|
||||
a->signalLevel[a->messages & 7] = mm->signalLevel;// replace the 8th oldest signal strength
|
||||
a->seen = time(NULL);
|
||||
a->timestamp = mm->timestampMsg;
|
||||
a->messages++;
|
||||
|
||||
// If a (new) CALLSIGN has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) {
|
||||
memcpy(a->flight, mm->flight, sizeof(a->flight));
|
||||
}
|
||||
|
||||
// If a (new) ALTITUDE has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
||||
if ( (a->modeCcount) // if we've a modeCcount already
|
||||
&& (a->altitude != mm->altitude ) ) // and Altitude has changed
|
||||
// && (a->modeC != mm->modeC + 1) // and Altitude not changed by +100 feet
|
||||
// && (a->modeC + 1 != mm->modeC ) ) // and Altitude not changes by -100 feet
|
||||
{
|
||||
a->modeCcount = 0; //....zero the hit count
|
||||
a->modeACflags &= ~MODEAC_MSG_MODEC_HIT;
|
||||
}
|
||||
a->altitude = mm->altitude;
|
||||
a->modeC = (mm->altitude + 49) / 100;
|
||||
}
|
||||
|
||||
// If a (new) SQUAWK has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
|
||||
if (a->modeA != mm->modeA) {
|
||||
a->modeAcount = 0; // Squawk has changed, so zero the hit count
|
||||
a->modeACflags &= ~MODEAC_MSG_MODEA_HIT;
|
||||
}
|
||||
a->modeA = mm->modeA;
|
||||
}
|
||||
|
||||
// If a (new) HEADING has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_HEADING_VALID) {
|
||||
a->track = mm->heading;
|
||||
}
|
||||
|
||||
// If a (new) SPEED has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_SPEED_VALID) {
|
||||
a->speed = mm->velocity;
|
||||
}
|
||||
|
||||
// If a (new) Vertical Descent rate has been received, copy it to the aircraft structure
|
||||
if (mm->bFlags & MODES_ACFLAGS_VERTRATE_VALID) {
|
||||
a->vert_rate = mm->vert_rate;
|
||||
}
|
||||
|
||||
// if the Aircraft has landed or taken off since the last message, clear the even/odd CPR flags
|
||||
if ((mm->bFlags & MODES_ACFLAGS_AOG_VALID) && ((a->bFlags ^ mm->bFlags) & MODES_ACFLAGS_AOG)) {
|
||||
a->bFlags &= ~(MODES_ACFLAGS_LLBOTH_VALID | MODES_ACFLAGS_AOG);
|
||||
}
|
||||
|
||||
// If we've got a new cprlat or cprlon
|
||||
if (mm->bFlags & MODES_ACFLAGS_LLEITHER_VALID) {
|
||||
int location_ok = 0;
|
||||
|
||||
if (mm->bFlags & MODES_ACFLAGS_LLODD_VALID) {
|
||||
a->odd_cprlat = mm->raw_latitude;
|
||||
a->odd_cprlon = mm->raw_longitude;
|
||||
a->odd_cprtime = mstime();
|
||||
} else {
|
||||
a->even_cprlat = mm->raw_latitude;
|
||||
a->even_cprlon = mm->raw_longitude;
|
||||
a->even_cprtime = mstime();
|
||||
}
|
||||
|
||||
// If we have enough recent data, try global CPR
|
||||
if (((mm->bFlags | a->bFlags) & MODES_ACFLAGS_LLEITHER_VALID) == MODES_ACFLAGS_LLBOTH_VALID && abs((int)(a->even_cprtime - a->odd_cprtime)) <= 10000) {
|
||||
if (decodeCPR(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) {
|
||||
location_ok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise try relative CPR.
|
||||
if (!location_ok && decodeCPRrelative(a, (mm->bFlags & MODES_ACFLAGS_LLODD_VALID), (mm->bFlags & MODES_ACFLAGS_AOG)) == 0) {
|
||||
location_ok = 1;
|
||||
}
|
||||
|
||||
//If we sucessfully decoded, back copy the results to mm so that we can print them in list output
|
||||
if (location_ok) {
|
||||
mm->bFlags |= MODES_ACFLAGS_LATLON_VALID;
|
||||
mm->fLat = a->lat;
|
||||
mm->fLon = a->lon;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the aircrafts a->bFlags to reflect the newly received mm->bFlags;
|
||||
a->bFlags |= mm->bFlags;
|
||||
|
||||
if (mm->msgtype == 32) {
|
||||
int flags = a->modeACflags;
|
||||
if ((flags & (MODEAC_MSG_MODEC_HIT | MODEAC_MSG_MODEC_OLD)) == MODEAC_MSG_MODEC_OLD) {
|
||||
//
|
||||
// This Mode-C doesn't currently hit any known Mode-S, but it used to because MODEAC_MSG_MODEC_OLD is
|
||||
// set So the aircraft it used to match has either changed altitude, or gone out of our receiver range
|
||||
//
|
||||
// We've now received this Mode-A/C again, so it must be a new aircraft. It could be another aircraft
|
||||
// at the same Mode-C altitude, or it could be a new airctraft with a new Mods-A squawk.
|
||||
//
|
||||
// To avoid masking this aircraft from the interactive display, clear the MODEAC_MSG_MODES_OLD flag
|
||||
// and set messages to 1;
|
||||
//
|
||||
a->modeACflags = flags & ~MODEAC_MSG_MODEC_OLD;
|
||||
a->messages = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are Logging DF's, and it's not a Mode A/C
|
||||
if ((Modes.bEnableDFLogging) && (mm->msgtype < 32)) {
|
||||
interactiveCreateDF(a,mm);
|
||||
}
|
||||
|
||||
return (a);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Show the currently captured interactive data on screen.
|
||||
//
|
||||
void interactiveShowData(void) {
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
time_t now = time(NULL);
|
||||
int count = 0;
|
||||
char progress;
|
||||
char spinner[4] = "|/-\\";
|
||||
|
||||
// Refresh screen every (MODES_INTERACTIVE_REFRESH_TIME) miliseconde
|
||||
if ((mstime() - Modes.interactive_last_update) < MODES_INTERACTIVE_REFRESH_TIME)
|
||||
{return;}
|
||||
|
||||
Modes.interactive_last_update = mstime();
|
||||
|
||||
// Attempt to reconsile any ModeA/C with known Mode-S
|
||||
// We can't condition on Modes.modeac because ModeA/C could be comming
|
||||
// in from a raw input port which we can't turn off.
|
||||
interactiveUpdateAircraftModeS();
|
||||
|
||||
progress = spinner[time(NULL)%4];
|
||||
|
||||
#ifndef _WIN32
|
||||
printf("\x1b[H\x1b[2J"); // Clear the screen
|
||||
#else
|
||||
cls();
|
||||
#endif
|
||||
|
||||
if (Modes.interactive_rtl1090 == 0) {
|
||||
printf (
|
||||
"Hex Mode Sqwk Flight Alt Spd Hdg Lat Long Sig Msgs Ti%c\n", progress);
|
||||
} else {
|
||||
printf (
|
||||
"Hex Flight Alt V/S GS TT SSR G*456^ Msgs Seen %c\n", progress);
|
||||
}
|
||||
printf(
|
||||
"-------------------------------------------------------------------------------\n");
|
||||
|
||||
while(a && (count < Modes.interactive_rows)) {
|
||||
|
||||
if ((now - a->seen) < Modes.interactive_display_ttl)
|
||||
{
|
||||
int msgs = a->messages;
|
||||
int flags = a->modeACflags;
|
||||
|
||||
if ( (((flags & (MODEAC_MSG_FLAG )) == 0 ) )
|
||||
|| (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEA_ONLY)) == MODEAC_MSG_MODEA_ONLY) && (msgs > 4 ) )
|
||||
|| (((flags & (MODEAC_MSG_MODES_HIT | MODEAC_MSG_MODEC_OLD )) == 0 ) && (msgs > 127) )
|
||||
) {
|
||||
int altitude = a->altitude, speed = a->speed;
|
||||
char strSquawk[5] = " ";
|
||||
char strFl[6] = " ";
|
||||
char strTt[5] = " ";
|
||||
char strGs[5] = " ";
|
||||
|
||||
// Convert units to metric if --metric was specified
|
||||
if (Modes.metric) {
|
||||
altitude = (int) (altitude / 3.2828);
|
||||
speed = (int) (speed * 1.852);
|
||||
}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) {
|
||||
snprintf(strSquawk,5,"%04x", a->modeA);}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) {
|
||||
snprintf (strGs, 5,"%3d", speed);}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) {
|
||||
snprintf (strTt, 5,"%03d", a->track);}
|
||||
|
||||
if (msgs > 99999) {
|
||||
msgs = 99999;}
|
||||
|
||||
if (Modes.interactive_rtl1090) { // RTL1090 display mode
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
||||
snprintf(strFl,6,"F%03d",(altitude/100));
|
||||
}
|
||||
printf("%06x %-8s %-4s %-3s %-3s %4s %-6d %-2d\n",
|
||||
a->addr, a->flight, strFl, strGs, strTt, strSquawk, msgs, (int)(now - a->seen));
|
||||
|
||||
} else { // Dump1090 display mode
|
||||
char strMode[5] = " ";
|
||||
char strLat[8] = " ";
|
||||
char strLon[9] = " ";
|
||||
unsigned char * pSig = a->signalLevel;
|
||||
unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] +
|
||||
pSig[4] + pSig[5] + pSig[6] + pSig[7] + 3) >> 3;
|
||||
|
||||
if ((flags & MODEAC_MSG_FLAG) == 0) {
|
||||
strMode[0] = 'S';
|
||||
} else if (flags & MODEAC_MSG_MODEA_ONLY) {
|
||||
strMode[0] = 'A';
|
||||
}
|
||||
if (flags & MODEAC_MSG_MODEA_HIT) {strMode[2] = 'a';}
|
||||
if (flags & MODEAC_MSG_MODEC_HIT) {strMode[3] = 'c';}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) {
|
||||
snprintf(strLat, 8,"%7.03f", a->lat);
|
||||
snprintf(strLon, 9,"%8.03f", a->lon);
|
||||
}
|
||||
|
||||
if (a->bFlags & MODES_ACFLAGS_AOG) {
|
||||
snprintf(strFl, 6," grnd");
|
||||
} else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) {
|
||||
snprintf(strFl, 6, "%5d", altitude);
|
||||
}
|
||||
|
||||
printf("%06X %-4s %-4s %-8s %5s %3s %3s %7s %8s %3d %5d %2d\n",
|
||||
a->addr, strMode, strSquawk, a->flight, strFl, strGs, strTt,
|
||||
strLat, strLon, signalAverage, msgs, (int)(now - a->seen));
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// When in interactive mode If we don't receive new nessages within
|
||||
// MODES_INTERACTIVE_DELETE_TTL seconds we remove the aircraft from the list.
|
||||
//
|
||||
void interactiveRemoveStaleAircrafts(void) {
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
struct aircraft *prev = NULL;
|
||||
time_t now = time(NULL);
|
||||
|
||||
// Only do cleanup once per second
|
||||
if (Modes.last_cleanup_time != now) {
|
||||
Modes.last_cleanup_time = now;
|
||||
|
||||
interactiveRemoveStaleDF(now);
|
||||
|
||||
while(a) {
|
||||
if ((now - a->seen) > Modes.interactive_delete_ttl) {
|
||||
// Remove the element from the linked list, with care
|
||||
// if we are removing the first element
|
||||
if (!prev) {
|
||||
Modes.aircrafts = a->next; free(a); a = Modes.aircrafts;
|
||||
} else {
|
||||
prev->next = a->next; free(a); a = prev->next;
|
||||
}
|
||||
} else {
|
||||
prev = a; a = a->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
|
@ -1,27 +0,0 @@
|
|||
#
|
||||
# When building a package or installing otherwise in the system, make
|
||||
# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
|
||||
#
|
||||
PROGNAME=dump1090
|
||||
|
||||
ifdef PREFIX
|
||||
BINDIR=$(PREFIX)/bin
|
||||
SHAREDIR=$(PREFIX)/share/$(PROGNAME)
|
||||
EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\"
|
||||
endif
|
||||
|
||||
CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr`
|
||||
LIBS=`pkg-config --libs librtlsdr` -lpthread -lm
|
||||
CC=gcc
|
||||
|
||||
|
||||
all: dump1090
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
|
||||
|
||||
dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o
|
||||
$(CC) -g -o dump1090 dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o dump1090
|
|
@ -1,27 +0,0 @@
|
|||
#
|
||||
# When building a package or installing otherwise in the system, make
|
||||
# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
|
||||
#
|
||||
PROGNAME=ppup1090
|
||||
|
||||
ifdef PREFIX
|
||||
BINDIR=$(PREFIX)/bin
|
||||
SHAREDIR=$(PREFIX)/share/$(PROGNAME)
|
||||
EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\"
|
||||
endif
|
||||
|
||||
CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr`
|
||||
LIBS=`pkg-config --libs librtlsdr` -lpthread -lm
|
||||
CC=gcc
|
||||
|
||||
|
||||
all: ppup1090
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
|
||||
|
||||
ppup1090: ppup1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o
|
||||
$(CC) -g -o ppup1090 ppup1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o coaa1090.obj $(LIBS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o ppup1090
|
|
@ -1,27 +0,0 @@
|
|||
#
|
||||
# When building a package or installing otherwise in the system, make
|
||||
# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
|
||||
#
|
||||
PROGNAME=view1090
|
||||
|
||||
ifdef PREFIX
|
||||
BINDIR=$(PREFIX)/bin
|
||||
SHAREDIR=$(PREFIX)/share/$(PROGNAME)
|
||||
EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\"
|
||||
endif
|
||||
|
||||
CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr`
|
||||
LIBS=`pkg-config --libs librtlsdr` -lpthread -lm
|
||||
CC=gcc
|
||||
|
||||
|
||||
all: view1090
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
|
||||
|
||||
view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o
|
||||
$(CC) -g -o view1090 view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o view1090
|
|
@ -1,386 +0,0 @@
|
|||
// dump1090, a Mode S messages decoder for RTLSDR devices.
|
||||
//
|
||||
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
#include "dump1090.h"
|
||||
//
|
||||
// ===================== Mode A/C detection and decoding ===================
|
||||
//
|
||||
//
|
||||
// This table is used to build the Mode A/C variable called ModeABits.Each
|
||||
// bit period is inspected, and if it's value exceeds the threshold limit,
|
||||
// then the value in this table is or-ed into ModeABits.
|
||||
//
|
||||
// At the end of message processing, ModeABits will be the decoded ModeA value.
|
||||
//
|
||||
// We can also flag noise in bits that should be zeros - the xx bits. Noise in
|
||||
// these bits cause bits (31-16) in ModeABits to be set. Then at the end of message
|
||||
// processing we can test for errors by looking at these bits.
|
||||
//
|
||||
uint32_t ModeABitTable[24] = {
|
||||
0x00000000, // F1 = 1
|
||||
0x00000010, // C1
|
||||
0x00001000, // A1
|
||||
0x00000020, // C2
|
||||
0x00002000, // A2
|
||||
0x00000040, // C4
|
||||
0x00004000, // A4
|
||||
0x40000000, // xx = 0 Set bit 30 if we see this high
|
||||
0x00000100, // B1
|
||||
0x00000001, // D1
|
||||
0x00000200, // B2
|
||||
0x00000002, // D2
|
||||
0x00000400, // B4
|
||||
0x00000004, // D4
|
||||
0x00000000, // F2 = 1
|
||||
0x08000000, // xx = 0 Set bit 27 if we see this high
|
||||
0x04000000, // xx = 0 Set bit 26 if we see this high
|
||||
0x00000080, // SPI
|
||||
0x02000000, // xx = 0 Set bit 25 if we see this high
|
||||
0x01000000, // xx = 0 Set bit 24 if we see this high
|
||||
0x00800000, // xx = 0 Set bit 23 if we see this high
|
||||
0x00400000, // xx = 0 Set bit 22 if we see this high
|
||||
0x00200000, // xx = 0 Set bit 21 if we see this high
|
||||
0x00100000, // xx = 0 Set bit 20 if we see this high
|
||||
};
|
||||
//
|
||||
// This table is used to produce an error variable called ModeAErrs.Each
|
||||
// inter-bit period is inspected, and if it's value falls outside of the
|
||||
// expected range, then the value in this table is or-ed into ModeAErrs.
|
||||
//
|
||||
// At the end of message processing, ModeAErrs will indicate if we saw
|
||||
// any inter-bit anomolies, and the bits that are set will show which
|
||||
// bits had them.
|
||||
//
|
||||
uint32_t ModeAMidTable[24] = {
|
||||
0x80000000, // F1 = 1 Set bit 31 if we see F1_C1 error
|
||||
0x00000010, // C1 Set bit 4 if we see C1_A1 error
|
||||
0x00001000, // A1 Set bit 12 if we see A1_C2 error
|
||||
0x00000020, // C2 Set bit 5 if we see C2_A2 error
|
||||
0x00002000, // A2 Set bit 13 if we see A2_C4 error
|
||||
0x00000040, // C4 Set bit 6 if we see C3_A4 error
|
||||
0x00004000, // A4 Set bit 14 if we see A4_xx error
|
||||
0x40000000, // xx = 0 Set bit 30 if we see xx_B1 error
|
||||
0x00000100, // B1 Set bit 8 if we see B1_D1 error
|
||||
0x00000001, // D1 Set bit 0 if we see D1_B2 error
|
||||
0x00000200, // B2 Set bit 9 if we see B2_D2 error
|
||||
0x00000002, // D2 Set bit 1 if we see D2_B4 error
|
||||
0x00000400, // B4 Set bit 10 if we see B4_D4 error
|
||||
0x00000004, // D4 Set bit 2 if we see D4_F2 error
|
||||
0x20000000, // F2 = 1 Set bit 29 if we see F2_xx error
|
||||
0x08000000, // xx = 0 Set bit 27 if we see xx_xx error
|
||||
0x04000000, // xx = 0 Set bit 26 if we see xx_SPI error
|
||||
0x00000080, // SPI Set bit 15 if we see SPI_xx error
|
||||
0x02000000, // xx = 0 Set bit 25 if we see xx_xx error
|
||||
0x01000000, // xx = 0 Set bit 24 if we see xx_xx error
|
||||
0x00800000, // xx = 0 Set bit 23 if we see xx_xx error
|
||||
0x00400000, // xx = 0 Set bit 22 if we see xx_xx error
|
||||
0x00200000, // xx = 0 Set bit 21 if we see xx_xx error
|
||||
0x00100000, // xx = 0 Set bit 20 if we see xx_xx error
|
||||
};
|
||||
//
|
||||
// The "off air" format is,,
|
||||
// _F1_C1_A1_C2_A2_C4_A4_xx_B1_D1_B2_D2_B4_D4_F2_xx_xx_SPI_
|
||||
//
|
||||
// Bit spacing is 1.45uS, with 0.45uS high, and 1.00us low. This is a problem
|
||||
// because we ase sampling at 2Mhz (500nS) so we are below Nyquist.
|
||||
//
|
||||
// The bit spacings are..
|
||||
// F1 : 0.00,
|
||||
// 1.45, 2.90, 4.35, 5.80, 7.25, 8.70,
|
||||
// X : 10.15,
|
||||
// : 11.60, 13.05, 14.50, 15.95, 17.40, 18.85,
|
||||
// F2 : 20.30,
|
||||
// X : 21.75, 23.20, 24.65
|
||||
//
|
||||
// This equates to the following sample point centers at 2Mhz.
|
||||
// [ 0.0],
|
||||
// [ 2.9], [ 5.8], [ 8.7], [11.6], [14.5], [17.4],
|
||||
// [20.3],
|
||||
// [23.2], [26.1], [29.0], [31.9], [34.8], [37.7]
|
||||
// [40.6]
|
||||
// [43.5], [46.4], [49.3]
|
||||
//
|
||||
// We know that this is a supposed to be a binary stream, so the signal
|
||||
// should either be a 1 or a 0. Therefore, any energy above the noise level
|
||||
// in two adjacent samples must be from the same pulse, so we can simply
|
||||
// add the values together..
|
||||
//
|
||||
int detectModeA(uint16_t *m, struct modesMessage *mm)
|
||||
{
|
||||
int j, lastBitWasOne;
|
||||
int ModeABits = 0;
|
||||
int ModeAErrs = 0;
|
||||
int byte, bit;
|
||||
int thisSample, lastBit, lastSpace = 0;
|
||||
int m0, m1, m2, m3, mPhase;
|
||||
int n0, n1, n2 ,n3;
|
||||
int F1_sig, F1_noise;
|
||||
int F2_sig, F2_noise;
|
||||
int fSig, fNoise, fLevel, fLoLo;
|
||||
|
||||
// m[0] contains the energy from 0 -> 499 nS
|
||||
// m[1] contains the energy from 500 -> 999 nS
|
||||
// m[2] contains the energy from 1000 -> 1499 nS
|
||||
// m[3] contains the energy from 1500 -> 1999 nS
|
||||
//
|
||||
// We are looking for a Frame bit (F1) whose width is 450nS, followed by
|
||||
// 1000nS of quiet.
|
||||
//
|
||||
// The width of the frame bit is 450nS, which is 90% of our sample rate.
|
||||
// Therefore, in an ideal world, all the energy for the frame bit will be
|
||||
// in a single sample, preceeded by (at least) one zero, and followed by
|
||||
// two zeros, Best case we can look for ...
|
||||
//
|
||||
// 0 - 1 - 0 - 0
|
||||
//
|
||||
// However, our samples are not phase aligned, so some of the energy from
|
||||
// each bit could be spread over two consecutive samples. Worst case is
|
||||
// that we sample half in one bit, and half in the next. In that case,
|
||||
// we're looking for
|
||||
//
|
||||
// 0 - 0.5 - 0.5 - 0.
|
||||
|
||||
m0 = m[0]; m1 = m[1];
|
||||
|
||||
if (m0 >= m1) // m1 *must* be bigger than m0 for this to be F1
|
||||
{return (0);}
|
||||
|
||||
m2 = m[2]; m3 = m[3];
|
||||
|
||||
//
|
||||
// if (m2 <= m0), then assume the sample bob on (Phase == 0), so don't look at m3
|
||||
if ((m2 <= m0) || (m2 < m3))
|
||||
{m3 = m2; m2 = m0;}
|
||||
|
||||
if ( (m3 >= m1) // m1 must be bigger than m3
|
||||
|| (m0 > m2) // m2 can be equal to m0 if ( 0,1,0,0 )
|
||||
|| (m3 > m2) ) // m2 can be equal to m3 if ( 0,1,0,0 )
|
||||
{return (0);}
|
||||
|
||||
// m0 = noise
|
||||
// m1 = noise + (signal * X))
|
||||
// m2 = noise + (signal * (1-X))
|
||||
// m3 = noise
|
||||
//
|
||||
// Hence, assuming all 4 samples have similar amounts of noise in them
|
||||
// signal = (m1 + m2) - ((m0 + m3) * 2)
|
||||
// noise = (m0 + m3) / 2
|
||||
//
|
||||
F1_sig = (m1 + m2) - ((m0 + m3) << 1);
|
||||
F1_noise = (m0 + m3) >> 1;
|
||||
|
||||
if ( (F1_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F1 signal amplitude
|
||||
|| (F1_sig < (F1_noise << 2)) ) // minimum allowable Sig/Noise ratio 4:1
|
||||
{return (0);}
|
||||
|
||||
// If we get here then we have a potential F1, so look for an equally valid F2 20.3uS later
|
||||
//
|
||||
// Our F1 is centered somewhere between samples m[1] and m[2]. We can guestimate where F2 is
|
||||
// by comparing the ratio of m1 and m2, and adding on 20.3 uS (40.6 samples)
|
||||
//
|
||||
mPhase = ((m2 * 20) / (m1 + m2));
|
||||
byte = (mPhase + 812) / 20;
|
||||
n0 = m[byte++]; n1 = m[byte++];
|
||||
|
||||
if (n0 >= n1) // n1 *must* be bigger than n0 for this to be F2
|
||||
{return (0);}
|
||||
|
||||
n2 = m[byte++];
|
||||
//
|
||||
// if the sample bob on (Phase == 0), don't look at n3
|
||||
//
|
||||
if ((mPhase + 812) % 20)
|
||||
{n3 = m[byte++];}
|
||||
else
|
||||
{n3 = n2; n2 = n0;}
|
||||
|
||||
if ( (n3 >= n1) // n1 must be bigger than n3
|
||||
|| (n0 > n2) // n2 can be equal to n0 ( 0,1,0,0 )
|
||||
|| (n3 > n2) ) // n2 can be equal to n3 ( 0,1,0,0 )
|
||||
{return (0);}
|
||||
|
||||
F2_sig = (n1 + n2) - ((n0 + n3) << 1);
|
||||
F2_noise = (n0 + n3) >> 1;
|
||||
|
||||
if ( (F2_sig < MODEAC_MSG_SQUELCH_LEVEL) // minimum required F2 signal amplitude
|
||||
|| (F2_sig < (F2_noise << 2)) ) // maximum allowable Sig/Noise ratio 4:1
|
||||
{return (0);}
|
||||
|
||||
fSig = (F1_sig + F2_sig) >> 1;
|
||||
fNoise = (F1_noise + F2_noise) >> 1;
|
||||
fLoLo = fNoise + (fSig >> 2); // 1/2
|
||||
fLevel = fNoise + (fSig >> 1);
|
||||
lastBitWasOne = 1;
|
||||
lastBit = F1_sig;
|
||||
//
|
||||
// Now step by a half ModeA bit, 0.725nS, which is 1.45 samples, which is 29/20
|
||||
// No need to do bit 0 because we've already selected it as a valid F1
|
||||
// Do several bits past the SPI to increase error rejection
|
||||
//
|
||||
for (j = 1, mPhase += 29; j < 48; mPhase += 29, j ++)
|
||||
{
|
||||
byte = 1 + (mPhase / 20);
|
||||
|
||||
thisSample = m[byte] - fNoise;
|
||||
if (mPhase % 20) // If the bit is split over two samples...
|
||||
{thisSample += (m[byte+1] - fNoise);} // add in the second sample's energy
|
||||
|
||||
// If we're calculating a space value
|
||||
if (j & 1)
|
||||
{lastSpace = thisSample;}
|
||||
|
||||
else
|
||||
{// We're calculating a new bit value
|
||||
bit = j >> 1;
|
||||
if (thisSample >= fLevel)
|
||||
{// We're calculating a new bit value, and its a one
|
||||
ModeABits |= ModeABitTable[bit--]; // or in the correct bit
|
||||
|
||||
if (lastBitWasOne)
|
||||
{ // This bit is one, last bit was one, so check the last space is somewhere less than one
|
||||
if ( (lastSpace >= (thisSample>>1)) || (lastSpace >= lastBit) )
|
||||
{ModeAErrs |= ModeAMidTable[bit];}
|
||||
}
|
||||
|
||||
else
|
||||
{// This bit,is one, last bit was zero, so check the last space is somewhere less than one
|
||||
if (lastSpace >= (thisSample >> 1))
|
||||
{ModeAErrs |= ModeAMidTable[bit];}
|
||||
}
|
||||
|
||||
lastBitWasOne = 1;
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
{// We're calculating a new bit value, and its a zero
|
||||
if (lastBitWasOne)
|
||||
{ // This bit is zero, last bit was one, so check the last space is somewhere in between
|
||||
if (lastSpace >= lastBit)
|
||||
{ModeAErrs |= ModeAMidTable[bit];}
|
||||
}
|
||||
|
||||
else
|
||||
{// This bit,is zero, last bit was zero, so check the last space is zero too
|
||||
if (lastSpace >= fLoLo)
|
||||
{ModeAErrs |= ModeAMidTable[bit];}
|
||||
}
|
||||
|
||||
lastBitWasOne = 0;
|
||||
}
|
||||
|
||||
lastBit = (thisSample >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Output format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1
|
||||
//
|
||||
if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) )
|
||||
{return (ModeABits = 0);}
|
||||
|
||||
fSig = (fSig + 0x7F) >> 8;
|
||||
mm->signalLevel = ((fSig < 255) ? fSig : 255);
|
||||
|
||||
return ModeABits;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
// Input format is : 00:A4:A2:A1:00:B4:B2:B1:00:C4:C2:C1:00:D4:D2:D1
|
||||
//
|
||||
int ModeAToModeC(unsigned int ModeA)
|
||||
{
|
||||
unsigned int FiveHundreds = 0;
|
||||
unsigned int OneHundreds = 0;
|
||||
|
||||
if ( (ModeA & 0xFFFF888B) // D1 set is illegal. D2 set is > 62700ft which is unlikely
|
||||
|| ((ModeA & 0x000000F0) == 0) ) // C1,,C4 cannot be Zero
|
||||
{return -9999;}
|
||||
|
||||
if (ModeA & 0x0010) {OneHundreds ^= 0x007;} // C1
|
||||
if (ModeA & 0x0020) {OneHundreds ^= 0x003;} // C2
|
||||
if (ModeA & 0x0040) {OneHundreds ^= 0x001;} // C4
|
||||
|
||||
// Remove 7s from OneHundreds (Make 7->5, snd 5->7).
|
||||
if ((OneHundreds & 5) == 5) {OneHundreds ^= 2;}
|
||||
|
||||
// Check for invalid codes, only 1 to 5 are valid
|
||||
if (OneHundreds > 5)
|
||||
{return -9999;}
|
||||
|
||||
//if (ModeA & 0x0001) {FiveHundreds ^= 0x1FF;} // D1 never used for altitude
|
||||
if (ModeA & 0x0002) {FiveHundreds ^= 0x0FF;} // D2
|
||||
if (ModeA & 0x0004) {FiveHundreds ^= 0x07F;} // D4
|
||||
|
||||
if (ModeA & 0x1000) {FiveHundreds ^= 0x03F;} // A1
|
||||
if (ModeA & 0x2000) {FiveHundreds ^= 0x01F;} // A2
|
||||
if (ModeA & 0x4000) {FiveHundreds ^= 0x00F;} // A4
|
||||
|
||||
if (ModeA & 0x0100) {FiveHundreds ^= 0x007;} // B1
|
||||
if (ModeA & 0x0200) {FiveHundreds ^= 0x003;} // B2
|
||||
if (ModeA & 0x0400) {FiveHundreds ^= 0x001;} // B4
|
||||
|
||||
// Correct order of OneHundreds.
|
||||
if (FiveHundreds & 1) {OneHundreds = 6 - OneHundreds;}
|
||||
|
||||
return ((FiveHundreds * 5) + OneHundreds - 13);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
void decodeModeAMessage(struct modesMessage *mm, int ModeA)
|
||||
{
|
||||
mm->msgtype = 32; // Valid Mode S DF's are DF-00 to DF-31.
|
||||
// so use 32 to indicate Mode A/C
|
||||
|
||||
mm->msgbits = 16; // Fudge up a Mode S style data stream
|
||||
mm->msg[0] = (ModeA >> 8);
|
||||
mm->msg[1] = (ModeA);
|
||||
|
||||
// Fudge an ICAO address based on Mode A (remove the Ident bit)
|
||||
// Use an upper address byte of FF, since this is ICAO unallocated
|
||||
mm->addr = 0x00FF0000 | (ModeA & 0x0000FF7F);
|
||||
|
||||
// Set the Identity field to ModeA
|
||||
mm->modeA = ModeA & 0x7777;
|
||||
mm->bFlags |= MODES_ACFLAGS_SQUAWK_VALID;
|
||||
|
||||
// Flag ident in flight status
|
||||
mm->fs = ModeA & 0x0080;
|
||||
|
||||
// Not much else we can tell from a Mode A/C reply.
|
||||
// Just fudge up a few bits to keep other code happy
|
||||
mm->crcok = 1;
|
||||
mm->correctedbits = 0;
|
||||
}
|
||||
//
|
||||
// ===================== Mode A/C detection and decoding ===================
|
||||
//
|
|
@ -1 +0,0 @@
|
|||
cd129a02b926ab42fbf9f41ab984f4bb2a3e5e59
|
File diff suppressed because it is too large
Load diff
|
@ -1,261 +0,0 @@
|
|||
// ppup1090, a Mode S PlanePlotter Uploader for dump1090 devices.
|
||||
//
|
||||
// Copyright (C) 2013 by Malcolm Robb <Support@ATTAvionics.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#include "coaa.h"
|
||||
#include "ppup1090.h"
|
||||
//
|
||||
// ============================= Utility functions ==========================
|
||||
//
|
||||
void sigintHandler(int dummy) {
|
||||
NOTUSED(dummy);
|
||||
signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety
|
||||
Modes.exit = 1; // Signal to threads that we are done
|
||||
}
|
||||
//
|
||||
// =============================== Initialization ===========================
|
||||
//
|
||||
void ppup1090InitConfig(void) {
|
||||
|
||||
int iErr;
|
||||
|
||||
// Default everything to zero/NULL
|
||||
memset(&Modes, 0, sizeof(Modes));
|
||||
memset(&ppup1090, 0, sizeof(ppup1090));
|
||||
|
||||
// Now initialise things that should not be 0/NULL to their defaults
|
||||
Modes.check_crc = 1;
|
||||
Modes.quiet = 1;
|
||||
Modes.bEnableDFLogging = 1;
|
||||
strcpy(ppup1090.net_input_beast_ipaddr,PPUP1090_NET_OUTPUT_IP_ADDRESS);
|
||||
Modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT;
|
||||
Modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL;
|
||||
Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL;
|
||||
Modes.fUserLat = MODES_USER_LATITUDE_DFLT;
|
||||
Modes.fUserLon = MODES_USER_LONGITUDE_DFLT;
|
||||
|
||||
if ((iErr = openCOAA()))
|
||||
{
|
||||
fprintf(stderr, "Error 0x%X initialising uploader\n", iErr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
void ppup1090Init(void) {
|
||||
|
||||
int iErr;
|
||||
|
||||
pthread_mutex_init(&Modes.pDF_mutex,NULL);
|
||||
pthread_mutex_init(&Modes.data_mutex,NULL);
|
||||
pthread_cond_init(&Modes.data_cond,NULL);
|
||||
|
||||
// Allocate the various buffers used by Modes
|
||||
if ( NULL == (Modes.icao_cache = (uint32_t *) malloc(sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2)))
|
||||
{
|
||||
fprintf(stderr, "Out of memory allocating data buffer.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Clear the buffers that have just been allocated, just in-case
|
||||
memset(Modes.icao_cache, 0, sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2);
|
||||
|
||||
// Validate the users Lat/Lon home location inputs
|
||||
if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90
|
||||
|| (Modes.fUserLat < -90.0) // and
|
||||
|| (Modes.fUserLon > 360.0) // Longitude must be -180 to +360
|
||||
|| (Modes.fUserLon < -180.0) ) {
|
||||
Modes.fUserLat = Modes.fUserLon = 0.0;
|
||||
} else if (Modes.fUserLon > 180.0) { // If Longitude is +180 to +360, make it -180 to 0
|
||||
Modes.fUserLon -= 360.0;
|
||||
}
|
||||
// If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the
|
||||
// Atlantic ocean off the west coast of Africa. This is unlikely to be correct.
|
||||
// Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian
|
||||
// is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both.
|
||||
// Testing the flag at runtime will be much quicker than ((fLon != 0.0) || (fLat != 0.0))
|
||||
Modes.bUserFlags &= ~MODES_USER_LATLON_VALID;
|
||||
if ((Modes.fUserLat != 0.0) || (Modes.fUserLon != 0.0)) {
|
||||
Modes.bUserFlags |= MODES_USER_LATLON_VALID;
|
||||
}
|
||||
|
||||
// Prepare error correction tables
|
||||
modesInitErrorInfo();
|
||||
|
||||
// Setup the uploader - read the user paramaters from the coaa.h header file
|
||||
coaa1090.ppIPAddr = ppup1090.net_pp_ipaddr;
|
||||
coaa1090.fUserLat = MODES_USER_LATITUDE_DFLT;
|
||||
coaa1090.fUserLon = MODES_USER_LONGITUDE_DFLT;
|
||||
strcpy(coaa1090.strAuthCode,STR(USER_AUTHCODE));
|
||||
strcpy(coaa1090.strRegNo, STR(USER_REGNO));
|
||||
strcpy(coaa1090.strVersion, MODES_DUMP1090_VERSION);
|
||||
|
||||
if ((iErr = initCOAA (coaa1090)))
|
||||
{
|
||||
fprintf(stderr, "Error 0x%X initialising uploader\n", iErr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
//
|
||||
// ================================ Main ====================================
|
||||
//
|
||||
void showHelp(void) {
|
||||
printf(
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"| ppup1090 RPi Uploader for COAA Planeplotter Ver : "MODES_DUMP1090_VERSION " |\n"
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"--net-bo-ipaddr <IPv4> TCP Beast output listen IPv4 (default: 127.0.0.1)\n"
|
||||
"--net-bo-port <port> TCP Beast output listen port (default: 30005)\n"
|
||||
"--net-pp-ipaddr <IPv4> Plane Plotter LAN IPv4 Address (default: 0.0.0.0)\n"
|
||||
"--quiet Disable output to stdout. Use for daemon applications\n"
|
||||
"--help Show this help\n"
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void showCopyright(void) {
|
||||
uint64_t llTime = time(NULL) + 1;
|
||||
|
||||
printf(
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"| ppup1090 RPi Uploader for COAA Planeplotter Ver : "MODES_DUMP1090_VERSION " |\n"
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"\n"
|
||||
" Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>\n"
|
||||
" Copyright (C) 2014 by Malcolm Robb <support@attavionics.com>\n"
|
||||
"\n"
|
||||
" All rights reserved.\n"
|
||||
"\n"
|
||||
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
|
||||
" ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
|
||||
" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
|
||||
" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
|
||||
" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
|
||||
" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
|
||||
" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
|
||||
" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
|
||||
" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
|
||||
" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
|
||||
" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
"\n"
|
||||
" For further details refer to <https://github.com/MalcolmRobb/dump1090>\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
// delay for 1 second to give the user a chance to read the copyright
|
||||
while (llTime >= time(NULL)) {}
|
||||
}
|
||||
#endif
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
int main(int argc, char **argv) {
|
||||
int j, fd;
|
||||
struct client *c;
|
||||
|
||||
// Set sane defaults
|
||||
|
||||
ppup1090InitConfig();
|
||||
signal(SIGINT, sigintHandler); // Define Ctrl/C handler (exit program)
|
||||
|
||||
// Parse the command line options
|
||||
for (j = 1; j < argc; j++) {
|
||||
int more = ((j + 1) < argc); // There are more arguments
|
||||
|
||||
if (!strcmp(argv[j],"--net-bo-port") && more) {
|
||||
Modes.net_input_beast_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-bo-ipaddr") && more) {
|
||||
strcpy(ppup1090.net_input_beast_ipaddr, argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-pp-ipaddr") && more) {
|
||||
inet_aton(argv[++j], (void *)&ppup1090.net_pp_ipaddr);
|
||||
} else if (!strcmp(argv[j],"--quiet")) {
|
||||
ppup1090.quiet = 1;
|
||||
} else if (!strcmp(argv[j],"--help")) {
|
||||
showHelp();
|
||||
exit(0);
|
||||
} else {
|
||||
fprintf(stderr, "Unknown or not enough arguments for option '%s'.\n\n", argv[j]);
|
||||
showHelp();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Try to comply with the Copyright license conditions for binary distribution
|
||||
if (!ppup1090.quiet) {showCopyright();}
|
||||
#endif
|
||||
|
||||
// Initialization
|
||||
ppup1090Init();
|
||||
|
||||
// Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here.
|
||||
if ((fd = anetTcpConnect(Modes.aneterr, ppup1090.net_input_beast_ipaddr, Modes.net_input_beast_port)) == ANET_ERR) {
|
||||
fprintf(stderr, "Failed to connect to %s:%d\n", ppup1090.net_input_beast_ipaddr, Modes.net_input_beast_port);
|
||||
exit(1);
|
||||
}
|
||||
//
|
||||
// Setup a service callback client structure for a beast binary input (from dump1090)
|
||||
// This is a bit dodgy under Windows. The fd parameter is a handle to the internet
|
||||
// socket on which we are receiving data. Under Linux, these seem to start at 0 and
|
||||
// count upwards. However, Windows uses "HANDLES" and these don't nececeriy start at 0.
|
||||
// dump1090 limits fd to values less than 1024, and then uses the fd parameter to
|
||||
// index into an array of clients. This is ok-ish if handles are allocated up from 0.
|
||||
// However, there is no gaurantee that Windows will behave like this, and if Windows
|
||||
// allocates a handle greater than 1024, then dump1090 won't like it. On my test machine,
|
||||
// the first Windows handle is usually in the 0x54 (84 decimal) region.
|
||||
|
||||
c = (struct client *) malloc(sizeof(*c));
|
||||
c->next = NULL;
|
||||
c->buflen = 0;
|
||||
c->fd =
|
||||
c->service =
|
||||
Modes.bis = fd;
|
||||
Modes.clients = c;
|
||||
|
||||
// Keep going till the user does something that stops us
|
||||
while (!Modes.exit) {
|
||||
modesReadFromClient(c,"",decodeBinMessage);
|
||||
interactiveRemoveStaleAircrafts();
|
||||
postCOAA ();
|
||||
}
|
||||
|
||||
// The user has stopped us, so close any socket we opened
|
||||
if (fd != ANET_ERR)
|
||||
{close(fd);}
|
||||
|
||||
closeCOAA ();
|
||||
#ifndef _WIN32
|
||||
pthread_exit(0);
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
|
@ -1,110 +0,0 @@
|
|||
// ppup1090, a Mode S PlanePlotter Uploader for dump1090 devices.
|
||||
//
|
||||
// Copyright (C) 2013 by Malcolm Robb <Support@ATTAvionics.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#ifndef __PPUP1090_H
|
||||
#define __PPUP1090_H
|
||||
|
||||
// ============================= Include files ==========================
|
||||
|
||||
#include "dump1090.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include "rtl-sdr.h"
|
||||
#include "anet.h"
|
||||
#include <netdb.h>
|
||||
#else
|
||||
#include "winstubs.h" //Put everything Windows specific in here
|
||||
#endif
|
||||
|
||||
// ============================= #defines ===============================
|
||||
|
||||
#define PPUP1090_NET_OUTPUT_IP_ADDRESS "127.0.0.1"
|
||||
|
||||
#define NOTUSED(V) ((void) V)
|
||||
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
// ======================== structure declarations ========================
|
||||
|
||||
// Program global state
|
||||
struct { // Internal state
|
||||
int quiet;
|
||||
// Networking
|
||||
uint32_t net_pp_ipaddr; // IPv4 address of PP instance
|
||||
char net_input_beast_ipaddr[32]; // IPv4 address or network name of server/RPi
|
||||
} ppup1090;
|
||||
|
||||
|
||||
// COAA Initialisation structure
|
||||
struct _coaa1090 {
|
||||
uint32_t ppIPAddr;
|
||||
double fUserLat;
|
||||
double fUserLon;
|
||||
char strAuthCode[16];
|
||||
char strRegNo[16];
|
||||
char strVersion[16];
|
||||
} coaa1090;
|
||||
|
||||
// ======================== function declarations =========================
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//
|
||||
// Functions exported from coaa1090.c
|
||||
//
|
||||
int openCOAA (void);
|
||||
int closeCOAA (void);
|
||||
int initCOAA (struct _coaa1090 coaa1090);
|
||||
void postCOAA (void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __PPUP1090_H
|
|
@ -1,85 +0,0 @@
|
|||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
#
|
||||
# Provides: dump1090
|
||||
# Required-Start: $remote_fs
|
||||
# Required-Stop: $remote_fs
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: dump1090 initscript
|
||||
|
||||
#
|
||||
### END INIT INFO
|
||||
## Fill in name of program here.
|
||||
PROG="dump1090"
|
||||
PROG_PATH="/home/pi/dump1090"
|
||||
PROG_ARGS="--quiet --net --net-ro-size 500 --net-ro-rate 5 --net-buffer 5"
|
||||
PIDFILE="/var/run/dump1090.pid"
|
||||
PROG2="ppup1090"
|
||||
PROG2_ARGS="--quiet --net-pp-addr 192.168.1.64"
|
||||
PIDFILE2="/var/run/$PROG2.pid"
|
||||
DELAY=5
|
||||
|
||||
start() {
|
||||
if [ -e $PIDFILE ]; then
|
||||
## Program is running, exit with error.
|
||||
echo "Error! $PROG is currently running!" 1>&2
|
||||
exit 1
|
||||
else
|
||||
## Change from /dev/null to something like /var/log/$PROG if you want to save output.
|
||||
cd $PROG_PATH
|
||||
./$PROG $PROG_ARGS 2>&1 >/dev/null &
|
||||
echo "$PROG started, waiting $DELAY seconds"
|
||||
touch $PIDFILE
|
||||
sleep $DELAY
|
||||
echo "Attempting to start $PROG2.."
|
||||
./$PROG2 $PROG2_ARGS 2>1 >/dev/null &
|
||||
echo "$PROG2 started"
|
||||
touch $PIDFILE2
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
if [ -e $PIDFILE ]; then
|
||||
## Program is running, so stop it
|
||||
echo "$PROG is running"
|
||||
killall $PROG2
|
||||
killall $PROG
|
||||
rm -f $PIDFILE2
|
||||
rm -f $PIDFILE
|
||||
echo "$PROG stopped"
|
||||
else
|
||||
## Program is not running, exit with error.
|
||||
echo "Error! $PROG not started!" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
## Check to see if we are running as root first.
|
||||
## Found at http://www.cyberciti.biz/tips/shell-root-user-check-script.html
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "This script must be run as root" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
exit 0
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
exit 0
|
||||
;;
|
||||
reload|restart|force-reload)
|
||||
stop
|
||||
start
|
||||
exit 0
|
||||
;;
|
||||
**)
|
||||
echo "Usage: $0 {start|stop|reload}" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
#
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -1,183 +0,0 @@
|
|||
/*
|
||||
* Module: sched.h
|
||||
*
|
||||
* Purpose:
|
||||
* Provides an implementation of POSIX realtime extensions
|
||||
* as defined in
|
||||
*
|
||||
* POSIX 1003.1b-1993 (POSIX.1b)
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The current list of contributors is contained
|
||||
* in the file CONTRIBUTORS included with the source
|
||||
* code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#if !defined(_SCHED_H)
|
||||
#define _SCHED_H
|
||||
|
||||
#undef PTW32_SCHED_LEVEL
|
||||
|
||||
#if defined(_POSIX_SOURCE)
|
||||
#define PTW32_SCHED_LEVEL 0
|
||||
/* Early POSIX */
|
||||
#endif
|
||||
|
||||
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
|
||||
#undef PTW32_SCHED_LEVEL
|
||||
#define PTW32_SCHED_LEVEL 1
|
||||
/* Include 1b, 1c and 1d */
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_NP)
|
||||
#undef PTW32_SCHED_LEVEL
|
||||
#define PTW32_SCHED_LEVEL 2
|
||||
/* Include Non-Portable extensions */
|
||||
#endif
|
||||
|
||||
#define PTW32_SCHED_LEVEL_MAX 3
|
||||
|
||||
#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_SCHED_LEVEL)
|
||||
#define PTW32_SCHED_LEVEL PTW32_SCHED_LEVEL_MAX
|
||||
/* Include everything */
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__GNUC__) && !defined(__declspec)
|
||||
# error Please upgrade your GNU compiler to one that supports __declspec.
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When building the library, you should define PTW32_BUILD so that
|
||||
* the variables/functions are exported correctly. When using the library,
|
||||
* do NOT define PTW32_BUILD, and then the variables/functions will
|
||||
* be imported correctly.
|
||||
*/
|
||||
#if !defined(PTW32_STATIC_LIB)
|
||||
# if defined(PTW32_BUILD)
|
||||
# define PTW32_DLLPORT __declspec (dllexport)
|
||||
# else
|
||||
# define PTW32_DLLPORT __declspec (dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define PTW32_DLLPORT
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is a duplicate of what is in the autoconf config.h,
|
||||
* which is only used when building the pthread-win32 libraries.
|
||||
*/
|
||||
|
||||
#if !defined(PTW32_CONFIG_H)
|
||||
# if defined(WINCE)
|
||||
# define NEED_ERRNO
|
||||
# define NEED_SEM
|
||||
# endif
|
||||
# if defined(__MINGW64__)
|
||||
# define HAVE_STRUCT_TIMESPEC
|
||||
# define HAVE_MODE_T
|
||||
# elif defined(_UWIN) || defined(__MINGW32__)
|
||||
# define HAVE_MODE_T
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX
|
||||
#if defined(NEED_ERRNO)
|
||||
#include "need_errno.h"
|
||||
#else
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#endif /* PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX */
|
||||
|
||||
#if (defined(__MINGW64__) || defined(__MINGW32__)) || defined(_UWIN)
|
||||
# if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX
|
||||
/* For pid_t */
|
||||
# include <sys/types.h>
|
||||
/* Required by Unix 98 */
|
||||
# include <time.h>
|
||||
# else
|
||||
typedef int pid_t;
|
||||
# endif
|
||||
#else
|
||||
typedef int pid_t;
|
||||
#endif
|
||||
|
||||
/* Thread scheduling policies */
|
||||
|
||||
enum {
|
||||
SCHED_OTHER = 0,
|
||||
SCHED_FIFO,
|
||||
SCHED_RR,
|
||||
SCHED_MIN = SCHED_OTHER,
|
||||
SCHED_MAX = SCHED_RR
|
||||
};
|
||||
|
||||
struct sched_param {
|
||||
int sched_priority;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
PTW32_DLLPORT int __cdecl sched_yield (void);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid);
|
||||
|
||||
/*
|
||||
* Note that this macro returns ENOTSUP rather than
|
||||
* ENOSYS as might be expected. However, returning ENOSYS
|
||||
* should mean that sched_get_priority_{min,max} are
|
||||
* not implemented as well as sched_rr_get_interval.
|
||||
* This is not the case, since we just don't support
|
||||
* round-robin scheduling. Therefore I have chosen to
|
||||
* return the same value as sched_setscheduler when
|
||||
* SCHED_RR is passed to it.
|
||||
*/
|
||||
#define sched_rr_get_interval(_pid, _interval) \
|
||||
( errno = ENOTSUP, (int) -1 )
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* End of extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#undef PTW32_SCHED_LEVEL
|
||||
#undef PTW32_SCHED_LEVEL_MAX
|
||||
|
||||
#endif /* !_SCHED_H */
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
/*
|
||||
* Module: semaphore.h
|
||||
*
|
||||
* Purpose:
|
||||
* Semaphores aren't actually part of the PThreads standard.
|
||||
* They are defined by the POSIX Standard:
|
||||
*
|
||||
* POSIX 1003.1b-1993 (POSIX.1b)
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The current list of contributors is contained
|
||||
* in the file CONTRIBUTORS included with the source
|
||||
* code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#if !defined( SEMAPHORE_H )
|
||||
#define SEMAPHORE_H
|
||||
|
||||
#undef PTW32_SEMAPHORE_LEVEL
|
||||
|
||||
#if defined(_POSIX_SOURCE)
|
||||
#define PTW32_SEMAPHORE_LEVEL 0
|
||||
/* Early POSIX */
|
||||
#endif
|
||||
|
||||
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
|
||||
#undef PTW32_SEMAPHORE_LEVEL
|
||||
#define PTW32_SEMAPHORE_LEVEL 1
|
||||
/* Include 1b, 1c and 1d */
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_NP)
|
||||
#undef PTW32_SEMAPHORE_LEVEL
|
||||
#define PTW32_SEMAPHORE_LEVEL 2
|
||||
/* Include Non-Portable extensions */
|
||||
#endif
|
||||
|
||||
#define PTW32_SEMAPHORE_LEVEL_MAX 3
|
||||
|
||||
#if !defined(PTW32_SEMAPHORE_LEVEL)
|
||||
#define PTW32_SEMAPHORE_LEVEL PTW32_SEMAPHORE_LEVEL_MAX
|
||||
/* Include everything */
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && ! defined (__declspec)
|
||||
# error Please upgrade your GNU compiler to one that supports __declspec.
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When building the library, you should define PTW32_BUILD so that
|
||||
* the variables/functions are exported correctly. When using the library,
|
||||
* do NOT define PTW32_BUILD, and then the variables/functions will
|
||||
* be imported correctly.
|
||||
*/
|
||||
#if !defined(PTW32_STATIC_LIB)
|
||||
# if defined(PTW32_BUILD)
|
||||
# define PTW32_DLLPORT __declspec (dllexport)
|
||||
# else
|
||||
# define PTW32_DLLPORT __declspec (dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define PTW32_DLLPORT
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is a duplicate of what is in the autoconf config.h,
|
||||
* which is only used when building the pthread-win32 libraries.
|
||||
*/
|
||||
|
||||
#if !defined(PTW32_CONFIG_H)
|
||||
# if defined(WINCE)
|
||||
# define NEED_ERRNO
|
||||
# define NEED_SEM
|
||||
# endif
|
||||
# if defined(__MINGW64__)
|
||||
# define HAVE_STRUCT_TIMESPEC
|
||||
# define HAVE_MODE_T
|
||||
# elif defined(_UWIN) || defined(__MINGW32__)
|
||||
# define HAVE_MODE_T
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#if PTW32_SEMAPHORE_LEVEL >= PTW32_SEMAPHORE_LEVEL_MAX
|
||||
#if defined(NEED_ERRNO)
|
||||
#include "need_errno.h"
|
||||
#else
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#endif /* PTW32_SEMAPHORE_LEVEL >= PTW32_SEMAPHORE_LEVEL_MAX */
|
||||
|
||||
#define _POSIX_SEMAPHORES
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#if !defined(HAVE_MODE_T)
|
||||
typedef unsigned int mode_t;
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct sem_t_ * sem_t;
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_init (sem_t * sem,
|
||||
int pshared,
|
||||
unsigned int value);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_wait (sem_t * sem);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_timedwait (sem_t * sem,
|
||||
const struct timespec * abstime);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_post (sem_t * sem);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem,
|
||||
int count);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_open (const char * name,
|
||||
int oflag,
|
||||
mode_t mode,
|
||||
unsigned int value);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_close (sem_t * sem);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_unlink (const char * name);
|
||||
|
||||
PTW32_DLLPORT int __cdecl sem_getvalue (sem_t * sem,
|
||||
int * sval);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* End of extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#undef PTW32_SEMAPHORE_LEVEL
|
||||
#undef PTW32_SEMAPHORE_LEVEL_MAX
|
||||
|
||||
#endif /* !SEMAPHORE_H */
|
|
@ -1,34 +0,0 @@
|
|||
// --------------------------------------------------------
|
||||
//
|
||||
// This file is to configure the configurable settings.
|
||||
// Load this file before script.js file at gmap.html.
|
||||
//
|
||||
// --------------------------------------------------------
|
||||
|
||||
// -- Output Settings -------------------------------------
|
||||
// Show metric values
|
||||
Metric = false; // true or false
|
||||
|
||||
// -- Map settings ----------------------------------------
|
||||
// The Latitude and Longitude in decimal format
|
||||
CONST_CENTERLAT = 45.0;
|
||||
CONST_CENTERLON = 9.0;
|
||||
// The google maps zoom level, 0 - 16, lower is further out
|
||||
CONST_ZOOMLVL = 5;
|
||||
|
||||
// -- Marker settings -------------------------------------
|
||||
// The default marker color
|
||||
MarkerColor = "rgb(127, 127, 127)";
|
||||
SelectedColor = "rgb(225, 225, 225)";
|
||||
StaleColor = "rgb(190, 190, 190)";
|
||||
|
||||
// -- Site Settings ---------------------------------------
|
||||
SiteShow = false; // true or false
|
||||
// The Latitude and Longitude in decimal format
|
||||
SiteLat = 45.0;
|
||||
SiteLon = 9.0;
|
||||
|
||||
SiteCircles = true; // true or false (Only shown if SiteShow is true)
|
||||
// In nautical miles or km (depending settings value 'Metric')
|
||||
SiteCirclesDistances = new Array(100,150,200);
|
||||
|
|
@ -1,318 +0,0 @@
|
|||
/**
|
||||
* CoolClock 2.1.4
|
||||
* Copyright 2010, Simon Baird
|
||||
* Released under the BSD License.
|
||||
*
|
||||
* Display an analog clock using canvas.
|
||||
* http://randomibis.com/coolclock/
|
||||
*
|
||||
*/
|
||||
|
||||
// Constructor for CoolClock objects
|
||||
window.CoolClock = function(options) {
|
||||
return this.init(options);
|
||||
}
|
||||
|
||||
// Config contains some defaults, and clock skins
|
||||
CoolClock.config = {
|
||||
tickDelay: 1000,
|
||||
longTickDelay: 15000,
|
||||
defaultRadius: 85,
|
||||
renderRadius: 100,
|
||||
defaultSkin: "chunkySwiss",
|
||||
// Should be in skin probably...
|
||||
// (TODO: allow skinning of digital display)
|
||||
showSecs: true,
|
||||
showAmPm: true,
|
||||
|
||||
skins: {
|
||||
// There are more skins in moreskins.js
|
||||
// Try making your own skin by copy/pasting one of these and tweaking it
|
||||
swissRail: {
|
||||
outerBorder: { lineWidth: 2, radius:95, color: "black", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 2, startAt: 88, endAt: 92, color: "black", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 4, startAt: 79, endAt: 92, color: "black", alpha: 1 },
|
||||
hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 }
|
||||
},
|
||||
chunkySwiss: {
|
||||
outerBorder: { lineWidth: 4, radius:97, color: "black", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 },
|
||||
hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
|
||||
},
|
||||
chunkySwissOnBlack: {
|
||||
outerBorder: { lineWidth: 4, radius:97, color: "white", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "white", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "white", alpha: 1 },
|
||||
hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "white", alpha: 1 },
|
||||
minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "white", alpha: 1 },
|
||||
secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// Test for IE so we can nurse excanvas in a couple of places
|
||||
isIE: !!document.all,
|
||||
|
||||
// Will store (a reference to) each clock here, indexed by the id of the canvas element
|
||||
clockTracker: {},
|
||||
|
||||
// For giving a unique id to coolclock canvases with no id
|
||||
noIdCount: 0
|
||||
};
|
||||
|
||||
// Define the CoolClock object's methods
|
||||
CoolClock.prototype = {
|
||||
|
||||
// Initialise using the parameters parsed from the colon delimited class
|
||||
init: function(options) {
|
||||
// Parse and store the options
|
||||
this.canvasId = options.canvasId;
|
||||
this.skinId = options.skinId || CoolClock.config.defaultSkin;
|
||||
this.displayRadius = options.displayRadius || CoolClock.config.defaultRadius;
|
||||
this.showSecondHand = typeof options.showSecondHand == "boolean" ? options.showSecondHand : true;
|
||||
this.gmtOffset = (options.gmtOffset != null && options.gmtOffset != '') ? parseFloat(options.gmtOffset) : null;
|
||||
this.showDigital = typeof options.showDigital == "boolean" ? options.showDigital : false;
|
||||
this.logClock = typeof options.logClock == "boolean" ? options.logClock : false;
|
||||
this.logClockRev = typeof options.logClock == "boolean" ? options.logClockRev : false;
|
||||
|
||||
this.tickDelay = CoolClock.config[ this.showSecondHand ? "tickDelay" : "longTickDelay" ];
|
||||
|
||||
// Get the canvas element
|
||||
this.canvas = document.getElementById(this.canvasId);
|
||||
|
||||
// Make the canvas the requested size. It's always square.
|
||||
this.canvas.setAttribute("width",this.displayRadius*2);
|
||||
this.canvas.setAttribute("height",this.displayRadius*2);
|
||||
this.canvas.style.width = this.displayRadius*2 + "px";
|
||||
this.canvas.style.height = this.displayRadius*2 + "px";
|
||||
|
||||
// Explain me please...?
|
||||
this.renderRadius = CoolClock.config.renderRadius;
|
||||
this.scale = this.displayRadius / this.renderRadius;
|
||||
|
||||
// Initialise canvas context
|
||||
this.ctx = this.canvas.getContext("2d");
|
||||
this.ctx.scale(this.scale,this.scale);
|
||||
|
||||
// Keep track of this object
|
||||
CoolClock.config.clockTracker[this.canvasId] = this;
|
||||
|
||||
// Start the clock going
|
||||
this.tick();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// Draw a circle at point x,y with params as defined in skin
|
||||
fullCircleAt: function(x,y,skin) {
|
||||
this.ctx.save();
|
||||
this.ctx.globalAlpha = skin.alpha;
|
||||
this.ctx.lineWidth = skin.lineWidth;
|
||||
|
||||
if (!CoolClock.config.isIE) {
|
||||
this.ctx.beginPath();
|
||||
}
|
||||
|
||||
if (CoolClock.config.isIE) {
|
||||
// excanvas doesn't scale line width so we will do it here
|
||||
this.ctx.lineWidth = this.ctx.lineWidth * this.scale;
|
||||
}
|
||||
|
||||
this.ctx.arc(x, y, skin.radius, 0, 2*Math.PI, false);
|
||||
|
||||
if (CoolClock.config.isIE) {
|
||||
// excanvas doesn't close the circle so let's fill in the tiny gap
|
||||
this.ctx.arc(x, y, skin.radius, -0.1, 0.1, false);
|
||||
}
|
||||
|
||||
if (skin.fillColor) {
|
||||
this.ctx.fillStyle = skin.fillColor
|
||||
this.ctx.fill();
|
||||
}
|
||||
else {
|
||||
// XXX why not stroke and fill
|
||||
this.ctx.strokeStyle = skin.color;
|
||||
this.ctx.stroke();
|
||||
}
|
||||
this.ctx.restore();
|
||||
},
|
||||
|
||||
// Draw some text centered vertically and horizontally
|
||||
drawTextAt: function(theText,x,y) {
|
||||
this.ctx.save();
|
||||
this.ctx.font = '15px sans-serif';
|
||||
var tSize = this.ctx.measureText(theText);
|
||||
if (!tSize.height) tSize.height = 15; // no height in firefox.. :(
|
||||
this.ctx.fillText(theText,x - tSize.width/2,y - tSize.height/2);
|
||||
this.ctx.restore();
|
||||
},
|
||||
|
||||
lpad2: function(num) {
|
||||
return (num < 10 ? '0' : '') + num;
|
||||
},
|
||||
|
||||
tickAngle: function(second) {
|
||||
// Log algorithm by David Bradshaw
|
||||
var tweak = 3; // If it's lower the one second mark looks wrong (?)
|
||||
if (this.logClock) {
|
||||
return second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak));
|
||||
}
|
||||
else if (this.logClockRev) {
|
||||
// Flip the seconds then flip the angle (trickiness)
|
||||
second = (60 - second) % 60;
|
||||
return 1.0 - (second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak)));
|
||||
}
|
||||
else {
|
||||
return second/60.0;
|
||||
}
|
||||
},
|
||||
|
||||
timeText: function(hour,min,sec) {
|
||||
var c = CoolClock.config;
|
||||
return '' +
|
||||
(c.showAmPm ? ((hour%12)==0 ? 12 : (hour%12)) : hour) + ':' +
|
||||
this.lpad2(min) +
|
||||
(c.showSecs ? ':' + this.lpad2(sec) : '') +
|
||||
(c.showAmPm ? (hour < 12 ? ' am' : ' pm') : '')
|
||||
;
|
||||
},
|
||||
|
||||
// Draw a radial line by rotating then drawing a straight line
|
||||
// Ha ha, I think I've accidentally used Taus, (see http://tauday.com/)
|
||||
radialLineAtAngle: function(angleFraction,skin) {
|
||||
this.ctx.save();
|
||||
this.ctx.translate(this.renderRadius,this.renderRadius);
|
||||
this.ctx.rotate(Math.PI * (2.0 * angleFraction - 0.5));
|
||||
this.ctx.globalAlpha = skin.alpha;
|
||||
this.ctx.strokeStyle = skin.color;
|
||||
this.ctx.lineWidth = skin.lineWidth;
|
||||
|
||||
if (CoolClock.config.isIE)
|
||||
// excanvas doesn't scale line width so we will do it here
|
||||
this.ctx.lineWidth = this.ctx.lineWidth * this.scale;
|
||||
|
||||
if (skin.radius) {
|
||||
this.fullCircleAt(skin.startAt,0,skin)
|
||||
}
|
||||
else {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(skin.startAt,0)
|
||||
this.ctx.lineTo(skin.endAt,0);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
this.ctx.restore();
|
||||
},
|
||||
|
||||
render: function(hour,min,sec) {
|
||||
// Get the skin
|
||||
var skin = CoolClock.config.skins[this.skinId];
|
||||
if (!skin) skin = CoolClock.config.skins[CoolClock.config.defaultSkin];
|
||||
|
||||
// Clear
|
||||
this.ctx.clearRect(0,0,this.renderRadius*2,this.renderRadius*2);
|
||||
|
||||
// Draw the outer edge of the clock
|
||||
if (skin.outerBorder)
|
||||
this.fullCircleAt(this.renderRadius,this.renderRadius,skin.outerBorder);
|
||||
|
||||
// Draw the tick marks. Every 5th one is a big one
|
||||
for (var i=0;i<60;i++) {
|
||||
(i%5) && skin.smallIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.smallIndicator);
|
||||
!(i%5) && skin.largeIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.largeIndicator);
|
||||
}
|
||||
|
||||
// Write the time
|
||||
if (this.showDigital) {
|
||||
this.drawTextAt(
|
||||
this.timeText(hour,min,sec),
|
||||
this.renderRadius,
|
||||
this.renderRadius+this.renderRadius/2
|
||||
);
|
||||
}
|
||||
|
||||
// Draw the hands
|
||||
if (skin.hourHand)
|
||||
this.radialLineAtAngle(this.tickAngle(((hour%12)*5 + min/12.0)),skin.hourHand);
|
||||
|
||||
if (skin.minuteHand)
|
||||
this.radialLineAtAngle(this.tickAngle((min + sec/60.0)),skin.minuteHand);
|
||||
|
||||
if (this.showSecondHand && skin.secondHand)
|
||||
this.radialLineAtAngle(this.tickAngle(sec),skin.secondHand);
|
||||
|
||||
// Second hand decoration doesn't render right in IE so lets turn it off
|
||||
if (!CoolClock.config.isIE && this.showSecondHand && skin.secondDecoration)
|
||||
this.radialLineAtAngle(this.tickAngle(sec),skin.secondDecoration);
|
||||
},
|
||||
|
||||
// Check the time and display the clock
|
||||
refreshDisplay: function() {
|
||||
var now = new Date();
|
||||
if (this.gmtOffset != null) {
|
||||
// Use GMT + gmtOffset
|
||||
var offsetNow = new Date(now.valueOf() + (this.gmtOffset * 1000 * 60 * 60));
|
||||
this.render(offsetNow.getUTCHours(),offsetNow.getUTCMinutes(),offsetNow.getUTCSeconds());
|
||||
}
|
||||
else {
|
||||
// Use local time
|
||||
this.render(now.getHours(),now.getMinutes(),now.getSeconds());
|
||||
}
|
||||
},
|
||||
|
||||
// Set timeout to trigger a tick in the future
|
||||
nextTick: function() {
|
||||
setTimeout("CoolClock.config.clockTracker['"+this.canvasId+"'].tick()",this.tickDelay);
|
||||
},
|
||||
|
||||
// Check the canvas element hasn't been removed
|
||||
stillHere: function() {
|
||||
return document.getElementById(this.canvasId) != null;
|
||||
},
|
||||
|
||||
// Main tick handler. Refresh the clock then setup the next tick
|
||||
tick: function() {
|
||||
if (this.stillHere()) {
|
||||
this.refreshDisplay()
|
||||
this.nextTick();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Find all canvas elements that have the CoolClock class and turns them into clocks
|
||||
CoolClock.findAndCreateClocks = function() {
|
||||
// (Let's not use a jQuery selector here so it's easier to use frameworks other than jQuery)
|
||||
var canvases = document.getElementsByTagName("canvas");
|
||||
for (var i=0;i<canvases.length;i++) {
|
||||
// Pull out the fields from the class. Example "CoolClock:chunkySwissOnBlack:1000"
|
||||
var fields = canvases[i].className.split(" ")[0].split(":");
|
||||
if (fields[0] == "CoolClock") {
|
||||
if (!canvases[i].id) {
|
||||
// If there's no id on this canvas element then give it one
|
||||
canvases[i].id = '_coolclock_auto_id_' + CoolClock.config.noIdCount++;
|
||||
}
|
||||
// Create a clock object for this element
|
||||
new CoolClock({
|
||||
canvasId: canvases[i].id,
|
||||
skinId: fields[1],
|
||||
displayRadius: fields[2],
|
||||
showSecondHand: fields[3]!='noSeconds',
|
||||
gmtOffset: fields[4],
|
||||
showDigital: fields[5]=='showDigital',
|
||||
logClock: fields[6]=='logClock',
|
||||
logClockRev: fields[6]=='logClockRev'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If you don't have jQuery then you need a body onload like this: <body onload="CoolClock.findAndCreateClocks()">
|
||||
// If you do have jQuery and it's loaded already then we can do it right now
|
||||
if (window.jQuery) jQuery(document).ready(CoolClock.findAndCreateClocks);
|
|
@ -1,785 +0,0 @@
|
|||
// Copyright 2006 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
// Known Issues:
|
||||
//
|
||||
// * Patterns are not implemented.
|
||||
// * Radial gradient are not implemented. The VML version of these look very
|
||||
// different from the canvas one.
|
||||
// * Clipping paths are not implemented.
|
||||
// * Coordsize. The width and height attribute have higher priority than the
|
||||
// width and height style values which isn't correct.
|
||||
// * Painting mode isn't implemented.
|
||||
// * Canvas width/height should is using content-box by default. IE in
|
||||
// Quirks mode will draw the canvas using border-box. Either change your
|
||||
// doctype to HTML5
|
||||
// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
|
||||
// or use Box Sizing Behavior from WebFX
|
||||
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
|
||||
// * Optimize. There is always room for speed improvements.
|
||||
|
||||
// only add this code if we do not already have a canvas implementation
|
||||
if (!window.CanvasRenderingContext2D) {
|
||||
|
||||
(function () {
|
||||
|
||||
// alias some functions to make (compiled) code shorter
|
||||
var m = Math;
|
||||
var mr = m.round;
|
||||
var ms = m.sin;
|
||||
var mc = m.cos;
|
||||
|
||||
// this is used for sub pixel precision
|
||||
var Z = 10;
|
||||
var Z2 = Z / 2;
|
||||
|
||||
var G_vmlCanvasManager_ = {
|
||||
init: function (opt_doc) {
|
||||
var doc = opt_doc || document;
|
||||
if (/MSIE/.test(navigator.userAgent) && !window.opera) {
|
||||
var self = this;
|
||||
doc.attachEvent("onreadystatechange", function () {
|
||||
self.init_(doc);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
init_: function (doc) {
|
||||
if (doc.readyState == "complete") {
|
||||
// create xmlns
|
||||
if (!doc.namespaces["g_vml_"]) {
|
||||
doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
|
||||
}
|
||||
|
||||
// setup default css
|
||||
var ss = doc.createStyleSheet();
|
||||
ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
|
||||
// default size is 300x150 in Gecko and Opera
|
||||
"text-align:left;width:300px;height:150px}" +
|
||||
"g_vml_\\:*{behavior:url(#default#VML)}";
|
||||
|
||||
// find all canvas elements
|
||||
var els = doc.getElementsByTagName("canvas");
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
if (!els[i].getContext) {
|
||||
this.initElement(els[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
fixElement_: function (el) {
|
||||
// in IE before version 5.5 we would need to add HTML: to the tag name
|
||||
// but we do not care about IE before version 6
|
||||
var outerHTML = el.outerHTML;
|
||||
|
||||
var newEl = el.ownerDocument.createElement(outerHTML);
|
||||
// if the tag is still open IE has created the children as siblings and
|
||||
// it has also created a tag with the name "/FOO"
|
||||
if (outerHTML.slice(-2) != "/>") {
|
||||
var tagName = "/" + el.tagName;
|
||||
var ns;
|
||||
// remove content
|
||||
while ((ns = el.nextSibling) && ns.tagName != tagName) {
|
||||
ns.removeNode();
|
||||
}
|
||||
// remove the incorrect closing tag
|
||||
if (ns) {
|
||||
ns.removeNode();
|
||||
}
|
||||
}
|
||||
el.parentNode.replaceChild(newEl, el);
|
||||
return newEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Public initializes a canvas element so that it can be used as canvas
|
||||
* element from now on. This is called automatically before the page is
|
||||
* loaded but if you are creating elements using createElement you need to
|
||||
* make sure this is called on the element.
|
||||
* @param {HTMLElement} el The canvas element to initialize.
|
||||
* @return {HTMLElement} the element that was created.
|
||||
*/
|
||||
initElement: function (el) {
|
||||
el = this.fixElement_(el);
|
||||
el.getContext = function () {
|
||||
if (this.context_) {
|
||||
return this.context_;
|
||||
}
|
||||
return this.context_ = new CanvasRenderingContext2D_(this);
|
||||
};
|
||||
|
||||
// do not use inline function because that will leak memory
|
||||
el.attachEvent('onpropertychange', onPropertyChange);
|
||||
el.attachEvent('onresize', onResize);
|
||||
|
||||
var attrs = el.attributes;
|
||||
if (attrs.width && attrs.width.specified) {
|
||||
// TODO: use runtimeStyle and coordsize
|
||||
// el.getContext().setWidth_(attrs.width.nodeValue);
|
||||
el.style.width = attrs.width.nodeValue + "px";
|
||||
} else {
|
||||
el.width = el.clientWidth;
|
||||
}
|
||||
if (attrs.height && attrs.height.specified) {
|
||||
// TODO: use runtimeStyle and coordsize
|
||||
// el.getContext().setHeight_(attrs.height.nodeValue);
|
||||
el.style.height = attrs.height.nodeValue + "px";
|
||||
} else {
|
||||
el.height = el.clientHeight;
|
||||
}
|
||||
//el.getContext().setCoordsize_()
|
||||
return el;
|
||||
}
|
||||
};
|
||||
|
||||
function onPropertyChange(e) {
|
||||
var el = e.srcElement;
|
||||
|
||||
switch (e.propertyName) {
|
||||
case 'width':
|
||||
el.style.width = el.attributes.width.nodeValue + "px";
|
||||
el.getContext().clearRect();
|
||||
break;
|
||||
case 'height':
|
||||
el.style.height = el.attributes.height.nodeValue + "px";
|
||||
el.getContext().clearRect();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function onResize(e) {
|
||||
var el = e.srcElement;
|
||||
if (el.firstChild) {
|
||||
el.firstChild.style.width = el.clientWidth + 'px';
|
||||
el.firstChild.style.height = el.clientHeight + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
G_vmlCanvasManager_.init();
|
||||
|
||||
// precompute "00" to "FF"
|
||||
var dec2hex = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
|
||||
}
|
||||
}
|
||||
|
||||
function createMatrixIdentity() {
|
||||
return [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
}
|
||||
|
||||
function matrixMultiply(m1, m2) {
|
||||
var result = createMatrixIdentity();
|
||||
|
||||
for (var x = 0; x < 3; x++) {
|
||||
for (var y = 0; y < 3; y++) {
|
||||
var sum = 0;
|
||||
|
||||
for (var z = 0; z < 3; z++) {
|
||||
sum += m1[x][z] * m2[z][y];
|
||||
}
|
||||
|
||||
result[x][y] = sum;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function copyState(o1, o2) {
|
||||
o2.fillStyle = o1.fillStyle;
|
||||
o2.lineCap = o1.lineCap;
|
||||
o2.lineJoin = o1.lineJoin;
|
||||
o2.lineWidth = o1.lineWidth;
|
||||
o2.miterLimit = o1.miterLimit;
|
||||
o2.shadowBlur = o1.shadowBlur;
|
||||
o2.shadowColor = o1.shadowColor;
|
||||
o2.shadowOffsetX = o1.shadowOffsetX;
|
||||
o2.shadowOffsetY = o1.shadowOffsetY;
|
||||
o2.strokeStyle = o1.strokeStyle;
|
||||
o2.arcScaleX_ = o1.arcScaleX_;
|
||||
o2.arcScaleY_ = o1.arcScaleY_;
|
||||
}
|
||||
|
||||
function processStyle(styleString) {
|
||||
var str, alpha = 1;
|
||||
|
||||
styleString = String(styleString);
|
||||
if (styleString.substring(0, 3) == "rgb") {
|
||||
var start = styleString.indexOf("(", 3);
|
||||
var end = styleString.indexOf(")", start + 1);
|
||||
var guts = styleString.substring(start + 1, end).split(",");
|
||||
|
||||
str = "#";
|
||||
for (var i = 0; i < 3; i++) {
|
||||
str += dec2hex[Number(guts[i])];
|
||||
}
|
||||
|
||||
if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
|
||||
alpha = guts[3];
|
||||
}
|
||||
} else {
|
||||
str = styleString;
|
||||
}
|
||||
|
||||
return [str, alpha];
|
||||
}
|
||||
|
||||
function processLineCap(lineCap) {
|
||||
switch (lineCap) {
|
||||
case "butt":
|
||||
return "flat";
|
||||
case "round":
|
||||
return "round";
|
||||
case "square":
|
||||
default:
|
||||
return "square";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements CanvasRenderingContext2D interface as described by
|
||||
* the WHATWG.
|
||||
* @param {HTMLElement} surfaceElement The element that the 2D context should
|
||||
* be associated with
|
||||
*/
|
||||
function CanvasRenderingContext2D_(surfaceElement) {
|
||||
this.m_ = createMatrixIdentity();
|
||||
|
||||
this.mStack_ = [];
|
||||
this.aStack_ = [];
|
||||
this.currentPath_ = [];
|
||||
|
||||
// Canvas context properties
|
||||
this.strokeStyle = "#000";
|
||||
this.fillStyle = "#000";
|
||||
|
||||
this.lineWidth = 1;
|
||||
this.lineJoin = "miter";
|
||||
this.lineCap = "butt";
|
||||
this.miterLimit = Z * 1;
|
||||
this.globalAlpha = 1;
|
||||
this.canvas = surfaceElement;
|
||||
|
||||
var el = surfaceElement.ownerDocument.createElement('div');
|
||||
el.style.width = surfaceElement.clientWidth + 'px';
|
||||
el.style.height = surfaceElement.clientHeight + 'px';
|
||||
el.style.overflow = 'hidden';
|
||||
el.style.position = 'absolute';
|
||||
surfaceElement.appendChild(el);
|
||||
|
||||
this.element_ = el;
|
||||
this.arcScaleX_ = 1;
|
||||
this.arcScaleY_ = 1;
|
||||
};
|
||||
|
||||
var contextPrototype = CanvasRenderingContext2D_.prototype;
|
||||
contextPrototype.clearRect = function() {
|
||||
this.element_.innerHTML = "";
|
||||
this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.beginPath = function() {
|
||||
// TODO: Branch current matrix so that save/restore has no effect
|
||||
// as per safari docs.
|
||||
|
||||
this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.moveTo = function(aX, aY) {
|
||||
this.currentPath_.push({type: "moveTo", x: aX, y: aY});
|
||||
this.currentX_ = aX;
|
||||
this.currentY_ = aY;
|
||||
};
|
||||
|
||||
contextPrototype.lineTo = function(aX, aY) {
|
||||
this.currentPath_.push({type: "lineTo", x: aX, y: aY});
|
||||
this.currentX_ = aX;
|
||||
this.currentY_ = aY;
|
||||
};
|
||||
|
||||
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
|
||||
aCP2x, aCP2y,
|
||||
aX, aY) {
|
||||
this.currentPath_.push({type: "bezierCurveTo",
|
||||
cp1x: aCP1x,
|
||||
cp1y: aCP1y,
|
||||
cp2x: aCP2x,
|
||||
cp2y: aCP2y,
|
||||
x: aX,
|
||||
y: aY});
|
||||
this.currentX_ = aX;
|
||||
this.currentY_ = aY;
|
||||
};
|
||||
|
||||
contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
|
||||
// the following is lifted almost directly from
|
||||
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
|
||||
var cp1x = this.currentX_ + 2.0 / 3.0 * (aCPx - this.currentX_);
|
||||
var cp1y = this.currentY_ + 2.0 / 3.0 * (aCPy - this.currentY_);
|
||||
var cp2x = cp1x + (aX - this.currentX_) / 3.0;
|
||||
var cp2y = cp1y + (aY - this.currentY_) / 3.0;
|
||||
this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, aX, aY);
|
||||
};
|
||||
|
||||
contextPrototype.arc = function(aX, aY, aRadius,
|
||||
aStartAngle, aEndAngle, aClockwise) {
|
||||
aRadius *= Z;
|
||||
var arcType = aClockwise ? "at" : "wa";
|
||||
|
||||
var xStart = aX + (mc(aStartAngle) * aRadius) - Z2;
|
||||
var yStart = aY + (ms(aStartAngle) * aRadius) - Z2;
|
||||
|
||||
var xEnd = aX + (mc(aEndAngle) * aRadius) - Z2;
|
||||
var yEnd = aY + (ms(aEndAngle) * aRadius) - Z2;
|
||||
|
||||
// IE won't render arches drawn counter clockwise if xStart == xEnd.
|
||||
if (xStart == xEnd && !aClockwise) {
|
||||
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
|
||||
// that can be represented in binary
|
||||
}
|
||||
|
||||
this.currentPath_.push({type: arcType,
|
||||
x: aX,
|
||||
y: aY,
|
||||
radius: aRadius,
|
||||
xStart: xStart,
|
||||
yStart: yStart,
|
||||
xEnd: xEnd,
|
||||
yEnd: yEnd});
|
||||
|
||||
};
|
||||
|
||||
contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
};
|
||||
|
||||
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
|
||||
// Will destroy any existing path (same as FF behaviour)
|
||||
this.beginPath();
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
this.stroke();
|
||||
};
|
||||
|
||||
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
|
||||
// Will destroy any existing path (same as FF behaviour)
|
||||
this.beginPath();
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
this.fill();
|
||||
};
|
||||
|
||||
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
|
||||
var gradient = new CanvasGradient_("gradient");
|
||||
return gradient;
|
||||
};
|
||||
|
||||
contextPrototype.createRadialGradient = function(aX0, aY0,
|
||||
aR0, aX1,
|
||||
aY1, aR1) {
|
||||
var gradient = new CanvasGradient_("gradientradial");
|
||||
gradient.radius1_ = aR0;
|
||||
gradient.radius2_ = aR1;
|
||||
gradient.focus_.x = aX0;
|
||||
gradient.focus_.y = aY0;
|
||||
return gradient;
|
||||
};
|
||||
|
||||
contextPrototype.drawImage = function (image, var_args) {
|
||||
var dx, dy, dw, dh, sx, sy, sw, sh;
|
||||
|
||||
// to find the original width we overide the width and height
|
||||
var oldRuntimeWidth = image.runtimeStyle.width;
|
||||
var oldRuntimeHeight = image.runtimeStyle.height;
|
||||
image.runtimeStyle.width = 'auto';
|
||||
image.runtimeStyle.height = 'auto';
|
||||
|
||||
// get the original size
|
||||
var w = image.width;
|
||||
var h = image.height;
|
||||
|
||||
// and remove overides
|
||||
image.runtimeStyle.width = oldRuntimeWidth;
|
||||
image.runtimeStyle.height = oldRuntimeHeight;
|
||||
|
||||
if (arguments.length == 3) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
sx = sy = 0;
|
||||
sw = dw = w;
|
||||
sh = dh = h;
|
||||
} else if (arguments.length == 5) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
dw = arguments[3];
|
||||
dh = arguments[4];
|
||||
sx = sy = 0;
|
||||
sw = w;
|
||||
sh = h;
|
||||
} else if (arguments.length == 9) {
|
||||
sx = arguments[1];
|
||||
sy = arguments[2];
|
||||
sw = arguments[3];
|
||||
sh = arguments[4];
|
||||
dx = arguments[5];
|
||||
dy = arguments[6];
|
||||
dw = arguments[7];
|
||||
dh = arguments[8];
|
||||
} else {
|
||||
throw "Invalid number of arguments";
|
||||
}
|
||||
|
||||
var d = this.getCoords_(dx, dy);
|
||||
|
||||
var w2 = sw / 2;
|
||||
var h2 = sh / 2;
|
||||
|
||||
var vmlStr = [];
|
||||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
|
||||
// For some reason that I've now forgotten, using divs didn't work
|
||||
vmlStr.push(' <g_vml_:group',
|
||||
' coordsize="', Z * W, ',', Z * H, '"',
|
||||
' coordorigin="0,0"' ,
|
||||
' style="width:', W, ';height:', H, ';position:absolute;');
|
||||
|
||||
// If filters are necessary (rotation exists), create them
|
||||
// filters are bog-slow, so only create them if abbsolutely necessary
|
||||
// The following check doesn't account for skews (which don't exist
|
||||
// in the canvas spec (yet) anyway.
|
||||
|
||||
if (this.m_[0][0] != 1 || this.m_[0][1]) {
|
||||
var filter = [];
|
||||
|
||||
// Note the 12/21 reversal
|
||||
filter.push("M11='", this.m_[0][0], "',",
|
||||
"M12='", this.m_[1][0], "',",
|
||||
"M21='", this.m_[0][1], "',",
|
||||
"M22='", this.m_[1][1], "',",
|
||||
"Dx='", mr(d.x / Z), "',",
|
||||
"Dy='", mr(d.y / Z), "'");
|
||||
|
||||
// Bounding box calculation (need to minimize displayed area so that
|
||||
// filters don't waste time on unused pixels.
|
||||
var max = d;
|
||||
var c2 = this.getCoords_(dx + dw, dy);
|
||||
var c3 = this.getCoords_(dx, dy + dh);
|
||||
var c4 = this.getCoords_(dx + dw, dy + dh);
|
||||
|
||||
max.x = Math.max(max.x, c2.x, c3.x, c4.x);
|
||||
max.y = Math.max(max.y, c2.y, c3.y, c4.y);
|
||||
|
||||
vmlStr.push("padding:0 ", mr(max.x / Z), "px ", mr(max.y / Z),
|
||||
"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
|
||||
filter.join(""), ", sizingmethod='clip');")
|
||||
} else {
|
||||
vmlStr.push("top:", mr(d.y / Z), "px;left:", mr(d.x / Z), "px;")
|
||||
}
|
||||
|
||||
vmlStr.push(' ">' ,
|
||||
'<g_vml_:image src="', image.src, '"',
|
||||
' style="width:', Z * dw, ';',
|
||||
' height:', Z * dh, ';"',
|
||||
' cropleft="', sx / w, '"',
|
||||
' croptop="', sy / h, '"',
|
||||
' cropright="', (w - sx - sw) / w, '"',
|
||||
' cropbottom="', (h - sy - sh) / h, '"',
|
||||
' />',
|
||||
'</g_vml_:group>');
|
||||
|
||||
this.element_.insertAdjacentHTML("BeforeEnd",
|
||||
vmlStr.join(""));
|
||||
};
|
||||
|
||||
contextPrototype.stroke = function(aFill) {
|
||||
var lineStr = [];
|
||||
var lineOpen = false;
|
||||
var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
|
||||
var color = a[0];
|
||||
var opacity = a[1] * this.globalAlpha;
|
||||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
|
||||
lineStr.push('<g_vml_:shape',
|
||||
' fillcolor="', color, '"',
|
||||
' filled="', Boolean(aFill), '"',
|
||||
' style="position:absolute;width:', W, ';height:', H, ';"',
|
||||
' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
|
||||
' stroked="', !aFill, '"',
|
||||
' strokeweight="', this.lineWidth, '"',
|
||||
' strokecolor="', color, '"',
|
||||
' path="');
|
||||
|
||||
var newSeq = false;
|
||||
var min = {x: null, y: null};
|
||||
var max = {x: null, y: null};
|
||||
|
||||
for (var i = 0; i < this.currentPath_.length; i++) {
|
||||
var p = this.currentPath_[i];
|
||||
|
||||
if (p.type == "moveTo") {
|
||||
lineStr.push(" m ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
lineStr.push(mr(c.x), ",", mr(c.y));
|
||||
} else if (p.type == "lineTo") {
|
||||
lineStr.push(" l ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
lineStr.push(mr(c.x), ",", mr(c.y));
|
||||
} else if (p.type == "close") {
|
||||
lineStr.push(" x ");
|
||||
} else if (p.type == "bezierCurveTo") {
|
||||
lineStr.push(" c ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
var c1 = this.getCoords_(p.cp1x, p.cp1y);
|
||||
var c2 = this.getCoords_(p.cp2x, p.cp2y);
|
||||
lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
|
||||
mr(c2.x), ",", mr(c2.y), ",",
|
||||
mr(c.x), ",", mr(c.y));
|
||||
} else if (p.type == "at" || p.type == "wa") {
|
||||
lineStr.push(" ", p.type, " ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
var cStart = this.getCoords_(p.xStart, p.yStart);
|
||||
var cEnd = this.getCoords_(p.xEnd, p.yEnd);
|
||||
|
||||
lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
|
||||
mr(c.y - this.arcScaleY_ * p.radius), " ",
|
||||
mr(c.x + this.arcScaleX_ * p.radius), ",",
|
||||
mr(c.y + this.arcScaleY_ * p.radius), " ",
|
||||
mr(cStart.x), ",", mr(cStart.y), " ",
|
||||
mr(cEnd.x), ",", mr(cEnd.y));
|
||||
}
|
||||
|
||||
|
||||
// TODO: Following is broken for curves due to
|
||||
// move to proper paths.
|
||||
|
||||
// Figure out dimensions so we can do gradient fills
|
||||
// properly
|
||||
if(c) {
|
||||
if (min.x == null || c.x < min.x) {
|
||||
min.x = c.x;
|
||||
}
|
||||
if (max.x == null || c.x > max.x) {
|
||||
max.x = c.x;
|
||||
}
|
||||
if (min.y == null || c.y < min.y) {
|
||||
min.y = c.y;
|
||||
}
|
||||
if (max.y == null || c.y > max.y) {
|
||||
max.y = c.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
lineStr.push(' ">');
|
||||
|
||||
if (typeof this.fillStyle == "object") {
|
||||
var focus = {x: "50%", y: "50%"};
|
||||
var width = (max.x - min.x);
|
||||
var height = (max.y - min.y);
|
||||
var dimension = (width > height) ? width : height;
|
||||
|
||||
focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
|
||||
focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
|
||||
|
||||
var colors = [];
|
||||
|
||||
// inside radius (%)
|
||||
if (this.fillStyle.type_ == "gradientradial") {
|
||||
var inside = (this.fillStyle.radius1_ / dimension * 100);
|
||||
|
||||
// percentage that outside radius exceeds inside radius
|
||||
var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
|
||||
} else {
|
||||
var inside = 0;
|
||||
var expansion = 100;
|
||||
}
|
||||
|
||||
var insidecolor = {offset: null, color: null};
|
||||
var outsidecolor = {offset: null, color: null};
|
||||
|
||||
// We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
|
||||
// won't interpret it correctly
|
||||
this.fillStyle.colors_.sort(function (cs1, cs2) {
|
||||
return cs1.offset - cs2.offset;
|
||||
});
|
||||
|
||||
for (var i = 0; i < this.fillStyle.colors_.length; i++) {
|
||||
var fs = this.fillStyle.colors_[i];
|
||||
|
||||
colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
|
||||
|
||||
if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
|
||||
insidecolor.offset = fs.offset;
|
||||
insidecolor.color = fs.color;
|
||||
}
|
||||
|
||||
if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
|
||||
outsidecolor.offset = fs.offset;
|
||||
outsidecolor.color = fs.color;
|
||||
}
|
||||
}
|
||||
colors.pop();
|
||||
|
||||
lineStr.push('<g_vml_:fill',
|
||||
' color="', outsidecolor.color, '"',
|
||||
' color2="', insidecolor.color, '"',
|
||||
' type="', this.fillStyle.type_, '"',
|
||||
' focusposition="', focus.x, ', ', focus.y, '"',
|
||||
' colors="', colors.join(""), '"',
|
||||
' opacity="', opacity, '" />');
|
||||
} else if (aFill) {
|
||||
lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
|
||||
} else {
|
||||
lineStr.push(
|
||||
'<g_vml_:stroke',
|
||||
' opacity="', opacity,'"',
|
||||
' joinstyle="', this.lineJoin, '"',
|
||||
' miterlimit="', this.miterLimit, '"',
|
||||
' endcap="', processLineCap(this.lineCap) ,'"',
|
||||
' weight="', this.lineWidth, 'px"',
|
||||
' color="', color,'" />'
|
||||
);
|
||||
}
|
||||
|
||||
lineStr.push("</g_vml_:shape>");
|
||||
|
||||
this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
|
||||
|
||||
this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.fill = function() {
|
||||
this.stroke(true);
|
||||
}
|
||||
|
||||
contextPrototype.closePath = function() {
|
||||
this.currentPath_.push({type: "close"});
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
contextPrototype.getCoords_ = function(aX, aY) {
|
||||
return {
|
||||
x: Z * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - Z2,
|
||||
y: Z * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - Z2
|
||||
}
|
||||
};
|
||||
|
||||
contextPrototype.save = function() {
|
||||
var o = {};
|
||||
copyState(this, o);
|
||||
this.aStack_.push(o);
|
||||
this.mStack_.push(this.m_);
|
||||
this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.restore = function() {
|
||||
copyState(this.aStack_.pop(), this);
|
||||
this.m_ = this.mStack_.pop();
|
||||
};
|
||||
|
||||
contextPrototype.translate = function(aX, aY) {
|
||||
var m1 = [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[aX, aY, 1]
|
||||
];
|
||||
|
||||
this.m_ = matrixMultiply(m1, this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.rotate = function(aRot) {
|
||||
var c = mc(aRot);
|
||||
var s = ms(aRot);
|
||||
|
||||
var m1 = [
|
||||
[c, s, 0],
|
||||
[-s, c, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
this.m_ = matrixMultiply(m1, this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.scale = function(aX, aY) {
|
||||
this.arcScaleX_ *= aX;
|
||||
this.arcScaleY_ *= aY;
|
||||
var m1 = [
|
||||
[aX, 0, 0],
|
||||
[0, aY, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
this.m_ = matrixMultiply(m1, this.m_);
|
||||
};
|
||||
|
||||
/******** STUBS ********/
|
||||
contextPrototype.clip = function() {
|
||||
// TODO: Implement
|
||||
};
|
||||
|
||||
contextPrototype.arcTo = function() {
|
||||
// TODO: Implement
|
||||
};
|
||||
|
||||
contextPrototype.createPattern = function() {
|
||||
return new CanvasPattern_;
|
||||
};
|
||||
|
||||
// Gradient / Pattern Stubs
|
||||
function CanvasGradient_(aType) {
|
||||
this.type_ = aType;
|
||||
this.radius1_ = 0;
|
||||
this.radius2_ = 0;
|
||||
this.colors_ = [];
|
||||
this.focus_ = {x: 0, y: 0};
|
||||
}
|
||||
|
||||
CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
|
||||
aColor = processStyle(aColor);
|
||||
this.colors_.push({offset: 1-aOffset, color: aColor});
|
||||
};
|
||||
|
||||
function CanvasPattern_() {}
|
||||
|
||||
// set up externs
|
||||
G_vmlCanvasManager = G_vmlCanvasManager_;
|
||||
CanvasRenderingContext2D = CanvasRenderingContext2D_;
|
||||
CanvasGradient = CanvasGradient_;
|
||||
CanvasPattern = CanvasPattern_;
|
||||
|
||||
})();
|
||||
|
||||
} // if
|
|
@ -1,212 +0,0 @@
|
|||
CoolClock.config.skins = {
|
||||
|
||||
swissRail: {
|
||||
outerBorder: { lineWidth: 2, radius: 95, color: "black", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 2, startAt: 88, endAt: 92, color: "black", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 4, startAt: 79, endAt: 92, color: "black", alpha: 1 },
|
||||
hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 }
|
||||
},
|
||||
|
||||
chunkySwiss: {
|
||||
outerBorder: { lineWidth: 4, radius: 97, color: "black", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 },
|
||||
hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
|
||||
},
|
||||
|
||||
chunkySwissOnBlack: {
|
||||
outerBorder: { lineWidth: 4, radius: 97, color: "white", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "white", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "white", alpha: 1 },
|
||||
hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "white", alpha: 1 },
|
||||
minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "white", alpha: 1 },
|
||||
secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
|
||||
},
|
||||
|
||||
fancy: {
|
||||
outerBorder: { lineWidth: 5, radius: 95, color: "green", alpha: 0.7 },
|
||||
smallIndicator: { lineWidth: 1, startAt: 80, endAt: 93, color: "black", alpha: 0.4 },
|
||||
largeIndicator: { lineWidth: 1, startAt: 30, endAt: 93, color: "black", alpha: 0.5 },
|
||||
hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "blue", alpha: 0.7 },
|
||||
minuteHand: { lineWidth: 7, startAt: -15, endAt: 92, color: "red", alpha: 0.7 },
|
||||
secondHand: { lineWidth: 10, startAt: 80, endAt: 85, color: "blue", alpha: 0.3 },
|
||||
secondDecoration: { lineWidth: 1, startAt: 30, radius: 50, fillColor: "blue", color: "red", alpha: 0.15 }
|
||||
},
|
||||
|
||||
machine: {
|
||||
outerBorder: { lineWidth: 60, radius: 55, color: "#dd6655", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 4, startAt: 80, endAt: 95, color: "white", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 14, startAt: 77, endAt: 92, color: "#dd6655", alpha: 1 },
|
||||
hourHand: { lineWidth: 18, startAt: -15, endAt: 40, color: "white", alpha: 1 },
|
||||
minuteHand: { lineWidth: 14, startAt: 24, endAt: 100, color: "#771100", alpha: 0.5 },
|
||||
secondHand: { lineWidth: 3, startAt: 22, endAt: 83, color: "green", alpha: 0 },
|
||||
secondDecoration: { lineWidth: 1, startAt: 52, radius: 26, fillColor: "#ffcccc", color: "red", alpha: 0.5 }
|
||||
},
|
||||
|
||||
simonbaird_com: {
|
||||
hourHand: { lineWidth: 80, startAt: -15, endAt: 35, color: 'magenta', alpha: 0.5 },
|
||||
minuteHand: { lineWidth: 80, startAt: -15, endAt: 65, color: 'cyan', alpha: 0.5 },
|
||||
secondDecoration: { lineWidth: 1, startAt: 40, radius: 40, color: "#fff", fillColor: 'yellow', alpha: 0.5 }
|
||||
},
|
||||
|
||||
// by bonstio, http://bonstio.net
|
||||
classic/*was gIG*/: {
|
||||
outerBorder: { lineWidth: 185, radius: 1, color: "#E5ECF9", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 2, startAt: 89, endAt: 94, color: "#3366CC", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 4, startAt: 83, endAt: 94, color: "#3366CC", alpha: 1 },
|
||||
hourHand: { lineWidth: 5, startAt: 0, endAt: 60, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 4, startAt: 0, endAt: 80, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: .85 },
|
||||
secondDecoration: { lineWidth: 3, startAt: 0, radius: 2, fillColor: "black", color: "black", alpha: 1 }
|
||||
},
|
||||
|
||||
modern/*was gIG2*/: {
|
||||
outerBorder: { lineWidth: 185, radius: 1, color: "#E5ECF9", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 5, startAt: 88, endAt: 94, color: "#3366CC", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 5, startAt: 88, endAt: 94, color: "#3366CC", alpha: 1 },
|
||||
hourHand: { lineWidth: 8, startAt: 0, endAt: 60, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 8, startAt: 0, endAt: 80, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 5, startAt: 80, endAt: 85, color: "red", alpha: .85 },
|
||||
secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
|
||||
},
|
||||
|
||||
simple/*was gIG3*/: {
|
||||
outerBorder: { lineWidth: 185, radius: 1, color: "#E5ECF9", alpha: 1 },
|
||||
smallIndicator: { lineWidth: 10, startAt: 90, endAt: 94, color: "#3366CC", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 10, startAt: 90, endAt: 94, color: "#3366CC", alpha: 1 },
|
||||
hourHand: { lineWidth: 8, startAt: 0, endAt: 60, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 8, startAt: 0, endAt: 80, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 5, startAt: 80, endAt: 85, color: "red", alpha: .85 },
|
||||
secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
|
||||
},
|
||||
|
||||
// by securephp
|
||||
securephp: {
|
||||
outerBorder: { lineWidth: 100, radius: 0.45, color: "#669900", alpha: 0.3 },
|
||||
smallIndicator: { lineWidth: 2, startAt: 80, endAt: 90 , color: "green", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 8.5, startAt: 20, endAt: 40 , color: "green", alpha: 0.4 },
|
||||
hourHand: { lineWidth: 3, startAt: 0, endAt: 60, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 2, startAt: 0, endAt: 75, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 1, startAt: -10, endAt: 80, color: "blue", alpha: 0.8 },
|
||||
secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "blue", color: "red", alpha: 1 }
|
||||
},
|
||||
|
||||
Tes2: {
|
||||
outerBorder: { lineWidth: 4, radius: 95, color: "black", alpha: 0.5 },
|
||||
smallIndicator: { lineWidth: 1, startAt: 10, endAt: 50 , color: "#66CCFF", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 8.5, startAt: 60, endAt: 70, color: "#6699FF", alpha: 1 },
|
||||
hourHand: { lineWidth: 5, startAt: -15, endAt: 60, color: "black", alpha: 0.7 },
|
||||
minuteHand: { lineWidth: 3, startAt: -25, endAt: 75, color: "black", alpha: 0.7 },
|
||||
secondHand: { lineWidth: 1.5, startAt: -20, endAt: 88, color: "red", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 1, startAt: 20, radius: 4, fillColor: "blue", color: "red", alpha: 1 }
|
||||
},
|
||||
|
||||
|
||||
Lev: {
|
||||
outerBorder: { lineWidth: 10, radius: 95, color: "#CCFF33", alpha: 0.65 },
|
||||
smallIndicator: { lineWidth: 5, startAt: 84, endAt: 90, color: "#996600", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 40, startAt: 25, endAt: 95, color: "#336600", alpha: 0.55 },
|
||||
hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 0.9 },
|
||||
minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 0.85 },
|
||||
secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 2, startAt: 5, radius: 10, fillColor: "black", color: "black", alpha: 1 }
|
||||
},
|
||||
|
||||
Sand: {
|
||||
outerBorder: { lineWidth: 1, radius: 70, color: "black", alpha: 0.5 },
|
||||
smallIndicator: { lineWidth: 3, startAt: 50, endAt: 70, color: "#0066FF", alpha: 0.5 },
|
||||
largeIndicator: { lineWidth: 200, startAt: 80, endAt: 95, color: "#996600", alpha: 0.75 },
|
||||
hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 0.9 },
|
||||
minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 0.85 },
|
||||
secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 2, startAt: 5, radius: 10, fillColor: "black", color: "black", alpha: 1 }
|
||||
},
|
||||
|
||||
Sun: {
|
||||
outerBorder: { lineWidth: 100, radius: 140, color: "#99FFFF", alpha: 0.2 },
|
||||
smallIndicator: { lineWidth: 300, startAt: 50, endAt: 70, color: "black", alpha: 0.1 },
|
||||
largeIndicator: { lineWidth: 5, startAt: 80, endAt: 95, color: "black", alpha: 0.65 },
|
||||
hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 0.9 },
|
||||
minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 0.85 },
|
||||
secondHand: { lineWidth: 1, startAt: 0, endAt: 90, color: "black", alpha: 1 },
|
||||
secondDecoration: { lineWidth: 2, startAt: 5, radius: 10, fillColor: "black", color: "black", alpha: 1 }
|
||||
},
|
||||
|
||||
Tor: {
|
||||
outerBorder: { lineWidth: 10, radius: 88, color: "#996600", alpha: 0.9 },
|
||||
smallIndicator: { lineWidth: 6, startAt: -10, endAt: 73, color: "green", alpha: 0.3 },
|
||||
largeIndicator: { lineWidth: 6, startAt: 73, endAt: 100, color: "black", alpha: 0.65 },
|
||||
hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 1, startAt: -73, endAt: 73, color: "black", alpha: 0.8 },
|
||||
secondDecoration: { lineWidth: 2, startAt: 5, radius: 10, fillColor: "black", color: "black", alpha: 1 }
|
||||
},
|
||||
|
||||
Cold: {
|
||||
outerBorder: { lineWidth: 15, radius: 90, color: "black", alpha: 0.3 },
|
||||
smallIndicator: { lineWidth: 15, startAt: -10, endAt: 95, color: "blue", alpha: 0.1 },
|
||||
largeIndicator: { lineWidth: 3, startAt: 80, endAt: 95, color: "blue", alpha: 0.65 },
|
||||
hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 0.8 },
|
||||
secondDecoration: { lineWidth: 5, startAt: 30, radius: 10, fillColor: "black", color: "black", alpha: 1 }
|
||||
},
|
||||
|
||||
Babosa: {
|
||||
outerBorder: { lineWidth: 100, radius: 25, color: "blue", alpha: 0.25 },
|
||||
smallIndicator: { lineWidth: 3, startAt: 90, endAt: 95, color: "#3366CC", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 4, startAt: 75, endAt: 95, color: "#3366CC", alpha: 1 },
|
||||
hourHand: { lineWidth: 4, startAt: 0, endAt: 60, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 3, startAt: 0, endAt: 85, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 12, startAt: 75, endAt: 90, color: "red", alpha: 0.8 },
|
||||
secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
|
||||
},
|
||||
|
||||
Tumb: {
|
||||
outerBorder: { lineWidth: 105, radius: 5, color: "green", alpha: 0.4 },
|
||||
smallIndicator: { lineWidth: 1, startAt: 93, endAt: 98, color: "green", alpha: 1 },
|
||||
largeIndicator: { lineWidth: 50, startAt: 0, endAt: 89, color: "red", alpha: 0.14 },
|
||||
hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 3, startAt: 0, endAt: 80, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 0.8 },
|
||||
secondDecoration: { lineWidth: 5, startAt: 50, radius: 90, fillColor: "black", color: "black", alpha: 0.05 }
|
||||
},
|
||||
|
||||
Stone: {
|
||||
outerBorder: { lineWidth: 15, radius: 80, color: "#339933", alpha: 0.5 },
|
||||
smallIndicator: { lineWidth: 2, startAt: 70, endAt: 90, color: "#FF3300", alpha: 0.7 },
|
||||
largeIndicator: { lineWidth: 15, startAt: 0, endAt: 29, color: "#FF6600", alpha: 0.3 },
|
||||
hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 3, startAt: 0, endAt: 75, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 1, startAt: 0, endAt: 85, color: "black", alpha: 0.8 },
|
||||
secondDecoration: { lineWidth: 5, startAt: 50, radius: 90, fillColor: "black", color: "black", alpha: 0.05 }
|
||||
},
|
||||
|
||||
Disc: {
|
||||
outerBorder: { lineWidth: 105, radius: 1, color: "#666600", alpha: 0.2 },
|
||||
smallIndicator: { lineWidth: 1, startAt: 58, endAt: 95, color: "#669900", alpha: 0.8 },
|
||||
largeIndicator: { lineWidth: 6, startAt: 25, endAt: 35, color: "#666600", alpha: 1 },
|
||||
hourHand: { lineWidth: 4, startAt: 0, endAt: 65, color: "black", alpha: 1 },
|
||||
minuteHand: { lineWidth: 3, startAt: 0, endAt: 75, color: "black", alpha: 1 },
|
||||
secondHand: { lineWidth: 1, startAt: -75, endAt: 75, color: "#99CC00", alpha: 0.8 },
|
||||
secondDecoration: { lineWidth: 5, startAt: 50, radius: 90, fillColor: "#00FF00", color: "green", alpha: 0.05 }
|
||||
},
|
||||
|
||||
// By Yoo Nhe
|
||||
watermelon: {
|
||||
outerBorder: { lineWidth: 100, radius: 1.7, color: "#d93d04", alpha: 5 },
|
||||
smallIndicator: { lineWidth: 2, startAt: 50, endAt: 70, color: "#d93d04", alpha: 5 },
|
||||
largeIndicator: { lineWidth: 2, startAt: 45, endAt: 94, color: "#a9bf04", alpha: 1 },
|
||||
hourHand: { lineWidth: 5, startAt: -20, endAt: 80, color: "#8c0d17", alpha: 1 },
|
||||
minuteHand: { lineWidth: 2, startAt: -20, endAt: 80, color: "#7c8c03", alpha: .9 },
|
||||
secondHand: { lineWidth: 2, startAt: 70, endAt: 94, color: "#d93d04", alpha: .85 },
|
||||
secondDecoration: { lineWidth: 1, startAt: 70, radius: 3, fillColor: "red", color: "black", alpha: .7 }
|
||||
}
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
// -----------------------------------------------------
|
||||
//
|
||||
// This file is so users can modify how the page acts
|
||||
// without diving to deep in the code. This way we can
|
||||
// also try out or hold custom code for ourselves and
|
||||
// not check it into the repo.
|
||||
//
|
||||
// There is a div id'ed as plane_extension for use with
|
||||
// this javascript file.
|
||||
// -----------------------------------------------------
|
||||
|
||||
function extendedInitalize() {
|
||||
// Write your initalization here
|
||||
// Gets called just before the 1-sec function call loop is setup
|
||||
}
|
||||
|
||||
function extendedPulse() {
|
||||
// This will get called every second after all the main functions
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
|
||||
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false&libraries=geometry"></script>
|
||||
<script type="text/javascript" src="config.js"></script>
|
||||
<script type="text/javascript" src="planeObject.js"></script>
|
||||
<script type="text/javascript" src="options.js"></script>
|
||||
<script type="text/javascript" src="extension.js"></script>
|
||||
<script type="text/javascript" src="script.js"></script>
|
||||
<script type="text/javascript" src="coolclock/excanvas.js"></script>
|
||||
<script type="text/javascript" src="coolclock/coolclock.js"></script>
|
||||
<script type="text/javascript" src="coolclock/moreskins.js"></script>
|
||||
<title>DUMP1090</title>
|
||||
</head>
|
||||
<body onload="initialize()">
|
||||
<div id="dialog-modal" title="Basic modal dialog" style="display:none;">
|
||||
<p>The settings feature is coming soon. Keep checking GitHub.</p>
|
||||
</div>
|
||||
<div id="map_container">
|
||||
<div id="map_canvas"></div>
|
||||
</div>
|
||||
<div id="sidebar_container">
|
||||
<div id="sidebar_canvas">
|
||||
<div id="timestamps" style="align: center">
|
||||
<table width="100%"><tr>
|
||||
<td>Local Time</td>
|
||||
<td>
|
||||
<canvas id="localclock" class="CoolClock:classic:40"></canvas>
|
||||
</td>
|
||||
<td>UTC Time</td>
|
||||
<td>
|
||||
<canvas id="gmtclock" class="CoolClock:classic:40::0"></canvas>
|
||||
</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<div id="sudo_buttons">
|
||||
<table width="100%"><tr>
|
||||
<td width="150" style="text-align: center;" class="pointer">
|
||||
[ <span onclick="resetMap();">Reset Map</span> ]
|
||||
</td>
|
||||
<td> </td>
|
||||
<td width="150" style="text-align: center;" id="setings_button" class="pointer">
|
||||
[ <span onclick="optionsModal();">Settings</span> ]
|
||||
</td>
|
||||
</tr></table>
|
||||
</div>
|
||||
|
||||
<div id="plane_detail"></div>
|
||||
<div id="options"></div>
|
||||
<div id="planes_table"></div>
|
||||
<div id="plane_extension"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="SpecialSquawkWarning">
|
||||
<b>Squak 7x00 is reported and shown.</b><br>
|
||||
This is most likely an error in reciving or decoding.<br>
|
||||
Please do not call the local authorities, they already know about it if it is valid squak.
|
||||
</div>
|
||||
<div id="container_splitter"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,17 +0,0 @@
|
|||
var listKMLType = ['Approch', 'Departure', 'Transit', 'Custom1', 'Custom2'];
|
||||
var listKMLs = localStorage['listKMLs'] || [];
|
||||
|
||||
function optionsInitalize() {
|
||||
// Write your initalization here
|
||||
// Gets called just before the 1-sec function call loop is setup
|
||||
$( "#dialog-modal" ).dialog({
|
||||
height: 140,
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
closeOnEscape: false
|
||||
});
|
||||
}
|
||||
|
||||
function optionsModal() {
|
||||
$( "#dialog-modal" ).dialog( "open");
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
var planeObject = {
|
||||
oldlat : null,
|
||||
oldlon : null,
|
||||
oldalt : null,
|
||||
|
||||
// Basic location information
|
||||
altitude : null,
|
||||
speed : null,
|
||||
track : null,
|
||||
latitude : null,
|
||||
longitude : null,
|
||||
|
||||
// Info about the plane
|
||||
flight : null,
|
||||
squawk : null,
|
||||
icao : null,
|
||||
is_selected : false,
|
||||
|
||||
// Data packet numbers
|
||||
messages : null,
|
||||
seen : null,
|
||||
|
||||
// Vaild...
|
||||
vPosition : false,
|
||||
vTrack : false,
|
||||
|
||||
// GMap Details
|
||||
marker : null,
|
||||
markerColor : MarkerColor,
|
||||
lines : [],
|
||||
trackdata : new Array(),
|
||||
trackline : new Array(),
|
||||
|
||||
// When was this last updated?
|
||||
updated : null,
|
||||
reapable : false,
|
||||
|
||||
// Appends data to the running track so we can get a visual tail on the plane
|
||||
// Only useful for a long running browser session.
|
||||
funcAddToTrack : function(){
|
||||
// TODO: Write this function out
|
||||
this.trackdata.push([this.latitude, this.longitude, this.altitude, this.track, this.speed]);
|
||||
this.trackline.push(new google.maps.LatLng(this.latitude, this.longitude));
|
||||
},
|
||||
|
||||
// This is to remove the line from the screen if we deselect the plane
|
||||
funcClearLine : function() {
|
||||
if (this.line) {
|
||||
this.line.setMap(null);
|
||||
this.line = null;
|
||||
}
|
||||
},
|
||||
|
||||
// Should create an icon for us to use on the map...
|
||||
funcGetIcon : function() {
|
||||
this.markerColor = MarkerColor;
|
||||
// If this marker is selected we should make it lighter than the rest.
|
||||
if (this.is_selected == true) {
|
||||
this.markerColor = SelectedColor;
|
||||
}
|
||||
|
||||
// If we have not seen a recent update, change color
|
||||
if (this.seen > 15) {
|
||||
this.markerColor = StaleColor;
|
||||
}
|
||||
|
||||
// Plane marker
|
||||
var baseSvg = {
|
||||
planeData : "M 1.9565564,41.694305 C 1.7174505,40.497708 1.6419973,38.448747 " +
|
||||
"1.8096508,37.70494 1.8936398,37.332056 2.0796653,36.88191 2.222907,36.70461 " +
|
||||
"2.4497603,36.423844 4.087816,35.47248 14.917931,29.331528 l 12.434577," +
|
||||
"-7.050718 -0.04295,-7.613412 c -0.03657,-6.4844888 -0.01164,-7.7625804 " +
|
||||
"0.168134,-8.6194061 0.276129,-1.3160905 0.762276,-2.5869575 1.347875," +
|
||||
"-3.5235502 l 0.472298,-0.7553719 1.083746,-0.6085497 c 1.194146,-0.67053522 " +
|
||||
"1.399524,-0.71738842 2.146113,-0.48960552 1.077005,0.3285939 2.06344," +
|
||||
"1.41299352 2.797602,3.07543322 0.462378,1.0469993 0.978731,2.7738408 " +
|
||||
"1.047635,3.5036272 0.02421,0.2570284 0.06357,3.78334 0.08732,7.836246 0.02375," +
|
||||
"4.052905 0.0658,7.409251 0.09345,7.458546 0.02764,0.04929 5.600384,3.561772 " +
|
||||
"12.38386,7.805502 l 12.333598,7.715871 0.537584,0.959688 c 0.626485,1.118378 " +
|
||||
"0.651686,1.311286 0.459287,3.516442 -0.175469,2.011604 -0.608966,2.863924 " +
|
||||
"-1.590344,3.127136 -0.748529,0.200763 -1.293144,0.03637 -10.184829,-3.07436 " +
|
||||
"C 48.007733,41.72562 44.793806,40.60197 43.35084,40.098045 l -2.623567," +
|
||||
"-0.916227 -1.981212,-0.06614 c -1.089663,-0.03638 -1.985079,-0.05089 -1.989804," +
|
||||
"-0.03225 -0.0052,0.01863 -0.02396,2.421278 -0.04267,5.339183 -0.0395,6.147742 " +
|
||||
"-0.143635,7.215456 -0.862956,8.845475 l -0.300457,0.680872 2.91906,1.361455 " +
|
||||
"c 2.929379,1.366269 3.714195,1.835385 4.04589,2.41841 0.368292,0.647353 " +
|
||||
"0.594634,2.901439 0.395779,3.941627 -0.0705,0.368571 -0.106308,0.404853 " +
|
||||
"-0.765159,0.773916 L 41.4545,62.83158 39.259237,62.80426 c -6.030106,-0.07507 " +
|
||||
"-16.19508,-0.495041 -16.870991,-0.697033 -0.359409,-0.107405 -0.523792," +
|
||||
"-0.227482 -0.741884,-0.541926 -0.250591,-0.361297 -0.28386,-0.522402 -0.315075," +
|
||||
"-1.52589 -0.06327,-2.03378 0.23288,-3.033615 1.077963,-3.639283 0.307525," +
|
||||
"-0.2204 4.818478,-2.133627 6.017853,-2.552345 0.247872,-0.08654 0.247455," +
|
||||
"-0.102501 -0.01855,-0.711959 -0.330395,-0.756986 -0.708622,-2.221756 -0.832676," +
|
||||
"-3.224748 -0.05031,-0.406952 -0.133825,-3.078805 -0.185533,-5.937448 -0.0517," +
|
||||
"-2.858644 -0.145909,-5.208974 -0.209316,-5.222958 -0.06341,-0.01399 -0.974464," +
|
||||
"-0.0493 -2.024551,-0.07845 L 23.247235,38.61921 18.831373,39.8906 C 4.9432155," +
|
||||
"43.88916 4.2929558,44.057819 3.4954426,43.86823 2.7487826,43.690732 2.2007966," +
|
||||
"42.916622 1.9565564,41.694305 z"
|
||||
};
|
||||
|
||||
// If the squawk code is one of the international emergency codes,
|
||||
// match the info window alert color.
|
||||
if (this.squawk == 7500) {
|
||||
this.markerColor = "rgb(255, 85, 85)";
|
||||
}
|
||||
if (this.squawk == 7600) {
|
||||
this.markerColor = "rgb(0, 255, 255)";
|
||||
}
|
||||
if (this.squawk == 7700) {
|
||||
this.markerColor = "rgb(255, 255, 0)";
|
||||
}
|
||||
|
||||
// If we have not overwritten color by now, an extension still could but
|
||||
// just keep on trucking. :)
|
||||
|
||||
return {
|
||||
strokeWeight: (this.is_selected ? 2 : 1),
|
||||
path: "M 0,0 "+ baseSvg["planeData"],
|
||||
scale: 0.4,
|
||||
fillColor: this.markerColor,
|
||||
fillOpacity: 0.9,
|
||||
anchor: new google.maps.Point(32, 32), // Set anchor to middle of plane.
|
||||
rotation: this.track
|
||||
};
|
||||
},
|
||||
|
||||
// TODO: Trigger actions of a selecting a plane
|
||||
funcSelectPlane : function(selectedPlane){
|
||||
selectPlaneByHex(this.icao);
|
||||
},
|
||||
|
||||
// Update our data
|
||||
funcUpdateData : function(data){
|
||||
// So we can find out if we moved
|
||||
var oldlat = this.latitude;
|
||||
var oldlon = this.longitude;
|
||||
var oldalt = this.altitude;
|
||||
|
||||
// Update all of our data
|
||||
this.updated = new Date().getTime();
|
||||
this.altitude = data.altitude;
|
||||
this.speed = data.speed;
|
||||
this.track = data.track;
|
||||
this.latitude = data.lat;
|
||||
this.longitude = data.lon;
|
||||
this.flight = data.flight;
|
||||
this.squawk = data.squawk;
|
||||
this.icao = data.hex;
|
||||
this.messages = data.messages;
|
||||
this.seen = data.seen;
|
||||
|
||||
// If no packet in over 58 seconds, consider the plane reapable
|
||||
// This way we can hold it, but not show it just in case the plane comes back
|
||||
if (this.seen > 58) {
|
||||
this.reapable = true;
|
||||
if (this.marker) {
|
||||
this.marker.setMap(null);
|
||||
this.marker = null;
|
||||
}
|
||||
if (this.line) {
|
||||
this.line.setMap(null);
|
||||
this.line = null;
|
||||
}
|
||||
if (SelectedPlane == this.icao) {
|
||||
if (this.is_selected) {
|
||||
this.is_selected = false;
|
||||
}
|
||||
SelectedPlane = null;
|
||||
}
|
||||
} else {
|
||||
if (this.reapable == true) {
|
||||
}
|
||||
this.reapable = false;
|
||||
}
|
||||
|
||||
// Is the position valid?
|
||||
if ((data.validposition == 1) && (this.reapable == false)) {
|
||||
this.vPosition = true;
|
||||
|
||||
// Detech if the plane has moved
|
||||
changeLat = false;
|
||||
changeLon = false;
|
||||
changeAlt = false;
|
||||
if (oldlat != this.latitude) {
|
||||
changeLat = true;
|
||||
}
|
||||
if (oldlon != this.longitude) {
|
||||
changeLon = true;
|
||||
}
|
||||
if (oldalt != this.altitude) {
|
||||
changeAlt = true;
|
||||
}
|
||||
// Right now we only care about lat/long, if alt is updated only, oh well
|
||||
if ((changeLat == true) || (changeLon == true)) {
|
||||
this.funcAddToTrack();
|
||||
if (this.is_selected) {
|
||||
this.line = this.funcUpdateLines();
|
||||
}
|
||||
}
|
||||
this.marker = this.funcUpdateMarker();
|
||||
PlanesOnMap++;
|
||||
} else {
|
||||
this.vPosition = false;
|
||||
}
|
||||
|
||||
// Do we have a valid track for the plane?
|
||||
if (data.validtrack == 1)
|
||||
this.vTrack = true;
|
||||
else
|
||||
this.vTrack = false;
|
||||
},
|
||||
|
||||
// Update our marker on the map
|
||||
funcUpdateMarker: function() {
|
||||
if (this.marker) {
|
||||
this.marker.setPosition(new google.maps.LatLng(this.latitude, this.longitude));
|
||||
this.marker.setIcon(this.funcGetIcon());
|
||||
} else {
|
||||
this.marker = new google.maps.Marker({
|
||||
position: new google.maps.LatLng(this.latitude, this.longitude),
|
||||
map: GoogleMap,
|
||||
icon: this.funcGetIcon(),
|
||||
visable: true
|
||||
});
|
||||
|
||||
// This is so we can match icao address
|
||||
this.marker.icao = this.icao;
|
||||
|
||||
// Trap clicks for this marker.
|
||||
google.maps.event.addListener(this.marker, 'click', this.funcSelectPlane);
|
||||
}
|
||||
|
||||
// Setting the marker title
|
||||
if (this.flight.length == 0) {
|
||||
this.marker.setTitle(this.hex);
|
||||
} else {
|
||||
this.marker.setTitle(this.flight+' ('+this.icao+')');
|
||||
}
|
||||
return this.marker;
|
||||
},
|
||||
|
||||
// Update our planes tail line,
|
||||
// TODO: Make this multi colored based on options
|
||||
// altitude (default) or speed
|
||||
funcUpdateLines: function() {
|
||||
if (this.line) {
|
||||
var path = this.line.getPath();
|
||||
path.push(new google.maps.LatLng(this.latitude, this.longitude));
|
||||
} else {
|
||||
this.line = new google.maps.Polyline({
|
||||
strokeColor: '#000000',
|
||||
strokeOpacity: 1.0,
|
||||
strokeWeight: 3,
|
||||
map: GoogleMap,
|
||||
path: this.trackline
|
||||
});
|
||||
}
|
||||
return this.line;
|
||||
}
|
||||
};
|
|
@ -1,658 +0,0 @@
|
|||
// Define our global variables
|
||||
var GoogleMap = null;
|
||||
var Planes = {};
|
||||
var PlanesOnMap = 0;
|
||||
var PlanesOnTable = 0;
|
||||
var PlanesToReap = 0;
|
||||
var SelectedPlane = null;
|
||||
var SpecialSquawk = false;
|
||||
|
||||
var iSortCol=-1;
|
||||
var bSortASC=true;
|
||||
var bDefaultSortASC=true;
|
||||
var iDefaultSortCol=3;
|
||||
|
||||
// Get current map settings
|
||||
CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT;
|
||||
CenterLon = Number(localStorage['CenterLon']) || CONST_CENTERLON;
|
||||
ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL;
|
||||
|
||||
function fetchData() {
|
||||
$.getJSON('/dump1090/data.json', function(data) {
|
||||
PlanesOnMap = 0
|
||||
SpecialSquawk = false;
|
||||
|
||||
// Loop through all the planes in the data packet
|
||||
for (var j=0; j < data.length; j++) {
|
||||
// Do we already have this plane object in Planes?
|
||||
// If not make it.
|
||||
if (Planes[data[j].hex]) {
|
||||
var plane = Planes[data[j].hex];
|
||||
} else {
|
||||
var plane = jQuery.extend(true, {}, planeObject);
|
||||
}
|
||||
|
||||
/* For special squawk tests
|
||||
if (data[j].hex == '48413x') {
|
||||
data[j].squawk = '7700';
|
||||
} //*/
|
||||
|
||||
// Set SpecialSquawk-value
|
||||
if (data[j].squawk == '7500' || data[j].squawk == '7600' || data[j].squawk == '7700') {
|
||||
SpecialSquawk = true;
|
||||
}
|
||||
|
||||
// Call the function update
|
||||
plane.funcUpdateData(data[j]);
|
||||
|
||||
// Copy the plane into Planes
|
||||
Planes[plane.icao] = plane;
|
||||
}
|
||||
|
||||
PlanesOnTable = data.length;
|
||||
});
|
||||
}
|
||||
|
||||
// Initalizes the map and starts up our timers to call various functions
|
||||
function initialize() {
|
||||
// Make a list of all the available map IDs
|
||||
var mapTypeIds = [];
|
||||
for(var type in google.maps.MapTypeId) {
|
||||
mapTypeIds.push(google.maps.MapTypeId[type]);
|
||||
}
|
||||
// Push OSM on to the end
|
||||
mapTypeIds.push("OSM");
|
||||
mapTypeIds.push("dark_map");
|
||||
|
||||
// Styled Map to outline airports and highways
|
||||
var styles = [
|
||||
{
|
||||
"featureType": "administrative",
|
||||
"stylers": [
|
||||
{ "visibility": "off" }
|
||||
]
|
||||
},{
|
||||
"featureType": "landscape",
|
||||
"stylers": [
|
||||
{ "visibility": "off" }
|
||||
]
|
||||
},{
|
||||
"featureType": "poi",
|
||||
"stylers": [
|
||||
{ "visibility": "off" }
|
||||
]
|
||||
},{
|
||||
"featureType": "road",
|
||||
"stylers": [
|
||||
{ "visibility": "off" }
|
||||
]
|
||||
},{
|
||||
"featureType": "transit",
|
||||
"stylers": [
|
||||
{ "visibility": "off" }
|
||||
]
|
||||
},{
|
||||
"featureType": "landscape",
|
||||
"stylers": [
|
||||
{ "visibility": "on" },
|
||||
{ "weight": 8 },
|
||||
{ "color": "#000000" }
|
||||
]
|
||||
},{
|
||||
"featureType": "water",
|
||||
"stylers": [
|
||||
{ "lightness": -74 }
|
||||
]
|
||||
},{
|
||||
"featureType": "transit.station.airport",
|
||||
"stylers": [
|
||||
{ "visibility": "on" },
|
||||
{ "weight": 8 },
|
||||
{ "invert_lightness": true },
|
||||
{ "lightness": 27 }
|
||||
]
|
||||
},{
|
||||
"featureType": "road.highway",
|
||||
"stylers": [
|
||||
{ "visibility": "simplified" },
|
||||
{ "invert_lightness": true },
|
||||
{ "gamma": 0.3 }
|
||||
]
|
||||
},{
|
||||
"featureType": "road",
|
||||
"elementType": "labels",
|
||||
"stylers": [
|
||||
{ "visibility": "off" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
// Add our styled map
|
||||
var styledMap = new google.maps.StyledMapType(styles, {name: "Dark Map"});
|
||||
|
||||
// Define the Google Map
|
||||
var mapOptions = {
|
||||
center: new google.maps.LatLng(CenterLat, CenterLon),
|
||||
zoom: ZoomLvl,
|
||||
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
||||
mapTypeControl: true,
|
||||
streetViewControl: false,
|
||||
mapTypeControlOptions: {
|
||||
mapTypeIds: mapTypeIds,
|
||||
position: google.maps.ControlPosition.TOP_LEFT,
|
||||
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
|
||||
}
|
||||
};
|
||||
|
||||
GoogleMap = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
|
||||
|
||||
//Define OSM map type pointing at the OpenStreetMap tile server
|
||||
GoogleMap.mapTypes.set("OSM", new google.maps.ImageMapType({
|
||||
getTileUrl: function(coord, zoom) {
|
||||
return "http://tile.openstreetmap.org/" + zoom + "/" + coord.x + "/" + coord.y + ".png";
|
||||
},
|
||||
tileSize: new google.maps.Size(256, 256),
|
||||
name: "OpenStreetMap",
|
||||
maxZoom: 18
|
||||
}));
|
||||
|
||||
GoogleMap.mapTypes.set("dark_map", styledMap);
|
||||
|
||||
// Listeners for newly created Map
|
||||
google.maps.event.addListener(GoogleMap, 'center_changed', function() {
|
||||
localStorage['CenterLat'] = GoogleMap.getCenter().lat();
|
||||
localStorage['CenterLon'] = GoogleMap.getCenter().lng();
|
||||
});
|
||||
|
||||
google.maps.event.addListener(GoogleMap, 'zoom_changed', function() {
|
||||
localStorage['ZoomLvl'] = GoogleMap.getZoom();
|
||||
});
|
||||
|
||||
// Add home marker if requested
|
||||
if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) {
|
||||
var siteMarker = new google.maps.LatLng(SiteLat, SiteLon);
|
||||
var markerImage = new google.maps.MarkerImage(
|
||||
'http://maps.google.com/mapfiles/kml/pal4/icon57.png',
|
||||
new google.maps.Size(32, 32), // Image size
|
||||
new google.maps.Point(0, 0), // Origin point of image
|
||||
new google.maps.Point(16, 16)); // Position where marker should point
|
||||
var marker = new google.maps.Marker({
|
||||
position: siteMarker,
|
||||
map: GoogleMap,
|
||||
icon: markerImage,
|
||||
title: 'My Radar Site',
|
||||
zIndex: -99999
|
||||
});
|
||||
|
||||
if (SiteCircles) {
|
||||
for (var i=0;i<SiteCirclesDistances.length;i++) {
|
||||
drawCircle(marker, SiteCirclesDistances[i]); // in meters
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These will run after page is complitely loaded
|
||||
$(window).load(function() {
|
||||
$('#dialog-modal').css('display', 'inline'); // Show hidden settings-windows content
|
||||
});
|
||||
|
||||
// Load up our options page
|
||||
optionsInitalize();
|
||||
|
||||
// Did our crafty user need some setup?
|
||||
extendedInitalize();
|
||||
|
||||
// Setup our timer to poll from the server.
|
||||
window.setInterval(function() {
|
||||
fetchData();
|
||||
refreshTableInfo();
|
||||
refreshSelected();
|
||||
reaper();
|
||||
extendedPulse();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// This looks for planes to reap out of the master Planes variable
|
||||
function reaper() {
|
||||
PlanesToReap = 0;
|
||||
// When did the reaper start?
|
||||
reaptime = new Date().getTime();
|
||||
// Loop the planes
|
||||
for (var reap in Planes) {
|
||||
// Is this plane possibly reapable?
|
||||
if (Planes[reap].reapable == true) {
|
||||
// Has it not been seen for 5 minutes?
|
||||
// This way we still have it if it returns before then
|
||||
// Due to loss of signal or other reasons
|
||||
if ((reaptime - Planes[reap].updated) > 300000) {
|
||||
// Reap it.
|
||||
delete Planes[reap];
|
||||
}
|
||||
PlanesToReap++;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Refresh the detail window about the plane
|
||||
function refreshSelected() {
|
||||
var selected = false;
|
||||
if (typeof SelectedPlane !== 'undefined' && SelectedPlane != "ICAO" && SelectedPlane != null) {
|
||||
selected = Planes[SelectedPlane];
|
||||
}
|
||||
|
||||
var columns = 2;
|
||||
var html = '';
|
||||
|
||||
if (selected) {
|
||||
html += '<table id="selectedinfo" width="100%">';
|
||||
} else {
|
||||
html += '<table id="selectedinfo" class="dim" width="100%">';
|
||||
}
|
||||
|
||||
// Flight header line including squawk if needed
|
||||
if (selected && selected.flight == "") {
|
||||
html += '<tr><td colspan="' + columns + '" id="selectedinfotitle"><b>N/A (' +
|
||||
selected.icao + ')</b>';
|
||||
} else if (selected && selected.flight != "") {
|
||||
html += '<tr><td colspan="' + columns + '" id="selectedinfotitle"><b>' +
|
||||
selected.flight + '</b>';
|
||||
} else {
|
||||
html += '<tr><td colspan="' + columns + '" id="selectedinfotitle"><b>DUMP1090</b>';
|
||||
}
|
||||
|
||||
if (selected && selected.squawk == 7500) { // Lets hope we never see this... Aircraft Hijacking
|
||||
html += ' <span class="squawk7500"> Squawking: Aircraft Hijacking </span>';
|
||||
} else if (selected && selected.squawk == 7600) { // Radio Failure
|
||||
html += ' <span class="squawk7600"> Squawking: Radio Failure </span>';
|
||||
} else if (selected && selected.squawk == 7700) { // General Emergency
|
||||
html += ' <span class="squawk7700"> Squawking: General Emergency </span>';
|
||||
} else if (selected && selected.flight != '') {
|
||||
html += ' <a href="http://fr24.com/'+selected.flight+'" target="_blank">[FR24]</a>';
|
||||
html += ' <a href="http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?';
|
||||
html += 'flightNumber='+selected.flight+'" target="_blank">[FlightStats]</a>';
|
||||
html += ' <a href="http://flightaware.com/live/flight/'+selected.flight+'" target="_blank">[FlightAware]</a>';
|
||||
}
|
||||
html += '<td></tr>';
|
||||
|
||||
if (selected) {
|
||||
if (Metric) {
|
||||
html += '<tr><td>Altitude: ' + Math.round(selected.altitude / 3.2828) + ' m</td>';
|
||||
} else {
|
||||
html += '<tr><td>Altitude: ' + selected.altitude + ' ft</td>';
|
||||
}
|
||||
} else {
|
||||
html += '<tr><td>Altitude: n/a</td>';
|
||||
}
|
||||
|
||||
if (selected && selected.squawk != '0000') {
|
||||
html += '<td>Squawk: ' + selected.squawk + '</td></tr>';
|
||||
} else {
|
||||
html += '<td>Squawk: n/a</td></tr>';
|
||||
}
|
||||
|
||||
html += '<tr><td>Speed: '
|
||||
if (selected) {
|
||||
if (Metric) {
|
||||
html += Math.round(selected.speed * 1.852) + ' km/h';
|
||||
} else {
|
||||
html += selected.speed + ' kt';
|
||||
}
|
||||
} else {
|
||||
html += 'n/a';
|
||||
}
|
||||
html += '</td>';
|
||||
|
||||
if (selected) {
|
||||
html += '<td>ICAO (hex): ' + selected.icao + '</td></tr>';
|
||||
} else {
|
||||
html += '<td>ICAO (hex): n/a</td></tr>'; // Something is wrong if we are here
|
||||
}
|
||||
|
||||
html += '<tr><td>Track: '
|
||||
if (selected && selected.vTrack) {
|
||||
html += selected.track + '°' + ' (' + normalizeTrack(selected.track, selected.vTrack)[1] +')';
|
||||
} else {
|
||||
html += 'n/a';
|
||||
}
|
||||
html += '</td><td> </td></tr>';
|
||||
|
||||
html += '<tr><td colspan="' + columns + '" align="center">Lat/Long: ';
|
||||
if (selected && selected.vPosition) {
|
||||
html += selected.latitude + ', ' + selected.longitude + '</td></tr>';
|
||||
|
||||
// Let's show some extra data if we have site coordinates
|
||||
if (SiteShow) {
|
||||
var siteLatLon = new google.maps.LatLng(SiteLat, SiteLon);
|
||||
var planeLatLon = new google.maps.LatLng(selected.latitude, selected.longitude);
|
||||
var dist = google.maps.geometry.spherical.computeDistanceBetween (siteLatLon, planeLatLon);
|
||||
|
||||
if (Metric) {
|
||||
dist /= 1000;
|
||||
} else {
|
||||
dist /= 1852;
|
||||
}
|
||||
dist = (Math.round((dist)*10)/10).toFixed(1);
|
||||
html += '<tr><td colspan="' + columns + '" align="center">Distance from Site: ' + dist +
|
||||
(Metric ? ' km' : ' NM') + '</td></tr>';
|
||||
} // End of SiteShow
|
||||
} else {
|
||||
if (SiteShow) {
|
||||
html += '<tr><td colspan="' + columns + '" align="center">Distance from Site: n/a ' +
|
||||
(Metric ? ' km' : ' NM') + '</td></tr>';
|
||||
} else {
|
||||
html += 'n/a</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
html += '</table>';
|
||||
|
||||
document.getElementById('plane_detail').innerHTML = html;
|
||||
}
|
||||
|
||||
// Right now we have no means to validate the speed is good
|
||||
// Want to return (n/a) when we dont have it
|
||||
// TODO: Edit C code to add a valid speed flag
|
||||
// TODO: Edit js code to use said flag
|
||||
function normalizeSpeed(speed, valid) {
|
||||
return speed
|
||||
}
|
||||
|
||||
// Returns back a long string, short string, and the track if we have a vaild track path
|
||||
function normalizeTrack(track, valid){
|
||||
x = []
|
||||
if ((track > -1) && (track < 22.5)) {
|
||||
x = ["North", "N", track]
|
||||
}
|
||||
if ((track > 22.5) && (track < 67.5)) {
|
||||
x = ["North East", "NE", track]
|
||||
}
|
||||
if ((track > 67.5) && (track < 112.5)) {
|
||||
x = ["East", "E", track]
|
||||
}
|
||||
if ((track > 112.5) && (track < 157.5)) {
|
||||
x = ["South East", "SE", track]
|
||||
}
|
||||
if ((track > 157.5) && (track < 202.5)) {
|
||||
x = ["South", "S", track]
|
||||
}
|
||||
if ((track > 202.5) && (track < 247.5)) {
|
||||
x = ["South West", "SW", track]
|
||||
}
|
||||
if ((track > 247.5) && (track < 292.5)) {
|
||||
x = ["West", "W", track]
|
||||
}
|
||||
if ((track > 292.5) && (track < 337.5)) {
|
||||
x = ["North West", "NW", track]
|
||||
}
|
||||
if ((track > 337.5) && (track < 361)) {
|
||||
x = ["North", "N", track]
|
||||
}
|
||||
if (!valid) {
|
||||
x = [" ", "n/a", ""]
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Refeshes the larger table of all the planes
|
||||
function refreshTableInfo() {
|
||||
var html = '<table id="tableinfo" width="100%">';
|
||||
html += '<thead style="background-color: #BBBBBB; cursor: pointer;">';
|
||||
html += '<td onclick="setASC_DESC(\'0\');sortTable(\'tableinfo\',\'0\');">ICAO</td>';
|
||||
html += '<td onclick="setASC_DESC(\'1\');sortTable(\'tableinfo\',\'1\');">Flight</td>';
|
||||
html += '<td onclick="setASC_DESC(\'2\');sortTable(\'tableinfo\',\'2\');" ' +
|
||||
'align="right">Squawk</td>';
|
||||
html += '<td onclick="setASC_DESC(\'3\');sortTable(\'tableinfo\',\'3\');" ' +
|
||||
'align="right">Altitude</td>';
|
||||
html += '<td onclick="setASC_DESC(\'4\');sortTable(\'tableinfo\',\'4\');" ' +
|
||||
'align="right">Speed</td>';
|
||||
// Add distance column header to table if site coordinates are provided
|
||||
if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) {
|
||||
html += '<td onclick="setASC_DESC(\'5\');sortTable(\'tableinfo\',\'5\');" ' +
|
||||
'align="right">Distance</td>';
|
||||
}
|
||||
html += '<td onclick="setASC_DESC(\'5\');sortTable(\'tableinfo\',\'6\');" ' +
|
||||
'align="right">Track</td>';
|
||||
html += '<td onclick="setASC_DESC(\'6\');sortTable(\'tableinfo\',\'7\');" ' +
|
||||
'align="right">Msgs</td>';
|
||||
html += '<td onclick="setASC_DESC(\'7\');sortTable(\'tableinfo\',\'8\');" ' +
|
||||
'align="right">Seen</td></thead><tbody>';
|
||||
for (var tablep in Planes) {
|
||||
var tableplane = Planes[tablep]
|
||||
if (!tableplane.reapable) {
|
||||
var specialStyle = "";
|
||||
// Is this the plane we selected?
|
||||
if (tableplane.icao == SelectedPlane) {
|
||||
specialStyle += " selected";
|
||||
}
|
||||
// Lets hope we never see this... Aircraft Hijacking
|
||||
if (tableplane.squawk == 7500) {
|
||||
specialStyle += " squawk7500";
|
||||
}
|
||||
// Radio Failure
|
||||
if (tableplane.squawk == 7600) {
|
||||
specialStyle += " squawk7600";
|
||||
}
|
||||
// Emergancy
|
||||
if (tableplane.squawk == 7700) {
|
||||
specialStyle += " squawk7700";
|
||||
}
|
||||
|
||||
if (tableplane.vPosition == true) {
|
||||
html += '<tr class="plane_table_row vPosition' + specialStyle + '">';
|
||||
} else {
|
||||
html += '<tr class="plane_table_row ' + specialStyle + '">';
|
||||
}
|
||||
|
||||
html += '<td>' + tableplane.icao + '</td>';
|
||||
html += '<td>' + tableplane.flight + '</td>';
|
||||
if (tableplane.squawk != '0000' ) {
|
||||
html += '<td align="right">' + tableplane.squawk + '</td>';
|
||||
} else {
|
||||
html += '<td align="right"> </td>';
|
||||
}
|
||||
|
||||
if (Metric) {
|
||||
html += '<td align="right">' + Math.round(tableplane.altitude / 3.2828) + '</td>';
|
||||
html += '<td align="right">' + Math.round(tableplane.speed * 1.852) + '</td>';
|
||||
} else {
|
||||
html += '<td align="right">' + tableplane.altitude + '</td>';
|
||||
html += '<td align="right">' + tableplane.speed + '</td>';
|
||||
}
|
||||
// Add distance column to table if site coordinates are provided
|
||||
if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) {
|
||||
html += '<td align="right">';
|
||||
if (tableplane.vPosition) {
|
||||
var siteLatLon = new google.maps.LatLng(SiteLat, SiteLon);
|
||||
var planeLatLon = new google.maps.LatLng(tableplane.latitude, tableplane.longitude);
|
||||
var dist = google.maps.geometry.spherical.computeDistanceBetween (siteLatLon, planeLatLon);
|
||||
if (Metric) {
|
||||
dist /= 1000;
|
||||
} else {
|
||||
dist /= 1852;
|
||||
}
|
||||
dist = (Math.round((dist)*10)/10).toFixed(1);
|
||||
html += dist;
|
||||
} else {
|
||||
html += '0';
|
||||
}
|
||||
html += '</td>';
|
||||
}
|
||||
|
||||
html += '<td align="right">';
|
||||
if (tableplane.vTrack) {
|
||||
html += normalizeTrack(tableplane.track, tableplane.vTrack)[2];
|
||||
// html += ' (' + normalizeTrack(tableplane.track, tableplane.vTrack)[1] + ')';
|
||||
} else {
|
||||
html += ' ';
|
||||
}
|
||||
html += '</td>';
|
||||
html += '<td align="right">' + tableplane.messages + '</td>';
|
||||
html += '<td align="right">' + tableplane.seen + '</td>';
|
||||
html += '</tr>';
|
||||
}
|
||||
}
|
||||
html += '</tbody></table>';
|
||||
|
||||
document.getElementById('planes_table').innerHTML = html;
|
||||
|
||||
if (SpecialSquawk) {
|
||||
$('#SpecialSquawkWarning').css('display', 'inline');
|
||||
} else {
|
||||
$('#SpecialSquawkWarning').css('display', 'none');
|
||||
}
|
||||
|
||||
// Click event for table
|
||||
$('#planes_table').find('tr').click( function(){
|
||||
var hex = $(this).find('td:first').text();
|
||||
if (hex != "ICAO") {
|
||||
selectPlaneByHex(hex);
|
||||
refreshTableInfo();
|
||||
refreshSelected();
|
||||
}
|
||||
});
|
||||
|
||||
sortTable("tableinfo");
|
||||
}
|
||||
|
||||
// Credit goes to a co-worker that needed a similar functions for something else
|
||||
// we get a copy of it free ;)
|
||||
function setASC_DESC(iCol) {
|
||||
if(iSortCol==iCol) {
|
||||
bSortASC=!bSortASC;
|
||||
} else {
|
||||
bSortASC=bDefaultSortASC;
|
||||
}
|
||||
}
|
||||
|
||||
function sortTable(szTableID,iCol) {
|
||||
//if iCol was not provided, and iSortCol is not set, assign default value
|
||||
if (typeof iCol==='undefined'){
|
||||
if(iSortCol!=-1){
|
||||
var iCol=iSortCol;
|
||||
} else if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) {
|
||||
var iCol=5;
|
||||
} else {
|
||||
var iCol=iDefaultSortCol;
|
||||
}
|
||||
}
|
||||
|
||||
//retrieve passed table element
|
||||
var oTbl=document.getElementById(szTableID).tBodies[0];
|
||||
var aStore=[];
|
||||
|
||||
//If supplied col # is greater than the actual number of cols, set sel col = to last col
|
||||
if (typeof oTbl.rows[0] !== 'undefined' && oTbl.rows[0].cells.length <= iCol) {
|
||||
iCol=(oTbl.rows[0].cells.length-1);
|
||||
}
|
||||
|
||||
//store the col #
|
||||
iSortCol=iCol;
|
||||
|
||||
//determine if we are delaing with numerical, or alphanumeric content
|
||||
var bNumeric = false;
|
||||
if ((typeof oTbl.rows[0] !== 'undefined') &&
|
||||
(!isNaN(parseFloat(oTbl.rows[0].cells[iSortCol].textContent ||
|
||||
oTbl.rows[0].cells[iSortCol].innerText)))) {
|
||||
bNumeric = true;
|
||||
}
|
||||
|
||||
//loop through the rows, storing each one inro aStore
|
||||
for (var i=0,iLen=oTbl.rows.length;i<iLen;i++){
|
||||
var oRow=oTbl.rows[i];
|
||||
vColData=bNumeric?parseFloat(oRow.cells[iSortCol].textContent||oRow.cells[iSortCol].innerText):String(oRow.cells[iSortCol].textContent||oRow.cells[iSortCol].innerText);
|
||||
aStore.push([vColData,oRow]);
|
||||
}
|
||||
|
||||
//sort aStore ASC/DESC based on value of bSortASC
|
||||
if (bNumeric) { //numerical sort
|
||||
aStore.sort(function(x,y){return bSortASC?x[0]-y[0]:y[0]-x[0];});
|
||||
} else { //alpha sort
|
||||
aStore.sort();
|
||||
if(!bSortASC) {
|
||||
aStore.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
//rewrite the table rows to the passed table element
|
||||
for(var i=0,iLen=aStore.length;i<iLen;i++){
|
||||
oTbl.appendChild(aStore[i][1]);
|
||||
}
|
||||
aStore=null;
|
||||
}
|
||||
|
||||
function selectPlaneByHex(hex) {
|
||||
// If SelectedPlane has something in it, clear out the selected
|
||||
if (SelectedPlane != null) {
|
||||
Planes[SelectedPlane].is_selected = false;
|
||||
Planes[SelectedPlane].funcClearLine();
|
||||
Planes[SelectedPlane].markerColor = MarkerColor;
|
||||
// If the selected has a marker, make it not stand out
|
||||
if (Planes[SelectedPlane].marker) {
|
||||
Planes[SelectedPlane].marker.setIcon(Planes[SelectedPlane].funcGetIcon());
|
||||
}
|
||||
}
|
||||
|
||||
// If we are clicking the same plane, we are deselected it.
|
||||
if (String(SelectedPlane) != String(hex)) {
|
||||
// Assign the new selected
|
||||
SelectedPlane = hex;
|
||||
Planes[SelectedPlane].is_selected = true;
|
||||
// If the selected has a marker, make it stand out
|
||||
if (Planes[SelectedPlane].marker) {
|
||||
Planes[SelectedPlane].funcUpdateLines();
|
||||
Planes[SelectedPlane].marker.setIcon(Planes[SelectedPlane].funcGetIcon());
|
||||
}
|
||||
} else {
|
||||
SelectedPlane = null;
|
||||
}
|
||||
refreshSelected();
|
||||
refreshTableInfo();
|
||||
}
|
||||
|
||||
function resetMap() {
|
||||
// Reset localStorage values
|
||||
localStorage['CenterLat'] = CONST_CENTERLAT;
|
||||
localStorage['CenterLon'] = CONST_CENTERLON;
|
||||
localStorage['ZoomLvl'] = CONST_ZOOMLVL;
|
||||
|
||||
// Try to read values from localStorage else use CONST_s
|
||||
CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT;
|
||||
CenterLon = Number(localStorage['CenterLon']) || CONST_CENTERLON;
|
||||
ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL;
|
||||
|
||||
// Set and refresh
|
||||
GoogleMap.setZoom(parseInt(ZoomLvl));
|
||||
GoogleMap.setCenter(new google.maps.LatLng(parseFloat(CenterLat), parseFloat(CenterLon)));
|
||||
|
||||
if (SelectedPlane) {
|
||||
selectPlaneByHex(SelectedPlane);
|
||||
}
|
||||
|
||||
refreshSelected();
|
||||
refreshTableInfo();
|
||||
}
|
||||
|
||||
function drawCircle(marker, distance) {
|
||||
if (typeof distance === 'undefined') {
|
||||
return false;
|
||||
|
||||
if (!(!isNaN(parseFloat(distance)) && isFinite(distance)) || distance < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
distance *= 1000.0;
|
||||
if (!Metric) {
|
||||
distance *= 1.852;
|
||||
}
|
||||
|
||||
// Add circle overlay and bind to marker
|
||||
var circle = new google.maps.Circle({
|
||||
map: GoogleMap,
|
||||
radius: distance, // In meters
|
||||
fillOpacity: 0.0,
|
||||
strokeWeight: 1,
|
||||
strokeOpacity: 0.3
|
||||
});
|
||||
circle.bindTo('center', marker, 'position');
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
html, body {
|
||||
margin: 0; padding: 0; background-color: #ffffff; font-family: Tahoma, Sans-Serif;
|
||||
font-size: 10pt; overflow: auto; height: 100%;
|
||||
}
|
||||
div#map_container { float: left; width: 100%; height: 100%; }
|
||||
div#map_canvas { height: 100%; margin-right: 420px; }
|
||||
|
||||
div#sidebar_container { float: left; width: 410px; margin-left: -410px; height: 100%; overflow: auto; }
|
||||
|
||||
div#SpecialSquawkWarning { position: absolute; bottom: 25px; right: 430px; border: 2px solid red;
|
||||
background-color: #FFFFA3; opacity: 0.75; filter:alpha(opacity=75); padding: 5px;
|
||||
display: none; text-align: center; }
|
||||
|
||||
table#optionsTabs { width: 100%; font-size: small; font-family: monospace; background-color: #ddd;
|
||||
border: 1px; border-color: #000000;}
|
||||
|
||||
#tableinfo, #sudo_buttons { font-size: x-small; font-family: monospace; }
|
||||
|
||||
.vPosition { font-weight: bold; background-color: #d5ffd5; }
|
||||
.squawk7500 { font-weight: bold; background-color: #ff5555; }
|
||||
.squawk7600 { font-weight: bold; background-color: #00ffff; }
|
||||
.squawk7700 { font-weight: bold; background-color: #ffff00; }
|
||||
.selected { background-color: #dddddd; }
|
||||
.plane_table_row { cursor: pointer; }
|
||||
|
||||
#selectedinfotitle { font-size: larger; }
|
||||
#selectedinfo { font-size: small; }
|
||||
#selectedinfo a { text-decoration: none; color: blue; font-size: x-small;}
|
||||
#selectedinfo.dim { opacity: 0.3; filter:alpha(opacity=30); /* For IE8 and earlier */ }
|
||||
|
||||
.pointer { cursor: pointer; }
|
||||
|
|
@ -1,366 +0,0 @@
|
|||
/*
|
||||
* rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
|
||||
* Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de>
|
||||
* Copyright (C) 2012 by Dimitri Stolnikov <horiz0n@gmx.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RTL_SDR_H
|
||||
#define __RTL_SDR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//#include <stdint.h>
|
||||
#include "rtl-sdr_export.h"
|
||||
|
||||
typedef struct rtlsdr_dev rtlsdr_dev_t;
|
||||
|
||||
RTLSDR_API uint32_t rtlsdr_get_device_count(void);
|
||||
|
||||
RTLSDR_API const char* rtlsdr_get_device_name(uint32_t index);
|
||||
|
||||
/*!
|
||||
* Get USB device strings.
|
||||
*
|
||||
* NOTE: The string arguments must provide space for up to 256 bytes.
|
||||
*
|
||||
* \param index the device index
|
||||
* \param manufact manufacturer name, may be NULL
|
||||
* \param product product name, may be NULL
|
||||
* \param serial serial number, may be NULL
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_get_device_usb_strings(uint32_t index,
|
||||
char *manufact,
|
||||
char *product,
|
||||
char *serial);
|
||||
|
||||
/*!
|
||||
* Get device index by USB serial string descriptor.
|
||||
*
|
||||
* \param serial serial string of the device
|
||||
* \return device index of first device where the name matched
|
||||
* \return -1 if name is NULL
|
||||
* \return -2 if no devices were found at all
|
||||
* \return -3 if devices were found, but none with matching name
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_get_index_by_serial(const char *serial);
|
||||
|
||||
RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index);
|
||||
|
||||
RTLSDR_API int rtlsdr_close(rtlsdr_dev_t *dev);
|
||||
|
||||
/* configuration functions */
|
||||
|
||||
/*!
|
||||
* Set crystal oscillator frequencies used for the RTL2832 and the tuner IC.
|
||||
*
|
||||
* Usually both ICs use the same clock. Changing the clock may make sense if
|
||||
* you are applying an external clock to the tuner or to compensate the
|
||||
* frequency (and samplerate) error caused by the original (cheap) crystal.
|
||||
*
|
||||
* NOTE: Call this function only if you fully understand the implications.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param rtl_freq frequency value used to clock the RTL2832 in Hz
|
||||
* \param tuner_freq frequency value used to clock the tuner IC in Hz
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq,
|
||||
uint32_t tuner_freq);
|
||||
|
||||
/*!
|
||||
* Get crystal oscillator frequencies used for the RTL2832 and the tuner IC.
|
||||
*
|
||||
* Usually both ICs use the same clock.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param rtl_freq frequency value used to clock the RTL2832 in Hz
|
||||
* \param tuner_freq frequency value used to clock the tuner IC in Hz
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq,
|
||||
uint32_t *tuner_freq);
|
||||
|
||||
/*!
|
||||
* Get USB device strings.
|
||||
*
|
||||
* NOTE: The string arguments must provide space for up to 256 bytes.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param manufact manufacturer name, may be NULL
|
||||
* \param product product name, may be NULL
|
||||
* \param serial serial number, may be NULL
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact,
|
||||
char *product, char *serial);
|
||||
|
||||
/*!
|
||||
* Write the device EEPROM
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param data buffer of data to be written
|
||||
* \param offset address where the data should be written
|
||||
* \param len length of the data
|
||||
* \return 0 on success
|
||||
* \return -1 if device handle is invalid
|
||||
* \return -2 if EEPROM size is exceeded
|
||||
* \return -3 if no EEPROM was found
|
||||
*/
|
||||
|
||||
RTLSDR_API int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data,
|
||||
uint8_t offset, uint16_t len);
|
||||
|
||||
/*!
|
||||
* Read the device EEPROM
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param data buffer where the data should be written
|
||||
* \param offset address where the data should be read from
|
||||
* \param len length of the data
|
||||
* \return 0 on success
|
||||
* \return -1 if device handle is invalid
|
||||
* \return -2 if EEPROM size is exceeded
|
||||
* \return -3 if no EEPROM was found
|
||||
*/
|
||||
|
||||
RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data,
|
||||
uint8_t offset, uint16_t len);
|
||||
|
||||
RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq);
|
||||
|
||||
/*!
|
||||
* Get actual frequency the device is tuned to.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \return 0 on error, frequency in Hz otherwise
|
||||
*/
|
||||
RTLSDR_API uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev);
|
||||
|
||||
/*!
|
||||
* Set the frequency correction value for the device.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param ppm correction value in parts per million (ppm)
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm);
|
||||
|
||||
/*!
|
||||
* Get actual frequency correction value of the device.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \return correction value in parts per million (ppm)
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev);
|
||||
|
||||
enum rtlsdr_tuner {
|
||||
RTLSDR_TUNER_UNKNOWN = 0,
|
||||
RTLSDR_TUNER_E4000,
|
||||
RTLSDR_TUNER_FC0012,
|
||||
RTLSDR_TUNER_FC0013,
|
||||
RTLSDR_TUNER_FC2580,
|
||||
RTLSDR_TUNER_R820T
|
||||
};
|
||||
|
||||
/*!
|
||||
* Get the tuner type.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \return RTLSDR_TUNER_UNKNOWN on error, tuner type otherwise
|
||||
*/
|
||||
RTLSDR_API enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev);
|
||||
|
||||
/*!
|
||||
* Get a list of gains supported by the tuner.
|
||||
*
|
||||
* NOTE: The gains argument must be preallocated by the caller. If NULL is
|
||||
* being given instead, the number of available gain values will be returned.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param gains array of gain values. In tenths of a dB, 115 means 11.5 dB.
|
||||
* \return <= 0 on error, number of available (returned) gain values otherwise
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains);
|
||||
|
||||
/*!
|
||||
* Set the gain for the device.
|
||||
* Manual gain mode must be enabled for this to work.
|
||||
*
|
||||
* Valid gain values (in tenths of a dB) for the E4000 tuner:
|
||||
* -10, 15, 40, 65, 90, 115, 140, 165, 190,
|
||||
* 215, 240, 290, 340, 420, 430, 450, 470, 490
|
||||
*
|
||||
* Valid gain values may be queried with \ref rtlsdr_get_tuner_gains function.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param gain in tenths of a dB, 115 means 11.5 dB.
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain);
|
||||
|
||||
/*!
|
||||
* Get actual gain the device is configured to.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \return 0 on error, gain in tenths of a dB, 115 means 11.5 dB.
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev);
|
||||
|
||||
/*!
|
||||
* Set the intermediate frequency gain for the device.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param stage intermediate frequency gain stage number (1 to 6 for E4000)
|
||||
* \param gain in tenths of a dB, -30 means -3.0 dB.
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain);
|
||||
|
||||
/*!
|
||||
* Set the gain mode (automatic/manual) for the device.
|
||||
* Manual gain mode must be enabled for the gain setter function to work.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param manual gain mode, 1 means manual gain mode shall be enabled.
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual);
|
||||
|
||||
/* this will select the baseband filters according to the requested sample rate */
|
||||
RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate);
|
||||
|
||||
/*!
|
||||
* Get actual sample rate the device is configured to.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \return 0 on error, sample rate in Hz otherwise
|
||||
*/
|
||||
RTLSDR_API uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev);
|
||||
|
||||
/*!
|
||||
* Enable test mode that returns an 8 bit counter instead of the samples.
|
||||
* The counter is generated inside the RTL2832.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param test mode, 1 means enabled, 0 disabled
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on);
|
||||
|
||||
/*!
|
||||
* Enable or disable the internal digital AGC of the RTL2832.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param digital AGC mode, 1 means enabled, 0 disabled
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on);
|
||||
|
||||
/*!
|
||||
* Enable or disable the direct sampling mode. When enabled, the IF mode
|
||||
* of the RTL2832 is activated, and rtlsdr_set_center_freq() will control
|
||||
* the IF-frequency of the DDC, which can be used to tune from 0 to 28.8 MHz
|
||||
* (xtal frequency of the RTL2832).
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on);
|
||||
|
||||
/*!
|
||||
* Get state of the direct sampling mode
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \return -1 on error, 0 means disabled, 1 I-ADC input enabled
|
||||
* 2 Q-ADC input enabled
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev);
|
||||
|
||||
/*!
|
||||
* Enable or disable offset tuning for zero-IF tuners, which allows to avoid
|
||||
* problems caused by the DC offset of the ADCs and 1/f noise.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param on 0 means disabled, 1 enabled
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on);
|
||||
|
||||
/*!
|
||||
* Get state of the offset tuning mode
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \return -1 on error, 0 means disabled, 1 enabled
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev);
|
||||
|
||||
/* streaming functions */
|
||||
|
||||
RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev);
|
||||
|
||||
RTLSDR_API int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read);
|
||||
|
||||
typedef void(*rtlsdr_read_async_cb_t)(unsigned char *buf, uint32_t len, void *ctx);
|
||||
|
||||
/*!
|
||||
* Read samples from the device asynchronously. This function will block until
|
||||
* it is being canceled using rtlsdr_cancel_async()
|
||||
*
|
||||
* NOTE: This function is deprecated and is subject for removal.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param cb callback function to return received samples
|
||||
* \param ctx user specific context to pass via the callback function
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx);
|
||||
|
||||
/*!
|
||||
* Read samples from the device asynchronously. This function will block until
|
||||
* it is being canceled using rtlsdr_cancel_async()
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \param cb callback function to return received samples
|
||||
* \param ctx user specific context to pass via the callback function
|
||||
* \param buf_num optional buffer count, buf_num * buf_len = overall buffer size
|
||||
* set to 0 for default buffer count (32)
|
||||
* \param buf_len optional buffer length, must be multiple of 512,
|
||||
* set to 0 for default buffer length (16 * 32 * 512)
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev,
|
||||
rtlsdr_read_async_cb_t cb,
|
||||
void *ctx,
|
||||
uint32_t buf_num,
|
||||
uint32_t buf_len);
|
||||
|
||||
/*!
|
||||
* Cancel all pending asynchronous operations on the device.
|
||||
*
|
||||
* \param dev the device handle given by rtlsdr_open()
|
||||
* \return 0 on success
|
||||
*/
|
||||
RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RTL_SDR_H */
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
|
||||
* Copyright (C) 2012 by Hoernchen <la@tfc-server.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RTLSDR_EXPORT_H
|
||||
#define RTLSDR_EXPORT_H
|
||||
|
||||
#if defined __GNUC__
|
||||
# if __GNUC__ >= 4
|
||||
# define __SDR_EXPORT __attribute__((visibility("default")))
|
||||
# define __SDR_IMPORT __attribute__((visibility("default")))
|
||||
# else
|
||||
# define __SDR_EXPORT
|
||||
# define __SDR_IMPORT
|
||||
# endif
|
||||
#elif _MSC_VER
|
||||
# define __SDR_EXPORT __declspec(dllexport)
|
||||
# define __SDR_IMPORT __declspec(dllimport)
|
||||
#else
|
||||
# define __SDR_EXPORT
|
||||
# define __SDR_IMPORT
|
||||
#endif
|
||||
|
||||
#ifndef rtlsdr_STATIC
|
||||
# ifdef rtlsdr_EXPORTS
|
||||
# define RTLSDR_API __SDR_EXPORT
|
||||
# else
|
||||
# define RTLSDR_API __SDR_IMPORT
|
||||
# endif
|
||||
#else
|
||||
#define RTLSDR_API
|
||||
#endif
|
||||
#endif /* RTLSDR_EXPORT_H */
|
|
@ -1 +0,0 @@
|
|||
62f7f97e41c0ad19d03f7bfd16da920c9b6b096b
|
|
@ -1,193 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<head>
|
||||
<script>
|
||||
var frames = [];
|
||||
var currentFrame = 0;
|
||||
|
||||
var modes_checksum_table = [
|
||||
0x3935ea, 0x1c9af5, 0xf1b77e, 0x78dbbf, 0xc397db, 0x9e31e9, 0xb0e2f0, 0x587178,
|
||||
0x2c38bc, 0x161c5e, 0x0b0e2f, 0xfa7d13, 0x82c48d, 0xbe9842, 0x5f4c21, 0xd05c14,
|
||||
0x682e0a, 0x341705, 0xe5f186, 0x72f8c3, 0xc68665, 0x9cb936, 0x4e5c9b, 0xd8d449,
|
||||
0x939020, 0x49c810, 0x24e408, 0x127204, 0x093902, 0x049c81, 0xfdb444, 0x7eda22,
|
||||
0x3f6d11, 0xe04c8c, 0x702646, 0x381323, 0xe3f395, 0x8e03ce, 0x4701e7, 0xdc7af7,
|
||||
0x91c77f, 0xb719bb, 0xa476d9, 0xadc168, 0x56e0b4, 0x2b705a, 0x15b82d, 0xf52612,
|
||||
0x7a9309, 0xc2b380, 0x6159c0, 0x30ace0, 0x185670, 0x0c2b38, 0x06159c, 0x030ace,
|
||||
0x018567, 0xff38b7, 0x80665f, 0xbfc92b, 0xa01e91, 0xaff54c, 0x57faa6, 0x2bfd53,
|
||||
0xea04ad, 0x8af852, 0x457c29, 0xdd4410, 0x6ea208, 0x375104, 0x1ba882, 0x0dd441,
|
||||
0xf91024, 0x7c8812, 0x3e4409, 0xe0d800, 0x706c00, 0x383600, 0x1c1b00, 0x0e0d80,
|
||||
0x0706c0, 0x038360, 0x01c1b0, 0x00e0d8, 0x00706c, 0x003836, 0x001c1b, 0xfff409,
|
||||
0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
|
||||
0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
|
||||
0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000
|
||||
];
|
||||
|
||||
function modesChecksum(frame) {
|
||||
var crc = 0;
|
||||
var bits = frame.bits;
|
||||
var offset = (bits == 112) ? 0 : (112-56);
|
||||
|
||||
for(var j = 0; j < bits; j++) {
|
||||
var byte = j/8;
|
||||
var bit = j%8;
|
||||
var bitmask = 1 << (7-bit);
|
||||
|
||||
/* If bit is set, xor with corresponding table entry. */
|
||||
if (frame.hex.charCodeAt(byte) & bitmask)
|
||||
crc ^= modes_checksum_table[j+offset];
|
||||
}
|
||||
return crc; /* 24 bit checksum. */
|
||||
}
|
||||
|
||||
function getFrameChecksum(frame) {
|
||||
var res = "";
|
||||
for (j = 0; j < frame.hex.length; j++) {
|
||||
var val = frame.hex.charCodeAt(j);
|
||||
var h = val.toString(16);
|
||||
if (h.length == 1) h = "0"+h;
|
||||
res += h;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function displayFrame(i) {
|
||||
var div = document.getElementById("frame");
|
||||
var msgbits = 8+112;
|
||||
var frame = frames[i];
|
||||
var padding = frame.mag.length - msgbits*2;
|
||||
|
||||
/* Remove the old representation. */
|
||||
var nodes = div.childNodes.length;
|
||||
for(var j = 0; j < nodes; j++) {
|
||||
div.removeChild(div.firstChild);
|
||||
}
|
||||
|
||||
/* Display the new one. */
|
||||
for (var j = -padding; j < msgbits*2+padding; j++) {
|
||||
var m = frame.mag[j+padding];
|
||||
var type;
|
||||
|
||||
if (j < 0) type = "noise";
|
||||
if (j >= 0 && j < 16) type = "pre";
|
||||
if (j >= 16) {
|
||||
if (!(j % 2)) {
|
||||
var next = frame.mag[j+padding+1];
|
||||
if (m > next)
|
||||
type = "one";
|
||||
else
|
||||
type = "zero";
|
||||
}
|
||||
var bit = (j-16)/2;
|
||||
if (bit == frame.fix1 ||
|
||||
bit == frame.fix2)
|
||||
type = "err";
|
||||
}
|
||||
var sample = document.createElement("div");
|
||||
sample.setAttribute("class","sample "+type);
|
||||
sample.setAttribute("title","sample "+j+" ("+m+")");
|
||||
sample.style.left = ""+((j+padding)*4)+"px";
|
||||
sample.style.height = ""+(m/256)+"px";
|
||||
div.appendChild(sample);
|
||||
}
|
||||
document.getElementById("info").innerHTML =
|
||||
"#"+currentFrame+" "+frame.descr+"<br>"+
|
||||
"Bits:"+frame.bits+"<br>"+
|
||||
"DF : "+(frame.hex.charCodeAt(0) >> 3)+"<br>"+
|
||||
"fix1: "+frame.fix1+"<br>"+
|
||||
"fix2: "+frame.fix2+"<br>"+
|
||||
"hex : "+getFrameChecksum(frame)+"<br>"+
|
||||
"crc (computed): "+modesChecksum(frame).toString(16)+"<br>";
|
||||
}
|
||||
|
||||
function recomputeHex(frame) {
|
||||
var padding = frame.mag.length - (112+8)*2;
|
||||
var b = [];
|
||||
var hex = "";
|
||||
|
||||
/* Get bits */
|
||||
for (var j = 0; j < frame.bits*2; j += 2) {
|
||||
var bit;
|
||||
var l = frame.mag[padding+j+16];
|
||||
var r = frame.mag[padding+j+1+16];
|
||||
if (l > r)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
b.push(bit);
|
||||
}
|
||||
/* Pack into bytes */
|
||||
for (j = 0; j < frame.bits; j+= 8) {
|
||||
hex += String.fromCharCode(
|
||||
b[j]<<7 |
|
||||
b[j+1]<<6 |
|
||||
b[j+2]<<5 |
|
||||
b[j+3]<<4 |
|
||||
b[j+4]<<3 |
|
||||
b[j+5]<<2 |
|
||||
b[j+6]<<1 |
|
||||
b[j+7]);
|
||||
}
|
||||
frame.hex = hex;
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
document.getElementById("next").onclick = function() {
|
||||
if (currentFrame != frames.length-1) currentFrame++;
|
||||
displayFrame(currentFrame);
|
||||
}
|
||||
document.getElementById("prev").onclick = function() {
|
||||
if (currentFrame != 0) currentFrame--;
|
||||
displayFrame(currentFrame);
|
||||
}
|
||||
document.getElementById("re").onclick = function() {
|
||||
recomputeHex(frames[currentFrame]);
|
||||
displayFrame(currentFrame);
|
||||
}
|
||||
displayFrame(currentFrame);
|
||||
}
|
||||
</script>
|
||||
<script src="frames.js"></script>
|
||||
<style>
|
||||
#frame {
|
||||
width: 1024px;
|
||||
height: 255px;
|
||||
border: 1px #aaa solid;
|
||||
position: relative;
|
||||
}
|
||||
.sample {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
}
|
||||
.pre {
|
||||
width:4px;
|
||||
background-color: orange;
|
||||
}
|
||||
.one {
|
||||
width:4px;
|
||||
background-color: #0000cc;
|
||||
}
|
||||
.zero {
|
||||
width:4px;
|
||||
background-color: #aaaaaa;
|
||||
}
|
||||
.err {
|
||||
width:4px;
|
||||
background-color: #cc6666;
|
||||
}
|
||||
.noise {
|
||||
width:2px;
|
||||
background-color: #ffffff;
|
||||
border: 1px #aaa dotted;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<div id="frame">
|
||||
</div>
|
||||
<pre id="info">
|
||||
</pre>
|
||||
<input type="button" id="prev" value="Prev frame">
|
||||
<input type="button" id="next" value="Next frame">
|
||||
<input type="button" id="re" value="Recompute Hex">
|
||||
</body>
|
||||
</html>
|
|
@ -1 +0,0 @@
|
|||
3574a87a5df1ebab0124dc0e276a82636506f036
|
|
@ -1,321 +0,0 @@
|
|||
// view1090, a Mode S messages viewer for dump1090 devices.
|
||||
//
|
||||
// Copyright (C) 2014 by Malcolm Robb <Support@ATTAvionics.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#include "coaa.h"
|
||||
#include "view1090.h"
|
||||
//
|
||||
// ============================= Utility functions ==========================
|
||||
//
|
||||
void sigintHandler(int dummy) {
|
||||
NOTUSED(dummy);
|
||||
signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety
|
||||
Modes.exit = 1; // Signal to threads that we are done
|
||||
}
|
||||
//
|
||||
// =============================== Terminal handling ========================
|
||||
//
|
||||
#ifndef _WIN32
|
||||
// Get the number of rows after the terminal changes size.
|
||||
int getTermRows() {
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
return (w.ws_row);
|
||||
}
|
||||
|
||||
// Handle resizing terminal
|
||||
void sigWinchCallback() {
|
||||
signal(SIGWINCH, SIG_IGN);
|
||||
Modes.interactive_rows = getTermRows();
|
||||
interactiveShowData();
|
||||
signal(SIGWINCH, sigWinchCallback);
|
||||
}
|
||||
#else
|
||||
int getTermRows() { return MODES_INTERACTIVE_ROWS;}
|
||||
#endif
|
||||
//
|
||||
// =============================== Initialization ===========================
|
||||
//
|
||||
void view1090InitConfig(void) {
|
||||
// Default everything to zero/NULL
|
||||
memset(&Modes, 0, sizeof(Modes));
|
||||
memset(&View1090, 0, sizeof(View1090));
|
||||
|
||||
// Now initialise things that should not be 0/NULL to their defaults
|
||||
Modes.check_crc = 1;
|
||||
strcpy(View1090.net_input_beast_ipaddr,VIEW1090_NET_OUTPUT_IP_ADDRESS);
|
||||
Modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT;
|
||||
Modes.interactive_rows = getTermRows();
|
||||
Modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL;
|
||||
Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL;
|
||||
Modes.fUserLat = MODES_USER_LATITUDE_DFLT;
|
||||
Modes.fUserLon = MODES_USER_LONGITUDE_DFLT;
|
||||
|
||||
Modes.interactive = 1;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
void view1090Init(void) {
|
||||
|
||||
pthread_mutex_init(&Modes.pDF_mutex,NULL);
|
||||
pthread_mutex_init(&Modes.data_mutex,NULL);
|
||||
pthread_cond_init(&Modes.data_cond,NULL);
|
||||
|
||||
#ifdef _WIN32
|
||||
if ( (!Modes.wsaData.wVersion)
|
||||
&& (!Modes.wsaData.wHighVersion) ) {
|
||||
// Try to start the windows socket support
|
||||
if (WSAStartup(MAKEWORD(2,1),&Modes.wsaData) != 0)
|
||||
{
|
||||
fprintf(stderr, "WSAStartup returned Error\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Allocate the various buffers used by Modes
|
||||
if ( NULL == (Modes.icao_cache = (uint32_t *) malloc(sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2)))
|
||||
{
|
||||
fprintf(stderr, "Out of memory allocating data buffer.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Clear the buffers that have just been allocated, just in-case
|
||||
memset(Modes.icao_cache, 0, sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2);
|
||||
|
||||
// Validate the users Lat/Lon home location inputs
|
||||
if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90
|
||||
|| (Modes.fUserLat < -90.0) // and
|
||||
|| (Modes.fUserLon > 360.0) // Longitude must be -180 to +360
|
||||
|| (Modes.fUserLon < -180.0) ) {
|
||||
Modes.fUserLat = Modes.fUserLon = 0.0;
|
||||
} else if (Modes.fUserLon > 180.0) { // If Longitude is +180 to +360, make it -180 to 0
|
||||
Modes.fUserLon -= 360.0;
|
||||
}
|
||||
// If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the
|
||||
// Atlantic ocean off the west coast of Africa. This is unlikely to be correct.
|
||||
// Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian
|
||||
// is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both.
|
||||
// Testing the flag at runtime will be much quicker than ((fLon != 0.0) || (fLat != 0.0))
|
||||
Modes.bUserFlags &= ~MODES_USER_LATLON_VALID;
|
||||
if ((Modes.fUserLat != 0.0) || (Modes.fUserLon != 0.0)) {
|
||||
Modes.bUserFlags |= MODES_USER_LATLON_VALID;
|
||||
}
|
||||
|
||||
// Prepare error correction tables
|
||||
modesInitErrorInfo();
|
||||
}
|
||||
|
||||
// Set up data connection
|
||||
int setupConnection(struct client *c) {
|
||||
int fd;
|
||||
|
||||
// Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here.
|
||||
if ((fd = anetTcpConnect(Modes.aneterr, View1090.net_input_beast_ipaddr, Modes.net_input_beast_port)) != ANET_ERR) {
|
||||
anetNonBlock(Modes.aneterr, fd);
|
||||
//
|
||||
// Setup a service callback client structure for a beast binary input (from dump1090)
|
||||
// This is a bit dodgy under Windows. The fd parameter is a handle to the internet
|
||||
// socket on which we are receiving data. Under Linux, these seem to start at 0 and
|
||||
// count upwards. However, Windows uses "HANDLES" and these don't nececeriy start at 0.
|
||||
// dump1090 limits fd to values less than 1024, and then uses the fd parameter to
|
||||
// index into an array of clients. This is ok-ish if handles are allocated up from 0.
|
||||
// However, there is no gaurantee that Windows will behave like this, and if Windows
|
||||
// allocates a handle greater than 1024, then dump1090 won't like it. On my test machine,
|
||||
// the first Windows handle is usually in the 0x54 (84 decimal) region.
|
||||
|
||||
c->next = NULL;
|
||||
c->buflen = 0;
|
||||
c->fd =
|
||||
c->service =
|
||||
Modes.bis = fd;
|
||||
Modes.clients = c;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
//
|
||||
// ================================ Main ====================================
|
||||
//
|
||||
void showHelp(void) {
|
||||
printf(
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"| view1090 dump1090 Viewer Ver : "MODES_DUMP1090_VERSION " |\n"
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"--interactive Interactive mode refreshing data on screen\n"
|
||||
"--interactive-rows <num> Max number of rows in interactive mode (default: 15)\n"
|
||||
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60)\n"
|
||||
"--interactive-rtl1090 Display flight table in RTL1090 format\n"
|
||||
"--modeac Enable decoding of SSR modes 3/A & 3/C\n"
|
||||
"--net-bo-ipaddr <IPv4> TCP Beast output listen IPv4 (default: 127.0.0.1)\n"
|
||||
"--net-bo-port <port> TCP Beast output listen port (default: 30005)\n"
|
||||
"--lat <latitude> Reference/receiver latitide for surface posn (opt)\n"
|
||||
"--lon <longitude> Reference/receiver longitude for surface posn (opt)\n"
|
||||
"--no-crc-check Disable messages with broken CRC (discouraged)\n"
|
||||
"--no-fix Disable single-bits error correction using CRC\n"
|
||||
"--fix Enable single-bits error correction using CRC\n"
|
||||
"--aggressive More CPU for more messages (two bits fixes, ...)\n"
|
||||
"--metric Use metric units (meters, km/h, ...)\n"
|
||||
"--help Show this help\n"
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void showCopyright(void) {
|
||||
uint64_t llTime = time(NULL) + 1;
|
||||
|
||||
printf(
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"| view1090 ModeS Viewer Ver : " MODES_DUMP1090_VERSION " |\n"
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"\n"
|
||||
" Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>\n"
|
||||
" Copyright (C) 2014 by Malcolm Robb <support@attavionics.com>\n"
|
||||
"\n"
|
||||
" All rights reserved.\n"
|
||||
"\n"
|
||||
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
|
||||
" ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
|
||||
" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
|
||||
" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
|
||||
" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
|
||||
" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
|
||||
" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
|
||||
" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
|
||||
" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
|
||||
" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
|
||||
" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
"\n"
|
||||
" For further details refer to <https://github.com/MalcolmRobb/dump1090>\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
// delay for 1 second to give the user a chance to read the copyright
|
||||
while (llTime >= time(NULL)) {}
|
||||
}
|
||||
#endif
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
int main(int argc, char **argv) {
|
||||
int j, fd;
|
||||
struct client *c;
|
||||
char pk_buf[8];
|
||||
|
||||
// Set sane defaults
|
||||
|
||||
view1090InitConfig();
|
||||
signal(SIGINT, sigintHandler); // Define Ctrl/C handler (exit program)
|
||||
|
||||
// Parse the command line options
|
||||
for (j = 1; j < argc; j++) {
|
||||
int more = ((j + 1) < argc); // There are more arguments
|
||||
|
||||
if (!strcmp(argv[j],"--net-bo-port") && more) {
|
||||
Modes.net_input_beast_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-bo-ipaddr") && more) {
|
||||
strcpy(View1090.net_input_beast_ipaddr, argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--modeac")) {
|
||||
Modes.mode_ac = 1;
|
||||
} else if (!strcmp(argv[j],"--interactive-rows") && more) {
|
||||
Modes.interactive_rows = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--interactive")) {
|
||||
Modes.interactive = 1;
|
||||
} else if (!strcmp(argv[j],"--interactive-ttl") && more) {
|
||||
Modes.interactive_display_ttl = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--interactive-rtl1090")) {
|
||||
Modes.interactive = 1;
|
||||
Modes.interactive_rtl1090 = 1;
|
||||
} else if (!strcmp(argv[j],"--lat") && more) {
|
||||
Modes.fUserLat = atof(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--lon") && more) {
|
||||
Modes.fUserLon = atof(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--metric")) {
|
||||
Modes.metric = 1;
|
||||
} else if (!strcmp(argv[j],"--no-crc-check")) {
|
||||
Modes.check_crc = 0;
|
||||
} else if (!strcmp(argv[j],"--fix")) {
|
||||
Modes.nfix_crc = 1;
|
||||
} else if (!strcmp(argv[j],"--no-fix")) {
|
||||
Modes.nfix_crc = 0;
|
||||
} else if (!strcmp(argv[j],"--aggressive")) {
|
||||
Modes.nfix_crc = MODES_MAX_BITERRORS;
|
||||
} else if (!strcmp(argv[j],"--help")) {
|
||||
showHelp();
|
||||
exit(0);
|
||||
} else {
|
||||
fprintf(stderr, "Unknown or not enough arguments for option '%s'.\n\n", argv[j]);
|
||||
showHelp();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Try to comply with the Copyright license conditions for binary distribution
|
||||
if (!Modes.quiet) {showCopyright();}
|
||||
#define MSG_DONTWAIT 0
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
// Setup for SIGWINCH for handling lines
|
||||
if (Modes.interactive) {signal(SIGWINCH, sigWinchCallback);}
|
||||
#endif
|
||||
|
||||
// Initialization
|
||||
view1090Init();
|
||||
|
||||
// Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here.
|
||||
c = (struct client *) malloc(sizeof(*c));
|
||||
if ((fd = setupConnection(c)) == ANET_ERR) {
|
||||
fprintf(stderr, "Failed to connect to %s:%d\n", View1090.net_input_beast_ipaddr, Modes.net_input_beast_port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Keep going till the user does something that stops us
|
||||
while (!Modes.exit) {
|
||||
interactiveRemoveStaleAircrafts();
|
||||
interactiveShowData();
|
||||
if ((fd == ANET_ERR) || (recv(c->fd, pk_buf, sizeof(pk_buf), MSG_PEEK | MSG_DONTWAIT) == 0)) {
|
||||
free(c);
|
||||
usleep(1000000);
|
||||
c = (struct client *) malloc(sizeof(*c));
|
||||
fd = setupConnection(c);
|
||||
continue;
|
||||
}
|
||||
modesReadFromClient(c,"",decodeBinMessage);
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
// The user has stopped us, so close any socket we opened
|
||||
if (fd != ANET_ERR)
|
||||
{close(fd);}
|
||||
|
||||
return (0);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
|
@ -1,149 +0,0 @@
|
|||
# Microsoft Developer Studio Project File - Name="view1090" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=view1090 - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "view1090.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "view1090.mak" CFG="view1090 - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "view1090 - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "view1090 - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "view1090 - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
|
||||
# ADD CPP /nologo /W3 /GX /O2 /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
|
||||
# SUBTRACT CPP /YX /Yc /Yu
|
||||
# ADD BASE RSC /l 0x809 /d "NDEBUG"
|
||||
# ADD RSC /l 0x809 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /machine:I386
|
||||
# SUBTRACT LINK32 /pdb:none
|
||||
|
||||
!ELSEIF "$(CFG)" == "view1090 - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
|
||||
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
|
||||
# SUBTRACT CPP /YX /Yc /Yu
|
||||
# ADD BASE RSC /l 0x809 /d "_DEBUG"
|
||||
# ADD RSC /l 0x809 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "view1090 - Win32 Release"
|
||||
# Name "view1090 - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\anet.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\interactive.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\mode_ac.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\mode_s.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\net_io.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\view1090.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\dump1090.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\view1090.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\winstubs.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# Begin Group "Library Files"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pthreads\pthreadVC2.lib
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\rtlsdr\rtlsdr.lib
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
|
@ -1,84 +0,0 @@
|
|||
// view1090, a Mode S messages viewer for dump1090 devices.
|
||||
//
|
||||
// Copyright (C) 2013 by Malcolm Robb <Support@ATTAvionics.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#ifndef __VIEW1090_H
|
||||
#define __VIEW1090_H
|
||||
|
||||
// ============================= Include files ==========================
|
||||
|
||||
#include "dump1090.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include "rtl-sdr.h"
|
||||
#include "anet.h"
|
||||
#else
|
||||
#include "winstubs.h" //Put everything Windows specific in here
|
||||
#endif
|
||||
|
||||
// ============================= #defines ===============================
|
||||
|
||||
#define VIEW1090_NET_OUTPUT_IP_ADDRESS "127.0.0.1"
|
||||
|
||||
#define NOTUSED(V) ((void) V)
|
||||
|
||||
// ======================== structure declarations ========================
|
||||
|
||||
// Program global state
|
||||
struct { // Internal state
|
||||
// Networking
|
||||
char net_input_beast_ipaddr[32]; // IPv4 address or network name of server/RPi
|
||||
} View1090;
|
||||
|
||||
// ======================== function declarations =========================
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __VIEW1090_H
|
|
@ -1,111 +0,0 @@
|
|||
// dump1090, a Mode S messages decoder for RTLSDR devices.
|
||||
//
|
||||
// Copyright (C) 2014 by Malcolm Robb <support@attavionics.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This file provides basic Windows implementation of Linux specific functions
|
||||
// used in the dump1090 project. This allows dump1090 to be compiled and debugged
|
||||
// using Microsoft Visual C++ 6.0
|
||||
//
|
||||
// Note that not all functions actually provide equivalent functionality to their
|
||||
// Linux equivalents. They are simply stubs to allow the project to compile.
|
||||
//
|
||||
#ifndef __WINSTUBS_H
|
||||
#define __WINSTUBS_H
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <basetsd.h>
|
||||
|
||||
typedef UCHAR uint8_t;
|
||||
typedef USHORT uint16_t;
|
||||
typedef UINT32 uint32_t;
|
||||
typedef UINT64 uint64_t;
|
||||
typedef UINT32 mode_t;
|
||||
typedef long ssize_t;
|
||||
typedef int socklen_t;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define M_PI 3.14159265358979323846
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//Functions not included in the MSVC maths library. This will do for our use.
|
||||
_inline double round(double d) {return floor(d + 0.5);}
|
||||
_inline double trunc(double d) {return (d>0) ? floor(d):ceil(d) ;}
|
||||
|
||||
//usleep works in microseconds, and isn't supported in Windows. This will do for our use.
|
||||
_inline void usleep(UINT32 ulSleep) {Sleep(ulSleep/1000);}
|
||||
_inline uint64_t strtoll(const char *p, void *e, UINT32 base) {return _atoi64(p);}
|
||||
_inline int inet_aton(const char * cp, DWORD * ulAddr) { *ulAddr = inet_addr(cp); return (INADDR_NONE != *ulAddr);}
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
|
||||
|
||||
_inline void cls() {
|
||||
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
COORD coord = {0, 0};
|
||||
DWORD count;
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(hStdOut, &csbi);
|
||||
|
||||
FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
|
||||
|
||||
SetConsoleCursorPosition(hStdOut, coord);
|
||||
}
|
||||
|
||||
_inline int gettimeofday(struct timeval *tv, struct timezone *tz) {
|
||||
SYSTEMTIME stSystemTime;
|
||||
GetLocalTime(&stSystemTime);
|
||||
|
||||
tv->tv_sec = stSystemTime.wSecond + (60 * (stSystemTime.wMinute + (60 * stSystemTime.wHour)));
|
||||
tv->tv_usec = stSystemTime.wMilliseconds * 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define STDIN_FILENO 0
|
||||
#define EINPROGRESS WSAEINPROGRESS
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __WINSTUBS_H
|
|
@ -1,36 +0,0 @@
|
|||
import pygame
|
||||
import os
|
||||
from time import sleep
|
||||
import RPi.GPIO as GPIO
|
||||
|
||||
#Note #21 changed to #27 for rev2 Pi
|
||||
button_map = {23:(255,0,0), 22:(0,255,0), 27:(0,0,255), 17:(0,0,0)}
|
||||
|
||||
#Setup the GPIOs as inputs with Pull Ups since the buttons are connected to GND
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
for k in button_map.keys():
|
||||
GPIO.setup(k, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
||||
|
||||
#Colours
|
||||
WHITE = (255,255,255)
|
||||
|
||||
os.putenv('SDL_FBDEV', '/dev/fb1')
|
||||
pygame.init()
|
||||
pygame.mouse.set_visible(False)
|
||||
lcd = pygame.display.set_mode((320, 240))
|
||||
lcd.fill((0,0,0))
|
||||
pygame.display.update()
|
||||
|
||||
font_big = pygame.font.Font(None, 100)
|
||||
|
||||
|
||||
while True:
|
||||
# Scan the buttons
|
||||
for (k,v) in button_map.items():
|
||||
if GPIO.input(k) == False:
|
||||
lcd.fill(v)
|
||||
text_surface = font_big.render('%d'%k, True, WHITE)
|
||||
rect = text_surface.get_rect(center=(160,120))
|
||||
lcd.blit(text_surface, rect)
|
||||
pygame.display.update()
|
||||
sleep(0.1)
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import pygame
|
||||
import os
|
||||
from time import sleep
|
||||
|
||||
#Colours
|
||||
WHITE = (255,255,255)
|
||||
|
||||
os.putenv('SDL_FBDEV', '/dev/fb1')
|
||||
pygame.init()
|
||||
pygame.mouse.set_visible(False)
|
||||
lcd = pygame.display.set_mode((320, 240))
|
||||
|
||||
font_big = pygame.font.Font(None, 100)
|
||||
font_small = pygame.font.Font(None, 18)
|
||||
|
||||
lcd.fill((0,0,0))
|
||||
pygame.display.update()
|
||||
|
||||
top_1 = (0,0,80,40);
|
||||
top_2 = (80,0,80,40);
|
||||
top_3 = (160,0,80,40);
|
||||
top_4 = (240,0,80,40);
|
||||
|
||||
pygame.draw.rect(lcd, (10,10,10), top_1, 2)
|
||||
pygame.draw.rect(lcd, (10,10,10), top_2, 2)
|
||||
pygame.draw.rect(lcd, (10,10,10), top_3, 2)
|
||||
pygame.draw.rect(lcd, (10,10,10), top_4, 2)
|
||||
|
||||
text_surface = font_small.render('DEMOD', True, WHITE)
|
||||
rect = text_surface.get_rect(center=(40, 20))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
text_surface = font_small.render('SQLCH', True, WHITE)
|
||||
rect = text_surface.get_rect(center=(120, 20))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
text_surface = font_small.render('GAIN', True, WHITE)
|
||||
rect = text_surface.get_rect(center=(200, 20))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
text_surface = font_small.render('MENU', True, WHITE)
|
||||
rect = text_surface.get_rect(center=(280, 20))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
|
||||
sql_gain_box = (0,40,32,200)
|
||||
|
||||
pygame.draw.rect(lcd, (10,10,10), sql_gain_box, 2)
|
||||
|
||||
for x in range(0,6):
|
||||
pygame.draw.rect(lcd, (10,10,10), (32 + x * 48,40,48,80),2)
|
||||
text_surface = font_big.render('%d'%x, True, WHITE)
|
||||
rect = text_surface.get_rect(center=(32 + (x + .5) * 48, 80))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
while True:
|
||||
pygame.display.update()
|
||||
sleep(1)
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import pygame
|
||||
import os
|
||||
from time import sleep
|
||||
|
||||
#Colours
|
||||
WHITE = (255,255,255)
|
||||
|
||||
#os.putenv('SDL_FBDEV', '/dev/fb1')
|
||||
pygame.init()
|
||||
pygame.mouse.set_visible(False)
|
||||
lcd = pygame.display.set_mode((320, 240))
|
||||
|
||||
font_big = pygame.font.Font(None, 100)
|
||||
font_small = pygame.font.Font(None, 18)
|
||||
|
||||
lcd.fill((0,0,0))
|
||||
pygame.display.update()
|
||||
|
||||
top_1 = (0,0,80,40);
|
||||
top_2 = (80,0,80,40);
|
||||
top_3 = (160,0,80,40);
|
||||
top_4 = (240,0,80,40);
|
||||
|
||||
pygame.draw.rect(lcd, (100,100,100), top_1, 2)
|
||||
pygame.draw.rect(lcd, (100,100,100), top_2, 2)
|
||||
pygame.draw.rect(lcd, (100,100,100), top_3, 2)
|
||||
pygame.draw.rect(lcd, (100,100,100), top_4, 2)
|
||||
|
||||
text_surface = font_small.render('DEMOD', True, WHITE)
|
||||
rect = text_surface.get_rect(center=(40, 20))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
text_surface = font_small.render('SQLCH', True, WHITE)
|
||||
rect = text_surface.get_rect(center=(120, 20))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
text_surface = font_small.render('GAIN', True, WHITE)
|
||||
rect = text_surface.get_rect(center=(200, 20))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
text_surface = font_small.render('MENU', True, WHITE)
|
||||
rect = text_surface.get_rect(center=(280, 20))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
|
||||
sql_gain_box = (0,40,32,200)
|
||||
|
||||
pygame.draw.rect(lcd, (10,10,10), sql_gain_box, 2)
|
||||
|
||||
for x in range(0,6):
|
||||
pygame.draw.rect(lcd, (10,10,10), (32 + x * 48,40,48,80),2)
|
||||
text_surface = font_big.render('%d'%x, True, WHITE)
|
||||
rect = text_surface.get_rect(center=(32 + (x + .5) * 48, 80))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
while True:
|
||||
pygame.display.update()
|
||||
sleep(1)
|
|
@ -1,137 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import pygame
|
||||
import os
|
||||
from time import sleep
|
||||
import RPi.GPIO as GPIO
|
||||
from rtl_fm_python_thread import *
|
||||
|
||||
make_rtl_fm_thread(block=False)
|
||||
|
||||
#Note #21 changed to #27 for rev2 Pi
|
||||
button_map = {23:(255,0,0), 22:(0,255,0), 27:(0,0,255), 17:(0,0,0)}
|
||||
|
||||
#Setup the GPIOs as inputs with Pull Ups since the buttons are connected to GND
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
for k in button_map.keys():
|
||||
GPIO.setup(k, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
||||
|
||||
#Colours
|
||||
WHITE = (255,255,255)
|
||||
|
||||
os.putenv('SDL_FBDEV', '/dev/fb1')
|
||||
pygame.init()
|
||||
pygame.mouse.set_visible(False)
|
||||
lcd = pygame.display.set_mode((320, 240))
|
||||
lcd.fill((0,0,0))
|
||||
pygame.display.update()
|
||||
|
||||
font_big = pygame.font.Font(None, 100)
|
||||
font_small = pygame.font.Font(None, 18)
|
||||
|
||||
dial = 0
|
||||
|
||||
dialColor = (255,255,255)
|
||||
|
||||
sql = 0
|
||||
|
||||
sql_str = list("0x")
|
||||
|
||||
freq = [1,6,2,5,5,0]
|
||||
|
||||
freq_idx = 0
|
||||
|
||||
wasdown = 0
|
||||
|
||||
sql_gain_select = 0
|
||||
|
||||
redraw = 1
|
||||
|
||||
set_frequency(int(''.join(str(x) for x in freq) + "000"))
|
||||
|
||||
while True:
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
redraw = 1
|
||||
if event.key == pygame.K_x:
|
||||
freq[freq_idx] = (freq[freq_idx] + 1) % 10
|
||||
set_frequency(int(''.join(str(x) for x in freq) + "000"))
|
||||
elif event.key == pygame.K_z:
|
||||
freq[freq_idx] = (freq[freq_idx] - 1) % 10
|
||||
set_frequency(int(''.join(str(x) for x in freq) + "000"))
|
||||
elif event.key == pygame.K_m:
|
||||
wasdown = 1;
|
||||
elif event.key == pygame.K_n:
|
||||
if(wasdown == 1):
|
||||
wasdown = 0
|
||||
freq_idx = (freq_idx + 1) % 6
|
||||
elif event.key == pygame.K_0:
|
||||
sql_str.append('0')
|
||||
elif event.key == pygame.K_1:
|
||||
sql_str.append('1')
|
||||
elif event.key == pygame.K_2:
|
||||
sql_str.append('2')
|
||||
elif event.key == pygame.K_3:
|
||||
sql_str.append('3')
|
||||
elif event.key == pygame.K_4:
|
||||
sql_str.append('4')
|
||||
elif event.key == pygame.K_5:
|
||||
sql_str.append('5')
|
||||
elif event.key == pygame.K_6:
|
||||
sql_str.append('6')
|
||||
elif event.key == pygame.K_7:
|
||||
sql_str.append('7')
|
||||
elif event.key == pygame.K_8:
|
||||
sql_str.append('8')
|
||||
elif event.key == pygame.K_9:
|
||||
sql_str.append('9')
|
||||
elif event.key == pygame.K_a:
|
||||
sql_str.append('a')
|
||||
elif event.key == pygame.K_b:
|
||||
sql_str.append('b')
|
||||
elif event.key == pygame.K_c:
|
||||
sql_str.append('c')
|
||||
elif event.key == pygame.K_d:
|
||||
sql_str.append('d')
|
||||
elif event.key == pygame.K_e:
|
||||
sql_str.append('e')
|
||||
elif event.key == pygame.K_f:
|
||||
sql_str.append('f')
|
||||
elif event.key == pygame.K_RETURN:
|
||||
sql = int("".join(sql_str), 0)
|
||||
if sql_gain_select:
|
||||
set_gain(sql)
|
||||
else:
|
||||
set_squelch(sql)
|
||||
sql_str = list("0x")
|
||||
|
||||
# Scan the buttons
|
||||
for (k,v) in button_map.items():
|
||||
if GPIO.input(k) == False:
|
||||
redraw = 1
|
||||
if k == 27:
|
||||
sql_gain_select = ~sql_gain_select
|
||||
|
||||
if redraw:
|
||||
lcd.fill((0,0,0))
|
||||
|
||||
for x in range(0,6):
|
||||
text_surface = font_big.render('%d'%freq[x], True, WHITE)
|
||||
rect = text_surface.get_rect(center=((x + .5) * 320 / 6, 80))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
pygame.draw.rect(lcd, WHITE, (freq_idx * 320 / 6, 50, 320 / 6, 70), 1)
|
||||
pygame.draw.rect(lcd, (255, 200, 10), (10,10,20, 220 * sql / 1024), 0)
|
||||
|
||||
if sql_gain_select:
|
||||
sql_gain_surface = font_small.render("VOL", True, WHITE)
|
||||
else:
|
||||
sql_gain_surface = font_small.render("SQL", True, WHITE)
|
||||
rect = sql_gain_surface.get_rect(center=(25,25))
|
||||
lcd.blit(sql_gain_surface, rect)
|
||||
|
||||
pygame.display.update()
|
||||
redraw = 0
|
||||
|
||||
sleep(0.1)
|
|
@ -1,103 +0,0 @@
|
|||
import pygame
|
||||
import os
|
||||
from time import sleep
|
||||
import RPi.GPIO as GPIO
|
||||
|
||||
#Note #21 changed to #27 for rev2 Pi
|
||||
button_map = {23:(255,0,0), 22:(0,255,0), 27:(0,0,255), 17:(0,0,0)}
|
||||
|
||||
#Setup the GPIOs as inputs with Pull Ups since the buttons are connected to GND
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
for k in button_map.keys():
|
||||
GPIO.setup(k, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
||||
|
||||
#Colours
|
||||
WHITE = (255,255,255)
|
||||
|
||||
os.putenv('SDL_FBDEV', '/dev/fb1')
|
||||
pygame.init()
|
||||
pygame.mouse.set_visible(False)
|
||||
lcd = pygame.display.set_mode((320, 240))
|
||||
lcd.fill((0,0,0))
|
||||
pygame.display.update()
|
||||
|
||||
font_big = pygame.font.Font(None, 100)
|
||||
|
||||
dial = 0
|
||||
|
||||
dialColor = (255,255,255)
|
||||
|
||||
sql = 0
|
||||
|
||||
sql_str = list("0x");
|
||||
|
||||
while True:
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
|
||||
print pygame.key.name(event.key)
|
||||
|
||||
if event.key == pygame.K_x:
|
||||
dial += 1
|
||||
elif event.key == pygame.K_z:
|
||||
dial -= 1
|
||||
elif event.key == pygame.K_m:
|
||||
dialColor = (127,255,255)
|
||||
elif event.key == pygame.K_n:
|
||||
dialColor = (255,255,255)
|
||||
elif event.key == pygame.K_0:
|
||||
sql_str.append('0')
|
||||
elif event.key == pygame.K_1:
|
||||
sql_str.append('1')
|
||||
elif event.key == pygame.K_2:
|
||||
sql_str.append('2')
|
||||
elif event.key == pygame.K_3:
|
||||
sql_str.append('3')
|
||||
elif event.key == pygame.K_4:
|
||||
sql_str.append('4')
|
||||
elif event.key == pygame.K_5:
|
||||
sql_str.append('5')
|
||||
elif event.key == pygame.K_6:
|
||||
sql_str.append('6')
|
||||
elif event.key == pygame.K_7:
|
||||
sql_str.append('7')
|
||||
elif event.key == pygame.K_8:
|
||||
sql_str.append('8')
|
||||
elif event.key == pygame.K_9:
|
||||
sql_str.append('9')
|
||||
elif event.key == pygame.K_a:
|
||||
sql_str.append('a')
|
||||
elif event.key == pygame.K_b:
|
||||
sql_str.append('b')
|
||||
elif event.key == pygame.K_c:
|
||||
sql_str.append('c')
|
||||
elif event.key == pygame.K_d:
|
||||
sql_str.append('d')
|
||||
elif event.key == pygame.K_e:
|
||||
sql_str.append('e')
|
||||
elif event.key == pygame.K_f:
|
||||
sql_str.append('f')
|
||||
elif event.key == pygame.K_RETURN:
|
||||
sql = int("".join(sql_str), 0)
|
||||
sql_str = list("0x")
|
||||
|
||||
lcd.fill((0,0,0))
|
||||
text_surface = font_big.render('%d'%dial, True, dialColor)
|
||||
rect = text_surface.get_rect(center=(160,100))
|
||||
lcd.blit(text_surface, rect)
|
||||
sql_surface = font_big.render('%d'%sql, True, WHITE)
|
||||
rect = text_surface.get_rect(center=(160,140))
|
||||
lcd.blit(sql_surface, rect)
|
||||
pygame.display.update()
|
||||
|
||||
# Scan the buttons
|
||||
# for (k,v) in button_map.items():
|
||||
# if GPIO.input(k) == False:
|
||||
# lcd.fill(v)
|
||||
# text_surface = font_big.render('%d'%k, True, WHITE)
|
||||
# rect = text_surface.get_rect(center=(160,120))
|
||||
# lcd.blit(text_surface, rect)
|
||||
# pygame.display.update()
|
||||
# sleep(0.1)
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
import pygame
|
||||
import os
|
||||
from time import sleep
|
||||
import RPi.GPIO as GPIO
|
||||
|
||||
#Note #21 changed to #27 for rev2 Pi
|
||||
button_map = {23:(255,0,0), 22:(0,255,0), 27:(0,0,255), 17:(0,0,0)}
|
||||
|
||||
#Setup the GPIOs as inputs with Pull Ups since the buttons are connected to GND
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
for k in button_map.keys():
|
||||
GPIO.setup(k, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
||||
|
||||
#Colours
|
||||
WHITE = (255,255,255)
|
||||
|
||||
os.putenv('SDL_FBDEV', '/dev/fb1')
|
||||
pygame.init()
|
||||
pygame.mouse.set_visible(False)
|
||||
lcd = pygame.display.set_mode((320, 240))
|
||||
lcd.fill((0,0,0))
|
||||
pygame.display.update()
|
||||
|
||||
font_big = pygame.font.Font(None, 100)
|
||||
|
||||
dial = 0
|
||||
|
||||
dialColor = (255,255,255)
|
||||
|
||||
sql = 0
|
||||
|
||||
sql_str = list("0x")
|
||||
|
||||
freq = [4,4,2,2,7,5]
|
||||
|
||||
freq_idx = 0;
|
||||
|
||||
wasdown = 0;
|
||||
|
||||
while True:
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_x:
|
||||
freq[freq_idx] = (freq[freq_idx] + 1) % 10
|
||||
elif event.key == pygame.K_z:
|
||||
freq[freq_idx] = (freq[freq_idx] - 1) % 10
|
||||
elif event.key == pygame.K_m:
|
||||
wasdown = 1;
|
||||
elif event.key == pygame.K_n:
|
||||
if(wasdown == 1):
|
||||
wasdown = 0
|
||||
freq_idx = (freq_idx + 1) % 6
|
||||
elif event.key == pygame.K_0:
|
||||
sql_str.append('0')
|
||||
elif event.key == pygame.K_1:
|
||||
sql_str.append('1')
|
||||
elif event.key == pygame.K_2:
|
||||
sql_str.append('2')
|
||||
elif event.key == pygame.K_3:
|
||||
sql_str.append('3')
|
||||
elif event.key == pygame.K_4:
|
||||
sql_str.append('4')
|
||||
elif event.key == pygame.K_5:
|
||||
sql_str.append('5')
|
||||
elif event.key == pygame.K_6:
|
||||
sql_str.append('6')
|
||||
elif event.key == pygame.K_7:
|
||||
sql_str.append('7')
|
||||
elif event.key == pygame.K_8:
|
||||
sql_str.append('8')
|
||||
elif event.key == pygame.K_9:
|
||||
sql_str.append('9')
|
||||
elif event.key == pygame.K_a:
|
||||
sql_str.append('a')
|
||||
elif event.key == pygame.K_b:
|
||||
sql_str.append('b')
|
||||
elif event.key == pygame.K_c:
|
||||
sql_str.append('c')
|
||||
elif event.key == pygame.K_d:
|
||||
sql_str.append('d')
|
||||
elif event.key == pygame.K_e:
|
||||
sql_str.append('e')
|
||||
elif event.key == pygame.K_f:
|
||||
sql_str.append('f')
|
||||
elif event.key == pygame.K_RETURN:
|
||||
sql = int("".join(sql_str), 0)
|
||||
sql_str = list("0x")
|
||||
|
||||
lcd.fill((0,0,0))
|
||||
#text_surface = font_big.render('%d'%dial, True, dialColor)
|
||||
#rect = text_surface.get_rect(center=(160,100))
|
||||
#lcd.blit(text_surface, rect)
|
||||
|
||||
for x in range(0,6):
|
||||
text_surface = font_big.render('%d'%freq[x], True, WHITE)
|
||||
rect = text_surface.get_rect(center=((x + .5) * 320 / 6, 80))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
pygame.draw.rect(lcd, WHITE, (freq_idx * 320 / 6, 50, 320 / 6, 70), 1)
|
||||
|
||||
pygame.draw.rect(lcd, (255, 200, 10), (10,10,20, 220 * sql / 1024), 0)
|
||||
#sql_surface = font_big.render('%d'%sql, True, WHITE)
|
||||
#rect = text_surface.get_rect(center=(160,140))
|
||||
#lcd.blit(sql_surface, rect)
|
||||
pygame.display.update()
|
||||
|
||||
# Scan the buttons
|
||||
# for (k,v) in button_map.items():
|
||||
# if GPIO.input(k) == False:
|
||||
# lcd.fill(v)
|
||||
# text_surface = font_big.render('%d'%k, True, WHITE)
|
||||
# rect = text_surface.get_rect(center=(160,120))
|
||||
# lcd.blit(text_surface, rect)
|
||||
# pygame.display.update()
|
||||
# sleep(0.1)
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import pygame
|
||||
import os
|
||||
from time import sleep
|
||||
import RPi.GPIO as GPIO
|
||||
from rtl_fm_python_thread import *
|
||||
|
||||
make_rtl_fm_thread(block=False)
|
||||
|
||||
#Note #21 changed to #27 for rev2 Pi
|
||||
button_map = {23:(255,0,0), 22:(0,255,0), 27:(0,0,255), 17:(0,0,0)}
|
||||
|
||||
#Setup the GPIOs as inputs with Pull Ups since the buttons are connected to GND
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
for k in button_map.keys():
|
||||
GPIO.setup(k, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
||||
|
||||
#Colours
|
||||
WHITE = (255,255,255)
|
||||
|
||||
os.putenv('SDL_FBDEV', '/dev/fb1')
|
||||
pygame.init()
|
||||
pygame.mouse.set_visible(False)
|
||||
lcd = pygame.display.set_mode((320, 240))
|
||||
lcd.fill((0,0,0))
|
||||
pygame.display.update()
|
||||
|
||||
font_big = pygame.font.Font(None, 100)
|
||||
|
||||
dial = 0
|
||||
|
||||
dialColor = (255,255,255)
|
||||
|
||||
sql = 0
|
||||
|
||||
sql_str = list("0x")
|
||||
|
||||
freq = [4,4,2,2,7,5]
|
||||
|
||||
freq_idx = 0;
|
||||
|
||||
wasdown = 0;
|
||||
|
||||
while True:
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_x:
|
||||
freq[freq_idx] = (freq[freq_idx] + 1) % 10
|
||||
set_frequency(int(''.join(str(x) for x in freq) + "000"))
|
||||
elif event.key == pygame.K_z:
|
||||
freq[freq_idx] = (freq[freq_idx] - 1) % 10
|
||||
set_frequency(int(''.join(str(x) for x in freq) + "000"))
|
||||
elif event.key == pygame.K_m:
|
||||
wasdown = 1;
|
||||
elif event.key == pygame.K_n:
|
||||
if(wasdown == 1):
|
||||
wasdown = 0
|
||||
freq_idx = (freq_idx + 1) % 6
|
||||
elif event.key == pygame.K_0:
|
||||
sql_str.append('0')
|
||||
elif event.key == pygame.K_1:
|
||||
sql_str.append('1')
|
||||
elif event.key == pygame.K_2:
|
||||
sql_str.append('2')
|
||||
elif event.key == pygame.K_3:
|
||||
sql_str.append('3')
|
||||
elif event.key == pygame.K_4:
|
||||
sql_str.append('4')
|
||||
elif event.key == pygame.K_5:
|
||||
sql_str.append('5')
|
||||
elif event.key == pygame.K_6:
|
||||
sql_str.append('6')
|
||||
elif event.key == pygame.K_7:
|
||||
sql_str.append('7')
|
||||
elif event.key == pygame.K_8:
|
||||
sql_str.append('8')
|
||||
elif event.key == pygame.K_9:
|
||||
sql_str.append('9')
|
||||
elif event.key == pygame.K_a:
|
||||
sql_str.append('a')
|
||||
elif event.key == pygame.K_b:
|
||||
sql_str.append('b')
|
||||
elif event.key == pygame.K_c:
|
||||
sql_str.append('c')
|
||||
elif event.key == pygame.K_d:
|
||||
sql_str.append('d')
|
||||
elif event.key == pygame.K_e:
|
||||
sql_str.append('e')
|
||||
elif event.key == pygame.K_f:
|
||||
sql_str.append('f')
|
||||
elif event.key == pygame.K_RETURN:
|
||||
sql = int("".join(sql_str), 0)
|
||||
set_squelch(sql)
|
||||
sql_str = list("0x")
|
||||
|
||||
lcd.fill((0,0,0))
|
||||
#text_surface = font_big.render('%d'%dial, True, dialColor)
|
||||
#rect = text_surface.get_rect(center=(160,100))
|
||||
#lcd.blit(text_surface, rect)
|
||||
|
||||
for x in range(0,6):
|
||||
text_surface = font_big.render('%d'%freq[x], True, WHITE)
|
||||
rect = text_surface.get_rect(center=((x + .5) * 320 / 6, 80))
|
||||
lcd.blit(text_surface, rect)
|
||||
|
||||
pygame.draw.rect(lcd, WHITE, (freq_idx * 320 / 6, 50, 320 / 6, 70), 1)
|
||||
|
||||
pygame.draw.rect(lcd, (255, 200, 10), (10,10,20, 220 * sql / 1024), 0)
|
||||
#sql_surface = font_big.render('%d'%sql, True, WHITE)
|
||||
#rect = text_surface.get_rect(center=(160,140))
|
||||
#lcd.blit(sql_surface, rect)
|
||||
pygame.display.update()
|
||||
|
||||
# Scan the buttons
|
||||
# for (k,v) in button_map.items():
|
||||
# if GPIO.input(k) == False:
|
||||
# lcd.fill(v)
|
||||
# text_surface = font_big.render('%d'%k, True, WHITE)
|
||||
# rect = text_surface.get_rect(center=(160,120))
|
||||
# lcd.blit(text_surface, rect)
|
||||
# pygame.display.update()
|
||||
# sleep(0.1)
|
|
@ -1 +0,0 @@
|
|||
a899270edd156bceac4243cab7ce9286364ca7d0
|
|
@ -1,141 +0,0 @@
|
|||
# rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
|
||||
# Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de>
|
||||
# Copyright (C) 2012 by Hoernchen <la@tfc-server.de>
|
||||
# Copyright (C) 2012 by Kyle Keen <keenerd@gmail.com>
|
||||
# Copyright (C) 2013 by Elias Oenal <EliasOenal@gmail.com>
|
||||
# Copyright (C) 2014 by Thomas Winningham <winningham@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import print_function
|
||||
import ctypes, sys
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
|
||||
printstderr = lambda x : print(x,file=sys.stderr)
|
||||
|
||||
|
||||
fm = ctypes.CDLL('./rtl_fm_python.so')
|
||||
get_s_level = fm.lib_get_s_level
|
||||
get_frequency = fm.lib_get_frequency
|
||||
set_demod_fm = fm.lib_set_demod_fm
|
||||
set_demod_wbfm = fm.lib_set_demod_wbfm
|
||||
set_demod_am = fm.lib_set_demod_am
|
||||
set_demod_lsb = fm.lib_set_demod_lsb
|
||||
set_demod_usb = fm.lib_set_demod_usb
|
||||
set_demod_raw = fm.lib_set_demod_raw
|
||||
set_frequency = lambda f : fm.lib_set_frequency(ctypes.c_uint32(f))
|
||||
set_squelch = lambda l : fm.lib_set_squelch_level(ctypes.c_int(l))
|
||||
get_demod = lambda : chr(fm.lib_get_demod_mode())
|
||||
str_to_freq = fm.lib_frequency_convert
|
||||
|
||||
def process_args(l):
|
||||
c=len(l)+1
|
||||
argc=ctypes.c_int(c)
|
||||
argv_var=ctypes.c_char_p *c
|
||||
argu = ['rtl_fm'] + l
|
||||
argv=argv_var(*argu)
|
||||
return (argc,argv)
|
||||
|
||||
def mag(value, e, suffix):
|
||||
t = float('1e%s' % e)
|
||||
i = int(value / float(t))
|
||||
r = int(value % float(t))
|
||||
r = str(r).rjust(e,'0')
|
||||
s = "%s.%s" % (i,r)
|
||||
while (s[-1]=="0" or s[-1]=="."):
|
||||
if s[-1]==".":
|
||||
s=s[:-1]
|
||||
break
|
||||
s=s[:-1]
|
||||
return s + suffix
|
||||
|
||||
def freq_to_str(f):
|
||||
if f >= 1000000000:
|
||||
return mag(f,9,'G')
|
||||
if f >= 1000000:
|
||||
return mag(f,6,'M')
|
||||
if f >= 1000:
|
||||
return mag(f,3,'K')
|
||||
return str(f)
|
||||
|
||||
set_freq_human = lambda f : set_frequency(str_to_freq(f))
|
||||
get_freq_human = lambda : freq_to_str(get_frequency())
|
||||
|
||||
def set_demod(c):
|
||||
if c=='w' : set_demod_wbfm()
|
||||
if c=='f' : set_demod_fm()
|
||||
if c=='a' : set_demod_am()
|
||||
if c=='l' : set_demod_lsb()
|
||||
if c=='u' : set_demod_usb()
|
||||
if c=='r' : set_demod_raw()
|
||||
|
||||
def get_gains():
|
||||
c=fm.lib_get_tuner_gains_count()
|
||||
b=(ctypes.c_int * c)()
|
||||
fm.lib_get_tuner_gains(b)
|
||||
return list(b)
|
||||
|
||||
def set_gain(g):
|
||||
fm.lib_set_real_gain(g)
|
||||
|
||||
def get_gain():
|
||||
return fm.lib_get_gain()
|
||||
|
||||
def get_auto_gain():
|
||||
return get_gain()==-100
|
||||
|
||||
def set_gain_human(g):
|
||||
fm.lib_set_gain(g)
|
||||
|
||||
def set_auto_gain():
|
||||
fm.lib_set_auto_gain()
|
||||
|
||||
|
||||
|
||||
def rtl_fm(args=["--help"]):
|
||||
fm.main(*process_args(args))
|
||||
|
||||
def rtl_fm_setup_and_go(args):
|
||||
fm.lib_init_first()
|
||||
fm.lib_process_args(*process_args(args))
|
||||
fm.lib_input_open()
|
||||
fm.lib_output_open()
|
||||
fm.lib_go()
|
||||
|
||||
def rtl_fm_loop():
|
||||
fm.lib_loop()
|
||||
|
||||
def rtl_fm_finish():
|
||||
fm.lib_stop()
|
||||
fm.lib_output_close()
|
||||
|
||||
|
||||
def rtl_fm_wrapped(args=["--help"]):
|
||||
rtl_fm_setup_and_go(args)
|
||||
try:
|
||||
while True:
|
||||
sleep(0.01)
|
||||
except KeyboardInterrupt:
|
||||
rtl_fm_finish()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Binary file not shown.
|
@ -1,54 +0,0 @@
|
|||
#!/usr/bin/python -i
|
||||
|
||||
# rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
|
||||
# Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de>
|
||||
# Copyright (C) 2012 by Hoernchen <la@tfc-server.de>
|
||||
# Copyright (C) 2012 by Kyle Keen <keenerd@gmail.com>
|
||||
# Copyright (C) 2013 by Elias Oenal <EliasOenal@gmail.com>
|
||||
# Copyright (C) 2014 by Thomas Winningham <winningham@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from rtl_fm_python_common import *
|
||||
|
||||
running = True
|
||||
queue=[]
|
||||
|
||||
def rtl_fm_thread(args):
|
||||
rtl_fm_setup_and_go(args)
|
||||
while running==True:
|
||||
if len(queue) > 0:
|
||||
queue.pop()()
|
||||
sleep(1)
|
||||
rtl_fm_finish()
|
||||
|
||||
def make_rtl_fm_thread(args=sys.argv[1:],block=False):
|
||||
t = Thread(target=rtl_fm_thread,args=(args,))
|
||||
t.daemon=True
|
||||
t.start()
|
||||
if block:
|
||||
try:
|
||||
while True:
|
||||
sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
running = False
|
||||
|
||||
def stop_thread():
|
||||
running = False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
make_rtl_fm_thread(block=False)
|
||||
|
Binary file not shown.
|
@ -1,85 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
|
||||
# Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de>
|
||||
# Copyright (C) 2012 by Hoernchen <la@tfc-server.de>
|
||||
# Copyright (C) 2012 by Kyle Keen <keenerd@gmail.com>
|
||||
# Copyright (C) 2013 by Elias Oenal <EliasOenal@gmail.com>
|
||||
# Copyright (C) 2014 by Thomas Winningham <winningham@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from flask import Flask, jsonify, url_for, redirect
|
||||
from rtl_fm_python_thread import *
|
||||
|
||||
make_rtl_fm_thread(block=False)
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def web_root():
|
||||
return redirect(url_for('static', filename='index.html'))
|
||||
|
||||
@app.route('/state')
|
||||
def web_state():
|
||||
return jsonify(
|
||||
{
|
||||
's_level' : get_s_level(),
|
||||
'freq_s' : get_freq_human(),
|
||||
'freq_i' : get_frequency(),
|
||||
'mod' : get_demod(),
|
||||
'gain' : get_gain(),
|
||||
'autogain' : get_auto_gain()
|
||||
})
|
||||
|
||||
@app.route('/frequency/<int:f>')
|
||||
def web_set_frequency(f):
|
||||
set_frequency(f)
|
||||
return web_state()
|
||||
|
||||
@app.route('/frequency/human/<f>')
|
||||
def web_set_human_frequency(f):
|
||||
set_freq_human(str(f))
|
||||
return web_state()
|
||||
|
||||
@app.route('/demod/<c>')
|
||||
def web_set_demod(c):
|
||||
set_demod(str(c))
|
||||
return web_state()
|
||||
|
||||
@app.route('/gain/<g>')
|
||||
def web_set_gain(g):
|
||||
gain = int(str(g))
|
||||
set_gain(gain)
|
||||
return web_state()
|
||||
|
||||
@app.route('/gain/human/<g>')
|
||||
def web_set_gain_human(g):
|
||||
gain = int(str(g))
|
||||
set_gain_human(gain)
|
||||
return web_state()
|
||||
|
||||
@app.route('/gain/auto')
|
||||
def web_set_auto_gain():
|
||||
set_auto_gain()
|
||||
return web_state()
|
||||
|
||||
@app.route('/gain/list')
|
||||
def web_get_gain_list():
|
||||
l=get_gains()
|
||||
return jsonify({'gains':l})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0',port=10100)
|
||||
stop_thread()
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/sh
|
||||
/usr/bin/fbi -T 2 -d /dev/fb1 -noverbose -a /home/nmatsuda/spidr/test_001.png
|
|
@ -1 +0,0 @@
|
|||
./key.py -M fm -f 162.55M -s 32k - |aplay -r 32k -f S16_LE -t raw -c 1
|
Binary file not shown.
Before Width: | Height: | Size: 3.7 KiB |
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
./map1090 --screensize 640 360 --fullscreen --server adsb --lat 47.6 --lon -122.3
|
2
run_viz1090.sh
Executable file
2
run_viz1090.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
./viz1090 --screensize 640 360 --fullscreen --server adsb --lat 47.6 --lon -122.3
|
Loading…
Reference in a new issue