You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

r8butil.h 7.6KB

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