kopia lustrzana https://github.com/cyoung/stratux
commit
c3476f064c
6
Makefile
6
Makefile
|
@ -3,7 +3,11 @@ GOARCH ?= arm
|
||||||
GOARM ?= 7
|
GOARM ?= 7
|
||||||
|
|
||||||
all:
|
all:
|
||||||
GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build gen_gdl90.go traffic.go ry835ai.go network.go
|
GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go get -t -d -v ./...
|
||||||
|
GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build main/gen_gdl90.go main/traffic.go main/ry835ai.go main/network.go main/managementinterface.go
|
||||||
|
|
||||||
|
test:
|
||||||
|
sh -c true
|
||||||
|
|
||||||
install:
|
install:
|
||||||
cp -f gen_gdl90 /usr/bin/gen_gdl90
|
cp -f gen_gdl90 /usr/bin/gen_gdl90
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
dependencies:
|
||||||
|
override:
|
||||||
|
- make
|
||||||
|
test:
|
||||||
|
override:
|
||||||
|
- make test
|
|
@ -0,0 +1,15 @@
|
||||||
|
CFLAGS?=-O2 -g -Wall -W $(shell pkg-config --cflags librtlsdr)
|
||||||
|
LDLIBS+=$(shell pkg-config --libs librtlsdr) -lpthread -lm
|
||||||
|
CC?=gcc
|
||||||
|
PROGNAME=dump1090
|
||||||
|
|
||||||
|
all: dump1090
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c $<
|
||||||
|
|
||||||
|
dump1090: dump1090.o anet.o
|
||||||
|
$(CC) -g -o dump1090 dump1090.o anet.o $(LDFLAGS) $(LDLIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o dump1090
|
|
@ -0,0 +1,284 @@
|
||||||
|
Dump1090 README
|
||||||
|
===
|
||||||
|
|
||||||
|
Dump 1090 is a Mode S decoder specifically designed for RTLSDR devices.
|
||||||
|
|
||||||
|
The main features are:
|
||||||
|
|
||||||
|
* Robust decoding of weak messages, with mode1090 many users observed
|
||||||
|
improved range compared to other popular decoders.
|
||||||
|
* Network support: TCP30003 stream (MSG5...), Raw packets, HTTP.
|
||||||
|
* Embedded HTTP server that displays the currently detected aircrafts on
|
||||||
|
Google Map.
|
||||||
|
* Single bit errors correction using the 24 bit CRC.
|
||||||
|
* Ability to decode DF11, DF17 messages.
|
||||||
|
* Ability to decode DF formats like DF0, DF4, DF5, DF16, DF20 and DF21
|
||||||
|
where the checksum is xored with the ICAO address by brute forcing the
|
||||||
|
checksum field using recently seen ICAO addresses.
|
||||||
|
* Decode raw IQ samples from file (using --ifile command line switch).
|
||||||
|
* Interactive command-line-interfae mode where aircrafts currently detected
|
||||||
|
are shown as a list refreshing as more data arrives.
|
||||||
|
* CPR coordinates decoding and track calculation from velocity.
|
||||||
|
* TCP server streaming and receiving raw data to/from connected clients
|
||||||
|
(using --net).
|
||||||
|
|
||||||
|
While from time to time I still add / fix stuff in my fork, I target
|
||||||
|
minimalism of the implementation. However there is a
|
||||||
|
[much more feature complete fork](https://github.com/MalcolmRobb/dump1090)
|
||||||
|
available, developed by MalcolmRobb.
|
||||||
|
|
||||||
|
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 tries to fix single bit errors using the checksum.
|
||||||
|
Basically the program will try to flip every bit of the message and check if
|
||||||
|
the checksum of the resulting message matches.
|
||||||
|
|
||||||
|
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 trying every two bits combination.
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,5 @@
|
||||||
|
TODO
|
||||||
|
|
||||||
|
* Extract more information from captured Mode S messages.
|
||||||
|
* Improve the web interface gmap.html.
|
||||||
|
* Enhance the algorithm to reliably decode more messages.
|
|
@ -0,0 +1,382 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
return ANET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int anetTcpNoDelay(char *err, int fd)
|
||||||
|
{
|
||||||
|
int yes = 1;
|
||||||
|
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &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, &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, &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, &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) {
|
||||||
|
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, &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;
|
||||||
|
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons(port);
|
||||||
|
if (inet_aton(addr, &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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int anetUnixGenericConnect(char *err, char *path, int flags)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
struct sockaddr_un sa;
|
||||||
|
|
||||||
|
if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR)
|
||||||
|
return ANET_ERR;
|
||||||
|
|
||||||
|
sa.sun_family = AF_LOCAL;
|
||||||
|
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
||||||
|
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 anetUnixConnect(char *err, char *path)
|
||||||
|
{
|
||||||
|
return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int anetUnixNonBlockConnect(char *err, char *path)
|
||||||
|
{
|
||||||
|
return anetUnixGenericConnect(err,path,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) {
|
||||||
|
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) {
|
||||||
|
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(port);
|
||||||
|
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
if (bindaddr && inet_aton(bindaddr, &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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int anetUnixServer(char *err, char *path, mode_t perm)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
struct sockaddr_un sa;
|
||||||
|
|
||||||
|
if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR)
|
||||||
|
return ANET_ERR;
|
||||||
|
|
||||||
|
memset(&sa,0,sizeof(sa));
|
||||||
|
sa.sun_family = AF_LOCAL;
|
||||||
|
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
||||||
|
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR)
|
||||||
|
return ANET_ERR;
|
||||||
|
if (perm)
|
||||||
|
chmod(sa.sun_path, perm);
|
||||||
|
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) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
else {
|
||||||
|
anetSetError(err, "accept: %s", strerror(errno));
|
||||||
|
return ANET_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 anetUnixAccept(char *err, int s) {
|
||||||
|
int fd;
|
||||||
|
struct sockaddr_un sa;
|
||||||
|
socklen_t salen = sizeof(sa);
|
||||||
|
if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR)
|
||||||
|
return ANET_ERR;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/* 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
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,180 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||||
|
<style type="text/css">
|
||||||
|
html { height: 100% }
|
||||||
|
body { height: 100%; margin: 0; padding: 0 }
|
||||||
|
#map_canvas { height: 100% }
|
||||||
|
#info {
|
||||||
|
position: absolute;
|
||||||
|
width:20%;
|
||||||
|
height:100%;
|
||||||
|
bottom:0px;
|
||||||
|
right:0px;
|
||||||
|
top:0px;
|
||||||
|
background-color: white;
|
||||||
|
border-left:1px #666 solid;
|
||||||
|
font-family:Helvetica;
|
||||||
|
}
|
||||||
|
#info div {
|
||||||
|
padding:0px;
|
||||||
|
padding-left:10px;
|
||||||
|
margin:0px;
|
||||||
|
}
|
||||||
|
#info div h1 {
|
||||||
|
margin-top:10px;
|
||||||
|
font-size:16px;
|
||||||
|
}
|
||||||
|
#info div p {
|
||||||
|
font-size:14px;
|
||||||
|
color:#333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript"
|
||||||
|
src="https://maps.googleapis.com/maps/api/js?sensor=true">
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
Map=null;
|
||||||
|
CenterLat=45.0;
|
||||||
|
CenterLon=9.0;
|
||||||
|
Planes={};
|
||||||
|
NumPlanes = 0;
|
||||||
|
Selected=null
|
||||||
|
|
||||||
|
function getIconForPlane(plane) {
|
||||||
|
var r = 255, g = 255, b = 0;
|
||||||
|
var maxalt = 40000; /* Max altitude in the average case */
|
||||||
|
var invalt = maxalt-plane.altitude;
|
||||||
|
var selected = (Selected == plane.hex);
|
||||||
|
|
||||||
|
if (invalt < 0) invalt = 0;
|
||||||
|
b = parseInt(255/maxalt*invalt);
|
||||||
|
return {
|
||||||
|
strokeWeight: (selected ? 2 : 1),
|
||||||
|
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
|
||||||
|
scale: 5,
|
||||||
|
fillColor: 'rgb('+r+','+g+','+b+')',
|
||||||
|
fillOpacity: 0.9,
|
||||||
|
rotation: plane.track
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectPlane() {
|
||||||
|
if (!Planes[this.planehex]) return;
|
||||||
|
var old = Selected;
|
||||||
|
Selected = this.planehex;
|
||||||
|
if (Planes[old]) {
|
||||||
|
/* Remove the highlight in the previously selected plane. */
|
||||||
|
Planes[old].marker.setIcon(getIconForPlane(Planes[old]));
|
||||||
|
}
|
||||||
|
Planes[Selected].marker.setIcon(getIconForPlane(Planes[Selected]));
|
||||||
|
refreshSelectedInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshGeneralInfo() {
|
||||||
|
var i = document.getElementById('geninfo');
|
||||||
|
|
||||||
|
i.innerHTML = NumPlanes+' planes on screen.';
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshSelectedInfo() {
|
||||||
|
var i = document.getElementById('selinfo');
|
||||||
|
var p = Planes[Selected];
|
||||||
|
|
||||||
|
if (!p) return;
|
||||||
|
var html = 'ICAO: '+p.hex+'<br>';
|
||||||
|
if (p.flight.length) {
|
||||||
|
html += '<b>'+p.flight+'</b><br>';
|
||||||
|
}
|
||||||
|
html += 'Altitude: '+p.altitude+' feet<br>';
|
||||||
|
html += 'Speed: '+p.speed+' knots<br>';
|
||||||
|
html += 'Coordinates: '+p.lat+', '+p.lon+'<br>';
|
||||||
|
i.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchData() {
|
||||||
|
$.getJSON('/data.json', function(data) {
|
||||||
|
var stillhere = {}
|
||||||
|
for (var j=0; j < data.length; j++) {
|
||||||
|
var plane = data[j];
|
||||||
|
var marker = null;
|
||||||
|
stillhere[plane.hex] = true;
|
||||||
|
plane.flight = $.trim(plane.flight);
|
||||||
|
|
||||||
|
if (Planes[plane.hex]) {
|
||||||
|
var myplane = Planes[plane.hex];
|
||||||
|
marker = myplane.marker;
|
||||||
|
var icon = marker.getIcon();
|
||||||
|
var newpos = new google.maps.LatLng(plane.lat, plane.lon);
|
||||||
|
marker.setPosition(newpos);
|
||||||
|
marker.setIcon(getIconForPlane(plane));
|
||||||
|
myplane.altitude = plane.altitude;
|
||||||
|
myplane.speed = plane.speed;
|
||||||
|
myplane.lat = plane.lat;
|
||||||
|
myplane.lon = plane.lon;
|
||||||
|
myplane.track = plane.track;
|
||||||
|
myplane.flight = plane.flight;
|
||||||
|
if (myplane.hex == Selected)
|
||||||
|
refreshSelectedInfo();
|
||||||
|
} else {
|
||||||
|
marker = new google.maps.Marker({
|
||||||
|
position: new google.maps.LatLng(plane.lat, plane.lon),
|
||||||
|
map: Map,
|
||||||
|
icon: getIconForPlane(plane)
|
||||||
|
});
|
||||||
|
plane.marker = marker;
|
||||||
|
marker.planehex = plane.hex;
|
||||||
|
Planes[plane.hex] = plane;
|
||||||
|
|
||||||
|
/* Trap clicks for this marker. */
|
||||||
|
google.maps.event.addListener(marker, 'click', selectPlane);
|
||||||
|
}
|
||||||
|
if (plane.flight.length == 0)
|
||||||
|
marker.setTitle(plane.hex)
|
||||||
|
else
|
||||||
|
marker.setTitle(plane.flight+' ('+plane.hex+')')
|
||||||
|
}
|
||||||
|
NumPlanes = data.length;
|
||||||
|
|
||||||
|
/* Remove idle planes. */
|
||||||
|
for (var p in Planes) {
|
||||||
|
if (!stillhere[p]) {
|
||||||
|
Planes[p].marker.setMap(null);
|
||||||
|
delete Planes[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
var mapOptions = {
|
||||||
|
center: new google.maps.LatLng(CenterLat, CenterLon),
|
||||||
|
zoom: 5,
|
||||||
|
mapTypeId: google.maps.MapTypeId.ROADMAP
|
||||||
|
};
|
||||||
|
Map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
|
||||||
|
|
||||||
|
/* Setup our timer to poll from the server. */
|
||||||
|
window.setInterval(function() {
|
||||||
|
fetchData();
|
||||||
|
refreshGeneralInfo();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="initialize()">
|
||||||
|
<div id="map_canvas" style="width:80%; height:100%"></div>
|
||||||
|
<div id="info">
|
||||||
|
<div>
|
||||||
|
<h1>Dump1090</h1>
|
||||||
|
<p id="geninfo"></p>
|
||||||
|
<p id="selinfo">Click on a plane for info.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,193 @@
|
||||||
|
<!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>
|
12
dump978.go
12
dump978.go
|
@ -3,6 +3,18 @@
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package dump978 wraps libdump978, a 978MHz UAT demodulator.
|
// Package dump978 wraps libdump978, a 978MHz UAT demodulator.
|
||||||
|
//
|
||||||
|
// Build example
|
||||||
|
//
|
||||||
|
// dump978.so:
|
||||||
|
// $ gcc -c -O2 -g -Wall -Werror -Ifec -fpic -DBUILD_LIB=1 dump978.c fec.c fec/decode_rs_char.c fec/init_rs_char.c
|
||||||
|
// $ gcc -shared -lm -o ../libdump978.so dump978.o fec.o decode_rs_char.o init_rs_char.o
|
||||||
|
//
|
||||||
|
// dump978 go wrapper:
|
||||||
|
// $ go build -o dump978.a dump978.go dump978_exports.go
|
||||||
|
//
|
||||||
|
// uat_read executable:
|
||||||
|
// $ go build uat_read.go
|
||||||
|
|
||||||
package dump978
|
package dump978
|
||||||
|
|
||||||
|
|
|
@ -3,5 +3,6 @@ driver=rtl871xdrv
|
||||||
ssid=stratux
|
ssid=stratux
|
||||||
hw_mode=g
|
hw_mode=g
|
||||||
channel=1
|
channel=1
|
||||||
wmm_enabled=0
|
wme_enabled=1
|
||||||
|
ieee80211n=1
|
||||||
ignore_broadcast_ssid=0
|
ignore_broadcast_ssid=0
|
||||||
|
|
|
@ -14,12 +14,12 @@ CURIMG=stage4.$IMGFORMAT
|
||||||
|
|
||||||
setup_stratux_bootconfig() {
|
setup_stratux_bootconfig() {
|
||||||
dotask attach_image_to_nbd $CURIMG $NBD_DEV
|
dotask attach_image_to_nbd $CURIMG $NBD_DEV
|
||||||
dotask sudo mkdir -p boot
|
mkdir boot
|
||||||
dotask sudo mount $BOOT_DEV boot
|
dotask sudo mount $BOOT_DEV boot
|
||||||
#usb power
|
#usb power
|
||||||
dotask sudo sh -c 'echo "max_usb_current=1" >>boot/config.txt'
|
echo "max_usb_current=1" >>boot/config.txt
|
||||||
#i2c
|
#i2c
|
||||||
dotask sudo sh -c 'echo "dtparam=i2c1=on" >>boot/config.txt'
|
echo "dtparam=i2c1=on" >>boot/config.txt
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_stratux() {
|
setup_stratux() {
|
||||||
|
@ -32,11 +32,8 @@ echo "**** STRATUX SETUP *****"
|
||||||
apt-get install -y screen
|
apt-get install -y screen
|
||||||
#wifi
|
#wifi
|
||||||
apt-get install -y hostapd isc-dhcp-server
|
apt-get install -y hostapd isc-dhcp-server
|
||||||
#apache for web management scripts
|
#troubleshooting
|
||||||
apt-get install -y apache2 php5 libapache2-mod-php5
|
apt-get install -y tcpdump
|
||||||
rm -rf /var/www/html/*
|
|
||||||
service apache2 start
|
|
||||||
update-rc.d apache2 enable
|
|
||||||
#wifi startup
|
#wifi startup
|
||||||
update-rc.d hostapd enable
|
update-rc.d hostapd enable
|
||||||
update-rc.d isc-dhcp-server enable
|
update-rc.d isc-dhcp-server enable
|
||||||
|
@ -118,6 +115,7 @@ cd /root
|
||||||
rm -rf stratux
|
rm -rf stratux
|
||||||
git clone https://github.com/cyoung/stratux
|
git clone https://github.com/cyoung/stratux
|
||||||
cd stratux
|
cd stratux
|
||||||
|
mkdir -p /var/www
|
||||||
cp -r web/* /var/www/
|
cp -r web/* /var/www/
|
||||||
cd dump978
|
cd dump978
|
||||||
make
|
make
|
||||||
|
@ -155,9 +153,6 @@ echo "**** END STRATUX SETUP *****"
|
||||||
cd $WORKDIR
|
cd $WORKDIR
|
||||||
dotask branch_image ../$OUTDIR/stage3.$IMGFORMAT $CURIMG
|
dotask branch_image ../$OUTDIR/stage3.$IMGFORMAT $CURIMG
|
||||||
|
|
||||||
#needs to be done before qemu is started.
|
|
||||||
|
|
||||||
dotask setup_stratux_bootconfig
|
|
||||||
|
|
||||||
dotask run_qemu $CURIMG
|
dotask run_qemu $CURIMG
|
||||||
dotask mount_apt_cache
|
dotask mount_apt_cache
|
||||||
|
@ -172,4 +167,10 @@ dotask allow_starting_services
|
||||||
dotask update_issue
|
dotask update_issue
|
||||||
dotask fingerprint_debian
|
dotask fingerprint_debian
|
||||||
dotask shutdown_qemu
|
dotask shutdown_qemu
|
||||||
|
|
||||||
|
|
||||||
|
dotask setup_stratux_bootconfig
|
||||||
|
universal_cleanup
|
||||||
|
|
||||||
|
|
||||||
dotask finish_image
|
dotask finish_image
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -17,7 +16,7 @@ import (
|
||||||
const (
|
const (
|
||||||
stratuxVersion = "v0.2"
|
stratuxVersion = "v0.2"
|
||||||
configLocation = "/etc/stratux.conf"
|
configLocation = "/etc/stratux.conf"
|
||||||
managementAddr = "127.0.0.1:9110"
|
managementAddr = ":80"
|
||||||
maxDatagramSize = 8192
|
maxDatagramSize = 8192
|
||||||
UPLINK_BLOCK_DATA_BITS = 576
|
UPLINK_BLOCK_DATA_BITS = 576
|
||||||
UPLINK_BLOCK_BITS = (UPLINK_BLOCK_DATA_BITS + 160)
|
UPLINK_BLOCK_BITS = (UPLINK_BLOCK_DATA_BITS + 160)
|
||||||
|
@ -390,55 +389,6 @@ type status struct {
|
||||||
var globalSettings settings
|
var globalSettings settings
|
||||||
var globalStatus status
|
var globalStatus status
|
||||||
|
|
||||||
func handleManagementConnection(conn net.Conn) {
|
|
||||||
defer conn.Close()
|
|
||||||
rw := bufio.NewReader(conn)
|
|
||||||
for {
|
|
||||||
s, err := rw.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
s = strings.Trim(s, "\r\n")
|
|
||||||
if s == "STATUS" {
|
|
||||||
resp, _ := json.Marshal(&globalStatus)
|
|
||||||
conn.Write(resp)
|
|
||||||
} else if s == "SETTINGS" {
|
|
||||||
resp, _ := json.Marshal(&globalSettings)
|
|
||||||
conn.Write(resp)
|
|
||||||
} else if s == "QUIT" {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
// Assume settings.
|
|
||||||
//TODO: Make this so that there is some positive way of doing this versus assuming that everything other than commands above are settings.
|
|
||||||
var newSettings settings
|
|
||||||
err := json.Unmarshal([]byte(s), &newSettings)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("%s - error: %s\n", s, err.Error())
|
|
||||||
} else {
|
|
||||||
log.Printf("new settings: %s\n", s)
|
|
||||||
globalSettings = newSettings
|
|
||||||
saveSettings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func managementInterface() {
|
|
||||||
ln, err := net.Listen("tcp", managementAddr)
|
|
||||||
if err != nil { //TODO
|
|
||||||
log.Printf("couldn't open management port: %s\n", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer ln.Close()
|
|
||||||
for {
|
|
||||||
conn, err := ln.Accept()
|
|
||||||
if err != nil { //TODO
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
go handleManagementConnection(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultSettings() {
|
func defaultSettings() {
|
||||||
globalSettings.UAT_Enabled = true //TODO
|
globalSettings.UAT_Enabled = true //TODO
|
||||||
globalSettings.ES_Enabled = false //TODO
|
globalSettings.ES_Enabled = false //TODO
|
|
@ -0,0 +1,78 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"golang.org/x/net/websocket"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SettingMessage struct {
|
||||||
|
Setting string `json:"setting"`
|
||||||
|
Value bool `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func statusSender(conn *websocket.Conn) {
|
||||||
|
timer := time.NewTicker(1 * time.Second)
|
||||||
|
for {
|
||||||
|
<-timer.C
|
||||||
|
|
||||||
|
statResp, _ := json.Marshal(&globalStatus)
|
||||||
|
conn.Write(statResp)
|
||||||
|
|
||||||
|
settingResp, _ := json.Marshal(&globalSettings)
|
||||||
|
_, err := conn.Write(settingResp)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Web client disconnected.\n")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleManagementConnection(conn *websocket.Conn) {
|
||||||
|
go statusSender(conn)
|
||||||
|
|
||||||
|
for {
|
||||||
|
var msg SettingMessage
|
||||||
|
err := websocket.JSON.Receive(conn, &msg)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
log.Printf("handleManagementConnection: %s\n", err.Error())
|
||||||
|
} else {
|
||||||
|
if msg.Setting == "UAT_Enabled" {
|
||||||
|
globalSettings.UAT_Enabled = msg.Value
|
||||||
|
}
|
||||||
|
if msg.Setting == "ES_Enabled" {
|
||||||
|
globalSettings.ES_Enabled = msg.Value
|
||||||
|
}
|
||||||
|
if msg.Setting == "GPS_Enabled" {
|
||||||
|
globalSettings.GPS_Enabled = msg.Value
|
||||||
|
}
|
||||||
|
if msg.Setting == "AHRS_Enabled" {
|
||||||
|
globalSettings.AHRS_Enabled = msg.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func managementInterface() {
|
||||||
|
http.Handle("/", http.FileServer(http.Dir("/var/www")))
|
||||||
|
http.HandleFunc("/control",
|
||||||
|
func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
s := websocket.Server{
|
||||||
|
Handler: websocket.Handler(handleManagementConnection)}
|
||||||
|
s.ServeHTTP(w, req)
|
||||||
|
})
|
||||||
|
|
||||||
|
err := http.ListenAndServe(managementAddr, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("managementInterface ListenAndServe: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/kidoman/embd/sensor/bmp180"
|
"github.com/kidoman/embd/sensor/bmp180"
|
||||||
"github.com/tarm/serial"
|
"github.com/tarm/serial"
|
||||||
|
|
||||||
"./mpu6050"
|
"../mpu6050"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SituationData struct {
|
type SituationData struct {
|
|
@ -344,7 +344,7 @@ func esListen() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rdr := bufio.NewReader(inConn)
|
rdr := bufio.NewReader(inConn)
|
||||||
for {
|
for globalSettings.ES_Enabled {
|
||||||
buf, err := rdr.ReadString('\n')
|
buf, err := rdr.ReadString('\n')
|
||||||
if err != nil { // Must have disconnected?
|
if err != nil { // Must have disconnected?
|
||||||
break
|
break
|
|
@ -1,3 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
screen -S stratux -d -m /usr/bin/start_uat
|
screen -S stratux -d -m /usr/bin/start_uat
|
||||||
|
screen -S dump1090 -d -m /usr/bin/dump1090 --net --device-index 1
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
/usr/local/bin/rtl_sdr -f 978000000 -s 2083334 -g 48 - | /usr/bin/dump978 | /usr/bin/gen_gdl90
|
/usr/local/bin/rtl_sdr -f 978000000 -s 2083334 -g 48 -d 0 - | /usr/bin/dump978 | /usr/bin/gen_gdl90
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"./mpu6050"
|
"../mpu6050"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kidoman/embd"
|
"github.com/kidoman/embd"
|
||||||
_ "github.com/kidoman/embd/host/all"
|
_ "github.com/kidoman/embd/host/all"
|
|
@ -1,64 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
function _str_send($sock, $str) {
|
|
||||||
return socket_send($sock, $str, strlen($str), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_json($tp) {
|
|
||||||
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
|
||||||
if (!socket_connect($sock, '127.0.0.1', '9110')) {
|
|
||||||
throw new Exception("couldn't connect");
|
|
||||||
}
|
|
||||||
|
|
||||||
_str_send($sock, $tp);
|
|
||||||
|
|
||||||
$buf = "";
|
|
||||||
socket_recv($sock, $buf, 1024, 0);
|
|
||||||
|
|
||||||
$x = json_decode($buf, true);
|
|
||||||
|
|
||||||
socket_close($sock);
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_settings() {
|
|
||||||
return get_json("SETTINGS\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_status() {
|
|
||||||
return get_json("STATUS\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_settings($to_set) {
|
|
||||||
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
|
||||||
if (!socket_connect($sock, '127.0.0.1', '9110')) {
|
|
||||||
print "couldn't connect\n";
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$buf = json_encode($to_set);
|
|
||||||
_str_send($sock, $buf . "\n");
|
|
||||||
_str_send($sock, "QUIT\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
$current_settings = get_settings();
|
|
||||||
|
|
||||||
// Copy over old settings to the new ones, such that if there is a field that doesn't change it gets sent over again.
|
|
||||||
if (isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'POST')) {
|
|
||||||
$new_settings = $current_settings;
|
|
||||||
foreach ($_POST as $k => $v) {
|
|
||||||
if ($v === "true") $v = true;
|
|
||||||
else if ($v === "false") $v = false;
|
|
||||||
$new_settings[$k] = $v; //FIXME.
|
|
||||||
}
|
|
||||||
set_settings($new_settings);
|
|
||||||
$current_settings = get_settings();
|
|
||||||
}
|
|
||||||
|
|
||||||
$current_status = get_status();
|
|
||||||
|
|
||||||
$p = array_merge($current_settings, $current_status);
|
|
||||||
|
|
||||||
print json_encode($p) . "\n";
|
|
||||||
?>
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,45 @@
|
||||||
|
.onoffswitch {
|
||||||
|
position: relative; width: 68px;
|
||||||
|
-webkit-user-select:none; -moz-user-select:none; -ms-user-select: none;
|
||||||
|
}
|
||||||
|
.onoffswitch-checkbox {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.onoffswitch-label {
|
||||||
|
display: block; overflow: hidden; cursor: pointer;
|
||||||
|
border: 2px solid #ddd; border-radius: 20px;
|
||||||
|
}
|
||||||
|
.onoffswitch-inner {
|
||||||
|
display: block; width: 200%; margin-left: -100%;
|
||||||
|
transition: margin 0.3s ease-in 0s;
|
||||||
|
}
|
||||||
|
.onoffswitch-inner:before, .onoffswitch-inner:after {
|
||||||
|
display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 30px;
|
||||||
|
font-size: 14px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.onoffswitch-inner:before {
|
||||||
|
content: "ON";
|
||||||
|
padding-left: 10px;
|
||||||
|
background-color: #337AB7; color: #FFFFFF;
|
||||||
|
}
|
||||||
|
.onoffswitch-inner:after {
|
||||||
|
content: "OFF";
|
||||||
|
padding-right: 10px;
|
||||||
|
background-color: #F5F5F5; color: #DDDDDD;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.onoffswitch-switch {
|
||||||
|
display: block; width: 18px; margin: 6px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
position: absolute; top: 0; bottom: 0;
|
||||||
|
right: 34px;
|
||||||
|
border: 2px solid #ddd; border-radius: 20px;
|
||||||
|
transition: all 0.3s ease-in 0s;
|
||||||
|
}
|
||||||
|
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
|
||||||
|
right: 0px;
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
@import url(bootstrap.min.css);
|
||||||
|
@import url(onoff.css);
|
||||||
|
|
||||||
|
body
|
||||||
|
{
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stratux-main
|
||||||
|
{
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: #ddd;
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
box-shadow: none;
|
||||||
|
margin: auto;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#RY835AI_connected-container
|
||||||
|
{
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.led-red
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #940;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: #000 0 -1px 7px 1px, inset #600 0 -1px 9px, #F00 0 2px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.led-green
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #00FF00;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: #000 0 -1px 7px 1px, inset #460 0 -1px 9px, #7D0 0 2px 12px;
|
||||||
|
}
|
166
web/index.html
166
web/index.html
|
@ -6,65 +6,117 @@
|
||||||
</title>
|
</title>
|
||||||
<script src="js/jquery-2.1.4.min.js" type="text/javascript"></script>
|
<script src="js/jquery-2.1.4.min.js" type="text/javascript"></script>
|
||||||
<script src="js/jquery.form.min.js" type="text/javascript"></script>
|
<script src="js/jquery.form.min.js" type="text/javascript"></script>
|
||||||
|
<script src="js/stratux.js" type="text/javascript"></script>
|
||||||
|
<link href="css/stratux.css" rel="stylesheet" media="screen">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="text/javascript">
|
<div id="stratux-main" class="well well-large">
|
||||||
$(document).ready(function() {
|
<h1>Stratux</h1>
|
||||||
$('input[name=UAT_Enabled]').change(function(){
|
|
||||||
$('#settings').ajaxSubmit({url: 'control.php', type: 'post'})
|
|
||||||
});
|
|
||||||
$('input[name=ES_Enabled]').change(function(){
|
|
||||||
$('#settings').ajaxSubmit({url: 'control.php', type: 'post'})
|
|
||||||
});
|
|
||||||
$('input[name=GPS_Enabled]').change(function(){
|
|
||||||
$('#settings').ajaxSubmit({url: 'control.php', type: 'post'})
|
|
||||||
});
|
|
||||||
$('input[name=AHRS_Enabled]').change(function(){
|
|
||||||
$('#settings').ajaxSubmit({url: 'control.php', type: 'post'})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
(function worker() {
|
|
||||||
$.ajax({
|
|
||||||
url: 'control.php',
|
|
||||||
success: function(data) {
|
|
||||||
obj = $.parseJSON(data);
|
|
||||||
$.each(obj, function(k, v) {
|
|
||||||
// Radio values.
|
|
||||||
if ((k == "UAT_Enabled") || (k == "ES_Enabled") || (k == "GPS_Enabled") || (k == "AHRS_Enabled")) {
|
|
||||||
$('[name=' + k + ']').val([v.toString()]);
|
|
||||||
}
|
|
||||||
$('#' + k).text(v);
|
|
||||||
});
|
|
||||||
|
|
||||||
},
|
<div class="panel panel-default">
|
||||||
complete: function() {
|
<div class="panel-heading">Status <span id="connectedLabel" class="label label-warning">Disconnected</span></div>
|
||||||
// Schedule the next request when the current one is complete.
|
<div class="panel-body">
|
||||||
setTimeout(worker, 1000);
|
<div class="form-horizontal">
|
||||||
}
|
<!--
|
||||||
});
|
<div class="row">
|
||||||
})();
|
<label class="col-sm-6">RTL-SDR devices:</label>
|
||||||
|
<span id="Devices" class="col-sm-2">--</span>
|
||||||
</script>
|
</div>
|
||||||
Status:<br>
|
-->
|
||||||
RTL-SDR devices: <span id="Devices">1</span><br>
|
<div class="row">
|
||||||
Clients connected: <span id="Connected_Users">2</span><br>
|
<label class="col-sm-6">Clients connected:</label>
|
||||||
Current firmware: <span id="Version">v0.1</span><br>
|
<span id="Connected_Users" class="col-sm-5">--</span>
|
||||||
UAT msgs: <span id="UAT_messages_last_minute"></span> / <span id="UAT_messages_max"></span><br>
|
</div>
|
||||||
1090ES msgs: <span id="ES_messages_last_minute"></span> / <span id="ES_messages_max"></span><br>
|
<div class="row">
|
||||||
GPS satellites: <span id="GPS_satellites_locked"></span><br>
|
<label class="col-sm-6">Current firmware:</label>
|
||||||
AHRS: <span id="RY835AI_connected"></span><br>
|
<span id="Version" class="col-sm-6">--</span>
|
||||||
Uptime: <span id="Uptime"></span><br>
|
</div>
|
||||||
<br>
|
<div class="row">
|
||||||
<form id="settings">
|
<label class="col-sm-6">UAT msgs:</label>
|
||||||
Set:<br>
|
<div class="col-sm-6">
|
||||||
978MHz <input type="radio" name="UAT_Enabled" value="true">On
|
<span id="UAT_messages_last_minute">--</span>
|
||||||
<input type="radio" name="UAT_Enabled" value="false">Off<br>
|
/
|
||||||
1090MHz <input type="radio" name="ES_Enabled" value="true">On
|
<span id="UAT_messages_max">--</span>
|
||||||
<input type="radio" name="ES_Enabled" value="false">Off<br>
|
</div>
|
||||||
GPS <input type="radio" name="GPS_Enabled" value="true">On
|
</div>
|
||||||
<input type="radio" name="GPS_Enabled" value="false">Off<br>
|
<div class="row">
|
||||||
AHRS <input type="radio" name="AHRS_Enabled" value="true">On
|
<label class="col-sm-6">1090ES msgs:</label>
|
||||||
<input type="radio" name="AHRS_Enabled" value="false">Off<br>
|
<div class="col-sm-6">
|
||||||
</form>
|
<span id="ES_messages_last_minute">--</span>
|
||||||
|
/
|
||||||
|
<span id="ES_messages_max">--</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-sm-6">GPS satellites:</label>
|
||||||
|
<span id="GPS_satellites_locked" class="col-sm-6">--</span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-sm-6">AHRS:</label>
|
||||||
|
<div id="RY835AI_connected-container" class="col-sm-6">
|
||||||
|
<div id="RY835AI_connected" class="led-red"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label class="col-sm-6">Uptime:</label>
|
||||||
|
<span id="Uptime" class="col-sm-6">--</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Settings</div>
|
||||||
|
<div class="panel-body form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-4">978MHz</label>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<div class="onoffswitch">
|
||||||
|
<input type="checkbox" name="UAT_Enabled" class="onoffswitch-checkbox" id="uat-onoffswitch" checked>
|
||||||
|
<label class="onoffswitch-label" for="uat-onoffswitch">
|
||||||
|
<span class="onoffswitch-inner"></span>
|
||||||
|
<span class="onoffswitch-switch"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-4">1090MHz</label>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<div class="onoffswitch">
|
||||||
|
<input type="checkbox" name="ES_Enabled" class="onoffswitch-checkbox" id="es-onoffswitch" checked>
|
||||||
|
<label class="onoffswitch-label" for="es-onoffswitch">
|
||||||
|
<span class="onoffswitch-inner"></span>
|
||||||
|
<span class="onoffswitch-switch"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-4">GPS</label>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<div class="onoffswitch">
|
||||||
|
<input type="checkbox" name="GPS_Enabled" class="onoffswitch-checkbox" id="gps-onoffswitch" checked>
|
||||||
|
<label class="onoffswitch-label" for="gps-onoffswitch">
|
||||||
|
<span class="onoffswitch-inner"></span>
|
||||||
|
<span class="onoffswitch-switch"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-4">AHRS</label>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<div class="onoffswitch">
|
||||||
|
<input type="checkbox" name="AHRS_Enabled" class="onoffswitch-checkbox" id="ahrs-onoffswitch" checked>
|
||||||
|
<label class="onoffswitch-label" for="ahrs-onoffswitch">
|
||||||
|
<span class="onoffswitch-inner"></span>
|
||||||
|
<span class="onoffswitch-switch"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
var socket;
|
||||||
|
|
||||||
|
function setLEDstatus (ledElement, status) {
|
||||||
|
if(status) {
|
||||||
|
ledElement.removeClass('led-red');
|
||||||
|
ledElement.addClass('led-green');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ledElement.removeClass('led-green');
|
||||||
|
ledElement.addClass('led-red');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setConnectedClass (cssClass) {
|
||||||
|
$('#connectedLabel').removeClass('label-success');
|
||||||
|
$('#connectedLabel').removeClass('label-warning');
|
||||||
|
$('#connectedLabel').removeClass('label-danger');
|
||||||
|
$('#connectedLabel').addClass( cssClass );
|
||||||
|
|
||||||
|
if(cssClass == 'label-success')
|
||||||
|
$('#connectedLabel').text('Connected');
|
||||||
|
else
|
||||||
|
$('#connectedLabel').text('Disconnected');
|
||||||
|
}
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
socket = new WebSocket('ws://' + window.location.hostname + '/control');
|
||||||
|
|
||||||
|
socket.onopen = function(msg) {
|
||||||
|
setConnectedClass('label-success');
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onclose = function(msg) {
|
||||||
|
setConnectedClass('label-danger');
|
||||||
|
setTimeout(connect,1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onerror = function(msg) {
|
||||||
|
setConnectedClass('label-danger');
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onmessage = function(msg) {
|
||||||
|
console.log('Received status update.')
|
||||||
|
|
||||||
|
var status = JSON.parse(msg.data)
|
||||||
|
|
||||||
|
// Update Status
|
||||||
|
$('#Version').text(status.Version);
|
||||||
|
$('#Devices').text(status.Devices);
|
||||||
|
$('#Connected_Users').text(status.Connected_Users);
|
||||||
|
$('#UAT_messages_last_minute').text(status.UAT_messages_last_minute);
|
||||||
|
$('#UAT_messages_max').text(status.UAT_messages_max);
|
||||||
|
$('#ES_messages_last_minute').text(status.ES_messages_last_minute);
|
||||||
|
$('#ES_messages_max').text(status.ES_messages_max);
|
||||||
|
$('#GPS_satellites_locked').text(status.GPS_satellites_locked);
|
||||||
|
setLEDstatus($('#RY835AI_connected'), status.RY835AI_connected);
|
||||||
|
$('#Uptime').text(status.Uptime);
|
||||||
|
|
||||||
|
// Update Settings
|
||||||
|
$('input[name=UAT_Enabled]').prop('checked', status.UAT_Enabled);
|
||||||
|
$('input[name=ES_Enabled]').prop('checked', status.ES_Enabled);
|
||||||
|
$('input[name=GPS_Enabled]').prop('checked', status.GPS_Enabled);
|
||||||
|
$('input[name=AHRS_Enabled]').prop('checked', status.AHRS_Enabled);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
connect();
|
||||||
|
|
||||||
|
$('input[name=UAT_Enabled]').click(function () {
|
||||||
|
console.log('UAT_Enabled clicked');
|
||||||
|
|
||||||
|
msg = {setting: 'UAT_Enabled', state: $('input[name=UAT_Enabled]').prop('checked') };
|
||||||
|
socket.send(JSON.stringify(msg));
|
||||||
|
});
|
||||||
|
|
||||||
|
$('input[name=ES_Enabled]').click(function () {
|
||||||
|
console.log('ES_Enabled clicked');
|
||||||
|
|
||||||
|
msg = {setting: 'ES_Enabled', state: $('input[name=ES_Enabled]').prop('checked') };
|
||||||
|
socket.send(JSON.stringify(msg));
|
||||||
|
});
|
||||||
|
|
||||||
|
$('input[name=GPS_Enabled]').click(function () {
|
||||||
|
console.log('GPS_Enabled clicked');
|
||||||
|
|
||||||
|
msg = {setting: 'GPS_Enabled', state: $('input[name=GPS_Enabled]').prop('checked') };
|
||||||
|
socket.send(JSON.stringify(msg));
|
||||||
|
});
|
||||||
|
|
||||||
|
$('input[name=AHRS_Enabled]').click(function () {
|
||||||
|
console.log('AHRS_Enabled clicked');
|
||||||
|
|
||||||
|
msg = {setting: 'AHRS_Enabled', state: $('input[name=AHRS_Enabled]').prop('checked') };
|
||||||
|
socket.send(JSON.stringify(msg));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Ładowanie…
Reference in New Issue