{-# LANGUAGE RecordWildCards #-} {-| Stream samples from a Realtek RTL2832U based device -} module SDR.RTLSDRStream ( RTLSDRParams(..), defaultRTLSDRParams, setRTLSDRParams, sdrStream, sdrStreamFromDevice ) where import Control.Monad import Control.Monad.Trans.Except import Data.Word import Data.Int import Foreign.ForeignPtr import Foreign.C.Types import Control.Concurrent hiding (yield) import Foreign.Marshal.Utils import qualified Data.Vector.Storable as VS import Pipes import Pipes.Concurrent import RTLSDR -- | 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 -> ExceptT String IO (Producer (VS.Vector CUChar) IO ()) -- ^ Either a string describing the error that occurred or the Producer sdrStream params bufNum bufLen = do lift $ putStrLn "Initializing RTLSDR device..." dev' <- lift $ open 0 dev <- maybe (throwE "Failed to open device") return dev' lift $ do t <- getTunerType dev putStrLn $ "Found a: " ++ show t setRTLSDRParams dev params sdrStreamFromDevice dev bufNum bufLen -- | 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 (output, input) <- spawn unbounded 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