Author Topic: Fast Fourier Transform  (Read 7283 times)

0 Members and 1 Guest are viewing this topic.

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Fast Fourier Transform
« on: November 30, 2019, 02:34:17 pm »
A fast algorithm for computing the DFT for N=2^k

To use in your own programs you just need SUB fft or SUB rfft if dealing with real only signals.
i.e.:
Code: QB64: [Select]
  1. const n = 512
  2.  
  3. pi = 4*atn(1)
  4.  
  5. dim x_r(n-1), x_i(n-1)
  6. dim xx_r(n-1), xx_i(n-1)
  7.  
  8. 'create signal
  9. for i=0 to n-1
  10.         x_r(i) = 100*sin(2*pi*62.27*i/n) + 25*cos(2*pi*132.27*i/n)
  11.         x_i(i) = 0
  12.  
  13. fft xx_r(), xx_i(), x_r(), x_i(), n
  14.  

SUB fft
x_r, x_i is the input signal real and complex values, must be arrays of size n
xx_r, xx_i must also be arrays of size n and will contain output
Code: QB64: [Select]
  1. sub fft(xx_r(), xx_i(), x_r(), x_i(), n)
  2.         dim w_r as double, w_i as double, wm_r as double, wm_i as double
  3.         dim u_r as double, u_i as double, v_r as double, v_i as double
  4.  
  5.         log2n = log(n)/log(2)
  6.  
  7.         'bit rev copy
  8.         for i=0 to n - 1
  9.                 rev = 0
  10.                 for j=0 to log2n - 1
  11.                         if i and (2^j) then rev = rev + (2^(log2n - 1 - j))
  12.                 next
  13.  
  14.                 xx_r(i) = x_r(rev)
  15.                 xx_i(i) = x_i(rev)
  16.         next
  17.  
  18.  
  19.         for i=1 to log2n
  20.                 m = 2^i
  21.                 wm_r = cos(-2*pi/m)
  22.                 wm_i = sin(-2*pi/m)
  23.  
  24.                 for j=0 to n - 1 step m
  25.                         w_r = 1
  26.                         w_i = 0
  27.  
  28.                         for k=0 to m/2 - 1
  29.                                 p = j + k
  30.                                 q = p + (m \ 2)
  31.  
  32.                                 u_r = w_r*xx_r(q) - w_i*xx_i(q)
  33.                                 u_i = w_r*xx_i(q) + w_i*xx_r(q)
  34.                                 v_r = xx_r(p)
  35.                                 v_i = xx_i(p)
  36.  
  37.                                 xx_r(p) = v_r + u_r
  38.                                 xx_i(p) = v_i + u_i
  39.                                 xx_r(q) = v_r - u_r
  40.                                 xx_i(q) = v_i - u_i
  41.  
  42.                                 u_r = w_r
  43.                                 u_i = w_i
  44.                                 w_r = u_r*wm_r - u_i*wm_i
  45.                                 w_i = u_r*wm_i + u_i*wm_r
  46.                         next
  47.                 next
  48.         next
  49.  

