1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-06-24 13:05:21 -04:00

Merge pull request #2465 from srcejon/freq_scanner

Map and ADS-B updates
This commit is contained in:
Edouard Griffiths 2025-06-13 14:38:33 +02:00 committed by GitHub
commit e5fc6fbd81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12750 changed files with 65166 additions and 5684 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 KiB

After

Width:  |  Height:  |  Size: 844 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 570 KiB

After

Width:  |  Height:  |  Size: 819 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 KiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 547 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

BIN
doc/img/Map_plugin_pfd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 KiB

View File

@ -28,7 +28,6 @@ set(adsb_HEADERS
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${Boost_INCLUDE_DIRS}
)
if(NOT SERVER_MODE)
@ -47,6 +46,7 @@ if(NOT SERVER_MODE)
adsbdemodicons.qrc
airlinelogos.qrc
flags.qrc
sideviews.qrc
)
set(adsb_HEADERS
${adsb_HEADERS}
@ -60,7 +60,7 @@ if(NOT SERVER_MODE)
)
set(TARGET_NAME ${PLUGINS_PREFIX}demodadsb)
set(TARGET_LIB Qt::Widgets Qt::Quick Qt::QuickWidgets Qt::Positioning)
set(TARGET_LIB Qt::Widgets Qt::Quick Qt::QuickWidgets Qt::Positioning Qt::Charts)
if(Qt${QT_DEFAULT_MAJOR_VERSION}Location_FOUND)
list(APPEND TARGET_LIB Qt::Location)
endif()
@ -86,12 +86,7 @@ if(NOT BUILD_SHARED_LIBS)
set_property(GLOBAL APPEND PROPERTY STATIC_PLUGINS_PROPERTY ${TARGET_NAME})
endif()
if (NOT WIN32)
link_directories(${Boost_LIBRARY_DIRS})
endif()
target_link_libraries(${TARGET_NAME} PRIVATE
Boost::disable_autolinking
Qt::Core
${TARGET_LIB}
sdrbase

View File

@ -17,10 +17,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/chrono/chrono.hpp>
#include <stdio.h>
#include <complex.h>
#include <QTime>
@ -49,6 +45,7 @@
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgConfigureADSBDemod, Message)
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgAircraftReport, Message)
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgResetStats, Message)
const char* const ADSBDemod::m_channelIdURI = "sdrangel.channel.adsbdemod";
const char* const ADSBDemod::m_channelId = "ADSBDemod";
@ -194,6 +191,13 @@ bool ADSBDemod::handleMessage(const Message& cmd)
m_aircraftReport = msg.getReport();
return true;
}
else if (MsgResetStats::match(cmd))
{
MsgResetStats& msg = (MsgResetStats&) cmd;
MsgResetStats* rep = new MsgResetStats(msg);
m_basebandSink->getInputMessageQueue()->push(rep);
return true;
}
else
{
return false;
@ -337,9 +341,6 @@ void ADSBDemod::webapiUpdateChannelSettings(
if (channelSettingsKeys.contains("samplesPerBit")) {
settings.m_samplesPerBit = response.getAdsbDemodSettings()->getSamplesPerBit();
}
if (channelSettingsKeys.contains("correlateFullPreamble")) {
settings.m_correlateFullPreamble = response.getAdsbDemodSettings()->getCorrelateFullPreamble() != 0;
}
if (channelSettingsKeys.contains("demodModeS")) {
settings.m_demodModeS = response.getAdsbDemodSettings()->getDemodModeS() != 0;
}
@ -458,7 +459,6 @@ void ADSBDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res
response.getAdsbDemodSettings()->setRfBandwidth(settings.m_rfBandwidth);
response.getAdsbDemodSettings()->setCorrelationThreshold(settings.m_correlationThreshold);
response.getAdsbDemodSettings()->setSamplesPerBit(settings.m_samplesPerBit);
response.getAdsbDemodSettings()->setCorrelateFullPreamble(settings.m_correlateFullPreamble ? 1 : 0);
response.getAdsbDemodSettings()->setDemodModeS(settings.m_demodModeS ? 1 : 0);
response.getAdsbDemodSettings()->setInterpolatorPhaseSteps(settings.m_interpolatorPhaseSteps);
response.getAdsbDemodSettings()->setInterpolatorTapsPerPhase(settings.m_interpolatorTapsPerPhase);
@ -587,9 +587,6 @@ void ADSBDemod::webapiReverseSendSettings(const QList<QString>& channelSettingsK
if (channelSettingsKeys.contains("samplesPerBit") || force) {
swgADSBDemodSettings->setSamplesPerBit(settings.m_samplesPerBit);
}
if (channelSettingsKeys.contains("correlateFullPreamble") || force) {
swgADSBDemodSettings->setCorrelateFullPreamble(settings.m_correlateFullPreamble ? 1 : 0);
}
if (channelSettingsKeys.contains("demodModeS") || force) {
swgADSBDemodSettings->setDemodModeS(settings.m_demodModeS ? 1 : 0);
}

View File

@ -92,6 +92,21 @@ public:
{ }
};
class MsgResetStats : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgResetStats* create()
{
return new MsgResetStats();
}
private:
MsgResetStats() :
Message()
{ }
};
ADSBDemod(DeviceAPI *deviceAPI);
virtual ~ADSBDemod();
virtual void destroy() { delete this; }

View File

@ -23,6 +23,7 @@
#include "dsp/downchannelizer.h"
#include "adsbdemodbaseband.h"
#include "adsbdemod.h"
#include "adsb.h"
MESSAGE_CLASS_DEFINITION(ADSBDemodBaseband::MsgConfigureADSBDemodBaseband, Message)
@ -132,6 +133,11 @@ bool ADSBDemodBaseband::handleMessage(const Message& cmd)
return true;
}
else if (ADSBDemod::MsgResetStats::match(cmd))
{
m_sink.resetStats();
return true;
}
else
{
return false;

View File

@ -50,7 +50,6 @@ ADSBDemodDisplayDialog::ADSBDemodDisplayDialog(ADSBDemodSettings *settings, QWid
ui->airportSize->setCurrentIndex((int)settings->m_airportMinimumSize);
ui->heliports->setChecked(settings->m_displayHeliports);
ui->units->setCurrentIndex((int)settings->m_siUnits);
ui->displayStats->setChecked(settings->m_displayDemodStats);
ui->autoResizeTableColumns->setChecked(settings->m_autoResizeTableColumns);
ui->aviationstackAPIKey->setText(settings->m_aviationstackAPIKey);
ui->checkWXAPIKey->setText(settings->m_checkWXAPIKey);
@ -67,12 +66,17 @@ ADSBDemodDisplayDialog::ADSBDemodDisplayDialog(ADSBDemodSettings *settings, QWid
ui->mapProvider->setCurrentText(settings->m_mapProvider);
}
ui->mapType->setCurrentIndex((int)settings->m_mapType);
ui->maptilerAPIKey->setText(m_settings->m_maptilerAPIKey);
ui->navAids->setChecked(settings->m_displayNavAids);
ui->atcCallsigns->setChecked(settings->m_atcCallsigns);
ui->photos->setChecked(settings->m_displayPhotos);
ui->verboseModelMatching->setChecked(settings->m_verboseModelMatching);
ui->airfieldElevation->setValue(settings->m_airfieldElevation);
ui->favourLivery->setChecked(settings->m_favourLivery);
ui->transitionAltitude->setValue(settings->m_transitionAlt);
for (auto i = ADSBDemodSettings::m_palettes.cbegin(), end = ADSBDemodSettings::m_palettes.cend(); i != end; ++i) {
ui->flightPathPalette->addItem(i.key());
}
ui->flightPathPalette->setCurrentText(settings->m_flightPathPaletteName);
}
ADSBDemodDisplayDialog::~ADSBDemodDisplayDialog()
@ -107,16 +111,11 @@ void ADSBDemodDisplayDialog::accept()
m_settings->m_displayHeliports = ui->heliports->isChecked();
m_settingsKeys.append("displayHeliports");
}
if (m_settings->m_siUnits != ui->units->currentIndex() == 0 ? false : true)
if (m_settings->m_siUnits != (ui->units->currentIndex() == 0 ? false : true))
{
m_settings->m_siUnits = ui->units->currentIndex() == 0 ? false : true;
m_settingsKeys.append("siUnits");
}
if (m_settings->m_displayDemodStats != ui->displayStats->isChecked())
{
m_settings->m_displayDemodStats = ui->displayStats->isChecked();
m_settingsKeys.append("displayDemodStats");
}
if (m_settings->m_autoResizeTableColumns != ui->autoResizeTableColumns->isChecked())
{
m_settings->m_autoResizeTableColumns = ui->autoResizeTableColumns->isChecked();
@ -160,6 +159,11 @@ void ADSBDemodDisplayDialog::accept()
m_settings->m_mapType = (ADSBDemodSettings::MapType)ui->mapType->currentIndex();
m_settingsKeys.append("mapType");
}
if (m_settings->m_maptilerAPIKey != ui->maptilerAPIKey->text())
{
m_settings->m_maptilerAPIKey = ui->maptilerAPIKey->text();
m_settingsKeys.append("maptilerAPIKey");
}
if (m_settings->m_displayNavAids != ui->navAids->isChecked())
{
m_settings->m_displayNavAids = ui->navAids->isChecked();
@ -180,10 +184,10 @@ void ADSBDemodDisplayDialog::accept()
m_settings->m_verboseModelMatching = ui->verboseModelMatching->isChecked();
m_settingsKeys.append("verboseModelMatching");
}
if (m_settings->m_airfieldElevation != ui->airfieldElevation->value())
if (m_settings->m_favourLivery != ui->favourLivery->isChecked())
{
m_settings->m_airfieldElevation = ui->airfieldElevation->value();
m_settingsKeys.append("airfieldElevation");
m_settings->m_favourLivery = ui->favourLivery->isChecked();
m_settingsKeys.append("favourLivery");
}
if (m_settings->m_transitionAlt != ui->transitionAltitude->value())
{
@ -200,6 +204,12 @@ void ADSBDemodDisplayDialog::accept()
m_settings->m_tableFontSize = m_fontSize;
m_settingsKeys.append("tableFontSize");
}
if (m_settings->m_flightPathPaletteName != ui->flightPathPalette->currentText())
{
m_settings->m_flightPathPaletteName = ui->flightPathPalette->currentText();
m_settingsKeys.append("flightPathPaletteName");
m_settings->applyPalette();
}
QDialog::accept();
}

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>417</width>
<height>467</height>
<height>505</height>
</rect>
</property>
<property name="font">
@ -124,22 +124,16 @@
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="displayStatsLabel">
<widget class="QLabel" name="verboseModelMatchingLabel">
<property name="text">
<string>Display demodulator statistics</string>
<string>Log 3D model matching information</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="displayStats">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class="QCheckBox" name="verboseModelMatching">
<property name="toolTip">
<string>Display demodulator statistics</string>
<string>Log information about how aircraft are matched to 3D models</string>
</property>
<property name="text">
<string/>
@ -147,16 +141,16 @@
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="verboseModelMatchingLabel">
<widget class="QLabel" name="favourLiveryLabel">
<property name="text">
<string>Log 3D model matching information</string>
<string>Favour airline livery over aircraft type</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="verboseModelMatching">
<widget class="QCheckBox" name="favourLivery">
<property name="toolTip">
<string>Log information about how aircraft are matched to 3D models</string>
<string>Favour airline livery over aircraft type for 3D models</string>
</property>
<property name="text">
<string/>
@ -192,36 +186,13 @@
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="airfieldElevationLabel">
<property name="text">
<string>Airfield barometric altitude (ft)</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QSpinBox" name="airfieldElevation">
<property name="toolTip">
<string>Barometric altitude reported by aircraft when on airfield surface</string>
</property>
<property name="minimum">
<number>-10000</number>
</property>
<property name="maximum">
<number>30000</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="transitionAltitudeLabel">
<property name="text">
<string>Transition altitude (ft)</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="9" column="1">
<widget class="QSpinBox" name="transitionAltitude">
<property name="toolTip">
<string>Transition altitude in feet</string>
@ -232,6 +203,9 @@
<property name="maximum">
<number>20000</number>
</property>
<property name="value">
<number>6000</number>
</property>
</widget>
</item>
</layout>
@ -303,14 +277,14 @@
</item>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="aircraftMinZoomLabel">
<property name="text">
<string>Zoom level for aircraft scaling</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QSpinBox" name="aircraftMinZoom">
<property name="toolTip">
<string>When map zoom (0 min zoom - 15 max zoom) is higher than this value, aircraft icon size will be scaled</string>
@ -320,14 +294,14 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="airportSizeLabel">
<property name="text">
<string>Display airports with size</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QComboBox" name="airportSize">
<property name="toolTip">
<string>Sets the minimum airport size that will be displayed on the map</string>
@ -349,14 +323,14 @@
</item>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="heliportsLabel">
<property name="text">
<string>Display heliports</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QCheckBox" name="heliports">
<property name="toolTip">
<string>When checked, heliports are displayed on the map</string>
@ -366,14 +340,14 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="airportRangeLabel">
<property name="text">
<string>Airport display distance (km)</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QSpinBox" name="airportRange">
<property name="toolTip">
<string>Displays airports within the specified distance in kilometres from My Position</string>
@ -383,14 +357,14 @@
</property>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="airspacesLabel">
<property name="text">
<string>Airspaces to display</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QListWidget" name="airspaces">
<property name="toolTip">
<string>Airspace categories to display</string>
@ -547,14 +521,14 @@
</item>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QLabel" name="airspaceRangeLabel">
<property name="text">
<string>Airspace display distance (km)</string>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="8" column="1">
<widget class="QSpinBox" name="airspaceRange">
<property name="toolTip">
<string>Displays airspace within the specified distance in kilometres from My Position</string>
@ -564,14 +538,14 @@
</property>
</widget>
</item>
<item row="8" column="0">
<item row="9" column="0">
<widget class="QLabel" name="displayNavAids">
<property name="text">
<string>Display NAVAIDs</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="9" column="1">
<widget class="QCheckBox" name="navAids">
<property name="toolTip">
<string>Display NAVAIDs such as VORs and NDBs</string>
@ -581,7 +555,7 @@
</property>
</widget>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QCheckBox" name="atcCallsigns">
<property name="toolTip">
<string>Use ATC callsigns (SPEEDBIRD) rather than ICAO (BAW) for aircraft labels on map</string>
@ -591,13 +565,44 @@
</property>
</widget>
</item>
<item row="9" column="0">
<item row="10" column="0">
<widget class="QLabel" name="atcCallsignsLabel">
<property name="text">
<string>Use ATC callsigns on map</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QComboBox" name="flightPathPalette">
<property name="toolTip">
<string>Colour palette to use for aircraft flight paths</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="flightPathPaletteLabel">
<property name="text">
<string>Flight path palette</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string/>
</property>
<property name="text">
<string>Maptiler API key</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="maptilerAPIKey">
<property name="toolTip">
<string>API key for Maptiler (https://maptiler.com) for satellite map</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@ -607,10 +612,10 @@
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@
#include <QTextToSpeech>
#include <QRandomGenerator>
#include <QNetworkAccessManager>
#include <QtCharts>
#include "channel/channelgui.h"
#include "dsp/dsptypes.h"
@ -56,11 +57,17 @@ class WebAPIAdapterInterface;
class HttpDownloadManager;
class ADSBDemodGUI;
class ADSBOSMTemplateServer;
class CheckList;
class AircraftModel;
namespace Ui {
class ADSBDemodGUI;
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
using namespace QtCharts;
#endif
// Custom widget to allow formatted decimal numbers to be sorted numerically
class CustomDoubleTableWidgetItem : public QTableWidgetItem
{
@ -89,12 +96,16 @@ struct Aircraft {
QString m_icaoHex;
QString m_callsign; // Flight callsign
QString m_flight; // Guess at flight number
bool m_globalPosition; // Position has been determined from global decode
Real m_latitude; // Latitude in decimal degrees
Real m_longitude; // Longitude in decimal degrees
int m_altitude; // Altitude in feet
float m_radius; // Horizontal containment radius limit (Rc) in metres
int m_altitude; // Altitude in feet (will be 0 if on surface)
int m_pressureAltitude; // Pressure altitude in feet for Map PFD altimeter (can be negative on surface)
bool m_onSurface; // Indicates if on surface or airborne
bool m_altitudeGNSS; // Altitude is GNSS HAE (Height above WGS-84 ellipsoid) rather than barometric alitute (relative to 29.92 Hg)
float m_heading; // Heading or track in degrees
float m_heading; // Heading in degrees magnetic
float m_track; // Track in degrees true?
int m_verticalRate; // Vertical climb rate in ft/min
QString m_emitterCategory; // Aircraft type
QString m_status; // Aircraft status
@ -102,23 +113,37 @@ struct Aircraft {
Real m_range; // Distance from station to aircraft
Real m_azimuth; // Azimuth from station to aircraft
Real m_elevation; // Elevation from station to aircraft
QDateTime m_time; // When last updated
QDateTime m_rxTime; // When last frame received (can be long ago if reading from log file)
QDateTime m_updateTime; // Last time we updated data for this aircraft (used for determining when to remove an aircraft)
int m_selAltitude; // Selected altitude in MCP/FCU or FMS in feet
int m_selHeading; // Selected heading in MCP/FCU in degrees
int m_baro; // Aircraft baro setting in mb (Mode-S)
float m_baro; // Aircraft baro setting in mb (Mode-S)
float m_roll; // In degrees
int m_groundspeed; // In knots
float m_turnRate; // In degrees per second
int m_trueAirspeed; // In knots
int m_indicatedAirspeed; // In knots
float m_mach; // Mach number
bool m_autopilot;
bool m_vnavMode;
bool m_altHoldMode;
bool m_approachMode;
bool m_lnavMode;
bool m_tcasOperational; // Appears only to be true if TA/RA, false if TA ONLY
bool m_bdsCapabilities[16][16]; // BDS capabilities are indicaited by BDS 1.7
int m_adsbVersion;
bool m_nicSupplementA;
bool m_nicSupplementB;
bool m_nicSupplementC;
bool m_positionValid; // Indicates if we have valid data for the above fields
bool m_altitudeValid;
bool m_pressureAltitudeValid;
bool m_onSurfaceValid;
bool m_headingValid;
bool m_trackValid;
bool m_verticalRateValid;
bool m_selAltitudeValid;
bool m_selHeadingValid;
@ -129,16 +154,30 @@ struct Aircraft {
bool m_trueAirspeedValid;
bool m_indicatedAirspeedValid;
bool m_machValid;
bool m_autopilotValid;
bool m_vnavModeValid;
bool m_altHoldModeValid;
bool m_approachModeValid;
bool m_lnavModeValid;
bool m_tcasOperationalValid;
bool m_bdsCapabilitiesValid;
bool m_adsbVersionValid;
bool m_nicSupplementAValid;
bool m_nicSupplementBValid;
bool m_nicSupplementCValid;
// State for calculating position using two CPR frames
bool m_cprValid[2];
Real m_cprLat[2];
Real m_cprLong[2];
double m_cprLat[2];
double m_cprLong[2];
QDateTime m_cprTime[2];
int m_adsbFrameCount; // Number of ADS-B frames for this aircraft
int m_modesFrameCount; // Number of Mode-S frames for this aircraft
int m_nonTransponderFrameCount;
int m_tisBFrameCount;
int m_adsrFrameCount;
float m_minCorrelation;
float m_maxCorrelation;
float m_correlation;
@ -148,8 +187,12 @@ struct Aircraft {
bool m_isHighlighted; // Are we highlighting this aircraft in the table and map
bool m_showAll;
QVariantList m_coordinates; // Coordinates we've recorded the aircraft at
QList<QVariantList> m_coordinates; // Coordinates we've recorded the aircraft at, split up in to altitude ranges
QList<QVariantList> m_recentCoordinates; // Last 20 seconds of coordinates for ATC mode
QList<QDateTime> m_coordinateDateTimes;
QList<int> m_coordinateColors; // 0-7 index to 8 color palette according to altitude
QList<int> m_recentCoordinateColors;
int m_lastColor;
AircraftInformation *m_aircraftInfo; // Info about the aircraft from the database
QString m_aircraft3DModel; // 3D model for map based on aircraft type
@ -159,6 +202,7 @@ struct Aircraft {
ADSBDemodGUI *m_gui;
QString m_flagIconURL;
QString m_airlineIconURL;
QString m_sideviewIconURL;
// For animation on 3D map
float m_runwayAltitude;
@ -168,10 +212,14 @@ struct Aircraft {
bool m_rotorStarted; // Rotors started on 'Rotorcraft'
bool m_engineStarted; // Engines started (typically propellors)
QDateTime m_positionDateTime;
QDateTime m_orientationDateTime;
QDateTime m_orientationDateTime; // FIXME
QDateTime m_headingDateTime;
QDateTime m_prevHeadingDateTime;
int m_prevHeading;
QDateTime m_trackDateTime;
QDateTime m_altitudeDateTime;
QDateTime m_indicatedAirspeedDateTime;
QDateTime m_prevTrackDateTime;
int m_prevTrack;
float m_trackWhenHeadingSet;
float m_pitchEst; // Estimated pitch based on vertical rate
float m_rollEst; // Estimated roll based on rate of change in heading
@ -182,30 +230,48 @@ struct Aircraft {
QTableWidgetItem *m_callsignItem;
QTableWidgetItem* m_atcCallsignItem;
QTableWidgetItem *m_modelItem;
QTableWidgetItem *m_typeItem;
QTableWidgetItem *m_sideviewItem;
QTableWidgetItem *m_airlineItem;
QTableWidgetItem *m_latitudeItem;
QTableWidgetItem *m_longitudeItem;
QTableWidgetItem *m_altitudeItem;
QTableWidgetItem *m_headingItem;
QTableWidgetItem *m_trackItem;
QTableWidgetItem *m_verticalRateItem;
CustomDoubleTableWidgetItem *m_rangeItem;
QTableWidgetItem *m_azElItem;
QTableWidgetItem *m_emitterCategoryItem;
QTableWidgetItem *m_statusItem;
QTableWidgetItem *m_squawkItem;
QTableWidgetItem *m_identItem;
QTableWidgetItem *m_registrationItem;
QTableWidgetItem *m_countryItem;
QTableWidgetItem *m_registeredItem;
QTableWidgetItem *m_manufacturerNameItem;
QTableWidgetItem *m_ownerItem;
QTableWidgetItem *m_operatorICAOItem;
QTableWidgetItem *m_interogatorCodeItem;
QTableWidgetItem *m_timeItem;
QTableWidgetItem *m_totalFrameCountItem;
QTableWidgetItem *m_adsbFrameCountItem;
QTableWidgetItem *m_modesFrameCountItem;
QTableWidgetItem *m_nonTransponderItem;
QTableWidgetItem *m_tisBFrameCountItem;
QTableWidgetItem *m_adsrFrameCountItem;
QTableWidgetItem *m_radiusItem;
QTableWidgetItem *m_nacpItem;
QTableWidgetItem *m_nacvItem;
QTableWidgetItem *m_gvaItem;
QTableWidgetItem *m_nicItem;
QTableWidgetItem *m_nicBaroItem;
QTableWidgetItem *m_silItem;
QTableWidgetItem *m_correlationItem;
QTableWidgetItem *m_rssiItem;
QTableWidgetItem *m_flightStatusItem;
QTableWidgetItem *m_depItem;
QTableWidgetItem *m_arrItem;
QTableWidgetItem *m_stopsItem;
QTableWidgetItem *m_stdItem;
QTableWidgetItem *m_etdItem;
QTableWidgetItem *m_atdItem;
@ -218,6 +284,13 @@ struct Aircraft {
QTableWidgetItem *m_apItem;
QTableWidgetItem *m_vModeItem;
QTableWidgetItem *m_lModeItem;
QTableWidgetItem *m_tcasItem;
QTableWidgetItem *m_acasItem;
QTableWidgetItem *m_raItem;
QTableWidgetItem *m_maxSpeedItem;
QTableWidgetItem *m_versionItem;
QTableWidgetItem *m_lengthItem;
QTableWidgetItem *m_widthItem;
QTableWidgetItem *m_rollItem;
QTableWidgetItem *m_groundspeedItem;
QTableWidgetItem *m_turnRateItem;
@ -231,16 +304,18 @@ struct Aircraft {
QTableWidgetItem *m_staticPressureItem;
QTableWidgetItem *m_staticAirTempItem;
QTableWidgetItem *m_humidityItem;
QTableWidgetItem *m_tisBItem;
Aircraft(ADSBDemodGUI *gui) :
m_icao(0),
m_globalPosition(false),
m_latitude(0),
m_longitude(0),
m_radius(0.0f),
m_altitude(0),
m_onSurface(false),
m_altitudeGNSS(false),
m_heading(0),
m_track(0),
m_verticalRate(0),
m_azimuth(0),
m_elevation(0),
@ -253,9 +328,22 @@ struct Aircraft {
m_trueAirspeed(0),
m_indicatedAirspeed(0),
m_mach(0.0f),
m_autopilot(false),
m_vnavMode(false),
m_altHoldMode(false),
m_approachMode(false),
m_lnavMode(false),
m_tcasOperational(false),
m_adsbVersion(0),
m_nicSupplementA(false),
m_nicSupplementB(false),
m_nicSupplementC(false),
m_positionValid(false),
m_altitudeValid(false),
m_pressureAltitudeValid(false),
m_onSurfaceValid(false),
m_headingValid(false),
m_trackValid(false),
m_verticalRateValid(false),
m_selAltitudeValid(false),
m_selHeadingValid(false),
@ -266,9 +354,22 @@ struct Aircraft {
m_trueAirspeedValid(false),
m_indicatedAirspeedValid(false),
m_machValid(false),
m_autopilotValid(false),
m_vnavModeValid(false),
m_altHoldModeValid(false),
m_approachModeValid(false),
m_lnavModeValid(false),
m_tcasOperationalValid(false),
m_bdsCapabilitiesValid(false),
m_adsbVersionValid(false),
m_nicSupplementAValid(false),
m_nicSupplementBValid(false),
m_nicSupplementCValid(false),
m_adsbFrameCount(0),
m_modesFrameCount(0),
m_nonTransponderFrameCount(0),
m_tisBFrameCount(0),
m_adsrFrameCount(0),
m_minCorrelation(INFINITY),
m_maxCorrelation(-INFINITY),
m_correlation(0.0f),
@ -302,9 +403,12 @@ struct Aircraft {
m_callsignItem = new QTableWidgetItem();
m_atcCallsignItem = new QTableWidgetItem();
m_modelItem = new QTableWidgetItem();
m_typeItem = new QTableWidgetItem();
m_sideviewItem = new QTableWidgetItem();
m_airlineItem = new QTableWidgetItem();
m_altitudeItem = new QTableWidgetItem();
m_headingItem = new QTableWidgetItem();
m_trackItem = new QTableWidgetItem();
m_verticalRateItem = new QTableWidgetItem();
m_rangeItem = new CustomDoubleTableWidgetItem();
m_azElItem = new QTableWidgetItem();
@ -313,19 +417,34 @@ struct Aircraft {
m_emitterCategoryItem = new QTableWidgetItem();
m_statusItem = new QTableWidgetItem();
m_squawkItem = new QTableWidgetItem();
m_identItem = new QTableWidgetItem();
m_registrationItem = new QTableWidgetItem();
m_countryItem = new QTableWidgetItem();
m_registeredItem = new QTableWidgetItem();
m_manufacturerNameItem = new QTableWidgetItem();
m_ownerItem = new QTableWidgetItem();
m_operatorICAOItem = new QTableWidgetItem();
m_interogatorCodeItem = new QTableWidgetItem();
m_timeItem = new QTableWidgetItem();
m_totalFrameCountItem = new QTableWidgetItem();
m_adsbFrameCountItem = new QTableWidgetItem();
m_modesFrameCountItem = new QTableWidgetItem();
m_nonTransponderItem = new QTableWidgetItem();
m_tisBFrameCountItem = new QTableWidgetItem();
m_adsrFrameCountItem = new QTableWidgetItem();
m_radiusItem = new QTableWidgetItem();
m_nacpItem = new QTableWidgetItem();
m_nacvItem = new QTableWidgetItem();
m_gvaItem = new QTableWidgetItem();
m_nicItem = new QTableWidgetItem();
m_nicBaroItem = new QTableWidgetItem();
m_silItem = new QTableWidgetItem();
m_correlationItem = new QTableWidgetItem();
m_rssiItem = new QTableWidgetItem();
m_flightStatusItem = new QTableWidgetItem();
m_depItem = new QTableWidgetItem();
m_arrItem = new QTableWidgetItem();
m_stopsItem = new QTableWidgetItem();
m_stdItem = new QTableWidgetItem();
m_etdItem = new QTableWidgetItem();
m_atdItem = new QTableWidgetItem();
@ -338,6 +457,13 @@ struct Aircraft {
m_apItem = new QTableWidgetItem();
m_vModeItem = new QTableWidgetItem();
m_lModeItem = new QTableWidgetItem();
m_tcasItem = new QTableWidgetItem();
m_acasItem = new QTableWidgetItem();
m_raItem = new QTableWidgetItem();
m_maxSpeedItem = new QTableWidgetItem();
m_versionItem = new QTableWidgetItem();
m_lengthItem = new QTableWidgetItem();
m_widthItem = new QTableWidgetItem();
m_rollItem = new QTableWidgetItem();
m_groundspeedItem = new QTableWidgetItem();
m_turnRateItem = new QTableWidgetItem();
@ -351,16 +477,15 @@ struct Aircraft {
m_staticPressureItem = new QTableWidgetItem();
m_staticAirTempItem = new QTableWidgetItem();
m_humidityItem = new QTableWidgetItem();
m_tisBItem = new QTableWidgetItem();
}
QString getImage() const;
QString getText(const ADSBDemodSettings *settings, bool all=false) const;
// Label to use for aircraft on map
QString getLabel(const ADSBDemodSettings *settings) const;
QString getLabel(const ADSBDemodSettings *settings, QDateTime& dateTime) const;
// Name to use when selected as a target (E.g. for use as target name in Rotator Controller)
QString targetName()
QString targetName() const
{
if (!m_callsign.isEmpty())
return QString("Callsign: %1").arg(m_callsign);
@ -368,6 +493,71 @@ struct Aircraft {
return QString("ICAO: %1").arg(m_icao, 0, 16);
}
void setOnSurface(const QDateTime& dateTime);
void setAltitude(int altitudeFt, bool gnss, const QDateTime& dateTime, const ADSBDemodSettings& settings);
void setVerticalRate(int verticalRate, const ADSBDemodSettings& settings);
void setGroundspeed(float groundspeed, const ADSBDemodSettings& settings);
void setTrueAirspeed(int airspeed, const ADSBDemodSettings& settings);
void setIndicatedAirspeed(int airspeed, const QDateTime& dateTime, const ADSBDemodSettings& settings);
void setTrack(float track, const QDateTime& dateTime);
void setHeading(float heading, const QDateTime& dateTime);
void addCoordinate(const QDateTime& dateTime, AircraftModel *model);
void clearCoordinates(AircraftModel *model);
};
class AircraftPathModel : public QAbstractListModel {
Q_OBJECT
public:
using QAbstractListModel::QAbstractListModel;
enum MarkerRoles{
coordinatesRole = Qt::UserRole + 1,
colorRole = Qt::UserRole + 2,
};
AircraftPathModel(AircraftModel *aircraftModel, Aircraft *aircraft) :
m_aircraftModel(aircraftModel),
m_aircraft(aircraft),
m_paths(0),
m_showFullPath(false),
m_showATCPath(false)
{
settingsUpdated();
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
(void) parent;
return m_paths;
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void add();
void updateLast();
void removed();
void clear();
void settingsUpdated();
Qt::ItemFlags flags(const QModelIndex &index) const override
{
(void) index;
return Qt::ItemIsEnabled;
}
QHash<int, QByteArray> roleNames() const {
QHash<int, QByteArray> roles;
roles[coordinatesRole] = "coordinates";
roles[colorRole] = "color";
return roles;
}
private:
AircraftModel *m_aircraftModel;
Aircraft *m_aircraft;
int m_paths; // Should match m_aircraft->m_coordinates.count()
bool m_showFullPath;
bool m_showATCPath;
};
// Aircraft data model used by QML map item
@ -385,12 +575,16 @@ public:
aircraftPathRole = Qt::UserRole + 6,
showAllRole = Qt::UserRole + 7,
highlightedRole = Qt::UserRole + 8,
targetRole = Qt::UserRole + 9
targetRole = Qt::UserRole + 9,
radiusRole = Qt::UserRole + 10,
showRadiusRole = Qt::UserRole + 11,
aircraftPathModelRole = Qt::UserRole + 12,
};
Q_INVOKABLE void addAircraft(Aircraft *aircraft) {
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_aircrafts.append(aircraft);
m_pathModels.append(new AircraftPathModel(this, aircraft));
endInsertRows();
}
@ -409,7 +603,8 @@ public:
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
void aircraftUpdated(Aircraft *aircraft) {
void aircraftUpdated(Aircraft *aircraft)
{
int row = m_aircrafts.indexOf(aircraft);
if (row >= 0)
{
@ -418,30 +613,72 @@ public:
}
}
void allAircraftUpdated() {
/*
// Not sure why this doesn't work - it should be more efficient
// than the following code
emit dataChanged(index(0), index(rowCount()));
*/
for (int i = 0; i < m_aircrafts.count(); i++)
void highlightChanged(Aircraft *aircraft)
{
int row = m_aircrafts.indexOf(aircraft);
if (row >= 0)
{
QModelIndex idx = index(i);
m_pathModels[row]->settingsUpdated();
QModelIndex idx = index(row);
emit dataChanged(idx, idx);
}
}
void removeAircraft(Aircraft *aircraft) {
void clearCoords(Aircraft *aircraft)
{
int row = m_aircrafts.indexOf(aircraft);
if (row >= 0) {
m_pathModels[row]->clear();
}
}
void aircraftCoordsUpdated(Aircraft *aircraft)
{
int row = m_aircrafts.indexOf(aircraft);
if (row >= 0) {
m_pathModels[row]->updateLast();
}
}
void aircraftCoordsAdded(Aircraft *aircraft)
{
int row = m_aircrafts.indexOf(aircraft);
if (row >= 0) {
m_pathModels[row]->add();
}
}
void aircraftCoordsRemoved(Aircraft *aircraft)
{
int row = m_aircrafts.indexOf(aircraft);
if (row >= 0) {
m_pathModels[row]->removed();
}
}
void allAircraftUpdated()
{
emit dataChanged(index(0), index(rowCount()-1));
for (int i = 0; i < m_aircrafts.count(); i++) {
m_pathModels[i]->settingsUpdated();
}
}
void removeAircraft(Aircraft *aircraft)
{
int row = m_aircrafts.indexOf(aircraft);
if (row >= 0)
{
beginRemoveRows(QModelIndex(), row, row);
m_aircrafts.removeAt(row);
delete m_pathModels.takeAt(row);
endRemoveRows();
}
}
QHash<int, QByteArray> roleNames() const {
QHash<int, QByteArray> roleNames() const
{
QHash<int, QByteArray> roles;
roles[positionRole] = "position";
roles[headingRole] = "heading";
@ -452,22 +689,12 @@ public:
roles[showAllRole] = "showAll";
roles[highlightedRole] = "highlighted";
roles[targetRole] = "target";
roles[radiusRole] = "containmentRadius";
roles[aircraftPathModelRole] = "aircraftPathModel";
return roles;
}
void setFlightPaths(bool flightPaths)
{
m_flightPaths = flightPaths;
allAircraftUpdated();
}
void setAllFlightPaths(bool allFlightPaths)
{
m_allFlightPaths = allFlightPaths;
allAircraftUpdated();
}
void setSettings(const ADSBDemodSettings *settings)
void setSettings(const ADSBDemodSettings *settings)
{
m_settings = settings;
allAircraftUpdated();
@ -487,11 +714,11 @@ public:
}
}
const ADSBDemodSettings *m_settings;
private:
QList<Aircraft *> m_aircrafts;
bool m_flightPaths;
bool m_allFlightPaths;
const ADSBDemodSettings *m_settings;
QList<AircraftPathModel *> m_pathModels;
};
// Airport data model used by QML map item
@ -667,26 +894,34 @@ public:
airspacePolygonRole = Qt::UserRole + 6
};
Q_INVOKABLE void addAirspace(Airspace *airspace) {
Q_INVOKABLE void addAirspace(Airspace *airspace)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_airspaces.append(airspace);
// Convert QPointF to QVariantList of QGeoCoordinates
QVariantList polygon;
for (const auto p : airspace->m_polygon)
{
QGeoCoordinate coord(p.y(), p.x(), airspace->topHeightInMetres());
polygon.push_back(QVariant::fromValue(coord));
}
m_polygons.append(polygon);
updatePolygon(airspace, -1);
endInsertRows();
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
Q_UNUSED(parent)
return m_airspaces.count();
}
void removeAllAirspaces() {
void removeAirspace(Airspace *airspace)
{
int idx = m_airspaces.indexOf(airspace);
if (idx >= 0)
{
beginRemoveRows(QModelIndex(), idx, idx);
m_airspaces.removeAt(idx);
m_polygons.removeAt(idx);
endRemoveRows();
}
}
void removeAllAirspaces()
{
if (m_airspaces.count() > 0)
{
beginRemoveRows(QModelIndex(), 0, m_airspaces.count() - 1);
@ -696,6 +931,23 @@ public:
}
}
void airspaceUpdated(const Airspace *airspace)
{
int row = m_airspaces.indexOf(airspace);
if (row >= 0)
{
updatePolygon(airspace, row);
QModelIndex idx = index(row);
emit dataChanged(idx, idx);
}
}
bool contains(const Airspace *airspace)
{
return m_airspaces.contains(airspace);
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant& value, int role = Qt::EditRole) override;
@ -706,7 +958,8 @@ public:
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
QHash<int, QByteArray> roleNames() const {
QHash<int, QByteArray> roleNames() const
{
QHash<int, QByteArray> roles;
roles[nameRole] = "name";
roles[detailsRole] = "details";
@ -718,8 +971,25 @@ public:
}
private:
QList<Airspace *> m_airspaces;
QList<const Airspace *> m_airspaces;
QList<QVariantList> m_polygons;
void updatePolygon(const Airspace *airspace, int row)
{
// Convert QPointF to QVariantList of QGeoCoordinates
QVariantList polygon;
for (const auto p : airspace->m_polygon)
{
QGeoCoordinate coord(p.y(), p.x(), airspace->topHeightInMetres());
polygon.push_back(QVariant::fromValue(coord));
}
if (row == -1) {
m_polygons.append(polygon);
} else {
m_polygons.replace(row, polygon);
}
}
};
// NavAid model used for each NavAid on the map
@ -867,6 +1137,23 @@ protected:
class ADSBDemodGUI : public ChannelGUI {
Q_OBJECT
struct Interogator {
Real m_minLatitude;
Real m_maxLatitude;
Real m_minLongitude;
Real m_maxLongitude;
bool m_valid;
Airspace m_airspace;
Interogator() :
m_valid(false)
{
}
void update(int ic, Aircraft *aircraft, AirspaceModel *airspaceModel, CheckList *checkList, bool display);
void calcPoly();
};
public:
static ADSBDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
virtual void destroy();
@ -923,6 +1210,7 @@ private:
QHash<int, Aircraft *> m_aircraft; // Hashed on ICAO
QSharedPointer<const QHash<int, AircraftInformation *>> m_aircraftInfo;
QSharedPointer<const QHash<QString, AircraftRouteInformation *>> m_routeInfo; // Hashed on callsign
QSharedPointer<const QHash<int, AirportInformation *>> m_airportInfo; // Hashed on id
AircraftModel m_aircraftModel;
AirportModel m_airportModel;
@ -936,12 +1224,85 @@ private:
Aircraft *m_trackAircraft; // Aircraft we want to track in Channel Report
MovingAverageUtil<float, double, 10> m_correlationAvg;
MovingAverageUtil<float, double, 10> m_correlationOnesAvg;
MovingAverageUtil<float, double, 100> m_qnhAvg;
Aircraft *m_highlightAircraft; // Aircraft we want to highlight, when selected in table
float m_currentAirportRange; // Current settings, so we only update if changed
ADSBDemodSettings::AirportType m_currentAirportMinimumSize;
bool m_currentDisplayHeliports;
static const int m_maxRangeDeg = 5;
QList<float> m_maxRange[2];
Airspace m_coverageAirspace[2];
Interogator m_interogators[ADSB_IC_MAX];
enum StatsRow {
ADSB_FRAMES,
MODE_S_FRAMES,
TOTAL_FRAMES,
ADSB_RATE,
MODE_S_RATE,
TOTAL_RATE,
DATA_RATE,
CORRELATOR_MATCHES,
PERCENT_VALID,
PREAMBLE_FAILS,
CRC_FAILS,
TYPE_FAILS,
INVALID_FAILS,
ICAO_FAILS,
RANGE_FAILS,
ALT_FAILS,
AVERAGE_CORRELATION,
TC_0,
TC_1_4,
TC_5_8,
TC_9_18,
TC_19,
TC_20_22,
TC_24,
TC_28,
TC_29,
TC_31,
TC_UNUSED,
DF0,
DF4,
DF5,
DF11,
DF16,
DF17,
DF18,
DF19,
DF20_21,
DF22,
DF24,
MAX_RANGE,
MAX_ALTITUDE,
MAX_RATE
};
qint64 m_rangeFails;
qint64 m_altFails;
QDateTime m_frameRateTime;
qint64 m_adsbFrameRateCount;
qint64 m_modesFrameRateCount;
qint64 m_totalBytes;
float m_maxRangeStat;
float m_maxAltitudeStat;
float m_maxRateState;
qint64 m_dfStats[32];
qint64 m_tcStats[32];
QChart *m_chart;
QLineSeries *m_adsbFrameRateSeries;
QLineSeries *m_modesFrameRateSeries;
QLineSeries *m_aircraftSeries;
QDateTimeAxis *m_xAxis;
QValueAxis *m_fpsYAxis;
QValueAxis *m_aircraftYAxis;
QDateTime m_averageTime;
#ifdef QT_TEXTTOSPEECH_FOUND
QTextToSpeech *m_speech;
#endif
@ -977,10 +1338,13 @@ private:
static const QString m_flightStatuses[];
static const QString m_hazardSeverity[];
static const QString m_fomSources[];
static const QString m_nacvStrings[];
explicit ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~ADSBDemodGUI();
QString maptilerAPIKey() const;
void blockApplySettings(bool block);
void applySetting(const QString& settingsKey);
void applySettings(const QStringList& settingsKeys, bool force = false);
@ -990,8 +1354,6 @@ private:
void makeUIConnections();
void updateAbsoluteCenterFrequency();
void updatePosition(Aircraft *aircraft);
bool updateLocalPosition(Aircraft *aircraft, double latitude, double longitude, bool surfacePosition);
void clearFromMap(const QString& name);
void sendToMap(Aircraft *aircraft, QList<SWGSDRangel::SWGMapAnimation *> *animations);
Aircraft *getAircraft(int icao, bool &newAircraft);
@ -1006,10 +1368,24 @@ private:
float correlationOnes,
unsigned crc,
bool updateModel);
void decodeModeS(const QByteArray data, int df, Aircraft *aircraft);
void decodeID(const QByteArray& data, QString& emitterCategory, QString& callsign);
void decodeGroundspeed(const QByteArray& data, float& v, float& h);
void decodeAirspeed(const QByteArray& data, bool& tas, int& as, bool& hdgValid, float& hdg);
void decodeVerticalRate(const QByteArray& data, int& verticalRate);
void updateAircraftPosition(Aircraft *aircraft, double latitude, double longitude, const QDateTime& dateTime);
bool validateGlobalPosition(double latitude, double longitude, bool countFailure);
bool validateLocalPosition(double latitude, double longitude, bool surfacePosition, bool countFailure);
bool decodeGlobalPosition(int f, const double cprLat[2], const double cprLong[2], const QDateTime cprTime[2], double& latitude, double& longitude, bool countFailure);
bool decodeLocalPosition(int f, double cprLat, double cprLong, bool onSurface, const Aircraft *aircraft, double& latitude, double& longitude, bool countFailure);
void decodeCpr(const QByteArray& data, int& f, double& latCpr, double& lonCpr) const;
bool decodeAltitude(const QByteArray& data, int& altFt) const;
void decodeModeSAltitude(const QByteArray& data, const QDateTime dateTime, Aircraft *aircraft);
void decodeModeS(const QByteArray data, const QDateTime dateTime, int df, Aircraft *aircraft);
void decodeCommB(const QByteArray data, const QDateTime dateTime, int df, Aircraft *aircraft, bool &updatedCallsign);
QList<SWGSDRangel::SWGMapAnimation *> *animate(QDateTime dateTime, Aircraft *aircraft);
SWGSDRangel::SWGMapAnimation *gearAnimation(QDateTime startDateTime, bool up);
SWGSDRangel::SWGMapAnimation *gearAngle(QDateTime startDateTime, bool flat);
SWGSDRangel::SWGMapAnimation *flapsAnimation(QDateTime startDateTime, float currentFlaps, float flaps);
SWGSDRangel::SWGMapAnimation *slatsAnimation(QDateTime startDateTime, bool retract);
SWGSDRangel::SWGMapAnimation *rotorAnimation(QDateTime startDateTime, bool stop);
@ -1022,18 +1398,18 @@ private:
QString subAircraftString(Aircraft *aircraft, const QString &string);
void resizeTable();
QString getDataDir();
void readAirportDB(const QString& filename);
void readAirportFrequenciesDB(const QString& filename);
void update3DModels();
void updateAirports();
void updateAirspaces();
void updateNavAids();
void updateChannelList();
void removeAircraft(QHash<int, Aircraft *>::iterator& i, Aircraft *aircraft);
QAction *createCheckableItem(QString& text, int idx, bool checked);
Aircraft* findAircraftByFlight(const QString& flight);
QString dataTimeToShortString(QDateTime dt);
void initFlightInformation();
void initAviationWeather();
void setShowContainmentRadius(bool show);
void applyMapSettings();
void updatePhotoText(Aircraft *aircraft);
void updatePhotoFlightInformation(Aircraft *aircraft);
@ -1045,16 +1421,33 @@ private:
void applyImportSettings();
void sendAircraftReport();
void updatePosition(float latitude, float longitude, float altitude);
void clearOldHeading(Aircraft *aircraft, const QDateTime& dateTime, float newTrack);
void updateQNH(const Aircraft *aircraft, float qnh);
void setCallsign(Aircraft *aircraft, const QString& callsign);
void initCoverageMap();
void clearCoverageMap();
void updateCoverageMap(float azimuth, float elevation, float distance, float altitude);
void leaveEvent(QEvent*);
void enterEvent(EnterEventType*);
void updateDFStats(int df);
bool updateTCStats(int tc, int row, int low, int high);
void resetStats();
void plotChart();
int countActiveAircraft();
void averageSeries(QLineSeries *series, const QDateTime& startTime, const QDateTime& endTime);
void legendMarkerClicked();
private slots:
void on_deltaFrequency_changed(qint64 value);
void on_rfBW_valueChanged(int value);
void on_threshold_valueChanged(int value);
void on_chipsThreshold_valueChanged(int value);
void on_phaseSteps_valueChanged(int value);
void on_tapsPerPhase_valueChanged(int value);
void statsTable_customContextMenuRequested(QPoint pos);
void adsbData_customContextMenuRequested(QPoint point);
void on_adsbData_cellClicked(int row, int column);
void on_adsbData_cellDoubleClicked(int row, int column);
@ -1063,18 +1456,24 @@ private slots:
void columnSelectMenu(QPoint pos);
void columnSelectMenuChecked(bool checked = false);
void on_spb_currentIndexChanged(int value);
void on_correlateFullPreamble_clicked(bool checked);
void on_demodModeS_clicked(bool checked);
void on_feed_clicked(bool checked);
void on_notifications_clicked();
void on_flightInfo_clicked();
void on_findOnMapFeature_clicked();
void on_getOSNDB_clicked();
void on_deleteAircraft_clicked();
void on_getAircraftDB_clicked();
void on_getAirportDB_clicked();
void on_getAirspacesDB_clicked();
void on_coverage_clicked(bool checked);
void on_displayChart_clicked(bool checked);
void on_stats_clicked(bool checked);
void on_flightPaths_clicked(bool checked);
void on_allFlightPaths_clicked(bool checked);
void on_atcLabels_clicked(bool checked);
void on_displayOrientation_clicked(bool checked);
void on_displayRadius_clicked(bool checked);
void on_ic_globalCheckStateChanged(int state);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
@ -1101,6 +1500,12 @@ private slots:
void devicePositionChanged(float latitude, float longitude, float altitude);
void requestMetar(const QString& icao);
void weatherUpdated(const AviationWeather::METAR &metar);
void on_manualQNH_clicked(bool checked);
void on_qnh_valueChanged(int value);
void clearCoverage(const QPoint& p);
void clearStats(const QPoint& p);
void clearChart(const QPoint& p);
void resetChartAxes();
signals:
void homePositionChanged();

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,13 @@
<file>icons/aircraft.png</file>
<file>icons/airport.png</file>
<file>icons/controltower.png</file>
<file>icons/coverage.png</file>
<file>icons/allflightpaths.png</file>
<file>icons/vor.png</file>
<file>icons/stats.png</file>
<file>icons/chart.png</file>
<file>icons/horizontal.png</file>
<file>icons/vertical.png</file>
<file>icons/radius.png</file>
</qresource>
</RCC>

View File

@ -5,14 +5,22 @@
<file>map/map_6_strict.qml</file>
<file>map/ModifiedMapView.qml</file>
<file>map/MapStation.qml</file>
<file>map/aircraft_2engine.png</file>
<file>map/aircraft_2enginesmall.png</file>
<file>map/aircraft_4engine.png</file>
<file>map/aircraft_small.png</file>
<file>map/aircraft_large.png</file>
<file>map/aircraft_heavy_2engine.png</file>
<file>map/aircraft_heavy_4engine.png</file>
<file>map/aircraft_helicopter.png</file>
<file>map/aircraft_light.png</file>
<file>map/aircraft_space.png</file>
<file>map/aircraft_drone.png</file>
<file>map/aircraft_fighter.png</file>
<file>map/aircraft_glider.png</file>
<file>map/spitfire.png</file>
<file>map/a400m.png</file>
<file>map/f35.png</file>
<file>map/apache.png</file>
<file>map/chinook.png</file>
<file>map/eurofighter.png</file>
<file>map/airport_large.png</file>
<file>map/airport_medium.png</file>
<file>map/airport_small.png</file>

View File

@ -25,7 +25,7 @@
// Map main ADS-B table column numbers to combo box indices
std::vector<int> ADSBDemodNotificationDialog::m_columnMap = {
ADSB_COL_ICAO, ADSB_COL_CALLSIGN, ADSB_COL_MODEL,
ADSB_COL_ICAO, ADSB_COL_CALLSIGN, ADSB_COL_MODEL, ADSB_COL_TYPE,
ADSB_COL_ALTITUDE, ADSB_COL_GROUND_SPEED, ADSB_COL_RANGE,
ADSB_COL_CATEGORY, ADSB_COL_STATUS, ADSB_COL_SQUAWK,
ADSB_COL_REGISTRATION, ADSB_COL_MANUFACTURER, ADSB_COL_OWNER, ADSB_COL_OPERATOR_ICAO
@ -117,6 +117,7 @@ void ADSBDemodNotificationDialog::addRow(ADSBDemodSettings::NotificationSettings
match->addItem("ICAO ID");
match->addItem("Callsign");
match->addItem("Aircraft");
match->addItem("Type");
match->addItem("Alt (ft)");
match->addItem("GS (kn)");
match->addItem("D (km)");

View File

@ -36,7 +36,8 @@ void ADSBDemodSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_rfBandwidth = 2*1300000;
m_correlationThreshold = 10.0f; //<! ones/zero powers correlation threshold in dB
m_correlationThreshold = 7.0f; //<! ones/zero powers correlation threshold in dB
m_chipsThreshold = 0;
m_samplesPerBit = 4;
m_removeTimeout = 60;
m_feedEnabled = false;
@ -73,13 +74,13 @@ void ADSBDemodSettings::resetToDefaults()
m_tableFontName = "Liberation Sans";
m_tableFontSize = 9;
m_displayDemodStats = false;
m_correlateFullPreamble = true;
m_demodModeS = true;
m_autoResizeTableColumns = false;
m_interpolatorPhaseSteps = 4; // Higher than these two values will struggle to run in real-time
m_interpolatorTapsPerPhase = 3.5f; // without gaining much improvement in PER
m_aviationstackAPIKey = "";
m_checkWXAPIKey = "";
m_maptilerAPIKey = "";
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
{
m_columnIndexes[i] = i;
@ -98,13 +99,24 @@ void ADSBDemodSettings::resetToDefaults()
m_displayNavAids = true;
m_displayPhotos = true;
m_verboseModelMatching = false;
m_airfieldElevation = 0;
m_aircraftMinZoom = 11;
m_workspaceIndex = 0;
m_hidden = false;
m_atcLabels = true;
m_atcCallsigns = true;
m_transitionAlt = 6000; // Depends on airport. 18,000 in USA
m_qnh = 1013.25;
m_manualQNH = false;
m_displayCoverage = false;
m_displayChart = false;
m_displayOrientation = false;
m_displayRadius = false;
for (int i = 0; i < ADSB_IC_MAX; i++) {
m_displayIC[i] = false;
}
m_flightPathPaletteName = "Spectral";
applyPalette();
m_favourLivery = true;
}
QByteArray ADSBDemodSettings::serialize() const
@ -141,7 +153,6 @@ QByteArray ADSBDemodSettings::serialize() const
s.writeString(25, m_tableFontName);
s.writeS32(26, m_tableFontSize);
s.writeBool(27, m_displayDemodStats);
s.writeBool(28, m_correlateFullPreamble);
s.writeBool(29, m_demodModeS);
s.writeBool(30, m_autoResizeTableColumns);
s.writeS32(31, m_interpolatorPhaseSteps);
@ -165,7 +176,6 @@ QByteArray ADSBDemodSettings::serialize() const
}
s.writeBool(44, m_verboseModelMatching);
s.writeS32(45, m_airfieldElevation);
s.writeBool(46, m_exportClientEnabled);
s.writeBool(47, m_exportServerEnabled);
@ -192,6 +202,19 @@ QByteArray ADSBDemodSettings::serialize() const
s.writeS32(67, m_transitionAlt);
s.writeString(68, m_amDemod);
s.writeFloat(69, m_qnh);
s.writeBool(70, m_manualQNH);
s.writeBool(71, m_displayCoverage);
s.writeBool(72, m_displayChart);
s.writeBool(73, m_displayOrientation);
s.writeBool(74, m_displayRadius);
s.writeString(75, m_flightPathPaletteName);
s.writeS32(76, m_chipsThreshold);
s.writeBool(77, m_favourLivery);
s.writeString(78, m_maptilerAPIKey);
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) {
s.writeS32(100 + i, m_columnIndexes[i]);
}
@ -229,7 +252,7 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
d.readS32(1, &tmp, 0);
m_inputFrequencyOffset = tmp;
d.readReal(2, &m_rfBandwidth, 2*1300000);
d.readReal(3, &m_correlationThreshold, 0.0f);
d.readReal(3, &m_correlationThreshold, 7.0f);
d.readS32(4, &m_samplesPerBit, 4);
d.readS32(5, &m_removeTimeout, 60);
d.readBool(6, &m_feedEnabled, false);
@ -268,7 +291,6 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
d.readString(25, &m_tableFontName, "Liberation Sans");
d.readS32(26, &m_tableFontSize, 9);
d.readBool(27, &m_displayDemodStats, false);
d.readBool(28, &m_correlateFullPreamble, true);
d.readBool(29, &m_demodModeS, true);
d.readBool(30, &m_autoResizeTableColumns, false);
d.readS32(31, &m_interpolatorPhaseSteps, 4);
@ -296,7 +318,6 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
}
d.readBool(44, &m_verboseModelMatching, false);
d.readS32(45, &m_airfieldElevation, 0);
d.readBool(46, &m_exportClientEnabled, true);
d.readBool(47, &m_exportServerEnabled, true);
@ -328,6 +349,21 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
d.readS32(67, &m_transitionAlt, 6000);
d.readString(68, &m_amDemod);
d.readFloat(69, &m_qnh, 1013.25);
d.readBool(70, &m_manualQNH, false);
d.readBool(71, &m_displayCoverage, false);
d.readBool(72, &m_displayChart, false);
d.readBool(73, &m_displayOrientation, false);
d.readBool(74, &m_displayRadius, false);
d.readString(75, &m_flightPathPaletteName, "Spectral");
d.readS32(76, &m_chipsThreshold, 0);
d.readBool(77, &m_favourLivery, true);
d.readString(78, &m_maptilerAPIKey, "");
applyPalette();
#ifdef LINUX
if (m_mapProvider == "osm") {
m_mapProvider = "mapboxgl";
@ -413,6 +449,9 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
if (settingsKeys.contains("correlationThreshold")) {
m_correlationThreshold = settings.m_correlationThreshold;
}
if (settingsKeys.contains("chipsThreshold")) {
m_chipsThreshold = settings.m_chipsThreshold;
}
if (settingsKeys.contains("samplesPerBit")) {
m_samplesPerBit = settings.m_samplesPerBit;
}
@ -521,9 +560,6 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
if (settingsKeys.contains("displayDemodStats")) {
m_displayDemodStats = settings.m_displayDemodStats;
}
if (settingsKeys.contains("correlateFullPreamble")) {
m_correlateFullPreamble = settings.m_correlateFullPreamble;
}
if (settingsKeys.contains("demodModeS")) {
m_demodModeS = settings.m_demodModeS;
}
@ -548,6 +584,9 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
if (settingsKeys.contains("checkWXAPIKey")) {
m_checkWXAPIKey = settings.m_checkWXAPIKey;
}
if (settingsKeys.contains("maptilerAPIKey")) {
m_maptilerAPIKey = settings.m_maptilerAPIKey;
}
if (settingsKeys.contains("logFilename")) {
m_logFilename = settings.m_logFilename;
}
@ -575,9 +614,6 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
if (settingsKeys.contains("verboseModelMatching")) {
m_verboseModelMatching = settings.m_verboseModelMatching;
}
if (settingsKeys.contains("airfieldElevation")) {
m_airfieldElevation = settings.m_airfieldElevation;
}
if (settingsKeys.contains("aircraftMinZoom")) {
m_aircraftMinZoom = settings.m_aircraftMinZoom;
}
@ -590,6 +626,32 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
if (settingsKeys.contains("transitionAlt")) {
m_transitionAlt = settings.m_transitionAlt;
}
if (settingsKeys.contains("qnh")) {
m_qnh = settings.m_qnh;
}
if (settingsKeys.contains("manualQNH")) {
m_manualQNH = settings.m_manualQNH;
}
if (settingsKeys.contains("displayCoverage")) {
m_displayCoverage = settings.m_displayCoverage;
}
if (settingsKeys.contains("displayChart")) {
m_displayChart = settings.m_displayChart;
}
if (settingsKeys.contains("displayOrientation")) {
m_displayOrientation = settings.m_displayOrientation;
}
if (settingsKeys.contains("displayRadius")) {
m_displayRadius = settings.m_displayRadius;
}
if (settingsKeys.contains("flightPathPaletteName"))
{
m_flightPathPaletteName = settings.m_flightPathPaletteName;
applyPalette();
}
if (settingsKeys.contains("favourLivery")) {
m_favourLivery = settings.m_favourLivery;
}
}
QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool force) const
@ -605,6 +667,9 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("correlationThreshold") || force) {
ostr << " m_correlationThreshold: " << m_correlationThreshold;
}
if (settingsKeys.contains("chipsThreshold") || force) {
ostr << " m_chipsThreshold: " << m_chipsThreshold;
}
if (settingsKeys.contains("samplesPerBit") || force) {
ostr << " m_samplesPerBit: " << m_samplesPerBit;
}
@ -704,9 +769,6 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("displayDemodStats") || force) {
ostr << " m_displayDemodStats: " << m_displayDemodStats;
}
if (settingsKeys.contains("correlateFullPreamble") || force) {
ostr << " m_correlateFullPreamble: " << m_correlateFullPreamble;
}
if (settingsKeys.contains("demodModeS") || force) {
ostr << " m_demodModeS: " << m_demodModeS;
}
@ -728,6 +790,9 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("checkWXAPIKey") || force) {
ostr << " m_checkWXAPIKey: " << m_checkWXAPIKey.toStdString();
}
if (settingsKeys.contains("maptilerAPIKey") || force) {
ostr << " m_maptilerAPIKey: " << m_maptilerAPIKey.toStdString();
}
if (settingsKeys.contains("logFilename") || force) {
ostr << " m_logFilename: " << m_logFilename.toStdString();
}
@ -755,9 +820,6 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("verboseModelMatching") || force) {
ostr << " m_verboseModelMatching: " << m_verboseModelMatching;
}
if (settingsKeys.contains("airfieldElevation") || force) {
ostr << " m_airfieldElevation: " << m_airfieldElevation;
}
if (settingsKeys.contains("aircraftMinZoom") || force) {
ostr << " m_aircraftMinZoom: " << m_aircraftMinZoom;
}
@ -770,6 +832,114 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("transitionAlt") || force) {
ostr << " m_transitionAlt: " << m_transitionAlt;
}
if (settingsKeys.contains("qnh") || force) {
ostr << " m_qnh: " << m_qnh;
}
if (settingsKeys.contains("manualQNH") || force) {
ostr << " m_manualQNH: " << m_manualQNH;
}
if (settingsKeys.contains("displayCoverage") || force) {
ostr << " m_displayCoverage: " << m_displayCoverage;
}
if (settingsKeys.contains("displayChart") || force) {
ostr << " m_displayChart: " << m_displayChart;
}
if (settingsKeys.contains("displayOrientation") || force) {
ostr << " m_displayOrientation: " << m_displayOrientation;
}
if (settingsKeys.contains("displayRadius") || force) {
ostr << " m_displayRadius: " << m_displayRadius;
}
if (settingsKeys.contains("flightPathPaletteName") || force) {
ostr << " m_flightPathPaletteName: " << m_flightPathPaletteName.toStdString();
}
if (settingsKeys.contains("favourLivery") || force) {
ostr << " m_favourLivery: " << m_favourLivery;
}
return QString(ostr.str().c_str());
}
void ADSBDemodSettings::applyPalette()
{
if (m_palettes.contains(m_flightPathPaletteName)) {
m_flightPathPalette = m_palettes.value(m_flightPathPaletteName);
} else {
m_flightPathPalette = m_rainbowPalette;
}
}
const QVariant ADSBDemodSettings::m_rainbowPalette[8] = {
QVariant(QColor(0xff, 0x00, 0x00)),
QVariant(QColor(0xff, 0x7f, 0x00)),
QVariant(QColor(0xff, 0xff, 0x00)),
QVariant(QColor(0xf7, 0xff, 0x00)),
QVariant(QColor(0x00, 0xff, 0x00)),
QVariant(QColor(0x00, 0xff, 0x7f)),
QVariant(QColor(0x00, 0xff, 0xff)),
QVariant(QColor(0x00, 0x7f, 0xff)),
};
const QVariant ADSBDemodSettings::m_pastelPalette[8] = {
QVariant(QColor(0xff, 0xad, 0xad)),
QVariant(QColor(0xff, 0xd6, 0xa5)),
QVariant(QColor(0xfd, 0xff, 0xb6)),
QVariant(QColor(0xca, 0xff, 0xbf)),
QVariant(QColor(0x9b, 0xf6, 0xff)),
QVariant(QColor(0xa0, 0xc4, 0xff)),
QVariant(QColor(0xbd, 0xb2, 0xff)),
QVariant(QColor(0xff, 0xc6, 0xff)),
};
const QVariant ADSBDemodSettings::m_spectralPalette[8] = {
QVariant(QColor(0xd5, 0x3e, 0x4f)),
QVariant(QColor(0xf4, 0x6d, 0x43)),
QVariant(QColor(0xfd, 0xae, 0x61)),
QVariant(QColor(0xfe, 0xe0, 0x8b)),
QVariant(QColor(0xe6, 0xf5, 0x98)),
QVariant(QColor(0xab, 0xdd, 0xa4)),
QVariant(QColor(0x66, 0xc2, 0xa5)),
QVariant(QColor(0x32, 0x88, 0xbd)),
};
const QVariant ADSBDemodSettings::m_bluePalette[8] = {
QVariant(QColor(0xde, 0xeb, 0xf7)),
QVariant(QColor(0xc6, 0xdb, 0xef)),
QVariant(QColor(0x9e, 0xca, 0xe1)),
QVariant(QColor(0x6b, 0xae, 0xd6)),
QVariant(QColor(0x42, 0x92, 0xc6)),
QVariant(QColor(0x21, 0x71, 0xb5)),
QVariant(QColor(0x08, 0x51, 0x9c)),
QVariant(QColor(0x08, 0x30, 0x6b)),
};
const QVariant ADSBDemodSettings::m_purplePalette[8] = {
QVariant(QColor(0xcc, 0xaf, 0xf2)),
QVariant(QColor(0xb6, 0x99, 0xe0)),
QVariant(QColor(0xa0, 0x84, 0xcf)),
QVariant(QColor(0x8a, 0x6f, 0xbd)),
QVariant(QColor(0x76, 0x5a, 0xac)),
QVariant(QColor(0x62, 0x45, 0x9a)),
QVariant(QColor(0x4f, 0x30, 0x89)),
QVariant(QColor(0x3e, 0x18, 0x78)),
};
const QVariant ADSBDemodSettings::m_greyPalette[8] = {
QVariant(QColor(0x80, 0x80, 0x80)),
QVariant(QColor(0x80, 0x80, 0x80)),
QVariant(QColor(0x80, 0x80, 0x80)),
QVariant(QColor(0x80, 0x80, 0x80)),
QVariant(QColor(0x80, 0x80, 0x80)),
QVariant(QColor(0x80, 0x80, 0x80)),
QVariant(QColor(0x80, 0x80, 0x80)),
QVariant(QColor(0x80, 0x80, 0x80)),
};
const QHash<QString, const QVariant *> ADSBDemodSettings::m_palettes = {
{"Rainbow", &ADSBDemodSettings::m_rainbowPalette[0]},
{"Pastel", &ADSBDemodSettings::m_pastelPalette[0]},
{"Spectral", &ADSBDemodSettings::m_spectralPalette[0]},
{"Blues", &ADSBDemodSettings::m_bluePalette[0]},
{"Purples", &ADSBDemodSettings::m_purplePalette[0]},
{"Grey", &ADSBDemodSettings::m_greyPalette[0]},
};

View File

@ -29,63 +29,89 @@
class Serializable;
// Number of columns in the table
#define ADSBDEMOD_COLUMNS 54
#define ADSBDEMOD_COLUMNS 78
// ADS-B table columns
#define ADSB_COL_ICAO 0
#define ADSB_COL_CALLSIGN 1
#define ADSB_COL_ATC_CALLSIGN 2
#define ADSB_COL_MODEL 3
#define ADSB_COL_AIRLINE 4
#define ADSB_COL_COUNTRY 5
#define ADSB_COL_GROUND_SPEED 6
#define ADSB_COL_TRUE_AIRSPEED 7
#define ADSB_COL_INDICATED_AIRSPEED 8
#define ADSB_COL_MACH 9
#define ADSB_COL_SEL_ALTITUDE 10
#define ADSB_COL_ALTITUDE 11
#define ADSB_COL_VERTICALRATE 12
#define ADSB_COL_SEL_HEADING 13
#define ADSB_COL_HEADING 14
#define ADSB_COL_TURNRATE 15
#define ADSB_COL_ROLL 16
#define ADSB_COL_RANGE 17
#define ADSB_COL_AZEL 18
#define ADSB_COL_CATEGORY 19
#define ADSB_COL_STATUS 20
#define ADSB_COL_SQUAWK 21
#define ADSB_COL_REGISTRATION 22
#define ADSB_COL_REGISTERED 23
#define ADSB_COL_MANUFACTURER 24
#define ADSB_COL_OWNER 25
#define ADSB_COL_OPERATOR_ICAO 26
#define ADSB_COL_AP 27
#define ADSB_COL_V_MODE 28
#define ADSB_COL_L_MODE 29
#define ADSB_COL_BARO 30
#define ADSB_COL_HEADWIND 31
#define ADSB_COL_EST_AIR_TEMP 32
#define ADSB_COL_WIND_SPEED 33
#define ADSB_COL_WIND_DIR 34
#define ADSB_COL_STATIC_PRESSURE 35
#define ADSB_COL_STATIC_AIR_TEMP 36
#define ADSB_COL_HUMIDITY 37
#define ADSB_COL_LATITUDE 38
#define ADSB_COL_LONGITUDE 39
#define ADSB_COL_TIME 40
#define ADSB_COL_FRAMECOUNT 41
#define ADSB_COL_TIS_B 42
#define ADSB_COL_CORRELATION 43
#define ADSB_COL_RSSI 44
#define ADSB_COL_FLIGHT_STATUS 45
#define ADSB_COL_DEP 46
#define ADSB_COL_ARR 47
#define ADSB_COL_STD 48
#define ADSB_COL_ETD 49
#define ADSB_COL_ATD 50
#define ADSB_COL_STA 51
#define ADSB_COL_ETA 52
#define ADSB_COL_ATA 53
#define ADSB_COL_TYPE 4
#define ADSB_COL_SIDEVIEW 5
#define ADSB_COL_AIRLINE 6
#define ADSB_COL_COUNTRY 7
#define ADSB_COL_GROUND_SPEED 8
#define ADSB_COL_TRUE_AIRSPEED 9
#define ADSB_COL_INDICATED_AIRSPEED 10
#define ADSB_COL_MACH 11
#define ADSB_COL_SEL_ALTITUDE 12
#define ADSB_COL_ALTITUDE 13
#define ADSB_COL_VERTICALRATE 14
#define ADSB_COL_SEL_HEADING 15
#define ADSB_COL_HEADING 16
#define ADSB_COL_TRACK 17
#define ADSB_COL_TURNRATE 18
#define ADSB_COL_ROLL 19
#define ADSB_COL_RANGE 20
#define ADSB_COL_AZEL 21
#define ADSB_COL_CATEGORY 22
#define ADSB_COL_STATUS 23
#define ADSB_COL_SQUAWK 24
#define ADSB_COL_IDENT 25
#define ADSB_COL_REGISTRATION 26
#define ADSB_COL_REGISTERED 27
#define ADSB_COL_MANUFACTURER 28
#define ADSB_COL_OWNER 29
#define ADSB_COL_OPERATOR_ICAO 30
#define ADSB_COL_AP 31
#define ADSB_COL_V_MODE 32
#define ADSB_COL_L_MODE 33
#define ADSB_COL_TCAS 34
#define ADSB_COL_ACAS 35
#define ADSB_COL_RA 36
#define ADSB_COL_MAX_SPEED 37
#define ADSB_COL_VERSION 38
#define ADSB_COL_LENGTH 39
#define ADSB_COL_WIDTH 40
#define ADSB_COL_BARO 41
#define ADSB_COL_HEADWIND 42
#define ADSB_COL_EST_AIR_TEMP 43
#define ADSB_COL_WIND_SPEED 44
#define ADSB_COL_WIND_DIR 45
#define ADSB_COL_STATIC_PRESSURE 46
#define ADSB_COL_STATIC_AIR_TEMP 47
#define ADSB_COL_HUMIDITY 48
#define ADSB_COL_LATITUDE 49
#define ADSB_COL_LONGITUDE 50
#define ADSB_COL_IC 51
#define ADSB_COL_TIME 52
#define ADSB_COL_FRAMECOUNT 53
#define ADSB_COL_ADSB_FRAMECOUNT 54
#define ADSB_COL_MODES_FRAMECOUNT 55
#define ADSB_COL_NON_TRANSPONDER 56
#define ADSB_COL_TIS_B_FRAMECOUNT 57
#define ADSB_COL_ADSR_FRAMECOUNT 58
#define ADSB_COL_RADIUS 59
#define ADSB_COL_NACP 60
#define ADSB_COL_NACV 61
#define ADSB_COL_GVA 62
#define ADSB_COL_NIC 63
#define ADSB_COL_NIC_BARO 64
#define ADSB_COL_SIL 65
#define ADSB_COL_CORRELATION 66
#define ADSB_COL_RSSI 67
#define ADSB_COL_FLIGHT_STATUS 68
#define ADSB_COL_DEP 69
#define ADSB_COL_ARR 70
#define ADSB_COL_STOPS 71
#define ADSB_COL_STD 72
#define ADSB_COL_ETD 73
#define ADSB_COL_ATD 74
#define ADSB_COL_STA 75
#define ADSB_COL_ETA 76
#define ADSB_COL_ATA 77
#define ADSB_IC_MAX 63
struct ADSBDemodSettings
{
@ -104,6 +130,7 @@ struct ADSBDemodSettings
int32_t m_inputFrequencyOffset;
Real m_rfBandwidth;
Real m_correlationThreshold; //!< Correlation power threshold in dB
int m_chipsThreshold; //!< How many chips in preamble can be incorrect for Mode S
int m_samplesPerBit;
int m_removeTimeout; //!< Time in seconds before removing an aircraft, unless a new frame is received
@ -159,7 +186,6 @@ struct ADSBDemodSettings
QString m_tableFontName; //!< Font to use for table
int m_tableFontSize;
bool m_displayDemodStats;
bool m_correlateFullPreamble;
bool m_demodModeS; //!< Demodulate all Mode-S frames, not just ADS-B
QString m_amDemod; //!< AM Demod to tune to selected ATC frequency
bool m_autoResizeTableColumns;
@ -169,6 +195,7 @@ struct ADSBDemodSettings
QList<NotificationSettings *> m_notificationSettings;
QString m_aviationstackAPIKey; //!< aviationstack.com API key
QString m_checkWXAPIKey; //!< checkwxapi.com API key
QString m_maptilerAPIKey; //!< maptiler.com API key (for osm/satellite map)
QString m_logFilename;
bool m_logEnabled;
@ -186,13 +213,24 @@ struct ADSBDemodSettings
bool m_displayPhotos;
Serializable *m_rollupState;
bool m_verboseModelMatching;
int m_airfieldElevation; //!< QFE in ft so aircraft takeoff/land from correct position
int m_aircraftMinZoom;
bool m_atcLabels;
bool m_atcCallsigns;
int m_transitionAlt;
float m_qnh;
bool m_manualQNH;
bool m_displayCoverage;
bool m_displayChart;
bool m_displayOrientation;
bool m_displayRadius;
bool m_displayIC[ADSB_IC_MAX];
QString m_flightPathPaletteName;
bool m_favourLivery; //!< Favour airline livery over aircraft type when exact 3D model isn't available
const QVariant *m_flightPathPalette;
ADSBDemodSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
@ -203,6 +241,16 @@ struct ADSBDemodSettings
void deserializeNotificationSettings(const QByteArray& data, QList<NotificationSettings *>& notificationSettings);
void applySettings(const QStringList& settingsKeys, const ADSBDemodSettings& settings);
QString getDebugString(const QStringList& settingsKeys, bool force = false) const;
void applyPalette();
static const QVariant m_rainbowPalette[8];
static const QVariant m_pastelPalette[8];
static const QVariant m_spectralPalette[8];
static const QVariant m_bluePalette[8];
static const QVariant m_purplePalette[8];
static const QVariant m_greyPalette[8];
static const QHash<QString, const QVariant *> m_palettes;
};
#endif /* PLUGINS_CHANNELRX_DEMODADSB_ADSBDEMODSETTINGS_H_ */

View File

@ -19,14 +19,16 @@
#include <QTime>
#include <QDebug>
#include "util/profiler.h"
#include "adsbdemodsink.h"
#include "adsbdemodsinkworker.h"
#include "adsbdemod.h"
#include "adsb.h"
ADSBDemodSink::ADSBDemodSink() :
m_channelSampleRate(6000000),
m_channelFrequencyOffset(0),
m_feedTime(0.0),
m_sampleBuffer{nullptr, nullptr, nullptr},
m_worker(this),
m_writeBuffer(0),
@ -53,7 +55,7 @@ ADSBDemodSink::~ADSBDemodSink()
void ADSBDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
// Start timing how long we are in this function
m_startPoint = boost::chrono::steady_clock::now();
PROFILER_START();
// Optimise for common case, where no resampling or frequency offset
if ((m_interpolatorDistance == 1.0f) && (m_channelFrequencyOffset == 0))
@ -113,8 +115,7 @@ void ADSBDemodSink::feed(const SampleVector::const_iterator& begin, const Sample
}
// Calculate number of seconds in this function
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - m_startPoint;
m_feedTime += sec.count();
PROFILER_STOP("ADSB feed");
}
void ADSBDemodSink::processOneSample(Real magsq)
@ -127,8 +128,17 @@ void ADSBDemodSink::processOneSample(Real magsq)
m_writeIdx++;
if (!m_bufferDateTimeValid[m_writeBuffer])
{
m_bufferFirstSampleDateTime[m_writeBuffer] = QDateTime::currentDateTime();
QDateTime dateTime = QDateTime::currentDateTime();
if (m_minFirstSampleDateTime.isValid() && (dateTime < m_minFirstSampleDateTime)) {
dateTime = m_minFirstSampleDateTime;
}
m_bufferFirstSampleDateTime[m_writeBuffer] = dateTime;
m_bufferDateTimeValid[m_writeBuffer] = true;
// Make sure timestamps from different buffers are in order, even if we receive samples faster than real time
const qint64 samplesPerSecondMSec = ADS_B_BITS_PER_SECOND * m_settings.m_samplesPerBit / 1000;
const qint64 offsetMSec = m_bufferSize / samplesPerSecondMSec;
m_minFirstSampleDateTime = dateTime.addMSecs(offsetMSec);
}
if (m_writeIdx >= m_bufferSize)
{
@ -138,15 +148,9 @@ void ADSBDemodSink::processOneSample(Real magsq)
if (m_writeBuffer >= m_buffers)
m_writeBuffer = 0;
// Don't include time spent waiting for a buffer
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - m_startPoint;
m_feedTime += sec.count();
if (m_worker.isRunning())
m_bufferWrite[m_writeBuffer].acquire();
m_startPoint = boost::chrono::steady_clock::now();
m_writeIdx = m_samplesPerFrame - 1; // Leave space for copying samples from previous buffer
m_bufferDateTimeValid[m_writeBuffer] = false;
@ -253,7 +257,6 @@ void ADSBDemodSink::applySettings(const ADSBDemodSettings& settings, const QStri
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_rfBandwidth: " << settings.m_rfBandwidth
<< " m_correlationThreshold: " << settings.m_correlationThreshold
<< " m_correlateFullPreamble: " << settings.m_correlateFullPreamble
<< " m_demodModeS: " << settings.m_demodModeS
<< " m_samplesPerBit: " << settings.m_samplesPerBit
<< " force: " << force;
@ -285,3 +288,9 @@ void ADSBDemodSink::applySettings(const ADSBDemodSettings& settings, const QStri
m_settings.applySettings(settingsKeys, settings);
}
}
void ADSBDemodSink::resetStats()
{
ADSBDemod::MsgResetStats* msg = ADSBDemod::MsgResetStats::create();
m_worker.getInputMessageQueue()->push(msg);
}

View File

@ -19,9 +19,6 @@
#ifndef INCLUDE_ADSBDEMODSINK_H
#define INCLUDE_ADSBDEMODSINK_H
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/chrono/chrono.hpp>
#include "dsp/channelsamplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
@ -60,6 +57,7 @@ public:
void setMessageQueueToWorker(MessageQueue *messageQueue) { m_messageQueueToWorker = messageQueue; }
void startWorker();
void stopWorker();
void resetStats();
private:
friend ADSBDemodSinkWorker;
@ -83,9 +81,6 @@ private:
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
boost::chrono::steady_clock::time_point m_startPoint;
double m_feedTime; //!< Time spent in feed()
// Triple buffering for sharing sample data between two threads
// Top area of each buffer is not used by writer, as it's used by the reader
// for copying the last few samples of the previous buffer, so it can
@ -96,6 +91,7 @@ private:
QSemaphore m_bufferWrite[3]; //!< Semaphore to control write access to the buffers
QSemaphore m_bufferRead[3]; //!< Semaphore to control read access from the buffers
QDateTime m_bufferFirstSampleDateTime[3]; //!< Time for first sample in the buffer
QDateTime m_minFirstSampleDateTime;
bool m_bufferDateTimeValid[3];
ADSBDemodSinkWorker m_worker; //!< Worker thread that does the actual demodulation
int m_writeBuffer; //!< Which of the 3 buffers we're writing in to

View File

@ -15,29 +15,288 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/chrono/chrono.hpp>
#include <QDebug>
#include "util/db.h"
#include "util/profiler.h"
#include "util/units.h"
#include "util/osndb.h"
#include "util/popcount.h"
#include "adsbdemodreport.h"
#include "adsbdemodsink.h"
#include "adsbdemodsinkworker.h"
#include "adsbdemod.h"
#include "adsb.h"
MESSAGE_CLASS_DEFINITION(ADSBDemodSinkWorker::MsgConfigureADSBDemodSinkWorker, Message)
const Real ADSBDemodSinkWorker::m_correlationScale = 3.0f;
static int grayToBinary(int gray, int bits)
{
int binary = 0;
for (int i = bits - 1; i >= 0; i--) {
binary = binary | ((((1 << (i+1)) & binary) >> 1) ^ ((1 << i) & gray));
}
return binary;
}
static int gillhamToFeet(int n)
{
int c1 = (n >> 10) & 1;
int a1 = (n >> 9) & 1;
int c2 = (n >> 8) & 1;
int a2 = (n >> 7) & 1;
int c4 = (n >> 6) & 1;
int a4 = (n >> 5) & 1;
int b1 = (n >> 4) & 1;
int b2 = (n >> 3) & 1;
int d2 = (n >> 2) & 1;
int b4 = (n >> 1) & 1;
int d4 = n & 1;
int n500 = grayToBinary((d2 << 7) | (d4 << 6) | (a1 << 5) | (a2 << 4) | (a4 << 3) | (b1 << 2) | (b2 << 1) | b4, 4);
int n100 = grayToBinary((c1 << 2) | (c2 << 1) | c4, 3) - 1;
if (n100 == 6) {
n100 = 4;
}
if (n500 %2 != 0) {
n100 = 4 - n100;
}
return -1200 + n500*500 + n100*100;
}
static int decodeModeSAltitude(const QByteArray& data)
{
int altitude = 0; // Altitude in feet
int altitudeCode = ((data[2] & 0x1f) << 8) | (data[3] & 0xff);
if (altitudeCode & 0x40) // M bit indicates metres
{
int altitudeMetres = ((altitudeCode & 0x1f80) >> 1) | (altitudeCode & 0x3f);
altitude = Units::metresToFeet(altitudeMetres);
}
else
{
// Remove M and Q bits
int altitudeFix = ((altitudeCode & 0x1f80) >> 2) | ((altitudeCode & 0x20) >> 1) | (altitudeCode & 0xf);
// Convert to feet
if (altitudeCode & 0x10) {
altitude = altitudeFix * 25 - 1000;
} else {
altitude = gillhamToFeet(altitudeFix);
}
}
return altitude;
}
void ADSBDemodSinkWorker::handleModeS(unsigned char *data, int bytes, unsigned icao, int df, int firstIndex, unsigned short preamble, Real preambleCorrelation, Real correlationOnes, const QDateTime& dateTime, unsigned crc)
{
// Ignore downlink formats we can't decode / unlikely
if ((df != 19) && (df != 22) && (df < 24))
{
QList<RXRecord> l;
if (m_modeSOnlyIcaos.contains(icao))
{
l = m_modeSOnlyIcaos.value(icao);
if (abs(l.last().m_firstIndex - firstIndex) < 4) {
return; // Duplicate
}
}
else
{
// Stop hash table from getting too big - clear every 10 seconds or so
QDateTime currentDateTime = QDateTime::currentDateTime();
if (m_lastClear.secsTo(currentDateTime) >= 10)
{
//qDebug() << "Clearing ModeS only hash. size=" << m_modeSOnlyIcaos.size();
m_modeSOnlyIcaos.clear();
m_lastClear = currentDateTime;
}
}
RXRecord rx;
rx.m_data = QByteArray((char*)data, bytes);
rx.m_firstIndex = firstIndex;
rx.m_preamble = preamble;
rx.m_preambleCorrelation = preambleCorrelation;
rx.m_correlationOnes = correlationOnes;
rx.m_dateTime = dateTime;
rx.m_crc = crc;
l.append(rx);
m_modeSOnlyIcaos.insert(icao, l);
// Have we heard from the same address several times in the last 10 seconds?
if (l.size() >= 5)
{
// Check all frames have consistent altitudes and identifiers
bool idConsistent = true;
bool altitudeConsistent = true;
int altitude = -1;
int id = -1;
for (int i = 0; i < l.size(); i++)
{
int df2 = ((l[i].m_data[0] >> 3) & ADS_B_DF_MASK);
if ((df2 == 0) || (df2 == 4) || (df2 == 16) || (df2 == 20))
{
int curAltitude = decodeModeSAltitude(l[i].m_data);
if (altitude == -1)
{
altitude = curAltitude;
}
else
{
if (abs(curAltitude - altitude) > 1000) {
altitudeConsistent = false;
}
}
}
else if ((df2 == 5) || (df2 == 21))
{
int curId = ((data[2] & 0x1f) << 8) | (data[3] & 0xff); // No decode - we just want to know if it changes
if (id == -1)
{
id = curId;
}
else
{
if (id != curId) {
idConsistent = false;
}
}
}
}
// FIXME: We could optionally check to see if aircraft ICAO is in db, but the db isn't complete
if (altitudeConsistent && idConsistent)
{
// Forward all frames
for (int i = 0; i < l.size(); i++) {
forwardFrame((const unsigned char *) l[i].m_data.data(), l[i].m_data.size(), l[i].m_preambleCorrelation, l[i].m_correlationOnes, l[i].m_dateTime, l[i].m_crc);
}
m_icaos.insert(icao, l.back().m_dateTime.toMSecsSinceEpoch());
}
else
{
m_modeSOnlyIcaos.remove(icao);
}
}
}
}
// Check if a Mode S frame has reserved bits set or reserved field values
// Refer: Annex 10 Volume IV
bool ADSBDemodSinkWorker::modeSValid(unsigned char *data, unsigned df)
{
bool invalid = false;
if (df == 0)
{
invalid = ((data[0] & 0x1) | (data[1] & 0x18) | (data[2] & 0x60)) != 0;
}
else if ((df == 4) || (df == 20))
{
unsigned fs = data[0] & 0x3;
unsigned dr = (data[1] >> 3) & 0x1f;
invalid = (fs == 6) || (fs == 7) || ((dr >= 8) && (dr <= 15));
}
else if ((df == 5) || (df == 21))
{
unsigned fs = data[0] & 0x3;
unsigned dr = (data[1] >> 3) & 0x1f;
invalid = (fs == 6) || (fs == 7) || ((dr >= 8) && (dr <= 15)) || ((data[3] & 0x40) != 0);
}
else if (df == 11)
{
unsigned ca = data[0] & 0x7;
invalid = ((ca >= 1) && (ca <= 3));
}
else if (df == 16)
{
invalid = ((data[0] & 0x3) | (data[1] & 0x18) | (data[2] & 0x60)) != 0;
}
return invalid;
}
// Check if valid ICAO address - i.e. not a reserved address - see Table 9-1 in Annex X Volume III
bool ADSBDemodSinkWorker::icaoValid(unsigned icao)
{
unsigned msn = (icao >> 20) & 0xf;
return (icao != 0) && (msn != 0xf) && (msn != 0xb) && (msn != 0xd);
}
// Is it less than a minute since the last received frame for this ICAO
bool ADSBDemodSinkWorker::icaoHeardRecently(unsigned icao, const QDateTime &dateTime)
{
const int timeLimitMSec = 60*1000;
if (m_icaos.contains(icao))
{
if ((dateTime.toMSecsSinceEpoch() - m_icaos.value(icao)) < timeLimitMSec) {
return true;
} else {
m_icaos.remove(icao);
}
}
return false;
}
void ADSBDemodSinkWorker::forwardFrame(const unsigned char *data, int size, float preambleCorrelation, float correlationOnes, const QDateTime& dateTime, unsigned crc)
{
// Pass to GUI
if (m_sink->getMessageQueueToGUI())
{
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
QByteArray((char*)data, size),
preambleCorrelation,
correlationOnes,
dateTime,
crc);
m_sink->getMessageQueueToGUI()->push(msg);
}
// Pass to worker to feed to other servers
if (m_sink->getMessageQueueToWorker())
{
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
QByteArray((char*)data, size),
preambleCorrelation,
correlationOnes,
dateTime,
crc);
m_sink->getMessageQueueToWorker()->push(msg);
}
}
void ADSBDemodSinkWorker::run()
{
int readBuffer = 0;
m_lastClear = QDateTime::currentDateTime();
// Acquire first buffer
m_sink->m_bufferRead[readBuffer].acquire();
if (isInterruptionRequested()) {
return;
}
// Start recording how much time is spent processing in this method
boost::chrono::steady_clock::time_point startPoint = boost::chrono::steady_clock::now();
PROFILER_START();
// Check for updated settings
handleInputMessages();
@ -51,8 +310,6 @@ void ADSBDemodSinkWorker::run()
<< " samplesPerFrame: " << samplesPerFrame
<< " samplesPerChip: " << samplesPerChip
<< " samplesPerBit: " << samplesPerBit
<< " correlateFullPreamble: " << m_settings.m_correlateFullPreamble
<< " correlationScale: " << m_correlationScale
<< " correlationThreshold: " << m_settings.m_correlationThreshold;
int readIdx = m_sink->m_samplesPerFrame - 1;
@ -63,61 +320,33 @@ void ADSBDemodSinkWorker::run()
// Correlate received signal with expected preamble
// chip+ indexes are 0, 2, 7, 9
// correlating over first 6 bits gives a reduction in per-sample
// processing, but more than doubles the number of false matches
Real preambleCorrelationOnes = 0.0;
Real preambleCorrelationZeros = 0.0;
if (m_settings.m_correlateFullPreamble)
for (int i = 0; i < samplesPerChip; i++)
{
for (int i = 0; i < samplesPerChip; i++)
{
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 0*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 1*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 0*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 1*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 2*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 3*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 2*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 3*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 4*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 5*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 4*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 5*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 6*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 7*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 6*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 7*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 8*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 9*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 8*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 9*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 10*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 11*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 10*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 11*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 12*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 13*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 12*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 13*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 14*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 15*samplesPerChip + i];
}
}
else
{
for (int i = 0; i < samplesPerChip; i++)
{
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 0*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 1*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 2*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 3*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 4*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 5*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 6*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 7*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 8*samplesPerChip + i];
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 9*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 10*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 11*samplesPerChip + i];
}
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 14*samplesPerChip + i];
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 15*samplesPerChip + i];
}
// Use the ratio of ones power over zeros power, as we don't care how powerful the signal
@ -138,6 +367,7 @@ void ADSBDemodSinkWorker::run()
int firstIdx = startIdx;
m_demodStats.m_correlatorMatches++;
// Skip over preamble
startIdx += samplesPerBit*ADS_B_PREAMBLE_BITS;
@ -147,6 +377,7 @@ void ADSBDemodSinkWorker::run()
int currentBit;
unsigned char currentByte = 0;
int df;
int firstIndex = 0;
for (int bit = 0; bit < ADS_B_ES_BITS; bit++)
{
@ -165,6 +396,9 @@ void ADSBDemodSinkWorker::run()
currentByte |= currentBit << (7-(bit & 0x7));
if ((bit & 0x7) == 0x7)
{
if (byteIdx == 0) {
firstIndex = startIdx;
}
data[byteIdx++] = currentByte;
currentByte = 0;
// Don't try to demodulate any further, if this isn't an ADS-B frame
@ -178,6 +412,10 @@ void ADSBDemodSinkWorker::run()
}
}
Real preambleCorrelationScaled = preambleCorrelation * m_correlationScale;
Real correlationOnes = preambleCorrelationOnes / samplesPerChip;
QDateTime dateTime = rxDateTime(firstIdx, readBuffer);
// Is ADS-B?
df = ((data[0] >> 3) & ADS_B_DF_MASK);
if ((df == 17) || (df == 18))
@ -188,109 +426,177 @@ void ADSBDemodSinkWorker::run()
m_crc.calculate(data, ADS_B_ES_BYTES-3);
if (parity == m_crc.get())
{
// Got a valid frame
m_demodStats.m_adsbFrames++;
// Get 24-bit ICAO and save in hash of ICAOs that have been seen
// Get 24-bit ICAO
unsigned icao = ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff);
m_icaos.insert(icao, true);
// Don't try to re-demodulate the same frame
// We could possibly allow a partial overlap here
readIdx += (ADS_B_ES_BITS+ADS_B_PREAMBLE_BITS)*ADS_B_CHIPS_PER_BIT*samplesPerChip - 1;
// Pass to GUI
if (m_sink->getMessageQueueToGUI())
if (icaoValid(icao))
{
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
QByteArray((char*)data, sizeof(data)),
preambleCorrelation * m_correlationScale,
preambleCorrelationOnes / samplesPerChip,
rxDateTime(firstIdx, readBuffer),
m_crc.get());
m_sink->getMessageQueueToGUI()->push(msg);
// Got a valid frame
m_demodStats.m_adsbFrames++;
// Save in hash of ICAOs that have been seen
m_icaos.insert(icao, dateTime.toMSecsSinceEpoch());
// Don't try to re-demodulate the same frame
// We could possibly allow a partial overlap here
readIdx += (ADS_B_ES_BITS+ADS_B_PREAMBLE_BITS)*ADS_B_CHIPS_PER_BIT*samplesPerChip - 1;
forwardFrame(data, sizeof(data), preambleCorrelationScaled, correlationOnes, dateTime, m_crc.get());
}
// Pass to worker to feed to other servers
if (m_sink->getMessageQueueToWorker())
else
{
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
QByteArray((char*)data, sizeof(data)),
preambleCorrelation * m_correlationScale,
preambleCorrelationOnes / samplesPerChip,
rxDateTime(firstIdx, readBuffer),
m_crc.get());
m_sink->getMessageQueueToWorker()->push(msg);
m_demodStats.m_icaoFails++;
}
}
else
{
m_demodStats.m_crcFails++;
}
}
else if (m_settings.m_demodModeS)
{
int bytes;
// Decode premable, as correlation alone results in too many false positives for Mode S frames
startIdx = readIdx;
unsigned short preamble = 0;
QVector<float> preambleChips(16);
// Determine number of bytes in frame depending on downlink format
if ((df == 0) || (df == 4) || (df == 5) || (df == 11)) {
bytes = 56/8;
} else if ((df == 16) || (df == 20) || (df == 21) || (df >= 24)) {
bytes = 112/8;
} else {
bytes = 0;
}
if (bytes > 0)
for (int bit = 0; bit < ADS_B_PREAMBLE_BITS; bit++)
{
// Extract received parity
int parity = (data[bytes-3] << 16) | (data[bytes-2] << 8) | data[bytes-1];
// Calculate CRC on received frame
m_crc.init();
m_crc.calculate(data, bytes-3);
int crc = m_crc.get();
// DF4 / DF5 / DF20 / DF21 have ICAO address XORed in to parity.
// Extract ICAO from parity and see if it matches an aircraft we've already
// received an ADS-B frame from
if ((df == 4) || (df == 5) || (df == 20) || (df == 21))
preambleChips[bit*2] = 0.0f;
preambleChips[bit*2+1] = 0.0f;
for (int i = 0; i < samplesPerChip; i++)
{
unsigned icao = (parity ^ crc) & 0xffffff;
if (m_icaos.contains(icao)) {
crc ^= icao;
}
preambleChips[bit*2] += m_sink->m_sampleBuffer[readBuffer][startIdx+i];
preambleChips[bit*2+1] += m_sink->m_sampleBuffer[readBuffer][startIdx+samplesPerChip+i];
}
// For DF11, the last 7 bits may have an address/interogration identifier (II)
// XORed in, so we ignore those bits
if ((parity == crc) || ((df == 11) && ((parity & 0xffff80) == (crc & 0xffff80))))
startIdx += samplesPerBit;
}
startIdx = firstIdx;
float onesAvg = (preambleChips[0] + preambleChips[2] + preambleChips[7] + preambleChips[9]) / 4.0f;
float zerosAvg = (preambleChips[1] + preambleChips[3] + preambleChips[4] + preambleChips[5] + preambleChips[6] + preambleChips[8]
+ preambleChips[10] + + preambleChips[11] + preambleChips[12] + preambleChips[13] + preambleChips[14] + preambleChips[15]) / 12.0f;
float midPoint = zerosAvg + (onesAvg - zerosAvg) / 2.0f;
for (int i = 0; i < 16; i++)
{
unsigned chip = preambleChips[i] > midPoint;
preamble |= chip << (15-i);
}
// qDebug() << "Preamble" << preambleChips << "zerosAvg" << zerosAvg << "onesAvg" << onesAvg << "midPoint" << midPoint << "chips" << Qt::hex << preamble;
const unsigned short expectedPreamble = 0xa140;
int preambleDifferences = popcount(expectedPreamble ^ preamble);
if (preambleDifferences <= m_settings.m_chipsThreshold)
{
int bytes;
// Determine number of bytes in frame depending on downlink format
if ((df == 0) || (df == 4) || (df == 5) || (df == 11)) {
bytes = 56/8;
} else if ((df == 16) || (df == 19) || (df == 20) || (df == 21) || (df == 22) || ((df >= 24) && (df <= 27))) {
bytes = 112/8;
} else {
bytes = 0;
}
if (bytes > 0)
{
m_demodStats.m_modesFrames++;
// Pass to GUI (only pass formats it can decode)
if (m_sink->getMessageQueueToGUI() && ((df == 4) || (df == 5) || (df == 20) || (df == 21)))
bool invalid = modeSValid(data, df);
if (!invalid)
{
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
QByteArray((char*)data, bytes),
preambleCorrelation * m_correlationScale,
preambleCorrelationOnes / samplesPerChip,
rxDateTime(firstIdx, readBuffer),
m_crc.get());
m_sink->getMessageQueueToGUI()->push(msg);
// Extract received parity
int parity = (data[bytes-3] << 16) | (data[bytes-2] << 8) | data[bytes-1];
// Calculate CRC on received frame
m_crc.init();
m_crc.calculate(data, bytes-3);
int crc = m_crc.get();
bool forward = false;
// ICAO address XORed in to parity, apart from DF11
// Extract ICAO from parity and see if it matches an aircraft we've already
// received an ADS-B or Mode S frame from
// For DF11, the last 7 bits may have an iterogration code (II/SI)
// XORed in, so we ignore those bits. This does sometimes lead to false-positives
if (df != 11)
{
unsigned icao = (parity ^ crc) & 0xffffff;
if (icaoValid(icao))
{
if (icaoHeardRecently(icao, dateTime)) {
forward = true;
} else {
handleModeS(data, bytes, icao, df, firstIndex, preamble, preambleCorrelationScaled, correlationOnes, dateTime, m_crc.get());
}
}
else
{
m_demodStats.m_icaoFails++;
}
}
else
{
// Ignore IC bits
parity &= 0xffff80;
crc &= 0xffff80;
if (parity == crc)
{
// Get 24-bit ICAO
unsigned icao = ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff);
if (icaoValid(icao))
{
if (icaoHeardRecently(icao, dateTime)) {
forward = true;
} else {
handleModeS(data, bytes, icao, df, firstIndex, preamble, preambleCorrelationScaled, correlationOnes, dateTime, m_crc.get());
}
}
else
{
m_demodStats.m_icaoFails++;
}
}
else
{
m_demodStats.m_crcFails++;
}
}
if (forward)
{
m_demodStats.m_modesFrames++;
// Don't try to re-demodulate the same frame
// We could possibly allow a partial overlap here
readIdx += ((bytes*8)+ADS_B_PREAMBLE_BITS)*ADS_B_CHIPS_PER_BIT*samplesPerChip - 1;
forwardFrame(data, bytes, preambleCorrelationScaled, correlationOnes, dateTime, m_crc.get());
}
else
{
m_demodStats.m_crcFails++;
}
}
// Pass to worker to feed to other servers
if (m_sink->getMessageQueueToWorker())
else
{
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
QByteArray((char*)data, bytes),
preambleCorrelation * m_correlationScale,
preambleCorrelationOnes / samplesPerChip,
rxDateTime(firstIdx, readBuffer),
m_crc.get());
m_sink->getMessageQueueToWorker()->push(msg);
m_demodStats.m_invalidFails++;
}
}
else
{
m_demodStats.m_crcFails++;
m_demodStats.m_typeFails++;
}
}
else
m_demodStats.m_typeFails++;
{
m_demodStats.m_preambleFails++;
}
}
else
{
m_demodStats.m_typeFails++;
}
}
readIdx++;
if (readIdx > m_sink->m_bufferSize - samplesPerFrame)
{
@ -299,9 +605,7 @@ void ADSBDemodSinkWorker::run()
nextBuffer = 0;
// Update amount of time spent processing (don't include time spend in acquire)
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - startPoint;
m_demodStats.m_demodTime += sec.count();
m_demodStats.m_feedTime = m_sink->m_feedTime;
PROFILER_STOP("ADS-B demod");
// Send stats to GUI
if (m_sink->getMessageQueueToGUI())
@ -319,7 +623,7 @@ void ADSBDemodSinkWorker::run()
handleInputMessages();
// Resume timing how long we are processing
startPoint = boost::chrono::steady_clock::now();
PROFILER_RESTART();
int samplesRemaining = m_sink->m_bufferSize - readIdx;
if (samplesRemaining > 0)
@ -359,15 +663,6 @@ void ADSBDemodSinkWorker::handleInputMessages()
QStringList settingsKeys = cfg->getSettingsKeys();
bool force = cfg->getForce();
if (settingsKeys.contains("correlateFullPreamble") || force)
{
if (settings.m_correlateFullPreamble) {
m_correlationScale = 3.0;
} else {
m_correlationScale = 2.0;
}
}
if ((settingsKeys.contains("correlationThreshold") && (m_settings.m_correlationThreshold != settings.m_correlationThreshold)) || force)
{
m_correlationThresholdLinear = CalcDb::powerFromdB(settings.m_correlationThreshold);
@ -382,6 +677,10 @@ void ADSBDemodSinkWorker::handleInputMessages()
}
delete message;
}
else if (ADSBDemod::MsgResetStats::match(*message))
{
m_demodStats.reset();
}
}
}

