kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			
		
			
				
	
	
		
			391 wiersze
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			391 wiersze
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
// Copyright (C) 2020 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/>.          //
 | 
						|
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#define BOOST_CHRONO_HEADER_ONLY
 | 
						|
#include <boost/chrono/chrono.hpp>
 | 
						|
 | 
						|
#include <QDebug>
 | 
						|
 | 
						|
#include "util/stepfunctions.h"
 | 
						|
#include "util/db.h"
 | 
						|
#include "dsp/dspengine.h"
 | 
						|
#include "dsp/dspcommands.h"
 | 
						|
#include "device/deviceapi.h"
 | 
						|
 | 
						|
#include "adsbdemodreport.h"
 | 
						|
#include "adsbdemodsink.h"
 | 
						|
#include "adsbdemodsinkworker.h"
 | 
						|
#include "adsbdemodsettings.h"
 | 
						|
#include "adsb.h"
 | 
						|
 | 
						|
MESSAGE_CLASS_DEFINITION(ADSBDemodSinkWorker::MsgConfigureADSBDemodSinkWorker, Message)
 | 
						|
 | 
						|
void ADSBDemodSinkWorker::run()
 | 
						|
{
 | 
						|
    int readBuffer = 0;
 | 
						|
 | 
						|
    // Acquire first buffer
 | 
						|
    m_sink->m_bufferRead[readBuffer].acquire();
 | 
						|
 | 
						|
    // Start recording how much time is spent processing in this method
 | 
						|
    boost::chrono::steady_clock::time_point startPoint = boost::chrono::steady_clock::now();
 | 
						|
 | 
						|
    // Check for updated settings
 | 
						|
    handleInputMessages();
 | 
						|
 | 
						|
    // samplesPerBit is only changed when the thread is stopped
 | 
						|
    int samplesPerBit = m_settings.m_samplesPerBit;
 | 
						|
    int samplesPerFrame = samplesPerBit*(ADS_B_PREAMBLE_BITS+ADS_B_ES_BITS);
 | 
						|
    int samplesPerChip = samplesPerBit/ADS_B_CHIPS_PER_BIT;
 | 
						|
 | 
						|
    qDebug() << "ADSBDemodSinkWorker:: running with"
 | 
						|
         << " samplesPerFrame: " << samplesPerFrame
 | 
						|
         << " samplesPerChip: " << samplesPerChip
 | 
						|
         << " samplesPerBit: " << samplesPerBit
 | 
						|
         << " correlateFullPreamble: " << m_settings.m_correlateFullPreamble
 | 
						|
         << " correlationScale: " << m_correlationScale
 | 
						|
         << " correlationThreshold: " << m_settings.m_correlationThreshold;
 | 
						|
 | 
						|
    int readIdx = m_sink->m_samplesPerFrame - 1;
 | 
						|
 | 
						|
    while (true)
 | 
						|
    {
 | 
						|
        int startIdx = readIdx;
 | 
						|
 | 
						|
        // Correlate received signal with expected preamble
 | 
						|
        // chip+ indexes are 0, 2, 7, 9
 | 
						|
        // correlating over first 6 bits gives a reduction in per-sample
 | 
						|
        // processing, but more than doubles the number of false matches
 | 
						|
        Real preambleCorrelationOnes = 0.0;
 | 
						|
        Real preambleCorrelationZeros = 0.0;
 | 
						|
        if (m_settings.m_correlateFullPreamble)
 | 
						|
        {
 | 
						|
            for (int i = 0; i < samplesPerChip; i++)
 | 
						|
            {
 | 
						|
                preambleCorrelationOnes  += m_sink->m_sampleBuffer[readBuffer][startIdx +  0*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  1*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationOnes  += m_sink->m_sampleBuffer[readBuffer][startIdx +  2*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  3*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  4*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  5*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  6*samplesPerChip + i];
 | 
						|
                preambleCorrelationOnes  += m_sink->m_sampleBuffer[readBuffer][startIdx +  7*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  8*samplesPerChip + i];
 | 
						|
                preambleCorrelationOnes  += m_sink->m_sampleBuffer[readBuffer][startIdx +  9*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 10*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 11*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 12*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 13*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 14*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 15*samplesPerChip + i];
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            for (int i = 0; i < samplesPerChip; i++)
 | 
						|
            {
 | 
						|
                preambleCorrelationOnes  += m_sink->m_sampleBuffer[readBuffer][startIdx +  0*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  1*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationOnes  += m_sink->m_sampleBuffer[readBuffer][startIdx +  2*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  3*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  4*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  5*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  6*samplesPerChip + i];
 | 
						|
                preambleCorrelationOnes  += m_sink->m_sampleBuffer[readBuffer][startIdx +  7*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx +  8*samplesPerChip + i];
 | 
						|
                preambleCorrelationOnes  += m_sink->m_sampleBuffer[readBuffer][startIdx +  9*samplesPerChip + i];
 | 
						|
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 10*samplesPerChip + i];
 | 
						|
                preambleCorrelationZeros += m_sink->m_sampleBuffer[readBuffer][startIdx + 11*samplesPerChip + i];
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // Use the ratio of ones power over zeros power, as we don't care how powerful the signal
 | 
						|
        // is, just whether there is a good correlation with the preamble. The absolute value varies
 | 
						|
        // too much with different radios, AGC settings and and the noise floor is not constant
 | 
						|
        // (E.g: it's quite possible to receive multiple frames simultaneously, so we don't
 | 
						|
        // want a maximum threshold for the zeros, as a weaker signal may transmit 1s in
 | 
						|
        // a stronger signals 0 chip position. Similarly a strong signal in an adjacent
 | 
						|
        // channel may casue AGC to reduce gain, reducing the ampltiude of an otherwise
 | 
						|
        // strong signal, as well as the noise floor)
 | 
						|
        // The threshold accounts for the different number of zeros and ones in the preamble
 | 
						|
        // If the sum of ones is exactly 0, it's probably no signal
 | 
						|
 | 
						|
        Real preambleCorrelation = preambleCorrelationOnes/preambleCorrelationZeros; // without one/zero ratio correction
 | 
						|
 | 
						|
        if ((preambleCorrelation > m_correlationThresholdLinear) && (preambleCorrelationOnes != 0.0f))
 | 
						|
        {
 | 
						|
            int firstIdx = startIdx;
 | 
						|
 | 
						|
            m_demodStats.m_correlatorMatches++;
 | 
						|
            // Skip over preamble
 | 
						|
            startIdx += samplesPerBit*ADS_B_PREAMBLE_BITS;
 | 
						|
 | 
						|
            // Demodulate waveform to bytes
 | 
						|
            unsigned char data[ADS_B_ES_BYTES];
 | 
						|
            int byteIdx = 0;
 | 
						|
            int currentBit;
 | 
						|
            unsigned char currentByte = 0;
 | 
						|
            int df;
 | 
						|
 | 
						|
            for (int bit = 0; bit < ADS_B_ES_BITS; bit++)
 | 
						|
            {
 | 
						|
                // PPM (Pulse position modulation) - Each bit spreads to two chips, 1->10, 0->01
 | 
						|
                // Determine if bit is 1 or 0, by seeing which chip has largest combined energy over the sampling period
 | 
						|
                Real oneSum = 0.0f;
 | 
						|
                Real zeroSum = 0.0f;
 | 
						|
                for (int i = 0; i < samplesPerChip; i++)
 | 
						|
                {
 | 
						|
                    oneSum += m_sink->m_sampleBuffer[readBuffer][startIdx+i];
 | 
						|
                    zeroSum += m_sink->m_sampleBuffer[readBuffer][startIdx+samplesPerChip+i];
 | 
						|
                }
 | 
						|
                currentBit = oneSum > zeroSum;
 | 
						|
                startIdx += samplesPerBit;
 | 
						|
                // Convert bit to bytes - MSB first
 | 
						|
                currentByte |= currentBit << (7-(bit & 0x7));
 | 
						|
                if ((bit & 0x7) == 0x7)
 | 
						|
                {
 | 
						|
                    data[byteIdx++] = currentByte;
 | 
						|
                    currentByte = 0;
 | 
						|
                    // Don't try to demodulate any further, if this isn't an ADS-B frame
 | 
						|
                    // to help reduce processing overhead
 | 
						|
                    if (!m_settings.m_demodModeS && (bit == 7))
 | 
						|
                    {
 | 
						|
                        df = ((data[0] >> 3) & ADS_B_DF_MASK);
 | 
						|
                        if ((df != 17) && (df != 18))
 | 
						|
                            break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Is ADS-B?
 | 
						|
            df = ((data[0] >> 3) & ADS_B_DF_MASK);
 | 
						|
            if ((df == 17) || (df == 18))
 | 
						|
            {
 | 
						|
                m_crc.init();
 | 
						|
                unsigned int parity = (data[11] << 16) | (data[12] << 8) | data[13]; // Parity / CRC
 | 
						|
 | 
						|
                m_crc.calculate(data, ADS_B_ES_BYTES-3);
 | 
						|
                if (parity == m_crc.get())
 | 
						|
                {
 | 
						|
                    // Got a valid frame
 | 
						|
                    m_demodStats.m_adsbFrames++;
 | 
						|
                    // Get 24-bit ICAO and save in hash of ICAOs that have been seen
 | 
						|
                    unsigned icao = ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff);
 | 
						|
                    m_icaos.insert(icao, true);
 | 
						|
                    // Don't try to re-demodulate the same frame
 | 
						|
                    // We could possibly allow a partial overlap here
 | 
						|
                    readIdx += (ADS_B_ES_BITS+ADS_B_PREAMBLE_BITS)*ADS_B_CHIPS_PER_BIT*samplesPerChip - 1;
 | 
						|
                    // Pass to GUI
 | 
						|
                    if (m_sink->getMessageQueueToGUI())
 | 
						|
                    {
 | 
						|
                        ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
 | 
						|
                            QByteArray((char*)data, sizeof(data)),
 | 
						|
                            preambleCorrelation * m_correlationScale,
 | 
						|
                            preambleCorrelationOnes / samplesPerChip,
 | 
						|
                            rxDateTime(firstIdx, readBuffer),
 | 
						|
                            m_crc.get());
 | 
						|
                        m_sink->getMessageQueueToGUI()->push(msg);
 | 
						|
                    }
 | 
						|
                    // Pass to worker to feed to other servers
 | 
						|
                    if (m_sink->getMessageQueueToWorker())
 | 
						|
                    {
 | 
						|
                        ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
 | 
						|
                            QByteArray((char*)data, sizeof(data)),
 | 
						|
                            preambleCorrelation * m_correlationScale,
 | 
						|
                            preambleCorrelationOnes / samplesPerChip,
 | 
						|
                            rxDateTime(firstIdx, readBuffer),
 | 
						|
                            m_crc.get());
 | 
						|
                        m_sink->getMessageQueueToWorker()->push(msg);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                    m_demodStats.m_crcFails++;
 | 
						|
            }
 | 
						|
            else if (m_settings.m_demodModeS)
 | 
						|
            {
 | 
						|
                int bytes;
 | 
						|
 | 
						|
                // Determine number of bytes in frame depending on downlink format
 | 
						|
                if ((df == 0) || (df == 4) || (df == 5) || (df == 11)) {
 | 
						|
                    bytes = 56/8;
 | 
						|
                } else if ((df == 16) || (df == 20) || (df == 21) || (df >= 24)) {
 | 
						|
                    bytes = 112/8;
 | 
						|
                } else {
 | 
						|
                    bytes = 0;
 | 
						|
                }
 | 
						|
                if (bytes > 0)
 | 
						|
                {
 | 
						|
                    // Extract received parity
 | 
						|
                    int parity = (data[bytes-3] << 16) | (data[bytes-2] << 8) | data[bytes-1];
 | 
						|
                    // Calculate CRC on received frame
 | 
						|
                    m_crc.init();
 | 
						|
                    m_crc.calculate(data, bytes-3);
 | 
						|
                    int crc = m_crc.get();
 | 
						|
                    // DF4 / DF5 / DF20 / DF21 have ICAO address XORed in to parity.
 | 
						|
                    // Extract ICAO from parity and see if it matches an aircraft we've already
 | 
						|
                    // received an ADS-B frame from
 | 
						|
                    if ((df == 4) || (df == 5) || (df == 20) || (df == 21))
 | 
						|
                    {
 | 
						|
                        unsigned icao = (parity ^ crc) & 0xffffff;
 | 
						|
                        if (m_icaos.contains(icao)) {
 | 
						|
                            crc ^= icao;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    // For DF11, the last 7 bits may have an address/interogration indentifier (II)
 | 
						|
                    // XORed in, so we ignore those bits
 | 
						|
                    if ((parity == crc) || ((df == 11) && ((parity & 0xffff80) == (crc & 0xffff80))))
 | 
						|
                    {
 | 
						|
                        m_demodStats.m_modesFrames++;
 | 
						|
                        // Pass to GUI (only pass formats it can decode)
 | 
						|
                        if (m_sink->getMessageQueueToGUI() && ((df == 4) || (df == 5) || (df == 20) || (df == 21)))
 | 
						|
                        {
 | 
						|
                            ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
 | 
						|
                                QByteArray((char*)data, sizeof(data)),
 | 
						|
                                preambleCorrelation * m_correlationScale,
 | 
						|
                                preambleCorrelationOnes / samplesPerChip,
 | 
						|
                                rxDateTime(firstIdx, readBuffer),
 | 
						|
                                m_crc.get());
 | 
						|
                            m_sink->getMessageQueueToGUI()->push(msg);
 | 
						|
                        }
 | 
						|
                        // Pass to worker to feed to other servers
 | 
						|
                        if (m_sink->getMessageQueueToWorker())
 | 
						|
                        {
 | 
						|
                            ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
 | 
						|
                                QByteArray((char*)data, sizeof(data)),
 | 
						|
                                preambleCorrelation * m_correlationScale,
 | 
						|
                                preambleCorrelationOnes / samplesPerChip,
 | 
						|
                                rxDateTime(firstIdx, readBuffer),
 | 
						|
                                m_crc.get());
 | 
						|
                            m_sink->getMessageQueueToWorker()->push(msg);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        m_demodStats.m_crcFails++;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                    m_demodStats.m_typeFails++;
 | 
						|
            }
 | 
						|
            else
 | 
						|
                m_demodStats.m_typeFails++;
 | 
						|
        }
 | 
						|
        readIdx++;
 | 
						|
        if (readIdx > m_sink->m_bufferSize - samplesPerFrame)
 | 
						|
        {
 | 
						|
            int nextBuffer = readBuffer+1;
 | 
						|
            if (nextBuffer >= m_sink->m_buffers)
 | 
						|
                nextBuffer = 0;
 | 
						|
 | 
						|
            // Update amount of time spent processing (don't include time spend in acquire)
 | 
						|
            boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - startPoint;
 | 
						|
            m_demodStats.m_demodTime += sec.count();
 | 
						|
            m_demodStats.m_feedTime = m_sink->m_feedTime;
 | 
						|
 | 
						|
            // Send stats to GUI
 | 
						|
            if (m_sink->getMessageQueueToGUI())
 | 
						|
            {
 | 
						|
                ADSBDemodReport::MsgReportDemodStats *msg = ADSBDemodReport::MsgReportDemodStats::create(m_demodStats);
 | 
						|
                m_sink->getMessageQueueToGUI()->push(msg);
 | 
						|
            }
 | 
						|
 | 
						|
            if (!isInterruptionRequested())
 | 
						|
            {
 | 
						|
                // Get next buffer
 | 
						|
                m_sink->m_bufferRead[nextBuffer].acquire();
 | 
						|
 | 
						|
                // Check for updated settings
 | 
						|
                handleInputMessages();
 | 
						|
 | 
						|
                // Resume timing how long we are processing
 | 
						|
                startPoint = boost::chrono::steady_clock::now();
 | 
						|
 | 
						|
                int samplesRemaining = m_sink->m_bufferSize - readIdx;
 | 
						|
                if (samplesRemaining > 0)
 | 
						|
                {
 | 
						|
                    // Copy remaining samples, to start of next buffer
 | 
						|
                    memcpy(&m_sink->m_sampleBuffer[nextBuffer][samplesPerFrame - 1 - samplesRemaining], &m_sink->m_sampleBuffer[readBuffer][readIdx], samplesRemaining*sizeof(Real));
 | 
						|
                    readIdx = samplesPerFrame - 1 - samplesRemaining;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    readIdx = samplesPerFrame - 1;
 | 
						|
                }
 | 
						|
 | 
						|
                m_sink->m_bufferWrite[readBuffer].release();
 | 
						|
 | 
						|
                readBuffer = nextBuffer;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Use a break to avoid testing a condition in the main loop
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
void ADSBDemodSinkWorker::handleInputMessages()
 | 
						|
{
 | 
						|
    Message* message;
 | 
						|
 | 
						|
    while ((message = m_inputMessageQueue.pop()) != nullptr)
 | 
						|
    {
 | 
						|
        if (MsgConfigureADSBDemodSinkWorker::match(*message))
 | 
						|
        {
 | 
						|
            MsgConfigureADSBDemodSinkWorker* cfg = (MsgConfigureADSBDemodSinkWorker*)message;
 | 
						|
 | 
						|
            ADSBDemodSettings settings = cfg->getSettings();
 | 
						|
            bool force = cfg->getForce();
 | 
						|
 | 
						|
            if (settings.m_correlateFullPreamble) {
 | 
						|
                m_correlationScale = 3.0;
 | 
						|
            } else {
 | 
						|
                m_correlationScale = 2.0;
 | 
						|
            }
 | 
						|
 | 
						|
            if ((m_settings.m_correlationThreshold != settings.m_correlationThreshold) || force)
 | 
						|
            {
 | 
						|
                m_correlationThresholdLinear = CalcDb::powerFromdB(settings.m_correlationThreshold);
 | 
						|
                m_correlationThresholdLinear /= m_correlationScale;
 | 
						|
                qDebug() << "m_correlationThresholdLinear: " << m_correlationThresholdLinear;
 | 
						|
            }
 | 
						|
 | 
						|
            m_settings = settings;
 | 
						|
            delete message;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
QDateTime ADSBDemodSinkWorker::rxDateTime(int firstIdx, int readBuffer) const
 | 
						|
{
 | 
						|
    const qint64 samplesPerSecondMSec = ADS_B_BITS_PER_SECOND * m_settings.m_samplesPerBit / 1000;
 | 
						|
    const qint64 offsetMSec = (firstIdx - m_sink->m_samplesPerFrame - 1) / samplesPerSecondMSec;
 | 
						|
    return m_sink->m_bufferFirstSampleDateTime[readBuffer].addMSecs(offsetMSec);
 | 
						|
}
 |