SUB rfft
This is about 2x faster than above fft if dealing with only real values
x_r is the input signal, must be an array of size n
xx_r, xx_i must also be arrays of size n and will contain output
Since in the real FFT the second half of the output are complex conjugates of the first half, you may wish to not compute the second half and remove the last 4 lines in this SUB. I left it in just in case.
Code: QB64: [Select]
  1. sub rfft(xx_r(), xx_i(), x_r(), n)
  2.         dim w_r as double, w_i as double, wm_r as double, wm_i as double
  3.         dim u_r as double, u_i as double, v_r as double, v_i as double
  4.  
  5.         log2n = log(n/2)/log(2)
  6.  
  7.         for i=0 to n/2 - 1
  8.                 rev = 0
  9.                 for j=0 to log2n - 1
  10.                         if i and (2^j) then rev = rev + (2^(log2n - 1 - j))
  11.                 next
  12.  
  13.                 xx_r(i) = x_r(2*rev)
  14.                 xx_i(i) = x_r(2*rev + 1)
  15.         next
  16.  
  17.         for i=1 to log2n
  18.                 m = 2^i
  19.                 wm_r = cos(-2*pi/m)
  20.                 wm_i = sin(-2*pi/m)
  21.  
  22.                 for j=0 to n/2 - 1 step m
  23.                         w_r = 1
  24.                         w_i = 0
  25.  
  26.                         for k=0 to m/2 - 1
  27.                                 p = j + k
  28.                                 q = p + (m \ 2)
  29.  
  30.                                 u_r = w_r*xx_r(q) - w_i*xx_i(q)
  31.                                 u_i = w_r*xx_i(q) + w_i*xx_r(q)
  32.                                 v_r = xx_r(p)
  33.                                 v_i = xx_i(p)
  34.  
  35.                                 xx_r(p) = v_r + u_r
  36.                                 xx_i(p) = v_i + u_i
  37.                                 xx_r(q) = v_r - u_r
  38.                                 xx_i(q) = v_i - u_i
  39.  
  40.                                 u_r = w_r
  41.                                 u_i = w_i
  42.                                 w_r = u_r*wm_r - u_i*wm_i
  43.                                 w_i = u_r*wm_i + u_i*wm_r
  44.                         next
  45.                 next
  46.         next
  47.  
  48.         xx_r(n/2) = xx_r(0)
  49.         xx_i(n/2) = xx_i(0)
  50.  
  51.         for i=1 to n/2 - 1
  52.                 xx_r(n/2 + i) = xx_r(n/2 - i)
  53.                 xx_i(n/2 + i) = xx_i(n/2 - i)
  54.         next
  55.  
  56.         dim xpr as double, xpi as double
  57.         dim xmr as double, xmi as double
  58.  
  59.         for i=0 to n/2 - 1
  60.                 xpr = (xx_r(i) + xx_r(n/2 + i)) / 2
  61.                 xpi = (xx_i(i) + xx_i(n/2 + i)) / 2
  62.  
  63.                 xmr = (xx_r(i) - xx_r(n/2 + i)) / 2
  64.                 xmi = (xx_i(i) - xx_i(n/2 + i)) / 2
  65.  
  66.                 xx_r(i) = xpr + xpi*cos(2*pi*i/n) - xmr*sin(2*pi*i/n)
  67.                 xx_i(i) = xmi - xpi*sin(2*pi*i/n) - xmr*cos(2*pi*i/n)
  68.         next
  69.  
  70.         'symmetry, complex conj
  71.         for i=0 to n/2 - 1
  72.                 xx_r(n/2 + i) = xx_r(n/2 - 1 - i)
  73.                 xx_i(n/2 + i) =-xx_i(n/2 - 1 - i)
  74.         next
  75.  

As an example FYI, here is a direct unoptimized DFT for comparison. It is much slower but can be used with any n
Code: QB64: [Select]
  1. sub dft(xx_r(), xx_i(), x_r(), x_i(), n)
  2.         for i=0 to n-1
  3.                 xx_r(i) = 0
  4.                 xx_i(i) = 0
  5.                 for j=0 to n-1
  6.                         xx_r(i) = xx_r(i) + x_r(j)*cos(2*pi*i*j/n) + x_i(j)*sin(2*pi*i*j/n)
  7.                         xx_i(i) = xx_i(i) - x_r(j)*sin(2*pi*i*j/n) + x_i(j)*cos(2*pi*i*j/n)
  8.                 next
  9.         next
  10.  

if you wish to take the inverse FFT then do the following to get back x_r and x_i from xx_r and xx_i. I chose not to include a inverse flag in the sub for my own reasons, it should be easy enough to modify above SUBs to add an inverse option if you so wish.
Code: QB64: [Select]
  1. 'inverse fft
  2. for i=0 to n-1
  3.         xx_i(i) = -xx_i(i)
  4.  
  5. fft x_r(), x_i(), xx_r(), xx_i(), n
  6.  
  7. for i=0 to n-1
  8.         x_r(i) = x_r(i)/n
  9.         x_i(i) = x_i(i)/n
  10.  

