mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-03 21:20:31 -05:00 
			
		
		
		
	APT demod: moved processPixels process to a separate thread
This commit is contained in:
		
							parent
							
								
									de23efe635
								
							
						
					
					
						commit
						aac8f6fe2c
					
				@ -5,6 +5,7 @@ set(demodapt_SOURCES
 | 
			
		||||
    aptdemodsettings.cpp
 | 
			
		||||
    aptdemodbaseband.cpp
 | 
			
		||||
    aptdemodsink.cpp
 | 
			
		||||
    aptdemodimageworker.cpp
 | 
			
		||||
    aptdemodplugin.cpp
 | 
			
		||||
    aptdemodwebapiadapter.cpp
 | 
			
		||||
)
 | 
			
		||||
@ -14,6 +15,7 @@ set(demodapt_HEADERS
 | 
			
		||||
    aptdemodsettings.h
 | 
			
		||||
    aptdemodbaseband.h
 | 
			
		||||
    aptdemodsink.h
 | 
			
		||||
    aptdemodimageworker.h
 | 
			
		||||
    aptdemodplugin.h
 | 
			
		||||
    aptdemodwebapiadapter.h
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -58,9 +58,12 @@ APTDemod::APTDemod(DeviceAPI *deviceAPI) :
 | 
			
		||||
    setObjectName(m_channelId);
 | 
			
		||||
 | 
			
		||||
    m_basebandSink = new APTDemodBaseband(this);
 | 
			
		||||
    m_basebandSink->setMessageQueueToChannel(getInputMessageQueue());
 | 
			
		||||
    m_basebandSink->moveToThread(&m_thread);
 | 
			
		||||
 | 
			
		||||
    m_imageWorker = new APTDemodImageWorker();
 | 
			
		||||
    m_basebandSink->setImagWorkerMessageQueue(m_imageWorker->getInputMessageQueue());
 | 
			
		||||
    m_imageWorker->moveToThread(&m_imageThread);
 | 
			
		||||
 | 
			
		||||
    applySettings(m_settings, true);
 | 
			
		||||
 | 
			
		||||
    m_deviceAPI->addChannelSink(this);
 | 
			
		||||
@ -74,7 +77,8 @@ APTDemod::APTDemod(DeviceAPI *deviceAPI) :
 | 
			
		||||
        m_image.prow[y] = new float[APT_PROW_WIDTH];
 | 
			
		||||
        m_tempImage.prow[y] = new float[APT_PROW_WIDTH];
 | 
			
		||||
    }
 | 
			
		||||
    resetDecoder();
 | 
			
		||||
 | 
			
		||||
    resetDecoder(); // FIXME: to be removed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APTDemod::~APTDemod()
 | 
			
		||||
