-----------------------------------------------------------------------------
-- |
-- Module      :  DSP.Filter.FIR.Kaiser
-- Copyright   :  (c) Matthew Donadio 1998
-- License     :  GPL
--
-- Maintainer  :  m.p.donadio@ieee.org
-- Stability   :  experimental
-- Portability :  portable
--
-- This module implements the Kaiser Window Method for designing FIR
-- filters.
--
-----------------------------------------------------------------------------

-- Reference:
--
-- @Book{dsp,
--   author = 	 "Alan V. Oppenheim and Ronald W. Schafer",
--   title = 	 "Discrete-Time Signal Processing",
--   publisher = 	 "Prentice-Hall",
--   year = 	 1989,
--   address =	 "Englewood Cliffs",
--   series =       {Prentice-Hall Signal Processing Series}
-- }

module DSP.Filter.FIR.Kaiser (kaiser_lpf, kaiser_hpf) where

import Data.Array

import DSP.Window
import DSP.Filter.FIR.Taps

-- Set the cutoff frequency to the middle of the transition band.  This
-- equation isn't numbered.

calc_wc :: Fractional a => a -> a -> a
calc_wc wp ws = (wp + ws) / 2

-- Equation 7.90

calc_dw :: Num a => a -> a -> a
calc_dw wp ws = abs (ws - wp)

-- Equation 7.91

calc_A :: (Floating a, Ord a) => a -> a -> a
calc_A d1 d2 = -20 * logBase 10 (min d1 d2)

-- xEquation 7.92

calc_beta :: (Ord a, Floating a) => a -> a
calc_beta a | a > 50    = 0.1102 * (a - 8.7)
            | a >= 21   = 0.5842 * ((a-21) ** 0.4) + 0.07886 * (a-21)
            | otherwise = 0.0

-- Equation 7.93

calc_M :: (Integral b, RealFrac a) => a -> a -> b
calc_M a dw = ceiling ((a - 8) / (2.285 * dw))

-- Procedure on pg 455.  We should really check the peak approximation
-- error and then increase M if necessary.

-- | Designs a lowpass Kaiser filter

kaiser_lpf :: Double -- ^ wp
           -> Double -- ^ ws
           -> Double -- ^ dp
           -> Double -- ^ ds
           -> Array Int Double -- ^ h[n]

kaiser_lpf wp ws d1 d2 = window (kaiser beta m) (lpf wc m)
    where wc = calc_wc wp ws
          dw = calc_dw wp ws
          a = calc_A d1 d2
          beta = calc_beta a
          m = calc_M a dw

-- The weird case for m below is because highpass (or bandstop) filters
-- should only be Type I.  Linear phase forces a null at w=pi for Type II
-- filters, which doesn't fit well with these kinds of filters.  Again,
-- we should really check the peak approximation error and then increase
-- M (by two) if necessary.

-- | Designs a highpass Kaiser filter

kaiser_hpf :: Double -- ^ wp
           -> Double -- ^ ws
           -> Double -- ^ dp
           -> Double -- ^ ds
           -> Array Int Double -- ^ h[n]

kaiser_hpf wp ws d1 d2 = window (kaiser beta m) (hpf wc m)
    where wc = calc_wc wp ws
          dw = calc_dw wp ws
          a = calc_A d1 d2
          beta = calc_beta a
          m = ceilingEven (calc_M a dw)

ceilingEven :: Integral b => b -> b
ceilingEven x = x + mod (-x) 2