Here is a basic example using and plotting all the SUBs:
Code: QB64: [Select]
  1. 'defdbl a-z
  2.  
  3. const sw = 1024
  4. const sh = 600
  5.  
  6. 'pi = 2*asin(1)
  7. pi = 4*atn(1)
  8.  
  9. declare sub rfft(xx_r(), xx_i(), x_r(), n)
  10. declare sub fft(xx_r(), xx_i(), x_r(), x_i(), n)
  11. declare sub dft(xx_r(), xx_i(), x_r(), x_i(), n)
  12.  
  13.  
  14. dim x_r(sw-1), x_i(sw-1)
  15. dim xx_r(sw-1), xx_i(sw-1)
  16.  
  17. for i=0 to sw-1
  18.         x_r(i) = 100*sin(2*pi*62.27*i/sw) + 25*cos(2*pi*132.27*i/sw)
  19.         x_i(i) = 0
  20.  
  21.  
  22. 'screenres sw, sh, 32
  23. screen _newimage(sw, sh, 32)
  24.  
  25. pset (0, sh/4 - x_r(0))
  26. for i=0 to sw-1
  27.         line -(i, sh/4 - x_r(i)), _rgb(100,100,100)
  28.  
  29. dft xx_r(), xx_i(), x_r(), x_i(), sw
  30. pset (0, 3*sh/4 - 0.1*sqr(xx_r(0)*xx_r(0) + xx_i(0)*xx_i(0)) ), _rgb(0,255,0)
  31. for i=0 to sw - 1
  32.         line -(i, 3*sh/4 - 0.1*sqr(xx_r(i)*xx_r(i) + xx_i(i)*xx_i(i)) ), _rgb(0,255,0)
  33. line (0, 3*sh/4)-step(sw,0), _rgb(0,255,0),,&h5555
  34.  
  35. t = timer
  36. for i=0 to 50
  37. fft xx_r(), xx_i(), x_r(), x_i(), sw
  38. locate 1,1
  39. print "50x fft ";timer - t
  40.  
  41. pset (0, 50+3*sh/4 - 0.1*sqr(xx_r(0)*xx_r(0) + xx_i(0)*xx_i(0)) ), _rgb(255,0,0)
  42. for i=0 to sw - 1
  43.         line -(i, 50+3*sh/4 - 0.1*sqr(xx_r(i)*xx_r(i) + xx_i(i)*xx_i(i)) ), _rgb(255,0,0)
  44. line (0, 50+3*sh/4)-step(sw,0), _rgb(255,0,0),,&h5555
  45.  
  46.  
  47. for i=0 to sw-1
  48.         xx_r(i) = 0
  49.         xx_i(i) = 0
  50.  
  51. t = timer
  52. for i=0 to 50
  53. rfft xx_r(), xx_i(), x_r(), sw
  54. locate 2,1
  55. print "50x rfft ";timer - t
  56.  
  57. pset (0, 100+3*sh/4 - 0.1*sqr(xx_r(0)*xx_r(0) + xx_i(0)*xx_i(0)) ), _rgb(255,255,0)
  58. for i=0 to sw - 1
  59.         line -(i, 100+3*sh/4 - 0.1*sqr(xx_r(i)*xx_r(i) + xx_i(i)*xx_i(i)) ), _rgb(255,255,0)
  60. line (0, 100+3*sh/4)-step(sw,0), _rgb(255,255,0),,&h5555
  61.  
  62.  
  63.  
  64. sub rfft(xx_r(), xx_i(), x_r(), n)
  65.         dim w_r as double, w_i as double, wm_r as double, wm_i as double
  66.         dim u_r as double, u_i as double, v_r as double, v_i as double
  67.  
  68.         log2n = log(n/2)/log(2)
  69.  
  70.         for i=0 to n/2 - 1
  71.                 rev = 0
  72.                 for j=0 to log2n - 1
  73.                         if i and (2^j) then rev = rev + (2^(log2n - 1 - j))
  74.                 next
  75.  
  76.                 xx_r(i) = x_r(2*rev)
  77.                 xx_i(i) = x_r(2*rev + 1)
  78.         next
  79.  
  80.         for i=1 to log2n
  81.                 m = 2^i
  82.                 wm_r = cos(-2*pi/m)
  83.                 wm_i = sin(-2*pi/m)
  84.  
  85.                 for j=0 to n/2 - 1 step m
  86.                         w_r = 1
  87.                         w_i = 0
  88.  
  89.                         for k=0 to m/2 - 1
  90.                                 p = j + k
  91.                                 q = p + (m \ 2)
  92.  
  93.                                 u_r = w_r*xx_r(q) - w_i*xx_i(q)
  94.                                 u_i = w_r*xx_i(q) + w_i*xx_r(q)
  95.                                 v_r = xx_r(p)
  96.                                 v_i = xx_i(p)
  97.  
  98.                                 xx_r(p) = v_r + u_r
  99.                                 xx_i(p) = v_i + u_i
  100.                                 xx_r(q) = v_r - u_r
  101.                                 xx_i(q) = v_i - u_i
  102.  
  103.                                 u_r = w_r
  104.                                 u_i = w_i
  105.                                 w_r = u_r*wm_r - u_i*wm_i
  106.                                 w_i = u_r*wm_i + u_i*wm_r
  107.                         next
  108.                 next
  109.         next
  110.  
  111.         xx_r(n/2) = xx_r(0)
  112.         xx_i(n/2) = xx_i(0)
  113.  
  114.         for i=1 to n/2 - 1
  115.                 xx_r(n/2 + i) = xx_r(n/2 - i)
  116.                 xx_i(n/2 + i) = xx_i(n/2 - i)
  117.         next
  118.  
  119.         dim xpr as double, xpi as double
  120.         dim xmr as double, xmi as double
  121.  
  122.         for i=0 to n/2 - 1
  123.                 xpr = (xx_r(i) + xx_r(n/2 + i)) / 2
  124.                 xpi = (xx_i(i) + xx_i(n/2 + i)) / 2
  125.  
  126.                 xmr = (xx_r(i) - xx_r(n/2 + i)) / 2
  127.                 xmi = (xx_i(i) - xx_i(n/2 + i)) / 2
  128.  
  129.                 xx_r(i) = xpr + xpi*cos(2*pi*i/n) - xmr*sin(2*pi*i/n)
  130.                 xx_i(i) = xmi - xpi*sin(2*pi*i/n) - xmr*cos(2*pi*i/n)
  131.         next
  132.  
  133.         'symmetry, complex conj
  134.         for i=0 to n/2 - 1
  135.                 xx_r(n/2 + i) = xx_r(n/2 - 1 - i)
  136.                 xx_i(n/2 + i) =-xx_i(n/2 - 1 - i)
  137.         next
  138.  
  139. sub fft(xx_r(), xx_i(), x_r(), x_i(), n)
  140.         dim w_r as double, w_i as double, wm_r as double, wm_i as double
  141.         dim u_r as double, u_i as double, v_r as double, v_i as double
  142.  
  143.         log2n = log(n)/log(2)
  144.  
  145.         'bit rev copy
  146.         for i=0 to n - 1
  147.                 rev = 0
  148.                 for j=0 to log2n - 1
  149.                         if i and (2^j) then rev = rev + (2^(log2n - 1 - j))
  150.                 next
  151.  
  152.                 xx_r(i) = x_r(rev)
  153.                 xx_i(i) = x_i(rev)
  154.         next
  155.  
  156.  
  157.         for i=1 to log2n
  158.                 m = 2^i
  159.                 wm_r = cos(-2*pi/m)
  160.                 wm_i = sin(-2*pi/m)
  161.  
  162.                 for j=0 to n - 1 step m
  163.                         w_r = 1
  164.                         w_i = 0
  165.  
  166.                         for k=0 to m/2 - 1
  167.                                 p = j + k
  168.                                 q = p + (m \ 2)
  169.  
  170.                                 u_r = w_r*xx_r(q) - w_i*xx_i(q)
  171.                                 u_i = w_r*xx_i(q) + w_i*xx_r(q)
  172.                                 v_r = xx_r(p)
  173.                                 v_i = xx_i(p)
  174.  
  175.                                 xx_r(p) = v_r + u_r
  176.                                 xx_i(p) = v_i + u_i
  177.                                 xx_r(q) = v_r - u_r
  178.                                 xx_i(q) = v_i - u_i
  179.  
  180.                                 u_r = w_r
  181.                                 u_i = w_i
  182.                                 w_r = u_r*wm_r - u_i*wm_i
  183.                                 w_i = u_r*wm_i + u_i*wm_r
  184.                         next
  185.                 next
  186.         next
  187.  
  188. sub dft(xx_r(), xx_i(), x_r(), x_i(), n)
  189.         for i=0 to n-1
  190.                 xx_r(i) = 0
  191.                 xx_i(i) = 0
  192.                 for j=0 to n-1
  193.                         xx_r(i) = xx_r(i) + x_r(j)*cos(2*pi*i*j/n) + x_i(j)*sin(2*pi*i*j/n)
  194.                         xx_i(i) = xx_i(i) - x_r(j)*sin(2*pi*i*j/n) + x_i(j)*cos(2*pi*i*j/n)
  195.                 next
  196.         next
  197.  

