{-# LANGUAGE ScopedTypeVariables #-} import Control.Monad.Primitive import Data.Complex import Control.Monad import qualified Data.Vector.Generic as VG import qualified Data.Vector.Generic.Mutable as VGM import qualified Data.Vector.Storable as VS import qualified Data.Vector.Storable.Mutable as VSM import Test.QuickCheck import Test.QuickCheck.Monadic import Test.Framework (defaultMain, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) import SDR.FilterInternal import SDR.Util import SDR.CPUID sameResultM :: Monad m => (a -> a -> Bool) -> [m a] -> m Bool sameResultM _ [] = return True sameResultM eq (x:xs) = do res <- x ress <- sequence xs return $ all (eq res) ress sameResult :: (a -> a -> Bool) -> [a] -> Bool sameResult _ [] = True sameResult eq (x:xs) = all (eq x) xs tests info = [ testGroup "filters" [ testProperty "real" propFiltersReal, testProperty "complex" propFiltersComplex ], testGroup "decimators" [ testProperty "real" propDecimationReal, testProperty "complex" propDecimationComplex ], testGroup "resamplers" [ testProperty "real" propResamplingReal, testProperty "complex" propResamplingComplex ], testGroup "conversion" [ testProperty "rtlsdr" propConversionRTLSDR, testProperty "BladeRF" propConversionBladeRF ], testProperty "scaling" propScaleReal ] where hasFeatures :: [(CPUInfo -> Bool, a)] -> [a] hasFeatures = map snd . filter (($ info) . fst) sizes = elements [1024, 2048, 4096, 8192, 16384, 32768, 65536] numCoeffs = elements [32, 64, 128, 256, 512] factors' = [1, 2, 3, 5, 7, 11, 13, 17, 23] factors = elements factors' propFiltersReal = forAll sizes $ \size -> forAll (vectorOf size (choose (-10, 10))) $ \inBuf -> forAll numCoeffs $ \numCoeffs -> forAll (vectorOf numCoeffs (choose (-10, 10))) $ \coeffs -> testFiltersReal size numCoeffs coeffs inBuf testFiltersReal :: Int -> Int -> [Float] -> [Float] -> Property testFiltersReal size numCoeffs coeffs inBuf = monadicIO $ do let vCoeffsHalf = VS.fromList coeffs vCoeffs = VS.fromList $ coeffs ++ reverse coeffs vInput = VS.fromList inBuf num = size - numCoeffs*2 + 1 res <- run $ sameResultM eqDelta $ map (getResult num) $ hasFeatures [ (const True, filterHighLevel vCoeffs num vInput), (const True, filterImperative1 vCoeffs num vInput), (const True, filterImperative2 vCoeffs num vInput), (const True, filterCRR vCoeffs num vInput), (hasSSE42, filterCSSERR vCoeffs num vInput), (hasAVX, filterCAVXRR vCoeffs num vInput), (hasSSE42, filterCSSESymmetricRR vCoeffsHalf num vInput), (hasAVX, filterCAVXSymmetricRR vCoeffsHalf num vInput) ] assert res propFiltersComplex = forAll sizes $ \size -> forAll (vectorOf size (choose (-10, 10))) $ \inBufR -> forAll (vectorOf size (choose (-10, 10))) $ \inBufI -> forAll numCoeffs $ \numCoeffs -> forAll (vectorOf numCoeffs (choose (-10, 10))) $ \coeffs -> testFiltersComplex size numCoeffs coeffs $ zipWith (:+) inBufR inBufI testFiltersComplex :: Int -> Int -> [Float] -> [Complex Float] -> Property testFiltersComplex size numCoeffs coeffs inBuf = monadicIO $ do let vCoeffsHalf = VS.fromList coeffs vCoeffs = VS.fromList $ coeffs ++ reverse coeffs vInput = VS.fromList inBuf num = size - numCoeffs*2 + 1 vCoeffs2 = VG.fromList $ duplicate $ coeffs ++ reverse coeffs res <- run $ sameResultM eqDeltaC $ map (getResult num) $ hasFeatures [ (const True, filterHighLevel vCoeffs num vInput), (const True, filterCRC vCoeffs num vInput), (hasSSE42, filterCSSERC vCoeffs2 num vInput), (hasSSE42, filterCSSERC2 vCoeffs num vInput), (hasAVX, filterCAVXRC vCoeffs2 num vInput), (hasSSE42, filterCSSESymmetricRC vCoeffsHalf num vInput), (hasAVX, filterCAVXSymmetricRC vCoeffsHalf num vInput) ] assert res propDecimationReal = forAll sizes $ \size -> forAll (vectorOf size (choose (-10, 10))) $ \inBuf -> forAll numCoeffs $ \numCoeffs -> forAll (vectorOf numCoeffs (choose (-10, 10))) $ \coeffs -> forAll factors $ \factor -> testDecimationReal size numCoeffs factor coeffs inBuf testDecimationReal :: Int -> Int -> Int -> [Float] -> [Float] -> Property testDecimationReal size numCoeffs factor coeffs inBuf = monadicIO $ do let vCoeffsHalf = VS.fromList coeffs vCoeffs = VS.fromList $ coeffs ++ reverse coeffs vInput = VS.fromList inBuf num = (size - numCoeffs*2 + 1) `quot` factor res <- run $ sameResultM eqDelta $ map (getResult num) $ hasFeatures [ (const True, decimateHighLevel factor vCoeffs num vInput), (const True, decimateCRR factor vCoeffs num vInput), (hasSSE42, decimateCSSERR factor vCoeffs num vInput), (hasAVX, decimateCAVXRR factor vCoeffs num vInput), (hasSSE42, decimateCSSESymmetricRR factor vCoeffsHalf num vInput), (hasAVX, decimateCAVXSymmetricRR factor vCoeffsHalf num vInput) ] assert res propDecimationComplex = forAll sizes $ \size -> forAll (vectorOf size (choose (-10, 10))) $ \inBufR -> forAll (vectorOf size (choose (-10, 10))) $ \inBufI -> forAll numCoeffs $ \numCoeffs -> forAll (vectorOf numCoeffs (choose (-10, 10))) $ \coeffs -> forAll factors $ \factor -> testDecimationComplex size numCoeffs factor coeffs $ zipWith (:+) inBufR inBufI testDecimationComplex :: Int -> Int -> Int -> [Float] -> [Complex Float] -> Property testDecimationComplex size numCoeffs factor coeffs inBuf = monadicIO $ do let vCoeffsHalf = VS.fromList coeffs vCoeffs = VS.fromList $ coeffs ++ reverse coeffs vInput = VS.fromList inBuf num = (size - numCoeffs*2 + 1) `quot` factor vCoeffs2 = VG.fromList $ duplicate $ coeffs ++ reverse coeffs res <- run $ sameResultM eqDeltaC $ map (getResult num) $ hasFeatures [ (const True, decimateHighLevel factor vCoeffs num vInput), (const True, decimateCRC factor vCoeffs num vInput), (hasSSE42, decimateCSSERC factor vCoeffs2 num vInput), (hasSSE42, decimateCSSERC2 factor vCoeffs num vInput), (hasAVX, decimateCAVXRC factor vCoeffs2 num vInput), (hasSSE42, decimateCSSESymmetricRC factor vCoeffsHalf num vInput), (hasAVX, decimateCAVXRC2 factor vCoeffs num vInput), (hasAVX, decimateCAVXSymmetricRC factor vCoeffsHalf num vInput) ] assert res propResamplingReal = forAll sizes $ \size -> forAll (vectorOf size (choose (-10, 10))) $ \inBuf -> forAll (elements [32 .. 512]) $ \numCoeffs -> forAll (vectorOf numCoeffs (choose (-10, 10))) $ \coeffs -> forAll (elements $ tail factors') $ \decimation -> forAll (elements $ filter (< decimation) factors') $ \interpolation -> forAll (elements [0..interpolation - 1]) $ \group -> testResamplingReal size group numCoeffs interpolation decimation coeffs inBuf testResamplingReal :: Int -> Int -> Int -> Int -> Int -> [Float] -> [Float] -> Property testResamplingReal size group numCoeffs interpolation decimation coeffs inBuf = monadicIO $ do let vCoeffsHalf = VS.fromList coeffs vCoeffs = VS.fromList $ coeffs ++ reverse coeffs vInput = VS.fromList inBuf num = (size - numCoeffs*2 + 1) `quot` decimation offset = interpolation - 1 - ((interpolation + group * decimation - 1) `mod` interpolation) resampler3 <- run $ resampleCRR2 interpolation decimation (coeffs ++ reverse coeffs) resampler4 <- run $ resampleCSSERR interpolation decimation (coeffs ++ reverse coeffs) resampler5 <- run $ resampleCAVXRR interpolation decimation (coeffs ++ reverse coeffs) res <- run $ sameResultM eqDelta $ map (getResult num) $ hasFeatures [ (const True, void . resampleHighLevel interpolation decimation vCoeffs offset num vInput), (const True, void . resampleCRR num interpolation decimation offset vCoeffs vInput), (const True, void . resampler3 group num vInput), (hasSSE42, void . resampler4 group num vInput), (hasAVX, void . resampler5 group num vInput) ] assert res propResamplingComplex = forAll sizes $ \size -> forAll (vectorOf size (choose (-10, 10))) $ \inBufR -> forAll (vectorOf size (choose (-10, 10))) $ \inBufI -> forAll (elements [32 .. 512]) $ \numCoeffs -> forAll (vectorOf numCoeffs (choose (-10, 10))) $ \coeffs -> forAll (elements $ tail factors') $ \decimation -> forAll (elements $ filter (< decimation) factors') $ \interpolation -> forAll (elements [0..interpolation - 1]) $ \group -> testResamplingComplex size group numCoeffs interpolation decimation coeffs $ zipWith (:+) inBufR inBufI testResamplingComplex :: Int -> Int -> Int -> Int -> Int -> [Float] -> [Complex Float] -> Property testResamplingComplex size group numCoeffs interpolation decimation coeffs inBuf = monadicIO $ do let vCoeffsHalf = VS.fromList coeffs vCoeffs = VS.fromList $ coeffs ++ reverse coeffs vInput = VS.fromList inBuf num = (size - numCoeffs*2 + 1) `quot` decimation offset = interpolation - 1 - ((interpolation + group * decimation - 1) `mod` interpolation) resampler3 <- run $ resampleCRC interpolation decimation (coeffs ++ reverse coeffs) resampler4 <- run $ resampleCSSERC interpolation decimation (coeffs ++ reverse coeffs) resampler5 <- run $ resampleCAVXRC interpolation decimation (coeffs ++ reverse coeffs) res <- run $ sameResultM eqDeltaC $ map (getResult num) $ hasFeatures [ (const True, void . resampleHighLevel interpolation decimation vCoeffs offset num vInput), (const True, void . resampler3 group num vInput), (hasSSE42, void . resampler4 group num vInput), (hasAVX, void . resampler5 group num vInput) ] assert res propConversionRTLSDR = forAll sizes $ \size -> forAll (vectorOf (2 * size) (choose (-10, 10))) $ \inBuf -> testConversionRTLSDR size inBuf testConversionRTLSDR :: Int -> [Int] -> Property testConversionRTLSDR size inBuf = monadicIO $ do let vInput = VS.fromList $ map fromIntegral inBuf let res = sameResult eqDeltaC $ map VG.toList $ hasFeatures [ (const True, (interleavedIQUnsigned256ToFloat vInput :: VS.Vector (Complex Float))), (const True, interleavedIQUnsignedByteToFloat vInput), (hasSSE42, interleavedIQUnsignedByteToFloatSSE vInput), (hasAVX2, interleavedIQUnsignedByteToFloatAVX vInput) ] assert res propConversionBladeRF = forAll sizes $ \size -> forAll (vectorOf (2 * size) (choose (-10, 10))) $ \inBuf -> testConversionBladeRF size inBuf testConversionBladeRF :: Int -> [Int] -> Property testConversionBladeRF size inBuf = monadicIO $ do let vInput = VS.fromList $ map fromIntegral inBuf let res = sameResult eqDeltaC $ map VG.toList $ hasFeatures [ (const True, (interleavedIQSigned2048ToFloat vInput :: VS.Vector (Complex Float))), (const True, interleavedIQSignedWordToFloat vInput), (hasSSE42, interleavedIQSignedWordToFloatSSE vInput), (hasAVX2, interleavedIQSignedWordToFloatAVX vInput) ] assert res scales = elements [0.1, 0.5, 1, 2, 10] propScaleReal = forAll sizes $ \size -> forAll (vectorOf size (choose (-10, 10))) $ \inBuf -> forAll scales $ \factor -> testScaleReal size inBuf factor testScaleReal :: Int -> [Float] -> Float -> Property testScaleReal size inBuf factor = monadicIO $ do let vInput = VS.fromList inBuf res <- run $ sameResultM eqDelta $ map (getResult size) $ hasFeatures [ (const True, scaleC factor vInput), (hasSSE42, scaleCSSE factor vInput), (hasAVX, scaleCAVX factor vInput) ] assert res getResult :: (VSM.Storable a) => Int -> (VS.MVector RealWorld a -> IO b) -> IO [a] getResult size func = do outBuf <- VGM.new size func outBuf out :: VS.Vector a <- VG.freeze outBuf return $ VG.toList out eqDelta x y = all (uncurry eqDelta') $ zip x y where eqDelta' x y = abs (x - y) < 0.01 eqDeltaC x y = all (uncurry eqDelta') $ zip x y where eqDelta' x y = magnitude (x - y) < 0.01 duplicate :: [a] -> [a] duplicate = concatMap func where func x = [x, x] main = do info <- getCPUInfo defaultMain $ tests info