dsp-0.2.2: DSP/Filter/FIR/FIR.hs
-----------------------------------------------------------------------------
-- |
-- Module : DSP.Filter.FIR.FIR
-- Copyright : (c) Matthew Donadio 2003
-- License : GPL
--
-- Maintainer : m.p.donadio@ieee.org
-- Stability : experimental
-- Portability : portable
--
-- Finite Impuse Response filtering functions
--
-----------------------------------------------------------------------------
module DSP.Filter.FIR.FIR (fir, test) where
import Data.Array
-- | Implements the following function, which is a FIR filter
--
-- @y[n] = sum(k=0,M) h[k]*x[n-k]@
--
-- We implement the fir function with five helper functions, depending on
-- the type of the filter. In the following functions, we use the O&S
-- convention that m is the order of the filter, which is equal to the
-- number of taps minus one.
{-# specialize fir :: Array Int Float -> [Float] -> [Float] #-}
{-# specialize fir :: Array Int Double -> [Double] -> [Double] #-}
fir :: (Num a, Eq a) => Array Int a -- ^ h[n]
-> [a] -- ^ x[n]
-> [a] -- ^ y[n]
fir _ [] = []
fir h (x:xs) | isFIRType1 h = fir'1 h w xs
| isFIRType2 h = fir'2 h w xs
| isFIRType3 h = fir'3 h w xs
| isFIRType4 h = fir'4 h w xs
| otherwise = fir'0 h w xs
where w = listArray (0,m) $ x : replicate m 0
m = snd $ bounds h
-- This is for testing the symmetric helpers.
fir0 :: Num a => Array Int a -> [a] -> [a]
fir0 _ [] = []
fir0 h (x:xs) = fir'0 h w xs
where w = listArray (0,m) $ x : replicate m 0
m = snd $ bounds h
-- Asymmetric FIR
{-# specialize fir'0 :: Array Int Float -> Array Int Float -> [Float] -> [Float] #-}
{-# specialize fir'0 :: Array Int Double -> Array Int Double -> [Double] -> [Double] #-}
fir'0 :: Num a => Array Int a -> Array Int a -> [a] -> [a]
fir'0 h w [] = y : []
where y = sum [ h!i * w!i | i <- [0..m] ]
m = snd $ bounds h
fir'0 h w (x:xs) = y : fir'0 h w' xs
where y = sum [ h!i * w!i | i <- [0..m] ]
w' = listArray (0,m) $ x : elems w
m = snd $ bounds h
-- Type 1: symmetric FIR, even order / odd length
{-# specialize fir'1 :: Array Int Float -> Array Int Float -> [Float] -> [Float] #-}
{-# specialize fir'1 :: Array Int Double -> Array Int Double -> [Double] -> [Double] #-}
fir'1 :: Num a => Array Int a -> Array Int a -> [a] -> [a]
fir'1 h w [] = y : []
where y = h!m2 * w!m2 + sum [ h!i * (w!i + w!(m-i)) | i <- [0..m2-1] ]
m = snd $ bounds h
m2 = m `div` 2
fir'1 h w (x:xs) = y : fir'1 h w' xs
where y = h!m2 * w!m2 + sum [ h!i * (w!i + w!(m-i)) | i <- [0..m2-1] ]
w' = listArray (0,m) $ x : elems w
m = snd $ bounds h
m2 = m `div` 2
-- Type 2: symmetric FIR, odd order / even length
{-# specialize fir'2 :: Array Int Float -> Array Int Float -> [Float] -> [Float] #-}
{-# specialize fir'2 :: Array Int Double -> Array Int Double -> [Double] -> [Double] #-}
fir'2 :: Num a => Array Int a -> Array Int a -> [a] -> [a]
fir'2 h w [] = y : []
where y = sum [ h!i * (w!i + w!(m-i)) | i <- [0..m2] ]
m = snd $ bounds h
m2 = m `div` 2
fir'2 h w (x:xs) = y : fir'2 h w' xs
where y = sum [ h!i * (w!i + w!(m-i)) | i <- [0..m2] ]
w' = listArray (0,m) $ x : elems w
m = snd $ bounds h
m2 = m `div` 2
-- Type 3: anti-symmetric FIR, even order / odd length
{-# specialize fir'3 :: Array Int Float -> Array Int Float -> [Float] -> [Float] #-}
{-# specialize fir'3 :: Array Int Double -> Array Int Double -> [Double] -> [Double] #-}
fir'3 :: Num a => Array Int a -> Array Int a -> [a] -> [a]
fir'3 h w [] = y : []
where y = h!m2 * w!m2 + sum [ h!i * (w!i - w!(m-i)) | i <- [0..m2-1] ]
m = snd $ bounds h
m2 = m `div` 2
fir'3 h w (x:xs) = y : fir'3 h w' xs
where y = h!m2 * w!m2 + sum [ h!i * (w!i - w!(m-i)) | i <- [0..m2-1] ]
w' = listArray (0,m) $ x : elems w
m = snd $ bounds h
m2 = m `div` 2
-- Type 4: anti-symmetric FIR, off order / even length
{-# specialize fir'4 :: Array Int Float -> Array Int Float -> [Float] -> [Float] #-}
{-# specialize fir'4 :: Array Int Double -> Array Int Double -> [Double] -> [Double] #-}
fir'4 :: Num a => Array Int a -> Array Int a -> [a] -> [a]
fir'4 h w [] = y : []
where y = sum [ h!i * (w!i - w!(m-i)) | i <- [0..m2] ]
m = snd $ bounds h
m2 = m `div` 2
fir'4 h w (x:xs) = y : fir'4 h w' xs
where y = sum [ h!i * (w!i - w!(m-i)) | i <- [0..m2] ]
w' = listArray (0,m) $ x : elems w
m = snd $ bounds h
m2 = m `div` 2
-- Aux functions. Note that the tap numbers go from [0..m], so if m is
-- even, then the filter has odd length, and vice versa.
{-# specialize isFIRType1 :: Array Int Float -> Bool #-}
{-# specialize isFIRType1 :: Array Int Double -> Bool #-}
isFIRType1 :: (Num a, Eq a) => Array Int a -> Bool
isFIRType1 h = even m && (h' == (reverse h'))
where m = snd $ bounds h
h' = elems h
{-# specialize isFIRType2 :: Array Int Float -> Bool #-}
{-# specialize isFIRType2 :: Array Int Double -> Bool #-}
isFIRType2 :: (Num a, Eq a) => Array Int a -> Bool
isFIRType2 h = odd m && (h' == (reverse h'))
where m = snd $ bounds h
h' = elems h
{-# specialize isFIRType3 :: Array Int Float -> Bool #-}
{-# specialize isFIRType3 :: Array Int Double -> Bool #-}
isFIRType3 :: (Num a, Eq a) => Array Int a -> Bool
isFIRType3 h = even m && ha == reverse hb
where m = snd $ bounds h
h' = elems h
ha = take n h'
hb = map negate (drop (n+1) h')
n = m `div` 2
{-# specialize isFIRType4 :: Array Int Float -> Bool #-}
{-# specialize isFIRType4 :: Array Int Double -> Bool #-}
isFIRType4 :: (Num a, Eq a) => Array Int a -> Bool
isFIRType4 h = odd m && ha == reverse hb
where m = snd $ bounds h
ha = elems h
hb = fmap negate $ ha
-- Test routines
-- This tests out fir'0
ht :: Array Int Double
ht = listArray (0,4) [ 1, 2, 0, -1, 1 ]
xt :: [Double]
xt = [1, 3, -1, -2, 0, 0, 0, 0 ]
yt :: [Double]
yt = [1, 5, 5, -5, -6, 4, 1, -2]
yt' :: [Double]
yt' = fir ht xt
-- This checks the symmetric routines against fir'0
h1 :: Array Int Double
h1 = listArray (0,4) [ 1, 2, 3, 2, 1 ]
h2 :: Array Int Double
h2 = listArray (0,5) [ 1, 2, 3, 3, 2, 1 ]
h3 :: Array Int Double
h3 = listArray (0,4) [ 1, 2, 3, -2, -1 ]
h4 :: Array Int Double
h4 = listArray (0,5) [ 1, 2, 3, -3, -2, -1 ]
y1, y2, y3, y4 :: [Double]
y1 = fir0 h1 xt
y2 = fir0 h2 xt
y3 = fir0 h3 xt
y4 = fir0 h4 xt
y1', y2', y3', y4' :: [Double]
y1' = fir h1 xt
y2' = fir h2 xt
y3' = fir h3 xt
y4' = fir h4 xt
-- If everything works, then test == True
test :: Bool
test = and [ yt == yt', y1 == y1', y2 == y2', y3 == y3', y4 == y4' ]