Allow ADS-B to send airport ATC frequencies to Frequency Scanner.

Add ATC callsigns.
Add ATC mode, displaying basic info for all aircraft.
Add airport range rings.
Change Device setting to be an AM Demod setting, so AM demod isn't at DC.
Add basic aircraft data to ADSB Web API report.
pull/1861/head
srcejon 2023-10-26 16:31:37 +01:00
rodzic a20e7999d1
commit a398381aaf
26 zmienionych plików z 6735 dodań i 450 usunięć

Plik binarny nie jest wyświetlany.

Przed

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

Po

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

Plik binarny nie jest wyświetlany.

Przed

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

Po

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

Wyświetl plik

@ -49,6 +49,7 @@
#include "adsbdemodworker.h"
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgConfigureADSBDemod, Message)
MESSAGE_CLASS_DEFINITION(ADSBDemod::MsgAircraftReport, Message)
const char* const ADSBDemod::m_channelIdURI = "sdrangel.channel.adsbdemod";
const char* const ADSBDemod::m_channelId = "ADSBDemod";
@ -186,6 +187,12 @@ bool ADSBDemod::handleMessage(const Message& cmd)
return true;
}
else if (MsgAircraftReport::match(cmd))
{
MsgAircraftReport& msg = (MsgAircraftReport&) cmd;
m_aircraftReport = msg.getReport();
return true;
}
else
{
return false;
@ -636,6 +643,19 @@ void ADSBDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& respons
response.getAdsbDemodReport()->setTargetElevation(m_targetElevation);
response.getAdsbDemodReport()->setTargetRange(m_targetRange);
}
QList<SWGSDRangel::SWGADSBDemodAircraftState *> *list = response.getAdsbDemodReport()->getAircraftState();
for (const auto& report : m_aircraftReport)
{
SWGSDRangel::SWGADSBDemodAircraftState *aircraftState = new SWGSDRangel::SWGADSBDemodAircraftState();
//aircraftState->setIcao(new QString(report.m_icao));
aircraftState->setCallsign(new QString(report.m_callsign));
aircraftState->setLatitude(report.m_latitude);
aircraftState->setLongitude(report.m_longitude);
aircraftState->setAltitude(report.m_altitude);
aircraftState->setGroundSpeed(report.m_groundSpeed);
list->append(aircraftState);
}
}
void ADSBDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const ADSBDemodSettings& settings, bool force)

Wyświetl plik

@ -62,6 +62,35 @@ public:
{ }
};
class MsgAircraftReport : public Message {
MESSAGE_CLASS_DECLARATION
public:
struct AircraftReport {
QString m_icao;
QString m_callsign;
float m_latitude;
float m_longitude;
int m_altitude;
int m_groundSpeed;
};
QList<AircraftReport>& getReport() { return m_report; }
static MsgAircraftReport* create()
{
return new MsgAircraftReport();
}
private:
QList<AircraftReport> m_report;
MsgAircraftReport() :
Message()
{ }
};
ADSBDemod(DeviceAPI *deviceAPI);
virtual ~ADSBDemod();
virtual void destroy() { delete this; }
@ -148,6 +177,7 @@ private:
float m_targetElevation;
float m_targetRange;
QString m_targetName;
QList<MsgAircraftReport::AircraftReport> m_aircraftReport;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;

Wyświetl plik

@ -49,9 +49,11 @@ ADSBDemodDisplayDialog::ADSBDemodDisplayDialog(ADSBDemodSettings *settings, QWid
ui->mapProvider->setCurrentText(settings->m_mapProvider);
ui->mapType->setCurrentIndex((int)settings->m_mapType);
ui->navAids->setChecked(settings->m_displayNavAids);
ui->atcCallsigns->setChecked(settings->m_atcCallsigns);
ui->photos->setChecked(settings->m_displayPhotos);
ui->verboseModelMatching->setChecked(settings->m_verboseModelMatching);
ui->airfieldElevation->setValue(settings->m_airfieldElevation);
ui->transitionAltitude->setValue(settings->m_transitionAlt);
}
ADSBDemodDisplayDialog::~ADSBDemodDisplayDialog()
@ -83,9 +85,11 @@ void ADSBDemodDisplayDialog::accept()
m_settings->m_mapProvider = ui->mapProvider->currentText();
m_settings->m_mapType = (ADSBDemodSettings::MapType)ui->mapType->currentIndex();
m_settings->m_displayNavAids = ui->navAids->isChecked();
m_settings->m_atcCallsigns = ui->atcCallsigns->isChecked();
m_settings->m_displayPhotos = ui->photos->isChecked();
m_settings->m_verboseModelMatching = ui->verboseModelMatching->isChecked();
m_settings->m_airfieldElevation = ui->airfieldElevation->value();
m_settings->m_transitionAlt = ui->transitionAltitude->value();
m_settings->m_tableFontName = m_fontName;
m_settings->m_tableFontSize = m_fontSize;
QDialog::accept();

Wyświetl plik

@ -22,7 +22,14 @@
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QGridLayout" name="gridLayout">
<item row="20" column="1">
<item row="9" column="0">
<widget class="QLabel" name="displayNavAids">
<property name="text">
<string>Display NAVAIDs</string>
</property>
</widget>
</item>
<item row="21" column="1">
<widget class="QSpinBox" name="airfieldElevation">
<property name="toolTip">
<string>Barometric altitude reported by aircraft when on airfield surface</string>
@ -38,34 +45,161 @@
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="displayNavAids">
<item row="6" column="0">
<widget class="QLabel" name="airportRangeLabel">
<property name="text">
<string>Display NAVAIDs</string>
<string>Airport display distance (km)</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QCheckBox" name="photos">
<item row="5" column="0">
<widget class="QLabel" name="heliportsLabel">
<property name="text">
<string>Display heliports</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QSpinBox" name="timeout">
<property name="toolTip">
<string>Download and display photos of highlighted aircraft</string>
<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="5" column="1">
<widget class="QCheckBox" name="heliports">
<property name="toolTip">
<string>When checked, heliports are displayed on the map</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QCheckBox" name="autoResizeTableColumns">
<item row="22" column="1">
<widget class="QSpinBox" name="transitionAltitude">
<property name="toolTip">
<string>Resize the columns in the table after an aircraft is added to it</string>
<string>Transition altitude in feet</string>
</property>
<property name="minimum">
<number>2500</number>
</property>
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="airportSize">
<property name="toolTip">
<string>Sets the minimum airport size that will be displayed on the map</string>
</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="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="2" column="0">
<widget class="QLabel" name="mapTypeLabel">
<property name="text">
<string/>
<string>Map type</string>
</property>
</widget>
</item>
<item row="16" column="0">
<widget class="QLabel" name="autoResizeTableColumnsLabel">
<property name="text">
<string>Resize columns after adding aircraft</string>
</property>
</widget>
</item>
<item row="17" 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="18" column="0">
<widget class="QLabel" name="verboseModelMatchingLabel">
<property name="text">
<string>Log 3D model matching information</string>
</property>
</widget>
</item>
<item row="13" 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="20" column="0">
<widget class="QLabel" name="checkWXAPIKeyLabel">
<property name="text">
<string>CheckWX API key</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="photosLabel">
<property name="text">
<string>Display aircraft photos</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="airspacesLabel">
<property name="text">
<string>Airspaces to display</string>
</property>
</widget>
</item>
<item row="18" column="1">
<widget class="QCheckBox" name="verboseModelMatching">
<property name="toolTip">
<string>Log information about how aircraft are matched to 3D models</string>
@ -75,6 +209,13 @@
</property>
</widget>
</item>
<item row="22" column="0">
<widget class="QLabel" name="transitionAltitudeLabel">
<property name="text">
<string>Transition altitude (ft)</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QSpinBox" name="airspaceRange">
<property name="toolTip">
@ -85,37 +226,6 @@
</property>
</widget>
</item>
<item row="16" column="0">
<widget class="QLabel" name="displayStatsLabel">
<property name="text">
<string>Display demodulator statistics</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="navAids">
<property name="toolTip">
<string>Display NAVAIDs such as VORs and NDBs</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mapTypeLabel">
<property name="text">
<string>Map type</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="airportRangeLabel">
<property name="text">
<string>Airport display distance (km)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="mapType">
<property name="toolTip">
@ -143,145 +253,37 @@
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="airportSizeLabel">
<property name="text">
<string>Display airports with size</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="timeoutLabel">
<property name="text">
<string>Aircraft timeout (s)</string>
</property>
</widget>
</item>
<item row="19" column="0">
<widget class="QLabel" name="checkWXAPIKeyLabel">
<property name="text">
<string>CheckWX API key</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="heliportsLabel">
<property name="text">
<string>Display heliports</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QSpinBox" name="timeout">
<item row="20" column="1">
<widget class="QLineEdit" name="checkWXAPIKey">
<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>
<string>checkwxapi.com API key for accessing airport weather (METARs)</string>
</property>
</widget>
</item>
<item row="12" 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="17" column="0">
<widget class="QLabel" name="verboseModelMatchingLabel">
<property name="text">
<string>Log 3D model matching information</string>
</property>
</widget>
</item>
<item row="15" 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">
<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="4" column="1">
<widget class="QComboBox" name="airportSize">
<property name="toolTip">
<string>Sets the minimum airport size that will be displayed on the map</string>
</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="1" column="1">
<widget class="QComboBox" name="mapProvider">
<property name="toolTip">
<string>Mapping service</string>
</property>
<item>
<property name="text">
<string>osm</string>
</property>
</item>
<item>
<property name="text">
<string>mapboxgl</string>
</property>
</item>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="airspaceRangeLabel">
<property name="text">
<string>Airspace display distance (km)</string>
</property>
</widget>
</item>
<item row="18" column="1">
<item row="19" column="1">
<widget class="QLineEdit" name="aviationstackAPIKey">
<property name="toolTip">
<string>aviationstack.com API key for accessing flight information</string>
</property>
</widget>
</item>
<item row="18" column="0">
<item row="19" column="0">
<widget class="QLabel" name="aviationstackAPIKeyLabel">
<property name="text">
<string>avaitionstack API key</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QCheckBox" name="photos">
<property name="toolTip">
<string>Download and display photos of highlighted aircraft</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mapProviderLabel">
<property name="text">
@ -289,53 +291,23 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="unitsLabel">
<property name="text">
<string>Units</string>
</property>
</widget>
</item>
<item row="16" column="1">
<widget class="QCheckBox" name="displayStats">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="3" column="1">
<widget class="QSpinBox" name="aircraftMinZoom">
<property name="toolTip">
<string>Display demodulator statistics</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="airportRange">
<property name="toolTip">
<string>Displays airports within the specified distance in kilometres from My Position</string>
<string>When map zoom (0 min zoom - 15 max zoom) is higher than this value, aircraft icon size will be scaled</string>
</property>
<property name="maximum">
<number>20000</number>
<number>15</number>
</property>
</widget>
</item>
<item row="12" column="0">
<item row="13" 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="photosLabel">
<property name="text">
<string>Display aircraft photos</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QListWidget" name="airspaces">
<property name="toolTip">
@ -493,34 +465,92 @@
</item>
</widget>
</item>
<item row="19" column="1">
<widget class="QLineEdit" name="checkWXAPIKey">
<property name="toolTip">
<string>checkwxapi.com API key for accessing airport weather (METARs)</string>
<item row="8" column="0">
<widget class="QLabel" name="airspaceRangeLabel">
<property name="text">
<string>Airspace display distance (km)</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="heliports">
<item row="0" column="0">
<widget class="QLabel" name="unitsLabel">
<property name="text">
<string>Units</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="navAids">
<property name="toolTip">
<string>When checked, heliports are displayed on the map</string>
<string>Display NAVAIDs such as VORs and NDBs</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="20" column="0">
<item row="4" column="0">
<widget class="QLabel" name="airportSizeLabel">
<property name="text">
<string>Display airports with size</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="airportRange">
<property name="toolTip">
<string>Displays airports within the specified distance in kilometres from My Position</string>
</property>
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="mapProvider">
<property name="toolTip">
<string>Mapping service</string>
</property>
<item>
<property name="text">
<string>osm</string>
</property>
</item>
<item>
<property name="text">
<string>mapboxgl</string>
</property>
</item>
</widget>
</item>
<item row="16" 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="12" column="0">
<widget class="QLabel" name="timeoutLabel">
<property name="text">
<string>Aircraft timeout (s)</string>
</property>
</widget>
</item>
<item row="21" column="0">
<widget class="QLabel" name="airfieldElevationLabel">
<property name="text">
<string>Airfield barometric altitude (ft)</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="airspacesLabel">
<item row="17" column="0">
<widget class="QLabel" name="displayStatsLabel">
<property name="text">
<string>Airspaces to display</string>
<string>Display demodulator statistics</string>
</property>
</widget>
</item>
@ -531,13 +561,20 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="aircraftMinZoom">
<property name="toolTip">
<string>When map zoom (0 min zoom - 15 max zoom) is higher than this value, aircraft icon size will be scaled</string>
<item row="10" column="0">
<widget class="QLabel" name="atcCallsignsLabel">
<property name="text">
<string>Use ATC callsigns on map</string>
</property>
<property name="maximum">
<number>15</number>
</widget>
</item>
<item row="10" column="1">
<widget class="QCheckBox" name="atcCallsigns">
<property name="toolTip">
<string>Use ATC callsigns (SPEEDBIRD) rather than ICAO (BAW) for aircraft labels on map</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>

