ADS-B: Add support for downloading flight departure and arrival airports and times via aviationstack.com API

pull/1025/head
Jon Beniston 2021-10-29 12:54:10 +01:00
rodzic 2292860f35
commit 1354cfe6c8
15 zmienionych plików z 825 dodań i 152 usunięć

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 121 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 152 KiB

Wyświetl plik

@ -23,7 +23,7 @@
ADSBDemodDisplayDialog::ADSBDemodDisplayDialog(
int removeTimeout, float airportRange, ADSBDemodSettings::AirportType airportMinimumSize,
bool displayHeliports, bool siUnits, QString fontName, int fontSize, bool displayDemodStats,
bool autoResizeTableColumns, QWidget* parent) :
bool autoResizeTableColumns, const QString& apiKey, QWidget* parent) :
QDialog(parent),
m_fontName(fontName),
m_fontSize(fontSize),
@ -37,6 +37,7 @@ ADSBDemodDisplayDialog::ADSBDemodDisplayDialog(
ui->units->setCurrentIndex((int)siUnits);
ui->displayStats->setChecked(displayDemodStats);
ui->autoResizeTableColumns->setChecked(autoResizeTableColumns);
ui->apiKey->setText(apiKey);
}
ADSBDemodDisplayDialog::~ADSBDemodDisplayDialog()
@ -53,6 +54,7 @@ void ADSBDemodDisplayDialog::accept()
m_siUnits = ui->units->currentIndex() == 0 ? false : true;
m_displayDemodStats = ui->displayStats->isChecked();
m_autoResizeTableColumns = ui->autoResizeTableColumns->isChecked();
m_apiKey = ui->apiKey->text();
QDialog::accept();
}

Wyświetl plik

@ -27,7 +27,7 @@ class ADSBDemodDisplayDialog : public QDialog {
public:
explicit ADSBDemodDisplayDialog(int removeTimeout, float airportRange, ADSBDemodSettings::AirportType airportMinimumSize,
bool displayHeliports, bool siUnits, QString fontName, int fontSize, bool displayDemodStats,
bool autoResizeTableColumns, QWidget* parent = 0);
bool autoResizeTableColumns, const QString& apiKey, QWidget* parent = 0);
~ADSBDemodDisplayDialog();
int m_removeTimeout;
@ -39,6 +39,7 @@ public:
int m_fontSize;
bool m_displayDemodStats;
bool m_autoResizeTableColumns;
QString m_apiKey;
private slots:
void accept();

Wyświetl plik

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>351</width>
<height>289</height>
<width>417</width>
<height>287</height>
</rect>
</property>
<property name="font">
@ -23,17 +23,31 @@
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="1">
<widget class="QPushButton" name="font">
<property name="toolTip">
<string>Select a font for the table</string>
</property>
<item row="1" column="0">
<widget class="QLabel" name="airportSizeLabel">
<property name="text">
<string>Select...</string>
<string>Display airports with size</string>
</property>
</widget>
</item>
<item row="4" column="0">
<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="5" column="0">
<widget class="QLabel" name="timeoutLabel">
<property name="text">
<string>Aircraft timeout (s)</string>
@ -50,70 +64,6 @@
</property>
</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="4" column="1">
<widget class="QSpinBox" name="timeout">
<property name="toolTip">
<string>How long in seconds after not receiving 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="1" column="0">
<widget class="QLabel" name="airportSizeLabel">
<property name="text">
<string>Display airports with size</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="airportRangeLabel">
<property name="text">
<string>Airport display distance (km)</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="fontLabel">
<property name="text">
<string>Table font</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="displayStats">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Display demodulator statistics</string>
</property>
<property name="text">
<string>Display demodulator statistics</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="unitsLabel">
<property name="text">
<string>Units</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="airportSize">
<property name="toolTip">
@ -136,31 +86,129 @@
</item>
</widget>
</item>
<item row="7" column="0" colspan="2">
<item row="0" column="0">
<widget class="QLabel" name="unitsLabel">
<property name="text">
<string>Units</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="5" column="1">
<widget class="QSpinBox" name="timeout">
<property name="toolTip">
<string>How long in seconds after not receiving 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="3" column="0">
<widget class="QLabel" name="airportRangeLabel">
<property name="text">
<string>Airport display distance (km)</string>
</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="10" column="0">
<widget class="QLabel" name="apiKeyLabel">
<property name="text">
<string>avaitionstack API key</string>
</property>
</widget>
</item>
<item row="11" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="autoResizeTableColumns">
<property name="toolTip">
<string>Resize the columns in the table after an aircraft is added to it</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="autoResizeTableColumnsLabel">
<property name="text">
<string>Resize columns after adding aircraft</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="units">
<item row="2" column="1">
<widget class="QCheckBox" name="heliports">
<property name="toolTip">
<string>The units to use for altitude, speed and climb rate</string>
<string>When checked, heliports are displayed on the map</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="displayStats">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Display demodulator statistics</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="heliportsLabel">
<property name="text">
<string>Display heliports</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="displayStatsLabel">
<property name="text">
<string>Display demodulator statistics</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLineEdit" name="apiKey">
<property name="toolTip">
<string>aviationstack.com API key for accessing flight information</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>
</layout>

Wyświetl plik

