diff --git a/UnitTests/IntegrationTests_DoasFit.cpp b/UnitTests/IntegrationTests_DoasFit.cpp index 9156c95..810c822 100644 --- a/UnitTests/IntegrationTests_DoasFit.cpp +++ b/UnitTests/IntegrationTests_DoasFit.cpp @@ -79,9 +79,9 @@ TEST_CASE("DoasFit - IntegrationTest with good scan - scan file 1", "[DoasFit][I // Change the settings to use HP500 filtering and make sure to filter the references // (but keep the unit in molec/cm2 as this makes it possible to have the same values in the assertions here as in the test above). so2FitWindow.fitType = FIT_TYPE::FIT_HP_DIV; - for (size_t refIdx = 0; refIdx < so2FitWindow.nRef; ++refIdx) + for (size_t refIdx = 0; refIdx < so2FitWindow.reference.size(); ++refIdx) { - HighPassFilter(*so2FitWindow.ref[refIdx].m_data, CrossSectionUnit::cm2_molecule); + HighPassFilter(*so2FitWindow.reference[refIdx].m_data, CrossSectionUnit::cm2_molecule); } // Setup the DOAS Fit @@ -119,9 +119,9 @@ TEST_CASE("DoasFit - IntegrationTest with good scan - scan file 1", "[DoasFit][I // Change the settings to use HP500 filtering and make sure to filter the references // (but keep the unit in molec/cm2 as this makes it possible to have the same values in the assertions here as in the test above). so2FitWindow.fitType = FIT_TYPE::FIT_HP_SUB; - for (size_t refIdx = 0; refIdx < so2FitWindow.nRef; ++refIdx) + for (size_t refIdx = 0; refIdx < so2FitWindow.reference.size(); ++refIdx) { - HighPassFilter(*so2FitWindow.ref[refIdx].m_data, novac::CrossSectionUnit::cm2_molecule); + HighPassFilter(*so2FitWindow.reference[refIdx].m_data, novac::CrossSectionUnit::cm2_molecule); } // Setup the DOAS Fit diff --git a/UnitTests/IntegrationTests_EvaluationBase.cpp b/UnitTests/IntegrationTests_EvaluationBase.cpp index d10d6ce..7abeeea 100644 --- a/UnitTests/IntegrationTests_EvaluationBase.cpp +++ b/UnitTests/IntegrationTests_EvaluationBase.cpp @@ -17,20 +17,19 @@ static CFitWindow PrepareFitWindow() window.fitLow = 475; window.fitHigh = 643; window.fitType = novac::FIT_TYPE::FIT_HP_DIV; - window.nRef = (int)references.size(); - int refIdx = 0; + window.reference.reserve(references.size()); for (auto& reference : references) { - window.ref[refIdx].m_path = reference; - window.ref[refIdx].m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; - window.ref[refIdx].m_shiftOption = novac::SHIFT_TYPE::SHIFT_FIX; - window.ref[refIdx].m_shiftValue = 0.0; - window.ref[refIdx].m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; - window.ref[refIdx].m_squeezeValue = 1.0; - - window.ref[refIdx].ReadCrossSectionDataFromFile(); - - ++refIdx; + CReferenceFile ref; + ref.m_path = reference; + ref.m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; + ref.m_shiftOption = novac::SHIFT_TYPE::SHIFT_FIX; + ref.m_shiftValue = 0.0; + ref.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; + ref.m_squeezeValue = 1.0; + ref.ReadCrossSectionDataFromFile(); + + window.reference.push_back(ref); } return window; @@ -92,7 +91,7 @@ TEST_CASE("Evaluate Avaspec spectrum number eight in scan", "[Evaluate][Evaluati // Assert REQUIRE(returnCode == 0); - REQUIRE(window.nRef == sut.m_result.m_referenceResult.size()); + REQUIRE(window.reference.size() == sut.m_result.m_referenceResult.size()); REQUIRE(sut.m_result.m_delta == Approx(0.0752).margin(0.001)); REQUIRE(sut.m_result.m_chiSquare == Approx(0.0119).margin(0.001)); @@ -145,7 +144,7 @@ TEST_CASE("Evaluate Avaspec spectrum number 21 in scan", "[Evaluate][EvaluationB // Assert REQUIRE(returnCode == 0); - REQUIRE(window.nRef == sut.m_result.m_referenceResult.size()); + REQUIRE(window.reference.size() == sut.m_result.m_referenceResult.size()); REQUIRE(sut.m_result.m_delta == Approx(0.0429).margin(0.001)); REQUIRE(sut.m_result.m_chiSquare == Approx(0.0066).margin(0.001)); @@ -177,7 +176,7 @@ TEST_CASE("EvaluateShift Avaspec spectrum number 28 in scan", "[Evaluate][Evalua novac::LogContext context; CFitWindow window = PrepareFitWindow(); - window.UV = 0; // Avaspec and the UV option are not great together + window.offsetRemovalRange = novac::IndexRange(2, 20); window.fraunhoferRef.m_path = TestData::GetSyntheticFraunhoferSpectrumName_2009175M1(); window.fraunhoferRef.ReadCrossSectionDataFromFile(); novac::HighPassFilter_Ring(*window.fraunhoferRef.m_data); // filter the fraunhofer reference, to match the other references. diff --git a/UnitTests/IntegrationTests_FitWindowFileHandler.cpp b/UnitTests/IntegrationTests_FitWindowFileHandler.cpp index 30edee1..1d801d5 100644 --- a/UnitTests/IntegrationTests_FitWindowFileHandler.cpp +++ b/UnitTests/IntegrationTests_FitWindowFileHandler.cpp @@ -6,7 +6,7 @@ namespace novac { TEST_CASE("FitWindowFileHandler can read .nfw file for SO2 from the NovacProgram", "[FitWindowFileHandler][IntegrationTest]") - { +{ const std::string& filename = TestData::GetBrORatioFitWindowFileSO2(); CFitWindowFileHandler sut; @@ -28,22 +28,23 @@ TEST_CASE("FitWindowFileHandler can read .nfw file for SO2 from the NovacProgram REQUIRE(result.front().specLength == 2048); REQUIRE(result.front().findOptimalShift == 0); - REQUIRE(result.front().UV == 1); + REQUIRE(result.front().offsetRemovalRange.from == 50); + REQUIRE(result.front().offsetRemovalRange.to == 200); REQUIRE(result.front().shiftSky == 0); REQUIRE(result.front().interlaceStep == 1); REQUIRE(result.front().fraunhoferRef.m_path == "../TestData/BrORatio/D2J2124_SolarSpec_Master.txt"); // finally the references - REQUIRE(result.front().nRef == 2); - REQUIRE(result.front().ref[0].m_specieName == "SO2"); - REQUIRE(result.front().ref[0].m_path == "../TestData/BrORatio/D2J2124_SO2_Bogumil_293K_Master.txt"); - REQUIRE(result.front().ref[1].m_specieName == "O3"); - REQUIRE(result.front().ref[1].m_path == "../TestData/BrORatio/D2J2124_O3_Voigt_223K_Master.txt"); - } + REQUIRE(result.front().reference.size() == 2); + REQUIRE(result.front().reference[0].m_specieName == "SO2"); + REQUIRE(result.front().reference[0].m_path == "../TestData/BrORatio/D2J2124_SO2_Bogumil_293K_Master.txt"); + REQUIRE(result.front().reference[1].m_specieName == "O3"); + REQUIRE(result.front().reference[1].m_path == "../TestData/BrORatio/D2J2124_O3_Voigt_223K_Master.txt"); +} TEST_CASE("FitWindowFileHandler can read .nfw file for BrO from the NovacProgram", "[FitWindowFileHandler][IntegrationTest]") - { +{ const std::string& filename = TestData::GetBrORatioFitWindowFileBrO(); CFitWindowFileHandler sut; @@ -65,24 +66,25 @@ TEST_CASE("FitWindowFileHandler can read .nfw file for BrO from the NovacProgram REQUIRE(result.front().specLength == 2048); REQUIRE(result.front().findOptimalShift == 0); - REQUIRE(result.front().UV == 1); + REQUIRE(result.front().offsetRemovalRange.from == 50); + REQUIRE(result.front().offsetRemovalRange.to == 200); REQUIRE(result.front().shiftSky == 0); REQUIRE(result.front().interlaceStep == 1); REQUIRE(result.front().fraunhoferRef.m_path == "../TestData/BrORatio/D2J2124_SolarSpec_Master.txt"); // finally the references - REQUIRE(result.front().nRef == 3); - REQUIRE(result.front().ref[0].m_specieName == "BrO"); - REQUIRE(result.front().ref[0].m_path == "../TestData/BrORatio/D2J2124_BrO_Fleischmann_298K.txt"); - REQUIRE(result.front().ref[1].m_specieName == "SO2"); - REQUIRE(result.front().ref[1].m_path == "../TestData/BrORatio/D2J2124_SO2_Bogumil_293K_Master.txt"); - REQUIRE(result.front().ref[2].m_specieName == "O3"); - REQUIRE(result.front().ref[2].m_path == "../TestData/BrORatio/D2J2124_O3_Voigt_223K_Master.txt"); - } + REQUIRE(result.front().reference.size() == 3); + REQUIRE(result.front().reference[0].m_specieName == "BrO"); + REQUIRE(result.front().reference[0].m_path == "../TestData/BrORatio/D2J2124_BrO_Fleischmann_298K.txt"); + REQUIRE(result.front().reference[1].m_specieName == "SO2"); + REQUIRE(result.front().reference[1].m_path == "../TestData/BrORatio/D2J2124_SO2_Bogumil_293K_Master.txt"); + REQUIRE(result.front().reference[2].m_specieName == "O3"); + REQUIRE(result.front().reference[2].m_path == "../TestData/BrORatio/D2J2124_O3_Voigt_223K_Master.txt"); +} TEST_CASE("FitWindowFileHandler can read back one saved fit window", "[FitWindowFileHandler][IntegrationTest]") - { +{ const std::string filename = TestData::GetTemporaryFitWindowFileName(); // Setup a fit-window to write to file. Add some odd values here such that we know we're not getting defaults back @@ -102,8 +104,8 @@ TEST_CASE("FitWindowFileHandler can read back one saved fit window", "[FitWindow originalWindow.skyShift = 3.141; originalWindow.skySqueeze = 0.98765; originalWindow.specLength = 1234; - originalWindow.UV = 0; - originalWindow.nRef = 6; + originalWindow.offsetRemovalRange = IndexRange(4, 73); + originalWindow.reference.resize(6); // Act: save the fit window to file and then read it back again. CFitWindowFileHandler sut; @@ -127,7 +129,6 @@ TEST_CASE("FitWindowFileHandler can read back one saved fit window", "[FitWindow REQUIRE(originalWindow.skyShift == readBackWindow.front().skyShift); REQUIRE(originalWindow.skySqueeze == readBackWindow.front().skySqueeze); REQUIRE(originalWindow.specLength == readBackWindow.front().specLength); - REQUIRE(originalWindow.UV == readBackWindow.front().UV); - - } + REQUIRE(originalWindow.offsetRemovalRange == readBackWindow.front().offsetRemovalRange); +} } \ No newline at end of file diff --git a/UnitTests/IntegrationTests_RatioCalculationController.cpp b/UnitTests/IntegrationTests_RatioCalculationController.cpp index cbf322b..ff3cc8f 100644 --- a/UnitTests/IntegrationTests_RatioCalculationController.cpp +++ b/UnitTests/IntegrationTests_RatioCalculationController.cpp @@ -42,7 +42,7 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - SO2 not included SO2 w so2Reference->m_includeInMajor = false; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = true; broReference->m_includeInMinor = true; GetReferenceFor(sut, StandardDoasSpecie::RING)->m_automaticallyCalculate = false; @@ -63,7 +63,7 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - BrO not included BrO w // Setup the references ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); @@ -87,11 +87,11 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - SO2 and BrO included i // Setup the references ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = true; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = true; broReference->m_includeInMinor = true; GetReferenceFor(sut, StandardDoasSpecie::RING)->m_automaticallyCalculate = false; @@ -108,15 +108,15 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - SO2 and BrO included i REQUIRE(result->so2Window.name == "SO2"); REQUIRE(result->so2Window.polyOrder == 5); REQUIRE(result->so2Window.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->so2Window.nRef == 2); - REQUIRE(result->so2Window.ref[0].m_path == so2Reference->m_path); - REQUIRE(result->so2Window.ref[0].m_specieName == "SO2"); - REQUIRE(result->so2Window.ref[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->so2Window.ref[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->so2Window.ref[1].m_path == broReference->m_path); - REQUIRE(result->so2Window.ref[1].m_specieName == "BrO"); - REQUIRE(result->so2Window.ref[1].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->so2Window.ref[1].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference.size() == 2); + REQUIRE(result->so2Window.reference[0].m_path == so2Reference->m_path); + REQUIRE(result->so2Window.reference[0].m_specieName == "SO2"); + REQUIRE(result->so2Window.reference[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference[1].m_path == broReference->m_path); + REQUIRE(result->so2Window.reference[1].m_specieName == "BrO"); + REQUIRE(result->so2Window.reference[1].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference[1].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); REQUIRE(result->so2Window.ringCalculation == novac::RING_CALCULATION_OPTION::DO_NOT_CALCULATE_RING); @@ -124,15 +124,15 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - SO2 and BrO included i REQUIRE(result->broWindow.name == "BrO"); REQUIRE(result->broWindow.polyOrder == 4); REQUIRE(result->broWindow.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->broWindow.nRef == 2); - REQUIRE(result->broWindow.ref[0].m_path == broReference->m_path); - REQUIRE(result->broWindow.ref[0].m_specieName == "BrO"); - REQUIRE(result->broWindow.ref[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->broWindow.ref[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->broWindow.ref[1].m_path == so2Reference->m_path); - REQUIRE(result->broWindow.ref[1].m_specieName == "SO2"); - REQUIRE(result->broWindow.ref[1].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->broWindow.ref[1].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference.size() == 2); + REQUIRE(result->broWindow.reference[0].m_path == broReference->m_path); + REQUIRE(result->broWindow.reference[0].m_specieName == "BrO"); + REQUIRE(result->broWindow.reference[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference[1].m_path == so2Reference->m_path); + REQUIRE(result->broWindow.reference[1].m_specieName == "SO2"); + REQUIRE(result->broWindow.reference[1].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference[1].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); REQUIRE(result->broWindow.ringCalculation == novac::RING_CALCULATION_OPTION::DO_NOT_CALCULATE_RING); } @@ -146,11 +146,11 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Sets up fitLow and fit // Setup the references ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = true; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = true; broReference->m_includeInMinor = true; GetReferenceFor(sut, StandardDoasSpecie::RING)->m_automaticallyCalculate = false; @@ -183,11 +183,11 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Sets fitType to HP_DIV // Setup the references ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = true; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = true; broReference->m_includeInMinor = true; GetReferenceFor(sut, StandardDoasSpecie::RING)->m_automaticallyCalculate = false; @@ -217,11 +217,11 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - SO2 included in only m // Setup the references ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = false; broReference->m_includeInMinor = true; GetReferenceFor(sut, StandardDoasSpecie::RING)->m_automaticallyCalculate = false; @@ -235,10 +235,10 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - SO2 included in only m REQUIRE(result->so2Window.name == "SO2"); REQUIRE(result->so2Window.polyOrder == 3); REQUIRE(result->so2Window.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->so2Window.nRef == 1); - REQUIRE(result->so2Window.ref[0].m_specieName == "SO2"); - REQUIRE(result->so2Window.ref[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->so2Window.ref[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference.size() == 1); + REQUIRE(result->so2Window.reference[0].m_specieName == "SO2"); + REQUIRE(result->so2Window.reference[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); REQUIRE(result->so2Window.ringCalculation == novac::RING_CALCULATION_OPTION::DO_NOT_CALCULATE_RING); @@ -246,11 +246,11 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - SO2 included in only m REQUIRE(result->broWindow.name == "BrO"); REQUIRE(result->broWindow.polyOrder == 3); REQUIRE(result->broWindow.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->broWindow.nRef == 1); - REQUIRE(result->broWindow.ref[0].m_path == broReference->m_path); - REQUIRE(result->broWindow.ref[0].m_specieName == "BrO"); - REQUIRE(result->broWindow.ref[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->broWindow.ref[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference.size() == 1); + REQUIRE(result->broWindow.reference[0].m_path == broReference->m_path); + REQUIRE(result->broWindow.reference[0].m_specieName == "BrO"); + REQUIRE(result->broWindow.reference[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); REQUIRE(result->broWindow.ringCalculation == novac::RING_CALCULATION_OPTION::DO_NOT_CALCULATE_RING); } @@ -264,15 +264,15 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Two references in each // Setup the references ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* o3Reference = GetReferenceFor(sut, StandardDoasSpecie::O3); - o3Reference->m_path = testWindows.so2Window.ref[1].m_path; + o3Reference->m_path = testWindows.so2Window.reference[1].m_path; o3Reference->m_includeInMajor = true; o3Reference->m_includeInMinor = true; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[1].m_path; + broReference->m_path = testWindows.broWindow.reference[1].m_path; broReference->m_includeInMajor = false; broReference->m_includeInMinor = true; GetReferenceFor(sut, StandardDoasSpecie::RING)->m_automaticallyCalculate = false; @@ -286,15 +286,15 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Two references in each REQUIRE(result->so2Window.name == "SO2"); REQUIRE(result->so2Window.polyOrder == 3); REQUIRE(result->so2Window.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->so2Window.nRef == 2); - REQUIRE(result->so2Window.ref[0].m_specieName == "SO2"); - REQUIRE(result->so2Window.ref[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->so2Window.ref[0].m_shiftValue == 0.0); - REQUIRE(result->so2Window.ref[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->so2Window.ref[0].m_squeezeValue == 1.0); - REQUIRE(result->so2Window.ref[1].m_specieName == "O3"); - REQUIRE(result->so2Window.ref[1].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->so2Window.ref[1].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference.size() == 2); + REQUIRE(result->so2Window.reference[0].m_specieName == "SO2"); + REQUIRE(result->so2Window.reference[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference[0].m_shiftValue == 0.0); + REQUIRE(result->so2Window.reference[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference[0].m_squeezeValue == 1.0); + REQUIRE(result->so2Window.reference[1].m_specieName == "O3"); + REQUIRE(result->so2Window.reference[1].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->so2Window.reference[1].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); REQUIRE(result->so2Window.ringCalculation == novac::RING_CALCULATION_OPTION::DO_NOT_CALCULATE_RING); @@ -302,15 +302,15 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Two references in each REQUIRE(result->broWindow.name == "BrO"); REQUIRE(result->broWindow.polyOrder == 3); REQUIRE(result->broWindow.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->broWindow.nRef == 2); - REQUIRE(result->broWindow.ref[0].m_specieName == "BrO"); - REQUIRE(result->broWindow.ref[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->broWindow.ref[0].m_shiftValue == 0.0); - REQUIRE(result->broWindow.ref[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->broWindow.ref[0].m_squeezeValue == 1.0); - REQUIRE(result->broWindow.ref[1].m_specieName == "O3"); - REQUIRE(result->broWindow.ref[1].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); - REQUIRE(result->broWindow.ref[1].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference.size() == 2); + REQUIRE(result->broWindow.reference[0].m_specieName == "BrO"); + REQUIRE(result->broWindow.reference[0].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference[0].m_shiftValue == 0.0); + REQUIRE(result->broWindow.reference[0].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference[0].m_squeezeValue == 1.0); + REQUIRE(result->broWindow.reference[1].m_specieName == "O3"); + REQUIRE(result->broWindow.reference[1].m_shiftOption == novac::SHIFT_TYPE::SHIFT_FIX); + REQUIRE(result->broWindow.reference[1].m_squeezeOption == novac::SHIFT_TYPE::SHIFT_FIX); REQUIRE(result->broWindow.ringCalculation == novac::RING_CALCULATION_OPTION::DO_NOT_CALCULATE_RING); } @@ -324,11 +324,11 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Calculated Ring includ // Setup the references ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = false; broReference->m_includeInMinor = true; ReferenceForRatioCalculation* ringReference = GetReferenceFor(sut, StandardDoasSpecie::RING); @@ -347,7 +347,7 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Calculated Ring includ REQUIRE(result->so2Window.name == "SO2"); REQUIRE(result->so2Window.polyOrder == 3); REQUIRE(result->so2Window.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->so2Window.nRef == 1); + REQUIRE(result->so2Window.reference.size() == 1); REQUIRE(result->so2Window.ringCalculation == novac::RING_CALCULATION_OPTION::CALCULATE_RING); @@ -355,7 +355,7 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Calculated Ring includ REQUIRE(result->broWindow.name == "BrO"); REQUIRE(result->broWindow.polyOrder == 3); REQUIRE(result->broWindow.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->broWindow.nRef == 1); + REQUIRE(result->broWindow.reference.size() == 1); REQUIRE(result->broWindow.ringCalculation == novac::RING_CALCULATION_OPTION::CALCULATE_RING); } @@ -369,11 +369,11 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Calculated Ring and Ri // Setup the references ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = false; broReference->m_includeInMinor = true; ReferenceForRatioCalculation* ringReference = GetReferenceFor(sut, StandardDoasSpecie::RING); @@ -395,7 +395,7 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Calculated Ring and Ri REQUIRE(result->so2Window.name == "SO2"); REQUIRE(result->so2Window.polyOrder == 3); REQUIRE(result->so2Window.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->so2Window.nRef == 1); + REQUIRE(result->so2Window.reference.size() == 1); REQUIRE(result->so2Window.ringCalculation == novac::RING_CALCULATION_OPTION::CALCULATE_RING_X2); @@ -403,7 +403,7 @@ TEST_CASE("RatioCalculationController - SetupFitWindows - Calculated Ring and Ri REQUIRE(result->broWindow.name == "BrO"); REQUIRE(result->broWindow.polyOrder == 3); REQUIRE(result->broWindow.fitType == novac::FIT_TYPE::FIT_POLY); - REQUIRE(result->broWindow.nRef == 1); + REQUIRE(result->broWindow.reference.size() == 1); REQUIRE(result->broWindow.ringCalculation == novac::RING_CALCULATION_OPTION::CALCULATE_RING_X2); } @@ -418,15 +418,15 @@ TEST_CASE("RatioCalculationController - Evaluate", "[RatioCalculationController] // Setup the references for a good fit ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* o3Reference = GetReferenceFor(sut, StandardDoasSpecie::O3); - o3Reference->m_path = testWindows.so2Window.ref[1].m_path; + o3Reference->m_path = testWindows.so2Window.reference[1].m_path; o3Reference->m_includeInMajor = true; o3Reference->m_includeInMinor = true; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = false; broReference->m_includeInMinor = true; ReferenceForRatioCalculation* ringReference = GetReferenceFor(sut, StandardDoasSpecie::RING); @@ -498,15 +498,15 @@ TEST_CASE("RatioCalculationController - Evaluate - plume too wide", "[RatioCalcu // Setup the references for a good fit ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* o3Reference = GetReferenceFor(sut, StandardDoasSpecie::O3); - o3Reference->m_path = testWindows.so2Window.ref[1].m_path; + o3Reference->m_path = testWindows.so2Window.reference[1].m_path; o3Reference->m_includeInMajor = true; o3Reference->m_includeInMinor = true; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = false; broReference->m_includeInMinor = true; ReferenceForRatioCalculation* ringReference = GetReferenceFor(sut, StandardDoasSpecie::RING); @@ -551,15 +551,15 @@ TEST_CASE("RatioCalculationController - Evaluate without Ring", "[RatioCalculati // Setup the references for a good fit ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* o3Reference = GetReferenceFor(sut, StandardDoasSpecie::O3); - o3Reference->m_path = testWindows.so2Window.ref[1].m_path; + o3Reference->m_path = testWindows.so2Window.reference[1].m_path; o3Reference->m_includeInMajor = true; o3Reference->m_includeInMinor = true; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = false; broReference->m_includeInMinor = true; ReferenceForRatioCalculation* ringReference = GetReferenceFor(sut, StandardDoasSpecie::RING); @@ -628,15 +628,15 @@ TEST_CASE("RatioCalculationController - Evaluate without offset polynomial", "[R // Setup the references, with varied number of references in each window ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* o3Reference = GetReferenceFor(sut, StandardDoasSpecie::O3); - o3Reference->m_path = testWindows.so2Window.ref[1].m_path; + o3Reference->m_path = testWindows.so2Window.reference[1].m_path; o3Reference->m_includeInMajor = true; o3Reference->m_includeInMinor = true; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = false; broReference->m_includeInMinor = true; ReferenceForRatioCalculation* ringReference = GetReferenceFor(sut, StandardDoasSpecie::RING); @@ -715,15 +715,15 @@ TEST_CASE("RatioCalculationController - DoInitialEvaluation", "[RatioCalculation // Setup the references for a good fit ReferenceForRatioCalculation* so2Reference = GetReferenceFor(sut, StandardDoasSpecie::SO2); - so2Reference->m_path = testWindows.so2Window.ref[0].m_path; + so2Reference->m_path = testWindows.so2Window.reference[0].m_path; so2Reference->m_includeInMajor = true; so2Reference->m_includeInMinor = false; ReferenceForRatioCalculation* o3Reference = GetReferenceFor(sut, StandardDoasSpecie::O3); - o3Reference->m_path = testWindows.so2Window.ref[1].m_path; + o3Reference->m_path = testWindows.so2Window.reference[1].m_path; o3Reference->m_includeInMajor = true; o3Reference->m_includeInMinor = true; ReferenceForRatioCalculation* broReference = GetReferenceFor(sut, StandardDoasSpecie::BRO); - broReference->m_path = testWindows.broWindow.ref[0].m_path; + broReference->m_path = testWindows.broWindow.reference[0].m_path; broReference->m_includeInMajor = false; broReference->m_includeInMinor = true; ReferenceForRatioCalculation* ringReference = GetReferenceFor(sut, StandardDoasSpecie::RING); diff --git a/UnitTests/IntegrationTests_RatioEvaluation.cpp b/UnitTests/IntegrationTests_RatioEvaluation.cpp index 7c293a1..ae5cca2 100644 --- a/UnitTests/IntegrationTests_RatioEvaluation.cpp +++ b/UnitTests/IntegrationTests_RatioEvaluation.cpp @@ -80,13 +80,13 @@ TEST_CASE("RatioEvaluation - IntegrationTest with good scan - scan file 1", "[Ra // (but keep the unit in molec/cm2 as this makes it possible to have the same values in the assertions here as in the test above). so2FitWindow.fitType = FIT_TYPE::FIT_HP_DIV; broFitWindow.fitType = FIT_TYPE::FIT_HP_DIV; - for (size_t refIdx = 0; refIdx < so2FitWindow.nRef; ++refIdx) + for (size_t refIdx = 0; refIdx < so2FitWindow.reference.size(); ++refIdx) { - HighPassFilter(*so2FitWindow.ref[refIdx].m_data, novac::CrossSectionUnit::cm2_molecule); + HighPassFilter(*so2FitWindow.reference[refIdx].m_data, novac::CrossSectionUnit::cm2_molecule); } - for (size_t refIdx = 0; refIdx < broFitWindow.nRef; ++refIdx) + for (size_t refIdx = 0; refIdx < broFitWindow.reference.size(); ++refIdx) { - HighPassFilter(*broFitWindow.ref[refIdx].m_data, novac::CrossSectionUnit::cm2_molecule); + HighPassFilter(*broFitWindow.reference[refIdx].m_data, novac::CrossSectionUnit::cm2_molecule); } // Setup the sut diff --git a/UnitTests/UnitTest_FitWindow.cpp b/UnitTests/UnitTest_FitWindow.cpp index 62ba6c6..804f59f 100644 --- a/UnitTests/UnitTest_FitWindow.cpp +++ b/UnitTests/UnitTest_FitWindow.cpp @@ -11,12 +11,11 @@ TEST_CASE("FitWindow - Default constructor", "[CFitWindow]") SECTION("No references are defined") { - REQUIRE(sut.ref[0].m_data == nullptr); - REQUIRE(sut.nRef == 0); + REQUIRE(sut.reference.size() == 0); } } -CCrossSectionData* CreateCrossSection(int startValue) +static CCrossSectionData* CreateCrossSection(int startValue) { CCrossSectionData* obj = new CCrossSectionData(); @@ -35,8 +34,7 @@ TEST_CASE("FitWindow - Copy constructor", "[CFitWindow]") SECTION("Original has no references, then no references are copied.") { - REQUIRE(original.nRef == 0); - REQUIRE(original.ref[0].m_data == nullptr); + REQUIRE(original.reference.size() == 0); CReferenceFile ref1; ref1.m_specieName = "SO2"; @@ -48,15 +46,14 @@ TEST_CASE("FitWindow - Copy constructor", "[CFitWindow]") ref2.m_path = "C:/Novac/O3.txt"; ref2.m_data.reset(CreateCrossSection(2)); - original.ref[0] = ref1; - original.ref[1] = ref2; - original.nRef = 2; + original.reference.push_back(ref1); + original.reference.push_back(ref2); CFitWindow copy{ original }; - REQUIRE(copy.nRef == 2); - REQUIRE(copy.ref[0].m_data != nullptr); - REQUIRE(copy.ref[1].m_data != nullptr); + REQUIRE(copy.reference.size() == 2); + REQUIRE(copy.reference[0].m_data != nullptr); + REQUIRE(copy.reference[1].m_data != nullptr); } } @@ -64,8 +61,7 @@ TEST_CASE("FitWindow - Copy constructor", "[CFitWindow]") TEST_CASE("FitWindow - AddAsReference", "[CFitWindow]") { CFitWindow window; - REQUIRE(window.nRef == 0); - REQUIRE(window.ref[0].m_specieName.empty()); + REQUIRE(window.reference.size() == 0); std::vector referenceData = GenerateVector(0.0, 1.0, window.specLength); const std::string referenceName = "ReferenceName"; @@ -73,58 +69,56 @@ TEST_CASE("FitWindow - AddAsReference", "[CFitWindow]") AddAsReference(window, referenceData, referenceName); // Assert - REQUIRE(window.nRef == 1); - REQUIRE(window.ref[0].m_specieName == referenceName); - REQUIRE((size_t)window.ref[0].m_data->GetSize() == referenceData.size()); - REQUIRE(window.ref[0].m_data->m_crossSection[10] == referenceData[10]); - - REQUIRE(window.ref[0].m_columnOption == SHIFT_TYPE::SHIFT_FREE); - REQUIRE(window.ref[0].m_shiftOption == SHIFT_TYPE::SHIFT_FIX); - REQUIRE(window.ref[0].m_shiftValue == 0.0); - REQUIRE(window.ref[0].m_squeezeOption == SHIFT_TYPE::SHIFT_FIX); - REQUIRE(window.ref[0].m_squeezeValue == 1.0); + REQUIRE(window.reference.size() == 1); + REQUIRE(window.reference[0].m_specieName == referenceName); + REQUIRE((size_t)window.reference[0].m_data->GetSize() == referenceData.size()); + REQUIRE(window.reference[0].m_data->m_crossSection[10] == referenceData[10]); + + REQUIRE(window.reference[0].m_columnOption == SHIFT_TYPE::SHIFT_FREE); + REQUIRE(window.reference[0].m_shiftOption == SHIFT_TYPE::SHIFT_FIX); + REQUIRE(window.reference[0].m_shiftValue == 0.0); + REQUIRE(window.reference[0].m_squeezeOption == SHIFT_TYPE::SHIFT_FIX); + REQUIRE(window.reference[0].m_squeezeValue == 1.0); } TEST_CASE("FitWindow - AddAsSky", "[CFitWindow]") { CFitWindow window; - REQUIRE(window.nRef == 0); - REQUIRE(window.ref[0].m_specieName.empty()); + REQUIRE(window.reference.size() == 0); std::vector referenceData = GenerateVector(0.0, 1.0, window.specLength); // Act AddAsSky(window, referenceData); // Assert - REQUIRE(window.nRef == 1); - REQUIRE(window.ref[0].m_specieName == "sky"); - REQUIRE((size_t)window.ref[0].m_data->GetSize() == referenceData.size()); - REQUIRE(window.ref[0].m_data->m_crossSection[10] == referenceData[10]); - - REQUIRE(window.ref[0].m_columnOption == SHIFT_TYPE::SHIFT_FIX); - REQUIRE(window.ref[0].m_columnValue == -1.0); - REQUIRE(window.ref[0].m_shiftOption == SHIFT_TYPE::SHIFT_FIX); - REQUIRE(window.ref[0].m_squeezeOption == SHIFT_TYPE::SHIFT_FIX); + REQUIRE(window.reference.size() == 1); + REQUIRE(window.reference[0].m_specieName == "sky"); + REQUIRE((size_t)window.reference[0].m_data->GetSize() == referenceData.size()); + REQUIRE(window.reference[0].m_data->m_crossSection[10] == referenceData[10]); + + REQUIRE(window.reference[0].m_columnOption == SHIFT_TYPE::SHIFT_FIX); + REQUIRE(window.reference[0].m_columnValue == -1.0); + REQUIRE(window.reference[0].m_shiftOption == SHIFT_TYPE::SHIFT_FIX); + REQUIRE(window.reference[0].m_squeezeOption == SHIFT_TYPE::SHIFT_FIX); } TEST_CASE("FitWindow - AddAsSky - shift free", "[CFitWindow]") { CFitWindow window; - REQUIRE(window.nRef == 0); - REQUIRE(window.ref[0].m_specieName.empty()); + REQUIRE(window.reference.size() == 0); std::vector referenceData = GenerateVector(0.0, 1.0, window.specLength); // Act AddAsSky(window, referenceData, SHIFT_TYPE::SHIFT_FREE); // Assert - REQUIRE(window.nRef == 1); - REQUIRE(window.ref[0].m_specieName == "sky"); - REQUIRE((size_t)window.ref[0].m_data->GetSize() == referenceData.size()); - REQUIRE(window.ref[0].m_data->m_crossSection[10] == referenceData[10]); - - REQUIRE(window.ref[0].m_columnOption == SHIFT_TYPE::SHIFT_FIX); - REQUIRE(window.ref[0].m_columnValue == -1.0); - REQUIRE(window.ref[0].m_shiftOption == SHIFT_TYPE::SHIFT_FREE); - REQUIRE(window.ref[0].m_squeezeOption == SHIFT_TYPE::SHIFT_FIX); + REQUIRE(window.reference.size() == 1); + REQUIRE(window.reference[0].m_specieName == "sky"); + REQUIRE((size_t)window.reference[0].m_data->GetSize() == referenceData.size()); + REQUIRE(window.reference[0].m_data->m_crossSection[10] == referenceData[10]); + + REQUIRE(window.reference[0].m_columnOption == SHIFT_TYPE::SHIFT_FIX); + REQUIRE(window.reference[0].m_columnValue == -1.0); + REQUIRE(window.reference[0].m_shiftOption == SHIFT_TYPE::SHIFT_FREE); + REQUIRE(window.reference[0].m_squeezeOption == SHIFT_TYPE::SHIFT_FIX); } \ No newline at end of file diff --git a/UnitTests/UnitTest_PlumeInScanProperty.cpp b/UnitTests/UnitTest_PlumeInScanProperty.cpp index c3ac973..4223653 100644 --- a/UnitTests/UnitTest_PlumeInScanProperty.cpp +++ b/UnitTests/UnitTest_PlumeInScanProperty.cpp @@ -249,7 +249,7 @@ TEST_CASE("CalculatePlumeProperties with Gaussian plume", "[PlumeProperties]") auto plume = GenerateGaussianScanResult(200.0, acutalPlumeCenter, actualPlumeFwhm, columnOffsetInMeasurement); // add a couple of 'bad' measurements in the beginning... plume.m_spec[0].m_referenceResult[0].m_column = -5000.0; - plume.m_spec[0].m_referenceResult[1].m_column = -5000.0; + plume.m_spec[1].m_referenceResult[0].m_column = -5000.0; // Act auto plumeProperties = CalculatePlumeProperties(plume, StandardMolecule::SO2, message); diff --git a/UnitTests/UnitTest_VectorUtils.cpp b/UnitTests/UnitTest_VectorUtils.cpp index 368dae8..b2f77e8 100644 --- a/UnitTests/UnitTest_VectorUtils.cpp +++ b/UnitTests/UnitTest_VectorUtils.cpp @@ -1,5 +1,6 @@ #include "catch.hpp" #include +#include TEST_CASE("Min", "[Min][VectorUtils]") { @@ -331,4 +332,256 @@ TEST_CASE("FindNLowest", "[FindNLowest][VectorUtils]") REQUIRE(9 == result[6]); REQUIRE(12 == result[7]); } -} \ No newline at end of file +} + +TEST_CASE("HighPassBinomial - Constant vector", "[HighPassBinomial][VectorUtils]") +{ + CBasicMath math; + + SECTION("Constant value vector zeroes out after one iteration") + { + std::vector values(128, 5.0); + std::vector values2(128, 5.0); + + // Act + ::HighPassBinomial(values, 1); + math.HighPassBinomial(values2.data(), static_cast(values2.size()), 1); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } + + SECTION("Constant value vector zeroes out after 500 iterations") + { + std::vector values(128, 5.0); + std::vector values2(128, 5.0); + + // Act + ::HighPassBinomial(values, 500); + math.HighPassBinomial(values2.data(), static_cast(values2.size()), 500); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } + + SECTION("Odd sized Constant value vector zeroes out after 500 iterations") + { + std::vector values(61, 5.0); + std::vector values2(61, 5.0); + + // Act + ::HighPassBinomial(values, 1); + math.HighPassBinomial(values2.data(), static_cast(values2.size()), 500); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } +} + + +TEST_CASE("HighPassBinomial - Random vector matches result from BasicMath", "[HighPassBinomial][VectorUtils]") +{ + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_real_distribution<> dist(-10.0, 10.0); // distribution in range [0, +10.0] + + std::vector values; + std::vector values2; + + CBasicMath math; + + SECTION("Even sized value vector: one iteration") + { + for (size_t ii = 0; ii < 128; ++ii) + { + const double v = dist(rng); + values.push_back(v); + values2.push_back(v); + } + + // Act + ::HighPassBinomial(values, 1); + math.HighPassBinomial(values2.data(), static_cast(values2.size()), 1); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } + SECTION("Even sized value vector: 500 iterations") + { + for (size_t ii = 0; ii < 128; ++ii) + { + const double v = dist(rng); + values.push_back(v); + values2.push_back(v); + } + + // Act + ::HighPassBinomial(values, 500); + math.HighPassBinomial(values2.data(), static_cast(values2.size()), 500); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } + + SECTION("Odd sized value vector: 500 iterations") + { + for (size_t ii = 0; ii < 61; ++ii) + { + const double v = dist(rng); + values.push_back(v); + values2.push_back(v); + } + + // Act + ::HighPassBinomial(values, 500); + math.HighPassBinomial(values2.data(), static_cast(values2.size()), 500); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } +} + + +TEST_CASE("LowPassBinomial - Constant vector", "[LowPassBinomial][VectorUtils]") +{ + CBasicMath math; + + SECTION("Constant value vector zeroes out after one iteration") + { + std::vector values(128, 5.0); + std::vector values2(128, 5.0); + + // Act + ::LowPassBinomial(values, 1); + math.LowPassBinomial(values2.data(), static_cast(values2.size()), 1); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } + + SECTION("Constant value vector zeroes out after 500 iterations") + { + std::vector values(128, 5.0); + std::vector values2(128, 5.0); + + // Act + ::LowPassBinomial(values, 500); + math.LowPassBinomial(values2.data(), static_cast(values2.size()), 500); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } + + SECTION("Odd sized Constant value vector zeroes out after 500 iterations") + { + std::vector values(61, 5.0); + std::vector values2(61, 5.0); + + // Act + ::LowPassBinomial(values, 1); + math.LowPassBinomial(values2.data(), static_cast(values2.size()), 500); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } +} + + +TEST_CASE("LowPassBinomial - Random vector matches result from BasicMath", "[LowPassBinomial][VectorUtils]") +{ + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_real_distribution<> dist(-10.0, 10.0); // distribution in range [0, +10.0] + + std::vector values; + std::vector values2; + + CBasicMath math; + + SECTION("Even sized value vector: one iteration") + { + for (size_t ii = 0; ii < 128; ++ii) + { + const double v = dist(rng); + values.push_back(v); + values2.push_back(v); + } + + // Act + ::LowPassBinomial(values, 1); + math.LowPassBinomial(values2.data(), static_cast(values2.size()), 1); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } + + SECTION("Even sized value vector: 500 iterations") + { + for (size_t ii = 0; ii < 128; ++ii) + { + const double v = dist(rng); + values.push_back(v); + values2.push_back(v); + } + + // Act + ::LowPassBinomial(values, 500); + math.LowPassBinomial(values2.data(), static_cast(values2.size()), 500); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } + + SECTION("Odd sized value vector: 500 iterations") + { + for (size_t ii = 0; ii < 61; ++ii) + { + const double v = dist(rng); + values.push_back(v); + values2.push_back(v); + } + + // Act + ::LowPassBinomial(values, 500); + math.LowPassBinomial(values2.data(), static_cast(values2.size()), 500); + + // Assert, the two vectors should now have the same value. + for (size_t ii = 0; ii < values.size(); ++ii) + { + REQUIRE(Approx(values[ii]) == values2[ii]); + } + } +} diff --git a/include/SpectralEvaluation/Evaluation/BasicMath.h b/include/SpectralEvaluation/Evaluation/BasicMath.h index 05b9d25..5f526aa 100644 --- a/include/SpectralEvaluation/Evaluation/BasicMath.h +++ b/include/SpectralEvaluation/Evaluation/BasicMath.h @@ -47,7 +47,7 @@ class CBasicMath void Mul(double* fFirst, int iSize, double fConst); - void Sub(double* fFirst, int iSize, double fConst); + void Sub(double* fFirst, int iSize, double fConst) const; void Add(double* fFirst, int iSize, double fConst); diff --git a/include/SpectralEvaluation/Evaluation/EvaluationBase.h b/include/SpectralEvaluation/Evaluation/EvaluationBase.h index 524c48f..f8c2785 100644 --- a/include/SpectralEvaluation/Evaluation/EvaluationBase.h +++ b/include/SpectralEvaluation/Evaluation/EvaluationBase.h @@ -71,11 +71,11 @@ class CEvaluationBase : public CBasicMath /** Removes the offset from the supplied spectrum */ // TODO: Change the last parameter from a boolean to the pixel-range which should be used!! - void RemoveOffset(double* spectrum, int sumChn, bool UV = true); + void RemoveOffset(double* spectrum, int sumChn, IndexRange range) const; /** Removes the offset from the supplied spectrum. The offset is calculated in the [from, to] region but the offset is subtracted from the entire spectrum. */ - void RemoveOffset(std::vector& spectrum, size_t from, size_t to); + void RemoveOffset(std::vector& spectrum, IndexRange range) const; /** Sets the sky-spectrum to use. This will be used in the upcoming evaluations. The provided spectrum must have been corrected for dark. */ @@ -167,16 +167,17 @@ class CEvaluationBase : public CBasicMath void CreateXDataVector(int numberOfChannels); // Prepares the spectra for evaluation + [[deprecated]] void PrepareSpectra(double* sky, double* meas, const CFitWindow& window); // Prepares the spectra for evaluation - void PrepareSpectra_HP_Div(double* sky, double* meas, const CFitWindow& window); + void PrepareSpectra(std::vector& sky, std::vector& meas, const CFitWindow& window) const; - // Prepares the spectra for evaluation - void PrepareSpectra_HP_Sub(double* sky, double* meas, const CFitWindow& window); + void PrepareSpectra_HP_Div(std::vector& sky, std::vector& meas, const CFitWindow& window) const; - // Prepares the spectra for evaluation - void PrepareSpectra_Poly(double* sky, double* meas, const CFitWindow& window); + void PrepareSpectra_HP_Sub(std::vector& sky, std::vector& meas, const CFitWindow& window) const; + + void PrepareSpectra_Poly(std::vector& sky, std::vector& meas, const CFitWindow& window) const; /** Updates the m_residual and m_result.delta */ void SaveResidual(MathFit::CStandardFit& firstFit); @@ -195,5 +196,16 @@ class CEvaluationBase : public CBasicMath // @return the name of the reference with the given index into m_ref std::string GetReferenceName(size_t referenceIndex) const; + +private: + // The following method is deprecated and should be removed eventually. + void PrepareSpectra_HP_Div(double* sky, double* meas, const CFitWindow& window); + + // The following method is deprecated and should be removed eventually. + void PrepareSpectra_HP_Sub(double* sky, double* meas, const CFitWindow& window); + + // The following method is deprecated and should be removed eventually. + void PrepareSpectra_Poly(double* sky, double* meas, const CFitWindow& window); + }; } \ No newline at end of file diff --git a/include/SpectralEvaluation/Evaluation/FitWindow.h b/include/SpectralEvaluation/Evaluation/FitWindow.h index 99f4569..302c6a9 100644 --- a/include/SpectralEvaluation/Evaluation/FitWindow.h +++ b/include/SpectralEvaluation/Evaluation/FitWindow.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -33,6 +34,14 @@ class CFitWindow CFitWindow& operator=(CFitWindow&& other); CFitWindow(CFitWindow&& other); + // The standard index range to use when removing offset for an USB2000 device in the UV range. + static IndexRange StandardUvOffsetRemovalRange() { return IndexRange(50, 200); } + + // The standard index range to use when removing offset for an USB2000 device in the visible range. + static IndexRange StandardUSB2000OffsetRemovalRange() { return IndexRange(2, 20); } + + size_t NumberOfReferences() const { return this->reference.size(); } + /** The lower edge of the fit window (in pixels) */ int fitLow = 320; @@ -48,12 +57,7 @@ class CFitWindow int channel = 0; /** The reference files to use */ - // TODO: Use std::vector for this, to remove unnecessary limit on number of references - CReferenceFile ref[MAX_N_REFERENCES]; - - /** The number of references to use */ - // TODO: Remove when 'ref' is a std::vector. - size_t nRef = 0; + std::vector reference; /** The Fraunhofer-reference spectrum which we can use to determine the shift (&squeeze) between the measured @@ -108,9 +112,11 @@ class CFitWindow This parameter works in the same way as the CSpectrumInfo::m_interlaceStep */ int interlaceStep = 1; - /** 'UV' is true if the start wavelength of the spectrum is 290 nm or shorter */ - // TODO: replace this with the pixel-range which should be used for the offset-correction (which what this variable is used for) - int UV = 1; + // offsetRemovalRange is the range of pixels which should be used to calculate an 'offset' + // which is then subtracted from the spectrum before the evaluation. + // For USB2000 series spectrometers starting in the UV range, this is the pixel range 50 to 200. + // For USB2000 series spectrometers starting in the visible range, this is the pixel range 2 to 20 (which are the optically covered pixels). + IndexRange offsetRemovalRange = IndexRange(50, 200); /** True if the scan should be twice, once for finding the highest column value. The spectrum with the highest column value is then evluated again with diff --git a/include/SpectralEvaluation/Evaluation/ReferenceFile.h b/include/SpectralEvaluation/Evaluation/ReferenceFile.h index 262ab73..0c74ab8 100644 --- a/include/SpectralEvaluation/Evaluation/ReferenceFile.h +++ b/include/SpectralEvaluation/Evaluation/ReferenceFile.h @@ -15,13 +15,11 @@ class InvalidReferenceException : public std::exception public: InvalidReferenceException(const char* msg) : m_msg(msg) - { - } + {} InvalidReferenceException(std::string msg) : m_msg(msg.c_str()) - { - } + {} const char* what() const noexcept override final { return m_msg.c_str(); } }; @@ -58,6 +56,10 @@ class CReferenceFile CReferenceFile() = default; ~CReferenceFile() = default; + CReferenceFile(const std::string path) + : m_path(path) + {} + /** Creates this reference file as an in-memory copy of the provided cross section */ CReferenceFile(const CCrossSectionData& contents); diff --git a/include/SpectralEvaluation/Math/IndexRange.h b/include/SpectralEvaluation/Math/IndexRange.h index bacb2fa..91ac3da 100644 --- a/include/SpectralEvaluation/Math/IndexRange.h +++ b/include/SpectralEvaluation/Math/IndexRange.h @@ -1,18 +1,18 @@ #pragma once #include +#include namespace novac { -/** Basic representation of a range of indices. */ +// IndexRange is a basic representation of a range of indices. +// Notice that there is no default constructor here, the values must be set. struct IndexRange { IndexRange(size_t low, size_t high) - { - this->from = (low < high) ? low : high; - this->to = (low < high) ? high : low; - } + : from((low < high) ? low : high), to((low < high) ? high : low) + {} size_t from = 0; @@ -21,6 +21,11 @@ struct IndexRange inline bool Empty() const { return to <= from; } inline size_t Length() const { return to - from; } + + bool operator==(const IndexRange& other) const { return this->from == other.from && this->to == other.to; } + + friend std::ostream& operator<<(std::ostream& os, const IndexRange& range); + }; } diff --git a/include/SpectralEvaluation/VectorUtils.h b/include/SpectralEvaluation/VectorUtils.h index b6f67cd..ac2a9a3 100644 --- a/include/SpectralEvaluation/VectorUtils.h +++ b/include/SpectralEvaluation/VectorUtils.h @@ -71,6 +71,15 @@ double SumOfSquaredDifferences(const std::vector& a, const std::vector& values, double factor); +// Divides each value in 'first' with the corresponding value in 'second'. +// The result is stored in 'first'. +// All divisions by zero will be equal to zero. +// @throws std::invalid_argument if first.size() != second.size() +void Div(std::vector& first, const std::vector& second); + +/** Adds the provided value to each value in the vector */ +void Add(std::vector& values, double factor); + /** Inverts all values in the provided vector, i.e. values[ii] = 1.0/values[ii] */ void Invert(std::vector& values); @@ -85,6 +94,15 @@ void Mult(const std::vector& firstVector, std::vector& secondVec /** Calculates the exponent of the provided values */ void Exp(std::vector& values); +/** Calculates the natural logarithm (base e) of the provided values */ +void Log(std::vector& values); + +/** Performs a high-pass binomial filtering on the given values */ +void HighPassBinomial(std::vector& values, int nIterations); + +/** Performs a low-pass binomial filtering on the given values */ +void LowPassBinomial(std::vector& values, int nIterations); + /** @return the Average of all the given values If values.size() == 0 then 0.0 is returned. */ double Average(const std::vector& values); diff --git a/src/Calibration/InstrumentLineShapeEstimation.cpp b/src/Calibration/InstrumentLineShapeEstimation.cpp index 13c7cc9..8baab08 100644 --- a/src/Calibration/InstrumentLineShapeEstimation.cpp +++ b/src/Calibration/InstrumentLineShapeEstimation.cpp @@ -641,38 +641,42 @@ namespace novac math.Log(filteredFraunhoferSpectrum.data(), currentFraunhoferSpectrum->m_length); // 1. Include the Fraunhofer reference as the first reference - doasFitSetup.nRef = 1; - doasFitSetup.ref[0].m_data = std::make_unique(filteredFraunhoferSpectrum); - doasFitSetup.ref[0].m_columnOption = novac::SHIFT_TYPE::SHIFT_FIX; - doasFitSetup.ref[0].m_columnValue = -1.0; - doasFitSetup.ref[0].m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; - doasFitSetup.ref[0].m_squeezeValue = 1.0; - doasFitSetup.ref[0].m_shiftOption = shiftOption; - doasFitSetup.ref[0].m_shiftValue = 0.0; + doasFitSetup.reference.clear(); + CReferenceFile fraunhofer; + fraunhofer.m_data = std::make_unique(filteredFraunhoferSpectrum); + fraunhofer.m_columnOption = novac::SHIFT_TYPE::SHIFT_FIX; + fraunhofer.m_columnValue = -1.0; + fraunhofer.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; + fraunhofer.m_squeezeValue = 1.0; + fraunhofer.m_shiftOption = shiftOption; + fraunhofer.m_shiftValue = 0.0; + doasFitSetup.reference.push_back(fraunhofer); // 2. Include the Ring spectrum as the second reference. auto ringSpectrum = std::make_unique(Doasis::Scattering::CalcRingSpectrum(*currentFraunhoferSpectrum)); std::vector filteredRingSpectrum{ ringSpectrum->m_data, ringSpectrum->m_data + ringSpectrum->m_length }; math.Log(filteredRingSpectrum.data(), ringSpectrum->m_length); - doasFitSetup.ref[doasFitSetup.nRef].m_data = std::make_unique(filteredRingSpectrum); - doasFitSetup.ref[doasFitSetup.nRef].m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; - doasFitSetup.ref[doasFitSetup.nRef].m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; - doasFitSetup.ref[doasFitSetup.nRef].m_squeezeValue = 1.0; - doasFitSetup.ref[doasFitSetup.nRef].m_shiftOption = novac::SHIFT_TYPE::SHIFT_LINK; - doasFitSetup.ref[doasFitSetup.nRef].m_shiftValue = 0.0; - doasFitSetup.nRef += 1; + CReferenceFile ringReference; + ringReference.m_data = std::make_unique(filteredRingSpectrum); + ringReference.m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; + ringReference.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; + ringReference.m_squeezeValue = 1.0; + ringReference.m_shiftOption = novac::SHIFT_TYPE::SHIFT_LINK; + ringReference.m_shiftValue = 0.0; + doasFitSetup.reference.push_back(ringReference); // 3. Include the Ozone spectrum as the third reference (if required) if (ozoneSpectrumGen != nullptr) { + CReferenceFile ozoneReference; auto currentOzoneSpectrum = ozoneSpectrumGen->GetCrossSection(selectedPixelToWavelengthMapping, sampledLineShape, fwhm, false); - doasFitSetup.ref[doasFitSetup.nRef].m_data = std::make_unique(*currentOzoneSpectrum); - doasFitSetup.ref[doasFitSetup.nRef].m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; - doasFitSetup.ref[doasFitSetup.nRef].m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; - doasFitSetup.ref[doasFitSetup.nRef].m_squeezeValue = 1.0; - doasFitSetup.ref[doasFitSetup.nRef].m_shiftOption = novac::SHIFT_TYPE::SHIFT_LINK; - doasFitSetup.ref[doasFitSetup.nRef].m_shiftValue = 0.0; - doasFitSetup.nRef += 1; + ozoneReference.m_data = std::make_unique(*currentOzoneSpectrum); + ozoneReference.m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; + ozoneReference.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; + ozoneReference.m_squeezeValue = 1.0; + ozoneReference.m_shiftOption = novac::SHIFT_TYPE::SHIFT_LINK; + ozoneReference.m_shiftValue = 0.0; + doasFitSetup.reference.push_back(ozoneReference); } // 4. Include the pseudo-absorbers derived from the derivative of the instrument-line-shape function. @@ -693,15 +697,16 @@ namespace novac pseudoAbsorber->m_crossSection[ii] = IsZero(currentFraunhoferSpectrum->m_data[ii]) ? 0.0 : diffFraunhofer->m_data[ii] / currentFraunhoferSpectrum->m_data[ii]; } - parameterIndices.push_back(doasFitSetup.nRef); // remember the index to pick out the results from + parameterIndices.push_back(doasFitSetup.reference.size()); // remember the index to pick out the results from - doasFitSetup.ref[doasFitSetup.nRef].m_data = std::move(pseudoAbsorber); - doasFitSetup.ref[doasFitSetup.nRef].m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; - doasFitSetup.ref[doasFitSetup.nRef].m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; - doasFitSetup.ref[doasFitSetup.nRef].m_squeezeValue = 1.0; - doasFitSetup.ref[doasFitSetup.nRef].m_shiftOption = novac::SHIFT_TYPE::SHIFT_LINK; - doasFitSetup.ref[doasFitSetup.nRef].m_shiftValue = 0.0; - doasFitSetup.nRef += 1; + CReferenceFile newReference; + newReference.m_data = std::move(pseudoAbsorber); + newReference.m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; + newReference.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; + newReference.m_squeezeValue = 1.0; + newReference.m_shiftOption = novac::SHIFT_TYPE::SHIFT_LINK; + newReference.m_shiftValue = 0.0; + doasFitSetup.reference.push_back(newReference); } DoasFit doas; @@ -721,11 +726,11 @@ namespace novac // Now also estimate the currentError by doing the DOAS fit without the Pseudo-absorbers { - doasFitSetup.ref[parameterIndices.front()].m_columnOption = novac::SHIFT_TYPE::SHIFT_FIX; - doasFitSetup.ref[parameterIndices.front()].m_columnValue = 0.0; + doasFitSetup.reference[parameterIndices.front()].m_columnOption = novac::SHIFT_TYPE::SHIFT_FIX; + doasFitSetup.reference[parameterIndices.front()].m_columnValue = 0.0; - doasFitSetup.ref[parameterIndices.back()].m_columnOption = novac::SHIFT_TYPE::SHIFT_FIX; - doasFitSetup.ref[parameterIndices.back()].m_columnValue = 0.0; + doasFitSetup.reference[parameterIndices.back()].m_columnOption = novac::SHIFT_TYPE::SHIFT_FIX; + doasFitSetup.reference[parameterIndices.back()].m_columnValue = 0.0; doas.Setup(doasFitSetup); diff --git a/src/DialogControllers/RatioCalculationController.cpp b/src/DialogControllers/RatioCalculationController.cpp index 0adde45..a1b7df7 100644 --- a/src/DialogControllers/RatioCalculationController.cpp +++ b/src/DialogControllers/RatioCalculationController.cpp @@ -268,7 +268,7 @@ size_t RatioCalculationController::NumberOfPakFilesInSetup() const } -void SetupFitWindowReferences(novac::CFitWindow& window, const std::vector& references, const novac::WavelengthRange& wavelengthRange, bool isMajor) +static void SetupFitWindowReferences(novac::CFitWindow& window, const std::vector& references, const novac::WavelengthRange& wavelengthRange, bool isMajor) { window.ringCalculation = novac::RING_CALCULATION_OPTION::DO_NOT_CALCULATE_RING; window.includeIntensitySpacePolyominal = true; @@ -284,14 +284,15 @@ void SetupFitWindowReferences(novac::CFitWindow& window, const std::vectorm_waveLength.size() == 0) + if (window.reference.front().m_data->m_waveLength.size() == 0) { std::stringstream message; - message << "failed to set the fit range,the reference " << window.ref[0].m_specieName << " does not have a wavelength calibration"; + message << "failed to set the fit range,the reference " << window.reference.front().m_specieName << " does not have a wavelength calibration"; throw std::invalid_argument(message.str()); } // Setup the channel range where the fit should be done. - const double fractionalFitLow = window.ref[0].m_data->FindWavelength(wavelengthRange.low); - const double fractionalFitHigh = window.ref[0].m_data->FindWavelength(wavelengthRange.high); + const double fractionalFitLow = window.reference.front().m_data->FindWavelength(wavelengthRange.low); + const double fractionalFitHigh = window.reference.front().m_data->FindWavelength(wavelengthRange.high); if (fractionalFitLow < -0.5 || fractionalFitHigh < -0.5) { std::stringstream message; - message << "failed to set the fit range,the reference " << window.ref[0].m_specieName; + message << "failed to set the fit range,the reference " << window.reference.front().m_specieName; message << " does not cover the fit range: " << wavelengthRange.low << " to " << wavelengthRange.high << " nm"; throw std::invalid_argument(message.str()); } diff --git a/src/Evaluation/BasicMath.cpp b/src/Evaluation/BasicMath.cpp index 51541ad..7a5dddd 100644 --- a/src/Evaluation/BasicMath.cpp +++ b/src/Evaluation/BasicMath.cpp @@ -31,12 +31,10 @@ using namespace MathFit; bool CBasicMath::mDoNotUseMathLimits = false; CBasicMath::CBasicMath() -{ -} +{} CBasicMath::~CBasicMath() -{ -} +{} double* CBasicMath::LowPassBinomial(double* fData, int iSize, int iNIterations) { @@ -104,10 +102,11 @@ double* CBasicMath::HighPassBinomial(double* fData, int iSize, int iNIterations) double* CBasicMath::Log(double* fData, int iSize) { - int i; - - for (i = 0; i < iSize; i++) + for (int i = 0; i < iSize; i++) + { fData[i] = fData[i] <= 0 ? 0.0 : log(fData[i]); + } + return(fData); } @@ -348,7 +347,7 @@ void CBasicMath::Sub(double* fFirst, const double* fSec, int iSize, double fFact } } -void CBasicMath::Sub(double* fFirst, int iSize, double fConst) +void CBasicMath::Sub(double* fFirst, int iSize, double fConst) const { for (int i = 0; i < iSize; i++) { diff --git a/src/Evaluation/DoasFit.cpp b/src/Evaluation/DoasFit.cpp index b0bae26..175ce26 100644 --- a/src/Evaluation/DoasFit.cpp +++ b/src/Evaluation/DoasFit.cpp @@ -116,13 +116,13 @@ void DoasFit::Setup(const CFitWindow& setup) newReferenceSetup->columnScaleFactor = (setup.fitType == FIT_TYPE::FIT_POLY) ? -1.0 : +1.0; // 1) Create the references - for (size_t refIdx = 0; refIdx < setup.nRef; ++refIdx) + for (size_t refIdx = 0; refIdx < setup.reference.size(); ++refIdx) { - if (setup.ref[refIdx].m_data == nullptr) + if (setup.reference[refIdx].m_data == nullptr) { throw std::invalid_argument("Error in setting up DOAS fit, reference is null."); } - if (setup.ref[refIdx].m_data->GetSize() == 0) + if (setup.reference[refIdx].m_data->GetSize() == 0) { throw std::invalid_argument("Error in setting up DOAS fit, reference does not contain any data."); } @@ -133,9 +133,9 @@ void DoasFit::Setup(const CFitWindow& setup) // transformation of the spectral data into a B-Spline that will be used to interpolate the // reference spectrum during shift and squeeze operations MathFit::CVector yValues; - yValues.Copy(setup.ref[refIdx].m_data->m_crossSection.data(), setup.ref[refIdx].m_data->GetSize()); + yValues.Copy(setup.reference[refIdx].m_data->m_crossSection.data(), setup.reference[refIdx].m_data->GetSize()); - auto tempXVec = Generate(0, setup.ref[refIdx].m_data->GetSize()); // the x-axis vector here is pixels. + auto tempXVec = Generate(0, setup.reference[refIdx].m_data->GetSize()); // the x-axis vector here is pixels. if (!newRef->SetData(tempXVec, yValues)) { throw std::invalid_argument("Error in DOAS reference, failed to initialize spline object. Make sure that the reference is ok and try again."); @@ -143,21 +143,21 @@ void DoasFit::Setup(const CFitWindow& setup) // Finally add this reference to the vector newReferenceSetup->m_ref.push_back(newRef); - newReferenceSetup->name.push_back(setup.ref[refIdx].m_specieName); + newReferenceSetup->name.push_back(setup.reference[refIdx].m_specieName); } // 2) Couple the references - for (size_t refIdx = 0; refIdx < setup.nRef; ++refIdx) + for (size_t refIdx = 0; refIdx < setup.reference.size(); ++refIdx) { // Check the options for the column value. // Notice the multiplication with minus one here, this is done to keep the signs of everything compatible with DOASIS. - switch (setup.ref[refIdx].m_columnOption) + switch (setup.reference[refIdx].m_columnOption) { case novac::SHIFT_TYPE::SHIFT_FIX: - newReferenceSetup->m_ref[refIdx]->FixParameter(MathFit::CReferenceSpectrumFunction::CONCENTRATION, -1.0 * setup.ref[refIdx].m_columnValue * newReferenceSetup->m_ref[refIdx]->GetAmplitudeScale()); + newReferenceSetup->m_ref[refIdx]->FixParameter(MathFit::CReferenceSpectrumFunction::CONCENTRATION, -1.0 * setup.reference[refIdx].m_columnValue * newReferenceSetup->m_ref[refIdx]->GetAmplitudeScale()); break; case novac::SHIFT_TYPE::SHIFT_LINK: - newReferenceSetup->m_ref[(int)setup.ref[refIdx].m_columnValue]->LinkParameter(MathFit::CReferenceSpectrumFunction::CONCENTRATION, *newReferenceSetup->m_ref[refIdx], MathFit::CReferenceSpectrumFunction::CONCENTRATION); + newReferenceSetup->m_ref[(int)setup.reference[refIdx].m_columnValue]->LinkParameter(MathFit::CReferenceSpectrumFunction::CONCENTRATION, *newReferenceSetup->m_ref[refIdx], MathFit::CReferenceSpectrumFunction::CONCENTRATION); break; case novac::SHIFT_TYPE::SHIFT_FREE: newReferenceSetup->m_ref[refIdx]->ReleaseParameter(MathFit::CReferenceSpectrumFunction::CONCENTRATION); @@ -167,16 +167,16 @@ void DoasFit::Setup(const CFitWindow& setup) } // Check the options for the shift - switch (setup.ref[refIdx].m_shiftOption) + switch (setup.reference[refIdx].m_shiftOption) { case novac::SHIFT_TYPE::SHIFT_FIX: - newReferenceSetup->m_ref[refIdx]->FixParameter(MathFit::CReferenceSpectrumFunction::SHIFT, setup.ref[refIdx].m_shiftValue); + newReferenceSetup->m_ref[refIdx]->FixParameter(MathFit::CReferenceSpectrumFunction::SHIFT, setup.reference[refIdx].m_shiftValue); break; case novac::SHIFT_TYPE::SHIFT_LINK: - newReferenceSetup->m_ref[(int)setup.ref[refIdx].m_shiftValue]->LinkParameter(MathFit::CReferenceSpectrumFunction::SHIFT, *newReferenceSetup->m_ref[refIdx], MathFit::CReferenceSpectrumFunction::SHIFT); + newReferenceSetup->m_ref[(int)setup.reference[refIdx].m_shiftValue]->LinkParameter(MathFit::CReferenceSpectrumFunction::SHIFT, *newReferenceSetup->m_ref[refIdx], MathFit::CReferenceSpectrumFunction::SHIFT); break; case novac::SHIFT_TYPE::SHIFT_LIMIT: - newReferenceSetup->m_ref[refIdx]->SetParameterLimits(MathFit::CReferenceSpectrumFunction::SHIFT, (MathFit::TFitData)setup.ref[refIdx].m_shiftValue, (MathFit::TFitData)setup.ref[refIdx].m_shiftMaxValue, 1); + newReferenceSetup->m_ref[refIdx]->SetParameterLimits(MathFit::CReferenceSpectrumFunction::SHIFT, (MathFit::TFitData)setup.reference[refIdx].m_shiftValue, (MathFit::TFitData)setup.reference[refIdx].m_shiftMaxValue, 1); break; default: newReferenceSetup->m_ref[refIdx]->SetDefaultParameter(MathFit::CReferenceSpectrumFunction::SHIFT, (MathFit::TFitData)0.0); @@ -185,16 +185,16 @@ void DoasFit::Setup(const CFitWindow& setup) } // Check the options for the squeeze - switch (setup.ref[refIdx].m_squeezeOption) + switch (setup.reference[refIdx].m_squeezeOption) { case novac::SHIFT_TYPE::SHIFT_FIX: - newReferenceSetup->m_ref[refIdx]->FixParameter(MathFit::CReferenceSpectrumFunction::SQUEEZE, setup.ref[refIdx].m_squeezeValue); + newReferenceSetup->m_ref[refIdx]->FixParameter(MathFit::CReferenceSpectrumFunction::SQUEEZE, setup.reference[refIdx].m_squeezeValue); break; case novac::SHIFT_TYPE::SHIFT_LINK: - newReferenceSetup->m_ref[(int)setup.ref[refIdx].m_squeezeValue]->LinkParameter(MathFit::CReferenceSpectrumFunction::SQUEEZE, *newReferenceSetup->m_ref[refIdx], MathFit::CReferenceSpectrumFunction::SQUEEZE); + newReferenceSetup->m_ref[(int)setup.reference[refIdx].m_squeezeValue]->LinkParameter(MathFit::CReferenceSpectrumFunction::SQUEEZE, *newReferenceSetup->m_ref[refIdx], MathFit::CReferenceSpectrumFunction::SQUEEZE); break; case novac::SHIFT_TYPE::SHIFT_LIMIT: - newReferenceSetup->m_ref[refIdx]->SetParameterLimits(MathFit::CReferenceSpectrumFunction::SQUEEZE, (MathFit::TFitData)setup.ref[refIdx].m_squeezeValue, (MathFit::TFitData)setup.ref[refIdx].m_squeezeMaxValue, 1e7); + newReferenceSetup->m_ref[refIdx]->SetParameterLimits(MathFit::CReferenceSpectrumFunction::SQUEEZE, (MathFit::TFitData)setup.reference[refIdx].m_squeezeValue, (MathFit::TFitData)setup.reference[refIdx].m_squeezeMaxValue, 1e7); break; default: newReferenceSetup->m_ref[refIdx]->SetDefaultParameter(MathFit::CReferenceSpectrumFunction::SQUEEZE, (MathFit::TFitData)1.0); diff --git a/src/Evaluation/EvaluationBase.cpp b/src/Evaluation/EvaluationBase.cpp index 4a41351..d48ddbe 100644 --- a/src/Evaluation/EvaluationBase.cpp +++ b/src/Evaluation/EvaluationBase.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -22,8 +23,6 @@ #include #include -using namespace MathFit; - namespace novac { CEvaluationBase::CEvaluationBase(novac::ILogger& log) @@ -75,7 +74,7 @@ int CEvaluationBase::CreateReferenceSpectra() ClearRefereneSpectra(); // 1) Create the references - for (size_t i = 0; i < m_window.nRef; i++) + for (size_t i = 0; i < m_window.reference.size(); i++) { auto newRef = DefaultReferenceSpectrumFunction(); @@ -83,9 +82,9 @@ int CEvaluationBase::CreateReferenceSpectra() // transformation of the spectral data into a B-Spline that will be used to interpolate the // reference spectrum during shift and squeeze operations CVector yValues; - yValues.Copy(m_window.ref[i].m_data->m_crossSection.data(), m_window.ref[i].m_data->GetSize()); + yValues.Copy(m_window.reference[i].m_data->m_crossSection.data(), m_window.reference[i].m_data->GetSize()); - auto tempXVec = vXData.SubVector(0, m_window.ref[i].m_data->GetSize()); + auto tempXVec = vXData.SubVector(0, m_window.reference[i].m_data->GetSize()); if (!newRef->SetData(tempXVec, yValues)) { Error0("Error initializing spline object!"); @@ -97,16 +96,16 @@ int CEvaluationBase::CreateReferenceSpectra() } // 2) Couple the references - for (size_t i = 0; i < m_window.nRef; i++) + for (size_t i = 0; i < m_window.reference.size(); i++) { // Chech the options for the column value - switch (m_window.ref[i].m_columnOption) + switch (m_window.reference[i].m_columnOption) { case SHIFT_TYPE::SHIFT_FIX: - m_ref[i]->FixParameter(CReferenceSpectrumFunction::CONCENTRATION, m_window.ref[i].m_columnValue * m_ref[i]->GetAmplitudeScale()); + m_ref[i]->FixParameter(CReferenceSpectrumFunction::CONCENTRATION, m_window.reference[i].m_columnValue * m_ref[i]->GetAmplitudeScale()); break; case SHIFT_TYPE::SHIFT_LINK: - m_ref[(int)m_window.ref[i].m_columnValue]->LinkParameter(CReferenceSpectrumFunction::CONCENTRATION, *m_ref[i], CReferenceSpectrumFunction::CONCENTRATION); + m_ref[(int)m_window.reference[i].m_columnValue]->LinkParameter(CReferenceSpectrumFunction::CONCENTRATION, *m_ref[i], CReferenceSpectrumFunction::CONCENTRATION); break; case SHIFT_TYPE::SHIFT_FREE: m_ref[i]->ReleaseParameter(CReferenceSpectrumFunction::CONCENTRATION); @@ -116,16 +115,16 @@ int CEvaluationBase::CreateReferenceSpectra() } // Check the options for the shift - switch (m_window.ref[i].m_shiftOption) + switch (m_window.reference[i].m_shiftOption) { case SHIFT_TYPE::SHIFT_FIX: - m_ref[i]->FixParameter(CReferenceSpectrumFunction::SHIFT, m_window.ref[i].m_shiftValue); + m_ref[i]->FixParameter(CReferenceSpectrumFunction::SHIFT, m_window.reference[i].m_shiftValue); break; case SHIFT_TYPE::SHIFT_LINK: - m_ref[(int)m_window.ref[i].m_shiftValue]->LinkParameter(CReferenceSpectrumFunction::SHIFT, *m_ref[i], CReferenceSpectrumFunction::SHIFT); + m_ref[(int)m_window.reference[i].m_shiftValue]->LinkParameter(CReferenceSpectrumFunction::SHIFT, *m_ref[i], CReferenceSpectrumFunction::SHIFT); break; case SHIFT_TYPE::SHIFT_LIMIT: - m_ref[i]->SetParameterLimits(CReferenceSpectrumFunction::SHIFT, (TFitData)m_window.ref[i].m_shiftValue, (TFitData)m_window.ref[i].m_shiftMaxValue, 1); + m_ref[i]->SetParameterLimits(CReferenceSpectrumFunction::SHIFT, (TFitData)m_window.reference[i].m_shiftValue, (TFitData)m_window.reference[i].m_shiftMaxValue, 1); break; default: m_ref[i]->SetDefaultParameter(CReferenceSpectrumFunction::SHIFT, (TFitData)0.0); @@ -134,16 +133,16 @@ int CEvaluationBase::CreateReferenceSpectra() } // Check the options for the squeeze - switch (m_window.ref[i].m_squeezeOption) + switch (m_window.reference[i].m_squeezeOption) { case SHIFT_TYPE::SHIFT_FIX: - m_ref[i]->FixParameter(CReferenceSpectrumFunction::SQUEEZE, m_window.ref[i].m_squeezeValue); + m_ref[i]->FixParameter(CReferenceSpectrumFunction::SQUEEZE, m_window.reference[i].m_squeezeValue); break; case SHIFT_TYPE::SHIFT_LINK: - m_ref[(int)m_window.ref[i].m_squeezeValue]->LinkParameter(CReferenceSpectrumFunction::SQUEEZE, *m_ref[i], CReferenceSpectrumFunction::SQUEEZE); + m_ref[(int)m_window.reference[i].m_squeezeValue]->LinkParameter(CReferenceSpectrumFunction::SQUEEZE, *m_ref[i], CReferenceSpectrumFunction::SQUEEZE); break; case SHIFT_TYPE::SHIFT_LIMIT: - m_ref[i]->SetParameterLimits(CReferenceSpectrumFunction::SQUEEZE, (TFitData)m_window.ref[i].m_squeezeValue, (TFitData)m_window.ref[i].m_squeezeMaxValue, 1e7); + m_ref[i]->SetParameterLimits(CReferenceSpectrumFunction::SQUEEZE, (TFitData)m_window.reference[i].m_squeezeValue, (TFitData)m_window.reference[i].m_squeezeMaxValue, 1e7); break; default: m_ref[i]->SetDefaultParameter(CReferenceSpectrumFunction::SQUEEZE, (TFitData)1.0); @@ -177,45 +176,40 @@ int CEvaluationBase::CreateReferenceSpectra() return 0; } -void CEvaluationBase::RemoveOffset(double* spectrum, int sumChn, bool UV) +void CEvaluationBase::RemoveOffset(double* spectrum, int spectrumLength, IndexRange range) const { - // TODO: Read this from the number of optically covered pixels of the SpectrometerModel - int offsetFrom = (UV) ? 50 : 2; - int offsetTo = (UV) ? 200 : 20; - - // remove any remaining offset in the measured spectrum + // remove any remaining offset in the measured spectrum double avg = 0; - for (int i = offsetFrom; i < offsetTo; i++) + for (size_t i = range.from; i < range.to; i++) { avg += spectrum[i]; } - avg = avg / (double)(offsetTo - offsetFrom); + avg = avg / (double)range.Length(); - Sub(spectrum, sumChn, avg); + Sub(spectrum, spectrumLength, avg); return; } -void CEvaluationBase::RemoveOffset(std::vector& spectrum, size_t from, size_t to) +void CEvaluationBase::RemoveOffset(std::vector& spectrum, IndexRange range) const { - if (from > to || to > spectrum.size()) + if (range.from > range.to || range.to > spectrum.size()) { std::stringstream msg; - msg << "Cannot remove offset, invalid spectrum region [" << from << ", " << to << "] for spectrum of length " << spectrum.size(); + msg << "Cannot remove offset, invalid spectrum region [" << range.from << ", " << range.to << "] for spectrum of length " << spectrum.size(); throw std::invalid_argument(msg.str()); } - double avg = 0; - for (size_t i = from; i < to; i++) + double average = 0; + for (size_t i = range.from; i < range.to; i++) { - avg += spectrum[i]; + average += spectrum[i]; } - avg = avg / (double)(to - from); + average = average / (double)range.Length(); - Sub(spectrum.data(), static_cast(spectrum.size()), avg); + ::Add(spectrum, -average); return; - } void CEvaluationBase::PrepareSpectra(double* sky, double* meas, const CFitWindow& window) @@ -229,11 +223,20 @@ void CEvaluationBase::PrepareSpectra(double* sky, double* meas, const CFitWindow return PrepareSpectra_Poly(sky, meas, window); } -void CEvaluationBase::PrepareSpectra_HP_Div(double* skyArray, double* measArray, const CFitWindow& window) +void CEvaluationBase::PrepareSpectra(std::vector& sky, std::vector& meas, const CFitWindow& window) const { + if (window.fitType == FIT_TYPE::FIT_HP_DIV) + return PrepareSpectra_HP_Div(sky, meas, window); + if (window.fitType == FIT_TYPE::FIT_HP_SUB) + return PrepareSpectra_HP_Sub(sky, meas, window); + if (window.fitType == FIT_TYPE::FIT_POLY) + return PrepareSpectra_Poly(sky, meas, window); +} +void CEvaluationBase::PrepareSpectra_HP_Div(double* skyArray, double* measArray, const CFitWindow& window) +{ // 1. Remove any remaining offset - RemoveOffset(measArray, window.specLength, window.UV); + RemoveOffset(measArray, window.specLength, window.offsetRemovalRange); // 2. Divide the measured spectrum with the sky spectrum Div(measArray, skyArray, window.specLength, 0.0); @@ -248,11 +251,41 @@ void CEvaluationBase::PrepareSpectra_HP_Div(double* skyArray, double* measArray, // LowPassBinomial(measArray,window.specLength, 5); } +void CEvaluationBase::PrepareSpectra_HP_Div(std::vector& skyArray, std::vector& measArray, const CFitWindow& window) const +{ + // 1. Remove any remaining offset + RemoveOffset(measArray, window.offsetRemovalRange); + + // 2. Divide the measured spectrum with the sky spectrum + ::Div(measArray, skyArray); + + // 3. high pass filter + ::HighPassBinomial(measArray, 500); + + // 4. log(spec) + ::Log(measArray); + + // 5. low pass filter + // LowPassBinomial(measArray,window.specLength, 5); +} + +void CEvaluationBase::PrepareSpectra_HP_Sub(std::vector& /*skyArray*/, std::vector& measArray, const CFitWindow& window) const +{ + // 1. remove any remaining offset in the measured spectrum + RemoveOffset(measArray, window.offsetRemovalRange); + + // 2. high pass filter + ::HighPassBinomial(measArray, 500); + + // 3. log(spec) + ::Log(measArray); +} + void CEvaluationBase::PrepareSpectra_HP_Sub(double* /*skyArray*/, double* measArray, const CFitWindow& window) { // 1. remove any remaining offset in the measured spectrum - RemoveOffset(measArray, window.specLength, window.UV); + RemoveOffset(measArray, window.specLength, window.offsetRemovalRange); // 2. high pass filter HighPassBinomial(measArray, window.specLength, 500); @@ -264,7 +297,7 @@ void CEvaluationBase::PrepareSpectra_HP_Sub(double* /*skyArray*/, double* measAr void CEvaluationBase::PrepareSpectra_Poly(double* /*skyArray*/, double* measArray, const CFitWindow& window) { // 1. remove any remaining offset in the measured spectrum - RemoveOffset(measArray, window.specLength, window.UV); + RemoveOffset(measArray, window.specLength, window.offsetRemovalRange); // 2. log(spec) Log(measArray, window.specLength); @@ -276,6 +309,18 @@ void CEvaluationBase::PrepareSpectra_Poly(double* /*skyArray*/, double* measArra } } +void CEvaluationBase::PrepareSpectra_Poly(std::vector& /*skyArray*/, std::vector& measArray, const CFitWindow& window) const +{ + // 1. remove any remaining offset in the measured spectrum + RemoveOffset(measArray, window.offsetRemovalRange); + + // 2. log(spec) + ::Log(measArray); + + // 3. Multiply the spectrum with -1 to get the correct sign for everything + ::Mult(measArray, -1.0); +} + int CEvaluationBase::SetSkySpectrum(const CSpectrum& spec) { CCrossSectionData sky; @@ -302,7 +347,7 @@ int CEvaluationBase::SetSkySpectrum(const CCrossSectionData& spec, bool removeOf // Remove any remaining offset of the sky-spectrum if (removeOffset) { - RemoveOffset(m_sky.m_crossSection.data(), m_sky.GetSize(), m_window.UV); + RemoveOffset(m_sky.m_crossSection.data(), m_sky.GetSize(), m_window.offsetRemovalRange); } if (m_window.fitType == FIT_TYPE::FIT_HP_SUB) @@ -557,7 +602,7 @@ int CEvaluationBase::Evaluate(const double* measured, size_t measuredLength, int // --------- prepare the spectrum for evaluation ----------------- //---------------------------------------------------------------- - PrepareSpectra(m_sky.m_crossSection.data(), measArray.data(), m_window); + PrepareSpectra(m_sky.m_crossSection, measArray, m_window); m_measuredData = std::vector(begin(measArray), end(measArray)); //---------------------------------------------------------------- @@ -753,7 +798,7 @@ int CEvaluationBase::EvaluateShift(novac::LogContext context, const CSpectrum& m // --------- prepare the spectrum for evaluation ----------------- //---------------------------------------------------------------- - RemoveOffset(measArray.data(), m_window.specLength, m_window.UV); + RemoveOffset(measArray, m_window.offsetRemovalRange); if (m_window.fitType == FIT_TYPE::FIT_HP_DIV || m_window.fitType == FIT_TYPE::FIT_HP_SUB) { HighPassBinomial(measArray.data(), m_window.specLength, 500); @@ -834,7 +879,7 @@ int CEvaluationBase::EvaluateShift(novac::LogContext context, const CSpectrum& m cRefSum.AddReference(*solarSpec); // <-- at last add the reference to the summation object // Link the shifts of the 'normal' cross sections to the shift of the solar spectrum - for (size_t ii = 0; ii < m_window.nRef; ++ii) + for (size_t ii = 0; ii < m_window.reference.size(); ++ii) { // Link the shift and squeeze to the solar-reference m_ref[ii]->ReleaseParameter(CReferenceSpectrumFunction::SHIFT); @@ -902,7 +947,7 @@ int CEvaluationBase::EvaluateShift(novac::LogContext context, const CSpectrum& m shiftResult.squeezeError = (double)solarSpec->GetModelParameterError(CReferenceSpectrumFunction::SQUEEZE); // also verify that the setup did what we expected out of it... - for (size_t ii = 0; ii < m_window.nRef; ++ii) + for (size_t ii = 0; ii < m_window.reference.size(); ++ii) { assert(std::abs(shiftResult.shift - m_ref[ii]->GetModelParameter(CReferenceSpectrumFunction::SHIFT)) < 1e-3); assert(std::abs(shiftResult.squeeze - m_ref[ii]->GetModelParameter(CReferenceSpectrumFunction::SQUEEZE)) < 1e-3); @@ -937,9 +982,9 @@ int CEvaluationBase::EvaluateShift(novac::LogContext context, const CSpectrum& m std::string CEvaluationBase::GetReferenceName(size_t referenceIndex) const { - if (referenceIndex < m_window.nRef) + if (referenceIndex < m_window.reference.size()) { - return m_window.ref[referenceIndex].m_specieName; // user supplied reference + return m_window.reference[referenceIndex].m_specieName; // user supplied reference } else if (referenceIndex >= this->m_ref.size()) { diff --git a/src/Evaluation/FitWindow.cpp b/src/Evaluation/FitWindow.cpp index 55b5387..83049bb 100644 --- a/src/Evaluation/FitWindow.cpp +++ b/src/Evaluation/FitWindow.cpp @@ -11,7 +11,7 @@ CFitWindow::CFitWindow(const CFitWindow& other) : fitLow(other.fitLow), fitHigh(other.fitHigh), channel(other.channel), - nRef(other.nRef), + reference(other.reference), fraunhoferRef(other.fraunhoferRef), polyOrder(other.polyOrder), includeIntensitySpacePolyominal(other.includeIntensitySpacePolyominal), @@ -24,21 +24,15 @@ CFitWindow::CFitWindow(const CFitWindow& other) skyShift(other.skyShift), skySqueeze(other.skySqueeze), interlaceStep(other.interlaceStep), - UV(other.UV), + offsetRemovalRange(other.offsetRemovalRange), findOptimalShift(other.findOptimalShift), child(begin(other.child), end(other.child)) -{ - for (size_t i = 0; i < other.nRef; ++i) - { - this->ref[i] = other.ref[i]; - } -} +{} CFitWindow::CFitWindow(CFitWindow&& other) : fitLow(other.fitLow), fitHigh(other.fitHigh), channel(other.channel), - nRef(other.nRef), fraunhoferRef(other.fraunhoferRef), polyOrder(other.polyOrder), includeIntensitySpacePolyominal(other.includeIntensitySpacePolyominal), @@ -51,14 +45,11 @@ CFitWindow::CFitWindow(CFitWindow&& other) skyShift(other.skyShift), skySqueeze(other.skySqueeze), interlaceStep(other.interlaceStep), - UV(other.UV), + offsetRemovalRange(other.offsetRemovalRange), findOptimalShift(other.findOptimalShift), child(begin(other.child), end(other.child)) { - for (size_t i = 0; i < other.nRef; ++i) - { - this->ref[i] = std::move(other.ref[i]); - } + reference = std::move(other.reference); } CFitWindow& CFitWindow::operator=(const CFitWindow& other) @@ -72,18 +63,15 @@ CFitWindow& CFitWindow::operator=(const CFitWindow& other) this->skySqueeze = other.skySqueeze; this->interlaceStep = other.interlaceStep; this->name = other.name; - this->nRef = other.nRef; this->polyOrder = other.polyOrder; this->includeIntensitySpacePolyominal = other.includeIntensitySpacePolyominal; this->ringCalculation = other.ringCalculation; - this->UV = other.UV; + this->offsetRemovalRange = other.offsetRemovalRange; this->specLength = other.specLength; this->startChannel = other.startChannel; - for (size_t i = 0; i < other.nRef; ++i) - { - this->ref[i] = other.ref[i]; - } + this->reference = std::vector(begin(other.reference), end(other.reference)); + this->fraunhoferRef = other.fraunhoferRef; this->findOptimalShift = other.findOptimalShift; this->child = std::vector(begin(other.child), end(other.child)); @@ -101,18 +89,14 @@ CFitWindow& CFitWindow::operator=(CFitWindow&& other) this->skySqueeze = other.skySqueeze; this->interlaceStep = other.interlaceStep; this->name = std::move(other.name); - this->nRef = other.nRef; this->polyOrder = other.polyOrder; this->includeIntensitySpacePolyominal = other.includeIntensitySpacePolyominal; this->ringCalculation = other.ringCalculation; - this->UV = other.UV; + this->offsetRemovalRange = other.offsetRemovalRange; this->specLength = other.specLength; this->startChannel = other.startChannel; - for (size_t i = 0; i < other.nRef; ++i) - { - this->ref[i] = std::move(other.ref[i]); - } + this->reference = std::move(other.reference); this->fraunhoferRef = other.fraunhoferRef; this->findOptimalShift = other.findOptimalShift; this->child = std::move(other.child); @@ -131,16 +115,12 @@ void CFitWindow::Clear() shiftSky = true; interlaceStep = 1; name = "SO2"; - nRef = 0; polyOrder = 5; ringCalculation = RING_CALCULATION_OPTION::DO_NOT_CALCULATE_RING; includeIntensitySpacePolyominal = false; - UV = true; - for (int i = 0; i < MAX_N_REFERENCES; ++i) - { - ref[i].m_path = ""; - ref[i].m_specieName = ""; - } + offsetRemovalRange = IndexRange(50, 200); + reference.clear(); + reference.reserve(10); child.clear(); @@ -152,9 +132,9 @@ void CFitWindow::Clear() void ReadReferences(CFitWindow& window) { // For each reference in the fit-window, read it in and make sure that it exists... - for (size_t referenceIndex = 0; referenceIndex < window.nRef; ++referenceIndex) + for (CReferenceFile& ref : window.reference) { - window.ref[referenceIndex].ReadCrossSectionDataFromFile(); + ref.ReadCrossSectionDataFromFile(); } if (window.fraunhoferRef.m_path.size() > 4) @@ -177,11 +157,8 @@ void ScaleReferencesToMolecCm2(CFitWindow& window) ScaleReferencesToMolecCm2(c); } - for (size_t referenceIndex = 0; referenceIndex < window.nRef; ++referenceIndex) + for (CReferenceFile& thisReference : window.reference) { - // Local handle for more convenient syntax. - CReferenceFile& thisReference = window.ref[referenceIndex]; - if (thisReference.m_isFiltered) { // Convert from ppmm to moleculues / cm2 @@ -192,41 +169,44 @@ void ScaleReferencesToMolecCm2(CFitWindow& window) void AddAsReference(CFitWindow& window, const std::vector& referenceData, const std::string& name, int linkShiftToIdx) { - window.ref[window.nRef].m_data = std::make_unique(referenceData); - window.ref[window.nRef].m_specieName = name; - window.ref[window.nRef].m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; - window.ref[window.nRef].m_columnValue = 1.0; - window.ref[window.nRef].m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; - window.ref[window.nRef].m_squeezeValue = 1.0; + CReferenceFile newReference; + + newReference.m_data = std::make_unique(referenceData); + newReference.m_specieName = name; + newReference.m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; + newReference.m_columnValue = 1.0; + newReference.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; + newReference.m_squeezeValue = 1.0; if (linkShiftToIdx >= 0) { - window.ref[window.nRef].m_shiftOption = SHIFT_TYPE::SHIFT_LINK; - window.ref[window.nRef].m_shiftValue = linkShiftToIdx; + newReference.m_shiftOption = SHIFT_TYPE::SHIFT_LINK; + newReference.m_shiftValue = linkShiftToIdx; } else { - window.ref[window.nRef].m_shiftOption = SHIFT_TYPE::SHIFT_FIX; - window.ref[window.nRef].m_shiftValue = 0.0; + newReference.m_shiftOption = SHIFT_TYPE::SHIFT_FIX; + newReference.m_shiftValue = 0.0; } - window.nRef += 1; + window.reference.push_back(newReference); } size_t AddAsSky(CFitWindow& window, const std::vector& referenceData, SHIFT_TYPE shiftOption) { - const size_t indexOfSkySpectrum = window.nRef; - - window.ref[window.nRef].m_data = std::make_unique(referenceData); - window.ref[window.nRef].m_specieName = "sky"; - window.ref[window.nRef].m_columnOption = novac::SHIFT_TYPE::SHIFT_FIX; - window.ref[window.nRef].m_columnValue = -1.0; - window.ref[window.nRef].m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; - window.ref[window.nRef].m_squeezeValue = 1.0; - window.ref[window.nRef].m_shiftOption = shiftOption; - window.ref[window.nRef].m_shiftValue = 0.0; - window.nRef += 1; - - return indexOfSkySpectrum; + CReferenceFile newReference; + + newReference.m_data = std::make_unique(referenceData); + newReference.m_specieName = "sky"; + newReference.m_columnOption = novac::SHIFT_TYPE::SHIFT_FIX; + newReference.m_columnValue = -1.0; + newReference.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; + newReference.m_squeezeValue = 1.0; + newReference.m_shiftOption = shiftOption; + newReference.m_shiftValue = 0.0; + + window.reference.push_back(newReference); + + return window.reference.size(); } } diff --git a/src/Evaluation/RatioEvaluation.cpp b/src/Evaluation/RatioEvaluation.cpp index 5f1ecc1..206bd56 100644 --- a/src/Evaluation/RatioEvaluation.cpp +++ b/src/Evaluation/RatioEvaluation.cpp @@ -86,11 +86,11 @@ void AddRingSpectraAsReferences(CFitWindow& localSO2FitWindow, const std::vector } } -int FindReferenceIndex(const CFitWindow& window, const std::string& nameToFind) +static int FindReferenceIndex(const CFitWindow& window, const std::string& nameToFind) { - for (size_t ii = 0; ii < window.nRef; ++ii) + for (size_t ii = 0; ii < window.reference.size(); ++ii) { - if (EqualsIgnoringCase(window.ref[ii].m_specieName, nameToFind)) + if (EqualsIgnoringCase(window.reference[ii].m_specieName, nameToFind)) { return static_cast(ii); } @@ -99,7 +99,7 @@ int FindReferenceIndex(const CFitWindow& window, const std::string& nameToFind) return -1; } -std::vector PrepareRingLambda4Spectrum(const std::vector& ringSpectrum, const std::vector& wavelength) +static std::vector PrepareRingLambda4Spectrum(const std::vector& ringSpectrum, const std::vector& wavelength) { if (ringSpectrum.size() != wavelength.size()) { @@ -239,9 +239,9 @@ std::vector RatioEvaluation::Run(novac::LogContext context, IScanSpectrum { debugInfo.outOfPlumeSpectrum.m_wavelength = m_masterFitWindow.fraunhoferRef.m_data->m_waveLength; } - else if (m_masterFitWindow.ref[0].m_data->m_waveLength.size() == m_masterFitWindow.ref[0].m_data->m_crossSection.size()) + else if (m_masterFitWindow.reference[0].m_data->m_waveLength.size() == m_masterFitWindow.reference[0].m_data->m_crossSection.size()) { - debugInfo.outOfPlumeSpectrum.m_wavelength = m_masterFitWindow.ref[0].m_data->m_waveLength; + debugInfo.outOfPlumeSpectrum.m_wavelength = m_masterFitWindow.reference[0].m_data->m_waveLength; } else { @@ -285,8 +285,8 @@ std::vector RatioEvaluation::Run(novac::LogContext context, IScanSpectrum const int so2RefIdx = FindReferenceIndex(broFitWindow, so2DoasResult.referenceResult[0].name); if (so2RefIdx > 0) { - broFitWindow.ref[so2RefIdx].m_columnOption = SHIFT_TYPE::SHIFT_FIX; - broFitWindow.ref[so2RefIdx].m_columnValue = so2DoasResult.referenceResult[0].column; + broFitWindow.reference[so2RefIdx].m_columnOption = SHIFT_TYPE::SHIFT_FIX; + broFitWindow.reference[so2RefIdx].m_columnValue = so2DoasResult.referenceResult[0].column; } DoasResult broDoasResult = RunEvaluation(broFitWindow, filteredSpectra); diff --git a/src/Evaluation/ScanEvaluationBase.cpp b/src/Evaluation/ScanEvaluationBase.cpp index 0229260..d7dd6ca 100644 --- a/src/Evaluation/ScanEvaluationBase.cpp +++ b/src/Evaluation/ScanEvaluationBase.cpp @@ -338,12 +338,12 @@ CEvaluationBase* ScanEvaluationBase::FindOptimumShiftAndSqueezeFromFraunhoferRef CFitWindow improvedFitWindow = fitWindow; // The fit is good enough to use the values - for (size_t it = 0; it < improvedFitWindow.nRef; ++it) + for (size_t it = 0; it < improvedFitWindow.reference.size(); ++it) { - improvedFitWindow.ref[it].m_shiftOption = SHIFT_TYPE::SHIFT_FIX; - improvedFitWindow.ref[it].m_squeezeOption = SHIFT_TYPE::SHIFT_FIX; - improvedFitWindow.ref[it].m_shiftValue = shiftResult.shift; - improvedFitWindow.ref[it].m_squeezeValue = shiftResult.squeeze; + improvedFitWindow.reference[it].m_shiftOption = SHIFT_TYPE::SHIFT_FIX; + improvedFitWindow.reference[it].m_squeezeOption = SHIFT_TYPE::SHIFT_FIX; + improvedFitWindow.reference[it].m_shiftValue = shiftResult.shift; + improvedFitWindow.reference[it].m_squeezeValue = shiftResult.squeeze; } std::stringstream msg; diff --git a/src/File/FitWindowFileHandler.cpp b/src/File/FitWindowFileHandler.cpp index 755d8d6..b21f9e1 100644 --- a/src/File/FitWindowFileHandler.cpp +++ b/src/File/FitWindowFileHandler.cpp @@ -72,7 +72,8 @@ bool ParseReference(rapidxml::xml_node<>* referenceNode, novac::CReferenceFile& else if (TagNameEqualsIgnoringCase(childNode, "shiftOption")) { const int value = std::atoi(childNode->value()); - switch (value) { + switch (value) + { case 0: reference.m_shiftOption = novac::SHIFT_TYPE::SHIFT_FREE; break; case 1: reference.m_shiftOption = novac::SHIFT_TYPE::SHIFT_FIX; break; case 2: reference.m_shiftOption = novac::SHIFT_TYPE::SHIFT_LINK; break; @@ -86,7 +87,8 @@ bool ParseReference(rapidxml::xml_node<>* referenceNode, novac::CReferenceFile& else if (TagNameEqualsIgnoringCase(childNode, "squeezeOption")) { const int value = std::atoi(childNode->value()); - switch (value) { + switch (value) + { case 0: reference.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FREE; break; case 1: reference.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_FIX; break; case 2: reference.m_squeezeOption = novac::SHIFT_TYPE::SHIFT_LINK; break; @@ -100,7 +102,8 @@ bool ParseReference(rapidxml::xml_node<>* referenceNode, novac::CReferenceFile& else if (TagNameEqualsIgnoringCase(childNode, "columnOption")) { const int value = std::atoi(childNode->value()); - switch (value) { + switch (value) + { case 0: reference.m_columnOption = novac::SHIFT_TYPE::SHIFT_FREE; break; case 1: reference.m_columnOption = novac::SHIFT_TYPE::SHIFT_FIX; break; case 2: reference.m_columnOption = novac::SHIFT_TYPE::SHIFT_LINK; break; @@ -118,7 +121,7 @@ bool ParseReference(rapidxml::xml_node<>* referenceNode, novac::CReferenceFile& return true; // TODO: When to return false? } -bool ParseFitWindow(rapidxml::xml_node<>* fitWindowNode, novac::CFitWindow& window) +static bool ParseFitWindow(rapidxml::xml_node<>* fitWindowNode, novac::CFitWindow& window) { // Get the name of the window itself. auto attr = fitWindowNode->first_attribute(); @@ -185,12 +188,28 @@ bool ParseFitWindow(rapidxml::xml_node<>* fitWindowNode, novac::CFitWindow& wind } else if (TagNameEqualsIgnoringCase(childNode, "UV")) { - window.UV = std::atoi(childNode->value()); + const int UV = std::atoi(childNode->value()); + if (UV) + { + window.offsetRemovalRange = novac::CFitWindow::StandardUvOffsetRemovalRange(); + } + else + { + window.offsetRemovalRange = novac::CFitWindow::StandardUSB2000OffsetRemovalRange(); + } } else if (TagNameEqualsIgnoringCase(childNode, "shiftSky")) { window.shiftSky = std::atoi(childNode->value()); } + else if (TagNameEqualsIgnoringCase(childNode, "offsetFrom")) + { + window.offsetRemovalRange.from = static_cast(std::atoi(childNode->value())); + } + else if (TagNameEqualsIgnoringCase(childNode, "offsetTo")) + { + window.offsetRemovalRange.to = static_cast(std::atoi(childNode->value())); + } else if (TagNameEqualsIgnoringCase(childNode, "skyShift")) { window.skyShift = std::atof(childNode->value()); @@ -217,8 +236,7 @@ bool ParseFitWindow(rapidxml::xml_node<>* fitWindowNode, novac::CFitWindow& wind CReferenceFile reference; if (ParseReference(childNode, reference)) { - window.ref[window.nRef] = reference; - window.nRef += 1; + window.reference.push_back(reference); } } @@ -298,7 +316,8 @@ bool CFitWindowFileHandler::WriteFitWindow(const novac::CFitWindow& window, cons fprintf(f, "%s%d\n", indent.c_str(), window.specLength); fprintf(f, "%s%d\n", indent.c_str(), window.findOptimalShift); - fprintf(f, "%s%d\n", indent.c_str(), window.UV); + fprintf(f, "%s%zd\n", indent.c_str(), window.offsetRemovalRange.from); + fprintf(f, "%s%zd\n", indent.c_str(), window.offsetRemovalRange.to); fprintf(f, "%s%d\n", indent.c_str(), window.shiftSky); if (window.shiftSky == 2) { @@ -312,29 +331,29 @@ bool CFitWindowFileHandler::WriteFitWindow(const novac::CFitWindow& window, cons fprintf(f, "%s%s\n", indent.c_str(), window.fraunhoferRef.m_path.c_str()); } - fprintf(f, "%s%zd\n", indent.c_str(), window.nRef); + fprintf(f, "%s%zd\n", indent.c_str(), window.reference.size()); - for (size_t i = 0; i < window.nRef; ++i) + for (size_t i = 0; i < window.reference.size(); ++i) { - fprintf(f, "%s\n", indent.c_str(), window.ref[i].m_specieName.c_str()); - fprintf(f, "%s\t%s\n", indent.c_str(), window.ref[i].m_path.c_str()); + fprintf(f, "%s\n", indent.c_str(), window.reference[i].m_specieName.c_str()); + fprintf(f, "%s\t%s\n", indent.c_str(), window.reference[i].m_path.c_str()); - fprintf(f, "%s\t%d\n", indent.c_str(), (int)window.ref[i].m_shiftOption); - if (window.ref[i].m_shiftOption != novac::SHIFT_TYPE::SHIFT_FREE) + fprintf(f, "%s\t%d\n", indent.c_str(), (int)window.reference[i].m_shiftOption); + if (window.reference[i].m_shiftOption != novac::SHIFT_TYPE::SHIFT_FREE) { - fprintf(f, "%s\t%lf\n", indent.c_str(), window.ref[i].m_shiftValue); + fprintf(f, "%s\t%lf\n", indent.c_str(), window.reference[i].m_shiftValue); } - fprintf(f, "%s\t%d\n", indent.c_str(), (int)window.ref[i].m_squeezeOption); - if (window.ref[i].m_squeezeOption != novac::SHIFT_TYPE::SHIFT_FREE) + fprintf(f, "%s\t%d\n", indent.c_str(), (int)window.reference[i].m_squeezeOption); + if (window.reference[i].m_squeezeOption != novac::SHIFT_TYPE::SHIFT_FREE) { - fprintf(f, "%s\t%lf\n", indent.c_str(), window.ref[i].m_squeezeValue); + fprintf(f, "%s\t%lf\n", indent.c_str(), window.reference[i].m_squeezeValue); } - fprintf(f, "%s\t%d\n", indent.c_str(), (int)window.ref[i].m_columnOption); - if (window.ref[i].m_columnOption != novac::SHIFT_TYPE::SHIFT_FREE) + fprintf(f, "%s\t%d\n", indent.c_str(), (int)window.reference[i].m_columnOption); + if (window.reference[i].m_columnOption != novac::SHIFT_TYPE::SHIFT_FREE) { - fprintf(f, "%s\t%lf\n", indent.c_str(), window.ref[i].m_columnValue); + fprintf(f, "%s\t%lf\n", indent.c_str(), window.reference[i].m_columnValue); } fprintf(f, "%s\n", indent.c_str()); diff --git a/src/Math/SpectrumMath.cpp b/src/Math/SpectrumMath.cpp index 23ea5a4..fbe94bd 100644 --- a/src/Math/SpectrumMath.cpp +++ b/src/Math/SpectrumMath.cpp @@ -1,10 +1,21 @@ #include #include #include +#include + +#include namespace novac { +std::ostream& operator<<(std::ostream& os, const IndexRange& range) +{ + char buffer[128]; + sprintf(buffer, "[%zd to %zd]", range.from, range.to); + os << std::string(buffer); + return os; +} + int AverageSpectra(IScanSpectrumSource& scan, const std::vector& indices, CSpectrum& result, bool average) { novac::LogContext context; // TODO: Get from input diff --git a/src/VectorUtils.cpp b/src/VectorUtils.cpp index 1c655e9..a56c72e 100644 --- a/src/VectorUtils.cpp +++ b/src/VectorUtils.cpp @@ -256,6 +256,34 @@ void Mult(std::vector& values, double factor) } } +void Div(std::vector& first, const std::vector& second) +{ + if (first.size() != second.size()) + { + throw std::invalid_argument("Invalid argument to 'Div'. The two vectors must have equal length."); + } + + for (size_t i = 0; i < first.size(); i++) + { + if (second[i] != 0) + { + first[i] /= second[i]; + } + else + { + first[i] = 0; + } + } +} + +void Add(std::vector& values, double factor) +{ + for (double& v : values) + { + v += factor; + } +} + void Invert(std::vector& values) { for (double& v : values) @@ -301,6 +329,75 @@ void Exp(std::vector& values) } } +void Log(std::vector& values) +{ + for (double& v : values) + { + v = (v <= 0) ? 0.0 : std::log(v); + } +} + +void LowPassBinomial(std::vector& values, int nIterations) +{ + if (values.size() < 3) + { + return; // nothing to do. + } + + // The binomial filtering is done by, for each, calculating a new value + // as (0.25 * value to the left + 0.5 * this value + 0.25 * value to the right) + // In order to do this efficiently, we keep the three values (left, middle, right) in memory + // and calculate the outputs based on this. Notice that this algo will in each iteration overwrite + // the contents of the vector, hence it is important that we load the values of the vector into memory + // before overwriting them. + + for (int iteration = 0; iteration < nIterations; ++iteration) + { + double leftValue = values[0]; + double thisValue = values[0]; + double rightValue = values[1]; + + for (size_t ii = 0; ii < values.size() - 2; ++ii) + { + const double output = 0.5 * thisValue + 0.25 * (leftValue + rightValue); + + // update the three values before saving in the calculated value + leftValue = values[ii]; + thisValue = values[ii + 1]; + rightValue = values[ii + 2]; + + // save the calculated value + values[ii] = output; + } + + // The last two values are calculated separately (in order to not have to check the indices in the loop) + values[values.size() - 2] = 0.5 * thisValue + 0.25 * (leftValue + rightValue); + values[values.size() - 1] = 0.5 * rightValue + 0.25 * (thisValue + rightValue); + } + + return; +} + +void HighPassBinomial(std::vector& values, int nIterations) +{ + // create copy of original data + std::vector fBuffer(begin(values), end(values)); + + // create low pass filtered data + LowPassBinomial(fBuffer, nIterations); + + // remove low pass part from data + for (size_t i = 0; i < values.size(); i++) + { + if (fBuffer[i] != 0.0) + values[i] /= fBuffer[i]; + else + values[i] = 0; + } + + return; +} + double Average(std::vector::const_iterator start, std::vector::const_iterator end) { if (start > end)