A common use for FFT is filtering unwanted frequencies. The following example demonstrates this, with screenshots attached
Code: QB64: [Select]
  1. 'defdbl a-z
  2.  
  3. const sw = 512
  4. const sh = 600
  5.  
  6. 'pi = 2*asin(1)
  7. pi = 4*atn(1)
  8.  
  9. declare sub fft(xx_r(), xx_i(), x_r(), x_i(), n)
  10.  
  11.  
  12. dim x_r(sw-1), x_i(sw-1)
  13. dim xx_r(sw-1), xx_i(sw-1)
  14.  
  15. for i=0 to sw-1
  16.         'x_r(i) = 100*sin(2*pi*62.27*i/sw) + 25*cos(2*pi*132.27*i/sw)
  17.         x_r(i) = 100*sin(0.08*i) + 25*cos(i)
  18.         x_i(i) = 0
  19.  
  20.  
  21. 'screenres sw, sh, 32
  22. screen _newimage(sw*2, sh, 32)
  23.  
  24. 'plot input signal
  25. pset (0, sh/4 - x_r(0))
  26. for i=0 to sw-1
  27.         line -(i, sh/4 - x_r(i)), _rgb(255,0,0)
  28. line (0, sh/4)-step(sw,0), _rgb(255,0,0),,&h5555
  29. color _rgb(255,0,0)
  30. _printstring (0,0), "input signal"
  31.  
  32. fft xx_r(), xx_i(), x_r(), x_i(), sw
  33.  
  34. 'plot its fft
  35. pset (0, 50+3*sh/4 - 0.01*sqr(xx_r(0)*xx_r(0) + xx_i(0)*xx_i(0)) ), _rgb(255,255,0)
  36. for i=0 to sw/2
  37.         line -(i*2, 50+3*sh/4 - 0.01*sqr(xx_r(i)*xx_r(i) + xx_i(i)*xx_i(i)) ), _rgb(255,255,0)
  38. line (0, 50+3*sh/4)-step(sw,0), _rgb(255,255,0),,&h5555
  39.  
  40.  
  41. 'set unwanted frequencies to zero
  42. for i=50 to sw/2
  43.         xx_r(i) = 0
  44.         xx_i(i) = 0
  45.         xx_r(sw - i) = 0
  46.         xx_i(sw - i) = 0
  47.  
  48. 'plot fft of filtered signal
  49. pset (sw, 50+3*sh/4 - 0.01*sqr(xx_r(0)*xx_r(0) + xx_i(0)*xx_i(0)) ), _rgb(255,255,0)
  50. for i=0 to sw/2
  51.         line -(sw + i*2, 50+3*sh/4 - 0.01*sqr(xx_r(i)*xx_r(i) + xx_i(i)*xx_i(i)) ), _rgb(0,155,255)
  52. line (sw, 50+3*sh/4)-step(sw,0), _rgb(0,155,255),,&h5555
  53.  
  54. 'take inverse fft
  55. for i=0 to sw-1
  56.         xx_i(i) = -xx_i(i)
  57.  
  58. fft x_r(), x_i(), xx_r(), xx_i(), sw
  59.  
  60. for i=0 to sw-1
  61.         x_r(i) = x_r(i)/sw
  62.         x_i(i) = x_i(i)/sw
  63.  
  64.  
  65. 'plot filtered signal
  66. pset (sw, sh/4 - x_r(0))
  67. for i=0 to sw-1
  68.         line -(sw + i, sh/4 - x_r(i)), _rgb(0,255,0)
  69. line (sw, sh/4)-step(sw,0), _rgb(0,255,0),,&h5555
  70.  
  71. color _rgb(0,255,0)
  72. _printstring (sw,0), "filtered signal"
  73.  
  74.  
  75. sub fft(xx_r(), xx_i(), x_r(), x_i(), n)
  76.         dim w_r as double, w_i as double, wm_r as double, wm_i as double
  77.         dim u_r as double, u_i as double, v_r as double, v_i as double
  78.  
  79.         log2n = log(n)/log(2)
  80.  
  81.         'bit rev copy
  82.         for i=0 to n - 1
  83.                 rev = 0
  84.                 for j=0 to log2n - 1
  85.                         if i and (2^j) then rev = rev + (2^(log2n - 1 - j))
  86.                 next
  87.  
  88.                 xx_r(i) = x_r(rev)
  89.                 xx_i(i) = x_i(rev)
  90.         next
  91.  
  92.  
  93.         for i=1 to log2n
  94.                 m = 2^i
  95.                 wm_r = cos(-2*pi/m)
  96.                 wm_i = sin(-2*pi/m)
  97.  
  98.                 for j=0 to n - 1 step m
  99.                         w_r = 1
  100.                         w_i = 0
  101.  
  102.                         for k=0 to m/2 - 1
  103.                                 p = j + k
  104.                                 q = p + (m \ 2)
  105.  
  106.                                 u_r = w_r*xx_r(q) - w_i*xx_i(q)
  107.                                 u_i = w_r*xx_i(q) + w_i*xx_r(q)
  108.                                 v_r = xx_r(p)
  109.                                 v_i = xx_i(p)
  110.  
  111.                                 xx_r(p) = v_r + u_r
  112.                                 xx_i(p) = v_i + u_i
  113.                                 xx_r(q) = v_r - u_r
  114.                                 xx_i(q) = v_i - u_i
  115.  
  116.                                 u_r = w_r
  117.                                 u_i = w_i
  118.                                 w_r = u_r*wm_r - u_i*wm_i
  119.                                 w_i = u_r*wm_i + u_i*wm_r
  120.                         next
  121.                 next
  122.         next
  123.  


