mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-03 13:11:20 -05: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) {
 | 
			
		||||
        reverseAPIKeys.append("inputFrequencyOffset");
 | 
			
		||||
    }
 | 
			
		||||
    if ((settings.m_rgbColor != m_settings.m_rgbColor) || force) {
 | 
			
		||||
        reverseAPIKeys.append("rgbColor");
 | 
			
		||||
    if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) {
 | 
			
		||||
        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) {
 | 
			
		||||
        reverseAPIKeys.append("title");
 | 
			
		||||
@ -303,6 +327,12 @@ void ADSBDemod::webapiUpdateChannelSettings(
 | 
			
		||||
    if (channelSettingsKeys.contains("beastPort")) {
 | 
			
		||||
        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")) {
 | 
			
		||||
        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()->setBeastPort(settings.m_feedPort);
 | 
			
		||||
    response.getAdsbDemodSettings()->setRgbColor(settings.m_rgbColor);
 | 
			
		||||
    response.getAdsbDemodSettings()->setLogFilename(new QString(settings.m_logFilename));
 | 
			
		||||
    response.getAdsbDemodSettings()->setLogEnabled(settings.m_logEnabled);
 | 
			
		||||
 | 
			
		||||
    if (response.getAdsbDemodSettings()->getTitle()) {
 | 
			
		||||
        *response.getAdsbDemodSettings()->getTitle() = settings.m_title;
 | 
			
		||||
@ -423,6 +455,12 @@ void ADSBDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, c
 | 
			
		||||
    if (channelSettingsKeys.contains("beastPort") || force) {
 | 
			
		||||
        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) {
 | 
			
		||||
        swgADSBDemodSettings->setRgbColor(settings.m_rgbColor);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@
 | 
			
		||||
#include <QMessageBox>
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
#include <QProcess>
 | 
			
		||||
#include <QFileDialog>
 | 
			
		||||
 | 
			
		||||
#include "SWGMapItem.h"
 | 
			
		||||
 | 
			
		||||
@ -1016,7 +1017,8 @@ void ADSBDemodGUI::handleADSB(
 | 
			
		||||
                        aircraft->m_latitudeItem->setData(Qt::DisplayRole, aircraft->m_latitude);
 | 
			
		||||
                        aircraft->m_longitude = 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);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@ -1070,7 +1072,8 @@ void ADSBDemodGUI::handleADSB(
 | 
			
		||||
                    aircraft->m_latitudeItem->setData(Qt::DisplayRole, aircraft->m_latitude);
 | 
			
		||||
                    aircraft->m_longitude = 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()
 | 
			
		||||
{
 | 
			
		||||
    delete ui;
 | 
			
		||||
    QHash<int,Aircraft *>::iterator i = m_aircraft.begin();
 | 
			
		||||
    while (i != m_aircraft.end())
 | 
			
		||||
    {
 | 
			
		||||
        Aircraft *a = i.value();
 | 
			
		||||
        delete a;
 | 
			
		||||
        ++i;
 | 
			
		||||
    qDeleteAll(m_aircraft);
 | 
			
		||||
    if (m_airportInfo) {
 | 
			
		||||
        qDeleteAll(*m_airportInfo);
 | 
			
		||||
    }
 | 
			
		||||
    if (m_aircraftInfo) {
 | 
			
		||||
        qDeleteAll(*m_aircraftInfo);
 | 
			
		||||
    }
 | 
			
		||||
    qDeleteAll(m_airlineIcons);
 | 
			
		||||
    qDeleteAll(m_flagIcons);
 | 
			
		||||
    if (m_flightInformation)
 | 
			
		||||
    {
 | 
			
		||||
        disconnect(m_flightInformation, &FlightInformation::flightUpdated, this, &ADSBDemodGUI::flightInformationUpdated);
 | 
			
		||||
@ -2242,6 +2247,9 @@ void ADSBDemodGUI::displaySettings()
 | 
			
		||||
    ui->allFlightPaths->setChecked(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();
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 feedSelect();
 | 
			
		||||
    void on_displaySettings_clicked();
 | 
			
		||||
    void on_logEnable_clicked(bool checked=false);
 | 
			
		||||
    void on_logFilename_clicked();
 | 
			
		||||
    void on_logOpen_clicked();
 | 
			
		||||
signals:
 | 
			
		||||
    void homePositionChanged();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -167,13 +167,6 @@
 | 
			
		||||
    </item>
 | 
			
		||||
    <item>
 | 
			
		||||
     <layout class="QHBoxLayout" name="levelMeterLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="Line" name="line_5">
 | 
			
		||||
        <property name="orientation">
 | 
			
		||||
         <enum>Qt::Vertical</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="channelPowerMeterUnits">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
@ -210,13 +203,6 @@
 | 
			
		||||
    </item>
 | 
			
		||||
    <item>
 | 
			
		||||
     <layout class="QHBoxLayout" name="rfBWLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="Line" name="line_4">
 | 
			
		||||
        <property name="orientation">
 | 
			
		||||
         <enum>Qt::Vertical</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="rfBWLabel">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
@ -634,6 +620,60 @@
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </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>
 | 
			
		||||
       <widget class="QLabel" name="stats">
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
 | 
			
		||||
@ -72,6 +72,8 @@ void ADSBDemodSettings::resetToDefaults()
 | 
			
		||||
        m_columnIndexes[i] = i;
 | 
			
		||||
        m_columnSizes[i] = -1; // Autosize
 | 
			
		||||
    }
 | 
			
		||||
    m_logFilename = "adsb_log.csv";
 | 
			
		||||
    m_logEnabled = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QByteArray ADSBDemodSettings::serialize() const
 | 
			
		||||
@ -118,6 +120,9 @@ QByteArray ADSBDemodSettings::serialize() const
 | 
			
		||||
    s.writeBlob(34, serializeNotificationSettings(m_notificationSettings));
 | 
			
		||||
    s.writeString(35, m_apiKey);
 | 
			
		||||
 | 
			
		||||
    s.writeString(36, m_logFilename);
 | 
			
		||||
    s.writeBool(37, m_logEnabled);
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
 | 
			
		||||
        s.writeS32(100 + i, m_columnIndexes[i]);
 | 
			
		||||
    for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
 | 
			
		||||
@ -203,6 +208,9 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
 | 
			
		||||
        deserializeNotificationSettings(blob, m_notificationSettings);
 | 
			
		||||
        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++)
 | 
			
		||||
            d.readS32(100 + i, &m_columnIndexes[i], i);
 | 
			
		||||
        for (int i = 0; i < ADSBDEMOD_COLUMNS; i++)
 | 
			
		||||
 | 
			
		||||
@ -130,6 +130,9 @@ struct ADSBDemodSettings
 | 
			
		||||
    QList<NotificationSettings *> m_notificationSettings;
 | 
			
		||||
    QString m_apiKey;                   //!< aviationstack.com API key
 | 
			
		||||
 | 
			
		||||
    QString m_logFilename;
 | 
			
		||||
    bool m_logEnabled;
 | 
			
		||||
 | 
			
		||||
    ADSBDemodSettings();
 | 
			
		||||
    void resetToDefaults();
 | 
			
		||||
    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_feedPort: " << settings.m_feedPort
 | 
			
		||||
            << " m_feedFormat: " << settings.m_feedFormat
 | 
			
		||||
            << " m_logEnabled: " << settings.m_logEnabled
 | 
			
		||||
            << " m_logFilename: " << settings.m_logFilename
 | 
			
		||||
            << " force: " << force;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -185,49 +217,57 @@ char *ADSBDemodWorker::escape(char *p, char c)
 | 
			
		||||
 | 
			
		||||
// Forward ADS-B data in Beast binary format to specified server
 | 
			
		||||
// 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)
 | 
			
		||||
{
 | 
			
		||||
    if (m_settings.m_feedFormat == ADSBDemodSettings::BeastBinary)
 | 
			
		||||
    if (m_logFile.isOpen())
 | 
			
		||||
    {
 | 
			
		||||
        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);
 | 
			
		||||
        m_logStream << dateTime.date().toString() << "," << dateTime.time().toString() << "," << data.toHex() << "," << correlation << "\n";
 | 
			
		||||
    }
 | 
			
		||||
    else if (m_settings.m_feedFormat == ADSBDemodSettings::BeastHex)
 | 
			
		||||
    if (m_settings.m_feedEnabled)
 | 
			
		||||
    {
 | 
			
		||||
        QString beastHex = "*" + data.toHex() + ";\n";
 | 
			
		||||
        send(beastHex.toUtf8(), beastHex.size());
 | 
			
		||||
        if (m_settings.m_feedFormat == ADSBDemodSettings::BeastBinary)
 | 
			
		||||
        {
 | 
			
		||||
            char beastBinary[2+6*2+1*2+14*2];
 | 
			
		||||
            int length;
 | 
			
		||||
            char *p = beastBinary;
 | 
			
		||||
            qint64 timestamp;
 | 
			
		||||
            unsigned char signalStrength;
 | 
			
		||||
 | 
			
		||||
            timestamp = dateTime.toMSecsSinceEpoch();
 | 
			
		||||
 | 
			
		||||
            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 <QTimer>
 | 
			
		||||
#include <QTcpSocket>
 | 
			
		||||
#include <QFile>
 | 
			
		||||
#include <QTextStream>
 | 
			
		||||
 | 
			
		||||
#include "util/message.h"
 | 
			
		||||
#include "util/messagequeue.h"
 | 
			
		||||
@ -71,6 +73,8 @@ private:
 | 
			
		||||
    QMutex m_mutex;
 | 
			
		||||
    QTimer m_heartbeatTimer;
 | 
			
		||||
    QTcpSocket m_socket;
 | 
			
		||||
    QFile m_logFile;
 | 
			
		||||
    QTextStream m_logStream;
 | 
			
		||||
 | 
			
		||||
    bool handleMessage(const Message& cmd);
 | 
			
		||||
    void applySettings(const ADSBDemodSettings& settings, bool force = false);
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,11 @@ struct AirportInformation {
 | 
			
		||||
    float m_elevation;
 | 
			
		||||
    QVector<FrequencyInformation *> m_frequencies;
 | 
			
		||||
 | 
			
		||||
    AirportInformation::~AirportInformation()
 | 
			
		||||
    {
 | 
			
		||||
        qDeleteAll(m_frequencies);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static QString trimQuotes(const QString s)
 | 
			
		||||
    {
 | 
			
		||||
        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].
 | 
			
		||||
 | 
			
		||||
<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>
 | 
			
		||||
 | 
			
		||||
Use this button to refresh the list of devices.
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,10 @@ ADSBDemodSettings:
 | 
			
		||||
      type: string
 | 
			
		||||
    beastPort:
 | 
			
		||||
      type: integer
 | 
			
		||||
    logFilename:
 | 
			
		||||
      type: string
 | 
			
		||||
    logEnabled:
 | 
			
		||||
      type: integer
 | 
			
		||||
    rgbColor:
 | 
			
		||||
      type: integer
 | 
			
		||||
    title:
 | 
			
		||||
 | 
			
		||||
@ -44,6 +44,10 @@ SWGADSBDemodSettings::SWGADSBDemodSettings() {
 | 
			
		||||
    m_beast_host_isSet = false;
 | 
			
		||||
    beast_port = 0;
 | 
			
		||||
    m_beast_port_isSet = false;
 | 
			
		||||
    log_filename = nullptr;
 | 
			
		||||
    m_log_filename_isSet = false;
 | 
			
		||||
    log_enabled = 0;
 | 
			
		||||
    m_log_enabled_isSet = false;
 | 
			
		||||
    rgb_color = 0;
 | 
			
		||||
    m_rgb_color_isSet = false;
 | 
			
		||||
    title = nullptr;
 | 
			
		||||
@ -84,6 +88,10 @@ SWGADSBDemodSettings::init() {
 | 
			
		||||
    m_beast_host_isSet = false;
 | 
			
		||||
    beast_port = 0;
 | 
			
		||||
    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;
 | 
			
		||||
    m_rgb_color_isSet = false;
 | 
			
		||||
    title = new QString("");
 | 
			
		||||
@ -114,6 +122,10 @@ SWGADSBDemodSettings::cleanup() {
 | 
			
		||||
        delete beast_host;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(log_filename != nullptr) { 
 | 
			
		||||
        delete log_filename;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if(title != nullptr) { 
 | 
			
		||||
        delete title;
 | 
			
		||||
@ -155,6 +167,10 @@ SWGADSBDemodSettings::fromJsonObject(QJsonObject &pJson) {
 | 
			
		||||
    
 | 
			
		||||
    ::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(&title, pJson["title"], "QString", "QString");
 | 
			
		||||
@ -211,6 +227,12 @@ SWGADSBDemodSettings::asJsonObject() {
 | 
			
		||||
    if(m_beast_port_isSet){
 | 
			
		||||
        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){
 | 
			
		||||
        obj->insert("rgbColor", QJsonValue(rgb_color));
 | 
			
		||||
    }
 | 
			
		||||
@ -319,6 +341,26 @@ SWGADSBDemodSettings::setBeastPort(qint32 beast_port) {
 | 
			
		||||
    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
 | 
			
		||||
SWGADSBDemodSettings::getRgbColor() {
 | 
			
		||||
    return rgb_color;
 | 
			
		||||
@ -428,6 +470,12 @@ SWGADSBDemodSettings::isSet(){
 | 
			
		||||
        if(m_beast_port_isSet){
 | 
			
		||||
            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){
 | 
			
		||||
            isObjectUpdated = true; break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -66,6 +66,12 @@ public:
 | 
			
		||||
    qint32 getBeastPort();
 | 
			
		||||
    void setBeastPort(qint32 beast_port);
 | 
			
		||||
 | 
			
		||||
    QString* getLogFilename();
 | 
			
		||||
    void setLogFilename(QString* log_filename);
 | 
			
		||||
 | 
			
		||||
    qint32 getLogEnabled();
 | 
			
		||||
    void setLogEnabled(qint32 log_enabled);
 | 
			
		||||
 | 
			
		||||
    qint32 getRgbColor();
 | 
			
		||||
    void setRgbColor(qint32 rgb_color);
 | 
			
		||||
 | 
			
		||||
@ -118,6 +124,12 @@ private:
 | 
			
		||||
    qint32 beast_port;
 | 
			
		||||
    bool m_beast_port_isSet;
 | 
			
		||||
 | 
			
		||||
    QString* log_filename;
 | 
			
		||||
    bool m_log_filename_isSet;
 | 
			
		||||
 | 
			
		||||
    qint32 log_enabled;
 | 
			
		||||
    bool m_log_enabled_isSet;
 | 
			
		||||
 | 
			
		||||
    qint32 rgb_color;
 | 
			
		||||
    bool m_rgb_color_isSet;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user