Wyświetl plik

@ -40,6 +40,7 @@
#include "channel/channelwebapiutils.h"
#include "feature/featurewebapiutils.h"
#include "plugin/pluginapi.h"
#include "util/airlines.h"
#include "util/crc.h"
#include "util/simpleserializer.h"
#include "util/db.h"
@ -157,7 +158,7 @@ bool ADSBDemodGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data))
{
updateDeviceSetList();
updateChannelList();
displaySettings();
applySettings(true);
return true;
@ -244,7 +245,7 @@ QString Aircraft::getImage() const
}
}
QString Aircraft::getText(bool all) const
QString Aircraft::getText(const ADSBDemodSettings *settings, bool all) const
{
QStringList list;
if (m_showAll || all)
@ -265,6 +266,10 @@ QString Aircraft::getText(bool all) const
if (!m_callsign.isEmpty()) {
list.append(QString("Callsign: %1").arg(m_callsign));
}
QString atcCallsign = m_atcCallsignItem->text();
if (!atcCallsign.isEmpty()) {
list.append(QString("ATC Callsign: %1").arg(atcCallsign));
}
if (m_aircraftInfo != nullptr)
{
if (!m_aircraftInfo->m_model.isEmpty()) {
@ -372,15 +377,81 @@ QString Aircraft::getText(bool all) const
}
}
}
else if (!m_callsign.isEmpty())
else
{
list.append(m_callsign);
list.append(getLabel(settings));
}
return list.join("<br>");
}
// See https://nats.aero/blog/2017/07/drone-disruption-gatwick/ for example of UK ATC display
QString Aircraft::getLabel(const ADSBDemodSettings *settings) const
{
QString id;
if (!m_callsign.isEmpty())
{
QString atcCallsign = m_atcCallsignItem->text();
if (settings->m_atcCallsigns && !atcCallsign.isEmpty()) {
id = QString("%1 %2").arg(atcCallsign).arg(m_callsign.mid(3));
} else {
id = m_callsign;
}
}
else
{
list.append(m_icaoHex);
id = m_icaoHex;
}
return list.join("<br>");
QStringList strings;
strings.append(id);
if (settings->m_atcLabels)
{
if (m_altitudeValid)
{
QStringList row1;
int transitionAlt = 6500;
QChar c = m_altitude >= settings->m_transitionAlt ? 'F' : 'A';
// Convert altitude to flight level
int fl = m_altitude / 100;
row1.append(QString("%1%2").arg(c).arg(fl));
// Indicate whether climbing or descending
if (m_verticalRateValid && ((m_verticalRate != 0) || (m_selAltitudeValid && (m_altitude != m_selAltitude))))
{
QChar dir = m_verticalRate == 0 ? QChar('-') : (m_verticalRate > 0 ? QChar(0x2191) : QChar(0x2193));
row1.append(dir);
}
if (m_selAltitudeValid && (m_altitude != m_selAltitude))
{
int selfl = m_selAltitude / 100;
row1.append(QString::number(selfl));
}
strings.append(row1.join(" "));
}
QStringList row2;
// Display speed
if (m_groundspeedValid) {
row2.append(QString("G%2").arg(m_groundspeed));
} else if (m_indicatedAirspeedValid) {
row2.append(QString("I%1").arg(m_indicatedAirspeed));
}
// Aircraft type
if (m_aircraftInfo && !m_aircraftInfo->m_model.isEmpty())
{
QString name = m_aircraftInfo->m_model;
int idx;
idx = name.indexOf(' ');
if (idx >= 0) {
name = name.left(idx);
}
idx = name.indexOf('-');
if (idx >= 0) {
name = name.left(idx);
}
row2.append(name);
}
strings.append(row2.join(" "));
// FIXME: Add ATC altitude and waypoint from ATC feature
}
return strings.join("<br>");
}
QVariant AircraftModel::data(const QModelIndex &index, int role) const
@ -405,7 +476,7 @@ QVariant AircraftModel::data(const QModelIndex &index, int role) const
else if (role == AircraftModel::adsbDataRole)
{
// Create the text to go in the bubble next to the aircraft
return QVariant::fromValue(m_aircrafts[row]->getText());
return QVariant::fromValue(m_aircrafts[row]->getText(m_settings));
}
else if (role == AircraftModel::aircraftImageRole)
{
@ -426,10 +497,28 @@ QVariant AircraftModel::data(const QModelIndex &index, int role) const
}
else if (role == AircraftModel::aircraftPathRole)
{
if ((m_flightPaths && m_aircrafts[row]->m_isHighlighted) || m_allFlightPaths)
return m_aircrafts[row]->m_coordinates;
else
return QVariantList();
if ((m_flightPaths && m_aircrafts[row]->m_isHighlighted) || m_allFlightPaths)
{
return m_aircrafts[row]->m_coordinates;
}
else if (m_settings->m_atcLabels)
{
// Display last 20 seconds of coordinates
QDateTime cutoff = QDateTime::currentDateTime().addSecs(-20);
QVariantList coordinates;
for (int i = m_aircrafts[row]->m_coordinateDateTimes.size() - 1; i >= 0; i--)
{
if (m_aircrafts[row]->m_coordinateDateTimes[i] < cutoff) {
break;
}
coordinates.push_front(m_aircrafts[row]->m_coordinates[i]);
}
return coordinates;
}
else
{
return QVariantList();
}
}
else if (role == AircraftModel::showAllRole)
return QVariant::fromValue(m_aircrafts[row]->m_showAll);
@ -486,6 +575,53 @@ void AircraftModel::findOnMap(int index)
FeatureWebAPIUtils::mapFind(m_aircrafts[index]->m_icaoHex);
}
// Get list of frequeny scanners to use in menu
QStringList AirportModel::getFreqScanners() const
{
QStringList list;
std::vector<ChannelAPI*> channels = MainCore::instance()->getChannels("sdrangel.channel.freqscanner");
for (const auto channel : channels) {
list.append(QString("R%1:%2").arg(channel->getDeviceSetIndex()).arg(channel->getIndexInDeviceSet()));
}
return list;
}
// Send airport frequencies to frequency scanner with given id (Rn:n)
void AirportModel::sendToFreqScanner(int index, const QString& id)
{
if ((index < 0) || (index >= m_airports.count())) {
return;
}
const AirportInformation *airport = m_airports[index];
const QRegularExpression re("R([0-9]+):([0-9]+)");
QRegularExpressionMatch match = re.match(id);
if (match.hasMatch())
{
int deviceSet = match.capturedTexts()[1].toInt();
int channelIndex = match.capturedTexts()[2].toInt();
QJsonArray array;
for (const auto airportFrequency : airport->m_frequencies)
{
QJsonObject obj;
QJsonValue frequency(airportFrequency->m_frequency * 1000000);
QJsonValue enabled(1); // true doesn't work
QJsonValue notes(QString("%1 %2").arg(airport->m_ident).arg(airportFrequency->m_description));
obj.insert("frequency", frequency);
obj.insert("enabled", enabled);
obj.insert("notes", notes);
QJsonValue value(obj);
array.append(value);
}
ChannelWebAPIUtils::patchChannelSetting(deviceSet, channelIndex, "frequencies", array);
}
else
{
qDebug() << "AirportModel::sendToFreqScanner: Malformed id: " << id;
}
}
QVariant AirportModel::data(const QModelIndex &index, int role) const
{
int row = index.row();
@ -565,7 +701,12 @@ bool AirportModel::setData(const QModelIndex &index, const QVariant& value, int
{
int idx = value.toInt();
if ((idx >= 0) && (idx < m_airports[row]->m_frequencies.size()))
m_gui->setFrequency(m_airports[row]->m_frequencies[idx]->m_frequency * 1000000);
{
// Do in two steps to avoid rounding errors
qint64 freqkHz = (qint64) std::round(m_airports[row]->m_frequencies[idx]->m_frequency*1000.0);
qint64 freqHz = freqkHz * 1000;
m_gui->setFrequency(freqHz);
}
else if (idx == m_airports[row]->m_frequencies.size())
{
// Set airport as target
@ -724,10 +865,56 @@ bool NavAidModel::setData(const QModelIndex &index, const QVariant& value, int r
return true;
}
// Set selected device to the given centre frequency (used to tune to ATC selected from airports on map)
bool ADSBDemodGUI::setFrequency(float targetFrequencyHz)
// Set selected AM Demod to the given frequency (used to tune to ATC selected from airports on map)
bool ADSBDemodGUI::setFrequency(qint64 targetFrequencyHz)
{
return ChannelWebAPIUtils::setCenterFrequency(m_settings.m_deviceIndex, targetFrequencyHz);
const QRegularExpression re("R([0-9]+):([0-9]+)");
QRegularExpressionMatch match = re.match(m_settings.m_amDemod);
if (match.hasMatch())
{
int deviceSet = match.capturedTexts()[1].toInt();
int channelIndex = match.capturedTexts()[2].toInt();
const int halfChannelBW = 20000/2;
int dcOffset = halfChannelBW;
double centerFrequency;
int sampleRate;
if (ChannelWebAPIUtils::getCenterFrequency(deviceSet, centerFrequency))
{
if (ChannelWebAPIUtils::getDevSampleRate(deviceSet, sampleRate))
{
sampleRate *= 0.75; // Don't use guard bands
// Adjust device center frequency if not in range
if ( ((targetFrequencyHz - halfChannelBW) < (centerFrequency - sampleRate / 2))
|| ((targetFrequencyHz + halfChannelBW) >= (centerFrequency + sampleRate / 2))
)
{
ChannelWebAPIUtils::setCenterFrequency(deviceSet, targetFrequencyHz - dcOffset);
ChannelWebAPIUtils::setFrequencyOffset(deviceSet, channelIndex, dcOffset);
}
else
{
qint64 offset = targetFrequencyHz - centerFrequency;
// Also adjust center frequency if channel would cross DC
if (std::abs(offset) < halfChannelBW)
{
ChannelWebAPIUtils::setCenterFrequency(deviceSet, targetFrequencyHz - dcOffset);
ChannelWebAPIUtils::setFrequencyOffset(deviceSet, channelIndex, dcOffset);
}
else
{
// Just tune channel
ChannelWebAPIUtils::setFrequencyOffset(deviceSet, channelIndex, offset);
}
}
return true;
}
}
}
return false;
}
// Called when we have both lat & long
@ -825,7 +1012,7 @@ void ADSBDemodGUI::sendToMap(Aircraft *aircraft, QList<SWGSDRangel::SWGMapAnimat
swgMapItem->setAvailableUntil(new QString(aircraft->m_positionDateTime.addSecs(60).toString(Qt::ISODateWithMs)));
swgMapItem->setImage(new QString(QString("qrc:///map/%1").arg(aircraft->getImage())));
swgMapItem->setImageRotation(aircraft->m_heading);
swgMapItem->setText(new QString(aircraft->getText(true)));
swgMapItem->setText(new QString(aircraft->getText(&m_settings, true)));
if (!aircraft->m_aircraft3DModel.isEmpty()) {
swgMapItem->setModel(new QString(aircraft->m_aircraft3DModel));
@ -833,7 +1020,7 @@ void ADSBDemodGUI::sendToMap(Aircraft *aircraft, QList<SWGSDRangel::SWGMapAnimat
swgMapItem->setModel(new QString(aircraft->m_aircraftCat3DModel));
}
swgMapItem->setLabel(new QString(aircraft->m_callsign));
swgMapItem->setLabel(new QString(aircraft->getLabel(&m_settings)));
if (aircraft->m_headingValid)
{
@ -883,6 +1070,7 @@ Aircraft *ADSBDemodGUI::getAircraft(int icao, bool &newAircraft)
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_ATC_CALLSIGN, aircraft->m_atcCallsignItem);
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);
@ -1012,6 +1200,45 @@ Aircraft *ADSBDemodGUI::getAircraft(int icao, bool &newAircraft)
return aircraft;
}
void ADSBDemodGUI::atcCallsign(Aircraft *aircraft)
{
QString icao = aircraft->m_callsign.left(3);
const Airline *airline = Airline::getByICAO(icao);
if (airline)
{
aircraft->m_atcCallsignItem->setText(airline->m_callsign);
// Create icons using data from Airline class, if it doesn't exist in database
if (!aircraft->m_aircraftInfo)
{
// Airline logo
QIcon *icon = nullptr;
aircraft->m_airlineIconURL = AircraftInformation::getFlagIconURL(airline->m_icao);
icon = AircraftInformation::getAirlineIcon(airline->m_icao);
if (icon != nullptr)
{
aircraft->m_airlineItem->setSizeHint(QSize(85, 20));
aircraft->m_airlineItem->setIcon(*icon);
}
else
{
aircraft->m_airlineItem->setText(airline->m_name);
}
// Flag
QString flag = airline->m_country.toLower().replace(" ", "_");
aircraft->m_flagIconURL = AircraftInformation::getFlagIconPath(flag);
if (aircraft->m_flagIconURL.startsWith(':')) {
aircraft->m_flagIconURL = "qrc://" + aircraft->m_flagIconURL.mid(1);
}
icon = AircraftInformation::getFlagIcon(flag);
if (icon != nullptr)
{
aircraft->m_countryItem->setSizeHint(QSize(40, 20));
aircraft->m_countryItem->setIcon(*icon);
}
}
}
}
// Try to map callsign to flight number
void ADSBDemodGUI::callsignToFlight(Aircraft *aircraft)
{
@ -1187,6 +1414,7 @@ void ADSBDemodGUI::handleADSB(
{
aircraft->m_callsign = callsignTrimmed;
aircraft->m_callsignItem->setText(aircraft->m_callsign);
atcCallsign(aircraft);
callsignToFlight(aircraft);
}
@ -1401,6 +1629,7 @@ void ADSBDemodGUI::handleADSB(
aircraft->m_positionDateTime = dateTime;
QGeoCoordinate coord(aircraft->m_latitude, aircraft->m_longitude, aircraft->m_altitude);
aircraft->m_coordinates.push_back(QVariant::fromValue(coord));
aircraft->m_coordinateDateTimes.push_back(dateTime);
updatePosition(aircraft);
}
}
@ -1457,6 +1686,7 @@ void ADSBDemodGUI::handleADSB(
aircraft->m_positionDateTime = dateTime;
QGeoCoordinate coord(aircraft->m_latitude, aircraft->m_longitude, aircraft->m_altitude);
aircraft->m_coordinates.push_back(QVariant::fromValue(coord));
aircraft->m_coordinateDateTimes.push_back(dateTime);
}
}
}
@ -2253,7 +2483,7 @@ void ADSBDemodGUI::decodeCommB(const QByteArray data, const QDateTime dateTime,
bool verticalVelInconsistent = (abs(verticalVel) > 6000) || (aircraft->m_verticalRateValid && abs(verticalVel - aircraft->m_verticalRate) > 2000)
|| (!verticalVelStatus && (verticalVelFix != 0));
bool bds_6_0 = !magHeadingInconsistent & !indicatedAirspeedInconsistent & !machInconsistent && !baroAltRateInconsistent && !verticalVelInconsistent;
bool bds_6_0 = !magHeadingInconsistent && !indicatedAirspeedInconsistent && !machInconsistent && !baroAltRateInconsistent && !verticalVelInconsistent;
int possibleMatches = bds_1_0 + bds_1_7 + bds_2_0 + bds_2_1 + bds_3_0 + bds_4_0 + bds_4_1 + bds_4_4 + bds_4_5 + bds_5_0 + bds_5_1 + bds_5_3 + bds_6_0;
@ -2295,6 +2525,7 @@ void ADSBDemodGUI::decodeCommB(const QByteArray data, const QDateTime dateTime,
{
aircraft->m_callsign = callsignTrimmed;
aircraft->m_callsignItem->setText(aircraft->m_callsign);
atcCallsign(aircraft);
callsignToFlight(aircraft);
}
}
@ -3661,6 +3892,13 @@ void ADSBDemodGUI::on_allFlightPaths_clicked(bool checked)
m_aircraftModel.setAllFlightPaths(checked);
}
void ADSBDemodGUI::on_atcLabels_clicked(bool checked)
{
m_settings.m_atcLabels = checked;
m_aircraftModel.setSettings(&m_settings);
applySettings();
}
QString ADSBDemodGUI::getDataDir()
{
// Get directory to store app data in (aircraft & airport databases and user-definable icons)
@ -3726,59 +3964,41 @@ void ADSBDemodGUI::onMenuDialogCalled(const QPoint &p)
resetContextMenuType();
}
void ADSBDemodGUI::updateDeviceSetList()
void ADSBDemodGUI::updateChannelList()
{
MainWindow *mainWindow = MainWindow::getInstance();
std::vector<DeviceUISet*>& deviceUISets = mainWindow->getDeviceUISets();
std::vector<DeviceUISet*>::const_iterator it = deviceUISets.begin();
std::vector<ChannelAPI*> channels = MainCore::instance()->getChannels("sdrangel.channel.amdemod");
ui->device->blockSignals(true);
ui->amDemod->blockSignals(true);
ui->amDemod->clear();
ui->device->clear();
unsigned int deviceIndex = 0;
for (; it != deviceUISets.end(); ++it, deviceIndex++)
{
DSPDeviceSourceEngine *deviceSourceEngine = (*it)->m_deviceSourceEngine;
if (deviceSourceEngine) {
ui->device->addItem(QString("R%1").arg(deviceIndex), deviceIndex);
}
for (const auto channel : channels) {
ui->amDemod->addItem(QString("R%1:%2").arg(channel->getDeviceSetIndex()).arg(channel->getIndexInDeviceSet()));
}
int newDeviceIndex;
if (it != deviceUISets.begin())
{
if (m_settings.m_deviceIndex < 0) {
ui->device->setCurrentIndex(0);
} else if (m_settings.m_deviceIndex >= (int) deviceUISets.size()) {
ui->device->setCurrentIndex(deviceUISets.size() - 1);
} else {
ui->device->setCurrentIndex(m_settings.m_deviceIndex);
}
newDeviceIndex = ui->device->currentData().toInt();
}
else
{
newDeviceIndex = -1;
// Select current setting, if exists
// If not, make sure nothing selected, as channel may be created later on
int idx = ui->amDemod->findText(m_settings.m_amDemod);
if (idx >= 0) {
ui->amDemod->setCurrentIndex(idx);
} else {
ui->amDemod->setCurrentIndex(-1);
}
if (newDeviceIndex != m_settings.m_deviceIndex)
{
qDebug("ADSBDemodGUI::updateDeviceSetLists: device index changed: %d", newDeviceIndex);
m_settings.m_deviceIndex = newDeviceIndex;
}
ui->amDemod->blockSignals(false);
ui->device->blockSignals(false);
// If no current settting, select first channel
if (m_settings.m_amDemod.isEmpty())
{
ui->amDemod->setCurrentIndex(0);
on_amDemod_currentIndexChanged(0);
}
}
void ADSBDemodGUI::on_device_currentIndexChanged(int index)
void ADSBDemodGUI::on_amDemod_currentIndexChanged(int index)
{
if (index >= 0)
{
m_settings.m_deviceIndex = ui->device->currentData().toInt();
m_settings.m_amDemod = ui->amDemod->currentText();
applySettings();
}
}
@ -4790,11 +5010,11 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
connect(&m_planeSpotters, &PlaneSpotters::aircraftPhoto, this, &ADSBDemodGUI::aircraftPhoto);
connect(ui->photo, &ClickableLabel::clicked, this, &ADSBDemodGUI::photoClicked);
// Update device list when devices are added or removed
connect(MainCore::instance(), &MainCore::deviceSetAdded, this, &ADSBDemodGUI::updateDeviceSetList);
connect(MainCore::instance(), &MainCore::deviceSetRemoved, this, &ADSBDemodGUI::updateDeviceSetList);
// Update demod list when channels are added or removed
connect(MainCore::instance(), &MainCore::channelAdded, this, &ADSBDemodGUI::updateChannelList);
connect(MainCore::instance(), &MainCore::channelRemoved, this, &ADSBDemodGUI::updateChannelList);
updateDeviceSetList();
updateChannelList();
displaySettings();
makeUIConnections();
applySettings(true);
@ -4907,6 +5127,8 @@ void ADSBDemodGUI::displaySettings()
ui->allFlightPaths->setChecked(m_settings.m_allFlightPaths);
m_aircraftModel.setAllFlightPaths(m_settings.m_allFlightPaths);
m_aircraftModel.setSettings(&m_settings);
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
ui->logEnable->setChecked(m_settings.m_logEnabled);
@ -5051,6 +5273,40 @@ void ADSBDemodGUI::tick()
}
}
}
// Create and send aircraft report every second for WebAPI
if (m_tickCount % (20*1) == 0) {
sendAircraftReport();
}
}
void ADSBDemodGUI::sendAircraftReport()
{
ADSBDemod::MsgAircraftReport* message = ADSBDemod::MsgAircraftReport::create();
QList<ADSBDemod::MsgAircraftReport::AircraftReport>& report = message->getReport();
report.reserve(m_aircraft.size());
QHash<int, Aircraft *>::iterator i = m_aircraft.begin();
while (i != m_aircraft.end())
{
Aircraft *aircraft = i.value();
ADSBDemod::MsgAircraftReport::AircraftReport aircraftReport {
aircraft->m_icaoHex,
aircraft->m_callsign,
aircraft->m_latitude,
aircraft->m_longitude,
aircraft->m_altitude,
aircraft->m_groundspeed
};
report.append(aircraftReport);
++i;
}
m_adsbDemod->getInputMessageQueue()->push(message);
}
void ADSBDemodGUI::resizeTable()
@ -5059,7 +5315,8 @@ 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_CALLSIGN, new QTableWidgetItem("Callsign-"));
ui->adsbData->setItem(row, ADSB_COL_CALLSIGN, new QTableWidgetItem("Callsign--"));
ui->adsbData->setItem(row, ADSB_COL_ATC_CALLSIGN, new QTableWidgetItem("ATC 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)"));
@ -5611,6 +5868,7 @@ void ADSBDemodGUI::handleImportReply(QNetworkReply* reply)
{
aircraft->m_callsign = callsign;
aircraft->m_callsignItem->setText(aircraft->m_callsign);
atcCallsign(aircraft);
}
QDateTime timePosition = dateTime;
@ -5648,6 +5906,7 @@ void ADSBDemodGUI::handleImportReply(QNetworkReply* reply)
{
QGeoCoordinate coord(aircraft->m_latitude, aircraft->m_longitude, aircraft->m_altitude);
aircraft->m_coordinates.push_back(QVariant::fromValue(coord));
aircraft->m_coordinateDateTimes.push_back(dateTime);
}
aircraft->m_positionDateTime = timePosition;
}
@ -5824,7 +6083,8 @@ void ADSBDemodGUI::makeUIConnections()
QObject::connect(ui->getAirspacesDB, &QToolButton::clicked, this, &ADSBDemodGUI::on_getAirspacesDB_clicked);
QObject::connect(ui->flightPaths, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_flightPaths_clicked);
QObject::connect(ui->allFlightPaths, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_allFlightPaths_clicked);
QObject::connect(ui->device, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ADSBDemodGUI::on_device_currentIndexChanged);
QObject::connect(ui->atcLabels, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_atcLabels_clicked);
QObject::connect(ui->amDemod, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ADSBDemodGUI::on_amDemod_currentIndexChanged);
QObject::connect(ui->displaySettings, &QToolButton::clicked, this, &ADSBDemodGUI::on_displaySettings_clicked);
QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_logEnable_clicked);
QObject::connect(ui->logFilename, &QToolButton::clicked, this, &ADSBDemodGUI::on_logFilename_clicked);

