r8butil.h 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. //$ nobt
  2. //$ nocpp
  3. /**
  4. * @file r8butil.h
  5. *
  6. * @brief The inclusion file with several utility functions.
  7. *
  8. * This file includes several utility functions used by various utility
  9. * programs like "calcErrorTable.cpp".
  10. *
  11. * r8brain-free-src Copyright (c) 2013-2021 Aleksey Vaneev
  12. * See the "LICENSE" file for license.
  13. */
  14. #ifndef R8BUTIL_INCLUDED
  15. #define R8BUTIL_INCLUDED
  16. #include "r8bbase.h"
  17. namespace r8b {
  18. /**
  19. * @param re Real part of the frequency response.
  20. * @param im Imaginary part of the frequency response.
  21. * @return A magnitude response value converted from the linear scale to the
  22. * logarithmic scale.
  23. */
  24. inline double convertResponseToLog( const double re, const double im )
  25. {
  26. return( 4.34294481903251828 * log( re * re + im * im + 1e-100 ));
  27. }
  28. /**
  29. * An utility function that performs frequency response scanning step update
  30. * based on the current magnitude response's slope.
  31. *
  32. * @param[in,out] step The current scanning step. Will be updated on
  33. * function's return. Must be a positive value.
  34. * @param curg Squared magnitude response at the current frequency point.
  35. * @param[in,out] prevg_log Previous magnitude response, log scale. Will be
  36. * updated on function's return.
  37. * @param prec Precision multiplier, affects the size of the step.
  38. * @param maxstep The maximal allowed step.
  39. * @param minstep The minimal allowed step.
  40. */
  41. inline void updateScanStep( double& step, const double curg,
  42. double& prevg_log, const double prec, const double maxstep,
  43. const double minstep = 1e-11 )
  44. {
  45. double curg_log = 4.34294481903251828 * log( curg + 1e-100 );
  46. curg_log += ( prevg_log - curg_log ) * 0.7;
  47. const double slope = fabs( curg_log - prevg_log );
  48. prevg_log = curg_log;
  49. if( slope > 0.0 )
  50. {
  51. step /= prec * slope;
  52. step = max( min( step, maxstep ), minstep );
  53. }
  54. }
  55. /**
  56. * Function locates normalized frequency at which the minimum filter gain
  57. * is reached. The scanning is performed from lower (left) to higher
  58. * (right) frequencies, the whole range is scanned.
  59. *
  60. * Function expects that the magnitude response is always reducing from lower
  61. * to high frequencies, starting at "minth".
  62. *
  63. * @param flt Filter response.
  64. * @param fltlen Filter response's length in samples (taps).
  65. * @param[out] ming The current minimal gain (squared). On function's return
  66. * will contain the minimal gain value found (squared).
  67. * @param[out] minth The normalized frequency where the minimal gain is
  68. * currently at. On function's return will point to the normalized frequency
  69. * where the new minimum was found.
  70. * @param thend The ending frequency, inclusive.
  71. */
  72. inline void findFIRFilterResponseMinLtoR( const double* const flt,
  73. const int fltlen, double& ming, double& minth, const double thend )
  74. {
  75. const double maxstep = minth * 2e-3;
  76. double curth = minth;
  77. double re;
  78. double im;
  79. calcFIRFilterResponse( flt, fltlen, R8B_PI * curth, re, im );
  80. double prevg_log = convertResponseToLog( re, im );
  81. double step = 1e-11;
  82. while( true )
  83. {
  84. curth += step;
  85. if( curth > thend )
  86. {
  87. break;
  88. }
  89. calcFIRFilterResponse( flt, fltlen, R8B_PI * curth, re, im );
  90. const double curg = re * re + im * im;
  91. if( curg > ming )
  92. {
  93. ming = curg;
  94. minth = curth;
  95. break;
  96. }
  97. ming = curg;
  98. minth = curth;
  99. updateScanStep( step, curg, prevg_log, 0.31, maxstep );
  100. }
  101. }
  102. /**
  103. * Function locates normalized frequency at which the maximal filter gain
  104. * is reached. The scanning is performed from lower (left) to higher
  105. * (right) frequencies, the whole range is scanned.
  106. *
  107. * Note: this function may "stall" in very rare cases if the magnitude
  108. * response happens to be "saw-tooth" like, requiring a very small stepping to
  109. * be used. If this happens, it may take dozens of seconds to complete.
  110. *
  111. * @param flt Filter response.
  112. * @param fltlen Filter response's length in samples (taps).
  113. * @param[out] maxg The current maximal gain (squared). On function's return
  114. * will contain the maximal gain value (squared).
  115. * @param[out] maxth The normalized frequency where the maximal gain is
  116. * currently at. On function's return will point to the normalized frequency
  117. * where the maximum was reached.
  118. * @param thend The ending frequency, inclusive.
  119. */
  120. inline void findFIRFilterResponseMaxLtoR( const double* const flt,
  121. const int fltlen, double& maxg, double& maxth, const double thend )
  122. {
  123. const double maxstep = maxth * 1e-4;
  124. double premaxth = maxth;
  125. double premaxg = maxg;
  126. double postmaxth = maxth;
  127. double postmaxg = maxg;
  128. double prevth = maxth;
  129. double prevg = maxg;
  130. double curth = maxth;
  131. double re;
  132. double im;
  133. calcFIRFilterResponse( flt, fltlen, R8B_PI * curth, re, im );
  134. double prevg_log = convertResponseToLog( re, im );
  135. double step = 1e-11;
  136. bool WasPeak = false;
  137. int AfterPeakCount = 0;
  138. while( true )
  139. {
  140. curth += step;
  141. if( curth > thend )
  142. {
  143. break;
  144. }
  145. calcFIRFilterResponse( flt, fltlen, R8B_PI * curth, re, im );
  146. const double curg = re * re + im * im;
  147. if( curg > maxg )
  148. {
  149. premaxth = prevth;
  150. premaxg = prevg;
  151. maxg = curg;
  152. maxth = curth;
  153. WasPeak = true;
  154. AfterPeakCount = 0;
  155. }
  156. else
  157. if( WasPeak )
  158. {
  159. if( AfterPeakCount == 0 )
  160. {
  161. postmaxth = curth;
  162. postmaxg = curg;
  163. }
  164. if( AfterPeakCount == 5 )
  165. {
  166. // Perform 2 approximate binary searches.
  167. int k;
  168. for( k = 0; k < 2; k++ )
  169. {
  170. double l = ( k == 0 ? premaxth : maxth );
  171. double curgl = ( k == 0 ? premaxg : maxg );
  172. double r = ( k == 0 ? maxth : postmaxth );
  173. double curgr = ( k == 0 ? maxg : postmaxg );
  174. while( true )
  175. {
  176. const double c = ( l + r ) * 0.5;
  177. calcFIRFilterResponse( flt, fltlen, R8B_PI * c,
  178. re, im );
  179. const double curg = re * re + im * im;
  180. if( curgl > curgr )
  181. {
  182. r = c;
  183. curgr = curg;
  184. }
  185. else
  186. {
  187. l = c;
  188. curgl = curg;
  189. }
  190. if( r - l < 1e-11 )
  191. {
  192. if( curgl > curgr )
  193. {
  194. maxth = l;
  195. maxg = curgl;
  196. }
  197. else
  198. {
  199. maxth = r;
  200. maxg = curgr;
  201. }
  202. break;
  203. }
  204. }
  205. }
  206. break;
  207. }
  208. AfterPeakCount++;
  209. }
  210. prevth = curth;
  211. prevg = curg;
  212. updateScanStep( step, curg, prevg_log, 1.0, maxstep );
  213. }
  214. }
  215. /**
  216. * Function locates normalized frequency at which the specified maximum
  217. * filter gain is reached. The scanning is performed from higher (right)
  218. * to lower (left) frequencies, scanning stops when the required gain
  219. * value was crossed. Function uses an extremely efficient binary search and
  220. * thus expects that the magnitude response has the "main lobe" form produced
  221. * by windowing, with a minimal pass-band ripple.
  222. *
  223. * @param flt Filter response.
  224. * @param fltlen Filter response's length in samples (taps).
  225. * @param maxg Maximal gain (squared).
  226. * @param[out] th The current normalized frequency. On function's return will
  227. * point to the normalized frequency where "maxg" is reached.
  228. * @param thend The leftmost frequency to scan, inclusive.
  229. */
  230. inline void findFIRFilterResponseLevelRtoL( const double* const flt,
  231. const int fltlen, const double maxg, double& th, const double thend )
  232. {
  233. // Perform exact binary search.
  234. double l = thend;
  235. double r = th;
  236. while( true )
  237. {
  238. const double c = ( l + r ) * 0.5;
  239. if( r - l < 1e-14 )
  240. {
  241. th = c;
  242. break;
  243. }
  244. double re;
  245. double im;
  246. calcFIRFilterResponse( flt, fltlen, R8B_PI * c, re, im );
  247. const double curg = re * re + im * im;
  248. if( curg > maxg )
  249. {
  250. l = c;
  251. }
  252. else
  253. {
  254. r = c;
  255. }
  256. }
  257. }
  258. } // namespace r8b
  259. #endif // R8BUTIL_INCLUDED