packages feed

sdr 0.1.0.3 → 0.1.0.4

raw patch · 4 files changed

+97/−47 lines, 4 files

Files

Readme.md view
@@ -15,10 +15,10 @@ * Line and waterfall plots using OpenGL * FM demodulation * PulseAudio sound sink-* [rtl-sdr](http://sdr.osmocom.org/trac/wiki/rtl-sdr) based radio source supported and other sources are easily added+* [rtl-sdr](http://sdr.osmocom.org/trac/wiki/rtl-sdr) and [BladeRF](https://nuand.com/) based radio sources/sinks supported and other sources are easily added * Extensive benchmark and test suites of signal processing functions -See https://github.com/adamwalker/sdr-apps for a collection of simple apps built on the library and https://github.com/adamwalker/sdr-demo for a demo application.+See [sdr-apps](https://github.com/adamwalker/sdr-apps) for a collection of simple apps built on the library, [sdr-demo](https://github.com/adamwalker/sdr-demo) for a demo application and [bladerf-sdr-apps](https://github.com/adamwalker/bladerf-sdr-apps) to get started with the BladeRF.  # Screenshot A chunk of the FM broadcast spectrum. Captured with an RTLSDR device and drawn as a waterfall using the [Plot](https://github.com/adamwalker/sdr/blob/master/hs_sources/SDR/Plot.hs) module.@@ -30,6 +30,8 @@  ## Installation +This library will only build and run on 64 bit x86 Linux systems.+ You can install it from [Hackage](https://hackage.haskell.org/package/sdr): ``` cabal install sdr@@ -45,6 +47,8 @@ cabal sandbox add-source dynamic-graph haskell-fftw-simple sdr cabal install sdr ```++If you want to use the BladeRF, you will also need [bladerf-pipes](https://github.com/adamwalker/bladerf-pipes) and [hlibBladeRF](https://github.com/victoredwardocallaghan/hlibBladeRF).   ## Example Applications 
hs_sources/SDR/RTLSDRStream.hs view
@@ -1,11 +1,18 @@+{-# LANGUAGE RecordWildCards #-}+ {-| Stream samples from a Realtek RTL2832U based device -} module SDR.RTLSDRStream (-    sdrStream+    RTLSDRParams(..),+    defaultRTLSDRParams,+    setRTLSDRParams,+    sdrStream,+    sdrStreamFromDevice     ) where  import           Control.Monad import           Control.Monad.Trans.Either import           Data.Word+import           Data.Int import           Foreign.ForeignPtr import           Foreign.C.Types import           Control.Concurrent         hiding (yield)@@ -16,14 +23,40 @@ import           Pipes.Concurrent  import           RTLSDR --- | Returns a producer that streams data from a Realtek RTL2832U based device. You probably want to use `makeComplexBufferVect` to turn it into a list of complex Floats.-sdrStream :: Word32                                                -- ^ Frequency-          -> Word32                                                -- ^ Sample rate+-- | RTLSDR configuration parameters+data RTLSDRParams = RTLSDRParams {+    centerFreq     :: Word32,+    sampleRate     :: Word32,+    freqCorrection :: Int32,+    tunerGain      :: Maybe Int32+}++-- | Some reasonable default parameters+defaultRTLSDRParams :: Word32       -- ^ Frequency+                    -> Word32       -- ^ Sample rate+                    -> RTLSDRParams +defaultRTLSDRParams freq sampleRate = RTLSDRParams freq sampleRate 0 Nothing ++-- | Set the configuration parameters for a device+setRTLSDRParams :: RTLSDR       -- ^ Device handle+                -> RTLSDRParams -- ^ Parameters+                -> IO ()+setRTLSDRParams dev RTLSDRParams{..} = do+    setCenterFreq     dev centerFreq+    setSampleRate dev sampleRate+    setFreqCorrection dev freqCorrection+    case tunerGain of+        Nothing -> setTunerGainMode dev False+        Just g  -> setTunerGainMode dev True  >> setTunerGain dev g+    return ()++-- | Returns a producer that streams data from a Realtek RTL2832U based device. You probably want to use `interleavedIQUnsigned256ToFloat` to turn it into a list of complex Floats. This function initializes and configures the device for you. Use `sdrStreamFromDevice` if you need more control over how the device is configured or want to configure it yourself.+sdrStream :: RTLSDRParams                                          -- ^ Configuration parameters           -> Word32                                                -- ^ Number of buffers           -> Word32                                                -- ^ Buffer length           -> EitherT String IO (Producer (VS.Vector CUChar) IO ()) -- ^ Either a string describing the error that occurred or the Producer-sdrStream frequency sampleRate bufNum bufLen = do-    lift $ putStrLn "Initializing RTLSDR device"+sdrStream params bufNum bufLen = do+    lift $ putStrLn "Initializing RTLSDR device..."      dev' <- lift $ open 0     dev  <- maybe (left "Failed to open device") return dev'@@ -31,23 +64,25 @@     lift $ do         t <- getTunerType dev         putStrLn $ "Found a: " ++ show t--        setFreqCorrection dev 0-        setSampleRate dev sampleRate-        setCenterFreq dev frequency-        setTunerGainMode dev False--        resetBuffer dev+        setRTLSDRParams dev params+        sdrStreamFromDevice dev bufNum bufLen -        (output, input) <- spawn unbounded+-- | Returns a producer that streams data from a Realtek RTL2832U based device. You probably want to use `interleavedIQUnsigned256ToFloat` to turn it into a list of complex Floats. This function takes a pre-configured device handle to stream from.+sdrStreamFromDevice :: RTLSDR                                 -- ^ Device handle+                    -> Word32                                 -- ^ Number of buffers+                    -> Word32                                 -- ^ Buffer length+                    -> IO (Producer (VS.Vector CUChar) IO ()) -- ^ The producer+sdrStreamFromDevice dev bufNum bufLen = do+    resetBuffer dev -        forkOS $ void $ readAsync dev bufNum bufLen $ \dat num -> void $ do-            let numBytes = fromIntegral $ bufNum * bufLen-            fp <- mallocForeignPtrArray numBytes-            withForeignPtr fp $ \fpp -> moveBytes fpp dat numBytes-            let v = VS.unsafeFromForeignPtr0 fp numBytes-            atomically (send output v)+    (output, input) <- spawn unbounded -        return $ fromInput input+    forkOS $ void $ readAsync dev bufNum bufLen $ \dat num -> void $ do+        let numBytes = fromIntegral $ bufNum * bufLen+        fp <- mallocForeignPtrArray numBytes+        withForeignPtr fp $ \fpp -> moveBytes fpp dat numBytes+        let v = VS.unsafeFromForeignPtr0 fp numBytes+        atomically (send output v) +    return $ fromInput input 
hs_sources/SDR/Util.hs view
@@ -7,11 +7,12 @@     mult,      -- * Conversion to Floating Point-    makeComplexBufferVect,-    convertC, -    convertCSSE,-    convertCAVX,-    convertFast,+    interleavedIQUnsigned256ToFloat,+    interleavedIQUnsignedByteToFloat,+    interleavedIQUnsignedByteToFloatSSE,+    interleavedIQUnsignedByteToFloatAVX,+    interleavedIQUnsignedByteToFloatFast,+    interleavedIQSigned2048ToFloat,      -- * Scaling     scaleC,@@ -48,12 +49,10 @@ instance (Num a) => Mult (Complex a) a where     mult (x :+ y) z = (x * z) :+ (y * z) ---TODO: none of these functions need the num argument---- | Create a vector of complex float samples from a vector of interleaved I Q component bytes.-{-# INLINE makeComplexBufferVect #-}-makeComplexBufferVect :: (Num a, Integral a, Num b, Fractional b, VG.Vector v1 a, VG.Vector v2 (Complex b)) => v1 a -> v2 (Complex b)-makeComplexBufferVect input = VG.generate (VG.length input `quot` 2) convert+-- | Create a vector of complex floating samples from a vector of interleaved I Q components. Each input element ranges from 0 to 255. This is the format that RTLSDR devices use.+{-# INLINE interleavedIQUnsigned256ToFloat #-}+interleavedIQUnsigned256ToFloat :: (Num a, Integral a, Num b, Fractional b, VG.Vector v1 a, VG.Vector v2 (Complex b)) => v1 a -> v2 (Complex b)+interleavedIQUnsigned256ToFloat input = VG.generate (VG.length input `quot` 2) convert     where     {-# INLINE convert #-}     convert idx  = convert' (input `VG.unsafeIndex` (2 * idx)) :+ convert' (input `VG.unsafeIndex` (2 * idx + 1))@@ -63,9 +62,9 @@ foreign import ccall unsafe "convertC"     convertC_c :: CInt -> Ptr CUChar -> Ptr CFloat -> IO () --- | Same as `makeComplexBufferVect` but written in C and specialized for Floats-convertC :: VS.Vector CUChar -> VS.Vector (Complex Float)-convertC inBuf = unsafePerformIO $ do+-- | Same as `interleavedIQUnsigned256ToFloat` but written in C and specialized for unsigned byte inputs and Float outputs.+interleavedIQUnsignedByteToFloat :: VS.Vector CUChar -> VS.Vector (Complex Float)+interleavedIQUnsignedByteToFloat inBuf = unsafePerformIO $ do     outBuf <- VGM.new $ VG.length inBuf `quot` 2     VS.unsafeWith inBuf $ \iPtr ->          VSM.unsafeWith (unsafeCoerce outBuf) $ \oPtr -> @@ -75,9 +74,9 @@ foreign import ccall unsafe "convertCSSE"     convertCSSE_c :: CInt -> Ptr CUChar -> Ptr CFloat -> IO () --- | Same as `makeComplexBufferVect` but written in C using SSE intrinsics and specialized for Floats-convertCSSE :: VS.Vector CUChar -> VS.Vector (Complex Float)-convertCSSE inBuf = unsafePerformIO $ do+-- | Same as `interleavedIQUnsigned256ToFloat` but written in C using SSE intrinsics and specialized for unsigned byte inputs and Float outputs.+interleavedIQUnsignedByteToFloatSSE :: VS.Vector CUChar -> VS.Vector (Complex Float)+interleavedIQUnsignedByteToFloatSSE inBuf = unsafePerformIO $ do     outBuf <- VGM.new $ VG.length inBuf `quot` 2     VS.unsafeWith inBuf $ \iPtr ->          VSM.unsafeWith (unsafeCoerce outBuf) $ \oPtr -> @@ -87,18 +86,28 @@ foreign import ccall unsafe "convertCAVX"     convertCAVX_c :: CInt -> Ptr CUChar -> Ptr CFloat -> IO () --- | Same as `makeComplexBufferVect` but written in C using AVX intrinsics and specialized for Floats-convertCAVX :: VS.Vector CUChar -> VS.Vector (Complex Float)-convertCAVX inBuf = unsafePerformIO $ do+-- | Same as `interleavedIQUnsigned256ToFloat` but written in C using AVX intrinsics and specialized for unsigned byte inputs and Float outputs.+interleavedIQUnsignedByteToFloatAVX :: VS.Vector CUChar -> VS.Vector (Complex Float)+interleavedIQUnsignedByteToFloatAVX inBuf = unsafePerformIO $ do     outBuf <- VGM.new $ VG.length inBuf `quot` 2     VS.unsafeWith inBuf $ \iPtr ->          VSM.unsafeWith (unsafeCoerce outBuf) $ \oPtr ->              convertCAVX_c (fromIntegral $ VG.length inBuf) iPtr oPtr     VG.freeze outBuf --- | Create a vector of complex float samples from a vector of interleaved I Q component bytes. Uses the fastest SIMD instruction set your processor supports.-convertFast :: CPUInfo -> VS.Vector CUChar -> VS.Vector (Complex Float)-convertFast info = featureSelect info convertC [(hasAVX2, convertCAVX), (hasSSE42, convertCSSE)]+-- | Same as `interleavedIQUnsigned256ToFloat` but uses the fastest SIMD instruction set your processor supports and specialized for unsigned byte inputs and Float outputs.+interleavedIQUnsignedByteToFloatFast :: CPUInfo -> VS.Vector CUChar -> VS.Vector (Complex Float)+interleavedIQUnsignedByteToFloatFast info = featureSelect info interleavedIQUnsignedByteToFloat [(hasAVX2, interleavedIQUnsignedByteToFloatAVX), (hasSSE42, interleavedIQUnsignedByteToFloatSSE)]++-- | Create a vector of complex float samples from a vector of interleaved I Q components. Each input element ranges from -2048 to 2047. This is the format that the BladeRF uses.+{-# INLINE interleavedIQSigned2048ToFloat #-}+interleavedIQSigned2048ToFloat :: (Num a, Integral a, Num b, Fractional b, VG.Vector v1 a, VG.Vector v2 (Complex b)) => v1 a -> v2 (Complex b)+interleavedIQSigned2048ToFloat input = VG.generate (VG.length input `quot` 2) convert+    where+    {-# INLINE convert #-}+    convert idx  = convert' (input `VG.unsafeIndex` (2 * idx)) :+ convert' (input `VG.unsafeIndex` (2 * idx + 1))+    {-# INLINE convert' #-}+    convert' val = fromIntegral val / 2048  -- | Scaling foreign import ccall unsafe "scale"
sdr.cabal view
@@ -1,5 +1,5 @@ name:                sdr-version:             0.1.0.3+version:             0.1.0.4 synopsis:            A software defined radio library description:              Write software defined radio applications in Haskell.@@ -47,6 +47,8 @@     location: https://github.com/adamwalker/sdr  library+    if arch(i386)+        Buildable: False     exposed-modules:              SDR.Pulse,          SDR.RTLSDRStream,