Wyświetl plik

@ -48,6 +48,7 @@
#include "adsbdemodsettings.h"
#include "util/ourairportsdb.h"
#include "util/osndb.h"
#include "util/airlines.h"
class PluginAPI;
class DeviceUISet;
@ -150,6 +151,7 @@ struct Aircraft {
bool m_showAll;
QVariantList m_coordinates; // Coordinates we've recorded the aircraft at
QList<QDateTime> m_coordinateDateTimes;
AircraftInformation *m_aircraftInfo; // Info about the aircraft from the database
QString m_aircraft3DModel; // 3D model for map based on aircraft type
@ -180,6 +182,7 @@ struct Aircraft {
// GUI table items for above data
QTableWidgetItem *m_icaoItem;
QTableWidgetItem *m_callsignItem;
QTableWidgetItem* m_atcCallsignItem;
QTableWidgetItem *m_modelItem;
QTableWidgetItem *m_airlineItem;
QTableWidgetItem *m_latitudeItem;
@ -299,6 +302,7 @@ struct Aircraft {
// These are deleted by QTableWidget
m_icaoItem = new QTableWidgetItem();
m_callsignItem = new QTableWidgetItem();
m_atcCallsignItem = new QTableWidgetItem();
m_modelItem = new QTableWidgetItem();
m_airlineItem = new QTableWidgetItem();
m_altitudeItem = new QTableWidgetItem();
@ -353,9 +357,11 @@ struct Aircraft {
}
QString getImage() const;
QString getText(bool all=false) const;
QString getText(const ADSBDemodSettings *settings, bool all=false) const;
// Label to use for aircraft on map
QString getLabel(const ADSBDemodSettings *settings) const;
// Name to use when selected as a target
// Name to use when selected as a target (E.g. for use as target name in Rotator Controller)
QString targetName()
{
if (!m_callsign.isEmpty())
@ -463,6 +469,12 @@ public:
allAircraftUpdated();
}
void setSettings(const ADSBDemodSettings *settings)
{
m_settings = settings;
allAircraftUpdated();
}
Q_INVOKABLE void findOnMap(int index);
void updateAircraftInformation(QSharedPointer<const QHash<int, AircraftInformation *>> aircraftInfo)
@ -481,6 +493,7 @@ private:
QList<Aircraft *> m_aircrafts;
bool m_flightPaths;
bool m_allFlightPaths;
const ADSBDemodSettings *m_settings;
};
// Airport data model used by QML map item
@ -623,6 +636,9 @@ public:
}
}
Q_INVOKABLE QStringList getFreqScanners() const;
Q_INVOKABLE void sendToFreqScanner(int index, const QString& id);
private:
ADSBDemodGUI *m_gui;
QList<const AirportInformation *> m_airports;
@ -874,7 +890,7 @@ public:
void highlightAircraft(Aircraft *aircraft);
void targetAircraft(Aircraft *aircraft);
void target(const QString& name, float az, float el, float range);
bool setFrequency(float frequency);
bool setFrequency(qint64 frequency);
bool useSIUints() const { return m_settings.m_siUnits; }
Q_INVOKABLE void clearHighlighted();
QString get3DModel(const QString &aircraft, const QString &operatorICAO) const;
@ -974,6 +990,7 @@ private:
void clearFromMap(const QString& name);
void sendToMap(Aircraft *aircraft, QList<SWGSDRangel::SWGMapAnimation *> *animations);
Aircraft *getAircraft(int icao, bool &newAircraft);
void atcCallsign(Aircraft *aircraft);
void callsignToFlight(Aircraft *aircraft);
int roundTo50Feet(int alt);
bool calcAirTemp(Aircraft *aircraft);
@ -1006,7 +1023,7 @@ private:
void updateAirports();
void updateAirspaces();
void updateNavAids();
void updateDeviceSetList();
void updateChannelList();
QAction *createCheckableItem(QString& text, int idx, bool checked);
Aircraft* findAircraftByFlight(const QString& flight);
QString dataTimeToShortString(QDateTime dt);
@ -1021,6 +1038,7 @@ private:
int grayToBinary(int gray, int bits) const;
void redrawMap();
void applyImportSettings();
void sendAircraftReport();
void leaveEvent(QEvent*);
void enterEvent(EnterEventType*);
@ -1050,11 +1068,12 @@ private slots:
void on_getAirspacesDB_clicked();
void on_flightPaths_clicked(bool checked);
void on_allFlightPaths_clicked(bool checked);
void on_atcLabels_clicked(bool checked);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
void tick();
void on_device_currentIndexChanged(int index);
void on_amDemod_currentIndexChanged(int index);
void feedSelect(const QPoint& p);
void on_displaySettings_clicked();
void on_logEnable_clicked(bool checked=false);

