mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-06-24 21:15:24 -04:00
Add additional demod stats.
Add analysis of Mode S frames in order to help filter out false positives. Remove Boost. Use Profiler instead. Fix crash if interrrupted before run. Decode full preamble for Mode S frames. Add support for additional Mode S downlink formats. Allow demod stats to be reset. Add timestamps for buffers, to ensure ordering of frames. Add additional data columns (Aircraft Type, Sideview, Track, Interrogator Code, TCAS, ACAS, RA, Max speed, Version, Length, Width, ADS-B/Mode S frame counts, radius, NACp, NACv, GVA, NIC, SIL, Stops). Add PCE (Preamble Chip Errors) settings for Mode S demod. Remove correlate full preamable setting. Send aircraft state to Map feature for display on PFD. Add support for airline route database. Use combined aircraft database from sdrangel.org rather than OSN database. Add stats table, with demod stats and breakdown of frame types received.. Add button to delete all aircraft from table. Add display of interrogator code coverage. Remove airport elevation setting as now calculated dynamically. Add QNH setting. Add coverage map. Add chart of frame rate and aircraft count. Add table/map orientation setting. Add display of aircraft position uncertainty. Add coloured flight paths with several palettes. Add setting to favour airline livery over aircraft type in 3D model matching. Only use 4 engine map icon for aircraft with 4 engines. Add specialised aircraft map icons (Eurofighter, Spitfire, F35, A400M, Apache, Chinook, Glider) Display aircraft route in labels. Better validate local/global aircraft positions. Add support for tc==31, aircraft operational status frames. Add decoding of Mode S df 0, 11, 16 frames. Add decoding of BDS 0,5 0,8, 0,9.
This commit is contained in:
parent
f882747e97
commit
1975c1748c
@ -17,10 +17,6 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
// 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 <complex.h>
|
||||||
|
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
@ -49,6 +45,7 @@
|
|||||||
|
|
||||||
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgConfigureADSBDemod, Message)
|
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgConfigureADSBDemod, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgAircraftReport, 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_channelIdURI = "sdrangel.channel.adsbdemod";
|
||||||
const char* const ADSBDemod::m_channelId = "ADSBDemod";
|
const char* const ADSBDemod::m_channelId = "ADSBDemod";
|
||||||
@ -194,6 +191,13 @@ bool ADSBDemod::handleMessage(const Message& cmd)
|
|||||||
m_aircraftReport = msg.getReport();
|
m_aircraftReport = msg.getReport();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (MsgResetStats::match(cmd))
|
||||||
|
{
|
||||||
|
MsgResetStats& msg = (MsgResetStats&) cmd;
|
||||||
|
MsgResetStats* rep = new MsgResetStats(msg);
|
||||||
|
m_basebandSink->getInputMessageQueue()->push(rep);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -337,9 +341,6 @@ void ADSBDemod::webapiUpdateChannelSettings(
|
|||||||
if (channelSettingsKeys.contains("samplesPerBit")) {
|
if (channelSettingsKeys.contains("samplesPerBit")) {
|
||||||
settings.m_samplesPerBit = response.getAdsbDemodSettings()->getSamplesPerBit();
|
settings.m_samplesPerBit = response.getAdsbDemodSettings()->getSamplesPerBit();
|
||||||
}
|
}
|
||||||
if (channelSettingsKeys.contains("correlateFullPreamble")) {
|
|
||||||
settings.m_correlateFullPreamble = response.getAdsbDemodSettings()->getCorrelateFullPreamble() != 0;
|
|
||||||
}
|
|
||||||
if (channelSettingsKeys.contains("demodModeS")) {
|
if (channelSettingsKeys.contains("demodModeS")) {
|
||||||
settings.m_demodModeS = response.getAdsbDemodSettings()->getDemodModeS() != 0;
|
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()->setRfBandwidth(settings.m_rfBandwidth);
|
||||||
response.getAdsbDemodSettings()->setCorrelationThreshold(settings.m_correlationThreshold);
|
response.getAdsbDemodSettings()->setCorrelationThreshold(settings.m_correlationThreshold);
|
||||||
response.getAdsbDemodSettings()->setSamplesPerBit(settings.m_samplesPerBit);
|
response.getAdsbDemodSettings()->setSamplesPerBit(settings.m_samplesPerBit);
|
||||||
response.getAdsbDemodSettings()->setCorrelateFullPreamble(settings.m_correlateFullPreamble ? 1 : 0);
|
|
||||||
response.getAdsbDemodSettings()->setDemodModeS(settings.m_demodModeS ? 1 : 0);
|
response.getAdsbDemodSettings()->setDemodModeS(settings.m_demodModeS ? 1 : 0);
|
||||||
response.getAdsbDemodSettings()->setInterpolatorPhaseSteps(settings.m_interpolatorPhaseSteps);
|
response.getAdsbDemodSettings()->setInterpolatorPhaseSteps(settings.m_interpolatorPhaseSteps);
|
||||||
response.getAdsbDemodSettings()->setInterpolatorTapsPerPhase(settings.m_interpolatorTapsPerPhase);
|
response.getAdsbDemodSettings()->setInterpolatorTapsPerPhase(settings.m_interpolatorTapsPerPhase);
|
||||||
@ -587,9 +587,6 @@ void ADSBDemod::webapiReverseSendSettings(const QList<QString>& channelSettingsK
|
|||||||
if (channelSettingsKeys.contains("samplesPerBit") || force) {
|
if (channelSettingsKeys.contains("samplesPerBit") || force) {
|
||||||
swgADSBDemodSettings->setSamplesPerBit(settings.m_samplesPerBit);
|
swgADSBDemodSettings->setSamplesPerBit(settings.m_samplesPerBit);
|
||||||
}
|
}
|
||||||
if (channelSettingsKeys.contains("correlateFullPreamble") || force) {
|
|
||||||
swgADSBDemodSettings->setCorrelateFullPreamble(settings.m_correlateFullPreamble ? 1 : 0);
|
|
||||||
}
|
|
||||||
if (channelSettingsKeys.contains("demodModeS") || force) {
|
if (channelSettingsKeys.contains("demodModeS") || force) {
|
||||||
swgADSBDemodSettings->setDemodModeS(settings.m_demodModeS ? 1 : 0);
|
swgADSBDemodSettings->setDemodModeS(settings.m_demodModeS ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
ADSBDemod(DeviceAPI *deviceAPI);
|
||||||
virtual ~ADSBDemod();
|
virtual ~ADSBDemod();
|
||||||
virtual void destroy() { delete this; }
|
virtual void destroy() { delete this; }
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "dsp/downchannelizer.h"
|
#include "dsp/downchannelizer.h"
|
||||||
|
|
||||||
#include "adsbdemodbaseband.h"
|
#include "adsbdemodbaseband.h"
|
||||||
|
#include "adsbdemod.h"
|
||||||
#include "adsb.h"
|
#include "adsb.h"
|
||||||
|
|
||||||
MESSAGE_CLASS_DEFINITION(ADSBDemodBaseband::MsgConfigureADSBDemodBaseband, Message)
|
MESSAGE_CLASS_DEFINITION(ADSBDemodBaseband::MsgConfigureADSBDemodBaseband, Message)
|
||||||
@ -132,6 +133,11 @@ bool ADSBDemodBaseband::handleMessage(const Message& cmd)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (ADSBDemod::MsgResetStats::match(cmd))
|
||||||
|
{
|
||||||
|
m_sink.resetStats();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -50,7 +50,6 @@ ADSBDemodDisplayDialog::ADSBDemodDisplayDialog(ADSBDemodSettings *settings, QWid
|
|||||||
ui->airportSize->setCurrentIndex((int)settings->m_airportMinimumSize);
|
ui->airportSize->setCurrentIndex((int)settings->m_airportMinimumSize);
|
||||||
ui->heliports->setChecked(settings->m_displayHeliports);
|
ui->heliports->setChecked(settings->m_displayHeliports);
|
||||||
ui->units->setCurrentIndex((int)settings->m_siUnits);
|
ui->units->setCurrentIndex((int)settings->m_siUnits);
|
||||||
ui->displayStats->setChecked(settings->m_displayDemodStats);
|
|
||||||
ui->autoResizeTableColumns->setChecked(settings->m_autoResizeTableColumns);
|
ui->autoResizeTableColumns->setChecked(settings->m_autoResizeTableColumns);
|
||||||
ui->aviationstackAPIKey->setText(settings->m_aviationstackAPIKey);
|
ui->aviationstackAPIKey->setText(settings->m_aviationstackAPIKey);
|
||||||
ui->checkWXAPIKey->setText(settings->m_checkWXAPIKey);
|
ui->checkWXAPIKey->setText(settings->m_checkWXAPIKey);
|
||||||
@ -71,8 +70,12 @@ ADSBDemodDisplayDialog::ADSBDemodDisplayDialog(ADSBDemodSettings *settings, QWid
|
|||||||
ui->atcCallsigns->setChecked(settings->m_atcCallsigns);
|
ui->atcCallsigns->setChecked(settings->m_atcCallsigns);
|
||||||
ui->photos->setChecked(settings->m_displayPhotos);
|
ui->photos->setChecked(settings->m_displayPhotos);
|
||||||
ui->verboseModelMatching->setChecked(settings->m_verboseModelMatching);
|
ui->verboseModelMatching->setChecked(settings->m_verboseModelMatching);
|
||||||
ui->airfieldElevation->setValue(settings->m_airfieldElevation);
|
ui->favourLivery->setChecked(settings->m_favourLivery);
|
||||||
ui->transitionAltitude->setValue(settings->m_transitionAlt);
|
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()
|
ADSBDemodDisplayDialog::~ADSBDemodDisplayDialog()
|
||||||
@ -107,16 +110,11 @@ void ADSBDemodDisplayDialog::accept()
|
|||||||
m_settings->m_displayHeliports = ui->heliports->isChecked();
|
m_settings->m_displayHeliports = ui->heliports->isChecked();
|
||||||
m_settingsKeys.append("displayHeliports");
|
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_settings->m_siUnits = ui->units->currentIndex() == 0 ? false : true;
|
||||||
m_settingsKeys.append("siUnits");
|
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())
|
if (m_settings->m_autoResizeTableColumns != ui->autoResizeTableColumns->isChecked())
|
||||||
{
|
{
|
||||||
m_settings->m_autoResizeTableColumns = ui->autoResizeTableColumns->isChecked();
|
m_settings->m_autoResizeTableColumns = ui->autoResizeTableColumns->isChecked();
|
||||||
@ -180,10 +178,10 @@ void ADSBDemodDisplayDialog::accept()
|
|||||||
m_settings->m_verboseModelMatching = ui->verboseModelMatching->isChecked();
|
m_settings->m_verboseModelMatching = ui->verboseModelMatching->isChecked();
|
||||||
m_settingsKeys.append("verboseModelMatching");
|
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_settings->m_favourLivery = ui->favourLivery->isChecked();
|
||||||
m_settingsKeys.append("airfieldElevation");
|
m_settingsKeys.append("favourLivery");
|
||||||
}
|
}
|
||||||
if (m_settings->m_transitionAlt != ui->transitionAltitude->value())
|
if (m_settings->m_transitionAlt != ui->transitionAltitude->value())
|
||||||
{
|
{
|
||||||
@ -200,6 +198,12 @@ void ADSBDemodDisplayDialog::accept()
|
|||||||
m_settings->m_tableFontSize = m_fontSize;
|
m_settings->m_tableFontSize = m_fontSize;
|
||||||
m_settingsKeys.append("tableFontSize");
|
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();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>417</width>
|
<width>417</width>
|
||||||
<height>467</height>
|
<height>505</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
@ -124,22 +124,16 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="displayStatsLabel">
|
<widget class="QLabel" name="verboseModelMatchingLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Display demodulator statistics</string>
|
<string>Log 3D model matching information</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QCheckBox" name="displayStats">
|
<widget class="QCheckBox" name="verboseModelMatching">
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Display demodulator statistics</string>
|
<string>Log information about how aircraft are matched to 3D models</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
@ -147,16 +141,16 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QLabel" name="verboseModelMatchingLabel">
|
<widget class="QLabel" name="favourLiveryLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Log 3D model matching information</string>
|
<string>Favour airline livery over aircraft type</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QCheckBox" name="verboseModelMatching">
|
<widget class="QCheckBox" name="favourLivery">
|
||||||
<property name="toolTip">
|
<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>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
@ -192,36 +186,13 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="0">
|
<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">
|
<widget class="QLabel" name="transitionAltitudeLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Transition altitude (ft)</string>
|
<string>Transition altitude (ft)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="1">
|
<item row="9" column="1">
|
||||||
<widget class="QSpinBox" name="transitionAltitude">
|
<widget class="QSpinBox" name="transitionAltitude">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Transition altitude in feet</string>
|
<string>Transition altitude in feet</string>
|
||||||
@ -232,6 +203,9 @@
|
|||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>20000</number>
|
<number>20000</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>6000</number>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -598,6 +572,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="10" 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="10" column="0">
|
||||||
|
<widget class="QLabel" name="flightPathPaletteLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Flight path palette</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -607,10 +595,10 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,7 @@
|
|||||||
#include <QTextToSpeech>
|
#include <QTextToSpeech>
|
||||||
#include <QRandomGenerator>
|
#include <QRandomGenerator>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QtCharts>
|
||||||
|
|
||||||
#include "channel/channelgui.h"
|
#include "channel/channelgui.h"
|
||||||
#include "dsp/dsptypes.h"
|
#include "dsp/dsptypes.h"
|
||||||
@ -56,11 +57,17 @@ class WebAPIAdapterInterface;
|
|||||||
class HttpDownloadManager;
|
class HttpDownloadManager;
|
||||||
class ADSBDemodGUI;
|
class ADSBDemodGUI;
|
||||||
class ADSBOSMTemplateServer;
|
class ADSBOSMTemplateServer;
|
||||||
|
class CheckList;
|
||||||
|
class AircraftModel;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ADSBDemodGUI;
|
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
|
// Custom widget to allow formatted decimal numbers to be sorted numerically
|
||||||
class CustomDoubleTableWidgetItem : public QTableWidgetItem
|
class CustomDoubleTableWidgetItem : public QTableWidgetItem
|
||||||
{
|
{
|
||||||
@ -89,12 +96,16 @@ struct Aircraft {
|
|||||||
QString m_icaoHex;
|
QString m_icaoHex;
|
||||||
QString m_callsign; // Flight callsign
|
QString m_callsign; // Flight callsign
|
||||||
QString m_flight; // Guess at flight number
|
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_latitude; // Latitude in decimal degrees
|
||||||
Real m_longitude; // Longitude 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_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)
|
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
|
int m_verticalRate; // Vertical climb rate in ft/min
|
||||||
QString m_emitterCategory; // Aircraft type
|
QString m_emitterCategory; // Aircraft type
|
||||||
QString m_status; // Aircraft status
|
QString m_status; // Aircraft status
|
||||||
@ -102,23 +113,37 @@ struct Aircraft {
|
|||||||
Real m_range; // Distance from station to aircraft
|
Real m_range; // Distance from station to aircraft
|
||||||
Real m_azimuth; // Azimuth from station to aircraft
|
Real m_azimuth; // Azimuth from station to aircraft
|
||||||
Real m_elevation; // Elevation 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_selAltitude; // Selected altitude in MCP/FCU or FMS in feet
|
||||||
int m_selHeading; // Selected heading in MCP/FCU in degrees
|
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
|
float m_roll; // In degrees
|
||||||
int m_groundspeed; // In knots
|
int m_groundspeed; // In knots
|
||||||
float m_turnRate; // In degrees per second
|
float m_turnRate; // In degrees per second
|
||||||
int m_trueAirspeed; // In knots
|
int m_trueAirspeed; // In knots
|
||||||
int m_indicatedAirspeed; // In knots
|
int m_indicatedAirspeed; // In knots
|
||||||
float m_mach; // Mach number
|
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
|
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_positionValid; // Indicates if we have valid data for the above fields
|
||||||
bool m_altitudeValid;
|
bool m_altitudeValid;
|
||||||
|
bool m_pressureAltitudeValid;
|
||||||
|
bool m_onSurfaceValid;
|
||||||
bool m_headingValid;
|
bool m_headingValid;
|
||||||
|
bool m_trackValid;
|
||||||
bool m_verticalRateValid;
|
bool m_verticalRateValid;
|
||||||
bool m_selAltitudeValid;
|
bool m_selAltitudeValid;
|
||||||
bool m_selHeadingValid;
|
bool m_selHeadingValid;
|
||||||
@ -129,16 +154,30 @@ struct Aircraft {
|
|||||||
bool m_trueAirspeedValid;
|
bool m_trueAirspeedValid;
|
||||||
bool m_indicatedAirspeedValid;
|
bool m_indicatedAirspeedValid;
|
||||||
bool m_machValid;
|
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_bdsCapabilitiesValid;
|
||||||
|
bool m_adsbVersionValid;
|
||||||
|
bool m_nicSupplementAValid;
|
||||||
|
bool m_nicSupplementBValid;
|
||||||
|
bool m_nicSupplementCValid;
|
||||||
|
|
||||||
// State for calculating position using two CPR frames
|
// State for calculating position using two CPR frames
|
||||||
bool m_cprValid[2];
|
bool m_cprValid[2];
|
||||||
Real m_cprLat[2];
|
double m_cprLat[2];
|
||||||
Real m_cprLong[2];
|
double m_cprLong[2];
|
||||||
QDateTime m_cprTime[2];
|
QDateTime m_cprTime[2];
|
||||||
|
|
||||||
int m_adsbFrameCount; // Number of ADS-B frames for this aircraft
|
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_tisBFrameCount;
|
||||||
|
int m_adsrFrameCount;
|
||||||
float m_minCorrelation;
|
float m_minCorrelation;
|
||||||
float m_maxCorrelation;
|
float m_maxCorrelation;
|
||||||
float m_correlation;
|
float m_correlation;
|
||||||
@ -148,8 +187,12 @@ struct Aircraft {
|
|||||||
bool m_isHighlighted; // Are we highlighting this aircraft in the table and map
|
bool m_isHighlighted; // Are we highlighting this aircraft in the table and map
|
||||||
bool m_showAll;
|
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<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
|
AircraftInformation *m_aircraftInfo; // Info about the aircraft from the database
|
||||||
QString m_aircraft3DModel; // 3D model for map based on aircraft type
|
QString m_aircraft3DModel; // 3D model for map based on aircraft type
|
||||||
@ -159,6 +202,7 @@ struct Aircraft {
|
|||||||
ADSBDemodGUI *m_gui;
|
ADSBDemodGUI *m_gui;
|
||||||
QString m_flagIconURL;
|
QString m_flagIconURL;
|
||||||
QString m_airlineIconURL;
|
QString m_airlineIconURL;
|
||||||
|
QString m_sideviewIconURL;
|
||||||
|
|
||||||
// For animation on 3D map
|
// For animation on 3D map
|
||||||
float m_runwayAltitude;
|
float m_runwayAltitude;
|
||||||
@ -168,10 +212,14 @@ struct Aircraft {
|
|||||||
bool m_rotorStarted; // Rotors started on 'Rotorcraft'
|
bool m_rotorStarted; // Rotors started on 'Rotorcraft'
|
||||||
bool m_engineStarted; // Engines started (typically propellors)
|
bool m_engineStarted; // Engines started (typically propellors)
|
||||||
QDateTime m_positionDateTime;
|
QDateTime m_positionDateTime;
|
||||||
QDateTime m_orientationDateTime;
|
QDateTime m_orientationDateTime; // FIXME
|
||||||
QDateTime m_headingDateTime;
|
QDateTime m_headingDateTime;
|
||||||
QDateTime m_prevHeadingDateTime;
|
QDateTime m_trackDateTime;
|
||||||
int m_prevHeading;
|
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_pitchEst; // Estimated pitch based on vertical rate
|
||||||
float m_rollEst; // Estimated roll based on rate of change in heading
|
float m_rollEst; // Estimated roll based on rate of change in heading
|
||||||
|
|
||||||
@ -182,30 +230,48 @@ struct Aircraft {
|
|||||||
QTableWidgetItem *m_callsignItem;
|
QTableWidgetItem *m_callsignItem;
|
||||||
QTableWidgetItem* m_atcCallsignItem;
|
QTableWidgetItem* m_atcCallsignItem;
|
||||||
QTableWidgetItem *m_modelItem;
|
QTableWidgetItem *m_modelItem;
|
||||||
|
QTableWidgetItem *m_typeItem;
|
||||||
|
QTableWidgetItem *m_sideviewItem;
|
||||||
QTableWidgetItem *m_airlineItem;
|
QTableWidgetItem *m_airlineItem;
|
||||||
QTableWidgetItem *m_latitudeItem;
|
QTableWidgetItem *m_latitudeItem;
|
||||||
QTableWidgetItem *m_longitudeItem;
|
QTableWidgetItem *m_longitudeItem;
|
||||||
QTableWidgetItem *m_altitudeItem;
|
QTableWidgetItem *m_altitudeItem;
|
||||||
QTableWidgetItem *m_headingItem;
|
QTableWidgetItem *m_headingItem;
|
||||||
|
QTableWidgetItem *m_trackItem;
|
||||||
QTableWidgetItem *m_verticalRateItem;
|
QTableWidgetItem *m_verticalRateItem;
|
||||||
CustomDoubleTableWidgetItem *m_rangeItem;
|
CustomDoubleTableWidgetItem *m_rangeItem;
|
||||||
QTableWidgetItem *m_azElItem;
|
QTableWidgetItem *m_azElItem;
|
||||||
QTableWidgetItem *m_emitterCategoryItem;
|
QTableWidgetItem *m_emitterCategoryItem;
|
||||||
QTableWidgetItem *m_statusItem;
|
QTableWidgetItem *m_statusItem;
|
||||||
QTableWidgetItem *m_squawkItem;
|
QTableWidgetItem *m_squawkItem;
|
||||||
|
QTableWidgetItem *m_identItem;
|
||||||
QTableWidgetItem *m_registrationItem;
|
QTableWidgetItem *m_registrationItem;
|
||||||
QTableWidgetItem *m_countryItem;
|
QTableWidgetItem *m_countryItem;
|
||||||
QTableWidgetItem *m_registeredItem;
|
QTableWidgetItem *m_registeredItem;
|
||||||
QTableWidgetItem *m_manufacturerNameItem;
|
QTableWidgetItem *m_manufacturerNameItem;
|
||||||
QTableWidgetItem *m_ownerItem;
|
QTableWidgetItem *m_ownerItem;
|
||||||
QTableWidgetItem *m_operatorICAOItem;
|
QTableWidgetItem *m_operatorICAOItem;
|
||||||
|
QTableWidgetItem *m_interogatorCodeItem;
|
||||||
QTableWidgetItem *m_timeItem;
|
QTableWidgetItem *m_timeItem;
|
||||||
|
QTableWidgetItem *m_totalFrameCountItem;
|
||||||
QTableWidgetItem *m_adsbFrameCountItem;
|
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_correlationItem;
|
||||||
QTableWidgetItem *m_rssiItem;
|
QTableWidgetItem *m_rssiItem;
|
||||||
QTableWidgetItem *m_flightStatusItem;
|
QTableWidgetItem *m_flightStatusItem;
|
||||||
QTableWidgetItem *m_depItem;
|
QTableWidgetItem *m_depItem;
|
||||||
QTableWidgetItem *m_arrItem;
|
QTableWidgetItem *m_arrItem;
|
||||||
|
QTableWidgetItem *m_stopsItem;
|
||||||
QTableWidgetItem *m_stdItem;
|
QTableWidgetItem *m_stdItem;
|
||||||
QTableWidgetItem *m_etdItem;
|
QTableWidgetItem *m_etdItem;
|
||||||
QTableWidgetItem *m_atdItem;
|
QTableWidgetItem *m_atdItem;
|
||||||
@ -218,6 +284,13 @@ struct Aircraft {
|
|||||||
QTableWidgetItem *m_apItem;
|
QTableWidgetItem *m_apItem;
|
||||||
QTableWidgetItem *m_vModeItem;
|
QTableWidgetItem *m_vModeItem;
|
||||||
QTableWidgetItem *m_lModeItem;
|
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_rollItem;
|
||||||
QTableWidgetItem *m_groundspeedItem;
|
QTableWidgetItem *m_groundspeedItem;
|
||||||
QTableWidgetItem *m_turnRateItem;
|
QTableWidgetItem *m_turnRateItem;
|
||||||
@ -231,16 +304,18 @@ struct Aircraft {
|
|||||||
QTableWidgetItem *m_staticPressureItem;
|
QTableWidgetItem *m_staticPressureItem;
|
||||||
QTableWidgetItem *m_staticAirTempItem;
|
QTableWidgetItem *m_staticAirTempItem;
|
||||||
QTableWidgetItem *m_humidityItem;
|
QTableWidgetItem *m_humidityItem;
|
||||||
QTableWidgetItem *m_tisBItem;
|
|
||||||
|
|
||||||
Aircraft(ADSBDemodGUI *gui) :
|
Aircraft(ADSBDemodGUI *gui) :
|
||||||
m_icao(0),
|
m_icao(0),
|
||||||
|
m_globalPosition(false),
|
||||||
m_latitude(0),
|
m_latitude(0),
|
||||||
m_longitude(0),
|
m_longitude(0),
|
||||||
|
m_radius(0.0f),
|
||||||
m_altitude(0),
|
m_altitude(0),
|
||||||
m_onSurface(false),
|
m_onSurface(false),
|
||||||
m_altitudeGNSS(false),
|
m_altitudeGNSS(false),
|
||||||
m_heading(0),
|
m_heading(0),
|
||||||
|
m_track(0),
|
||||||
m_verticalRate(0),
|
m_verticalRate(0),
|
||||||
m_azimuth(0),
|
m_azimuth(0),
|
||||||
m_elevation(0),
|
m_elevation(0),
|
||||||
@ -253,9 +328,22 @@ struct Aircraft {
|
|||||||
m_trueAirspeed(0),
|
m_trueAirspeed(0),
|
||||||
m_indicatedAirspeed(0),
|
m_indicatedAirspeed(0),
|
||||||
m_mach(0.0f),
|
m_mach(0.0f),
|
||||||
|
m_autopilot(false),
|
||||||
|
m_vnavMode(false),
|
||||||
|
m_altHoldMode(false),
|
||||||
|
m_approachMode(false),
|
||||||
|
m_lnavMode(false),
|
||||||
|
m_adsbVersion(0),
|
||||||
|
m_tcasOperational(false),
|
||||||
|
m_nicSupplementA(false),
|
||||||
|
m_nicSupplementB(false),
|
||||||
|
m_nicSupplementC(false),
|
||||||
m_positionValid(false),
|
m_positionValid(false),
|
||||||
m_altitudeValid(false),
|
m_altitudeValid(false),
|
||||||
|
m_pressureAltitudeValid(false),
|
||||||
|
m_onSurfaceValid(false),
|
||||||
m_headingValid(false),
|
m_headingValid(false),
|
||||||
|
m_trackValid(false),
|
||||||
m_verticalRateValid(false),
|
m_verticalRateValid(false),
|
||||||
m_selAltitudeValid(false),
|
m_selAltitudeValid(false),
|
||||||
m_selHeadingValid(false),
|
m_selHeadingValid(false),
|
||||||
@ -266,9 +354,22 @@ struct Aircraft {
|
|||||||
m_trueAirspeedValid(false),
|
m_trueAirspeedValid(false),
|
||||||
m_indicatedAirspeedValid(false),
|
m_indicatedAirspeedValid(false),
|
||||||
m_machValid(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_bdsCapabilitiesValid(false),
|
||||||
|
m_adsbVersionValid(false),
|
||||||
|
m_nicSupplementAValid(false),
|
||||||
|
m_nicSupplementBValid(false),
|
||||||
|
m_nicSupplementCValid(false),
|
||||||
m_adsbFrameCount(0),
|
m_adsbFrameCount(0),
|
||||||
|
m_modesFrameCount(0),
|
||||||
|
m_nonTransponderFrameCount(0),
|
||||||
m_tisBFrameCount(0),
|
m_tisBFrameCount(0),
|
||||||
|
m_adsrFrameCount(0),
|
||||||
m_minCorrelation(INFINITY),
|
m_minCorrelation(INFINITY),
|
||||||
m_maxCorrelation(-INFINITY),
|
m_maxCorrelation(-INFINITY),
|
||||||
m_correlation(0.0f),
|
m_correlation(0.0f),
|
||||||
@ -302,9 +403,12 @@ struct Aircraft {
|
|||||||
m_callsignItem = new QTableWidgetItem();
|
m_callsignItem = new QTableWidgetItem();
|
||||||
m_atcCallsignItem = new QTableWidgetItem();
|
m_atcCallsignItem = new QTableWidgetItem();
|
||||||
m_modelItem = new QTableWidgetItem();
|
m_modelItem = new QTableWidgetItem();
|
||||||
|
m_typeItem = new QTableWidgetItem();
|
||||||
|
m_sideviewItem = new QTableWidgetItem();
|
||||||
m_airlineItem = new QTableWidgetItem();
|
m_airlineItem = new QTableWidgetItem();
|
||||||
m_altitudeItem = new QTableWidgetItem();
|
m_altitudeItem = new QTableWidgetItem();
|
||||||
m_headingItem = new QTableWidgetItem();
|
m_headingItem = new QTableWidgetItem();
|
||||||
|
m_trackItem = new QTableWidgetItem();
|
||||||
m_verticalRateItem = new QTableWidgetItem();
|
m_verticalRateItem = new QTableWidgetItem();
|
||||||
m_rangeItem = new CustomDoubleTableWidgetItem();
|
m_rangeItem = new CustomDoubleTableWidgetItem();
|
||||||
m_azElItem = new QTableWidgetItem();
|
m_azElItem = new QTableWidgetItem();
|
||||||
@ -313,19 +417,34 @@ struct Aircraft {
|
|||||||
m_emitterCategoryItem = new QTableWidgetItem();
|
m_emitterCategoryItem = new QTableWidgetItem();
|
||||||
m_statusItem = new QTableWidgetItem();
|
m_statusItem = new QTableWidgetItem();
|
||||||
m_squawkItem = new QTableWidgetItem();
|
m_squawkItem = new QTableWidgetItem();
|
||||||
|
m_identItem = new QTableWidgetItem();
|
||||||
m_registrationItem = new QTableWidgetItem();
|
m_registrationItem = new QTableWidgetItem();
|
||||||
m_countryItem = new QTableWidgetItem();
|
m_countryItem = new QTableWidgetItem();
|
||||||
m_registeredItem = new QTableWidgetItem();
|
m_registeredItem = new QTableWidgetItem();
|
||||||
m_manufacturerNameItem = new QTableWidgetItem();
|
m_manufacturerNameItem = new QTableWidgetItem();
|
||||||
m_ownerItem = new QTableWidgetItem();
|
m_ownerItem = new QTableWidgetItem();
|
||||||
m_operatorICAOItem = new QTableWidgetItem();
|
m_operatorICAOItem = new QTableWidgetItem();
|
||||||
|
m_interogatorCodeItem = new QTableWidgetItem();
|
||||||
m_timeItem = new QTableWidgetItem();
|
m_timeItem = new QTableWidgetItem();
|
||||||
|
m_totalFrameCountItem = new QTableWidgetItem();
|
||||||
m_adsbFrameCountItem = 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_correlationItem = new QTableWidgetItem();
|
||||||
m_rssiItem = new QTableWidgetItem();
|
m_rssiItem = new QTableWidgetItem();
|
||||||
m_flightStatusItem = new QTableWidgetItem();
|
m_flightStatusItem = new QTableWidgetItem();
|
||||||
m_depItem = new QTableWidgetItem();
|
m_depItem = new QTableWidgetItem();
|
||||||
m_arrItem = new QTableWidgetItem();
|
m_arrItem = new QTableWidgetItem();
|
||||||
|
m_stopsItem = new QTableWidgetItem();
|
||||||
m_stdItem = new QTableWidgetItem();
|
m_stdItem = new QTableWidgetItem();
|
||||||
m_etdItem = new QTableWidgetItem();
|
m_etdItem = new QTableWidgetItem();
|
||||||
m_atdItem = new QTableWidgetItem();
|
m_atdItem = new QTableWidgetItem();
|
||||||
@ -338,6 +457,13 @@ struct Aircraft {
|
|||||||
m_apItem = new QTableWidgetItem();
|
m_apItem = new QTableWidgetItem();
|
||||||
m_vModeItem = new QTableWidgetItem();
|
m_vModeItem = new QTableWidgetItem();
|
||||||
m_lModeItem = 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_rollItem = new QTableWidgetItem();
|
||||||
m_groundspeedItem = new QTableWidgetItem();
|
m_groundspeedItem = new QTableWidgetItem();
|
||||||
m_turnRateItem = new QTableWidgetItem();
|
m_turnRateItem = new QTableWidgetItem();
|
||||||
@ -351,16 +477,15 @@ struct Aircraft {
|
|||||||
m_staticPressureItem = new QTableWidgetItem();
|
m_staticPressureItem = new QTableWidgetItem();
|
||||||
m_staticAirTempItem = new QTableWidgetItem();
|
m_staticAirTempItem = new QTableWidgetItem();
|
||||||
m_humidityItem = new QTableWidgetItem();
|
m_humidityItem = new QTableWidgetItem();
|
||||||
m_tisBItem = new QTableWidgetItem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getImage() const;
|
QString getImage() const;
|
||||||
QString getText(const ADSBDemodSettings *settings, bool all=false) const;
|
QString getText(const ADSBDemodSettings *settings, bool all=false) const;
|
||||||
// Label to use for aircraft on map
|
// 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)
|
// 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())
|
if (!m_callsign.isEmpty())
|
||||||
return QString("Callsign: %1").arg(m_callsign);
|
return QString("Callsign: %1").arg(m_callsign);
|
||||||
@ -368,6 +493,71 @@ struct Aircraft {
|
|||||||
return QString("ICAO: %1").arg(m_icao, 0, 16);
|
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
|
// Aircraft data model used by QML map item
|
||||||
@ -385,12 +575,16 @@ public:
|
|||||||
aircraftPathRole = Qt::UserRole + 6,
|
aircraftPathRole = Qt::UserRole + 6,
|
||||||
showAllRole = Qt::UserRole + 7,
|
showAllRole = Qt::UserRole + 7,
|
||||||
highlightedRole = Qt::UserRole + 8,
|
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) {
|
Q_INVOKABLE void addAircraft(Aircraft *aircraft) {
|
||||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||||
m_aircrafts.append(aircraft);
|
m_aircrafts.append(aircraft);
|
||||||
|
m_pathModels.append(new AircraftPathModel(this, aircraft));
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,7 +603,8 @@ public:
|
|||||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
|
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void aircraftUpdated(Aircraft *aircraft) {
|
void aircraftUpdated(Aircraft *aircraft)
|
||||||
|
{
|
||||||
int row = m_aircrafts.indexOf(aircraft);
|
int row = m_aircrafts.indexOf(aircraft);
|
||||||
if (row >= 0)
|
if (row >= 0)
|
||||||
{
|
{
|
||||||
@ -418,30 +613,72 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void allAircraftUpdated() {
|
void highlightChanged(Aircraft *aircraft)
|
||||||
/*
|
{
|
||||||
// Not sure why this doesn't work - it should be more efficient
|
int row = m_aircrafts.indexOf(aircraft);
|
||||||
// than the following code
|
if (row >= 0)
|
||||||
emit dataChanged(index(0), index(rowCount()));
|
|
||||||
*/
|
|
||||||
for (int i = 0; i < m_aircrafts.count(); i++)
|
|
||||||
{
|
{
|
||||||
QModelIndex idx = index(i);
|
m_pathModels[row]->settingsUpdated();
|
||||||
|
QModelIndex idx = index(row);
|
||||||
emit dataChanged(idx, idx);
|
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);
|
int row = m_aircrafts.indexOf(aircraft);
|
||||||
if (row >= 0)
|
if (row >= 0)
|
||||||
{
|
{
|
||||||
beginRemoveRows(QModelIndex(), row, row);
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
m_aircrafts.removeAt(row);
|
m_aircrafts.removeAt(row);
|
||||||
|
delete m_pathModels.takeAt(row);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const {
|
QHash<int, QByteArray> roleNames() const
|
||||||
|
{
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
roles[positionRole] = "position";
|
roles[positionRole] = "position";
|
||||||
roles[headingRole] = "heading";
|
roles[headingRole] = "heading";
|
||||||
@ -452,22 +689,12 @@ public:
|
|||||||
roles[showAllRole] = "showAll";
|
roles[showAllRole] = "showAll";
|
||||||
roles[highlightedRole] = "highlighted";
|
roles[highlightedRole] = "highlighted";
|
||||||
roles[targetRole] = "target";
|
roles[targetRole] = "target";
|
||||||
|
roles[radiusRole] = "containmentRadius";
|
||||||
|
roles[aircraftPathModelRole] = "aircraftPathModel";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setFlightPaths(bool flightPaths)
|
void setSettings(const ADSBDemodSettings *settings)
|
||||||
{
|
|
||||||
m_flightPaths = flightPaths;
|
|
||||||
allAircraftUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAllFlightPaths(bool allFlightPaths)
|
|
||||||
{
|
|
||||||
m_allFlightPaths = allFlightPaths;
|
|
||||||
allAircraftUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSettings(const ADSBDemodSettings *settings)
|
|
||||||
{
|
{
|
||||||
m_settings = settings;
|
m_settings = settings;
|
||||||
allAircraftUpdated();
|
allAircraftUpdated();
|
||||||
@ -487,11 +714,11 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ADSBDemodSettings *m_settings;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<Aircraft *> m_aircrafts;
|
QList<Aircraft *> m_aircrafts;
|
||||||
bool m_flightPaths;
|
QList<AircraftPathModel *> m_pathModels;
|
||||||
bool m_allFlightPaths;
|
|
||||||
const ADSBDemodSettings *m_settings;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Airport data model used by QML map item
|
// Airport data model used by QML map item
|
||||||
@ -667,26 +894,34 @@ public:
|
|||||||
airspacePolygonRole = Qt::UserRole + 6
|
airspacePolygonRole = Qt::UserRole + 6
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_INVOKABLE void addAirspace(Airspace *airspace) {
|
Q_INVOKABLE void addAirspace(Airspace *airspace)
|
||||||
|
{
|
||||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||||
m_airspaces.append(airspace);
|
m_airspaces.append(airspace);
|
||||||
// Convert QPointF to QVariantList of QGeoCoordinates
|
updatePolygon(airspace, -1);
|
||||||
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);
|
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override
|
||||||
|
{
|
||||||
Q_UNUSED(parent)
|
Q_UNUSED(parent)
|
||||||
return m_airspaces.count();
|
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)
|
if (m_airspaces.count() > 0)
|
||||||
{
|
{
|
||||||
beginRemoveRows(QModelIndex(), 0, m_airspaces.count() - 1);
|
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;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
bool setData(const QModelIndex &index, const QVariant& value, int role = Qt::EditRole) 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;
|
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const {
|
QHash<int, QByteArray> roleNames() const
|
||||||
|
{
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
roles[nameRole] = "name";
|
roles[nameRole] = "name";
|
||||||
roles[detailsRole] = "details";
|
roles[detailsRole] = "details";
|
||||||
@ -720,6 +973,23 @@ public:
|
|||||||
private:
|
private:
|
||||||
QList<Airspace *> m_airspaces;
|
QList<Airspace *> m_airspaces;
|
||||||
QList<QVariantList> m_polygons;
|
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
|
// NavAid model used for each NavAid on the map
|
||||||
@ -867,6 +1137,23 @@ protected:
|
|||||||
class ADSBDemodGUI : public ChannelGUI {
|
class ADSBDemodGUI : public ChannelGUI {
|
||||||
Q_OBJECT
|
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:
|
public:
|
||||||
static ADSBDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
|
static ADSBDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
|
||||||
virtual void destroy();
|
virtual void destroy();
|
||||||
@ -923,6 +1210,7 @@ private:
|
|||||||
|
|
||||||
QHash<int, Aircraft *> m_aircraft; // Hashed on ICAO
|
QHash<int, Aircraft *> m_aircraft; // Hashed on ICAO
|
||||||
QSharedPointer<const QHash<int, AircraftInformation *>> m_aircraftInfo;
|
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
|
QSharedPointer<const QHash<int, AirportInformation *>> m_airportInfo; // Hashed on id
|
||||||
AircraftModel m_aircraftModel;
|
AircraftModel m_aircraftModel;
|
||||||
AirportModel m_airportModel;
|
AirportModel m_airportModel;
|
||||||
@ -936,12 +1224,85 @@ private:
|
|||||||
Aircraft *m_trackAircraft; // Aircraft we want to track in Channel Report
|
Aircraft *m_trackAircraft; // Aircraft we want to track in Channel Report
|
||||||
MovingAverageUtil<float, double, 10> m_correlationAvg;
|
MovingAverageUtil<float, double, 10> m_correlationAvg;
|
||||||
MovingAverageUtil<float, double, 10> m_correlationOnesAvg;
|
MovingAverageUtil<float, double, 10> m_correlationOnesAvg;
|
||||||
|
MovingAverageUtil<float, double, 100> m_qnhAvg;
|
||||||
|
|
||||||
Aircraft *m_highlightAircraft; // Aircraft we want to highlight, when selected in table
|
Aircraft *m_highlightAircraft; // Aircraft we want to highlight, when selected in table
|
||||||
|
|
||||||
float m_currentAirportRange; // Current settings, so we only update if changed
|
float m_currentAirportRange; // Current settings, so we only update if changed
|
||||||
ADSBDemodSettings::AirportType m_currentAirportMinimumSize;
|
ADSBDemodSettings::AirportType m_currentAirportMinimumSize;
|
||||||
bool m_currentDisplayHeliports;
|
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_RESERVED,
|
||||||
|
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
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
||||||
QTextToSpeech *m_speech;
|
QTextToSpeech *m_speech;
|
||||||
#endif
|
#endif
|
||||||
@ -977,6 +1338,7 @@ private:
|
|||||||
static const QString m_flightStatuses[];
|
static const QString m_flightStatuses[];
|
||||||
static const QString m_hazardSeverity[];
|
static const QString m_hazardSeverity[];
|
||||||
static const QString m_fomSources[];
|
static const QString m_fomSources[];
|
||||||
|
static const QString m_nacvStrings[];
|
||||||
|
|
||||||
explicit ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
explicit ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
||||||
virtual ~ADSBDemodGUI();
|
virtual ~ADSBDemodGUI();
|
||||||
@ -990,8 +1352,6 @@ private:
|
|||||||
void makeUIConnections();
|
void makeUIConnections();
|
||||||
void updateAbsoluteCenterFrequency();
|
void updateAbsoluteCenterFrequency();
|
||||||
|
|
||||||
void updatePosition(Aircraft *aircraft);
|
|
||||||
bool updateLocalPosition(Aircraft *aircraft, double latitude, double longitude, bool surfacePosition);
|
|
||||||
void clearFromMap(const QString& name);
|
void clearFromMap(const QString& name);
|
||||||
void sendToMap(Aircraft *aircraft, QList<SWGSDRangel::SWGMapAnimation *> *animations);
|
void sendToMap(Aircraft *aircraft, QList<SWGSDRangel::SWGMapAnimation *> *animations);
|
||||||
Aircraft *getAircraft(int icao, bool &newAircraft);
|
Aircraft *getAircraft(int icao, bool &newAircraft);
|
||||||
@ -1006,10 +1366,24 @@ private:
|
|||||||
float correlationOnes,
|
float correlationOnes,
|
||||||
unsigned crc,
|
unsigned crc,
|
||||||
bool updateModel);
|
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 QDateTime& dateTime, 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);
|
void decodeCommB(const QByteArray data, const QDateTime dateTime, int df, Aircraft *aircraft, bool &updatedCallsign);
|
||||||
QList<SWGSDRangel::SWGMapAnimation *> *animate(QDateTime dateTime, Aircraft *aircraft);
|
QList<SWGSDRangel::SWGMapAnimation *> *animate(QDateTime dateTime, Aircraft *aircraft);
|
||||||
SWGSDRangel::SWGMapAnimation *gearAnimation(QDateTime startDateTime, bool up);
|
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 *flapsAnimation(QDateTime startDateTime, float currentFlaps, float flaps);
|
||||||
SWGSDRangel::SWGMapAnimation *slatsAnimation(QDateTime startDateTime, bool retract);
|
SWGSDRangel::SWGMapAnimation *slatsAnimation(QDateTime startDateTime, bool retract);
|
||||||
SWGSDRangel::SWGMapAnimation *rotorAnimation(QDateTime startDateTime, bool stop);
|
SWGSDRangel::SWGMapAnimation *rotorAnimation(QDateTime startDateTime, bool stop);
|
||||||
@ -1022,18 +1396,18 @@ private:
|
|||||||
QString subAircraftString(Aircraft *aircraft, const QString &string);
|
QString subAircraftString(Aircraft *aircraft, const QString &string);
|
||||||
void resizeTable();
|
void resizeTable();
|
||||||
QString getDataDir();
|
QString getDataDir();
|
||||||
void readAirportDB(const QString& filename);
|
|
||||||
void readAirportFrequenciesDB(const QString& filename);
|
|
||||||
void update3DModels();
|
void update3DModels();
|
||||||
void updateAirports();
|
void updateAirports();
|
||||||
void updateAirspaces();
|
void updateAirspaces();
|
||||||
void updateNavAids();
|
void updateNavAids();
|
||||||
void updateChannelList();
|
void updateChannelList();
|
||||||
|
void removeAircraft(QHash<int, Aircraft *>::iterator& i, Aircraft *aircraft);
|
||||||
QAction *createCheckableItem(QString& text, int idx, bool checked);
|
QAction *createCheckableItem(QString& text, int idx, bool checked);
|
||||||
Aircraft* findAircraftByFlight(const QString& flight);
|
Aircraft* findAircraftByFlight(const QString& flight);
|
||||||
QString dataTimeToShortString(QDateTime dt);
|
QString dataTimeToShortString(QDateTime dt);
|
||||||
void initFlightInformation();
|
void initFlightInformation();
|
||||||
void initAviationWeather();
|
void initAviationWeather();
|
||||||
|
void setShowContainmentRadius(bool show);
|
||||||
void applyMapSettings();
|
void applyMapSettings();
|
||||||
void updatePhotoText(Aircraft *aircraft);
|
void updatePhotoText(Aircraft *aircraft);
|
||||||
void updatePhotoFlightInformation(Aircraft *aircraft);
|
void updatePhotoFlightInformation(Aircraft *aircraft);
|
||||||
@ -1045,16 +1419,33 @@ private:
|
|||||||
void applyImportSettings();
|
void applyImportSettings();
|
||||||
void sendAircraftReport();
|
void sendAircraftReport();
|
||||||
void updatePosition(float latitude, float longitude, float altitude);
|
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 leaveEvent(QEvent*);
|
||||||
void enterEvent(EnterEventType*);
|
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:
|
private slots:
|
||||||
void on_deltaFrequency_changed(qint64 value);
|
void on_deltaFrequency_changed(qint64 value);
|
||||||
void on_rfBW_valueChanged(int value);
|
void on_rfBW_valueChanged(int value);
|
||||||
void on_threshold_valueChanged(int value);
|
void on_threshold_valueChanged(int value);
|
||||||
|
void on_chipsThreshold_valueChanged(int value);
|
||||||
void on_phaseSteps_valueChanged(int value);
|
void on_phaseSteps_valueChanged(int value);
|
||||||
void on_tapsPerPhase_valueChanged(int value);
|
void on_tapsPerPhase_valueChanged(int value);
|
||||||
|
void statsTable_customContextMenuRequested(QPoint pos);
|
||||||
void adsbData_customContextMenuRequested(QPoint point);
|
void adsbData_customContextMenuRequested(QPoint point);
|
||||||
void on_adsbData_cellClicked(int row, int column);
|
void on_adsbData_cellClicked(int row, int column);
|
||||||
void on_adsbData_cellDoubleClicked(int row, int column);
|
void on_adsbData_cellDoubleClicked(int row, int column);
|
||||||
@ -1063,18 +1454,24 @@ private slots:
|
|||||||
void columnSelectMenu(QPoint pos);
|
void columnSelectMenu(QPoint pos);
|
||||||
void columnSelectMenuChecked(bool checked = false);
|
void columnSelectMenuChecked(bool checked = false);
|
||||||
void on_spb_currentIndexChanged(int value);
|
void on_spb_currentIndexChanged(int value);
|
||||||
void on_correlateFullPreamble_clicked(bool checked);
|
|
||||||
void on_demodModeS_clicked(bool checked);
|
void on_demodModeS_clicked(bool checked);
|
||||||
void on_feed_clicked(bool checked);
|
void on_feed_clicked(bool checked);
|
||||||
void on_notifications_clicked();
|
void on_notifications_clicked();
|
||||||
void on_flightInfo_clicked();
|
void on_flightInfo_clicked();
|
||||||
void on_findOnMapFeature_clicked();
|
void on_findOnMapFeature_clicked();
|
||||||
void on_getOSNDB_clicked();
|
void on_deleteAircraft_clicked();
|
||||||
|
void on_getAircraftDB_clicked();
|
||||||
void on_getAirportDB_clicked();
|
void on_getAirportDB_clicked();
|
||||||
void on_getAirspacesDB_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_flightPaths_clicked(bool checked);
|
||||||
void on_allFlightPaths_clicked(bool checked);
|
void on_allFlightPaths_clicked(bool checked);
|
||||||
void on_atcLabels_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 onWidgetRolled(QWidget* widget, bool rollDown);
|
||||||
void onMenuDialogCalled(const QPoint& p);
|
void onMenuDialogCalled(const QPoint& p);
|
||||||
void handleInputMessages();
|
void handleInputMessages();
|
||||||
@ -1101,6 +1498,12 @@ private slots:
|
|||||||
void devicePositionChanged(float latitude, float longitude, float altitude);
|
void devicePositionChanged(float latitude, float longitude, float altitude);
|
||||||
void requestMetar(const QString& icao);
|
void requestMetar(const QString& icao);
|
||||||
void weatherUpdated(const AviationWeather::METAR &metar);
|
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:
|
signals:
|
||||||
void homePositionChanged();
|
void homePositionChanged();
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
// Map main ADS-B table column numbers to combo box indices
|
// Map main ADS-B table column numbers to combo box indices
|
||||||
std::vector<int> ADSBDemodNotificationDialog::m_columnMap = {
|
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_ALTITUDE, ADSB_COL_GROUND_SPEED, ADSB_COL_RANGE,
|
||||||
ADSB_COL_CATEGORY, ADSB_COL_STATUS, ADSB_COL_SQUAWK,
|
ADSB_COL_CATEGORY, ADSB_COL_STATUS, ADSB_COL_SQUAWK,
|
||||||
ADSB_COL_REGISTRATION, ADSB_COL_MANUFACTURER, ADSB_COL_OWNER, ADSB_COL_OPERATOR_ICAO
|
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("ICAO ID");
|
||||||
match->addItem("Callsign");
|
match->addItem("Callsign");
|
||||||
match->addItem("Aircraft");
|
match->addItem("Aircraft");
|
||||||
|
match->addItem("Type");
|
||||||
match->addItem("Alt (ft)");
|
match->addItem("Alt (ft)");
|
||||||
match->addItem("GS (kn)");
|
match->addItem("GS (kn)");
|
||||||
match->addItem("D (km)");
|
match->addItem("D (km)");
|
||||||
|
@ -36,7 +36,8 @@ void ADSBDemodSettings::resetToDefaults()
|
|||||||
{
|
{
|
||||||
m_inputFrequencyOffset = 0;
|
m_inputFrequencyOffset = 0;
|
||||||
m_rfBandwidth = 2*1300000;
|
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_samplesPerBit = 4;
|
||||||
m_removeTimeout = 60;
|
m_removeTimeout = 60;
|
||||||
m_feedEnabled = false;
|
m_feedEnabled = false;
|
||||||
@ -73,7 +74,6 @@ void ADSBDemodSettings::resetToDefaults()
|
|||||||
m_tableFontName = "Liberation Sans";
|
m_tableFontName = "Liberation Sans";
|
||||||
m_tableFontSize = 9;
|
m_tableFontSize = 9;
|
||||||
m_displayDemodStats = false;
|
m_displayDemodStats = false;
|
||||||
m_correlateFullPreamble = true;
|
|
||||||
m_demodModeS = true;
|
m_demodModeS = true;
|
||||||
m_autoResizeTableColumns = false;
|
m_autoResizeTableColumns = false;
|
||||||
m_interpolatorPhaseSteps = 4; // Higher than these two values will struggle to run in real-time
|
m_interpolatorPhaseSteps = 4; // Higher than these two values will struggle to run in real-time
|
||||||
@ -98,13 +98,24 @@ void ADSBDemodSettings::resetToDefaults()
|
|||||||
m_displayNavAids = true;
|
m_displayNavAids = true;
|
||||||
m_displayPhotos = true;
|
m_displayPhotos = true;
|
||||||
m_verboseModelMatching = false;
|
m_verboseModelMatching = false;
|
||||||
m_airfieldElevation = 0;
|
|
||||||
m_aircraftMinZoom = 11;
|
m_aircraftMinZoom = 11;
|
||||||
m_workspaceIndex = 0;
|
m_workspaceIndex = 0;
|
||||||
m_hidden = false;
|
m_hidden = false;
|
||||||
m_atcLabels = true;
|
m_atcLabels = true;
|
||||||
m_atcCallsigns = true;
|
m_atcCallsigns = true;
|
||||||
m_transitionAlt = 6000; // Depends on airport. 18,000 in USA
|
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
|
QByteArray ADSBDemodSettings::serialize() const
|
||||||
@ -141,7 +152,6 @@ QByteArray ADSBDemodSettings::serialize() const
|
|||||||
s.writeString(25, m_tableFontName);
|
s.writeString(25, m_tableFontName);
|
||||||
s.writeS32(26, m_tableFontSize);
|
s.writeS32(26, m_tableFontSize);
|
||||||
s.writeBool(27, m_displayDemodStats);
|
s.writeBool(27, m_displayDemodStats);
|
||||||
s.writeBool(28, m_correlateFullPreamble);
|
|
||||||
s.writeBool(29, m_demodModeS);
|
s.writeBool(29, m_demodModeS);
|
||||||
s.writeBool(30, m_autoResizeTableColumns);
|
s.writeBool(30, m_autoResizeTableColumns);
|
||||||
s.writeS32(31, m_interpolatorPhaseSteps);
|
s.writeS32(31, m_interpolatorPhaseSteps);
|
||||||
@ -165,7 +175,6 @@ QByteArray ADSBDemodSettings::serialize() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.writeBool(44, m_verboseModelMatching);
|
s.writeBool(44, m_verboseModelMatching);
|
||||||
s.writeS32(45, m_airfieldElevation);
|
|
||||||
|
|
||||||
s.writeBool(46, m_exportClientEnabled);
|
s.writeBool(46, m_exportClientEnabled);
|
||||||
s.writeBool(47, m_exportServerEnabled);
|
s.writeBool(47, m_exportServerEnabled);
|
||||||
@ -192,6 +201,17 @@ QByteArray ADSBDemodSettings::serialize() const
|
|||||||
s.writeS32(67, m_transitionAlt);
|
s.writeS32(67, m_transitionAlt);
|
||||||
s.writeString(68, m_amDemod);
|
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);
|
||||||
|
|
||||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) {
|
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) {
|
||||||
s.writeS32(100 + i, m_columnIndexes[i]);
|
s.writeS32(100 + i, m_columnIndexes[i]);
|
||||||
}
|
}
|
||||||
@ -229,7 +249,7 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
|||||||
d.readS32(1, &tmp, 0);
|
d.readS32(1, &tmp, 0);
|
||||||
m_inputFrequencyOffset = tmp;
|
m_inputFrequencyOffset = tmp;
|
||||||
d.readReal(2, &m_rfBandwidth, 2*1300000);
|
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(4, &m_samplesPerBit, 4);
|
||||||
d.readS32(5, &m_removeTimeout, 60);
|
d.readS32(5, &m_removeTimeout, 60);
|
||||||
d.readBool(6, &m_feedEnabled, false);
|
d.readBool(6, &m_feedEnabled, false);
|
||||||
@ -268,7 +288,6 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
|||||||
d.readString(25, &m_tableFontName, "Liberation Sans");
|
d.readString(25, &m_tableFontName, "Liberation Sans");
|
||||||
d.readS32(26, &m_tableFontSize, 9);
|
d.readS32(26, &m_tableFontSize, 9);
|
||||||
d.readBool(27, &m_displayDemodStats, false);
|
d.readBool(27, &m_displayDemodStats, false);
|
||||||
d.readBool(28, &m_correlateFullPreamble, true);
|
|
||||||
d.readBool(29, &m_demodModeS, true);
|
d.readBool(29, &m_demodModeS, true);
|
||||||
d.readBool(30, &m_autoResizeTableColumns, false);
|
d.readBool(30, &m_autoResizeTableColumns, false);
|
||||||
d.readS32(31, &m_interpolatorPhaseSteps, 4);
|
d.readS32(31, &m_interpolatorPhaseSteps, 4);
|
||||||
@ -296,7 +315,6 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
d.readBool(44, &m_verboseModelMatching, false);
|
d.readBool(44, &m_verboseModelMatching, false);
|
||||||
d.readS32(45, &m_airfieldElevation, 0);
|
|
||||||
|
|
||||||
d.readBool(46, &m_exportClientEnabled, true);
|
d.readBool(46, &m_exportClientEnabled, true);
|
||||||
d.readBool(47, &m_exportServerEnabled, true);
|
d.readBool(47, &m_exportServerEnabled, true);
|
||||||
@ -328,6 +346,19 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
|||||||
d.readS32(67, &m_transitionAlt, 6000);
|
d.readS32(67, &m_transitionAlt, 6000);
|
||||||
d.readString(68, &m_amDemod);
|
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);
|
||||||
|
|
||||||
|
applyPalette();
|
||||||
|
|
||||||
#ifdef LINUX
|
#ifdef LINUX
|
||||||
if (m_mapProvider == "osm") {
|
if (m_mapProvider == "osm") {
|
||||||
m_mapProvider = "mapboxgl";
|
m_mapProvider = "mapboxgl";
|
||||||
@ -413,6 +444,9 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
|
|||||||
if (settingsKeys.contains("correlationThreshold")) {
|
if (settingsKeys.contains("correlationThreshold")) {
|
||||||
m_correlationThreshold = settings.m_correlationThreshold;
|
m_correlationThreshold = settings.m_correlationThreshold;
|
||||||
}
|
}
|
||||||
|
if (settingsKeys.contains("chipsThreshold")) {
|
||||||
|
m_chipsThreshold = settings.m_chipsThreshold;
|
||||||
|
}
|
||||||
if (settingsKeys.contains("samplesPerBit")) {
|
if (settingsKeys.contains("samplesPerBit")) {
|
||||||
m_samplesPerBit = settings.m_samplesPerBit;
|
m_samplesPerBit = settings.m_samplesPerBit;
|
||||||
}
|
}
|
||||||
@ -521,9 +555,6 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
|
|||||||
if (settingsKeys.contains("displayDemodStats")) {
|
if (settingsKeys.contains("displayDemodStats")) {
|
||||||
m_displayDemodStats = settings.m_displayDemodStats;
|
m_displayDemodStats = settings.m_displayDemodStats;
|
||||||
}
|
}
|
||||||
if (settingsKeys.contains("correlateFullPreamble")) {
|
|
||||||
m_correlateFullPreamble = settings.m_correlateFullPreamble;
|
|
||||||
}
|
|
||||||
if (settingsKeys.contains("demodModeS")) {
|
if (settingsKeys.contains("demodModeS")) {
|
||||||
m_demodModeS = settings.m_demodModeS;
|
m_demodModeS = settings.m_demodModeS;
|
||||||
}
|
}
|
||||||
@ -575,9 +606,6 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
|
|||||||
if (settingsKeys.contains("verboseModelMatching")) {
|
if (settingsKeys.contains("verboseModelMatching")) {
|
||||||
m_verboseModelMatching = settings.m_verboseModelMatching;
|
m_verboseModelMatching = settings.m_verboseModelMatching;
|
||||||
}
|
}
|
||||||
if (settingsKeys.contains("airfieldElevation")) {
|
|
||||||
m_airfieldElevation = settings.m_airfieldElevation;
|
|
||||||
}
|
|
||||||
if (settingsKeys.contains("aircraftMinZoom")) {
|
if (settingsKeys.contains("aircraftMinZoom")) {
|
||||||
m_aircraftMinZoom = settings.m_aircraftMinZoom;
|
m_aircraftMinZoom = settings.m_aircraftMinZoom;
|
||||||
}
|
}
|
||||||
@ -590,6 +618,32 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
|
|||||||
if (settingsKeys.contains("transitionAlt")) {
|
if (settingsKeys.contains("transitionAlt")) {
|
||||||
m_transitionAlt = settings.m_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
|
QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool force) const
|
||||||
@ -605,6 +659,9 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
|
|||||||
if (settingsKeys.contains("correlationThreshold") || force) {
|
if (settingsKeys.contains("correlationThreshold") || force) {
|
||||||
ostr << " m_correlationThreshold: " << m_correlationThreshold;
|
ostr << " m_correlationThreshold: " << m_correlationThreshold;
|
||||||
}
|
}
|
||||||
|
if (settingsKeys.contains("chipsThreshold") || force) {
|
||||||
|
ostr << " m_chipsThreshold: " << m_chipsThreshold;
|
||||||
|
}
|
||||||
if (settingsKeys.contains("samplesPerBit") || force) {
|
if (settingsKeys.contains("samplesPerBit") || force) {
|
||||||
ostr << " m_samplesPerBit: " << m_samplesPerBit;
|
ostr << " m_samplesPerBit: " << m_samplesPerBit;
|
||||||
}
|
}
|
||||||
@ -704,9 +761,6 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
|
|||||||
if (settingsKeys.contains("displayDemodStats") || force) {
|
if (settingsKeys.contains("displayDemodStats") || force) {
|
||||||
ostr << " m_displayDemodStats: " << m_displayDemodStats;
|
ostr << " m_displayDemodStats: " << m_displayDemodStats;
|
||||||
}
|
}
|
||||||
if (settingsKeys.contains("correlateFullPreamble") || force) {
|
|
||||||
ostr << " m_correlateFullPreamble: " << m_correlateFullPreamble;
|
|
||||||
}
|
|
||||||
if (settingsKeys.contains("demodModeS") || force) {
|
if (settingsKeys.contains("demodModeS") || force) {
|
||||||
ostr << " m_demodModeS: " << m_demodModeS;
|
ostr << " m_demodModeS: " << m_demodModeS;
|
||||||
}
|
}
|
||||||
@ -755,9 +809,6 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
|
|||||||
if (settingsKeys.contains("verboseModelMatching") || force) {
|
if (settingsKeys.contains("verboseModelMatching") || force) {
|
||||||
ostr << " m_verboseModelMatching: " << m_verboseModelMatching;
|
ostr << " m_verboseModelMatching: " << m_verboseModelMatching;
|
||||||
}
|
}
|
||||||
if (settingsKeys.contains("airfieldElevation") || force) {
|
|
||||||
ostr << " m_airfieldElevation: " << m_airfieldElevation;
|
|
||||||
}
|
|
||||||
if (settingsKeys.contains("aircraftMinZoom") || force) {
|
if (settingsKeys.contains("aircraftMinZoom") || force) {
|
||||||
ostr << " m_aircraftMinZoom: " << m_aircraftMinZoom;
|
ostr << " m_aircraftMinZoom: " << m_aircraftMinZoom;
|
||||||
}
|
}
|
||||||
@ -770,6 +821,114 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
|
|||||||
if (settingsKeys.contains("transitionAlt") || force) {
|
if (settingsKeys.contains("transitionAlt") || force) {
|
||||||
ostr << " m_transitionAlt: " << m_transitionAlt;
|
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());
|
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::fromString("#ff0000")),
|
||||||
|
QVariant(QColor::fromString("#ff7f00")),
|
||||||
|
QVariant(QColor::fromString("#ffff00")),
|
||||||
|
QVariant(QColor::fromString("#f7ff00")),
|
||||||
|
QVariant(QColor::fromString("#00ff00")),
|
||||||
|
QVariant(QColor::fromString("#00ff7f")),
|
||||||
|
QVariant(QColor::fromString("#00ffff")),
|
||||||
|
QVariant(QColor::fromString("#007fff")),
|
||||||
|
};
|
||||||
|
|
||||||
|
const QVariant ADSBDemodSettings::m_pastelPalette[8] = {
|
||||||
|
QVariant(QColor::fromString("#ffadad")),
|
||||||
|
QVariant(QColor::fromString("#ffd6a5")),
|
||||||
|
QVariant(QColor::fromString("#fdffb6")),
|
||||||
|
QVariant(QColor::fromString("#caffbf")),
|
||||||
|
QVariant(QColor::fromString("#9bf6ff")),
|
||||||
|
QVariant(QColor::fromString("#a0c4ff")),
|
||||||
|
QVariant(QColor::fromString("#bdb2ff")),
|
||||||
|
QVariant(QColor::fromString("#ffc6ff")),
|
||||||
|
};
|
||||||
|
|
||||||
|
const QVariant ADSBDemodSettings::m_spectralPalette[8] = {
|
||||||
|
QVariant(QColor::fromString("#d53e4f")),
|
||||||
|
QVariant(QColor::fromString("#f46d43")),
|
||||||
|
QVariant(QColor::fromString("#fdae61")),
|
||||||
|
QVariant(QColor::fromString("#fee08b")),
|
||||||
|
QVariant(QColor::fromString("#e6f598")),
|
||||||
|
QVariant(QColor::fromString("#abdda4")),
|
||||||
|
QVariant(QColor::fromString("#66c2a5")),
|
||||||
|
QVariant(QColor::fromString("#3288bd")),
|
||||||
|
};
|
||||||
|
|
||||||
|
const QVariant ADSBDemodSettings::m_bluePalette[8] = {
|
||||||
|
QVariant(QColor::fromString("#deebf7")),
|
||||||
|
QVariant(QColor::fromString("#c6dbef")),
|
||||||
|
QVariant(QColor::fromString("#9ecae1")),
|
||||||
|
QVariant(QColor::fromString("#6baed6")),
|
||||||
|
QVariant(QColor::fromString("#4292c6")),
|
||||||
|
QVariant(QColor::fromString("#2171b5")),
|
||||||
|
QVariant(QColor::fromString("#08519c")),
|
||||||
|
QVariant(QColor::fromString("#08306b")),
|
||||||
|
};
|
||||||
|
|
||||||
|
const QVariant ADSBDemodSettings::m_purplePalette[8] = {
|
||||||
|
QVariant(QColor::fromString("#ccaff2")),
|
||||||
|
QVariant(QColor::fromString("#b699e0")),
|
||||||
|
QVariant(QColor::fromString("#a084cf")),
|
||||||
|
QVariant(QColor::fromString("#8a6fbd")),
|
||||||
|
QVariant(QColor::fromString("#765aac")),
|
||||||
|
QVariant(QColor::fromString("#62459a")),
|
||||||
|
QVariant(QColor::fromString("#4f3089")),
|
||||||
|
QVariant(QColor::fromString("#3e1878")),
|
||||||
|
};
|
||||||
|
|
||||||
|
const QVariant ADSBDemodSettings::m_greyPalette[8] = {
|
||||||
|
QVariant(QColor::fromString("#808080")),
|
||||||
|
QVariant(QColor::fromString("#808080")),
|
||||||
|
QVariant(QColor::fromString("#808080")),
|
||||||
|
QVariant(QColor::fromString("#808080")),
|
||||||
|
QVariant(QColor::fromString("#808080")),
|
||||||
|
QVariant(QColor::fromString("#808080")),
|
||||||
|
QVariant(QColor::fromString("#808080")),
|
||||||
|
QVariant(QColor::fromString("#808080")),
|
||||||
|
};
|
||||||
|
|
||||||
|
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]},
|
||||||
|
};
|
@ -29,63 +29,89 @@
|
|||||||
class Serializable;
|
class Serializable;
|
||||||
|
|
||||||
// Number of columns in the table
|
// Number of columns in the table
|
||||||
#define ADSBDEMOD_COLUMNS 54
|
#define ADSBDEMOD_COLUMNS 78
|
||||||
|
|
||||||
// ADS-B table columns
|
// ADS-B table columns
|
||||||
#define ADSB_COL_ICAO 0
|
#define ADSB_COL_ICAO 0
|
||||||
#define ADSB_COL_CALLSIGN 1
|
#define ADSB_COL_CALLSIGN 1
|
||||||
#define ADSB_COL_ATC_CALLSIGN 2
|
#define ADSB_COL_ATC_CALLSIGN 2
|
||||||
#define ADSB_COL_MODEL 3
|
#define ADSB_COL_MODEL 3
|
||||||
#define ADSB_COL_AIRLINE 4
|
#define ADSB_COL_TYPE 4
|
||||||
#define ADSB_COL_COUNTRY 5
|
#define ADSB_COL_SIDEVIEW 5
|
||||||
#define ADSB_COL_GROUND_SPEED 6
|
#define ADSB_COL_AIRLINE 6
|
||||||
#define ADSB_COL_TRUE_AIRSPEED 7
|
#define ADSB_COL_COUNTRY 7
|
||||||
#define ADSB_COL_INDICATED_AIRSPEED 8
|
#define ADSB_COL_GROUND_SPEED 8
|
||||||
#define ADSB_COL_MACH 9
|
#define ADSB_COL_TRUE_AIRSPEED 9
|
||||||
#define ADSB_COL_SEL_ALTITUDE 10
|
#define ADSB_COL_INDICATED_AIRSPEED 10
|
||||||
#define ADSB_COL_ALTITUDE 11
|
#define ADSB_COL_MACH 11
|
||||||
#define ADSB_COL_VERTICALRATE 12
|
#define ADSB_COL_SEL_ALTITUDE 12
|
||||||
#define ADSB_COL_SEL_HEADING 13
|
#define ADSB_COL_ALTITUDE 13
|
||||||
#define ADSB_COL_HEADING 14
|
#define ADSB_COL_VERTICALRATE 14
|
||||||
#define ADSB_COL_TURNRATE 15
|
#define ADSB_COL_SEL_HEADING 15
|
||||||
#define ADSB_COL_ROLL 16
|
#define ADSB_COL_HEADING 16
|
||||||
#define ADSB_COL_RANGE 17
|
#define ADSB_COL_TRACK 17
|
||||||
#define ADSB_COL_AZEL 18
|
#define ADSB_COL_TURNRATE 18
|
||||||
#define ADSB_COL_CATEGORY 19
|
#define ADSB_COL_ROLL 19
|
||||||
#define ADSB_COL_STATUS 20
|
#define ADSB_COL_RANGE 20
|
||||||
#define ADSB_COL_SQUAWK 21
|
#define ADSB_COL_AZEL 21
|
||||||
#define ADSB_COL_REGISTRATION 22
|
#define ADSB_COL_CATEGORY 22
|
||||||
#define ADSB_COL_REGISTERED 23
|
#define ADSB_COL_STATUS 23
|
||||||
#define ADSB_COL_MANUFACTURER 24
|
#define ADSB_COL_SQUAWK 24
|
||||||
#define ADSB_COL_OWNER 25
|
#define ADSB_COL_IDENT 25
|
||||||
#define ADSB_COL_OPERATOR_ICAO 26
|
#define ADSB_COL_REGISTRATION 26
|
||||||
#define ADSB_COL_AP 27
|
#define ADSB_COL_REGISTERED 27
|
||||||
#define ADSB_COL_V_MODE 28
|
#define ADSB_COL_MANUFACTURER 28
|
||||||
#define ADSB_COL_L_MODE 29
|
#define ADSB_COL_OWNER 29
|
||||||
#define ADSB_COL_BARO 30
|
#define ADSB_COL_OPERATOR_ICAO 30
|
||||||
#define ADSB_COL_HEADWIND 31
|
#define ADSB_COL_AP 31
|
||||||
#define ADSB_COL_EST_AIR_TEMP 32
|
#define ADSB_COL_V_MODE 32
|
||||||
#define ADSB_COL_WIND_SPEED 33
|
#define ADSB_COL_L_MODE 33
|
||||||
#define ADSB_COL_WIND_DIR 34
|
#define ADSB_COL_TCAS 34
|
||||||
#define ADSB_COL_STATIC_PRESSURE 35
|
#define ADSB_COL_ACAS 35
|
||||||
#define ADSB_COL_STATIC_AIR_TEMP 36
|
#define ADSB_COL_RA 36
|
||||||
#define ADSB_COL_HUMIDITY 37
|
#define ADSB_COL_MAX_SPEED 37
|
||||||
#define ADSB_COL_LATITUDE 38
|
#define ADSB_COL_VERSION 38
|
||||||
#define ADSB_COL_LONGITUDE 39
|
#define ADSB_COL_LENGTH 39
|
||||||
#define ADSB_COL_TIME 40
|
#define ADSB_COL_WIDTH 40
|
||||||
#define ADSB_COL_FRAMECOUNT 41
|
#define ADSB_COL_BARO 41
|
||||||
#define ADSB_COL_TIS_B 42
|
#define ADSB_COL_HEADWIND 42
|
||||||
#define ADSB_COL_CORRELATION 43
|
#define ADSB_COL_EST_AIR_TEMP 43
|
||||||
#define ADSB_COL_RSSI 44
|
#define ADSB_COL_WIND_SPEED 44
|
||||||
#define ADSB_COL_FLIGHT_STATUS 45
|
#define ADSB_COL_WIND_DIR 45
|
||||||
#define ADSB_COL_DEP 46
|
#define ADSB_COL_STATIC_PRESSURE 46
|
||||||
#define ADSB_COL_ARR 47
|
#define ADSB_COL_STATIC_AIR_TEMP 47
|
||||||
#define ADSB_COL_STD 48
|
#define ADSB_COL_HUMIDITY 48
|
||||||
#define ADSB_COL_ETD 49
|
#define ADSB_COL_LATITUDE 49
|
||||||
#define ADSB_COL_ATD 50
|
#define ADSB_COL_LONGITUDE 50
|
||||||
#define ADSB_COL_STA 51
|
#define ADSB_COL_IC 51
|
||||||
#define ADSB_COL_ETA 52
|
#define ADSB_COL_TIME 52
|
||||||
#define ADSB_COL_ATA 53
|
#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
|
struct ADSBDemodSettings
|
||||||
{
|
{
|
||||||
@ -104,6 +130,7 @@ struct ADSBDemodSettings
|
|||||||
int32_t m_inputFrequencyOffset;
|
int32_t m_inputFrequencyOffset;
|
||||||
Real m_rfBandwidth;
|
Real m_rfBandwidth;
|
||||||
Real m_correlationThreshold; //!< Correlation power threshold in dB
|
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_samplesPerBit;
|
||||||
int m_removeTimeout; //!< Time in seconds before removing an aircraft, unless a new frame is received
|
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
|
QString m_tableFontName; //!< Font to use for table
|
||||||
int m_tableFontSize;
|
int m_tableFontSize;
|
||||||
bool m_displayDemodStats;
|
bool m_displayDemodStats;
|
||||||
bool m_correlateFullPreamble;
|
|
||||||
bool m_demodModeS; //!< Demodulate all Mode-S frames, not just ADS-B
|
bool m_demodModeS; //!< Demodulate all Mode-S frames, not just ADS-B
|
||||||
QString m_amDemod; //!< AM Demod to tune to selected ATC frequency
|
QString m_amDemod; //!< AM Demod to tune to selected ATC frequency
|
||||||
bool m_autoResizeTableColumns;
|
bool m_autoResizeTableColumns;
|
||||||
@ -186,13 +212,24 @@ struct ADSBDemodSettings
|
|||||||
bool m_displayPhotos;
|
bool m_displayPhotos;
|
||||||
Serializable *m_rollupState;
|
Serializable *m_rollupState;
|
||||||
bool m_verboseModelMatching;
|
bool m_verboseModelMatching;
|
||||||
int m_airfieldElevation; //!< QFE in ft so aircraft takeoff/land from correct position
|
|
||||||
int m_aircraftMinZoom;
|
int m_aircraftMinZoom;
|
||||||
|
|
||||||
bool m_atcLabels;
|
bool m_atcLabels;
|
||||||
bool m_atcCallsigns;
|
bool m_atcCallsigns;
|
||||||
int m_transitionAlt;
|
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();
|
ADSBDemodSettings();
|
||||||
void resetToDefaults();
|
void resetToDefaults();
|
||||||
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
||||||
@ -203,6 +240,16 @@ struct ADSBDemodSettings
|
|||||||
void deserializeNotificationSettings(const QByteArray& data, QList<NotificationSettings *>& notificationSettings);
|
void deserializeNotificationSettings(const QByteArray& data, QList<NotificationSettings *>& notificationSettings);
|
||||||
void applySettings(const QStringList& settingsKeys, const ADSBDemodSettings& settings);
|
void applySettings(const QStringList& settingsKeys, const ADSBDemodSettings& settings);
|
||||||
QString getDebugString(const QStringList& settingsKeys, bool force = false) const;
|
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_ */
|
#endif /* PLUGINS_CHANNELRX_DEMODADSB_ADSBDEMODSETTINGS_H_ */
|
||||||
|
@ -19,14 +19,16 @@
|
|||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "util/profiler.h"
|
||||||
|
|
||||||
#include "adsbdemodsink.h"
|
#include "adsbdemodsink.h"
|
||||||
#include "adsbdemodsinkworker.h"
|
#include "adsbdemodsinkworker.h"
|
||||||
|
#include "adsbdemod.h"
|
||||||
#include "adsb.h"
|
#include "adsb.h"
|
||||||
|
|
||||||
ADSBDemodSink::ADSBDemodSink() :
|
ADSBDemodSink::ADSBDemodSink() :
|
||||||
m_channelSampleRate(6000000),
|
m_channelSampleRate(6000000),
|
||||||
m_channelFrequencyOffset(0),
|
m_channelFrequencyOffset(0),
|
||||||
m_feedTime(0.0),
|
|
||||||
m_sampleBuffer{nullptr, nullptr, nullptr},
|
m_sampleBuffer{nullptr, nullptr, nullptr},
|
||||||
m_worker(this),
|
m_worker(this),
|
||||||
m_writeBuffer(0),
|
m_writeBuffer(0),
|
||||||
@ -53,7 +55,7 @@ ADSBDemodSink::~ADSBDemodSink()
|
|||||||
void ADSBDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
void ADSBDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
||||||
{
|
{
|
||||||
// Start timing how long we are in this function
|
// 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
|
// Optimise for common case, where no resampling or frequency offset
|
||||||
if ((m_interpolatorDistance == 1.0f) && (m_channelFrequencyOffset == 0))
|
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
|
// Calculate number of seconds in this function
|
||||||
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - m_startPoint;
|
PROFILER_STOP("ADSB feed");
|
||||||
m_feedTime += sec.count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADSBDemodSink::processOneSample(Real magsq)
|
void ADSBDemodSink::processOneSample(Real magsq)
|
||||||
@ -127,8 +128,17 @@ void ADSBDemodSink::processOneSample(Real magsq)
|
|||||||
m_writeIdx++;
|
m_writeIdx++;
|
||||||
if (!m_bufferDateTimeValid[m_writeBuffer])
|
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;
|
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)
|
if (m_writeIdx >= m_bufferSize)
|
||||||
{
|
{
|
||||||
@ -138,15 +148,9 @@ void ADSBDemodSink::processOneSample(Real magsq)
|
|||||||
if (m_writeBuffer >= m_buffers)
|
if (m_writeBuffer >= m_buffers)
|
||||||
m_writeBuffer = 0;
|
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())
|
if (m_worker.isRunning())
|
||||||
m_bufferWrite[m_writeBuffer].acquire();
|
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_writeIdx = m_samplesPerFrame - 1; // Leave space for copying samples from previous buffer
|
||||||
|
|
||||||
m_bufferDateTimeValid[m_writeBuffer] = false;
|
m_bufferDateTimeValid[m_writeBuffer] = false;
|
||||||
@ -253,7 +257,6 @@ void ADSBDemodSink::applySettings(const ADSBDemodSettings& settings, const QStri
|
|||||||
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
|
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
|
||||||
<< " m_rfBandwidth: " << settings.m_rfBandwidth
|
<< " m_rfBandwidth: " << settings.m_rfBandwidth
|
||||||
<< " m_correlationThreshold: " << settings.m_correlationThreshold
|
<< " m_correlationThreshold: " << settings.m_correlationThreshold
|
||||||
<< " m_correlateFullPreamble: " << settings.m_correlateFullPreamble
|
|
||||||
<< " m_demodModeS: " << settings.m_demodModeS
|
<< " m_demodModeS: " << settings.m_demodModeS
|
||||||
<< " m_samplesPerBit: " << settings.m_samplesPerBit
|
<< " m_samplesPerBit: " << settings.m_samplesPerBit
|
||||||
<< " force: " << force;
|
<< " force: " << force;
|
||||||
@ -285,3 +288,9 @@ void ADSBDemodSink::applySettings(const ADSBDemodSettings& settings, const QStri
|
|||||||
m_settings.applySettings(settingsKeys, settings);
|
m_settings.applySettings(settingsKeys, settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ADSBDemodSink::resetStats()
|
||||||
|
{
|
||||||
|
ADSBDemod::MsgResetStats* msg = ADSBDemod::MsgResetStats::create();
|
||||||
|
m_worker.getInputMessageQueue()->push(msg);
|
||||||
|
}
|
||||||
|
@ -19,9 +19,6 @@
|
|||||||
#ifndef INCLUDE_ADSBDEMODSINK_H
|
#ifndef INCLUDE_ADSBDEMODSINK_H
|
||||||
#define INCLUDE_ADSBDEMODSINK_H
|
#define INCLUDE_ADSBDEMODSINK_H
|
||||||
|
|
||||||
#define BOOST_CHRONO_HEADER_ONLY
|
|
||||||
#include <boost/chrono/chrono.hpp>
|
|
||||||
|
|
||||||
#include "dsp/channelsamplesink.h"
|
#include "dsp/channelsamplesink.h"
|
||||||
#include "dsp/nco.h"
|
#include "dsp/nco.h"
|
||||||
#include "dsp/interpolator.h"
|
#include "dsp/interpolator.h"
|
||||||
@ -60,6 +57,7 @@ public:
|
|||||||
void setMessageQueueToWorker(MessageQueue *messageQueue) { m_messageQueueToWorker = messageQueue; }
|
void setMessageQueueToWorker(MessageQueue *messageQueue) { m_messageQueueToWorker = messageQueue; }
|
||||||
void startWorker();
|
void startWorker();
|
||||||
void stopWorker();
|
void stopWorker();
|
||||||
|
void resetStats();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend ADSBDemodSinkWorker;
|
friend ADSBDemodSinkWorker;
|
||||||
@ -83,9 +81,6 @@ private:
|
|||||||
Real m_interpolatorDistance;
|
Real m_interpolatorDistance;
|
||||||
Real m_interpolatorDistanceRemain;
|
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
|
// 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
|
// 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
|
// 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_bufferWrite[3]; //!< Semaphore to control write access to the buffers
|
||||||
QSemaphore m_bufferRead[3]; //!< Semaphore to control read access from 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_bufferFirstSampleDateTime[3]; //!< Time for first sample in the buffer
|
||||||
|
QDateTime m_minFirstSampleDateTime;
|
||||||
bool m_bufferDateTimeValid[3];
|
bool m_bufferDateTimeValid[3];
|
||||||
ADSBDemodSinkWorker m_worker; //!< Worker thread that does the actual demodulation
|
ADSBDemodSinkWorker m_worker; //!< Worker thread that does the actual demodulation
|
||||||
int m_writeBuffer; //!< Which of the 3 buffers we're writing in to
|
int m_writeBuffer; //!< Which of the 3 buffers we're writing in to
|
||||||
|
@ -15,29 +15,292 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
// 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 <QDebug>
|
||||||
|
|
||||||
#include "util/db.h"
|
#include "util/db.h"
|
||||||
|
#include "util/profiler.h"
|
||||||
|
#include "util/units.h"
|
||||||
|
#include "util/osndb.h"
|
||||||
|
#include "util/popcount.h"
|
||||||
|
|
||||||
#include "adsbdemodreport.h"
|
#include "adsbdemodreport.h"
|
||||||
#include "adsbdemodsink.h"
|
#include "adsbdemodsink.h"
|
||||||
#include "adsbdemodsinkworker.h"
|
#include "adsbdemodsinkworker.h"
|
||||||
|
#include "adsbdemod.h"
|
||||||
#include "adsb.h"
|
#include "adsb.h"
|
||||||
|
|
||||||
MESSAGE_CLASS_DEFINITION(ADSBDemodSinkWorker::MsgConfigureADSBDemodSinkWorker, Message)
|
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);
|
||||||
|
Real corr = CalcDb::dbPower(m_correlationScale * l[i].m_preambleCorrelation);
|
||||||
|
|
||||||
|
int curAltitude = -1;
|
||||||
|
int curId = -1;
|
||||||
|
|
||||||
|
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()
|
void ADSBDemodSinkWorker::run()
|
||||||
{
|
{
|
||||||
int readBuffer = 0;
|
int readBuffer = 0;
|
||||||
|
m_lastClear = QDateTime::currentDateTime();
|
||||||
|
|
||||||
// Acquire first buffer
|
// Acquire first buffer
|
||||||
m_sink->m_bufferRead[readBuffer].acquire();
|
m_sink->m_bufferRead[readBuffer].acquire();
|
||||||
|
|
||||||
|
if (isInterruptionRequested()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Start recording how much time is spent processing in this method
|
// 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
|
// Check for updated settings
|
||||||
handleInputMessages();
|
handleInputMessages();
|
||||||
@ -51,8 +314,6 @@ void ADSBDemodSinkWorker::run()
|
|||||||
<< " samplesPerFrame: " << samplesPerFrame
|
<< " samplesPerFrame: " << samplesPerFrame
|
||||||
<< " samplesPerChip: " << samplesPerChip
|
<< " samplesPerChip: " << samplesPerChip
|
||||||
<< " samplesPerBit: " << samplesPerBit
|
<< " samplesPerBit: " << samplesPerBit
|
||||||
<< " correlateFullPreamble: " << m_settings.m_correlateFullPreamble
|
|
||||||
<< " correlationScale: " << m_correlationScale
|
|
||||||
<< " correlationThreshold: " << m_settings.m_correlationThreshold;
|
<< " correlationThreshold: " << m_settings.m_correlationThreshold;
|
||||||
|
|
||||||
int readIdx = m_sink->m_samplesPerFrame - 1;
|
int readIdx = m_sink->m_samplesPerFrame - 1;
|
||||||
@ -63,61 +324,33 @@ void ADSBDemodSinkWorker::run()
|
|||||||
|
|
||||||
// Correlate received signal with expected preamble
|
// Correlate received signal with expected preamble
|
||||||
// chip+ indexes are 0, 2, 7, 9
|
// 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 preambleCorrelationOnes = 0.0;
|
||||||
Real preambleCorrelationZeros = 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];
|
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 + 3*samplesPerChip + i];
|
||||||
|
|
||||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 4*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 + 5*samplesPerChip + i];
|
||||||
|
|
||||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 6*samplesPerChip + i];
|
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 6*samplesPerChip + i];
|
||||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 7*samplesPerChip + i];
|
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 7*samplesPerChip + i];
|
||||||
|
|
||||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 8*samplesPerChip + i];
|
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 8*samplesPerChip + i];
|
||||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 9*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 + 10*samplesPerChip + i];
|
||||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 11*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 + 12*samplesPerChip + i];
|
||||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 13*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 + 14*samplesPerChip + i];
|
||||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 15*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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the ratio of ones power over zeros power, as we don't care how powerful the signal
|
// Use the ratio of ones power over zeros power, as we don't care how powerful the signal
|
||||||
@ -138,6 +371,7 @@ void ADSBDemodSinkWorker::run()
|
|||||||
int firstIdx = startIdx;
|
int firstIdx = startIdx;
|
||||||
|
|
||||||
m_demodStats.m_correlatorMatches++;
|
m_demodStats.m_correlatorMatches++;
|
||||||
|
|
||||||
// Skip over preamble
|
// Skip over preamble
|
||||||
startIdx += samplesPerBit*ADS_B_PREAMBLE_BITS;
|
startIdx += samplesPerBit*ADS_B_PREAMBLE_BITS;
|
||||||
|
|
||||||
@ -147,6 +381,7 @@ void ADSBDemodSinkWorker::run()
|
|||||||
int currentBit;
|
int currentBit;
|
||||||
unsigned char currentByte = 0;
|
unsigned char currentByte = 0;
|
||||||
int df;
|
int df;
|
||||||
|
int firstIndex = 0;
|
||||||
|
|
||||||
for (int bit = 0; bit < ADS_B_ES_BITS; bit++)
|
for (int bit = 0; bit < ADS_B_ES_BITS; bit++)
|
||||||
{
|
{
|
||||||
@ -165,6 +400,9 @@ void ADSBDemodSinkWorker::run()
|
|||||||
currentByte |= currentBit << (7-(bit & 0x7));
|
currentByte |= currentBit << (7-(bit & 0x7));
|
||||||
if ((bit & 0x7) == 0x7)
|
if ((bit & 0x7) == 0x7)
|
||||||
{
|
{
|
||||||
|
if (byteIdx == 0) {
|
||||||
|
firstIndex = startIdx;
|
||||||
|
}
|
||||||
data[byteIdx++] = currentByte;
|
data[byteIdx++] = currentByte;
|
||||||
currentByte = 0;
|
currentByte = 0;
|
||||||
// Don't try to demodulate any further, if this isn't an ADS-B frame
|
// Don't try to demodulate any further, if this isn't an ADS-B frame
|
||||||
@ -178,6 +416,10 @@ void ADSBDemodSinkWorker::run()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Real preambleCorrelationScaled = preambleCorrelation * m_correlationScale;
|
||||||
|
Real correlationOnes = preambleCorrelationOnes / samplesPerChip;
|
||||||
|
QDateTime dateTime = rxDateTime(firstIdx, readBuffer);
|
||||||
|
|
||||||
// Is ADS-B?
|
// Is ADS-B?
|
||||||
df = ((data[0] >> 3) & ADS_B_DF_MASK);
|
df = ((data[0] >> 3) & ADS_B_DF_MASK);
|
||||||
if ((df == 17) || (df == 18))
|
if ((df == 17) || (df == 18))
|
||||||
@ -188,109 +430,177 @@ void ADSBDemodSinkWorker::run()
|
|||||||
m_crc.calculate(data, ADS_B_ES_BYTES-3);
|
m_crc.calculate(data, ADS_B_ES_BYTES-3);
|
||||||
if (parity == m_crc.get())
|
if (parity == m_crc.get())
|
||||||
{
|
{
|
||||||
// Got a valid frame
|
// Get 24-bit ICAO
|
||||||
m_demodStats.m_adsbFrames++;
|
|
||||||
// Get 24-bit ICAO and save in hash of ICAOs that have been seen
|
|
||||||
unsigned icao = ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff);
|
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
|
if (icaoValid(icao))
|
||||||
// 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())
|
|
||||||
{
|
{
|
||||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
// Got a valid frame
|
||||||
QByteArray((char*)data, sizeof(data)),
|
m_demodStats.m_adsbFrames++;
|
||||||
preambleCorrelation * m_correlationScale,
|
// Save in hash of ICAOs that have been seen
|
||||||
preambleCorrelationOnes / samplesPerChip,
|
m_icaos.insert(icao, dateTime.toMSecsSinceEpoch());
|
||||||
rxDateTime(firstIdx, readBuffer),
|
// Don't try to re-demodulate the same frame
|
||||||
m_crc.get());
|
// We could possibly allow a partial overlap here
|
||||||
m_sink->getMessageQueueToGUI()->push(msg);
|
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
|
else
|
||||||
if (m_sink->getMessageQueueToWorker())
|
|
||||||
{
|
{
|
||||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
m_demodStats.m_icaoFails++;
|
||||||
QByteArray((char*)data, sizeof(data)),
|
|
||||||
preambleCorrelation * m_correlationScale,
|
|
||||||
preambleCorrelationOnes / samplesPerChip,
|
|
||||||
rxDateTime(firstIdx, readBuffer),
|
|
||||||
m_crc.get());
|
|
||||||
m_sink->getMessageQueueToWorker()->push(msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
m_demodStats.m_crcFails++;
|
m_demodStats.m_crcFails++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (m_settings.m_demodModeS)
|
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
|
for (int bit = 0; bit < ADS_B_PREAMBLE_BITS; bit++)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
// Extract received parity
|
preambleChips[bit*2] = 0.0f;
|
||||||
int parity = (data[bytes-3] << 16) | (data[bytes-2] << 8) | data[bytes-1];
|
preambleChips[bit*2+1] = 0.0f;
|
||||||
// Calculate CRC on received frame
|
for (int i = 0; i < samplesPerChip; i++)
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
unsigned icao = (parity ^ crc) & 0xffffff;
|
preambleChips[bit*2] += m_sink->m_sampleBuffer[readBuffer][startIdx+i];
|
||||||
if (m_icaos.contains(icao)) {
|
preambleChips[bit*2+1] += m_sink->m_sampleBuffer[readBuffer][startIdx+samplesPerChip+i];
|
||||||
crc ^= icao;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// For DF11, the last 7 bits may have an address/interogration identifier (II)
|
|
||||||
// XORed in, so we ignore those bits
|
startIdx += samplesPerBit;
|
||||||
if ((parity == crc) || ((df == 11) && ((parity & 0xffff80) == (crc & 0xffff80))))
|
}
|
||||||
|
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++;
|
bool invalid = modeSValid(data, df);
|
||||||
// Pass to GUI (only pass formats it can decode)
|
|
||||||
if (m_sink->getMessageQueueToGUI() && ((df == 4) || (df == 5) || (df == 20) || (df == 21)))
|
if (!invalid)
|
||||||
{
|
{
|
||||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
// Extract received parity
|
||||||
QByteArray((char*)data, bytes),
|
int parity = (data[bytes-3] << 16) | (data[bytes-2] << 8) | data[bytes-1];
|
||||||
preambleCorrelation * m_correlationScale,
|
// Calculate CRC on received frame
|
||||||
preambleCorrelationOnes / samplesPerChip,
|
m_crc.init();
|
||||||
rxDateTime(firstIdx, readBuffer),
|
m_crc.calculate(data, bytes-3);
|
||||||
m_crc.get());
|
int crc = m_crc.get();
|
||||||
m_sink->getMessageQueueToGUI()->push(msg);
|
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
|
else
|
||||||
if (m_sink->getMessageQueueToWorker())
|
|
||||||
{
|
{
|
||||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
m_demodStats.m_invalidFails++;
|
||||||
QByteArray((char*)data, bytes),
|
|
||||||
preambleCorrelation * m_correlationScale,
|
|
||||||
preambleCorrelationOnes / samplesPerChip,
|
|
||||||
rxDateTime(firstIdx, readBuffer),
|
|
||||||
m_crc.get());
|
|
||||||
m_sink->getMessageQueueToWorker()->push(msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_demodStats.m_crcFails++;
|
m_demodStats.m_typeFails++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_demodStats.m_typeFails++;
|
{
|
||||||
|
m_demodStats.m_preambleFails++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
m_demodStats.m_typeFails++;
|
m_demodStats.m_typeFails++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readIdx++;
|
readIdx++;
|
||||||
if (readIdx > m_sink->m_bufferSize - samplesPerFrame)
|
if (readIdx > m_sink->m_bufferSize - samplesPerFrame)
|
||||||
{
|
{
|
||||||
@ -299,9 +609,7 @@ void ADSBDemodSinkWorker::run()
|
|||||||
nextBuffer = 0;
|
nextBuffer = 0;
|
||||||
|
|
||||||
// Update amount of time spent processing (don't include time spend in acquire)
|
// Update amount of time spent processing (don't include time spend in acquire)
|
||||||
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - startPoint;
|
PROFILER_STOP("ADS-B demod");
|
||||||
m_demodStats.m_demodTime += sec.count();
|
|
||||||
m_demodStats.m_feedTime = m_sink->m_feedTime;
|
|
||||||
|
|
||||||
// Send stats to GUI
|
// Send stats to GUI
|
||||||
if (m_sink->getMessageQueueToGUI())
|
if (m_sink->getMessageQueueToGUI())
|
||||||
@ -319,7 +627,7 @@ void ADSBDemodSinkWorker::run()
|
|||||||
handleInputMessages();
|
handleInputMessages();
|
||||||
|
|
||||||
// Resume timing how long we are processing
|
// Resume timing how long we are processing
|
||||||
startPoint = boost::chrono::steady_clock::now();
|
PROFILER_RESTART();
|
||||||
|
|
||||||
int samplesRemaining = m_sink->m_bufferSize - readIdx;
|
int samplesRemaining = m_sink->m_bufferSize - readIdx;
|
||||||
if (samplesRemaining > 0)
|
if (samplesRemaining > 0)
|
||||||
@ -359,15 +667,6 @@ void ADSBDemodSinkWorker::handleInputMessages()
|
|||||||
QStringList settingsKeys = cfg->getSettingsKeys();
|
QStringList settingsKeys = cfg->getSettingsKeys();
|
||||||
bool force = cfg->getForce();
|
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)
|
if ((settingsKeys.contains("correlationThreshold") && (m_settings.m_correlationThreshold != settings.m_correlationThreshold)) || force)
|
||||||
{
|
{
|
||||||
m_correlationThresholdLinear = CalcDb::powerFromdB(settings.m_correlationThreshold);
|
m_correlationThresholdLinear = CalcDb::powerFromdB(settings.m_correlationThreshold);
|
||||||
@ -382,6 +681,10 @@ void ADSBDemodSinkWorker::handleInputMessages()
|
|||||||
}
|
}
|
||||||
delete message;
|
delete message;
|
||||||
}
|
}
|
||||||
|
else if (ADSBDemod::MsgResetStats::match(*message))
|
||||||
|
{
|
||||||
|
m_demodStats.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,11 +77,29 @@ private:
|
|||||||
ADSBDemodSink *m_sink;
|
ADSBDemodSink *m_sink;
|
||||||
ADSBDemodStats m_demodStats;
|
ADSBDemodStats m_demodStats;
|
||||||
Real m_correlationThresholdLinear;
|
Real m_correlationThresholdLinear;
|
||||||
Real m_correlationScale;
|
static const Real m_correlationScale;
|
||||||
crcadsb m_crc; //!< Have as member to avoid recomputing LUT
|
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;
|
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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,20 +25,27 @@ struct ADSBDemodStats {
|
|||||||
qint64 m_correlatorMatches; //!< Total number of correlator matches
|
qint64 m_correlatorMatches; //!< Total number of correlator matches
|
||||||
qint64 m_adsbFrames; //!< How many ADS-B frames with correct CRCs
|
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_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_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
|
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()
|
qint64 m_invalidFails; //!< How many frames we've demoded with reserved bits set
|
||||||
double m_feedTime; //!< How long we've spent in feed()
|
qint64 m_icaoFails; //!< How many frames we've demoded with reserved ICAO
|
||||||
|
|
||||||
ADSBDemodStats() :
|
ADSBDemodStats()
|
||||||
m_correlatorMatches(0),
|
|
||||||
m_adsbFrames(0),
|
|
||||||
m_modesFrames(0),
|
|
||||||
m_crcFails(0),
|
|
||||||
m_typeFails(0),
|
|
||||||
m_demodTime(0.0),
|
|
||||||
m_feedTime(0.0)
|
|
||||||
{
|
{
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user