167 lines
6.5 KiB
C#
167 lines
6.5 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|