sdr 0.1.0.4 → 0.1.0.5
raw patch · 8 files changed
+169/−12 lines, 8 filesdep ~sdr
Dependency ranges changed: sdr
Files
- benchmarks/Benchmarks.hs +18/−0
- c_sources/convert.c +36/−0
- hs_sources/SDR/FilterDesign.hs +17/−0
- hs_sources/SDR/PipeUtils.hs +10/−0
- hs_sources/SDR/Util.hs +40/−0
- hs_sources/SDR/VectorUtils.hs +17/−0
- sdr.cabal +3/−3
- tests/TestSuite.hs +28/−9
benchmarks/Benchmarks.hs view
@@ -43,6 +43,10 @@ inBufComplex = VG.fromList $ take size $ do i <- [0..] return $ i :+ i+ inBufRTLSDR :: VS.Vector CUChar+ inBufRTLSDR = VG.fromList $ take size [0 ..]+ inBufBladeRF :: VS.Vector CShort+ inBufBladeRF = VG.fromList $ take size [0 ..] numConv = 16386 inBufConv :: VS.Vector CUChar@@ -127,6 +131,20 @@ (const True, bench "c" $ nfIO $ scaleC 0.3 inBuf outBuf), (hasSSE42, bench "cSSE" $ nfIO $ scaleCSSE 0.3 inBuf outBuf), (hasAVX, bench "cAVX" $ nfIO $ scaleCAVX 0.3 inBuf outBuf)+ ],+ bgroup "conversion" [+ bgroup "RTLSDR" $ hasFeatures [+ (const True, bench "h" $ nf (interleavedIQUnsigned256ToFloat :: VS.Vector CUChar -> VS.Vector (Complex Float)) inBufRTLSDR),+ (const True, bench "c" $ nf interleavedIQUnsignedByteToFloat inBufRTLSDR),+ (hasSSE42, bench "cSSE" $ nf interleavedIQUnsignedByteToFloatSSE inBufRTLSDR),+ (hasAVX2, bench "cAVX" $ nf interleavedIQUnsignedByteToFloatAVX inBufRTLSDR)+ ],+ bgroup "BladeRF" $ hasFeatures [+ (const True, bench "h" $ nf (interleavedIQSigned2048ToFloat :: VS.Vector CShort -> VS.Vector (Complex Float)) inBufBladeRF),+ (const True, bench "c" $ nf interleavedIQSignedWordToFloat inBufBladeRF),+ (hasSSE42, bench "cSSE" $ nf interleavedIQSignedWordToFloatSSE inBufBladeRF),+ (hasAVX2, bench "cAVX" $ nf interleavedIQSignedWordToFloatAVX inBufBladeRF)+ ] ] ]
c_sources/convert.c view
@@ -48,3 +48,39 @@ _mm256_storeu_ps(out + i, res); } }++void convertCBladeRF(int num, int16_t *in, float *out){+ int i;+ for(i=0; i<num; i++){+ out[i] = (float)in[i] * (1/2048.0);+ }+}++void convertCSSEBladeRF(int num, int16_t *in, float *out){+ int i;+ __m128 mul = _mm_set1_ps(1/2048.0);+ for(i=0; i<num; i+=4){+ __m128i val = _mm_loadu_si128((__m128i *)(in + i));+ __m128i ints = _mm_cvtepi16_epi32(val);+ __m128 cvtd = _mm_cvtepi32_ps(ints);++ __m128 res = _mm_mul_ps(cvtd, mul);++ _mm_storeu_ps(out + i, res);+ }+}++void convertCAVXBladeRF(int num, int16_t *in, float *out){+ int i;+ __m256 mul = _mm256_set1_ps(1/2048.0);+ for(i=0; i<num; i+=8){+ __m128i val = _mm_loadu_si128((__m128i *)(in + i));+ __m256i ints = _mm256_cvtepi16_epi32(val);+ __m256 cvtd = _mm256_cvtepi32_ps(ints);++ __m256 res = _mm256_mul_ps(cvtd, mul);++ _mm256_storeu_ps(out + i, res);+ }+}+
hs_sources/SDR/FilterDesign.hs view
@@ -4,6 +4,9 @@ -- * Sinc Function sinc, + -- * Root raised cosine+ srrc,+ -- * Windows hanning, hamming,@@ -74,3 +77,17 @@ plotFrequency coeffs = toFile def "frequency_response.png" $ do layout_title .= "Frequency Response" plot (line "Frequency Response" [signal coeffs $ takeWhile (< pi) $ iterate (+ 0.01) 0])++--ts is really the ratio ts / sampling period+-- | Square root raised cosine+srrc :: (Ord a, Floating a) => Int -> Int -> a -> [a]+srrc n ts beta = map func [(-n) .. n]+ where+ func x + | x == 0 = 1 - beta + 4 * beta / pi+ | abs (fromIntegral x) ~= (fromIntegral ts / (4 * beta)) = (beta / sqrt 2) * ((1 + 2/pi) * sin (pi / (4 * beta)) + (1 - 2/pi) * cos (pi / (4 * beta)))+ | otherwise = (sin (pi * xdivts * (1 - beta)) + 4 * beta * xdivts * cos (pi * xdivts * (1 + beta))) / (pi * xdivts * (1 - (4 * beta * xdivts) ** 2))+ where+ xdivts = fromIntegral x / fromIntegral ts+ x ~= y = abs (x - y) < 0.001+
hs_sources/SDR/PipeUtils.hs view
@@ -5,6 +5,7 @@ printStream, devnull, rate,+ pMapAccum ) where import Data.Time.Clock@@ -52,4 +53,13 @@ yield res rate' (buffers + 1) rate' 1++pMapAccum :: (Monad m) => (acc -> x -> (acc, y)) -> acc -> Pipe x y m () +pMapAccum func acc = go acc+ where+ go acc = do+ dat <- await+ let (acc', res) = func acc dat+ yield res+ go acc'
hs_sources/SDR/Util.hs view
@@ -12,7 +12,11 @@ interleavedIQUnsignedByteToFloatSSE, interleavedIQUnsignedByteToFloatAVX, interleavedIQUnsignedByteToFloatFast,+ interleavedIQSigned2048ToFloat,+ interleavedIQSignedWordToFloat,+ interleavedIQSignedWordToFloatSSE,+ interleavedIQSignedWordToFloatAVX, -- * Scaling scaleC,@@ -108,6 +112,42 @@ convert idx = convert' (input `VG.unsafeIndex` (2 * idx)) :+ convert' (input `VG.unsafeIndex` (2 * idx + 1)) {-# INLINE convert' #-} convert' val = fromIntegral val / 2048++foreign import ccall unsafe "convertCBladeRF"+ convertCBladeRF_c :: CInt -> Ptr CShort -> Ptr CFloat -> IO ()++-- | Same as `interleavedIQUnsigned256ToFloat` but written in C and specialized for unsigned byte inputs and Float outputs.+interleavedIQSignedWordToFloat :: VS.Vector CShort -> VS.Vector (Complex Float)+interleavedIQSignedWordToFloat inBuf = unsafePerformIO $ do+ outBuf <- VGM.new $ VG.length inBuf `quot` 2+ VS.unsafeWith inBuf $ \iPtr -> + VSM.unsafeWith (unsafeCoerce outBuf) $ \oPtr -> + convertCBladeRF_c (fromIntegral $ VG.length inBuf) iPtr oPtr+ VG.freeze outBuf++foreign import ccall unsafe "convertCSSEBladeRF"+ convertCSSEBladeRF_c :: CInt -> Ptr CShort -> Ptr CFloat -> IO ()++-- | Same as `interleavedIQUnsigned256ToFloat` but written in C using SSE intrinsics and specialized for unsigned byte inputs and Float outputs.+interleavedIQSignedWordToFloatSSE :: VS.Vector CShort -> VS.Vector (Complex Float)+interleavedIQSignedWordToFloatSSE inBuf = unsafePerformIO $ do+ outBuf <- VGM.new $ VG.length inBuf `quot` 2+ VS.unsafeWith inBuf $ \iPtr -> + VSM.unsafeWith (unsafeCoerce outBuf) $ \oPtr -> + convertCSSEBladeRF_c (fromIntegral $ VG.length inBuf) iPtr oPtr+ VG.freeze outBuf++foreign import ccall unsafe "convertCAVXBladeRF"+ convertCAVXBladeRF_c :: CInt -> Ptr CShort -> Ptr CFloat -> IO ()++-- | Same as `interleavedIQUnsigned256ToFloat` but written in C using AVX intrinsics and specialized for unsigned byte inputs and Float outputs.+interleavedIQSignedWordToFloatAVX :: VS.Vector CShort -> VS.Vector (Complex Float)+interleavedIQSignedWordToFloatAVX inBuf = unsafePerformIO $ do+ outBuf <- VGM.new $ VG.length inBuf `quot` 2+ VS.unsafeWith inBuf $ \iPtr -> + VSM.unsafeWith (unsafeCoerce outBuf) $ \oPtr -> + convertCAVXBladeRF_c (fromIntegral $ VG.length inBuf) iPtr oPtr+ VG.freeze outBuf -- | Scaling foreign import ccall unsafe "scale"
hs_sources/SDR/VectorUtils.hs view
@@ -5,10 +5,12 @@ mapAccumMV, stride, fill,+ vUnfoldr ) where import Control.Monad import Control.Monad.Primitive+import Control.Monad.ST import Data.Vector.Generic as VG hiding ((++)) import qualified Data.Vector.Generic.Mutable as VGM@@ -62,3 +64,18 @@ VGM.unsafeWrite outBuf i x return $ i + 1 +vUnfoldr :: VG.Vector v x => Int -> (acc -> (x, acc)) -> acc -> (v x, acc)+vUnfoldr size func acc = runST $ do+ vect <- VGM.new size+ acc' <- go vect 0 acc+ vect' <- VG.unsafeFreeze vect+ return (vect', acc')+ where+ go vect offset acc = go' offset acc+ where+ go' offset acc + | offset == size = return acc+ | otherwise = do+ let (res, acc') = func acc+ VGM.write vect offset res+ go' (offset + 1) acc'
sdr.cabal view
@@ -1,5 +1,5 @@ name: sdr-version: 0.1.0.4+version: 0.1.0.5 synopsis: A software defined radio library description: Write software defined radio applications in Haskell.@@ -116,7 +116,7 @@ base >=4.6 && <4.9, QuickCheck >=2.8 && <2.9, vector >=0.10 && <0.11, - sdr ==0.1.0.2, + sdr ==0.1.0.5, primitive >=0.5 && <0.7, storable-complex >=0.2 && <0.3, test-framework >=0.8 && <0.9,@@ -132,7 +132,7 @@ base >=4.6 && <4.9, criterion >=1.0 && <1.2, vector >=0.10 && <0.11, - sdr ==0.1.0.2, + sdr ==0.1.0.5, primitive >=0.5 && <0.7, storable-complex >=0.2 && <0.3 hs-source-dirs: benchmarks
tests/TestSuite.hs view
@@ -41,7 +41,10 @@ testGroup "resamplers" [ testProperty "real" propResamplingReal ],- testProperty "conversion" propConversion,+ testGroup "conversion" [+ testProperty "rtlsdr" propConversionRTLSDR,+ testProperty "BladeRF" propConversionBladeRF+ ], testProperty "scaling" propScaleReal ] where@@ -191,19 +194,35 @@ ] assert res - propConversion = + propConversionRTLSDR = forAll sizes $ \size -> forAll (vectorOf (2 * size) (choose (-10, 10))) $ \inBuf -> - testConversion size inBuf- testConversion :: Int -> [Int] -> Property- testConversion size inBuf = monadicIO $ do+ 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, (makeComplexBufferVect vInput :: VS.Vector (Complex Float))),- (const True, convertC vInput),- (hasSSE42, convertCSSE vInput),- (hasAVX2, convertCAVX vInput)+ (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