@ -160,9 +160,9 @@ QString Aircraft::getImage()
QString Aircraft::getText(bool all)
{
QStringList list;
if (m_flight.length() > 0)
if (m_callsign.length() > 0)
{
list.append(QString("Flight: %1").arg(m_flight));
list.append(QString("Callsign: %1").arg(m_callsign));
}
else
{
@ -215,10 +215,54 @@ QString Aircraft::getText(bool all)
desc = QString("Descending: %1 (%2)").arg(rate).arg(units);
list.append(QString(desc));
}
if ((m_status.length() > 0) && m_status.compare("No emergency"))
{
if ((m_status.length() > 0) && m_status.compare("No emergency")) {
list.append(m_status);
}
QString flightStatus = m_flightStatusItem->text();
if (!flightStatus.isEmpty()) {
list.append(QString("Flight status: %1").arg(flightStatus));
}
QString dep = m_depItem->text();
if (!dep.isEmpty()) {
list.append(QString("Departed: %1").arg(dep));
}
QString std = m_stdItem->text();
if (!std.isEmpty()) {
list.append(QString("STD: %1").arg(std));
}
QString atd = m_atdItem->text();
if (!atd.isEmpty())
{
list.append(QString("ATD: %1").arg(atd));
}
else
{
QString etd = m_etdItem->text();
if (!etd.isEmpty()) {
list.append(QString("ETD: %1").arg(etd));
}
}
QString arr = m_arrItem->text();
if (!arr.isEmpty()) {
list.append(QString("Arrival: %1").arg(arr));
}
QString sta = m_staItem->text();
if (!sta.isEmpty()) {
list.append(QString("STA: %1").arg(sta));
}
QString ata = m_ataItem->text();
if (!ata.isEmpty())
{
list.append(QString("ATA: %1").arg(ata));
}
else
{
QString eta = m_etaItem->text();
if (!eta.isEmpty()) {
list.append(QString("ETA: %1").arg(eta));
}
}
}
return list.join("\n");
}
@ -501,6 +545,8 @@ QIcon *ADSBDemodGUI::getAirlineIcon(const QString &operatorICAO)
icon = new QIcon(":" + endPath);
m_airlineIcons.insert(operatorICAO, icon);
}
else
qDebug() << "ADSBDemodGUI: No airline logo for " << operatorICAO;
}
return icon;
}
@ -611,7 +657,7 @@ void ADSBDemodGUI::handleADSB(
int row = ui->adsbData->rowCount();
ui->adsbData->setRowCount(row + 1);
ui->adsbData->setItem(row, ADSB_COL_ICAO, aircraft->m_icaoItem);
ui->adsbData->setItem(row, ADSB_COL_FLIGHT, aircraft->m_flightItem);
ui->adsbData->setItem(row, ADSB_COL_CALLSIGN, aircraft->m_callsignItem);
ui->adsbData->setItem(row, ADSB_COL_MODEL, aircraft->m_modelItem);
ui->adsbData->setItem(row, ADSB_COL_AIRLINE, aircraft->m_airlineItem);
ui->adsbData->setItem(row, ADSB_COL_ALTITUDE, aircraft->m_altitudeItem);
@ -635,6 +681,15 @@ void ADSBDemodGUI::handleADSB(
ui->adsbData->setItem(row, ADSB_COL_FRAMECOUNT, aircraft->m_adsbFrameCountItem);
ui->adsbData->setItem(row, ADSB_COL_CORRELATION, aircraft->m_correlationItem);
ui->adsbData->setItem(row, ADSB_COL_RSSI, aircraft->m_rssiItem);
ui->adsbData->setItem(row, ADSB_COL_FLIGHT_STATUS, aircraft->m_flightStatusItem);
ui->adsbData->setItem(row, ADSB_COL_DEP, aircraft->m_depItem);
ui->adsbData->setItem(row, ADSB_COL_ARR, aircraft->m_arrItem);
ui->adsbData->setItem(row, ADSB_COL_STD, aircraft->m_stdItem);
ui->adsbData->setItem(row, ADSB_COL_ETD, aircraft->m_etdItem);
ui->adsbData->setItem(row, ADSB_COL_ATD, aircraft->m_atdItem);
ui->adsbData->setItem(row, ADSB_COL_STA, aircraft->m_staItem);
ui->adsbData->setItem(row, ADSB_COL_ETA, aircraft->m_etaItem);
ui->adsbData->setItem(row, ADSB_COL_ATA, aircraft->m_ataItem);
// Look aircraft up in database
if (m_aircraftInfo != nullptr)
{
@ -764,8 +819,39 @@ void ADSBDemodGUI::handleADSB(
callsign[i] = idMap[c[i]];
callsign[8] = '\0';
aircraft->m_flight = QString(callsign);
aircraft->m_flightItem->setText(aircraft->m_flight);
aircraft->m_callsign = QString(callsign).trimmed();
aircraft->m_callsignItem->setText(aircraft->m_callsign);
// Attempt to map callsign to flight number
if (!aircraft->m_callsign.isEmpty())
{
QRegularExpression flightNoExp("^[A-Z]{2,3}[0-9]{1,4}$");
// Airlines line BA add a single charater suffix that can be stripped
// If the suffix is two characters, then it typically means a digit
// has been replaced which I don't know how to guess
// E.g Easyjet might use callsign EZY67JQ for flight EZY6267
// BA use BAW90BG for BA890
QRegularExpression suffixedFlightNoExp("^([A-Z]{2,3})([0-9]{1,4})[A-Z]?$");
QRegularExpressionMatch suffixMatch;
if (flightNoExp.match(aircraft->m_callsign).hasMatch())
{
aircraft->m_flight = aircraft->m_callsign;
}
else if ((suffixMatch = suffixedFlightNoExp.match(aircraft->m_callsign)).hasMatch())
{
aircraft->m_flight = QString("%1%2").arg(suffixMatch.captured(1)).arg(suffixMatch.captured(2));
}
else
{
// Don't guess, to save wasting API calls
aircraft->m_flight = "";
}
}
else
{
aircraft->m_flight = "";
}
}
else if (((tc >= 5) && (tc <= 18)) || ((tc >= 20) && (tc <= 22)))
{
@ -1136,11 +1222,11 @@ void ADSBDemodGUI::checkStaticNotification(Aircraft *aircraft)
}
if (!match.isEmpty())
{
//QRegularExpression regExp(m_settings.m_notificationSettings[i]->m_regExp);
if (m_settings.m_notificationSettings[i]->m_regularExpression.isValid())
{
if (m_settings.m_notificationSettings[i]->m_regularExpression.match(match).hasMatch())
{
highlightAircraft(aircraft);
if (!m_settings.m_notificationSettings[i]->m_speech.isEmpty()) {
speechNotification(aircraft, m_settings.m_notificationSettings[i]->m_speech);
}
@ -1160,7 +1246,7 @@ void ADSBDemodGUI::checkDynamicNotification(Aircraft *aircraft)
{
for (int i = 0; i < m_settings.m_notificationSettings.size(); i++)
{
if ( (m_settings.m_notificationSettings[i]->m_matchColumn == ADSB_COL_FLIGHT)
if ( (m_settings.m_notificationSettings[i]->m_matchColumn == ADSB_COL_CALLSIGN)
|| (m_settings.m_notificationSettings[i]->m_matchColumn == ADSB_COL_ALTITUDE)
|| (m_settings.m_notificationSettings[i]->m_matchColumn == ADSB_COL_SPEED)
|| (m_settings.m_notificationSettings[i]->m_matchColumn == ADSB_COL_RANGE)
@ -1172,8 +1258,8 @@ void ADSBDemodGUI::checkDynamicNotification(Aircraft *aircraft)
QString match;
switch (m_settings.m_notificationSettings[i]->m_matchColumn)
{
case ADSB_COL_FLIGHT:
match = aircraft->m_flightItem->data(Qt::DisplayRole).toString();
case ADSB_COL_CALLSIGN:
match = aircraft->m_callsignItem->data(Qt::DisplayRole).toString();
break;
case ADSB_COL_ALTITUDE:
match = aircraft->m_altitudeItem->data(Qt::DisplayRole).toString();
@ -1202,6 +1288,7 @@ void ADSBDemodGUI::checkDynamicNotification(Aircraft *aircraft)
{
if (m_settings.m_notificationSettings[i]->m_regularExpression.match(match).hasMatch())
{
highlightAircraft(aircraft);
if (!m_settings.m_notificationSettings[i]->m_speech.isEmpty()) {
speechNotification(aircraft, m_settings.m_notificationSettings[i]->m_speech);
}
@ -1231,7 +1318,7 @@ QString ADSBDemodGUI::subAircraftString(Aircraft *aircraft, const QString &strin
{
QString s = string;
s = s.replace("${icao}", aircraft->m_icaoItem->data(Qt::DisplayRole).toString());
s = s.replace("${flight}", aircraft->m_flightItem->data(Qt::DisplayRole).toString());
s = s.replace("${callsign}", aircraft->m_callsignItem->data(Qt::DisplayRole).toString());
s = s.replace("${aircraft}", aircraft->m_modelItem->data(Qt::DisplayRole).toString());
s = s.replace("${latitude}", aircraft->m_latitudeItem->data(Qt::DisplayRole).toString());
s = s.replace("${longitude}", aircraft->m_longitudeItem->data(Qt::DisplayRole).toString());
@ -1246,6 +1333,15 @@ QString ADSBDemodGUI::subAircraftString(Aircraft *aircraft, const QString &strin
s = s.replace("${manufacturer}", aircraft->m_manufacturerNameItem->data(Qt::DisplayRole).toString());
s = s.replace("${owner}", aircraft->m_ownerItem->data(Qt::DisplayRole).toString());
s = s.replace("${operator}", aircraft->m_operatorICAOItem->data(Qt::DisplayRole).toString());
s = s.replace("${flightstatus}", aircraft->m_flightStatusItem->data(Qt::DisplayRole).toString());
s = s.replace("${departure}", aircraft->m_depItem->data(Qt::DisplayRole).toString());
s = s.replace("${arrival}", aircraft->m_arrItem->data(Qt::DisplayRole).toString());
s = s.replace("${std}", aircraft->m_stdItem->data(Qt::DisplayRole).toString());
s = s.replace("${etd}", aircraft->m_etdItem->data(Qt::DisplayRole).toString());
s = s.replace("${atd}", aircraft->m_atdItem->data(Qt::DisplayRole).toString());
s = s.replace("${sta}", aircraft->m_staItem->data(Qt::DisplayRole).toString());
s = s.replace("${eta}", aircraft->m_etaItem->data(Qt::DisplayRole).toString());
s = s.replace("${ata}", aircraft->m_ataItem->data(Qt::DisplayRole).toString());
return s;
}
@ -1364,6 +1460,45 @@ void ADSBDemodGUI::on_notifications_clicked()
}
}
void ADSBDemodGUI::on_flightInfo_clicked()
{
if (m_flightInformation)
{
// Selection mode is single, so only a single row should be returned
QModelIndexList indexList = ui->adsbData->selectionModel()->selectedRows();
if (!indexList.isEmpty())
{
int row = indexList.at(0).row();
int icao = ui->adsbData->item(row, 0)->text().toInt(nullptr, 16);
if (m_aircraft.contains(icao))
{
Aircraft *aircraft = m_aircraft.value(icao);
if (!aircraft->m_flight.isEmpty())
{
// Download flight information
m_flightInformation->getFlightInformation(aircraft->m_flight);
}
else
{
qDebug() << "ADSBDemodGUI::on_flightInfo_clicked - No flight number for selected aircraft";
}
}
else
{
qDebug() << "ADSBDemodGUI::on_flightInfo_clicked - No aircraft with icao " << icao;
}
}
else
{
qDebug() << "ADSBDemodGUI::on_flightInfo_clicked - No aircraft selected";
}
}
else
{
qDebug() << "ADSBDemodGUI::on_flightInfo_clicked - No flight information service - have you set an API key?";
}
}
void ADSBDemodGUI::on_adsbData_cellClicked(int row, int column)
{
(void) column;
@ -1387,12 +1522,12 @@ void ADSBDemodGUI::on_adsbData_cellDoubleClicked(int row, int column)
{
Aircraft *aircraft = m_aircraft.value(icao);
if (column == ADSB_COL_FLIGHT)
if (column == ADSB_COL_CALLSIGN)
{
if (aircraft->m_flight.length() > 0)
if (!aircraft->m_callsign.isEmpty())
{
// Search for flight on flightradar24
QDesktopServices::openUrl(QUrl(QString("https://www.flightradar24.com/%1").arg(aircraft->m_flight.trimmed())));
QDesktopServices::openUrl(QUrl(QString("https://www.flightradar24.com/%1").arg(aircraft->m_callsign)));
}
}
else
@ -1876,7 +2011,8 @@ void ADSBDemodGUI::on_displaySettings_clicked()
ADSBDemodDisplayDialog dialog(m_settings.m_removeTimeout, m_settings.m_airportRange, m_settings.m_airportMinimumSize,
m_settings.m_displayHeliports, m_settings.m_siUnits,
m_settings.m_tableFontName, m_settings.m_tableFontSize,
m_settings.m_displayDemodStats, m_settings.m_autoResizeTableColumns);
m_settings.m_displayDemodStats, m_settings.m_autoResizeTableColumns,
m_settings.m_apiKey);
if (dialog.exec() == QDialog::Accepted)
{
bool unitsChanged = m_settings.m_siUnits != dialog.m_siUnits;
@ -1890,6 +2026,7 @@ void ADSBDemodGUI::on_displaySettings_clicked()
m_settings.m_tableFontSize = dialog.m_fontSize;
m_settings.m_displayDemodStats = dialog.m_displayDemodStats;
m_settings.m_autoResizeTableColumns = dialog.m_autoResizeTableColumns;
m_settings.m_apiKey = dialog.m_apiKey;
if (unitsChanged)
m_aircraftModel.allAircraftUpdated();
@ -2027,6 +2164,8 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
// Initialise text to speech engine
m_speech = new QTextToSpeech(this);
m_flightInformation = nullptr;
updateDeviceSetList();
displaySettings();
applySettings(true);
@ -2042,6 +2181,11 @@ ADSBDemodGUI::~ADSBDemodGUI()
delete a;
++i;
}
if (m_flightInformation)
{
disconnect(m_flightInformation, &FlightInformation::flightUpdated, this, &ADSBDemodGUI::flightInformationUpdated);
delete m_flightInformation;
}
}
void ADSBDemodGUI::applySettings(bool force)
@ -2141,6 +2285,8 @@ void ADSBDemodGUI::displaySettings()
if (!m_settings.m_displayDemodStats)
ui->stats->setText("");
initFlightInformation();
blockApplySettings(false);
}
@ -2242,7 +2388,7 @@ void ADSBDemodGUI::resizeTable()
int row = ui->adsbData->rowCount();
ui->adsbData->setRowCount(row + 1);
ui->adsbData->setItem(row, ADSB_COL_ICAO, new QTableWidgetItem("ICAO ID"));
ui->adsbData->setItem(row, ADSB_COL_FLIGHT, new QTableWidgetItem("Flight No."));
ui->adsbData->setItem(row, ADSB_COL_CALLSIGN, new QTableWidgetItem("Callsign"));
ui->adsbData->setItem(row, ADSB_COL_MODEL, new QTableWidgetItem("Aircraft12345"));
ui->adsbData->setItem(row, ADSB_COL_AIRLINE, new QTableWidgetItem("airbrigdecargo1"));
ui->adsbData->setItem(row, ADSB_COL_ALTITUDE, new QTableWidgetItem("Alt (ft)"));
@ -2266,31 +2412,96 @@ void ADSBDemodGUI::resizeTable()
ui->adsbData->setItem(row, ADSB_COL_FRAMECOUNT, new QTableWidgetItem("Frames"));
ui->adsbData->setItem(row, ADSB_COL_CORRELATION, new QTableWidgetItem("0.001/0.001/0.001"));
ui->adsbData->setItem(row, ADSB_COL_RSSI, new QTableWidgetItem("-100.0"));
ui->adsbData->setItem(row, ADSB_COL_FLIGHT_STATUS, new QTableWidgetItem("scheduled"));
ui->adsbData->setItem(row, ADSB_COL_DEP, new QTableWidgetItem("WWWW"));
ui->adsbData->setItem(row, ADSB_COL_ARR, new QTableWidgetItem("WWWW"));
ui->adsbData->setItem(row, ADSB_COL_STD, new QTableWidgetItem("12:00 -1"));
ui->adsbData->setItem(row, ADSB_COL_ETD, new QTableWidgetItem("12:00 -1"));
ui->adsbData->setItem(row, ADSB_COL_ATD, new QTableWidgetItem("12:00 -1"));
ui->adsbData->setItem(row, ADSB_COL_STA, new QTableWidgetItem("12:00 +1"));
ui->adsbData->setItem(row, ADSB_COL_ETA, new QTableWidgetItem("12:00 +1"));
ui->adsbData->setItem(row, ADSB_COL_ATA, new QTableWidgetItem("12:00 +1"));
ui->adsbData->resizeColumnsToContents();
ui->adsbData->removeCellWidget(row, ADSB_COL_ICAO);
ui->adsbData->removeCellWidget(row, ADSB_COL_FLIGHT);
ui->adsbData->removeCellWidget(row, ADSB_COL_MODEL);
ui->adsbData->removeCellWidget(row, ADSB_COL_AIRLINE);
ui->adsbData->removeCellWidget(row, ADSB_COL_ALTITUDE);
ui->adsbData->removeCellWidget(row, ADSB_COL_SPEED);
ui->adsbData->removeCellWidget(row, ADSB_COL_HEADING);
ui->adsbData->removeCellWidget(row, ADSB_COL_VERTICALRATE);
ui->adsbData->removeCellWidget(row, ADSB_COL_RANGE);
ui->adsbData->removeCellWidget(row, ADSB_COL_AZEL);
ui->adsbData->removeCellWidget(row, ADSB_COL_LATITUDE);
ui->adsbData->removeCellWidget(row, ADSB_COL_LONGITUDE);
ui->adsbData->removeCellWidget(row, ADSB_COL_CATEGORY);
ui->adsbData->removeCellWidget(row, ADSB_COL_STATUS);
ui->adsbData->removeCellWidget(row, ADSB_COL_SQUAWK);
ui->adsbData->removeCellWidget(row, ADSB_COL_REGISTRATION);
ui->adsbData->removeCellWidget(row, ADSB_COL_COUNTRY);
ui->adsbData->removeCellWidget(row, ADSB_COL_REGISTERED);
ui->adsbData->removeCellWidget(row, ADSB_COL_MANUFACTURER);
ui->adsbData->removeCellWidget(row, ADSB_COL_OWNER);
ui->adsbData->removeCellWidget(row, ADSB_COL_OPERATOR_ICAO);
ui->adsbData->removeCellWidget(row, ADSB_COL_TIME);
ui->adsbData->removeCellWidget(row, ADSB_COL_FRAMECOUNT);
ui->adsbData->removeCellWidget(row, ADSB_COL_CORRELATION);
ui->adsbData->removeCellWidget(row, ADSB_COL_RSSI);
ui->adsbData->setRowCount(row);
}
Aircraft* ADSBDemodGUI::findAircraftByFlight(const QString& flight)
{
QHash<int, Aircraft *>::iterator i = m_aircraft.begin();
while (i != m_aircraft.end())
{
Aircraft *aircraft = i.value();
if (aircraft->m_flight == flight) {
return aircraft;
}
++i;
}
return nullptr;
}
// Convert to hh:mm (+/-days)
QString ADSBDemodGUI::dataTimeToShortString(QDateTime dt)
{
if (dt.isValid())
{
QDate currentDate = QDateTime::currentDateTimeUtc().date();
if (dt.date() == currentDate)
{
return dt.time().toString("hh:mm");
}
else
{
int days = currentDate.daysTo(dt.date());
if (days >= 0) {
return QString("%1 +%2").arg(dt.time().toString("hh:mm")).arg(days);
} else {
return QString("%1 %2").arg(dt.time().toString("hh:mm")).arg(days);
}
}
}
else
{
return "";
}
}
void ADSBDemodGUI::initFlightInformation()
{
if (m_flightInformation)
{
disconnect(m_flightInformation, &FlightInformation::flightUpdated, this, &ADSBDemodGUI::flightInformationUpdated);
delete m_flightInformation;
m_flightInformation = nullptr;
}
if (!m_settings.m_apiKey.isEmpty())
{
m_flightInformation = FlightInformation::create(m_settings.m_apiKey);
if (m_flightInformation) {
connect(m_flightInformation, &FlightInformation::flightUpdated, this, &ADSBDemodGUI::flightInformationUpdated);
}
}
}
void ADSBDemodGUI::flightInformationUpdated(const FlightInformation::Flight& flight)
{
Aircraft* aircraft = findAircraftByFlight(flight.m_flightICAO);
if (aircraft)
{
aircraft->m_flightStatusItem->setText(flight.m_flightStatus);
aircraft->m_depItem->setText(flight.m_departureICAO);
aircraft->m_arrItem->setText(flight.m_arrivalICAO);
aircraft->m_stdItem->setText(dataTimeToShortString(flight.m_departureScheduled));
aircraft->m_etdItem->setText(dataTimeToShortString(flight.m_departureEstimated));
aircraft->m_atdItem->setText(dataTimeToShortString(flight.m_departureActual));
aircraft->m_staItem->setText(dataTimeToShortString(flight.m_arrivalScheduled));
aircraft->m_etaItem->setText(dataTimeToShortString(flight.m_arrivalEstimated));
aircraft->m_ataItem->setText(dataTimeToShortString(flight.m_arrivalActual));
if (aircraft->m_positionValid) {
m_aircraftModel.aircraftUpdated(aircraft);
}
}
else
{
qDebug() << "ADSBDemodGUI::flightInformationUpdated - Flight not found in ADS-B table: " << flight.m_flightICAO;
}
}

Wyświetl plik

@ -35,6 +35,7 @@
#include "util/azel.h"
#include "util/movingaverage.h"
#include "util/httpdownloadmanager.h"
#include "util/flightinformation.h"
#include "maincore.h"
#include "adsbdemodsettings.h"
@ -78,7 +79,8 @@ public:
// Data about an aircraft extracted from an ADS-B frames
struct Aircraft {
int m_icao; // 24-bit ICAO aircraft address
QString m_flight; // Flight callsign
QString m_callsign; // Flight callsign
QString m_flight; // Guess at flight number
Real m_latitude; // Latitude in decimal degrees
Real m_longitude; // Longitude in decimal degrees
int m_altitude; // Altitude in feet
@ -130,7 +132,7 @@ struct Aircraft {
// GUI table items for above data
QTableWidgetItem *m_icaoItem;
QTableWidgetItem *m_flightItem;
QTableWidgetItem *m_callsignItem;
QTableWidgetItem *m_modelItem;
QTableWidgetItem *m_airlineItem;
QTableWidgetItem *m_latitudeItem;
@ -154,6 +156,15 @@ struct Aircraft {
QTableWidgetItem *m_adsbFrameCountItem;
QTableWidgetItem *m_correlationItem;
QTableWidgetItem *m_rssiItem;
QTableWidgetItem *m_flightStatusItem;
QTableWidgetItem *m_depItem;
QTableWidgetItem *m_arrItem;
QTableWidgetItem *m_stdItem;
QTableWidgetItem *m_etdItem;
QTableWidgetItem *m_atdItem;
QTableWidgetItem *m_staItem;
QTableWidgetItem *m_etaItem;
QTableWidgetItem *m_ataItem;
Aircraft(ADSBDemodGUI *gui) :
m_icao(0),
@ -187,7 +198,7 @@ struct Aircraft {
}
// These are deleted by QTableWidget
m_icaoItem = new QTableWidgetItem();
m_flightItem = new QTableWidgetItem();
m_callsignItem = new QTableWidgetItem();
m_modelItem = new QTableWidgetItem();
m_airlineItem = new QTableWidgetItem();
m_altitudeItem = new QTableWidgetItem();
@ -211,6 +222,15 @@ struct Aircraft {
m_adsbFrameCountItem = new QTableWidgetItem();
m_correlationItem = new QTableWidgetItem();
m_rssiItem = new QTableWidgetItem();
m_flightStatusItem = new QTableWidgetItem();
m_depItem = new QTableWidgetItem();
m_arrItem = new QTableWidgetItem();
m_stdItem = new QTableWidgetItem();
m_etdItem = new QTableWidgetItem();
m_atdItem = new QTableWidgetItem();
m_staItem = new QTableWidgetItem();
m_etaItem = new QTableWidgetItem();
m_ataItem = new QTableWidgetItem();
}
QString getImage();
@ -219,8 +239,8 @@ struct Aircraft {
// Name to use when selected as a target
QString targetName()
{
if (!m_flight.isEmpty())
return QString("Flight: %1").arg(m_flight);
if (!m_callsign.isEmpty())
return QString("Callsign: %1").arg(m_callsign);
else
return QString("ICAO: %1").arg(m_icao, 0, 16);
}
@ -480,6 +500,7 @@ public:
public slots:
void channelMarkerChangedByCursor();
void channelMarkerHighlightedByCursor();
void flightInformationUpdated(const FlightInformation::Flight& flight);
private:
Ui::ADSBDemodGUI* ui;
@ -516,7 +537,7 @@ private:
QTextToSpeech *m_speech;
QMenu *menu; // Column select context menu
FlightInformation *m_flightInformation;
WebAPIAdapterInterface *m_webAPIAdapterInterface;
HttpDownloadManager m_dlm;
QProgressDialog *m_progressDialog;
@ -558,6 +579,9 @@ private:
QIcon *getFlagIcon(const QString &country);
void updateDeviceSetList();
QAction *createCheckableItem(QString& text, int idx, bool checked);
Aircraft* findAircraftByFlight(const QString& flight);
QString dataTimeToShortString(QDateTime dt);
void initFlightInformation();
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
@ -579,6 +603,7 @@ private slots:
void on_demodModeS_clicked(bool checked);
void on_feed_clicked(bool checked);
void on_notifications_clicked();
void on_flightInfo_clicked();
void on_getOSNDB_clicked();
void on_getAirportDB_clicked();
void on_flightPaths_clicked(bool checked);

Wyświetl plik

@ -613,7 +613,24 @@
<normaloff>:/mono.png</normaloff>:/mono.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="flightInfo">
<property name="toolTip">
<string>Download flight information for selected aircraft</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/info.png</normaloff>:/info.png</iconset>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
@ -721,10 +738,10 @@
</column>
<column>
<property name="text">
<string>Flight No.</string>
<string>Callsign</string>
</property>
<property name="toolTip">
<string>Commercial flight number. Links to www.flightradar24.com</string>
<string>Callsign. Links to www.flightradar24.com</string>
</property>
</column>
<column>
@ -908,6 +925,78 @@
<string>RSSI</string>
</property>
</column>
<column>
<property name="text">
<string>Flight Status</string>
</property>
<property name="toolTip">
<string>Status of flight</string>
</property>
</column>
<column>
<property name="text">
<string>Dep</string>
</property>
<property name="toolTip">
<string>Departure airport</string>
</property>
</column>
<column>
<property name="text">
<string>Arr</string>
</property>
<property name="toolTip">
<string>Arrival airport</string>
</property>
</column>
<column>
<property name="text">
<string>STD</string>
</property>
<property name="toolTip">
<string>Scheduled time of departure</string>
</property>
</column>
<column>
<property name="text">
<string>ETD</string>
</property>
<property name="toolTip">
<string>Estimated time of departure</string>
</property>
</column>
<column>
<property name="text">
<string>ATD</string>
</property>
<property name="toolTip">
<string>Actual time of departure</string>
</property>
</column>
<column>
<property name="text">
<string>STA</string>
</property>
<property name="toolTip">
<string>Scheduled time of arrival</string>
</property>
</column>
<column>
<property name="text">
<string>ETA</string>
</property>
<property name="toolTip">
<string>Estimated time of arrival</string>
</property>
</column>
<column>
<property name="text">
<string>ATA</string>
</property>
<property name="toolTip">
<string>Actual time of arrival</string>
</property>
</column>
</widget>
</item>
</layout>

Wyświetl plik

@ -25,7 +25,7 @@
// Map main ADS-B table column numbers to combo box indicies
std::vector<int> ADSBDemodNotificationDialog::m_columnMap = {
ADSB_COL_ICAO, ADSB_COL_FLIGHT, ADSB_COL_MODEL,
ADSB_COL_ICAO, ADSB_COL_CALLSIGN, ADSB_COL_MODEL,
ADSB_COL_ALTITUDE, ADSB_COL_SPEED, ADSB_COL_RANGE,
ADSB_COL_CATEGORY, ADSB_COL_STATUS, ADSB_COL_SQUAWK,
ADSB_COL_REGISTRATION, ADSB_COL_MANUFACTURER, ADSB_COL_OWNER, ADSB_COL_OPERATOR_ICAO
@ -111,7 +111,7 @@ void ADSBDemodNotificationDialog::addRow(ADSBDemodSettings::NotificationSettings
matchWidget->setLayout(pLayout);
match->addItem("ICAO ID");
match->addItem("Flight No.");
match->addItem("Callsign");
match->addItem("Aircraft");
match->addItem("Alt (ft)");
match->addItem("Spd (kn)");

Wyświetl plik

@ -66,6 +66,7 @@ void ADSBDemodSettings::resetToDefaults()
m_autoResizeTableColumns = false;
m_interpolatorPhaseSteps = 4; // Higher than these two values will struggle to run in real-time
m_interpolatorTapsPerPhase = 3.5f; // without gaining much improvement in PER
m_apiKey = "";
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
{
m_columnIndexes[i] = i;
@ -115,6 +116,7 @@ QByteArray ADSBDemodSettings::serialize() const
s.writeBool(33, m_allFlightPaths);
s.writeBlob(34, serializeNotificationSettings(m_notificationSettings));
s.writeString(35, m_apiKey);
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
s.writeS32(100 + i, m_columnIndexes[i]);
@ -199,6 +201,7 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
d.readBlob(34, &blob);
deserializeNotificationSettings(blob, m_notificationSettings);
d.readString(35, &m_apiKey, "");
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
d.readS32(100 + i, &m_columnIndexes[i], i);
@ -259,7 +262,7 @@ void ADSBDemodSettings::NotificationSettings::updateRegularExpression()
{
m_regularExpression.setPattern(m_regExp);
m_regularExpression.optimize();
if (m_regularExpression.isValid()) {
if (!m_regularExpression.isValid()) {
qDebug() << "ADSBDemod: Regular expression is not valid: " << m_regExp;
}
}

Wyświetl plik

@ -29,11 +29,11 @@
class Serializable;
// Number of columns in the table
#define ADSBDEMOD_COLUMNS 25
#define ADSBDEMOD_COLUMNS 34
// ADS-B table columns
#define ADSB_COL_ICAO 0
#define ADSB_COL_FLIGHT 1
#define ADSB_COL_CALLSIGN 1
#define ADSB_COL_MODEL 2
#define ADSB_COL_AIRLINE 3
#define ADSB_COL_ALTITUDE 4
@ -57,6 +57,15 @@ class Serializable;
#define ADSB_COL_FRAMECOUNT 22
#define ADSB_COL_CORRELATION 23
#define ADSB_COL_RSSI 24
#define ADSB_COL_FLIGHT_STATUS 25
#define ADSB_COL_DEP 26
#define ADSB_COL_ARR 27
#define ADSB_COL_STD 28
#define ADSB_COL_ETD 29
#define ADSB_COL_ATD 30
#define ADSB_COL_STA 31
#define ADSB_COL_ETA 32
#define ADSB_COL_ATA 33
struct ADSBDemodSettings
{
@ -119,6 +128,7 @@ struct ADSBDemodSettings
float m_interpolatorTapsPerPhase;
QList<NotificationSettings *> m_notificationSettings;
QString m_apiKey; //!< aviationstack.com API key
ADSBDemodSettings();
void resetToDefaults();

Wyświetl plik

@ -67,6 +67,8 @@ Clicking the Display Settings button will open the Display Settings dialog, whic
* Whether demodulator statistics are displayed (primarily an option for developers).
* Whether the columns in the table are automatically resized after an aircraft is added to it. If unchecked, columns can be resized manually and should be saved with presets.
You can also enter an [avaiationstack](https://aviationstack.com/product) API key, needed to download flight information (such as departure and arrival airports and times).
<h3>12: Display Flight Paths</h3>
Checking this button draws a line on the map showing aircraft's flight paths, as determined from received ADS-B frames.
@ -125,10 +127,10 @@ Emergency status are:
* Unlawful interference
* Downed aircraft
In the Speech and Command strings, variables can be used to substitute in ADS-B data for the aircraft:
In the Speech and Command strings, variables can be used to substitute in data from the ADS-B table for the aircraft:
* ${icao},
* ${flight}
* ${callsign}
* ${aircraft}
* ${latitude}
* ${longitude}
@ -143,6 +145,23 @@ In the Speech and Command strings, variables can be used to substitute in ADS-B
* ${manufacturer}
* ${owner}
* ${operator}
* ${flightstatus}
* ${departure}
* ${arrival}
* ${std}
* ${etd}
* ${atd}
* ${sta}
* ${eta}
* ${ata}
<h3>Download flight information for selected flight</h3>
When clicked, flight information (departure and arrival airport and times) is downloaded for the aircraft highlighted in the ADS-B data table using the aviationstack.com API.
To be able to use this, a callsign for the highlighted aircraft must have been received. Also, the callsign must be mappable to a flight number, which is not always possible (this is tpyically
the case for callsigns that end in two characters, as for these, some digits from the flight number will have been omitted).
To use this feature, an aviationstack API Key must be entered in the Display Settings dialog (11). A free key giving 500 API calls per month is available from: https://aviationstack.com/product
<h3>14: Refresh list of devices</h3>
@ -154,12 +173,12 @@ Specify the SDRangel device set that will be have its centre frequency set when
<h3>ADS-B Data</h3>
The table displays the decoded ADS-B data for each aircraft along side data available for the aircraft from the Opensky Network database. The data is not all able to be transmitted in a single ADS-B frame, so the table displays an amalgamation of the latest received data of each type.
The table displays the decoded ADS-B data for each aircraft along side data available for the aircraft from the Opensky Network database and aviationstack API. The data is not all able to be transmitted in a single ADS-B frame, so the table displays an amalgamation of the latest received data of each type.
![ADS-B Demodulator Data](../../../doc/img/ADSBDemod_plugin_table.png)
* ICAO ID - 24-bit hexidecimal ICAO aircraft address. This is unique for each aircraft. (ADS-B)
* Flight No. - Airline flight number or callsign. (ADS-B)
* Callsign - Aircraft callsign (which is sometimes also the flight number). (ADS-B)
* Aircraft - The aircraft model. (DB)
* Airline - The logo of the operator of the aircraft (or owner if no operator known). (DB)
* Altitude (Alt) - Altitude in feet or metres. (ADS-B)
@ -182,6 +201,15 @@ The table displays the decoded ADS-B data for each aircraft along side data avai
* RX Frames - A count of the number of ADS-B frames received from this aircraft.
* Correlation - Displays the minimun, average and maximum of the preamable correlation in dB for each recevied frame. These values can be used to help select a threshold setting. This correlation value is the ratio between the presence and absence of the signal corresponding to the "ones" and the "zeros" of the sync word adjusted by the bits ratio. It can be interpreted as a SNR estimation.
* RSSI - This Received Signal Strength Indicator is based on the signal power during correlation estimation. This is the power sum during the expected presence of the signal i.e. the "ones" of the sync word.
* Flight status - scheduled, active, landed, cancelled, incident or diverted. (API)
* Dep - Departure airport. (API)
* Arr - Arrival airport. (API)
* STD - Scheduled time of departure. (API)
* ETD - Estimated time of departure. (API)
* ATD - Actual time of departure. (API)
* STA - Scheduled time of arrival. (API)
* ETA - Estimated time of arrival. (API)
* ATA - Actual time of arrival. (API)
If an ADS-B frame has not been received from an aircraft for 60 seconds, the aircraft is removed from the table and map. This timeout can be adjusted in the Display Settings dialog.
@ -190,7 +218,7 @@ If an ADS-B frame has not been received from an aircraft for 60 seconds, the air
* To reorder the columns, left click and drag left or right a column header.
* Left click on a header to sort the table by the data in that column.
* Double clicking in an ICAO ID cell will open a Web browser and search for the corresponding aircraft on https://www.planespotters.net/
* Double clicking in an Flight No cell will open a Web browser and search for the corresponding flight on https://www.flightradar24.com/
* Double clicking in an Callsign cell will open a Web browser and search for the corresponding flight on https://www.flightradar24.com/
* Double clicking in an Az/El cell will set the aircraft as the active target. The azimuth and elevation to the aicraft will be sent to a rotator controller plugin. The aircraft information box will be coloured green, rather than blue, on the map.
* Double clicking on any other cell in the table will centre the map on the corresponding aircraft.

Wyświetl plik

@ -190,6 +190,7 @@ set(sdrbase_SOURCES
util/db.cpp
util/fixedtraits.cpp
util/fits.cpp
util/flightinformation.cpp
util/golay2312.cpp
util/httpdownloadmanager.cpp
util/interpolation.cpp
@ -397,6 +398,7 @@ set(sdrbase_HEADERS
util/doublebuffermultiple.h
util/fixedtraits.h
util/fits.h
util/flightinformation.h
util/golay2312.h
util/httpdownloadmanager.h
util/incrementalarray.h

Wyświetl plik

@ -0,0 +1,164 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 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 "flightinformation.h"
#include <QDebug>
#include <QFile>
#include <QUrl>
#include <QUrlQuery>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
FlightInformation::FlightInformation()
{
}
FlightInformation* FlightInformation::create(const QString& apiKey, const QString& service)
{
if (service == "aviationstack.com")
{
if (!apiKey.isEmpty())
{
return new AviationStack(apiKey);
}
else
{
qDebug() << "FlightInformation::create: An API key is required for: " << service;
return nullptr;
}
}
else
{
qDebug() << "FlightInformation::create: Unsupported service: " << service;
return nullptr;
}
}
AviationStack::AviationStack(const QString& apiKey) :
m_apiKey(apiKey)
{
m_networkManager = new QNetworkAccessManager();
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleReply(QNetworkReply*)));
}
AviationStack::~AviationStack()
{
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleReply(QNetworkReply*)));
delete m_networkManager;
}
void AviationStack::getFlightInformation(const QString& flight)
{
QUrl url(QString("http://api.aviationstack.com/v1/flights"));
QUrlQuery query;
query.addQueryItem("flight_icao",flight);
query.addQueryItem("access_key", m_apiKey);
url.setQuery(query);
m_networkManager->get(QNetworkRequest(url));
/*QFile file("flight.json");
if (file.open(QIODevice::ReadOnly))
{
parseJson(file.readAll());
}*/
}
void AviationStack::handleReply(QNetworkReply* reply)
{
if (reply)
{
if (!reply->error())
{
parseJson(reply->readAll());
}
else
{
qDebug() << "AviationStack::handleReply: error: " << reply->error();
}
reply->deleteLater();
}
else
{
qDebug() << "AviationStack::handleReply: reply is null";
}
}
void AviationStack::parseJson(QByteArray bytes)
{
QJsonDocument document = QJsonDocument::fromJson(bytes);
if (document.isObject())
{
QJsonObject obj = document.object();
if (obj.contains(QStringLiteral("data")))
{
QJsonArray data = obj.value(QStringLiteral("data")).toArray();
if (data.size() > 0)
{
QJsonObject flightObj = data[0].toObject();
Flight flight;
if (flightObj.contains(QStringLiteral("flight_status"))) {
flight.m_flightStatus = flightObj.value(QStringLiteral("flight_status")).toString();
}
if (flightObj.contains(QStringLiteral("departure")))
{
QJsonObject departure = flightObj.value(QStringLiteral("departure")).toObject();
flight.m_departureAirport = departure.value(QStringLiteral("airport")).toString();
flight.m_departureICAO = departure.value(QStringLiteral("icao")).toString();
flight.m_departureTerminal = departure.value(QStringLiteral("terminal")).toString();
flight.m_departureGate = departure.value(QStringLiteral("gate")).toString();
flight.m_departureScheduled = QDateTime::fromString(departure.value(QStringLiteral("scheduled")).toString(), Qt::ISODate);
flight.m_departureEstimated = QDateTime::fromString(departure.value(QStringLiteral("estimated")).toString(), Qt::ISODate);
flight.m_departureActual = QDateTime::fromString(departure.value(QStringLiteral("actual")).toString(), Qt::ISODate);
}
if (flightObj.contains(QStringLiteral("arrival")))
{
QJsonObject departure = flightObj.value(QStringLiteral("arrival")).toObject();
flight.m_arrivalAirport = departure.value(QStringLiteral("airport")).toString();
flight.m_arrivalICAO = departure.value(QStringLiteral("icao")).toString();
flight.m_arrivalTerminal = departure.value(QStringLiteral("terminal")).toString();
flight.m_arrivalGate = departure.value(QStringLiteral("gate")).toString();
flight.m_arrivalScheduled = QDateTime::fromString(departure.value(QStringLiteral("scheduled")).toString(), Qt::ISODate);
flight.m_arrivalEstimated = QDateTime::fromString(departure.value(QStringLiteral("estimated")).toString(), Qt::ISODate);
flight.m_arrivalActual = QDateTime::fromString(departure.value(QStringLiteral("actual")).toString(), Qt::ISODate);
}
if (flightObj.contains(QStringLiteral("flight")))
{
QJsonObject flightNo = flightObj.value(QStringLiteral("flight")).toObject();
flight.m_flightICAO = flightNo.value(QStringLiteral("icao")).toString();
flight.m_flightIATA = flightNo.value(QStringLiteral("iata")).toString();
}
emit flightUpdated(flight);
}
else
{
qDebug() << "AviationStack::handleReply: data array is empty";
}
}
else
{
qDebug() << "AviationStack::handleReply: Object doesn't contain data: " << obj;
}
}
else
{
qDebug() << "AviationStack::handleReply: Document is not an object: " << document;
}
}

Wyświetl plik

@ -0,0 +1,90 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 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_FLIGHTINFORMATION_H
#define INCLUDE_FLIGHTINFORMATION_H
#include <QtCore>
#include <QDateTime>
#include "export.h"
class QNetworkAccessManager;
class QNetworkReply;
// Flight information API wrapper
// Allows searching for departure/arrival airports and status of a flight
// Currently supports aviationstack.com
class SDRBASE_API FlightInformation : public QObject
{
Q_OBJECT
protected:
FlightInformation();
public:
struct Flight {
QString m_flightICAO;
QString m_flightIATA;
QString m_flightStatus; // active, landed...
QString m_departureAirport;
QString m_departureICAO;
QString m_departureTerminal;
QString m_departureGate;
QDateTime m_departureScheduled;
QDateTime m_departureEstimated;
QDateTime m_departureActual;
QString m_arrivalAirport;
QString m_arrivalICAO;
QString m_arrivalTerminal;
QString m_arrivalGate;
QDateTime m_arrivalScheduled;
QDateTime m_arrivalEstimated;
QDateTime m_arrivalActual;
};
static FlightInformation* create(const QString& apiKey, const QString& service="aviationstack.com");
virtual void getFlightInformation(const QString& flight) = 0;
signals:
void flightUpdated(const Flight& flight); // Called when new data available.
private:
};
class SDRBASE_API AviationStack : public FlightInformation {
Q_OBJECT
public:
AviationStack(const QString& apiKey);
~AviationStack();
virtual void getFlightInformation(const QString& flight) override;
private:
void parseJson(QByteArray bytes);
QString m_apiKey;
QNetworkAccessManager *m_networkManager;
public slots:
void handleReply(QNetworkReply* reply);
};
#endif /* INCLUDE_FLIGHTINFORMATION_H */

Wyświetl plik

@ -39,13 +39,13 @@ Weather* Weather::create(const QString& apiKey, const QString& service)
}
else
{
qDebug() << "Weather::connect: An API key is required for: " << service;
qDebug() << "Weather::create: An API key is required for: " << service;
return nullptr;
}
}
else
{
qDebug() << "Weather::connect: Unsupported service: " << service;
qDebug() << "Weather::create: Unsupported service: " << service;
return nullptr;
}
}