Merge pull request #2465 from srcejon/freq_scanner
Map and ADS-B updates
Before Width: | Height: | Size: 640 KiB After Width: | Height: | Size: 1.4 MiB |
BIN
doc/img/ADSBDemod_plugin_chart.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
doc/img/ADSBDemod_plugin_coverage.png
Normal file
After Width: | Height: | Size: 217 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 37 KiB |
BIN
doc/img/ADSBDemod_plugin_displaysettings_map.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
doc/img/ADSBDemod_plugin_ic.png
Normal file
After Width: | Height: | Size: 181 KiB |
Before Width: | Height: | Size: 490 KiB After Width: | Height: | Size: 844 KiB |
Before Width: | Height: | Size: 570 KiB After Width: | Height: | Size: 819 KiB |
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 2.3 MiB |
BIN
doc/img/ADSBDemod_plugin_radius.png
Normal file
After Width: | Height: | Size: 259 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 42 KiB |
BIN
doc/img/ADSBDemod_plugin_stats.png
Normal file
After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 351 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.6 MiB |
Before Width: | Height: | Size: 603 KiB After Width: | Height: | Size: 1.4 MiB |
BIN
doc/img/Map_plugin_aurora.png
Normal file
After Width: | Height: | Size: 859 KiB |
Before Width: | Height: | Size: 547 KiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 53 KiB |
BIN
doc/img/Map_plugin_magdec.png
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
doc/img/Map_plugin_maidenhead_converter.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
doc/img/Map_plugin_maidenhead_grid1.png
Normal file
After Width: | Height: | Size: 426 KiB |
BIN
doc/img/Map_plugin_maidenhead_grid2.png
Normal file
After Width: | Height: | Size: 473 KiB |
BIN
doc/img/Map_plugin_pfd.png
Normal file
After Width: | Height: | Size: 745 KiB |
@ -28,7 +28,6 @@ set(adsb_HEADERS
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
${Boost_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
@ -47,6 +46,7 @@ if(NOT SERVER_MODE)
|
||||
adsbdemodicons.qrc
|
||||
airlinelogos.qrc
|
||||
flags.qrc
|
||||
sideviews.qrc
|
||||
)
|
||||
set(adsb_HEADERS
|
||||
${adsb_HEADERS}
|
||||
@ -60,7 +60,7 @@ if(NOT SERVER_MODE)
|
||||
)
|
||||
|
||||
set(TARGET_NAME ${PLUGINS_PREFIX}demodadsb)
|
||||
set(TARGET_LIB Qt::Widgets Qt::Quick Qt::QuickWidgets Qt::Positioning)
|
||||
set(TARGET_LIB Qt::Widgets Qt::Quick Qt::QuickWidgets Qt::Positioning Qt::Charts)
|
||||
if(Qt${QT_DEFAULT_MAJOR_VERSION}Location_FOUND)
|
||||
list(APPEND TARGET_LIB Qt::Location)
|
||||
endif()
|
||||
@ -86,12 +86,7 @@ if(NOT BUILD_SHARED_LIBS)
|
||||
set_property(GLOBAL APPEND PROPERTY STATIC_PLUGINS_PROPERTY ${TARGET_NAME})
|
||||
endif()
|
||||
|
||||
if (NOT WIN32)
|
||||
link_directories(${Boost_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE
|
||||
Boost::disable_autolinking
|
||||
Qt::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
|
@ -17,10 +17,6 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define BOOST_CHRONO_HEADER_ONLY
|
||||
#include <boost/chrono/chrono.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <complex.h>
|
||||
|
||||
#include <QTime>
|
||||
@ -49,6 +45,7 @@
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgConfigureADSBDemod, Message)
|
||||
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgAircraftReport, Message)
|
||||
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgResetStats, Message)
|
||||
|
||||
const char* const ADSBDemod::m_channelIdURI = "sdrangel.channel.adsbdemod";
|
||||
const char* const ADSBDemod::m_channelId = "ADSBDemod";
|
||||
@ -194,6 +191,13 @@ bool ADSBDemod::handleMessage(const Message& cmd)
|
||||
m_aircraftReport = msg.getReport();
|
||||
return true;
|
||||
}
|
||||
else if (MsgResetStats::match(cmd))
|
||||
{
|
||||
MsgResetStats& msg = (MsgResetStats&) cmd;
|
||||
MsgResetStats* rep = new MsgResetStats(msg);
|
||||
m_basebandSink->getInputMessageQueue()->push(rep);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
@ -337,9 +341,6 @@ void ADSBDemod::webapiUpdateChannelSettings(
|
||||
if (channelSettingsKeys.contains("samplesPerBit")) {
|
||||
settings.m_samplesPerBit = response.getAdsbDemodSettings()->getSamplesPerBit();
|
||||
}
|
||||
if (channelSettingsKeys.contains("correlateFullPreamble")) {
|
||||
settings.m_correlateFullPreamble = response.getAdsbDemodSettings()->getCorrelateFullPreamble() != 0;
|
||||
}
|
||||
if (channelSettingsKeys.contains("demodModeS")) {
|
||||
settings.m_demodModeS = response.getAdsbDemodSettings()->getDemodModeS() != 0;
|
||||
}
|
||||
@ -458,7 +459,6 @@ void ADSBDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res
|
||||
response.getAdsbDemodSettings()->setRfBandwidth(settings.m_rfBandwidth);
|
||||
response.getAdsbDemodSettings()->setCorrelationThreshold(settings.m_correlationThreshold);
|
||||
response.getAdsbDemodSettings()->setSamplesPerBit(settings.m_samplesPerBit);
|
||||
response.getAdsbDemodSettings()->setCorrelateFullPreamble(settings.m_correlateFullPreamble ? 1 : 0);
|
||||
response.getAdsbDemodSettings()->setDemodModeS(settings.m_demodModeS ? 1 : 0);
|
||||
response.getAdsbDemodSettings()->setInterpolatorPhaseSteps(settings.m_interpolatorPhaseSteps);
|
||||
response.getAdsbDemodSettings()->setInterpolatorTapsPerPhase(settings.m_interpolatorTapsPerPhase);
|
||||
@ -587,9 +587,6 @@ void ADSBDemod::webapiReverseSendSettings(const QList<QString>& channelSettingsK
|
||||
if (channelSettingsKeys.contains("samplesPerBit") || force) {
|
||||
swgADSBDemodSettings->setSamplesPerBit(settings.m_samplesPerBit);
|
||||
}
|
||||
if (channelSettingsKeys.contains("correlateFullPreamble") || force) {
|
||||
swgADSBDemodSettings->setCorrelateFullPreamble(settings.m_correlateFullPreamble ? 1 : 0);
|
||||
}
|
||||
if (channelSettingsKeys.contains("demodModeS") || force) {
|
||||
swgADSBDemodSettings->setDemodModeS(settings.m_demodModeS ? 1 : 0);
|
||||
}
|
||||
|
@ -92,6 +92,21 @@ public:
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgResetStats : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
static MsgResetStats* create()
|
||||
{
|
||||
return new MsgResetStats();
|
||||
}
|
||||
|
||||
private:
|
||||
MsgResetStats() :
|
||||
Message()
|
||||
{ }
|
||||
};
|
||||
|
||||
ADSBDemod(DeviceAPI *deviceAPI);
|
||||
virtual ~ADSBDemod();
|
||||
virtual void destroy() { delete this; }
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "dsp/downchannelizer.h"
|
||||
|
||||
#include "adsbdemodbaseband.h"
|
||||
#include "adsbdemod.h"
|
||||
#include "adsb.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(ADSBDemodBaseband::MsgConfigureADSBDemodBaseband, Message)
|
||||
@ -132,6 +133,11 @@ bool ADSBDemodBaseband::handleMessage(const Message& cmd)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (ADSBDemod::MsgResetStats::match(cmd))
|
||||
{
|
||||
m_sink.resetStats();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
|
@ -50,7 +50,6 @@ ADSBDemodDisplayDialog::ADSBDemodDisplayDialog(ADSBDemodSettings *settings, QWid
|
||||
ui->airportSize->setCurrentIndex((int)settings->m_airportMinimumSize);
|
||||
ui->heliports->setChecked(settings->m_displayHeliports);
|
||||
ui->units->setCurrentIndex((int)settings->m_siUnits);
|
||||
ui->displayStats->setChecked(settings->m_displayDemodStats);
|
||||
ui->autoResizeTableColumns->setChecked(settings->m_autoResizeTableColumns);
|
||||
ui->aviationstackAPIKey->setText(settings->m_aviationstackAPIKey);
|
||||
ui->checkWXAPIKey->setText(settings->m_checkWXAPIKey);
|
||||
@ -67,12 +66,17 @@ ADSBDemodDisplayDialog::ADSBDemodDisplayDialog(ADSBDemodSettings *settings, QWid
|
||||
ui->mapProvider->setCurrentText(settings->m_mapProvider);
|
||||
}
|
||||
ui->mapType->setCurrentIndex((int)settings->m_mapType);
|
||||
ui->maptilerAPIKey->setText(m_settings->m_maptilerAPIKey);
|
||||
ui->navAids->setChecked(settings->m_displayNavAids);
|
||||
ui->atcCallsigns->setChecked(settings->m_atcCallsigns);
|
||||
ui->photos->setChecked(settings->m_displayPhotos);
|
||||
ui->verboseModelMatching->setChecked(settings->m_verboseModelMatching);
|
||||
ui->airfieldElevation->setValue(settings->m_airfieldElevation);
|
||||
ui->favourLivery->setChecked(settings->m_favourLivery);
|
||||
ui->transitionAltitude->setValue(settings->m_transitionAlt);
|
||||
for (auto i = ADSBDemodSettings::m_palettes.cbegin(), end = ADSBDemodSettings::m_palettes.cend(); i != end; ++i) {
|
||||
ui->flightPathPalette->addItem(i.key());
|
||||
}
|
||||
ui->flightPathPalette->setCurrentText(settings->m_flightPathPaletteName);
|
||||
}
|
||||
|
||||
ADSBDemodDisplayDialog::~ADSBDemodDisplayDialog()
|
||||
@ -107,16 +111,11 @@ void ADSBDemodDisplayDialog::accept()
|
||||
m_settings->m_displayHeliports = ui->heliports->isChecked();
|
||||
m_settingsKeys.append("displayHeliports");
|
||||
}
|
||||
if (m_settings->m_siUnits != ui->units->currentIndex() == 0 ? false : true)
|
||||
if (m_settings->m_siUnits != (ui->units->currentIndex() == 0 ? false : true))
|
||||
{
|
||||
m_settings->m_siUnits = ui->units->currentIndex() == 0 ? false : true;
|
||||
m_settingsKeys.append("siUnits");
|
||||
}
|
||||
if (m_settings->m_displayDemodStats != ui->displayStats->isChecked())
|
||||
{
|
||||
m_settings->m_displayDemodStats = ui->displayStats->isChecked();
|
||||
m_settingsKeys.append("displayDemodStats");
|
||||
}
|
||||
if (m_settings->m_autoResizeTableColumns != ui->autoResizeTableColumns->isChecked())
|
||||
{
|
||||
m_settings->m_autoResizeTableColumns = ui->autoResizeTableColumns->isChecked();
|
||||
@ -160,6 +159,11 @@ void ADSBDemodDisplayDialog::accept()
|
||||
m_settings->m_mapType = (ADSBDemodSettings::MapType)ui->mapType->currentIndex();
|
||||
m_settingsKeys.append("mapType");
|
||||
}
|
||||
if (m_settings->m_maptilerAPIKey != ui->maptilerAPIKey->text())
|
||||
{
|
||||
m_settings->m_maptilerAPIKey = ui->maptilerAPIKey->text();
|
||||
m_settingsKeys.append("maptilerAPIKey");
|
||||
}
|
||||
if (m_settings->m_displayNavAids != ui->navAids->isChecked())
|
||||
{
|
||||
m_settings->m_displayNavAids = ui->navAids->isChecked();
|
||||
@ -180,10 +184,10 @@ void ADSBDemodDisplayDialog::accept()
|
||||
m_settings->m_verboseModelMatching = ui->verboseModelMatching->isChecked();
|
||||
m_settingsKeys.append("verboseModelMatching");
|
||||
}
|
||||
if (m_settings->m_airfieldElevation != ui->airfieldElevation->value())
|
||||
if (m_settings->m_favourLivery != ui->favourLivery->isChecked())
|
||||
{
|
||||
m_settings->m_airfieldElevation = ui->airfieldElevation->value();
|
||||
m_settingsKeys.append("airfieldElevation");
|
||||
m_settings->m_favourLivery = ui->favourLivery->isChecked();
|
||||
m_settingsKeys.append("favourLivery");
|
||||
}
|
||||
if (m_settings->m_transitionAlt != ui->transitionAltitude->value())
|
||||
{
|
||||
@ -200,6 +204,12 @@ void ADSBDemodDisplayDialog::accept()
|
||||
m_settings->m_tableFontSize = m_fontSize;
|
||||
m_settingsKeys.append("tableFontSize");
|
||||
}
|
||||
if (m_settings->m_flightPathPaletteName != ui->flightPathPalette->currentText())
|
||||
{
|
||||
m_settings->m_flightPathPaletteName = ui->flightPathPalette->currentText();
|
||||
m_settingsKeys.append("flightPathPaletteName");
|
||||
m_settings->applyPalette();
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>417</width>
|
||||
<height>467</height>
|
||||
<height>505</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
@ -124,22 +124,16 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="displayStatsLabel">
|
||||
<widget class="QLabel" name="verboseModelMatchingLabel">
|
||||
<property name="text">
|
||||
<string>Display demodulator statistics</string>
|
||||
<string>Log 3D model matching information</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="displayStats">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<widget class="QCheckBox" name="verboseModelMatching">
|
||||
<property name="toolTip">
|
||||
<string>Display demodulator statistics</string>
|
||||
<string>Log information about how aircraft are matched to 3D models</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
@ -147,16 +141,16 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="verboseModelMatchingLabel">
|
||||
<widget class="QLabel" name="favourLiveryLabel">
|
||||
<property name="text">
|
||||
<string>Log 3D model matching information</string>
|
||||
<string>Favour airline livery over aircraft type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="verboseModelMatching">
|
||||
<widget class="QCheckBox" name="favourLivery">
|
||||
<property name="toolTip">
|
||||
<string>Log information about how aircraft are matched to 3D models</string>
|
||||
<string>Favour airline livery over aircraft type for 3D models</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
@ -192,36 +186,13 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="airfieldElevationLabel">
|
||||
<property name="text">
|
||||
<string>Airfield barometric altitude (ft)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QSpinBox" name="airfieldElevation">
|
||||
<property name="toolTip">
|
||||
<string>Barometric altitude reported by aircraft when on airfield surface</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-10000</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>30000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="transitionAltitudeLabel">
|
||||
<property name="text">
|
||||
<string>Transition altitude (ft)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QSpinBox" name="transitionAltitude">
|
||||
<property name="toolTip">
|
||||
<string>Transition altitude in feet</string>
|
||||
@ -232,6 +203,9 @@
|
||||
<property name="maximum">
|
||||
<number>20000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>6000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -303,14 +277,14 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="aircraftMinZoomLabel">
|
||||
<property name="text">
|
||||
<string>Zoom level for aircraft scaling</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="aircraftMinZoom">
|
||||
<property name="toolTip">
|
||||
<string>When map zoom (0 min zoom - 15 max zoom) is higher than this value, aircraft icon size will be scaled</string>
|
||||
@ -320,14 +294,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="airportSizeLabel">
|
||||
<property name="text">
|
||||
<string>Display airports with size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="airportSize">
|
||||
<property name="toolTip">
|
||||
<string>Sets the minimum airport size that will be displayed on the map</string>
|
||||
@ -349,14 +323,14 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="heliportsLabel">
|
||||
<property name="text">
|
||||
<string>Display heliports</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="heliports">
|
||||
<property name="toolTip">
|
||||
<string>When checked, heliports are displayed on the map</string>
|
||||
@ -366,14 +340,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="airportRangeLabel">
|
||||
<property name="text">
|
||||
<string>Airport display distance (km)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QSpinBox" name="airportRange">
|
||||
<property name="toolTip">
|
||||
<string>Displays airports within the specified distance in kilometres from My Position</string>
|
||||
@ -383,14 +357,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="airspacesLabel">
|
||||
<property name="text">
|
||||
<string>Airspaces to display</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="7" column="1">
|
||||
<widget class="QListWidget" name="airspaces">
|
||||
<property name="toolTip">
|
||||
<string>Airspace categories to display</string>
|
||||
@ -547,14 +521,14 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="airspaceRangeLabel">
|
||||
<property name="text">
|
||||
<string>Airspace display distance (km)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<item row="8" column="1">
|
||||
<widget class="QSpinBox" name="airspaceRange">
|
||||
<property name="toolTip">
|
||||
<string>Displays airspace within the specified distance in kilometres from My Position</string>
|
||||
@ -564,14 +538,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="displayNavAids">
|
||||
<property name="text">
|
||||
<string>Display NAVAIDs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="navAids">
|
||||
<property name="toolTip">
|
||||
<string>Display NAVAIDs such as VORs and NDBs</string>
|
||||
@ -581,7 +555,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<item row="10" column="1">
|
||||
<widget class="QCheckBox" name="atcCallsigns">
|
||||
<property name="toolTip">
|
||||
<string>Use ATC callsigns (SPEEDBIRD) rather than ICAO (BAW) for aircraft labels on map</string>
|
||||
@ -591,13 +565,44 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="atcCallsignsLabel">
|
||||
<property name="text">
|
||||
<string>Use ATC callsigns on map</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<widget class="QComboBox" name="flightPathPalette">
|
||||
<property name="toolTip">
|
||||
<string>Colour palette to use for aircraft flight paths</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="flightPathPaletteLabel">
|
||||
<property name="text">
|
||||
<string>Flight path palette</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="toolTip">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Maptiler API key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="maptilerAPIKey">
|
||||
<property name="toolTip">
|
||||
<string>API key for Maptiler (https://maptiler.com) for satellite map</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@ -607,10 +612,10 @@
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <QTextToSpeech>
|
||||
#include <QRandomGenerator>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QtCharts>
|
||||
|
||||
#include "channel/channelgui.h"
|
||||
#include "dsp/dsptypes.h"
|
||||
@ -56,11 +57,17 @@ class WebAPIAdapterInterface;
|
||||
class HttpDownloadManager;
|
||||
class ADSBDemodGUI;
|
||||
class ADSBOSMTemplateServer;
|
||||
class CheckList;
|
||||
class AircraftModel;
|
||||
|
||||
namespace Ui {
|
||||
class ADSBDemodGUI;
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
using namespace QtCharts;
|
||||
#endif
|
||||
|
||||
// Custom widget to allow formatted decimal numbers to be sorted numerically
|
||||
class CustomDoubleTableWidgetItem : public QTableWidgetItem
|
||||
{
|
||||
@ -89,12 +96,16 @@ struct Aircraft {
|
||||
QString m_icaoHex;
|
||||
QString m_callsign; // Flight callsign
|
||||
QString m_flight; // Guess at flight number
|
||||
bool m_globalPosition; // Position has been determined from global decode
|
||||
Real m_latitude; // Latitude in decimal degrees
|
||||
Real m_longitude; // Longitude in decimal degrees
|
||||
int m_altitude; // Altitude in feet
|
||||
float m_radius; // Horizontal containment radius limit (Rc) in metres
|
||||
int m_altitude; // Altitude in feet (will be 0 if on surface)
|
||||
int m_pressureAltitude; // Pressure altitude in feet for Map PFD altimeter (can be negative on surface)
|
||||
bool m_onSurface; // Indicates if on surface or airborne
|
||||
bool m_altitudeGNSS; // Altitude is GNSS HAE (Height above WGS-84 ellipsoid) rather than barometric alitute (relative to 29.92 Hg)
|
||||
float m_heading; // Heading or track in degrees
|
||||
float m_heading; // Heading in degrees magnetic
|
||||
float m_track; // Track in degrees true?
|
||||
int m_verticalRate; // Vertical climb rate in ft/min
|
||||
QString m_emitterCategory; // Aircraft type
|
||||
QString m_status; // Aircraft status
|
||||
@ -102,23 +113,37 @@ struct Aircraft {
|
||||
Real m_range; // Distance from station to aircraft
|
||||
Real m_azimuth; // Azimuth from station to aircraft
|
||||
Real m_elevation; // Elevation from station to aircraft
|
||||
QDateTime m_time; // When last updated
|
||||
QDateTime m_rxTime; // When last frame received (can be long ago if reading from log file)
|
||||
QDateTime m_updateTime; // Last time we updated data for this aircraft (used for determining when to remove an aircraft)
|
||||
|
||||
int m_selAltitude; // Selected altitude in MCP/FCU or FMS in feet
|
||||
int m_selHeading; // Selected heading in MCP/FCU in degrees
|
||||
int m_baro; // Aircraft baro setting in mb (Mode-S)
|
||||
float m_baro; // Aircraft baro setting in mb (Mode-S)
|
||||
float m_roll; // In degrees
|
||||
int m_groundspeed; // In knots
|
||||
float m_turnRate; // In degrees per second
|
||||
int m_trueAirspeed; // In knots
|
||||
int m_indicatedAirspeed; // In knots
|
||||
float m_mach; // Mach number
|
||||
bool m_autopilot;
|
||||
bool m_vnavMode;
|
||||
bool m_altHoldMode;
|
||||
bool m_approachMode;
|
||||
bool m_lnavMode;
|
||||
bool m_tcasOperational; // Appears only to be true if TA/RA, false if TA ONLY
|
||||
|
||||
bool m_bdsCapabilities[16][16]; // BDS capabilities are indicaited by BDS 1.7
|
||||
int m_adsbVersion;
|
||||
bool m_nicSupplementA;
|
||||
bool m_nicSupplementB;
|
||||
bool m_nicSupplementC;
|
||||
|
||||
bool m_positionValid; // Indicates if we have valid data for the above fields
|
||||
bool m_altitudeValid;
|
||||
bool m_pressureAltitudeValid;
|
||||
bool m_onSurfaceValid;
|
||||
bool m_headingValid;
|
||||
bool m_trackValid;
|
||||
bool m_verticalRateValid;
|
||||
bool m_selAltitudeValid;
|
||||
bool m_selHeadingValid;
|
||||
@ -129,16 +154,30 @@ struct Aircraft {
|
||||
bool m_trueAirspeedValid;
|
||||
bool m_indicatedAirspeedValid;
|
||||
bool m_machValid;
|
||||
bool m_autopilotValid;
|
||||
bool m_vnavModeValid;
|
||||
bool m_altHoldModeValid;
|
||||
bool m_approachModeValid;
|
||||
bool m_lnavModeValid;
|
||||
bool m_tcasOperationalValid;
|
||||
|
||||
bool m_bdsCapabilitiesValid;
|
||||
bool m_adsbVersionValid;
|
||||
bool m_nicSupplementAValid;
|
||||
bool m_nicSupplementBValid;
|
||||
bool m_nicSupplementCValid;
|
||||
|
||||
// State for calculating position using two CPR frames
|
||||
bool m_cprValid[2];
|
||||
Real m_cprLat[2];
|
||||
Real m_cprLong[2];
|
||||
double m_cprLat[2];
|
||||
double m_cprLong[2];
|
||||
QDateTime m_cprTime[2];
|
||||
|
||||
int m_adsbFrameCount; // Number of ADS-B frames for this aircraft
|
||||
int m_modesFrameCount; // Number of Mode-S frames for this aircraft
|
||||
int m_nonTransponderFrameCount;
|
||||
int m_tisBFrameCount;
|
||||
int m_adsrFrameCount;
|
||||
float m_minCorrelation;
|
||||
float m_maxCorrelation;
|
||||
float m_correlation;
|
||||
@ -148,8 +187,12 @@ struct Aircraft {
|
||||
bool m_isHighlighted; // Are we highlighting this aircraft in the table and map
|
||||
bool m_showAll;
|
||||
|
||||
QVariantList m_coordinates; // Coordinates we've recorded the aircraft at
|
||||
QList<QVariantList> m_coordinates; // Coordinates we've recorded the aircraft at, split up in to altitude ranges
|
||||
QList<QVariantList> m_recentCoordinates; // Last 20 seconds of coordinates for ATC mode
|
||||
QList<QDateTime> m_coordinateDateTimes;
|
||||
QList<int> m_coordinateColors; // 0-7 index to 8 color palette according to altitude
|
||||
QList<int> m_recentCoordinateColors;
|
||||
int m_lastColor;
|
||||
|
||||
AircraftInformation *m_aircraftInfo; // Info about the aircraft from the database
|
||||
QString m_aircraft3DModel; // 3D model for map based on aircraft type
|
||||
@ -159,6 +202,7 @@ struct Aircraft {
|
||||
ADSBDemodGUI *m_gui;
|
||||
QString m_flagIconURL;
|
||||
QString m_airlineIconURL;
|
||||
QString m_sideviewIconURL;
|
||||
|
||||
// For animation on 3D map
|
||||
float m_runwayAltitude;
|
||||
@ -168,10 +212,14 @@ struct Aircraft {
|
||||
bool m_rotorStarted; // Rotors started on 'Rotorcraft'
|
||||
bool m_engineStarted; // Engines started (typically propellors)
|
||||
QDateTime m_positionDateTime;
|
||||
QDateTime m_orientationDateTime;
|
||||
QDateTime m_orientationDateTime; // FIXME
|
||||
QDateTime m_headingDateTime;
|
||||
QDateTime m_prevHeadingDateTime;
|
||||
int m_prevHeading;
|
||||
QDateTime m_trackDateTime;
|
||||
QDateTime m_altitudeDateTime;
|
||||
QDateTime m_indicatedAirspeedDateTime;
|
||||
QDateTime m_prevTrackDateTime;
|
||||
int m_prevTrack;
|
||||
float m_trackWhenHeadingSet;
|
||||
float m_pitchEst; // Estimated pitch based on vertical rate
|
||||
float m_rollEst; // Estimated roll based on rate of change in heading
|
||||
|
||||
@ -182,30 +230,48 @@ struct Aircraft {
|
||||
QTableWidgetItem *m_callsignItem;
|
||||
QTableWidgetItem* m_atcCallsignItem;
|
||||
QTableWidgetItem *m_modelItem;
|
||||
QTableWidgetItem *m_typeItem;
|
||||
QTableWidgetItem *m_sideviewItem;
|
||||
QTableWidgetItem *m_airlineItem;
|
||||
QTableWidgetItem *m_latitudeItem;
|
||||
QTableWidgetItem *m_longitudeItem;
|
||||
QTableWidgetItem *m_altitudeItem;
|
||||
QTableWidgetItem *m_headingItem;
|
||||
QTableWidgetItem *m_trackItem;
|
||||
QTableWidgetItem *m_verticalRateItem;
|
||||
CustomDoubleTableWidgetItem *m_rangeItem;
|
||||
QTableWidgetItem *m_azElItem;
|
||||
QTableWidgetItem *m_emitterCategoryItem;
|
||||
QTableWidgetItem *m_statusItem;
|
||||
QTableWidgetItem *m_squawkItem;
|
||||
QTableWidgetItem *m_identItem;
|
||||
QTableWidgetItem *m_registrationItem;
|
||||
QTableWidgetItem *m_countryItem;
|
||||
QTableWidgetItem *m_registeredItem;
|
||||
QTableWidgetItem *m_manufacturerNameItem;
|
||||
QTableWidgetItem *m_ownerItem;
|
||||
QTableWidgetItem *m_operatorICAOItem;
|
||||
QTableWidgetItem *m_interogatorCodeItem;
|
||||
QTableWidgetItem *m_timeItem;
|
||||
QTableWidgetItem *m_totalFrameCountItem;
|
||||
QTableWidgetItem *m_adsbFrameCountItem;
|
||||
QTableWidgetItem *m_modesFrameCountItem;
|
||||
QTableWidgetItem *m_nonTransponderItem;
|
||||
QTableWidgetItem *m_tisBFrameCountItem;
|
||||
QTableWidgetItem *m_adsrFrameCountItem;
|
||||
QTableWidgetItem *m_radiusItem;
|
||||
QTableWidgetItem *m_nacpItem;
|
||||
QTableWidgetItem *m_nacvItem;
|
||||
QTableWidgetItem *m_gvaItem;
|
||||
QTableWidgetItem *m_nicItem;
|
||||
QTableWidgetItem *m_nicBaroItem;
|
||||
QTableWidgetItem *m_silItem;
|
||||
QTableWidgetItem *m_correlationItem;
|
||||
QTableWidgetItem *m_rssiItem;
|
||||
QTableWidgetItem *m_flightStatusItem;
|
||||
QTableWidgetItem *m_depItem;
|
||||
QTableWidgetItem *m_arrItem;
|
||||
QTableWidgetItem *m_stopsItem;
|
||||
QTableWidgetItem *m_stdItem;
|
||||
QTableWidgetItem *m_etdItem;
|
||||
QTableWidgetItem *m_atdItem;
|
||||
@ -218,6 +284,13 @@ struct Aircraft {
|
||||
QTableWidgetItem *m_apItem;
|
||||
QTableWidgetItem *m_vModeItem;
|
||||
QTableWidgetItem *m_lModeItem;
|
||||
QTableWidgetItem *m_tcasItem;
|
||||
QTableWidgetItem *m_acasItem;
|
||||
QTableWidgetItem *m_raItem;
|
||||
QTableWidgetItem *m_maxSpeedItem;
|
||||
QTableWidgetItem *m_versionItem;
|
||||
QTableWidgetItem *m_lengthItem;
|
||||
QTableWidgetItem *m_widthItem;
|
||||
QTableWidgetItem *m_rollItem;
|
||||
QTableWidgetItem *m_groundspeedItem;
|
||||
QTableWidgetItem *m_turnRateItem;
|
||||
@ -231,16 +304,18 @@ struct Aircraft {
|
||||
QTableWidgetItem *m_staticPressureItem;
|
||||
QTableWidgetItem *m_staticAirTempItem;
|
||||
QTableWidgetItem *m_humidityItem;
|
||||
QTableWidgetItem *m_tisBItem;
|
||||
|
||||
Aircraft(ADSBDemodGUI *gui) :
|
||||
m_icao(0),
|
||||
m_globalPosition(false),
|
||||
m_latitude(0),
|
||||
m_longitude(0),
|
||||
m_radius(0.0f),
|
||||
m_altitude(0),
|
||||
m_onSurface(false),
|
||||
m_altitudeGNSS(false),
|
||||
m_heading(0),
|
||||
m_track(0),
|
||||
m_verticalRate(0),
|
||||
m_azimuth(0),
|
||||
m_elevation(0),
|
||||
@ -253,9 +328,22 @@ struct Aircraft {
|
||||
m_trueAirspeed(0),
|
||||
m_indicatedAirspeed(0),
|
||||
m_mach(0.0f),
|
||||
m_autopilot(false),
|
||||
m_vnavMode(false),
|
||||
m_altHoldMode(false),
|
||||
m_approachMode(false),
|
||||
m_lnavMode(false),
|
||||
m_tcasOperational(false),
|
||||
m_adsbVersion(0),
|
||||
m_nicSupplementA(false),
|
||||
m_nicSupplementB(false),
|
||||
m_nicSupplementC(false),
|
||||
m_positionValid(false),
|
||||
m_altitudeValid(false),
|
||||
m_pressureAltitudeValid(false),
|
||||
m_onSurfaceValid(false),
|
||||
m_headingValid(false),
|
||||
m_trackValid(false),
|
||||
m_verticalRateValid(false),
|
||||
m_selAltitudeValid(false),
|
||||
m_selHeadingValid(false),
|
||||
@ -266,9 +354,22 @@ struct Aircraft {
|
||||
m_trueAirspeedValid(false),
|
||||
m_indicatedAirspeedValid(false),
|
||||
m_machValid(false),
|
||||
m_autopilotValid(false),
|
||||
m_vnavModeValid(false),
|
||||
m_altHoldModeValid(false),
|
||||
m_approachModeValid(false),
|
||||
m_lnavModeValid(false),
|
||||
m_tcasOperationalValid(false),
|
||||
m_bdsCapabilitiesValid(false),
|
||||
m_adsbVersionValid(false),
|
||||
m_nicSupplementAValid(false),
|
||||
m_nicSupplementBValid(false),
|
||||
m_nicSupplementCValid(false),
|
||||
m_adsbFrameCount(0),
|
||||
m_modesFrameCount(0),
|
||||
m_nonTransponderFrameCount(0),
|
||||
m_tisBFrameCount(0),
|
||||
m_adsrFrameCount(0),
|
||||
m_minCorrelation(INFINITY),
|
||||
m_maxCorrelation(-INFINITY),
|
||||
m_correlation(0.0f),
|
||||
@ -302,9 +403,12 @@ struct Aircraft {
|
||||
m_callsignItem = new QTableWidgetItem();
|
||||
m_atcCallsignItem = new QTableWidgetItem();
|
||||
m_modelItem = new QTableWidgetItem();
|
||||
m_typeItem = new QTableWidgetItem();
|
||||
m_sideviewItem = new QTableWidgetItem();
|
||||
m_airlineItem = new QTableWidgetItem();
|
||||
m_altitudeItem = new QTableWidgetItem();
|
||||
m_headingItem = new QTableWidgetItem();
|
||||
m_trackItem = new QTableWidgetItem();
|
||||
m_verticalRateItem = new QTableWidgetItem();
|
||||
m_rangeItem = new CustomDoubleTableWidgetItem();
|
||||
m_azElItem = new QTableWidgetItem();
|
||||
@ -313,19 +417,34 @@ struct Aircraft {
|
||||
m_emitterCategoryItem = new QTableWidgetItem();
|
||||
m_statusItem = new QTableWidgetItem();
|
||||
m_squawkItem = new QTableWidgetItem();
|
||||
m_identItem = new QTableWidgetItem();
|
||||
m_registrationItem = new QTableWidgetItem();
|
||||
m_countryItem = new QTableWidgetItem();
|
||||
m_registeredItem = new QTableWidgetItem();
|
||||
m_manufacturerNameItem = new QTableWidgetItem();
|
||||
m_ownerItem = new QTableWidgetItem();
|
||||
m_operatorICAOItem = new QTableWidgetItem();
|
||||
m_interogatorCodeItem = new QTableWidgetItem();
|
||||
m_timeItem = new QTableWidgetItem();
|
||||
m_totalFrameCountItem = new QTableWidgetItem();
|
||||
m_adsbFrameCountItem = new QTableWidgetItem();
|
||||
m_modesFrameCountItem = new QTableWidgetItem();
|
||||
m_nonTransponderItem = new QTableWidgetItem();
|
||||
m_tisBFrameCountItem = new QTableWidgetItem();
|
||||
m_adsrFrameCountItem = new QTableWidgetItem();
|
||||
m_radiusItem = new QTableWidgetItem();
|
||||
m_nacpItem = new QTableWidgetItem();
|
||||
m_nacvItem = new QTableWidgetItem();
|
||||
m_gvaItem = new QTableWidgetItem();
|
||||
m_nicItem = new QTableWidgetItem();
|
||||
m_nicBaroItem = new QTableWidgetItem();
|
||||
m_silItem = new QTableWidgetItem();
|
||||
m_correlationItem = new QTableWidgetItem();
|
||||
m_rssiItem = new QTableWidgetItem();
|
||||
m_flightStatusItem = new QTableWidgetItem();
|
||||
m_depItem = new QTableWidgetItem();
|
||||
m_arrItem = new QTableWidgetItem();
|
||||
m_stopsItem = new QTableWidgetItem();
|
||||
m_stdItem = new QTableWidgetItem();
|
||||
m_etdItem = new QTableWidgetItem();
|
||||
m_atdItem = new QTableWidgetItem();
|
||||
@ -338,6 +457,13 @@ struct Aircraft {
|
||||
m_apItem = new QTableWidgetItem();
|
||||
m_vModeItem = new QTableWidgetItem();
|
||||
m_lModeItem = new QTableWidgetItem();
|
||||
m_tcasItem = new QTableWidgetItem();
|
||||
m_acasItem = new QTableWidgetItem();
|
||||
m_raItem = new QTableWidgetItem();
|
||||
m_maxSpeedItem = new QTableWidgetItem();
|
||||
m_versionItem = new QTableWidgetItem();
|
||||
m_lengthItem = new QTableWidgetItem();
|
||||
m_widthItem = new QTableWidgetItem();
|
||||
m_rollItem = new QTableWidgetItem();
|
||||
m_groundspeedItem = new QTableWidgetItem();
|
||||
m_turnRateItem = new QTableWidgetItem();
|
||||
@ -351,16 +477,15 @@ struct Aircraft {
|
||||
m_staticPressureItem = new QTableWidgetItem();
|
||||
m_staticAirTempItem = new QTableWidgetItem();
|
||||
m_humidityItem = new QTableWidgetItem();
|
||||
m_tisBItem = new QTableWidgetItem();
|
||||
}
|
||||
|
||||
QString getImage() const;
|
||||
QString getText(const ADSBDemodSettings *settings, bool all=false) const;
|
||||
// Label to use for aircraft on map
|
||||
QString getLabel(const ADSBDemodSettings *settings) const;
|
||||
QString getLabel(const ADSBDemodSettings *settings, QDateTime& dateTime) const;
|
||||
|
||||
// Name to use when selected as a target (E.g. for use as target name in Rotator Controller)
|
||||
QString targetName()
|
||||
QString targetName() const
|
||||
{
|
||||
if (!m_callsign.isEmpty())
|
||||
return QString("Callsign: %1").arg(m_callsign);
|
||||
@ -368,6 +493,71 @@ struct Aircraft {
|
||||
return QString("ICAO: %1").arg(m_icao, 0, 16);
|
||||
}
|
||||
|
||||
void setOnSurface(const QDateTime& dateTime);
|
||||
void setAltitude(int altitudeFt, bool gnss, const QDateTime& dateTime, const ADSBDemodSettings& settings);
|
||||
void setVerticalRate(int verticalRate, const ADSBDemodSettings& settings);
|
||||
void setGroundspeed(float groundspeed, const ADSBDemodSettings& settings);
|
||||
void setTrueAirspeed(int airspeed, const ADSBDemodSettings& settings);
|
||||
void setIndicatedAirspeed(int airspeed, const QDateTime& dateTime, const ADSBDemodSettings& settings);
|
||||
void setTrack(float track, const QDateTime& dateTime);
|
||||
void setHeading(float heading, const QDateTime& dateTime);
|
||||
void addCoordinate(const QDateTime& dateTime, AircraftModel *model);
|
||||
void clearCoordinates(AircraftModel *model);
|
||||
};
|
||||
|
||||
class AircraftPathModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using QAbstractListModel::QAbstractListModel;
|
||||
enum MarkerRoles{
|
||||
coordinatesRole = Qt::UserRole + 1,
|
||||
colorRole = Qt::UserRole + 2,
|
||||
};
|
||||
|
||||
AircraftPathModel(AircraftModel *aircraftModel, Aircraft *aircraft) :
|
||||
m_aircraftModel(aircraftModel),
|
||||
m_aircraft(aircraft),
|
||||
m_paths(0),
|
||||
m_showFullPath(false),
|
||||
m_showATCPath(false)
|
||||
{
|
||||
settingsUpdated();
|
||||
}
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
|
||||
(void) parent;
|
||||
return m_paths;
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
void add();
|
||||
void updateLast();
|
||||
void removed();
|
||||
void clear();
|
||||
void settingsUpdated();
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override
|
||||
{
|
||||
(void) index;
|
||||
return Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> roleNames() const {
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[coordinatesRole] = "coordinates";
|
||||
roles[colorRole] = "color";
|
||||
return roles;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
AircraftModel *m_aircraftModel;
|
||||
Aircraft *m_aircraft;
|
||||
int m_paths; // Should match m_aircraft->m_coordinates.count()
|
||||
bool m_showFullPath;
|
||||
bool m_showATCPath;
|
||||
};
|
||||
|
||||
// Aircraft data model used by QML map item
|
||||
@ -385,12 +575,16 @@ public:
|
||||
aircraftPathRole = Qt::UserRole + 6,
|
||||
showAllRole = Qt::UserRole + 7,
|
||||
highlightedRole = Qt::UserRole + 8,
|
||||
targetRole = Qt::UserRole + 9
|
||||
targetRole = Qt::UserRole + 9,
|
||||
radiusRole = Qt::UserRole + 10,
|
||||
showRadiusRole = Qt::UserRole + 11,
|
||||
aircraftPathModelRole = Qt::UserRole + 12,
|
||||
};
|
||||
|
||||
Q_INVOKABLE void addAircraft(Aircraft *aircraft) {
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_aircrafts.append(aircraft);
|
||||
m_pathModels.append(new AircraftPathModel(this, aircraft));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
@ -409,7 +603,8 @@ public:
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
void aircraftUpdated(Aircraft *aircraft) {
|
||||
void aircraftUpdated(Aircraft *aircraft)
|
||||
{
|
||||
int row = m_aircrafts.indexOf(aircraft);
|
||||
if (row >= 0)
|
||||
{
|
||||
@ -418,30 +613,72 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void allAircraftUpdated() {
|
||||
/*
|
||||
// Not sure why this doesn't work - it should be more efficient
|
||||
// than the following code
|
||||
emit dataChanged(index(0), index(rowCount()));
|
||||
*/
|
||||
for (int i = 0; i < m_aircrafts.count(); i++)
|
||||
void highlightChanged(Aircraft *aircraft)
|
||||
{
|
||||
int row = m_aircrafts.indexOf(aircraft);
|
||||
if (row >= 0)
|
||||
{
|
||||
QModelIndex idx = index(i);
|
||||
m_pathModels[row]->settingsUpdated();
|
||||
QModelIndex idx = index(row);
|
||||
emit dataChanged(idx, idx);
|
||||
}
|
||||
}
|
||||
|
||||
void removeAircraft(Aircraft *aircraft) {
|
||||
void clearCoords(Aircraft *aircraft)
|
||||
{
|
||||
int row = m_aircrafts.indexOf(aircraft);
|
||||
if (row >= 0) {
|
||||
m_pathModels[row]->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void aircraftCoordsUpdated(Aircraft *aircraft)
|
||||
{
|
||||
int row = m_aircrafts.indexOf(aircraft);
|
||||
if (row >= 0) {
|
||||
m_pathModels[row]->updateLast();
|
||||
}
|
||||
}
|
||||
|
||||
void aircraftCoordsAdded(Aircraft *aircraft)
|
||||
{
|
||||
int row = m_aircrafts.indexOf(aircraft);
|
||||
if (row >= 0) {
|
||||
m_pathModels[row]->add();
|
||||
}
|
||||
}
|
||||
|
||||
void aircraftCoordsRemoved(Aircraft *aircraft)
|
||||
{
|
||||
int row = m_aircrafts.indexOf(aircraft);
|
||||
if (row >= 0) {
|
||||
m_pathModels[row]->removed();
|
||||
}
|
||||
}
|
||||
|
||||
void allAircraftUpdated()
|
||||
{
|
||||
emit dataChanged(index(0), index(rowCount()-1));
|
||||
|
||||
for (int i = 0; i < m_aircrafts.count(); i++) {
|
||||
m_pathModels[i]->settingsUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void removeAircraft(Aircraft *aircraft)
|
||||
{
|
||||
int row = m_aircrafts.indexOf(aircraft);
|
||||
if (row >= 0)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
m_aircrafts.removeAt(row);
|
||||
delete m_pathModels.takeAt(row);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> roleNames() const {
|
||||
QHash<int, QByteArray> roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[positionRole] = "position";
|
||||
roles[headingRole] = "heading";
|
||||
@ -452,22 +689,12 @@ public:
|
||||
roles[showAllRole] = "showAll";
|
||||
roles[highlightedRole] = "highlighted";
|
||||
roles[targetRole] = "target";
|
||||
roles[radiusRole] = "containmentRadius";
|
||||
roles[aircraftPathModelRole] = "aircraftPathModel";
|
||||
return roles;
|
||||
}
|
||||
|
||||
void setFlightPaths(bool flightPaths)
|
||||
{
|
||||
m_flightPaths = flightPaths;
|
||||
allAircraftUpdated();
|
||||
}
|
||||
|
||||
void setAllFlightPaths(bool allFlightPaths)
|
||||
{
|
||||
m_allFlightPaths = allFlightPaths;
|
||||
allAircraftUpdated();
|
||||
}
|
||||
|
||||
void setSettings(const ADSBDemodSettings *settings)
|
||||
void setSettings(const ADSBDemodSettings *settings)
|
||||
{
|
||||
m_settings = settings;
|
||||
allAircraftUpdated();
|
||||
@ -487,11 +714,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const ADSBDemodSettings *m_settings;
|
||||
|
||||
private:
|
||||
QList<Aircraft *> m_aircrafts;
|
||||
bool m_flightPaths;
|
||||
bool m_allFlightPaths;
|
||||
const ADSBDemodSettings *m_settings;
|
||||
QList<AircraftPathModel *> m_pathModels;
|
||||
};
|
||||
|
||||
// Airport data model used by QML map item
|
||||
@ -667,26 +894,34 @@ public:
|
||||
airspacePolygonRole = Qt::UserRole + 6
|
||||
};
|
||||
|
||||
Q_INVOKABLE void addAirspace(Airspace *airspace) {
|
||||
Q_INVOKABLE void addAirspace(Airspace *airspace)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_airspaces.append(airspace);
|
||||
// Convert QPointF to QVariantList of QGeoCoordinates
|
||||
QVariantList polygon;
|
||||
for (const auto p : airspace->m_polygon)
|
||||
{
|
||||
QGeoCoordinate coord(p.y(), p.x(), airspace->topHeightInMetres());
|
||||
polygon.push_back(QVariant::fromValue(coord));
|
||||
}
|
||||
m_polygons.append(polygon);
|
||||
updatePolygon(airspace, -1);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_airspaces.count();
|
||||
}
|
||||
|
||||
void removeAllAirspaces() {
|
||||
void removeAirspace(Airspace *airspace)
|
||||
{
|
||||
int idx = m_airspaces.indexOf(airspace);
|
||||
if (idx >= 0)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), idx, idx);
|
||||
m_airspaces.removeAt(idx);
|
||||
m_polygons.removeAt(idx);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
void removeAllAirspaces()
|
||||
{
|
||||
if (m_airspaces.count() > 0)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), 0, m_airspaces.count() - 1);
|
||||
@ -696,6 +931,23 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void airspaceUpdated(const Airspace *airspace)
|
||||
{
|
||||
int row = m_airspaces.indexOf(airspace);
|
||||
if (row >= 0)
|
||||
{
|
||||
updatePolygon(airspace, row);
|
||||
|
||||
QModelIndex idx = index(row);
|
||||
emit dataChanged(idx, idx);
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(const Airspace *airspace)
|
||||
{
|
||||
return m_airspaces.contains(airspace);
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
@ -706,7 +958,8 @@ public:
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> roleNames() const {
|
||||
QHash<int, QByteArray> roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[nameRole] = "name";
|
||||
roles[detailsRole] = "details";
|
||||
@ -718,8 +971,25 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
QList<Airspace *> m_airspaces;
|
||||
QList<const Airspace *> m_airspaces;
|
||||
QList<QVariantList> m_polygons;
|
||||
|
||||
void updatePolygon(const Airspace *airspace, int row)
|
||||
{
|
||||
// Convert QPointF to QVariantList of QGeoCoordinates
|
||||
QVariantList polygon;
|
||||
for (const auto p : airspace->m_polygon)
|
||||
{
|
||||
QGeoCoordinate coord(p.y(), p.x(), airspace->topHeightInMetres());
|
||||
polygon.push_back(QVariant::fromValue(coord));
|
||||
}
|
||||
if (row == -1) {
|
||||
m_polygons.append(polygon);
|
||||
} else {
|
||||
m_polygons.replace(row, polygon);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// NavAid model used for each NavAid on the map
|
||||
@ -867,6 +1137,23 @@ protected:
|
||||
class ADSBDemodGUI : public ChannelGUI {
|
||||
Q_OBJECT
|
||||
|
||||
struct Interogator {
|
||||
Real m_minLatitude;
|
||||
Real m_maxLatitude;
|
||||
Real m_minLongitude;
|
||||
Real m_maxLongitude;
|
||||
bool m_valid;
|
||||
Airspace m_airspace;
|
||||
|
||||
Interogator() :
|
||||
m_valid(false)
|
||||
{
|
||||
}
|
||||
|
||||
void update(int ic, Aircraft *aircraft, AirspaceModel *airspaceModel, CheckList *checkList, bool display);
|
||||
void calcPoly();
|
||||
};
|
||||
|
||||
public:
|
||||
static ADSBDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
|
||||
virtual void destroy();
|
||||
@ -923,6 +1210,7 @@ private:
|
||||
|
||||
QHash<int, Aircraft *> m_aircraft; // Hashed on ICAO
|
||||
QSharedPointer<const QHash<int, AircraftInformation *>> m_aircraftInfo;
|
||||
QSharedPointer<const QHash<QString, AircraftRouteInformation *>> m_routeInfo; // Hashed on callsign
|
||||
QSharedPointer<const QHash<int, AirportInformation *>> m_airportInfo; // Hashed on id
|
||||
AircraftModel m_aircraftModel;
|
||||
AirportModel m_airportModel;
|
||||
@ -936,12 +1224,85 @@ private:
|
||||
Aircraft *m_trackAircraft; // Aircraft we want to track in Channel Report
|
||||
MovingAverageUtil<float, double, 10> m_correlationAvg;
|
||||
MovingAverageUtil<float, double, 10> m_correlationOnesAvg;
|
||||
MovingAverageUtil<float, double, 100> m_qnhAvg;
|
||||
|
||||
Aircraft *m_highlightAircraft; // Aircraft we want to highlight, when selected in table
|
||||
|
||||
float m_currentAirportRange; // Current settings, so we only update if changed
|
||||
ADSBDemodSettings::AirportType m_currentAirportMinimumSize;
|
||||
bool m_currentDisplayHeliports;
|
||||
|
||||
static const int m_maxRangeDeg = 5;
|
||||
QList<float> m_maxRange[2];
|
||||
Airspace m_coverageAirspace[2];
|
||||
|
||||
Interogator m_interogators[ADSB_IC_MAX];
|
||||
|
||||
enum StatsRow {
|
||||
ADSB_FRAMES,
|
||||
MODE_S_FRAMES,
|
||||
TOTAL_FRAMES,
|
||||
ADSB_RATE,
|
||||
MODE_S_RATE,
|
||||
TOTAL_RATE,
|
||||
DATA_RATE,
|
||||
CORRELATOR_MATCHES,
|
||||
PERCENT_VALID,
|
||||
PREAMBLE_FAILS,
|
||||
CRC_FAILS,
|
||||
TYPE_FAILS,
|
||||
INVALID_FAILS,
|
||||
ICAO_FAILS,
|
||||
RANGE_FAILS,
|
||||
ALT_FAILS,
|
||||
AVERAGE_CORRELATION,
|
||||
TC_0,
|
||||
TC_1_4,
|
||||
TC_5_8,
|
||||
TC_9_18,
|
||||
TC_19,
|
||||
TC_20_22,
|
||||
TC_24,
|
||||
TC_28,
|
||||
TC_29,
|
||||
TC_31,
|
||||
TC_UNUSED,
|
||||
DF0,
|
||||
DF4,
|
||||
DF5,
|
||||
DF11,
|
||||
DF16,
|
||||
DF17,
|
||||
DF18,
|
||||
DF19,
|
||||
DF20_21,
|
||||
DF22,
|
||||
DF24,
|
||||
MAX_RANGE,
|
||||
MAX_ALTITUDE,
|
||||
MAX_RATE
|
||||
};
|
||||
|
||||
qint64 m_rangeFails;
|
||||
qint64 m_altFails;
|
||||
QDateTime m_frameRateTime;
|
||||
qint64 m_adsbFrameRateCount;
|
||||
qint64 m_modesFrameRateCount;
|
||||
qint64 m_totalBytes;
|
||||
float m_maxRangeStat;
|
||||
float m_maxAltitudeStat;
|
||||
float m_maxRateState;
|
||||
qint64 m_dfStats[32];
|
||||
qint64 m_tcStats[32];
|
||||
QChart *m_chart;
|
||||
QLineSeries *m_adsbFrameRateSeries;
|
||||
QLineSeries *m_modesFrameRateSeries;
|
||||
QLineSeries *m_aircraftSeries;
|
||||
QDateTimeAxis *m_xAxis;
|
||||
QValueAxis *m_fpsYAxis;
|
||||
QValueAxis *m_aircraftYAxis;
|
||||
QDateTime m_averageTime;
|
||||
|
||||
#ifdef QT_TEXTTOSPEECH_FOUND
|
||||
QTextToSpeech *m_speech;
|
||||
#endif
|
||||
@ -977,10 +1338,13 @@ private:
|
||||
static const QString m_flightStatuses[];
|
||||
static const QString m_hazardSeverity[];
|
||||
static const QString m_fomSources[];
|
||||
static const QString m_nacvStrings[];
|
||||
|
||||
explicit ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
||||
virtual ~ADSBDemodGUI();
|
||||
|
||||
QString maptilerAPIKey() const;
|
||||
|
||||
void blockApplySettings(bool block);
|
||||
void applySetting(const QString& settingsKey);
|
||||
void applySettings(const QStringList& settingsKeys, bool force = false);
|
||||
@ -990,8 +1354,6 @@ private:
|
||||
void makeUIConnections();
|
||||
void updateAbsoluteCenterFrequency();
|
||||
|
||||
void updatePosition(Aircraft *aircraft);
|
||||
bool updateLocalPosition(Aircraft *aircraft, double latitude, double longitude, bool surfacePosition);
|
||||
void clearFromMap(const QString& name);
|
||||
void sendToMap(Aircraft *aircraft, QList<SWGSDRangel::SWGMapAnimation *> *animations);
|
||||
Aircraft *getAircraft(int icao, bool &newAircraft);
|
||||
@ -1006,10 +1368,24 @@ private:
|
||||
float correlationOnes,
|
||||
unsigned crc,
|
||||
bool updateModel);
|
||||
void decodeModeS(const QByteArray data, int df, Aircraft *aircraft);
|
||||
|
||||
void decodeID(const QByteArray& data, QString& emitterCategory, QString& callsign);
|
||||
void decodeGroundspeed(const QByteArray& data, float& v, float& h);
|
||||
void decodeAirspeed(const QByteArray& data, bool& tas, int& as, bool& hdgValid, float& hdg);
|
||||
void decodeVerticalRate(const QByteArray& data, int& verticalRate);
|
||||
void updateAircraftPosition(Aircraft *aircraft, double latitude, double longitude, const QDateTime& dateTime);
|
||||
bool validateGlobalPosition(double latitude, double longitude, bool countFailure);
|
||||
bool validateLocalPosition(double latitude, double longitude, bool surfacePosition, bool countFailure);
|
||||
bool decodeGlobalPosition(int f, const double cprLat[2], const double cprLong[2], const QDateTime cprTime[2], double& latitude, double& longitude, bool countFailure);
|
||||
bool decodeLocalPosition(int f, double cprLat, double cprLong, bool onSurface, const Aircraft *aircraft, double& latitude, double& longitude, bool countFailure);
|
||||
void decodeCpr(const QByteArray& data, int& f, double& latCpr, double& lonCpr) const;
|
||||
bool decodeAltitude(const QByteArray& data, int& altFt) const;
|
||||
void decodeModeSAltitude(const QByteArray& data, const QDateTime dateTime, Aircraft *aircraft);
|
||||
void decodeModeS(const QByteArray data, const QDateTime dateTime, int df, Aircraft *aircraft);
|
||||
void decodeCommB(const QByteArray data, const QDateTime dateTime, int df, Aircraft *aircraft, bool &updatedCallsign);
|
||||
QList<SWGSDRangel::SWGMapAnimation *> *animate(QDateTime dateTime, Aircraft *aircraft);
|
||||
SWGSDRangel::SWGMapAnimation *gearAnimation(QDateTime startDateTime, bool up);
|
||||
SWGSDRangel::SWGMapAnimation *gearAngle(QDateTime startDateTime, bool flat);
|
||||
SWGSDRangel::SWGMapAnimation *flapsAnimation(QDateTime startDateTime, float currentFlaps, float flaps);
|
||||
SWGSDRangel::SWGMapAnimation *slatsAnimation(QDateTime startDateTime, bool retract);
|
||||
SWGSDRangel::SWGMapAnimation *rotorAnimation(QDateTime startDateTime, bool stop);
|
||||
@ -1022,18 +1398,18 @@ private:
|
||||
QString subAircraftString(Aircraft *aircraft, const QString &string);
|
||||
void resizeTable();
|
||||
QString getDataDir();
|
||||
void readAirportDB(const QString& filename);
|
||||
void readAirportFrequenciesDB(const QString& filename);
|
||||
void update3DModels();
|
||||
void updateAirports();
|
||||
void updateAirspaces();
|
||||
void updateNavAids();
|
||||
void updateChannelList();
|
||||
void removeAircraft(QHash<int, Aircraft *>::iterator& i, Aircraft *aircraft);
|
||||
QAction *createCheckableItem(QString& text, int idx, bool checked);
|
||||
Aircraft* findAircraftByFlight(const QString& flight);
|
||||
QString dataTimeToShortString(QDateTime dt);
|
||||
void initFlightInformation();
|
||||
void initAviationWeather();
|
||||
void setShowContainmentRadius(bool show);
|
||||
void applyMapSettings();
|
||||
void updatePhotoText(Aircraft *aircraft);
|
||||
void updatePhotoFlightInformation(Aircraft *aircraft);
|
||||
@ -1045,16 +1421,33 @@ private:
|
||||
void applyImportSettings();
|
||||
void sendAircraftReport();
|
||||
void updatePosition(float latitude, float longitude, float altitude);
|
||||
void clearOldHeading(Aircraft *aircraft, const QDateTime& dateTime, float newTrack);
|
||||
void updateQNH(const Aircraft *aircraft, float qnh);
|
||||
void setCallsign(Aircraft *aircraft, const QString& callsign);
|
||||
|
||||
void initCoverageMap();
|
||||
void clearCoverageMap();
|
||||
void updateCoverageMap(float azimuth, float elevation, float distance, float altitude);
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(EnterEventType*);
|
||||
|
||||
void updateDFStats(int df);
|
||||
bool updateTCStats(int tc, int row, int low, int high);
|
||||
void resetStats();
|
||||
void plotChart();
|
||||
int countActiveAircraft();
|
||||
void averageSeries(QLineSeries *series, const QDateTime& startTime, const QDateTime& endTime);
|
||||
void legendMarkerClicked();
|
||||
|
||||
private slots:
|
||||
void on_deltaFrequency_changed(qint64 value);
|
||||
void on_rfBW_valueChanged(int value);
|
||||
void on_threshold_valueChanged(int value);
|
||||
void on_chipsThreshold_valueChanged(int value);
|
||||
void on_phaseSteps_valueChanged(int value);
|
||||
void on_tapsPerPhase_valueChanged(int value);
|
||||
void statsTable_customContextMenuRequested(QPoint pos);
|
||||
void adsbData_customContextMenuRequested(QPoint point);
|
||||
void on_adsbData_cellClicked(int row, int column);
|
||||
void on_adsbData_cellDoubleClicked(int row, int column);
|
||||
@ -1063,18 +1456,24 @@ private slots:
|
||||
void columnSelectMenu(QPoint pos);
|
||||
void columnSelectMenuChecked(bool checked = false);
|
||||
void on_spb_currentIndexChanged(int value);
|
||||
void on_correlateFullPreamble_clicked(bool checked);
|
||||
void on_demodModeS_clicked(bool checked);
|
||||
void on_feed_clicked(bool checked);
|
||||
void on_notifications_clicked();
|
||||
void on_flightInfo_clicked();
|
||||
void on_findOnMapFeature_clicked();
|
||||
void on_getOSNDB_clicked();
|
||||
void on_deleteAircraft_clicked();
|
||||
void on_getAircraftDB_clicked();
|
||||
void on_getAirportDB_clicked();
|
||||
void on_getAirspacesDB_clicked();
|
||||
void on_coverage_clicked(bool checked);
|
||||
void on_displayChart_clicked(bool checked);
|
||||
void on_stats_clicked(bool checked);
|
||||
void on_flightPaths_clicked(bool checked);
|
||||
void on_allFlightPaths_clicked(bool checked);
|
||||
void on_atcLabels_clicked(bool checked);
|
||||
void on_displayOrientation_clicked(bool checked);
|
||||
void on_displayRadius_clicked(bool checked);
|
||||
void on_ic_globalCheckStateChanged(int state);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
void handleInputMessages();
|
||||
@ -1101,6 +1500,12 @@ private slots:
|
||||
void devicePositionChanged(float latitude, float longitude, float altitude);
|
||||
void requestMetar(const QString& icao);
|
||||
void weatherUpdated(const AviationWeather::METAR &metar);
|
||||
void on_manualQNH_clicked(bool checked);
|
||||
void on_qnh_valueChanged(int value);
|
||||
void clearCoverage(const QPoint& p);
|
||||
void clearStats(const QPoint& p);
|
||||
void clearChart(const QPoint& p);
|
||||
void resetChartAxes();
|
||||
|
||||
signals:
|
||||
void homePositionChanged();
|
||||
|
@ -3,7 +3,13 @@
|
||||
<file>icons/aircraft.png</file>
|
||||
<file>icons/airport.png</file>
|
||||
<file>icons/controltower.png</file>
|
||||
<file>icons/coverage.png</file>
|
||||
<file>icons/allflightpaths.png</file>
|
||||
<file>icons/vor.png</file>
|
||||
<file>icons/stats.png</file>
|
||||
<file>icons/chart.png</file>
|
||||
<file>icons/horizontal.png</file>
|
||||
<file>icons/vertical.png</file>
|
||||
<file>icons/radius.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -5,14 +5,22 @@
|
||||
<file>map/map_6_strict.qml</file>
|
||||
<file>map/ModifiedMapView.qml</file>
|
||||
<file>map/MapStation.qml</file>
|
||||
<file>map/aircraft_2engine.png</file>
|
||||
<file>map/aircraft_2enginesmall.png</file>
|
||||
<file>map/aircraft_4engine.png</file>
|
||||
<file>map/aircraft_small.png</file>
|
||||
<file>map/aircraft_large.png</file>
|
||||
<file>map/aircraft_heavy_2engine.png</file>
|
||||
<file>map/aircraft_heavy_4engine.png</file>
|
||||
<file>map/aircraft_helicopter.png</file>
|
||||
<file>map/aircraft_light.png</file>
|
||||
<file>map/aircraft_space.png</file>
|
||||
<file>map/aircraft_drone.png</file>
|
||||
<file>map/aircraft_fighter.png</file>
|
||||
<file>map/aircraft_glider.png</file>
|
||||
<file>map/spitfire.png</file>
|
||||
<file>map/a400m.png</file>
|
||||
<file>map/f35.png</file>
|
||||
<file>map/apache.png</file>
|
||||
<file>map/chinook.png</file>
|
||||
<file>map/eurofighter.png</file>
|
||||
<file>map/airport_large.png</file>
|
||||
<file>map/airport_medium.png</file>
|
||||
<file>map/airport_small.png</file>
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
// Map main ADS-B table column numbers to combo box indices
|
||||
std::vector<int> ADSBDemodNotificationDialog::m_columnMap = {
|
||||
ADSB_COL_ICAO, ADSB_COL_CALLSIGN, ADSB_COL_MODEL,
|
||||
ADSB_COL_ICAO, ADSB_COL_CALLSIGN, ADSB_COL_MODEL, ADSB_COL_TYPE,
|
||||
ADSB_COL_ALTITUDE, ADSB_COL_GROUND_SPEED, ADSB_COL_RANGE,
|
||||
ADSB_COL_CATEGORY, ADSB_COL_STATUS, ADSB_COL_SQUAWK,
|
||||
ADSB_COL_REGISTRATION, ADSB_COL_MANUFACTURER, ADSB_COL_OWNER, ADSB_COL_OPERATOR_ICAO
|
||||
@ -117,6 +117,7 @@ void ADSBDemodNotificationDialog::addRow(ADSBDemodSettings::NotificationSettings
|
||||
match->addItem("ICAO ID");
|
||||
match->addItem("Callsign");
|
||||
match->addItem("Aircraft");
|
||||
match->addItem("Type");
|
||||
match->addItem("Alt (ft)");
|
||||
match->addItem("GS (kn)");
|
||||
match->addItem("D (km)");
|
||||
|
@ -36,7 +36,8 @@ void ADSBDemodSettings::resetToDefaults()
|
||||
{
|
||||
m_inputFrequencyOffset = 0;
|
||||
m_rfBandwidth = 2*1300000;
|
||||
m_correlationThreshold = 10.0f; //<! ones/zero powers correlation threshold in dB
|
||||
m_correlationThreshold = 7.0f; //<! ones/zero powers correlation threshold in dB
|
||||
m_chipsThreshold = 0;
|
||||
m_samplesPerBit = 4;
|
||||
m_removeTimeout = 60;
|
||||
m_feedEnabled = false;
|
||||
@ -73,13 +74,13 @@ void ADSBDemodSettings::resetToDefaults()
|
||||
m_tableFontName = "Liberation Sans";
|
||||
m_tableFontSize = 9;
|
||||
m_displayDemodStats = false;
|
||||
m_correlateFullPreamble = true;
|
||||
m_demodModeS = true;
|
||||
m_autoResizeTableColumns = false;
|
||||
m_interpolatorPhaseSteps = 4; // Higher than these two values will struggle to run in real-time
|
||||
m_interpolatorTapsPerPhase = 3.5f; // without gaining much improvement in PER
|
||||
m_aviationstackAPIKey = "";
|
||||
m_checkWXAPIKey = "";
|
||||
m_maptilerAPIKey = "";
|
||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||
{
|
||||
m_columnIndexes[i] = i;
|
||||
@ -98,13 +99,24 @@ void ADSBDemodSettings::resetToDefaults()
|
||||
m_displayNavAids = true;
|
||||
m_displayPhotos = true;
|
||||
m_verboseModelMatching = false;
|
||||
m_airfieldElevation = 0;
|
||||
m_aircraftMinZoom = 11;
|
||||
m_workspaceIndex = 0;
|
||||
m_hidden = false;
|
||||
m_atcLabels = true;
|
||||
m_atcCallsigns = true;
|
||||
m_transitionAlt = 6000; // Depends on airport. 18,000 in USA
|
||||
m_qnh = 1013.25;
|
||||
m_manualQNH = false;
|
||||
m_displayCoverage = false;
|
||||
m_displayChart = false;
|
||||
m_displayOrientation = false;
|
||||
m_displayRadius = false;
|
||||
for (int i = 0; i < ADSB_IC_MAX; i++) {
|
||||
m_displayIC[i] = false;
|
||||
}
|
||||
m_flightPathPaletteName = "Spectral";
|
||||
applyPalette();
|
||||
m_favourLivery = true;
|
||||
}
|
||||
|
||||
QByteArray ADSBDemodSettings::serialize() const
|
||||
@ -141,7 +153,6 @@ QByteArray ADSBDemodSettings::serialize() const
|
||||
s.writeString(25, m_tableFontName);
|
||||
s.writeS32(26, m_tableFontSize);
|
||||
s.writeBool(27, m_displayDemodStats);
|
||||
s.writeBool(28, m_correlateFullPreamble);
|
||||
s.writeBool(29, m_demodModeS);
|
||||
s.writeBool(30, m_autoResizeTableColumns);
|
||||
s.writeS32(31, m_interpolatorPhaseSteps);
|
||||
@ -165,7 +176,6 @@ QByteArray ADSBDemodSettings::serialize() const
|
||||
}
|
||||
|
||||
s.writeBool(44, m_verboseModelMatching);
|
||||
s.writeS32(45, m_airfieldElevation);
|
||||
|
||||
s.writeBool(46, m_exportClientEnabled);
|
||||
s.writeBool(47, m_exportServerEnabled);
|
||||
@ -192,6 +202,19 @@ QByteArray ADSBDemodSettings::serialize() const
|
||||
s.writeS32(67, m_transitionAlt);
|
||||
s.writeString(68, m_amDemod);
|
||||
|
||||
s.writeFloat(69, m_qnh);
|
||||
s.writeBool(70, m_manualQNH);
|
||||
|
||||
s.writeBool(71, m_displayCoverage);
|
||||
s.writeBool(72, m_displayChart);
|
||||
s.writeBool(73, m_displayOrientation);
|
||||
s.writeBool(74, m_displayRadius);
|
||||
s.writeString(75, m_flightPathPaletteName);
|
||||
s.writeS32(76, m_chipsThreshold);
|
||||
s.writeBool(77, m_favourLivery);
|
||||
|
||||
s.writeString(78, m_maptilerAPIKey);
|
||||
|
||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) {
|
||||
s.writeS32(100 + i, m_columnIndexes[i]);
|
||||
}
|
||||
@ -229,7 +252,7 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
||||
d.readS32(1, &tmp, 0);
|
||||
m_inputFrequencyOffset = tmp;
|
||||
d.readReal(2, &m_rfBandwidth, 2*1300000);
|
||||
d.readReal(3, &m_correlationThreshold, 0.0f);
|
||||
d.readReal(3, &m_correlationThreshold, 7.0f);
|
||||
d.readS32(4, &m_samplesPerBit, 4);
|
||||
d.readS32(5, &m_removeTimeout, 60);
|
||||
d.readBool(6, &m_feedEnabled, false);
|
||||
@ -268,7 +291,6 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
||||
d.readString(25, &m_tableFontName, "Liberation Sans");
|
||||
d.readS32(26, &m_tableFontSize, 9);
|
||||
d.readBool(27, &m_displayDemodStats, false);
|
||||
d.readBool(28, &m_correlateFullPreamble, true);
|
||||
d.readBool(29, &m_demodModeS, true);
|
||||
d.readBool(30, &m_autoResizeTableColumns, false);
|
||||
d.readS32(31, &m_interpolatorPhaseSteps, 4);
|
||||
@ -296,7 +318,6 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
||||
}
|
||||
|
||||
d.readBool(44, &m_verboseModelMatching, false);
|
||||
d.readS32(45, &m_airfieldElevation, 0);
|
||||
|
||||
d.readBool(46, &m_exportClientEnabled, true);
|
||||
d.readBool(47, &m_exportServerEnabled, true);
|
||||
@ -328,6 +349,21 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
||||
d.readS32(67, &m_transitionAlt, 6000);
|
||||
d.readString(68, &m_amDemod);
|
||||
|
||||
d.readFloat(69, &m_qnh, 1013.25);
|
||||
d.readBool(70, &m_manualQNH, false);
|
||||
|
||||
d.readBool(71, &m_displayCoverage, false);
|
||||
d.readBool(72, &m_displayChart, false);
|
||||
d.readBool(73, &m_displayOrientation, false);
|
||||
d.readBool(74, &m_displayRadius, false);
|
||||
d.readString(75, &m_flightPathPaletteName, "Spectral");
|
||||
d.readS32(76, &m_chipsThreshold, 0);
|
||||
d.readBool(77, &m_favourLivery, true);
|
||||
|
||||
d.readString(78, &m_maptilerAPIKey, "");
|
||||
|
||||
applyPalette();
|
||||
|
||||
#ifdef LINUX
|
||||
if (m_mapProvider == "osm") {
|
||||
m_mapProvider = "mapboxgl";
|
||||
@ -413,6 +449,9 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
|
||||
if (settingsKeys.contains("correlationThreshold")) {
|
||||
m_correlationThreshold = settings.m_correlationThreshold;
|
||||
}
|
||||
if (settingsKeys.contains("chipsThreshold")) {
|
||||
m_chipsThreshold = settings.m_chipsThreshold;
|
||||
}
|
||||
if (settingsKeys.contains("samplesPerBit")) {
|
||||
m_samplesPerBit = settings.m_samplesPerBit;
|
||||
}
|
||||
@ -521,9 +560,6 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
|
||||
if (settingsKeys.contains("displayDemodStats")) {
|
||||
m_displayDemodStats = settings.m_displayDemodStats;
|
||||
}
|
||||
if (settingsKeys.contains("correlateFullPreamble")) {
|
||||
m_correlateFullPreamble = settings.m_correlateFullPreamble;
|
||||
}
|
||||
if (settingsKeys.contains("demodModeS")) {
|
||||
m_demodModeS = settings.m_demodModeS;
|
||||
}
|
||||
@ -548,6 +584,9 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
|
||||
if (settingsKeys.contains("checkWXAPIKey")) {
|
||||
m_checkWXAPIKey = settings.m_checkWXAPIKey;
|
||||
}
|
||||
if (settingsKeys.contains("maptilerAPIKey")) {
|
||||
m_maptilerAPIKey = settings.m_maptilerAPIKey;
|
||||
}
|
||||
if (settingsKeys.contains("logFilename")) {
|
||||
m_logFilename = settings.m_logFilename;
|
||||
}
|
||||
@ -575,9 +614,6 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
|
||||
if (settingsKeys.contains("verboseModelMatching")) {
|
||||
m_verboseModelMatching = settings.m_verboseModelMatching;
|
||||
}
|
||||
if (settingsKeys.contains("airfieldElevation")) {
|
||||
m_airfieldElevation = settings.m_airfieldElevation;
|
||||
}
|
||||
if (settingsKeys.contains("aircraftMinZoom")) {
|
||||
m_aircraftMinZoom = settings.m_aircraftMinZoom;
|
||||
}
|
||||
@ -590,6 +626,32 @@ void ADSBDemodSettings::applySettings(const QStringList& settingsKeys, const ADS
|
||||
if (settingsKeys.contains("transitionAlt")) {
|
||||
m_transitionAlt = settings.m_transitionAlt;
|
||||
}
|
||||
if (settingsKeys.contains("qnh")) {
|
||||
m_qnh = settings.m_qnh;
|
||||
}
|
||||
if (settingsKeys.contains("manualQNH")) {
|
||||
m_manualQNH = settings.m_manualQNH;
|
||||
}
|
||||
if (settingsKeys.contains("displayCoverage")) {
|
||||
m_displayCoverage = settings.m_displayCoverage;
|
||||
}
|
||||
if (settingsKeys.contains("displayChart")) {
|
||||
m_displayChart = settings.m_displayChart;
|
||||
}
|
||||
if (settingsKeys.contains("displayOrientation")) {
|
||||
m_displayOrientation = settings.m_displayOrientation;
|
||||
}
|
||||
if (settingsKeys.contains("displayRadius")) {
|
||||
m_displayRadius = settings.m_displayRadius;
|
||||
}
|
||||
if (settingsKeys.contains("flightPathPaletteName"))
|
||||
{
|
||||
m_flightPathPaletteName = settings.m_flightPathPaletteName;
|
||||
applyPalette();
|
||||
}
|
||||
if (settingsKeys.contains("favourLivery")) {
|
||||
m_favourLivery = settings.m_favourLivery;
|
||||
}
|
||||
}
|
||||
|
||||
QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool force) const
|
||||
@ -605,6 +667,9 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
|
||||
if (settingsKeys.contains("correlationThreshold") || force) {
|
||||
ostr << " m_correlationThreshold: " << m_correlationThreshold;
|
||||
}
|
||||
if (settingsKeys.contains("chipsThreshold") || force) {
|
||||
ostr << " m_chipsThreshold: " << m_chipsThreshold;
|
||||
}
|
||||
if (settingsKeys.contains("samplesPerBit") || force) {
|
||||
ostr << " m_samplesPerBit: " << m_samplesPerBit;
|
||||
}
|
||||
@ -704,9 +769,6 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
|
||||
if (settingsKeys.contains("displayDemodStats") || force) {
|
||||
ostr << " m_displayDemodStats: " << m_displayDemodStats;
|
||||
}
|
||||
if (settingsKeys.contains("correlateFullPreamble") || force) {
|
||||
ostr << " m_correlateFullPreamble: " << m_correlateFullPreamble;
|
||||
}
|
||||
if (settingsKeys.contains("demodModeS") || force) {
|
||||
ostr << " m_demodModeS: " << m_demodModeS;
|
||||
}
|
||||
@ -728,6 +790,9 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
|
||||
if (settingsKeys.contains("checkWXAPIKey") || force) {
|
||||
ostr << " m_checkWXAPIKey: " << m_checkWXAPIKey.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("maptilerAPIKey") || force) {
|
||||
ostr << " m_maptilerAPIKey: " << m_maptilerAPIKey.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("logFilename") || force) {
|
||||
ostr << " m_logFilename: " << m_logFilename.toStdString();
|
||||
}
|
||||
@ -755,9 +820,6 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
|
||||
if (settingsKeys.contains("verboseModelMatching") || force) {
|
||||
ostr << " m_verboseModelMatching: " << m_verboseModelMatching;
|
||||
}
|
||||
if (settingsKeys.contains("airfieldElevation") || force) {
|
||||
ostr << " m_airfieldElevation: " << m_airfieldElevation;
|
||||
}
|
||||
if (settingsKeys.contains("aircraftMinZoom") || force) {
|
||||
ostr << " m_aircraftMinZoom: " << m_aircraftMinZoom;
|
||||
}
|
||||
@ -770,6 +832,114 @@ QString ADSBDemodSettings::getDebugString(const QStringList& settingsKeys, bool
|
||||
if (settingsKeys.contains("transitionAlt") || force) {
|
||||
ostr << " m_transitionAlt: " << m_transitionAlt;
|
||||
}
|
||||
if (settingsKeys.contains("qnh") || force) {
|
||||
ostr << " m_qnh: " << m_qnh;
|
||||
}
|
||||
if (settingsKeys.contains("manualQNH") || force) {
|
||||
ostr << " m_manualQNH: " << m_manualQNH;
|
||||
}
|
||||
if (settingsKeys.contains("displayCoverage") || force) {
|
||||
ostr << " m_displayCoverage: " << m_displayCoverage;
|
||||
}
|
||||
if (settingsKeys.contains("displayChart") || force) {
|
||||
ostr << " m_displayChart: " << m_displayChart;
|
||||
}
|
||||
if (settingsKeys.contains("displayOrientation") || force) {
|
||||
ostr << " m_displayOrientation: " << m_displayOrientation;
|
||||
}
|
||||
if (settingsKeys.contains("displayRadius") || force) {
|
||||
ostr << " m_displayRadius: " << m_displayRadius;
|
||||
}
|
||||
if (settingsKeys.contains("flightPathPaletteName") || force) {
|
||||
ostr << " m_flightPathPaletteName: " << m_flightPathPaletteName.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("favourLivery") || force) {
|
||||
ostr << " m_favourLivery: " << m_favourLivery;
|
||||
}
|
||||
|
||||
return QString(ostr.str().c_str());
|
||||
}
|
||||
|
||||
void ADSBDemodSettings::applyPalette()
|
||||
{
|
||||
if (m_palettes.contains(m_flightPathPaletteName)) {
|
||||
m_flightPathPalette = m_palettes.value(m_flightPathPaletteName);
|
||||
} else {
|
||||
m_flightPathPalette = m_rainbowPalette;
|
||||
}
|
||||
}
|
||||
|
||||
const QVariant ADSBDemodSettings::m_rainbowPalette[8] = {
|
||||
QVariant(QColor(0xff, 0x00, 0x00)),
|
||||
QVariant(QColor(0xff, 0x7f, 0x00)),
|
||||
QVariant(QColor(0xff, 0xff, 0x00)),
|
||||
QVariant(QColor(0xf7, 0xff, 0x00)),
|
||||
QVariant(QColor(0x00, 0xff, 0x00)),
|
||||
QVariant(QColor(0x00, 0xff, 0x7f)),
|
||||
QVariant(QColor(0x00, 0xff, 0xff)),
|
||||
QVariant(QColor(0x00, 0x7f, 0xff)),
|
||||
};
|
||||
|
||||
const QVariant ADSBDemodSettings::m_pastelPalette[8] = {
|
||||
QVariant(QColor(0xff, 0xad, 0xad)),
|
||||
QVariant(QColor(0xff, 0xd6, 0xa5)),
|
||||
QVariant(QColor(0xfd, 0xff, 0xb6)),
|
||||
QVariant(QColor(0xca, 0xff, 0xbf)),
|
||||
QVariant(QColor(0x9b, 0xf6, 0xff)),
|
||||
QVariant(QColor(0xa0, 0xc4, 0xff)),
|
||||
QVariant(QColor(0xbd, 0xb2, 0xff)),
|
||||
QVariant(QColor(0xff, 0xc6, 0xff)),
|
||||
};
|
||||
|
||||
const QVariant ADSBDemodSettings::m_spectralPalette[8] = {
|
||||
QVariant(QColor(0xd5, 0x3e, 0x4f)),
|
||||
QVariant(QColor(0xf4, 0x6d, 0x43)),
|
||||
QVariant(QColor(0xfd, 0xae, 0x61)),
|
||||
QVariant(QColor(0xfe, 0xe0, 0x8b)),
|
||||
QVariant(QColor(0xe6, 0xf5, 0x98)),
|
||||
QVariant(QColor(0xab, 0xdd, 0xa4)),
|
||||
QVariant(QColor(0x66, 0xc2, 0xa5)),
|
||||
QVariant(QColor(0x32, 0x88, 0xbd)),
|
||||
};
|
||||
|
||||
const QVariant ADSBDemodSettings::m_bluePalette[8] = {
|
||||
QVariant(QColor(0xde, 0xeb, 0xf7)),
|
||||
QVariant(QColor(0xc6, 0xdb, 0xef)),
|
||||
QVariant(QColor(0x9e, 0xca, 0xe1)),
|
||||
QVariant(QColor(0x6b, 0xae, 0xd6)),
|
||||
QVariant(QColor(0x42, 0x92, 0xc6)),
|
||||
QVariant(QColor(0x21, 0x71, 0xb5)),
|
||||
QVariant(QColor(0x08, 0x51, 0x9c)),
|
||||
QVariant(QColor(0x08, 0x30, 0x6b)),
|
||||
};
|
||||
|
||||
const QVariant ADSBDemodSettings::m_purplePalette[8] = {
|
||||
QVariant(QColor(0xcc, 0xaf, 0xf2)),
|
||||
QVariant(QColor(0xb6, 0x99, 0xe0)),
|
||||
QVariant(QColor(0xa0, 0x84, 0xcf)),
|
||||
QVariant(QColor(0x8a, 0x6f, 0xbd)),
|
||||
QVariant(QColor(0x76, 0x5a, 0xac)),
|
||||
QVariant(QColor(0x62, 0x45, 0x9a)),
|
||||
QVariant(QColor(0x4f, 0x30, 0x89)),
|
||||
QVariant(QColor(0x3e, 0x18, 0x78)),
|
||||
};
|
||||
|
||||
const QVariant ADSBDemodSettings::m_greyPalette[8] = {
|
||||
QVariant(QColor(0x80, 0x80, 0x80)),
|
||||
QVariant(QColor(0x80, 0x80, 0x80)),
|
||||
QVariant(QColor(0x80, 0x80, 0x80)),
|
||||
QVariant(QColor(0x80, 0x80, 0x80)),
|
||||
QVariant(QColor(0x80, 0x80, 0x80)),
|
||||
QVariant(QColor(0x80, 0x80, 0x80)),
|
||||
QVariant(QColor(0x80, 0x80, 0x80)),
|
||||
QVariant(QColor(0x80, 0x80, 0x80)),
|
||||
};
|
||||
|
||||
const QHash<QString, const QVariant *> ADSBDemodSettings::m_palettes = {
|
||||
{"Rainbow", &ADSBDemodSettings::m_rainbowPalette[0]},
|
||||
{"Pastel", &ADSBDemodSettings::m_pastelPalette[0]},
|
||||
{"Spectral", &ADSBDemodSettings::m_spectralPalette[0]},
|
||||
{"Blues", &ADSBDemodSettings::m_bluePalette[0]},
|
||||
{"Purples", &ADSBDemodSettings::m_purplePalette[0]},
|
||||
{"Grey", &ADSBDemodSettings::m_greyPalette[0]},
|
||||
};
|
@ -29,63 +29,89 @@
|
||||
class Serializable;
|
||||
|
||||
// Number of columns in the table
|
||||
#define ADSBDEMOD_COLUMNS 54
|
||||
#define ADSBDEMOD_COLUMNS 78
|
||||
|
||||
// ADS-B table columns
|
||||
#define ADSB_COL_ICAO 0
|
||||
#define ADSB_COL_CALLSIGN 1
|
||||
#define ADSB_COL_ATC_CALLSIGN 2
|
||||
#define ADSB_COL_MODEL 3
|
||||
#define ADSB_COL_AIRLINE 4
|
||||
#define ADSB_COL_COUNTRY 5
|
||||
#define ADSB_COL_GROUND_SPEED 6
|
||||
#define ADSB_COL_TRUE_AIRSPEED 7
|
||||
#define ADSB_COL_INDICATED_AIRSPEED 8
|
||||
#define ADSB_COL_MACH 9
|
||||
#define ADSB_COL_SEL_ALTITUDE 10
|
||||
#define ADSB_COL_ALTITUDE 11
|
||||
#define ADSB_COL_VERTICALRATE 12
|
||||
#define ADSB_COL_SEL_HEADING 13
|
||||
#define ADSB_COL_HEADING 14
|
||||
#define ADSB_COL_TURNRATE 15
|
||||
#define ADSB_COL_ROLL 16
|
||||
#define ADSB_COL_RANGE 17
|
||||
#define ADSB_COL_AZEL 18
|
||||
#define ADSB_COL_CATEGORY 19
|
||||
#define ADSB_COL_STATUS 20
|
||||
#define ADSB_COL_SQUAWK 21
|
||||
#define ADSB_COL_REGISTRATION 22
|
||||
#define ADSB_COL_REGISTERED 23
|
||||
#define ADSB_COL_MANUFACTURER 24
|
||||
#define ADSB_COL_OWNER 25
|
||||
#define ADSB_COL_OPERATOR_ICAO 26
|
||||
#define ADSB_COL_AP 27
|
||||
#define ADSB_COL_V_MODE 28
|
||||
#define ADSB_COL_L_MODE 29
|
||||
#define ADSB_COL_BARO 30
|
||||
#define ADSB_COL_HEADWIND 31
|
||||
#define ADSB_COL_EST_AIR_TEMP 32
|
||||
#define ADSB_COL_WIND_SPEED 33
|
||||
#define ADSB_COL_WIND_DIR 34
|
||||
#define ADSB_COL_STATIC_PRESSURE 35
|
||||
#define ADSB_COL_STATIC_AIR_TEMP 36
|
||||
#define ADSB_COL_HUMIDITY 37
|
||||
#define ADSB_COL_LATITUDE 38
|
||||
#define ADSB_COL_LONGITUDE 39
|
||||
#define ADSB_COL_TIME 40
|
||||
#define ADSB_COL_FRAMECOUNT 41
|
||||
#define ADSB_COL_TIS_B 42
|
||||
#define ADSB_COL_CORRELATION 43
|
||||
#define ADSB_COL_RSSI 44
|
||||
#define ADSB_COL_FLIGHT_STATUS 45
|
||||
#define ADSB_COL_DEP 46
|
||||
#define ADSB_COL_ARR 47
|
||||
#define ADSB_COL_STD 48
|
||||
#define ADSB_COL_ETD 49
|
||||
#define ADSB_COL_ATD 50
|
||||
#define ADSB_COL_STA 51
|
||||
#define ADSB_COL_ETA 52
|
||||
#define ADSB_COL_ATA 53
|
||||
#define ADSB_COL_TYPE 4
|
||||
#define ADSB_COL_SIDEVIEW 5
|
||||
#define ADSB_COL_AIRLINE 6
|
||||
#define ADSB_COL_COUNTRY 7
|
||||
#define ADSB_COL_GROUND_SPEED 8
|
||||
#define ADSB_COL_TRUE_AIRSPEED 9
|
||||
#define ADSB_COL_INDICATED_AIRSPEED 10
|
||||
#define ADSB_COL_MACH 11
|
||||
#define ADSB_COL_SEL_ALTITUDE 12
|
||||
#define ADSB_COL_ALTITUDE 13
|
||||
#define ADSB_COL_VERTICALRATE 14
|
||||
#define ADSB_COL_SEL_HEADING 15
|
||||
#define ADSB_COL_HEADING 16
|
||||
#define ADSB_COL_TRACK 17
|
||||
#define ADSB_COL_TURNRATE 18
|
||||
#define ADSB_COL_ROLL 19
|
||||
#define ADSB_COL_RANGE 20
|
||||
#define ADSB_COL_AZEL 21
|
||||
#define ADSB_COL_CATEGORY 22
|
||||
#define ADSB_COL_STATUS 23
|
||||
#define ADSB_COL_SQUAWK 24
|
||||
#define ADSB_COL_IDENT 25
|
||||
#define ADSB_COL_REGISTRATION 26
|
||||
#define ADSB_COL_REGISTERED 27
|
||||
#define ADSB_COL_MANUFACTURER 28
|
||||
#define ADSB_COL_OWNER 29
|
||||
#define ADSB_COL_OPERATOR_ICAO 30
|
||||
#define ADSB_COL_AP 31
|
||||
#define ADSB_COL_V_MODE 32
|
||||
#define ADSB_COL_L_MODE 33
|
||||
#define ADSB_COL_TCAS 34
|
||||
#define ADSB_COL_ACAS 35
|
||||
#define ADSB_COL_RA 36
|
||||
#define ADSB_COL_MAX_SPEED 37
|
||||
#define ADSB_COL_VERSION 38
|
||||
#define ADSB_COL_LENGTH 39
|
||||
#define ADSB_COL_WIDTH 40
|
||||
#define ADSB_COL_BARO 41
|
||||
#define ADSB_COL_HEADWIND 42
|
||||
#define ADSB_COL_EST_AIR_TEMP 43
|
||||
#define ADSB_COL_WIND_SPEED 44
|
||||
#define ADSB_COL_WIND_DIR 45
|
||||
#define ADSB_COL_STATIC_PRESSURE 46
|
||||
#define ADSB_COL_STATIC_AIR_TEMP 47
|
||||
#define ADSB_COL_HUMIDITY 48
|
||||
#define ADSB_COL_LATITUDE 49
|
||||
#define ADSB_COL_LONGITUDE 50
|
||||
#define ADSB_COL_IC 51
|
||||
#define ADSB_COL_TIME 52
|
||||
#define ADSB_COL_FRAMECOUNT 53
|
||||
#define ADSB_COL_ADSB_FRAMECOUNT 54
|
||||
#define ADSB_COL_MODES_FRAMECOUNT 55
|
||||
#define ADSB_COL_NON_TRANSPONDER 56
|
||||
#define ADSB_COL_TIS_B_FRAMECOUNT 57
|
||||
#define ADSB_COL_ADSR_FRAMECOUNT 58
|
||||
#define ADSB_COL_RADIUS 59
|
||||
#define ADSB_COL_NACP 60
|
||||
#define ADSB_COL_NACV 61
|
||||
#define ADSB_COL_GVA 62
|
||||
#define ADSB_COL_NIC 63
|
||||
#define ADSB_COL_NIC_BARO 64
|
||||
#define ADSB_COL_SIL 65
|
||||
#define ADSB_COL_CORRELATION 66
|
||||
#define ADSB_COL_RSSI 67
|
||||
#define ADSB_COL_FLIGHT_STATUS 68
|
||||
#define ADSB_COL_DEP 69
|
||||
#define ADSB_COL_ARR 70
|
||||
#define ADSB_COL_STOPS 71
|
||||
#define ADSB_COL_STD 72
|
||||
#define ADSB_COL_ETD 73
|
||||
#define ADSB_COL_ATD 74
|
||||
#define ADSB_COL_STA 75
|
||||
#define ADSB_COL_ETA 76
|
||||
#define ADSB_COL_ATA 77
|
||||
|
||||
#define ADSB_IC_MAX 63
|
||||
|
||||
struct ADSBDemodSettings
|
||||
{
|
||||
@ -104,6 +130,7 @@ struct ADSBDemodSettings
|
||||
int32_t m_inputFrequencyOffset;
|
||||
Real m_rfBandwidth;
|
||||
Real m_correlationThreshold; //!< Correlation power threshold in dB
|
||||
int m_chipsThreshold; //!< How many chips in preamble can be incorrect for Mode S
|
||||
int m_samplesPerBit;
|
||||
int m_removeTimeout; //!< Time in seconds before removing an aircraft, unless a new frame is received
|
||||
|
||||
@ -159,7 +186,6 @@ struct ADSBDemodSettings
|
||||
QString m_tableFontName; //!< Font to use for table
|
||||
int m_tableFontSize;
|
||||
bool m_displayDemodStats;
|
||||
bool m_correlateFullPreamble;
|
||||
bool m_demodModeS; //!< Demodulate all Mode-S frames, not just ADS-B
|
||||
QString m_amDemod; //!< AM Demod to tune to selected ATC frequency
|
||||
bool m_autoResizeTableColumns;
|
||||
@ -169,6 +195,7 @@ struct ADSBDemodSettings
|
||||
QList<NotificationSettings *> m_notificationSettings;
|
||||
QString m_aviationstackAPIKey; //!< aviationstack.com API key
|
||||
QString m_checkWXAPIKey; //!< checkwxapi.com API key
|
||||
QString m_maptilerAPIKey; //!< maptiler.com API key (for osm/satellite map)
|
||||
|
||||
QString m_logFilename;
|
||||
bool m_logEnabled;
|
||||
@ -186,13 +213,24 @@ struct ADSBDemodSettings
|
||||
bool m_displayPhotos;
|
||||
Serializable *m_rollupState;
|
||||
bool m_verboseModelMatching;
|
||||
int m_airfieldElevation; //!< QFE in ft so aircraft takeoff/land from correct position
|
||||
int m_aircraftMinZoom;
|
||||
|
||||
bool m_atcLabels;
|
||||
bool m_atcCallsigns;
|
||||
int m_transitionAlt;
|
||||
|
||||
float m_qnh;
|
||||
bool m_manualQNH;
|
||||
bool m_displayCoverage;
|
||||
bool m_displayChart;
|
||||
bool m_displayOrientation;
|
||||
bool m_displayRadius;
|
||||
bool m_displayIC[ADSB_IC_MAX];
|
||||
QString m_flightPathPaletteName;
|
||||
bool m_favourLivery; //!< Favour airline livery over aircraft type when exact 3D model isn't available
|
||||
|
||||
const QVariant *m_flightPathPalette;
|
||||
|
||||
ADSBDemodSettings();
|
||||
void resetToDefaults();
|
||||
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
||||
@ -203,6 +241,16 @@ struct ADSBDemodSettings
|
||||
void deserializeNotificationSettings(const QByteArray& data, QList<NotificationSettings *>& notificationSettings);
|
||||
void applySettings(const QStringList& settingsKeys, const ADSBDemodSettings& settings);
|
||||
QString getDebugString(const QStringList& settingsKeys, bool force = false) const;
|
||||
void applyPalette();
|
||||
|
||||
static const QVariant m_rainbowPalette[8];
|
||||
static const QVariant m_pastelPalette[8];
|
||||
static const QVariant m_spectralPalette[8];
|
||||
static const QVariant m_bluePalette[8];
|
||||
static const QVariant m_purplePalette[8];
|
||||
static const QVariant m_greyPalette[8];
|
||||
|
||||
static const QHash<QString, const QVariant *> m_palettes;
|
||||
};
|
||||
|
||||
#endif /* PLUGINS_CHANNELRX_DEMODADSB_ADSBDEMODSETTINGS_H_ */
|
||||
|
@ -19,14 +19,16 @@
|
||||
#include <QTime>
|
||||
#include <QDebug>
|
||||
|
||||
#include "util/profiler.h"
|
||||
|
||||
#include "adsbdemodsink.h"
|
||||
#include "adsbdemodsinkworker.h"
|
||||
#include "adsbdemod.h"
|
||||
#include "adsb.h"
|
||||
|
||||
ADSBDemodSink::ADSBDemodSink() :
|
||||
m_channelSampleRate(6000000),
|
||||
m_channelFrequencyOffset(0),
|
||||
m_feedTime(0.0),
|
||||
m_sampleBuffer{nullptr, nullptr, nullptr},
|
||||
m_worker(this),
|
||||
m_writeBuffer(0),
|
||||
@ -53,7 +55,7 @@ ADSBDemodSink::~ADSBDemodSink()
|
||||
void ADSBDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
||||
{
|
||||
// Start timing how long we are in this function
|
||||
m_startPoint = boost::chrono::steady_clock::now();
|
||||
PROFILER_START();
|
||||
|
||||
// Optimise for common case, where no resampling or frequency offset
|
||||
if ((m_interpolatorDistance == 1.0f) && (m_channelFrequencyOffset == 0))
|
||||
@ -113,8 +115,7 @@ void ADSBDemodSink::feed(const SampleVector::const_iterator& begin, const Sample
|
||||
}
|
||||
|
||||
// Calculate number of seconds in this function
|
||||
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - m_startPoint;
|
||||
m_feedTime += sec.count();
|
||||
PROFILER_STOP("ADSB feed");
|
||||
}
|
||||
|
||||
void ADSBDemodSink::processOneSample(Real magsq)
|
||||
@ -127,8 +128,17 @@ void ADSBDemodSink::processOneSample(Real magsq)
|
||||
m_writeIdx++;
|
||||
if (!m_bufferDateTimeValid[m_writeBuffer])
|
||||
{
|
||||
m_bufferFirstSampleDateTime[m_writeBuffer] = QDateTime::currentDateTime();
|
||||
QDateTime dateTime = QDateTime::currentDateTime();
|
||||
if (m_minFirstSampleDateTime.isValid() && (dateTime < m_minFirstSampleDateTime)) {
|
||||
dateTime = m_minFirstSampleDateTime;
|
||||
}
|
||||
m_bufferFirstSampleDateTime[m_writeBuffer] = dateTime;
|
||||
m_bufferDateTimeValid[m_writeBuffer] = true;
|
||||
|
||||
// Make sure timestamps from different buffers are in order, even if we receive samples faster than real time
|
||||
const qint64 samplesPerSecondMSec = ADS_B_BITS_PER_SECOND * m_settings.m_samplesPerBit / 1000;
|
||||
const qint64 offsetMSec = m_bufferSize / samplesPerSecondMSec;
|
||||
m_minFirstSampleDateTime = dateTime.addMSecs(offsetMSec);
|
||||
}
|
||||
if (m_writeIdx >= m_bufferSize)
|
||||
{
|
||||
@ -138,15 +148,9 @@ void ADSBDemodSink::processOneSample(Real magsq)
|
||||
if (m_writeBuffer >= m_buffers)
|
||||
m_writeBuffer = 0;
|
||||
|
||||
// Don't include time spent waiting for a buffer
|
||||
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - m_startPoint;
|
||||
m_feedTime += sec.count();
|
||||
|
||||
if (m_worker.isRunning())
|
||||
m_bufferWrite[m_writeBuffer].acquire();
|
||||
|
||||
m_startPoint = boost::chrono::steady_clock::now();
|
||||
|
||||
m_writeIdx = m_samplesPerFrame - 1; // Leave space for copying samples from previous buffer
|
||||
|
||||
m_bufferDateTimeValid[m_writeBuffer] = false;
|
||||
@ -253,7 +257,6 @@ void ADSBDemodSink::applySettings(const ADSBDemodSettings& settings, const QStri
|
||||
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
|
||||
<< " m_rfBandwidth: " << settings.m_rfBandwidth
|
||||
<< " m_correlationThreshold: " << settings.m_correlationThreshold
|
||||
<< " m_correlateFullPreamble: " << settings.m_correlateFullPreamble
|
||||
<< " m_demodModeS: " << settings.m_demodModeS
|
||||
<< " m_samplesPerBit: " << settings.m_samplesPerBit
|
||||
<< " force: " << force;
|
||||
@ -285,3 +288,9 @@ void ADSBDemodSink::applySettings(const ADSBDemodSettings& settings, const QStri
|
||||
m_settings.applySettings(settingsKeys, settings);
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBDemodSink::resetStats()
|
||||
{
|
||||
ADSBDemod::MsgResetStats* msg = ADSBDemod::MsgResetStats::create();
|
||||
m_worker.getInputMessageQueue()->push(msg);
|
||||
}
|
||||
|
@ -19,9 +19,6 @@
|
||||
#ifndef INCLUDE_ADSBDEMODSINK_H
|
||||
#define INCLUDE_ADSBDEMODSINK_H
|
||||
|
||||
#define BOOST_CHRONO_HEADER_ONLY
|
||||
#include <boost/chrono/chrono.hpp>
|
||||
|
||||
#include "dsp/channelsamplesink.h"
|
||||
#include "dsp/nco.h"
|
||||
#include "dsp/interpolator.h"
|
||||
@ -60,6 +57,7 @@ public:
|
||||
void setMessageQueueToWorker(MessageQueue *messageQueue) { m_messageQueueToWorker = messageQueue; }
|
||||
void startWorker();
|
||||
void stopWorker();
|
||||
void resetStats();
|
||||
|
||||
private:
|
||||
friend ADSBDemodSinkWorker;
|
||||
@ -83,9 +81,6 @@ private:
|
||||
Real m_interpolatorDistance;
|
||||
Real m_interpolatorDistanceRemain;
|
||||
|
||||
boost::chrono::steady_clock::time_point m_startPoint;
|
||||
double m_feedTime; //!< Time spent in feed()
|
||||
|
||||
// Triple buffering for sharing sample data between two threads
|
||||
// Top area of each buffer is not used by writer, as it's used by the reader
|
||||
// for copying the last few samples of the previous buffer, so it can
|
||||
@ -96,6 +91,7 @@ private:
|
||||
QSemaphore m_bufferWrite[3]; //!< Semaphore to control write access to the buffers
|
||||
QSemaphore m_bufferRead[3]; //!< Semaphore to control read access from the buffers
|
||||
QDateTime m_bufferFirstSampleDateTime[3]; //!< Time for first sample in the buffer
|
||||
QDateTime m_minFirstSampleDateTime;
|
||||
bool m_bufferDateTimeValid[3];
|
||||
ADSBDemodSinkWorker m_worker; //!< Worker thread that does the actual demodulation
|
||||
int m_writeBuffer; //!< Which of the 3 buffers we're writing in to
|
||||
|
@ -15,29 +15,288 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define BOOST_CHRONO_HEADER_ONLY
|
||||
#include <boost/chrono/chrono.hpp>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "util/db.h"
|
||||
#include "util/profiler.h"
|
||||
#include "util/units.h"
|
||||
#include "util/osndb.h"
|
||||
#include "util/popcount.h"
|
||||
|
||||
#include "adsbdemodreport.h"
|
||||
#include "adsbdemodsink.h"
|
||||
#include "adsbdemodsinkworker.h"
|
||||
#include "adsbdemod.h"
|
||||
#include "adsb.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(ADSBDemodSinkWorker::MsgConfigureADSBDemodSinkWorker, Message)
|
||||
|
||||
const Real ADSBDemodSinkWorker::m_correlationScale = 3.0f;
|
||||
|
||||
static int grayToBinary(int gray, int bits)
|
||||
{
|
||||
int binary = 0;
|
||||
for (int i = bits - 1; i >= 0; i--) {
|
||||
binary = binary | ((((1 << (i+1)) & binary) >> 1) ^ ((1 << i) & gray));
|
||||
}
|
||||
return binary;
|
||||
}
|
||||
|
||||
static int gillhamToFeet(int n)
|
||||
{
|
||||
int c1 = (n >> 10) & 1;
|
||||
int a1 = (n >> 9) & 1;
|
||||
int c2 = (n >> 8) & 1;
|
||||
int a2 = (n >> 7) & 1;
|
||||
int c4 = (n >> 6) & 1;
|
||||
int a4 = (n >> 5) & 1;
|
||||
int b1 = (n >> 4) & 1;
|
||||
int b2 = (n >> 3) & 1;
|
||||
int d2 = (n >> 2) & 1;
|
||||
int b4 = (n >> 1) & 1;
|
||||
int d4 = n & 1;
|
||||
|
||||
int n500 = grayToBinary((d2 << 7) | (d4 << 6) | (a1 << 5) | (a2 << 4) | (a4 << 3) | (b1 << 2) | (b2 << 1) | b4, 4);
|
||||
int n100 = grayToBinary((c1 << 2) | (c2 << 1) | c4, 3) - 1;
|
||||
|
||||
if (n100 == 6) {
|
||||
n100 = 4;
|
||||
}
|
||||
if (n500 %2 != 0) {
|
||||
n100 = 4 - n100;
|
||||
}
|
||||
|
||||
return -1200 + n500*500 + n100*100;
|
||||
}
|
||||
|
||||
static int decodeModeSAltitude(const QByteArray& data)
|
||||
{
|
||||
int altitude = 0; // Altitude in feet
|
||||
int altitudeCode = ((data[2] & 0x1f) << 8) | (data[3] & 0xff);
|
||||
|
||||
if (altitudeCode & 0x40) // M bit indicates metres
|
||||
{
|
||||
int altitudeMetres = ((altitudeCode & 0x1f80) >> 1) | (altitudeCode & 0x3f);
|
||||
altitude = Units::metresToFeet(altitudeMetres);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove M and Q bits
|
||||
int altitudeFix = ((altitudeCode & 0x1f80) >> 2) | ((altitudeCode & 0x20) >> 1) | (altitudeCode & 0xf);
|
||||
|
||||
// Convert to feet
|
||||
if (altitudeCode & 0x10) {
|
||||
altitude = altitudeFix * 25 - 1000;
|
||||
} else {
|
||||
altitude = gillhamToFeet(altitudeFix);
|
||||
}
|
||||
}
|
||||
return altitude;
|
||||
}
|
||||
|
||||
void ADSBDemodSinkWorker::handleModeS(unsigned char *data, int bytes, unsigned icao, int df, int firstIndex, unsigned short preamble, Real preambleCorrelation, Real correlationOnes, const QDateTime& dateTime, unsigned crc)
|
||||
{
|
||||
// Ignore downlink formats we can't decode / unlikely
|
||||
if ((df != 19) && (df != 22) && (df < 24))
|
||||
{
|
||||
QList<RXRecord> l;
|
||||
|
||||
if (m_modeSOnlyIcaos.contains(icao))
|
||||
{
|
||||
l = m_modeSOnlyIcaos.value(icao);
|
||||
if (abs(l.last().m_firstIndex - firstIndex) < 4) {
|
||||
return; // Duplicate
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Stop hash table from getting too big - clear every 10 seconds or so
|
||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||
if (m_lastClear.secsTo(currentDateTime) >= 10)
|
||||
{
|
||||
//qDebug() << "Clearing ModeS only hash. size=" << m_modeSOnlyIcaos.size();
|
||||
m_modeSOnlyIcaos.clear();
|
||||
m_lastClear = currentDateTime;
|
||||
}
|
||||
}
|
||||
|
||||
RXRecord rx;
|
||||
rx.m_data = QByteArray((char*)data, bytes);
|
||||
rx.m_firstIndex = firstIndex;
|
||||
rx.m_preamble = preamble;
|
||||
rx.m_preambleCorrelation = preambleCorrelation;
|
||||
rx.m_correlationOnes = correlationOnes;
|
||||
rx.m_dateTime = dateTime;
|
||||
rx.m_crc = crc;
|
||||
l.append(rx);
|
||||
m_modeSOnlyIcaos.insert(icao, l);
|
||||
|
||||
// Have we heard from the same address several times in the last 10 seconds?
|
||||
if (l.size() >= 5)
|
||||
{
|
||||
// Check all frames have consistent altitudes and identifiers
|
||||
bool idConsistent = true;
|
||||
bool altitudeConsistent = true;
|
||||
int altitude = -1;
|
||||
int id = -1;
|
||||
|
||||
for (int i = 0; i < l.size(); i++)
|
||||
{
|
||||
int df2 = ((l[i].m_data[0] >> 3) & ADS_B_DF_MASK);
|
||||
|
||||
if ((df2 == 0) || (df2 == 4) || (df2 == 16) || (df2 == 20))
|
||||
{
|
||||
int curAltitude = decodeModeSAltitude(l[i].m_data);
|
||||
if (altitude == -1)
|
||||
{
|
||||
altitude = curAltitude;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (abs(curAltitude - altitude) > 1000) {
|
||||
altitudeConsistent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((df2 == 5) || (df2 == 21))
|
||||
{
|
||||
int curId = ((data[2] & 0x1f) << 8) | (data[3] & 0xff); // No decode - we just want to know if it changes
|
||||
|
||||
if (id == -1)
|
||||
{
|
||||
id = curId;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (id != curId) {
|
||||
idConsistent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: We could optionally check to see if aircraft ICAO is in db, but the db isn't complete
|
||||
|
||||
if (altitudeConsistent && idConsistent)
|
||||
{
|
||||
// Forward all frames
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
forwardFrame((const unsigned char *) l[i].m_data.data(), l[i].m_data.size(), l[i].m_preambleCorrelation, l[i].m_correlationOnes, l[i].m_dateTime, l[i].m_crc);
|
||||
}
|
||||
|
||||
m_icaos.insert(icao, l.back().m_dateTime.toMSecsSinceEpoch());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_modeSOnlyIcaos.remove(icao);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a Mode S frame has reserved bits set or reserved field values
|
||||
// Refer: Annex 10 Volume IV
|
||||
bool ADSBDemodSinkWorker::modeSValid(unsigned char *data, unsigned df)
|
||||
{
|
||||
bool invalid = false;
|
||||
|
||||
if (df == 0)
|
||||
{
|
||||
invalid = ((data[0] & 0x1) | (data[1] & 0x18) | (data[2] & 0x60)) != 0;
|
||||
}
|
||||
else if ((df == 4) || (df == 20))
|
||||
{
|
||||
unsigned fs = data[0] & 0x3;
|
||||
unsigned dr = (data[1] >> 3) & 0x1f;
|
||||
|
||||
invalid = (fs == 6) || (fs == 7) || ((dr >= 8) && (dr <= 15));
|
||||
}
|
||||
else if ((df == 5) || (df == 21))
|
||||
{
|
||||
unsigned fs = data[0] & 0x3;
|
||||
unsigned dr = (data[1] >> 3) & 0x1f;
|
||||
|
||||
invalid = (fs == 6) || (fs == 7) || ((dr >= 8) && (dr <= 15)) || ((data[3] & 0x40) != 0);
|
||||
}
|
||||
else if (df == 11)
|
||||
{
|
||||
unsigned ca = data[0] & 0x7;
|
||||
|
||||
invalid = ((ca >= 1) && (ca <= 3));
|
||||
}
|
||||
else if (df == 16)
|
||||
{
|
||||
invalid = ((data[0] & 0x3) | (data[1] & 0x18) | (data[2] & 0x60)) != 0;
|
||||
}
|
||||
|
||||
return invalid;
|
||||
}
|
||||
|
||||
// Check if valid ICAO address - i.e. not a reserved address - see Table 9-1 in Annex X Volume III
|
||||
bool ADSBDemodSinkWorker::icaoValid(unsigned icao)
|
||||
{
|
||||
unsigned msn = (icao >> 20) & 0xf;
|
||||
|
||||
return (icao != 0) && (msn != 0xf) && (msn != 0xb) && (msn != 0xd);
|
||||
}
|
||||
|
||||
// Is it less than a minute since the last received frame for this ICAO
|
||||
bool ADSBDemodSinkWorker::icaoHeardRecently(unsigned icao, const QDateTime &dateTime)
|
||||
{
|
||||
const int timeLimitMSec = 60*1000;
|
||||
|
||||
if (m_icaos.contains(icao))
|
||||
{
|
||||
if ((dateTime.toMSecsSinceEpoch() - m_icaos.value(icao)) < timeLimitMSec) {
|
||||
return true;
|
||||
} else {
|
||||
m_icaos.remove(icao);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ADSBDemodSinkWorker::forwardFrame(const unsigned char *data, int size, float preambleCorrelation, float correlationOnes, const QDateTime& dateTime, unsigned crc)
|
||||
{
|
||||
// Pass to GUI
|
||||
if (m_sink->getMessageQueueToGUI())
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, size),
|
||||
preambleCorrelation,
|
||||
correlationOnes,
|
||||
dateTime,
|
||||
crc);
|
||||
m_sink->getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
// Pass to worker to feed to other servers
|
||||
if (m_sink->getMessageQueueToWorker())
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, size),
|
||||
preambleCorrelation,
|
||||
correlationOnes,
|
||||
dateTime,
|
||||
crc);
|
||||
m_sink->getMessageQueueToWorker()->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBDemodSinkWorker::run()
|
||||
{
|
||||
int readBuffer = 0;
|
||||
m_lastClear = QDateTime::currentDateTime();
|
||||
|
||||
// Acquire first buffer
|
||||
m_sink->m_bufferRead[readBuffer].acquire();
|
||||
|
||||
if (isInterruptionRequested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start recording how much time is spent processing in this method
|
||||
boost::chrono::steady_clock::time_point startPoint = boost::chrono::steady_clock::now();
|
||||
PROFILER_START();
|
||||
|
||||
// Check for updated settings
|
||||
handleInputMessages();
|
||||
@ -51,8 +310,6 @@ void ADSBDemodSinkWorker::run()
|
||||
<< " samplesPerFrame: " << samplesPerFrame
|
||||
<< " samplesPerChip: " << samplesPerChip
|
||||
<< " samplesPerBit: " << samplesPerBit
|
||||
<< " correlateFullPreamble: " << m_settings.m_correlateFullPreamble
|
||||
<< " correlationScale: " << m_correlationScale
|
||||
<< " correlationThreshold: " << m_settings.m_correlationThreshold;
|
||||
|
||||
int readIdx = m_sink->m_samplesPerFrame - 1;
|
||||
@ -63,61 +320,33 @@ void ADSBDemodSinkWorker::run()
|
||||
|
||||
// Correlate received signal with expected preamble
|
||||
// chip+ indexes are 0, 2, 7, 9
|
||||
// correlating over first 6 bits gives a reduction in per-sample
|
||||
// processing, but more than doubles the number of false matches
|
||||
Real preambleCorrelationOnes = 0.0;
|
||||
Real preambleCorrelationZeros = 0.0;
|
||||
if (m_settings.m_correlateFullPreamble)
|
||||
for (int i = 0; i < samplesPerChip; i++)
|
||||
{
|
||||
for (int i = 0; i < samplesPerChip; i++)
|
||||
{
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 0*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 1*samplesPerChip + i];
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 0*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 1*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 2*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 3*samplesPerChip + i];
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 2*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 3*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 4*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 5*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 4*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 5*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 6*samplesPerChip + i];
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 7*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 6*samplesPerChip + i];
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 7*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 8*samplesPerChip + i];
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 9*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 8*samplesPerChip + i];
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 9*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 10*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 11*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 10*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 11*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 12*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 13*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 12*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 13*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 14*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 15*samplesPerChip + i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < samplesPerChip; i++)
|
||||
{
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 0*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 1*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 2*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 3*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 4*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 5*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 6*samplesPerChip + i];
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 7*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 8*samplesPerChip + i];
|
||||
preambleCorrelationOnes += m_sink->m_sampleBuffer[readBuffer][startIdx + 9*samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 10*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 11*samplesPerChip + i];
|
||||
}
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 14*samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 15*samplesPerChip + i];
|
||||
}
|
||||
|
||||
// Use the ratio of ones power over zeros power, as we don't care how powerful the signal
|
||||
@ -138,6 +367,7 @@ void ADSBDemodSinkWorker::run()
|
||||
int firstIdx = startIdx;
|
||||
|
||||
m_demodStats.m_correlatorMatches++;
|
||||
|
||||
// Skip over preamble
|
||||
startIdx += samplesPerBit*ADS_B_PREAMBLE_BITS;
|
||||
|
||||
@ -147,6 +377,7 @@ void ADSBDemodSinkWorker::run()
|
||||
int currentBit;
|
||||
unsigned char currentByte = 0;
|
||||
int df;
|
||||
int firstIndex = 0;
|
||||
|
||||
for (int bit = 0; bit < ADS_B_ES_BITS; bit++)
|
||||
{
|
||||
@ -165,6 +396,9 @@ void ADSBDemodSinkWorker::run()
|
||||
currentByte |= currentBit << (7-(bit & 0x7));
|
||||
if ((bit & 0x7) == 0x7)
|
||||
{
|
||||
if (byteIdx == 0) {
|
||||
firstIndex = startIdx;
|
||||
}
|
||||
data[byteIdx++] = currentByte;
|
||||
currentByte = 0;
|
||||
// Don't try to demodulate any further, if this isn't an ADS-B frame
|
||||
@ -178,6 +412,10 @@ void ADSBDemodSinkWorker::run()
|
||||
}
|
||||
}
|
||||
|
||||
Real preambleCorrelationScaled = preambleCorrelation * m_correlationScale;
|
||||
Real correlationOnes = preambleCorrelationOnes / samplesPerChip;
|
||||
QDateTime dateTime = rxDateTime(firstIdx, readBuffer);
|
||||
|
||||
// Is ADS-B?
|
||||
df = ((data[0] >> 3) & ADS_B_DF_MASK);
|
||||
if ((df == 17) || (df == 18))
|
||||
@ -188,109 +426,177 @@ void ADSBDemodSinkWorker::run()
|
||||
m_crc.calculate(data, ADS_B_ES_BYTES-3);
|
||||
if (parity == m_crc.get())
|
||||
{
|
||||
// Got a valid frame
|
||||
m_demodStats.m_adsbFrames++;
|
||||
// Get 24-bit ICAO and save in hash of ICAOs that have been seen
|
||||
// Get 24-bit ICAO
|
||||
unsigned icao = ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff);
|
||||
m_icaos.insert(icao, true);
|
||||
// Don't try to re-demodulate the same frame
|
||||
// We could possibly allow a partial overlap here
|
||||
readIdx += (ADS_B_ES_BITS+ADS_B_PREAMBLE_BITS)*ADS_B_CHIPS_PER_BIT*samplesPerChip - 1;
|
||||
// Pass to GUI
|
||||
if (m_sink->getMessageQueueToGUI())
|
||||
|
||||
if (icaoValid(icao))
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, sizeof(data)),
|
||||
preambleCorrelation * m_correlationScale,
|
||||
preambleCorrelationOnes / samplesPerChip,
|
||||
rxDateTime(firstIdx, readBuffer),
|
||||
m_crc.get());
|
||||
m_sink->getMessageQueueToGUI()->push(msg);
|
||||
// Got a valid frame
|
||||
m_demodStats.m_adsbFrames++;
|
||||
// Save in hash of ICAOs that have been seen
|
||||
m_icaos.insert(icao, dateTime.toMSecsSinceEpoch());
|
||||
// Don't try to re-demodulate the same frame
|
||||
// We could possibly allow a partial overlap here
|
||||
readIdx += (ADS_B_ES_BITS+ADS_B_PREAMBLE_BITS)*ADS_B_CHIPS_PER_BIT*samplesPerChip - 1;
|
||||
forwardFrame(data, sizeof(data), preambleCorrelationScaled, correlationOnes, dateTime, m_crc.get());
|
||||
}
|
||||
// Pass to worker to feed to other servers
|
||||
if (m_sink->getMessageQueueToWorker())
|
||||
else
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, sizeof(data)),
|
||||
preambleCorrelation * m_correlationScale,
|
||||
preambleCorrelationOnes / samplesPerChip,
|
||||
rxDateTime(firstIdx, readBuffer),
|
||||
m_crc.get());
|
||||
m_sink->getMessageQueueToWorker()->push(msg);
|
||||
m_demodStats.m_icaoFails++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_demodStats.m_crcFails++;
|
||||
}
|
||||
}
|
||||
else if (m_settings.m_demodModeS)
|
||||
{
|
||||
int bytes;
|
||||
// Decode premable, as correlation alone results in too many false positives for Mode S frames
|
||||
startIdx = readIdx;
|
||||
unsigned short preamble = 0;
|
||||
QVector<float> preambleChips(16);
|
||||
|
||||
// Determine number of bytes in frame depending on downlink format
|
||||
if ((df == 0) || (df == 4) || (df == 5) || (df == 11)) {
|
||||
bytes = 56/8;
|
||||
} else if ((df == 16) || (df == 20) || (df == 21) || (df >= 24)) {
|
||||
bytes = 112/8;
|
||||
} else {
|
||||
bytes = 0;
|
||||
}
|
||||
if (bytes > 0)
|
||||
for (int bit = 0; bit < ADS_B_PREAMBLE_BITS; bit++)
|
||||
{
|
||||
// Extract received parity
|
||||
int parity = (data[bytes-3] << 16) | (data[bytes-2] << 8) | data[bytes-1];
|
||||
// Calculate CRC on received frame
|
||||
m_crc.init();
|
||||
m_crc.calculate(data, bytes-3);
|
||||
int crc = m_crc.get();
|
||||
// DF4 / DF5 / DF20 / DF21 have ICAO address XORed in to parity.
|
||||
// Extract ICAO from parity and see if it matches an aircraft we've already
|
||||
// received an ADS-B frame from
|
||||
if ((df == 4) || (df == 5) || (df == 20) || (df == 21))
|
||||
preambleChips[bit*2] = 0.0f;
|
||||
preambleChips[bit*2+1] = 0.0f;
|
||||
for (int i = 0; i < samplesPerChip; i++)
|
||||
{
|
||||
unsigned icao = (parity ^ crc) & 0xffffff;
|
||||
if (m_icaos.contains(icao)) {
|
||||
crc ^= icao;
|
||||
}
|
||||
preambleChips[bit*2] += m_sink->m_sampleBuffer[readBuffer][startIdx+i];
|
||||
preambleChips[bit*2+1] += m_sink->m_sampleBuffer[readBuffer][startIdx+samplesPerChip+i];
|
||||
}
|
||||
// For DF11, the last 7 bits may have an address/interogration identifier (II)
|
||||
// XORed in, so we ignore those bits
|
||||
if ((parity == crc) || ((df == 11) && ((parity & 0xffff80) == (crc & 0xffff80))))
|
||||
|
||||
startIdx += samplesPerBit;
|
||||
}
|
||||
startIdx = firstIdx;
|
||||
|
||||
float onesAvg = (preambleChips[0] + preambleChips[2] + preambleChips[7] + preambleChips[9]) / 4.0f;
|
||||
float zerosAvg = (preambleChips[1] + preambleChips[3] + preambleChips[4] + preambleChips[5] + preambleChips[6] + preambleChips[8]
|
||||
+ preambleChips[10] + + preambleChips[11] + preambleChips[12] + preambleChips[13] + preambleChips[14] + preambleChips[15]) / 12.0f;
|
||||
float midPoint = zerosAvg + (onesAvg - zerosAvg) / 2.0f;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
unsigned chip = preambleChips[i] > midPoint;
|
||||
preamble |= chip << (15-i);
|
||||
}
|
||||
|
||||
// qDebug() << "Preamble" << preambleChips << "zerosAvg" << zerosAvg << "onesAvg" << onesAvg << "midPoint" << midPoint << "chips" << Qt::hex << preamble;
|
||||
|
||||
const unsigned short expectedPreamble = 0xa140;
|
||||
int preambleDifferences = popcount(expectedPreamble ^ preamble);
|
||||
|
||||
if (preambleDifferences <= m_settings.m_chipsThreshold)
|
||||
{
|
||||
int bytes;
|
||||
|
||||
// Determine number of bytes in frame depending on downlink format
|
||||
if ((df == 0) || (df == 4) || (df == 5) || (df == 11)) {
|
||||
bytes = 56/8;
|
||||
} else if ((df == 16) || (df == 19) || (df == 20) || (df == 21) || (df == 22) || ((df >= 24) && (df <= 27))) {
|
||||
bytes = 112/8;
|
||||
} else {
|
||||
bytes = 0;
|
||||
}
|
||||
|
||||
if (bytes > 0)
|
||||
{
|
||||
m_demodStats.m_modesFrames++;
|
||||
// Pass to GUI (only pass formats it can decode)
|
||||
if (m_sink->getMessageQueueToGUI() && ((df == 4) || (df == 5) || (df == 20) || (df == 21)))
|
||||
bool invalid = modeSValid(data, df);
|
||||
|
||||
if (!invalid)
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, bytes),
|
||||
preambleCorrelation * m_correlationScale,
|
||||
preambleCorrelationOnes / samplesPerChip,
|
||||
rxDateTime(firstIdx, readBuffer),
|
||||
m_crc.get());
|
||||
m_sink->getMessageQueueToGUI()->push(msg);
|
||||
// Extract received parity
|
||||
int parity = (data[bytes-3] << 16) | (data[bytes-2] << 8) | data[bytes-1];
|
||||
// Calculate CRC on received frame
|
||||
m_crc.init();
|
||||
m_crc.calculate(data, bytes-3);
|
||||
int crc = m_crc.get();
|
||||
bool forward = false;
|
||||
|
||||
// ICAO address XORed in to parity, apart from DF11
|
||||
// Extract ICAO from parity and see if it matches an aircraft we've already
|
||||
// received an ADS-B or Mode S frame from
|
||||
// For DF11, the last 7 bits may have an iterogration code (II/SI)
|
||||
// XORed in, so we ignore those bits. This does sometimes lead to false-positives
|
||||
if (df != 11)
|
||||
{
|
||||
unsigned icao = (parity ^ crc) & 0xffffff;
|
||||
|
||||
if (icaoValid(icao))
|
||||
{
|
||||
if (icaoHeardRecently(icao, dateTime)) {
|
||||
forward = true;
|
||||
} else {
|
||||
handleModeS(data, bytes, icao, df, firstIndex, preamble, preambleCorrelationScaled, correlationOnes, dateTime, m_crc.get());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_demodStats.m_icaoFails++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore IC bits
|
||||
parity &= 0xffff80;
|
||||
crc &= 0xffff80;
|
||||
if (parity == crc)
|
||||
{
|
||||
// Get 24-bit ICAO
|
||||
unsigned icao = ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff);
|
||||
|
||||
if (icaoValid(icao))
|
||||
{
|
||||
if (icaoHeardRecently(icao, dateTime)) {
|
||||
forward = true;
|
||||
} else {
|
||||
handleModeS(data, bytes, icao, df, firstIndex, preamble, preambleCorrelationScaled, correlationOnes, dateTime, m_crc.get());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_demodStats.m_icaoFails++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_demodStats.m_crcFails++;
|
||||
}
|
||||
}
|
||||
if (forward)
|
||||
{
|
||||
m_demodStats.m_modesFrames++;
|
||||
// Don't try to re-demodulate the same frame
|
||||
// We could possibly allow a partial overlap here
|
||||
readIdx += ((bytes*8)+ADS_B_PREAMBLE_BITS)*ADS_B_CHIPS_PER_BIT*samplesPerChip - 1;
|
||||
forwardFrame(data, bytes, preambleCorrelationScaled, correlationOnes, dateTime, m_crc.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_demodStats.m_crcFails++;
|
||||
}
|
||||
}
|
||||
// Pass to worker to feed to other servers
|
||||
if (m_sink->getMessageQueueToWorker())
|
||||
else
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, bytes),
|
||||
preambleCorrelation * m_correlationScale,
|
||||
preambleCorrelationOnes / samplesPerChip,
|
||||
rxDateTime(firstIdx, readBuffer),
|
||||
m_crc.get());
|
||||
m_sink->getMessageQueueToWorker()->push(msg);
|
||||
m_demodStats.m_invalidFails++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_demodStats.m_crcFails++;
|
||||
m_demodStats.m_typeFails++;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_demodStats.m_typeFails++;
|
||||
{
|
||||
m_demodStats.m_preambleFails++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_demodStats.m_typeFails++;
|
||||
}
|
||||
}
|
||||
|
||||
readIdx++;
|
||||
if (readIdx > m_sink->m_bufferSize - samplesPerFrame)
|
||||
{
|
||||
@ -299,9 +605,7 @@ void ADSBDemodSinkWorker::run()
|
||||
nextBuffer = 0;
|
||||
|
||||
// Update amount of time spent processing (don't include time spend in acquire)
|
||||
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - startPoint;
|
||||
m_demodStats.m_demodTime += sec.count();
|
||||
m_demodStats.m_feedTime = m_sink->m_feedTime;
|
||||
PROFILER_STOP("ADS-B demod");
|
||||
|
||||
// Send stats to GUI
|
||||
if (m_sink->getMessageQueueToGUI())
|
||||
@ -319,7 +623,7 @@ void ADSBDemodSinkWorker::run()
|
||||
handleInputMessages();
|
||||
|
||||
// Resume timing how long we are processing
|
||||
startPoint = boost::chrono::steady_clock::now();
|
||||
PROFILER_RESTART();
|
||||
|
||||
int samplesRemaining = m_sink->m_bufferSize - readIdx;
|
||||
if (samplesRemaining > 0)
|
||||
@ -359,15 +663,6 @@ void ADSBDemodSinkWorker::handleInputMessages()
|
||||
QStringList settingsKeys = cfg->getSettingsKeys();
|
||||
bool force = cfg->getForce();
|
||||
|
||||
if (settingsKeys.contains("correlateFullPreamble") || force)
|
||||
{
|
||||
if (settings.m_correlateFullPreamble) {
|
||||
m_correlationScale = 3.0;
|
||||
} else {
|
||||
m_correlationScale = 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((settingsKeys.contains("correlationThreshold") && (m_settings.m_correlationThreshold != settings.m_correlationThreshold)) || force)
|
||||
{
|
||||
m_correlationThresholdLinear = CalcDb::powerFromdB(settings.m_correlationThreshold);
|
||||
@ -382,6 +677,10 @@ void ADSBDemodSinkWorker::handleInputMessages()
|
||||
}
|
||||
delete message;
|
||||
}
|
||||
else if (ADSBDemod::MsgResetStats::match(*message))
|
||||
{
|
||||
m_demodStats.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,11 +77,29 @@ private:
|
||||
ADSBDemodSink *m_sink;
|
||||
ADSBDemodStats m_demodStats;
|
||||
Real m_correlationThresholdLinear;
|
||||
Real m_correlationScale;
|
||||
static const Real m_correlationScale;
|
||||
crcadsb m_crc; //!< Have as member to avoid recomputing LUT
|
||||
QHash<int, bool> m_icaos; //!< ICAO addresses that have been received
|
||||
QHash<unsigned, qint64> m_icaos; //!< ICAO addresses that have been received and msecsSinceEpoch last heard
|
||||
QDateTime m_lastClear;
|
||||
|
||||
struct RXRecord {
|
||||
QByteArray m_data;
|
||||
int m_firstIndex;
|
||||
unsigned short m_preamble;
|
||||
Real m_preambleCorrelation;
|
||||
Real m_correlationOnes;
|
||||
QDateTime m_dateTime;
|
||||
unsigned m_crc;
|
||||
};
|
||||
|
||||
QHash<int, QList<RXRecord>> m_modeSOnlyIcaos;
|
||||
|
||||
QDateTime rxDateTime(int firstIdx, int readBuffer) const;
|
||||
void handleModeS(unsigned char *data, int bytes, unsigned icao, int df, int firstIndex, unsigned short preamble, Real preambleCorrelation, Real correlationOnes, const QDateTime& dateTime, unsigned crc);
|
||||
void forwardFrame(const unsigned char *data, int size, float preambleCorrelation, float correlationOnes, const QDateTime& dateTime, unsigned crc);
|
||||
bool icaoHeardRecently(unsigned icao, const QDateTime &dateTime);
|
||||
static bool icaoValid(unsigned icao);
|
||||
static bool modeSValid(unsigned char *data, unsigned df);
|
||||
|
||||
};
|
||||
|
||||
|
@ -25,20 +25,27 @@ struct ADSBDemodStats {
|
||||
qint64 m_correlatorMatches; //!< Total number of correlator matches
|
||||
qint64 m_adsbFrames; //!< How many ADS-B frames with correct CRCs
|
||||
qint64 m_modesFrames; //!< How many non-ADS-B Mode-S frames with correct CRCs
|
||||
qint64 m_preambleFails; //!< How many non-ADS-B Mode S frames with errors in preamble
|
||||
qint64 m_crcFails; //!< How many frames we've demoded with incorrect CRCs
|
||||
qint64 m_typeFails; //!< How many frames we've demoded with unknown type (DF) so we can't check CRC
|
||||
double m_demodTime; //!< How long we've spent in run()
|
||||
double m_feedTime; //!< How long we've spent in feed()
|
||||
qint64 m_invalidFails; //!< How many frames we've demoded with reserved bits set
|
||||
qint64 m_icaoFails; //!< How many frames we've demoded with reserved ICAO
|
||||
|
||||
ADSBDemodStats() :
|
||||
m_correlatorMatches(0),
|
||||
m_adsbFrames(0),
|
||||
m_modesFrames(0),
|
||||
m_crcFails(0),
|
||||
m_typeFails(0),
|
||||
m_demodTime(0.0),
|
||||
m_feedTime(0.0)
|
||||
ADSBDemodStats()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_correlatorMatches = 0;
|
||||
m_adsbFrames = 0;
|
||||
m_modesFrames = 0;
|
||||
m_preambleFails = 0;
|
||||
m_crcFails = 0;
|
||||
m_typeFails = 0;
|
||||
m_invalidFails = 0;
|
||||
m_icaoFails = 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -67,14 +67,14 @@ private slots:
|
||||
{
|
||||
xml = QString("\
|
||||
{\
|
||||
\"UrlTemplate\" : \"https://maps.wikimedia.org/osm-intl/%z/%x/%y%1.png\",\
|
||||
\"UrlTemplate\" : \"https://tile.openstreetmap.org/%z/%x/%y.png\",\
|
||||
\"ImageFormat\" : \"png\",\
|
||||
\"QImageFormat\" : \"Indexed8\",\
|
||||
\"ID\" : \"wmf-intl-%2x\",\
|
||||
\"ID\" : \"osm-%1x\",\
|
||||
\"MaximumZoomLevel\" : 18,\
|
||||
\"MapCopyRight\" : \"<a href='https://wikimediafoundation.org/wiki/Terms_of_Use'>WikiMedia Foundation</a>\",\
|
||||
\"DataCopyRight\" : \"<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\"\
|
||||
}").arg(hiresURL).arg(hires ? 1 : 2);
|
||||
}").arg(hires ? 1 : 2);
|
||||
}
|
||||
else if (tokens[1] == "/satellite")
|
||||
{
|
||||
|
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/5AH.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAF.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAH.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAL.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAQ.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAR.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAV.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAW.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAY.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAZ.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AB.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABA.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABB.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABD.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABG.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABL.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABN.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABP.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABQ.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABR.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABS.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABV.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABW.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABX.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABY.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ACA.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ACG.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ACI.png
Normal file
After Width: | Height: | Size: 2.7 KiB |