| 
									
										
										
										
											2021-06-09 16:17:59 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | // 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>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-10 20:10:19 +01:00
										 |  |  | #include <boost/math/interpolators/barycentric_rational.hpp>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-09 16:17:59 +01:00
										 |  |  | #include "noisefigureenrdialog.h"
 | 
					
						
							| 
									
										
										
										
											2021-06-10 20:10:19 +01:00
										 |  |  | #include "util/interpolation.h"
 | 
					
						
							| 
									
										
										
										
											2021-06-09 16:17:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | NoiseFigureENRDialog::NoiseFigureENRDialog(NoiseFigureSettings *settings, QWidget* parent) : | 
					
						
							|  |  |  |     QDialog(parent), | 
					
						
							|  |  |  |     m_settings(settings), | 
					
						
							| 
									
										
										
										
											2021-06-10 20:10:19 +01:00
										 |  |  |     m_chart(nullptr), | 
					
						
							| 
									
										
										
										
											2021-06-09 16:17:59 +01:00
										 |  |  |     ui(new Ui::NoiseFigureENRDialog) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ui->setupUi(this); | 
					
						
							|  |  |  |     ui->enr->sortByColumn(0, Qt::AscendingOrder); | 
					
						
							|  |  |  |     for (int i = 0; i < m_settings->m_enr.size(); i++) { | 
					
						
							|  |  |  |         addRow( m_settings->m_enr[i]->m_frequency, m_settings->m_enr[i]->m_enr); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-10 20:10:19 +01:00
										 |  |  |     ui->interpolation->setCurrentIndex((int)m_settings->m_interpolation); | 
					
						
							|  |  |  |     plotChart(); | 
					
						
							| 
									
										
										
										
											2021-06-09 16:17:59 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NoiseFigureENRDialog::~NoiseFigureENRDialog() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     delete ui; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NoiseFigureENRDialog::accept() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QDialog::accept(); | 
					
						
							|  |  |  |     qDeleteAll(m_settings->m_enr); | 
					
						
							|  |  |  |     m_settings->m_enr.clear(); | 
					
						
							|  |  |  |     ui->enr->sortByColumn(0, Qt::AscendingOrder); | 
					
						
							|  |  |  |     for (int i = 0; i < ui->enr->rowCount(); i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         QTableWidgetItem *freqItem = ui->enr->item(i, ENR_COL_FREQ); | 
					
						
							|  |  |  |         QTableWidgetItem *enrItem =  ui->enr->item(i, ENR_COL_ENR); | 
					
						
							|  |  |  |         double freqValue = freqItem->data(Qt::DisplayRole).toDouble(); | 
					
						
							|  |  |  |         double enrValue = enrItem->data(Qt::DisplayRole).toDouble(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         NoiseFigureSettings::ENR *enr = new NoiseFigureSettings::ENR(freqValue, enrValue); | 
					
						
							|  |  |  |         m_settings->m_enr.append(enr); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-10 20:10:19 +01:00
										 |  |  |     m_settings->m_interpolation = (NoiseFigureSettings::Interpolation)ui->interpolation->currentIndex(); | 
					
						
							| 
									
										
										
										
											2021-06-09 16:17:59 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NoiseFigureENRDialog::addRow(double freq, double enr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ui->enr->setSortingEnabled(false); | 
					
						
							| 
									
										
										
										
											2021-06-10 20:10:19 +01:00
										 |  |  |     ui->enr->blockSignals(true); | 
					
						
							| 
									
										
										
										
											2021-06-09 16:17:59 +01:00
										 |  |  |     int row = ui->enr->rowCount(); | 
					
						
							|  |  |  |     ui->enr->setRowCount(row + 1); | 
					
						
							|  |  |  |     QTableWidgetItem *freqItem = new QTableWidgetItem(); | 
					
						
							|  |  |  |     QTableWidgetItem *enrItem = new QTableWidgetItem(); | 
					
						
							|  |  |  |     ui->enr->setItem(row, ENR_COL_FREQ, freqItem); | 
					
						
							|  |  |  |     ui->enr->setItem(row, ENR_COL_ENR, enrItem); | 
					
						
							|  |  |  |     freqItem->setData(Qt::DisplayRole, freq); | 
					
						
							|  |  |  |     enrItem->setData(Qt::DisplayRole, enr); | 
					
						
							| 
									
										
										
										
											2021-06-10 20:10:19 +01:00
										 |  |  |     ui->enr->blockSignals(false); | 
					
						
							| 
									
										
										
										
											2021-06-09 16:17:59 +01:00
										 |  |  |     ui->enr->setSortingEnabled(true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NoiseFigureENRDialog::on_addRow_clicked() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     addRow(0.0, 0.0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NoiseFigureENRDialog::on_deleteRow_clicked() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QModelIndexList indexList = ui->enr->selectionModel()->selectedRows(); | 
					
						
							|  |  |  |     if (!indexList.isEmpty()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int row = indexList.at(0).row(); | 
					
						
							|  |  |  |         ui->enr->removeRow(row); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-10 20:10:19 +01:00
										 |  |  |     plotChart(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NoiseFigureENRDialog::on_enr_cellChanged(int row, int column) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     (void) row; | 
					
						
							|  |  |  |     (void) column; | 
					
						
							|  |  |  |     plotChart(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NoiseFigureENRDialog::on_start_valueChanged(int value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     (void) value; | 
					
						
							|  |  |  |     plotChart(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NoiseFigureENRDialog::on_stop_valueChanged(int value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     (void) value; | 
					
						
							|  |  |  |     plotChart(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void NoiseFigureENRDialog::plotChart() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QChart *oldChart = m_chart; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_chart = new QChart(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_chart->layout()->setContentsMargins(0, 0, 0, 0); | 
					
						
							|  |  |  |     m_chart->setMargins(QMargins(1, 1, 1, 1)); | 
					
						
							|  |  |  |     m_chart->setTheme(QChart::ChartThemeDark); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int size = ui->enr->rowCount(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QLineSeries *linearSeries = new QLineSeries(); | 
					
						
							|  |  |  |     QLineSeries *barySeries = new QLineSeries(); | 
					
						
							|  |  |  |     double maxENR = 0.0; | 
					
						
							|  |  |  |     double minENR = 1000.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (size >= 2) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Sort in to ascending frequency
 | 
					
						
							|  |  |  |         std::vector<std::array<double,2>> points; | 
					
						
							|  |  |  |         for (int i = 0; i < size; i++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             QTableWidgetItem *freqItem = ui->enr->item(i, ENR_COL_FREQ); | 
					
						
							|  |  |  |             QTableWidgetItem *enrItem =  ui->enr->item(i, ENR_COL_ENR); | 
					
						
							|  |  |  |             double freq = freqItem->data(Qt::DisplayRole).toDouble(); | 
					
						
							|  |  |  |             double enr = enrItem->data(Qt::DisplayRole).toDouble(); | 
					
						
							|  |  |  |             std::array<double,2> p = {freq, enr}; | 
					
						
							|  |  |  |             points.push_back(p); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         sort(points.begin(), points.end()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Copy to vectors for interpolation routines. Is there a better way?
 | 
					
						
							|  |  |  |         std::vector<double> x(size); | 
					
						
							|  |  |  |         std::vector<double> y(size); | 
					
						
							|  |  |  |         for (int i = 0; i < size; i++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             x[i] = points[i][0]; | 
					
						
							|  |  |  |             y[i] = points[i][1]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         int order = size - 1; | 
					
						
							|  |  |  |         boost::math::barycentric_rational<double> interpolant(std::move(x), std::move(y), order); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         x.resize(size); | 
					
						
							|  |  |  |         y.resize(size); | 
					
						
							|  |  |  |         for (int i = 0; i < size; i++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             x[i] = points[i][0]; | 
					
						
							|  |  |  |             y[i] = points[i][1]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Plot interpolated ENR using both methods for comparison
 | 
					
						
							|  |  |  |         linearSeries->setName("Linear"); | 
					
						
							|  |  |  |         barySeries->setName("Barycentric"); | 
					
						
							|  |  |  |         double startFreq = ui->start->value(); | 
					
						
							|  |  |  |         double stopFreq = ui->stop->value(); | 
					
						
							|  |  |  |         double step = (stopFreq - startFreq) / 50.0; | 
					
						
							|  |  |  |         for (double freq = startFreq; freq < stopFreq; freq += step) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             double enr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Linear interpolation
 | 
					
						
							|  |  |  |             enr = Interpolation::linear(x.begin(), x.end(), y.begin(), freq); | 
					
						
							|  |  |  |             linearSeries->append(freq, enr); | 
					
						
							|  |  |  |             minENR = std::min(minENR, enr); | 
					
						
							|  |  |  |             maxENR = std::max(maxENR, enr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Barycentric interpolation
 | 
					
						
							|  |  |  |             enr = interpolant(freq); | 
					
						
							|  |  |  |             barySeries->append(freq, enr); | 
					
						
							|  |  |  |             minENR = std::min(minENR, enr); | 
					
						
							|  |  |  |             maxENR = std::max(maxENR, enr); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QValueAxis *xAxis = new QValueAxis(); | 
					
						
							|  |  |  |     QValueAxis *yAxis = new QValueAxis(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_chart->addAxis(xAxis, Qt::AlignBottom); | 
					
						
							|  |  |  |     m_chart->addAxis(yAxis, Qt::AlignLeft); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     xAxis->setTitleText("Frequency (MHz)"); | 
					
						
							|  |  |  |     yAxis->setTitleText("ENR (dB)"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_chart->addSeries(linearSeries); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_chart->addSeries(barySeries); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     linearSeries->attachAxis(xAxis); | 
					
						
							|  |  |  |     linearSeries->attachAxis(yAxis); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     barySeries->attachAxis(xAxis); | 
					
						
							|  |  |  |     barySeries->attachAxis(yAxis); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     yAxis->setRange(std::floor(minENR * 10.0)/10.0, std::ceil(maxENR * 10.0)/10.0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ui->chart->setChart(m_chart); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete oldChart; | 
					
						
							| 
									
										
										
										
											2021-06-09 16:17:59 +01:00
										 |  |  | } |