// Copyright (c) 2021-2022 Nicholas Corgan
// SPDX-License-Identifier: BSL-1.0
using System;
using System.Buffers;
using System.Linq;
using System.Runtime.InteropServices;
namespace Pothosware.SoapySDR
{
///
/// A class representing a receive stream. With this class, you can receive
/// samples from your device into provided buffers.
///
public class RxStream: Stream
{
internal RxStream(
Device device,
string format,
uint[] channels,
Kwargs kwargs
):
base(device, format, channels, kwargs)
{
_streamHandle = device.SetupStreamInternal(Direction.Rx, format, new UnsignedListInternal(channels), kwargs);
}
///
/// Receive data from a single channel into an arbitrary memory location.
///
/// The type of the given receive buffer. This type must match the stream's format type.
/// The buffer to receive into. For complex types, this buffer is interleaved.
/// The operation timeout in microseconds.
/// An output to store stream metadata.
/// An error code for the stream operation.
public unsafe ErrorCode Read(
Memory memory,
int timeoutUs,
out StreamResult result) where T : unmanaged
{
return Read(
new Memory[] { memory },
timeoutUs,
out result);
}
///
/// Receive data from multiple channels into arbitrary memory locations.
///
/// The type of the given receive buffers. This type must match the stream's format type.
/// The buffers to receive into. For complex types, this buffer is interleaved. The array length must match the number of channels, and all Memory<> instances must be of the same length.
/// The operation timeout in microseconds.
/// An output to store stream metadata.
/// An error code for the stream operation.
public unsafe ErrorCode Read(
Memory[] memory,
int timeoutUs,
out StreamResult result) where T : unmanaged
{
ErrorCode ret;
if (_streamHandle != null)
{
ValidateMemory(memory);
var memsAsSizes = Utility.ToPointerListInternal(
memory,
out MemoryHandle[] _);
var deviceOutput = _device.ReadStreamInternal(
_streamHandle,
memsAsSizes,
(uint)memory[0].Length,
timeoutUs);
result = deviceOutput.Second;
ret = deviceOutput.First;
}
else throw new InvalidOperationException("Stream is closed");
return ret;
}
///
/// Receive data from a single channel into an arbitrary memory location.
///
/// The type of the given receive buffer. This type must match the stream's format type.
/// The buffer to receive into. For complex types, this buffer is interleaved.
/// Optional input flags.
/// The buffer's timestamp in nanoseconds.
/// The operation timeout in microseconds.
/// An output to store stream metadata.
/// An error code for the stream operation.
public unsafe ErrorCode Read(
Span span,
int timeoutUs,
out StreamResult result) where T : unmanaged
{
ValidateSpan(span);
fixed(T* data = &MemoryMarshal.GetReference(span))
{
return Read((IntPtr)data, (uint)span.Length, timeoutUs, out result);
}
}
///
/// Receive data from a single channel into a managed C# array.
///
/// The type of the underlying destination array. This type must match the stream's format type.
/// The managed array to receive into. For complex types, this buffer is interleaved.
/// The operation timeout in microseconds.
/// An output to store stream metadata.
/// An error code for the stream operation.
public unsafe ErrorCode Read(
ref T[] buff,
int timeoutUs,
out StreamResult result) where T: unmanaged
{
return Read(new Memory(buff), timeoutUs, out result);
}
///
/// Receive data from multiple channels into a managed C# array.
///
/// The type of the underlying destination array. This type must match the stream's format type.
/// The managed arrays to receive into. For complex types, this buffer is interleaved.
/// The operation timeout in microseconds.
/// An output to store stream metadata.
/// An error code for the stream operation.
public unsafe ErrorCode Read(
ref T[][] buffs,
int timeoutUs,
out StreamResult result) where T: unmanaged
{
return Read(buffs.Select(buff => new Memory(buff)).ToArray(), timeoutUs, out result);
}
///
/// Receive data into an arbitrary memory location referenced by an unmanaged pointer. Minimal validation is performed on input parameters.
///
/// A pointer to the buffer to receive into.
/// The number of elements (of the stream format's size) in the destination buffer.
/// The operation timeout in microseconds.
/// An output to store stream metadata.
/// An error code for the stream operation.
public unsafe ErrorCode Read(
IntPtr ptr,
uint numElems,
int timeoutUs,
out StreamResult result)
{
return Read(
new IntPtr[] { ptr },
numElems,
timeoutUs,
out result);
}
///
/// Receive data into arbitrary memory locations referenced by unmanaged pointers. Minimal validation is performed on input parameters except channel count.
///
/// Pointers to the receive buffers.
/// The number of elements (of the stream format's size) in each destination buffer.
/// The operation timeout in microseconds.
/// An output to store stream metadata.
/// An error code for the stream operation.
public unsafe ErrorCode Read(
IntPtr[] ptrs,
uint numElems,
int timeoutUs,
out StreamResult result)
{
ErrorCode ret;
if (_streamHandle != null)
{
ValidateIntPtrArray(ptrs);
var deviceOutput = _device.ReadStreamInternal(
_streamHandle,
Utility.ToPointerListInternal(ptrs),
numElems,
timeoutUs);
result = deviceOutput.Second;
ret = deviceOutput.First;
}
else throw new InvalidOperationException("Stream is closed");
return ret;
}
//
// Object overrides
//
public override bool Equals(object other) => base.Equals(other);
public override int GetHashCode() => base.GetHashCode();
public override string ToString() => string.Format("{0}:{1} {2} RX stream (format: {3}, channels: {4})",
_device.DriverKey,
_device.HardwareKey,
(_active ? "active" : "inactive"),
Format,
Channels);
}
}