@ -85,16 +89,22 @@ APTDemod::~APTDemod()
 | 
			
		||||
    m_deviceAPI->removeChannelSinkAPI(this);
 | 
			
		||||
    m_deviceAPI->removeChannelSink(this);
 | 
			
		||||
 | 
			
		||||
    if (m_imageWorker->isRunning()) {
 | 
			
		||||
        stopImageWorker();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    delete m_imageWorker;
 | 
			
		||||
 | 
			
		||||
    if (m_basebandSink->isRunning()) {
 | 
			
		||||
        stop();
 | 
			
		||||
        stopBasebandSink();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    delete m_basebandSink;
 | 
			
		||||
 | 
			
		||||
    for (int y = 0; y < APT_MAX_HEIGHT; y++)
 | 
			
		||||
    {
 | 
			
		||||
        delete m_image.prow[y];
 | 
			
		||||
        delete m_tempImage.prow[y];
 | 
			
		||||
        delete[] m_image.prow[y];
 | 
			
		||||
        delete[] m_tempImage.prow[y];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -110,6 +120,12 @@ void APTDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemod::start()
 | 
			
		||||
{
 | 
			
		||||
    startBasebandSink();
 | 
			
		||||
    startImageWorker();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemod::startBasebandSink()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("APTDemod::start");
 | 
			
		||||
 | 
			
		||||
@ -124,7 +140,25 @@ void APTDemod::start()
 | 
			
		||||
    m_basebandSink->getInputMessageQueue()->push(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemod::startImageWorker()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("APTDemod::startImageWorker");
 | 
			
		||||
 | 
			
		||||
    m_imageWorker->reset();
 | 
			
		||||
    m_imageWorker->startWork();
 | 
			
		||||
    m_imageThread.start();
 | 
			
		||||
 | 
			
		||||
    APTDemodImageWorker::MsgConfigureAPTDemodImageWorker *msg = APTDemodImageWorker::MsgConfigureAPTDemodImageWorker::create(m_settings, true);
 | 
			
		||||
    m_imageWorker->getInputMessageQueue()->push(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemod::stop()
 | 
			
		||||
{
 | 
			
		||||
    stopImageWorker();
 | 
			
		||||
    stopBasebandSink();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemod::stopBasebandSink()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("APTDemod::stop");
 | 
			
		||||
    m_basebandSink->stopWork();
 | 
			
		||||
@ -132,6 +166,14 @@ void APTDemod::stop()
 | 
			
		||||
    m_thread.wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemod::stopImageWorker()
 | 
			
		||||
{
 | 
			
		||||
    qDebug("APTDemod::stopImageWorker");
 | 
			
		||||
    m_imageWorker->stopWork();
 | 
			
		||||
    m_imageThread.quit();
 | 
			
		||||
    m_imageThread.wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool APTDemod::matchSatellite(const QString satelliteName)
 | 
			
		||||
{
 | 
			
		||||
   return    m_settings.m_satelliteTrackerControl
 | 
			
		||||
@ -176,7 +218,8 @@ bool APTDemod::handleMessage(const Message& cmd)
 | 
			
		||||
    }
 | 
			
		||||
    else if (APTDemod::MsgResetDecoder::match(cmd))
 | 
			
		||||
    {
 | 
			
		||||
        resetDecoder();
 | 
			
		||||
        resetDecoder(); // FIXME: to be removed
 | 
			
		||||
        m_imageWorker->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
 | 
			
		||||
        // Forward to sink
 | 
			
		||||
        m_basebandSink->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
 | 
			
		||||
        return true;
 | 
			
		||||
@ -579,7 +622,8 @@ int APTDemod::webapiActionsPost(
 | 
			
		||||
                if (matchSatellite(*satelliteName))
 | 
			
		||||
                {
 | 
			
		||||
                    // Reset for new pass
 | 
			
		||||
                    resetDecoder();
 | 
			
		||||
                    resetDecoder(); // FIXME: to be removed
 | 
			
		||||
                    m_imageWorker->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
 | 
			
		||||
                    m_basebandSink->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
 | 
			
		||||
 | 
			
		||||
                    // Save satellite name
 | 
			
		||||
 | 
			
		||||
@ -32,12 +32,14 @@
 | 
			
		||||
#include "util/message.h"
 | 
			
		||||
 | 
			
		||||
#include "aptdemodbaseband.h"
 | 
			
		||||
#include "aptdemodimageworker.h"
 | 
			
		||||
#include "aptdemodsettings.h"
 | 
			
		||||
 | 
			
		||||
class QNetworkAccessManager;
 | 
			
		||||
class QNetworkReply;
 | 
			
		||||
class QThread;
 | 
			
		||||
class DeviceAPI;
 | 
			
		||||
class APTDemodImageWorker;
 | 
			
		||||
 | 
			
		||||
class APTDemod : public BasebandSampleSink, public ChannelAPI {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
@ -144,8 +146,18 @@ public:
 | 
			
		||||
    virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
 | 
			
		||||
    virtual void start();
 | 
			
		||||
    virtual void stop();
 | 
			
		||||
    virtual void startBasebandSink();
 | 
			
		||||
    virtual void stopBasebandSink();
 | 
			
		||||
    virtual void startImageWorker();
 | 
			
		||||
    virtual void stopImageWorker();
 | 
			
		||||
    virtual bool handleMessage(const Message& cmd);
 | 
			
		||||
 | 
			
		||||
    void setMessageQueueToGUI(MessageQueue* queue) override
 | 
			
		||||
    {
 | 
			
		||||
        ChannelAPI::setMessageQueueToGUI(queue);
 | 
			
		||||
        m_imageWorker->setMessageQueueToGUI(queue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual void getIdentifier(QString& id) { id = objectName(); }
 | 
			
		||||
    virtual const QString& getURI() const { return getName(); }
 | 
			
		||||
    virtual void getTitle(QString& title) { title = m_settings.m_title; }
 | 
			
		||||
@ -202,7 +214,9 @@ public:
 | 
			
		||||
private:
 | 
			
		||||
    DeviceAPI *m_deviceAPI;
 | 
			
		||||
    QThread m_thread;
 | 
			
		||||
    QThread m_imageThread;
 | 
			
		||||
    APTDemodBaseband* m_basebandSink;
 | 
			
		||||
    APTDemodImageWorker *m_imageWorker;
 | 
			
		||||
    APTDemodSettings m_settings;
 | 
			
		||||
    int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
 | 
			
		||||
    qint64 m_centerFrequency;
 | 
			
		||||
 | 
			
		||||
@ -68,7 +68,7 @@ public:
 | 
			
		||||
    void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
 | 
			
		||||
        m_sink.getMagSqLevels(avg, peak, nbSamples);
 | 
			
		||||
    }
 | 
			
		||||
    void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); }
 | 
			
		||||
    void setImagWorkerMessageQueue(MessageQueue *messageQueue) { m_sink.setImageWorkerMessageQueue(messageQueue); }
 | 
			
		||||
    void setBasebandSampleRate(int sampleRate);
 | 
			
		||||
    double getMagSq() const { return m_sink.getMagSq(); }
 | 
			
		||||
    bool isRunning() const { return m_running; }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										279
									
								
								plugins/channelrx/demodapt/aptdemodimageworker.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								plugins/channelrx/demodapt/aptdemodimageworker.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,279 @@
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB.                             //
 | 
			
		||||
// Copyright (C) 2021 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/>.          //
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
#include "aptdemod.h"
 | 
			
		||||
#include "aptdemodimageworker.h"
 | 
			
		||||
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(APTDemodImageWorker::MsgConfigureAPTDemodImageWorker, Message)
 | 
			
		||||
 | 
			
		||||
APTDemodImageWorker::APTDemodImageWorker() :
 | 
			
		||||
    m_messageQueueToGUI(nullptr),
 | 
			
		||||
    m_running(false),
 | 
			
		||||
    m_mutex(QMutex::Recursive)
 | 
			
		||||
{
 | 
			
		||||
    for (int y = 0; y < APT_MAX_HEIGHT; y++)
 | 
			
		||||
    {
 | 
			
		||||
        m_image.prow[y] = new float[APT_PROW_WIDTH];
 | 
			
		||||
        m_tempImage.prow[y] = new float[APT_PROW_WIDTH];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resetDecoder();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APTDemodImageWorker::~APTDemodImageWorker()
 | 
			
		||||
{
 | 
			
		||||
    m_inputMessageQueue.clear();
 | 
			
		||||
 | 
			
		||||
    for (int y = 0; y < APT_MAX_HEIGHT; y++)
 | 
			
		||||
    {
 | 
			
		||||
        delete[] m_image.prow[y];
 | 
			
		||||
        delete[] m_tempImage.prow[y];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemodImageWorker::reset()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
    m_inputMessageQueue.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemodImageWorker::startWork()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
    connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
			
		||||
    m_running = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemodImageWorker::stopWork()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
    disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
			
		||||
    m_running = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemodImageWorker::handleInputMessages()
 | 
			
		||||
{
 | 
			
		||||
    Message* message;
 | 
			
		||||
 | 
			
		||||
    while ((message = m_inputMessageQueue.pop()) != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (handleMessage(*message)) {
 | 
			
		||||
            delete message;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool APTDemodImageWorker::handleMessage(const Message& cmd)
 | 
			
		||||
{
 | 
			
		||||
    if (MsgConfigureAPTDemodImageWorker::match(cmd))
 | 
			
		||||
    {
 | 
			
		||||
        QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
        MsgConfigureAPTDemodImageWorker& cfg = (MsgConfigureAPTDemodImageWorker&) cmd;
 | 
			
		||||
        qDebug("APTDemodImageWorker::handleMessage: MsgConfigureAPTDemodImageWorker");
 | 
			
		||||
        applySettings(cfg.getSettings(), cfg.getForce());
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (APTDemod::MsgPixels::match(cmd))
 | 
			
		||||
    {
 | 
			
		||||
        QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
        const APTDemod::MsgPixels& pixelsMsg = (APTDemod::MsgPixels&) cmd;
 | 
			
		||||
        const float *pixels = pixelsMsg.getPixels();
 | 
			
		||||
        processPixels(pixels);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (APTDemod::MsgResetDecoder::match(cmd))
 | 
			
		||||
    {
 | 
			
		||||
        resetDecoder();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemodImageWorker::applySettings(const APTDemodSettings& settings, bool force)
 | 
			
		||||
{
 | 
			
		||||
    (void) force;
 | 
			
		||||
    m_settings = settings;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemodImageWorker::resetDecoder()
 | 
			
		||||
{
 | 
			
		||||
    m_image.nrow = 0;
 | 
			
		||||
    m_tempImage.nrow = 0;
 | 
			
		||||
    m_greyImage = QImage(APT_IMG_WIDTH, APT_MAX_HEIGHT, QImage::Format_Grayscale8);
 | 
			
		||||
    m_greyImage.fill(0);
 | 
			
		||||
    m_colourImage = QImage(APT_IMG_WIDTH, APT_MAX_HEIGHT, QImage::Format_RGB888);
 | 
			
		||||
    m_colourImage.fill(0);
 | 
			
		||||
    m_satelliteName = "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemodImageWorker::processPixels(const float *pixels)
 | 
			
		||||
{
 | 
			
		||||
    std::copy(pixels, pixels + APT_PROW_WIDTH, m_image.prow[m_image.nrow]);
 | 
			
		||||
    m_image.nrow++;
 | 
			
		||||
    sendImageToGUI();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemodImageWorker::sendImageToGUI()
 | 
			
		||||
{
 | 
			
		||||
    // Send image to GUI
 | 
			
		||||
    if (m_messageQueueToGUI)
 | 
			
		||||
    {
 | 
			
		||||
        QStringList imageTypes;
 | 
			
		||||
        QImage image = processImage(imageTypes);
 | 
			
		||||
        m_messageQueueToGUI->push(APTDemod::MsgImage::create(image, imageTypes, m_satelliteName));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QImage APTDemodImageWorker::processImage(QStringList& imageTypes)
 | 
			
		||||
{
 | 
			
		||||
    copyImage(&m_tempImage, &m_image);
 | 
			
		||||
 | 
			
		||||
    // Calibrate channels according to wavelength
 | 
			
		||||
    if (m_tempImage.nrow >= APT_CALIBRATION_ROWS)
 | 
			
		||||
    {
 | 
			
		||||
        m_tempImage.chA = apt_calibrate(m_tempImage.prow, m_tempImage.nrow, APT_CHA_OFFSET, APT_CH_WIDTH);
 | 
			
		||||
        m_tempImage.chB = apt_calibrate(m_tempImage.prow, m_tempImage.nrow, APT_CHB_OFFSET, APT_CH_WIDTH);
 | 
			
		||||
        QStringList channelTypes({
 | 
			
		||||
            "",  // Unknown
 | 
			
		||||
            "Visible (0.58-0.68 um)",
 | 
			
		||||
            "Near-IR (0.725-1.0 um)",
 | 
			
		||||
            "Near-IR (1.58-1.64 um)",
 | 
			
		||||
            "Mid-infrared (3.55-3.93 um)",
 | 
			
		||||
            "Thermal-infrared (10.3-11.3 um)",
 | 
			
		||||
            "Thermal-infrared (11.5-12.5 um)"
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        imageTypes.append(channelTypes[m_tempImage.chA]);
 | 
			
		||||
        imageTypes.append(channelTypes[m_tempImage.chB]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Crop noise due to low elevation at top and bottom of image
 | 
			
		||||
    if (m_settings.m_cropNoise)
 | 
			
		||||
        m_tempImage.zenith -= apt_cropNoise(&m_tempImage);
 | 
			
		||||
 | 
			
		||||
    // Denoise filter
 | 
			
		||||
    if (m_settings.m_denoise)
 | 
			
		||||
    {
 | 
			
		||||
        apt_denoise(m_tempImage.prow, m_tempImage.nrow, APT_CHA_OFFSET, APT_CH_WIDTH);
 | 
			
		||||
        apt_denoise(m_tempImage.prow, m_tempImage.nrow, APT_CHB_OFFSET, APT_CH_WIDTH);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Flip image if satellite pass is North to South
 | 
			
		||||
    if (m_settings.m_flip)
 | 
			
		||||
    {
 | 
			
		||||
        apt_flipImage(&m_tempImage, APT_CH_WIDTH, APT_CHA_OFFSET);
 | 
			
		||||
        apt_flipImage(&m_tempImage, APT_CH_WIDTH, APT_CHB_OFFSET);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Linear equalise to improve contrast
 | 
			
		||||
    if (m_settings.m_linearEqualise)
 | 
			
		||||
    {
 | 
			
		||||
        apt_linearEnhance(m_tempImage.prow, m_tempImage.nrow, APT_CHA_OFFSET, APT_CH_WIDTH);
 | 
			
		||||
        apt_linearEnhance(m_tempImage.prow, m_tempImage.nrow, APT_CHB_OFFSET, APT_CH_WIDTH);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Histogram equalise to improve contrast
 | 
			
		||||
    if (m_settings.m_histogramEqualise)
 | 
			
		||||
    {
 | 
			
		||||
        apt_histogramEqualise(m_tempImage.prow, m_tempImage.nrow, APT_CHA_OFFSET, APT_CH_WIDTH);
 | 
			
		||||
        apt_histogramEqualise(m_tempImage.prow, m_tempImage.nrow, APT_CHB_OFFSET, APT_CH_WIDTH);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (m_settings.m_precipitationOverlay)
 | 
			
		||||
    {
 | 
			
		||||
        // Overlay precipitation
 | 
			
		||||
        for (int r = 0; r < m_tempImage.nrow; r++)
 | 
			
		||||
        {
 | 
			
		||||
            uchar *l = m_colourImage.scanLine(r);
 | 
			
		||||
            for (int i = 0; i < APT_IMG_WIDTH; i++)
 | 
			
		||||
            {
 | 
			
		||||
                float p = m_tempImage.prow[r][i];
 | 
			
		||||
 | 
			
		||||
                if ((i >= APT_CHB_OFFSET) && (i < APT_CHB_OFFSET + APT_CH_WIDTH) && (p >= 198))
 | 
			
		||||
                {
 | 
			
		||||
                    apt_rgb_t rgb = apt_applyPalette(apt_PrecipPalette, p - 198);
 | 
			
		||||
                    // Negative float values get converted to positive uchars here
 | 
			
		||||
                    l[i*3] = (uchar)rgb.r;
 | 
			
		||||
                    l[i*3+1] = (uchar)rgb.g;
 | 
			
		||||
                    l[i*3+2] = (uchar)rgb.b;
 | 
			
		||||
                    int a = i - APT_CHB_OFFSET + APT_CHA_OFFSET;
 | 
			
		||||
                    l[a*3] = (uchar)rgb.r;
 | 
			
		||||
                    l[a*3+1] = (uchar)rgb.g;
 | 
			
		||||
                    l[a*3+2] = (uchar)rgb.b;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    uchar q = roundAndClip(p);
 | 
			
		||||
                    l[i*3] = q;
 | 
			
		||||
                    l[i*3+1] = q;
 | 
			
		||||
                    l[i*3+2] = q;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return extractImage(m_colourImage);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        for (int r = 0; r < m_tempImage.nrow; r++)
 | 
			
		||||
        {
 | 
			
		||||
            uchar *l = m_greyImage.scanLine(r);
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < APT_IMG_WIDTH; i++)
 | 
			
		||||
            {
 | 
			
		||||
                float p = m_tempImage.prow[r][i];
 | 
			
		||||
                l[i] = roundAndClip(p);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return extractImage(m_greyImage);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QImage APTDemodImageWorker::extractImage(QImage image)
 | 
			
		||||
{
 | 
			
		||||
    if (m_settings.m_channels == APTDemodSettings::BOTH_CHANNELS) {
 | 
			
		||||
        return image.copy(0, 0, APT_IMG_WIDTH, m_tempImage.nrow);
 | 
			
		||||
    } else if (m_settings.m_channels == APTDemodSettings::CHANNEL_A) {
 | 
			
		||||
        return image.copy(APT_CHA_OFFSET, 0, APT_CH_WIDTH, m_tempImage.nrow);
 | 
			
		||||
    } else {
 | 
			
		||||
        return image.copy(APT_CHB_OFFSET, 0, APT_CH_WIDTH, m_tempImage.nrow);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APTDemodImageWorker::copyImage(apt_image_t *dst, apt_image_t *src)
 | 
			
		||||
{
 | 
			
		||||
    dst->nrow = src->nrow;
 | 
			
		||||
    dst->zenith = src->zenith;
 | 
			
		||||
    dst->chA = src->chA;
 | 
			
		||||
    dst->chB = src->chB;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < src->nrow; i++) {
 | 
			
		||||
        std::copy(src->prow[i], src->prow[i] + APT_PROW_WIDTH, dst->prow[i]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uchar APTDemodImageWorker::roundAndClip(float p)
 | 
			
		||||
{
 | 
			
		||||
    int q = (int) round(p);
 | 
			
		||||
    q = q > 255 ? 255 : q < 0 ? 0 : q;
 | 
			
		||||
    return q;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										100
									
								
								plugins/channelrx/demodapt/aptdemodimageworker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								plugins/channelrx/demodapt/aptdemodimageworker.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,100 @@
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB.                             //
 | 
			
		||||
// Copyright (C) 2021 Jon Beniston, M7RCE                                        //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// This program is free software; you can redistribute it and/or modify          //
 | 
			
		||||
// it under the terms of the GNU General Public License as published by          //
 | 
			
		||||
// the Free Software Foundation as version 3 of the License, or                  //
 | 
			
		||||
// (at your option) any later version.                                           //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// This program is distributed in the hope that it will be useful,               //
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
			
		||||
// GNU General Public License V3 for more details.                               //
 | 
			
		||||
//                                                                               //
 | 
			
		||||
// You should have received a copy of the GNU General Public License             //
 | 
			
		||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
#ifndef INCLUDE_APTDEMODIMAGEWORKER_H
 | 
			
		||||
#define INCLUDE_APTDEMODIMAGEWORKER_H
 | 
			
		||||
 | 
			
		||||
#include <QObject>
 | 
			
		||||
#include <QMutex>
 | 
			
		||||
#include <QImage>
 | 
			
		||||
 | 
			
		||||
#include <apt.h>
 | 
			
		||||
 | 
			
		||||
#include "util/messagequeue.h"
 | 
			
		||||
#include "util/message.h"
 | 
			
		||||
 | 
			
		||||
#include "aptdemodsettings.h"
 | 
			
		||||
 | 
			
		||||
class APTDemodImageWorker : public QObject
 | 
			
		||||
{
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
public:
 | 
			
		||||
    class MsgConfigureAPTDemodImageWorker : public Message {
 | 
			
		||||
        MESSAGE_CLASS_DECLARATION
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        const APTDemodSettings& getSettings() const { return m_settings; }
 | 
			
		||||
        bool getForce() const { return m_force; }
 | 
			
		||||
 | 
			
		||||
        static MsgConfigureAPTDemodImageWorker* create(const APTDemodSettings& settings, bool force)
 | 
			
		||||
        {
 | 
			
		||||
            return new MsgConfigureAPTDemodImageWorker(settings, force);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        APTDemodSettings m_settings;
 | 
			
		||||
        bool m_force;
 | 
			
		||||
 | 
			
		||||
        MsgConfigureAPTDemodImageWorker(const APTDemodSettings& settings, bool force) :
 | 
			
		||||
            Message(),
 | 
			
		||||
            m_settings(settings),
 | 
			
		||||
            m_force(force)
 | 
			
		||||
        { }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    APTDemodImageWorker();
 | 
			
		||||
    ~APTDemodImageWorker();
 | 
			
		||||
    void reset();
 | 
			
		||||
    void startWork();
 | 
			
		||||
    void stopWork();
 | 
			
		||||
    bool isRunning() const { return m_running; }
 | 
			
		||||
 | 
			
		||||
    MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
 | 
			
		||||
    void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
 | 
			
		||||
    MessageQueue *m_messageQueueToGUI;
 | 
			
		||||
    APTDemodSettings m_settings;
 | 
			
		||||
 | 
			
		||||
    // Image buffers
 | 
			
		||||
    apt_image_t m_image;                // Received image
 | 
			
		||||
    apt_image_t m_tempImage;            // Processed image
 | 
			
		||||
    QImage m_greyImage;
 | 
			
		||||
    QImage m_colourImage;
 | 
			
		||||
    QString m_satelliteName;
 | 
			
		||||
 | 
			
		||||
    bool m_running;
 | 
			
		||||
    QMutex m_mutex;
 | 
			
		||||
 | 
			
		||||
    bool handleMessage(const Message& cmd);
 | 
			
		||||
    void applySettings(const APTDemodSettings& settings, bool force = false);
 | 
			
		||||
    void resetDecoder();
 | 
			
		||||
    void processPixels(const float *pixels);
 | 
			
		||||
    void sendImageToGUI();
 | 
			
		||||
    QImage processImage(QStringList& imageTypes);
 | 
			
		||||
    QImage extractImage(QImage image);
 | 
			
		||||
 | 
			
		||||
    static void copyImage(apt_image_t *dst, apt_image_t *src);
 | 
			
		||||
    static uchar roundAndClip(float p);
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
    void handleInputMessages();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // INCLUDE_APTDEMODIMAGEWORKER_H
 | 
			
		||||
@ -37,7 +37,7 @@ APTDemodSink::APTDemodSink(APTDemod *packetDemod) :
 | 
			
		||||
        m_magsqSum(0.0f),
 | 
			
		||||
        m_magsqPeak(0.0f),
 | 
			
		||||
        m_magsqCount(0),
 | 
			
		||||
        m_messageQueueToChannel(nullptr),
 | 
			
		||||
        m_imageWorkerMessageQueue(nullptr),
 | 
			
		||||
        m_samples(nullptr)
 | 
			
		||||
{
 | 
			
		||||
    m_magsq = 0.0;
 | 
			
		||||
@ -124,7 +124,11 @@ void APTDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV
 | 
			
		||||
    {
 | 
			
		||||
        float pixels[APT_PROW_WIDTH];
 | 
			
		||||
        apt_getpixelrow(pixels, m_row, &m_zenith, m_row == 0, getsamples, this);
 | 
			
		||||
        getMessageQueueToChannel()->push(APTDemod::MsgPixels::create(pixels, m_zenith));
 | 
			
		||||
 | 
			
		||||
        if (getImageWorkerMessageQueue()) {
 | 
			
		||||
            getImageWorkerMessageQueue()->push(APTDemod::MsgPixels::create(pixels, m_zenith));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        m_row++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
 | 
			
		||||
    void applySettings(const APTDemodSettings& settings, bool force = false);
 | 
			
		||||
    void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; }
 | 
			
		||||
    void setImageWorkerMessageQueue(MessageQueue *messageQueue) { m_imageWorkerMessageQueue = messageQueue; }
 | 
			
		||||
 | 
			
		||||
    double getMagSq() const { return m_magsq; }
 | 
			
		||||
 | 
			
		||||
@ -103,7 +103,7 @@ private:
 | 
			
		||||
    int  m_magsqCount;
 | 
			
		||||
    MagSqLevelsStore m_magSqLevelStore;
 | 
			
		||||
 | 
			
		||||
    MessageQueue *m_messageQueueToChannel;
 | 
			
		||||
    MessageQueue *m_imageWorkerMessageQueue;
 | 
			
		||||
 | 
			
		||||
    MovingAverageUtil<Real, double, 16> m_movingAverage;
 | 
			
		||||
 | 
			
		||||
@ -120,7 +120,7 @@ private:
 | 
			
		||||
    int m_zenith;       // Row number of Zenith
 | 
			
		||||
 | 
			
		||||
    void processOneSample(Complex &ci);
 | 
			
		||||
    MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
 | 
			
		||||
    MessageQueue *getImageWorkerMessageQueue() { return m_imageWorkerMessageQueue; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // INCLUDE_APTDEMODSINK_H
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user