View File

@ -77,11 +77,29 @@ private:
ADSBDemodSink *m_sink;
ADSBDemodStats m_demodStats;
Real m_correlationThresholdLinear;
Real m_correlationScale;
static const Real m_correlationScale;
crcadsb m_crc; //!< Have as member to avoid recomputing LUT
QHash<int, bool> m_icaos; //!< ICAO addresses that have been received
QHash<unsigned, qint64> m_icaos; //!< ICAO addresses that have been received and msecsSinceEpoch last heard
QDateTime m_lastClear;
struct RXRecord {
QByteArray m_data;
int m_firstIndex;
unsigned short m_preamble;
Real m_preambleCorrelation;
Real m_correlationOnes;
QDateTime m_dateTime;
unsigned m_crc;
};
QHash<int, QList<RXRecord>> m_modeSOnlyIcaos;
QDateTime rxDateTime(int firstIdx, int readBuffer) const;
void handleModeS(unsigned char *data, int bytes, unsigned icao, int df, int firstIndex, unsigned short preamble, Real preambleCorrelation, Real correlationOnes, const QDateTime& dateTime, unsigned crc);
void forwardFrame(const unsigned char *data, int size, float preambleCorrelation, float correlationOnes, const QDateTime& dateTime, unsigned crc);
bool icaoHeardRecently(unsigned icao, const QDateTime &dateTime);
static bool icaoValid(unsigned icao);
static bool modeSValid(unsigned char *data, unsigned df);
};

