From 64d75baadfc1f5300dc5fe93e447732e984d241a Mon Sep 17 00:00:00 2001
From: srcejon <jon@beniston.com>
Date: Tue, 18 Jun 2024 16:26:08 +0100
Subject: [PATCH] Heat Map: Handle memory allocation errors. Allow selecting
 which data to be saved to reduce memory requirements.

---
 plugins/channelrx/heatmap/heatmapgui.cpp      | 311 ++++++++++++++----
 plugins/channelrx/heatmap/heatmapgui.h        |   7 +-
 plugins/channelrx/heatmap/heatmapgui.ui       | 121 ++++++-
 plugins/channelrx/heatmap/heatmapsettings.cpp |  15 +
 plugins/channelrx/heatmap/heatmapsettings.h   |   5 +
 5 files changed, 388 insertions(+), 71 deletions(-)

diff --git a/plugins/channelrx/heatmap/heatmapgui.cpp b/plugins/channelrx/heatmap/heatmapgui.cpp
index 7bbdb5022..13f4cdb0a 100644
--- a/plugins/channelrx/heatmap/heatmapgui.cpp
+++ b/plugins/channelrx/heatmap/heatmapgui.cpp
@@ -22,7 +22,6 @@
 #include <QDebug>
 #include <QMessageBox>
 #include <QAction>
-#include <QRegExp>
 #include <QClipboard>
 #include <QBuffer>
 
@@ -226,9 +225,17 @@ void HeatMapGUI::on_mode_currentIndexChanged(int index)
         ui->writeImage->setEnabled(!none);
         ui->writeCSV->setEnabled(!none);
         ui->readCSV->setEnabled(!none);