Another common thing seems to be wanting to find an exact frequency of a signal. The following demonstrates calculating the frequency of a sine corrupted in noise and applying bin to frequency correction for extra accuracy, screenshot attached.
Code: QB64: [Select]
  1. 'defdbl a-z
  2.  
  3. const sw = 1024
  4. const sh = 600
  5.  
  6. 'pi = 2*asin(1)
  7. pi = 4*atn(1)
  8.  
  9. declare sub rfft(xx_r(), xx_i(), x_r(), n)
  10.  
  11. dim x_r(sw-1), x_i(sw-1)
  12. dim xx_r(sw-1), xx_i(sw-1)
  13.  
  14. for i=0 to sw-1
  15.         x_r(i) = 100*sin(2*pi*(sw*2000/44000)*i/sw) + (100*rnd - 50)
  16.  
  17.  
  18. 'screenres sw, sh, 32
  19. screen _newimage(sw, sh, 32)
  20.  
  21. 'plot signal
  22. pset (0, sh/4 - x_r(0))
  23. for i=0 to sw-1
  24.         line -(i, sh/4 - x_r(i)), _rgb(255,0,0)
  25. line (0, sh/4)-step(sw,0), _rgb(255,0,0),,&h5555
  26.  
  27. _printstring (0, 0), "2000 kHz signal with RND noise sampled at 44 kHz in 1024 samples"
  28.  
  29.  
  30. rfft xx_r(), xx_i(), x_r(), sw
  31.  
  32. 'plot its fft
  33. pset (0, 50+3*sh/4 - 0.005*sqr(xx_r(0)*xx_r(0) + xx_i(0)*xx_i(0)) ), _rgb(255,255,0)
  34. for i=0 to sw/2
  35.         line -(i*2, 50+3*sh/4 - 0.005*sqr(xx_r(i)*xx_r(i) + xx_i(i)*xx_i(i)) ), _rgb(255,255,0)
  36. line (0, 50+3*sh/4)-step(sw,0), _rgb(255,255,0),,&h5555
  37.  
  38.  
  39. 'find peak
  40. max = 0
  41. m = 0
  42. for i=0 to sw/2
  43.         d = 0.01*sqr(xx_r(i)*xx_r(i) + xx_i(i)*xx_i(i))
  44.         if d > max then
  45.                 max = d
  46.                 m = i
  47.         end if
  48.  
  49. _printstring (0, sh/2), "m_peak ="+str$(m)
  50. _printstring (0, sh/2 + 16), "f_peak = m_peak * 44 kHz / 1024 samples = "+str$(m*44000/1024)+" Hz"
  51.  
  52. 'apply frequency correction, only works for some signals
  53. dim u_r as double, u_i as double
  54. dim v_r as double, v_i as double
  55.  
  56. u_r = xx_r(m - 1) - xx_r(m + 1)
  57. u_i = xx_i(m - 1) - xx_i(m + 1)
  58. v_r = 2*xx_r(m) - xx_r(m - 1) - xx_r(m + 1)
  59. v_i = 2*xx_i(m) - xx_i(m - 1) - xx_i(m + 1)
  60. c = (u_r*v_r + u_i*v_i)/(v_r*v_r + v_i*v_i)
  61.  
  62. _printstring (0, sh/2 + 2*16), "f_corrected = "+str$((m+c)*44000/1024)+" Hz"
  63.  
  64.  
  65.  
  66. sub rfft(xx_r(), xx_i(), x_r(), n)
  67.         dim w_r as double, w_i as double, wm_r as double, wm_i as double
  68.         dim u_r as double, u_i as double, v_r as double, v_i as double
  69.  
  70.         log2n = log(n/2)/log(2)
  71.  
  72.         for i=0 to n/2 - 1
  73.                 rev = 0
  74.                 for j=0 to log2n - 1
  75.                         if i and (2^j) then rev = rev + (2^(log2n - 1 - j))
  76.                 next
  77.  
  78.                 xx_r(i) = x_r(2*rev)
  79.                 xx_i(i) = x_r(2*rev + 1)
  80.         next
  81.  
  82.         for i=1 to log2n
  83.                 m = 2^i
  84.                 wm_r = cos(-2*pi/m)
  85.                 wm_i = sin(-2*pi/m)
  86.  
  87.                 for j=0 to n/2 - 1 step m
  88.                         w_r = 1
  89.                         w_i = 0
  90.  
  91.                         for k=0 to m/2 - 1
  92.                                 p = j + k
  93.                                 q = p + (m \ 2)
  94.  
  95.                                 u_r = w_r*xx_r(q) - w_i*xx_i(q)
  96.                                 u_i = w_r*xx_i(q) + w_i*xx_r(q)
  97.                                 v_r = xx_r(p)
  98.                                 v_i = xx_i(p)
  99.  
  100.                                 xx_r(p) = v_r + u_r
  101.                                 xx_i(p) = v_i + u_i
  102.                                 xx_r(q) = v_r - u_r
  103.                                 xx_i(q) = v_i - u_i
  104.  
  105.                                 u_r = w_r
  106.                                 u_i = w_i
  107.                                 w_r = u_r*wm_r - u_i*wm_i
  108.                                 w_i = u_r*wm_i + u_i*wm_r
  109.                         next
  110.                 next
  111.         next
  112.  
  113.         xx_r(n/2) = xx_r(0)
  114.         xx_i(n/2) = xx_i(0)
  115.  
  116.         for i=1 to n/2 - 1
  117.                 xx_r(n/2 + i) = xx_r(n/2 - i)
  118.                 xx_i(n/2 + i) = xx_i(n/2 - i)
  119.         next
  120.  
  121.         dim xpr as double, xpi as double
  122.         dim xmr as double, xmi as double
  123.  
  124.         for i=0 to n/2 - 1
  125.                 xpr = (xx_r(i) + xx_r(n/2 + i)) / 2
  126.                 xpi = (xx_i(i) + xx_i(n/2 + i)) / 2
  127.  
  128.                 xmr = (xx_r(i) - xx_r(n/2 + i)) / 2
  129.                 xmi = (xx_i(i) - xx_i(n/2 + i)) / 2
  130.  
  131.                 xx_r(i) = xpr + xpi*cos(2*pi*i/n) - xmr*sin(2*pi*i/n)
  132.                 xx_i(i) = xmi - xpi*sin(2*pi*i/n) - xmr*cos(2*pi*i/n)
  133.         next
  134.  
  135.         'symmetry, complex conj
  136.         for i=0 to n/2 - 1
  137.                 xx_r(n/2 + i) = xx_r(n/2 - 1 - i)
  138.                 xx_i(n/2 + i) =-xx_i(n/2 - 1 - i)
  139.         next
  140.  
