1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-22 08:04:49 -05:00

ADS-B demod: optimization of sync word correlation. Some UI changes. Implements #675

This commit is contained in:
f4exb 2020-10-30 14:27:00 +01:00
parent 66324c9ee4
commit 5cb17be298
10 changed files with 185 additions and 68 deletions

View File

@ -264,7 +264,7 @@ void ADSBDemodGUI::updatePosition(Aircraft *aircraft)
m_adsbDemod->setTarget(aircraft->m_azimuth, aircraft->m_elevation);
}
void ADSBDemodGUI::handleADSB(const QByteArray data, const QDateTime dateTime, float correlation)
void ADSBDemodGUI::handleADSB(const QByteArray data, const QDateTime dateTime, float correlationOnes, float correlationZeros)
{
const char idMap[] = "?ABCDEFGHIJKLMNOPQRSTUVWXYZ????? ???????????????0123456789??????";
const QString categorySetA[] = {
@ -350,12 +350,14 @@ void ADSBDemodGUI::handleADSB(const QByteArray data, const QDateTime dateTime, f
aircraft->m_adsbFrameCount++;
aircraft->m_adsbFrameCountItem->setText(QString("%1").arg(aircraft->m_adsbFrameCount));
if (correlation < aircraft->m_minCorrelation)
aircraft->m_minCorrelation = correlation;
if (correlation > aircraft->m_maxCorrelation)
aircraft->m_maxCorrelation = correlation;
aircraft->m_sumCorrelation += correlation;
aircraft->m_correlationItem->setText(QString("%1/%2/%3").arg(aircraft->m_minCorrelation, 3, 'f', 1).arg(aircraft->m_sumCorrelation / aircraft->m_adsbFrameCount, 3, 'f', 1).arg(aircraft->m_maxCorrelation, 3, 'f', 1));
aircraft->m_minCorrelation = correlationZeros;
if (correlationOnes > aircraft->m_maxCorrelation)
aircraft->m_maxCorrelation = correlationOnes;
aircraft->m_correlation = correlationOnes;
aircraft->m_correlationItem->setText(QString("%1/%2/%3")
.arg(CalcDb::dbPower(aircraft->m_minCorrelation), 3, 'f', 1)
.arg(CalcDb::dbPower(aircraft->m_correlation), 3, 'f', 1)
.arg(CalcDb::dbPower(aircraft->m_maxCorrelation), 3, 'f', 1));
if ((tc >= 1) && ((tc <= 4)))
@ -607,7 +609,10 @@ bool ADSBDemodGUI::handleMessage(const Message& message)
if (ADSBDemodReport::MsgReportADSB::match(message))
{
ADSBDemodReport::MsgReportADSB& report = (ADSBDemodReport::MsgReportADSB&) message;
handleADSB(report.getData(), report.getDateTime(), report.getPreambleCorrelation());
handleADSB(
report.getData(), report.getDateTime(),
report.getPreambleCorrelationOnes(),
report.getPreambleCorrelationZeros());
return true;
}
else if (ADSBDemod::MsgConfigureADSBDemod::match(message))
@ -667,9 +672,9 @@ void ADSBDemodGUI::on_rfBW_valueChanged(int value)
void ADSBDemodGUI::on_threshold_valueChanged(int value)
{
Real threshold = ((Real)value)/10.0f;
ui->thresholdText->setText(QString("%1").arg(threshold, 0, 'f', 1));
m_settings.m_correlationThreshold = threshold;
Real thresholddB = ((Real)value)/10.0f;
ui->thresholdText->setText(QString("%1").arg(thresholddB, 0, 'f', 1));
m_settings.m_correlationThreshold = thresholddB;
applySettings();
}
@ -856,7 +861,7 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
ui->adsbData->resizeColumnsToContents();
resizeTable();
// Get station position
Real stationLatitude = MainCore::instance()->getSettings().getLatitude();
@ -1022,3 +1027,41 @@ void ADSBDemodGUI::tick()
}
}
}
void ADSBDemodGUI::resizeTable()
{
int row = ui->adsbData->rowCount();
ui->adsbData->setRowCount(row + 1);
ui->adsbData->setItem(row, ADSB_COL_ICAO, new QTableWidgetItem("ICAO ID"));
ui->adsbData->setItem(row, ADSB_COL_FLIGHT, new QTableWidgetItem("Flight No"));
ui->adsbData->setItem(row, ADSB_COL_LATITUDE, new QTableWidgetItem("-90.00000 L"));
ui->adsbData->setItem(row, ADSB_COL_LONGITUDE, new QTableWidgetItem("-180.00000 L"));
ui->adsbData->setItem(row, ADSB_COL_ALTITUDE, new QTableWidgetItem("Alt (ft)"));
ui->adsbData->setItem(row, ADSB_COL_SPEED, new QTableWidgetItem("Sp (kn)"));
ui->adsbData->setItem(row, ADSB_COL_HEADING, new QTableWidgetItem("Hd (o)"));
ui->adsbData->setItem(row, ADSB_COL_VERTICALRATE, new QTableWidgetItem("Climb"));
ui->adsbData->setItem(row, ADSB_COL_CATEGORY, new QTableWidgetItem("Category"));
ui->adsbData->setItem(row, ADSB_COL_STATUS, new QTableWidgetItem("No emergency"));
ui->adsbData->setItem(row, ADSB_COL_RANGE, new QTableWidgetItem("D (km)"));
ui->adsbData->setItem(row, ADSB_COL_AZEL, new QTableWidgetItem("Az/El (o)"));
ui->adsbData->setItem(row, ADSB_COL_TIME, new QTableWidgetItem("99:99:99"));
ui->adsbData->setItem(row, ADSB_COL_FRAMECOUNT, new QTableWidgetItem("Frames"));
ui->adsbData->setItem(row, ADSB_COL_CORRELATION, new QTableWidgetItem("-99.9/-99.9/=99.9"));
ui->adsbData->resizeColumnsToContents();
ui->adsbData->removeCellWidget(row, ADSB_COL_ICAO);
ui->adsbData->removeCellWidget(row, ADSB_COL_FLIGHT);
ui->adsbData->removeCellWidget(row, ADSB_COL_LATITUDE);
ui->adsbData->removeCellWidget(row, ADSB_COL_LONGITUDE);
ui->adsbData->removeCellWidget(row, ADSB_COL_ALTITUDE);
ui->adsbData->removeCellWidget(row, ADSB_COL_SPEED);
ui->adsbData->removeCellWidget(row, ADSB_COL_HEADING);
ui->adsbData->removeCellWidget(row, ADSB_COL_VERTICALRATE);
ui->adsbData->removeCellWidget(row, ADSB_COL_CATEGORY);
ui->adsbData->removeCellWidget(row, ADSB_COL_STATUS);
ui->adsbData->removeCellWidget(row, ADSB_COL_RANGE);
ui->adsbData->removeCellWidget(row, ADSB_COL_AZEL);
ui->adsbData->removeCellWidget(row, ADSB_COL_TIME);
ui->adsbData->removeCellWidget(row, ADSB_COL_FRAMECOUNT);
ui->adsbData->removeCellWidget(row, ADSB_COL_CORRELATION);
ui->adsbData->setRowCount(row);
}

