-----------------------------------------------------------------------------
-- |
-- Module      :  DSP.Filter.IIR.Prony
-- Copyright   :  (c) Matthew Donadio 2003
-- License     :  GPL
--
-- Maintainer  :  m.p.donadio@ieee.org
-- Stability   :  experimental
-- Portability :  portable
--
-- General case of Prony's Method where K > p+q
--
-- References: L&I, Sect 8.1; P&B, Sect 7.5; P&M, Sect 8.5.2
--
-- Notation follows L&I
--
-----------------------------------------------------------------------------

-- TODO: Handle rank deficiencies of G3 gracefully.  Can/should we
-- generate a (K/2+1) by (K/2+1) G2, and set p=q=rank(G2)?  Need SVD to
-- compute rank, though.

module DSP.Filter.IIR.Prony (prony) where

import Data.Array

import Matrix.Matrix
import Matrix.LU

{------------------------------------------------------------------------------

Case 1: K=p+q

a = array (0,p)
b = array (0,q)

g1 : q+1 by p+1
g2 : p   by p+1
g3 : p   by p

We do not define G1 and G2, but

mg2 = array ((1,1),(p,p+1)) [ ((i,j), g!(p+i+1-j)) | j <- [1..p+1], i <- [1..p] ]

prony p q g = (a,b)
    where mg3 = array ((1,1),(p,p)) [ ((i,j), g!(p+i-j)) | j <- [1..p], i <- [1..p] ]
          g1  = array (1,p) [ (i, g!(p+i)) | i <- [1..p] ]
          a'  = solve mg3 (fmap negate g1)
          a   = array (0,p) $ (0,1) : [ (i,a'!i) | i <- [1..p] ]
          b   = listArray (0,q) [ sum [ a!j * g!(i-j) | j <- [0..(min i p)] ] | i <- [0..q] ]

Test case, pg 422

g = listArray (0,6) [ 1, 18, 9, 2, 1, 2/9, 1/9 ] :: Array Int Double

------------------------------------------------------------------------------}

-- Case 2: K>p+q

-- a = array (0,p)
-- b = array (0,q)

-- g1 : q+1 by p+1
-- g2 : K-q by p+1
-- g3 : K-q by p

-- We need gi for the q<p cases because these generate zero elements in
-- G3, and this is the easiest way to take care of that.

-- mg1 = array ((1,1),(q+1,p+1)) [ ((i,j), gi (i-j)) | j <- [1..p+1], i <- [1..q+1] ]
-- mg2 = array ((1,1),(k-q,p+1)) [ ((i,j), gi (q+i-j+1)) | j <- [1..p+1], i <- [1..k-q] ]

-- | Implementation of Prony's method

prony :: Int -- ^ p
      -> Int -- ^ q
      -> Array Int Double -- ^ g[n]
      -> (Array Int Double, Array Int Double) -- ^ (b,a)

prony p q g = (b,a)
    where k   = snd $ bounds g
          gi i | i < 0     = 0
               | i > k     = 0
            | otherwise = g!i
          mg3 = array ((1,1),(k-q,p)) [ ((i,j), gi (q+i-j)) | j <- [1..p], i <- [1..k-q] ]
          g1  = array (1,k-q) [ (i, gi (q+i)) | i <- [1..k-q] ]
          a'  = solve (mm_mult (m_trans mg3) mg3) (fmap negate (mv_mult (m_trans mg3) g1))
          a   = array (0,p) $ (0,1) : [ (i,a'!i) | i <- [1..p] ]
          b   = listArray (0,q) [ sum [ a!j * gi (i-j) | j <- [0..(min i p)] ] | i <- [0..q] ]