mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-05-24 11:12:27 -04: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
|
aptdemodsettings.cpp
|
||||||
aptdemodbaseband.cpp
|
aptdemodbaseband.cpp
|
||||||
aptdemodsink.cpp
|
aptdemodsink.cpp
|
||||||
|
aptdemodimageworker.cpp
|
||||||
aptdemodplugin.cpp
|
aptdemodplugin.cpp
|
||||||
aptdemodwebapiadapter.cpp
|
aptdemodwebapiadapter.cpp
|
||||||
)
|
)
|
||||||
@ -14,6 +15,7 @@ set(demodapt_HEADERS
|
|||||||
aptdemodsettings.h
|
aptdemodsettings.h
|
||||||
aptdemodbaseband.h
|
aptdemodbaseband.h
|
||||||
aptdemodsink.h
|
aptdemodsink.h
|
||||||
|
aptdemodimageworker.h
|
||||||
aptdemodplugin.h
|
aptdemodplugin.h
|
||||||
aptdemodwebapiadapter.h
|
aptdemodwebapiadapter.h
|
||||||
)
|
)
|
||||||
|
@ -58,9 +58,12 @@ APTDemod::APTDemod(DeviceAPI *deviceAPI) :
|
|||||||
setObjectName(m_channelId);
|
setObjectName(m_channelId);
|
||||||
|
|
||||||
m_basebandSink = new APTDemodBaseband(this);
|
m_basebandSink = new APTDemodBaseband(this);
|
||||||
m_basebandSink->setMessageQueueToChannel(getInputMessageQueue());
|
|
||||||
m_basebandSink->moveToThread(&m_thread);
|
m_basebandSink->moveToThread(&m_thread);
|
||||||
|
|
||||||
|
m_imageWorker = new APTDemodImageWorker();
|
||||||
|
m_basebandSink->setImagWorkerMessageQueue(m_imageWorker->getInputMessageQueue());
|
||||||
|
m_imageWorker->moveToThread(&m_imageThread);
|
||||||
|
|
||||||
applySettings(m_settings, true);
|
applySettings(m_settings, true);
|
||||||
|
|
||||||
m_deviceAPI->addChannelSink(this);
|
m_deviceAPI->addChannelSink(this);
|
||||||
@ -74,7 +77,8 @@ APTDemod::APTDemod(DeviceAPI *deviceAPI) :
|
|||||||
m_image.prow[y] = new float[APT_PROW_WIDTH];
|
m_image.prow[y] = new float[APT_PROW_WIDTH];
|
||||||
m_tempImage.prow[y] = new float[APT_PROW_WIDTH];
|
m_tempImage.prow[y] = new float[APT_PROW_WIDTH];
|
||||||
}
|
}
|
||||||
resetDecoder();
|
|
||||||
|
resetDecoder(); // FIXME: to be removed
|
||||||
}
|
}
|
||||||
|
|
||||||
APTDemod::~APTDemod()
|
APTDemod::~APTDemod()
|
||||||
@ -85,16 +89,22 @@ APTDemod::~APTDemod()
|
|||||||
m_deviceAPI->removeChannelSinkAPI(this);
|
m_deviceAPI->removeChannelSinkAPI(this);
|
||||||
m_deviceAPI->removeChannelSink(this);
|
m_deviceAPI->removeChannelSink(this);
|
||||||
|
|
||||||
|
if (m_imageWorker->isRunning()) {
|
||||||
|
stopImageWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete m_imageWorker;
|
||||||
|
|
||||||
if (m_basebandSink->isRunning()) {
|
if (m_basebandSink->isRunning()) {
|
||||||
stop();
|
stopBasebandSink();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete m_basebandSink;
|
delete m_basebandSink;
|
||||||
|
|
||||||
for (int y = 0; y < APT_MAX_HEIGHT; y++)
|
for (int y = 0; y < APT_MAX_HEIGHT; y++)
|
||||||
{
|
{
|
||||||
delete m_image.prow[y];
|
delete[] m_image.prow[y];
|
||||||
delete m_tempImage.prow[y];
|
delete[] m_tempImage.prow[y];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +120,12 @@ void APTDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APTDemod::start()
|
void APTDemod::start()
|
||||||
|
{
|
||||||
|
startBasebandSink();
|
||||||
|
startImageWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemod::startBasebandSink()
|
||||||
{
|
{
|
||||||
qDebug("APTDemod::start");
|
qDebug("APTDemod::start");
|
||||||
|
|
||||||
@ -124,7 +140,25 @@ void APTDemod::start()
|
|||||||
m_basebandSink->getInputMessageQueue()->push(msg);
|
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()
|
void APTDemod::stop()
|
||||||
|
{
|
||||||
|
stopImageWorker();
|
||||||
|
stopBasebandSink();
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemod::stopBasebandSink()
|
||||||
{
|
{
|
||||||
qDebug("APTDemod::stop");
|
qDebug("APTDemod::stop");
|
||||||
m_basebandSink->stopWork();
|
m_basebandSink->stopWork();
|
||||||
@ -132,6 +166,14 @@ void APTDemod::stop()
|
|||||||
m_thread.wait();
|
m_thread.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APTDemod::stopImageWorker()
|
||||||
|
{
|
||||||
|
qDebug("APTDemod::stopImageWorker");
|
||||||
|
m_imageWorker->stopWork();
|
||||||
|
m_imageThread.quit();
|
||||||
|
m_imageThread.wait();
|
||||||
|
}
|
||||||
|
|
||||||
bool APTDemod::matchSatellite(const QString satelliteName)
|
bool APTDemod::matchSatellite(const QString satelliteName)
|
||||||
{
|
{
|
||||||
return m_settings.m_satelliteTrackerControl
|
return m_settings.m_satelliteTrackerControl
|
||||||
@ -176,7 +218,8 @@ bool APTDemod::handleMessage(const Message& cmd)
|
|||||||
}
|
}
|
||||||
else if (APTDemod::MsgResetDecoder::match(cmd))
|
else if (APTDemod::MsgResetDecoder::match(cmd))
|
||||||
{
|
{
|
||||||
resetDecoder();
|
resetDecoder(); // FIXME: to be removed
|
||||||
|
m_imageWorker->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
||||||
// Forward to sink
|
// Forward to sink
|
||||||
m_basebandSink->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
m_basebandSink->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
||||||
return true;
|
return true;
|
||||||
@ -579,7 +622,8 @@ int APTDemod::webapiActionsPost(
|
|||||||
if (matchSatellite(*satelliteName))
|
if (matchSatellite(*satelliteName))
|
||||||
{
|
{
|
||||||
// Reset for new pass
|
// Reset for new pass
|
||||||
resetDecoder();
|
resetDecoder(); // FIXME: to be removed
|
||||||
|
m_imageWorker->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
||||||
m_basebandSink->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
m_basebandSink->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
||||||
|
|
||||||
// Save satellite name
|
// Save satellite name
|
||||||
|
@ -32,12 +32,14 @@
|
|||||||
#include "util/message.h"
|
#include "util/message.h"
|
||||||
|
|
||||||
#include "aptdemodbaseband.h"
|
#include "aptdemodbaseband.h"
|
||||||
|
#include "aptdemodimageworker.h"
|
||||||
#include "aptdemodsettings.h"
|
#include "aptdemodsettings.h"
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class QThread;
|
class QThread;
|
||||||
class DeviceAPI;
|
class DeviceAPI;
|
||||||
|
class APTDemodImageWorker;
|
||||||
|
|
||||||
class APTDemod : public BasebandSampleSink, public ChannelAPI {
|
class APTDemod : public BasebandSampleSink, public ChannelAPI {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -144,8 +146,18 @@ public:
|
|||||||
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
|
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
|
||||||
virtual void start();
|
virtual void start();
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
|
virtual void startBasebandSink();
|
||||||
|
virtual void stopBasebandSink();
|
||||||
|
virtual void startImageWorker();
|
||||||
|
virtual void stopImageWorker();
|
||||||
virtual bool handleMessage(const Message& cmd);
|
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 void getIdentifier(QString& id) { id = objectName(); }
|
||||||
virtual const QString& getURI() const { return getName(); }
|
virtual const QString& getURI() const { return getName(); }
|
||||||
virtual void getTitle(QString& title) { title = m_settings.m_title; }
|
virtual void getTitle(QString& title) { title = m_settings.m_title; }
|
||||||
@ -202,7 +214,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
DeviceAPI *m_deviceAPI;
|
DeviceAPI *m_deviceAPI;
|
||||||
QThread m_thread;
|
QThread m_thread;
|
||||||
|
QThread m_imageThread;
|
||||||
APTDemodBaseband* m_basebandSink;
|
APTDemodBaseband* m_basebandSink;
|
||||||
|
APTDemodImageWorker *m_imageWorker;
|
||||||
APTDemodSettings m_settings;
|
APTDemodSettings m_settings;
|
||||||
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
|
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
|
||||||
qint64 m_centerFrequency;
|
qint64 m_centerFrequency;
|
||||||
|
@ -68,7 +68,7 @@ public:
|
|||||||
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
|
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
|
||||||
m_sink.getMagSqLevels(avg, peak, 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);
|
void setBasebandSampleRate(int sampleRate);
|
||||||
double getMagSq() const { return m_sink.getMagSq(); }
|
double getMagSq() const { return m_sink.getMagSq(); }
|
||||||
bool isRunning() const { return m_running; }
|
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_magsqSum(0.0f),
|
||||||
m_magsqPeak(0.0f),
|
m_magsqPeak(0.0f),
|
||||||
m_magsqCount(0),
|
m_magsqCount(0),
|
||||||
m_messageQueueToChannel(nullptr),
|
m_imageWorkerMessageQueue(nullptr),
|
||||||
m_samples(nullptr)
|
m_samples(nullptr)
|
||||||
{
|
{
|
||||||
m_magsq = 0.0;
|
m_magsq = 0.0;
|
||||||
@ -124,7 +124,11 @@ void APTDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV
|
|||||||
{
|
{
|
||||||
float pixels[APT_PROW_WIDTH];
|
float pixels[APT_PROW_WIDTH];
|
||||||
apt_getpixelrow(pixels, m_row, &m_zenith, m_row == 0, getsamples, this);
|
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++;
|
m_row++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ public:
|
|||||||
|
|
||||||
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
|
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
|
||||||
void applySettings(const APTDemodSettings& settings, 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; }
|
double getMagSq() const { return m_magsq; }
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ private:
|
|||||||
int m_magsqCount;
|
int m_magsqCount;
|
||||||
MagSqLevelsStore m_magSqLevelStore;
|
MagSqLevelsStore m_magSqLevelStore;
|
||||||
|
|
||||||
MessageQueue *m_messageQueueToChannel;
|
MessageQueue *m_imageWorkerMessageQueue;
|
||||||
|
|
||||||
MovingAverageUtil<Real, double, 16> m_movingAverage;
|
MovingAverageUtil<Real, double, 16> m_movingAverage;
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ private:
|
|||||||
int m_zenith; // Row number of Zenith
|
int m_zenith; // Row number of Zenith
|
||||||
|
|
||||||
void processOneSample(Complex &ci);
|
void processOneSample(Complex &ci);
|
||||||
MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
|
MessageQueue *getImageWorkerMessageQueue() { return m_imageWorkerMessageQueue; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INCLUDE_APTDEMODSINK_H
|
#endif // INCLUDE_APTDEMODSINK_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user