using System;
using System.Collections;
using System.Collections.Generic;
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
{
///
/// this class handles decoding a SPS realtime stream packet.
///
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)
*/
///
/// this is the order of information in the packet
/// the enum value is the offset in bytes from the start of the packet
///
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; }
///
/// this is all data, not transformed into (short) yet, which requires 2's compliment
/// it is also for all channels in the realtime stream
///
public ushort[] RtData { get; }
///
/// constructors and decodes byte stream
///
///
///
public RealtimeStreamDecoder(IReadOnlyList 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();
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];
}
}
///
/// 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
///
///
///
///
private static ulong GetULong(IReadOnlyList 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;
}
///
/// gets a ushort from the response parameters
/// SLICE header bytes are sent in reverse order, so we have to fix
/// them first
///
///
///
///
private static uint GetUINT(IReadOnlyList bytes, int offset)
{
return (uint)bytes[offset + 3] << 0 |
(uint)bytes[offset + 2] << 8 |
(uint)bytes[offset + 1] << 16 |
(uint)bytes[offset + 0] << 32;
}
///
/// 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
///
///
///
///
private static ushort GetUShort(IReadOnlyList bytes, int offset)
{
return (ushort)(bytes[offset + 1] |
bytes[offset + 0] << 8);
}
///
/// 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
///
///
///
///
private static ushort GetRTUShort(IReadOnlyList bytes, int offset)
{
return (ushort)(bytes[offset + 0] |
bytes[offset + 1] << 8);
}
}
}