kopia lustrzana https://github.com/f4exb/sdrangel
APT Demod updates.
Add projection of image on to 3D map. Add support for temperature map. Add support for colour palettes for image enhancements. Fix IR channel names.pull/1127/head
rodzic
26b8619bb1
commit
7b6708a256
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 961 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 944 KiB |
|
@ -23,6 +23,7 @@ set(demodapt_HEADERS
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||||
${APT_INCLUDE_DIR}
|
${APT_INCLUDE_DIR}
|
||||||
|
${SGP4_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT SERVER_MODE)
|
if(NOT SERVER_MODE)
|
||||||
|
@ -32,12 +33,15 @@ if(NOT SERVER_MODE)
|
||||||
aptdemodgui.ui
|
aptdemodgui.ui
|
||||||
aptdemodsettingsdialog.cpp
|
aptdemodsettingsdialog.cpp
|
||||||
aptdemodsettingsdialog.ui
|
aptdemodsettingsdialog.ui
|
||||||
|
aptdemodselectdialog.cpp
|
||||||
|
aptdemodselectdialog.ui
|
||||||
icons.qrc
|
icons.qrc
|
||||||
)
|
)
|
||||||
set(demodapt_HEADERS
|
set(demodapt_HEADERS
|
||||||
${demodapt_HEADERS}
|
${demodapt_HEADERS}
|
||||||
aptdemodgui.h
|
aptdemodgui.h
|
||||||
aptdemodsettingsdialog.h
|
aptdemodsettingsdialog.h
|
||||||
|
aptdemodselectdialog.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TARGET_NAME demodapt)
|
set(TARGET_NAME demodapt)
|
||||||
|
@ -59,12 +63,22 @@ if(APT_EXTERNAL)
|
||||||
add_dependencies(${TARGET_NAME} apt)
|
add_dependencies(${TARGET_NAME} apt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(SGP4_EXTERNAL)
|
||||||
|
add_dependencies(${TARGET_NAME} sgp4)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${TARGET_NAME}
|
target_link_libraries(${TARGET_NAME}
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
${TARGET_LIB}
|
${TARGET_LIB}
|
||||||
sdrbase
|
sdrbase
|
||||||
${TARGET_LIB_GUI}
|
${TARGET_LIB_GUI}
|
||||||
${APT_LIBRARIES}
|
${APT_LIBRARIES}
|
||||||
|
${SGP4_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||||
|
|
||||||
|
# Install debug symbols
|
||||||
|
if (WIN32)
|
||||||
|
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
|
||||||
|
endif()
|
||||||
|
|
|
@ -47,6 +47,7 @@ MESSAGE_CLASS_DEFINITION(APTDemod::MsgConfigureAPTDemod, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(APTDemod::MsgPixels, Message)
|
MESSAGE_CLASS_DEFINITION(APTDemod::MsgPixels, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(APTDemod::MsgImage, Message)
|
MESSAGE_CLASS_DEFINITION(APTDemod::MsgImage, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(APTDemod::MsgLine, Message)
|
MESSAGE_CLASS_DEFINITION(APTDemod::MsgLine, Message)
|
||||||
|
MESSAGE_CLASS_DEFINITION(APTDemod::MsgMapImageName, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(APTDemod::MsgResetDecoder, Message)
|
MESSAGE_CLASS_DEFINITION(APTDemod::MsgResetDecoder, Message)
|
||||||
|
|
||||||
const char * const APTDemod::m_channelIdURI = "sdrangel.channel.aptdemod";
|
const char * const APTDemod::m_channelIdURI = "sdrangel.channel.aptdemod";
|
||||||
|
@ -62,7 +63,7 @@ APTDemod::APTDemod(DeviceAPI *deviceAPI) :
|
||||||
m_basebandSink = new APTDemodBaseband(this);
|
m_basebandSink = new APTDemodBaseband(this);
|
||||||
m_basebandSink->moveToThread(&m_thread);
|
m_basebandSink->moveToThread(&m_thread);
|
||||||
|
|
||||||
m_imageWorker = new APTDemodImageWorker();
|
m_imageWorker = new APTDemodImageWorker(this);
|
||||||
m_basebandSink->setImagWorkerMessageQueue(m_imageWorker->getInputMessageQueue());
|
m_basebandSink->setImagWorkerMessageQueue(m_imageWorker->getInputMessageQueue());
|
||||||
m_imageWorker->moveToThread(&m_imageThread);
|
m_imageWorker->moveToThread(&m_imageThread);
|
||||||
|
|
||||||
|
@ -286,6 +287,42 @@ void APTDemod::applySettings(const APTDemodSettings& settings, bool force)
|
||||||
if ((settings.m_autoSaveMinScanLines != m_settings.m_autoSaveMinScanLines) || force) {
|
if ((settings.m_autoSaveMinScanLines != m_settings.m_autoSaveMinScanLines) || force) {
|
||||||
reverseAPIKeys.append("autoSaveMinScanLines");
|
reverseAPIKeys.append("autoSaveMinScanLines");
|
||||||
}
|
}
|
||||||
|
if ((settings.m_saveCombined != m_settings.m_saveCombined) || force) {
|
||||||
|
reverseAPIKeys.append("saveCombined");
|
||||||
|
}
|
||||||
|
if ((settings.m_saveSeparate != m_settings.m_saveSeparate) || force) {
|
||||||
|
reverseAPIKeys.append("saveSeparate");
|
||||||
|
}
|
||||||
|
if ((settings.m_saveProjection != m_settings.m_saveProjection) || force) {
|
||||||
|
reverseAPIKeys.append("saveProjection");
|
||||||
|
}
|
||||||
|
if ((settings.m_scanlinesPerImageUpdate != m_settings.m_scanlinesPerImageUpdate) || force) {
|
||||||
|
reverseAPIKeys.append("scanlinesPerImageUpdate");
|
||||||
|
}
|
||||||
|
if ((settings.m_transparencyThreshold != m_settings.m_transparencyThreshold) || force) {
|
||||||
|
reverseAPIKeys.append("transparencyThreshold");
|
||||||
|
}
|
||||||
|
if ((settings.m_opacityThreshold != m_settings.m_opacityThreshold) || force) {
|
||||||
|
reverseAPIKeys.append("opacityThreshold");
|
||||||
|
}
|
||||||
|
if ((settings.m_palettes != m_settings.m_palettes) || force) {
|
||||||
|
reverseAPIKeys.append("palettes");
|
||||||
|
}
|
||||||
|
if ((settings.m_palette != m_settings.m_palette) || force) {
|
||||||
|
reverseAPIKeys.append("palette");
|
||||||
|
}
|
||||||
|
if ((settings.m_horizontalPixelsPerDegree != m_settings.m_horizontalPixelsPerDegree) || force) {
|
||||||
|
reverseAPIKeys.append("horizontalPixelsPerDegree");
|
||||||
|
}
|
||||||
|
if ((settings.m_verticalPixelsPerDegree != m_settings.m_verticalPixelsPerDegree) || force) {
|
||||||
|
reverseAPIKeys.append("verticalPixelsPerDegree");
|
||||||
|
}
|
||||||
|
if ((settings.m_satTimeOffset != m_settings.m_satTimeOffset) || force) {
|
||||||
|
reverseAPIKeys.append("satTimeOffset");
|
||||||
|
}
|
||||||
|
if ((settings.m_satYaw != m_settings.m_satYaw) || force) {
|
||||||
|
reverseAPIKeys.append("satYaw");
|
||||||
|
}
|
||||||
|
|
||||||
if (m_settings.m_streamIndex != settings.m_streamIndex)
|
if (m_settings.m_streamIndex != settings.m_streamIndex)
|
||||||
{
|
{
|
||||||
|
@ -426,6 +463,42 @@ void APTDemod::webapiUpdateChannelSettings(
|
||||||
if (channelSettingsKeys.contains("autoSaveMinScanLines")) {
|
if (channelSettingsKeys.contains("autoSaveMinScanLines")) {
|
||||||
settings.m_autoSaveMinScanLines = response.getAptDemodSettings()->getAutoSaveMinScanLines();
|
settings.m_autoSaveMinScanLines = response.getAptDemodSettings()->getAutoSaveMinScanLines();
|
||||||
}
|
}
|
||||||
|
if (channelSettingsKeys.contains("saveCombined")) {
|
||||||
|
settings.m_saveCombined = response.getAptDemodSettings()->getSaveCombined();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("saveSeparate")) {
|
||||||
|
settings.m_saveSeparate = response.getAptDemodSettings()->getSaveSeparate();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("saveProjection")) {
|
||||||
|
settings.m_saveProjection = response.getAptDemodSettings()->getSaveProjection();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("scanlinesPerImageUpdate")) {
|
||||||
|
settings.m_scanlinesPerImageUpdate = response.getAptDemodSettings()->getScanlinesPerImageUpdate();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("transparencyThreshold")) {
|
||||||
|
settings.m_transparencyThreshold = response.getAptDemodSettings()->getTransparencyThreshold();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("m_opacityThreshold")) {
|
||||||
|
settings.m_opacityThreshold = response.getAptDemodSettings()->getOpacityThreshold();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("palettes")) {
|
||||||
|
settings.m_palettes = (*response.getAptDemodSettings()->getPalettes()).split(";");
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("palette")) {
|
||||||
|
settings.m_palette = response.getAptDemodSettings()->getPalette();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("horizontalPixelsPerDegree")) {
|
||||||
|
settings.m_horizontalPixelsPerDegree = response.getAptDemodSettings()->getHorizontalPixelsPerDegree();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("verticalPixelsPerDegree")) {
|
||||||
|
settings.m_verticalPixelsPerDegree = response.getAptDemodSettings()->getVerticalPixelsPerDegree();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("satTimeOffset")) {
|
||||||
|
settings.m_satTimeOffset = response.getAptDemodSettings()->getSatTimeOffset();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("satYaw")) {
|
||||||
|
settings.m_satYaw = response.getAptDemodSettings()->getSatYaw();
|
||||||
|
}
|
||||||
if (channelSettingsKeys.contains("rgbColor")) {
|
if (channelSettingsKeys.contains("rgbColor")) {
|
||||||
settings.m_rgbColor = response.getAptDemodSettings()->getRgbColor();
|
settings.m_rgbColor = response.getAptDemodSettings()->getRgbColor();
|
||||||
}
|
}
|
||||||
|
@ -474,6 +547,18 @@ void APTDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp
|
||||||
response.getAptDemodSettings()->setAutoSave(settings.m_autoSave);
|
response.getAptDemodSettings()->setAutoSave(settings.m_autoSave);
|
||||||
response.getAptDemodSettings()->setAutoSavePath(new QString(settings.m_autoSavePath));
|
response.getAptDemodSettings()->setAutoSavePath(new QString(settings.m_autoSavePath));
|
||||||
response.getAptDemodSettings()->setAutoSaveMinScanLines(settings.m_autoSaveMinScanLines);
|
response.getAptDemodSettings()->setAutoSaveMinScanLines(settings.m_autoSaveMinScanLines);
|
||||||
|
response.getAptDemodSettings()->setSaveCombined(settings.m_saveCombined);
|
||||||
|
response.getAptDemodSettings()->setSaveSeparate(settings.m_saveSeparate);
|
||||||
|
response.getAptDemodSettings()->setSaveProjection(settings.m_saveProjection);
|
||||||
|
response.getAptDemodSettings()->setScanlinesPerImageUpdate(settings.m_scanlinesPerImageUpdate);
|
||||||
|
response.getAptDemodSettings()->setTransparencyThreshold(settings.m_transparencyThreshold);
|
||||||
|
response.getAptDemodSettings()->setOpacityThreshold(settings.m_opacityThreshold);
|
||||||
|
response.getAptDemodSettings()->setPalettes(new QString(settings.m_palettes.join(";")));
|
||||||
|
response.getAptDemodSettings()->setPalette(settings.m_palette);
|
||||||
|
response.getAptDemodSettings()->setHorizontalPixelsPerDegree(settings.m_horizontalPixelsPerDegree);
|
||||||
|
response.getAptDemodSettings()->setVerticalPixelsPerDegree(settings.m_verticalPixelsPerDegree);
|
||||||
|
response.getAptDemodSettings()->setSatTimeOffset(settings.m_satTimeOffset);
|
||||||
|
response.getAptDemodSettings()->setSatYaw(settings.m_satYaw);
|
||||||
|
|
||||||
response.getAptDemodSettings()->setRgbColor(settings.m_rgbColor);
|
response.getAptDemodSettings()->setRgbColor(settings.m_rgbColor);
|
||||||
|
|
||||||
|
@ -599,15 +684,51 @@ void APTDemod::webapiFormatChannelSettings(
|
||||||
if (channelSettingsKeys.contains("decodeEnabled") || force) {
|
if (channelSettingsKeys.contains("decodeEnabled") || force) {
|
||||||
swgAPTDemodSettings->setDecodeEnabled(settings.m_decodeEnabled);
|
swgAPTDemodSettings->setDecodeEnabled(settings.m_decodeEnabled);
|
||||||
}
|
}
|
||||||
if (channelSettingsKeys.contains("m_autoSave") || force) {
|
if (channelSettingsKeys.contains("autoSave") || force) {
|
||||||
swgAPTDemodSettings->setAutoSave(settings.m_autoSave);
|
swgAPTDemodSettings->setAutoSave(settings.m_autoSave);
|
||||||
}
|
}
|
||||||
if (channelSettingsKeys.contains("m_autoSavePath") || force) {
|
if (channelSettingsKeys.contains("autoSavePath") || force) {
|
||||||
swgAPTDemodSettings->setAutoSavePath(new QString(settings.m_autoSavePath));
|
swgAPTDemodSettings->setAutoSavePath(new QString(settings.m_autoSavePath));
|
||||||
}
|
}
|
||||||
if (channelSettingsKeys.contains("m_autoSaveMinScanLines") || force) {
|
if (channelSettingsKeys.contains("autoSaveMinScanLines") || force) {
|
||||||
swgAPTDemodSettings->setAutoSaveMinScanLines(settings.m_autoSaveMinScanLines);
|
swgAPTDemodSettings->setAutoSaveMinScanLines(settings.m_autoSaveMinScanLines);
|
||||||
}
|
}
|
||||||
|
if (channelSettingsKeys.contains("saveCombined") || force) {
|
||||||
|
swgAPTDemodSettings->setSaveCombined(settings.m_saveCombined);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("saveSeparate") || force) {
|
||||||
|
swgAPTDemodSettings->setSaveSeparate(settings.m_saveSeparate);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("saveProjection") || force) {
|
||||||
|
swgAPTDemodSettings->setSaveProjection(settings.m_saveProjection);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("scanlinesPerImageUpdate") || force) {
|
||||||
|
swgAPTDemodSettings->setScanlinesPerImageUpdate(settings.m_scanlinesPerImageUpdate);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("transparencyThreshold") || force) {
|
||||||
|
swgAPTDemodSettings->setTransparencyThreshold(settings.m_transparencyThreshold);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("opacityThreshold") || force) {
|
||||||
|
swgAPTDemodSettings->setOpacityThreshold(settings.m_opacityThreshold);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("palettes") || force) {
|
||||||
|
swgAPTDemodSettings->setPalettes(new QString(settings.m_palettes.join(";")));
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("palette") || force) {
|
||||||
|
swgAPTDemodSettings->setPalette(settings.m_palette);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("horizontalPixelsPerDegree") || force) {
|
||||||
|
swgAPTDemodSettings->setHorizontalPixelsPerDegree(settings.m_horizontalPixelsPerDegree);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("verticalPixelsPerDegree") || force) {
|
||||||
|
swgAPTDemodSettings->setVerticalPixelsPerDegree(settings.m_verticalPixelsPerDegree);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("satTimeOffset") || force) {
|
||||||
|
swgAPTDemodSettings->setSatTimeOffset(settings.m_satTimeOffset);
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("satYaw") || force) {
|
||||||
|
swgAPTDemodSettings->setSatYaw(settings.m_satYaw);
|
||||||
|
}
|
||||||
if (channelSettingsKeys.contains("rgbColor") || force) {
|
if (channelSettingsKeys.contains("rgbColor") || force) {
|
||||||
swgAPTDemodSettings->setRgbColor(settings.m_rgbColor);
|
swgAPTDemodSettings->setRgbColor(settings.m_rgbColor);
|
||||||
}
|
}
|
||||||
|
@ -654,6 +775,9 @@ int APTDemod::webapiActionsPost(
|
||||||
// Reset for new pass
|
// Reset for new pass
|
||||||
m_imageWorker->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
m_imageWorker->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
||||||
m_basebandSink->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
m_basebandSink->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
||||||
|
if (m_guiMessageQueue) {
|
||||||
|
m_guiMessageQueue->push(APTDemod::MsgResetDecoder::create());
|
||||||
|
}
|
||||||
|
|
||||||
// Save satellite name
|
// Save satellite name
|
||||||
m_imageWorker->getInputMessageQueue()->push(APTDemodImageWorker::MsgSetSatelliteName::create(*satelliteName));
|
m_imageWorker->getInputMessageQueue()->push(APTDemodImageWorker::MsgSetSatelliteName::create(*satelliteName));
|
||||||
|
@ -662,10 +786,14 @@ int APTDemod::webapiActionsPost(
|
||||||
APTDemodSettings settings = m_settings;
|
APTDemodSettings settings = m_settings;
|
||||||
settings.m_decodeEnabled = true;
|
settings.m_decodeEnabled = true;
|
||||||
settings.m_flip = !aos->getNorthToSouthPass();
|
settings.m_flip = !aos->getNorthToSouthPass();
|
||||||
|
settings.m_tle = *aos->getTle();
|
||||||
|
settings.m_aosDateTime = QDateTime::fromString(*aos->getDateTime(), Qt::ISODateWithMs);
|
||||||
|
settings.m_northToSouth = aos->getNorthToSouthPass();
|
||||||
|
|
||||||
m_inputMessageQueue.push(MsgConfigureAPTDemod::create(settings, false));
|
m_inputMessageQueue.push(MsgConfigureAPTDemod::create(settings, false));
|
||||||
if (m_guiMessageQueue)
|
if (m_guiMessageQueue) {
|
||||||
m_guiMessageQueue->push(MsgConfigureAPTDemod::create(settings, false));
|
m_guiMessageQueue->push(MsgConfigureAPTDemod::create(settings, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 202;
|
return 202;
|
||||||
|
|
|
@ -146,6 +146,28 @@ public:
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sent from worker to GUI to indicate name of image on Map
|
||||||
|
class MsgMapImageName : public Message {
|
||||||
|
MESSAGE_CLASS_DECLARATION
|
||||||
|
|
||||||
|
public:
|
||||||
|
QString getName() const { return m_name; }
|
||||||
|
|
||||||
|
static MsgMapImageName* create(const QString &name)
|
||||||
|
{
|
||||||
|
return new MsgMapImageName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_name;
|
||||||
|
|
||||||
|
MsgMapImageName(const QString &name) :
|
||||||
|
Message(),
|
||||||
|
m_name(name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Sent from GUI to reset decoder
|
// Sent from GUI to reset decoder
|
||||||
class MsgResetDecoder : public Message {
|
class MsgResetDecoder : public Message {
|
||||||
MESSAGE_CLASS_DECLARATION
|
MESSAGE_CLASS_DECLARATION
|
||||||
|
|
|
@ -25,8 +25,10 @@
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QGraphicsScene>
|
#include <QGraphicsScene>
|
||||||
#include <QGraphicsPixmapItem>
|
#include <QGraphicsPixmapItem>
|
||||||
|
#include <QGraphicsSceneMouseEvent>
|
||||||
|
|
||||||
#include "aptdemodgui.h"
|
#include "aptdemodgui.h"
|
||||||
|
|
||||||
|
@ -50,6 +52,45 @@
|
||||||
#include "aptdemod.h"
|
#include "aptdemod.h"
|
||||||
#include "aptdemodsink.h"
|
#include "aptdemodsink.h"
|
||||||
#include "aptdemodsettingsdialog.h"
|
#include "aptdemodsettingsdialog.h"
|
||||||
|
#include "aptdemodselectdialog.h"
|
||||||
|
|
||||||
|
#include "SWGMapItem.h"
|
||||||
|
|
||||||
|
TempScale::TempScale(QGraphicsItem *parent) :
|
||||||
|
QGraphicsRectItem(parent)
|
||||||
|
{
|
||||||
|
// Temp scale appears to be -100 to +60C
|
||||||
|
// We just draw -100 to +50C, so it's nicely divides up according to the palette
|
||||||
|
setRect(30, 30, 25, 240);
|
||||||
|
m_gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
|
||||||
|
m_gradient.setStart(0.0, 0.0);
|
||||||
|
m_gradient.setFinalStop(0.0, 1.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 240; i++)
|
||||||
|
{
|
||||||
|
int idx = (240 - i) * 3;
|
||||||
|
QColor color((unsigned char)apt_TempPalette[idx], (unsigned char)apt_TempPalette[idx+1], (unsigned char)apt_TempPalette[idx+2]);
|
||||||
|
m_gradient.setColorAt(i/240.0, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TempScale::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||||
|
{
|
||||||
|
Q_UNUSED(option);
|
||||||
|
Q_UNUSED(widget);
|
||||||
|
|
||||||
|
int left = rect().left() + rect().width() + 10;
|
||||||
|
painter->setPen(QPen(Qt::black));
|
||||||
|
painter->setBrush(m_gradient);
|
||||||
|
painter->drawRect(rect());
|
||||||
|
painter->drawText(left, rect().top(), "50C");
|
||||||
|
painter->drawText(left, rect().top() + rect().height() * 1 / 6, "25C");
|
||||||
|
painter->drawText(left, rect().top() + rect().height() * 2 / 6, "0C");
|
||||||
|
painter->drawText(left, rect().top() + rect().height() * 3 / 6, "-25C");
|
||||||
|
painter->drawText(left, rect().top() + rect().height() * 4 / 6, "-50C");
|
||||||
|
painter->drawText(left, rect().top() + rect().height() * 5 / 6, "-75C");
|
||||||
|
painter->drawText(left, rect().top() + rect().height(), "-100C");
|
||||||
|
}
|
||||||
|
|
||||||
APTDemodGUI* APTDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
|
APTDemodGUI* APTDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
|
||||||
{
|
{
|
||||||
|
@ -76,7 +117,8 @@ QByteArray APTDemodGUI::serialize() const
|
||||||
|
|
||||||
bool APTDemodGUI::deserialize(const QByteArray& data)
|
bool APTDemodGUI::deserialize(const QByteArray& data)
|
||||||
{
|
{
|
||||||
if(m_settings.deserialize(data)) {
|
if(m_settings.deserialize(data))
|
||||||
|
{
|
||||||
displaySettings();
|
displaySettings();
|
||||||
applySettings(true);
|
applySettings(true);
|
||||||
return true;
|
return true;
|
||||||
|
@ -103,19 +145,29 @@ bool APTDemodGUI::handleMessage(const Message& message)
|
||||||
{
|
{
|
||||||
const APTDemod::MsgImage& imageMsg = (APTDemod::MsgImage&) message;
|
const APTDemod::MsgImage& imageMsg = (APTDemod::MsgImage&) message;
|
||||||
m_image = imageMsg.getImage();
|
m_image = imageMsg.getImage();
|
||||||
m_pixmap.convertFromImage(m_image);
|
// Display can be corrupted if we try to drawn an image with 0 height
|
||||||
if (m_pixmapItem != nullptr)
|
if (m_image.height() > 0)
|
||||||
{
|
{
|
||||||
m_pixmapItem->setPixmap(m_pixmap);
|
m_pixmap.convertFromImage(m_image);
|
||||||
if (ui->zoomAll->isChecked()) {
|
if (m_pixmapItem != nullptr)
|
||||||
|
{
|
||||||
|
m_pixmapItem->setPixmap(m_pixmap);
|
||||||
|
if (ui->zoomAll->isChecked()) {
|
||||||
|
ui->image->fitInView(m_pixmapItem, Qt::KeepAspectRatio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_pixmapItem = m_scene->addPixmap(m_pixmap);
|
||||||
|
m_pixmapItem->setPos(0, 0);
|
||||||
ui->image->fitInView(m_pixmapItem, Qt::KeepAspectRatio);
|
ui->image->fitInView(m_pixmapItem, Qt::KeepAspectRatio);
|
||||||
}
|
}
|
||||||
}
|
bool temp = m_settings.m_channels == APTDemodSettings::TEMPERATURE;
|
||||||
else
|
m_tempScale->setVisible(temp);
|
||||||
{
|
m_tempScaleBG->setVisible(temp);
|
||||||
m_pixmapItem = m_scene->addPixmap(m_pixmap);
|
if (!temp) {
|
||||||
m_pixmapItem->setPos(0, 0);
|
m_tempText->setVisible(false);
|
||||||
ui->image->fitInView(m_pixmapItem, Qt::KeepAspectRatio);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList imageTypes = imageMsg.getImageTypes();
|
QStringList imageTypes = imageMsg.getImageTypes();
|
||||||
|
@ -126,20 +178,24 @@ bool APTDemodGUI::handleMessage(const Message& message)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (imageTypes[0].isEmpty())
|
if (imageTypes[0].isEmpty()) {
|
||||||
ui->channelALabel->setText("Channel A");
|
ui->channelALabel->setText("Channel A");
|
||||||
else
|
} else {
|
||||||
ui->channelALabel->setText(imageTypes[0]);
|
ui->channelALabel->setText(imageTypes[0]);
|
||||||
if (imageTypes[1].isEmpty())
|
}
|
||||||
|
if (imageTypes[1].isEmpty()) {
|
||||||
ui->channelBLabel->setText("Channel B");
|
ui->channelBLabel->setText("Channel B");
|
||||||
else
|
} else {
|
||||||
ui->channelBLabel->setText(imageTypes[1]);
|
ui->channelBLabel->setText(imageTypes[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
QString satelliteName = imageMsg.getSatelliteName();
|
QString satelliteName = imageMsg.getSatelliteName();
|
||||||
if (!satelliteName.isEmpty())
|
if (!satelliteName.isEmpty()) {
|
||||||
ui->imageContainer->setWindowTitle("Received image from " + satelliteName);
|
ui->imageContainer->setWindowTitle("Received image from " + satelliteName);
|
||||||
else
|
} else {
|
||||||
ui->imageContainer->setWindowTitle("Received image");
|
ui->imageContainer->setWindowTitle("Received image");
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (APTDemod::MsgLine::match(message))
|
else if (APTDemod::MsgLine::match(message))
|
||||||
|
@ -203,6 +259,18 @@ bool APTDemodGUI::handleMessage(const Message& message)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (APTDemod::MsgMapImageName::match(message))
|
||||||
|
{
|
||||||
|
const APTDemod::MsgMapImageName& mapNameMsg = (APTDemod::MsgMapImageName&) message;
|
||||||
|
QString name = mapNameMsg.getName();
|
||||||
|
if (!m_mapImages.contains(name)) {
|
||||||
|
m_mapImages.append(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (APTDemod::MsgResetDecoder::match(message))
|
||||||
|
{
|
||||||
|
resetDecoder();
|
||||||
|
}
|
||||||
else if (DSPSignalNotification::match(message))
|
else if (DSPSignalNotification::match(message))
|
||||||
{
|
{
|
||||||
DSPSignalNotification& notif = (DSPSignalNotification&) message;
|
DSPSignalNotification& notif = (DSPSignalNotification&) message;
|
||||||
|
@ -261,24 +329,88 @@ void APTDemodGUI::on_fmDev_valueChanged(int value)
|
||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APTDemodGUI::on_channels_currentIndexChanged(int index)
|
void APTDemodGUI::displayLabels()
|
||||||
{
|
{
|
||||||
m_settings.m_channels = (APTDemodSettings::ChannelSelection)index;
|
|
||||||
if (m_settings.m_channels == APTDemodSettings::BOTH_CHANNELS)
|
if (m_settings.m_channels == APTDemodSettings::BOTH_CHANNELS)
|
||||||
{
|
{
|
||||||
ui->channelALabel->setVisible(true);
|
ui->channelALabel->setVisible(true);
|
||||||
ui->channelBLabel->setVisible(true);
|
ui->channelBLabel->setVisible(true);
|
||||||
|
ui->precipitation->setVisible(true);
|
||||||
}
|
}
|
||||||
else if (m_settings.m_channels == APTDemodSettings::CHANNEL_A)
|
else if (m_settings.m_channels == APTDemodSettings::CHANNEL_A)
|
||||||
{
|
{
|
||||||
ui->channelALabel->setVisible(true);
|
ui->channelALabel->setVisible(true);
|
||||||
ui->channelBLabel->setVisible(false);
|
ui->channelBLabel->setVisible(false);
|
||||||
|
ui->precipitation->setVisible(true);
|
||||||
|
}
|
||||||
|
else if (m_settings.m_channels == APTDemodSettings::CHANNEL_B)
|
||||||
|
{
|
||||||
|
ui->channelALabel->setVisible(false);
|
||||||
|
ui->channelBLabel->setVisible(true);
|
||||||
|
ui->precipitation->setVisible(true);
|
||||||
|
}
|
||||||
|
else if (m_settings.m_channels == APTDemodSettings::TEMPERATURE)
|
||||||
|
{
|
||||||
|
ui->channelALabel->setVisible(false);
|
||||||
|
ui->channelBLabel->setVisible(false);
|
||||||
|
ui->precipitation->setVisible(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ui->channelALabel->setVisible(false);
|
ui->channelALabel->setVisible(false);
|
||||||
ui->channelBLabel->setVisible(true);
|
ui->channelBLabel->setVisible(false);
|
||||||
|
ui->precipitation->setVisible(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodGUI::on_channels_currentIndexChanged(int index)
|
||||||
|
{
|
||||||
|
if (index <= (int)APTDemodSettings::CHANNEL_B)
|
||||||
|
{
|
||||||
|
m_settings.m_channels = (APTDemodSettings::ChannelSelection)index;
|
||||||
|
}
|
||||||
|
else if (index == (int)APTDemodSettings::TEMPERATURE)
|
||||||
|
{
|
||||||
|
m_settings.m_channels = APTDemodSettings::TEMPERATURE;
|
||||||
|
m_settings.m_precipitationOverlay = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_settings.m_channels = APTDemodSettings::PALETTE;
|
||||||
|
m_settings.m_palette = index - (int)APTDemodSettings::PALETTE;
|
||||||
|
m_settings.m_precipitationOverlay = false;
|
||||||
|
}
|
||||||
|
displayLabels();
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodGUI::on_transparencyThreshold_valueChanged(int value)
|
||||||
|
{
|
||||||
|
m_settings.m_transparencyThreshold = value;
|
||||||
|
ui->transparencyThresholdText->setText(QString::number(m_settings.m_transparencyThreshold));
|
||||||
|
// Don't applySettings while tracking, as processing an image takes a long time
|
||||||
|
if (!ui->transparencyThreshold->isSliderDown()) {
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodGUI::on_transparencyThreshold_sliderReleased()
|
||||||
|
{
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodGUI::on_opacityThreshold_valueChanged(int value)
|
||||||
|
{
|
||||||
|
m_settings.m_opacityThreshold = value;
|
||||||
|
ui->opacityThresholdText->setText(QString::number(m_settings.m_opacityThreshold));
|
||||||
|
// Don't applySettings while tracking, as processing an image takes a long time
|
||||||
|
if (!ui->opacityThreshold->isSliderDown()) {
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodGUI::on_opacityThreshold_sliderReleased()
|
||||||
|
{
|
||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,10 +447,11 @@ void APTDemodGUI::on_precipitation_clicked(bool checked)
|
||||||
void APTDemodGUI::on_flip_clicked(bool checked)
|
void APTDemodGUI::on_flip_clicked(bool checked)
|
||||||
{
|
{
|
||||||
m_settings.m_flip = checked;
|
m_settings.m_flip = checked;
|
||||||
if (m_settings.m_flip)
|
if (m_settings.m_flip) {
|
||||||
ui->image->setAlignment(Qt::AlignBottom | Qt::AlignHCenter);
|
ui->image->setAlignment(Qt::AlignBottom | Qt::AlignHCenter);
|
||||||
else
|
} else {
|
||||||
ui->image->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
|
ui->image->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
|
||||||
|
}
|
||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,13 +461,22 @@ void APTDemodGUI::on_startStop_clicked(bool checked)
|
||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APTDemodGUI::on_resetDecoder_clicked()
|
void APTDemodGUI::resetDecoder()
|
||||||
{
|
{
|
||||||
if (m_pixmapItem != nullptr) {
|
if (m_pixmapItem != nullptr)
|
||||||
|
{
|
||||||
|
m_image = QImage();
|
||||||
m_pixmapItem->setPixmap(QPixmap());
|
m_pixmapItem->setPixmap(QPixmap());
|
||||||
}
|
}
|
||||||
ui->imageContainer->setWindowTitle("Received image");
|
ui->imageContainer->setWindowTitle("Received image");
|
||||||
// Send message to reset decoder
|
ui->channelALabel->setText("Channel A");
|
||||||
|
ui->channelBLabel->setText("Channel B");
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodGUI::on_resetDecoder_clicked()
|
||||||
|
{
|
||||||
|
resetDecoder();
|
||||||
|
// Send message to reset decoder to other parts of demod
|
||||||
m_aptDemod->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
m_aptDemod->getInputMessageQueue()->push(APTDemod::MsgResetDecoder::create());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +484,10 @@ void APTDemodGUI::on_showSettings_clicked()
|
||||||
{
|
{
|
||||||
APTDemodSettingsDialog dialog(&m_settings);
|
APTDemodSettingsDialog dialog(&m_settings);
|
||||||
if (dialog.exec() == QDialog::Accepted)
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
|
{
|
||||||
|
displayPalettes();
|
||||||
applySettings();
|
applySettings();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save image to disk
|
// Save image to disk
|
||||||
|
@ -356,8 +501,9 @@ void APTDemodGUI::on_saveImage_clicked()
|
||||||
if (fileNames.size() > 0)
|
if (fileNames.size() > 0)
|
||||||
{
|
{
|
||||||
qDebug() << "APT: Saving image to " << fileNames;
|
qDebug() << "APT: Saving image to " << fileNames;
|
||||||
if (!m_image.save(fileNames[0]))
|
if (!m_image.save(fileNames[0])) {
|
||||||
QMessageBox::critical(this, "APT Demodulator", QString("Failed to save image to %1").arg(fileNames[0]));
|
QMessageBox::critical(this, "APT Demodulator", QString("Failed to save image to %1").arg(fileNames[0]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -489,10 +635,28 @@ APTDemodGUI::APTDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban
|
||||||
m_zoom = new GraphicsViewZoom(ui->image); // Deleted automatically when view is deleted
|
m_zoom = new GraphicsViewZoom(ui->image); // Deleted automatically when view is deleted
|
||||||
connect(m_zoom, SIGNAL(zoomed()), this, SLOT(on_image_zoomed()));
|
connect(m_zoom, SIGNAL(zoomed()), this, SLOT(on_image_zoomed()));
|
||||||
|
|
||||||
|
// Create slightly transparent white background so labels can be seen
|
||||||
|
m_tempScale = new TempScale();
|
||||||
|
m_tempScale->setZValue(2.0);
|
||||||
|
m_tempScale->setVisible(false);
|
||||||
|
QRectF rect = m_tempScale->rect();
|
||||||
|
m_tempScaleBG = new QGraphicsRectItem(rect.left()-10, rect.top()-15, rect.width()+60, rect.height()+45);
|
||||||
|
m_tempScaleBG->setPen(QColor(200, 200, 200, 200));
|
||||||
|
m_tempScaleBG->setBrush(QColor(200, 200, 200, 200));
|
||||||
|
m_tempScaleBG->setZValue(1.0);
|
||||||
|
m_tempScaleBG->setVisible(false);
|
||||||
|
m_tempText = new QGraphicsSimpleTextItem("");
|
||||||
|
m_tempText->setZValue(3.0);
|
||||||
|
m_tempText->setVisible(false);
|
||||||
m_scene = new QGraphicsScene(ui->image);
|
m_scene = new QGraphicsScene(ui->image);
|
||||||
|
m_scene->addItem(m_tempScale);
|
||||||
|
m_scene->addItem(m_tempScaleBG);
|
||||||
|
m_scene->addItem(m_tempText);
|
||||||
ui->image->setScene(m_scene);
|
ui->image->setScene(m_scene);
|
||||||
ui->image->show();
|
ui->image->show();
|
||||||
|
|
||||||
|
m_scene->installEventFilter(this);
|
||||||
|
|
||||||
displaySettings();
|
displaySettings();
|
||||||
applySettings(true);
|
applySettings(true);
|
||||||
}
|
}
|
||||||
|
@ -502,6 +666,55 @@ APTDemodGUI::~APTDemodGUI()
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool APTDemodGUI::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if ((obj == m_scene) && (m_settings.m_channels == APTDemodSettings::TEMPERATURE))
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::GraphicsSceneMouseMove)
|
||||||
|
{
|
||||||
|
QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent *>(event);
|
||||||
|
// Find temperature under cursor
|
||||||
|
int x = round(mouseEvent->scenePos().x());
|
||||||
|
int y = round(mouseEvent->scenePos().y());
|
||||||
|
if ((x >= 0) && (y >= 0) && (x < m_image.width()) && (y < m_image.height()))
|
||||||
|
{
|
||||||
|
// Map from colored temperature pixel back to greyscale level
|
||||||
|
// This is perhaps a bit slow - might be better to give GUI access to greyscale image as well
|
||||||
|
QRgb p = m_image.pixel(x, y);
|
||||||
|
int r = qRed(p);
|
||||||
|
int g = qGreen(p);
|
||||||
|
int b = qBlue(p);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
if ( (r == (unsigned char)apt_TempPalette[i*3])
|
||||||
|
&& (g == (unsigned char)apt_TempPalette[i*3+1])
|
||||||
|
&& (b == (unsigned char)apt_TempPalette[i*3+2]))
|
||||||
|
{
|
||||||
|
// Map from palette index to degrees C
|
||||||
|
int temp = (i / 255.0) * 160.0 - 100.0;
|
||||||
|
m_tempText->setText(QString("%1C").arg(temp));
|
||||||
|
int width = m_tempText->boundingRect().width();
|
||||||
|
int height = m_tempText->boundingRect().height();
|
||||||
|
QRectF rect = m_tempScaleBG->rect();
|
||||||
|
m_tempText->setPos(rect.left()+rect.width()/2-width/2, rect.top()+rect.height()-height-5);
|
||||||
|
m_tempText->setVisible(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == 256) {
|
||||||
|
m_tempText->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_tempText->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ChannelGUI::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
void APTDemodGUI::blockApplySettings(bool block)
|
void APTDemodGUI::blockApplySettings(bool block)
|
||||||
{
|
{
|
||||||
m_doApplySettings = !block;
|
m_doApplySettings = !block;
|
||||||
|
@ -538,6 +751,11 @@ void APTDemodGUI::displaySettings()
|
||||||
ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 1000.0, 0, 'f', 1));
|
ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 1000.0, 0, 'f', 1));
|
||||||
ui->fmDev->setValue(m_settings.m_fmDeviation / 100.0);
|
ui->fmDev->setValue(m_settings.m_fmDeviation / 100.0);
|
||||||
|
|
||||||
|
ui->transparencyThreshold->setValue(m_settings.m_transparencyThreshold);
|
||||||
|
ui->transparencyThresholdText->setText(QString::number(m_settings.m_transparencyThreshold));
|
||||||
|
ui->opacityThreshold->setValue(m_settings.m_opacityThreshold);
|
||||||
|
ui->opacityThresholdText->setText(QString::number(m_settings.m_opacityThreshold));
|
||||||
|
|
||||||
ui->startStop->setChecked(m_settings.m_decodeEnabled);
|
ui->startStop->setChecked(m_settings.m_decodeEnabled);
|
||||||
ui->cropNoise->setChecked(m_settings.m_cropNoise);
|
ui->cropNoise->setChecked(m_settings.m_cropNoise);
|
||||||
ui->denoise->setChecked(m_settings.m_denoise);
|
ui->denoise->setChecked(m_settings.m_denoise);
|
||||||
|
@ -552,14 +770,38 @@ void APTDemodGUI::displaySettings()
|
||||||
ui->image->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
|
ui->image->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->channels->setCurrentIndex((int)m_settings.m_channels);
|
displayPalettes();
|
||||||
|
displayLabels();
|
||||||
displayStreamIndex();
|
displayStreamIndex();
|
||||||
|
|
||||||
restoreState(m_rollupState);
|
restoreState(m_rollupState);
|
||||||
blockApplySettings(false);
|
blockApplySettings(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APTDemodGUI::displayPalettes()
|
||||||
|
{
|
||||||
|
ui->channels->blockSignals(true);
|
||||||
|
ui->channels->clear();
|
||||||
|
ui->channels->addItem("Both");
|
||||||
|
ui->channels->addItem("A");
|
||||||
|
ui->channels->addItem("B");
|
||||||
|
ui->channels->addItem("Temperature");
|
||||||
|
for (auto palette : m_settings.m_palettes)
|
||||||
|
{
|
||||||
|
QFileInfo fi(palette);
|
||||||
|
ui->channels->addItem(fi.baseName());
|
||||||
|
}
|
||||||
|
if (m_settings.m_channels == APTDemodSettings::PALETTE)
|
||||||
|
{
|
||||||
|
ui->channels->setCurrentIndex(((int)m_settings.m_channels) + m_settings.m_palette);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->channels->setCurrentIndex((int)m_settings.m_channels);
|
||||||
|
}
|
||||||
|
ui->channels->blockSignals(false);
|
||||||
|
}
|
||||||
|
|
||||||
void APTDemodGUI::displayStreamIndex()
|
void APTDemodGUI::displayStreamIndex()
|
||||||
{
|
{
|
||||||
if (m_deviceUISet->m_deviceMIMOEngine) {
|
if (m_deviceUISet->m_deviceMIMOEngine) {
|
||||||
|
@ -598,3 +840,47 @@ void APTDemodGUI::tick()
|
||||||
|
|
||||||
m_tickCount++;
|
m_tickCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APTDemodGUI::on_deleteImageFromMap_clicked()
|
||||||
|
{
|
||||||
|
// If more than one image, pop up a dialog to select which to delete
|
||||||
|
if (m_mapImages.size() > 1)
|
||||||
|
{
|
||||||
|
APTDemodSelectDialog dialog(m_mapImages, this);
|
||||||
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
|
{
|
||||||
|
for (auto name : dialog.getSelected())
|
||||||
|
{
|
||||||
|
deleteImageFromMap(name);
|
||||||
|
m_mapImages.removeAll(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto name : m_mapImages) {
|
||||||
|
deleteImageFromMap(name);
|
||||||
|
}
|
||||||
|
m_mapImages.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodGUI::deleteImageFromMap(const QString &name)
|
||||||
|
{
|
||||||
|
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||||
|
QList<MessageQueue*> *mapMessageQueues = messagePipes.getMessageQueues(m_aptDemod, "mapitems");
|
||||||
|
if (mapMessageQueues)
|
||||||
|
{
|
||||||
|
QList<MessageQueue*>::iterator it = mapMessageQueues->begin();
|
||||||
|
for (; it != mapMessageQueues->end(); ++it)
|
||||||
|
{
|
||||||
|
SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem();
|
||||||
|
swgMapItem->setName(new QString(name));
|
||||||
|
swgMapItem->setImage(new QString()); // Set image to "" to delete it
|
||||||
|
swgMapItem->setType(1);
|
||||||
|
|
||||||
|
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_aptDemod, swgMapItem);
|
||||||
|
(*it)->push(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
#include <QGraphicsRectItem>
|
||||||
|
|
||||||
#include "channel/channelgui.h"
|
#include "channel/channelgui.h"
|
||||||
#include "dsp/channelmarker.h"
|
#include "dsp/channelmarker.h"
|
||||||
|
@ -52,6 +53,16 @@ namespace Ui {
|
||||||
}
|
}
|
||||||
class APTDemodGUI;
|
class APTDemodGUI;
|
||||||
|
|
||||||
|
// Temperature scale
|
||||||
|
class TempScale : public QObject, public QGraphicsRectItem {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
TempScale(QGraphicsItem *parent = nullptr);
|
||||||
|
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||||
|
private:
|
||||||
|
QLinearGradient m_gradient;
|
||||||
|
};
|
||||||
|
|
||||||
class APTDemodGUI : public ChannelGUI {
|
class APTDemodGUI : public ChannelGUI {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -63,6 +74,7 @@ public:
|
||||||
QByteArray serialize() const;
|
QByteArray serialize() const;
|
||||||
bool deserialize(const QByteArray& data);
|
bool deserialize(const QByteArray& data);
|
||||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||||
|
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void channelMarkerChangedByCursor();
|
void channelMarkerChangedByCursor();
|
||||||
|
@ -87,6 +99,11 @@ private:
|
||||||
QGraphicsScene* m_scene;
|
QGraphicsScene* m_scene;
|
||||||
QGraphicsPixmapItem* m_pixmapItem;
|
QGraphicsPixmapItem* m_pixmapItem;
|
||||||
GraphicsViewZoom* m_zoom;
|
GraphicsViewZoom* m_zoom;
|
||||||
|
TempScale *m_tempScale;
|
||||||
|
QGraphicsRectItem *m_tempScaleBG;
|
||||||
|
QGraphicsSimpleTextItem *m_tempText;
|
||||||
|
|
||||||
|
QList<QString> m_mapImages;
|
||||||
|
|
||||||
explicit APTDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
explicit APTDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
||||||
virtual ~APTDemodGUI();
|
virtual ~APTDemodGUI();
|
||||||
|
@ -94,8 +111,12 @@ private:
|
||||||
void blockApplySettings(bool block);
|
void blockApplySettings(bool block);
|
||||||
void applySettings(bool force = false);
|
void applySettings(bool force = false);
|
||||||
void displaySettings();
|
void displaySettings();
|
||||||
|
void displayPalettes();
|
||||||
|
void displayLabels();
|
||||||
void displayStreamIndex();
|
void displayStreamIndex();
|
||||||
bool handleMessage(const Message& message);
|
bool handleMessage(const Message& message);
|
||||||
|
void deleteImageFromMap(const QString &name);
|
||||||
|
void resetDecoder();
|
||||||
|
|
||||||
void leaveEvent(QEvent*);
|
void leaveEvent(QEvent*);
|
||||||
void enterEvent(QEvent*);
|
void enterEvent(QEvent*);
|
||||||
|
@ -105,6 +126,11 @@ private slots:
|
||||||
void on_rfBW_valueChanged(int index);
|
void on_rfBW_valueChanged(int index);
|
||||||
void on_fmDev_valueChanged(int value);
|
void on_fmDev_valueChanged(int value);
|
||||||
void on_channels_currentIndexChanged(int index);
|
void on_channels_currentIndexChanged(int index);
|
||||||
|
void on_transparencyThreshold_valueChanged(int value);
|
||||||
|
void on_transparencyThreshold_sliderReleased();
|
||||||
|
void on_opacityThreshold_valueChanged(int value);
|
||||||
|
void on_opacityThreshold_sliderReleased();
|
||||||
|
void on_deleteImageFromMap_clicked();
|
||||||
void on_cropNoise_clicked(bool checked=false);
|
void on_cropNoise_clicked(bool checked=false);
|
||||||
void on_denoise_clicked(bool checked=false);
|
void on_denoise_clicked(bool checked=false);
|
||||||
void on_linear_clicked(bool checked=false);
|
void on_linear_clicked(bool checked=false);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>451</width>
|
<width>440</width>
|
||||||
<height>569</height>
|
<height>569</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -18,13 +18,12 @@
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>352</width>
|
<width>440</width>
|
||||||
<height>0</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<family>Liberation Sans</family>
|
|
||||||
<pointsize>9</pointsize>
|
<pointsize>9</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
|
@ -105,7 +104,6 @@
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<family>Liberation Mono</family>
|
|
||||||
<pointsize>12</pointsize>
|
<pointsize>12</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
|
@ -198,7 +196,6 @@
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<family>Liberation Mono</family>
|
|
||||||
<pointsize>8</pointsize>
|
<pointsize>8</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
|
@ -275,26 +272,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="fmDevLabel">
|
<widget class="QLabel" name="fmDevLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -352,6 +329,132 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="transparencyThresholdLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>T<sub>TH</sub></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDial" name="transparencyThreshold">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Transparency threshold for image on map</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>255</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="transparencyThresholdText">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>255</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="opacityThresholdLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>O<sub>TH</sub></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDial" name="opacityThreshold">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Opacity threshold for image on map</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>255</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="opacityThresholdText">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>255</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="deleteImageFromMap">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Delete images from map</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||||
|
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -729,7 +832,6 @@
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>deltaFrequency</tabstop>
|
<tabstop>deltaFrequency</tabstop>
|
||||||
<tabstop>rfBW</tabstop>
|
<tabstop>rfBW</tabstop>
|
||||||
<tabstop>fmDev</tabstop>
|
|
||||||
<tabstop>startStop</tabstop>
|
<tabstop>startStop</tabstop>
|
||||||
<tabstop>showSettings</tabstop>
|
<tabstop>showSettings</tabstop>
|
||||||
<tabstop>resetDecoder</tabstop>
|
<tabstop>resetDecoder</tabstop>
|
||||||
|
|
|
@ -19,17 +19,25 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
#include <QBuffer>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "maincore.h"
|
||||||
|
#include "util/units.h"
|
||||||
|
|
||||||
#include "aptdemod.h"
|
#include "aptdemod.h"
|
||||||
#include "aptdemodimageworker.h"
|
#include "aptdemodimageworker.h"
|
||||||
|
|
||||||
|
#include "SWGMapItem.h"
|
||||||
|
|
||||||
MESSAGE_CLASS_DEFINITION(APTDemodImageWorker::MsgConfigureAPTDemodImageWorker, Message)
|
MESSAGE_CLASS_DEFINITION(APTDemodImageWorker::MsgConfigureAPTDemodImageWorker, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(APTDemodImageWorker::MsgSaveImageToDisk, Message)
|
MESSAGE_CLASS_DEFINITION(APTDemodImageWorker::MsgSaveImageToDisk, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(APTDemodImageWorker::MsgSetSatelliteName, Message)
|
MESSAGE_CLASS_DEFINITION(APTDemodImageWorker::MsgSetSatelliteName, Message)
|
||||||
|
|
||||||
APTDemodImageWorker::APTDemodImageWorker() :
|
APTDemodImageWorker::APTDemodImageWorker(APTDemod *aptDemod) :
|
||||||
m_messageQueueToGUI(nullptr),
|
m_messageQueueToGUI(nullptr),
|
||||||
|
m_aptDemod(aptDemod),
|
||||||
|
m_sgp4(nullptr),
|
||||||
m_running(false),
|
m_running(false),
|
||||||
m_mutex(QMutex::Recursive)
|
m_mutex(QMutex::Recursive)
|
||||||
{
|
{
|
||||||
|
@ -51,6 +59,8 @@ APTDemodImageWorker::~APTDemodImageWorker()
|
||||||
delete[] m_image.prow[y];
|
delete[] m_image.prow[y];
|
||||||
delete[] m_tempImage.prow[y];
|
delete[] m_tempImage.prow[y];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete m_sgp4;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APTDemodImageWorker::reset()
|
void APTDemodImageWorker::reset()
|
||||||
|
@ -129,6 +139,8 @@ bool APTDemodImageWorker::handleMessage(const Message& cmd)
|
||||||
void APTDemodImageWorker::applySettings(const APTDemodSettings& settings, bool force)
|
void APTDemodImageWorker::applySettings(const APTDemodSettings& settings, bool force)
|
||||||
{
|
{
|
||||||
(void) force;
|
(void) force;
|
||||||
|
|
||||||
|
bool callRecalcCoords = false;
|
||||||
bool callProcessImage = false;
|
bool callProcessImage = false;
|
||||||
|
|
||||||
if ((settings.m_cropNoise != m_settings.m_cropNoise) ||
|
if ((settings.m_cropNoise != m_settings.m_cropNoise) ||
|
||||||
|
@ -137,14 +149,52 @@ void APTDemodImageWorker::applySettings(const APTDemodSettings& settings, bool f
|
||||||
(settings.m_histogramEqualise != m_settings.m_histogramEqualise) ||
|
(settings.m_histogramEqualise != m_settings.m_histogramEqualise) ||
|
||||||
(settings.m_precipitationOverlay != m_settings.m_precipitationOverlay) ||
|
(settings.m_precipitationOverlay != m_settings.m_precipitationOverlay) ||
|
||||||
(settings.m_flip != m_settings.m_flip) ||
|
(settings.m_flip != m_settings.m_flip) ||
|
||||||
(settings.m_channels != m_settings.m_channels))
|
(settings.m_channels != m_settings.m_channels) ||
|
||||||
|
(settings.m_transparencyThreshold != m_settings.m_transparencyThreshold) ||
|
||||||
|
(settings.m_opacityThreshold != m_settings.m_opacityThreshold) ||
|
||||||
|
(settings.m_palettes != m_settings.m_palettes) ||
|
||||||
|
(settings.m_palette != m_settings.m_palette) ||
|
||||||
|
(settings.m_horizontalPixelsPerDegree != m_settings.m_horizontalPixelsPerDegree) ||
|
||||||
|
(settings.m_verticalPixelsPerDegree != m_settings.m_verticalPixelsPerDegree))
|
||||||
{
|
{
|
||||||
// Call after settings have been applied
|
// Call after settings have been applied
|
||||||
callProcessImage = true;
|
callProcessImage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((settings.m_satTimeOffset != m_settings.m_satTimeOffset) ||
|
||||||
|
(settings.m_satYaw != m_settings.m_satYaw))
|
||||||
|
{
|
||||||
|
callRecalcCoords = true;
|
||||||
|
callProcessImage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.m_decodeEnabled && m_settings.m_decodeEnabled)
|
||||||
|
{
|
||||||
|
// Decode complete - make sure we do a full image update
|
||||||
|
// so we aren't left we unprocessed lines
|
||||||
|
callProcessImage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.m_palettes != m_settings.m_palettes)
|
||||||
|
{
|
||||||
|
// Load colour palettes
|
||||||
|
m_palettes.clear();
|
||||||
|
for (auto palette : settings.m_palettes)
|
||||||
|
{
|
||||||
|
QImage img;
|
||||||
|
img.load(palette);
|
||||||
|
if ((img.width() != 256) || (img.height() != 256)) {
|
||||||
|
qWarning() << "APT colour palette " << palette << " is not 256x256 pixels - " << img.width() << "x" << img.height();
|
||||||
|
}
|
||||||
|
m_palettes.append(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_settings = settings;
|
m_settings = settings;
|
||||||
|
|
||||||
|
if (callRecalcCoords) {
|
||||||
|
recalcCoords();
|
||||||
|
}
|
||||||
if (callProcessImage) {
|
if (callProcessImage) {
|
||||||
sendImageToGUI();
|
sendImageToGUI();
|
||||||
}
|
}
|
||||||
|
@ -153,25 +203,234 @@ void APTDemodImageWorker::applySettings(const APTDemodSettings& settings, bool f
|
||||||
void APTDemodImageWorker::resetDecoder()
|
void APTDemodImageWorker::resetDecoder()
|
||||||
{
|
{
|
||||||
m_image.nrow = 0;
|
m_image.nrow = 0;
|
||||||
|
m_image.zenith = 0;
|
||||||
m_tempImage.nrow = 0;
|
m_tempImage.nrow = 0;
|
||||||
|
m_tempImage.zenith = 0;
|
||||||
m_greyImage = QImage(APT_IMG_WIDTH, APT_MAX_HEIGHT, QImage::Format_Grayscale8);
|
m_greyImage = QImage(APT_IMG_WIDTH, APT_MAX_HEIGHT, QImage::Format_Grayscale8);
|
||||||
m_greyImage.fill(0);
|
m_greyImage.fill(0);
|
||||||
m_colourImage = QImage(APT_IMG_WIDTH, APT_MAX_HEIGHT, QImage::Format_RGB888);
|
m_colourImage = QImage(APT_IMG_WIDTH, APT_MAX_HEIGHT, QImage::Format_RGB888);
|
||||||
m_colourImage.fill(0);
|
m_colourImage.fill(0);
|
||||||
m_satelliteName = "";
|
m_satelliteName = "";
|
||||||
|
m_satCoords.clear();
|
||||||
|
m_pixelCoords.clear();
|
||||||
|
delete m_sgp4;
|
||||||
|
m_sgp4 = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Qt QDataTime to QGP4 DateTime
|
||||||
|
static DateTime qDateTimeToDateTime(QDateTime qdt)
|
||||||
|
{
|
||||||
|
QDateTime utc = qdt.toUTC();
|
||||||
|
QDate date = utc.date();
|
||||||
|
QTime time = utc.time();
|
||||||
|
DateTime dt;
|
||||||
|
dt.Initialise(date.year(), date.month(), date.day(), time.hour(), time.minute(), time.second(), time.msec() * 1000);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get heading in range [0,360)
|
||||||
|
static double normaliseHeading(double heading)
|
||||||
|
{
|
||||||
|
return fmod(heading + 360.0, 360.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get longitude in range -180,180
|
||||||
|
static double normaliseLongitude(double lon)
|
||||||
|
{
|
||||||
|
return fmod(lon + 540.0, 360.0) - 180.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate heading (azimuth) in degrees
|
||||||
|
double APTDemodImageWorker::calcHeading(CoordGeodetic from, CoordGeodetic to) const
|
||||||
|
{
|
||||||
|
// From https://en.wikipedia.org/wiki/Azimuth Section In Geodesy
|
||||||
|
double flattening = 1.0 / 298.257223563; // For WGS84 ellipsoid
|
||||||
|
double eSq = flattening * (2.0 - flattening);
|
||||||
|
double oneMinusESq = (1.0 - flattening) * (1.0 - flattening);
|
||||||
|
|
||||||
|
double tl1 = tan(from.latitude);
|
||||||
|
double tl2 = tan(to.latitude);
|
||||||
|
double n1 = 1.0 + oneMinusESq * tl2 * tl2;
|
||||||
|
double d1 = 1.0 + oneMinusESq * tl1 * tl1;
|
||||||
|
|
||||||
|
double l = to.longitude - from.longitude;
|
||||||
|
|
||||||
|
double alpha;
|
||||||
|
|
||||||
|
if (from.latitude == 0.0)
|
||||||
|
{
|
||||||
|
alpha = atan2(sin(l), (oneMinusESq * tan(to.latitude)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double lambda = oneMinusESq * tan(to.latitude) / tan(from.latitude) + eSq * sqrt(n1/d1);
|
||||||
|
|
||||||
|
alpha = atan2(sin(l), ((lambda - cos(l)) * sin(from.latitude)));
|
||||||
|
}
|
||||||
|
|
||||||
|
double deg = Units::radiansToDegrees(alpha);
|
||||||
|
if (!m_settings.m_northToSouth) {
|
||||||
|
deg += 180.0;
|
||||||
|
}
|
||||||
|
deg = normaliseHeading(deg);
|
||||||
|
return deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CoordGeodetic are in radians. Distance in metres. Bearing in radians.
|
||||||
|
// https://www.movable-type.co.uk/scripts/latlong.html
|
||||||
|
// This approximates Earth as spherical. If we need more accurate algorithm, see:
|
||||||
|
// https://www.movable-type.co.uk/scripts/latlong-vincenty.html
|
||||||
|
static void calcRadialEndPoint(CoordGeodetic start, double distance, double bearing, CoordGeodetic &end)
|
||||||
|
{
|
||||||
|
double earthRadius = 6378137.0; // At equator
|
||||||
|
double delta = distance/earthRadius;
|
||||||
|
end.latitude = std::asin(sin(start.latitude)*cos(delta) + cos(start.latitude)*sin(delta)*cos(bearing));
|
||||||
|
end.longitude = start.longitude + std::atan2(sin(bearing)*sin(delta)*cos(start.latitude), cos(delta) - sin(start.latitude)*sin(end.latitude));
|
||||||
|
end.longitude = normaliseLongitude(end.longitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodImageWorker::calcPixelCoords(CoordGeodetic centreCoord, double heading)
|
||||||
|
{
|
||||||
|
// Calculate coordinates of each pixel in a row (swath)
|
||||||
|
// Assume satellite is at centre pixel, and project +-90 degrees from satellite heading
|
||||||
|
// https://www.ncei.noaa.gov/pub/data/satellite/publications/podguides/N-15%20thru%20N-19/pdf/APPENDIX%20J%20Instrument%20Scan%20Properties.pdf
|
||||||
|
// Swath for AVHRR/3 of 2926.6km at 833km altitude over spherical Earth
|
||||||
|
// Some docs say resolution is 4.0km, but it varies as per fig 4.2.3-1 in:
|
||||||
|
// https://www.ncei.noaa.gov/pub/data/satellite/publications/podguides/N-15%20thru%20N-19/pdf/2.1%20Section%204.0%20Real%20Time%20Data%20Systems%20for%20Local%20Users%20.pdf
|
||||||
|
// TODO: Could try to adjust for altitude
|
||||||
|
|
||||||
|
QVector<CoordGeodetic> pixelCoords(APT_CH_WIDTH);
|
||||||
|
pixelCoords[APT_CH_WIDTH/2] = centreCoord;
|
||||||
|
double heading1 = Units::degreesToRadians(heading + m_settings.m_satYaw + 90.0);
|
||||||
|
double heading2 = Units::degreesToRadians(heading + m_settings.m_satYaw - 90.0);
|
||||||
|
for (int i = 1; i <= APT_CH_WIDTH/2; i++)
|
||||||
|
{
|
||||||
|
double distance = i * 2926600.0/APT_CH_WIDTH;
|
||||||
|
calcRadialEndPoint(centreCoord, distance, heading1, pixelCoords[APT_CH_WIDTH/2-i]);
|
||||||
|
calcRadialEndPoint(centreCoord, distance, heading2, pixelCoords[APT_CH_WIDTH/2+i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_settings.m_northToSouth) {
|
||||||
|
m_pixelCoords.append(pixelCoords);
|
||||||
|
} else {
|
||||||
|
m_pixelCoords.prepend(pixelCoords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate all pixel coordiantes as satTimeOffset or satYaw has changed
|
||||||
|
void APTDemodImageWorker::recalcCoords()
|
||||||
|
{
|
||||||
|
if (m_sgp4)
|
||||||
|
{
|
||||||
|
m_satCoords.clear();
|
||||||
|
m_pixelCoords.clear();
|
||||||
|
for (int row = 0; row < m_image.nrow; row++)
|
||||||
|
{
|
||||||
|
QDateTime qdt = m_settings.m_aosDateTime.addMSecs(m_settings.m_satTimeOffset * 1000.0f + row * 500);
|
||||||
|
calcCoords(qdt, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate pixel coordinates for a single row at the given date and time
|
||||||
|
void APTDemodImageWorker::calcCoords(QDateTime qdt, int row)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DateTime dt = qDateTimeToDateTime(qdt);
|
||||||
|
|
||||||
|
// Calculate satellite position
|
||||||
|
Eci eci = m_sgp4->FindPosition(dt);
|
||||||
|
|
||||||
|
// Convert satellite position to geodetic coordinates (lat and long)
|
||||||
|
CoordGeodetic geo = eci.ToGeodetic();
|
||||||
|
|
||||||
|
m_satCoords.append(geo);
|
||||||
|
|
||||||
|
// Calculate satellite heading (Could convert eci.Velocity() instead)
|
||||||
|
double heading;
|
||||||
|
if (m_satCoords.size() == 2)
|
||||||
|
{
|
||||||
|
heading = calcHeading(m_satCoords[0], m_satCoords[1]);
|
||||||
|
calcPixelCoords(m_satCoords[0], heading);
|
||||||
|
calcPixelCoords(m_satCoords[1], heading);
|
||||||
|
}
|
||||||
|
else if (m_satCoords.size() > 2)
|
||||||
|
{
|
||||||
|
heading = calcHeading(m_satCoords[row-1], m_satCoords[row]);
|
||||||
|
calcPixelCoords(geo, heading);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SatelliteException& se)
|
||||||
|
{
|
||||||
|
qDebug() << "APTDemodImageWorker::calcCoord: " << se.what();
|
||||||
|
}
|
||||||
|
catch (DecayedException& de)
|
||||||
|
{
|
||||||
|
qDebug() << "APTDemodImageWorker::calcCoord: " << de.what();
|
||||||
|
}
|
||||||
|
catch (TleException& tlee)
|
||||||
|
{
|
||||||
|
qDebug() << "APTDemodImageWorker::calcCoord: " << tlee.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate satellite's geodetic coordinates and heading
|
||||||
|
void APTDemodImageWorker::calcCoord(int row)
|
||||||
|
{
|
||||||
|
if (row == 0)
|
||||||
|
{
|
||||||
|
QStringList tle = m_settings.m_tle.trimmed().split("\n");
|
||||||
|
if (tle.size() == 3)
|
||||||
|
{
|
||||||
|
// Initalise SGP4
|
||||||
|
Tle tle(tle[0].toStdString(), tle[1].toStdString(), tle[2].toStdString());
|
||||||
|
m_sgp4 = new SGP4(tle);
|
||||||
|
|
||||||
|
// Output time so we can check time offset from when AOS is signalled
|
||||||
|
qDebug() << "APTDemod: Processing row 0 at " << QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
calcCoords(m_settings.m_aosDateTime, row);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "APTDemodImageWorker::calcCoord: No TLE for satellite. Is Satellite Tracker running?";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_sgp4 == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Calculate time at which
|
||||||
|
// Don't try to use QDateTime::currentDateTime() as processing & scheduling delays mean
|
||||||
|
// it's not constant and can sometimes even be 0
|
||||||
|
// Lines should be transmitted at 2 per second, so just use number of rows since AOS
|
||||||
|
// We add a user-defined delay to account for delays in transferring SDR data and demodulation
|
||||||
|
QDateTime qdt = m_settings.m_aosDateTime.addMSecs(m_settings.m_satTimeOffset * 1000.0f + row * 500);
|
||||||
|
calcCoords(qdt, row);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APTDemodImageWorker::processPixels(const float *pixels)
|
void APTDemodImageWorker::processPixels(const float *pixels)
|
||||||
{
|
{
|
||||||
std::copy(pixels, pixels + APT_PROW_WIDTH, m_image.prow[m_image.nrow]);
|
if (m_image.nrow < APT_MAX_HEIGHT)
|
||||||
|
{
|
||||||
|
// Calculate lat and lon of centre of row
|
||||||
|
calcCoord(m_image.nrow);
|
||||||
|
|
||||||
if (m_image.nrow % 20 == 0) { // send full image only every 20 lines
|
std::copy(pixels, pixels + APT_PROW_WIDTH, m_image.prow[m_image.nrow]);
|
||||||
sendImageToGUI();
|
m_image.nrow++;
|
||||||
} else { // else send unprocessed line just to show stg is moving
|
|
||||||
sendLineToGUI();
|
if (m_image.nrow % m_settings.m_scanlinesPerImageUpdate == 0) { // send full image only every N lines
|
||||||
|
sendImageToGUI();
|
||||||
|
} else { // else send unprocessed line just to show stg is moving
|
||||||
|
sendLineToGUI();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_image.nrow++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void APTDemodImageWorker::sendImageToGUI()
|
void APTDemodImageWorker::sendImageToGUI()
|
||||||
|
@ -180,8 +439,278 @@ void APTDemodImageWorker::sendImageToGUI()
|
||||||
if (m_messageQueueToGUI)
|
if (m_messageQueueToGUI)
|
||||||
{
|
{
|
||||||
QStringList imageTypes;
|
QStringList imageTypes;
|
||||||
QImage image = processImage(imageTypes);
|
QImage image = processImage(imageTypes, m_settings.m_channels);
|
||||||
m_messageQueueToGUI->push(APTDemod::MsgImage::create(image, imageTypes, m_satelliteName));
|
m_messageQueueToGUI->push(APTDemod::MsgImage::create(image, imageTypes, m_satelliteName));
|
||||||
|
if (m_sgp4) {
|
||||||
|
sendImageToMap(image, imageTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the value of the pixel closest to the given coordinates
|
||||||
|
// If we have previously found a pixel, we constrain the search to be nearby, in order to speed up the search
|
||||||
|
QRgb APTDemodImageWorker::findNearest(const QImage &image, double latitude, double longitude, int xPrevious, int yPrevious, int &xNearest, int &yNearest) const
|
||||||
|
{
|
||||||
|
double dmin = 360.0 * 360.0 + 90.0 * 90.0;
|
||||||
|
xNearest = -1;
|
||||||
|
yNearest = -1;
|
||||||
|
QRgb p = qRgba(0, 0, 0, 0); // Transparent
|
||||||
|
|
||||||
|
int xMin, xMax;
|
||||||
|
int yMin, yMax;
|
||||||
|
|
||||||
|
int yStartPostCrop;
|
||||||
|
int yEndPostCrop;
|
||||||
|
if (m_settings.m_northToSouth)
|
||||||
|
{
|
||||||
|
yStartPostCrop = abs(m_tempImage.zenith);
|
||||||
|
yEndPostCrop = yStartPostCrop + image.height();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yStartPostCrop = m_image.nrow - m_tempImage.nrow - abs(m_tempImage.zenith);
|
||||||
|
yEndPostCrop = yStartPostCrop + image.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xPrevious == -1)
|
||||||
|
{
|
||||||
|
yMin = yStartPostCrop;
|
||||||
|
yMax = yEndPostCrop;
|
||||||
|
xMin = 0;
|
||||||
|
xMax = m_pixelCoords[0].size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int searchRadius = 4;
|
||||||
|
yMin = yPrevious - searchRadius;
|
||||||
|
yMax = yPrevious + searchRadius + 1;
|
||||||
|
xMin = xPrevious - searchRadius;
|
||||||
|
xMax = xPrevious + searchRadius + 1;
|
||||||
|
yMin = std::max(yMin, yStartPostCrop);
|
||||||
|
yMax = std::min(yMax, yEndPostCrop);
|
||||||
|
xMin = std::max(xMin, 0);
|
||||||
|
xMax = std::min(xMax, m_pixelCoords[0].size());
|
||||||
|
}
|
||||||
|
|
||||||
|
const int ySize = yEndPostCrop-1;
|
||||||
|
const int xSize = m_pixelCoords[0].size()-1;
|
||||||
|
for (int y = yMin; y < yMax; y++)
|
||||||
|
{
|
||||||
|
for (int x = xMin; x < xMax; x++)
|
||||||
|
{
|
||||||
|
CoordGeodetic coord = m_pixelCoords[y][x];
|
||||||
|
double dlat = coord.latitude - latitude;
|
||||||
|
double dlon = coord.longitude - longitude;
|
||||||
|
double d = dlat * dlat + dlon * dlon;
|
||||||
|
if (d < dmin)
|
||||||
|
{
|
||||||
|
dmin = d;
|
||||||
|
xNearest = x;
|
||||||
|
yNearest = y;
|
||||||
|
// Only use color of pixel if we're inside the source image
|
||||||
|
if ( ((y != yStartPostCrop) || ((y == yStartPostCrop) && (latitude <= coord.latitude)))
|
||||||
|
&& ((y != ySize) || ((y == ySize) && (latitude >= coord.latitude)))
|
||||||
|
&& ((x != 0) || ((x == 0) && (longitude >= coord.longitude)))
|
||||||
|
&& ((x != xSize) || ((x == xSize) && (longitude <= coord.longitude)))
|
||||||
|
)
|
||||||
|
{
|
||||||
|
p = image.pixel(x, y - yStartPostCrop);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p = qRgba(0, 0, 0, 0); // Transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate bounding box for projected image in terms of latitude and longitude
|
||||||
|
// TODO: Handle crossing of anti-meridian
|
||||||
|
void APTDemodImageWorker::calcBoundingBox(double &east, double &south, double &west, double &north, const QImage &image)
|
||||||
|
{
|
||||||
|
int start;
|
||||||
|
if (m_settings.m_northToSouth) {
|
||||||
|
start = abs(m_tempImage.zenith);
|
||||||
|
} else {
|
||||||
|
start = m_image.nrow - m_tempImage.nrow - abs(m_tempImage.zenith);
|
||||||
|
}
|
||||||
|
int stop = start + image.height();
|
||||||
|
|
||||||
|
east = -M_PI;
|
||||||
|
west = M_PI;
|
||||||
|
north = -M_PI/2.0;
|
||||||
|
south = M_PI/2.0;
|
||||||
|
|
||||||
|
//FILE *f = fopen("coords.txt", "w");
|
||||||
|
for (int y = start; y < stop; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < m_pixelCoords[y].size(); x++)
|
||||||
|
{
|
||||||
|
double latitude = m_pixelCoords[y][x].latitude;
|
||||||
|
double longitude = m_pixelCoords[y][x].longitude;
|
||||||
|
//fprintf(f, "%f,%f ", Units::radiansToDegrees(m_pixelCoords[y][x].latitude), Units::radiansToDegrees(m_pixelCoords[y][x].longitude));
|
||||||
|
south = std::min(latitude, south);
|
||||||
|
north = std::max(latitude, north);
|
||||||
|
east = std::max(longitude, east);
|
||||||
|
west = std::min(longitude, west);
|
||||||
|
}
|
||||||
|
//fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
//fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project satellite image to equidistant cyclindrical projection (Plate Carree) for use on 3D Map
|
||||||
|
// We've previously computed lat and lon for each pixel in satellite image
|
||||||
|
// so we just work through coords in projected image, trying to find closest pixel in satellite image
|
||||||
|
// FIXME: How do we handle sat going over the poles?
|
||||||
|
QImage APTDemodImageWorker::projectImage(const QImage &image)
|
||||||
|
{
|
||||||
|
double east, south, west, north;
|
||||||
|
|
||||||
|
// Calculate bounding box for image tile
|
||||||
|
calcBoundingBox(east, south, west, north, image);
|
||||||
|
m_tileEast = ceil(Units::radiansToDegrees(east));
|
||||||
|
m_tileWest = floor(Units::radiansToDegrees(west));
|
||||||
|
m_tileNorth = ceil(Units::radiansToDegrees(north));
|
||||||
|
m_tileSouth = floor(Units::radiansToDegrees(south));
|
||||||
|
|
||||||
|
double widthDeg = m_tileEast - m_tileWest;
|
||||||
|
double heightDeg = m_tileNorth - m_tileSouth;
|
||||||
|
|
||||||
|
int width = widthDeg * m_settings.m_horizontalPixelsPerDegree;
|
||||||
|
int height = heightDeg * m_settings.m_verticalPixelsPerDegree;
|
||||||
|
|
||||||
|
//image.save("source.png");
|
||||||
|
//FILE *f = fopen("mapping.txt", "w");
|
||||||
|
QImage projection(width, height, QImage::Format_ARGB32);
|
||||||
|
int xNearest, yNearest, xPrevious, yPrevious;
|
||||||
|
xPrevious = -1;
|
||||||
|
yPrevious = -1;
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
// Calculate geodetic coords of pixel in projected image
|
||||||
|
double lat = m_tileNorth - (y / (double)m_settings.m_verticalPixelsPerDegree);
|
||||||
|
// Reverse search direction in alternate rows, so we are always seaching
|
||||||
|
// close to previously found pixel
|
||||||
|
if ((y & 1) == 0)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
double lon = m_tileWest + (x / (double)m_settings.m_horizontalPixelsPerDegree);
|
||||||
|
// Find closest pixel in source image
|
||||||
|
QRgb pixel = findNearest(image, Units::degreesToRadians(lat), Units::degreesToRadians(lon), xPrevious, yPrevious, xNearest, yNearest);
|
||||||
|
xPrevious = xNearest;
|
||||||
|
yPrevious = yNearest;
|
||||||
|
projection.setPixel(x, y, pixel);
|
||||||
|
//fprintf(f, "%f,%f,%d,%d,%d ", lat, lon, xNearest, yNearest, pixel==0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int x = width - 1; x >= 0; x--)
|
||||||
|
{
|
||||||
|
double lon = m_tileWest + (x / (double)m_settings.m_horizontalPixelsPerDegree);
|
||||||
|
// Find closest pixel in source image
|
||||||
|
QRgb pixel = findNearest(image, Units::degreesToRadians(lat), Units::degreesToRadians(lon), xPrevious, yPrevious, xNearest, yNearest);
|
||||||
|
xPrevious = xNearest;
|
||||||
|
yPrevious = yNearest;
|
||||||
|
projection.setPixel(x, y, pixel);
|
||||||
|
//fprintf(f, "%f,%f,%d,%d,%d ", lat, lon, xNearest, yNearest, pixel==0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
//fclose(f);
|
||||||
|
|
||||||
|
return projection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make an image transparent, so when overlaid on 3D map, we can see the underlying terrain
|
||||||
|
// Image is full transparent below m_transparencyThreshold and fully opaque above m_opacityThreshold
|
||||||
|
void APTDemodImageWorker::makeTransparent(QImage &image)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < image.height(); y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < image.width(); x++)
|
||||||
|
{
|
||||||
|
QRgb pixel = image.pixel(x, y);
|
||||||
|
int grey = qGray(pixel);
|
||||||
|
if (grey < m_settings.m_transparencyThreshold)
|
||||||
|
{
|
||||||
|
// Make fully transparent
|
||||||
|
pixel = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), 0);
|
||||||
|
image.setPixel(x, y, pixel);
|
||||||
|
}
|
||||||
|
else if (grey < m_settings.m_opacityThreshold)
|
||||||
|
{
|
||||||
|
// Make slightly transparent
|
||||||
|
float opacity = 1.0f - ((m_settings.m_opacityThreshold - grey) / (float)(m_settings.m_opacityThreshold - m_settings.m_transparencyThreshold));
|
||||||
|
opacity = opacity * 255.0f;
|
||||||
|
opacity = std::min(255.0f, opacity);
|
||||||
|
opacity = std::max(0.0f, opacity);
|
||||||
|
pixel = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), (int)std::round(opacity));
|
||||||
|
image.setPixel(x, y, pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodImageWorker::sendImageToMap(QImage image, QStringList imageTypes)
|
||||||
|
{
|
||||||
|
// Send to Map feature
|
||||||
|
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||||
|
QList<MessageQueue*> *mapMessageQueues = messagePipes.getMessageQueues(m_aptDemod, "mapitems");
|
||||||
|
if (mapMessageQueues)
|
||||||
|
{
|
||||||
|
// Only display one channel on map
|
||||||
|
QImage selectedChannel;
|
||||||
|
if (m_settings.m_channels == APTDemodSettings::BOTH_CHANNELS) {
|
||||||
|
selectedChannel = extractImage(image, APTDemodSettings::CHANNEL_B);
|
||||||
|
} else {
|
||||||
|
selectedChannel = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project image to geodetic coords (lat & lon)
|
||||||
|
selectedChannel = projectImage(selectedChannel);
|
||||||
|
//selectedChannel.save("projected.png");
|
||||||
|
|
||||||
|
// Use alpha channel to remove land & sea
|
||||||
|
makeTransparent(selectedChannel);
|
||||||
|
|
||||||
|
// Encode image as base64 PNG
|
||||||
|
QByteArray ba;
|
||||||
|
QBuffer buffer(&ba);
|
||||||
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
selectedChannel.save(&buffer, "PNG");
|
||||||
|
QByteArray data = ba.toBase64();
|
||||||
|
|
||||||
|
// Create name for the image
|
||||||
|
QString satName = m_satelliteName;
|
||||||
|
satName.replace(" ", "_");
|
||||||
|
QString name = QString("apt_%1_%2").arg(satName).arg(m_settings.m_aosDateTime.toString("yyyyMMdd_hhmmss"));
|
||||||
|
|
||||||
|
// Send name to GUI
|
||||||
|
m_messageQueueToGUI->push(APTDemod::MsgMapImageName::create(name));
|
||||||
|
|
||||||
|
QList<MessageQueue*>::iterator it = mapMessageQueues->begin();
|
||||||
|
for (; it != mapMessageQueues->end(); ++it)
|
||||||
|
{
|
||||||
|
SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem();
|
||||||
|
swgMapItem->setName(new QString(name));
|
||||||
|
swgMapItem->setImage(new QString(data));
|
||||||
|
swgMapItem->setAltitude(3000.0); // Typical cloud height - So it appears above objects on the ground
|
||||||
|
swgMapItem->setType(1);
|
||||||
|
swgMapItem->setImageTileEast(m_tileEast);
|
||||||
|
swgMapItem->setImageTileWest(m_tileWest);
|
||||||
|
swgMapItem->setImageTileNorth(m_tileNorth);
|
||||||
|
swgMapItem->setImageTileSouth(m_tileSouth);
|
||||||
|
|
||||||
|
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_aptDemod, swgMapItem);
|
||||||
|
(*it)->push(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +718,7 @@ void APTDemodImageWorker::sendLineToGUI()
|
||||||
{
|
{
|
||||||
if (m_messageQueueToGUI)
|
if (m_messageQueueToGUI)
|
||||||
{
|
{
|
||||||
float *pixels = m_image.prow[m_image.nrow];
|
float *pixels = m_image.prow[m_image.nrow-1];
|
||||||
uchar *line;
|
uchar *line;
|
||||||
APTDemod::MsgLine *msg = APTDemod::MsgLine::create(&line);
|
APTDemod::MsgLine *msg = APTDemod::MsgLine::create(&line);
|
||||||
|
|
||||||
|
@ -219,12 +748,12 @@ void APTDemodImageWorker::sendLineToGUI()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage APTDemodImageWorker::processImage(QStringList& imageTypes)
|
QImage APTDemodImageWorker::processImage(QStringList& imageTypes, APTDemodSettings::ChannelSelection channels)
|
||||||
{
|
{
|
||||||
copyImage(&m_tempImage, &m_image);
|
copyImage(&m_tempImage, &m_image);
|
||||||
|
|
||||||
// Calibrate channels according to wavelength
|
// Calibrate channels according to wavelength (1.7x to stop flickering)
|
||||||
if (m_tempImage.nrow >= APT_CALIBRATION_ROWS)
|
if (m_tempImage.nrow >= 1.7 * APT_CALIBRATION_ROWS)
|
||||||
{
|
{
|
||||||
m_tempImage.chA = apt_calibrate(m_tempImage.prow, m_tempImage.nrow, APT_CHA_OFFSET, APT_CH_WIDTH);
|
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);
|
m_tempImage.chB = apt_calibrate(m_tempImage.prow, m_tempImage.nrow, APT_CHB_OFFSET, APT_CH_WIDTH);
|
||||||
|
@ -233,9 +762,9 @@ QImage APTDemodImageWorker::processImage(QStringList& imageTypes)
|
||||||
"Visible (0.58-0.68 um)",
|
"Visible (0.58-0.68 um)",
|
||||||
"Near-IR (0.725-1.0 um)",
|
"Near-IR (0.725-1.0 um)",
|
||||||
"Near-IR (1.58-1.64 um)",
|
"Near-IR (1.58-1.64 um)",
|
||||||
"Mid-infrared (3.55-3.93 um)",
|
|
||||||
"Thermal-infrared (10.3-11.3 um)",
|
"Thermal-infrared (10.3-11.3 um)",
|
||||||
"Thermal-infrared (11.5-12.5 um)"
|
"Thermal-infrared (11.5-12.5 um)",
|
||||||
|
"Mid-infrared (3.55-3.93 um)"
|
||||||
});
|
});
|
||||||
|
|
||||||
imageTypes.append(channelTypes[m_tempImage.chA]);
|
imageTypes.append(channelTypes[m_tempImage.chA]);
|
||||||
|
@ -243,8 +772,9 @@ QImage APTDemodImageWorker::processImage(QStringList& imageTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crop noise due to low elevation at top and bottom of image
|
// Crop noise due to low elevation at top and bottom of image
|
||||||
if (m_settings.m_cropNoise)
|
if (m_settings.m_cropNoise) {
|
||||||
m_tempImage.zenith -= apt_cropNoise(&m_tempImage);
|
m_tempImage.zenith -= apt_cropNoise(&m_tempImage);
|
||||||
|
}
|
||||||
|
|
||||||
// Denoise filter
|
// Denoise filter
|
||||||
if (m_settings.m_denoise)
|
if (m_settings.m_denoise)
|
||||||
|
@ -305,10 +835,65 @@ QImage APTDemodImageWorker::processImage(QStringList& imageTypes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return extractImage(m_colourImage);
|
return extractImage(m_colourImage, channels);
|
||||||
|
}
|
||||||
|
else if (channels == APTDemodSettings::TEMPERATURE)
|
||||||
|
{
|
||||||
|
// Temperature calibration
|
||||||
|
int satnum = 15;
|
||||||
|
if (m_satelliteName == "NOAA 18") {
|
||||||
|
satnum = 18;
|
||||||
|
} else if (m_satelliteName == "NOAA 19") {
|
||||||
|
satnum = 19;
|
||||||
|
}
|
||||||
|
apt_temperature(satnum, &m_tempImage, APT_CHB_OFFSET, APT_CH_WIDTH);
|
||||||
|
|
||||||
|
// Apply colour palette
|
||||||
|
for (int r = 0; r < m_tempImage.nrow; r++)
|
||||||
|
{
|
||||||
|
uchar *l = m_colourImage.scanLine(r);
|
||||||
|
for (int i = 0; i < APT_CH_WIDTH; i++)
|
||||||
|
{
|
||||||
|
float p = m_tempImage.prow[r][i+APT_CHB_OFFSET];
|
||||||
|
uchar q = roundAndClip(p);
|
||||||
|
l[i*3] = apt_TempPalette[q*3];
|
||||||
|
l[i*3+1] = apt_TempPalette[q*3+1];
|
||||||
|
l[i*3+2] = apt_TempPalette[q*3+2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_colourImage.copy(0, 0, APT_CH_WIDTH, m_tempImage.nrow);
|
||||||
|
}
|
||||||
|
else if (channels == APTDemodSettings::PALETTE)
|
||||||
|
{
|
||||||
|
if ((m_settings.m_palette >= 0) && (m_settings.m_palette < m_palettes.size()))
|
||||||
|
{
|
||||||
|
// Apply colour palette
|
||||||
|
for (int r = 0; r < m_tempImage.nrow; r++)
|
||||||
|
{
|
||||||
|
uchar *l = m_colourImage.scanLine(r);
|
||||||
|
for (int i = 0; i < APT_CH_WIDTH; i++)
|
||||||
|
{
|
||||||
|
float pA = m_tempImage.prow[r][i+APT_CHA_OFFSET];
|
||||||
|
float pB = m_tempImage.prow[r][i+APT_CHB_OFFSET];
|
||||||
|
uchar qA = roundAndClip(pA);
|
||||||
|
uchar qB = roundAndClip(pB);
|
||||||
|
QRgb rgb = m_palettes[m_settings.m_palette].pixel(qA, qB);
|
||||||
|
l[i*3] = qRed(rgb);
|
||||||
|
l[i*3+1] = qGreen(rgb);
|
||||||
|
l[i*3+2] = qBlue(rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_colourImage.copy(0, 0, APT_CH_WIDTH, m_tempImage.nrow);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "APTDemodImageWorker::processImage - Illegal palette number: " << m_settings.m_palette;
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Extract grey-scale image
|
||||||
for (int r = 0; r < m_tempImage.nrow; r++)
|
for (int r = 0; r < m_tempImage.nrow; r++)
|
||||||
{
|
{
|
||||||
uchar *l = m_greyImage.scanLine(r);
|
uchar *l = m_greyImage.scanLine(r);
|
||||||
|
@ -319,43 +904,92 @@ QImage APTDemodImageWorker::processImage(QStringList& imageTypes)
|
||||||
l[i] = roundAndClip(p);
|
l[i] = roundAndClip(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return extractImage(m_greyImage);
|
return extractImage(m_greyImage, channels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage APTDemodImageWorker::extractImage(QImage image)
|
QImage APTDemodImageWorker::extractImage(QImage image, APTDemodSettings::ChannelSelection channels)
|
||||||
{
|
{
|
||||||
if (m_settings.m_channels == APTDemodSettings::BOTH_CHANNELS) {
|
if (channels == APTDemodSettings::BOTH_CHANNELS) {
|
||||||
return image.copy(0, 0, APT_IMG_WIDTH, m_tempImage.nrow);
|
return image.copy(0, 0, APT_IMG_WIDTH, m_tempImage.nrow);
|
||||||
} else if (m_settings.m_channels == APTDemodSettings::CHANNEL_A) {
|
} else if (channels == APTDemodSettings::CHANNEL_A) {
|
||||||
return image.copy(APT_CHA_OFFSET, 0, APT_CH_WIDTH, m_tempImage.nrow);
|
return image.copy(APT_CHA_OFFSET, 0, APT_CH_WIDTH, m_tempImage.nrow);
|
||||||
} else {
|
} else {
|
||||||
return image.copy(APT_CHB_OFFSET, 0, APT_CH_WIDTH, m_tempImage.nrow);
|
return image.copy(APT_CHB_OFFSET, 0, APT_CH_WIDTH, m_tempImage.nrow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APTDemodImageWorker::prependPath(QString &filename)
|
||||||
|
{
|
||||||
|
if (!m_settings.m_autoSavePath.isEmpty())
|
||||||
|
{
|
||||||
|
if (m_settings.m_autoSavePath.endsWith('/')) {
|
||||||
|
filename = m_settings.m_autoSavePath + filename;
|
||||||
|
} else {
|
||||||
|
filename = m_settings.m_autoSavePath + '/' + filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void APTDemodImageWorker::saveImageToDisk()
|
void APTDemodImageWorker::saveImageToDisk()
|
||||||
{
|
{
|
||||||
QStringList imageTypes;
|
QStringList imageTypes;
|
||||||
QImage image = processImage(imageTypes);
|
QImage image = processImage(imageTypes, APTDemodSettings::BOTH_CHANNELS);
|
||||||
|
|
||||||
if (image.height() >= m_settings.m_autoSaveMinScanLines)
|
if (image.height() >= m_settings.m_autoSaveMinScanLines)
|
||||||
{
|
{
|
||||||
QString filename;
|
QString filename;
|
||||||
QDateTime datetime = QDateTime::currentDateTime();
|
QDateTime dateTime;
|
||||||
filename = QString("apt_%1_%2.png").arg(m_satelliteName.replace(" ", "_")).arg(datetime.toString("yyyyMMdd_hhmm"));
|
QString dt;
|
||||||
|
if (m_settings.m_aosDateTime.isValid()) {
|
||||||
|
dateTime = m_settings.m_aosDateTime;
|
||||||
|
} else {
|
||||||
|
dateTime = QDateTime::currentDateTime();
|
||||||
|
}
|
||||||
|
dt = dateTime.toString("yyyyMMdd_hhmm");
|
||||||
|
QString sat = m_satelliteName;
|
||||||
|
sat.replace(" ", "_");
|
||||||
|
|
||||||
if (!m_settings.m_autoSavePath.isEmpty())
|
if (m_settings.m_saveCombined)
|
||||||
{
|
{
|
||||||
if (m_settings.m_autoSavePath.endsWith('/')) {
|
filename = QString("apt_%1_%2.png").arg(sat).arg(dt);
|
||||||
filename = m_settings.m_autoSavePath + filename;
|
prependPath(filename);
|
||||||
} else {
|
if (!image.save(filename)) {
|
||||||
filename = m_settings.m_autoSavePath + '/' + filename;
|
qCritical() << "Failed to save APT image to: " << filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!image.save(filename)) {
|
QImage chA = extractImage(image, APTDemodSettings::CHANNEL_A);
|
||||||
qCritical() << "Failed to save APT image to: " << filename;
|
QImage chB = extractImage(image, APTDemodSettings::CHANNEL_B);
|
||||||
|
|
||||||
|
if (m_settings.m_saveSeparate)
|
||||||
|
{
|
||||||
|
filename = QString("apt_%1_%2_cha.png").arg(sat).arg(dt);
|
||||||
|
prependPath(filename);
|
||||||
|
if (!chA.save(filename)) {
|
||||||
|
qCritical() << "Failed to save APT image to: " << filename;
|
||||||
|
}
|
||||||
|
filename = QString("apt_%1_%2_chb.png").arg(sat).arg(dt);
|
||||||
|
prependPath(filename);
|
||||||
|
if (!chB.save(filename)) {
|
||||||
|
qCritical() << "Failed to save APT image to: " << filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_settings.m_saveProjection)
|
||||||
|
{
|
||||||
|
filename = QString("apt_%1_%2_cha_eqi_cylindrical.png").arg(sat).arg(dt);
|
||||||
|
prependPath(filename);
|
||||||
|
QImage chAProj = projectImage(chA);
|
||||||
|
if (!chAProj.save(filename)) {
|
||||||
|
qCritical() << "Failed to save APT image to: " << filename;
|
||||||
|
}
|
||||||
|
filename = QString("apt_%1_%2_chb_eqi_cylindrical.png").arg(sat).arg(dt);
|
||||||
|
prependPath(filename);
|
||||||
|
QImage chBProj = projectImage(chB);
|
||||||
|
if (!chBProj.save(filename)) {
|
||||||
|
qCritical() << "Failed to save APT image to: " << filename;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,22 @@
|
||||||
|
|
||||||
#include <apt.h>
|
#include <apt.h>
|
||||||
|
|
||||||
|
#include <CoordTopocentric.h>
|
||||||
|
#include <CoordGeodetic.h>
|
||||||
|
#include <Observer.h>
|
||||||
|
#include <SGP4.h>
|
||||||
|
|
||||||
#include "util/messagequeue.h"
|
#include "util/messagequeue.h"
|
||||||
#include "util/message.h"
|
#include "util/message.h"
|
||||||
|
|
||||||
#include "aptdemodsettings.h"
|
#include "aptdemodsettings.h"
|
||||||
|
|
||||||
|
class APTDemod;
|
||||||
|
|
||||||
class APTDemodImageWorker : public QObject
|
class APTDemodImageWorker : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class MsgConfigureAPTDemodImageWorker : public Message {
|
class MsgConfigureAPTDemodImageWorker : public Message {
|
||||||
MESSAGE_CLASS_DECLARATION
|
MESSAGE_CLASS_DECLARATION
|
||||||
|
@ -95,7 +103,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
APTDemodImageWorker();
|
APTDemodImageWorker(APTDemod *aptDemod);
|
||||||
~APTDemodImageWorker();
|
~APTDemodImageWorker();
|
||||||
void reset();
|
void reset();
|
||||||
void startWork();
|
void startWork();
|
||||||
|
@ -109,6 +117,7 @@ private:
|
||||||
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
|
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
|
||||||
MessageQueue *m_messageQueueToGUI;
|
MessageQueue *m_messageQueueToGUI;
|
||||||
APTDemodSettings m_settings;
|
APTDemodSettings m_settings;
|
||||||
|
APTDemod *m_aptDemod;
|
||||||
|
|
||||||
// Image buffers
|
// Image buffers
|
||||||
apt_image_t m_image; // Received image
|
apt_image_t m_image; // Received image
|
||||||
|
@ -117,18 +126,39 @@ private:
|
||||||
QImage m_colourImage;
|
QImage m_colourImage;
|
||||||
QString m_satelliteName;
|
QString m_satelliteName;
|
||||||
|
|
||||||
|
QList<CoordGeodetic> m_satCoords; // Lat,lon for satellite for each image row - in received order for both pass directions
|
||||||
|
QVector<QVector<CoordGeodetic>> m_pixelCoords; // Coordinates for each pixel - reversed y order for south to north passes, so always highest lat first
|
||||||
|
SGP4 *m_sgp4; // For calculating satellite position
|
||||||
|
double m_tileEast; // Bounding box for projected image, in degrees
|
||||||
|
double m_tileWest;
|
||||||
|
double m_tileNorth;
|
||||||
|
double m_tileSouth;
|
||||||
|
|
||||||
|
QList<QImage> m_palettes;
|
||||||
|
|
||||||
bool m_running;
|
bool m_running;
|
||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
|
|
||||||
bool handleMessage(const Message& cmd);
|
bool handleMessage(const Message& cmd);
|
||||||
void applySettings(const APTDemodSettings& settings, bool force = false);
|
void applySettings(const APTDemodSettings& settings, bool force = false);
|
||||||
void resetDecoder();
|
void resetDecoder();
|
||||||
|
double calcHeading(CoordGeodetic from, CoordGeodetic to) const;
|
||||||
|
void calcPixelCoords(CoordGeodetic centreCoord, double heading);
|
||||||
|
void recalcCoords();
|
||||||
|
void calcCoords(QDateTime qdt, int row);
|
||||||
|
void calcCoord(int row);
|
||||||
void processPixels(const float *pixels);
|
void processPixels(const float *pixels);
|
||||||
void sendImageToGUI();
|
void sendImageToGUI();
|
||||||
|
QRgb findNearest(const QImage &image, double latitude, double longitude, int xPrevious, int yPrevious, int &xNearest, int &yNearest) const;
|
||||||
|
void calcBoundingBox(double &east, double &south, double &west, double &north, const QImage &image);
|
||||||
|
QImage projectImage(const QImage &image);
|
||||||
|
void makeTransparent(QImage &image);
|
||||||
|
void sendImageToMap(QImage image, QStringList imageTypes);
|
||||||
void sendLineToGUI();
|
void sendLineToGUI();
|
||||||
|
void prependPath(QString &filename);
|
||||||
void saveImageToDisk();
|
void saveImageToDisk();
|
||||||
QImage processImage(QStringList& imageTypes);
|
QImage processImage(QStringList& imageTypes, APTDemodSettings::ChannelSelection channels);
|
||||||
QImage extractImage(QImage image);
|
QImage extractImage(QImage image, APTDemodSettings::ChannelSelection channels);
|
||||||
|
|
||||||
static void copyImage(apt_image_t *dst, apt_image_t *src);
|
static void copyImage(apt_image_t *dst, apt_image_t *src);
|
||||||
static uchar roundAndClip(float p);
|
static uchar roundAndClip(float p);
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 <QDebug>
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
#include "aptdemodselectdialog.h"
|
||||||
|
|
||||||
|
APTDemodSelectDialog::APTDemodSelectDialog(const QStringList &list, QWidget* parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::APTDemodSelectDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
for (auto item : list) {
|
||||||
|
ui->list->addItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
APTDemodSelectDialog::~APTDemodSelectDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodSelectDialog::accept()
|
||||||
|
{
|
||||||
|
QList<QListWidgetItem *> items = ui->list->selectedItems();
|
||||||
|
m_selected.clear();
|
||||||
|
for (auto item : items)
|
||||||
|
{
|
||||||
|
m_selected.append(item->text());
|
||||||
|
}
|
||||||
|
QDialog::accept();
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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_APTDEMODSELECTDIALOG_H
|
||||||
|
#define INCLUDE_APTDEMODSELECTDIALOG_H
|
||||||
|
|
||||||
|
#include "ui_aptdemodselectdialog.h"
|
||||||
|
#include "aptdemodsettings.h"
|
||||||
|
|
||||||
|
class APTDemodSelectDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit APTDemodSelectDialog(const QStringList &list, QWidget* parent = 0);
|
||||||
|
~APTDemodSelectDialog();
|
||||||
|
QStringList getSelected() const { return m_selected; }
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void accept();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QStringList m_selected;
|
||||||
|
Ui::APTDemodSelectDialog* ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDE_APTDEMODSELECTDIALOG_H
|
|
@ -0,0 +1,98 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>APTDemodSelectDialog</class>
|
||||||
|
<widget class="QDialog" name="APTDemodSelectDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>600</width>
|
||||||
|
<height>304</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>9</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>APT Demodulator Settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="listLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select images to delete from the map</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="list">
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::MultiSelection</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||||
|
<include location="icons.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>APTDemodSelectDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>APTDemodSelectDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
|
@ -48,6 +48,18 @@ void APTDemodSettings::resetToDefaults()
|
||||||
m_autoSave = false;
|
m_autoSave = false;
|
||||||
m_autoSavePath = "";
|
m_autoSavePath = "";
|
||||||
m_autoSaveMinScanLines = 200;
|
m_autoSaveMinScanLines = 200;
|
||||||
|
m_saveCombined = true;
|
||||||
|
m_saveSeparate = false;
|
||||||
|
m_saveProjection = false;
|
||||||
|
m_scanlinesPerImageUpdate = 20;
|
||||||
|
m_transparencyThreshold = 100;
|
||||||
|
m_opacityThreshold = 200;
|
||||||
|
m_palettes.clear();
|
||||||
|
m_palette = 0;
|
||||||
|
m_horizontalPixelsPerDegree = 10;
|
||||||
|
m_verticalPixelsPerDegree = 20;
|
||||||
|
m_satTimeOffset = 0.0f;
|
||||||
|
m_satYaw = 0.0f;
|
||||||
|
|
||||||
m_rgbColor = QColor(216, 112, 169).rgb();
|
m_rgbColor = QColor(216, 112, 169).rgb();
|
||||||
m_title = "APT Demodulator";
|
m_title = "APT Demodulator";
|
||||||
|
@ -79,6 +91,8 @@ QByteArray APTDemodSettings::serialize() const
|
||||||
s.writeBool(15, m_autoSave);
|
s.writeBool(15, m_autoSave);
|
||||||
s.writeString(16, m_autoSavePath);
|
s.writeString(16, m_autoSavePath);
|
||||||
s.writeS32(17, m_autoSaveMinScanLines);
|
s.writeS32(17, m_autoSaveMinScanLines);
|
||||||
|
s.writeBool(18, m_saveProjection);
|
||||||
|
s.writeS32(19, m_scanlinesPerImageUpdate);
|
||||||
|
|
||||||
if (m_channelMarker) {
|
if (m_channelMarker) {
|
||||||
s.writeBlob(20, m_channelMarker->serialize());
|
s.writeBlob(20, m_channelMarker->serialize());
|
||||||
|
@ -96,6 +110,17 @@ QByteArray APTDemodSettings::serialize() const
|
||||||
s.writeBlob(28, m_rollupState->serialize());
|
s.writeBlob(28, m_rollupState->serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.writeBool(29, m_saveCombined);
|
||||||
|
s.writeBool(30, m_saveSeparate);
|
||||||
|
s.writeS32(31, m_transparencyThreshold);
|
||||||
|
s.writeS32(32, m_opacityThreshold);
|
||||||
|
s.writeString(33, m_palettes.join(";"));
|
||||||
|
s.writeS32(34, m_palette);
|
||||||
|
s.writeS32(35, m_horizontalPixelsPerDegree);
|
||||||
|
s.writeS32(36, m_verticalPixelsPerDegree);
|
||||||
|
s.writeFloat(37, m_satTimeOffset);
|
||||||
|
s.writeFloat(38, m_satYaw);
|
||||||
|
|
||||||
return s.final();
|
return s.final();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +157,8 @@ bool APTDemodSettings::deserialize(const QByteArray& data)
|
||||||
d.readBool(15, &m_autoSave, false);
|
d.readBool(15, &m_autoSave, false);
|
||||||
d.readString(16, &m_autoSavePath, "");
|
d.readString(16, &m_autoSavePath, "");
|
||||||
d.readS32(17, &m_autoSaveMinScanLines, 200);
|
d.readS32(17, &m_autoSaveMinScanLines, 200);
|
||||||
|
d.readBool(18, &m_saveProjection, false);
|
||||||
|
d.readS32(19, &m_scanlinesPerImageUpdate, 20);
|
||||||
|
|
||||||
if (m_channelMarker)
|
if (m_channelMarker)
|
||||||
{
|
{
|
||||||
|
@ -162,6 +189,19 @@ bool APTDemodSettings::deserialize(const QByteArray& data)
|
||||||
m_rollupState->deserialize(bytetmp);
|
m_rollupState->deserialize(bytetmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.readBool(29, &m_saveCombined, true);
|
||||||
|
d.readBool(30, &m_saveSeparate, false);
|
||||||
|
d.readS32(31, &m_transparencyThreshold, 100);
|
||||||
|
d.readS32(32, &m_opacityThreshold, 200);
|
||||||
|
d.readString(33, &strtmp);
|
||||||
|
m_palettes = strtmp.split(";");
|
||||||
|
m_palettes.removeAll("");
|
||||||
|
d.readS32(34, &m_palette, 0);
|
||||||
|
d.readS32(35, &m_horizontalPixelsPerDegree, 10);
|
||||||
|
d.readS32(36, &m_verticalPixelsPerDegree, 20);
|
||||||
|
d.readFloat(37, &m_satTimeOffset, 0.0f);
|
||||||
|
d.readFloat(38, &m_satYaw, 0.0f);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
class Serializable;
|
class Serializable;
|
||||||
|
|
||||||
|
@ -35,18 +36,29 @@ struct APTDemodSettings
|
||||||
bool m_histogramEqualise;
|
bool m_histogramEqualise;
|
||||||
bool m_precipitationOverlay;
|
bool m_precipitationOverlay;
|
||||||
bool m_flip;
|
bool m_flip;
|
||||||
enum ChannelSelection {BOTH_CHANNELS, CHANNEL_A, CHANNEL_B} m_channels;
|
enum ChannelSelection {BOTH_CHANNELS, CHANNEL_A, CHANNEL_B, TEMPERATURE, PALETTE} m_channels;
|
||||||
bool m_decodeEnabled;
|
bool m_decodeEnabled;
|
||||||
bool m_satelliteTrackerControl; //! Whether Sat Tracker can set direction of pass
|
bool m_satelliteTrackerControl; //! Whether Sat Tracker can set direction of pass
|
||||||
QString m_satelliteName; //!< All, NOAA 15, NOAA 18 or NOAA 19
|
QString m_satelliteName; //!< All, NOAA 15, NOAA 18 or NOAA 19
|
||||||
bool m_autoSave;
|
bool m_autoSave;
|
||||||
QString m_autoSavePath;
|
QString m_autoSavePath;
|
||||||
int m_autoSaveMinScanLines;
|
int m_autoSaveMinScanLines;
|
||||||
|
bool m_saveCombined;
|
||||||
|
bool m_saveSeparate;
|
||||||
|
bool m_saveProjection;
|
||||||
|
int m_scanlinesPerImageUpdate;
|
||||||
|
int m_transparencyThreshold;
|
||||||
|
int m_opacityThreshold;
|
||||||
|
QStringList m_palettes; // List of 256x256 images to use a colour palette
|
||||||
|
int m_palette; // Index in to m_palettes - only if m_channels==PALETTE
|
||||||
|
int m_horizontalPixelsPerDegree; // Resolution for projected image
|
||||||
|
int m_verticalPixelsPerDegree;
|
||||||
|
float m_satTimeOffset;
|
||||||
|
float m_satYaw;
|
||||||
|
|
||||||
quint32 m_rgbColor;
|
quint32 m_rgbColor;
|
||||||
QString m_title;
|
QString m_title;
|
||||||
Serializable *m_channelMarker;
|
Serializable *m_channelMarker;
|
||||||
QString m_audioDeviceName;
|
|
||||||
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
|
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
|
||||||
bool m_useReverseAPI;
|
bool m_useReverseAPI;
|
||||||
QString m_reverseAPIAddress;
|
QString m_reverseAPIAddress;
|
||||||
|
@ -55,6 +67,11 @@ struct APTDemodSettings
|
||||||
uint16_t m_reverseAPIChannelIndex;
|
uint16_t m_reverseAPIChannelIndex;
|
||||||
Serializable *m_rollupState;
|
Serializable *m_rollupState;
|
||||||
|
|
||||||
|
// The following are really working state, rather than settings
|
||||||
|
QString m_tle; // Satelite two-line elements, from satellite tracker
|
||||||
|
QDateTime m_aosDateTime; // When decoder was started (may not be current time, if replaying old file)
|
||||||
|
bool m_northToSouth; // Separate from flip, in case user changes it
|
||||||
|
|
||||||
APTDemodSettings();
|
APTDemodSettings();
|
||||||
void resetToDefaults();
|
void resetToDefaults();
|
||||||
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
||||||
|
|
|
@ -25,12 +25,27 @@ APTDemodSettingsDialog::APTDemodSettingsDialog(APTDemodSettings *settings, QWidg
|
||||||
m_settings(settings),
|
m_settings(settings),
|
||||||
ui(new Ui::APTDemodSettingsDialog)
|
ui(new Ui::APTDemodSettingsDialog)
|
||||||
{
|
{
|
||||||
|
int idx;
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->satelliteTrackerControl->setChecked(settings->m_satelliteTrackerControl);
|
ui->satelliteTrackerControl->setChecked(settings->m_satelliteTrackerControl);
|
||||||
ui->satellite->setCurrentText(settings->m_satelliteName);
|
ui->satellite->setCurrentText(settings->m_satelliteName);
|
||||||
ui->autoSave->setChecked(settings->m_autoSave);
|
ui->autoSave->setChecked(settings->m_autoSave);
|
||||||
|
ui->saveCombined->setChecked(settings->m_saveCombined);
|
||||||
|
ui->saveSeparate->setChecked(settings->m_saveSeparate);
|
||||||
|
ui->saveProjection->setChecked(settings->m_saveProjection);
|
||||||
ui->autoSavePath->setText(settings->m_autoSavePath);
|
ui->autoSavePath->setText(settings->m_autoSavePath);
|
||||||
ui->minScanlines->setValue(settings->m_autoSaveMinScanLines);
|
ui->minScanlines->setValue(settings->m_autoSaveMinScanLines);
|
||||||
|
ui->scanlinesPerImageUpdate->setValue(settings->m_scanlinesPerImageUpdate);
|
||||||
|
idx = ui->horizontalPixelsPerDegree->findText(QString::number(settings->m_horizontalPixelsPerDegree));
|
||||||
|
ui->horizontalPixelsPerDegree->setCurrentIndex(idx);
|
||||||
|
idx = ui->verticalPixelsPerDegree->findText(QString::number(settings->m_verticalPixelsPerDegree));
|
||||||
|
ui->verticalPixelsPerDegree->setCurrentIndex(idx);
|
||||||
|
ui->satTimeOffset->setValue(settings->m_satTimeOffset);
|
||||||
|
ui->satYaw->setValue(settings->m_satYaw);
|
||||||
|
for (auto file : settings->m_palettes) {
|
||||||
|
ui->palettes->addItem(file);
|
||||||
|
}
|
||||||
|
on_autoSave_clicked(settings->m_autoSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
APTDemodSettingsDialog::~APTDemodSettingsDialog()
|
APTDemodSettingsDialog::~APTDemodSettingsDialog()
|
||||||
|
@ -43,8 +58,20 @@ void APTDemodSettingsDialog::accept()
|
||||||
m_settings->m_satelliteTrackerControl = ui->satelliteTrackerControl->isChecked();
|
m_settings->m_satelliteTrackerControl = ui->satelliteTrackerControl->isChecked();
|
||||||
m_settings->m_satelliteName = ui->satellite->currentText();
|
m_settings->m_satelliteName = ui->satellite->currentText();
|
||||||
m_settings->m_autoSave = ui->autoSave->isChecked();
|
m_settings->m_autoSave = ui->autoSave->isChecked();
|
||||||
|
m_settings->m_saveCombined = ui->saveCombined->isChecked();
|
||||||
|
m_settings->m_saveSeparate = ui->saveSeparate->isChecked();
|
||||||
|
m_settings->m_saveProjection = ui->saveProjection->isChecked();
|
||||||
m_settings->m_autoSavePath = ui->autoSavePath->text();
|
m_settings->m_autoSavePath = ui->autoSavePath->text();
|
||||||
m_settings->m_autoSaveMinScanLines = ui->minScanlines->value();
|
m_settings->m_autoSaveMinScanLines = ui->minScanlines->value();
|
||||||
|
m_settings->m_scanlinesPerImageUpdate = ui->scanlinesPerImageUpdate->value();
|
||||||
|
m_settings->m_palettes.clear();
|
||||||
|
m_settings->m_horizontalPixelsPerDegree = ui->horizontalPixelsPerDegree->currentText().toInt();
|
||||||
|
m_settings->m_verticalPixelsPerDegree = ui->verticalPixelsPerDegree->currentText().toInt();
|
||||||
|
m_settings->m_satTimeOffset = ui->satTimeOffset->value();
|
||||||
|
m_settings->m_satYaw = ui->satYaw->value();
|
||||||
|
for (int i = 0; i < ui->palettes->count(); i++) {
|
||||||
|
m_settings->m_palettes.append(ui->palettes->item(i)->text());
|
||||||
|
}
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,3 +81,41 @@ void APTDemodSettingsDialog::on_autoSavePathBrowse_clicked()
|
||||||
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||||
ui->autoSavePath->setText(dir);
|
ui->autoSavePath->setText(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APTDemodSettingsDialog::on_autoSave_clicked(bool checked)
|
||||||
|
{
|
||||||
|
/* Commented out until theme greys out disabled widgets
|
||||||
|
ui->saveProjectionLabel->setEnabled(checked);
|
||||||
|
ui->saveCombined->setEnabled(checked);
|
||||||
|
ui->saveSeparate->setEnabled(checked);
|
||||||
|
ui->saveProjection->setEnabled(checked);
|
||||||
|
ui->autoSavePathLabel->setEnabled(checked);
|
||||||
|
ui->autoSavePath->setEnabled(checked);
|
||||||
|
ui->autoSavePathBrowse->setEnabled(checked);
|
||||||
|
ui->minScanlinesLabel->setEnabled(checked);
|
||||||
|
ui->minScanlines->setEnabled(checked);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodSettingsDialog::on_addPalette_clicked()
|
||||||
|
{
|
||||||
|
QFileDialog fileDialog(nullptr, "Select palette files", "", "*.png;*.bmp");
|
||||||
|
fileDialog.setFileMode(QFileDialog::ExistingFiles);
|
||||||
|
if (fileDialog.exec())
|
||||||
|
{
|
||||||
|
QStringList fileNames = fileDialog.selectedFiles();
|
||||||
|
for (auto fileName : fileNames) {
|
||||||
|
ui->palettes->addItem(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APTDemodSettingsDialog::on_removePalette_clicked()
|
||||||
|
{
|
||||||
|
QList<QListWidgetItem *> items = ui->palettes->selectedItems();
|
||||||
|
for (auto item : items)
|
||||||
|
{
|
||||||
|
ui->palettes->removeItemWidget(item);
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,9 @@ public:
|
||||||
private slots:
|
private slots:
|
||||||
void accept();
|
void accept();
|
||||||
void on_autoSavePathBrowse_clicked();
|
void on_autoSavePathBrowse_clicked();
|
||||||
|
void on_autoSave_clicked(bool checked);
|
||||||
|
void on_addPalette_clicked();
|
||||||
|
void on_removePalette_clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::APTDemodSettingsDialog* ui;
|
Ui::APTDemodSettingsDialog* ui;
|
||||||
|
|
|
@ -6,13 +6,12 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>385</width>
|
<width>600</width>
|
||||||
<height>212</height>
|
<height>576</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<family>Liberation Sans</family>
|
|
||||||
<pointsize>9</pointsize>
|
<pointsize>9</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
|
@ -29,69 +28,31 @@
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
<item row="5" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="autoSavePathLabel">
|
<widget class="QLabel" name="satelliteTrackerControlLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Path to save image</string>
|
<string>Enable Satellite Tracker control</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="0" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<widget class="QCheckBox" name="satelliteTrackerControl">
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="autoSavePath">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Path to save images to</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="autoSavePathBrowse">
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
|
||||||
<normaloff>:/load.png</normaloff>:/load.png</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="0">
|
|
||||||
<widget class="QLabel" name="minScanlinesLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Minimum scanlines</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="1">
|
|
||||||
<widget class="QSpinBox" name="minScanlines">
|
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Enter the minimum number of scanlines in an image (after cropping) for it to be automatically saved</string>
|
<string>Check to enable control by Satellite Tracker feature</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum">
|
<property name="text">
|
||||||
<number>1</number>
|
<string/>
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>30000</number>
|
|
||||||
</property>
|
|
||||||
<property name="singleStep">
|
|
||||||
<number>100</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>200</number>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="satelliteLabel">
|
<widget class="QLabel" name="satelliteLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Satellite</string>
|
<string>Satellite</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QComboBox" name="satellite">
|
<widget class="QComboBox" name="satellite">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Select which satellite this channel will be used for</string>
|
<string>Select which satellite this channel will be used for</string>
|
||||||
|
@ -121,23 +82,316 @@
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" colspan="2">
|
<item row="4" column="0">
|
||||||
<widget class="QCheckBox" name="satelliteTrackerControl">
|
<widget class="QLabel" name="autoSaveLabel">
|
||||||
<property name="toolTip">
|
|
||||||
<string>Check to enable control by Satellite Tracker feature</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Satellite Tracker control</string>
|
<string>Auto save images</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="2">
|
<item row="4" column="1">
|
||||||
<widget class="QCheckBox" name="autoSave">
|
<widget class="QCheckBox" name="autoSave">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Check to automatically save images when acquisition is stopped or LOS</string>
|
<string>Check to automatically save images when acquisition is stopped or LOS</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Auto save image</string>
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="saveCombinedLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save combined image</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QCheckBox" name="saveCombined">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Save a combined image of both channel A and B</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="saveSeparateLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save separate images</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QCheckBox" name="saveSeparate">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Save images from channels A and B to separate files</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="saveProjectionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save projected images</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QCheckBox" name="saveProjection">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Saves the equidistant cylindrical projected image</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="0">
|
||||||
|
<widget class="QLabel" name="autoSavePathLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Path to save images</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="autoSavePath">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Path to save images to</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="autoSavePathBrowse">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||||
|
<normaloff>:/load.png</normaloff>:/load.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="0">
|
||||||
|
<widget class="QLabel" name="minScanlinesLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Minimum scanlines for auto save</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="1">
|
||||||
|
<widget class="QSpinBox" name="minScanlines">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Enter the minimum number of scanlines in an image (after cropping) for it to be automatically saved</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>30000</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>200</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="12" column="0">
|
||||||
|
<widget class="QLabel" name="scanlinesPerImageUpdateLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Scanlines per image update</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="12" column="1">
|
||||||
|
<widget class="QSpinBox" name="scanlinesPerImageUpdate">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>How often the image processing functions are applied to the image and how often it is sent to the map</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>9999</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="17" column="0">
|
||||||
|
<widget class="QLabel" name="palettesLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Colour palettes</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="17" column="1">
|
||||||
|
<widget class="QListWidget" name="palettes">
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::MultiSelection</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="18" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="buttonsHhorizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="addPalette">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="removePalette">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="buttonsHorizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="13" column="1">
|
||||||
|
<widget class="QComboBox" name="horizontalPixelsPerDegree">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Number of pixels per degree longitude in projected image</string>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>10</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>15</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>20</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="14" column="1">
|
||||||
|
<widget class="QComboBox" name="verticalPixelsPerDegree">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Number of pixels per degree latitude in projected image</string>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>20</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>30</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>40</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>45</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>50</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>55</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>60</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="13" column="0">
|
||||||
|
<widget class="QLabel" name="horizontalPixelsPerDegreeLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Horizontal pixels per degree</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="14" column="0">
|
||||||
|
<widget class="QLabel" name="verticalPixelsPerDegreeLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Vertical pixels per degree</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="15" column="0">
|
||||||
|
<widget class="QLabel" name="satTimeOffsetLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Satellite position time offset (s)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="15" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="satTimeOffset">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Time offset in seconds to add when calculating satellites position.
|
||||||
|
This may be used to help align images on the map.</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>-100.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="16" column="0">
|
||||||
|
<widget class="QLabel" name="satYawLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Satellite yaw correction (°)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="16" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="satYaw">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Add yaw offset to help with aligning images on the map.</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>-10.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>10.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.250000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -160,9 +414,16 @@
|
||||||
<tabstop>satelliteTrackerControl</tabstop>
|
<tabstop>satelliteTrackerControl</tabstop>
|
||||||
<tabstop>satellite</tabstop>
|
<tabstop>satellite</tabstop>
|
||||||
<tabstop>autoSave</tabstop>
|
<tabstop>autoSave</tabstop>
|
||||||
|
<tabstop>saveCombined</tabstop>
|
||||||
|
<tabstop>saveSeparate</tabstop>
|
||||||
|
<tabstop>saveProjection</tabstop>
|
||||||
<tabstop>autoSavePath</tabstop>
|
<tabstop>autoSavePath</tabstop>
|
||||||
<tabstop>autoSavePathBrowse</tabstop>
|
<tabstop>autoSavePathBrowse</tabstop>
|
||||||
<tabstop>minScanlines</tabstop>
|
<tabstop>minScanlines</tabstop>
|
||||||
|
<tabstop>scanlinesPerImageUpdate</tabstop>
|
||||||
|
<tabstop>palettes</tabstop>
|
||||||
|
<tabstop>addPalette</tabstop>
|
||||||
|
<tabstop>removePalette</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||||
|
|
|
@ -81,6 +81,12 @@ APTDemodActions:
|
||||||
northToSouthPass:
|
northToSouthPass:
|
||||||
description: "Satellite is passing from the North to the South (1) or South to North (0)"
|
description: "Satellite is passing from the North to the South (1) or South to North (0)"
|
||||||
type: integer
|
type: integer
|
||||||
|
tle:
|
||||||
|
description: "Two line elements for satellite"
|
||||||
|
type: string
|
||||||
|
dateTime:
|
||||||
|
description: "Date and time of AOS (May differ from system clock when replaying old passes)"
|
||||||
|
type: string
|
||||||
los:
|
los:
|
||||||
description: "Loss of signal"
|
description: "Loss of signal"
|
||||||
type: object
|
type: object
|
||||||
|
|
Ładowanie…
Reference in New Issue