A low cost DIY sound pressure level sensor for enabling environmental noise awareness. https://lukasschwarz.org/noise-sensor
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.

arduinoFFT.cpp 9.4 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /*
  2. FFT libray
  3. Copyright (C) 2010 Didier Longueville
  4. Copyright (C) 2014 Enrique Condes
  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 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <arduinoFFT.h>
  17. arduinoFFT::arduinoFFT(double *vReal, double *vImag, uint16_t samples, double samplingFrequency)
  18. {// Constructor
  19. this->_vReal = vReal;
  20. this->_vImag = vImag;
  21. this->_samples = samples;
  22. this->_samplingFrequency = samplingFrequency;
  23. this->_power = Exponent(samples);
  24. }
  25. arduinoFFT::~arduinoFFT(void)
  26. {
  27. // Destructor
  28. }
  29. uint8_t arduinoFFT::Revision(void)
  30. {
  31. return(FFT_LIB_REV);
  32. }
  33. void arduinoFFT::Compute(uint8_t dir)
  34. {// Computes in-place complex-to-complex FFT /
  35. // Reverse bits /
  36. uint16_t j = 0;
  37. for (uint16_t i = 0; i < (this->_samples - 1); i++) {
  38. if (i < j) {
  39. Swap(&this->_vReal[i], &this->_vReal[j]);
  40. if(dir==FFT_REVERSE)
  41. Swap(&this->_vImag[i], &this->_vImag[j]);
  42. }
  43. uint16_t k = (this->_samples >> 1);
  44. while (k <= j) {
  45. j -= k;
  46. k >>= 1;
  47. }
  48. j += k;
  49. }
  50. // Compute the FFT /
  51. #ifdef __AVR__
  52. uint8_t index = 0;
  53. #endif
  54. double c1 = -1.0;
  55. double c2 = 0.0;
  56. uint16_t l2 = 1;
  57. for (uint8_t l = 0; (l < this->_power); l++) {
  58. uint16_t l1 = l2;
  59. l2 <<= 1;
  60. double u1 = 1.0;
  61. double u2 = 0.0;
  62. for (j = 0; j < l1; j++) {
  63. for (uint16_t i = j; i < this->_samples; i += l2) {
  64. uint16_t i1 = i + l1;
  65. double t1 = u1 * this->_vReal[i1] - u2 * this->_vImag[i1];
  66. double t2 = u1 * this->_vImag[i1] + u2 * this->_vReal[i1];
  67. this->_vReal[i1] = this->_vReal[i] - t1;
  68. this->_vImag[i1] = this->_vImag[i] - t2;
  69. this->_vReal[i] += t1;
  70. this->_vImag[i] += t2;
  71. }
  72. double z = ((u1 * c1) - (u2 * c2));
  73. u2 = ((u1 * c2) + (u2 * c1));
  74. u1 = z;
  75. }
  76. #ifdef __AVR__
  77. c2 = pgm_read_float_near(&(_c2[index]));
  78. c1 = pgm_read_float_near(&(_c1[index]));
  79. index++;
  80. #else
  81. c2 = sqrt((1.0 - c1) / 2.0);
  82. c1 = sqrt((1.0 + c1) / 2.0);
  83. #endif
  84. if (dir == FFT_FORWARD) {
  85. c2 = -c2;
  86. }
  87. }
  88. // Scaling for reverse transform /
  89. if (dir != FFT_FORWARD) {
  90. for (uint16_t i = 0; i < this->_samples; i++) {
  91. this->_vReal[i] /= this->_samples;
  92. this->_vImag[i] /= this->_samples;
  93. }
  94. }
  95. }
  96. void arduinoFFT::ComplexToMagnitude()
  97. { // vM is half the size of vReal and vImag
  98. for (uint16_t i = 0; i < this->_samples; i++) {
  99. this->_vReal[i] = sqrt(sq(this->_vReal[i]) + sq(this->_vImag[i]));
  100. }
  101. }
  102. void arduinoFFT::DCRemoval()
  103. {
  104. // calculate the mean of vData
  105. double mean = 0;
  106. for (uint16_t i = 0; i < this->_samples; i++)
  107. {
  108. mean += this->_vReal[i];
  109. }
  110. mean /= this->_samples;
  111. // Subtract the mean from vData
  112. for (uint16_t i = 0; i < this->_samples; i++)
  113. {
  114. this->_vReal[i] -= mean;
  115. }
  116. }
  117. void arduinoFFT::Windowing(uint8_t windowType, uint8_t dir)
  118. {// Weighing factors are computed once before multiple use of FFT
  119. // The weighing function is symetric; half the weighs are recorded
  120. double samplesMinusOne = (double(this->_samples) - 1.0);
  121. for (uint16_t i = 0; i < (this->_samples >> 1); i++) {
  122. double indexMinusOne = double(i);
  123. double ratio = (indexMinusOne / samplesMinusOne);
  124. double weighingFactor = 1.0;
  125. // TODO Make this only calculate once
  126. // Compute and record weighting factor
  127. switch (windowType) {
  128. case FFT_WIN_TYP_RECTANGLE: // rectangle (box car)
  129. weighingFactor = 1.0;
  130. break;
  131. case FFT_WIN_TYP_HAMMING: // hamming
  132. weighingFactor = 0.54 - (0.46 * cos(twoPi * ratio));
  133. //weighingFactor = 0.080000000000000016;
  134. break;
  135. case FFT_WIN_TYP_HANN: // hann
  136. weighingFactor = 0.54 * (1.0 - cos(twoPi * ratio));
  137. break;
  138. case FFT_WIN_TYP_TRIANGLE: // triangle (Bartlett)
  139. #if defined(ESP8266) || defined(ESP32)
  140. weighingFactor = 1.0 - ((2.0 * fabs(indexMinusOne - (samplesMinusOne / 2.0))) / samplesMinusOne);
  141. #else
  142. weighingFactor = 1.0 - ((2.0 * abs(indexMinusOne - (samplesMinusOne / 2.0))) / samplesMinusOne);
  143. #endif
  144. break;
  145. case FFT_WIN_TYP_NUTTALL: // nuttall
  146. weighingFactor = 0.355768 - (0.487396 * (cos(twoPi * ratio))) + (0.144232 * (cos(fourPi * ratio))) - (0.012604 * (cos(sixPi * ratio)));
  147. break;
  148. case FFT_WIN_TYP_BLACKMAN: // blackman
  149. weighingFactor = 0.42323 - (0.49755 * (cos(twoPi * ratio))) + (0.07922 * (cos(fourPi * ratio)));
  150. break;
  151. case FFT_WIN_TYP_BLACKMAN_NUTTALL: // blackman nuttall
  152. weighingFactor = 0.3635819 - (0.4891775 * (cos(twoPi * ratio))) + (0.1365995 * (cos(fourPi * ratio))) - (0.0106411 * (cos(sixPi * ratio)));
  153. break;
  154. case FFT_WIN_TYP_BLACKMAN_HARRIS: // blackman harris
  155. weighingFactor = 0.35875 - (0.48829 * (cos(twoPi * ratio))) + (0.14128 * (cos(fourPi * ratio))) - (0.01168 * (cos(sixPi * ratio)));
  156. break;
  157. case FFT_WIN_TYP_FLT_TOP: // flat top
  158. weighingFactor = 0.2810639 - (0.5208972 * cos(twoPi * ratio)) + (0.1980399 * cos(fourPi * ratio));
  159. break;
  160. case FFT_WIN_TYP_WELCH: // welch
  161. weighingFactor = 1.0 - sq((indexMinusOne - samplesMinusOne / 2.0) / (samplesMinusOne / 2.0));
  162. break;
  163. }
  164. if (dir == FFT_FORWARD) {
  165. this->_vReal[i] *= weighingFactor;
  166. this->_vReal[this->_samples - (i + 1)] *= weighingFactor;
  167. }
  168. else {
  169. this->_vReal[i] /= weighingFactor;
  170. this->_vReal[this->_samples - (i + 1)] /= weighingFactor;
  171. }
  172. }
  173. }
  174. double arduinoFFT::MajorPeak()
  175. {
  176. double maxY = 0;
  177. uint16_t IndexOfMaxY = 0;
  178. //If sampling_frequency = 2 * max_frequency in signal,
  179. //value would be stored at position samples/2
  180. for (uint16_t i = 1; i < ((this->_samples >> 1) + 1); i++) {
  181. if ((this->_vReal[i-1] < this->_vReal[i]) && (this->_vReal[i] > this->_vReal[i+1])) {
  182. if (this->_vReal[i] > maxY) {
  183. maxY = this->_vReal[i];
  184. IndexOfMaxY = i;
  185. }
  186. }
  187. }
  188. double delta = 0.5 * ((this->_vReal[IndexOfMaxY-1] - this->_vReal[IndexOfMaxY+1]) / (this->_vReal[IndexOfMaxY-1] - (2.0 * this->_vReal[IndexOfMaxY]) + this->_vReal[IndexOfMaxY+1]));
  189. double interpolatedX = ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples-1);
  190. if(IndexOfMaxY==(this->_samples >> 1)) //To improve calculation on edge values
  191. interpolatedX = ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples);
  192. // returned value: interpolated frequency peak apex
  193. return(interpolatedX);
  194. }
  195. void arduinoFFT::MajorPeakAndMagnitude(double *freq_interpolated, double *mag_interpolated)
  196. // Added by me
  197. {
  198. double maxY = 0;
  199. uint16_t IndexOfMaxY = 0;
  200. //If sampling_frequency = 2 * max_frequency in signal,
  201. //value would be stored at position samples/2
  202. for (uint16_t i = 1; i < ((this->_samples >> 1) + 1); i++) {
  203. if ((this->_vReal[i-1] < this->_vReal[i]) && (this->_vReal[i] > this->_vReal[i+1])) {
  204. if (this->_vReal[i] > maxY) {
  205. maxY = this->_vReal[i];
  206. IndexOfMaxY = i;
  207. }
  208. }
  209. }
  210. double delta = 0.5 * ((this->_vReal[IndexOfMaxY-1] - this->_vReal[IndexOfMaxY+1]) / (this->_vReal[IndexOfMaxY-1] - (2.0 * this->_vReal[IndexOfMaxY]) + this->_vReal[IndexOfMaxY+1]));
  211. *freq_interpolated = ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples-1);
  212. if(IndexOfMaxY==(this->_samples >> 1)) //To improve calculation on edge values
  213. *freq_interpolated = ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples);
  214. if (delta <= 0) { // We can add the equals check here as well as a delta of zero has no effect.
  215. *mag_interpolated = ( this->_vReal[IndexOfMaxY] + (delta * (this->_vReal[IndexOfMaxY] - this->_vReal[IndexOfMaxY-1])) );
  216. } else if (delta > 0) {
  217. *mag_interpolated = ( this->_vReal[IndexOfMaxY] - (delta * (this->_vReal[IndexOfMaxY] - this->_vReal[IndexOfMaxY+1])) );
  218. }
  219. }
  220. void arduinoFFT::MajorPeak(double *f, double *v)
  221. {
  222. double maxY = 0;
  223. uint16_t IndexOfMaxY = 0;
  224. //If sampling_frequency = 2 * max_frequency in signal,
  225. //value would be stored at position samples/2
  226. for (uint16_t i = 1; i < ((this->_samples >> 1) + 1); i++) {
  227. if ((this->_vReal[i - 1] < this->_vReal[i]) && (this->_vReal[i] > this->_vReal[i + 1])) {
  228. if (this->_vReal[i] > maxY) {
  229. maxY = this->_vReal[i];
  230. IndexOfMaxY = i;
  231. }
  232. }
  233. }
  234. double delta = 0.5 * ((this->_vReal[IndexOfMaxY - 1] - this->_vReal[IndexOfMaxY + 1]) / (this->_vReal[IndexOfMaxY - 1] - (2.0 * this->_vReal[IndexOfMaxY]) + this->_vReal[IndexOfMaxY + 1]));
  235. double interpolatedX = ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples - 1);
  236. if (IndexOfMaxY == (this->_samples >> 1)) //To improve calculation on edge values
  237. interpolatedX = ((IndexOfMaxY + delta) * this->_samplingFrequency) / (this->_samples);
  238. // returned value: interpolated frequency peak apex
  239. *f = interpolatedX;
  240. #if defined(ESP8266) || defined(ESP32)
  241. *v = fabs(this->_vReal[IndexOfMaxY - 1] - (2.0 * this->_vReal[IndexOfMaxY]) + this->_vReal[IndexOfMaxY + 1]);
  242. #else
  243. *v = abs(this->_vReal[IndexOfMaxY - 1] - (2.0 * this->_vReal[IndexOfMaxY]) + this->_vReal[IndexOfMaxY + 1]);
  244. #endif
  245. }
  246. uint8_t arduinoFFT::Exponent(uint16_t value)
  247. {
  248. #warning("This method may not be accessible on future revisions.")
  249. // Calculates the base 2 logarithm of a value
  250. uint8_t result = 0;
  251. while (((value >> result) & 1) != 1) result++;
  252. return(result);
  253. }
  254. // Private functions
  255. void arduinoFFT::Swap(double *x, double *y)
  256. {
  257. double temp = *x;
  258. *x = *y;
  259. *y = temp;
  260. }