Compare commits
63 commits
Author | SHA1 | Date | |
---|---|---|---|
|
26a610c1a3 | ||
|
1922324b40 | ||
![]() |
d1f53019b2 | ||
![]() |
36a398814e | ||
![]() |
ec055e1220 | ||
![]() |
94930dde48 | ||
![]() |
43d4edebee | ||
![]() |
69081b84c6 | ||
![]() |
efe2687c10 | ||
![]() |
319d910e8d | ||
![]() |
721b9a4a17 | ||
![]() |
383081d0de | ||
![]() |
2c484dc5fd | ||
![]() |
74b1ae1c36 | ||
![]() |
c3bfbe4ff0 | ||
![]() |
6dbdaca8d0 | ||
![]() |
f50cfc3c6e | ||
![]() |
56a4f252d6 | ||
![]() |
e9372475c9 | ||
![]() |
d29a1e04e4 | ||
![]() |
e3563f49e7 | ||
![]() |
532d174362 | ||
![]() |
4b976a273b | ||
![]() |
ddb9019194 | ||
![]() |
3762585202 | ||
![]() |
831f4f672a | ||
![]() |
dc4c63aeff | ||
![]() |
3fdc00b49d | ||
![]() |
cdb10b3d00 | ||
![]() |
85ded9d60c | ||
![]() |
9c46dcbd58 | ||
![]() |
80e776d470 | ||
![]() |
ab1dce1d15 | ||
![]() |
9c284a8460 | ||
![]() |
f953b46f8e | ||
![]() |
1115280ba4 | ||
![]() |
352a06c80c | ||
![]() |
5afa90b2dc | ||
![]() |
a580fbbba1 | ||
![]() |
ac5e777d78 | ||
![]() |
5d6aa0e398 | ||
![]() |
40f8edca66 | ||
![]() |
6b219b43e7 | ||
![]() |
14f0e15c76 | ||
![]() |
e8cf66ebb9 | ||
![]() |
eaf6bc70a8 | ||
![]() |
83f2d6aa30 | ||
![]() |
e198fffbd3 | ||
![]() |
626a8fafbd | ||
![]() |
92d4bf928e | ||
![]() |
df2ea21cde | ||
![]() |
6caca48666 | ||
![]() |
cd1ce278c1 | ||
![]() |
b8dd741165 | ||
![]() |
def7ee20e1 | ||
![]() |
bc88c0ba5d | ||
![]() |
c45c788cae | ||
![]() |
ca8610758f | ||
![]() |
2f8ce3447d | ||
![]() |
5234e9aa12 | ||
![]() |
01bf0bc396 | ||
![]() |
dba4babbd2 | ||
![]() |
bf3e7d42d7 |
25
.gitignore
vendored
25
.gitignore
vendored
|
@ -2,9 +2,28 @@
|
|||
*.bin
|
||||
*.svg
|
||||
|
||||
view1090
|
||||
map1090
|
||||
|
||||
*.swp
|
||||
.~
|
||||
thumbs.db
|
||||
|
||||
*.shp
|
||||
*.shp*
|
||||
*.shx*
|
||||
*.shx
|
||||
*.geojson
|
||||
|
||||
*.cpg
|
||||
*.dbf
|
||||
*.zip*
|
||||
*.prj
|
||||
*.xml
|
||||
*.html
|
||||
*.txt
|
||||
|
||||
airportnames
|
||||
mapnames
|
||||
|
||||
|
||||
viz1090
|
||||
|
||||
result
|
||||
|
|
48
Aircraft.cpp
48
Aircraft.cpp
|
@ -1,21 +1,47 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 "Aircraft.h"
|
||||
#include "AircraftLabel.h"
|
||||
|
||||
Aircraft::Aircraft(uint32_t addr) {
|
||||
this->addr = addr;
|
||||
created = 0;
|
||||
prev_seen = 0;
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
cx = 0;
|
||||
cy = 0;
|
||||
lon = 0;
|
||||
lat = 0;
|
||||
|
||||
ox = 0;
|
||||
oy = 0;
|
||||
dox = 0;
|
||||
doy = 0;
|
||||
ddox = 0;
|
||||
ddoy = 0;
|
||||
label = NULL;
|
||||
|
||||
next = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
|
63
Aircraft.h
63
Aircraft.h
|
@ -1,14 +1,48 @@
|
|||
#include <stdint.h>
|
||||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
class AircraftLabel;
|
||||
|
||||
class Aircraft {
|
||||
public:
|
||||
uint32_t addr; // ICAO address
|
||||
char flight[16]; // Flight number
|
||||
unsigned char signalLevel[8]; // Last 8 Signal Amplitudes
|
||||
double messageRate;
|
||||
float messageRate;
|
||||
int altitude; // Altitude
|
||||
int speed; // Velocity
|
||||
int track; // Angle of flight
|
||||
|
@ -16,29 +50,36 @@ public:
|
|||
time_t seen; // Time at which the last packet was received
|
||||
time_t seenLatLon; // Time at which the last packet was received
|
||||
time_t prev_seen;
|
||||
double lat, lon; // Coordinated obtained from CPR encoded data
|
||||
float lat, lon; // Coordinated obtained from CPR encoded data
|
||||
|
||||
//history
|
||||
|
||||
std::list <float> lonHistory, latHistory, headingHistory, timestampHistory;
|
||||
std::vector <float> lonHistory, latHistory, headingHistory;
|
||||
std::vector <std::chrono::high_resolution_clock::time_point> timestampHistory;
|
||||
|
||||
|
||||
AircraftLabel *label;
|
||||
|
||||
// float oldLon[TRAIL_LENGTH];
|
||||
// float oldLat[TRAIL_LENGTH];
|
||||
// float oldHeading[TRAIL_LENGTH];
|
||||
// time_t oldSeen[TRAIL_LENGTH];
|
||||
// uint8_t oldIdx;
|
||||
uint64_t created;
|
||||
uint64_t msSeen;
|
||||
uint64_t msSeenLatLon;
|
||||
std::chrono::high_resolution_clock::time_point created;
|
||||
std::chrono::high_resolution_clock::time_point msSeen;
|
||||
std::chrono::high_resolution_clock::time_point msSeenLatLon;
|
||||
int live;
|
||||
|
||||
struct Aircraft *next; // Next aircraft in our linked list
|
||||
|
||||
//// label stuff -> should go to aircraft icon class
|
||||
|
||||
int x, y, cx, cy, w, h;
|
||||
float ox, oy, dox, doy, ddox, ddoy;
|
||||
float pressure;
|
||||
// int x, y, cx, cy;
|
||||
int x, y;
|
||||
// float w, h, target_w, target_h;
|
||||
// float ox, oy, dox, doy, ddox, ddoy;
|
||||
// float labelLevel;
|
||||
// float opacity, target_opacity;
|
||||
|
||||
/// methods
|
||||
|
||||
|
|
538
AircraftLabel.cpp
Normal file
538
AircraftLabel.cpp
Normal file
|
@ -0,0 +1,538 @@
|
|||
#include "AircraftLabel.h"
|
||||
#include "Aircraft.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "SDL2/SDL2_gfxPrimitives.h"
|
||||
|
||||
using fmilliseconds = std::chrono::duration<float, std::milli>;
|
||||
|
||||
static std::chrono::high_resolution_clock::time_point now() {
|
||||
return std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
static float elapsed(std::chrono::high_resolution_clock::time_point ref) {
|
||||
return (fmilliseconds {now() - ref}).count();
|
||||
}
|
||||
|
||||
static float sign(float x) {
|
||||
return (x > 0) - (x < 0);
|
||||
}
|
||||
|
||||
SDL_Rect AircraftLabel::getFullRect(int labelLevel) {
|
||||
SDL_Rect rect = {static_cast<int>(x),static_cast<int>(y),0,0};
|
||||
|
||||
SDL_Rect currentRect;
|
||||
|
||||
if(labelLevel < 2) {
|
||||
currentRect = speedLabel.getRect();
|
||||
|
||||
rect.w = std::max(rect.w,currentRect.w);
|
||||
rect.h += currentRect.h;
|
||||
}
|
||||
|
||||
if(labelLevel < 1) {
|
||||
currentRect = altitudeLabel.getRect();
|
||||
|
||||
rect.w = std::max(rect.w,currentRect.w);
|
||||
rect.h += currentRect.h;
|
||||
|
||||
currentRect = speedLabel.getRect();
|
||||
|
||||
rect.w = std::max(rect.w,currentRect.w);
|
||||
rect.h += currentRect.h;
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
void AircraftLabel::update() {
|
||||
char flight[17] = "";
|
||||
snprintf(flight,17," %s", p->flight);
|
||||
|
||||
|
||||
std::string flightString = flight;
|
||||
flightString.erase(std::remove_if(flightString.begin(), flightString.end(), isspace), flightString.end());
|
||||
|
||||
flightLabel.setText(flightString);
|
||||
|
||||
char alt[10] = "";
|
||||
if (metric) {
|
||||
snprintf(alt,10," %dm", static_cast<int>(p->altitude / 3.2828));
|
||||
} else {
|
||||
snprintf(alt,10," %d'", p->altitude);
|
||||
}
|
||||
|
||||
altitudeLabel.setText(alt);
|
||||
|
||||
char speed[10] = "";
|
||||
if (metric) {
|
||||
snprintf(speed,10," %dkm/h", static_cast<int>(p->speed * 1.852));
|
||||
} else {
|
||||
snprintf(speed,10," %dmph", p->speed);
|
||||
}
|
||||
|
||||
speedLabel.setText(speed);
|
||||
}
|
||||
|
||||
void AircraftLabel::clearAcceleration() {
|
||||
ddx = 0;
|
||||
ddy = 0;
|
||||
}
|
||||
|
||||
float AircraftLabel::calculateDensity(Aircraft *check_p, int labelLevel) {
|
||||
float density_max = 0;
|
||||
|
||||
while(check_p) {
|
||||
if(check_p->addr == p->addr) {
|
||||
check_p = check_p->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!check_p->label) {
|
||||
check_p = check_p->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(check_p->label->x + check_p->label->w < 0) {
|
||||
check_p = check_p->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(check_p->label->y + check_p->label->h < 0) {
|
||||
check_p = check_p->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(check_p->label->x > screen_width) {
|
||||
check_p = check_p->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(check_p->label->y > screen_height) {
|
||||
check_p = check_p->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
SDL_Rect currentRect = getFullRect(labelLevel);
|
||||
|
||||
float width_proportion = (currentRect.w + check_p->label->w) / fabs(x - check_p->label->x);
|
||||
float height_proportion = (currentRect.h + check_p->label->h) / fabs(y - check_p->label->y);
|
||||
|
||||
float density = width_proportion * height_proportion;
|
||||
|
||||
if(density > density_max) {
|
||||
density_max = density;
|
||||
}
|
||||
|
||||
check_p = check_p -> next;
|
||||
}
|
||||
|
||||
return density_max;
|
||||
}
|
||||
|
||||
void AircraftLabel::calculateForces(Aircraft *check_p) {
|
||||
if(w == 0 || h == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Aircraft *head = check_p;
|
||||
|
||||
int p_left = x;
|
||||
int p_right = x + w;
|
||||
int p_top = y;
|
||||
int p_bottom = y + h;
|
||||
|
||||
|
||||
float boxmid_x = static_cast<float>(p_left + p_right) / 2.0f;
|
||||
float boxmid_y = static_cast<float>(p_top + p_bottom) / 2.0f;
|
||||
|
||||
float offset_x = boxmid_x - p->x;
|
||||
float offset_y = boxmid_y - p->y;
|
||||
|
||||
float target_length_x = attachment_dist + w / 2.0f;
|
||||
float target_length_y = attachment_dist + h / 2.0f;
|
||||
|
||||
// stay icon_dist away from own icon
|
||||
|
||||
ddx -= sign(offset_x) * attachment_force * (fabs(offset_x) - target_length_x);
|
||||
ddy -= sign(offset_y) * attachment_force * (fabs(offset_y) - target_length_y);
|
||||
|
||||
|
||||
// screen edge
|
||||
|
||||
if(p_left < edge_margin) {
|
||||
ddx += boundary_force * static_cast<float>(edge_margin - p_left);
|
||||
}
|
||||
|
||||
if(p_right > screen_width - edge_margin) {
|
||||
ddx += boundary_force * static_cast<float>(screen_width - edge_margin - p_right);
|
||||
}
|
||||
|
||||
if(p_top < edge_margin) {
|
||||
ddy += boundary_force * static_cast<float>(edge_margin - p_top);
|
||||
}
|
||||
|
||||
if(p_bottom > screen_height - edge_margin) {
|
||||
ddy += boundary_force * static_cast<float>(screen_height - edge_margin - p_bottom);
|
||||
}
|
||||
|
||||
|
||||
float all_x = 0;
|
||||
float all_y = 0;
|
||||
int count = 0;
|
||||
//check against other labels
|
||||
|
||||
while(check_p) {
|
||||
if(check_p->addr == p->addr) {
|
||||
check_p = check_p->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!check_p->label) {
|
||||
check_p = check_p->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
int check_left = check_p->label->x;
|
||||
int check_right = check_p->label->x + check_p->label->w;
|
||||
int check_top = check_p->label->y;
|
||||
int check_bottom = check_p->label->y + check_p->label->h;
|
||||
|
||||
float icon_x = static_cast<float>(check_p->x);
|
||||
float icon_y = static_cast<float>(check_p->y);
|
||||
|
||||
float checkboxmid_x = static_cast<float>(check_left + check_right) / 2.0f;
|
||||
float checkboxmid_y = static_cast<float>(check_top + check_bottom) / 2.0f;
|
||||
|
||||
float offset_x = boxmid_x - checkboxmid_x;
|
||||
float offset_y = boxmid_y - checkboxmid_y;
|
||||
|
||||
float target_length_x = label_dist + static_cast<float>(check_p->label->w + w) / 2.0f;
|
||||
float target_length_y = label_dist + static_cast<float>(check_p->label->h + h) / 2.0f;
|
||||
|
||||
float x_mag = std::max(0.0f,(target_length_x - fabs(offset_x)));
|
||||
float y_mag = std::max(0.0f,(target_length_y - fabs(offset_y)));
|
||||
|
||||
// stay at least label_dist away from other icons
|
||||
|
||||
if(x_mag > 0 && y_mag > 0) {
|
||||
ddx += sign(offset_x) * label_force * x_mag;
|
||||
ddy += sign(offset_y) * label_force * y_mag;
|
||||
}
|
||||
|
||||
// stay at least icon_dist away from other icons
|
||||
|
||||
offset_x = boxmid_x - check_p->x;
|
||||
offset_y = boxmid_y - check_p->y;
|
||||
|
||||
target_length_x = icon_dist + static_cast<float>(check_p->label->w) / 2.0f;
|
||||
target_length_y = icon_dist + static_cast<float>(check_p->label->h) / 2.0f;
|
||||
|
||||
x_mag = std::max(0.0f,(target_length_x - fabs(offset_x)));
|
||||
y_mag = std::max(0.0f,(target_length_y - fabs(offset_y)));
|
||||
|
||||
if(x_mag > 0 && y_mag > 0) {
|
||||
ddx += sign(offset_x) * icon_force * x_mag;
|
||||
ddy += sign(offset_y) * icon_force * y_mag;
|
||||
}
|
||||
|
||||
all_x += sign(boxmid_x - checkboxmid_x);
|
||||
all_y += sign(boxmid_y - checkboxmid_y);
|
||||
|
||||
count++;
|
||||
|
||||
check_p = check_p -> next;
|
||||
}
|
||||
|
||||
// move away from others
|
||||
ddx += density_force * all_x / count;
|
||||
ddy += density_force * all_y / count;
|
||||
|
||||
// char buff[100];
|
||||
// snprintf(buff, sizeof(buff), "%2.2f", labelLevel);
|
||||
// debugLabel.setText(buff);
|
||||
|
||||
float density_mult = 0.5f;
|
||||
float level_rate = 0.25f;
|
||||
|
||||
if(elapsed(lastLevelChange) > 1000.0) {
|
||||
if(labelLevel < 0.8f * density_mult * calculateDensity(head, labelLevel + 1)) {
|
||||
if(labelLevel <= 2) {
|
||||
labelLevel += level_rate;
|
||||
lastLevelChange = now();
|
||||
}
|
||||
} else if (labelLevel > 1.2f * density_mult * calculateDensity(head, labelLevel - 1)) {
|
||||
if(labelLevel >= 0) {
|
||||
labelLevel -= level_rate;
|
||||
lastLevelChange = now();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AircraftLabel::applyForces() {
|
||||
dx += ddx;
|
||||
dy += ddy;
|
||||
|
||||
dx *= damping_force;
|
||||
dy *= damping_force;
|
||||
|
||||
if(fabs(dx) > velocity_limit) {
|
||||
dx = sign(dx) * velocity_limit;
|
||||
}
|
||||
|
||||
if(fabs(dy) > velocity_limit) {
|
||||
dy = sign(dy) * velocity_limit;
|
||||
}
|
||||
|
||||
if(fabs(dx) < 0.01f) {
|
||||
dx = 0;
|
||||
}
|
||||
|
||||
if(fabs(dy) < 0.01f) {
|
||||
dy = 0;
|
||||
}
|
||||
|
||||
x += dx;
|
||||
y += dy;
|
||||
|
||||
if(isnan(x)) {
|
||||
x = 0;
|
||||
}
|
||||
|
||||
if(isnan(y)) {
|
||||
y = 0;
|
||||
}
|
||||
|
||||
// x = p->cx + (int)round(p->ox);
|
||||
// y = p->cy + (int)round(p->oy);
|
||||
}
|
||||
|
||||
// SDL_Color signalToColor(int signal) {
|
||||
// SDL_Color planeColor;
|
||||
|
||||
// if(signal > 127) {
|
||||
// signal = 127;
|
||||
// }
|
||||
|
||||
// if(signal < 0) {
|
||||
// planeColor = setColor(96, 96, 96);
|
||||
// } else {
|
||||
// planeColor = setColor(parula[signal][0], parula[signal][1], parula[signal][2]);
|
||||
// }
|
||||
|
||||
// return planeColor;
|
||||
// }
|
||||
// void View::drawSignalMarks(Aircraft *p, int x, int y) {
|
||||
// unsigned char * pSig = p->signalLevel;
|
||||
// unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] +
|
||||
// pSig[4] + pSig[5] + pSig[6] + pSig[7] + 3) >> 3;
|
||||
|
||||
// SDL_Color barColor = signalToColor(signalAverage);
|
||||
|
||||
// Uint8 seenFade;
|
||||
|
||||
// if(elapsed(p->msSeen) < 1024) {
|
||||
// seenFade = (Uint8) (255.0 - elapsed(p->msSeen) / 4.0);
|
||||
|
||||
// circleRGBA(renderer, x + mapFontWidth, y - 5, 2 * screen_uiscale, barColor.r, barColor.g, barColor.b, seenFade);
|
||||
// }
|
||||
|
||||
// if(elapsed(p->msSeenLatLon) < 1024) {
|
||||
// seenFade = (Uint8) (255.0 - elapsed(p->msSeenLatLon) / 4.0);
|
||||
|
||||
// hlineRGBA(renderer, x + mapFontWidth + 5 * screen_uiscale, x + mapFontWidth + 9 * screen_uiscale, y - 5, barColor.r, barColor.g, barColor.b, seenFade);
|
||||
// vlineRGBA(renderer, x + mapFontWidth + 7 * screen_uiscale, y - 2 * screen_uiscale - 5, y + 2 * screen_uiscale - 5, barColor.r, barColor.g, barColor.b, seenFade);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
void AircraftLabel::draw(SDL_Renderer *renderer, bool selected) {
|
||||
if(x == 0 || y == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// char buff[100];
|
||||
// snprintf(buff, sizeof(buff), "%f %f", x, y);
|
||||
// debugLabel.setText(buff);
|
||||
|
||||
int totalWidth = 0;
|
||||
int totalHeight = 0;
|
||||
|
||||
// int margin = 4 * screen_uiscale;
|
||||
|
||||
int margin = 4;
|
||||
|
||||
SDL_Rect outRect;
|
||||
|
||||
if(opacity == 0 && labelLevel < 2) {
|
||||
target_opacity = 1.0f;
|
||||
}
|
||||
|
||||
if(opacity > 0 && labelLevel >= 2) {
|
||||
target_opacity = 0.0f;
|
||||
}
|
||||
|
||||
opacity += 0.25f * (target_opacity - opacity);
|
||||
|
||||
if(opacity < 0.05f) {
|
||||
opacity = 0;
|
||||
}
|
||||
|
||||
if(w != 0 && h != 0 && opacity > 0) {
|
||||
|
||||
SDL_Color drawColor = style.labelLineColor;
|
||||
|
||||
drawColor.a = static_cast<int>(255.0f * opacity);
|
||||
|
||||
if(selected) {
|
||||
drawColor = style.selectedColor;
|
||||
}
|
||||
|
||||
int tick = 4;
|
||||
|
||||
int anchor_x, anchor_y, exit_x, exit_y;
|
||||
|
||||
if(x + w / 2 > p->x) {
|
||||
anchor_x = x;
|
||||
} else {
|
||||
anchor_x = x + w;
|
||||
}
|
||||
|
||||
if(y + h / 2 > p->y) {
|
||||
anchor_y = y - margin;
|
||||
} else {
|
||||
anchor_y = y + h + margin;
|
||||
}
|
||||
|
||||
if(abs(anchor_x - p->x) > abs(anchor_y - p->y)) {
|
||||
exit_x = (anchor_x + p->x) / 2;
|
||||
exit_y = anchor_y;
|
||||
} else {
|
||||
exit_x = anchor_x;
|
||||
exit_y = (anchor_y + p->y) / 2;
|
||||
}
|
||||
|
||||
Sint16 vx[3] = {
|
||||
static_cast<Sint16>(p->x),
|
||||
static_cast<Sint16>(exit_x),
|
||||
static_cast<Sint16>(anchor_x)};
|
||||
|
||||
Sint16 vy[3] = {
|
||||
static_cast<Sint16>(p->y),
|
||||
static_cast<Sint16>(exit_y),
|
||||
static_cast<Sint16>(anchor_y)};
|
||||
|
||||
boxRGBA(renderer, x, y, x + w, y + h, style.labelBackground.r, style.labelBackground.g, style.labelBackground.b, drawColor.a);
|
||||
|
||||
// char buff[100];
|
||||
// snprintf(buff, sizeof(buff), "%d", drawColor.a);
|
||||
// debugLabel.setText(buff);
|
||||
|
||||
bezierRGBA(renderer, vx, vy, 3, 2, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
|
||||
//lineRGBA(renderer, x,y - margin, x + tick, y - margin, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
lineRGBA(renderer, x,y - margin, x + w, y - margin, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
lineRGBA(renderer, x,y - margin, x, y - margin + tick, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
|
||||
// lineRGBA(renderer, x + w, y - margin, x + w - tick, y - margin, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
lineRGBA(renderer, x + w, y - margin, x + w, y - margin + tick, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
|
||||
//lineRGBA(renderer, x, y + h + margin, x + tick, y + h + margin, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
lineRGBA(renderer, x, y + h + margin, x + w, y + h + margin, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
lineRGBA(renderer, x, y + h + margin, x, y + h + margin - tick, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
|
||||
// lineRGBA(renderer, x + w, y + h + margin,x + w - tick, y + h + margin, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
lineRGBA(renderer, x + w, y + h + margin,x + w, y + h + margin - tick, drawColor.r, drawColor.g, drawColor.b, drawColor.a);
|
||||
}
|
||||
|
||||
if(labelLevel < 2 || selected) {
|
||||
// drawSignalMarks(p, x, y);
|
||||
|
||||
SDL_Color drawColor = style.labelColor;
|
||||
drawColor.a = static_cast<int>(255.0f * opacity);
|
||||
|
||||
flightLabel.setColor(drawColor);
|
||||
flightLabel.setPosition(x,y);
|
||||
flightLabel.draw(renderer);
|
||||
// outRect = drawString(flight, x, y, mapBoldFont, drawColor);
|
||||
outRect = flightLabel.getRect();
|
||||
|
||||
totalWidth = std::max(totalWidth,outRect.w);
|
||||
totalHeight += outRect.h;
|
||||
|
||||
}
|
||||
|
||||
if(labelLevel < 1 || selected) {
|
||||
SDL_Color drawColor = style.subLabelColor;
|
||||
drawColor.a = static_cast<int>(255.0f * opacity);
|
||||
|
||||
altitudeLabel.setColor(drawColor);
|
||||
altitudeLabel.setPosition(x,y + totalHeight);
|
||||
altitudeLabel.draw(renderer);
|
||||
outRect = altitudeLabel.getRect();
|
||||
|
||||
totalWidth = std::max(totalWidth,outRect.w);
|
||||
totalHeight += outRect.h;
|
||||
|
||||
speedLabel.setColor(drawColor);
|
||||
speedLabel.setPosition(x,y + totalHeight);
|
||||
speedLabel.draw(renderer);
|
||||
outRect = speedLabel.getRect();
|
||||
|
||||
totalWidth = std::max(totalWidth,outRect.w);
|
||||
totalHeight += outRect.h;
|
||||
|
||||
}
|
||||
|
||||
debugLabel.setPosition(x,y + totalHeight);
|
||||
debugLabel.draw(renderer);
|
||||
|
||||
target_w = totalWidth;
|
||||
target_h = totalHeight;
|
||||
|
||||
w += 0.25f * (target_w - w);
|
||||
h += 0.25f * (target_h - h);
|
||||
|
||||
if(w < 0.05f) {
|
||||
w = 0;
|
||||
}
|
||||
|
||||
if(h < 0.05f) {
|
||||
h = 0;
|
||||
}
|
||||
}
|
||||
|
||||
AircraftLabel::AircraftLabel(Aircraft *p, bool metric, int screen_width, int screen_height, TTF_Font *font) {
|
||||
this->p = p;
|
||||
|
||||
this->metric = metric;
|
||||
|
||||
x = p->x;
|
||||
y = p->y + 20; //*screen_uiscale
|
||||
w = 0;
|
||||
h = 0;
|
||||
target_w = 0;
|
||||
target_h = 0;
|
||||
|
||||
opacity = 0;
|
||||
target_opacity = 0;
|
||||
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
ddx = 0;
|
||||
ddy = 0;
|
||||
|
||||
this->screen_width = screen_width;
|
||||
this->screen_height = screen_height;
|
||||
|
||||
labelLevel = 0;
|
||||
|
||||
flightLabel.setFont(font);
|
||||
altitudeLabel.setFont(font);
|
||||
speedLabel.setFont(font);
|
||||
debugLabel.setFont(font);
|
||||
|
||||
lastLevelChange = now();
|
||||
}
|
76
AircraftLabel.h
Normal file
76
AircraftLabel.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
|
||||
#include <string>
|
||||
#include "SDL2/SDL_ttf.h"
|
||||
#include <chrono>
|
||||
|
||||
#include "Label.h"
|
||||
#include "Style.h"
|
||||
|
||||
class Aircraft;
|
||||
|
||||
|
||||
class AircraftLabel {
|
||||
public:
|
||||
void update();
|
||||
void clearAcceleration();
|
||||
void calculateForces(Aircraft *check_p);
|
||||
void applyForces();
|
||||
|
||||
void draw(SDL_Renderer *renderer, bool selected);
|
||||
|
||||
AircraftLabel(Aircraft *p, bool metric, int screen_width, int screen_height, TTF_Font *font);
|
||||
|
||||
private:
|
||||
SDL_Rect getFullRect(int labelLevel);
|
||||
float calculateDensity(Aircraft *check_p, int labelLevel);
|
||||
|
||||
Aircraft *p;
|
||||
|
||||
Label flightLabel;
|
||||
Label altitudeLabel;
|
||||
Label speedLabel;
|
||||
Label debugLabel;
|
||||
|
||||
float labelLevel;
|
||||
|
||||
bool metric;
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float w;
|
||||
float h;
|
||||
|
||||
float target_w;
|
||||
float target_h;
|
||||
|
||||
float dx;
|
||||
float dy;
|
||||
float ddx;
|
||||
float ddy;
|
||||
|
||||
float opacity;
|
||||
float target_opacity;
|
||||
|
||||
float pressure;
|
||||
|
||||
int screen_width;
|
||||
int screen_height;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point lastLevelChange;
|
||||
|
||||
///////////
|
||||
|
||||
float label_force = 0.001f;
|
||||
float label_dist = 2.0f;
|
||||
float density_force = 0.05f;
|
||||
float attachment_force = 0.0015f;
|
||||
float attachment_dist = 10.0f;
|
||||
float icon_force = 0.001f;
|
||||
float icon_dist = 15.0f;
|
||||
float boundary_force = 0.01f;
|
||||
float damping_force = 0.85f;
|
||||
float velocity_limit = 2.0f;
|
||||
float edge_margin = 15.0f;
|
||||
|
||||
Style style;
|
||||
};
|
|
@ -1,15 +1,41 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 "AircraftList.h"
|
||||
|
||||
static uint64_t mstime(void) {
|
||||
struct timeval tv;
|
||||
uint64_t mst;
|
||||
|
||||
gettimeofday(&tv, nullptr);
|
||||
mst = ((uint64_t)tv.tv_sec)*1000;
|
||||
mst += tv.tv_usec/1000;
|
||||
return mst;
|
||||
static std::chrono::high_resolution_clock::time_point now() {
|
||||
return std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
|
||||
Aircraft *AircraftList::find(uint32_t addr) {
|
||||
Aircraft *p = head;
|
||||
|
||||
|
@ -31,11 +57,14 @@ void AircraftList::update(Modes *modes) {
|
|||
p = p->next;
|
||||
}
|
||||
|
||||
//debug
|
||||
//find(1)->live = 1;
|
||||
|
||||
|
||||
while(a) {
|
||||
|
||||
p = find(a->addr);
|
||||
if (!p) {
|
||||
//p = createPlaneObj(a);
|
||||
p = new Aircraft(a->addr);
|
||||
p->next = head;
|
||||
head = p;
|
||||
|
@ -51,7 +80,7 @@ void AircraftList::update(Modes *modes) {
|
|||
}
|
||||
|
||||
p->seen = a->seen;
|
||||
p->msSeen = mstime();
|
||||
p->msSeen = now();
|
||||
|
||||
if((p->seen - p->prev_seen) > 0) {
|
||||
p->messageRate = 1.0 / (double)(p->seen - p->prev_seen);
|
||||
|
@ -60,31 +89,38 @@ void AircraftList::update(Modes *modes) {
|
|||
memcpy(p->flight, a->flight, sizeof(p->flight));
|
||||
memcpy(p->signalLevel, a->signalLevel, sizeof(p->signalLevel));
|
||||
|
||||
|
||||
if(p->seenLatLon == a->seenLatLon) {
|
||||
a = a->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
p->msSeenLatLon = now();
|
||||
|
||||
p->seenLatLon = a->seenLatLon;
|
||||
|
||||
p->altitude = a->altitude;
|
||||
p->speed = a->speed;
|
||||
p->track = a->track;
|
||||
|
||||
p->vert_rate = a->vert_rate;
|
||||
|
||||
if(p->lon == 0) {
|
||||
p->created = now();
|
||||
}
|
||||
|
||||
if(p->lon == a->lon && p->lat == a->lat) {
|
||||
a = a->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
p->lon = a->lon;
|
||||
p->lat = a->lat;
|
||||
|
||||
if(p->seenLatLon < a->seenLatLon) {
|
||||
p->msSeenLatLon = mstime();
|
||||
|
||||
// p->oldIdx = (p->oldIdx+1) % 32;
|
||||
|
||||
// p->oldLon[p->oldIdx] = p->lon;
|
||||
// p->oldLat[p->oldIdx] = p->lat;
|
||||
p->lonHistory.push_back(p->lon);
|
||||
p->latHistory.push_back(p->lat);
|
||||
p->headingHistory.push_back(p->track);
|
||||
p->timestampHistory.push_back(p->seenLatLon);
|
||||
|
||||
// p->oldHeading[p->oldIdx] = p->track;
|
||||
|
||||
// p->oldSeen[p->oldIdx] = p->seenLatLon;
|
||||
}
|
||||
|
||||
p->seenLatLon = a->seenLatLon;
|
||||
p->timestampHistory.push_back(p->msSeenLatLon);
|
||||
|
||||
a = a->next;
|
||||
}
|
||||
|
@ -112,6 +148,10 @@ void AircraftList::update(Modes *modes) {
|
|||
|
||||
AircraftList::AircraftList() {
|
||||
head = nullptr;
|
||||
|
||||
// //debug aircraft attached to mouse
|
||||
// head = new Aircraft(1);
|
||||
// memcpy(head->flight, "mouse", sizeof("mouse"));
|
||||
}
|
||||
|
||||
AircraftList::~AircraftList() {
|
||||
|
|
|
@ -1,3 +1,34 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 "Aircraft.h"
|
||||
|
||||
#include "dump1090.h" //for Modes
|
||||
|
|
122
AppData.cpp
122
AppData.cpp
|
@ -1,27 +1,45 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 "AppData.h"
|
||||
|
||||
//
|
||||
//carried over from view1090.c
|
||||
//
|
||||
|
||||
|
||||
int AppData::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, server, 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 =
|
||||
|
@ -32,39 +50,32 @@ int AppData::setupConnection(struct client *c) {
|
|||
return fd;
|
||||
}
|
||||
|
||||
//
|
||||
// end view1090.c
|
||||
//
|
||||
|
||||
|
||||
void AppData::initialize() {
|
||||
// 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);
|
||||
|
||||
// Prepare error correction tables
|
||||
modesInitErrorInfo(&(modes));
|
||||
}
|
||||
|
||||
|
||||
void AppData::connect() {
|
||||
// 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));
|
||||
while(1) {
|
||||
if ((fd = setupConnection(c)) == ANET_ERR) {
|
||||
fprintf(stderr, "Waiting on %s:%d\n", server, modes.net_input_beast_port);
|
||||
sleep(1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if(connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
c = (struct client *) malloc(sizeof(*c));
|
||||
|
||||
if ((fd = setupConnection(c)) == ANET_ERR) {
|
||||
fprintf(stderr, "Waiting on %s:%d\n", server, modes.net_input_beast_port);
|
||||
return;
|
||||
}
|
||||
|
||||
connected = true;
|
||||
fprintf(stderr, "Connected to %s:%d\n", server, modes.net_input_beast_port);
|
||||
}
|
||||
|
||||
|
||||
|
@ -75,6 +86,10 @@ void AppData::disconnect() {
|
|||
|
||||
|
||||
void AppData::update() {
|
||||
if(!connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((fd == ANET_ERR) || (recv(c->fd, pk_buf, sizeof(pk_buf), MSG_PEEK | MSG_DONTWAIT) == 0)) {
|
||||
free(c);
|
||||
usleep(1000000);
|
||||
|
@ -82,7 +97,8 @@ void AppData::update() {
|
|||
fd = setupConnection(c);
|
||||
return;
|
||||
}
|
||||
modesReadFromClient(&modes, c,"",decodeBinMessage);
|
||||
char empty;
|
||||
modesReadFromClient(&modes, c, &empty,decodeBinMessage);
|
||||
|
||||
interactiveRemoveStaleAircrafts(&modes);
|
||||
|
||||
|
@ -104,39 +120,37 @@ void AppData::updateStatus() {
|
|||
msgRateAccumulate = 0.0;
|
||||
|
||||
|
||||
// PlaneObj *p = appData.planes;
|
||||
Aircraft *p = aircraftList.head;
|
||||
|
||||
// while(p) {
|
||||
// unsigned char * pSig = p->signalLevel;
|
||||
// unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] +
|
||||
// pSig[4] + pSig[5] + pSig[6] + pSig[7]);
|
||||
while(p) {
|
||||
unsigned char * pSig = p->signalLevel;
|
||||
unsigned int signalAverage = (pSig[0] + pSig[1] + pSig[2] + pSig[3] +
|
||||
pSig[4] + pSig[5] + pSig[6] + pSig[7]);
|
||||
|
||||
// sigAccumulate += signalAverage;
|
||||
sigAccumulate += signalAverage;
|
||||
|
||||
// if (p->lon && p->lat) {
|
||||
// numVisiblePlanes++;
|
||||
// }
|
||||
if (p->lon && p->lat) {
|
||||
numVisiblePlanes++;
|
||||
}
|
||||
|
||||
// totalCount++;
|
||||
totalCount++;
|
||||
|
||||
// msgRateAccumulate += p->messageRate;
|
||||
msgRateAccumulate += p->messageRate;
|
||||
|
||||
// p = p->next;
|
||||
// }
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
// Status.msgRate = msgRateAccumulate;
|
||||
// Status.avgSig = sigAccumulate / (double) totalCount;
|
||||
// Status.numPlanes = totalCount;
|
||||
// Status.numVisiblePlanes = numVisiblePlanes;
|
||||
// Status.maxDist = maxDist;
|
||||
msgRate = msgRateAccumulate;
|
||||
avgSig = sigAccumulate / (double) totalCount;
|
||||
numPlanes = totalCount;
|
||||
numVisiblePlanes = numVisiblePlanes;
|
||||
maxDist = maxDist;
|
||||
}
|
||||
|
||||
|
||||
AppData::AppData(){
|
||||
// Default everything to zero/NULL
|
||||
memset(&modes, 0, sizeof(Modes));
|
||||
|
||||
// Now initialise things that should not be 0/NULL to their defaults
|
||||
modes.check_crc = 1;
|
||||
strcpy(server,VIEW1090_NET_OUTPUT_IP_ADDRESS);
|
||||
modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT;
|
||||
|
@ -148,4 +162,6 @@ AppData::AppData(){
|
|||
|
||||
modes.interactive = 0;
|
||||
modes.quiet = 1;
|
||||
|
||||
connected = false;
|
||||
}
|
||||
|
|
32
AppData.h
32
AppData.h
|
@ -1,3 +1,34 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 APPDATA_H
|
||||
#define APPDATA_H
|
||||
|
||||
|
@ -14,6 +45,7 @@ class AppData {
|
|||
//
|
||||
|
||||
struct client *c;
|
||||
bool connected;
|
||||
int fd;
|
||||
char pk_buf[8];
|
||||
|
||||
|
|
69
Input.cpp
69
Input.cpp
|
@ -1,13 +1,46 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 "Input.h"
|
||||
|
||||
static uint64_t mstime(void) {
|
||||
struct timeval tv;
|
||||
uint64_t mst;
|
||||
static std::chrono::high_resolution_clock::time_point now() {
|
||||
return std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
mst = ((uint64_t)tv.tv_sec)*1000;
|
||||
mst += tv.tv_usec/1000;
|
||||
return mst;
|
||||
// static uint64_t now() {
|
||||
// return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch).count();
|
||||
// }
|
||||
|
||||
static uint64_t elapsed(std::chrono::high_resolution_clock::time_point ref) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(now() - ref).count();
|
||||
}
|
||||
|
||||
template <typename T> int sgn(T val) {
|
||||
|
@ -54,31 +87,33 @@ void Input::getInput()
|
|||
view->mapTargetMaxDist = 0;
|
||||
view->mapMoved = 1;
|
||||
|
||||
if(mstime() - touchDownTime > 100) {
|
||||
touchDownTime = 0;
|
||||
if(elapsed(touchDownTime) > 100) {
|
||||
//touchDownTime = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_FINGERMOTION:;
|
||||
if(mstime() - touchDownTime > 150) {
|
||||
if(elapsed(touchDownTime) > 150) {
|
||||
tapCount = 0;
|
||||
touchDownTime = 0;
|
||||
//touchDownTime = 0;
|
||||
}
|
||||
view->moveCenterRelative( view->screen_width * event.tfinger.dx, view->screen_height * event.tfinger.dy);
|
||||
break;
|
||||
|
||||
case SDL_FINGERDOWN:
|
||||
if(mstime() - touchDownTime > 500) {
|
||||
if(elapsed(touchDownTime) > 500) {
|
||||
tapCount = 0;
|
||||
}
|
||||
|
||||
if(SDL_GetNumTouchFingers(event.tfinger.touchId) == 0) {
|
||||
touchDownTime = mstime();
|
||||
|
||||
//this finger number is always 1 for down and 0 for up an rpi+hyperpixel??
|
||||
if(SDL_GetNumTouchFingers(event.tfinger.touchId) <= 1) {
|
||||
touchDownTime = now();
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_FINGERUP:
|
||||
if(mstime() - touchDownTime < 150 && SDL_GetNumTouchFingers(event.tfinger.touchId) == 0) {
|
||||
if(elapsed(touchDownTime) < 150 && SDL_GetNumTouchFingers(event.tfinger.touchId) == 0) {
|
||||
touchx = view->screen_width * event.tfinger.x;
|
||||
touchy = view->screen_height * event.tfinger.y;
|
||||
tapCount++;
|
||||
|
@ -93,10 +128,10 @@ void Input::getInput()
|
|||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if(event.button.which != SDL_TOUCH_MOUSEID) {
|
||||
if(mstime() - touchDownTime > 500) {
|
||||
if(elapsed(touchDownTime) > 500) {
|
||||
tapCount = 0;
|
||||
}
|
||||
touchDownTime = mstime();
|
||||
touchDownTime = now();
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
35
Input.h
35
Input.h
|
@ -1,9 +1,42 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 INPUT_H
|
||||
#define INPUT_H
|
||||
|
||||
#include "AppData.h"
|
||||
#include "View.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
class Input {
|
||||
public:
|
||||
void getInput();
|
||||
|
@ -14,7 +47,7 @@ public:
|
|||
View *view;
|
||||
AppData *appData;
|
||||
|
||||
uint64_t touchDownTime;
|
||||
std::chrono::high_resolution_clock::time_point touchDownTime;
|
||||
int touchx;
|
||||
int touchy;
|
||||
int tapCount;
|
||||
|
|
70
Label.cpp
Normal file
70
Label.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include "Label.h"
|
||||
#include <string>
|
||||
|
||||
void Label::draw(SDL_Renderer *renderer) {
|
||||
SDL_Rect rect = getRect();
|
||||
|
||||
if(rect.h == 0 || rect.w == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
SDL_RenderCopy(renderer, texture, NULL, &rect);
|
||||
SDL_DestroyTexture(texture);
|
||||
}
|
||||
|
||||
void Label::makeSurface() {
|
||||
if(surface != NULL) {
|
||||
SDL_FreeSurface(surface);
|
||||
}
|
||||
|
||||
surface = TTF_RenderUTF8_Solid(font, text.c_str(), color);
|
||||
}
|
||||
|
||||
SDL_Rect Label::getRect() {
|
||||
SDL_Rect rect = {0,0,0,0};
|
||||
|
||||
if(!text.length()) {
|
||||
return rect;
|
||||
}
|
||||
|
||||
if(surface == NULL) {
|
||||
return rect;
|
||||
}
|
||||
|
||||
rect.x = x;
|
||||
rect.y = y;
|
||||
rect.w = surface->w;
|
||||
rect.h = surface->h;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
void Label::setText(std::string text) {
|
||||
this->text = text;
|
||||
makeSurface();
|
||||
}
|
||||
|
||||
void Label::setPosition(int x, int y) {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
}
|
||||
|
||||
void Label::setFont(TTF_Font *font) {
|
||||
this->font = font;
|
||||
}
|
||||
|
||||
void Label::setColor(SDL_Color color) {
|
||||
this->color = color;
|
||||
}
|
||||
//
|
||||
Label::Label() {
|
||||
this->color = {255,255,255,255};
|
||||
surface = NULL;
|
||||
}
|
||||
|
||||
Label::~Label() {
|
||||
SDL_FreeSurface(surface);
|
||||
}
|
||||
|
||||
|
29
Label.h
Normal file
29
Label.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_ttf.h"
|
||||
#include <string>
|
||||
|
||||
class Label {
|
||||
public:
|
||||
void draw(SDL_Renderer *renderer);
|
||||
|
||||
void setText(std::string text);
|
||||
void setPosition(int x, int y);
|
||||
void setFont(TTF_Font *font);
|
||||
void setColor(SDL_Color color);
|
||||
|
||||
SDL_Rect getRect();
|
||||
|
||||
Label();
|
||||
~Label();
|
||||
|
||||
private:
|
||||
void makeSurface();
|
||||
|
||||
std::string text;
|
||||
int x;
|
||||
int y;
|
||||
TTF_Font *font;
|
||||
SDL_Color color;
|
||||
SDL_Surface *surface;
|
||||
};
|
23
Makefile
23
Makefile
|
@ -3,17 +3,24 @@
|
|||
# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
|
||||
#
|
||||
|
||||
CFLAGS=-O2 -g -Wno-write-strings
|
||||
LIBS=-lm -lSDL2 -lSDL2_ttf -lSDL2_gfx
|
||||
CC=g++
|
||||
CXXFLAGS=-O2 -std=c++11 -g
|
||||
LIBS= -lm -lSDL2 -lSDL2_ttf -lSDL2_gfx -g
|
||||
CXX=g++
|
||||
|
||||
all: map1090
|
||||
all: viz1090
|
||||
|
||||
%.o: %.c %.cpp
|
||||
$(CC) $(CFLAGS) $(EXTRACFLAGS) -c $<
|
||||
$(CXX) $(CXXFLAGS) $(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 Label.o AircraftLabel.o anet.o interactive.o mode_ac.o mode_s.o net_io.o Input.o View.o Map.o parula.o monokai.o
|
||||
$(CXX) -o viz1090 viz1090.o AppData.o AircraftList.o Aircraft.o Label.o AircraftLabel.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 \
|
||||
airportdata.bin \
|
||||
airportnames \
|
||||
mapdata/* \
|
||||
mapdata.bin \
|
||||
mapnames \
|
||||
*.o \
|
||||
viz1090
|
||||
|
|
311
Map.cpp
311
Map.cpp
|
@ -1,19 +1,76 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 "Map.h"
|
||||
#include <stdio.h>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
bool Map::QTInsert(QuadTree *tree, Polygon *polygon) {
|
||||
// printf("Inserting %d point poly\n", polygon->numPoints);
|
||||
bool Map::QTInsert(QuadTree *tree, Line *line, int depth) {
|
||||
|
||||
if (!(polygon->lat_min >= tree->lat_min &&
|
||||
polygon->lat_max <= tree->lat_max &&
|
||||
polygon->lon_min >= tree->lon_min &&
|
||||
polygon->lon_max <= tree->lon_max)) {
|
||||
// printf("doesnt fit: %f > %f, %f < %f, %f < %f,%f > %f \n",polygon->lat_min, tree->lat_min, polygon->lat_max, tree->lat_max, polygon->lon_min, tree->lon_min, polygon->lon_max,tree->lon_max);
|
||||
// if(depth > 25) {
|
||||
// // printf("fail [%f %f] -> [%f %f]\n",line->start.lon,line->start.lat,line->end.lon,line->end.lat);
|
||||
|
||||
// // printf("bounds %f %f %f %f\n",tree->lon_min, tree->lon_max, tree->lat_min, tree->lat_max);
|
||||
// // fflush(stdout);
|
||||
// tree->lines.push_back(&(*line));
|
||||
// return true;
|
||||
// }
|
||||
|
||||
|
||||
bool startInside = line->start.lat >= tree->lat_min &&
|
||||
line->start.lat <= tree->lat_max &&
|
||||
line->start.lon >= tree->lon_min &&
|
||||
line->start.lon <= tree->lon_max;
|
||||
|
||||
bool endInside = line->end.lat >= tree->lat_min &&
|
||||
line->end.lat <= tree->lat_max &&
|
||||
line->end.lon >= tree->lon_min &&
|
||||
line->end.lon <= tree->lon_max;
|
||||
|
||||
// if (!startInside || !endInside) {
|
||||
// return false;
|
||||
// }
|
||||
if (!startInside && !endInside) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (startInside != endInside) {
|
||||
tree->lines.push_back(&(*line));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tree->nw == NULL) {
|
||||
tree->nw = new QuadTree;
|
||||
|
||||
|
@ -23,7 +80,7 @@ bool Map::QTInsert(QuadTree *tree, Polygon *polygon) {
|
|||
tree->nw->lon_max = tree->lon_min + 0.5 * (tree->lon_max - tree->lon_min);
|
||||
}
|
||||
|
||||
if (QTInsert(tree->nw,polygon)){
|
||||
if (QTInsert(tree->nw, line, depth++)){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -36,7 +93,7 @@ bool Map::QTInsert(QuadTree *tree, Polygon *polygon) {
|
|||
tree->sw->lon_max = tree->lon_max;
|
||||
}
|
||||
|
||||
if (QTInsert(tree->sw,polygon)){
|
||||
if (QTInsert(tree->sw, line, depth++)){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -49,7 +106,7 @@ bool Map::QTInsert(QuadTree *tree, Polygon *polygon) {
|
|||
tree->ne->lon_max = tree->lon_min + 0.5 * (tree->lon_max - tree->lon_min);
|
||||
}
|
||||
|
||||
if (QTInsert(tree->ne,polygon)){
|
||||
if (QTInsert(tree->ne, line, depth++)){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -62,60 +119,78 @@ bool Map::QTInsert(QuadTree *tree, Polygon *polygon) {
|
|||
tree->se->lon_max = tree->lon_max;
|
||||
}
|
||||
|
||||
if (QTInsert(tree->se,polygon)){
|
||||
if (QTInsert(tree->se, line, depth++)){
|
||||
return true;
|
||||
}
|
||||
|
||||
tree->polygons.push_back(*polygon);
|
||||
tree->lines.push_back(&(*line));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::list<Polygon> Map::getPolysRecursive(QuadTree *tree, float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max) {
|
||||
std::list<Polygon> retPolys;
|
||||
std::vector<Line*> Map::getLinesRecursive(QuadTree *tree, float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max) {
|
||||
std::vector<Line*> retLines;
|
||||
|
||||
if(tree == NULL) {
|
||||
return retPolys;
|
||||
return retLines;
|
||||
}
|
||||
|
||||
if (tree->lat_min > screen_lat_max || screen_lat_min > tree->lat_max) {
|
||||
return retPolys;
|
||||
return retLines;
|
||||
}
|
||||
|
||||
if (tree->lon_min > screen_lon_max || screen_lon_min > tree->lon_max) {
|
||||
return retPolys;
|
||||
return retLines;
|
||||
}
|
||||
|
||||
retPolys.splice(retPolys.end(),getPolysRecursive(tree->nw, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max));
|
||||
retPolys.splice(retPolys.end(),getPolysRecursive(tree->sw, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max));
|
||||
retPolys.splice(retPolys.end(),getPolysRecursive(tree->ne, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max));
|
||||
retPolys.splice(retPolys.end(),getPolysRecursive(tree->se, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max));
|
||||
std::vector<Line*> ret;
|
||||
ret = getLinesRecursive(tree->nw, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max);
|
||||
retLines.insert(retLines.end(), ret.begin(), ret.end());
|
||||
|
||||
float dx, dy;
|
||||
ret = getLinesRecursive(tree->sw, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max);
|
||||
retLines.insert(retLines.end(), ret.begin(), ret.end());
|
||||
|
||||
std::list<Polygon>::iterator currentPolygon;
|
||||
ret = getLinesRecursive(tree->ne, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max);
|
||||
retLines.insert(retLines.end(), ret.begin(), ret.end());
|
||||
|
||||
for (currentPolygon = tree->polygons.begin(); currentPolygon != tree->polygons.end(); ++currentPolygon) {
|
||||
if(currentPolygon->points.empty()) {
|
||||
continue;
|
||||
}
|
||||
ret = getLinesRecursive(tree->se, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max);
|
||||
retLines.insert(retLines.end(), ret.begin(), ret.end());
|
||||
|
||||
retPolys.push_back(*currentPolygon);
|
||||
}
|
||||
retLines.insert(retLines.end(), tree->lines.begin(), tree->lines.end());
|
||||
|
||||
// Debug quadtree
|
||||
// Point TL, TR, BL, BR;
|
||||
|
||||
// TL.lat = tree->lat_min;
|
||||
// TL.lon = tree->lon_min;
|
||||
|
||||
// TR.lat = tree->lat_max;
|
||||
// TR.lon = tree->lon_min;
|
||||
|
||||
// BL.lat = tree->lat_min;
|
||||
// BL.lon = tree->lon_max;
|
||||
|
||||
// BR.lat = tree->lat_max;
|
||||
// BR.lon = tree->lon_max;
|
||||
|
||||
// retLines.push_back(new Line(TL,TR));
|
||||
// retLines.push_back(new Line(TR,BR));
|
||||
// retLines.push_back(new Line(BL,BR));
|
||||
// retLines.push_back(new Line(TL,BL));
|
||||
|
||||
return retLines;
|
||||
}
|
||||
|
||||
std::list<Polygon> Map::getPolys(float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max) {
|
||||
return getPolysRecursive(&root, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max);
|
||||
std::vector<Line*> Map::getLines(float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max) {
|
||||
return getLinesRecursive(&root, screen_lat_min, screen_lat_max, screen_lon_min, screen_lon_max);
|
||||
};
|
||||
|
||||
Map::Map() {
|
||||
FILE *fileptr;
|
||||
|
||||
if(!(fileptr = fopen("mapdata.bin", "rb"))) {
|
||||
printf("Couldn't read mapdata.bin\nDid you run getmap.sh?\n");
|
||||
exit(0);
|
||||
}
|
||||
if((fileptr = fopen("mapdata.bin", "rb"))) {
|
||||
|
||||
|
||||
fseek(fileptr, 0, SEEK_END);
|
||||
mapPoints_count = ftell(fileptr) / sizeof(float);
|
||||
|
@ -123,13 +198,13 @@ Map::Map() {
|
|||
|
||||
mapPoints = (float *)malloc(mapPoints_count * sizeof(float));
|
||||
if(!fread(mapPoints, sizeof(float), mapPoints_count, fileptr)){
|
||||
printf("Read error\n");
|
||||
printf("Map read error\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
fclose(fileptr);
|
||||
|
||||
printf("Read %d map points.\n",mapPoints_count);
|
||||
printf("Read %d map points.\n",mapPoints_count / 2);
|
||||
|
||||
// load quad tree
|
||||
|
||||
|
@ -150,35 +225,161 @@ Map::Map() {
|
|||
}
|
||||
}
|
||||
|
||||
Polygon *currentPolygon = new Polygon;
|
||||
//printf("map bounds: %f %f %f %f\n",root.lon_min, root.lon_max, root.lat_min, root.lat_max);
|
||||
|
||||
for(int i = 0; i < mapPoints_count; i+=2) {
|
||||
Point currentPoint;
|
||||
Point nextPoint;
|
||||
|
||||
if(mapPoints[i] == 0) {
|
||||
QTInsert(&root, currentPolygon);
|
||||
currentPolygon = new Polygon;
|
||||
for(int i = 0; i < mapPoints_count - 2; i+=2) {
|
||||
if(mapPoints[i] == 0)
|
||||
continue;
|
||||
if(mapPoints[i + 1] == 0)
|
||||
continue;
|
||||
if(mapPoints[i + 2] == 0)
|
||||
continue;
|
||||
if(mapPoints[i + 3] == 0)
|
||||
continue;
|
||||
currentPoint.lon = mapPoints[i];
|
||||
currentPoint.lat = mapPoints[i + 1];
|
||||
|
||||
nextPoint.lon = mapPoints[i + 2];
|
||||
nextPoint.lat = mapPoints[i + 3];
|
||||
|
||||
// printf("inserting [%f %f] -> [%f %f]\n",currentPoint.lon,currentPoint.lat,nextPoint.lon,nextPoint.lat);
|
||||
|
||||
QTInsert(&root, new Line(currentPoint, nextPoint), 0);
|
||||
}
|
||||
} else {
|
||||
printf("No map file found\n");
|
||||
}
|
||||
//
|
||||
|
||||
|
||||
if((fileptr = fopen("airportdata.bin", "rb"))) {
|
||||
fseek(fileptr, 0, SEEK_END);
|
||||
airportPoints_count = ftell(fileptr) / sizeof(float);
|
||||
rewind(fileptr);
|
||||
|
||||
airportPoints = (float *)malloc(airportPoints_count * sizeof(float));
|
||||
if(!fread(airportPoints, sizeof(float), airportPoints_count, fileptr)){
|
||||
printf("Map read error\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
currentPolygon->numPoints++;
|
||||
fclose(fileptr);
|
||||
|
||||
Point *currentPoint = new Point;
|
||||
printf("Read %d airport points.\n",airportPoints_count / 2);
|
||||
|
||||
if(mapPoints[i] < currentPolygon->lon_min) {
|
||||
currentPolygon->lon_min = mapPoints[i];
|
||||
} else if(mapPoints[i] > currentPolygon->lon_max) {
|
||||
currentPolygon->lon_max = mapPoints[i];
|
||||
// load quad tree
|
||||
|
||||
for(int i = 0; i < airportPoints_count; i+=2) {
|
||||
if(airportPoints[i] == 0)
|
||||
continue;
|
||||
|
||||
if(airportPoints[i] < airport_root.lon_min) {
|
||||
airport_root.lon_min = airportPoints[i];
|
||||
} else if(airportPoints[i] > airport_root.lon_max) {
|
||||
airport_root.lon_max = airportPoints[i];
|
||||
}
|
||||
|
||||
if(mapPoints[i+1] < currentPolygon->lat_min) {
|
||||
currentPolygon->lat_min = mapPoints[i+1];
|
||||
} else if(mapPoints[i+1] > currentPolygon->lat_max) {
|
||||
currentPolygon->lat_max = mapPoints[i+1];
|
||||
if(airportPoints[i+1] < airport_root.lat_min) {
|
||||
airport_root.lat_min = airportPoints[i+1];
|
||||
} else if(airportPoints[i+1] > airport_root.lat_max) {
|
||||
airport_root.lat_max = airportPoints[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
currentPoint->lon = mapPoints[i];
|
||||
currentPoint->lat = mapPoints[i+1];
|
||||
//printf("map bounds: %f %f %f %f\n",root.lon_min, root.lon_max, root.lat_min, root.lat_max);
|
||||
|
||||
currentPolygon->points.push_back(*currentPoint);
|
||||
Point currentPoint;
|
||||
Point nextPoint;
|
||||
|
||||
for(int i = 0; i < airportPoints_count - 2; i+=2) {
|
||||
if(airportPoints[i] == 0)
|
||||
continue;
|
||||
if(airportPoints[i + 1] == 0)
|
||||
continue;
|
||||
if(airportPoints[i + 2] == 0)
|
||||
continue;
|
||||
if(airportPoints[i + 3] == 0)
|
||||
continue;
|
||||
currentPoint.lon = airportPoints[i];
|
||||
currentPoint.lat = airportPoints[i + 1];
|
||||
|
||||
nextPoint.lon = airportPoints[i + 2];
|
||||
nextPoint.lat = airportPoints[i + 3];
|
||||
|
||||
//printf("inserting [%f %f] -> [%f %f]\n",currentPoint.lon,currentPoint.lat,nextPoint.lon,nextPoint.lat);
|
||||
|
||||
QTInsert(&airport_root, new Line(currentPoint, nextPoint), 0);
|
||||
}
|
||||
} else {
|
||||
printf("No airport file found\n");
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
|
||||
std::string line;
|
||||
std::ifstream infile("mapnames");
|
||||
|
||||
|
||||
while (std::getline(infile, line))
|
||||
{
|
||||
float lon, lat;
|
||||
|
||||
std::istringstream iss(line);
|
||||
|
||||
iss >> lon;
|
||||
iss >> lat;
|
||||
|
||||
std::string assemble;
|
||||
|
||||
iss >> assemble;
|
||||
|
||||
for(std::string s; iss >> s; ) {
|
||||
assemble = assemble + " " + s;
|
||||
}
|
||||
|
||||
// std::cout << "[" << x << "," << y << "] " << assemble << "\n";
|
||||
MapLabel *label = new MapLabel(lon,lat,assemble);
|
||||
mapnames.push_back(label);
|
||||
}
|
||||
|
||||
std::cout << "Read " << mapnames.size() << " place names\n";
|
||||
|
||||
infile.close();
|
||||
|
||||
infile.open("airportnames");
|
||||
|
||||
|
||||
while (std::getline(infile, line))
|
||||
{
|
||||
float lon, lat;
|
||||
|
||||
std::istringstream iss(line);
|
||||
|
||||
iss >> lon;
|
||||
iss >> lat;
|
||||
|
||||
std::string assemble;
|
||||
|
||||
iss >> assemble;
|
||||
|
||||
for(std::string s; iss >> s; ) {
|
||||
assemble = assemble + " " + s;
|
||||
}
|
||||
|
||||
// std::cout << "[" << x << "," << y << "] " << assemble << "\n";
|
||||
MapLabel *label = new MapLabel(lon,lat,assemble);
|
||||
airportnames.push_back(label);
|
||||
}
|
||||
|
||||
std::cout << "Read " << airportnames.size() << " airport names\n";
|
||||
|
||||
infile.close();
|
||||
|
||||
|
||||
|
||||
printf("done\n");
|
||||
}
|
||||
|
|
83
Map.h
83
Map.h
|
@ -1,30 +1,75 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 MAP_H
|
||||
#define MAP_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
typedef struct Point{
|
||||
float lat;
|
||||
float lon;
|
||||
} Point;
|
||||
|
||||
typedef struct Polygon{
|
||||
typedef struct MapLabel{
|
||||
Point location;
|
||||
std::string text;
|
||||
|
||||
MapLabel(float lon, float lat, std::string text) {
|
||||
this->location.lon = lon;
|
||||
this->location.lat = lat;
|
||||
this->text = text;
|
||||
}
|
||||
} MapLabel;
|
||||
|
||||
typedef struct Line{
|
||||
float lat_min;
|
||||
float lat_max;
|
||||
float lon_min;
|
||||
float lon_max;
|
||||
|
||||
std::list<Point> points;
|
||||
int numPoints;
|
||||
Point start;
|
||||
Point end;
|
||||
|
||||
Polygon() {
|
||||
lat_min = 180.0f;
|
||||
lon_min = 180.0f;
|
||||
lat_max = -180.0f;
|
||||
lon_max = -180.0f;
|
||||
numPoints = 0;
|
||||
Line(Point start, Point end) {
|
||||
this->start = start;
|
||||
this->end = end;
|
||||
|
||||
lat_min = std::min(start.lat,end.lat);
|
||||
lat_max = std::max(start.lat,end.lat);
|
||||
lon_min = std::min(start.lon,end.lon);
|
||||
lon_max = std::max(start.lon,end.lon);
|
||||
}
|
||||
} Polygon;
|
||||
} Line;
|
||||
|
||||
typedef struct QuadTree{
|
||||
float lat_min;
|
||||
|
@ -32,7 +77,7 @@ typedef struct QuadTree{
|
|||
float lon_min;
|
||||
float lon_max;
|
||||
|
||||
std::list<Polygon> polygons;
|
||||
std::vector<Line*> lines;
|
||||
|
||||
struct QuadTree *nw;
|
||||
struct QuadTree *sw;
|
||||
|
@ -74,14 +119,22 @@ class Map {
|
|||
|
||||
public:
|
||||
QuadTree root;
|
||||
QuadTree airport_root;
|
||||
|
||||
bool QTInsert(QuadTree *tree, Polygon *polygon);
|
||||
std::list<Polygon> getPolysRecursive(QuadTree *tree, float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max);
|
||||
std::list<Polygon> getPolys(float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max);
|
||||
bool QTInsert(QuadTree *tree, Line *line, int depth);
|
||||
std::vector<Line*> getLinesRecursive(QuadTree *tree, float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max);
|
||||
std::vector<Line*> getLines(float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max);
|
||||
|
||||
std::vector<MapLabel*> mapnames;
|
||||
std::vector<MapLabel*> airportnames;
|
||||
|
||||
Map();
|
||||
|
||||
int mapPoints_count;
|
||||
float *mapPoints;
|
||||
|
||||
int airportPoints_count;
|
||||
float *airportPoints;
|
||||
|
||||
};
|
||||
#endif
|
144
README.md
144
README.md
|
@ -1,80 +1,156 @@
|
|||
#map1090
|
||||
# viz1090
|
||||
|
||||

|
||||
|
||||
###BUILDING
|
||||
**This is a work in progress**
|
||||
|
||||
Tested and working on Ubuntu 18.04, Raspbian Stretch, Buster
|
||||
There are some major fixes and cleanup that need to happen before a release:
|
||||
* Everything is a grab bag of C and C++, need to more consistently modernize
|
||||
* A full refactor, especially View.cpp, necessary for many of the new features below.
|
||||
* A working Android build, as this is the best way to run this on portable hardware.
|
||||
|
||||
There are also a lot of missing features:
|
||||
* Map improvements
|
||||
* Labels, different colors/line weights for features
|
||||
* Tile prerenderer for improved performance
|
||||
* In-application menus for view options and configuration
|
||||
* Theming/colormaps (important as this is primarily intended to be eye candy!)
|
||||
* Integration with handheld features like GPS, battery monitors, buttons/dials, etc.
|
||||
|
||||
### BUILDING
|
||||
|
||||
Tested and working on Ubuntu 18.04, Raspbian Stretch / Buster, Windows Subsystem for Linux (with Ubuntu 18.04), and Mac
|
||||
|
||||
0. Install build essentials
|
||||
|
||||
1. Install SDL and RTL-SDR libararies
|
||||
```
|
||||
sudo apt-get install libsdl2-dev libsdl2-ttf-dev libsdl2-gfx-dev librtlsdr-dev
|
||||
sudo apt-get install build-essential
|
||||
```
|
||||
Note: On Raspbian the SDL2 package requires X to be running. See the Raspberry Pi section for notes on running from the terminal and other improvements.
|
||||
|
||||
2. Download and build spidr
|
||||
1. Install SDL and RTL-SDR libraries
|
||||
```
|
||||
sudo apt-get install libsdl2-dev libsdl2-ttf-dev libsdl2-gfx-dev librtlsdr-dev libgdal-dev
|
||||
```
|
||||
|
||||
Note: On Raspbian the SDL2 package requires X to be running. See the Raspberry Pi section for notes on running from the terminal and other improvements.
|
||||
|
||||
2. Download and build viz1090
|
||||
```
|
||||
cd ~
|
||||
git clone https://www.github.com/nmatsuda/spidr
|
||||
cd spidr
|
||||
git clone https://www.github.com/nmatsuda/viz1090
|
||||
cd viz1090
|
||||
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
|
||||
|
||||
The getmap.sh pulls the svg file for the contiguous 48 US states and produces a binary file for map1090 to read.
|
||||
|
||||
```
|
||||
sudo apt install python3 python3-pip
|
||||
pip3 install fiona tqdm shapely
|
||||
./getmap.sh
|
||||
```
|
||||
|
||||
###RUNNING
|
||||
This will produce files for map and airport geometry, with labels, that viz1090 reads. If any of these files don't exist then visualizer will show planes and trails without any geography.
|
||||
|
||||
The default parameters for mapconverter should render reasonably quickly on a Raspberry Pi 4. See the mapconverter section below for other options and more information about map sources.
|
||||
|
||||
|
||||
|
||||
3. (Windows only)
|
||||
|
||||
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/
|
||||
|
||||
* run Xlaunch from the start menu
|
||||
* Uncheck "Use Native openGL"
|
||||
* Add parameter ```-ac``` (WSL 2 only)
|
||||
* Open the Ubuntu WSL terminal
|
||||
* Specify the X display to use (WSL 1)
|
||||
```
|
||||
export DISPLAY=:0
|
||||
```
|
||||
* or for (WSL 2)
|
||||
```
|
||||
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0
|
||||
```
|
||||
* Start viz1090 as described below.
|
||||
|
||||
### RUNNING
|
||||
|
||||
1. Start dump1090 (http://www.github.com/MalcolmRobb/dump1090) locally in network mode:
|
||||
```
|
||||
dump1090 --net
|
||||
```
|
||||
|
||||
2. Run map1090
|
||||
2. Run viz1090
|
||||
```
|
||||
./view1090 --fullsceen
|
||||
./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
|
||||
### 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
|
||||
--metric Display metric units rather than imperial.
|
||||
| Argument | Description |
|
||||
| ----------------------------- | ----------- |
|
||||
| --server [domain name or ip] | Specify a dump1090 server |
|
||||
| --port [port number] | Specify dump1090 server port |
|
||||
| --metric | Display metric units |
|
||||
| --lat | Specify your latitude in degrees |
|
||||
| --lon | Specify your longitude in degrees |
|
||||
| --screensize [width] [height] | Specify a resolution, otherwise use resolution of display |
|
||||
| --uiscale [scale] | Scale up UI elements by integer amounts for high resolution screen |
|
||||
| --fullscreen | Render fullscreen rather than in a window |
|
||||
|
||||
--screensize [width] [height] Specify a specific resolution to pass to SDL_RenderSetLogicalSize, otherwise use resolution of display
|
||||
--uiscale [scale] Scale up UI elements by integer amounts for high resolution screen
|
||||
--fullscreen Render fullscreen rather than in a window
|
||||
### MAPS
|
||||
|
||||
###HARDWARE NOTES
|
||||
The best map data source I've found so far is https://www.naturalearthdata.com. This has a lot of useful GIS data, but not airport runways, which you can get from the FAA Aeronautical Data Delivery Service (https://adds-faa.opendata.arcgis.com/)
|
||||
|
||||
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:
|
||||
I've been using these files:
|
||||
|
||||
* [Map geometry](https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip)
|
||||
* [Place names](https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_populated_places.zip)
|
||||
* [Airport IATA codes](https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_airports.zip)
|
||||
* [Airport runway geometry](https://opendata.arcgis.com/datasets/4d8fa46181aa470d809776c57a8ab1f6_0.zip)
|
||||
|
||||
The bash script getmap.sh will download (so long as the links don't break) and convert these. Alternatively, you can pass shapefiles and other arguments to mapconverter.py directly
|
||||
|
||||
### MAPCONVERTER.PY RUNTIME OPTIONS
|
||||
|
||||
| Argument | Description |
|
||||
| ----------------------------- | ----------- |
|
||||
| --mapfile | shapefile for main map |
|
||||
| --mapnames | shapefile for map place names |
|
||||
| --airportfile | shapefile for airport runway outlines |
|
||||
| --airportnames | shapefile for airport IATA names |
|
||||
| --minpop | minimum population to show place names for (defaults to 100000) |
|
||||
| --tolerance" | map simplification tolerance (defaults to 0.001, which works well on a Raspberry Pi 4 - smaller values will produce more detail but slow down the map refresh rate) |
|
||||
|
||||
### HARDWARE NOTES
|
||||
|
||||
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 you want to print the case in the GIF shown above, you can [download it here](https://github.com/nmatsuda/viz1090_case).
|
||||
|
||||
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)
|
||||
|
|
94
Style.h
Normal file
94
Style.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
#ifndef STYLE_H
|
||||
#define STYLE_H
|
||||
|
||||
#include "SDL2/SDL.h"
|
||||
|
||||
|
||||
//
|
||||
// This should go to a full theming class
|
||||
//
|
||||
typedef struct Style {
|
||||
SDL_Color backgroundColor;
|
||||
|
||||
SDL_Color selectedColor;
|
||||
SDL_Color planeColor;
|
||||
SDL_Color planeGoneColor;
|
||||
SDL_Color trailColor;
|
||||
|
||||
SDL_Color geoColor;
|
||||
SDL_Color airportColor;
|
||||
|
||||
SDL_Color labelColor;
|
||||
SDL_Color labelLineColor;
|
||||
SDL_Color subLabelColor;
|
||||
SDL_Color labelBackground;
|
||||
|
||||
SDL_Color scaleBarColor;
|
||||
SDL_Color buttonColor;
|
||||
SDL_Color buttonBackground;
|
||||
SDL_Color buttonOutline;
|
||||
|
||||
SDL_Color clickColor;
|
||||
|
||||
SDL_Color black;
|
||||
SDL_Color white;
|
||||
SDL_Color red;
|
||||
SDL_Color green;
|
||||
SDL_Color blue;
|
||||
|
||||
//
|
||||
// todo separate style stuff
|
||||
//
|
||||
|
||||
Style() {
|
||||
|
||||
SDL_Color pink = {249,38,114,255};
|
||||
|
||||
SDL_Color purple = {85, 0, 255,255};
|
||||
SDL_Color purple_dark = {33, 0, 122,255};
|
||||
|
||||
SDL_Color blue = {102,217,239,255};
|
||||
SDL_Color blue_dark = {102,217,239,255};
|
||||
|
||||
SDL_Color green = {0,255,234,255};
|
||||
SDL_Color green_dark = {24,100,110,255};
|
||||
|
||||
SDL_Color yellow = {216,255,0,255};
|
||||
SDL_Color yellow_dark = {90,133,50,255};
|
||||
|
||||
SDL_Color orange = {253,151,31,255};
|
||||
SDL_Color grey_light = {196,196,196,255};
|
||||
SDL_Color grey = {127,127,127,255};
|
||||
SDL_Color grey_dark = {64,64,64,255};
|
||||
|
||||
black = {0,0,0,255};
|
||||
white = {255,255,255,255};
|
||||
red = {255,0,0,255};
|
||||
green = {0,255,0,255};
|
||||
blue = {0,0,255,255};
|
||||
|
||||
|
||||
backgroundColor = black;
|
||||
|
||||
selectedColor = pink;
|
||||
planeColor = yellow;
|
||||
planeGoneColor = grey;
|
||||
trailColor = yellow_dark;
|
||||
|
||||
geoColor = purple_dark;
|
||||
airportColor = purple;
|
||||
|
||||
labelColor = white;
|
||||
labelLineColor = grey_dark;
|
||||
subLabelColor = grey;
|
||||
labelBackground = black;
|
||||
scaleBarColor = grey_light;
|
||||
buttonColor = grey_light;
|
||||
buttonBackground = black;
|
||||
buttonOutline = grey_light;
|
||||
|
||||
clickColor = grey;
|
||||
}
|
||||
} Style;
|
||||
|
||||
#endif
|
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
|
73
Text.cpp
Normal file
73
Text.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "SDL2/SDL2_gfxPrimitives.h"
|
||||
|
||||
#include "Text.h"
|
||||
|
||||
SDL_Rect Text::drawString(std::string text, int x, int y, TTF_Font *font, SDL_Color color)
|
||||
{
|
||||
SDL_Rect dest = {0,0,0,0};
|
||||
|
||||
if(!text.length()) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
SDL_Surface *surface;
|
||||
|
||||
surface = TTF_RenderUTF8_Solid(font, text.c_str(), color);
|
||||
|
||||
if (surface == NULL)
|
||||
{
|
||||
printf("Couldn't create String %s: %s\n", text.c_str(), SDL_GetError());
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
dest.x = x;
|
||||
dest.y = y;
|
||||
dest.w = surface->w;
|
||||
dest.h = surface->h;
|
||||
|
||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
SDL_RenderCopy(renderer, texture, NULL, &dest);
|
||||
SDL_DestroyTexture(texture);
|
||||
SDL_FreeSurface(surface);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
SDL_Rect Text::drawStringBG(std::string text, int x, int y, TTF_Font *font, SDL_Color color, SDL_Color bgColor) {
|
||||
SDL_Rect dest = {0,0,0,0};
|
||||
|
||||
if(!text.length()) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
SDL_Surface *surface;
|
||||
|
||||
surface = TTF_RenderUTF8_Shaded(font, text.c_str(), color, bgColor);
|
||||
|
||||
if (surface == NULL)
|
||||
{
|
||||
printf("Couldn't create String %s: %s\n", text.c_str(), SDL_GetError());
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
dest.x = x;
|
||||
dest.y = y;
|
||||
dest.w = surface->w;
|
||||
dest.h = surface->h;
|
||||
|
||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
SDL_RenderCopy(renderer, texture, NULL, &dest);
|
||||
SDL_DestroyTexture(texture);
|
||||
SDL_FreeSurface(surface);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
// check if text has changed and surface exists
|
||||
// redraw existing surface
|
||||
// or make new surface
|
||||
// for BG, draw bg size of surface
|
||||
// draw surface
|
100
View.h
100
View.h
|
@ -1,19 +1,52 @@
|
|||
// viz1090, a vizualizer for dump1090 ADSB output
|
||||
//
|
||||
// Copyright (C) 2020, Nathan Matsuda <info@nathanmatsuda.com>
|
||||
// Copyright (C) 2014, Malcolm Robb <Support@ATTAvionics.com>
|
||||
// Copyright (C) 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.
|
||||
//
|
||||
// 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 VIEW_H
|
||||
#define VIEW_H
|
||||
|
||||
#include "AppData.h"
|
||||
#include "Map.h"
|
||||
#include "Style.h"
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_ttf.h"
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
|
||||
//defs - should all move to config file setup
|
||||
#define ROUND_RADIUS 3 //radius of text box corners
|
||||
|
||||
#define CENTEROFFSET .375 //vertical offset for middle of screen
|
||||
|
||||
#define TRAIL_LENGTH 120
|
||||
#define TRAIL_TTL 240.0
|
||||
#define DISPLAY_ACTIVE 30
|
||||
#define DISPLAY_ACTIVE 30.0
|
||||
#define TRAIL_TTL_STEP 2
|
||||
|
||||
#define MIN_MAP_FEATURE 2
|
||||
|
@ -22,25 +55,7 @@
|
|||
|
||||
#define PAD 5
|
||||
|
||||
|
||||
|
||||
//
|
||||
// This should go to a full theming class
|
||||
//
|
||||
typedef struct Style {
|
||||
SDL_Color backgroundColor;
|
||||
|
||||
SDL_Color selectedColor;
|
||||
SDL_Color planeColor;
|
||||
SDL_Color planeGoneColor;
|
||||
|
||||
SDL_Color mapInnerColor;
|
||||
SDL_Color mapOuterColor;
|
||||
SDL_Color scaleBarColor;
|
||||
|
||||
SDL_Color buttonColor;
|
||||
} Style;
|
||||
|
||||
#define LATLONMULT 111.195 // 6371.0 * M_PI / 180.0
|
||||
|
||||
|
||||
class View {
|
||||
|
@ -49,19 +64,26 @@ class View {
|
|||
AppData *appData;
|
||||
|
||||
//for cursor drawing
|
||||
uint64_t mouseMovedTime;
|
||||
std::chrono::high_resolution_clock::time_point mouseMovedTime;
|
||||
bool mouseMoved;
|
||||
int mousex;
|
||||
int mousey;
|
||||
|
||||
uint64_t clickTime;
|
||||
std::chrono::high_resolution_clock::time_point clickTime;
|
||||
bool clicked;
|
||||
int clickx;
|
||||
int clicky;
|
||||
|
||||
TTF_Font* loadFont(char *name, int size);
|
||||
int lineCount;
|
||||
|
||||
float dx_mult;
|
||||
float dy_mult;
|
||||
|
||||
TTF_Font* loadFont(const char *name, int size);
|
||||
void closeFont(TTF_Font *font);
|
||||
void drawString(char * text, int x, int y, TTF_Font *font, SDL_Color color);
|
||||
void drawStringBG(char * text, int x, int y, TTF_Font *font, SDL_Color color, SDL_Color bgColor);
|
||||
void drawStatusBox(int *left, int *top, char *label, char *message, SDL_Color color);
|
||||
SDL_Rect drawString(std::string text, int x, int y, TTF_Font *font, SDL_Color color);
|
||||
SDL_Rect drawStringBG(std::string text, int x, int y, TTF_Font *font, SDL_Color color, SDL_Color bgColor);
|
||||
void drawStatusBox(int *left, int *top, std::string label, std::string message, SDL_Color color);
|
||||
void drawStatus();
|
||||
|
||||
Aircraft *selectedAircraft;
|
||||
|
@ -74,16 +96,18 @@ class View {
|
|||
void latLonFromScreenCoords(float *lat, float *lon, int x, int y);
|
||||
void screenCoords(int *outX, int *outY, float dx, float dy);
|
||||
int outOfBounds(int x, int y);
|
||||
int outOfBounds(int x, int y, int left, int top, int right, int bottom);
|
||||
void drawPlaneOffMap(int x, int y, int *returnx, int *returny, SDL_Color planeColor);
|
||||
void drawPlaneIcon(int x, int y, float heading, SDL_Color planeColor);
|
||||
void drawTrail(Aircraft *p);
|
||||
void drawTrails(int left, int top, int right, int bottom);
|
||||
void drawScaleBars();
|
||||
void drawPolys(float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max);
|
||||
void drawLinesRecursive(QuadTree *tree, float screen_lat_min, float screen_lat_max, float screen_lon_min, float screen_lon_max, SDL_Color color);
|
||||
void drawLines(int left, int top, int right, int bottom, int bailTime);
|
||||
void drawPlaceNames();
|
||||
void drawGeography();
|
||||
void drawSignalMarks(Aircraft *p, int x, int y);
|
||||
void drawPlaneText(Aircraft *p);
|
||||
void drawSelectedAircraftText(Aircraft *p);
|
||||
void resolveLabelConflicts();
|
||||
float resolveLabelConflicts();
|
||||
void drawPlanes();
|
||||
void animateCenterAbsolute(float x, float y);
|
||||
void moveCenterAbsolute(float x, float y);
|
||||
|
@ -106,7 +130,10 @@ class View {
|
|||
////////////////
|
||||
bool metric;
|
||||
|
||||
bool fps;
|
||||
|
||||
float maxDist;
|
||||
float currentMaxDist;
|
||||
|
||||
float centerLon;
|
||||
float centerLat;
|
||||
|
@ -116,7 +143,14 @@ class View {
|
|||
float mapTargetLon;
|
||||
|
||||
int mapMoved;
|
||||
uint64_t lastFrameTime;
|
||||
int mapRedraw;
|
||||
int mapAnimating;
|
||||
|
||||
float currentLon;
|
||||
float currentLat;
|
||||
std::chrono::high_resolution_clock::time_point lastFrameTime;
|
||||
std::chrono::high_resolution_clock::time_point drawStartTime;
|
||||
std::chrono::high_resolution_clock::time_point lastRedraw;
|
||||
|
||||
Map map;
|
||||
|
||||
|
|
|
@ -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)
|
6
anet.c
6
anet.c
|
@ -122,7 +122,7 @@ int anetResolve(char *err, char *host, char *ipbuf)
|
|||
struct sockaddr_in sa;
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
if (inet_aton(host, (in_addr*)&sa.sin_addr) == 0) {
|
||||
if (inet_aton(host, &sa.sin_addr) == 0) {
|
||||
struct hostent *he;
|
||||
|
||||
he = gethostbyname(host);
|
||||
|
@ -168,7 +168,7 @@ static int anetTcpGenericConnect(char *err, char *addr, int port, int flags)
|
|||
memset(&sa,0,sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons((uint16_t)port);
|
||||
if (inet_aton(addr, (in_addr*)&sa.sin_addr) == 0) {
|
||||
if (inet_aton(addr, &sa.sin_addr) == 0) {
|
||||
struct hostent *he;
|
||||
|
||||
he = gethostbyname(addr);
|
||||
|
@ -271,7 +271,7 @@ int anetTcpServer(char *err, int port, char *bindaddr)
|
|||
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, (in_addr*)&sa.sin_addr) == 0) {
|
||||
if (bindaddr && inet_aton(bindaddr, &sa.sin_addr) == 0) {
|
||||
anetSetError(err, "invalid bind address");
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
|
|
9
anet.h
9
anet.h
|
@ -39,6 +39,10 @@
|
|||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int anetTcpConnect(char *err, char *addr, int port);
|
||||
int anetTcpNonBlockConnect(char *err, char *addr, int port);
|
||||
int anetUnixConnect(char *err, char *path);
|
||||
|
@ -56,4 +60,9 @@ int anetTcpKeepAlive(char *err, int fd);
|
|||
int anetPeerToString(int fd, char *ip, int *port);
|
||||
int anetSetSendBuffer(char *err, int fd, int buffsize);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -428,7 +428,7 @@ extern "C" {
|
|||
// Functions exported from mode_ac.c
|
||||
//
|
||||
int detectModeA (uint16_t *m, struct modesMessage *mm);
|
||||
void decodeModeAMessage(Modes *modes, struct modesMessage *mm, int ModeA);
|
||||
//void decodeModeAMessage(Modes *modes, struct modesMessage *mm, int ModeA);
|
||||
int ModeAToModeC (unsigned int ModeA);
|
||||
|
||||
//
|
||||
|
@ -449,7 +449,7 @@ struct aircraft* interactiveReceiveData(Modes *modes, struct modesMessage *mm);
|
|||
void interactiveShowData(void);
|
||||
void interactiveRemoveStaleAircrafts(Modes *modes);
|
||||
int decodeBinMessage (Modes *modes, struct client *c, char *p);
|
||||
struct aircraft *interactiveFindAircraft(uint32_t addr);
|
||||
// struct aircraft *interactiveFindAircraft(uint32_t addr);
|
||||
struct stDF *interactiveFindDF (uint32_t addr);
|
||||
|
||||
//
|
||||
|
|
61
flake.lock
Normal file
61
flake.lock
Normal file
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1739758141,
|
||||
"narHash": "sha256-uq6A2L7o1/tR6VfmYhZWoVAwb3gTy7j4Jx30MIrH0rE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c618e28f70257593de75a7044438efc1c1fc0791",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
50
flake.nix
Normal file
50
flake.nix
Normal file
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
# TODO desc
|
||||
description = "viz1090";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in rec
|
||||
{
|
||||
# Nix formatter, run using `$ nix fmt`
|
||||
formatter = pkgs.alejandra;
|
||||
|
||||
# Exports the package
|
||||
# Build with `$ nix build`
|
||||
packages.viz1090 = pkgs.callPackage ./nix/viz1090.nix {inherit self system;};
|
||||
packages.viz1090-mapdata = pkgs.callPackage ./nix/viz1090-mapdata.nix {};
|
||||
packages.default = packages.viz1090;
|
||||
|
||||
# Creates a shell with the necessary dependencies
|
||||
# Enter using `$ nix develop`
|
||||
devShell = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
rtl-sdr-librtlsdr
|
||||
SDL2
|
||||
SDL2_ttf
|
||||
SDL2_gfx
|
||||
gdal
|
||||
python3
|
||||
python312Packages.pip
|
||||
python312Packages.shapely
|
||||
python312Packages.fiona
|
||||
python312Packages.tqdm
|
||||
unzip
|
||||
python312Packages.requests
|
||||
git
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
BIN
font/TerminusTTF-4.46.0.ttf
Normal file
BIN
font/TerminusTTF-4.46.0.ttf
Normal file
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
eafa3a6e408def39bcd002f60a6d9a9810d54234
|
BIN
font/TerminusTTF-Bold-4.46.0.ttf
Normal file
BIN
font/TerminusTTF-Bold-4.46.0.ttf
Normal file
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
9dc2aed0a76993fd7ba9c28e0f55fce873a8c790
|
25
getmap.sh
25
getmap.sh
|
@ -1,5 +1,24 @@
|
|||
#!/bin/bash
|
||||
|
||||
wget -O all.svg.gz https://www.mccurley.org/svg/data/allzips.svgz
|
||||
gunzip all.svg.gz
|
||||
python mapconverter.py all.svg
|
||||
mkdir -p mapdata
|
||||
pushd mapdata > /dev/null
|
||||
|
||||
wget --no-verbose https://naciscdn.org/naturalearth/10m/cultural/ne_10m_admin_1_states_provinces.zip
|
||||
wget --no-verbose https://naciscdn.org/naturalearth/10m/cultural/ne_10m_populated_places.zip
|
||||
wget --no-verbose https://naciscdn.org/naturalearth/10m/cultural/ne_10m_airports.zip
|
||||
|
||||
#this may not be up to date
|
||||
wget --no-verbose https://opendata.arcgis.com/datasets/4d8fa46181aa470d809776c57a8ab1f6_0.zip
|
||||
|
||||
for file in *.zip; do
|
||||
unzip -o "${file}"
|
||||
rm "${file}"
|
||||
done
|
||||
|
||||
popd > /dev/null
|
||||
|
||||
python3 mapconverter.py \
|
||||
--mapfile mapdata/ne_10m_admin_1_states_provinces.shp \
|
||||
--mapnames mapdata/ne_10m_populated_places.shp \
|
||||
--airportfile mapdata/Runways.shp \
|
||||
--airportnames mapdata/ne_10m_airports.shp
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
85bf9e07a831ad5a30abf7ae4737f9c8a543f9e2
|
|
@ -1,6 +0,0 @@
|
|||
curl "https://api.mapbox.com/v4/mapbox.mapbox-streets-v8/8/40/89.mvt?access_token=pk.eyJ1Ijoibm1hdHN1ZGEiLCJhIjoiY2swazhrdGNjMGZ3NzNvcmE0OGxoaGd2byJ9.PAYHqO3any_6vLdjQ44RGw"
|
||||
|
||||
mapbox://styles/nmatsuda/ck0k7rvvt3rkv1cq6l74wjda2
|
||||
|
||||
|
||||
47.695,-123.489
|
142
mapconverter.py
142
mapconverter.py
|
@ -1,27 +1,133 @@
|
|||
from lxml import etree as ElementTree
|
||||
import fiona
|
||||
from shapely.geometry import shape
|
||||
import numpy as np
|
||||
import sys
|
||||
from tqdm import tqdm
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
#from urllib.request import urlopen
|
||||
import requests
|
||||
import argparse
|
||||
import os
|
||||
|
||||
filename = sys.argv[1]
|
||||
def convertLinestring(linestring):
|
||||
outlist = []
|
||||
|
||||
if(len(filename) == 0):
|
||||
print "No input filename given"
|
||||
exit()
|
||||
pointx = linestring.coords.xy[0]
|
||||
pointy = linestring.coords.xy[1]
|
||||
|
||||
parser = ElementTree.XMLParser(recover=True)
|
||||
tree = ElementTree.parse(filename, parser)
|
||||
polys = tree.xpath('//polygon')
|
||||
for j in range(len(pointx)):
|
||||
outlist.extend([float(pointx[j]),float(pointy[j])])
|
||||
|
||||
bin_file = open("mapdata.bin", "wt")
|
||||
outlist.extend([0,0])
|
||||
return outlist
|
||||
|
||||
outlist = []
|
||||
def extractLines(shapefile, tolerance):
|
||||
print("Extracting map lines")
|
||||
outlist = []
|
||||
|
||||
for p in polys:
|
||||
currentPoints = (p.attrib['points']).replace(","," ").split()
|
||||
for i in tqdm(range(len(shapefile))):
|
||||
|
||||
if(len(currentPoints) > 12): #remove little circles in the McCurley maps
|
||||
outlist.extend(currentPoints)
|
||||
outlist.extend(["0","0"])
|
||||
if(tolerance > 0):
|
||||
simplified = shape(shapefile[i]['geometry']).simplify(tolerance, preserve_topology=False)
|
||||
else:
|
||||
simplified =shape(shapefile[i]['geometry'])
|
||||
|
||||
if(simplified.geom_type == "LineString"):
|
||||
outlist.extend(convertLinestring(simplified))
|
||||
|
||||
elif(simplified.geom_type == "MultiPolygon" or simplified.geom_type == "Polygon"):
|
||||
|
||||
if(simplified.boundary.geom_type == "MultiLineString"):
|
||||
for boundary in simplified.boundary.geoms:
|
||||
outlist.extend(convertLinestring(boundary))
|
||||
else:
|
||||
outlist.extend(convertLinestring(simplified.boundary))
|
||||
|
||||
else:
|
||||
print("Unsupported type: " + simplified.geom_type)
|
||||
|
||||
|
||||
|
||||
return outlist
|
||||
|
||||
parser = argparse.ArgumentParser(description='viz1090 Natural Earth Data Map Converter')
|
||||
parser.add_argument("--mapfile", type=str, help="shapefile for main map")
|
||||
parser.add_argument("--mapnames", type=str, help="shapefile for map place names")
|
||||
parser.add_argument("--airportfile", type=str, help="shapefile for airport runway outlines")
|
||||
parser.add_argument("--airportnames", type=str, help="shapefile for airport IATA names")
|
||||
parser.add_argument("--minpop", default=100000, type=int, help="map simplification tolerance")
|
||||
parser.add_argument("--tolerance", default=0.001, type=float, help="map simplification tolerance")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# mapfile
|
||||
if args.mapfile is not None:
|
||||
shapefile = fiona.open(args.mapfile)
|
||||
|
||||
outlist = extractLines(shapefile, args.tolerance)
|
||||
|
||||
bin_file = open("mapdata.bin", "wb")
|
||||
np.asarray(outlist).astype(np.single).tofile(bin_file)
|
||||
bin_file.close()
|
||||
|
||||
print("Wrote %d points" % (len(outlist) / 2))
|
||||
|
||||
# mapnames
|
||||
bin_file = open("mapnames", "w")
|
||||
|
||||
if args.mapnames is not None:
|
||||
shapefile = fiona.open(args.mapnames)
|
||||
|
||||
count = 0
|
||||
|
||||
for i in tqdm(range(len(shapefile))):
|
||||
|
||||
xcoord = shapefile[i]['geometry']['coordinates'][0]
|
||||
ycoord = shapefile[i]['geometry']['coordinates'][1]
|
||||
pop = shapefile[i]['properties']['POP_MIN']
|
||||
name = shapefile[i]['properties']['NAME']
|
||||
|
||||
if pop > args.minpop:
|
||||
outstring = "{0} {1} {2}\n".format(xcoord, ycoord, name)
|
||||
bin_file.write(outstring)
|
||||
count = count + 1
|
||||
|
||||
bin_file.close()
|
||||
|
||||
print("Wrote %d place names" % count)
|
||||
|
||||
#airportfile
|
||||
if args.airportfile is not None:
|
||||
shapefile = fiona.open(args.airportfile)
|
||||
|
||||
outlist = extractLines(shapefile, 0)
|
||||
|
||||
bin_file = open("airportdata.bin", "wb")
|
||||
np.asarray(outlist).astype(np.single).tofile(bin_file)
|
||||
bin_file.close()
|
||||
|
||||
print("Wrote %d points" % (len(outlist) / 2))
|
||||
|
||||
|
||||
#airportnames
|
||||
if args.airportnames is not None:
|
||||
bin_file = open("airportnames", "w")
|
||||
|
||||
shapefile = fiona.open(args.airportnames)
|
||||
|
||||
count = 0
|
||||
|
||||
for i in tqdm(range(len(shapefile))):
|
||||
|
||||
xcoord = shapefile[i]['geometry']['coordinates'][0]
|
||||
ycoord = shapefile[i]['geometry']['coordinates'][1]
|
||||
name = shapefile[i]['properties']['iata_code']
|
||||
|
||||
outstring = "{0} {1} {2}\n".format(xcoord, ycoord, name)
|
||||
bin_file.write(outstring)
|
||||
count = count + 1
|
||||
|
||||
bin_file.close()
|
||||
|
||||
print("Wrote %d airport names" % count)
|
||||
|
||||
np.asarray(outlist).astype(np.single).tofile(bin_file)
|
||||
bin_file.close()
|
2
mode_s.c
2
mode_s.c
|
@ -210,7 +210,7 @@ int fixBitErrors(unsigned char *msg, int bits, int maxfix, char *fixedbits) {
|
|||
int bitpos, offset, res, i;
|
||||
memset(&ei, 0, sizeof(struct errorinfo));
|
||||
ei.syndrome = modesChecksum(msg, bits);
|
||||
pei = (errorinfo*)bsearch(&ei, bitErrorTable, NERRORINFO,
|
||||
pei = bsearch(&ei, bitErrorTable, NERRORINFO,
|
||||
sizeof(struct errorinfo), cmpErrorInfo);
|
||||
if (pei == NULL) {
|
||||
return 0; // No syndrome found
|
||||
|
|
69
nix/viz1090-mapdata.nix
Normal file
69
nix/viz1090-mapdata.nix
Normal file
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
stdenv,
|
||||
fetchFromGitea,
|
||||
}: let
|
||||
runways = fetchTarball {
|
||||
url = "https://opendata.arcgis.com/datasets/4d8fa46181aa470d809776c57a8ab1f6_0.zip";
|
||||
sha256 = "sha256:1ivwx8glk8yk68nmqz467yzvlb3l66l1s3ibmd9p41wz737lmz88";
|
||||
};
|
||||
provinces = fetchTarball {
|
||||
url = "https://naciscdn.org/naturalearth/10m/cultural/ne_10m_admin_1_states_provinces.zip";
|
||||
sha256 = "sha256:06ai02b8rfsfzpa0gq4nsg29lxvwy4zvjw44099hc78vr7dkfsdp";
|
||||
};
|
||||
places = fetchTarball {
|
||||
url = "https://naciscdn.org/naturalearth/10m/cultural/ne_10m_populated_places.zip";
|
||||
sha256 = "sha256:1dhh569520f02yml1m5zp2znjv85cqbccl4nvpmigynxd37kid3j";
|
||||
};
|
||||
airports = fetchTarball {
|
||||
url = "https://naciscdn.org/naturalearth/10m/cultural/ne_10m_airports.zip";
|
||||
sha256 = "sha256:0893zg63ygr2l2d1wpyigls1syfkryjlygvnbbjikpqk5i5cwr56";
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "viz1090-mapdata";
|
||||
version = "0.1.0";
|
||||
|
||||
src = fetchFromGitea {
|
||||
domain = "git.vulpecula.zone";
|
||||
owner = "tasiaiso";
|
||||
repo = pname;
|
||||
rev = "d1f53019b22a9e605506bed90fcffcdc5f7e6186";
|
||||
hash = "sha256-gtv0u7o+5fqVgA0CHDWdZr0h9A1Nbky1+okHvSv1cVU=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
python3
|
||||
python312Packages.pip
|
||||
python312Packages.shapely
|
||||
python312Packages.fiona
|
||||
python312Packages.tqdm
|
||||
python312Packages.requests
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
mkdir -p mapdata
|
||||
|
||||
cp ${runways}/* mapdata
|
||||
cp ${provinces}/* mapdata
|
||||
cp ${places}/* mapdata
|
||||
cp ${airports}/* mapdata
|
||||
# ls -al mapdata
|
||||
|
||||
python3 mapconverter.py \
|
||||
--mapfile mapdata/ne_10m_admin_1_states_provinces.shp \
|
||||
--mapnames mapdata/ne_10m_populated_places.shp \
|
||||
--airportfile mapdata/Runways.shp \
|
||||
--airportnames mapdata/ne_10m_airports.shp
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
|
||||
cp airportdata.bin $out
|
||||
cp airportnames $out
|
||||
cp mapdata.bin $out
|
||||
cp mapnames $out
|
||||
'';
|
||||
}
|
93
nix/viz1090.nix
Normal file
93
nix/viz1090.nix
Normal file
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
stdenv,
|
||||
fetchFromGitea,
|
||||
self,
|
||||
system,
|
||||
}: let
|
||||
viz1090-mapdata = self.packages.${system}.viz1090-mapdata;
|
||||
in
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "viz1090";
|
||||
version = "0.1.0";
|
||||
|
||||
src = fetchFromGitea {
|
||||
domain = "git.vulpecula.zone";
|
||||
owner = "tasiaiso";
|
||||
repo = pname;
|
||||
rev = "1922324b40f84fd449cec3fbfdade8aa33597bf6";
|
||||
hash = "sha256-bPVFKbGtPXOitzzHb3yJ6XW3fRh8PF/7kfP7EJkJX3c=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
rtl-sdr-librtlsdr
|
||||
SDL2
|
||||
SDL2_ttf
|
||||
SDL2_gfx
|
||||
gdal
|
||||
git
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
viz1090-mapdata
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
echo "diff --git a/Makefile b/Makefile
|
||||
index 5e60779..d5b30ab 100644
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -3,7 +3,7 @@
|
||||
# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local
|
||||
#
|
||||
|
||||
-CXXFLAGS=-O2 -std=c++11 -g
|
||||
+CXXFLAGS=-O2 -std=c++11 -g -I ${pkgs.SDL2.dev}/include/SDL2/
|
||||
LIBS= -lm -lSDL2 -lSDL2_ttf -lSDL2_gfx -g
|
||||
CXX=g++
|
||||
|
||||
diff --git a/Map.cpp b/Map.cpp
|
||||
index cd798ec..c5736bd 100644
|
||||
--- a/Map.cpp
|
||||
+++ b/Map.cpp
|
||||
@@ -189,7 +189,7 @@ std::vector<Line*> Map::getLines(float screen_lat_min, float screen_lat_max, flo
|
||||
Map::Map() {
|
||||
FILE *fileptr;
|
||||
|
||||
- if((fileptr = fopen(\"mapdata.bin\", \"rb\"))) {
|
||||
+ if((fileptr = fopen(\"${viz1090-mapdata}/mapdata.bin\", \"rb\"))) {
|
||||
|
||||
|
||||
fseek(fileptr, 0, SEEK_END);
|
||||
@@ -255,7 +255,7 @@ Map::Map() {
|
||||
//
|
||||
|
||||
|
||||
- if((fileptr = fopen(\"airportdata.bin\", \"rb\"))) {
|
||||
+ if((fileptr = fopen(\"${viz1090-mapdata}/airportdata.bin\", \"rb\"))) {
|
||||
fseek(fileptr, 0, SEEK_END);
|
||||
airportPoints_count = ftell(fileptr) / sizeof(float);
|
||||
rewind(fileptr);
|
||||
@@ -350,7 +350,7 @@ Map::Map() {
|
||||
|
||||
infile.close();
|
||||
|
||||
- infile.open(\"airportnames\");
|
||||
+ infile.open(\"${viz1090-mapdata}/airportnames\");
|
||||
|
||||
|
||||
while (std::getline(infile, line))
|
||||
|
||||
" | git apply -
|
||||
|
||||
cat Map.cpp | grep mapdata
|
||||
|
||||
make -j $NIX_BUILD_CORES
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp -v viz1090 $out/bin
|
||||
'';
|
||||
}
|
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)
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue