Files
DP44/DataPRO/SLICECommands/RealtimeCommands/RealtimeStreamDecoder.cs
2026-04-17 14:55:32 -04:00

167 lines
6.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
{
/// <summary>
/// this class handles decoding a SPS realtime stream packet.
/// </summary>
public class RealtimeStreamDecoder
{
/* per lp,
* Byte index:
0 Sync = CMD_SYNC_BYTE = 0xFA
1 type = CMD_TYPE_REALTIME = 0x08
2 command = CMDRT_REALTIME_STREAM_SAMPLE_PUSH = 0x08
3 status = STATUS_ERROR_NONE = 0x00
4 group = 0x00;
5 id = 0x00;
6-7 datalength
8-9 sequence number
10-11 headerCRC16
12-13 dataCRC16 (currently not used)
… payload
8-byte uint64_t Timestamp (for first sample in payload.)
4-byte uint32_t Channel list (bit set = channel present. Bit clear = channel not present.
8-byte uint64_t Sample Number (starting with first ADC scan in payload)
Number of ADC-Scan sample = (datalength 20) / (channellist*2)
*/
/// <summary>
/// this is the order of information in the packet
/// the enum value is the offset in bytes from the start of the packet
/// </summary>
public enum ByteIndex
{
Sync = 0,
Type = 1,
Command = 2,
Status = 3,
Group = 4,
Id = 5,
DataLength = 6,
SequenceNumber = 8,
HeaderCRC = 10,
DataCRC = 12,
TimeStamp = 14,
ChannelList = 22,
SampleNumber = 26,
DataStart = 34
}
public ushort SequenceNumber { get; }
public ulong TimeStamp { get; }
public ulong SampleNumber { get; }
public int[] Channels { get; }
/// <summary>
/// this is all data, not transformed into (short) yet, which requires 2's compliment
/// it is also for all channels in the realtime stream
/// </summary>
public ushort[] RtData { get; }
/// <summary>
/// constructors and decodes byte stream
///
/// </summary>
/// <param name="bytes"></param>
public RealtimeStreamDecoder(IReadOnlyList<byte> bytes)
{
if (bytes.Count > (int)ByteIndex.DataStart)
{
TimeStamp = GetULong(bytes, (int)ByteIndex.TimeStamp);
SequenceNumber = GetUShort(bytes, (int)ByteIndex.SequenceNumber);
SampleNumber = GetULong(bytes, (int)ByteIndex.SampleNumber);
RtData = new ushort[(bytes.Count - (int)ByteIndex.DataStart) / 2];
var uintChannelList = GetUINT(bytes, (int)ByteIndex.ChannelList);
var channels = new List<int>();
var ba = new BitArray(BitConverter.GetBytes(uintChannelList));
for (var i = 0; i < ba.Length; i++)
{
if (ba.Get(i))
{
channels.Add(i);
}
}
Channels = channels.ToArray();
for (var idx = 0; idx < RtData.Length; idx++)
{
//all the data is stored as uint16 with channel order so
// AABBCCDDEEFF, so split out the ushorts
RtData[idx] = GetRTUShort(bytes, (int)(ByteIndex.DataStart + idx * 2));
}
}
else
{
RtData = new ushort[0];
SampleNumber = 0;
Channels = new int[0];
}
}
/// <summary>
/// returns a ulong given a sequence of bytes and a starting offset in bytes
/// that the ulong starts at
/// SLICE header bytes are sent in reverse order so we have to fix them first
/// </summary>
/// <param name="bytes"></param>
/// <param name="offset"></param>
/// <returns></returns>
private static ulong GetULong(IReadOnlyList<byte> bytes, int offset)
{
return (ulong)bytes[offset + 7] << 0 |
(ulong)bytes[offset + 6] << 8 |
(ulong)bytes[offset + 5] << 16 |
(ulong)bytes[offset + 4] << 24 |
(ulong)bytes[offset + 3] << 32 |
(ulong)bytes[offset + 2] << 40 |
(ulong)bytes[offset + 1] << 48 |
(ulong)bytes[offset + 0] << 56;
}
/// <summary>
/// gets a ushort from the response parameters
/// SLICE header bytes are sent in reverse order, so we have to fix
/// them first
/// </summary>
/// <param name="bytes"></param>
/// <param name="offset"></param>
/// <returns></returns>
private static uint GetUINT(IReadOnlyList<byte> bytes, int offset)
{
return (uint)bytes[offset + 3] << 0 |
(uint)bytes[offset + 2] << 8 |
(uint)bytes[offset + 1] << 16 |
(uint)bytes[offset + 0] << 32;
}
/// <summary>
/// returns a ushort given a sequence of bytes and a starting offset in bytes
/// SLICE header bytes are sent in reverse order, so we have to fix the byte
/// order first
/// </summary>
/// <param name="bytes"></param>
/// <param name="offset"></param>
/// <returns></returns>
private static ushort GetUShort(IReadOnlyList<byte> bytes, int offset)
{
return (ushort)(bytes[offset + 1] |
bytes[offset + 0] << 8);
}
/// <summary>
/// returns a ushort given a sequence of bytes and a starting offset in bytes
/// note that DATA is not in reverse order unlike the header, so we
/// DON'T need to fix the order
/// </summary>
/// <param name="bytes"></param>
/// <param name="offset"></param>
/// <returns></returns>
private static ushort GetRTUShort(IReadOnlyList<byte> bytes, int offset)
{
return (ushort)(bytes[offset + 0] |
bytes[offset + 1] << 8);
}
}
}