module SPCore
# General FIR filter class. Contains the filter kernel and performs
# convolution.
class FIR
  
  attr_reader :kernel, :order, :sample_rate
  
  # A new instance of FIR. Filter order will by kernel size - 1.
  # @param [Array] kernel Filter kernel values.
  # @param [Numeric] sample_rate The sample rate the filter operates at.
  def initialize kernel, sample_rate
    @kernel = kernel
    @order = kernel.size - 1
    @sample_rate = sample_rate
  end
  
  # Convolve the given input data with the filter kernel.
  # @param [Array] input Array of input data to by convolved with filter kernel.
  #                      The array size must be greater than the filter kernel size.
  def convolve input
    kernel_size = @kernel.size
    raise ArgumentError, "input.size #{input.size} is not greater than filter kernel size #{kernel_size}" unless input.size > kernel_size
    
    output = Array.new(input.size, 0.0)

    for i in 0...kernel_size
      sum = 0.0
      # convolve the input with the filter kernel
      for j in 0...i
        sum += (input[j] * @kernel[kernel_size - (1 + i - j)])
      end
      output[i] = sum
    end
    
    for i in kernel_size...input.size
      sum = 0.0
      # convolve the input with the filter kernel
      for j in 0...kernel_size
        sum += (input[i-j] * @kernel[j])
      end
      output[i] = sum
    end
    
    return output
  end
  
  # Calculate the filter frequency magnitude response.
  # @param [Numeric] use_db Calculate magnitude in dB.
  def freq_response use_db = false

    input = [0.0] + @kernel # make the size even
    output = FFT.forward input
    
    output = output[0...(output.size / 2)]  # ignore second half (mirror image)
    output = output.map {|x| x.magnitude }  # calculate magnitudes from complex values
    
    if use_db
      output = output.map {|x| Gain::linear_to_db x }
    end
    
    response = {}
    output.each_index do |n|
      frequency = (@sample_rate * n) / (output.size * 2.0)
      response[frequency] = output[n]
    end
    
    ## amplitude = 2 * f[j] / size
    #output = output.map {|x| 2 * x / output.size }
    
    return response
  end

  # Calculate the filter frequency magnitude response and
  # graph the results.
  # @param [Numeric] use_db Calculate magnitude in dB.  
  def plot_freq_response use_db = true
    plotter = Plotter.new(
      :title => "Freq magnitude response of #{@order}-order FIR filter",
      :xlabel => "frequency (Hz)",
      :ylabel => "magnitude#{use_db ? " (dB)" : ""}",
      :logscale => "x"
    )
    
    plotter.plot_2d "" => freq_response(use_db)
  end
end
end