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