//$ nobt //$ nocpp /** * @file r8butil.h * * @brief The inclusion file with several utility functions. * * This file includes several utility functions used by various utility * programs like "calcErrorTable.cpp". * * r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev * See the "License.txt" file for license. */ #ifndef R8BUTIL_INCLUDED #define R8BUTIL_INCLUDED #include "r8bbase.h" namespace r8b { /** * @param re Real part of the frequency response. * @param im Imaginary part of the frequency response. * @return A magnitude response value converted from the linear scale to the * logarithmic scale. */ inline double convertResponseToLog(const double re, const double im) { return (4.34294481903251828 * log(re * re + im * im + 1e-100)); } /** * An utility function that performs frequency response scanning step update * based on the current magnitude response's slope. * * @param[in,out] step The current scanning step. Will be updated on * function's return. Must be a positive value. * @param curg Squared magnitude response at the current frequency point. * @param[in,out] prevg_log Previous magnitude response, log scale. Will be * updated on function's return. * @param prec Precision multiplier, affects the size of the step. * @param maxstep The maximal allowed step. * @param minstep The minimal allowed step. */ inline void updateScanStep(double& step, const double curg, double& prevg_log, const double prec, const double maxstep, const double minstep = 1e-11) { double curg_log = 4.34294481903251828 * log(curg + 1e-100); curg_log += (prevg_log - curg_log) * 0.7; const double slope = fabs(curg_log - prevg_log); prevg_log = curg_log; if (slope > 0.0) { step /= prec * slope; step = max(min(step, maxstep), minstep); } } /** * Function locates normalized frequency at which the minimum filter gain * is reached. The scanning is performed from lower (left) to higher * (right) frequencies, the whole range is scanned. * * Function expects that the magnitude response is always reducing from lower * to high frequencies, starting at "minth". * * @param flt Filter response. * @param fltlen Filter response's length in samples (taps). * @param[out] ming The current minimal gain (squared). On function's return * will contain the minimal gain value found (squared). * @param[out] minth The normalized frequency where the minimal gain is * currently at. On function's return will point to the normalized frequency * where the new minimum was found. * @param thend The ending frequency, inclusive. */ inline void findFIRFilterResponseMinLtoR(const double* const flt, const int fltlen, double& ming, double& minth, const double thend) { const double maxstep = minth * 2e-3; double curth = minth; double re; double im; calcFIRFilterResponse(flt, fltlen, M_PI * curth, re, im); double prevg_log = convertResponseToLog(re, im); double step = 1e-11; while (true) { curth += step; if (curth > thend) { break; } calcFIRFilterResponse(flt, fltlen, M_PI * curth, re, im); const double curg = re * re + im * im; if (curg > ming) { ming = curg; minth = curth; break; } ming = curg; minth = curth; updateScanStep(step, curg, prevg_log, 0.31, maxstep); } } /** * Function locates normalized frequency at which the maximal filter gain * is reached. The scanning is performed from lower (left) to higher * (right) frequencies, the whole range is scanned. * * Note: this function may "stall" in very rare cases if the magnitude * response happens to be "saw-tooth" like, requiring a very small stepping to * be used. If this happens, it may take dozens of seconds to complete. * * @param flt Filter response. * @param fltlen Filter response's length in samples (taps). * @param[out] maxg The current maximal gain (squared). On function's return * will contain the maximal gain value (squared). * @param[out] maxth The normalized frequency where the maximal gain is * currently at. On function's return will point to the normalized frequency * where the maximum was reached. * @param thend The ending frequency, inclusive. */ inline void findFIRFilterResponseMaxLtoR(const double* const flt, const int fltlen, double& maxg, double& maxth, const double thend) { const double maxstep = maxth * 1e-4; double premaxth = maxth; double premaxg = maxg; double postmaxth = maxth; double postmaxg = maxg; double prevth = maxth; double prevg = maxg; double curth = maxth; double re; double im; calcFIRFilterResponse(flt, fltlen, M_PI * curth, re, im); double prevg_log = convertResponseToLog(re, im); double step = 1e-11; bool WasPeak = false; int AfterPeakCount = 0; while (true) { curth += step; if (curth > thend) { break; } calcFIRFilterResponse(flt, fltlen, M_PI * curth, re, im); const double curg = re * re + im * im; if (curg > maxg) { premaxth = prevth; premaxg = prevg; maxg = curg; maxth = curth; WasPeak = true; AfterPeakCount = 0; } else if (WasPeak) { if (AfterPeakCount == 0) { postmaxth = curth; postmaxg = curg; } if (AfterPeakCount == 5) { // Perform 2 approximate binary searches. for (int k = 0; k < 2; ++k) { double l = (k == 0 ? premaxth : maxth); double curgl = (k == 0 ? premaxg : maxg); double r = (k == 0 ? maxth : postmaxth); double curgr = (k == 0 ? maxg : postmaxg); while (true) { const double c = (l + r) * 0.5; calcFIRFilterResponse(flt, fltlen, M_PI * c, re, im); const double curgTmp = re * re + im * im; if (curgl > curgr) { r = c; curgr = curgTmp; } else { l = c; curgl = curgTmp; } if (r - l < 1e-11) { if (curgl > curgr) { maxth = l; maxg = curgl; } else { maxth = r; maxg = curgr; } break; } } } break; } AfterPeakCount++; } prevth = curth; prevg = curg; updateScanStep(step, curg, prevg_log, 1.0, maxstep); } } /** * Function locates normalized frequency at which the specified maximum * filter gain is reached. The scanning is performed from higher (right) * to lower (left) frequencies, scanning stops when the required gain * value was crossed. Function uses an extremely efficient binary search and * thus expects that the magnitude response has the "main lobe" form produced * by windowing, with a minimal pass-band ripple. * * @param flt Filter response. * @param fltlen Filter response's length in samples (taps). * @param maxg Maximal gain (squared). * @param[out] th The current normalized frequency. On function's return will * point to the normalized frequency where "maxg" is reached. * @param thend The leftmost frequency to scan, inclusive. */ inline void findFIRFilterResponseLevelRtoL(const double* const flt, const int fltlen, const double maxg, double& th, const double thend) { // Perform exact binary search. double l = thend; double r = th; while (true) { const double c = (l + r) * 0.5; if (r - l < 1e-14) { th = c; break; } double re; double im; calcFIRFilterResponse(flt, fltlen, M_PI * c, re, im); const double curg = re * re + im * im; if (curg > maxg) { l = c; } else { r = c; } } } } // namespace r8b #endif // R8BUTIL_INCLUDED