qb64fft1.png
* qb64fft1.png (Filesize: 4.98 KB, Dimensions: 1024x600, Views: 425)
qb64fft2.png
* qb64fft2.png (Filesize: 6.83 KB, Dimensions: 1024x600, Views: 426)
« Last Edit: May 05, 2020, 06:56:27 am by _vince »

Offline Ashish

  • Forum Resident
  • Posts: 630
  • Never Give Up!
    • View Profile
Re: Fast Fourier Transform
« Reply #1 on: December 01, 2019, 11:04:03 am »
I don't understand it but I think it is something good, something advance... :)
if (Me.success) {Me.improve()} else {Me.tryAgain()}


My Projects - https://github.com/AshishKingdom?tab=repositories
OpenGL tutorials - https://ashishkingdom.github.io/OpenGL-Tutorials

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Fast Fourier Transform
« Reply #2 on: December 01, 2019, 11:16:03 am »
What is DFT? https://en.wikipedia.org/wiki/Discrete_Fourier_transform

Ah, maybe you haven't heard, it's discreet.

Or it is Density Function Theory, nope not this code...

with an S it means Don't Forget To Smile :D
« Last Edit: December 01, 2019, 11:21:21 am by bplus »

Offline anttiylikoski

  • Newbie
  • Posts: 25
    • View Profile