-        if (none) {
+        ui->colorMapLabel->setEnabled(!none);
+        ui->colorMap->setEnabled(!none);
+        if (none)
+        {
             deleteFromMap();
-        } else {
+        }
+        else
+        {
+            if (m_image.isNull()) {
+                createImage(m_width, m_height);
+            }
             plotMap();
         }
         applySettings();
@@ -551,6 +558,9 @@ HeatMapGUI::HeatMapGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS
     m_heatMap = reinterpret_cast<HeatMap*>(rxChannel);
     m_heatMap->setMessageQueueToGUI(getInputMessageQueue());
 
+    // Disable 256MB limit on image size
+    QImageReader::setAllocationLimit(0);
+
     connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
 
     m_scopeVis = m_heatMap->getScopeSink();
@@ -708,6 +718,11 @@ void HeatMapGUI::displaySettings()
     ui->displayPulseAverage->setChecked(m_settings.m_displayPulseAverage);
     ui->displayPathLoss->setChecked(m_settings.m_displayPathLoss);
     ui->displayMins->setValue(m_settings.m_displayMins);
+    ui->recordAverage->setChecked(m_settings.m_recordAverage);
+    ui->recordMax->setChecked(m_settings.m_recordMax);
+    ui->recordMin->setChecked(m_settings.m_recordMin);
+    ui->recordPulseAverage->setChecked(m_settings.m_recordPulseAverage);
+    ui->recordPathLoss->setChecked(m_settings.m_recordPathLoss);
 
     m_scopeVis->setLiveRate(m_settings.m_sampleRate);
 
@@ -765,7 +780,9 @@ void HeatMapGUI::tick()
         if (!std::isnan(magAvg))
         {
             powDbAvg = CalcDb::dbPower(magAvg * magAvg);
-            m_powerAverage[idx] = powDbAvg;
+            if (m_powerAverage) {
+                m_powerAverage[idx] = powDbAvg;
+            }
             if (m_tickCount % 4 == 0) {
                 ui->average->setText(QString::number(powDbAvg, 'f', 1));
             }
@@ -777,7 +794,9 @@ void HeatMapGUI::tick()
         if (!std::isnan(magPulseAvg))
         {
             powDbPulseAvg = CalcDb::dbPower(magPulseAvg * magPulseAvg);
-            m_powerPulseAverage[idx] = powDbPulseAvg;
+            if (m_powerPulseAverage) {
+                m_powerPulseAverage[idx] = powDbPulseAvg;
+            }
             if (m_tickCount % 4 == 0) {
                 ui->pulseAverage->setText(QString::number(powDbPulseAvg, 'f', 1));
             }
@@ -789,7 +808,9 @@ void HeatMapGUI::tick()
         if (magMaxPeak != -std::numeric_limits<double>::max())
         {
             powDbMaxPeak = CalcDb::dbPower(magMaxPeak * magMaxPeak);
-            m_powerMaxPeak[idx] = powDbMaxPeak;
+            if (m_powerMaxPeak) {
+                m_powerMaxPeak[idx] = powDbMaxPeak;
+            }
             if (m_tickCount % 4 == 0) {
                 ui->maxPeak->setText(QString::number(powDbMaxPeak, 'f', 1));
             }
@@ -801,7 +822,9 @@ void HeatMapGUI::tick()
         if (magMinPeak != std::numeric_limits<double>::max())
         {
             powDbMinPeak = CalcDb::dbPower(magMinPeak * magMinPeak);
-            m_powerMinPeak[idx] = powDbMinPeak;
+            if (m_powerMinPeak) {
+                m_powerMinPeak[idx] = powDbMinPeak;
+            }
             if (m_tickCount % 4 == 0) {
                 ui->minPeak->setText(QString::number(powDbMinPeak, 'f', 1));
             }
@@ -814,7 +837,9 @@ void HeatMapGUI::tick()
         double range = calcRange(m_latitude, m_longitude);
         double frequency = m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset;
         powDbPathLoss = m_settings.m_txPower - calcFreeSpacePathLoss(range, frequency);
-        m_powerPathLoss[idx] = powDbPathLoss;
+        if (m_powerPathLoss) {
+            m_powerPathLoss[idx] = powDbPathLoss;
+        }
 
         if (m_heatMap->getDeviceAPI()->state(0) == DeviceAPI::StRunning)
         {
@@ -823,9 +848,12 @@ void HeatMapGUI::tick()
             {
                 // Plot newest measurement on map
                 float *power = getCurrentModePowerData();
-                double powDb = power[idx];
-                if (!std::isnan(powDb)) {
-                    plotPixel(m_x, m_y, powDb);
+                if (power)
+                {
+                    double powDb = power[idx];
+                    if (!std::isnan(powDb)) {
+                        plotPixel(m_x, m_y, powDb);
+                    }
                 }
             }
 
@@ -882,9 +910,15 @@ void HeatMapGUI::updatePower(double latitude, double longitude, float power)
 
 void HeatMapGUI::plotMap()
 {
-    clearImage();
-    plotMap(getCurrentModePowerData());
-    sendToMap();
+    if ((m_settings.m_mode != HeatMapSettings::None) && !m_image.isNull())
+    {
+        clearImage();
+        float *data = getCurrentModePowerData();
+        if (data) {
+            plotMap(data);
+        }
+        sendToMap();
+    }
 }
 
 void HeatMapGUI::clearPower()
@@ -903,7 +937,35 @@ void HeatMapGUI::clearPower(float *power)
 
 void HeatMapGUI::clearPower(float *power, int size)
 {
-    std::fill_n(power, size, std::numeric_limits<float>::quiet_NaN());
+    if (power) {
+        std::fill_n(power, size, std::numeric_limits<float>::quiet_NaN());
+    }
+}
+
+void HeatMapGUI::createImage(int width, int height)
+{
+    if (!m_image.isNull()) {
+        m_painter.end();
+    }
+
+    try
+    {
+        if (m_settings.m_mode != HeatMapSettings::None)
+        {
+            qDebug() << "HeatMapGUI::createImage" << width << "*" << height;
+            m_image = QImage(width, height, QImage::Format_ARGB32);
+            m_painter.begin(&m_image);
+        }
+        else
+        {
+            m_image = QImage();
+        }
+    }
+    catch (std::bad_alloc&)
+    {
+        m_image = QImage();
+        QMessageBox::critical(this, "Heat Map", QString("Failed to allocate memory (width=%1 height=%2)").arg(m_width).arg(m_height));
+    }
 }
 
 void HeatMapGUI::clearImage()
@@ -929,6 +991,10 @@ void HeatMapGUI::plotMap(float *power)
 
 void HeatMapGUI::plotPixel(int x, int y, double power)
 {
+    if (m_image.isNull()) {
+        return;
+    }
+
     // Normalise to [0,1]
     float powNorm = (power - m_settings.m_minPower) / (m_settings.m_maxPower - m_settings.m_minPower);
     if (powNorm < 0) {
@@ -1017,6 +1083,11 @@ void HeatMapGUI::makeUIConnections()
     QObject::connect(ui->displayPulseAverage, &QCheckBox::clicked, this, &HeatMapGUI::on_displayPulseAverage_clicked);
     QObject::connect(ui->displayPathLoss, &QCheckBox::clicked, this, &HeatMapGUI::on_displayPathLoss_clicked);
     QObject::connect(ui->displayMins, QOverload<int>::of(&QSpinBox::valueChanged), this, &HeatMapGUI::on_displayMins_valueChanged);
+    QObject::connect(ui->recordAverage, &QCheckBox::clicked, this, &HeatMapGUI::on_recordAverage_clicked);
+    QObject::connect(ui->recordMax, &QCheckBox::clicked, this, &HeatMapGUI::on_recordMax_clicked);
+    QObject::connect(ui->recordMin, &QCheckBox::clicked, this, &HeatMapGUI::on_recordMin_clicked);
+    QObject::connect(ui->recordPulseAverage, &QCheckBox::clicked, this, &HeatMapGUI::on_recordPulseAverage_clicked);
+    QObject::connect(ui->recordPathLoss, &QCheckBox::clicked, this, &HeatMapGUI::on_recordPathLoss_clicked);
 }
 
 void HeatMapGUI::updateAbsoluteCenterFrequency()
@@ -1171,21 +1242,48 @@ void HeatMapGUI::createMap()
     m_degreesLonPerPixel = m_resolution / scale / (earthCircumference / 360.0);
     m_degreesLatPerPixel = m_resolution  / (earthCircumference / 360.0);
     int size = m_width * m_height;
-    m_powerAverage = new float[size];
-    m_powerPulseAverage = new float[size];
-    m_powerMaxPeak = new float[size];
-    m_powerMinPeak = new float[size];
-    m_powerPathLoss = new float[size];
+    try
+    {
+        if (m_settings.m_recordAverage) {
+            m_powerAverage = new float[size];
+        } else {
+            m_powerAverage = nullptr;
+        }
+        if (m_settings.m_recordPulseAverage) {
+            m_powerPulseAverage = new float[size];
+        } else {
+            m_powerPulseAverage = nullptr;
+        }
+        if (m_settings.m_recordMax) {
+            m_powerMaxPeak = new float[size];
+        } else {
+            m_powerMaxPeak = nullptr;
+        }
+        if (m_settings.m_recordMin) {
+            m_powerMinPeak = new float[size];
+        } else {
+            m_powerMinPeak = nullptr;
+        }
+        if (m_settings.m_recordPathLoss) {
+            m_powerPathLoss = new float[size];
+        } else {
+            m_powerPathLoss = nullptr;
+        }
 
-    m_north = m_latitude + m_degreesLatPerPixel * m_height / 2;
-    m_south = m_latitude - m_degreesLatPerPixel * m_height / 2;
-    m_east = m_longitude + m_degreesLonPerPixel * m_width / 2;
-    m_west = m_longitude - m_degreesLonPerPixel * m_width / 2;
-    m_x = m_width / 2;
-    m_y = m_height / 2;
+        m_north = m_latitude + m_degreesLatPerPixel * m_height / 2;
+        m_south = m_latitude - m_degreesLatPerPixel * m_height / 2;
+        m_east = m_longitude + m_degreesLonPerPixel * m_width / 2;
+        m_west = m_longitude - m_degreesLonPerPixel * m_width / 2;
+        m_x = m_width / 2;
+        m_y = m_height / 2;
 
-    m_image = QImage(m_width, m_height, QImage::Format_ARGB32);
-    m_painter.begin(&m_image);
+        createImage(m_width, m_height);
+    }
+    catch (std::bad_alloc&)
+    {
+        deleteMap();
+        QMessageBox::critical(this, "Heat Map", QString("Failed to allocate memory (width=%1 height=%2)").arg(m_width).arg(m_height));
+    }
 
     on_clearHeatMap_clicked();
 }
@@ -1203,7 +1301,9 @@ void HeatMapGUI::deleteMap()
     m_powerMinPeak = nullptr;
     delete[] m_powerPathLoss;
     m_powerPathLoss = nullptr;
-    m_painter.end();
+    if (!m_image.isNull()) {
+        m_painter.end();
+    }
 }
 
 void HeatMapGUI::resizeMap(int x, int y)
@@ -1245,47 +1345,92 @@ void HeatMapGUI::resizeMap(int x, int y)
             m_south -= m_blockSize * m_degreesLatPerPixel;
         }
 
+        float *powerAverage = nullptr;
+        float *powerPulseAverage = nullptr;
+        float *powerMaxPeak = nullptr;
+        float *powerMinPeak = nullptr;
+        float *powerPathLoss = nullptr;
+
         int newSize = newWidth * newHeight;
-        float *powerAverage = new float[newSize];
-        float *powerPulseAverage = new float[newSize];
-        float *powerMaxPeak = new float[newSize];
-        float *powerMinPeak = new float[newSize];
-        float *powerPathLoss = new float[newSize];
-        clearPower(powerAverage, newSize);
-        clearPower(powerPulseAverage, newSize);
-        clearPower(powerMaxPeak, newSize);
-        clearPower(powerMinPeak, newSize);
-        clearPower(powerPathLoss, newSize);
+        qDebug() << "HeatMapGUI::resizeMap:" << m_width << "*" << m_height << "to" << newWidth << "*" << newHeight;
 
-        // Copy across old data
-        for (int j = 0; j < m_height; j++)
+        try
         {
-            int srcStart = j * m_width;
-            int srcEnd = (j + 1) * m_width;
-            int destStart = j * newWidth + yOffset + xOffset;
-            //qDebug() << srcStart << srcEnd << destStart;
-            std::copy(m_powerAverage + srcStart, m_powerAverage + srcEnd, powerAverage + destStart);
-            std::copy(m_powerPulseAverage + srcStart, m_powerPulseAverage + srcEnd, powerPulseAverage + destStart);
-            std::copy(m_powerMaxPeak + srcStart, m_powerMaxPeak + srcEnd, powerMaxPeak + destStart);
-            std::copy(m_powerMinPeak + srcStart, m_powerMinPeak + srcEnd, powerMinPeak + destStart);
-            std::copy(m_powerPathLoss + srcStart, m_powerPathLoss + srcEnd, powerPathLoss + destStart);
-        }
+            // Allocate new memory
+            if (m_settings.m_recordAverage) {
+                powerAverage = new float[newSize];
+            }
+            if (m_settings.m_recordPulseAverage) {
+                powerPulseAverage = new float[newSize];
+            }
+            if (m_settings.m_recordMax) {
+                powerMaxPeak = new float[newSize];
+            }
+            if (m_settings.m_recordMin) {
+                powerMinPeak = new float[newSize];
+            }
+            if (m_settings.m_recordPathLoss) {
+                powerPathLoss = new float[newSize];
+            }
 
-        delete[] m_powerAverage;
-        delete[] m_powerPulseAverage;
-        delete[] m_powerMaxPeak;
-        delete[] m_powerMinPeak;
-        m_powerAverage = powerAverage;
-        m_powerPulseAverage = powerPulseAverage;
-        m_powerMaxPeak = powerMaxPeak;
-        m_powerMinPeak = powerMinPeak;
-        m_powerPathLoss = powerPathLoss;
-        m_width = newWidth;
-        m_height = newHeight;
-        m_painter.end();
-        m_image = QImage(m_width, m_height, QImage::Format_ARGB32);
-        m_painter.begin(&m_image);
-        plotMap();
+            clearPower(powerAverage, newSize);
+            clearPower(powerPulseAverage, newSize);
+            clearPower(powerMaxPeak, newSize);
+            clearPower(powerMinPeak, newSize);
+            clearPower(powerPathLoss, newSize);
+
+            // Copy across old data
+            for (int j = 0; j < m_height; j++)
+            {
+                int srcStart = j * m_width;
+                int srcEnd = (j + 1) * m_width;
+                int destStart = j * newWidth + yOffset + xOffset;
+                //qDebug() << srcStart << srcEnd << destStart;
+                if (powerAverage && m_powerAverage) {
+                    std::copy(m_powerAverage + srcStart, m_powerAverage + srcEnd, powerAverage + destStart);
+                }
+                if (powerPulseAverage && m_powerPulseAverage) {
+                    std::copy(m_powerPulseAverage + srcStart, m_powerPulseAverage + srcEnd, powerPulseAverage + destStart);
+                }
+                if (powerMaxPeak && m_powerMaxPeak) {
+                    std::copy(m_powerMaxPeak + srcStart, m_powerMaxPeak + srcEnd, powerMaxPeak + destStart);
+                }
+                if (powerMinPeak && m_powerMinPeak) {
+                    std::copy(m_powerMinPeak + srcStart, m_powerMinPeak + srcEnd, powerMinPeak + destStart);
+                }
+                if (powerPathLoss && m_powerPathLoss) {
+                    std::copy(m_powerPathLoss + srcStart, m_powerPathLoss + srcEnd, powerPathLoss + destStart);
+                }
+            }
+
+            createImage(newWidth, newHeight);
+
+            m_width = newWidth;
+            m_height = newHeight;
+
+            // Delete old memory
+            delete[] m_powerAverage;
+            delete[] m_powerPulseAverage;
+            delete[] m_powerMaxPeak;
+            delete[] m_powerMinPeak;
+            m_powerAverage = powerAverage;
+            m_powerPulseAverage = powerPulseAverage;
+            m_powerMaxPeak = powerMaxPeak;
+            m_powerMinPeak = powerMinPeak;
+            m_powerPathLoss = powerPathLoss;
+
+            plotMap();
+        }
+        catch (std::bad_alloc&)
+        {
+            // Detete partially allocated memory
+            delete[] powerAverage;
+            delete[] powerPulseAverage;
+            delete[] powerMaxPeak;
+            delete[] powerMinPeak;
+            delete[] powerPathLoss;
+            QMessageBox::critical(this, "Heat Map", QString("Failed to allocate memory (width=%1 height=%2)").arg(newWidth).arg(newHeight));
+        }
     }
 }
 
@@ -1462,3 +1607,37 @@ void HeatMapGUI::on_displayMins_valueChanged(int value)
     applySettings();
 }
 
+void HeatMapGUI::on_recordAverage_clicked(bool checked)
+{
+    m_settings.m_recordAverage = checked;
+    resizeMap(0, 0);
+    applySettings();
+}
+
+void HeatMapGUI::on_recordMax_clicked(bool checked)
+{
+    m_settings.m_recordMax = checked;
+    resizeMap(0, 0);
+    applySettings();
+}
+
+void HeatMapGUI::on_recordMin_clicked(bool checked)
+{
+    m_settings.m_recordMin = checked;
+    resizeMap(0, 0);
+    applySettings();
+}
+
+void HeatMapGUI::on_recordPulseAverage_clicked(bool checked)
+{
+    m_settings.m_recordPulseAverage = checked;
+    resizeMap(0, 0);
+    applySettings();
+}
+
+void HeatMapGUI::on_recordPathLoss_clicked(bool checked)
+{
+    m_settings.m_recordPathLoss = checked;
+    resizeMap(0, 0);
+    applySettings();
+}
diff --git a/plugins/channelrx/heatmap/heatmapgui.h b/plugins/channelrx/heatmap/heatmapgui.h
index b3f8d9409..398f05c35 100644
--- a/plugins/channelrx/heatmap/heatmapgui.h
+++ b/plugins/channelrx/heatmap/heatmapgui.h
@@ -142,7 +142,6 @@ private:
     void blockApplySettings(bool block);
     void applySettings(bool force = false);
     void displaySettings();
-    void messageReceived(const QByteArray& message, const QDateTime& dateTime);
     bool handleMessage(const Message& message);
     void makeUIConnections();
     void updateAbsoluteCenterFrequency();
@@ -158,6 +157,7 @@ private:
     void clearPower();
     void clearPower(float *power);
     void clearPower(float *power, int size);
+    void createImage(int width, int height);
     void clearImage();
     void updatePower(double latitude, double longitude, float power);
     void plotMap();
@@ -209,6 +209,11 @@ private slots:
     void on_displayPulseAverage_clicked(bool checked=false);
     void on_displayPathLoss_clicked(bool checked=false);
     void on_displayMins_valueChanged(int value);
+    void on_recordAverage_clicked(bool checked=false);
+    void on_recordMax_clicked(bool checked=false);
+    void on_recordMin_clicked(bool checked=false);
+    void on_recordPulseAverage_clicked(bool checked=false);
+    void on_recordPathLoss_clicked(bool checked=false);
     void onWidgetRolled(QWidget* widget, bool rollDown);
     void onMenuDialogCalled(const QPoint& p);
     void handleInputMessages();
diff --git a/plugins/channelrx/heatmap/heatmapgui.ui b/plugins/channelrx/heatmap/heatmapgui.ui
index 7cd3f80b8..c55d170b5 100644
--- a/plugins/channelrx/heatmap/heatmapgui.ui
+++ b/plugins/channelrx/heatmap/heatmapgui.ui
@@ -40,7 +40,7 @@
      <x>0</x>
      <y>0</y>
      <width>390</width>
-     <height>191</height>
+     <height>211</height>
     </rect>
    </property>
    <property name="minimumSize">
@@ -800,13 +800,127 @@
       </item>
      </layout>
     </item>
+    <item>
+     <widget class="Line" name="line_20">
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+     </widget>
+    </item>
+    <item>
+     <layout class="QHBoxLayout" name="recordLayout">
+      <item>
+       <widget class="QLabel" name="recordLabel">
+        <property name="text">
+         <string>Record</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="recordAverage">
+        <property name="statusTip">
+         <string>Check to record average power</string>
+        </property>
+        <property name="accessibleName">
+         <string/>
+        </property>
+        <property name="text">
+         <string>Avg</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="recordMax">
+        <property name="statusTip">
+         <string>Check to record max power</string>
+        </property>
+        <property name="text">
+         <string>Max</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="recordMin">
+        <property name="statusTip">
+         <string>Check to record min power</string>
+        </property>
+        <property name="text">
+         <string>Min</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="recordPulseAverage">
+        <property name="statusTip">
+         <string>Check to record pulse average power</string>
+        </property>
+        <property name="text">
+         <string>Pulse</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="recordPathLoss">
+        <property name="statusTip">
+         <string>Check to record path loss</string>
+        </property>
+        <property name="text">
+         <string>Path Loss</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer_9">
+        <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>
    </layout>
   </widget>
   <widget class="QWidget" name="dataContainer" native="true">
    <property name="geometry">
     <rect>
      <x>0</x>
-     <y>200</y>
+     <y>220</y>
      <width>391</width>
      <height>91</height>
     </rect>
@@ -1349,7 +1463,7 @@
    <property name="geometry">
     <rect>
      <x>0</x>
-     <y>300</y>
+     <y>320</y>
      <width>381</width>
      <height>121</height>
     </rect>
@@ -1617,7 +1731,6 @@
  </tabstops>
  <resources>
   <include location="../../../sdrgui/resources/res.qrc"/>
-  <include location="../demodadsb/icons.qrc"/>
  </resources>
  <connections/>
 </ui>
diff --git a/plugins/channelrx/heatmap/heatmapsettings.cpp b/plugins/channelrx/heatmap/heatmapsettings.cpp
index 5ff0d51f0..97e356315 100644
--- a/plugins/channelrx/heatmap/heatmapsettings.cpp
+++ b/plugins/channelrx/heatmap/heatmapsettings.cpp
@@ -50,6 +50,11 @@ void HeatMapSettings::resetToDefaults()
     m_displayPulseAverage = true;
     m_displayPathLoss = true;
     m_displayMins = 2;
+    m_recordAverage = true;
+    m_recordMax = true;
+    m_recordMin = true;
+    m_recordPulseAverage = true;
+    m_recordPathLoss = true;
     m_rgbColor = QColor(102, 40, 220).rgb();
     m_title = "Heat Map";
     m_streamIndex = 0;
@@ -86,6 +91,11 @@ QByteArray HeatMapSettings::serialize() const
     s.writeBool(18, m_displayPulseAverage);
     s.writeBool(19, m_displayPathLoss);
     s.writeS32(20, m_displayMins);
+    s.writeBool(40, m_recordAverage);
+    s.writeBool(41, m_recordMax);
+    s.writeBool(42, m_recordMin);
+    s.writeBool(43, m_recordPulseAverage);
+    s.writeBool(44, m_recordPathLoss);
 
     s.writeU32(21, m_rgbColor);
     s.writeString(22, m_title);
@@ -148,6 +158,11 @@ bool HeatMapSettings::deserialize(const QByteArray& data)
         d.readBool(18, &m_displayPulseAverage, true);
         d.readBool(19, &m_displayPathLoss, true);
         d.readS32(20, &m_displayMins, 2);
+        d.readBool(40, &m_recordAverage, true);
+        d.readBool(41, &m_recordMax, true);
+        d.readBool(42, &m_recordMin, true);
+        d.readBool(43, &m_recordPulseAverage, true);
+        d.readBool(44, &m_recordPathLoss, true);
 
         d.readU32(21, &m_rgbColor, QColor(102, 40, 220).rgb());
         d.readString(22, &m_title, "Heat Map");
diff --git a/plugins/channelrx/heatmap/heatmapsettings.h b/plugins/channelrx/heatmap/heatmapsettings.h
index 3c8282974..5acb28008 100644
--- a/plugins/channelrx/heatmap/heatmapsettings.h
+++ b/plugins/channelrx/heatmap/heatmapsettings.h
@@ -57,6 +57,11 @@ struct HeatMapSettings
     bool m_displayPulseAverage;
     bool m_displayPathLoss;
     int m_displayMins;
+    bool m_recordAverage;
+    bool m_recordMax;
+    bool m_recordMin;
+    bool m_recordPulseAverage;
+    bool m_recordPathLoss;
 
     quint32 m_rgbColor;
     QString m_title;