Wyświetl plik

@ -511,7 +511,7 @@
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/icons/controltower.png</normaloff>:/icons/controltower.png</iconset>
<normaloff>:/icons/airport.png</normaloff>:/icons/airport.png</iconset>
</property>
</widget>
</item>
@ -583,6 +583,26 @@
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="atcLabels">
<property name="toolTip">
<string>Display altitude, speed and short paths for all aircraft</string>
</property>
<property name="text">
<string>^</string>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/icons/controltower.png</normaloff>:/icons/controltower.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="flightInfo">
<property name="toolTip">
@ -729,16 +749,16 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="amDemodLabel">
<property name="text">
<string>Device</string>
<string>AM</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="device">
<widget class="QComboBox" name="amDemod">
<property name="toolTip">
<string>Receive device set to set frequency on when selecting an ATC frequency on the map</string>
<string>AM Demod to tune when selecting an ATC frequency on the map</string>
</property>
</widget>
</item>
@ -863,6 +883,14 @@
<string>Callsign. Links to www.flightradar24.com</string>
</property>
</column>
<column>
<property name="text">
<string>ATC Callsign</string>
</property>
<property name="toolTip">
<string>Airline callsign used by ATC</string>
</property>
</column>
<column>
<property name="text">
<string>Aircraft</string>
@ -1478,7 +1506,7 @@
<tabstop>logFilename</tabstop>
<tabstop>logOpen</tabstop>
<tabstop>findOnMapFeature</tabstop>
<tabstop>device</tabstop>
<tabstop>amDemod</tabstop>
<tabstop>adsbData</tabstop>
<tabstop>map</tabstop>
</tabstops>