View File

@ -79,7 +79,7 @@ struct Aircraft {
int m_adsbFrameCount; // Number of ADS-B frames for this aircraft
float m_minCorrelation;
float m_maxCorrelation;
float m_sumCorrelation;
float m_correlation;
bool m_isBeingTracked; // Are we tracking this aircraft
// GUI table items for above data
@ -117,7 +117,7 @@ struct Aircraft {
m_adsbFrameCount(0),
m_minCorrelation(INFINITY),
m_maxCorrelation(-INFINITY),
m_sumCorrelation(0.0f),
m_correlation(0.0f),
m_isBeingTracked(false)
{
for (int i = 0; i < 2; i++)
@ -247,7 +247,8 @@ private:
void displayStreamIndex();
bool handleMessage(const Message& message);
void updatePosition(Aircraft *aircraft);
void handleADSB(const QByteArray data, const QDateTime dateTime, float correlation);
void handleADSB(const QByteArray data, const QDateTime dateTime, float correlationOnes, float correlationZeros);
void resizeTable();
void leaveEvent(QEvent*);
void enterEvent(QEvent*);

View File

@ -337,16 +337,19 @@
</size>
</property>
<property name="toolTip">
<string>Correlation threshold. Lower values will increase the number of frames that can be received, but require more processing.</string>
<string>Correlation threshold in dB. Lower values will increase the number of frames that can be received, but require more processing.</string>
</property>
<property name="minimum">
<number>-100</number>
<number>-990</number>
</property>
<property name="maximum">
<number>20</number>
<number>0</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>0</number>
<number>-500</number>
</property>
</widget>
</item>
@ -442,6 +445,11 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
</font>
</property>
<property name="windowTitle">
<string>ADS-B Data</string>
</property>
@ -465,8 +473,7 @@
<widget class="QTableWidget" name="adsbData">
<property name="font">
<font>
<family>Segoe UI</family>
<pointsize>9</pointsize>
<family>Liberation Mono</family>
</font>
</property>
<property name="editTriggers">
@ -477,84 +484,120 @@
<string>ICAO ID</string>
</property>
<property name="toolTip">
<string extracomment="Double click to search for the aircraft on www.planespotters.net"/>
<string extracomment="Double click to search for the aircraft on www.planespotters.net">International Civil Aviation Organization identifier. Links to www.planespotters.net</string>
</property>
</column>
<column>
<property name="text">
<string>Flight No.</string>
</property>
<property name="toolTip">
<string>Commercial flight number. Links to www.flightradar24.com</string>
</property>
</column>
<column>
<property name="text">
<string>Latitude (°)</string>
<string>Lat (°)</string>
</property>
<property name="toolTip">
<string extracomment="Double click to centre the map on this aircraft"/>
<string extracomment="Double click to centre the map on this aircraft">Latitude in degrees postive towards the North</string>
</property>
</column>
<column>
<property name="text">
<string>Longitude (°)</string>
<string>Lon (°)</string>
</property>
<property name="toolTip">
<string extracomment="Double click to centre the map on this aircraft"/>
<string extracomment="Double click to centre the map on this aircraft">Longitude in degrees. Positive towards the East</string>
</property>
</column>
<column>
<property name="text">
<string>Altitude (ft)</string>
<string>Alt (ft)</string>
</property>
<property name="toolTip">
<string>Altitude in feet</string>
</property>
</column>
<column>
<property name="text">
<string>Speed (kn)</string>
<string>Sp (kn)</string>
</property>
<property name="toolTip">
<string>Speed in knots</string>
</property>
</column>
<column>
<property name="text">
<string>Heading (°)</string>
<string>Hd (°)</string>
</property>
<property name="toolTip">
<string>Aircraft heading in degrees</string>
</property>
</column>
<column>
<property name="text">
<string>Climb (ft/min)</string>
<string>Climb</string>
</property>
<property name="toolTip">
<string>Climbing rate in feet per minute</string>
</property>
</column>
<column>
<property name="text">
<string>Category</string>
</property>
<property name="toolTip">
<string>Aircraft standard category</string>
</property>
</column>
<column>
<property name="text">
<string>Status</string>
</property>
<property name="toolTip">
<string>Aircraft standard status</string>
</property>
</column>
<column>
<property name="text">
<string>Range (km)</string>
<string>D (km)</string>
</property>
<property name="toolTip">
<string>Range or distance of aircraft to home location</string>
</property>
</column>
<column>
<property name="text">
<string>Az/El (°)</string>
</property>
<property name="toolTip">
<string>Aircraft azimuth and elevation from home point in degrees</string>
</property>
</column>
<column>
<property name="text">
<string>Updated</string>
</property>
<property name="toolTip">
<string>Last time updated</string>
</property>
</column>
<column>
<property name="text">
<string>RX Frames</string>
<string>Frames</string>
</property>
<property name="toolTip">
<string>Number of frames received</string>
</property>
</column>
<column>
<property name="text">
<string>Correlation</string>
</property>
<property name="toolTip">
<string>Correlation power min/avg/max in dB</string>
</property>
</column>
</widget>
</item>

View File

@ -36,22 +36,25 @@ public:
public:
QByteArray getData() const { return m_data; }
QDateTime getDateTime() const { return m_dateTime; }
float getPreambleCorrelation() const { return m_premableCorrelation; }
float getPreambleCorrelationOnes() const { return m_premableCorrelationOnes; }
float getPreambleCorrelationZeros() const { return m_premableCorrelationZeros; }
static MsgReportADSB* create(QByteArray data, float premableCorrelation)
static MsgReportADSB* create(QByteArray data, float premableCorrelationOnes, float premableCorrelationZeros)
{
return new MsgReportADSB(data, premableCorrelation);
return new MsgReportADSB(data, premableCorrelationOnes, premableCorrelationZeros);
}
private:
QByteArray m_data;
QDateTime m_dateTime;
float m_premableCorrelation;
float m_premableCorrelationOnes;
float m_premableCorrelationZeros;
MsgReportADSB(QByteArray data, float premableCorrelation) :
MsgReportADSB(QByteArray data, float premableCorrelationOnes, float premableCorrelationZeros) :
Message(),
m_data(data),
m_premableCorrelation(premableCorrelation)
m_premableCorrelationOnes(premableCorrelationOnes),
m_premableCorrelationZeros(premableCorrelationZeros)
{
m_dateTime = QDateTime::currentDateTime();
}

View File

@ -34,7 +34,7 @@ void ADSBDemodSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_rfBandwidth = 2*1300000;
m_correlationThreshold = 0.0f;
m_correlationThreshold = -50.0f;
m_samplesPerBit = 6;
m_removeTimeout = 60;
m_beastEnabled = false;
@ -102,7 +102,7 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
d.readS32(1, &tmp, 0);
m_inputFrequencyOffset = tmp;
d.readReal(2, &m_rfBandwidth, 2*1300000);
d.readReal(3, &m_correlationThreshold, 1.0f);
d.readReal(3, &m_correlationThreshold, -50.0f);
d.readS32(4, &m_samplesPerBit, 6);
d.readS32(5, &m_removeTimeout, 60);
d.readBool(6, &m_beastEnabled, false);

View File

@ -30,9 +30,9 @@ struct ADSBDemodSettings
{
int32_t m_inputFrequencyOffset;
Real m_rfBandwidth;
Real m_correlationThreshold;
Real m_correlationThreshold; //!< Correlation power threshold in dB
int m_samplesPerBit;
int m_removeTimeout; // Time in seconds before removing an aircraft, unless a new frame is received
int m_removeTimeout; //!< Time in seconds before removing an aircraft, unless a new frame is received
bool m_beastEnabled;
QString m_beastHost;
uint16_t m_beastPort;

View File

@ -42,13 +42,13 @@ ADSBDemodSink::ADSBDemodSink() :
m_sampleIdx(0),
m_sampleCount(0),
m_skipCount(0),
m_correlationThresholdLinear(0.0),
m_magsq(0.0f),
m_magsqSum(0.0f),
m_magsqPeak(0.0f),
m_magsqCount(0),
m_messageQueueToGUI(nullptr),
m_sampleBuffer(nullptr),
m_preamble(nullptr)
m_sampleBuffer(nullptr)
{
applySettings(m_settings, true);
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
@ -57,7 +57,6 @@ ADSBDemodSink::ADSBDemodSink() :
ADSBDemodSink::~ADSBDemodSink()
{
delete m_sampleBuffer;
delete m_preamble;
}
void ADSBDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
@ -108,7 +107,7 @@ void ADSBDemodSink::processOneSample(Complex &ci)
}
m_magsqCount++;
sample = sqrtf(magsq);
sample = magsq;
m_sampleBuffer[m_sampleCount] = sample;
m_sampleCount++;
@ -118,12 +117,37 @@ void ADSBDemodSink::processOneSample(Complex &ci)
int startIdx = m_sampleCount - m_totalSamples;
// Correlate received signal with expected preamble
Real preambleCorrelation = 0.0f;
for (int i = 0; i < ADS_B_PREAMBLE_CHIPS*m_samplesPerChip; i++)
preambleCorrelation += m_preamble[i] * m_sampleBuffer[startIdx+i];
// chip+ indexes are 0, 2, 7, 9
// we correlate only over 6 symbols so that the number of zero chips is twice the
// number of one chips - empirically this is enough to get good correlation
Real premableCorrelationOnes = 0.0;
Real preambleCorrelationZeros = 0.0;
for (int i = 0; i < m_samplesPerChip; i++)
{
premableCorrelationOnes += m_sampleBuffer[startIdx + 0*m_samplesPerChip + i];
preambleCorrelationZeros += m_sampleBuffer[startIdx + 1*m_samplesPerChip + i];
premableCorrelationOnes += m_sampleBuffer[startIdx + 2*m_samplesPerChip + i];
preambleCorrelationZeros += m_sampleBuffer[startIdx + 3*m_samplesPerChip + i];
preambleCorrelationZeros += m_sampleBuffer[startIdx + 4*m_samplesPerChip + i];
preambleCorrelationZeros += m_sampleBuffer[startIdx + 5*m_samplesPerChip + i];
preambleCorrelationZeros += m_sampleBuffer[startIdx + 6*m_samplesPerChip + i];
premableCorrelationOnes += m_sampleBuffer[startIdx + 7*m_samplesPerChip + i];
preambleCorrelationZeros += m_sampleBuffer[startIdx + 8*m_samplesPerChip + i];
premableCorrelationOnes += m_sampleBuffer[startIdx + 9*m_samplesPerChip + i];
preambleCorrelationZeros += m_sampleBuffer[startIdx + 10*m_samplesPerChip + i];
preambleCorrelationZeros += m_sampleBuffer[startIdx + 11*m_samplesPerChip + i];
}
// If the correlation is exactly 0, it's probably no signal
if ((preambleCorrelation > m_settings.m_correlationThreshold) && (preambleCorrelation != 0.0f))
if ((premableCorrelationOnes > m_correlationThresholdLinear) &&
(preambleCorrelationZeros < 2.0*m_correlationThresholdLinear) &&
(premableCorrelationOnes != 0.0f))
{
// Skip over preamble
startIdx += m_settings.m_samplesPerBit*ADS_B_PREAMBLE_BITS;
@ -183,13 +207,19 @@ void ADSBDemodSink::processOneSample(Complex &ci)
// Pass to GUI
if (getMessageQueueToGUI())
{
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(QByteArray((char*)data, sizeof(data)), preambleCorrelation);
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
QByteArray((char*)data, sizeof(data)),
premableCorrelationOnes,
preambleCorrelationZeros/2.0);
getMessageQueueToGUI()->push(msg);
}
// Pass to worker
if (getMessageQueueToWorker())
{
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(QByteArray((char*)data, sizeof(data)), preambleCorrelation);
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
QByteArray((char*)data, sizeof(data)),
premableCorrelationOnes,
preambleCorrelationZeros/2.0);
getMessageQueueToWorker()->push(msg);
}
}
@ -212,23 +242,11 @@ void ADSBDemodSink::init(int samplesPerBit)
{
if (m_sampleBuffer)
delete m_sampleBuffer;
if (m_preamble)
delete m_preamble;
m_totalSamples = samplesPerBit*(ADS_B_PREAMBLE_BITS+ADS_B_ES_BITS);
m_samplesPerChip = samplesPerBit/ADS_B_CHIPS_PER_BIT;
m_sampleBuffer = new Real[2*m_totalSamples];
m_preamble = new Real[ADS_B_PREAMBLE_CHIPS*m_samplesPerChip];
// Possibly don't look for first chip
const int preambleChips[] = {1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1};
for (int i = 0; i < ADS_B_PREAMBLE_CHIPS; i++)
{
for (int j = 0; j < m_samplesPerChip; j++)
{
m_preamble[i*m_samplesPerChip+j] = preambleChips[i];
}
}
}
void ADSBDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
@ -270,10 +288,15 @@ void ADSBDemodSink::applySettings(const ADSBDemodSettings& settings, bool force)
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) (ADS_B_BITS_PER_SECOND * settings.m_samplesPerBit);
}
if ((settings.m_samplesPerBit != m_settings.m_samplesPerBit) || force)
{
init(settings.m_samplesPerBit);
}
if ((settings.m_correlationThreshold != m_settings.m_correlationThreshold) || force) {
m_correlationThresholdLinear = CalcDb::powerFromdB(m_settings.m_correlationThreshold);
}
m_settings = settings;
}

