Merge pull request #688 from srcejon/adsb_improvements
ADS-B demodulator improvements
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 468 KiB After Width: | Height: | Size: 603 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 16 KiB |
@ -6,6 +6,7 @@ set(adsb_SOURCES
|
||||
adsbdemodwebapiadapter.cpp
|
||||
adsbplugin.cpp
|
||||
adsbdemodsink.cpp
|
||||
adsbdemodsinkworker.cpp
|
||||
adsbdemodbaseband.cpp
|
||||
adsbdemodreport.cpp
|
||||
adsbdemodworker.cpp
|
||||
@ -17,14 +18,17 @@ set(adsb_HEADERS
|
||||
adsbdemodwebapiadapter.h
|
||||
adsbplugin.h
|
||||
adsbdemodsink.h
|
||||
absddemodsinkworker.h
|
||||
adsbdemodbaseband.h
|
||||
adsbdemodreport.h
|
||||
adsbdemodworker.h
|
||||
adsbdemodstats.h
|
||||
adsb.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
${Boost_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
@ -32,10 +36,24 @@ if(NOT SERVER_MODE)
|
||||
${adsb_SOURCES}
|
||||
adsbdemodgui.cpp
|
||||
adsbdemodgui.ui
|
||||
adsbdemodfeeddialog.cpp
|
||||
adsbdemodfeeddialog.ui
|
||||
adsbdemoddisplaydialog.cpp
|
||||
adsbdemoddisplaydialog.ui
|
||||
csv.cpp
|
||||
airlinelogos.qrc
|
||||
flags.qrc
|
||||
map.qrc
|
||||
icons.qrc
|
||||
)
|
||||
set(adsb_HEADERS
|
||||
${adsb_HEADERS}
|
||||
adsbdemodgui.h
|
||||
adsbdemodfeeddialog.h
|
||||
adsbdemoddisplaydialog.h
|
||||
ourairports.h
|
||||
osndb.h
|
||||
csv.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME demodadsb)
|
||||
@ -53,7 +71,12 @@ add_library(${TARGET_NAME} SHARED
|
||||
${adsb_SOURCES}
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
link_directories(${Boost_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Boost::disable_autolinking
|
||||
Qt5::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
@ -66,5 +89,5 @@ install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
if(WIN32)
|
||||
# Run deployqt for QtQuick etc
|
||||
include(DeployQt)
|
||||
windeployqt(${TARGET_NAME} ${SDRANGEL_BINARY_BIN_DIR} ${PROJECT_SOURCE_DIR}/../../../sdrgui/resources)
|
||||
windeployqt(${TARGET_NAME} ${SDRANGEL_BINARY_BIN_DIR} ${PROJECT_SOURCE_DIR}/map)
|
||||
endif()
|
||||
|
@ -17,6 +17,9 @@
|
||||
// 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>
|
||||
|
||||
@ -109,6 +112,7 @@ void ADSBDemod::start()
|
||||
m_worker->reset();
|
||||
m_worker->startWork();
|
||||
m_basebandSink->reset();
|
||||
m_basebandSink->startWork();
|
||||
m_thread->start();
|
||||
|
||||
ADSBDemodWorker::MsgConfigureADSBDemodWorker *msg = ADSBDemodWorker::MsgConfigureADSBDemodWorker::create(m_settings, true);
|
||||
@ -118,6 +122,7 @@ void ADSBDemod::start()
|
||||
void ADSBDemod::stop()
|
||||
{
|
||||
qDebug() << "ADSBDemod::stop";
|
||||
m_basebandSink->stopWork();
|
||||
m_worker->stopWork();
|
||||
m_thread->exit();
|
||||
m_thread->wait();
|
||||
@ -288,13 +293,13 @@ void ADSBDemod::webapiUpdateChannelSettings(
|
||||
settings.m_removeTimeout = response.getAdsbDemodSettings()->getRemoveTimeout();
|
||||
}
|
||||
if (channelSettingsKeys.contains("beastEnabled")) {
|
||||
settings.m_beastEnabled = response.getAdsbDemodSettings()->getBeastEnabled() != 0;
|
||||
settings.m_feedEnabled = response.getAdsbDemodSettings()->getBeastEnabled() != 0;
|
||||
}
|
||||
if (channelSettingsKeys.contains("beastHost")) {
|
||||
settings.m_beastHost = *response.getAdsbDemodSettings()->getBeastHost();
|
||||
settings.m_feedHost = *response.getAdsbDemodSettings()->getBeastHost();
|
||||
}
|
||||
if (channelSettingsKeys.contains("beastPort")) {
|
||||
settings.m_beastPort = response.getAdsbDemodSettings()->getBeastPort();
|
||||
settings.m_feedPort = response.getAdsbDemodSettings()->getBeastPort();
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor")) {
|
||||
settings.m_rgbColor = response.getAdsbDemodSettings()->getRgbColor();
|
||||
@ -340,9 +345,9 @@ void ADSBDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res
|
||||
response.getAdsbDemodSettings()->setCorrelationThreshold(settings.m_correlationThreshold);
|
||||
response.getAdsbDemodSettings()->setSamplesPerBit(settings.m_samplesPerBit);
|
||||
response.getAdsbDemodSettings()->setRemoveTimeout(settings.m_removeTimeout);
|
||||
response.getAdsbDemodSettings()->setBeastEnabled(settings.m_beastEnabled ? 1 : 0);
|
||||
response.getAdsbDemodSettings()->setBeastHost(new QString(settings.m_beastHost));
|
||||
response.getAdsbDemodSettings()->setBeastPort(settings.m_beastPort);
|
||||
response.getAdsbDemodSettings()->setBeastEnabled(settings.m_feedEnabled ? 1 : 0);
|
||||
response.getAdsbDemodSettings()->setBeastHost(new QString(settings.m_feedHost));
|
||||
response.getAdsbDemodSettings()->setBeastPort(settings.m_feedPort);
|
||||
response.getAdsbDemodSettings()->setRgbColor(settings.m_rgbColor);
|
||||
|
||||
if (response.getAdsbDemodSettings()->getTitle()) {
|
||||
@ -408,13 +413,13 @@ void ADSBDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, c
|
||||
swgADSBDemodSettings->setRemoveTimeout(settings.m_removeTimeout);
|
||||
}
|
||||
if (channelSettingsKeys.contains("beastEnabled") || force) {
|
||||
swgADSBDemodSettings->setBeastEnabled(settings.m_beastEnabled ? 1 : 0);
|
||||
swgADSBDemodSettings->setBeastEnabled(settings.m_feedEnabled ? 1 : 0);
|
||||
}
|
||||
if (channelSettingsKeys.contains("beastHost") || force) {
|
||||
swgADSBDemodSettings->setBeastHost(new QString(settings.m_beastHost));
|
||||
swgADSBDemodSettings->setBeastHost(new QString(settings.m_feedHost));
|
||||
}
|
||||
if (channelSettingsKeys.contains("beastPort") || force) {
|
||||
swgADSBDemodSettings->setBeastPort(settings.m_beastPort);
|
||||
swgADSBDemodSettings->setBeastPort(settings.m_feedPort);
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor") || force) {
|
||||
swgADSBDemodSettings->setRgbColor(settings.m_rgbColor);
|
||||
|
@ -56,6 +56,16 @@ void ADSBDemodBaseband::reset()
|
||||
m_sampleFifo.reset();
|
||||
}
|
||||
|
||||
void ADSBDemodBaseband::startWork()
|
||||
{
|
||||
m_sink.startWorker();
|
||||
}
|
||||
|
||||
void ADSBDemodBaseband::stopWork()
|
||||
{
|
||||
m_sink.stopWorker();
|
||||
}
|
||||
|
||||
void ADSBDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
||||
{
|
||||
m_sampleFifo.write(begin, end);
|
||||
|
@ -60,6 +60,8 @@ public:
|
||||
ADSBDemodBaseband();
|
||||
~ADSBDemodBaseband();
|
||||
void reset();
|
||||
void startWork();
|
||||
void stopWork();
|
||||
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
|
||||
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
|
||||
int getChannelSampleRate() const;
|
||||
|
66
plugins/channelrx/demodadsb/adsbdemoddisplaydialog.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QFontDialog>
|
||||
#include <QDebug>
|
||||
|
||||
#include "adsbdemoddisplaydialog.h"
|
||||
|
||||
ADSBDemodDisplayDialog::ADSBDemodDisplayDialog
|
||||
(int removeTimeout, float airportRange, ADSBDemodSettings::AirportType airportMinimumSize,
|
||||
bool displayHeliports, bool siUnits, QString fontName, int fontSize, bool displayDemodStats, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ADSBDemodDisplayDialog),
|
||||
m_fontName(fontName),
|
||||
m_fontSize(fontSize)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->timeout->setValue(removeTimeout);
|
||||
ui->airportRange->setValue(airportRange);
|
||||
ui->airportSize->setCurrentIndex((int)airportMinimumSize);
|
||||
ui->heliports->setChecked(displayHeliports);
|
||||
ui->units->setCurrentIndex((int)siUnits);
|
||||
ui->displayStats->setChecked(displayDemodStats);
|
||||
}
|
||||
|
||||
ADSBDemodDisplayDialog::~ADSBDemodDisplayDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ADSBDemodDisplayDialog::accept()
|
||||
{
|
||||
m_removeTimeout = ui->timeout->value();
|
||||
m_airportRange = ui->airportRange->value();
|
||||
m_airportMinimumSize = (ADSBDemodSettings::AirportType)ui->airportSize->currentIndex();
|
||||
m_displayHeliports = ui->heliports->isChecked();
|
||||
m_siUnits = ui->units->currentIndex() == 0 ? false : true;
|
||||
m_displayDemodStats = ui->displayStats->isChecked();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void ADSBDemodDisplayDialog::on_font_clicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
QFont font = QFontDialog::getFont(&ok, QFont(m_fontName, m_fontSize), this);
|
||||
if (ok)
|
||||
{
|
||||
qDebug() << font;
|
||||
m_fontName = font.family();
|
||||
m_fontSize = font.pointSize();
|
||||
}
|
||||
}
|
50
plugins/channelrx/demodadsb/adsbdemoddisplaydialog.h
Normal file
@ -0,0 +1,50 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_ADSBDEMODDISPLAYDIALOG_H
|
||||
#define INCLUDE_ADSBDEMODDISPLAYDIALOG_H
|
||||
|
||||
#include "ui_adsbdemoddisplaydialog.h"
|
||||
#include "adsbdemodsettings.h"
|
||||
|
||||
class ADSBDemodDisplayDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ADSBDemodDisplayDialog(int removeTimeout, float airportRange, ADSBDemodSettings::AirportType airportMinimumSize,
|
||||
bool displayHeliports, bool siUnits, QString fontName, int fontSize, bool displayDemodStats
|
||||
, QWidget* parent = 0);
|
||||
~ADSBDemodDisplayDialog();
|
||||
|
||||
int m_removeTimeout;
|
||||
float m_airportRange;
|
||||
ADSBDemodSettings::AirportType m_airportMinimumSize;
|
||||
bool m_displayHeliports;
|
||||
bool m_siUnits;
|
||||
QString m_fontName;
|
||||
int m_fontSize;
|
||||
bool m_displayDemodStats;
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
void on_font_clicked(bool checked = false);
|
||||
|
||||
private:
|
||||
Ui::ADSBDemodDisplayDialog* ui;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_ADSBDEMODDISPLAYDIALOG_H
|
200
plugins/channelrx/demodadsb/adsbdemoddisplaydialog.ui
Normal file
@ -0,0 +1,200 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ADSBDemodDisplayDialog</class>
|
||||
<widget class="QDialog" name="ADSBDemodDisplayDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>351</width>
|
||||
<height>275</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Display Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="unitsLabel">
|
||||
<property name="text">
|
||||
<string>Units</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="units">
|
||||
<property name="toolTip">
|
||||
<string>The units to use for altitude, speed and climb rate</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ft, kn, ft/min</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>m, kph, m/s</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="airportSizeLabel">
|
||||
<property name="text">
|
||||
<string>Display airports with size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="airportSize">
|
||||
<property name="toolTip">
|
||||
<string>Sets the minimum airport size that will be displayed on the map</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Small</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Medium</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Large</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="heliports">
|
||||
<property name="toolTip">
|
||||
<string>When checked, heliports are displayed on the map</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Display heliports</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="airportRangeLabel">
|
||||
<property name="text">
|
||||
<string>Airport display range (nm)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="airportRange">
|
||||
<property name="toolTip">
|
||||
<string>Displays airports within the specified range in nautical miles from My Position</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>20000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="timeoutLabel">
|
||||
<property name="text">
|
||||
<string>Aircraft timeout (s)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QSpinBox" name="timeout">
|
||||
<property name="toolTip">
|
||||
<string>How long in seconds after not receving any frames will an aircraft be removed from the table and map</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="fontLabel">
|
||||
<property name="text">
|
||||
<string>Table font</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QPushButton" name="font">
|
||||
<property name="toolTip">
|
||||
<string>Select a font for the table</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="displayStats">
|
||||
<property name="toolTip">
|
||||
<string>Display demodulator statistics</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Display demodulator statistics</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ADSBDemodDisplayDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ADSBDemodDisplayDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
60
plugins/channelrx/demodadsb/adsbdemodfeeddialog.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "adsbdemodfeeddialog.h"
|
||||
#include "adsbdemodsettings.h"
|
||||
|
||||
ADSBDemodFeedDialog::ADSBDemodFeedDialog(QString& feedHost, int feedPort, ADSBDemodSettings::FeedFormat feedFormat, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ADSBDemodFeedDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->host->lineEdit()->setText(feedHost);
|
||||
ui->port->setValue(feedPort);
|
||||
ui->format->setCurrentIndex((int)feedFormat);
|
||||
}
|
||||
|
||||
ADSBDemodFeedDialog::~ADSBDemodFeedDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ADSBDemodFeedDialog::accept()
|
||||
{
|
||||
m_feedHost = ui->host->currentText();
|
||||
m_feedPort = ui->port->value();
|
||||
m_feedFormat = (ADSBDemodSettings::FeedFormat )ui->format->currentIndex();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void ADSBDemodFeedDialog::on_host_currentIndexChanged(int value)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
ui->host->lineEdit()->setText("feed.adsbexchange.com");
|
||||
ui->port->setValue(30005);
|
||||
ui->format->setCurrentIndex(0);
|
||||
}
|
||||
else if (value == 1)
|
||||
{
|
||||
ui->host->lineEdit()->setText("data.adsbhub.org");
|
||||
ui->port->setValue(5002);
|
||||
ui->format->setCurrentIndex(1);
|
||||
}
|
||||
}
|
43
plugins/channelrx/demodadsb/adsbdemodfeeddialog.h
Normal file
@ -0,0 +1,43 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_ADSBDEMODFEEDDIALOG_H
|
||||
#define INCLUDE_ADSBDEMODFEEDDIALOG_H
|
||||
|
||||
#include "ui_adsbdemodfeeddialog.h"
|
||||
#include "adsbdemodsettings.h"
|
||||
|
||||
class ADSBDemodFeedDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ADSBDemodFeedDialog(QString& feedHost, int feedPort, ADSBDemodSettings::FeedFormat feedFormat, QWidget* parent = 0);
|
||||
~ADSBDemodFeedDialog();
|
||||
|
||||
QString m_feedHost;
|
||||
int m_feedPort;
|
||||
ADSBDemodSettings::FeedFormat m_feedFormat;
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
void on_host_currentIndexChanged(int value);
|
||||
|
||||
private:
|
||||
Ui::ADSBDemodFeedDialog* ui;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_ADSBDEMODFEEDDIALOG_H
|
150
plugins/channelrx/demodadsb/adsbdemodfeeddialog.ui
Normal file
@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ADSBDemodFeedDialog</class>
|
||||
<widget class="QDialog" name="ADSBDemodFeedDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>351</width>
|
||||
<height>138</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Feed Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="hostLabel">
|
||||
<property name="text">
|
||||
<string>Server hostname</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="portLabel">
|
||||
<property name="text">
|
||||
<string>Port number</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="port">
|
||||
<property name="toolTip">
|
||||
<string>The TCP port number the server is listening on</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1024</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="format">
|
||||
<property name="toolTip">
|
||||
<string>Format to feed the data to the server in</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Beast binary</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Beast hex</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="formatLabel">
|
||||
<property name="text">
|
||||
<string>Format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="host">
|
||||
<property name="toolTip">
|
||||
<string>Hostname of server to feed ADS-B data to</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>feed.adsbexchange.com</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>data.adsbhub.org</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>port</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ADSBDemodFeedDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ADSBDemodFeedDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -20,9 +20,11 @@
|
||||
#define INCLUDE_ADSBDEMODGUI_H
|
||||
|
||||
#include <QTableWidgetItem>
|
||||
#include <QMenu>
|
||||
#include <QGeoCoordinate>
|
||||
#include <QDateTime>
|
||||
#include <QAbstractListModel>
|
||||
#include <QProgressDialog>
|
||||
|
||||
#include "channel/channelgui.h"
|
||||
#include "dsp/dsptypes.h"
|
||||
@ -31,18 +33,46 @@
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/azel.h"
|
||||
#include "util/movingaverage.h"
|
||||
#include "util/httpdownloadmanager.h"
|
||||
|
||||
#include "adsbdemodsettings.h"
|
||||
#include "ourairportsdb.h"
|
||||
#include "osndb.h"
|
||||
|
||||
class PluginAPI;
|
||||
class DeviceUISet;
|
||||
class BasebandSampleSink;
|
||||
class ADSBDemod;
|
||||
class WebAPIAdapterInterface;
|
||||
class HttpDownloadManager;
|
||||
class ADSBDemodGUI;
|
||||
|
||||
namespace Ui {
|
||||
class ADSBDemodGUI;
|
||||
}
|
||||
|
||||
// Custom widget to allow formatted decimal numbers to be sorted numerically
|
||||
class CustomDoubleTableWidgetItem : public QTableWidgetItem
|
||||
{
|
||||
public:
|
||||
CustomDoubleTableWidgetItem(const QString text = QString("")) :
|
||||
QTableWidgetItem(text)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator <(const QTableWidgetItem& other) const
|
||||
{
|
||||
// Treat "" as less than 0
|
||||
QString thisText = text();
|
||||
QString otherText = other.text();
|
||||
if (thisText == "")
|
||||
return true;
|
||||
if (otherText == "")
|
||||
return false;
|
||||
return thisText.toDouble() < otherText.toDouble();
|
||||
}
|
||||
};
|
||||
|
||||
// Data about an aircraft extracted from an ADS-B frames
|
||||
struct Aircraft {
|
||||
int m_icao; // 24-bit ICAO aircraft address
|
||||
@ -76,31 +106,49 @@ struct Aircraft {
|
||||
bool m_cprValid[2];
|
||||
Real m_cprLat[2];
|
||||
Real m_cprLong[2];
|
||||
QDateTime m_cprTime[2];
|
||||
|
||||
int m_adsbFrameCount; // Number of ADS-B frames for this aircraft
|
||||
float m_minCorrelation;
|
||||
float m_maxCorrelation;
|
||||
float m_correlation;
|
||||
bool m_isBeingTracked; // Are we tracking this aircraft
|
||||
MovingAverageUtil<float, double, 100> m_correlationAvg;
|
||||
|
||||
bool m_isTarget; // Are we targetting this aircraft (sending az/el to rotator)
|
||||
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
|
||||
|
||||
AircraftInformation *m_aircraftInfo; // Info about the aircraft from the database
|
||||
ADSBDemodGUI *m_gui;
|
||||
|
||||
// GUI table items for above data
|
||||
QTableWidgetItem *m_icaoItem;
|
||||
QTableWidgetItem *m_flightItem;
|
||||
QTableWidgetItem *m_modelItem;
|
||||
QTableWidgetItem *m_airlineItem;
|
||||
QTableWidgetItem *m_latitudeItem;
|
||||
QTableWidgetItem *m_longitudeItem;
|
||||
QTableWidgetItem *m_altitudeItem;
|
||||
QTableWidgetItem *m_speedItem;
|
||||
QTableWidgetItem *m_headingItem;
|
||||
QTableWidgetItem *m_verticalRateItem;
|
||||
CustomDoubleTableWidgetItem *m_rangeItem;
|
||||
QTableWidgetItem *m_azElItem;
|
||||
QTableWidgetItem *m_emitterCategoryItem;
|
||||
QTableWidgetItem *m_statusItem;
|
||||
QTableWidgetItem *m_rangeItem;
|
||||
QTableWidgetItem *m_azElItem;
|
||||
QTableWidgetItem *m_registrationItem;
|
||||
QTableWidgetItem *m_countryItem;
|
||||
QTableWidgetItem *m_registeredItem;
|
||||
QTableWidgetItem *m_manufacturerNameItem;
|
||||
QTableWidgetItem *m_ownerItem;
|
||||
QTableWidgetItem *m_operatorICAOItem;
|
||||
QTableWidgetItem *m_timeItem;
|
||||
QTableWidgetItem *m_adsbFrameCountItem;
|
||||
QTableWidgetItem *m_correlationItem;
|
||||
|
||||
Aircraft() :
|
||||
Aircraft(ADSBDemodGUI *gui) :
|
||||
m_icao(0),
|
||||
m_latitude(0),
|
||||
m_longitude(0),
|
||||
@ -119,7 +167,11 @@ struct Aircraft {
|
||||
m_minCorrelation(INFINITY),
|
||||
m_maxCorrelation(-INFINITY),
|
||||
m_correlation(0.0f),
|
||||
m_isBeingTracked(false)
|
||||
m_isTarget(false),
|
||||
m_isHighlighted(false),
|
||||
m_showAll(false),
|
||||
m_aircraftInfo(nullptr),
|
||||
m_gui(gui)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
@ -128,16 +180,24 @@ struct Aircraft {
|
||||
// These are deleted by QTableWidget
|
||||
m_icaoItem = new QTableWidgetItem();
|
||||
m_flightItem = new QTableWidgetItem();
|
||||
m_latitudeItem = new QTableWidgetItem();
|
||||
m_longitudeItem = new QTableWidgetItem();
|
||||
m_modelItem = new QTableWidgetItem();
|
||||
m_airlineItem = new QTableWidgetItem();
|
||||
m_altitudeItem = new QTableWidgetItem();
|
||||
m_speedItem = new QTableWidgetItem();
|
||||
m_headingItem = new QTableWidgetItem();
|
||||
m_verticalRateItem = new QTableWidgetItem();
|
||||
m_rangeItem = new CustomDoubleTableWidgetItem();
|
||||
m_azElItem = new QTableWidgetItem();
|
||||
m_latitudeItem = new QTableWidgetItem();
|
||||
m_longitudeItem = new QTableWidgetItem();
|
||||
m_emitterCategoryItem = new QTableWidgetItem();
|
||||
m_statusItem = new QTableWidgetItem();
|
||||
m_rangeItem = new QTableWidgetItem();
|
||||
m_azElItem = 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_timeItem = new QTableWidgetItem();
|
||||
m_adsbFrameCountItem = new QTableWidgetItem();
|
||||
m_correlationItem = new QTableWidgetItem();
|
||||
@ -155,7 +215,11 @@ public:
|
||||
headingRole = Qt::UserRole + 2,
|
||||
adsbDataRole = Qt::UserRole + 3,
|
||||
aircraftImageRole = Qt::UserRole + 4,
|
||||
bubbleColourRole = Qt::UserRole + 5
|
||||
bubbleColourRole = Qt::UserRole + 5,
|
||||
aircraftPathRole = Qt::UserRole + 6,
|
||||
showAllRole = Qt::UserRole + 7,
|
||||
highlightedRole = Qt::UserRole + 8,
|
||||
targetRole = Qt::UserRole + 9
|
||||
};
|
||||
|
||||
Q_INVOKABLE void addAircraft(Aircraft *aircraft) {
|
||||
@ -171,6 +235,12 @@ public:
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override {
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
void aircraftUpdated(Aircraft *aircraft) {
|
||||
int row = m_aircrafts.indexOf(aircraft);
|
||||
if (row >= 0)
|
||||
@ -180,6 +250,19 @@ 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++)
|
||||
{
|
||||
QModelIndex idx = index(i);
|
||||
emit dataChanged(idx, idx);
|
||||
}
|
||||
}
|
||||
|
||||
void removeAircraft(Aircraft *aircraft) {
|
||||
int row = m_aircrafts.indexOf(aircraft);
|
||||
if (row >= 0)
|
||||
@ -197,11 +280,136 @@ public:
|
||||
roles[adsbDataRole] = "adsbData";
|
||||
roles[aircraftImageRole] = "aircraftImage";
|
||||
roles[bubbleColourRole] = "bubbleColour";
|
||||
roles[aircraftPathRole] = "aircraftPath";
|
||||
roles[showAllRole] = "showAll";
|
||||
roles[highlightedRole] = "highlighted";
|
||||
roles[targetRole] = "target";
|
||||
return roles;
|
||||
}
|
||||
|
||||
void setFlightPaths(bool flightPaths)
|
||||
{
|
||||
m_flightPaths = flightPaths;
|
||||
allAircraftUpdated();
|
||||
}
|
||||
|
||||
private:
|
||||
QList<Aircraft *> m_aircrafts;
|
||||
bool m_flightPaths;
|
||||
};
|
||||
|
||||
// Airport data model used by QML map item
|
||||
class AirportModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using QAbstractListModel::QAbstractListModel;
|
||||
enum MarkerRoles {
|
||||
positionRole = Qt::UserRole + 1,
|
||||
airportDataRole = Qt::UserRole + 2,
|
||||
airportDataRowsRole = Qt::UserRole + 3,
|
||||
airportImageRole = Qt::UserRole + 4,
|
||||
bubbleColourRole = Qt::UserRole + 5,
|
||||
showFreqRole = Qt::UserRole + 6,
|
||||
selectedFreqRole = Qt::UserRole + 7
|
||||
};
|
||||
|
||||
AirportModel(ADSBDemodGUI *gui) :
|
||||
m_gui(gui)
|
||||
{
|
||||
}
|
||||
|
||||
Q_INVOKABLE void addAirport(AirportInformation *airport) {
|
||||
QString text;
|
||||
int rows;
|
||||
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_airports.append(airport);
|
||||
airportFreq(airport, text, rows);
|
||||
m_airportDataFreq.append(text);
|
||||
m_airportDataFreqRows.append(rows);
|
||||
m_showFreq.append(false);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void removeAirport(AirportInformation *airport) {
|
||||
int row = m_airports.indexOf(airport);
|
||||
if (row >= 0)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
m_airports.removeAt(row);
|
||||
m_airportDataFreq.removeAt(row);
|
||||
m_airportDataFreqRows.removeAt(row);
|
||||
m_showFreq.removeAt(row);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
void removeAllAirports() {
|
||||
beginRemoveRows(QModelIndex(), 0, m_airports.count());
|
||||
m_airports.clear();
|
||||
m_airportDataFreq.clear();
|
||||
m_airportDataFreqRows.clear();
|
||||
m_showFreq.clear();
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
|
||||
Q_UNUSED(parent)
|
||||
return m_airports.count();
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override {
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
void airportFreq(AirportInformation *airport, QString& text, int& rows) {
|
||||
// Create the text to go in the bubble next to the airport
|
||||
// Display name and frequencies
|
||||
QStringList list;
|
||||
|
||||
list.append(QString("%1: %2").arg(airport->m_ident).arg(airport->m_name));
|
||||
rows = 1;
|
||||
for (int i = 0; i < airport->m_frequencies.size(); i++)
|
||||
{
|
||||
AirportInformation::FrequencyInformation *frequencyInfo = airport->m_frequencies[i];
|
||||
list.append(QString("%1: %2 MHz").arg(frequencyInfo->m_type).arg(frequencyInfo->m_frequency));
|
||||
rows++;
|
||||
}
|
||||
text = list.join("\n");
|
||||
}
|
||||
|
||||
void airportUpdated(AirportInformation *airport) {
|
||||
int row = m_airports.indexOf(airport);
|
||||
if (row >= 0)
|
||||
{
|
||||
QModelIndex idx = index(row);
|
||||
emit dataChanged(idx, idx);
|
||||
}
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> roleNames() const {
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[positionRole] = "position";
|
||||
roles[airportDataRole] = "airportData";
|
||||
roles[airportDataRowsRole] = "airportDataRows";
|
||||
roles[airportImageRole] = "airportImage";
|
||||
roles[bubbleColourRole] = "bubbleColour";
|
||||
roles[showFreqRole] = "showFreq";
|
||||
roles[selectedFreqRole] = "selectedFreq";
|
||||
return roles;
|
||||
}
|
||||
|
||||
private:
|
||||
ADSBDemodGUI *m_gui;
|
||||
QList<AirportInformation *> m_airports;
|
||||
QList<QString> m_airportDataFreq;
|
||||
QList<int> m_airportDataFreqRows;
|
||||
QList<bool> m_showFreq;
|
||||
};
|
||||
|
||||
class ADSBDemodGUI : public ChannelGUI {
|
||||
@ -215,6 +423,10 @@ public:
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
void highlightAircraft(Aircraft *aircraft);
|
||||
void targetAircraft(Aircraft *aircraft);
|
||||
bool setFrequency(float frequency);
|
||||
bool useSIUints() { return m_settings.m_siUnits; }
|
||||
|
||||
public slots:
|
||||
void channelMarkerChangedByCursor();
|
||||
@ -233,13 +445,30 @@ private:
|
||||
uint32_t m_tickCount;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
|
||||
QHash<int,Aircraft *> m_aircraft; // Hashed on ICAO
|
||||
QHash<int, Aircraft *> m_aircraft; // Hashed on ICAO
|
||||
QHash<int, AircraftInformation *> *m_aircraftInfo;
|
||||
QHash<int, AirportInformation *> *m_airportInfo; // Hashed on id
|
||||
AircraftModel m_aircraftModel;
|
||||
AirportModel m_airportModel;
|
||||
QHash<QString, QIcon *> m_airlineIcons; // Hashed on airline ICAO
|
||||
QHash<QString, QIcon *> m_flagIcons; // Hashed on country
|
||||
QHash<QString, QString> *m_prefixMap; // Registration to country (flag name)
|
||||
QHash<QString, QString> *m_militaryMap; // Operator airforce to military (flag name)
|
||||
|
||||
AzEl m_azEl; // Position of station
|
||||
Aircraft *m_trackAircraft; // Aircraft we want to track in Channel Report
|
||||
MovingAverageUtil<float, double, 10> m_correlationOnesAvg;
|
||||
MovingAverageUtil<float, double, 10> m_correlationZerosAvg;
|
||||
MovingAverageUtil<float, double, 10> m_correlationAvg;
|
||||
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;
|
||||
|
||||
QMenu *menu; // Column select context menu
|
||||
|
||||
WebAPIAdapterInterface *m_webAPIAdapterInterface;
|
||||
HttpDownloadManager m_dlm;
|
||||
QProgressDialog *m_progressDialog;
|
||||
|
||||
explicit ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
||||
virtual ~ADSBDemodGUI();
|
||||
@ -250,8 +479,22 @@ private:
|
||||
void displayStreamIndex();
|
||||
bool handleMessage(const Message& message);
|
||||
void updatePosition(Aircraft *aircraft);
|
||||
void handleADSB(const QByteArray data, const QDateTime dateTime, float correlationOnes, float correlationZeros);
|
||||
void handleADSB(const QByteArray data, const QDateTime dateTime, float correlation);
|
||||
void resizeTable();
|
||||
QString getDataDir();
|
||||
QString getAirportDBFilename();
|
||||
QString getAirportFrequenciesDBFilename();
|
||||
QString getOSNDBFilename();
|
||||
QString getFastDBFilename();
|
||||
void readAirportDB(const QString& filename);
|
||||
void readAirportFrequenciesDB(const QString& filename);
|
||||
bool readOSNDB(const QString& filename);
|
||||
bool readFastDB(const QString& filename);
|
||||
void updateAirports();
|
||||
QIcon *getAirlineIcon(const QString &operatorICAO);
|
||||
QIcon *getFlagIcon(const QString &country);
|
||||
void updateDeviceSetList();
|
||||
QAction *createCheckableItem(QString& text, int idx, bool checked);
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(QEvent*);
|
||||
@ -260,15 +503,29 @@ private slots:
|
||||
void on_deltaFrequency_changed(qint64 value);
|
||||
void on_rfBW_valueChanged(int value);
|
||||
void on_threshold_valueChanged(int value);
|
||||
void on_adsbData_cellClicked(int row, int column);
|
||||
void on_adsbData_cellDoubleClicked(int row, int column);
|
||||
void adsbData_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
|
||||
void adsbData_sectionResized(int logicalIndex, int oldSize, int newSize);
|
||||
void columnSelectMenu(QPoint pos);
|
||||
void columnSelectMenuChecked(bool checked = false);
|
||||
void on_spb_currentIndexChanged(int value);
|
||||
void on_beastEnabled_stateChanged(int state);
|
||||
void on_host_editingFinished(QString value);
|
||||
void on_port_valueChanged(int value);
|
||||
void on_correlateFullPreamble_clicked(bool checked=false);
|
||||
void on_demodModeS_clicked(bool checked=false);
|
||||
void on_feed_clicked(bool checked=false);
|
||||
void on_getOSNDB_clicked(bool checked = false);
|
||||
void on_getAirportDB_clicked(bool checked = false);
|
||||
void on_flightPaths_clicked(bool checked = false);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
void handleInputMessages();
|
||||
void tick();
|
||||
void updateDownloadProgress(qint64 bytesRead, qint64 totalBytes);
|
||||
void downloadFinished(const QString& filename, bool success);
|
||||
void on_devicesRefresh_clicked();
|
||||
void on_device_currentIndexChanged(int index);
|
||||
void feedSelect();
|
||||
void on_displaySettings_clicked(bool checked=false);
|
||||
signals:
|
||||
void homePositionChanged();
|
||||
};
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>350</width>
|
||||
<height>1019</height>
|
||||
<height>1046</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -37,7 +37,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>340</width>
|
||||
<height>101</height>
|
||||
<height>141</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
@ -326,6 +326,39 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="demodModeS">
|
||||
<property name="toolTip">
|
||||
<string>Demodulate all Mode-S frames, not just ADS-B</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>S</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="correlateFullPreamble">
|
||||
<property name="toolTip">
|
||||
<string>Correlate against full preamble.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>FP</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="thresholdLabel">
|
||||
<property name="text">
|
||||
@ -342,10 +375,10 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Correlation threshold in dB. Lower values will increase the number of frames that can be received, but require more processing.</string>
|
||||
<string>Correlation threshold in dB. Lower values will increase the number of frames that can be received, but require more processing and possibly result in invalid frames.</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-990</number>
|
||||
<number>-450</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
@ -354,7 +387,7 @@
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>-500</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -376,58 +409,132 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="beastEnabled">
|
||||
<widget class="QToolButton" name="getOSNDB">
|
||||
<property name="toolTip">
|
||||
<string>Enable feeding of received ADS-B messages in Beast binary format to the specifed server</string>
|
||||
<string>Download the latest Opensky-Network aircraft database (80MB)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Feed</string>
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/aircraft.png</normaloff>:/icons/aircraft.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="hostLabel">
|
||||
<property name="text">
|
||||
<string>Server</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="host">
|
||||
<widget class="QToolButton" name="getAirportDB">
|
||||
<property name="toolTip">
|
||||
<string>Hostname of the server to feed</string>
|
||||
<string>Download the latest OurAirports airport databases (10MB)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>feed.adsbexchange.com</string>
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/controltower.png</normaloff>:/icons/controltower.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="portLabel">
|
||||
<widget class="QToolButton" name="displaySettings">
|
||||
<property name="toolTip">
|
||||
<string>Open display settings dialog</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Port</string>
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/listing.png</normaloff>:/listing.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="port">
|
||||
<property name="minimumSize">
|
||||
<widget class="QToolButton" name="flightPaths">
|
||||
<property name="toolTip">
|
||||
<string>Display flight paths</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/logarithmic.png</normaloff>:/logarithmic.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="feed">
|
||||
<property name="toolTip">
|
||||
<string>Enable feeding of received ADS-B messages to the specifed server. Right click for settings.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/txon.png</normaloff>:/txon.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="stats">
|
||||
<property name="toolTip">
|
||||
<string>Demodulator statistics</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>0</height>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="devicesRefresh">
|
||||
<property name="toolTip">
|
||||
<string>Port the server is listening on</string>
|
||||
<string>Refresh device list</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1024</number>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>30005</number>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Device</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="device">
|
||||
<property name="toolTip">
|
||||
<string>Receive device set to set frequency on when selecting an ATC frequency on the map</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -439,7 +546,7 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>114</y>
|
||||
<y>140</y>
|
||||
<width>341</width>
|
||||
<height>291</height>
|
||||
</rect>
|
||||
@ -450,11 +557,6 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Mono</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>ADS-B Data</string>
|
||||
</property>
|
||||
@ -476,11 +578,6 @@
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="adsbData">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Mono</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
@ -489,7 +586,7 @@
|
||||
<string>ICAO ID</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string extracomment="Double click to search for the aircraft on www.planespotters.net">International Civil Aviation Organization identifier. Links to www.planespotters.net</string>
|
||||
<string extracomment="Double click to search for the aircraft on www.planespotters.net">Aircraft International Civil Aviation Organization identifier. Links to www.planespotters.net</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
@ -500,6 +597,70 @@
|
||||
<string>Commercial flight number. Links to www.flightradar24.com</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Aircraft</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Aircraft model</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Airline</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Airline logo</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Alt (ft)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Altitude in feet or metres</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Spd (kn)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Speed in knots or kilometres per hour</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Hd (°)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Aircraft heading in degrees</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>VR (ft/m)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Vertical climb rate in feet per minute or metres per second</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>D (km)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Range or distance of aircraft from home location</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Az/El (°)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Azimuth and elevation to aircraft from My Position. Double click to set as target.</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Lat (°)</string>
|
||||
@ -518,39 +679,7 @@
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Alt (ft)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Altitude in feet</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Sp (kn)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Speed in knots</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Hd (°)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Aircraft heading in degrees</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Climb</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Climbing rate in feet per minute</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Category</string>
|
||||
<string>Cat</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Aircraft standard category</string>
|
||||
@ -561,23 +690,55 @@
|
||||
<string>Status</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Aircraft standard status</string>
|
||||
<string>Aircraft emergency status</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>D (km)</string>
|
||||
<string>Reg</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Range or distance of aircraft to home location</string>
|
||||
<string>Aircraft registration</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Az/El (°)</string>
|
||||
<string>Country</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Aircraft azimuth and elevation from home point in degrees</string>
|
||||
<string>Country of registration</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Registered</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Date aircraft was registered</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Manufacturer</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Aircraft manufacturer</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Owner</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Owner of the aircraft</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Operator</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Aircraft operator ICAO code</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
@ -585,7 +746,7 @@
|
||||
<string>Updated</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Last time updated</string>
|
||||
<string>Time when the last ADS-B message from this aircraft was received.</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
@ -593,7 +754,7 @@
|
||||
<string>Frames</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Number of frames received</string>
|
||||
<string>Number of ADS-B frames received from this aircraft</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
@ -601,7 +762,7 @@
|
||||
<string>Correlation</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Correlation power min/avg/max in dB</string>
|
||||
<string>Correlation values for received frames. min/avg/max</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
@ -612,7 +773,7 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>420</y>
|
||||
<y>450</y>
|
||||
<width>331</width>
|
||||
<height>581</height>
|
||||
</rect>
|
||||
@ -653,18 +814,18 @@
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>400</height>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Aircraft location map</string>
|
||||
<string>Aircraft map</string>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QQuickWidget::SizeRootObjectToView</enum>
|
||||
</property>
|
||||
<property name="source">
|
||||
<url>
|
||||
<string>qrc:/map.qml</string>
|
||||
<string/>
|
||||
</url>
|
||||
</property>
|
||||
</widget>
|
||||
@ -702,14 +863,10 @@
|
||||
<tabstop>rfBW</tabstop>
|
||||
<tabstop>spb</tabstop>
|
||||
<tabstop>threshold</tabstop>
|
||||
<tabstop>beastEnabled</tabstop>
|
||||
<tabstop>host</tabstop>
|
||||
<tabstop>port</tabstop>
|
||||
<tabstop>adsbData</tabstop>
|
||||
<tabstop>map</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
<include location="icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -19,3 +19,4 @@
|
||||
#include "adsbdemodreport.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(ADSBDemodReport::MsgReportADSB, Message)
|
||||
MESSAGE_CLASS_DEFINITION(ADSBDemodReport::MsgReportDemodStats, Message)
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "util/message.h"
|
||||
#include "adsbdemodstats.h"
|
||||
|
||||
class ADSBDemodReport : public QObject
|
||||
{
|
||||
@ -36,30 +37,48 @@ public:
|
||||
public:
|
||||
QByteArray getData() const { return m_data; }
|
||||
QDateTime getDateTime() const { return m_dateTime; }
|
||||
float getPreambleCorrelationOnes() const { return m_premableCorrelationOnes; }
|
||||
float getPreambleCorrelationZeros() const { return m_premableCorrelationZeros; }
|
||||
float getPreambleCorrelation() const { return m_preambleCorrelation; }
|
||||
|
||||
static MsgReportADSB* create(QByteArray data, float premableCorrelationOnes, float premableCorrelationZeros)
|
||||
static MsgReportADSB* create(QByteArray data, float preambleCorrelation)
|
||||
{
|
||||
return new MsgReportADSB(data, premableCorrelationOnes, premableCorrelationZeros);
|
||||
return new MsgReportADSB(data, preambleCorrelation);
|
||||
}
|
||||
|
||||
private:
|
||||
QByteArray m_data;
|
||||
QDateTime m_dateTime;
|
||||
float m_premableCorrelationOnes;
|
||||
float m_premableCorrelationZeros;
|
||||
float m_preambleCorrelation;
|
||||
|
||||
MsgReportADSB(QByteArray data, float premableCorrelationOnes, float premableCorrelationZeros) :
|
||||
MsgReportADSB(QByteArray data, float preambleCorrelation) :
|
||||
Message(),
|
||||
m_data(data),
|
||||
m_premableCorrelationOnes(premableCorrelationOnes),
|
||||
m_premableCorrelationZeros(premableCorrelationZeros)
|
||||
m_preambleCorrelation(preambleCorrelation)
|
||||
{
|
||||
m_dateTime = QDateTime::currentDateTime();
|
||||
}
|
||||
};
|
||||
|
||||
class MsgReportDemodStats : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
ADSBDemodStats getDemodStats() const { return m_demodStats; }
|
||||
|
||||
static MsgReportDemodStats* create(ADSBDemodStats demodStats)
|
||||
{
|
||||
return new MsgReportDemodStats(demodStats);
|
||||
}
|
||||
|
||||
private:
|
||||
ADSBDemodStats m_demodStats;
|
||||
|
||||
MsgReportDemodStats(ADSBDemodStats demodStats) :
|
||||
Message(),
|
||||
m_demodStats(demodStats)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
ADSBDemodReport() {}
|
||||
~ADSBDemodReport() {}
|
||||
|
@ -34,12 +34,13 @@ void ADSBDemodSettings::resetToDefaults()
|
||||
{
|
||||
m_inputFrequencyOffset = 0;
|
||||
m_rfBandwidth = 2*1300000;
|
||||
m_correlationThreshold = -50.0f;
|
||||
m_correlationThreshold = -20.0f;
|
||||
m_samplesPerBit = 4;
|
||||
m_removeTimeout = 60;
|
||||
m_beastEnabled = false;
|
||||
m_beastHost = "feed.adsbexchange.com";
|
||||
m_beastPort = 30005;
|
||||
m_feedEnabled = false;
|
||||
m_feedHost = "feed.adsbexchange.com";
|
||||
m_feedPort = 30005;
|
||||
m_feedFormat = BeastBinary;
|
||||
m_rgbColor = QColor(255, 0, 0).rgb();
|
||||
m_title = "ADS-B Demodulator";
|
||||
m_streamIndex = 0;
|
||||
@ -48,6 +49,22 @@ void ADSBDemodSettings::resetToDefaults()
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIDeviceIndex = 0;
|
||||
m_reverseAPIChannelIndex = 0;
|
||||
m_airportRange = 100;
|
||||
m_airportMinimumSize = AirportType::Medium;
|
||||
m_displayHeliports = false;
|
||||
m_flightPaths = true;
|
||||
m_siUnits = false;
|
||||
m_tableFontName = "Liberation Sans";
|
||||
m_tableFontSize = 9;
|
||||
m_displayDemodStats = true;
|
||||
m_correlateFullPreamble = true;
|
||||
m_demodModeS = false;
|
||||
m_deviceIndex = -1;
|
||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||
{
|
||||
m_columnIndexes[i] = i;
|
||||
m_columnSizes[i] = -1; // Autosize
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray ADSBDemodSettings::serialize() const
|
||||
@ -58,9 +75,9 @@ QByteArray ADSBDemodSettings::serialize() const
|
||||
s.writeReal(3, m_correlationThreshold);
|
||||
s.writeS32(4, m_samplesPerBit);
|
||||
s.writeS32(5, m_removeTimeout);
|
||||
s.writeBool(6, m_beastEnabled);
|
||||
s.writeString(7, m_beastHost);
|
||||
s.writeU32(8, m_beastPort);
|
||||
s.writeBool(6, m_feedEnabled);
|
||||
s.writeString(7, m_feedHost);
|
||||
s.writeU32(8, m_feedPort);
|
||||
|
||||
s.writeU32(9, m_rgbColor);
|
||||
if (m_channelMarker) {
|
||||
@ -74,6 +91,24 @@ QByteArray ADSBDemodSettings::serialize() const
|
||||
s.writeU32(16, m_reverseAPIChannelIndex);
|
||||
s.writeS32(17, m_streamIndex);
|
||||
|
||||
s.writeFloat(18, m_airportRange);
|
||||
s.writeS32(19, (int)m_airportMinimumSize);
|
||||
s.writeBool(20, m_displayHeliports);
|
||||
s.writeBool(21, m_flightPaths);
|
||||
s.writeS32(22, m_deviceIndex);
|
||||
s.writeBool(23, m_siUnits);
|
||||
s.writeS32(24, (int)m_feedFormat);
|
||||
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);
|
||||
|
||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||
s.writeS32(100 + i, m_columnIndexes[i]);
|
||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||
s.writeS32(200 + i, m_columnSizes[i]);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
@ -102,16 +137,16 @@ 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, -50.0f);
|
||||
d.readReal(3, &m_correlationThreshold, 0.0f);
|
||||
d.readS32(4, &m_samplesPerBit, 4);
|
||||
d.readS32(5, &m_removeTimeout, 60);
|
||||
d.readBool(6, &m_beastEnabled, false);
|
||||
d.readString(7, &m_beastHost, "feed.adsbexchange.com");
|
||||
d.readBool(6, &m_feedEnabled, false);
|
||||
d.readString(7, &m_feedHost, "feed.adsbexchange.com");
|
||||
d.readU32(8, &utmp, 0);
|
||||
if ((utmp > 1023) && (utmp < 65535)) {
|
||||
m_beastPort = utmp;
|
||||
m_feedPort = utmp;
|
||||
} else {
|
||||
m_beastPort = 30005;
|
||||
m_feedPort = 30005;
|
||||
}
|
||||
|
||||
d.readU32(9, &m_rgbColor, QColor(255, 0, 0).rgb());
|
||||
@ -132,6 +167,24 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
||||
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
|
||||
d.readS32(17, &m_streamIndex, 0);
|
||||
|
||||
d.readFloat(18, &m_airportRange, 100);
|
||||
d.readS32(19, (int *)&m_airportMinimumSize, AirportType::Medium);
|
||||
d.readBool(20, &m_displayHeliports, false);
|
||||
d.readBool(21, &m_flightPaths, true);
|
||||
d.readS32(22, &m_deviceIndex, -1);
|
||||
d.readBool(23, &m_siUnits, false);
|
||||
d.readS32(24, (int *) &m_feedFormat, BeastBinary);
|
||||
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, false);
|
||||
|
||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||
d.readS32(100 + i, &m_columnIndexes[i], i);
|
||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||
d.readS32(200 + i, &m_columnSizes[i], -1);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -26,6 +26,9 @@
|
||||
|
||||
class Serializable;
|
||||
|
||||
// Number of columns in the table
|
||||
#define ADSBDEMOD_COLUMNS 23
|
||||
|
||||
struct ADSBDemodSettings
|
||||
{
|
||||
int32_t m_inputFrequencyOffset;
|
||||
@ -33,9 +36,13 @@ struct ADSBDemodSettings
|
||||
Real m_correlationThreshold; //!< Correlation power threshold in dB
|
||||
int m_samplesPerBit;
|
||||
int m_removeTimeout; //!< Time in seconds before removing an aircraft, unless a new frame is received
|
||||
bool m_beastEnabled;
|
||||
QString m_beastHost;
|
||||
uint16_t m_beastPort;
|
||||
bool m_feedEnabled;
|
||||
QString m_feedHost;
|
||||
uint16_t m_feedPort;
|
||||
enum FeedFormat {
|
||||
BeastBinary,
|
||||
BeastHex
|
||||
} m_feedFormat;
|
||||
|
||||
quint32 m_rgbColor;
|
||||
QString m_title;
|
||||
@ -45,9 +52,28 @@ struct ADSBDemodSettings
|
||||
uint16_t m_reverseAPIPort;
|
||||
uint16_t m_reverseAPIDeviceIndex;
|
||||
uint16_t m_reverseAPIChannelIndex;
|
||||
int m_columnIndexes[ADSBDEMOD_COLUMNS];//!< How the columns are ordered in the table
|
||||
int m_columnSizes[ADSBDEMOD_COLUMNS]; //!< Size of the coumns in the table
|
||||
|
||||
Serializable *m_channelMarker;
|
||||
|
||||
float m_airportRange; //!< How far away should we display airports (nm)
|
||||
enum AirportType {
|
||||
Small,
|
||||
Medium,
|
||||
Large,
|
||||
Heliport
|
||||
} m_airportMinimumSize; //!< What's the minimum size airport that should be displayed
|
||||
bool m_displayHeliports; //!< Whether to display heliports on the map
|
||||
bool m_flightPaths; //!< Whether to display flight paths
|
||||
bool m_siUnits; //!< Uses m,kph rather than ft/knts
|
||||
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
|
||||
int m_deviceIndex; //!< Device to set to ATC frequencies
|
||||
|
||||
ADSBDemodSettings();
|
||||
void resetToDefaults();
|
||||
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
||||
|
@ -16,16 +16,11 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdio.h>
|
||||
#include <complex.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <QTime>
|
||||
#include <QDebug>
|
||||
|
||||
#include "util/stepfunctions.h"
|
||||
#include "util/db.h"
|
||||
#include "util/crc.h"
|
||||
#include "audio/audiooutput.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
@ -34,219 +29,189 @@
|
||||
|
||||
#include "adsbdemodreport.h"
|
||||
#include "adsbdemodsink.h"
|
||||
#include "adsbdemodsinkworker.h"
|
||||
#include "adsb.h"
|
||||
|
||||
ADSBDemodSink::ADSBDemodSink() :
|
||||
m_channelSampleRate(6000000),
|
||||
m_channelFrequencyOffset(0),
|
||||
m_sampleIdx(0),
|
||||
m_sampleCount(0),
|
||||
m_skipCount(0),
|
||||
m_correlationThresholdLinear(0.0),
|
||||
m_magsq(0.0f),
|
||||
m_magsqSum(0.0f),
|
||||
m_magsqPeak(0.0f),
|
||||
m_magsqCount(0),
|
||||
m_messageQueueToGUI(nullptr),
|
||||
m_sampleBuffer(nullptr)
|
||||
m_channelSampleRate(6000000),
|
||||
m_channelFrequencyOffset(0),
|
||||
m_feedTime(0.0),
|
||||
m_sampleBuffer{nullptr, nullptr, nullptr},
|
||||
m_worker(this),
|
||||
m_writeBuffer(0),
|
||||
m_writeIdx(0),
|
||||
m_magsq(0.0f),
|
||||
m_magsqSum(0.0f),
|
||||
m_magsqPeak(0.0f),
|
||||
m_magsqCount(0),
|
||||
m_messageQueueToGUI(nullptr)
|
||||
{
|
||||
applySettings(m_settings, true);
|
||||
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
|
||||
for (int i = 0; i < m_buffers; i++)
|
||||
m_bufferWrite[i].release(1);
|
||||
m_bufferWrite[m_writeBuffer].acquire();
|
||||
}
|
||||
|
||||
ADSBDemodSink::~ADSBDemodSink()
|
||||
{
|
||||
delete m_sampleBuffer;
|
||||
for (int i = 0; i < m_buffers; i++)
|
||||
delete m_sampleBuffer[i];
|
||||
}
|
||||
|
||||
void ADSBDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
||||
{
|
||||
Complex ci;
|
||||
// Start timing how long we are in this function
|
||||
m_startPoint = boost::chrono::steady_clock::now();
|
||||
|
||||
for (SampleVector::const_iterator it = begin; it != end; ++it)
|
||||
// Optimise for common case, where no resampling or frequency offset
|
||||
if ((m_interpolatorDistance == 1.0f) && (m_channelFrequencyOffset == 0))
|
||||
{
|
||||
Complex c(it->real(), it->imag());
|
||||
c *= m_nco.nextIQ();
|
||||
for (SampleVector::const_iterator it = begin; it != end; ++it)
|
||||
{
|
||||
/*
|
||||
// SampleVector is vector of qint32 or qint16
|
||||
// Use integer mul to save one FP conversion and it has lower latency
|
||||
qint64 r = (qint64)it->real();
|
||||
qint64 i = (qint64)it->imag();
|
||||
qint64 magsqRaw = r*r + i*i;
|
||||
Real magsq = (Real)((double)magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED));
|
||||
processOneSample(magsq);
|
||||
*/
|
||||
Complex c(it->real(), it->imag());
|
||||
Real magsq = complexMagSq(c);
|
||||
processOneSample(magsq);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (SampleVector::const_iterator it = begin; it != end; ++it)
|
||||
{
|
||||
Complex c(it->real(), it->imag());
|
||||
Complex ci;
|
||||
c *= m_nco.nextIQ();
|
||||
|
||||
if (m_interpolatorDistance == 1.0f)
|
||||
{
|
||||
processOneSample(c);
|
||||
}
|
||||
else if (m_interpolatorDistance < 1.0f) // interpolate
|
||||
{
|
||||
while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
|
||||
if (m_interpolatorDistance == 1.0f)
|
||||
{
|
||||
processOneSample(ci);
|
||||
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
||||
processOneSample(complexMagSq(c));
|
||||
}
|
||||
}
|
||||
else // decimate
|
||||
{
|
||||
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
|
||||
else if (m_interpolatorDistance < 1.0f) // interpolate
|
||||
{
|
||||
processOneSample(ci);
|
||||
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
||||
while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
|
||||
{
|
||||
processOneSample(complexMagSq(ci));
|
||||
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
||||
}
|
||||
}
|
||||
else // decimate
|
||||
{
|
||||
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
|
||||
{
|
||||
processOneSample(complexMagSq(ci));
|
||||
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate number of seconds in this function
|
||||
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - m_startPoint;
|
||||
m_feedTime += sec.count();
|
||||
}
|
||||
|
||||
void ADSBDemodSink::processOneSample(Complex &ci)
|
||||
void ADSBDemodSink::processOneSample(Real magsq)
|
||||
{
|
||||
Real sample;
|
||||
|
||||
double magsqRaw = ci.real()*ci.real() + ci.imag()*ci.imag();
|
||||
Real magsq = magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED);
|
||||
m_movingAverage(magsq);
|
||||
m_magsqSum += magsq;
|
||||
|
||||
if (magsq > m_magsqPeak)
|
||||
{
|
||||
m_magsqPeak = magsq;
|
||||
}
|
||||
m_magsqCount++;
|
||||
|
||||
sample = magsq;
|
||||
m_sampleBuffer[m_sampleCount] = sample;
|
||||
m_sampleCount++;
|
||||
|
||||
// Do we have enough data for a frame
|
||||
if ((m_sampleCount >= m_totalSamples) && (m_skipCount == 0))
|
||||
m_sampleBuffer[m_writeBuffer][m_writeIdx] = magsq;
|
||||
m_writeIdx++;
|
||||
if (m_writeIdx >= m_bufferSize)
|
||||
{
|
||||
int startIdx = m_sampleCount - m_totalSamples;
|
||||
m_bufferRead[m_writeBuffer].release();
|
||||
|
||||
// Correlate received signal with expected preamble
|
||||
// chip+ indexes are 0, 2, 7, 9
|
||||
// we correlate only over 6 symbols so that the number of zero chips is twice the
|
||||
// number of one chips - empirically this is enough to get good correlation
|
||||
Real premableCorrelationOnes = 0.0;
|
||||
Real preambleCorrelationZeros = 0.0;
|
||||
m_writeBuffer++;
|
||||
if (m_writeBuffer >= m_buffers)
|
||||
m_writeBuffer = 0;
|
||||
|
||||
for (int i = 0; i < m_samplesPerChip; i++)
|
||||
{
|
||||
premableCorrelationOnes += m_sampleBuffer[startIdx + 0*m_samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sampleBuffer[startIdx + 1*m_samplesPerChip + i];
|
||||
// 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();
|
||||
|
||||
premableCorrelationOnes += m_sampleBuffer[startIdx + 2*m_samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sampleBuffer[startIdx + 3*m_samplesPerChip + i];
|
||||
if (m_worker.isRunning())
|
||||
m_bufferWrite[m_writeBuffer].acquire();
|
||||
|
||||
preambleCorrelationZeros += m_sampleBuffer[startIdx + 4*m_samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sampleBuffer[startIdx + 5*m_samplesPerChip + i];
|
||||
m_startPoint = boost::chrono::steady_clock::now();
|
||||
|
||||
preambleCorrelationZeros += m_sampleBuffer[startIdx + 6*m_samplesPerChip + i];
|
||||
premableCorrelationOnes += m_sampleBuffer[startIdx + 7*m_samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sampleBuffer[startIdx + 8*m_samplesPerChip + i];
|
||||
premableCorrelationOnes += m_sampleBuffer[startIdx + 9*m_samplesPerChip + i];
|
||||
|
||||
preambleCorrelationZeros += m_sampleBuffer[startIdx + 10*m_samplesPerChip + i];
|
||||
preambleCorrelationZeros += m_sampleBuffer[startIdx + 11*m_samplesPerChip + i];
|
||||
}
|
||||
|
||||
// If the correlation is exactly 0, it's probably no signal
|
||||
if ((premableCorrelationOnes > m_correlationThresholdLinear) &&
|
||||
(preambleCorrelationZeros < 2.0*m_correlationThresholdLinear) &&
|
||||
(premableCorrelationOnes != 0.0f))
|
||||
{
|
||||
// Skip over preamble
|
||||
startIdx += m_settings.m_samplesPerBit*ADS_B_PREAMBLE_BITS;
|
||||
|
||||
// Demodulate waveform to bytes
|
||||
unsigned char data[ADS_B_ES_BYTES];
|
||||
int byteIdx = 0;
|
||||
int currentBit;
|
||||
unsigned char currentByte = 0;
|
||||
bool adsbOnly = true;
|
||||
int df;
|
||||
|
||||
for (int bit = 0; bit < ADS_B_ES_BITS; bit++)
|
||||
{
|
||||
// PPM (Pulse position modulation) - Each bit spreads to two chips, 1->10, 0->01
|
||||
// Determine if bit is 1 or 0, by seeing which chip has largest combined energy over the sampling period
|
||||
Real oneSum = 0.0f;
|
||||
Real zeroSum = 0.0f;
|
||||
for (int i = 0; i < m_samplesPerChip; i++)
|
||||
{
|
||||
oneSum += m_sampleBuffer[startIdx+i];
|
||||
zeroSum += m_sampleBuffer[startIdx+m_samplesPerChip+i];
|
||||
}
|
||||
currentBit = oneSum > zeroSum;
|
||||
startIdx += m_settings.m_samplesPerBit;
|
||||
// Convert bit to bytes - MSB first
|
||||
currentByte |= currentBit << (7-(bit & 0x7));
|
||||
if ((bit & 0x7) == 0x7)
|
||||
{
|
||||
data[byteIdx++] = currentByte;
|
||||
currentByte = 0;
|
||||
// Don't try to demodulate any further, if this isn't an ADS-B frame
|
||||
// to help reduce processing overhead
|
||||
if (adsbOnly && (bit == 7))
|
||||
{
|
||||
df = ((data[0] >> 3) & ADS_B_DF_MASK);
|
||||
if ((df != 17) && (df != 18))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is ADS-B?
|
||||
df = ((data[0] >> 3) & ADS_B_DF_MASK);
|
||||
if ((df == 17) || (df == 18))
|
||||
{
|
||||
crcadsb crc;
|
||||
//int icao = (data[1] << 16) | (data[2] << 8) | data[3]; // ICAO aircraft address
|
||||
int parity = (data[11] << 16) | (data[12] << 8) | data[13]; // Parity / CRC
|
||||
|
||||
crc.calculate(data, ADS_B_ES_BYTES-3);
|
||||
if (parity == crc.get())
|
||||
{
|
||||
// Got a valid frame
|
||||
// Don't try to re-demodulate the same frame
|
||||
m_skipCount = (ADS_B_ES_BITS+ADS_B_PREAMBLE_BITS)*ADS_B_CHIPS_PER_BIT*m_samplesPerChip;
|
||||
// Pass to GUI
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, sizeof(data)),
|
||||
premableCorrelationOnes,
|
||||
preambleCorrelationZeros/2.0);
|
||||
getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
// Pass to worker
|
||||
if (getMessageQueueToWorker())
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, sizeof(data)),
|
||||
premableCorrelationOnes,
|
||||
preambleCorrelationZeros/2.0);
|
||||
getMessageQueueToWorker()->push(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_writeIdx = m_samplesPerFrame - 1; // Leave space for copying samples from previous buffer
|
||||
}
|
||||
if (m_skipCount > 0)
|
||||
m_skipCount--;
|
||||
if (m_sampleCount >= 2*m_totalSamples)
|
||||
}
|
||||
|
||||
void ADSBDemodSink::startWorker()
|
||||
{
|
||||
qDebug() << "ADSBDemodSink::startWorker";
|
||||
if (!m_worker.isRunning())
|
||||
m_worker.start();
|
||||
}
|
||||
|
||||
void ADSBDemodSink::stopWorker()
|
||||
{
|
||||
if (m_worker.isRunning())
|
||||
{
|
||||
// Copy second half of buffer to first
|
||||
memcpy(&m_sampleBuffer[0], &m_sampleBuffer[m_totalSamples], m_totalSamples*sizeof(Real));
|
||||
m_sampleCount = m_totalSamples;
|
||||
qDebug() << "ADSBDemodSink::stopWorker: Stopping worker";
|
||||
m_worker.requestInterruption();
|
||||
// Worker may be blocked waiting for a buffer
|
||||
for (int i = 0; i < m_buffers; i++)
|
||||
{
|
||||
if (m_bufferRead[i].available() == 0)
|
||||
m_bufferRead[i].release(1);
|
||||
}
|
||||
m_worker.wait();
|
||||
// If this is called from ADSBDemod, we need to also
|
||||
// make sure baseband sink thread isnt blocked in processOneSample
|
||||
for (int i = 0; i < m_buffers; i++)
|
||||
{
|
||||
if (m_bufferWrite[i].available() == 0)
|
||||
m_bufferWrite[i].release(1);
|
||||
}
|
||||
qDebug() << "ADSBDemodSink::stopWorker: Worker stopped";
|
||||
}
|
||||
m_sampleIdx++;
|
||||
|
||||
}
|
||||
|
||||
void ADSBDemodSink::init(int samplesPerBit)
|
||||
{
|
||||
if (m_sampleBuffer)
|
||||
delete m_sampleBuffer;
|
||||
bool restart = m_worker.isRunning();
|
||||
if (restart)
|
||||
{
|
||||
// Stop worker as we're going to delete the buffers
|
||||
stopWorker();
|
||||
}
|
||||
// Reset state of semaphores
|
||||
for (int i = 0; i < m_buffers; i++)
|
||||
{
|
||||
m_bufferWrite[i].acquire(m_bufferWrite[i].available());
|
||||
m_bufferWrite[i].release(1);
|
||||
m_bufferRead[i].acquire(m_bufferRead[i].available());
|
||||
}
|
||||
m_writeBuffer = 0;
|
||||
m_bufferWrite[m_writeBuffer].acquire();
|
||||
|
||||
m_totalSamples = samplesPerBit*(ADS_B_PREAMBLE_BITS+ADS_B_ES_BITS);
|
||||
for (int i = 0; i < m_buffers; i++)
|
||||
{
|
||||
if (m_sampleBuffer[i])
|
||||
delete m_sampleBuffer[i];
|
||||
}
|
||||
|
||||
m_samplesPerFrame = samplesPerBit*(ADS_B_PREAMBLE_BITS+ADS_B_ES_BITS);
|
||||
m_samplesPerChip = samplesPerBit/ADS_B_CHIPS_PER_BIT;
|
||||
m_writeIdx = m_samplesPerFrame - 1; // Leave space for copying samples from previous buffer
|
||||
|
||||
m_sampleBuffer = new Real[2*m_totalSamples];
|
||||
for (int i = 0; i < m_buffers; i++)
|
||||
m_sampleBuffer[i] = new Real[m_bufferSize];
|
||||
|
||||
if (restart)
|
||||
startWorker();
|
||||
}
|
||||
|
||||
void ADSBDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
|
||||
@ -278,6 +243,8 @@ void ADSBDemodSink::applySettings(const ADSBDemodSettings& settings, bool force)
|
||||
<< " 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;
|
||||
|
||||
@ -294,9 +261,10 @@ void ADSBDemodSink::applySettings(const ADSBDemodSettings& settings, bool force)
|
||||
init(settings.m_samplesPerBit);
|
||||
}
|
||||
|
||||
if ((settings.m_correlationThreshold != m_settings.m_correlationThreshold) || force) {
|
||||
m_correlationThresholdLinear = CalcDb::powerFromdB(m_settings.m_correlationThreshold);
|
||||
}
|
||||
// Forward to worker
|
||||
ADSBDemodSinkWorker::MsgConfigureADSBDemodSinkWorker *msg = ADSBDemodSinkWorker::MsgConfigureADSBDemodSinkWorker::create(
|
||||
settings, force);
|
||||
m_worker.getInputMessageQueue()->push(msg);
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
@ -19,7 +19,8 @@
|
||||
#ifndef INCLUDE_ADSBDEMODSINK_H
|
||||
#define INCLUDE_ADSBDEMODSINK_H
|
||||
|
||||
#include <vector>
|
||||
#define BOOST_CHRONO_HEADER_ONLY
|
||||
#include <boost/chrono/chrono.hpp>
|
||||
|
||||
#include "dsp/channelsamplesink.h"
|
||||
#include "dsp/nco.h"
|
||||
@ -27,6 +28,8 @@
|
||||
#include "util/movingaverage.h"
|
||||
|
||||
#include "adsbdemodsettings.h"
|
||||
#include "adsbdemodstats.h"
|
||||
#include "adsbdemodsinkworker.h"
|
||||
|
||||
class ADSBDemodSink : public ChannelSampleSink {
|
||||
public:
|
||||
@ -53,13 +56,16 @@ public:
|
||||
m_magsqCount = 0;
|
||||
}
|
||||
|
||||
void init(int samplesPerBit);
|
||||
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
|
||||
void applySettings(const ADSBDemodSettings& settings, bool force = false);
|
||||
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; }
|
||||
void setMessageQueueToWorker(MessageQueue *messageQueue) { m_messageQueueToWorker = messageQueue; }
|
||||
void startWorker();
|
||||
void stopWorker();
|
||||
|
||||
private:
|
||||
friend ADSBDemodSinkWorker;
|
||||
|
||||
struct MagSqLevelsStore
|
||||
{
|
||||
MagSqLevelsStore() :
|
||||
@ -78,14 +84,26 @@ private:
|
||||
Interpolator m_interpolator;
|
||||
Real m_interpolatorDistance;
|
||||
Real m_interpolatorDistanceRemain;
|
||||
int m_sampleIdx;
|
||||
int m_sampleCount;
|
||||
int m_skipCount; // Samples to skip, because we've already received a frame
|
||||
Real *m_sampleBuffer;
|
||||
|
||||
int m_totalSamples; // These two values are derived from samplesPerBit
|
||||
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
|
||||
// be processed contiguously
|
||||
const int m_buffers = 3;
|
||||
const int m_bufferSize = 200000;
|
||||
Real *m_sampleBuffer[3]; //!< Each buffer is m_bufferSize samples
|
||||
QSemaphore m_bufferWrite[3]; //!< Sempahore to control write access to the buffers
|
||||
QSemaphore m_bufferRead[3]; //!< Sempahore to control read access from the buffers
|
||||
ADSBDemodSinkWorker m_worker; //!< Worker thread that does the actual demodulation
|
||||
int m_writeBuffer; //!< Which of the 3 buffers we're writing in to
|
||||
int m_writeIdx; //!< Index to to current write buffer
|
||||
|
||||
// These values are derived from samplesPerBit
|
||||
int m_samplesPerFrame; //!< Including preamble
|
||||
int m_samplesPerChip;
|
||||
double m_correlationThresholdLinear; //!< settings m_correlationThreshold is in dB. Linear value is calculated once.
|
||||
|
||||
double m_magsq; //!< displayed averaged value
|
||||
double m_magsqSum;
|
||||
@ -93,12 +111,16 @@ private:
|
||||
int m_magsqCount;
|
||||
MagSqLevelsStore m_magSqLevelStore;
|
||||
|
||||
MovingAverageUtil<Real, double, 32> m_movingAverage;
|
||||
|
||||
MessageQueue *m_messageQueueToGUI;
|
||||
MessageQueue *m_messageQueueToWorker;
|
||||
|
||||
void processOneSample(Complex &ci);
|
||||
void init(int samplesPerBit);
|
||||
Real inline complexMagSq(Complex& ci)
|
||||
{
|
||||
double magsqRaw = ci.real()*ci.real() + ci.imag()*ci.imag();
|
||||
return (Real)(magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED));
|
||||
}
|
||||
void processOneSample(Real magsq);
|
||||
MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; }
|
||||
MessageQueue *getMessageQueueToWorker() { return m_messageQueueToWorker; }
|
||||
};
|
||||
|
349
plugins/channelrx/demodadsb/adsbdemodsinkworker.cpp
Normal file
@ -0,0 +1,349 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define BOOST_CHRONO_HEADER_ONLY
|
||||
#include <boost/chrono/chrono.hpp>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "util/stepfunctions.h"
|
||||
#include "util/db.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "device/deviceapi.h"
|
||||
|
||||
#include "adsbdemodreport.h"
|
||||
#include "adsbdemodsink.h"
|
||||
#include "adsbdemodsinkworker.h"
|
||||
#include "adsbdemodsettings.h"
|
||||
#include "adsb.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(ADSBDemodSinkWorker::MsgConfigureADSBDemodSinkWorker, Message)
|
||||
|
||||
void ADSBDemodSinkWorker::run()
|
||||
{
|
||||
int readBuffer = 0;
|
||||
|
||||
// Acquire first buffer
|
||||
m_sink->m_bufferRead[readBuffer].acquire();
|
||||
|
||||
// Start recording how much time is spent processing in this method
|
||||
boost::chrono::steady_clock::time_point startPoint = boost::chrono::steady_clock::now();
|
||||
|
||||
// Check for updated settings
|
||||
handleInputMessages();
|
||||
|
||||
// samplesPerBit is only changed when the thread is stopped
|
||||
int samplesPerBit = m_settings.m_samplesPerBit;
|
||||
int samplesPerFrame = samplesPerBit*(ADS_B_PREAMBLE_BITS+ADS_B_ES_BITS);
|
||||
int samplesPerChip = samplesPerBit/ADS_B_CHIPS_PER_BIT;
|
||||
|
||||
qDebug() << "ADSBDemodSinkWorker:: running with"
|
||||
<< " samplesPerFrame: " << samplesPerFrame
|
||||
<< " samplesPerChip: " << samplesPerChip
|
||||
<< " samplesPerBit: " << samplesPerBit
|
||||
<< " correlateFullPreamble: " << m_settings.m_correlateFullPreamble
|
||||
<< " correlationZerosScale: " << m_correlationZerosScale
|
||||
<< " correlationThreshold: " << m_settings.m_correlationThreshold;
|
||||
|
||||
int readIdx = m_sink->m_samplesPerFrame - 1;
|
||||
|
||||
int cnt = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int startIdx = readIdx;
|
||||
|
||||
// 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++)
|
||||
{
|
||||
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 + 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];
|
||||
}
|
||||
}
|
||||
|
||||
// Use the difference rather than absolute value, as we don't care how powerful the signal
|
||||
// is, just whether there is a good correlation with the preamble. The absolute value varies
|
||||
// too much with different radios, AGC settings and and the noise floor is not constant
|
||||
// (E.g: it's quite possible to receive multiple frames simultaneously, so we don't
|
||||
// want a maximum threshold for the zeros, as a weaker signal may transmit 1s in
|
||||
// a stronger signals 0 chip position. Similarly a strong signal in an adjacent
|
||||
// channel may casue AGC to reduce gain, reducing the ampltiude of an otherwise
|
||||
// strong signal, as well as the noise floor)
|
||||
// The scale factors account for different values of samplesPerBit and the different
|
||||
// number of zeros and ones in the preamble
|
||||
// If the sum of ones is exactly 0, it's probably no signal
|
||||
Real preambleCorrelation = (preambleCorrelationOnes * m_correlationZerosScale)
|
||||
- (preambleCorrelationZeros * m_correlationOnesScale);
|
||||
if ((preambleCorrelation > m_correlationThresholdLinear) && (preambleCorrelationOnes != 0.0f))
|
||||
{
|
||||
m_demodStats.m_correlatorMatches++;
|
||||
// Skip over preamble
|
||||
startIdx += samplesPerBit*ADS_B_PREAMBLE_BITS;
|
||||
|
||||
// Demodulate waveform to bytes
|
||||
unsigned char data[ADS_B_ES_BYTES];
|
||||
int byteIdx = 0;
|
||||
int currentBit;
|
||||
unsigned char currentByte = 0;
|
||||
int df;
|
||||
|
||||
for (int bit = 0; bit < ADS_B_ES_BITS; bit++)
|
||||
{
|
||||
// PPM (Pulse position modulation) - Each bit spreads to two chips, 1->10, 0->01
|
||||
// Determine if bit is 1 or 0, by seeing which chip has largest combined energy over the sampling period
|
||||
Real oneSum = 0.0f;
|
||||
Real zeroSum = 0.0f;
|
||||
for (int i = 0; i < samplesPerChip; i++)
|
||||
{
|
||||
oneSum += m_sink->m_sampleBuffer[readBuffer][startIdx+i];
|
||||
zeroSum += m_sink->m_sampleBuffer[readBuffer][startIdx+samplesPerChip+i];
|
||||
}
|
||||
currentBit = oneSum > zeroSum;
|
||||
startIdx += samplesPerBit;
|
||||
// Convert bit to bytes - MSB first
|
||||
currentByte |= currentBit << (7-(bit & 0x7));
|
||||
if ((bit & 0x7) == 0x7)
|
||||
{
|
||||
data[byteIdx++] = currentByte;
|
||||
currentByte = 0;
|
||||
// Don't try to demodulate any further, if this isn't an ADS-B frame
|
||||
// to help reduce processing overhead
|
||||
if (!m_settings.m_demodModeS && (bit == 7))
|
||||
{
|
||||
df = ((data[0] >> 3) & ADS_B_DF_MASK);
|
||||
if ((df != 17) && (df != 18))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is ADS-B?
|
||||
df = ((data[0] >> 3) & ADS_B_DF_MASK);
|
||||
if ((df == 17) || (df == 18))
|
||||
{
|
||||
m_crc.init();
|
||||
int parity = (data[11] << 16) | (data[12] << 8) | data[13]; // Parity / CRC
|
||||
|
||||
m_crc.calculate(data, ADS_B_ES_BYTES-3);
|
||||
if (parity == m_crc.get())
|
||||
{
|
||||
// Got a valid frame
|
||||
m_demodStats.m_adsbFrames++;
|
||||
// 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())
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, sizeof(data)),
|
||||
preambleCorrelation);
|
||||
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, sizeof(data)),
|
||||
preambleCorrelation);
|
||||
m_sink->getMessageQueueToWorker()->push(msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_demodStats.m_crcFails++;
|
||||
}
|
||||
else if (m_settings.m_demodModeS)
|
||||
{
|
||||
int bytes;
|
||||
|
||||
m_crc.init();
|
||||
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)
|
||||
{
|
||||
int parity = (data[bytes-3] << 16) | (data[bytes-2] << 8) | data[bytes-1];
|
||||
m_crc.calculate(data, bytes-3);
|
||||
int crc = m_crc.get();
|
||||
// For DF11, the last 7 bits may have an address/interogration indentifier (II)
|
||||
// XORed in, so we ignore those bits
|
||||
if ((parity == crc) || ((df == 11) && (parity & 0xffff80) == (crc & 0xffff80)))
|
||||
{
|
||||
m_demodStats.m_modesFrames++;
|
||||
// Pass to worker to feed to other servers
|
||||
if (m_sink->getMessageQueueToWorker())
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
|
||||
QByteArray((char*)data, sizeof(data)),
|
||||
preambleCorrelation);
|
||||
m_sink->getMessageQueueToWorker()->push(msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_demodStats.m_crcFails++;
|
||||
}
|
||||
else
|
||||
m_demodStats.m_typeFails++;
|
||||
}
|
||||
else
|
||||
m_demodStats.m_typeFails++;
|
||||
}
|
||||
readIdx++;
|
||||
if (readIdx > m_sink->m_bufferSize - samplesPerFrame)
|
||||
{
|
||||
int nextBuffer = readBuffer+1;
|
||||
if (nextBuffer >= m_sink->m_buffers)
|
||||
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;
|
||||
|
||||
// Send stats to GUI
|
||||
if (m_sink->getMessageQueueToGUI())
|
||||
{
|
||||
ADSBDemodReport::MsgReportDemodStats *msg = ADSBDemodReport::MsgReportDemodStats::create(m_demodStats);
|
||||
m_sink->getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
|
||||
if (!isInterruptionRequested())
|
||||
{
|
||||
// Get next buffer
|
||||
m_sink->m_bufferRead[nextBuffer].acquire();
|
||||
|
||||
// Check for updated settings
|
||||
handleInputMessages();
|
||||
|
||||
// Resume timing how long we are processing
|
||||
startPoint = boost::chrono::steady_clock::now();
|
||||
|
||||
int samplesRemaining = m_sink->m_bufferSize - readIdx;
|
||||
if (samplesRemaining > 0)
|
||||
{
|
||||
// Copy remaining samples, to start of next buffer
|
||||
memcpy(&m_sink->m_sampleBuffer[nextBuffer][samplesPerFrame - 1 - samplesRemaining], &m_sink->m_sampleBuffer[readBuffer][readIdx], samplesRemaining*sizeof(Real));
|
||||
readIdx = samplesPerFrame - 1 - samplesRemaining;
|
||||
}
|
||||
else
|
||||
{
|
||||
readIdx = samplesPerFrame - 1;
|
||||
}
|
||||
|
||||
m_sink->m_bufferWrite[readBuffer].release();
|
||||
|
||||
readBuffer = nextBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use a break to avoid testing a condition in the main loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBDemodSinkWorker::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != nullptr)
|
||||
{
|
||||
if (MsgConfigureADSBDemodSinkWorker::match(*message))
|
||||
{
|
||||
MsgConfigureADSBDemodSinkWorker* cfg = (MsgConfigureADSBDemodSinkWorker*)message;
|
||||
|
||||
ADSBDemodSettings settings = cfg->getSettings();
|
||||
bool force = cfg->getForce();
|
||||
|
||||
if ((m_settings.m_correlationThreshold != settings.m_correlationThreshold) || force)
|
||||
{
|
||||
m_correlationThresholdLinear = CalcDb::powerFromdB(settings.m_correlationThreshold);
|
||||
qDebug() << "m_correlationThresholdLinear: " << m_correlationThresholdLinear;
|
||||
}
|
||||
|
||||
if (settings.m_correlateFullPreamble)
|
||||
{
|
||||
m_correlationOnesScale = 1.0f / settings.m_samplesPerBit;
|
||||
m_correlationZerosScale = 2.0 * 1.0f / settings.m_samplesPerBit; // As 2x more 0s than 1s
|
||||
}
|
||||
else
|
||||
{
|
||||
m_correlationOnesScale = 1.0f / settings.m_samplesPerBit;
|
||||
m_correlationZerosScale = 3.0 * 1.0f / settings.m_samplesPerBit; // As 3x more 0s than 1s
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
82
plugins/channelrx/demodadsb/adsbdemodsinkworker.h
Normal file
@ -0,0 +1,82 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_ADSBDEMODSINKWORKER_H
|
||||
#define INCLUDE_ADSBDEMODSINKWORKER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "util/crc.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "adsbdemodstats.h"
|
||||
|
||||
class ADSBDemodSink;
|
||||
struct ADSBDemodSettings;
|
||||
struct ADSBDemodStats;
|
||||
|
||||
class ADSBDemodSinkWorker : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
class MsgConfigureADSBDemodSinkWorker : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const ADSBDemodSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureADSBDemodSinkWorker* create(const ADSBDemodSettings& settings, bool force)
|
||||
{
|
||||
return new MsgConfigureADSBDemodSinkWorker(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
ADSBDemodSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureADSBDemodSinkWorker(const ADSBDemodSettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
ADSBDemodSinkWorker(ADSBDemodSink *sink) :
|
||||
m_sink(sink),
|
||||
m_demodStats(),
|
||||
m_correlationThresholdLinear(0.02f),
|
||||
m_crc()
|
||||
{
|
||||
}
|
||||
void run() override;
|
||||
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
|
||||
private:
|
||||
void handleInputMessages();
|
||||
MessageQueue m_inputMessageQueue;
|
||||
ADSBDemodSettings m_settings;
|
||||
ADSBDemodSink *m_sink;
|
||||
ADSBDemodStats m_demodStats;
|
||||
Real m_correlationThresholdLinear;
|
||||
Real m_correlationZerosScale;
|
||||
Real m_correlationOnesScale;
|
||||
crcadsb m_crc; //!< Have as member to avoid recomputing LUT
|
||||
};
|
||||
|
||||
#endif // INCLUDE_ADSBDEMODSINKWORKER_H
|
46
plugins/channelrx/demodadsb/adsbdemodstats.h
Normal file
@ -0,0 +1,46 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_ADSBDEMODSTATS_H
|
||||
#define INCLUDE_ADSBDEMODSTATS_H
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
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_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()
|
||||
|
||||
ADSBDemodStats() :
|
||||
m_correlatorMatches(0),
|
||||
m_adsbFrames(0),
|
||||
m_modesFrames(0),
|
||||
m_crcFails(0),
|
||||
m_typeFails(0),
|
||||
m_demodTime(0.0),
|
||||
m_feedTime(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_ADSBDEMODSTATS_H
|
@ -17,6 +17,7 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QDebug>
|
||||
#include <QAbstractSocket>
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QEventLoop>
|
||||
@ -33,6 +34,13 @@ ADSBDemodWorker::ADSBDemodWorker() :
|
||||
{
|
||||
connect(&m_heartbeatTimer, SIGNAL(timeout()), this, SLOT(heartbeat()));
|
||||
connect(&m_socket, SIGNAL(readyRead()),this, SLOT(recv()));
|
||||
connect(&m_socket, SIGNAL(connected()), this, SLOT(connected()));
|
||||
connect(&m_socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(&m_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &ADSBDemodWorker::errorOccurred);
|
||||
#else
|
||||
connect(&m_socket, &QAbstractSocket::errorOccurred, this, &ADSBDemodWorker::errorOccurred);
|
||||
#endif
|
||||
}
|
||||
|
||||
ADSBDemodWorker::~ADSBDemodWorker()
|
||||
@ -88,7 +96,7 @@ bool ADSBDemodWorker::handleMessage(const Message& message)
|
||||
else if (ADSBDemodReport::MsgReportADSB::match(message))
|
||||
{
|
||||
ADSBDemodReport::MsgReportADSB& report = (ADSBDemodReport::MsgReportADSB&) message;
|
||||
handleADSB(report.getData(), report.getDateTime(), report.getPreambleCorrelationOnes());
|
||||
handleADSB(report.getData(), report.getDateTime(), report.getPreambleCorrelation());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -100,26 +108,42 @@ bool ADSBDemodWorker::handleMessage(const Message& message)
|
||||
void ADSBDemodWorker::applySettings(const ADSBDemodSettings& settings, bool force)
|
||||
{
|
||||
qDebug() << "ADSBDemodWorker::applySettings:"
|
||||
<< " m_beastEnabled: " << settings.m_beastEnabled
|
||||
<< " m_beastHost: " << settings.m_beastHost
|
||||
<< " m_beastPort: " << settings.m_beastPort
|
||||
<< " m_feedEnabled: " << settings.m_feedEnabled
|
||||
<< " m_feedHost: " << settings.m_feedHost
|
||||
<< " m_feedPort: " << settings.m_feedPort
|
||||
<< " m_feedFormat: " << settings.m_feedFormat
|
||||
<< " force: " << force;
|
||||
|
||||
if ((settings.m_beastEnabled != m_settings.m_beastEnabled)
|
||||
|| (settings.m_beastHost != m_settings.m_beastHost)
|
||||
|| (settings.m_beastPort != m_settings.m_beastPort) || force)
|
||||
if ((settings.m_feedEnabled != m_settings.m_feedEnabled)
|
||||
|| (settings.m_feedHost != m_settings.m_feedHost)
|
||||
|| (settings.m_feedPort != m_settings.m_feedPort) || force)
|
||||
{
|
||||
// Close any existing connection
|
||||
if (m_socket.isOpen())
|
||||
m_socket.close();
|
||||
// Open connection
|
||||
if (settings.m_beastEnabled)
|
||||
m_socket.connectToHost(settings.m_beastHost, settings.m_beastPort);
|
||||
if (settings.m_feedEnabled)
|
||||
m_socket.connectToHost(settings.m_feedHost, settings.m_feedPort);
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
void ADSBDemodWorker::connected()
|
||||
{
|
||||
qDebug() << "ADSBDemodWorker::connected " << m_settings.m_feedHost;
|
||||
}
|
||||
|
||||
void ADSBDemodWorker::disconnected()
|
||||
{
|
||||
qDebug() << "ADSBDemodWorker::disconnected";
|
||||
}
|
||||
|
||||
void ADSBDemodWorker::errorOccurred(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
qDebug() << "ADSBDemodWorker::errorOccurred: " << socketError;
|
||||
}
|
||||
|
||||
void ADSBDemodWorker::recv()
|
||||
{
|
||||
// Not expecting to receving anything from server
|
||||
@ -129,11 +153,11 @@ void ADSBDemodWorker::recv()
|
||||
|
||||
void ADSBDemodWorker::send(const char *data, int length)
|
||||
{
|
||||
if (m_settings.m_beastEnabled)
|
||||
if (m_settings.m_feedEnabled)
|
||||
{
|
||||
// Reopen connection if it was lost
|
||||
if (!m_socket.isOpen())
|
||||
m_socket.connectToHost(m_settings.m_beastHost, m_settings.m_beastPort);
|
||||
m_socket.connectToHost(m_settings.m_feedHost, m_settings.m_feedPort);
|
||||
// Send data
|
||||
m_socket.write(data, length);
|
||||
}
|
||||
@ -153,40 +177,48 @@ char *ADSBDemodWorker::escape(char *p, char c)
|
||||
// See: https://wiki.jetvision.de/wiki/Mode-S_Beast:Data_Output_Formats
|
||||
void ADSBDemodWorker::handleADSB(QByteArray data, const QDateTime dateTime, float correlation)
|
||||
{
|
||||
char beastBinary[2+6*2+1*2+14*2];
|
||||
int length;
|
||||
char *p = beastBinary;
|
||||
qint64 timestamp;
|
||||
unsigned char signalStrength;
|
||||
if (m_settings.m_feedFormat == ADSBDemodSettings::BeastBinary)
|
||||
{
|
||||
char beastBinary[2+6*2+1*2+14*2];
|
||||
int length;
|
||||
char *p = beastBinary;
|
||||
qint64 timestamp;
|
||||
unsigned char signalStrength;
|
||||
|
||||
timestamp = dateTime.toMSecsSinceEpoch();
|
||||
timestamp = dateTime.toMSecsSinceEpoch();
|
||||
|
||||
if (correlation > 255)
|
||||
signalStrength = 255;
|
||||
if (correlation < 1)
|
||||
signalStrength = 1;
|
||||
else
|
||||
signalStrength = (unsigned char)correlation;
|
||||
if (correlation > 255)
|
||||
signalStrength = 255;
|
||||
if (correlation < 1)
|
||||
signalStrength = 1;
|
||||
else
|
||||
signalStrength = (unsigned char)correlation;
|
||||
|
||||
*p++ = BEAST_ESC;
|
||||
*p++ = '3'; // Mode-S long
|
||||
*p++ = BEAST_ESC;
|
||||
*p++ = '3'; // Mode-S long
|
||||
|
||||
p = escape(p, timestamp >> 56); // Big-endian timestamp
|
||||
p = escape(p, timestamp >> 48);
|
||||
p = escape(p, timestamp >> 32);
|
||||
p = escape(p, timestamp >> 24);
|
||||
p = escape(p, timestamp >> 16);
|
||||
p = escape(p, timestamp >> 8);
|
||||
p = escape(p, timestamp);
|
||||
p = escape(p, timestamp >> 56); // Big-endian timestamp
|
||||
p = escape(p, timestamp >> 48);
|
||||
p = escape(p, timestamp >> 32);
|
||||
p = escape(p, timestamp >> 24);
|
||||
p = escape(p, timestamp >> 16);
|
||||
p = escape(p, timestamp >> 8);
|
||||
p = escape(p, timestamp);
|
||||
|
||||
p = escape(p, signalStrength); // Signal strength
|
||||
p = escape(p, signalStrength); // Signal strength
|
||||
|
||||
for (int i = 0; i < data.length(); i++) // ADS-B data
|
||||
p = escape(p, data[i]);
|
||||
for (int i = 0; i < data.length(); i++) // ADS-B data
|
||||
p = escape(p, data[i]);
|
||||
|
||||
length = p - beastBinary;
|
||||
length = p - beastBinary;
|
||||
|
||||
send(beastBinary, length);
|
||||
send(beastBinary, length);
|
||||
}
|
||||
else if (m_settings.m_feedFormat == ADSBDemodSettings::BeastHex)
|
||||
{
|
||||
QString beastHex = "*" + data.toHex() + ";\n";
|
||||
send(beastHex.toUtf8(), beastHex.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Periodically send heartbeat to keep connection alive
|
||||
|
@ -80,6 +80,9 @@ private:
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
void connected();
|
||||
void disconnected();
|
||||
void errorOccurred(QAbstractSocket::SocketError socketError);
|
||||
void recv();
|
||||
void heartbeat();
|
||||
};
|
||||
|
758
plugins/channelrx/demodadsb/airlinelogos.qrc
Normal file
@ -0,0 +1,758 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>airlinelogos/5AH.bmp</file>
|
||||
<file>airlinelogos/AAF.bmp</file>
|
||||
<file>airlinelogos/AAH.bmp</file>
|
||||
<file>airlinelogos/AAL.bmp</file>
|
||||
<file>airlinelogos/AAR.bmp</file>
|
||||
<file>airlinelogos/AAW.bmp</file>
|
||||
<file>airlinelogos/AAY.bmp</file>
|
||||
<file>airlinelogos/AAZ.bmp</file>
|
||||
<file>airlinelogos/ABB.bmp</file>
|
||||
<file>airlinelogos/ABD.bmp</file>
|
||||
<file>airlinelogos/ABG.bmp</file>
|
||||
<file>airlinelogos/ABL.bmp</file>
|
||||
<file>airlinelogos/ABN.bmp</file>
|
||||
<file>airlinelogos/ABP.bmp</file>
|
||||
<file>airlinelogos/ABQ.bmp</file>
|
||||
<file>airlinelogos/ABR.bmp</file>
|
||||
<file>airlinelogos/ABS.bmp</file>
|
||||
<file>airlinelogos/ABV.bmp</file>
|
||||
<file>airlinelogos/ABW.bmp</file>
|
||||
<file>airlinelogos/ABX.bmp</file>
|
||||
<file>airlinelogos/ABY.bmp</file>
|
||||
<file>airlinelogos/ACA.bmp</file>
|
||||
<file>airlinelogos/ACG.bmp</file>
|
||||
<file>airlinelogos/ACI.bmp</file>
|
||||
<file>airlinelogos/ACV.bmp</file>
|
||||
<file>airlinelogos/ADN.bmp</file>
|
||||
<file>airlinelogos/ADY.bmp</file>
|
||||
<file>airlinelogos/AEA.bmp</file>
|
||||
<file>airlinelogos/AEE.bmp</file>
|
||||
<file>airlinelogos/AEG.bmp</file>
|
||||
<file>airlinelogos/AEH.bmp</file>
|
||||
<file>airlinelogos/AEI.bmp</file>
|
||||
<file>airlinelogos/AERORESCUE.bmp</file>
|
||||
<file>airlinelogos/AFE.bmp</file>
|
||||
<file>airlinelogos/AFG.bmp</file>
|
||||
<file>airlinelogos/AFL.bmp</file>
|
||||
<file>airlinelogos/AFR.bmp</file>
|
||||
<file>airlinelogos/AFW.bmp</file>
|
||||
<file>airlinelogos/AGF.bmp</file>
|
||||
<file>airlinelogos/AGO.bmp</file>
|
||||
<file>airlinelogos/AGU.bmp</file>
|
||||
<file>airlinelogos/AHK.bmp</file>
|
||||
<file>airlinelogos/AHO.bmp</file>
|
||||
<file>airlinelogos/AHY.bmp</file>
|
||||
<file>airlinelogos/AIB.bmp</file>
|
||||
<file>airlinelogos/AIC.bmp</file>
|
||||
<file>airlinelogos/AIE.bmp</file>
|
||||
<file>airlinelogos/AIH.bmp</file>
|
||||
<file>airlinelogos/AIRCOSTA.bmp</file>
|
||||
<file>airlinelogos/AIZ.bmp</file>
|
||||
<file>airlinelogos/AJA.bmp</file>
|
||||
<file>airlinelogos/AJB.bmp</file>
|
||||
<file>airlinelogos/AJD.bmp</file>
|
||||
<file>airlinelogos/AJI.bmp</file>
|
||||
<file>airlinelogos/AJK.bmp</file>
|
||||
<file>airlinelogos/AJT.bmp</file>
|
||||
<file>airlinelogos/AKC.bmp</file>
|
||||
<file>airlinelogos/ALV.bmp</file>
|
||||
<file>airlinelogos/ALW.bmp</file>
|
||||
<file>airlinelogos/ALX.bmp</file>
|
||||
<file>airlinelogos/ALY.bmp</file>
|
||||
<file>airlinelogos/AMC.bmp</file>
|
||||
<file>airlinelogos/AMU.bmp</file>
|
||||
<file>airlinelogos/AMV.bmp</file>
|
||||
<file>airlinelogos/AMX.bmp</file>
|
||||
<file>airlinelogos/AMY.bmp</file>
|
||||
<file>airlinelogos/ANA.bmp</file>
|
||||
<file>airlinelogos/AND.bmp</file>
|
||||
<file>airlinelogos/ANE.bmp</file>
|
||||
<file>airlinelogos/ANG.bmp</file>
|
||||
<file>airlinelogos/ANK.bmp</file>
|
||||
<file>airlinelogos/ANO.bmp</file>
|
||||
<file>airlinelogos/ANQ.bmp</file>
|
||||
<file>airlinelogos/ANR.bmp</file>
|
||||
<file>airlinelogos/ANS.bmp</file>
|
||||
<file>airlinelogos/ANT.bmp</file>
|
||||
<file>airlinelogos/ANZ.bmp</file>
|
||||
<file>airlinelogos/AOJ.bmp</file>
|
||||
<file>airlinelogos/APF.bmp</file>
|
||||
<file>airlinelogos/APG.bmp</file>
|
||||
<file>airlinelogos/APJ.bmp</file>
|
||||
<file>airlinelogos/APK.bmp</file>
|
||||
<file>airlinelogos/APZ.bmp</file>
|
||||
<file>airlinelogos/ARA.bmp</file>
|
||||
<file>airlinelogos/ARE.bmp</file>
|
||||
<file>airlinelogos/ARG.bmp</file>
|
||||
<file>airlinelogos/ARK.bmp</file>
|
||||
<file>airlinelogos/ARN.bmp</file>
|
||||
<file>airlinelogos/ARR.bmp</file>
|
||||
<file>airlinelogos/ARU.bmp</file>
|
||||
<file>airlinelogos/ARZ.bmp</file>
|
||||
<file>airlinelogos/ASA.bmp</file>
|
||||
<file>airlinelogos/ASB.bmp</file>
|
||||
<file>airlinelogos/ASH.bmp</file>
|
||||
<file>airlinelogos/ASL.bmp</file>
|
||||
<file>airlinelogos/ASQ.bmp</file>
|
||||
<file>airlinelogos/ASV.bmp</file>
|
||||
<file>airlinelogos/ASY.bmp</file>
|
||||
<file>airlinelogos/ATC.bmp</file>
|
||||
<file>airlinelogos/ATG.bmp</file>
|
||||
<file>airlinelogos/ATN.bmp</file>
|
||||
<file>airlinelogos/ATR.bmp</file>
|
||||
<file>airlinelogos/ATV.bmp</file>
|
||||
<file>airlinelogos/ATW.bmp</file>
|
||||
<file>airlinelogos/ATX.bmp</file>
|
||||
<file>airlinelogos/AUA.bmp</file>
|
||||
<file>airlinelogos/AUI.bmp</file>
|
||||
<file>airlinelogos/AUL.bmp</file>
|
||||
<file>airlinelogos/AUR.bmp</file>
|
||||
<file>airlinelogos/AUS5.bmp</file>
|
||||
<file>airlinelogos/AUT.bmp</file>
|
||||
<file>airlinelogos/AVA.bmp</file>
|
||||
<file>airlinelogos/AVJ.bmp</file>
|
||||
<file>airlinelogos/AVN.bmp</file>
|
||||
<file>airlinelogos/AVV.bmp</file>
|
||||
<file>airlinelogos/AVW.bmp</file>
|
||||
<file>airlinelogos/AWE.bmp</file>
|
||||
<file>airlinelogos/AWG.bmp</file>
|
||||
<file>airlinelogos/AWI.bmp</file>
|
||||
<file>airlinelogos/AWK.bmp</file>
|
||||
<file>airlinelogos/AWM.bmp</file>
|
||||
<file>airlinelogos/AWT.bmp</file>
|
||||
<file>airlinelogos/AXB.bmp</file>
|
||||
<file>airlinelogos/AXE.bmp</file>
|
||||
<file>airlinelogos/AXK.bmp</file>
|
||||
<file>airlinelogos/AXM.bmp</file>
|
||||
<file>airlinelogos/AXU.bmp</file>
|
||||
<file>airlinelogos/AYG.bmp</file>
|
||||
<file>airlinelogos/AYT.bmp</file>
|
||||
<file>airlinelogos/AZA.bmp</file>
|
||||
<file>airlinelogos/AZG.bmp</file>
|
||||
<file>airlinelogos/AZI.bmp</file>
|
||||
<file>airlinelogos/AZM.bmp</file>
|
||||
<file>airlinelogos/AZN.bmp</file>
|
||||
<file>airlinelogos/AZO.bmp</file>
|
||||
<file>airlinelogos/AZQ.bmp</file>
|
||||
<file>airlinelogos/AZU.bmp</file>
|
||||
<file>airlinelogos/AZV.bmp</file>
|
||||
<file>airlinelogos/AZW.bmp</file>
|
||||
<file>airlinelogos/BAW.bmp</file>
|
||||
<file>airlinelogos/BBC.bmp</file>
|
||||
<file>airlinelogos/BBD.bmp</file>
|
||||
<file>airlinelogos/BBG.bmp</file>
|
||||
<file>airlinelogos/BCI.bmp</file>
|
||||
<file>airlinelogos/BCY.bmp</file>
|
||||
<file>airlinelogos/BDA.bmp</file>
|
||||
<file>airlinelogos/BEE.bmp</file>
|
||||
<file>airlinelogos/BEL.bmp</file>
|
||||
<file>airlinelogos/BER.bmp</file>
|
||||
<file>airlinelogos/BGA.bmp</file>
|
||||
<file>airlinelogos/BGH.bmp</file>
|
||||
<file>airlinelogos/BGL.bmp</file>
|
||||
<file>airlinelogos/BGY.bmp</file>
|
||||
<file>airlinelogos/BHA.bmp</file>
|
||||
<file>airlinelogos/BHP.bmp</file>
|
||||
<file>airlinelogos/BIE.bmp</file>
|
||||
<file>airlinelogos/BLF.bmp</file>
|
||||
<file>airlinelogos/BLX.bmp</file>
|
||||
<file>airlinelogos/BMR.bmp</file>
|
||||
<file>airlinelogos/BOE.bmp</file>
|
||||
<file>airlinelogos/BON.bmp</file>
|
||||
<file>airlinelogos/BOS.bmp</file>
|
||||
<file>airlinelogos/BOT.bmp</file>
|
||||
<file>airlinelogos/BOV.bmp</file>
|
||||
<file>airlinelogos/BOX.bmp</file>
|
||||
<file>airlinelogos/BQB.bmp</file>
|
||||
<file>airlinelogos/BRINDABELLA.bmp</file>
|
||||
<file>airlinelogos/BRJ.bmp</file>
|
||||
<file>airlinelogos/BRQ.bmp</file>
|
||||
<file>airlinelogos/BRU.bmp</file>
|
||||
<file>airlinelogos/BRXb.bmp</file>
|
||||
<file>airlinelogos/BSK.bmp</file>
|
||||
<file>airlinelogos/BTI.bmp</file>
|
||||
<file>airlinelogos/BTN.bmp</file>
|
||||
<file>airlinelogos/BTQ.bmp</file>
|
||||
<file>airlinelogos/BUC.bmp</file>
|
||||
<file>airlinelogos/BUR.bmp</file>
|
||||
<file>airlinelogos/BVR.bmp</file>
|
||||
<file>airlinelogos/BXA.bmp</file>
|
||||
<file>airlinelogos/BYR.bmp</file>
|
||||
<file>airlinelogos/BZH.bmp</file>
|
||||
<file>airlinelogos/CAD.bmp</file>
|
||||
<file>airlinelogos/CAI.bmp</file>
|
||||
<file>airlinelogos/CAO.bmp</file>
|
||||
<file>airlinelogos/CAV.bmp</file>
|
||||
<file>airlinelogos/CAY.bmp</file>
|
||||
<file>airlinelogos/CBJ.bmp</file>
|
||||
<file>airlinelogos/CCA.bmp</file>
|
||||
<file>airlinelogos/CCD.bmp</file>
|
||||
<file>airlinelogos/CCE.bmp</file>
|
||||
<file>airlinelogos/CCM.bmp</file>
|
||||
<file>airlinelogos/CDA.bmp</file>
|
||||
<file>airlinelogos/CDC.bmp</file>
|
||||
<file>airlinelogos/CDG.bmp</file>
|
||||
<file>airlinelogos/CEB.bmp</file>
|
||||
<file>airlinelogos/CEL.bmp</file>
|
||||
<file>airlinelogos/CES.bmp</file>
|
||||
<file>airlinelogos/CEY.bmp</file>
|
||||
<file>airlinelogos/CFE.bmp</file>
|
||||
<file>airlinelogos/CFG.bmp</file>
|
||||
<file>airlinelogos/CGF.bmp</file>
|
||||
<file>airlinelogos/CGH.bmp</file>
|
||||
<file>airlinelogos/CGN.bmp</file>
|
||||
<file>airlinelogos/CHB.bmp</file>
|
||||
<file>airlinelogos/CHH.bmp</file>
|
||||
<file>airlinelogos/CIM.bmp</file>
|
||||
<file>airlinelogos/CJA.bmp</file>
|
||||
<file>airlinelogos/CJC.bmp</file>
|
||||
<file>airlinelogos/CJT.bmp</file>
|
||||
<file>airlinelogos/CKK.bmp</file>
|
||||
<file>airlinelogos/CKS.bmp</file>
|
||||
<file>airlinelogos/CLG.bmp</file>
|
||||
<file>airlinelogos/CLX.bmp</file>
|
||||
<file>airlinelogos/CMM.bmp</file>
|
||||
<file>airlinelogos/CND.bmp</file>
|
||||
<file>airlinelogos/CNK.bmp</file>
|
||||
<file>airlinelogos/CON1.bmp</file>
|
||||
<file>airlinelogos/COT.bmp</file>
|
||||
<file>airlinelogos/CPA.bmp</file>
|
||||
<file>airlinelogos/CPN.bmp</file>
|
||||
<file>airlinelogos/CPZ.bmp</file>
|
||||
<file>airlinelogos/CQH.bmp</file>
|
||||
<file>airlinelogos/CQN.bmp</file>
|
||||
<file>airlinelogos/CRC.bmp</file>
|
||||
<file>airlinelogos/CRL.bmp</file>
|
||||
<file>airlinelogos/CRN.bmp</file>
|
||||
<file>airlinelogos/CRO.bmp</file>
|
||||
<file>airlinelogos/CRQ.bmp</file>
|
||||
<file>airlinelogos/CRUZ.bmp</file>
|
||||
<file>airlinelogos/CSA.bmp</file>
|
||||
<file>airlinelogos/CSC.bmp</file>
|
||||
<file>airlinelogos/CSH.bmp</file>
|
||||
<file>airlinelogos/CSN.bmp</file>
|
||||
<file>airlinelogos/CSS.bmp</file>
|
||||
<file>airlinelogos/CSZ.bmp</file>
|
||||
<file>airlinelogos/CTM.bmp</file>
|
||||
<file>airlinelogos/CTN.bmp</file>
|
||||
<file>airlinelogos/CUA.bmp</file>
|
||||
<file>airlinelogos/CUB.bmp</file>
|
||||
<file>airlinelogos/CVA.bmp</file>
|
||||
<file>airlinelogos/CWC.bmp</file>
|
||||
<file>airlinelogos/CXA.bmp</file>
|
||||
<file>airlinelogos/CXB.bmp</file>
|
||||
<file>airlinelogos/CXH.bmp</file>
|
||||
<file>airlinelogos/CYL.bmp</file>
|
||||
<file>airlinelogos/CYP.bmp</file>
|
||||
<file>airlinelogos/CYZ.bmp</file>
|
||||
<file>airlinelogos/DAC.bmp</file>
|
||||
<file>airlinelogos/DAH.bmp</file>
|
||||
<file>airlinelogos/DAL.bmp</file>
|
||||
<file>airlinelogos/DAO.bmp</file>
|
||||
<file>airlinelogos/DAP.bmp</file>
|
||||
<file>airlinelogos/DCS.bmp</file>
|
||||
<file>airlinelogos/DER.bmp</file>
|
||||
<file>airlinelogos/DHL.bmp</file>
|
||||
<file>airlinelogos/DHX.bmp</file>
|
||||
<file>airlinelogos/DJT.bmp</file>
|
||||
<file>airlinelogos/DJU.bmp</file>
|
||||
<file>airlinelogos/DKH.bmp</file>
|
||||
<file>airlinelogos/DLA.bmp</file>
|
||||
<file>airlinelogos/DLH.bmp</file>
|
||||
<file>airlinelogos/DNV.bmp</file>
|
||||
<file>airlinelogos/DOB.bmp</file>
|
||||
<file>airlinelogos/DQI.bmp</file>
|
||||
<file>airlinelogos/DRK.bmp</file>
|
||||
<file>airlinelogos/DRU.bmp</file>
|
||||
<file>airlinelogos/DSM.bmp</file>
|
||||
<file>airlinelogos/DTA.bmp</file>
|
||||
<file>airlinelogos/DTH.bmp</file>
|
||||
<file>airlinelogos/DTR.bmp</file>
|
||||
<file>airlinelogos/DYA.bmp</file>
|
||||
<file>airlinelogos/EAA.bmp</file>
|
||||
<file>airlinelogos/EAL.bmp</file>
|
||||
<file>airlinelogos/EAQ.bmp</file>
|
||||
<file>airlinelogos/ECO.bmp</file>
|
||||
<file>airlinelogos/EGF.bmp</file>
|
||||
<file>airlinelogos/EKA.bmp</file>
|
||||
<file>airlinelogos/ELL.bmp</file>
|
||||
<file>airlinelogos/EMB.bmp</file>
|
||||
<file>airlinelogos/ENJ.bmp</file>
|
||||
<file>airlinelogos/ENT.bmp</file>
|
||||
<file>airlinelogos/EPA.bmp</file>
|
||||
<file>airlinelogos/ERT.bmp</file>
|
||||
<file>airlinelogos/ESQ.bmp</file>
|
||||
<file>airlinelogos/ETD.bmp</file>
|
||||
<file>airlinelogos/ETH.bmp</file>
|
||||
<file>airlinelogos/ETS.bmp</file>
|
||||
<file>airlinelogos/EUG.bmp</file>
|
||||
<file>airlinelogos/EVA.bmp</file>
|
||||
<file>airlinelogos/EWG.bmp</file>
|
||||
<file>airlinelogos/EXS.bmp</file>
|
||||
<file>airlinelogos/EYT.bmp</file>
|
||||
<file>airlinelogos/EZA.bmp</file>
|
||||
<file>airlinelogos/EZD.bmp</file>
|
||||
<file>airlinelogos/EZE.bmp</file>
|
||||
<file>airlinelogos/EZY.bmp</file>
|
||||
<file>airlinelogos/FAB.bmp</file>
|
||||
<file>airlinelogos/FAG.bmp</file>
|
||||
<file>airlinelogos/FAH.bmp</file>
|
||||
<file>airlinelogos/FAT.bmp</file>
|
||||
<file>airlinelogos/FBD.bmp</file>
|
||||
<file>airlinelogos/FBR.bmp</file>
|
||||
<file>airlinelogos/FCM.bmp</file>
|
||||
<file>airlinelogos/FDB.bmp</file>
|
||||
<file>airlinelogos/FDX.bmp</file>
|
||||
<file>airlinelogos/FFT.bmp</file>
|
||||
<file>airlinelogos/FFV.bmp</file>
|
||||
<file>airlinelogos/FHY.bmp</file>
|
||||
<file>airlinelogos/FIN.bmp</file>
|
||||
<file>airlinelogos/FJA.bmp</file>
|
||||
<file>airlinelogos/FJI.bmp</file>
|
||||
<file>airlinelogos/FLE.bmp</file>
|
||||
<file>airlinelogos/FLI.bmp</file>
|
||||
<file>airlinelogos/FNA.bmp</file>
|
||||
<file>airlinelogos/FPK.bmp</file>
|
||||
<file>airlinelogos/FPO.bmp</file>
|
||||
<file>airlinelogos/FPY.bmp</file>
|
||||
<file>airlinelogos/FRF.bmp</file>
|
||||
<file>airlinelogos/FSK.bmp</file>
|
||||
<file>airlinelogos/FTZ.bmp</file>
|
||||
<file>airlinelogos/FWI.bmp</file>
|
||||
<file>airlinelogos/FZA.bmp</file>
|
||||
<file>airlinelogos/FZW.bmp</file>
|
||||
<file>airlinelogos/GAA.bmp</file>
|
||||
<file>airlinelogos/GAI.bmp</file>
|
||||
<file>airlinelogos/GBK.bmp</file>
|
||||
<file>airlinelogos/GBQ.bmp</file>
|
||||
<file>airlinelogos/GCR.bmp</file>
|
||||
<file>airlinelogos/GDC.bmp</file>
|
||||
<file>airlinelogos/GEA.bmp</file>
|
||||
<file>airlinelogos/GEC.bmp</file>
|
||||
<file>airlinelogos/GEO.bmp</file>
|
||||
<file>airlinelogos/GFA.bmp</file>
|
||||
<file>airlinelogos/GFG.bmp</file>
|
||||
<file>airlinelogos/GGN.bmp</file>
|
||||
<file>airlinelogos/GIA.bmp</file>
|
||||
<file>airlinelogos/GLG.bmp</file>
|
||||
<file>airlinelogos/GLJ.bmp</file>
|
||||
<file>airlinelogos/GLO.bmp</file>
|
||||
<file>airlinelogos/GLR.bmp</file>
|
||||
<file>airlinelogos/GMI.bmp</file>
|
||||
<file>airlinelogos/GMQ.bmp</file>
|
||||
<file>airlinelogos/GOW.bmp</file>
|
||||
<file>airlinelogos/GRL.bmp</file>
|
||||
<file>airlinelogos/GTI.bmp</file>
|
||||
<file>airlinelogos/GTV.bmp</file>
|
||||
<file>airlinelogos/GUG.bmp</file>
|
||||
<file>airlinelogos/GUY.bmp</file>
|
||||
<file>airlinelogos/GWI.bmp</file>
|
||||
<file>airlinelogos/GXL.bmp</file>
|
||||
<file>airlinelogos/HAL.bmp</file>
|
||||
<file>airlinelogos/HAT.bmp</file>
|
||||
<file>airlinelogos/HBH.bmp</file>
|
||||
<file>airlinelogos/HCC.bmp</file>
|
||||
<file>airlinelogos/HCV.bmp</file>
|
||||
<file>airlinelogos/HLX.bmp</file>
|
||||
<file>airlinelogos/HOP.bmp</file>
|
||||
<file>airlinelogos/HRV.bmp</file>
|
||||
<file>airlinelogos/HUN.bmp</file>
|
||||
<file>airlinelogos/HVN.bmp</file>
|
||||
<file>airlinelogos/HVY.bmp</file>
|
||||
<file>airlinelogos/HXA.bmp</file>
|
||||
<file>airlinelogos/IAD.bmp</file>
|
||||
<file>airlinelogos/IAW.bmp</file>
|
||||
<file>airlinelogos/IBB.bmp</file>
|
||||
<file>airlinelogos/IBE.bmp</file>
|
||||
<file>airlinelogos/IBS.bmp</file>
|
||||
<file>airlinelogos/ICE.bmp</file>
|
||||
<file>airlinelogos/IFC.bmp</file>
|
||||
<file>airlinelogos/IGA.bmp</file>
|
||||
<file>airlinelogos/IGO.bmp</file>
|
||||
<file>airlinelogos/IJM.bmp</file>
|
||||
<file>airlinelogos/Inter Island Air.bmp</file>
|
||||
<file>airlinelogos/IRA.bmp</file>
|
||||
<file>airlinelogos/IRC.bmp</file>
|
||||
<file>airlinelogos/IRK.bmp</file>
|
||||
<file>airlinelogos/IRM.bmp</file>
|
||||
<file>airlinelogos/ISK.bmp</file>
|
||||
<file>airlinelogos/ISS.bmp</file>
|
||||
<file>airlinelogos/ISV.bmp</file>
|
||||
<file>airlinelogos/IYE.bmp</file>
|
||||
<file>airlinelogos/JAF.bmp</file>
|
||||
<file>airlinelogos/JAI.bmp</file>
|
||||
<file>airlinelogos/JAT.bmp</file>
|
||||
<file>airlinelogos/JATnew.bmp</file>
|
||||
<file>airlinelogos/JAV.bmp</file>
|
||||
<file>airlinelogos/JBU.bmp</file>
|
||||
<file>airlinelogos/JCC.bmp</file>
|
||||
<file>airlinelogos/JET.bmp</file>
|
||||
<file>airlinelogos/JG.bmp</file>
|
||||
<file>airlinelogos/JLL.bmp</file>
|
||||
<file>airlinelogos/JNA.bmp</file>
|
||||
<file>airlinelogos/JOR.bmp</file>
|
||||
<file>airlinelogos/JOY.bmp</file>
|
||||
<file>airlinelogos/JSA.bmp</file>
|
||||
<file>airlinelogos/JST.bmp</file>
|
||||
<file>airlinelogos/JTF.bmp</file>
|
||||
<file>airlinelogos/JTG.bmp</file>
|
||||
<file>airlinelogos/JUS.bmp</file>
|
||||
<file>airlinelogos/JW.bmp</file>
|
||||
<file>airlinelogos/JYH.bmp</file>
|
||||
<file>airlinelogos/JZA.bmp</file>
|
||||
<file>airlinelogos/JZR.bmp</file>
|
||||
<file>airlinelogos/KAB.bmp</file>
|
||||
<file>airlinelogos/KAC.bmp</file>
|
||||
<file>airlinelogos/KAL.bmp</file>
|
||||
<file>airlinelogos/KFA.bmp</file>
|
||||
<file>airlinelogos/KGL.bmp</file>
|
||||
<file>airlinelogos/KGO.bmp</file>
|
||||
<file>airlinelogos/KHH.bmp</file>
|
||||
<file>airlinelogos/KHV.bmp</file>
|
||||
<file>airlinelogos/KIL.bmp</file>
|
||||
<file>airlinelogos/KKK.bmp</file>
|
||||
<file>airlinelogos/KLC.bmp</file>
|
||||
<file>airlinelogos/KLM.bmp</file>
|
||||
<file>airlinelogos/KMF.bmp</file>
|
||||
<file>airlinelogos/KN.bmp</file>
|
||||
<file>airlinelogos/KNA.bmp</file>
|
||||
<file>airlinelogos/KNE.bmp</file>
|
||||
<file>airlinelogos/KOR.bmp</file>
|
||||
<file>airlinelogos/KQA.bmp</file>
|
||||
<file>airlinelogos/KRE.bmp</file>
|
||||
<file>airlinelogos/KRN.bmp</file>
|
||||
<file>airlinelogos/KRP.bmp</file>
|
||||
<file>airlinelogos/KZR.bmp</file>
|
||||
<file>airlinelogos/LAA.bmp</file>
|
||||
<file>airlinelogos/LAL.bmp</file>
|
||||
<file>airlinelogos/LAN.bmp</file>
|
||||
<file>airlinelogos/LAV.bmp</file>
|
||||
<file>airlinelogos/LBN.bmp</file>
|
||||
<file>airlinelogos/LBR.bmp</file>
|
||||
<file>airlinelogos/LBY.bmp</file>
|
||||
<file>airlinelogos/LC.bmp</file>
|
||||
<file>airlinelogos/LCO.bmp</file>
|
||||
<file>airlinelogos/LER.bmp</file>
|
||||
<file>airlinelogos/LFO.bmp</file>
|
||||
<file>airlinelogos/LGL.bmp</file>
|
||||
<file>airlinelogos/LIA.bmp</file>
|
||||
<file>airlinelogos/LKA.bmp</file>
|
||||
<file>airlinelogos/LKE.bmp</file>
|
||||
<file>airlinelogos/LLP.bmp</file>
|
||||
<file>airlinelogos/LLR.bmp</file>
|
||||
<file>airlinelogos/LMU.bmp</file>
|
||||
<file>airlinelogos/LNI.bmp</file>
|
||||
<file>airlinelogos/LNK.bmp</file>
|
||||
<file>airlinelogos/LOG.bmp</file>
|
||||
<file>airlinelogos/LOT.bmp</file>
|
||||
<file>airlinelogos/LPA.bmp</file>
|
||||
<file>airlinelogos/LPV.bmp</file>
|
||||
<file>airlinelogos/LRC.bmp</file>
|
||||
<file>airlinelogos/LUR.bmp</file>
|
||||
<file>airlinelogos/LVR.bmp</file>
|
||||
<file>airlinelogos/LYM.bmp</file>
|
||||
<file>airlinelogos/LZB.bmp</file>
|
||||
<file>airlinelogos/MAC.bmp</file>
|
||||
<file>airlinelogos/MAL.bmp</file>
|
||||
<file>airlinelogos/MAR.bmp</file>
|
||||
<file>airlinelogos/MAS.bmp</file>
|
||||
<file>airlinelogos/MAU.bmp</file>
|
||||
<file>airlinelogos/MDG.bmp</file>
|
||||
<file>airlinelogos/MEA.bmp</file>
|
||||
<file>airlinelogos/MGX.bmp</file>
|
||||
<file>airlinelogos/MHS.bmp</file>
|
||||
<file>airlinelogos/MJF.bmp</file>
|
||||
<file>airlinelogos/MKG.bmp</file>
|
||||
<file>airlinelogos/MLD.bmp</file>
|
||||
<file>airlinelogos/MLH.bmp</file>
|
||||
<file>airlinelogos/MLO.bmp</file>
|
||||
<file>airlinelogos/MLT.bmp</file>
|
||||
<file>airlinelogos/MMA.bmp</file>
|
||||
<file>airlinelogos/MMD.bmp</file>
|
||||
<file>airlinelogos/MMZ.bmp</file>
|
||||
<file>airlinelogos/MNB.bmp</file>
|
||||
<file>airlinelogos/MNO.bmp</file>
|
||||
<file>airlinelogos/MON.bmp</file>
|
||||
<file>airlinelogos/MPA.bmp</file>
|
||||
<file>airlinelogos/MPE.bmp</file>
|
||||
<file>airlinelogos/MPH.bmp</file>
|
||||
<file>airlinelogos/MSC.bmp</file>
|
||||
<file>airlinelogos/MSE.bmp</file>
|
||||
<file>airlinelogos/MSF.bmp</file>
|
||||
<file>airlinelogos/MSR.bmp</file>
|
||||
<file>airlinelogos/MSX.bmp</file>
|
||||
<file>airlinelogos/MYP.bmp</file>
|
||||
<file>airlinelogos/MZN.bmp</file>
|
||||
<file>airlinelogos/NAC.bmp</file>
|
||||
<file>airlinelogos/NAX.bmp</file>
|
||||
<file>airlinelogos/NCB.bmp</file>
|
||||
<file>airlinelogos/NGB.bmp</file>
|
||||
<file>airlinelogos/NGT.bmp</file>
|
||||
<file>airlinelogos/NIA.bmp</file>
|
||||
<file>airlinelogos/NKS.bmp</file>
|
||||
<file>airlinelogos/NLU.bmp</file>
|
||||
<file>airlinelogos/NLY.bmp</file>
|
||||
<file>airlinelogos/NMA.bmp</file>
|
||||
<file>airlinelogos/NMB.bmp</file>
|
||||
<file>airlinelogos/NOK.bmp</file>
|
||||
<file>airlinelogos/NOS.bmp</file>
|
||||
<file>airlinelogos/NPT.bmp</file>
|
||||
<file>airlinelogos/NRL.bmp</file>
|
||||
<file>airlinelogos/NSE.bmp</file>
|
||||
<file>airlinelogos/NVC.bmp</file>
|
||||
<file>airlinelogos/NVD.bmp</file>
|
||||
<file>airlinelogos/NVR.bmp</file>
|
||||
<file>airlinelogos/NWS.bmp</file>
|
||||
<file>airlinelogos/OAE.bmp</file>
|
||||
<file>airlinelogos/OAL.bmp</file>
|
||||
<file>airlinelogos/OBS.bmp</file>
|
||||
<file>airlinelogos/OCA.bmp</file>
|
||||
<file>airlinelogos/OHY.bmp</file>
|
||||
<file>airlinelogos/OKA.bmp</file>
|
||||
<file>airlinelogos/OKS.bmp</file>
|
||||
<file>airlinelogos/OLC.bmp</file>
|
||||
<file>airlinelogos/OMA.bmp</file>
|
||||
<file>airlinelogos/ONE.bmp</file>
|
||||
<file>airlinelogos/ONX.bmp</file>
|
||||
<file>airlinelogos/OPJ.bmp</file>
|
||||
<file>airlinelogos/ORB.bmp</file>
|
||||
<file>airlinelogos/OTC.bmp</file>
|
||||
<file>airlinelogos/OVA.bmp</file>
|
||||
<file>airlinelogos/OZW.bmp</file>
|
||||
<file>airlinelogos/PAG.bmp</file>
|
||||
<file>airlinelogos/PAL.bmp</file>
|
||||
<file>airlinelogos/PAM.bmp</file>
|
||||
<file>airlinelogos/PCP.bmp</file>
|
||||
<file>airlinelogos/PER.bmp</file>
|
||||
<file>airlinelogos/PEV.bmp</file>
|
||||
<file>airlinelogos/PEX.bmp</file>
|
||||
<file>airlinelogos/PFZ.bmp</file>
|
||||
<file>airlinelogos/PGA.bmp</file>
|
||||
<file>airlinelogos/PGT.bmp</file>
|
||||
<file>airlinelogos/PIA.bmp</file>
|
||||
<file>airlinelogos/PIC.bmp</file>
|
||||
<file>airlinelogos/PKZ.bmp</file>
|
||||
<file>airlinelogos/PLM.bmp</file>
|
||||
<file>airlinelogos/PLV.bmp</file>
|
||||
<file>airlinelogos/PLY.bmp</file>
|
||||
<file>airlinelogos/PMT.bmp</file>
|
||||
<file>airlinelogos/POE.bmp</file>
|
||||
<file>airlinelogos/POT.bmp</file>
|
||||
<file>airlinelogos/PRF.bmp</file>
|
||||
<file>airlinelogos/PRI.bmp</file>
|
||||
<file>airlinelogos/PRW.bmp</file>
|
||||
<file>airlinelogos/PSC.bmp</file>
|
||||
<file>airlinelogos/PST.bmp</file>
|
||||
<file>airlinelogos/PTB.bmp</file>
|
||||
<file>airlinelogos/PTR.bmp</file>
|
||||
<file>airlinelogos/PVN.bmp</file>
|
||||
<file>airlinelogos/PWD.bmp</file>
|
||||
<file>airlinelogos/QAJ.bmp</file>
|
||||
<file>airlinelogos/QDA.bmp</file>
|
||||
<file>airlinelogos/QFA.bmp</file>
|
||||
<file>airlinelogos/QLK.bmp</file>
|
||||
<file>airlinelogos/QTR.bmp</file>
|
||||
<file>airlinelogos/RAE.bmp</file>
|
||||
<file>airlinelogos/RAM.bmp</file>
|
||||
<file>airlinelogos/RAR.bmp</file>
|
||||
<file>airlinelogos/RBA.bmp</file>
|
||||
<file>airlinelogos/RBG.bmp</file>
|
||||
<file>airlinelogos/REU.bmp</file>
|
||||
<file>airlinelogos/RGE.bmp</file>
|
||||
<file>airlinelogos/RJA.bmp</file>
|
||||
<file>airlinelogos/RJD.bmp</file>
|
||||
<file>airlinelogos/RKM.bmp</file>
|
||||
<file>airlinelogos/RLA.bmp</file>
|
||||
<file>airlinelogos/RLH.bmp</file>
|
||||
<file>airlinelogos/RLK.bmp</file>
|
||||
<file>airlinelogos/RLU.bmp</file>
|
||||
<file>airlinelogos/RLX.bmp</file>
|
||||
<file>airlinelogos/RNV.bmp</file>
|
||||
<file>airlinelogos/ROI.bmp</file>
|
||||
<file>airlinelogos/ROT.bmp</file>
|
||||
<file>airlinelogos/ROU.bmp</file>
|
||||
<file>airlinelogos/RPB.bmp</file>
|
||||
<file>airlinelogos/RSY.bmp</file>
|
||||
<file>airlinelogos/RUC.bmp</file>
|
||||
<file>airlinelogos/RWG.bmp</file>
|
||||
<file>airlinelogos/RWZ.bmp</file>
|
||||
<file>airlinelogos/RXA.bmp</file>
|
||||
<file>airlinelogos/RYR.bmp</file>
|
||||
<file>airlinelogos/RYW.bmp</file>
|
||||
<file>airlinelogos/RZO.bmp</file>
|
||||
<file>airlinelogos/SAI.bmp</file>
|
||||
<file>airlinelogos/SAS.bmp</file>
|
||||
<file>airlinelogos/SBM.bmp</file>
|
||||
<file>airlinelogos/SCO.bmp</file>
|
||||
<file>airlinelogos/SCX.bmp</file>
|
||||
<file>airlinelogos/SDM.bmp</file>
|
||||
<file>airlinelogos/SEJ.bmp</file>
|
||||
<file>airlinelogos/SEY.bmp</file>
|
||||
<file>airlinelogos/SFF.bmp</file>
|
||||
<file>airlinelogos/SFW.bmp</file>
|
||||
<file>airlinelogos/SGA.bmp</file>
|
||||
<file>airlinelogos/SGG.bmp</file>
|
||||
<file>airlinelogos/SHU.bmp</file>
|
||||
<file>airlinelogos/SIA.bmp</file>
|
||||
<file>airlinelogos/SID.bmp</file>
|
||||
<file>airlinelogos/SIF.bmp</file>
|
||||
<file>airlinelogos/SIN.bmp</file>
|
||||
<file>airlinelogos/SJO.bmp</file>
|
||||
<file>airlinelogos/SKK.bmp</file>
|
||||
<file>airlinelogos/SKP.bmp</file>
|
||||
<file>airlinelogos/SKU.bmp</file>
|
||||
<file>airlinelogos/SKV.bmp</file>
|
||||
<file>airlinelogos/SKZ.bmp</file>
|
||||
<file>airlinelogos/SLI.bmp</file>
|
||||
<file>airlinelogos/SLK.bmp</file>
|
||||
<file>airlinelogos/SLM.bmp</file>
|
||||
<file>airlinelogos/SLX.bmp</file>
|
||||
<file>airlinelogos/SME.bmp</file>
|
||||
<file>airlinelogos/SMJ.bmp</file>
|
||||
<file>airlinelogos/SMR.bmp</file>
|
||||
<file>airlinelogos/SOR.bmp</file>
|
||||
<file>airlinelogos/SOV.bmp</file>
|
||||
<file>airlinelogos/SPR.bmp</file>
|
||||
<file>airlinelogos/SQC.bmp</file>
|
||||
<file>airlinelogos/SQS.bmp</file>
|
||||
<file>airlinelogos/SRR.bmp</file>
|
||||
<file>airlinelogos/SSQ.bmp</file>
|
||||
<file>airlinelogos/SSV.bmp</file>
|
||||
<file>airlinelogos/SUS.bmp</file>
|
||||
<file>airlinelogos/SVA.bmp</file>
|
||||
<file>airlinelogos/SVR.bmp</file>
|
||||
<file>airlinelogos/SWA.bmp</file>
|
||||
<file>airlinelogos/SWAnew.bmp</file>
|
||||
<file>airlinelogos/SWG.bmp</file>
|
||||
<file>airlinelogos/SWM.bmp</file>
|
||||
<file>airlinelogos/SWR.bmp</file>
|
||||
<file>airlinelogos/SWT.bmp</file>
|
||||
<file>airlinelogos/SXS.bmp</file>
|
||||
<file>airlinelogos/SYL.bmp</file>
|
||||
<file>airlinelogos/TAI.bmp</file>
|
||||
<file>airlinelogos/TAK.bmp</file>
|
||||
<file>airlinelogos/TAM.bmp</file>
|
||||
<file>airlinelogos/TAO.bmp</file>
|
||||
<file>airlinelogos/TAP.bmp</file>
|
||||
<file>airlinelogos/TAR.bmp</file>
|
||||
<file>airlinelogos/TAY.bmp</file>
|
||||
<file>airlinelogos/TBA.bmp</file>
|
||||
<file>airlinelogos/TBN.bmp</file>
|
||||
<file>airlinelogos/TBZ.bmp</file>
|
||||
<file>airlinelogos/TCV.bmp</file>
|
||||
<file>airlinelogos/TCW.bmp</file>
|
||||
<file>airlinelogos/TCX.bmp</file>
|
||||
<file>airlinelogos/TDR.bmp</file>
|
||||
<file>airlinelogos/TFL.bmp</file>
|
||||
<file>airlinelogos/TGW.bmp</file>
|
||||
<file>airlinelogos/TGZ.bmp</file>
|
||||
<file>airlinelogos/THA.bmp</file>
|
||||
<file>airlinelogos/THE.bmp</file>
|
||||
<file>airlinelogos/THT.bmp</file>
|
||||
<file>airlinelogos/THY.bmp</file>
|
||||
<file>airlinelogos/THYANA.bmp</file>
|
||||
<file>airlinelogos/TIA.bmp</file>
|
||||
<file>airlinelogos/TIW.bmp</file>
|
||||
<file>airlinelogos/TJK.bmp</file>
|
||||
<file>airlinelogos/TJS.bmp</file>
|
||||
<file>airlinelogos/TMA.bmp</file>
|
||||
<file>airlinelogos/TMN.bmp</file>
|
||||
<file>airlinelogos/TMW.bmp</file>
|
||||
<file>airlinelogos/TNA.bmp</file>
|
||||
<file>airlinelogos/TNO.bmp</file>
|
||||
<file>airlinelogos/TOM.bmp</file>
|
||||
<file>airlinelogos/TPA.bmp</file>
|
||||
<file>airlinelogos/TPC.bmp</file>
|
||||
<file>airlinelogos/TPU.bmp</file>
|
||||
<file>airlinelogos/TRA.bmp</file>
|
||||
<file>airlinelogos/TSC.bmp</file>
|
||||
<file>airlinelogos/TSG.bmp</file>
|
||||
<file>airlinelogos/TSH.bmp</file>
|
||||
<file>airlinelogos/TSO.bmp</file>
|
||||
<file>airlinelogos/TSY.bmp</file>
|
||||
<file>airlinelogos/TTL.bmp</file>
|
||||
<file>airlinelogos/TUA.bmp</file>
|
||||
<file>airlinelogos/TUI.bmp</file>
|
||||
<file>airlinelogos/TUS.bmp</file>
|
||||
<file>airlinelogos/TUY.bmp</file>
|
||||
<file>airlinelogos/TVF.bmp</file>
|
||||
<file>airlinelogos/TVP.bmp</file>
|
||||
<file>airlinelogos/TVQ.bmp</file>
|
||||
<file>airlinelogos/TVS.bmp</file>
|
||||
<file>airlinelogos/TWI.bmp</file>
|
||||
<file>airlinelogos/UAE.bmp</file>
|
||||
<file>airlinelogos/UAL.bmp</file>
|
||||
<file>airlinelogos/UBD.bmp</file>
|
||||
<file>airlinelogos/UCA.bmp</file>
|
||||
<file>airlinelogos/UEA.bmp</file>
|
||||
<file>airlinelogos/UJX.bmp</file>
|
||||
<file>airlinelogos/ULG.bmp</file>
|
||||
<file>airlinelogos/UPS.bmp</file>
|
||||
<file>airlinelogos/URG.bmp</file>
|
||||
<file>airlinelogos/URS.bmp</file>
|
||||
<file>airlinelogos/UTA.bmp</file>
|
||||
<file>airlinelogos/UTN.bmp</file>
|
||||
<file>airlinelogos/UTP.bmp</file>
|
||||
<file>airlinelogos/UTY.bmp</file>
|
||||
<file>airlinelogos/UZB.bmp</file>
|
||||
<file>airlinelogos/VAL.bmp</file>
|
||||
<file>airlinelogos/VAR.bmp</file>
|
||||
<file>airlinelogos/VAS.bmp</file>
|
||||
<file>airlinelogos/VAU.bmp</file>
|
||||
<file>airlinelogos/VAV.bmp</file>
|
||||
<file>airlinelogos/VBB.bmp</file>
|
||||
<file>airlinelogos/VBW.bmp</file>
|
||||
<file>airlinelogos/VCV.bmp</file>
|
||||
<file>airlinelogos/VDA.bmp</file>
|
||||
<file>airlinelogos/VEL.bmp</file>
|
||||
<file>airlinelogos/VFC.bmp</file>
|
||||
<file>airlinelogos/VIL.bmp</file>
|
||||
<file>airlinelogos/VIM.bmp</file>
|
||||
<file>airlinelogos/VIR.bmp</file>
|
||||
<file>airlinelogos/VIT.bmp</file>
|
||||
<file>airlinelogos/VIV.bmp</file>
|
||||
<file>airlinelogos/VJC.bmp</file>
|
||||
<file>airlinelogos/VJS.bmp</file>
|
||||
<file>airlinelogos/VKG.bmp</file>
|
||||
<file>airlinelogos/VLG.bmp</file>
|
||||
<file>airlinelogos/VLK.bmp</file>
|
||||
<file>airlinelogos/VLM.bmp</file>
|
||||
<file>airlinelogos/VMP.bmp</file>
|
||||
<file>airlinelogos/VNE.bmp</file>
|
||||
<file>airlinelogos/VNL.bmp</file>
|
||||
<file>airlinelogos/VOE.bmp</file>
|
||||
<file>airlinelogos/VOZ.bmp</file>
|
||||
<file>airlinelogos/VPA.bmp</file>
|
||||
<file>airlinelogos/VRD.bmp</file>
|
||||
<file>airlinelogos/VRE.bmp</file>
|
||||
<file>airlinelogos/VRG.bmp</file>
|
||||
<file>airlinelogos/VSV.bmp</file>
|
||||
<file>airlinelogos/VTA.bmp</file>
|
||||
<file>airlinelogos/VTI.bmp</file>
|
||||
<file>airlinelogos/VTM.bmp</file>
|
||||
<file>airlinelogos/VUN.bmp</file>
|
||||
<file>airlinelogos/VVC.bmp</file>
|
||||
<file>airlinelogos/WAJ.bmp</file>
|
||||
<file>airlinelogos/WDA.bmp</file>
|
||||
<file>airlinelogos/WEN.bmp</file>
|
||||
<file>airlinelogos/WEW.bmp</file>
|
||||
<file>airlinelogos/WFR.bmp</file>
|
||||
<file>airlinelogos/WIF.bmp</file>
|
||||
<file>airlinelogos/WJA.bmp</file>
|
||||
<file>airlinelogos/WLC.bmp</file>
|
||||
<file>airlinelogos/WOW.bmp</file>
|
||||
<file>airlinelogos/WRC.bmp</file>
|
||||
<file>airlinelogos/WSG.bmp</file>
|
||||
<file>airlinelogos/WUK.bmp</file>
|
||||
<file>airlinelogos/WWW.bmp</file>
|
||||
<file>airlinelogos/WZZ.bmp</file>
|
||||
<file>airlinelogos/XAH.bmp</file>
|
||||
<file>airlinelogos/XAX.bmp</file>
|
||||
<file>airlinelogos/XLF.bmp</file>
|
||||
<file>airlinelogos/XLR.bmp</file>
|
||||
<file>airlinelogos/XME.bmp</file>
|
||||
<file>airlinelogos/YZR.bmp</file>
|
||||
</qresource>
|
||||
</RCC>
|
BIN
plugins/channelrx/demodadsb/airlinelogos/5AH.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAF.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAH.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAL.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAR.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAW.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAY.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AAZ.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABB.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABD.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABG.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABL.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABN.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABP.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABQ.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABR.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABS.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABV.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABW.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABX.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ABY.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ACA.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ACG.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ACI.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ACV.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ADN.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ADY.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AEA.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AEE.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AEG.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AEH.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AEI.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AERORESCUE.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AFE.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AFG.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AFL.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AFR.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AFW.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AGF.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AGO.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AGU.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AHK.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AHO.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AHY.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AIB.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AIC.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AIE.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AIH.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AIRCOSTA.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AIZ.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AJA.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AJB.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AJD.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AJI.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AJK.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AJT.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AKC.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ALV.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ALW.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ALX.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ALY.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AMC.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AMU.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AMV.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AMX.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AMY.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ANA.bmp
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/AND.bmp
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
plugins/channelrx/demodadsb/airlinelogos/ANE.bmp
Normal file
After Width: | Height: | Size: 6.7 KiB |