Wyświetl plik

@ -77,7 +77,6 @@ void ADSBDemodSettings::resetToDefaults()
m_displayDemodStats = false;
m_correlateFullPreamble = true;
m_demodModeS = true;
m_deviceIndex = -1;
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
@ -105,6 +104,9 @@ void ADSBDemodSettings::resetToDefaults()
m_aircraftMinZoom = 11;
m_workspaceIndex = 0;
m_hidden = false;
m_atcLabels = true;
m_atcCallsigns = true;
m_transitionAlt = 6000; // Depends on airport. 18,000 in USA
}
QByteArray ADSBDemodSettings::serialize() const
@ -136,7 +138,6 @@ QByteArray ADSBDemodSettings::serialize() const
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_exportClientFormat);
s.writeString(25, m_tableFontName);
@ -188,6 +189,11 @@ QByteArray ADSBDemodSettings::serialize() const
s.writeString(63, m_mapProvider);
s.writeS32(64, m_aircraftMinZoom);
s.writeBool(65, m_atcLabels);
s.writeBool(66, m_atcCallsigns);
s.writeS32(67, m_transitionAlt);
s.writeString(68, m_amDemod);
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) {
s.writeS32(100 + i, m_columnIndexes[i]);
}
@ -259,7 +265,6 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
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_exportClientFormat, BeastBinary);
d.readString(25, &m_tableFontName, "Liberation Sans");
@ -319,6 +324,12 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
d.readString(62, &m_checkWXAPIKey, "");
d.readString(63, &m_mapProvider, "osm");
d.readS32(64, &m_aircraftMinZoom, 11);
d.readBool(65, &m_atcLabels, true);
d.readBool(66, &m_atcCallsigns, true);
d.readS32(67, &m_transitionAlt, 6000);
d.readString(68, &m_amDemod);
#ifdef LINUX
if (m_mapProvider == "osm") {
m_mapProvider = "mapboxgl";

Wyświetl plik

@ -29,62 +29,63 @@
class Serializable;
// Number of columns in the table
#define ADSBDEMOD_COLUMNS 53
#define ADSBDEMOD_COLUMNS 54
// ADS-B table columns
#define ADSB_COL_ICAO 0
#define ADSB_COL_CALLSIGN 1
#define ADSB_COL_MODEL 2
#define ADSB_COL_AIRLINE 3
#define ADSB_COL_COUNTRY 4
#define ADSB_COL_GROUND_SPEED 5
#define ADSB_COL_TRUE_AIRSPEED 6
#define ADSB_COL_INDICATED_AIRSPEED 7
#define ADSB_COL_MACH 8
#define ADSB_COL_SEL_ALTITUDE 9
#define ADSB_COL_ALTITUDE 10
#define ADSB_COL_VERTICALRATE 11
#define ADSB_COL_SEL_HEADING 12
#define ADSB_COL_HEADING 13
#define ADSB_COL_TURNRATE 14
#define ADSB_COL_ROLL 15
#define ADSB_COL_RANGE 16
#define ADSB_COL_AZEL 17
#define ADSB_COL_CATEGORY 18
#define ADSB_COL_STATUS 19
#define ADSB_COL_SQUAWK 20
#define ADSB_COL_REGISTRATION 21
#define ADSB_COL_REGISTERED 22
#define ADSB_COL_MANUFACTURER 23
#define ADSB_COL_OWNER 24
#define ADSB_COL_OPERATOR_ICAO 25
#define ADSB_COL_AP 26
#define ADSB_COL_V_MODE 27
#define ADSB_COL_L_MODE 28
#define ADSB_COL_BARO 29
#define ADSB_COL_HEADWIND 30
#define ADSB_COL_EST_AIR_TEMP 31
#define ADSB_COL_WIND_SPEED 32
#define ADSB_COL_WIND_DIR 33
#define ADSB_COL_STATIC_PRESSURE 34
#define ADSB_COL_STATIC_AIR_TEMP 35
#define ADSB_COL_HUMIDITY 36
#define ADSB_COL_LATITUDE 37
#define ADSB_COL_LONGITUDE 38
#define ADSB_COL_TIME 39
#define ADSB_COL_FRAMECOUNT 40
#define ADSB_COL_TIS_B 41
#define ADSB_COL_CORRELATION 42
#define ADSB_COL_RSSI 43
#define ADSB_COL_FLIGHT_STATUS 44
#define ADSB_COL_DEP 45
#define ADSB_COL_ARR 46
#define ADSB_COL_STD 47
#define ADSB_COL_ETD 48
#define ADSB_COL_ATD 49
#define ADSB_COL_STA 50
#define ADSB_COL_ETA 51
#define ADSB_COL_ATA 52
#define ADSB_COL_ATC_CALLSIGN 2
#define ADSB_COL_MODEL 3
#define ADSB_COL_AIRLINE 4
#define ADSB_COL_COUNTRY 5
#define ADSB_COL_GROUND_SPEED 6
#define ADSB_COL_TRUE_AIRSPEED 7
#define ADSB_COL_INDICATED_AIRSPEED 8
#define ADSB_COL_MACH 9
#define ADSB_COL_SEL_ALTITUDE 10
#define ADSB_COL_ALTITUDE 11
#define ADSB_COL_VERTICALRATE 12
#define ADSB_COL_SEL_HEADING 13
#define ADSB_COL_HEADING 14
#define ADSB_COL_TURNRATE 15
#define ADSB_COL_ROLL 16
#define ADSB_COL_RANGE 17
#define ADSB_COL_AZEL 18
#define ADSB_COL_CATEGORY 19
#define ADSB_COL_STATUS 20
#define ADSB_COL_SQUAWK 21
#define ADSB_COL_REGISTRATION 22
#define ADSB_COL_REGISTERED 23
#define ADSB_COL_MANUFACTURER 24
#define ADSB_COL_OWNER 25
#define ADSB_COL_OPERATOR_ICAO 26
#define ADSB_COL_AP 27
#define ADSB_COL_V_MODE 28
#define ADSB_COL_L_MODE 29
#define ADSB_COL_BARO 30
#define ADSB_COL_HEADWIND 31
#define ADSB_COL_EST_AIR_TEMP 32
#define ADSB_COL_WIND_SPEED 33
#define ADSB_COL_WIND_DIR 34
#define ADSB_COL_STATIC_PRESSURE 35
#define ADSB_COL_STATIC_AIR_TEMP 36
#define ADSB_COL_HUMIDITY 37
#define ADSB_COL_LATITUDE 38
#define ADSB_COL_LONGITUDE 39
#define ADSB_COL_TIME 40
#define ADSB_COL_FRAMECOUNT 41
#define ADSB_COL_TIS_B 42
#define ADSB_COL_CORRELATION 43
#define ADSB_COL_RSSI 44
#define ADSB_COL_FLIGHT_STATUS 45
#define ADSB_COL_DEP 46
#define ADSB_COL_ARR 47
#define ADSB_COL_STD 48
#define ADSB_COL_ETD 49
#define ADSB_COL_ATD 50
#define ADSB_COL_STA 51
#define ADSB_COL_ETA 52
#define ADSB_COL_ATA 53
struct ADSBDemodSettings
{
@ -160,7 +161,7 @@ struct ADSBDemodSettings
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
QString m_amDemod; //!< AM Demod to tune to selected ATC frequency
bool m_autoResizeTableColumns;
int m_interpolatorPhaseSteps;
float m_interpolatorTapsPerPhase;
@ -188,6 +189,10 @@ struct ADSBDemodSettings
int m_airfieldElevation; //!< QFE in ft so aircraft takeoff/land from correct position
int m_aircraftMinZoom;
bool m_atcLabels;
bool m_atcCallsigns;
int m_transitionAlt;
ADSBDemodSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }

Wyświetl plik

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/">
<file>icons/aircraft.png</file>
<file>icons/airport.png</file>
<file>icons/controltower.png</file>
<file>icons/allflightpaths.png</file>
<file>icons/vor.png</file>

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 675 B

Wyświetl plik

@ -328,61 +328,189 @@ Item {
Component {
id: airportComponent
MapQuickItem {
id: aircraft
anchorPoint.x: image.width/2
anchorPoint.y: image.height/2
coordinate: position
zoomLevel: airportZoomLevel
MapItemGroup {
MapItemGroup {
property var groupVisible: false
id: rangeGroup
MapCircle {
id: circle5nm
center: position
color: "transparent"
border.color: "gray"
radius: 9260 // 5nm in metres
visible: rangeGroup.groupVisible
}
MapCircle {
id: circle10nm
center: position
color: "transparent"
border.color: "gray"
radius: 18520
visible: rangeGroup.groupVisible
}
MapCircle {
id: circle15nm
center: airport.coordinate
color: "transparent"
border.color: "gray"
radius: 27780
visible: rangeGroup.groupVisible
}
MapQuickItem {
id: text5nm
coordinate {
latitude: position.latitude
longitude: position.longitude + (5/60)/Math.cos(Math.abs(position.latitude)*Math.PI/180)
}
anchorPoint.x: 0
anchorPoint.y: height/2
sourceItem: Text {
color: "grey"
text: "5nm"
}
visible: rangeGroup.groupVisible
}
MapQuickItem {
id: text10nm
coordinate {
latitude: position.latitude
longitude: position.longitude + (10/60)/Math.cos(Math.abs(position.latitude)*Math.PI/180)
}
anchorPoint.x: 0
anchorPoint.y: height/2
sourceItem: Text {
color: "grey"
text: "10nm"
}
visible: rangeGroup.groupVisible
}
MapQuickItem {
id: text15nm
coordinate {
latitude: position.latitude
longitude: position.longitude + (15/60)/Math.cos(Math.abs(position.latitude)*Math.PI/180)
}
anchorPoint.x: 0
anchorPoint.y: height/2
sourceItem: Text {
color: "grey"
text: "15nm"
}
visible: rangeGroup.groupVisible
}
}
sourceItem: Grid {
columns: 1
Grid {
horizontalItemAlignment: Grid.AlignHCenter
layer.enabled: smoothing
layer.smooth: smoothing
Image {
id: image
source: airportImage
visible: !lightIcons
}
ColorOverlay {
cached: true
width: image.width
height: image.height
source: image
color: "#c0ffffff"
visible: lightIcons
}
Rectangle {
id: bubble
color: bubbleColour
border.width: 1
width: text.width + 5
height: text.height + 5
radius: 5
Text {
id: text
anchors.centerIn: parent
text: airportData
}
MouseArea {
anchors.fill: parent
onClicked: (mouse) => {
if (showFreq) {
var freqIdx = Math.floor((mouse.y-5)/((height-10)/airportDataRows))
if (freqIdx == 0) {
showFreq = false
MapQuickItem {
id: airport
anchorPoint.x: image.width/2
anchorPoint.y: image.height/2
coordinate: position
zoomLevel: airportZoomLevel
sourceItem: Grid {
columns: 1
Grid {
horizontalItemAlignment: Grid.AlignHCenter
layer.enabled: smoothing
layer.smooth: smoothing
Image {
id: image
source: airportImage
visible: !lightIcons
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
if (mouse.button === Qt.RightButton) {
showRangeItem.visible = !rangeGroup.groupVisible
hideRangeItem.visible = rangeGroup.groupVisible
menuItems.clear()
var scanners = airportModel.getFreqScanners()
for (var i = 0; i < scanners.length; i++) {
menuItems.append({
text: "Send to Frequency Scanner " + scanners[i],
airport: index,
scanner: scanners[i]
})
}
contextMenu.popup()
}
}
onDoubleClicked: (mouse) => {
rangeGroup.groupVisible = !rangeGroup.groupVisible
}
ListModel {
id: menuItems
}
Menu {
id: contextMenu
MenuItem {
id: showRangeItem
text: "Show range rings"
onTriggered: rangeGroup.groupVisible = true
height: visible ? implicitHeight : 0
}
MenuItem {
id: hideRangeItem
text: "Hide range rings"
onTriggered: rangeGroup.groupVisible = false
height: visible ? implicitHeight : 0
}
Instantiator {
model: menuItems
MenuItem {
text: model.text
onTriggered: airportModel.sendToFreqScanner(model.airport, model.scanner)
}
onObjectAdded: function(index, object) {
contextMenu.insertItem(index, object)
}
onObjectRemoved: function(index, object) {
contextMenu.removeItem(object)
}
}
} else {
showFreq = true
}
}
onDoubleClicked: (mouse) => {
if (showFreq) {
var freqIdx = Math.floor((mouse.y-5)/((height-10)/airportDataRows))
if (freqIdx != 0) {
selectedFreq = freqIdx - 1
}
ColorOverlay {
cached: true
width: image.width
height: image.height
source: image
color: "#c0ffffff"
visible: lightIcons
}
Rectangle {
id: bubble
color: bubbleColour
border.width: 1
width: text.width + 5
height: text.height + 5
radius: 5
Text {
id: text
anchors.centerIn: parent
text: airportData
}
MouseArea {
anchors.fill: parent
onClicked: (mouse) => {
if (showFreq) {
var freqIdx = Math.floor((mouse.y-5)/((height-10)/airportDataRows))
if (freqIdx == 0) {
showFreq = false
}
} else {
showFreq = true
}
}
onDoubleClicked: (mouse) => {
if (showFreq) {
var freqIdx = Math.floor((mouse.y-5)/((height-10)/airportDataRows))
if (freqIdx != 0) {
selectedFreq = freqIdx - 1
}
}
}
}

Wyświetl plik

@ -80,11 +80,13 @@ Clicking the Display Settings button will open the Display Settings dialog, whic
* What category of airspaces should be displayed.
* The distance (in kilometres), from the location set under Preferences > My Position, at which airspaces will be displayed on the map. Displaying too many airspaces will slow down drawing of the map.
* Whether NAVAIDs, such as VORs, are displayed on the map.
* Whether callsigns as said by ATC (E.g. Speedbird) are used on the map instead of the airline ICAO designator (E.g. BAW).
* Whether aircraft photos are displayed for the highlighted aircraft.
* The timeout, in seconds, after which an aircraft will be removed from the table and map, if an ADS-B frame has not been received from it.
* The font used for the table.
* 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.
* The transistion altitude in feet for use in ATC mode. Below the TA, altitude will be displayed. Above the TA flight levels will be displayed.
You can also enter an [aviationstack](https://aviationstack.com/product) API key, needed to download flight information (such as departure and arrival airports and times).
@ -100,6 +102,10 @@ Checking this button draws a line on the map showing the highlighted aircraft's
Checking this button draws flight paths for all aircraft.
<h3>ATC Mode</h3>
When in ATC mode, the map will display callsign, alitude, ground speed and type for all aircraft. When unchecked, only callsign (or ICAO, until callsign is received) will be displayed.
<h3>15: 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.
@ -235,9 +241,9 @@ Click to specify the name of the .csv file which received ADS-B frames are logge
Click to specify a previously written ADS-B .csv log file, which is read and used to update the ADS-B data table and map.
<h3>21: Select device set</h3>
<h3>21: AM Demod</h3>
Specify the SDRangel device set that will be have its centre frequency set when an airport ATC frequency is clicked on the map. Typically, this device set would be a second SDR (as ATC frequencies are around 120MHz, so they can not be received simultaneously with 1090MHz for ADS-B) and have an AM Demodulator channel plugin.
Specify the AM Demodulator that will be have its centre frequency set when an airport ATC frequency is clicked on the map.
<h3>ADS-B Data</h3>
@ -247,6 +253,7 @@ The table displays the decoded ADS-B and Mode-S data for each aircraft along sid
* ICAO ID - 24-bit hexadecimal ICAO aircraft address. This is unique for each aircraft. (ADS-B)
* Callsign - Aircraft callsign (which is sometimes also the flight number). (ADS-B / Mode-S)
* ATC callsign - Callsign used by ATC (E.g. Speedbird for British Airways).
* Aircraft - The aircraft model. (DB)
* Airline - The logo of the operator of the aircraft (or owner if no operator known). (DB)
* Country - The flag of the country the aircraft is registered in. (DB)
@ -321,7 +328,7 @@ The map displays aircraft locations and data geographically. Four types of map c
The antenna location is placed according to My Position set under the Preferences > My Position menu.
If My Position is not set correctly, the position of aircraft may not be computed correctly.
Aircraft are only placed upon the map when a position can be calculated, which can require several frames to be received.
Aircraft are only placed upon the map when a position can be calculated, which can require several ADS-B frames to be received.
* To pan around the map, click the left mouse button and drag. To zoom in or out, use the mouse scroll wheel.
* Left clicking on an aircraft will highlight the corresponding row in the table for the aircraft and the information box on the map will be coloured orange, rather than blue.
@ -330,13 +337,14 @@ Aircraft are only placed upon the map when a position can be calculated, which c
* Left clicking the information box next to an airport will reveal ATC frequencies for the airport (if the OurAirports database has been downloaded) and METAR weather information (if the CheckWX API key has been entered).
The METAR for the airport is downloaded each time the information box is opened.
This information box can be closed by left clicking on the airport identifier.
Double clicking on one of the listed frequencies, will set it as the centre frequency on the selected SDRangel device set (21).
Double clicking on one of the listed frequencies, will tune the AM Demod (21) to that frequency.
The Az/El row gives the azimuth and elevation of the airport from the location set under Preferences > My Position. Double clicking on this row will set the airport as the active target.
* Right clicking on an airport will display a popup menu, allowing range rings to be shown or hidden, and for the ATC frequencies for the airport to be send to a Frequency Scanner.
<h2>Attribution</h2>
Airline logos and flags are by Steve Hibberd from https://radarspotting.com
Map icons are by Alice Design, Alex Ahineev, Botho Willer, Verry Obito, Sean Maldjia, Tinashe Mugayi, Georgiana Ionescu, Andreas Vögele, Tom Fricker, Will Sullivan, Tim Tores, BGBOXXX Design, and Angriawan Ditya Zulkarnain from the Noun Project https://thenounproject.com/
Map icons are by Cuperto, Alice Design, Alex Ahineev, Botho Willer, Verry Obito, Sean Maldjia, Tinashe Mugayi, Georgiana Ionescu, Andreas Vögele, Tom Fricker, Will Sullivan, Tim Tores, BGBOXXX Design, and Angriawan Ditya Zulkarnain from the Noun Project https://thenounproject.com/
NDB icon is by Inductiveload from WikiMedia.

Wyświetl plik

@ -33,7 +33,6 @@
#include "SWGWorkspaceInfo.h"
#include "SWGFreqScannerSettings.h"
#include "SWGChannelReport.h"
#include "SWGMapItem.h"
#include "device/deviceset.h"
#include "dsp/dspengine.h"
@ -673,7 +672,6 @@ void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStri
}
if (settingsKeys.contains("frequencies")
|| settingsKeys.contains("enabled")
|| settingsKeys.contains("priority")
|| settingsKeys.contains("measurement")
|| settingsKeys.contains("mode")
@ -785,6 +783,26 @@ void FreqScanner::webapiUpdateChannelSettings(
if (channelSettingsKeys.contains("threshold")) {
settings.m_threshold = response.getFreqScannerSettings()->getThreshold();
}
if (channelSettingsKeys.contains("frequencies"))
{
settings.m_frequencies.clear();
settings.m_enabled.clear();
settings.m_notes.clear();
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = response.getFreqScannerSettings()->getFrequencies();
if (frequencies)
{
for (const auto frequency : *frequencies)
{
settings.m_frequencies.append(frequency->getFrequency());
settings.m_enabled.append((bool)frequency->getEnabled());
if (frequency->getNotes()) {
settings.m_notes.append(*frequency->getNotes());
} else {
settings.m_notes.append("");
}
}
}
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getFreqScannerSettings()->getRgbColor();
}
@ -817,12 +835,36 @@ void FreqScanner::webapiUpdateChannelSettings(
}
}
QList<SWGSDRangel::SWGFreqScannerFrequency *> *FreqScanner::createFrequencyList(const FreqScannerSettings& settings)
{
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = new QList<SWGSDRangel::SWGFreqScannerFrequency *>();
for (int i = 0; i < settings.m_frequencies.size(); i++)
{
SWGSDRangel::SWGFreqScannerFrequency *frequency = new SWGSDRangel::SWGFreqScannerFrequency();
frequency->init();
frequency->setFrequency(settings.m_frequencies[i]);
frequency->setEnabled(settings.m_enabled[i]);
if (!settings.m_notes[i].isEmpty()) {
frequency->setNotes(new QString(settings.m_notes[i]));
}
frequencies->append(frequency);
}
return frequencies;
}
void FreqScanner::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const FreqScannerSettings& settings)
{
response.getFreqScannerSettings()->setChannelFrequencyOffset(settings.m_channelFrequencyOffset);
response.getFreqScannerSettings()->setChannelBandwidth(settings.m_channelBandwidth);
response.getFreqScannerSettings()->setThreshold(settings.m_threshold);
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = createFrequencyList(settings);
if (response.getFreqScannerSettings()->getFrequencies()) {
*response.getFreqScannerSettings()->getFrequencies() = *frequencies;
} else {
response.getFreqScannerSettings()->setFrequencies(frequencies);
}
response.getFreqScannerSettings()->setRgbColor(settings.m_rgbColor);
if (response.getFreqScannerSettings()->getTitle()) {
*response.getFreqScannerSettings()->getTitle() = settings.m_title;
@ -927,6 +969,15 @@ void FreqScanner::webapiFormatChannelSettings(
if (channelSettingsKeys.contains("threshold") || force) {
swgFreqScannerSettings->setThreshold(settings.m_threshold);
}
if (channelSettingsKeys.contains("frequencies") || force) {
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = createFrequencyList(settings);
if (swgFreqScannerSettings->getFrequencies()) {
*swgFreqScannerSettings->getFrequencies() = *frequencies;
} else {
swgFreqScannerSettings->setFrequencies(frequencies);
}
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgFreqScannerSettings->setRgbColor(settings.m_rgbColor);
}

Wyświetl plik

@ -32,6 +32,8 @@
#include "freqscannerbaseband.h"
#include "freqscannersettings.h"
#include "SWGChannelSettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
@ -407,6 +409,8 @@ private:
void processScanResults(const QDateTime& fftStartTime, const QList<MsgScanResult::ScanResult>& results);
void setDeviceCenterFrequency(qint64 frequency);
static QList<SWGSDRangel::SWGFreqScannerFrequency *> *FreqScanner::createFrequencyList(const FreqScannerSettings& settings);
private slots:
void networkManagerFinished(QNetworkReply *reply);
void handleIndexInDeviceSetChanged(int index);

Wyświetl plik

@ -630,12 +630,7 @@ void FreqScannerGUI::on_addRange_clicked()
addRow(f, true);
}
blockApplySettings(false);
QList<QString> settingsKeys({
"frequencies",
"enabled",
"notes"
});
applySettings(settingsKeys);
applySetting("frequencies");
}
}
@ -651,12 +646,7 @@ void FreqScannerGUI::on_remove_clicked()
m_settings.m_enabled.removeAt(row);
m_settings.m_notes.removeAt(row);
}
QList<QString> settingsKeys({
"frequencies",
"enabled",
"notes"
});
applySettings(settingsKeys);
applySetting("frequencies");
}
void FreqScannerGUI::on_removeInactive_clicked()
@ -671,15 +661,9 @@ void FreqScannerGUI::on_removeInactive_clicked()
m_settings.m_notes.removeAt(i);
}
}
QList<QString> settingsKeys({
"frequencies",
"enabled",
"notes"
});
applySettings(settingsKeys);
applySetting("frequencies");
}
static QList<QTableWidgetItem*> takeRow(QTableWidget* table, int row)
{
QList<QTableWidgetItem*> rowItems;
@ -754,22 +738,17 @@ void FreqScannerGUI::on_table_cellChanged(int row, int column)
}
m_settings.m_frequencies[row] = value;
updateAnnotation(row);
QList<QString> settingsKeys({
"frequencies",
"enabled",
"notes"
});
applySettings(settingsKeys);
applySetting("frequencies");
}
else if (column == COL_ENABLE)
{
m_settings.m_enabled[row] = item->checkState() == Qt::Checked;
applySetting("enabled");
applySetting("frequencies");
}
else if (column == COL_NOTES)
{
m_settings.m_notes[row] = item->text();
applySetting("notes");
applySetting("frequencies");
}
}
}

Wyświetl plik

@ -203,11 +203,7 @@ void FreqScannerSettings::applySettings(const QStringList& settingsKeys, const F
}
if (settingsKeys.contains("frequencies")) {
m_frequencies = settings.m_frequencies;
}
if (settingsKeys.contains("enabled")) {
m_enabled = settings.m_enabled;
}
if (settingsKeys.contains("notes")) {
m_notes = settings.m_notes;
}
if (settingsKeys.contains("channel")) {
@ -293,19 +289,6 @@ QString FreqScannerSettings::getDebugString(const QStringList& settingsKeys, boo
}
ostr << " m_frequencies: " << s.join(",").toStdString();
}
if (settingsKeys.contains("enabled") || force)
{
// Don't display
/*QStringList s;
for (auto e : m_enabled) {
s.append(e ? "true" : "false");
}
ostr << " m_enabled: " << s.join(",").toStdString();*/
}
if (settingsKeys.contains("notes") || force) {
// Don't display
//ostr << " m_notes: " << m_notes.join(",").toStdString();
}
if (settingsKeys.contains("channel") || force) {
ostr << " m_channel: " << m_channel.toStdString();
}

Wyświetl plik

@ -213,6 +213,7 @@ set(sdrbase_SOURCES
settings/mainsettings.cpp
settings/rollupstate.cpp
util/airlines.cpp
util/ais.cpp
util/android.cpp
util/aprsfi.cpp
@ -447,6 +448,7 @@ set(sdrbase_HEADERS
settings/mainsettings.h
settings/rollupstate.h
util/airlines.h
util/ais.h
util/android.h
util/aprsfi.h

Wyświetl plik

@ -1292,6 +1292,79 @@ bool ChannelWebAPIUtils::patchFeatureSetting(unsigned int featureSetIndex, unsig
}
}
bool ChannelWebAPIUtils::patchChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, const QJsonArray& value)
{
SWGSDRangel::SWGChannelSettings channelSettingsResponse;
QString errorResponse;
int httpRC;
ChannelAPI *channel;
if (getChannelSettings(deviceSetIndex, channelIndex, channelSettingsResponse, channel))
{
// Patch setting
QJsonObject *jsonObj = channelSettingsResponse.asJsonObject();
// Set value
bool found = false;
for (QJsonObject::iterator it = jsonObj->begin(); it != jsonObj->end(); it++)
{
QJsonValue jsonValue = it.value();
if (jsonValue.isObject())
{
QJsonObject subObject = jsonValue.toObject();
if (subObject.contains(setting))
{
subObject[setting] = value;
it.value() = subObject;
found = true;
break;
}
}
}
if (!found)
{
for (QJsonObject::iterator it = jsonObj->begin(); it != jsonObj->end(); it++)
{
QJsonValueRef jsonValue = it.value();
if (jsonValue.isObject())
{
QJsonObject subObject = jsonValue.toObject();
subObject.insert(setting, value);
jsonValue = subObject;
}
}
}
QStringList channelSettingsKeys;
channelSettingsKeys.append(setting);
channelSettingsResponse.init();
channelSettingsResponse.fromJsonObject(*jsonObj);
SWGSDRangel::SWGErrorResponse errorResponse2;
httpRC = channel->webapiSettingsPutPatch(false, channelSettingsKeys, channelSettingsResponse, *errorResponse2.getMessage());
if (httpRC/100 == 2)
{
qDebug("ChannelWebAPIUtils::patchChannelSetting: set channel setting %s OK", qPrintable(setting));
return true;
}
else
{
qWarning("ChannelWebAPIUtils::patchChannelSetting: set channel setting error %d: %s",
httpRC, qPrintable(*errorResponse2.getMessage()));
return false;
}
}
else
{
return false;
}
}
bool ChannelWebAPIUtils::getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, int &value)
{
SWGSDRangel::SWGFeatureSettings featureSettingsResponse;

Wyświetl plik

@ -70,6 +70,7 @@ public:
static bool patchDeviceSetting(unsigned int deviceIndex, const QString &setting, int value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, const QString &value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double value);
static bool patchChannelSetting(unsigned int deviceSetIndex, unsigned int channeIndex, const QString &setting, const QJsonArray& value);
static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, int &value);
static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double &value);
static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, QString &value);

Wyświetl plik

@ -433,3 +433,20 @@ void MainCore::updateWakeLock()
}
#endif
std::vector<ChannelAPI*> MainCore::getChannels(const QString& uri)
{
std::vector<ChannelAPI*> channels;
for (const auto deviceSet : m_deviceSets)
{
for (int chi = 0; chi < deviceSet->getNumberOfChannels(); chi++)
{
ChannelAPI* channel = deviceSet->getChannelAt(chi);
if (channel->getURI() == uri) {
channels.push_back(channel);
}
}
}
return channels;
}

Wyświetl plik

@ -853,6 +853,7 @@ public:
PluginManager *getPluginManager() const { return m_pluginManager; }
std::vector<DeviceSet*>& getDeviceSets() { return m_deviceSets; }
std::vector<FeatureSet*>& getFeatureeSets() { return m_featureSets; }
std::vector<ChannelAPI*> getChannels(const QString& uri); //!< Get all channels from any device set with the given URI
void setLoggingOptions();
DeviceAPI *getDevice(unsigned int deviceSetIndex);
ChannelAPI *getChannel(unsigned int deviceSetIndex, int channelIndex);

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,61 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 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_UTIL_AIRLINES_H
#define INCLUDE_UTIL_AIRLINES_H
#include <QString>
#include <QList>
#include <QHash>
#include "export.h"
class SDRBASE_API Airline {
public:
QString m_icao;
QString m_name;
QString m_callsign;
QString m_country;
static const Airline *getByICAO(const QString& icao);
static const Airline *getByCallsign(const QString& callsign);
private:
Airline(const QString& icao, const QString& name, const QString& callsign, const QString& country) :
m_icao(icao),
m_name(name),
m_callsign(callsign),
m_country(country)
{
}
static QHash<QString, const Airline*> m_icaoHash;
static QHash<QString, const Airline*> m_callsignHash;
friend struct Init;
struct Init {
Init();
static const char *m_airlines[];
};
static Init m_init;
};
#endif