sdr 0.1.0.3 → 0.1.0.4
raw patch · 4 files changed
+97/−47 lines, 4 files
Files
- Readme.md +6/−2
- hs_sources/SDR/RTLSDRStream.hs +56/−21
- hs_sources/SDR/Util.hs +32/−23
- sdr.cabal +3/−1
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,