307 lines
7.4 KiB
C
307 lines
7.4 KiB
C
|
//$ 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-2021 Aleksey Vaneev
|
||
|
* See the "LICENSE" 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, R8B_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, R8B_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, R8B_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, R8B_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.
|
||
|
|
||
|
int k;
|
||
|
|
||
|
for( 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, R8B_PI * c,
|
||
|
re, im );
|
||
|
|
||
|
const double curg = re * re + im * im;
|
||
|
|
||
|
if( curgl > curgr )
|
||
|
{
|
||
|
r = c;
|
||
|
curgr = curg;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
l = c;
|
||
|
curgl = curg;
|
||
|
}
|
||
|
|
||
|
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, R8B_PI * c, re, im );
|
||
|
const double curg = re * re + im * im;
|
||
|
|
||
|
if( curg > maxg )
|
||
|
{
|
||
|
l = c;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
r = c;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace r8b
|
||
|
|
||
|
#endif // R8BUTIL_INCLUDED
|