// 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); } }