mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-06-05 16:32:25 -04:00
ADS-B Updates
Add support for writing/reading .csv log file containing received ADS-B frames for #1035. Fix some memory leaks.
This commit is contained in:
parent
71d2378b7e
commit
132244ef64
@ -176,8 +176,32 @@ void ADSBDemod::applySettings(const ADSBDemodSettings& settings, bool force)
|
|||||||
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
|
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
|
||||||
reverseAPIKeys.append("inputFrequencyOffset");
|
reverseAPIKeys.append("inputFrequencyOffset");
|
||||||
}
|
}
|
||||||
if ((settings.m_rgbColor != m_settings.m_rgbColor) || force) {
|
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) {
|
||||||
reverseAPIKeys.append("rgbColor");
|
reverseAPIKeys.append("rfBandwidth");
|
||||||
|
}
|
||||||
|
if ((settings.m_correlationThreshold != m_settings.m_correlationThreshold) || force) {
|
||||||
|
reverseAPIKeys.append("correlationThreshold");
|
||||||
|
}
|
||||||
|
if ((settings.m_samplesPerBit != m_settings.m_samplesPerBit) || force) {
|
||||||
|
reverseAPIKeys.append("samplesPerBit");
|
||||||
|
}
|
||||||
|
if ((settings.m_removeTimeout != m_settings.m_removeTimeout) || force) {
|
||||||
|
reverseAPIKeys.append("removeTimeout");
|
||||||
|
}
|
||||||
|
if ((settings.m_feedEnabled != m_settings.m_feedEnabled) || force) {
|
||||||
|
reverseAPIKeys.append("beastEnabled");
|
||||||
|
}
|
||||||
|
if ((settings.m_feedHost != m_settings.m_feedHost) || force) {
|
||||||
|
reverseAPIKeys.append("beastHost");
|
||||||
|
}
|
||||||
|
if ((settings.m_feedPort != m_settings.m_feedPort) || force) {
|
||||||
|
reverseAPIKeys.append("beastPort");
|
||||||
|
}
|
||||||
|
if ((settings.m_logFilename != m_settings.m_logFilename) || force) {
|
||||||
|
reverseAPIKeys.append("logFilename");
|
||||||
|
}
|
||||||
|
if ((settings.m_logEnabled != m_settings.m_logEnabled) || force) {
|
||||||
|
reverseAPIKeys.append("logEnabled");
|
||||||
}
|
}
|
||||||
if ((settings.m_title != m_settings.m_title) || force) {
|
if ((settings.m_title != m_settings.m_title) || force) {
|
||||||
reverseAPIKeys.append("title");
|
reverseAPIKeys.append("title");
|
||||||
@ -303,6 +327,12 @@ void ADSBDemod::webapiUpdateChannelSettings(
|
|||||||
if (channelSettingsKeys.contains("beastPort")) {
|
if (channelSettingsKeys.contains("beastPort")) {
|
||||||
settings.m_feedPort = response.getAdsbDemodSettings()->getBeastPort();
|
settings.m_feedPort = response.getAdsbDemodSettings()->getBeastPort();
|
||||||
}
|
}
|
||||||
|
if (channelSettingsKeys.contains("logFilename")) {
|
||||||
|
settings.m_logFilename = *response.getAdsbDemodSettings()->getLogFilename();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("logEnabled")) {
|
||||||
|
settings.m_logEnabled = response.getAdsbDemodSettings()->getLogEnabled();
|
||||||
|
}
|
||||||
if (channelSettingsKeys.contains("rgbColor")) {
|
if (channelSettingsKeys.contains("rgbColor")) {
|
||||||
settings.m_rgbColor = response.getAdsbDemodSettings()->getRgbColor();
|
settings.m_rgbColor = response.getAdsbDemodSettings()->getRgbColor();
|
||||||
}
|
}
|
||||||
@ -351,6 +381,8 @@ void ADSBDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res
|
|||||||
response.getAdsbDemodSettings()->setBeastHost(new QString(settings.m_feedHost));
|
response.getAdsbDemodSettings()->setBeastHost(new QString(settings.m_feedHost));
|
||||||
response.getAdsbDemodSettings()->setBeastPort(settings.m_feedPort);
|
response.getAdsbDemodSettings()->setBeastPort(settings.m_feedPort);
|
||||||
response.getAdsbDemodSettings()->setRgbColor(settings.m_rgbColor);
|
response.getAdsbDemodSettings()->setRgbColor(settings.m_rgbColor);
|
||||||
|
response.getAdsbDemodSettings()->setLogFilename(new QString(settings.m_logFilename));
|
||||||
|
response.getAdsbDemodSettings()->setLogEnabled(settings.m_logEnabled);
|
||||||
|
|
||||||
if (response.getAdsbDemodSettings()->getTitle()) {
|
if (response.getAdsbDemodSettings()->getTitle()) {
|
||||||
*response.getAdsbDemodSettings()->getTitle() = settings.m_title;
|
*response.getAdsbDemodSettings()->getTitle() = settings.m_title;
|
||||||
@ -423,6 +455,12 @@ void ADSBDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, c
|
|||||||
if (channelSettingsKeys.contains("beastPort") || force) {
|
if (channelSettingsKeys.contains("beastPort") || force) {
|
||||||
swgADSBDemodSettings->setBeastPort(settings.m_feedPort);
|
swgADSBDemodSettings->setBeastPort(settings.m_feedPort);
|
||||||
}
|
}
|
||||||
|
if (channelSettingsKeys.contains("logFilename") || force) {
|
||||||
|
swgADSBDemodSettings->setLogFilename(new QString(settings.m_logFilename));
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("logEnabled") || force) {
|
||||||
|
swgADSBDemodSettings->setLogEnabled(settings.m_logEnabled);
|
||||||
|
}
|
||||||
if (channelSettingsKeys.contains("rgbColor") || force) {
|
if (channelSettingsKeys.contains("rgbColor") || force) {
|
||||||
swgADSBDemodSettings->setRgbColor(settings.m_rgbColor);
|
swgADSBDemodSettings->setRgbColor(settings.m_rgbColor);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
#include "SWGMapItem.h"
|
#include "SWGMapItem.h"
|
||||||
|
|
||||||
@ -1016,7 +1017,8 @@ void ADSBDemodGUI::handleADSB(
|
|||||||
aircraft->m_latitudeItem->setData(Qt::DisplayRole, aircraft->m_latitude);
|
aircraft->m_latitudeItem->setData(Qt::DisplayRole, aircraft->m_latitude);
|
||||||
aircraft->m_longitude = longitude;
|
aircraft->m_longitude = longitude;
|
||||||
aircraft->m_longitudeItem->setData(Qt::DisplayRole, aircraft->m_longitude);
|
aircraft->m_longitudeItem->setData(Qt::DisplayRole, aircraft->m_longitude);
|
||||||
aircraft->m_coordinates.push_back(QVariant::fromValue(*new QGeoCoordinate(aircraft->m_latitude, aircraft->m_longitude, aircraft->m_altitude)));
|
QGeoCoordinate coord(aircraft->m_latitude, aircraft->m_longitude, aircraft->m_altitude);
|
||||||
|
aircraft->m_coordinates.push_back(QVariant::fromValue(coord));
|
||||||
updatePosition(aircraft);
|
updatePosition(aircraft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1070,7 +1072,8 @@ void ADSBDemodGUI::handleADSB(
|
|||||||
aircraft->m_latitudeItem->setData(Qt::DisplayRole, aircraft->m_latitude);
|
aircraft->m_latitudeItem->setData(Qt::DisplayRole, aircraft->m_latitude);
|
||||||
aircraft->m_longitude = longitude;
|
aircraft->m_longitude = longitude;
|
||||||
aircraft->m_longitudeItem->setData(Qt::DisplayRole, aircraft->m_longitude);
|
aircraft->m_longitudeItem->setData(Qt::DisplayRole, aircraft->m_longitude);
|
||||||
aircraft->m_coordinates.push_back(QVariant::fromValue(*new QGeoCoordinate(aircraft->m_latitude, aircraft->m_longitude, aircraft->m_altitude)));
|
QGeoCoordinate coord(aircraft->m_latitude, aircraft->m_longitude, aircraft->m_altitude);
|
||||||
|
aircraft->m_coordinates.push_back(QVariant::fromValue(coord));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2172,13 +2175,15 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
|
|||||||
ADSBDemodGUI::~ADSBDemodGUI()
|
ADSBDemodGUI::~ADSBDemodGUI()
|
||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
QHash<int,Aircraft *>::iterator i = m_aircraft.begin();
|
qDeleteAll(m_aircraft);
|
||||||
while (i != m_aircraft.end())
|
if (m_airportInfo) {
|
||||||
{
|
qDeleteAll(*m_airportInfo);
|
||||||
Aircraft *a = i.value();
|
|
||||||
delete a;
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
if (m_aircraftInfo) {
|
||||||
|
qDeleteAll(*m_aircraftInfo);
|
||||||
|
}
|
||||||
|
qDeleteAll(m_airlineIcons);
|
||||||
|
qDeleteAll(m_flagIcons);
|
||||||
if (m_flightInformation)
|
if (m_flightInformation)
|
||||||
{
|
{
|
||||||
disconnect(m_flightInformation, &FlightInformation::flightUpdated, this, &ADSBDemodGUI::flightInformationUpdated);
|
disconnect(m_flightInformation, &FlightInformation::flightUpdated, this, &ADSBDemodGUI::flightInformationUpdated);
|
||||||
@ -2242,6 +2247,9 @@ void ADSBDemodGUI::displaySettings()
|
|||||||
ui->allFlightPaths->setChecked(m_settings.m_allFlightPaths);
|
ui->allFlightPaths->setChecked(m_settings.m_allFlightPaths);
|
||||||
m_aircraftModel.setAllFlightPaths(m_settings.m_allFlightPaths);
|
m_aircraftModel.setAllFlightPaths(m_settings.m_allFlightPaths);
|
||||||
|
|
||||||
|
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
|
||||||
|
ui->logEnable->setChecked(m_settings.m_logEnabled);
|
||||||
|
|
||||||
displayStreamIndex();
|
displayStreamIndex();
|
||||||
|
|
||||||
QFont font(m_settings.m_tableFontName, m_settings.m_tableFontSize);
|
QFont font(m_settings.m_tableFontName, m_settings.m_tableFontSize);
|
||||||
@ -2503,3 +2511,104 @@ void ADSBDemodGUI::flightInformationUpdated(const FlightInformation::Flight& fli
|
|||||||
qDebug() << "ADSBDemodGUI::flightInformationUpdated - Flight not found in ADS-B table: " << flight.m_flightICAO;
|
qDebug() << "ADSBDemodGUI::flightInformationUpdated - Flight not found in ADS-B table: " << flight.m_flightICAO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ADSBDemodGUI::on_logEnable_clicked(bool checked)
|
||||||
|
{
|
||||||
|
m_settings.m_logEnabled = checked;
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADSBDemodGUI::on_logFilename_clicked()
|
||||||
|
{
|
||||||
|
// Get filename to save to
|
||||||
|
QFileDialog fileDialog(nullptr, "Select file to log received frames to", "", "*.csv");
|
||||||
|
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||||
|
if (fileDialog.exec())
|
||||||
|
{
|
||||||
|
QStringList fileNames = fileDialog.selectedFiles();
|
||||||
|
if (fileNames.size() > 0)
|
||||||
|
{
|
||||||
|
m_settings.m_logFilename = fileNames[0];
|
||||||
|
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read .csv log and process as received frames
|
||||||
|
void ADSBDemodGUI::on_logOpen_clicked()
|
||||||
|
{
|
||||||
|
QFileDialog fileDialog(nullptr, "Select .csv log file to read", "", "*.csv");
|
||||||
|
if (fileDialog.exec())
|
||||||
|
{
|
||||||
|
QStringList fileNames = fileDialog.selectedFiles();
|
||||||
|
if (fileNames.size() > 0)
|
||||||
|
{
|
||||||
|
QFile file(fileNames[0]);
|
||||||
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
|
{
|
||||||
|
QTextStream in(&file);
|
||||||
|
QString header = in.readLine();
|
||||||
|
QStringList colNames = header.split(",");
|
||||||
|
int dateCol = colNames.indexOf("Date");
|
||||||
|
int timeCol = colNames.indexOf("Time");
|
||||||
|
int dataCol = colNames.indexOf("Data");
|
||||||
|
int correlationCol = colNames.indexOf("Correlation");
|
||||||
|
if (dateCol == -1)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, "ADS-B", QString(".csv file doesn't contain a column named 'Date'"));
|
||||||
|
}
|
||||||
|
else if (timeCol == -1)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, "ADS-B", QString(".csv file doesn't contain a column named 'Time'"));
|
||||||
|
}
|
||||||
|
else if (dataCol == -1)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, "ADS-B", QString(".csv file doesn't contain a column named 'Data'"));
|
||||||
|
}
|
||||||
|
else if (correlationCol == -1)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, "ADS-B", QString(".csv file doesn't contain a column named 'Correlation'"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox dialog(this);
|
||||||
|
dialog.setText("Reading ADS-B data");
|
||||||
|
dialog.addButton(QMessageBox::Cancel);
|
||||||
|
dialog.show();
|
||||||
|
QApplication::processEvents();
|
||||||
|
int count = 0;
|
||||||
|
bool cancelled = false;
|
||||||
|
while (!in.atEnd() && !cancelled)
|
||||||
|
{
|
||||||
|
QString row = in.readLine();
|
||||||
|
QStringList cols = row.split(",");
|
||||||
|
if (cols.size() >= dataCol)
|
||||||
|
{
|
||||||
|
//QDate date = QDate::fromString(cols[dateCol]);
|
||||||
|
//QTime time = QTime::fromString(cols[timeCol]);
|
||||||
|
//QDateTime dateTime(date, time);
|
||||||
|
QDateTime dateTime = QDateTime::currentDateTime(); // So they aren't removed immediately as too old
|
||||||
|
QByteArray bytes = QByteArray::fromHex(cols[dataCol].toLatin1());
|
||||||
|
float correlation = cols[correlationCol].toFloat();
|
||||||
|
handleADSB(bytes, dateTime, correlation, correlation);
|
||||||
|
if (count % 1000 == 0)
|
||||||
|
{
|
||||||
|
QApplication::processEvents();
|
||||||
|
if (dialog.clickedButton()) {
|
||||||
|
cancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, "ADS-B", QString("Failed to open file %1").arg(fileNames[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -618,6 +618,9 @@ private slots:
|
|||||||
void on_device_currentIndexChanged(int index);
|
void on_device_currentIndexChanged(int index);
|
||||||
void feedSelect();
|
void feedSelect();
|
||||||
void on_displaySettings_clicked();
|
void on_displaySettings_clicked();
|
||||||
|
void on_logEnable_clicked(bool checked=false);
|
||||||
|
void on_logFilename_clicked();
|
||||||
|
void on_logOpen_clicked();
|
||||||
signals:
|
signals:
|
||||||
void homePositionChanged();
|
void homePositionChanged();
|
||||||
};
|
};
|
||||||
|
@ -167,13 +167,6 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="levelMeterLayout">
|
<layout class="QHBoxLayout" name="levelMeterLayout">
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line_5">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="channelPowerMeterUnits">
|
<widget class="QLabel" name="channelPowerMeterUnits">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -210,13 +203,6 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="rfBWLayout">
|
<layout class="QHBoxLayout" name="rfBWLayout">
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line_4">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="rfBWLabel">
|
<widget class="QLabel" name="rfBWLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -634,6 +620,60 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="ButtonSwitch" name="logEnable">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Start/stop logging of received frames to .csv file</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||||
|
<normaloff>:/record_off.png</normaloff>:/record_off.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="logFilename">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Set log .csv filename</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||||
|
<normaloff>:/save.png</normaloff>:/save.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="logOpen">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Read data from .csv log file</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||||
|
<normaloff>:/load.png</normaloff>:/load.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="stats">
|
<widget class="QLabel" name="stats">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
|
@ -72,6 +72,8 @@ void ADSBDemodSettings::resetToDefaults()
|
|||||||
m_columnIndexes[i] = i;
|
m_columnIndexes[i] = i;
|
||||||
m_columnSizes[i] = -1; // Autosize
|
m_columnSizes[i] = -1; // Autosize
|
||||||
}
|
}
|
||||||
|
m_logFilename = "adsb_log.csv";
|
||||||
|
m_logEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray ADSBDemodSettings::serialize() const
|
QByteArray ADSBDemodSettings::serialize() const
|
||||||
@ -118,6 +120,9 @@ QByteArray ADSBDemodSettings::serialize() const
|
|||||||
s.writeBlob(34, serializeNotificationSettings(m_notificationSettings));
|
s.writeBlob(34, serializeNotificationSettings(m_notificationSettings));
|
||||||
s.writeString(35, m_apiKey);
|
s.writeString(35, m_apiKey);
|
||||||
|
|
||||||
|
s.writeString(36, m_logFilename);
|
||||||
|
s.writeBool(37, m_logEnabled);
|
||||||
|
|
||||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||||
s.writeS32(100 + i, m_columnIndexes[i]);
|
s.writeS32(100 + i, m_columnIndexes[i]);
|
||||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||||
@ -203,6 +208,9 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
|
|||||||
deserializeNotificationSettings(blob, m_notificationSettings);
|
deserializeNotificationSettings(blob, m_notificationSettings);
|
||||||
d.readString(35, &m_apiKey, "");
|
d.readString(35, &m_apiKey, "");
|
||||||
|
|
||||||
|
d.readString(36, &m_logFilename, "adsb_log.csv");
|
||||||
|
d.readBool(37, &m_logEnabled, false);
|
||||||
|
|
||||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||||
d.readS32(100 + i, &m_columnIndexes[i], i);
|
d.readS32(100 + i, &m_columnIndexes[i], i);
|
||||||
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
|
||||||
|
@ -130,6 +130,9 @@ struct ADSBDemodSettings
|
|||||||
QList<NotificationSettings *> m_notificationSettings;
|
QList<NotificationSettings *> m_notificationSettings;
|
||||||
QString m_apiKey; //!< aviationstack.com API key
|
QString m_apiKey; //!< aviationstack.com API key
|
||||||
|
|
||||||
|
QString m_logFilename;
|
||||||
|
bool m_logEnabled;
|
||||||
|
|
||||||
ADSBDemodSettings();
|
ADSBDemodSettings();
|
||||||
void resetToDefaults();
|
void resetToDefaults();
|
||||||
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
||||||
|
@ -122,6 +122,8 @@ void ADSBDemodWorker::applySettings(const ADSBDemodSettings& settings, bool forc
|
|||||||
<< " m_feedHost: " << settings.m_feedHost
|
<< " m_feedHost: " << settings.m_feedHost
|
||||||
<< " m_feedPort: " << settings.m_feedPort
|
<< " m_feedPort: " << settings.m_feedPort
|
||||||
<< " m_feedFormat: " << settings.m_feedFormat
|
<< " m_feedFormat: " << settings.m_feedFormat
|
||||||
|
<< " m_logEnabled: " << settings.m_logEnabled
|
||||||
|
<< " m_logFilename: " << settings.m_logFilename
|
||||||
<< " force: " << force;
|
<< " force: " << force;
|
||||||
|
|
||||||
if ((settings.m_feedEnabled != m_settings.m_feedEnabled)
|
if ((settings.m_feedEnabled != m_settings.m_feedEnabled)
|
||||||
@ -136,6 +138,36 @@ void ADSBDemodWorker::applySettings(const ADSBDemodSettings& settings, bool forc
|
|||||||
m_socket.connectToHost(settings.m_feedHost, settings.m_feedPort);
|
m_socket.connectToHost(settings.m_feedHost, settings.m_feedPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((settings.m_logEnabled != m_settings.m_logEnabled)
|
||||||
|
|| (settings.m_logFilename != m_settings.m_logFilename)
|
||||||
|
|| force)
|
||||||
|
{
|
||||||
|
if (m_logFile.isOpen())
|
||||||
|
{
|
||||||
|
m_logStream.flush();
|
||||||
|
m_logFile.close();
|
||||||
|
}
|
||||||
|
if (settings.m_logEnabled && !settings.m_logFilename.isEmpty())
|
||||||
|
{
|
||||||
|
m_logFile.setFileName(settings.m_logFilename);
|
||||||
|
if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
|
||||||
|
{
|
||||||
|
qDebug() << "ADSBDemodWorker::applySettings - Logging to: " << settings.m_logFilename;
|
||||||
|
bool newFile = m_logFile.size() == 0;
|
||||||
|
m_logStream.setDevice(&m_logFile);
|
||||||
|
if (newFile)
|
||||||
|
{
|
||||||
|
// Write header
|
||||||
|
m_logStream << "Date,Time,Data,Correlation\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "ADSBDemodWorker::applySettings - Unable to open log file: " << settings.m_logFilename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_settings = settings;
|
m_settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,49 +217,57 @@ char *ADSBDemodWorker::escape(char *p, char c)
|
|||||||
|
|
||||||
// Forward ADS-B data in Beast binary format to specified server
|
// Forward ADS-B data in Beast binary format to specified server
|
||||||
// See: https://wiki.jetvision.de/wiki/Mode-S_Beast:Data_Output_Formats
|
// See: https://wiki.jetvision.de/wiki/Mode-S_Beast:Data_Output_Formats
|
||||||
|
// Log to .csv file
|
||||||
void ADSBDemodWorker::handleADSB(QByteArray data, const QDateTime dateTime, float correlation)
|
void ADSBDemodWorker::handleADSB(QByteArray data, const QDateTime dateTime, float correlation)
|
||||||
{
|
{
|
||||||
if (m_settings.m_feedFormat == ADSBDemodSettings::BeastBinary)
|
if (m_logFile.isOpen())
|
||||||
{
|
{
|
||||||
char beastBinary[2+6*2+1*2+14*2];
|
m_logStream << dateTime.date().toString() << "," << dateTime.time().toString() << "," << data.toHex() << "," << correlation << "\n";
|
||||||
int length;
|
|
||||||
char *p = beastBinary;
|
|
||||||
qint64 timestamp;
|
|
||||||
unsigned char signalStrength;
|
|
||||||
|
|
||||||
timestamp = dateTime.toMSecsSinceEpoch();
|
|
||||||
|
|
||||||
if (correlation > 255)
|
|
||||||
signalStrength = 255;
|
|
||||||
if (correlation < 1)
|
|
||||||
signalStrength = 1;
|
|
||||||
else
|
|
||||||
signalStrength = (unsigned char)correlation;
|
|
||||||
|
|
||||||
*p++ = BEAST_ESC;
|
|
||||||
*p++ = '3'; // Mode-S long
|
|
||||||
|
|
||||||
p = escape(p, timestamp >> 56); // Big-endian timestamp
|
|
||||||
p = escape(p, timestamp >> 48);
|
|
||||||
p = escape(p, timestamp >> 32);
|
|
||||||
p = escape(p, timestamp >> 24);
|
|
||||||
p = escape(p, timestamp >> 16);
|
|
||||||
p = escape(p, timestamp >> 8);
|
|
||||||
p = escape(p, timestamp);
|
|
||||||
|
|
||||||
p = escape(p, signalStrength); // Signal strength
|
|
||||||
|
|
||||||
for (int i = 0; i < data.length(); i++) // ADS-B data
|
|
||||||
p = escape(p, data[i]);
|
|
||||||
|
|
||||||
length = p - beastBinary;
|
|
||||||
|
|
||||||
send(beastBinary, length);
|
|
||||||
}
|
}
|
||||||
else if (m_settings.m_feedFormat == ADSBDemodSettings::BeastHex)
|
if (m_settings.m_feedEnabled)
|
||||||
{
|
{
|
||||||
QString beastHex = "*" + data.toHex() + ";\n";
|
if (m_settings.m_feedFormat == ADSBDemodSettings::BeastBinary)
|
||||||
send(beastHex.toUtf8(), beastHex.size());
|
{
|
||||||
|
char beastBinary[2+6*2+1*2+14*2];
|
||||||
|
int length;
|
||||||
|
char *p = beastBinary;
|
||||||
|
qint64 timestamp;
|
||||||
|
unsigned char signalStrength;
|
||||||
|
|
||||||
|
timestamp = dateTime.toMSecsSinceEpoch();
|
||||||
|
|
||||||
|
if (correlation > 255)
|
||||||
|
signalStrength = 255;
|
||||||
|
if (correlation < 1)
|
||||||
|
signalStrength = 1;
|
||||||
|
else
|
||||||
|
signalStrength = (unsigned char)correlation;
|
||||||
|
|
||||||
|
*p++ = BEAST_ESC;
|
||||||
|
*p++ = '3'; // Mode-S long
|
||||||
|
|
||||||
|
p = escape(p, timestamp >> 56); // Big-endian timestamp
|
||||||
|
p = escape(p, timestamp >> 48);
|
||||||
|
p = escape(p, timestamp >> 32);
|
||||||
|
p = escape(p, timestamp >> 24);
|
||||||
|
p = escape(p, timestamp >> 16);
|
||||||
|
p = escape(p, timestamp >> 8);
|
||||||
|
p = escape(p, timestamp);
|
||||||
|
|
||||||
|
p = escape(p, signalStrength); // Signal strength
|
||||||
|
|
||||||
|
for (int i = 0; i < data.length(); i++) // ADS-B data
|
||||||
|
p = escape(p, data[i]);
|
||||||
|
|
||||||
|
length = p - beastBinary;
|
||||||
|
|
||||||
|
send(beastBinary, length);
|
||||||
|
}
|
||||||
|
else if (m_settings.m_feedFormat == ADSBDemodSettings::BeastHex)
|
||||||
|
{
|
||||||
|
QString beastHex = "*" + data.toHex() + ";\n";
|
||||||
|
send(beastHex.toUtf8(), beastHex.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
#include "util/message.h"
|
#include "util/message.h"
|
||||||
#include "util/messagequeue.h"
|
#include "util/messagequeue.h"
|
||||||
@ -71,6 +73,8 @@ private:
|
|||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
QTimer m_heartbeatTimer;
|
QTimer m_heartbeatTimer;
|
||||||
QTcpSocket m_socket;
|
QTcpSocket m_socket;
|
||||||
|
QFile m_logFile;
|
||||||
|
QTextStream m_logStream;
|
||||||
|
|
||||||
bool handleMessage(const Message& cmd);
|
bool handleMessage(const Message& cmd);
|
||||||
void applySettings(const ADSBDemodSettings& settings, bool force = false);
|
void applySettings(const ADSBDemodSettings& settings, bool force = false);
|
||||||
|
@ -51,6 +51,11 @@ struct AirportInformation {
|
|||||||
float m_elevation;
|
float m_elevation;
|
||||||
QVector<FrequencyInformation *> m_frequencies;
|
QVector<FrequencyInformation *> m_frequencies;
|
||||||
|
|
||||||
|
AirportInformation::~AirportInformation()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_frequencies);
|
||||||
|
}
|
||||||
|
|
||||||
static QString trimQuotes(const QString s)
|
static QString trimQuotes(const QString s)
|
||||||
{
|
{
|
||||||
if (s.startsWith('\"') && s.endsWith('\"'))
|
if (s.startsWith('\"') && s.endsWith('\"'))
|
||||||
|
@ -163,6 +163,18 @@ the case for callsigns that end in two characters, as for these, some digits fro
|
|||||||
|
|
||||||
To use this feature, an (aviationstack)[aviationstack.com] API Key must be entered in the Display Settings dialog (11). A free key giving 500 API calls per month is (available)[https://aviationstack.com/product].
|
To use this feature, an (aviationstack)[aviationstack.com] API Key must be entered in the Display Settings dialog (11). A free key giving 500 API calls per month is (available)[https://aviationstack.com/product].
|
||||||
|
|
||||||
|
<h3>Start/stop Logging ADS-B frames to .csv File</h3>
|
||||||
|
|
||||||
|
When checked, writes all received ADS-B frames to a .csv file.
|
||||||
|
|
||||||
|
<h3>.csv Log Filename</h3>
|
||||||
|
|
||||||
|
Click to specify the name of the .csv file which received ADS-B frames are logged to.
|
||||||
|
|
||||||
|
<h3>Read Data from .csv File</h3>
|
||||||
|
|
||||||
|
Click to specify a previously written ADS-B .csv log file, which is read and used to updated the ADS-B data table and map.
|
||||||
|
|
||||||
<h3>14: Refresh list of devices</h3>
|
<h3>14: Refresh list of devices</h3>
|
||||||
|
|
||||||
Use this button to refresh the list of devices.
|
Use this button to refresh the list of devices.
|
||||||
|
@ -22,6 +22,10 @@ ADSBDemodSettings:
|
|||||||
type: string
|
type: string
|
||||||
beastPort:
|
beastPort:
|
||||||
type: integer
|
type: integer
|
||||||
|
logFilename:
|
||||||
|
type: string
|
||||||
|
logEnabled:
|
||||||
|
type: integer
|
||||||
rgbColor:
|
rgbColor:
|
||||||
type: integer
|
type: integer
|
||||||
title:
|
title:
|
||||||
|
@ -44,6 +44,10 @@ SWGADSBDemodSettings::SWGADSBDemodSettings() {
|
|||||||
m_beast_host_isSet = false;
|
m_beast_host_isSet = false;
|
||||||
beast_port = 0;
|
beast_port = 0;
|
||||||
m_beast_port_isSet = false;
|
m_beast_port_isSet = false;
|
||||||
|
log_filename = nullptr;
|
||||||
|
m_log_filename_isSet = false;
|
||||||
|
log_enabled = 0;
|
||||||
|
m_log_enabled_isSet = false;
|
||||||
rgb_color = 0;
|
rgb_color = 0;
|
||||||
m_rgb_color_isSet = false;
|
m_rgb_color_isSet = false;
|
||||||
title = nullptr;
|
title = nullptr;
|
||||||
@ -84,6 +88,10 @@ SWGADSBDemodSettings::init() {
|
|||||||
m_beast_host_isSet = false;
|
m_beast_host_isSet = false;
|
||||||
beast_port = 0;
|
beast_port = 0;
|
||||||
m_beast_port_isSet = false;
|
m_beast_port_isSet = false;
|
||||||
|
log_filename = new QString("");
|
||||||
|
m_log_filename_isSet = false;
|
||||||
|
log_enabled = 0;
|
||||||
|
m_log_enabled_isSet = false;
|
||||||
rgb_color = 0;
|
rgb_color = 0;
|
||||||
m_rgb_color_isSet = false;
|
m_rgb_color_isSet = false;
|
||||||
title = new QString("");
|
title = new QString("");
|
||||||
@ -114,6 +122,10 @@ SWGADSBDemodSettings::cleanup() {
|
|||||||
delete beast_host;
|
delete beast_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(log_filename != nullptr) {
|
||||||
|
delete log_filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if(title != nullptr) {
|
if(title != nullptr) {
|
||||||
delete title;
|
delete title;
|
||||||
@ -155,6 +167,10 @@ SWGADSBDemodSettings::fromJsonObject(QJsonObject &pJson) {
|
|||||||
|
|
||||||
::SWGSDRangel::setValue(&beast_port, pJson["beastPort"], "qint32", "");
|
::SWGSDRangel::setValue(&beast_port, pJson["beastPort"], "qint32", "");
|
||||||
|
|
||||||
|
::SWGSDRangel::setValue(&log_filename, pJson["logFilename"], "QString", "QString");
|
||||||
|
|
||||||
|
::SWGSDRangel::setValue(&log_enabled, pJson["logEnabled"], "qint32", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
|
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
|
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
|
||||||
@ -211,6 +227,12 @@ SWGADSBDemodSettings::asJsonObject() {
|
|||||||
if(m_beast_port_isSet){
|
if(m_beast_port_isSet){
|
||||||
obj->insert("beastPort", QJsonValue(beast_port));
|
obj->insert("beastPort", QJsonValue(beast_port));
|
||||||
}
|
}
|
||||||
|
if(log_filename != nullptr && *log_filename != QString("")){
|
||||||
|
toJsonValue(QString("logFilename"), log_filename, obj, QString("QString"));
|
||||||
|
}
|
||||||
|
if(m_log_enabled_isSet){
|
||||||
|
obj->insert("logEnabled", QJsonValue(log_enabled));
|
||||||
|
}
|
||||||
if(m_rgb_color_isSet){
|
if(m_rgb_color_isSet){
|
||||||
obj->insert("rgbColor", QJsonValue(rgb_color));
|
obj->insert("rgbColor", QJsonValue(rgb_color));
|
||||||
}
|
}
|
||||||
@ -319,6 +341,26 @@ SWGADSBDemodSettings::setBeastPort(qint32 beast_port) {
|
|||||||
this->m_beast_port_isSet = true;
|
this->m_beast_port_isSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString*
|
||||||
|
SWGADSBDemodSettings::getLogFilename() {
|
||||||
|
return log_filename;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
SWGADSBDemodSettings::setLogFilename(QString* log_filename) {
|
||||||
|
this->log_filename = log_filename;
|
||||||
|
this->m_log_filename_isSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32
|
||||||
|
SWGADSBDemodSettings::getLogEnabled() {
|
||||||
|
return log_enabled;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
SWGADSBDemodSettings::setLogEnabled(qint32 log_enabled) {
|
||||||
|
this->log_enabled = log_enabled;
|
||||||
|
this->m_log_enabled_isSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
qint32
|
qint32
|
||||||
SWGADSBDemodSettings::getRgbColor() {
|
SWGADSBDemodSettings::getRgbColor() {
|
||||||
return rgb_color;
|
return rgb_color;
|
||||||
@ -428,6 +470,12 @@ SWGADSBDemodSettings::isSet(){
|
|||||||
if(m_beast_port_isSet){
|
if(m_beast_port_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
|
if(log_filename && *log_filename != QString("")){
|
||||||
|
isObjectUpdated = true; break;
|
||||||
|
}
|
||||||
|
if(m_log_enabled_isSet){
|
||||||
|
isObjectUpdated = true; break;
|
||||||
|
}
|
||||||
if(m_rgb_color_isSet){
|
if(m_rgb_color_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,12 @@ public:
|
|||||||
qint32 getBeastPort();
|
qint32 getBeastPort();
|
||||||
void setBeastPort(qint32 beast_port);
|
void setBeastPort(qint32 beast_port);
|
||||||
|
|
||||||
|
QString* getLogFilename();
|
||||||
|
void setLogFilename(QString* log_filename);
|
||||||
|
|
||||||
|
qint32 getLogEnabled();
|
||||||
|
void setLogEnabled(qint32 log_enabled);
|
||||||
|
|
||||||
qint32 getRgbColor();
|
qint32 getRgbColor();
|
||||||
void setRgbColor(qint32 rgb_color);
|
void setRgbColor(qint32 rgb_color);
|
||||||
|
|
||||||
@ -118,6 +124,12 @@ private:
|
|||||||
qint32 beast_port;
|
qint32 beast_port;
|
||||||
bool m_beast_port_isSet;
|
bool m_beast_port_isSet;
|
||||||
|
|
||||||
|
QString* log_filename;
|
||||||
|
bool m_log_filename_isSet;
|
||||||
|
|
||||||
|
qint32 log_enabled;
|
||||||
|
bool m_log_enabled_isSet;
|
||||||
|
|
||||||
qint32 rgb_color;
|
qint32 rgb_color;
|
||||||
bool m_rgb_color_isSet;
|
bool m_rgb_color_isSet;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user