sc/inc/scfuncs.hrc | 4 + sc/source/core/data/funcdesc.cxx | 2 sc/source/core/tool/interpr3.cxx | 79 +++++++++++++++++++++++++---------- sc/source/core/tool/parclass.cxx | 2 sc/source/filter/excel/xlformula.cxx | 2 sc/source/filter/oox/formulabase.cxx | 2 6 files changed, 64 insertions(+), 27 deletions(-)
New commits: commit 88b52d4ecc908a817211a5beb908df378cbf5fae Author: Dennis Francis <dennis.fran...@collabora.com> AuthorDate: Tue Mar 12 21:38:14 2019 +0530 Commit: Dennis Francis <dennis.fran...@collabora.com> CommitDate: Tue Mar 26 14:30:09 2019 +0100 tdf#74664: FOURIER: add 5th optional parameter MinimumMagnitude This parameter is used only if Polar=TRUE. All frequency components with magnitude less than MinimumMagnitude will be suppressed with a zero magnitude-phase entry. This is very useful when looking at the magnitude-phase spectrum of a signal because there is always some very tiny amount of rounding error when doing FFT algorithms and results in incorrect non-zero phase for non-existent frequencies. By providing a suitable value to this parameter, these non-existent frequency components can be filtered out. By default the value of this 5th parameter is 0.0, so *no* suppression is done by default. Change-Id: I422ad1bf91f42b320e98e58a19c99bf8528e4708 Reviewed-on: https://gerrit.libreoffice.org/69471 Tested-by: Jenkins Reviewed-by: Dennis Francis <dennis.fran...@collabora.com> diff --git a/sc/inc/scfuncs.hrc b/sc/inc/scfuncs.hrc index f0f06eee6427..e17eb9250c65 100644 --- a/sc/inc/scfuncs.hrc +++ b/sc/inc/scfuncs.hrc @@ -4129,7 +4129,9 @@ const char* SC_OPCODE_FOURIER_ARY[] = NC_("SC_OPCODE_FOURIER", "Inverse"), NC_("SC_OPCODE_FOURIER", "Flag to indicate whether an inverse DFT is to be computed (default FALSE)."), NC_("SC_OPCODE_FOURIER", "Polar"), - NC_("SC_OPCODE_FOURIER", "Flag to indicate whether to return the results in polar form (default FALSE).") + NC_("SC_OPCODE_FOURIER", "Flag to indicate whether to return the results in polar form (default FALSE)."), + NC_("SC_OPCODE_FOURIER", "MinimumMagnitude"), + NC_("SC_OPCODE_FOURIER", "In case of Polar=TRUE, the frequency components below this magnitude are clipped out (default 0.0).") }; #endif diff --git a/sc/source/core/data/funcdesc.cxx b/sc/source/core/data/funcdesc.cxx index a5cc499e44df..4679f3e029b4 100644 --- a/sc/source/core/data/funcdesc.cxx +++ b/sc/source/core/data/funcdesc.cxx @@ -788,7 +788,7 @@ ScFunctionList::ScFunctionList() { SC_OPCODE_FINDB, ENTRY(SC_OPCODE_FINDB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_FINDB, 3, { 0, 0, 1 } }, { SC_OPCODE_SEARCHB, ENTRY(SC_OPCODE_SEARCHB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_SEARCHB, 3, { 0, 0, 1 } }, { SC_OPCODE_REGEX, ENTRY(SC_OPCODE_REGEX_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_REGEX, 4, { 0, 0, 1, 1 } }, - { SC_OPCODE_FOURIER, ENTRY(SC_OPCODE_FOURIER_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_FOURIER, 4, { 0, 0, 1, 1 } } + { SC_OPCODE_FOURIER, ENTRY(SC_OPCODE_FOURIER_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_FOURIER, 5, { 0, 0, 1, 1, 1 } } }; ScFuncDesc* pDesc = nullptr; diff --git a/sc/source/core/tool/interpr3.cxx b/sc/source/core/tool/interpr3.cxx index bd21be239f92..517e2d258f70 100644 --- a/sc/source/core/tool/interpr3.cxx +++ b/sc/source/core/tool/interpr3.cxx @@ -4902,12 +4902,14 @@ class ScComplexFFT2 public: // rfArray.size() would always be even and a power of 2. (asserted in prepare()) // rfArray's first half contains the real parts and the later half contains the imaginary parts. - ScComplexFFT2(std::vector<double>& raArray, bool bInverse, bool bPolar, ScTwiddleFactors& rTF, bool bSubSampleTFs = false, bool bDisableNormalize = false) : + ScComplexFFT2(std::vector<double>& raArray, bool bInverse, bool bPolar, double fMinMag, + ScTwiddleFactors& rTF, bool bSubSampleTFs = false, bool bDisableNormalize = false) : mrArray(raArray), mfWReal(rTF.mfWReal), mfWImag(rTF.mfWImag), mnPoints(raArray.size()/2), mnStages(0), + mfMinMag(fMinMag), mbInverse(bInverse), mbPolar(bPolar), mbDisableNormalize(bDisableNormalize), @@ -4977,6 +4979,7 @@ private: std::vector<double>& mfWImag; SCSIZE mnPoints; SCSIZE mnStages; + double mfMinMag; bool mbInverse:1; bool mbPolar:1; bool mbDisableNormalize:1; @@ -5023,7 +5026,7 @@ static void lcl_normalize(std::vector<double>& rCmplxArray, bool bScaleOnlyReal) } } -static void lcl_convertToPolar(std::vector<double>& rCmplxArray) +static void lcl_convertToPolar(std::vector<double>& rCmplxArray, double fMinMag) { const SCSIZE nPoints = rCmplxArray.size()/2; double fMag, fPhase, fR, fI; @@ -5031,8 +5034,16 @@ static void lcl_convertToPolar(std::vector<double>& rCmplxArray) { fR = rCmplxArray[nIdx]; fI = rCmplxArray[nPoints+nIdx]; - fPhase = atan2(fI, fR); fMag = sqrt(fR*fR + fI*fI); + if (fMag < fMinMag) + { + fMag = 0.0; + fPhase = 0.0; + } + else + { + fPhase = atan2(fI, fR); + } rCmplxArray[nIdx] = fMag; rCmplxArray[nPoints+nIdx] = fPhase; @@ -5066,7 +5077,7 @@ void ScComplexFFT2::Compute() } if (mbPolar) - lcl_convertToPolar(mrArray); + lcl_convertToPolar(mrArray, mfMinMag); // Normalize after converting to polar, so we have a chance to // save O(mnPoints) flops. @@ -5080,9 +5091,11 @@ class ScComplexBluesteinFFT { public: - ScComplexBluesteinFFT(std::vector<double>& rArray, bool bReal, bool bInverse, bool bPolar, bool bDisableNormalize = false) : + ScComplexBluesteinFFT(std::vector<double>& rArray, bool bReal, bool bInverse, + bool bPolar, double fMinMag, bool bDisableNormalize = false) : mrArray(rArray), mnPoints(rArray.size()/2), // rArray should have space for imaginary parts even if real input. + mfMinMag(fMinMag), mbReal(bReal), mbInverse(bInverse), mbPolar(bPolar), @@ -5094,6 +5107,7 @@ public: private: std::vector<double>& mrArray; const SCSIZE mnPoints; + double mfMinMag; bool mbReal:1; bool mbInverse:1; bool mbPolar:1; @@ -5144,10 +5158,12 @@ void ScComplexBluesteinFFT::Compute() aTF.Compute(); // Do complex-FFT2 of both A and B signal. - ScComplexFFT2 aFFT2A(aASignal, false /*not inverse*/, false /*no polar*/, aTF, false /*no subsample*/, true /* disable normalize */); + ScComplexFFT2 aFFT2A(aASignal, false /*not inverse*/, false /*no polar*/, 0.0 /* no clipping */, + aTF, false /*no subsample*/, true /* disable normalize */); aFFT2A.Compute(); - ScComplexFFT2 aFFT2B(aBSignal, false /*not inverse*/, false /*no polar*/, aTF, false /*no subsample*/, true /* disable normalize */); + ScComplexFFT2 aFFT2B(aBSignal, false /*not inverse*/, false /*no polar*/, 0.0 /* no clipping */, + aTF, false /*no subsample*/, true /* disable normalize */); aFFT2B.Compute(); double fAR, fAI, fBR, fBI; @@ -5165,7 +5181,7 @@ void ScComplexBluesteinFFT::Compute() // Do complex-inverse-FFT2 of aASignal. aTF.Conjugate(); - ScComplexFFT2 aFFT2AI(aASignal, true /*inverse*/, false /*no polar*/, aTF); // Need normalization here. + ScComplexFFT2 aFFT2AI(aASignal, true /*inverse*/, false /*no polar*/, 0.0 /* no clipping */, aTF); // Need normalization here. aFFT2AI.Compute(); } @@ -5180,7 +5196,7 @@ void ScComplexBluesteinFFT::Compute() // Normalize/Polar operations if (mbPolar) - lcl_convertToPolar(mrArray); + lcl_convertToPolar(mrArray, mfMinMag); // Normalize after converting to polar, so we have a chance to // save O(mnPoints) flops. @@ -5195,9 +5211,11 @@ class ScRealFFT { public: - ScRealFFT(std::vector<double>& rInArray, std::vector<double>& rOutArray, bool bInverse, bool bPolar) : + ScRealFFT(std::vector<double>& rInArray, std::vector<double>& rOutArray, bool bInverse, + bool bPolar, double fMinMag) : mrInArray(rInArray), mrOutArray(rOutArray), + mfMinMag(fMinMag), mbInverse(bInverse), mbPolar(bPolar) {} @@ -5207,6 +5225,7 @@ public: private: std::vector<double>& mrInArray; std::vector<double>& mrOutArray; + double mfMinMag; bool mbInverse:1; bool mbPolar:1; }; @@ -5244,14 +5263,14 @@ void ScRealFFT::Compute() if (nNextPow2 == nN) { - ScComplexFFT2 aFFT2(aWorkArray, mbInverse, false /*disable polar*/, + ScComplexFFT2 aFFT2(aWorkArray, mbInverse, false /*disable polar*/, 0.0 /* no clipping */, aTFs, true /*subsample tf*/, true /*disable normalize*/); aFFT2.Compute(); } else { ScComplexBluesteinFFT aFFT(aWorkArray, false /*complex input*/, mbInverse, false /*disable polar*/, - true /*disable normalize*/); + 0.0 /* no clipping */, true /*disable normalize*/); aFFT.Compute(); } @@ -5300,7 +5319,7 @@ void ScRealFFT::Compute() // Normalize/Polar operations if (mbPolar) - lcl_convertToPolar(mrOutArray); + lcl_convertToPolar(mrOutArray, mfMinMag); // Normalize after converting to polar, so we have a chance to // save O(mnPoints) flops. @@ -5315,8 +5334,9 @@ class ScFFT { public: - ScFFT(ScMatrixRef& pMat, bool bReal, bool bInverse, bool bPolar) : + ScFFT(ScMatrixRef& pMat, bool bReal, bool bInverse, bool bPolar, double fMinMag) : mpInputMat(pMat), + mfMinMag(fMinMag), mbReal(bReal), mbInverse(bInverse), mbPolar(bPolar) @@ -5326,6 +5346,7 @@ public: private: ScMatrixRef& mpInputMat; + double mfMinMag; bool mbReal:1; bool mbInverse:1; bool mbPolar:1; @@ -5345,7 +5366,7 @@ ScMatrixRef ScFFT::Compute(std::function<ScMatrixGenerator>& rMatGenFunc) if (mbReal && (nPoints % 2) == 0) { std::vector<double> aOutArray(nPoints*2); - ScRealFFT aFFT(aArray, aOutArray, mbInverse, mbPolar); + ScRealFFT aFFT(aArray, aOutArray, mbInverse, mbPolar, mfMinMag); aFFT.Compute(); return rMatGenFunc(2, nPoints, aOutArray); } @@ -5356,14 +5377,14 @@ ScMatrixRef ScFFT::Compute(std::function<ScMatrixGenerator>& rMatGenFunc) { ScTwiddleFactors aTF(nPoints, mbInverse); aTF.Compute(); - ScComplexFFT2 aFFT2(aArray, mbInverse, mbPolar, aTF); + ScComplexFFT2 aFFT2(aArray, mbInverse, mbPolar, mfMinMag, aTF); aFFT2.Compute(); return rMatGenFunc(2, nPoints, aArray); } if (mbReal) aArray.resize(nPoints*2, 0.0); - ScComplexBluesteinFFT aFFT(aArray, mbReal, mbInverse, mbPolar); + ScComplexBluesteinFFT aFFT(aArray, mbReal, mbInverse, mbPolar, mfMinMag); aFFT.Compute(); return rMatGenFunc(2, nPoints, aArray); } @@ -5371,16 +5392,30 @@ ScMatrixRef ScFFT::Compute(std::function<ScMatrixGenerator>& rMatGenFunc) void ScInterpreter::ScFourier() { sal_uInt8 nParamCount = GetByte(); - if ( !MustHaveParamCount( nParamCount, 2, 4 ) ) + if ( !MustHaveParamCount( nParamCount, 2, 5 ) ) return; bool bInverse = false; bool bPolar = false; + double fMinMag = 0.0; - if (nParamCount == 4) - bPolar = GetBool(); + if (nParamCount == 5) + { + if (IsMissing()) + Pop(); + else + fMinMag = GetDouble(); + } + + if (nParamCount >= 4) + { + if (IsMissing()) + Pop(); + else + bPolar = GetBool(); + } - if (nParamCount > 2) + if (nParamCount >= 3) { if (IsMissing()) Pop(); @@ -5425,7 +5460,7 @@ void ScInterpreter::ScFourier() bRealInput = (nC == 1); } - ScFFT aFFT(pInputMat, bRealInput, bInverse, bPolar); + ScFFT aFFT(pInputMat, bRealInput, bInverse, bPolar, fMinMag); std::function<ScMatrixGenerator> aFunc = [this](SCSIZE nCol, SCSIZE nRow, std::vector<double>& rVec) -> ScMatrixRef { return this->GetNewMat(nCol, nRow, rVec); diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx index b8428191842e..18c4ad5b84f7 100644 --- a/sc/source/core/tool/parclass.cxx +++ b/sc/source/core/tool/parclass.cxx @@ -146,7 +146,7 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] = { ocForecast_ETS_STA, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value }, 0, Value }}, { ocForecast_ETS_STM, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value }, 0, Value }}, { ocFormula, {{ Reference }, 0, Value }}, - { ocFourier, {{ ForceArray, Value, Value, Value }, 0, Value }}, + { ocFourier, {{ ForceArray, Value, Value, Value, Value }, 0, Value }}, { ocFrequency, {{ ReferenceOrForceArray, ReferenceOrForceArray }, 0, ForceArrayReturn }}, { ocGCD, {{ Reference }, 1, Value }}, { ocGeoMean, {{ Reference }, 1, Value }}, diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx index 8cdf9806896f..99fb54a530d0 100644 --- a/sc/source/filter/excel/xlformula.cxx +++ b/sc/source/filter/excel/xlformula.cxx @@ -638,7 +638,7 @@ static const XclFunctionInfo saFuncTable_OOoLO[] = EXC_FUNCENTRY_OOO( ocForecast_ETS_STM, 3, 6, 0, "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT" ), EXC_FUNCENTRY_OOO( ocRoundSig, 2, 2, 0, "ORG.LIBREOFFICE.ROUNDSIG" ), EXC_FUNCENTRY_OOO( ocRegex, 2, 4, 0, "ORG.LIBREOFFICE.REGEX" ), - EXC_FUNCENTRY_OOO( ocFourier, 2, 4, 0, "ORG.LIBREOFFICE.FOURIER" ) + EXC_FUNCENTRY_OOO( ocFourier, 2, 5, 0, "ORG.LIBREOFFICE.FOURIER" ) }; #undef EXC_FUNCENTRY_OOO_IBR diff --git a/sc/source/filter/oox/formulabase.cxx b/sc/source/filter/oox/formulabase.cxx index e4cefc504b06..1a7822e7942f 100644 --- a/sc/source/filter/oox/formulabase.cxx +++ b/sc/source/filter/oox/formulabase.cxx @@ -911,7 +911,7 @@ static const FunctionData saFuncTableOOoLO[] = { "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT", "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW }, { "ORG.LIBREOFFICE.ROUNDSIG", "ORG.LIBREOFFICE.ROUNDSIG", NOID, NOID, 2, 2, V, { RX }, FuncFlags::MACROCALL_NEW }, { "ORG.LIBREOFFICE.REGEX", "ORG.LIBREOFFICE.REGEX", NOID, NOID, 2, 4, V, { RX }, FuncFlags::MACROCALL_NEW }, - { "ORG.LIBREOFFICE.FOURIER", "ORG.LIBREOFFICE.FOURIER", NOID, NOID, 2, 4, A, { RX }, FuncFlags::MACROCALL_NEW } + { "ORG.LIBREOFFICE.FOURIER", "ORG.LIBREOFFICE.FOURIER", NOID, NOID, 2, 5, A, { RX }, FuncFlags::MACROCALL_NEW } }; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits