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.

filter.c 3.2 KiB

1 year ago
1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /*
  2. * aptdec - A lightweight FOSS (NOAA) APT decoder
  3. * Copyright (C) 2019-2023 Xerbo (xerbo@protonmail.com)
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. #include "filter.h"
  19. #include <math.h>
  20. // SSE2 intrinsics
  21. #ifdef __x86_64__
  22. #include <emmintrin.h>
  23. #endif
  24. #include "algebra.h"
  25. // Blackman window
  26. // https://en.wikipedia.org/wiki/Window_function#Blackman_window
  27. static float blackman(float n, size_t ntaps) {
  28. n = (M_PIf * n) / (float)(ntaps - 1);
  29. return 0.42f - 0.5f*cosf(2 * n) + 0.08f*cosf(4 * n);
  30. }
  31. // Sinc low pass with blackman window.
  32. // https://tomroelandts.com/articles/how-to-create-a-simple-low-pass-filter
  33. void design_low_pass(float *taps, float samp_rate, float cutoff, size_t ntaps) {
  34. for (size_t i = 0; i < ntaps; i++) {
  35. int x = i - ntaps/2;
  36. taps[i] = sincf(2.0f * cutoff/samp_rate * (float)x);
  37. taps[i] *= blackman(i, ntaps);
  38. }
  39. // Achieve unity gain
  40. normalizef(taps, ntaps);
  41. }
  42. // Hilbert filter with blackman window.
  43. // https://www.recordingblogs.com/wiki/hilbert-transform
  44. void design_hilbert(float *taps, size_t ntaps) {
  45. for (size_t i = 0; i < ntaps; i++) {
  46. int x = i - ntaps/2;
  47. if (x % 2 == 0) {
  48. taps[i] = 0.0f;
  49. } else {
  50. taps[i] = 2.0f / (M_PIf * (float)x);
  51. taps[i] *= blackman(i, ntaps);
  52. }
  53. }
  54. // Achieve unity gain
  55. normalizef(taps, ntaps);
  56. }
  57. float convolve(const float *in, const float *taps, size_t len) {
  58. #ifdef __SSE2__
  59. __m128 sum = _mm_setzero_ps();
  60. size_t i;
  61. for (i = 0; i < len - 3; i += 4) {
  62. __m128 _taps = _mm_loadu_ps(&taps[i]);
  63. __m128 _in = _mm_loadu_ps(&in[i]);
  64. sum = _mm_add_ps(sum, _mm_mul_ps(_taps, _in));
  65. }
  66. float residual = 0.0f;
  67. for (; i < len; i++) {
  68. residual += in[i] * taps[i];
  69. }
  70. __attribute__((aligned(16))) float _sum[4];
  71. _mm_store_ps(_sum, sum);
  72. return _sum[0] + _sum[1] + _sum[2] + _sum[3] + residual;
  73. #else
  74. float sum = 0.0f;
  75. for (size_t i = 0; i < len; i++) {
  76. sum += in[i] * taps[i];
  77. }
  78. return sum;
  79. #endif
  80. }
  81. complexf_t hilbert_transform(const float *in, const float *taps, size_t len) {
  82. return complex_build(in[len / 2], convolve(in, taps, len));
  83. }
  84. float interpolating_convolve(const float *in, const float *taps, size_t len, float offset) {
  85. #ifdef _MSC_VER
  86. float *_taps = (float *)_alloca(len * sizeof(float));
  87. #else
  88. float _taps[len];
  89. #endif
  90. for (size_t i = 0; i < len; i++) {
  91. float next = (i == len-1) ? 0.0f : taps[i+1];
  92. _taps[i] = taps[i]*(1.0f-offset) + next*offset;
  93. }
  94. return convolve(in, _taps, len);
  95. }