View File

@ -25,20 +25,27 @@ struct ADSBDemodStats {
qint64 m_correlatorMatches; //!< Total number of correlator matches
qint64 m_adsbFrames; //!< How many ADS-B frames with correct CRCs
qint64 m_modesFrames; //!< How many non-ADS-B Mode-S frames with correct CRCs
qint64 m_preambleFails; //!< How many non-ADS-B Mode S frames with errors in preamble
qint64 m_crcFails; //!< How many frames we've demoded with incorrect CRCs
qint64 m_typeFails; //!< How many frames we've demoded with unknown type (DF) so we can't check CRC
double m_demodTime; //!< How long we've spent in run()
double m_feedTime; //!< How long we've spent in feed()
qint64 m_invalidFails; //!< How many frames we've demoded with reserved bits set
qint64 m_icaoFails; //!< How many frames we've demoded with reserved ICAO
ADSBDemodStats() :
m_correlatorMatches(0),
m_adsbFrames(0),
m_modesFrames(0),
m_crcFails(0),
m_typeFails(0),
m_demodTime(0.0),
m_feedTime(0.0)
ADSBDemodStats()
{
reset();
}
void reset()
{
m_correlatorMatches = 0;
m_adsbFrames = 0;
m_modesFrames = 0;
m_preambleFails = 0;
m_crcFails = 0;
m_typeFails = 0;
m_invalidFails = 0;
m_icaoFails = 0;
}
};

View File

@ -67,14 +67,14 @@ private slots:
{
xml = QString("\
{\
\"UrlTemplate\" : \"https://maps.wikimedia.org/osm-intl/%z/%x/%y%1.png\",\
\"UrlTemplate\" : \"https://tile.openstreetmap.org/%z/%x/%y.png\",\
\"ImageFormat\" : \"png\",\
\"QImageFormat\" : \"Indexed8\",\
\"ID\" : \"wmf-intl-%2x\",\
\"ID\" : \"osm-%1x\",\
\"MaximumZoomLevel\" : 18,\
\"MapCopyRight\" : \"<a href='https://wikimediafoundation.org/wiki/Terms_of_Use'>WikiMedia Foundation</a>\",\
\"DataCopyRight\" : \"<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\"\
}").arg(hiresURL).arg(hires ? 1 : 2);
}").arg(hires ? 1 : 2);
}
else if (tokens[1] == "/satellite")
{

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Some files were not shown because too many files have changed in this diff Show More