View File

@ -82,10 +82,10 @@ private:
int m_sampleCount;
int m_skipCount; // Samples to skip, because we've already received a frame
Real *m_sampleBuffer;
Real *m_preamble;
int m_totalSamples; // These two values are derived from samplesPerBit
int m_samplesPerChip;
double m_correlationThresholdLinear; //!< settings m_correlationThreshold is in dB. Linear value is calculated once.
double m_magsq; //!< displayed averaged value
double m_magsqSum;

View File

@ -88,7 +88,7 @@ bool ADSBDemodWorker::handleMessage(const Message& message)
else if (ADSBDemodReport::MsgReportADSB::match(message))
{
ADSBDemodReport::MsgReportADSB& report = (ADSBDemodReport::MsgReportADSB&) message;
handleADSB(report.getData(), report.getDateTime(), report.getPreambleCorrelation());
handleADSB(report.getData(), report.getDateTime(), report.getPreambleCorrelationOnes());
return true;
}
else

View File

@ -34,7 +34,11 @@ Higher channel sample rates may help decode more frames, but will require more p
<h3>6: Threshold</h3>
This sets the correlation threshold between the received signal and expected 1090ES preamble, that is required to be exceeded before the demodulator will try to decode a frame. Lower values should decode more frames, but will require more processing power.
This sets the correlation power threshold in dB between the received signal and expected 1090ES preamble, that is required to be exceeded before the demodulator will try to decode a frame. Thus it acts as a kind of squelch. Lower values should decode more frames amd will require more processing power but more often just to process garbage. It is a compromise and setting it correctly is kind of subtle. You may start at ~12 dB over the noise shown in channel power (2). You may also look at correlation values obtained with reliable signals in the "Correlation" column of the data table.
In detail this threshold is used to test two conditions are met:
- the correlation on the "ones" chips is above threshold
- the correlation of the "zeros" chips is below threshold. As zeros chips are two times the ones chips in the correlation (done over 6 first symbols of preamble) the raw value of zeros correlation sum is halved before comparison.
<h3>7: Feed</h3>
@ -59,7 +63,7 @@ The table displays the decoded ADS-B data for each aircraft. The data is not all
* Az/El - The azimuth and elevation angles to the aircraft from the receiving antenna in degrees. These values can be sent to a rotator controller to track the aircraft.
* Updated - The local time at which the last ADS-B message was received.
* RX Frames - A count of the number of ADS-B frames received from this aircraft.
* Correlation - Displays the minimum, average and maximum of the preamable correlation for each recevied frame. These values can be used to help select a threshold setting.
* Correlation - Displays the zeros chip correlation, average of current ones chip corelation and maximum of the preamable correlation power in dB for each recevied frame. These values can be used to help select a threshold setting.
A suffix of L in the latitude and longitude columns, indicates the position is based on a local decode, using a single received frame and the position of the radio station. No suffix will be added for a global decode, which is based upon receving and odd and even frame.
If an ADS-B frame has not been received from an aircraft for 60 seconds, the aircraft is removed from the table and map.