Re: Fast Fourier Transform
« Reply #3 on: December 25, 2019, 09:37:35 am »
I have planned to author an article about the Fourier Transform for a Scandinavian radio amateur magazine.

It would be nice, if I could print your FFT programs in that article:

1.  giving the full copyrights credits, according to the immaterial rights laws

2.  only with the author's permission.

I would list the BASIC code, and, explain the code in the article.

See the Finland Radio Amateurs Association  https://sral.info/   


Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Fast Fourier Transform
« Reply #4 on: December 29, 2019, 11:03:55 pm »
free to use for anything, no credit required but a link to this forum or post appreciated

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Fast Fourier Transform
« Reply #5 on: April 30, 2020, 02:30:42 pm »
Hi _Vince,

i try using your last source code, but it is not QB64 source. What is SHL statement? I'm playing with your program (with the sources above). The program obtains one signal from the signals. Could you please show me how to calculate the frequency for this acquired signal? I assume that a time window must be used to obtain the frequency, and the frequency will be calculated, for example, in a sample of 441 samples for a recording length of 10 milliseconds (for a sampling frequency of 44100). Or it will be enough to calculate the number of changes (ie if the previous sample increases and the next decreases, it is one change, if the previous increases and the next also, it is not a change), then the frequency would be calculated as 1/441 * number of changes - can this be count this way?
« Last Edit: April 30, 2020, 03:58:59 pm by Petr »

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Fast Fourier Transform
« Reply #6 on: April 30, 2020, 10:17:34 pm »
Hah, didn't think anyone would ever use this.  I've revised the original post with easier to use code and better examples.

