JuliaTelecom / SoapySDR.jl

Julia Wrappers for SoapySDR
https://juliatelecom.github.io/SoapySDR.jl/dev/
Other
15 stars 3 forks source link

`read` with RTLSDR sometimes returns `TIMEOUT` or `OVERFLOW` #13

Closed mbaz closed 3 years ago

mbaz commented 3 years ago
julia> SoapySDR.activate!(stream)

Allocating 15 zero-copy buffers
julia> x = Base.read(stream, 1000);
ERROR: TIMEOUT
Stacktrace:
 [1] SoapySDRDevice_readStream(device::SoapySDR.Device, stream::SoapySDR.Stream{ComplexF32}, buffs::Base.RefValue{Tuple{Ptr{ComplexF32}}}, numElems::Int64, timeoutUs::Float64)
   @ SoapySDR ~/.julia/packages/SoapySDR/ElzKN/src/lowlevel/Device.jl:493
 [2] #_read!#15
   @ ~/.julia/packages/SoapySDR/ElzKN/src/highlevel.jl:517 [inlined]
 [3] _read!(s::SoapySDR.Stream{ComplexF32}, buffers::Tuple{Vector{ComplexF32}})
   @ SoapySDR ~/.julia/packages/SoapySDR/ElzKN/src/highlevel.jl:513
 [4] read(s::SoapySDR.Stream{ComplexF32}, n::Int64; kwargs::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ SoapySDR ~/.julia/packages/SoapySDR/ElzKN/src/highlevel.jl:535
 [5] read(s::SoapySDR.Stream{ComplexF32}, n::Int64)
   @ SoapySDR ~/.julia/packages/SoapySDR/ElzKN/src/highlevel.jl:534
 [6] top-level scope
   @ REPL[7]:1

This seems to happen a few times (from 1 to maybe 10) after activate!() is called. Once read succeeds, it never seems to fail again.

sjkelly commented 3 years ago

Some other notes. This is fine looping for several minutes:

julia> using SoapySDR, SoapyRTLSDR_jll
julia> channel = Devices()[1].rx[1]
julia> stream = SoapySDR.Stream(ComplexF32, [channel])
julia> SoapySDR.activate!(stream)
Allocating 15 zero-copy buffers
julia> n = Int64(SoapySDR.SoapySDRDevice_getStreamMTU(stream.d, stream))
131072
julia> while true; Base.read(stream, n); end

But this fails almost immediately:

julia> while true; Base.read(stream, 1000); end
OERROR: OVERFLOW
Stacktrace:
 [1] SoapySDRDevice_readStream(device::SoapySDR.Device, stream::SoapySDR.Stream{ComplexF32}, buffs::Base.RefValue{Tuple{Ptr{ComplexF32}}}, numElems::Int64, timeoutUs::Float64)
   @ SoapySDR ~/.julia/dev/SoapySDR/src/lowlevel/Device.jl:493
 [2] #_read!#15
   @ ~/.julia/dev/SoapySDR/src/highlevel.jl:517 [inlined]
 [3] _read!(s::SoapySDR.Stream{ComplexF32}, buffers::Tuple{Vector{ComplexF32}})
   @ SoapySDR ~/.julia/dev/SoapySDR/src/highlevel.jl:513
 [4] read(s::SoapySDR.Stream{ComplexF32}, n::Int64; kwargs::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ SoapySDR ~/.julia/dev/SoapySDR/src/highlevel.jl:535
 [5] read(s::SoapySDR.Stream{ComplexF32}, n::Int64)
   @ SoapySDR ~/.julia/dev/SoapySDR/src/highlevel.jl:534
 [6] top-level scope
   @ ./REPL[7]:1
sjkelly commented 3 years ago

Okay so some further investigation. I think this is actually expected behavior. The stream is buffered in the SoapySDR layer, and read by us. If we don't read fast enough, it will throw the buffer overflow error. Wrapping in SoapySDR.activate! and SoapySDR.deactivate! calls fixes this issue, like so:

SoapySDR.activate!(stream); read(stream, 1000); SoapySDR.deactivate!(stream)

This should be documented and explained, so I will leave this open for further discussion and ideation.

mbaz commented 3 years ago

Related: https://github.com/pothosware/SoapyAirspy/issues/10#issuecomment-247165980

It seems that the ideal number of samples to request is sdr.getStreamMTU(readStream). Is that function wrapped in SoapySDR.jll? I couldn't find it.

sjkelly commented 3 years ago

It is not handled at the highlevel API yet, but the following should work:

julia> stream = SoapySDR.Stream(ComplexF32, [channel])
julia> SoapySDR.activate!(stream)
julia> n = Int64(SoapySDR.SoapySDRDevice_getStreamMTU(stream.d, stream))

I think a reasonable solution here will be to use readavailable to implicitly read the MTU amount. I really appreciate the testing and bug reports. I should be able to get the High Level API modified and documented in the next couple days to avoid these issues.

sjkelly commented 3 years ago

FYI with #18 this becomes simply:

julia> stream = SoapySDR.Stream([channel])
julia> SoapySDR.activate!(stream)
julia> samples = read(stream)

So the MTU is now baked-in and a stream type is no longer needed, we use the native format supported by the device (multiple dispatch FTW!).

sjkelly commented 3 years ago

With #22 these error should not occur. Please comment and reopen if otherwise.