--[[ This package provides functions to carry out Fast Fourier Transformations. Copyright (C) 2011 by Benjamin von Ardenne Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] complex = require "modules.complex" --------------------------------------------------------------- --This is a lua port of the KissFFT Library by Mark Borgerding --It provides a simple function to carry out a fast fourier transformation (FFT). -- --module("LuaFFT", package.seeall) local cos,sin = math.cos,math.sin debugging = false function msg(...) if debugging == true then print(...) end end --------------------------------------------------------------- -- Returns the next possible size for data input -- --@param n Size -- --@return Next fast size. function next_possible_size(n) local m = n while (1) do m = n while m%2 == 0 do m = m/2 end while m%3 == 0 do m = m/3 end while m%5 == 0 do m = m/5 end if m <= 1 then break end n = n + 1 end return n end --------------------------------------------------------------- --Calculates the Fast Fourier Transformation of the given input -- --@param input A set of points that will be transformed. -- At this point, the input has to be a list of complex numbers, -- according to the format in complex.lua. --@param inverse Boolean that controls whether a transformation -- or inverse transformation will be carried out. --@return Returns a list of complex numbers with the same size -- as the input list. Contains the fourier transformation of the input. --------------------------------------------------------------- function fft(input, inverse) --the size of input defines the number of total points local num_points = #input assert(#input == next_possible_size(#input), string.format("The size of your input is not correct. For your size=%i, use a table of size=%i with zeros at the end.", #input, next_possible_size(#input))) local twiddles = {} for i = 0,num_points-1 do local phase = -2*math.pi * i / num_points if inverse then phase = phase * -1 end twiddles[1+i] = complex.new( cos(phase), sin(phase) ) end msg("Twiddles initialized...") local factors = calculate_factors(num_points) local output = {} msg("FFT Initialization completed.\nFactors of size " .. #factors) work(input, output, 1, 1, factors,1, twiddles, 1, 1, inverse) return output end --------------------------------------------------------------- --Calculates the real Fast Fourier Transformation of the given real input -- --------------------------------------------------------------- function fftr(input, inverse) end --------------------------------------------------------------- -- Short helper function that provides an easy way to print a list with values. --------------------------------------------------------------- function print_list(list) for i,v in ipairs(list) do print(i,v) end end --------------------------------------------------------------- --The essential work function that performs the FFT --------------------------------------------------------------- function work(input, output, out_index, f, factors, factors_index, twiddles, fstride, in_stride, inverse) local p = factors[factors_index] local m = factors[factors_index+1] factors_index = factors_index + 2 msg(p,m) local last = out_index + p*m local beg = out_index if m == 1 then repeat if type(input[f]) == "number" then output[out_index] = complex.new(input[f],0) else output[out_index] = input[f] end f = f + fstride*in_stride out_index = out_index +1 until out_index == last else repeat --msg("Out_index", out_index,"f", f) work(input, output,out_index, f, factors, factors_index, twiddles, fstride*p, in_stride, inverse) f = f + fstride*in_stride out_index = out_index + m until out_index == last end out_index = beg if p == 2 then butterfly2(output,out_index, fstride, twiddles, m, inverse) elseif p == 3 then butterfly3(output,out_index, fstride, twiddles, m, inverse) elseif p == 4 then butterfly4(output,out_index, fstride, twiddles, m, inverse) elseif p == 5 then butterfly5(output,out_index, fstride, twiddles, m, inverse) else butterfly_generic(output,out_index, fstride, twiddles, m, p, inverse) end end --------------------------------------------------------------- ---devides a number into a sequence of factors -- --@param num_points Number of points that are used. -- --@return Returns a list with the factors --------------------------------------------------------------- function calculate_factors(num_points) local buf = {} local p = 4 floor_sqrt = math.floor( math.sqrt( num_points) ) local n = num_points repeat while n%p > 0 do if p == 4 then p = 2 elseif p == 2 then p = 3 else p = p + 2 end if p > floor_sqrt then p = n end end n = n / p table.insert(buf, p) table.insert(buf, n) until n <= 1 return buf end --------------------------------------------------------------- --Carries out a butterfly 2 run of the input sample. --------------------------------------------------------------- function butterfly2(input,out_index,fstride, twiddles, m, inverse) local i1 = out_index local i2 = out_index + m local ti = 1 repeat local t = input[i2]* twiddles[ti] ti = ti + fstride input[i2] = input[i1] - t input[i1] = input[i1] + t i1 = i1 + 1 i2 = i2 + 1 m = m - 1 until m == 0 end --------------------------------------------------------------- --Carries out a butterfly 4 run of the input sample. --------------------------------------------------------------- function butterfly4(input,out_index, fstride, twiddles, m, inverse) local ti1, ti2, ti3 = 1,1,1 local scratch = {} local k = m local m2 = 2*m local m3 = 3*m local i = out_index repeat scratch[0] = input[i+m]*twiddles[ti1] scratch[1] = input[i+m2]*twiddles[ti2] scratch[2] = input[i+m3]*twiddles[ti3] scratch[5] = input[i]-scratch[1] input[i] = input[i] + scratch[1] scratch[3] = scratch[0] + scratch[2] scratch[4] = scratch[0] - scratch[2] input[i+m2] = input[i] - scratch[3] ti1 = ti1 + fstride ti2 = ti2 + fstride*2 ti3 = ti3 + fstride*3 input[i] = input[i] + scratch[3] if inverse then input[i+m][1] = scratch[5][1] - scratch[4][2] input[i+m][2] = scratch[5][2] + scratch[4][1] input[i+m3][1] = scratch[5][1] + scratch[4][2] input[i+m3][2] = scratch[5][2] - scratch[4][1] else input[i+m][1] = scratch[5][1] + scratch[4][2] input[i+m][2] = scratch[5][2] - scratch[4][1] input[i+m3][1] = scratch[5][1] - scratch[4][2] input[i+m3][2] = scratch[5][2] + scratch[4][1] end i = i + 1 k = k - 1 until k == 0 end --------------------------------------------------------------- --Carries out a butterfly 3 run of the input sample. --------------------------------------------------------------- function butterfly3(input,out_index, fstride, twiddles, m, inverse) local k = m local m2 = m*2 local tw1, tw2 = 1,1 local scratch = {} local epi3 = twiddles[fstride*m] local i = out_index repeat scratch[1] = input[i+m] * twiddles[tw1] scratch[2] = input[i+m2] * twiddles[tw2] scratch[3] = scratch[1] + scratch[2] scratch[0] = scratch[1] - scratch[2] tw1 = tw1 + fstride tw2 = tw2 + fstride*2 input[i+m][1] = input[i][1] - scratch[3][1]*0.5 input[i+m][2] = input[i][2] - scratch[3][2]*0.5 scratch[0] = scratch[0]:mulnum(epi3[2] ) input[i] = input[i] + scratch[3] input[i+m2][1] = input[i+m][1] + scratch[0][2] input[i+m2][2] = input[i+m][2] - scratch[0][1] input[i+m][1] = input[i+m][1] - scratch[0][2] input[i+m][2] = input[i+m][2] + scratch[0][1] i = i + 1 k = k-1 until k == 0 end --------------------------------------------------------------- --Carries out a butterfly 5 run of the input sample. --------------------------------------------------------------- function butterfly5(input,out_index, fstride, twiddles, m, inverse) local i0,i1,i2,i3,i4 = out_index,out_index+m,out_index+2*m,out_index+3*m,out_index+4*m local scratch = {} local tw = twiddles local ya,yb = tw[1+fstride*m],tw[1+fstride*2*m] for u = 0,m-1 do scratch[0] = input[i0] scratch[1] = input[i1] * tw[1+u*fstride] scratch[2] = input[i2] * tw[1+2*u*fstride] scratch[3] = input[i3] * tw[1+3*u*fstride] scratch[4] = input[i4] * tw[1+4*u*fstride] scratch[7] = scratch[1] + scratch[4] scratch[8] = scratch[2] + scratch[3] scratch[9] = scratch[2] - scratch[3] scratch[10] = scratch[1] - scratch[4] input[i0][1] = input[i0][1] + scratch[7][1] + scratch[8][1] input[i0][2] = input[i0][2] + scratch[7][2] + scratch[8][2] scratch[5] = complex.new( scratch[0][1] + scratch[7][1]*ya[1] + scratch[8][1]*yb[1], scratch[0][2] + scratch[7][2]*ya[1] + scratch[8][2]*yb[1]) scratch[6] = complex.new( scratch[10][2]*ya[2] + scratch[9][2]*yb[2], -1* scratch[10][1]*ya[2] + scratch[9][1]*yb[2]) input[i1] = scratch[5] - scratch[6] input[i4] = scratch[5] + scratch[6] scratch[11] = complex.new( scratch[0][1] + scratch[7][1]*yb[1] + scratch[8][1]*ya[1], scratch[0][2] + scratch[7][2]*yb[1] + scratch[8][2]*ya[1]) scratch[12] = complex.new( -1* scratch[10][2]*yb[2] + scratch[9][2]*ya[2], scratch[10][1]*yb[2] - scratch[9][1]*ya[2]) input[i2] = scratch[11] + scratch[12] input[i3] = scratch[11] - scratch[12] i0=i0+1 i1=i1+1 i2=i2+1 i3=i3+1 i4=i4+1 end end --------------------------------------------------------------- --Carries out a generic butterfly run of the input sample. --------------------------------------------------------------- function butterfly_generic(input,out_index, fstride, twiddles, m, p, inverse ) local norig = #input for u = 0,m-1 do local k = u for q1 = 0,p-1 do scratchbuf[q1] = input[out_index+k] k = k + m end k = u for q1=0,p-1 do local twidx = 0 input[out_index+k] = scratchbuf[0] for q=1,p-1 do twidx = twidx + fstride*k if twidx >= Norix then twidx = twidx - Norig end local t = scratchbuf[q] * twiddles[1+twidx] input[out_index+k] = input[out_index+k] + t end k = k + m end end end