To answer your question, when you take an FFT of, say, 1024 samples sampled at 44 kHz you only have 1024 bins giving you about 44 Hz of resolution. That's the accuracy with which you can pick out individual frequencies, the more samples the better accuracy.  If you want to know the exact frequency of a particular bin, say bin m, then

f = m * f_s / 1024

where f_s is the sampling frequency, ie 44100 Hz.  That is, the magnitude of that frequency f is sqr(xx_r(m)^2 + xx_i(m)^2).  I've included this concept as an example in above post.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Fast Fourier Transform
« Reply #7 on: May 01, 2020, 06:50:36 am »
_Vince, thank you very much for this detailed answer, I will do some experiments to it for maximal possible understandt.

Offline Sanmayce

  • Newbie
  • Posts: 63
  • Where is that English Text Sidekick?
    • View Profile
    • Sanmayce's home
Re: Fast Fourier Transform
« Reply #8 on: December 23, 2021, 02:17:43 am »
Many thanks for sharing!
If I am successful, in next days will share my BAR-JUMPER - to enable visual feedback to the MP3/WAV files...
He learns not to learn and reverts to what all men pass by.

Offline MasterGy

  • Seasoned Forum Regular
  • Posts: 327
  • people lie, math never lies
    • View Profile
Re: Fast Fourier Transform
« Reply #9 on: December 23, 2021, 07:20:38 am »
Great !! Congratulations! It is a very useful thing to deal with. Rarely, but then very! About 15 years ago, I controlled an engraving machine with qb from dos. I experimented with using a microphone to tell the computer when the milling head would reach the object. This was necessary to be able to engrave on an irregular surface. Unfortunately, the solution was inaccurate due to the noises, and I remember looking for this fourier transform thing at the time, because I wanted to take out the frequency where the milling motor was buzzing.
That’s what you’re dealing with now, I should have really, really used to! Congratulations!

http://tukorgravirozas.fw.hu/mastergy/4pohar/pg.avi