{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
module SDR.FFT (
hamming,
hanning,
blackman,
fftw',
fftw,
fftwReal',
fftwReal,
fftwParallel
) where
import Control.Monad as CM
import Foreign.Storable
import Foreign.Storable.Complex
import Foreign.C.Types
import Data.Complex
import Foreign.ForeignPtr
import Control.Concurrent hiding (yield)
import qualified Data.Map as Map
import Data.Coerce
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Storable.Mutable as VSM
import Pipes
import Numeric.FFTW
import SDR.FilterDesign
import SDR.VectorUtils
mallocForeignBufferAligned :: forall a. Storable a => Int -> IO (ForeignPtr a)
mallocForeignBufferAligned elems = do
ptr <- fftwMalloc $ fromIntegral $ elems * sizeOf (undefined :: a)
newForeignPtr fftwFreePtr ptr
fftw' :: (VG.Vector v (Complex Double))
=> Int
-> IO (v (Complex Double) -> IO (VS.Vector (Complex Double)))
fftw' samples = do
ina <- mallocForeignBufferAligned samples
out <- mallocForeignBufferAligned samples
plan <- withForeignPtr ina $ \ip ->
withForeignPtr out $ \op ->
planDFT1d samples ip op Forward fftwEstimate
return $ \inv' -> do
out <- mallocForeignBufferAligned samples
ina <- mallocForeignBufferAligned samples
let inv = VSM.unsafeFromForeignPtr0 ina samples
copyInto inv inv'
let (fp, offset, length) = VSM.unsafeToForeignPtr inv
withForeignPtr (coerce fp) $ \fpp ->
withForeignPtr (coerce out) $ \op ->
executeDFT plan fpp op
return $ VS.unsafeFromForeignPtr0 out samples
fftw :: (VG.Vector v (Complex Double))
=> Int
-> IO (Pipe (v (Complex Double)) (VS.Vector (Complex Double)) IO ())
fftw samples = do
func <- fftw' samples
return $ for cat $ \dat -> lift (func dat) >>= yield
fftwReal' :: (VG.Vector v Double)
=> Int
-> IO (v Double -> IO (VS.Vector (Complex Double)))
fftwReal' samples = do
ina <- mallocForeignBufferAligned samples
out <- mallocForeignBufferAligned samples
plan <- withForeignPtr ina $ \ip ->
withForeignPtr out $ \op ->
planDFTR2C1d samples ip op fftwEstimate
return $ \inv' -> do
out <- mallocForeignBufferAligned ((samples `quot` 2) + 1)
ina <- mallocForeignBufferAligned samples
let inv = VSM.unsafeFromForeignPtr0 ina samples
copyInto inv inv'
let (fp, offset, length) = VSM.unsafeToForeignPtr inv
withForeignPtr (coerce fp) $ \fpp ->
withForeignPtr (coerce out) $ \op ->
executeDFTR2C plan fpp op
return $ VS.unsafeFromForeignPtr0 out samples
fftwReal :: (VG.Vector v Double)
=> Int
-> IO (Pipe (v Double) (VS.Vector (Complex Double)) IO ())
fftwReal samples = do
func <- fftwReal' samples
return $ for cat $ \dat -> lift (func dat) >>= yield
fftwParallel :: (VG.Vector v (Complex Double))
=> Int
-> Int
-> IO (Pipe (v (Complex Double)) (VS.Vector (Complex Double)) IO ())
fftwParallel threads samples = do
ina <- mallocForeignBufferAligned samples
out <- mallocForeignBufferAligned samples
plan <- withForeignPtr ina $ \ip ->
withForeignPtr out $ \op ->
planDFT1d samples ip op Forward fftwEstimate
inChan <- newChan
outMap <- newMVar Map.empty
CM.replicateM threads $ forkIO $ forever $ do
(idx, res) <- readChan inChan
out <- mallocForeignBufferAligned samples
ina <- mallocForeignBufferAligned samples
let inv = VSM.unsafeFromForeignPtr0 ina samples
copyInto inv res
let (fp, offset, length) = VSM.unsafeToForeignPtr inv
withForeignPtr (coerce fp) $ \fpp ->
withForeignPtr (coerce out) $ \op ->
executeDFT plan fpp op
theMap <- takeMVar outMap
putMVar outMap $ Map.insert idx (VS.unsafeFromForeignPtr0 out samples) theMap
let pipe nextIn nextOut = do
dat <- await
lift $ writeChan inChan (nextIn, dat)
theMap <- lift $ takeMVar outMap
case Map.lookup nextOut theMap of
Nothing -> do
lift $ putMVar outMap theMap
pipe (nextIn + 1) nextOut
Just dat -> do
lift $ putMVar outMap $ Map.delete nextOut theMap
yield dat
pipe (nextIn + 1) (nextOut + 1)
return $ pipe 0 0