File indexing completed on 2024-05-19 14:58:11
0001 /* 0002 SPDX-FileCopyrightText: 2023 Joseph McGee <joseph.mcgee@sbcglobal.net> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 0008 #include <QLoggingCategory> 0009 #include <QDir> 0010 #include <QVectorIterator> 0011 #include <QVariant> 0012 #include <QTableWidgetItem> 0013 #include "fileutilitycameradata.h" 0014 #include "optimalsubexposurecalculator.h" 0015 #include "optimalexposuredetail.h" 0016 #include <string> 0017 #include <iostream> 0018 #include <filesystem> 0019 #include "fileutilitycameradatadialog.h" 0020 #include "exposurecalculatordialog.h" 0021 #include "./ui_exposurecalculatordialog.h" 0022 #include <kspaths.h> 0023 #include <ekos_capture_debug.h> 0024 0025 /* 0026 * 0027 * 0028 * https://www.astrobin.com/forum/c/astrophotography/deep-sky/robin-glover-talk-questioning-length-of-single-exposure/ 0029 * http://astro.physics.uiowa.edu/~kaaret/2013f_29c137/Lab03_noise.html#:~:text=The%20read%20noise%20of%20the,removing%20hot%20and%20dead%20pixels 0030 * 0031 * Resource for DSLR read-noise: 0032 * https://www.photonstophotos.net/Charts/RN_ADU.htm 0033 * 0034 */ 0035 0036 OptimalExposure::OptimalExposureDetail 0037 aSubExposureDetail; // Added during refactoring to simplify and better support the noise graph 0038 0039 ExposureCalculatorDialog::ExposureCalculatorDialog(QWidget *parent, 0040 double aPreferredSkyQualityValue, 0041 double aPreferredFocalRatioValue, 0042 const QString &aPreferredCameraId) : 0043 QDialog(parent), 0044 aPreferredSkyQualityValue(aPreferredSkyQualityValue), 0045 aPreferredFocalRatioValue(aPreferredFocalRatioValue), 0046 aPreferredCameraId(aPreferredCameraId), 0047 ui(new Ui::ExposureCalculatorDialog) 0048 { 0049 ui->setupUi(this); 0050 0051 ui->exposureDiffLabel->setText(QString("\u0394=")); 0052 0053 QStringList availableCameraFiles = OptimalExposure::FileUtilityCameraData::getAvailableCameraFilesList(); 0054 0055 if(availableCameraFiles.length() == 0) 0056 { 0057 // qCWarning(KSTARS_EKOS_CAPTURE) << "Exposure Calculator - No Camera data available, closing dialog"; 0058 // QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); 0059 qCWarning(KSTARS_EKOS_CAPTURE) << "Exposure Calculator - No Camera data available, opening camera data download dialog"; 0060 FileUtilityCameraDataDialog aCameraDownloadDialog(this, aPreferredCameraId); 0061 aCameraDownloadDialog.setWindowModality(Qt::WindowModal); 0062 aCameraDownloadDialog.exec(); 0063 // QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); 0064 // Look again for files... 0065 availableCameraFiles = OptimalExposure::FileUtilityCameraData::getAvailableCameraFilesList(); 0066 0067 } 0068 0069 0070 if(availableCameraFiles.length() > 0) 0071 { 0072 ui->imagingCameraSelector->clear(); 0073 refreshCameraSelector(ui, availableCameraFiles, aPreferredCameraId); 0074 0075 ui->exposureCalculatorFrame->setEnabled(false); 0076 0077 hideGainSelectionWidgets(); 0078 0079 ui->gainSelector->setMinimum(0); 0080 ui->gainSelector->setMaximum(100); 0081 ui->gainSelector->setValue(50); 0082 0083 ui->indiFocalRatio->setMinimum(1.0); 0084 ui->indiFocalRatio->setMaximum(12.0); 0085 ui->indiFocalRatio->setSingleStep(0.1); 0086 // was coded to default as ui->indiFocalRatio->setValue(5.0); 0087 ui->indiFocalRatio->setValue(aPreferredFocalRatioValue); 0088 0089 // Setup the "user" controls. 0090 ui->userSkyQuality->setMinimum(16.00); 0091 ui->userSkyQuality->setMaximum(22.00); 0092 ui->userSkyQuality->setSingleStep(0.01); 0093 // was coded ui->userSkyQuality->setValue(20.0); 0094 ui->userSkyQuality->setValue(aPreferredSkyQualityValue); 0095 0096 ui->noiseTolerance->setMinimum(0.02); 0097 ui->noiseTolerance->setMaximum(500.00); 0098 ui->noiseTolerance->setSingleStep(0.25); 0099 ui->noiseTolerance->setValue(5.0); 0100 0101 ui->filterBandwidth->setMinimum(2.8); 0102 ui->filterBandwidth->setMaximum(300); 0103 ui->filterBandwidth->setValue(300); 0104 ui->filterBandwidth->setSingleStep(0.1); 0105 0106 0107 0108 /* 0109 Experimental compensation for filters on light the pollution calculation are a bit tricky. 0110 Part 1... 0111 0112 An unfiltered camera may include some IR and UV, and be able to read a bandwidth of say 360nm (at a reasonably high QE %). 0113 0114 But for simplicity, the filter compensation calculation will be based on the range for visible light, and use a nominal 0115 bandwidth of 300, (roughly the bandwidth of a typical luminance filter). 0116 0117 The filter compensation factor that will be applied to light pollution will be the filter bandwidth (from the UI) / 300. 0118 This means that a typical luminance filter would produce a "nuetral" compensation of 1.0 (300 / 300). 0119 0120 But the user interface will allow selection of wider bands for true "unfiltered" exposure calculations. Example: by selecting a 0121 bandwith of 360, the light pollution compensation will 1.2, calculated as (360 / 300). This is to recognize that light pollution 0122 may be entering the IR and UV range of an unfiltered camera sensor. 0123 0124 A Luminance filter may only pass 300nm, so the filter compensaton value would be 1.0 (300 / 300) 0125 An RGB filter may only pass 100nm, so the filter compensaton value would be 0.3333 = (100 / 300) 0126 An SHO filter may only pass 3nm, so the filter compensaton value would be 0.0100 = (3 / 300) 0127 0128 Filters will reduce bandwidth, but also slightly reduce tranmission within the range that they "pass". 0129 The values stated are only for demonstration and testing purposes, further research is needed. 0130 0131 */ 0132 0133 // Set up plot colors of Sub Exposure (night friendly) 0134 ui->qCustomPlotSubExposure->setBackground(QBrush(Qt::black)); 0135 ui->qCustomPlotSubExposure->xAxis->setBasePen(QPen(Qt::white, 1)); 0136 ui->qCustomPlotSubExposure->yAxis->setBasePen(QPen(Qt::white, 1)); 0137 ui->qCustomPlotSubExposure->xAxis->setTickPen(QPen(Qt::white, 1)); 0138 ui->qCustomPlotSubExposure->yAxis->setTickPen(QPen(Qt::white, 1)); 0139 ui->qCustomPlotSubExposure->xAxis->setSubTickPen(QPen(Qt::white, 1)); 0140 ui->qCustomPlotSubExposure->yAxis->setSubTickPen(QPen(Qt::white, 1)); 0141 ui->qCustomPlotSubExposure->xAxis->setTickLabelColor(Qt::white); 0142 ui->qCustomPlotSubExposure->yAxis->setTickLabelColor(Qt::white); 0143 ui->qCustomPlotSubExposure->xAxis->setLabelColor(Qt::white); 0144 ui->qCustomPlotSubExposure->yAxis->setLabelColor(Qt::white); 0145 0146 ui->qCustomPlotSubExposure->xAxis->grid()->setPen(QPen(Qt::darkGray)); 0147 ui->qCustomPlotSubExposure->yAxis->grid()->setPen(QPen(Qt::darkGray)); 0148 0149 ui->qCustomPlotSubExposure->xAxis->setLabel("Gain"); 0150 0151 ui->qCustomPlotSubExposure->yAxis->setLabel("Exposure Time"); 0152 0153 ui->qCustomPlotSubExposure->addGraph(); 0154 0155 0156 // Set up plot colors of Integrated Image Noise (night friendly) 0157 ui->qCustomPlotIntegrationNoise->setBackground(QBrush(Qt::black)); 0158 ui->qCustomPlotIntegrationNoise->xAxis->setBasePen(QPen(Qt::white, 1)); 0159 ui->qCustomPlotIntegrationNoise->yAxis->setBasePen(QPen(Qt::white, 1)); 0160 ui->qCustomPlotIntegrationNoise->xAxis->setTickPen(QPen(Qt::white, 1)); 0161 ui->qCustomPlotIntegrationNoise->yAxis->setTickPen(QPen(Qt::white, 1)); 0162 ui->qCustomPlotIntegrationNoise->xAxis->setSubTickPen(QPen(Qt::white, 1)); 0163 ui->qCustomPlotIntegrationNoise->yAxis->setSubTickPen(QPen(Qt::white, 1)); 0164 ui->qCustomPlotIntegrationNoise->xAxis->setTickLabelColor(Qt::white); 0165 ui->qCustomPlotIntegrationNoise->yAxis->setTickLabelColor(Qt::white); 0166 ui->qCustomPlotIntegrationNoise->xAxis->setLabelColor(Qt::white); 0167 ui->qCustomPlotIntegrationNoise->yAxis->setLabelColor(Qt::white); 0168 0169 ui->qCustomPlotIntegrationNoise->xAxis->grid()->setPen(QPen(Qt::darkGray)); 0170 ui->qCustomPlotIntegrationNoise->yAxis->grid()->setPen(QPen(Qt::darkGray)); 0171 0172 ui->qCustomPlotIntegrationNoise->addGraph(ui->qCustomPlotIntegrationNoise->xAxis, ui->qCustomPlotIntegrationNoise->yAxis); 0173 ui->qCustomPlotIntegrationNoise->graph(0)->setPen(QPen(Qt::yellow)); 0174 ui->qCustomPlotIntegrationNoise->graph(0)->setName("Integration Time to Noise Ratio"); 0175 ui->qCustomPlotIntegrationNoise->xAxis->setLabel("Stacked Exposures"); 0176 ui->qCustomPlotIntegrationNoise->yAxis->setLabel("Noise Ratio"); 0177 0178 // ui->qCustomPlotIntegrationNoise->addGraph(ui->qCustomPlotIntegrationNoise->xAxis, ui->qCustomPlotIntegrationNoise->yAxis); 0179 // ui->qCustomPlotIntegrationNoise->graph(1)->setPen(QPen(Qt::green)); 0180 // ui->qCustomPlotIntegrationNoise->graph(1)->setName("Integration to Noise Ratio"); 0181 // ui->qCustomPlotIntegrationNoise->yAxis2->setLabel("Noise Ratio"); 0182 0183 0184 0185 0186 connect(ui->imagingCameraSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0187 &ExposureCalculatorDialog::applyInitialInputs); 0188 0189 connect(ui->cameraReadModeSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0190 &ExposureCalculatorDialog::handleUserAdjustment); 0191 0192 connect(ui->gainSelector, QOverload<int>::of(&QSpinBox::valueChanged), this, 0193 &ExposureCalculatorDialog::handleUserAdjustment); 0194 0195 connect(ui->userSkyQuality, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, 0196 &ExposureCalculatorDialog::handleUserAdjustment); 0197 0198 connect(ui->indiFocalRatio, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, 0199 &ExposureCalculatorDialog::handleUserAdjustment); 0200 0201 connect(ui->filterBandwidth, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, 0202 &ExposureCalculatorDialog::handleUserAdjustment); 0203 0204 connect(ui->noiseTolerance, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, 0205 &ExposureCalculatorDialog::handleUserAdjustment); 0206 0207 connect(ui->gainISODiscreteSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0208 &ExposureCalculatorDialog::handleUserAdjustment); 0209 0210 connect(ui->targetNoiseRatio, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, 0211 &ExposureCalculatorDialog::handleStackCalculation); 0212 0213 // Hide the gain selector frames (until a camera is selected) 0214 hideGainSelectionWidgets(); 0215 0216 applyInitialInputs(); 0217 0218 } 0219 /* 0220 else 0221 { 0222 // qCWarning(KSTARS_EKOS_CAPTURE) << "Exposure Calculator - No Camera data available, closing dialog"; 0223 // QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); 0224 qCWarning(KSTARS_EKOS_CAPTURE) << "Exposure Calculator - No Camera data available, opening camera data download dialog"; 0225 FileUtilityCameraDataDialog aCameraDownloadDialog(this, aPreferredCameraId); 0226 aCameraDownloadDialog.setWindowModality(Qt::WindowModal); 0227 aCameraDownloadDialog.exec(); 0228 QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); 0229 } 0230 */ 0231 } 0232 0233 0234 0235 0236 int ExposureCalculatorDialog::getGainSelection(OptimalExposure::GainSelectionType aGainSelectionType) 0237 { 0238 int aSelectedGain = 0; 0239 switch(aGainSelectionType) 0240 { 0241 0242 case OptimalExposure::GAIN_SELECTION_TYPE_NORMAL: 0243 // aSelectedGain = ui->gainSlider->value(); 0244 aSelectedGain = ui->gainSelector->value(); 0245 break; 0246 0247 case OptimalExposure::GAIN_SELECTION_TYPE_ISO_DISCRETE: 0248 // qCInfo(KSTARS_EKOS_CAPTURE) << " iso selector text: " << ui->isoDiscreteSelector->currentText(); 0249 aSelectedGain = ui->gainISODiscreteSelector->currentText().toInt(); 0250 break; 0251 0252 case OptimalExposure::GAIN_SELECTION_TYPE_FIXED: 0253 // qCInfo(KSTARS_EKOS_CAPTURE) << "Fixed read-noise camera gain set to 0"; 0254 aSelectedGain = 0; // Fixed noise cameras have read noise data at 0 gain. 0255 break; 0256 0257 } 0258 0259 return(aSelectedGain); 0260 } 0261 0262 0263 QString skyQualityToBortleClassNumber(double anSQMValue) 0264 { 0265 0266 QString aBortleClassNumber; 0267 if(anSQMValue < 18.38) 0268 { 0269 aBortleClassNumber = "8 to 9"; 0270 } 0271 else if(anSQMValue < 18.94) 0272 { 0273 aBortleClassNumber = "7"; 0274 } 0275 else if(anSQMValue < 19.50) 0276 { 0277 aBortleClassNumber = "6"; 0278 } 0279 else if(anSQMValue < 20.49) 0280 { 0281 aBortleClassNumber = "5"; 0282 } 0283 else if(anSQMValue < 21.69) 0284 { 0285 aBortleClassNumber = "4"; 0286 } 0287 else if(anSQMValue < 21.89) 0288 { 0289 aBortleClassNumber = "3"; 0290 } 0291 else if(anSQMValue < 21.99) 0292 { 0293 aBortleClassNumber = "2"; 0294 } 0295 else aBortleClassNumber = "1"; 0296 0297 return(aBortleClassNumber); 0298 } 0299 0300 // calculate a Bortle style color based on SQM 0301 QColor makeASkyQualityColor(double anSQMValue) 0302 { 0303 QColor aSkyBrightnessColor; 0304 int aHueDegree, aSaturation, aValue; 0305 0306 if(anSQMValue < 18.32) // White Zone 0307 { 0308 aHueDegree = 40; 0309 aSaturation = 0; // Saturation must move from 0310 aValue = 254; // Value must move from 254 to 240 0311 } 0312 else if(anSQMValue < 18.44) // From White Zone at 18.32 transitioning to Red Zone at 18.44 0313 { 0314 aHueDegree = 0; // Hue is Red, 0315 // Saturation must transition from 0 to 255 as SQM moves from 18.32 to 18.44 0316 aSaturation = (int)(255 * ((anSQMValue - (double)18.32) / (18.44 - 18.32))); 0317 aValue = 254; 0318 } 0319 else if(anSQMValue < 21.82 ) 0320 { 0321 // In the color range transitions hue of Bortle can be approximated with a polynomial 0322 aHueDegree = 17.351411032 * pow(anSQMValue, 4) 0323 - 1384.0773705 * pow(anSQMValue, 3) 0324 + 41383.66777 * pow(anSQMValue, 2) 0325 - 549664.28976 * anSQMValue 0326 + 2736244.0733; 0327 0328 if(aHueDegree < 0) aHueDegree = 0; 0329 0330 aSaturation = 255; 0331 aValue = 240; 0332 0333 } 0334 else if(anSQMValue < 21.92) // Transition from Blue to Dark Gray between 21.82 and 21.92 0335 { 0336 aHueDegree = 240; 0337 // Saturation must transition from 255 to 0 0338 aSaturation = (int) ((2550 * (21.92 - anSQMValue))); 0339 // Value must transition from 240 to 100 0340 aValue = (int)(100 + (1400 * (21.92 - anSQMValue))); 0341 0342 } 0343 else if(anSQMValue < 21.99) // Dark gray zone 0344 { 0345 aHueDegree = 240; 0346 aSaturation = 0; 0347 aValue = 100; 0348 } 0349 else // Black zone should only be 21.99 and up 0350 { 0351 aHueDegree = 240; 0352 aSaturation = 0; 0353 aValue = 0; 0354 } 0355 0356 // qCInfo(KSTARS_EKOS_CAPTURE) << "Sky Quality Color Hue: " << aHueDegree; 0357 // qCInfo(KSTARS_EKOS_CAPTURE) << "Sky Quality Color Saturation: " << aSaturation; 0358 // qCInfo(KSTARS_EKOS_CAPTURE) << "Sky Quality Color Value: " << aValue; 0359 0360 aSkyBrightnessColor.setHsv(aHueDegree, aSaturation, aValue); 0361 0362 return(aSkyBrightnessColor); 0363 } 0364 0365 0366 void refreshSkyQualityPresentation(Ui::ExposureCalculatorDialog *ui, double aSkyQualityValue) 0367 { 0368 0369 // qCInfo(KSTARS_EKOS_CAPTURE) << "\ta selected Sky Quality: " << aSkyQualityValue; 0370 QColor aSkyQualityColor = makeASkyQualityColor(aSkyQualityValue); 0371 0372 ui->bortleScaleValue->setText(skyQualityToBortleClassNumber(aSkyQualityValue)); 0373 0374 // Update the skyQualityColor Widget 0375 QPalette pal = QPalette(); 0376 0377 pal.setColor(QPalette::Window, aSkyQualityColor); 0378 ui->skyQualityColor->setAutoFillBackground(true); 0379 ui->skyQualityColor->setPalette(pal); 0380 ui->skyQualityColor->show(); 0381 0382 } 0383 0384 0385 void ExposureCalculatorDialog::handleUserAdjustment() 0386 { 0387 0388 // This test for enabled was needed because dynamic changes to a 0389 // combo box during initialization of the calculator were firing 0390 // this method and prematurely triggering a calculation which was 0391 // crashing because the initialization was incomplete. 0392 0393 if(ui->exposureCalculatorFrame->isEnabled()) 0394 { 0395 // Recalculate and refresh the graph, with changed inputs from the ui 0396 QString aSelectedImagingCamera = ui->imagingCameraSelector->itemText(ui->imagingCameraSelector->currentIndex()); 0397 0398 // ui->cameraReadModeSelector->currentData() 0399 int aSelectedReadMode = ui->cameraReadModeSelector->currentData().toInt(); 0400 0401 double aFocalRatioValue = ui->indiFocalRatio->value(); 0402 0403 double aSkyQualityValue = ui->userSkyQuality->value(); 0404 refreshSkyQualityPresentation(ui, aSkyQualityValue); 0405 0406 double aNoiseTolerance = ui->noiseTolerance->value(); 0407 0408 // double aFilterCompensationValue = 1.0; 0409 double aFilterCompensationValue = ((double)ui->filterBandwidth->value() / (double)300); 0410 0411 int aSelectedGainValue = getGainSelection(anOptimalSubExposureCalculator->getImagingCameraData().getGainSelectionType()); 0412 0413 0414 // double aSelectedGainValue = ui->gainSlider->value(); 0415 // qCInfo(KSTARS_EKOS_CAPTURE) << "\ta selected gain: " << aSelectedGainValue; 0416 0417 calculateSubExposure(aNoiseTolerance, aSkyQualityValue, aFocalRatioValue, aFilterCompensationValue, aSelectedReadMode, 0418 aSelectedGainValue); 0419 } 0420 } 0421 0422 void ExposureCalculatorDialog::hideGainSelectionWidgets() 0423 { 0424 ui->gainSelectionFrame->setEnabled(false); 0425 ui->gainSelectionFrame->setVisible(false); 0426 0427 ui->gainSpinnerLabel->setVisible(false); 0428 ui->gainSelector->setVisible(false); 0429 ui->gainSelector->setEnabled(false); 0430 0431 ui->gainISOSelectorLabel->setVisible(false); 0432 ui->gainISODiscreteSelector->setVisible(false); 0433 ui->gainISODiscreteSelector->setEnabled(false); 0434 0435 ui->gainSelectionFixedLabel->setVisible(false); 0436 0437 /* 0438 ui->gainSelectionISODiscreteFrame->setEnabled(false); 0439 ui->gainSelectionISODiscreteFrame->setVisible(false); 0440 ui->gainSelectionFixedFrame->setEnabled(false); 0441 ui->gainSelectionFixedFrame->setVisible(false); 0442 */ 0443 } 0444 0445 0446 void ExposureCalculatorDialog::showGainSelectionNormalWidgets() 0447 { 0448 ui->gainSpinnerLabel->setVisible(true); 0449 ui->gainSelector->setEnabled(true); 0450 ui->gainSelector->setVisible(true); 0451 0452 ui->gainSelectionFrame->setEnabled(true); 0453 ui->gainSelectionFrame->setVisible(true); 0454 } 0455 0456 void ExposureCalculatorDialog::showGainSelectionISODiscreteWidgets() 0457 { 0458 ui->gainISOSelectorLabel->setVisible(true); 0459 ui->gainISODiscreteSelector->setEnabled(true); 0460 ui->gainISODiscreteSelector->setVisible(true); 0461 0462 ui->gainSelectionFrame->setEnabled(true); 0463 ui->gainSelectionFrame->setVisible(true); 0464 } 0465 0466 void ExposureCalculatorDialog::showGainSelectionFixedWidgets() 0467 { 0468 ui->gainSelectionFixedLabel->setVisible(true); 0469 0470 ui->gainSelectionFrame->setEnabled(true); 0471 ui->gainSelectionFrame->setVisible(true); 0472 } 0473 0474 0475 void ExposureCalculatorDialog::applyInitialInputs() 0476 { 0477 ui->exposureCalculatorFrame->setEnabled(false); 0478 0479 // QString aSelectedImagingCameraName = ui->imagingCameraSelector->itemText(ui->imagingCameraSelector->currentIndex()); 0480 QString aSelectedImagingCameraFileName = ui->imagingCameraSelector->itemData( 0481 ui->imagingCameraSelector->currentIndex()).toString(); 0482 0483 0484 // qCInfo(KSTARS_EKOS_CAPTURE) << ui->cameraReadModeSelector->currentData(); 0485 0486 int aSelectedReadMode = 0; 0487 0488 double aFocalRatioValue = ui->indiFocalRatio->value(); 0489 double aSkyQualityValue = ui->userSkyQuality->value(); 0490 refreshSkyQualityPresentation(ui, aSkyQualityValue); 0491 0492 double aNoiseTolerance = ui->noiseTolerance->value(); 0493 0494 // double aFilterCompensationValue = 1.0; 0495 // double aFilterCompensationValue = ui->filterSelection->itemData(ui->filterSelection->currentIndex()).toDouble(); 0496 double aFilterCompensationValue = ((double)ui->filterBandwidth->value() / (double)300); 0497 0498 initializeSubExposureCalculator(aNoiseTolerance, aSkyQualityValue, aFocalRatioValue, aFilterCompensationValue, 0499 aSelectedImagingCameraFileName); 0500 0501 int aSelectedGainValue = ui->gainSelector->value(); 0502 0503 calculateSubExposure(aNoiseTolerance, aSkyQualityValue, aFocalRatioValue, aFilterCompensationValue, aSelectedReadMode, 0504 aSelectedGainValue); 0505 0506 ui->exposureCalculatorFrame->setEnabled(true); 0507 0508 } 0509 0510 0511 void plotIntegratedNoise(Ui::ExposureCalculatorDialog *ui, 0512 OptimalExposure::OptimalExposureDetail *subExposureDetail) 0513 { 0514 0515 ui->qCustomPlotIntegrationNoise->graph()->data()->clear(); 0516 0517 double aCoefficient = (subExposureDetail->getSubExposureTime() / subExposureDetail->getExposureTotalNoise()); 0518 /* 0519 qCInfo(KSTARS_EKOS_CAPTURE) << "Noise Ratio Function: Noise Ratio = " << aCoefficient << " * Sqrt(Exposure Count)"; 0520 0521 qCInfo(KSTARS_EKOS_CAPTURE) << "Differential of Noise Ratio Function: = " << aCoefficient << " / (2 * Sqrt(Exposure Count)"; 0522 0523 qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Count Function (for desired Noise Ratio):" 0524 << "Exposure Count = (Noise Ratio / " << aCoefficient << ") ^2 = pow(Noise Ratio / " << aCoefficient << ", 2)"; 0525 */ 0526 double aTargetNoiseRatio = ui->targetNoiseRatio->value(); 0527 // qCInfo(KSTARS_EKOS_CAPTURE) << "Target Noise Ratio: " << aTargetNoiseRatio; 0528 0529 int aRequiredExposureCount = std::max(1, (int)(pow((aTargetNoiseRatio / aCoefficient), 2))); 0530 ui->exposureCount->setText(QString::number(aRequiredExposureCount)); 0531 0532 double aDifferential = aCoefficient / (2 * sqrt(aRequiredExposureCount)); 0533 ui->exposureCountDifferential->setText(QString::number(aDifferential, 'f', 2)); 0534 0535 0536 ui->qCustomPlotIntegrationNoise->graph()->data()->clear(); 0537 0538 // ui->qCustomPlotIntegrationNoise 0539 // ui->qCustomPlotIntegrationNoise->graph(0)->setData(ExposureCount, noise); 0540 0541 QVector<double> xValue((aRequiredExposureCount * 2) + 1), yValue((aRequiredExposureCount * 2) + 1); 0542 for (int exposureCount = 1; exposureCount < (aRequiredExposureCount * 2) + 1; exposureCount++) 0543 { 0544 xValue[exposureCount] = exposureCount; 0545 yValue[exposureCount] = aCoefficient * pow(exposureCount, 0.5); 0546 } 0547 0548 ui->qCustomPlotIntegrationNoise->graph(0)->setData(xValue, yValue); 0549 0550 ui->qCustomPlotIntegrationNoise->xAxis->setRange(0, aRequiredExposureCount * 2); 0551 ui->qCustomPlotIntegrationNoise->yAxis->setRange(0, yValue[yValue.size() - 1]); 0552 0553 // Also add a graph with a vertical line to show the computed integration 0554 ui->qCustomPlotIntegrationNoise->addGraph(); 0555 0556 QVector<double> selectedIntegrationSizeX(2), selectedIntegrationSizeY(2); 0557 selectedIntegrationSizeX[0] = aRequiredExposureCount; 0558 selectedIntegrationSizeY[0] = 0; 0559 selectedIntegrationSizeX[1] = aRequiredExposureCount; 0560 selectedIntegrationSizeY[1] = aTargetNoiseRatio; 0561 ui->qCustomPlotIntegrationNoise->graph(1)->setData(selectedIntegrationSizeX, selectedIntegrationSizeY); 0562 0563 QPen penSelectedIntegrationSize; 0564 penSelectedIntegrationSize.setWidth(1); 0565 // penSelectedIntegrationSize.setColor(QColor(180, 0, 0)); 0566 // On the black background need more contrast 0567 penSelectedIntegrationSize.setColor(QColor(240, 0, 0)); 0568 0569 ui->qCustomPlotIntegrationNoise->graph(1)->setPen(penSelectedIntegrationSize); 0570 0571 ui->qCustomPlotIntegrationNoise->graph(1)->setScatterStyle(QCPScatterStyle::ssCircle); 0572 0573 ui->qCustomPlotIntegrationNoise->graph()->rescaleAxes(true); 0574 ui->qCustomPlotIntegrationNoise->replot(); 0575 0576 } 0577 0578 // Slot for adjustments made to desired noise ratio that require a refresh of the NR graph 0579 void ExposureCalculatorDialog::handleStackCalculation() 0580 { 0581 plotIntegratedNoise(ui, &aSubExposureDetail); 0582 } 0583 0584 0585 void plotSubExposureEnvelope(Ui::ExposureCalculatorDialog *ui, 0586 OptimalExposure::OptimalSubExposureCalculator *anOptimalSubExposureCalculator, 0587 OptimalExposure::OptimalExposureDetail *subExposureDetail) 0588 { 0589 0590 OptimalExposure::CameraExposureEnvelope aCameraExposureEnvelope = 0591 anOptimalSubExposureCalculator->calculateCameraExposureEnvelope(); 0592 // qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Envelope has a set of: " << aCameraExposureEnvelope.getASubExposureVector().size(); 0593 // qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Envelope has a minimum Exposure Time of " << aCameraExposureEnvelope.getExposureTimeMin(); 0594 // qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Envelope has a maximum Exposure Time of " << aCameraExposureEnvelope.getExposureTimeMax(); 0595 0596 // anOptimalSubExposureCalculator->getImagingCameraData() 0597 0598 // Reset the graph axis (But maybe this was not necessary, 0599 ui->qCustomPlotSubExposure->xAxis->setRange(anOptimalSubExposureCalculator->getImagingCameraData().getGainMin(), 0600 anOptimalSubExposureCalculator->getImagingCameraData().getGainMax()); 0601 // But for the exposure yAxis include a bit of a margin so that data is not encoaching on the axis. 0602 ui->qCustomPlotSubExposure->yAxis->setRange(aCameraExposureEnvelope.getExposureTimeMin() - 10, 0603 aCameraExposureEnvelope.getExposureTimeMax() + 10); 0604 ui->qCustomPlotSubExposure->replot(); 0605 0606 // Prepare for the exposure line plot, move the data to parallel arrays for the custom plotter 0607 QVector<double> gain(aCameraExposureEnvelope.getASubExposureVector().size()), 0608 exposuretime(aCameraExposureEnvelope.getASubExposureVector().size()); 0609 for(int index = 0; index < aCameraExposureEnvelope.getASubExposureVector().size(); index++) 0610 { 0611 OptimalExposure::CalculatedGainSubExposureTime aGainExposureTime = aCameraExposureEnvelope.getASubExposureVector()[index]; 0612 gain[index] = (double)aGainExposureTime.getSubExposureGain(); 0613 exposuretime[index] = aGainExposureTime.getSubExposureTime(); 0614 } 0615 ui->qCustomPlotSubExposure->graph()->data()->clear(); 0616 0617 ui->qCustomPlotSubExposure->graph(0)->setData(gain, exposuretime); 0618 0619 // Also add a graph with a vertical line to show the selected gain... 0620 ui->qCustomPlotSubExposure->addGraph(); 0621 0622 QVector<double> selectedExposureX(2), selectedExposureY(2); 0623 selectedExposureX[0] = subExposureDetail->getSelectedGain(); 0624 selectedExposureY[0] = 0; 0625 selectedExposureX[1] = subExposureDetail->getSelectedGain(); 0626 selectedExposureY[1] = subExposureDetail->getSubExposureTime(); 0627 ui->qCustomPlotSubExposure->graph(1)->setData(selectedExposureX, selectedExposureY); 0628 0629 QPen penExposureEnvelope; 0630 penExposureEnvelope.setWidth(1); 0631 // penExposureEnvelope.setColor(QColor(0, 180, 180)); 0632 // On the black background need more contrast 0633 penExposureEnvelope.setColor(QColor(0, 220, 220)); 0634 ui->qCustomPlotSubExposure->graph(0)->setPen(penExposureEnvelope); 0635 0636 QPen penSelectedExposure; 0637 penSelectedExposure.setWidth(1); 0638 // penSelectedExposure.setColor(QColor(180, 0, 0)); 0639 // On the black background need more contrast 0640 penSelectedExposure.setColor(QColor(240, 0, 0)); 0641 0642 ui->qCustomPlotSubExposure->graph(1)->setPen(penSelectedExposure); 0643 0644 ui->qCustomPlotSubExposure->graph(1)->setScatterStyle(QCPScatterStyle::ssCircle); 0645 0646 // extend the x-axis slightly so that the markers aren't hidden at the extreme edges 0647 ui->qCustomPlotSubExposure->xAxis->setRange(anOptimalSubExposureCalculator->getImagingCameraData().getGainMin() - 5, 0648 anOptimalSubExposureCalculator->getImagingCameraData().getGainMax() + 5); 0649 // force the y-axis to start at 0, (sometimes the auto rescale was making the y-axis range start a negative value 0650 ui->qCustomPlotSubExposure->yAxis->setRange(0, aCameraExposureEnvelope.getExposureTimeMax()); 0651 0652 ui->qCustomPlotSubExposure->graph()->rescaleAxes(true); 0653 ui->qCustomPlotSubExposure->replot(); 0654 0655 } 0656 0657 void ExposureCalculatorDialog::initializeSubExposureCalculator(double aNoiseTolerance, double aSkyQualityValue, 0658 double aFocalRatioValue, double aFilterCompensationValue, QString aSelectedImagingCameraName) 0659 { 0660 // qCInfo(KSTARS_EKOS_CAPTURE) << "initializeSubExposureComputer"; 0661 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taNoiseTolerance: " << aNoiseTolerance; 0662 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taSkyQualityValue: " << aSkyQualityValue; 0663 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taFocalRatioValue: " << aFocalRatioValue; 0664 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taFilterCompensation: " << aFilterCompensationValue; 0665 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taSelectedImagingCamera: " << aSelectedImagingCameraName; 0666 0667 // QVector<int> *aGainSelectionRange = new QVector<int>(); 0668 0669 // QVector<OptimalExposure::CameraGainReadNoise> *aCameraGainReadNoiseVector 0670 // = new QVector<OptimalExposure::CameraGainReadNoise>(); 0671 0672 // QVector<OptimalExposure::CameraGainReadMode> *aCameraGainReadModeVector 0673 // = new QVector<OptimalExposure::CameraGainReadMode>(); 0674 0675 // // Initialize with some default values before attempting to load from file 0676 0677 // anImagingCameraData = new OptimalExposure::ImagingCameraData(aSelectedImagingCameraName, OptimalExposure::SENSORTYPE_COLOR, 0678 // OptimalExposure::GAIN_SELECTION_TYPE_NORMAL, *aGainSelectionRange, *aCameraGainReadModeVector); 0679 0680 anImagingCameraData = new OptimalExposure::ImagingCameraData(); 0681 // Load camera data from file 0682 OptimalExposure::FileUtilityCameraData::readCameraDataFile(aSelectedImagingCameraName, anImagingCameraData); 0683 0684 // qCInfo(KSTARS_EKOS_CAPTURE) << "Loaded Imaging Camera Data for " + anImagingCameraData->getCameraId(); 0685 // qCInfo(KSTARS_EKOS_CAPTURE) << "Camera Gain Selection Type " + QString::number(anImagingCameraData->getGainSelectionType()); 0686 0687 // qCInfo(KSTARS_EKOS_CAPTURE) << "Camera Read Mode Vector Size " + QString::number( 0688 // anImagingCameraData->getCameraGainReadModeVector().size()); 0689 0690 switch(anImagingCameraData->getSensorType()) 0691 { 0692 case OptimalExposure::SENSORTYPE_COLOR: 0693 ui->SensorType->setText("Color"); 0694 break; 0695 case OptimalExposure::SENSORTYPE_MONOCHROME: 0696 ui->SensorType->setText("Mono"); 0697 break; 0698 } 0699 0700 ui->cameraReadModeSelector->clear(); 0701 foreach(OptimalExposure::CameraGainReadMode aReadMode, anImagingCameraData->getCameraGainReadModeVector()) 0702 { 0703 QString readModeText = QString::number(aReadMode.getCameraGainReadModeNumber()) + " : " + 0704 aReadMode.getCameraGainReadModeName(); 0705 ui->cameraReadModeSelector->addItem(readModeText, aReadMode.getCameraGainReadModeNumber()); 0706 } 0707 if(anImagingCameraData->getCameraGainReadModeVector().size() > 1) 0708 { 0709 ui->cameraReadModeSelector->setEnabled(true); 0710 } 0711 else 0712 { 0713 ui->cameraReadModeSelector->setEnabled(false); 0714 } 0715 0716 0717 // qCInfo(KSTARS_EKOS_CAPTURE) << "Camera Gain Read-Noise Vector Size " 0718 // + QString::number(anImagingCameraData->getCameraGainReadNoiseVector().size()); 0719 0720 0721 0722 switch( anImagingCameraData->getGainSelectionType() ) 0723 { 0724 case OptimalExposure::GAIN_SELECTION_TYPE_FIXED: 0725 // qCInfo(KSTARS_EKOS_CAPTURE) << "Gain Selection Type: GAIN_SELECTION_TYPE_FIXED"; 0726 hideGainSelectionWidgets(); 0727 showGainSelectionFixedWidgets(); 0728 0729 break; 0730 0731 case OptimalExposure::GAIN_SELECTION_TYPE_ISO_DISCRETE: 0732 // qCInfo(KSTARS_EKOS_CAPTURE) << "Gain Selection Type: GAIN_SELECTION_TYPE_ISO_DISCRETE"; 0733 hideGainSelectionWidgets(); 0734 0735 ui->gainISODiscreteSelector->clear(); 0736 // Load the ISO Combo from the camera data 0737 foreach(int isoSetting, anImagingCameraData->getGainSelectionRange()) 0738 { 0739 ui->gainISODiscreteSelector->addItem(QString::number(isoSetting)); 0740 } 0741 ui->gainISODiscreteSelector->setCurrentIndex(0); 0742 0743 // qCInfo(KSTARS_EKOS_CAPTURE) << "Camera Data Gain min " + QString::number(anImagingCameraData->getGainMin()); 0744 // qCInfo(KSTARS_EKOS_CAPTURE) << "Camera Data Gain max " + QString::number(anImagingCameraData->getGainMax()); 0745 0746 showGainSelectionISODiscreteWidgets(); 0747 0748 break; 0749 0750 case OptimalExposure::GAIN_SELECTION_TYPE_NORMAL: 0751 // qCInfo(KSTARS_EKOS_CAPTURE) << "Gain Selection Type: GAIN_SELECTION_TYPE_NORMAL"; 0752 0753 hideGainSelectionWidgets(); 0754 showGainSelectionNormalWidgets(); 0755 // qCInfo(KSTARS_EKOS_CAPTURE) << "Camera Data Gain min " + QString::number(anImagingCameraData->getGainMin()); 0756 // qCInfo(KSTARS_EKOS_CAPTURE) << "Camera Data Gain max " + QString::number(anImagingCameraData->getGainMax()); 0757 break; 0758 0759 } 0760 0761 anOptimalSubExposureCalculator = new OptimalExposure::OptimalSubExposureCalculator(aNoiseTolerance, aSkyQualityValue, 0762 aFocalRatioValue, aFilterCompensationValue, *anImagingCameraData); 0763 0764 // qCInfo(KSTARS_EKOS_CAPTURE) << "Calculating... "; 0765 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Noise Tolerance " << anOptimalSubExposureCalculator->getANoiseTolerance(); 0766 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Sky Quality " << anOptimalSubExposureCalculator->getASkyQuality(); 0767 0768 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Focal Ratio " << anOptimalSubExposureCalculator->getAFocalRatio(); 0769 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Filter Compensation Value (ignored): " << anOptimalSubExposureCalculator->getAFilterCompensation(); 0770 0771 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Camera Gain Min " << anOptimalSubExposureCalculator->getImagingCameraData().getGainMin(); 0772 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Camera Gain Max " << anOptimalSubExposureCalculator->getImagingCameraData().getGainMax(); 0773 0774 } 0775 0776 0777 void refreshStackTable(Ui::ExposureCalculatorDialog *ui, 0778 OptimalExposure::OptimalExposureDetail *subExposureDetail) 0779 { 0780 QTableWidget *resultStackTable = ui->exposureStackResult; 0781 0782 int stackSummarySize = subExposureDetail->getStackSummary().size(); 0783 resultStackTable->setRowCount(stackSummarySize); 0784 0785 for(int stackSummaryIndex = 0; stackSummaryIndex < stackSummarySize; stackSummaryIndex++) 0786 { 0787 OptimalExposure::OptimalExposureStack anOptimalExposureStack = subExposureDetail->getStackSummary()[stackSummaryIndex]; 0788 0789 resultStackTable->setItem(stackSummaryIndex, 0, 0790 new QTableWidgetItem(QString::number(anOptimalExposureStack.getPlannedTime()))); 0791 resultStackTable->item(stackSummaryIndex, 0)->setTextAlignment(Qt::AlignCenter); 0792 0793 resultStackTable->setItem(stackSummaryIndex, 1, 0794 new QTableWidgetItem(QString::number(anOptimalExposureStack.getExposureCount()))); 0795 resultStackTable->item(stackSummaryIndex, 1)->setTextAlignment(Qt::AlignRight); 0796 0797 resultStackTable->setItem(stackSummaryIndex, 2, 0798 new QTableWidgetItem(QString::number(anOptimalExposureStack.getStackTime()))); 0799 resultStackTable->item(stackSummaryIndex, 2)->setTextAlignment(Qt::AlignRight); 0800 0801 resultStackTable->setItem(stackSummaryIndex, 3, 0802 new QTableWidgetItem(QString::number(anOptimalExposureStack.getStackTotalNoise(), 'f', 2))); 0803 resultStackTable->item(stackSummaryIndex, 3)->setTextAlignment(Qt::AlignRight); 0804 0805 double ratio = anOptimalExposureStack.getStackTime() / anOptimalExposureStack.getStackTotalNoise(); 0806 resultStackTable->setItem(stackSummaryIndex, 4, new QTableWidgetItem(QString::number(ratio, 'f', 2))); 0807 resultStackTable->item(stackSummaryIndex, 4)->setTextAlignment(Qt::AlignRight); 0808 0809 resultStackTable->setRowHeight(stackSummaryIndex, 22); 0810 /* 0811 qCInfo(KSTARS_EKOS_CAPTURE) << "Stack info: Hours: " << anOptimalExposureStack.getPlannedTime() 0812 << " Exposure Count: " << anOptimalExposureStack.getExposureCount() 0813 << " Stack Time: " << anOptimalExposureStack.getStackTime() 0814 << " Stack Total Noise: " << anOptimalExposureStack.getStackTotalNoise(); 0815 */ 0816 0817 /* 0818 2023-10 Add plot of the ratio of Total Noise to Stack Integration Time into new plot widget qCustomPlotIntegrationNoise 0819 */ 0820 0821 } 0822 resultStackTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); 0823 0824 } 0825 0826 0827 void ExposureCalculatorDialog::calculateSubExposure(double aNoiseTolerance, double aSkyQualityValue, 0828 double aFocalRatioValue, double aFilterCompensationValue, int aSelectedReadMode, int aSelectedGainValue) 0829 { 0830 0831 anOptimalSubExposureCalculator->setANoiseTolerance(aNoiseTolerance); 0832 anOptimalSubExposureCalculator->setASkyQuality(aSkyQualityValue); 0833 anOptimalSubExposureCalculator->setAFocalRatio(aFocalRatioValue); 0834 anOptimalSubExposureCalculator->setAFilterCompensation(aFilterCompensationValue); 0835 anOptimalSubExposureCalculator->setASelectedCameraReadMode(aSelectedReadMode); 0836 anOptimalSubExposureCalculator->setASelectedGain(aSelectedGainValue); 0837 0838 0839 // qCInfo(KSTARS_EKOS_CAPTURE) << "initializeSubExposureComputer"; 0840 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taNoiseTolerance: " << aNoiseTolerance; 0841 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taSkyQualityValue: " << aSkyQualityValue; 0842 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taFocalRatioValue: " << aFocalRatioValue; 0843 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taFilterCompensation: (ignored) " << aFilterCompensationValue; 0844 // qCInfo(KSTARS_EKOS_CAPTURE) << "\taSelectedGainValue: " << aSelectedGainValue; 0845 0846 anOptimalSubExposureCalculator->setAFilterCompensation(aFilterCompensationValue); 0847 0848 // qCInfo(KSTARS_EKOS_CAPTURE) << "Calculating... "; 0849 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Noise Tolerance " << anOptimalSubExposureCalculator->getANoiseTolerance(); 0850 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Sky Quality " << anOptimalSubExposureCalculator->getASkyQuality(); 0851 0852 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Focal Ratio " << anOptimalSubExposureCalculator->getAFocalRatio(); 0853 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Filter Compensation Value (ignored): " << anOptimalSubExposureCalculator->getAFilterCompensation(); 0854 0855 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Camera Gain Min " << anOptimalSubExposureCalculator->getImagingCameraData().getGainMin(); 0856 // qCInfo(KSTARS_EKOS_CAPTURE) << "A Camera Gain Max " << anOptimalSubExposureCalculator->getImagingCameraData().getGainMax(); 0857 0858 0859 OptimalExposure::CameraExposureEnvelope aCameraExposureEnvelope = 0860 anOptimalSubExposureCalculator->calculateCameraExposureEnvelope(); 0861 // qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Envelope has a set of: " << aCameraExposureEnvelope.getASubExposureVector().size(); 0862 // qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Envelope has a minimum Exposure Time of " << aCameraExposureEnvelope.getExposureTimeMin(); 0863 // qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Envelope has a maximum Exposure Time of " << aCameraExposureEnvelope.getExposureTimeMax(); 0864 0865 0866 //OptimalExposure::OptimalExposureDetail subExposureDetail = anOptimalSubExposureCalculator->calculateSubExposureDetail( 0867 // aSelectedGainValue); 0868 0869 0870 aSubExposureDetail = anOptimalSubExposureCalculator->calculateSubExposureDetail(); 0871 // Get the exposure details into the ui 0872 //ui->exposureCalculatonResult. 0873 0874 plotSubExposureEnvelope(ui, anOptimalSubExposureCalculator, &aSubExposureDetail); 0875 0876 if(ui->gainSelector->isEnabled()) 0877 { 0878 // realignGainSlider(); 0879 ui->gainSelector->setMaximum(anOptimalSubExposureCalculator->getImagingCameraData().getGainMax()); 0880 ui->gainSelector->setMinimum(anOptimalSubExposureCalculator->getImagingCameraData().getGainMin()); 0881 } 0882 0883 ui->subExposureTime->setText(QString::number(aSubExposureDetail.getSubExposureTime(), 'f', 2)); 0884 ui->subPollutionElectrons->setText(QString::number(aSubExposureDetail.getExposurePollutionElectrons(), 'f', 0)); 0885 ui->subShotNoise->setText(QString::number(aSubExposureDetail.getExposureShotNoise(), 'f', 2)); 0886 ui->subTotalNoise->setText(QString::number(aSubExposureDetail.getExposureTotalNoise(), 'f', 2)); 0887 0888 0889 // qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Pollution Electrons: " << subExposureDetail.getExposurePollutionElectrons(); 0890 // qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Shot Noise: " << subExposureDetail.getExposureShotNoise(); 0891 // qCInfo(KSTARS_EKOS_CAPTURE) << "Exposure Total Noise: " << subExposureDetail.getExposureTotalNoise(); 0892 0893 0894 QTableWidget *resultStackTable = ui->exposureStackResult; 0895 resultStackTable->setColumnCount(5); 0896 resultStackTable->verticalHeader()->setVisible(false); 0897 0898 QStringList stackDetailHeaders; 0899 stackDetailHeaders << "Planned Hours" << "Exposure Count" << "Stack Time" << "Noise" << "Ratio"; 0900 resultStackTable->setHorizontalHeaderLabels(stackDetailHeaders); 0901 resultStackTable->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter | (Qt::Alignment)Qt::TextWordWrap); 0902 resultStackTable->horizontalHeader()->setFixedHeight(32); 0903 0904 resultStackTable->horizontalHeaderItem(1)->setToolTip("Sub-exposure count in stacked image"); 0905 resultStackTable->horizontalHeaderItem(2)->setToolTip("Integration time of stacked image (seconds)"); 0906 resultStackTable->horizontalHeaderItem(3)->setToolTip("Total Noise in Stacked Image"); 0907 resultStackTable->horizontalHeaderItem(4)->setToolTip("Integration time to noise ratio (potential quality)"); 0908 0909 /* 0910 double initializedTargetNoiseRatio = ceil((100.0 * aSubExposureDetail.getSubExposureTime()) / 0911 aSubExposureDetail.getExposureTotalNoise()) / 10.0; 0912 0913 // Reinitialize the time/noise input in the stack calculator to produce a stack of 100 images. 0914 ui->targetNoiseRatio->setValue(initializedTargetNoiseRatio); 0915 0916 */ 0917 refreshStackTable(ui, &aSubExposureDetail); 0918 0919 plotIntegratedNoise(ui, &aSubExposureDetail); 0920 0921 } 0922 0923 0924 0925 void ExposureCalculatorDialog::refreshCameraSelector(Ui::ExposureCalculatorDialog *ui, 0926 QStringList availableCameraFileNames, const QString aPreferredCameraId) 0927 { 0928 // Present the aCameraId in a way that hopfully matches the cameraId from the driver 0929 // but set the full path in the combo box data as a QVariant 0930 // Retrievable as: 0931 // QString filePath = ui->imagingCameraSelector->itemData(index).toString(); 0932 0933 int preferredIndex = 0; 0934 ui->imagingCameraSelector->clear(); 0935 0936 /* 0937 * 2023-10-05 Added sorting to the filelist, but the full path is included in this 0938 * list, and since camera data can come from either the applicaton folder, or a user local folder 0939 * the sort result can produce two groupings of sorted camera ids. 0940 * In Linux, files from the user local folder will appear first in the QCombo. 0941 */ 0942 availableCameraFileNames.sort(); 0943 0944 foreach(QString filename, availableCameraFileNames) 0945 { 0946 QString aCameraId = OptimalExposure::FileUtilityCameraData::cameraDataFileNameToCameraId(filename); 0947 0948 // qCInfo(KSTARS_EKOS_CAPTURE) << "Camera Filename: " << filename << " Camera Id:" << aCameraId; 0949 0950 ui->imagingCameraSelector->addItem(aCameraId, filename); 0951 if(aPreferredCameraId != nullptr && aPreferredCameraId.length() > 0) 0952 { 0953 if(aCameraId == aPreferredCameraId) 0954 preferredIndex = ui->imagingCameraSelector->count() - 1; 0955 } 0956 } 0957 0958 ui->imagingCameraSelector->setCurrentIndex(preferredIndex); 0959 0960 } 0961 0962 0963 ExposureCalculatorDialog::~ExposureCalculatorDialog() 0964 { 0965 delete ui; 0966 } 0967 0968 void ExposureCalculatorDialog::on_downloadCameraB_clicked() 0969 { 0970 // User may want to add more camera files. 0971 FileUtilityCameraDataDialog aCameraDownloadDialog(this, aPreferredCameraId); 0972 aCameraDownloadDialog.setWindowModality(Qt::WindowModal); 0973 aCameraDownloadDialog.exec(); 0974 0975 // Using refresh is causing an error because the combobox->clear is 0976 // making the selection change. Need to resolve this. 0977 // but for now, if a user adds more cameras they will be available 0978 // in the exposure calculator on the next start. 0979 // refreshCameraSelector(ui, aPreferredCameraId); 0980 0981 } 0982