private void WriteQualityChange(uint quality, TsscPointMetadata point) { if (point.PrevQuality2 == quality) { m_lastPoint.WriteCode(TsscCodeWords.Quality2); } else { m_lastPoint.WriteCode(TsscCodeWords.Quality7Bit32); Encoding7Bit.Write(m_data, ref m_position, quality); } point.PrevQuality2 = point.PrevQuality1; point.PrevQuality1 = quality; }
/// <summary> /// Resets the TSSC Decoder to the initial state. /// </summary> /// <remarks> /// TSSC is a stateful encoder that requires a state /// of the previous data to be maintained. Therefore, if /// the state ever becomes corrupt (out of order, dropped, corrupted, or duplicated) /// the state must be reset on both ends. /// </remarks> public void Reset() { m_points = new IndexedArray <TsscPointMetadata>(); m_lastPoint = new TsscPointMetadata(null, ReadBit, ReadBits5); m_data = EmptyArray <byte> .Empty; m_position = 0; m_lastPosition = 0; ClearBitStream(); m_prevTimeDelta1 = long.MaxValue; m_prevTimeDelta2 = long.MaxValue; m_prevTimeDelta3 = long.MaxValue; m_prevTimeDelta4 = long.MaxValue; m_prevTimestamp1 = 0; m_prevTimestamp2 = 0; }
private uint DecodeQuality(int code, TsscPointMetadata nextPoint) { uint quality; if (code == TsscCodeWords.Quality2) { quality = nextPoint.PrevQuality2; } else { quality = Encoding7Bit.ReadUInt32(m_data, ref m_position); } nextPoint.PrevQuality2 = nextPoint.PrevQuality1; nextPoint.PrevQuality1 = quality; return(quality); }
/// <summary> /// Resets the TSSC Encoder to the initial state. /// </summary> /// <remarks> /// TSSC is a stateful encoder that requires a state /// of the previous data to be maintained. Therefore, if /// the state ever becomes corrupt (out of order, dropped, corrupted, or duplicated) /// the state must be reset on both ends. /// </remarks> public void Reset() { m_points = new IndexedArray<TsscPointMetadata>(); m_lastPoint = new TsscPointMetadata(WriteBits, null, null); m_data = EmptyArray<byte>.Empty; m_position = 0; m_lastPosition = 0; ClearBitStream(); m_prevTimeDelta1 = long.MaxValue; m_prevTimeDelta2 = long.MaxValue; m_prevTimeDelta3 = long.MaxValue; m_prevTimeDelta4 = long.MaxValue; m_prevTimestamp1 = 0; m_prevTimestamp2 = 0; }
private void DecodePointID(int code, TsscPointMetadata lastPoint) { if (code == TsscCodeWords.PointIDXOR4) { lastPoint.PrevNextPointId1 ^= (ushort)ReadBits4(); } else if (code == TsscCodeWords.PointIDXOR8) { lastPoint.PrevNextPointId1 ^= m_data[m_position++]; } else if (code == TsscCodeWords.PointIDXOR12) { lastPoint.PrevNextPointId1 ^= (ushort)ReadBits4(); lastPoint.PrevNextPointId1 ^= (ushort)(m_data[m_position++] << 4); } else { lastPoint.PrevNextPointId1 ^= m_data[m_position++]; lastPoint.PrevNextPointId1 ^= (ushort)(m_data[m_position++] << 8); } }
/// <summary> /// Reads the next measurement from the stream. If the end of the stream has been encountered, /// return false. /// </summary> /// <param name="id">the id</param> /// <param name="timestamp">the timestamp in ticks</param> /// <param name="quality">the quality</param> /// <param name="value">the value</param> /// <returns>true if successful, false otherwise.</returns> public unsafe bool TryGetMeasurement(out ushort id, out long timestamp, out uint quality, out float value) { TsscPointMetadata nextPoint = null; if (m_position == m_lastPosition && BitStreamIsEmpty) { ClearBitStream(); id = 0; timestamp = 0; quality = 0; value = 0; return(false); } //Note: since I will not know the incoming pointID. The most recent // measurement received will be the one that contains the // coding algorithm for this measurement. Since for the more part // measurements generally have some sort of sequence to them, // this still ends up being a good enough assumption. int code = m_lastPoint.ReadCode(); if (code == TsscCodeWords.EndOfStream) { ClearBitStream(); id = 0; timestamp = 0; quality = 0; value = 0; return(false); } if (code <= TsscCodeWords.PointIDXOR16) { DecodePointID(code, m_lastPoint); code = m_lastPoint.ReadCode(); if (code < TsscCodeWords.TimeDelta1Forward) { throw new Exception($"Expecting code >= {TsscCodeWords.TimeDelta1Forward} Received {code} at position {m_position} with last position { m_lastPosition}"); } } id = m_lastPoint.PrevNextPointId1; nextPoint = m_points[m_lastPoint.PrevNextPointId1]; if (nextPoint == null) { nextPoint = new TsscPointMetadata(null, ReadBit, ReadBits5); m_points[id] = nextPoint; nextPoint.PrevNextPointId1 = (ushort)(id + 1); } if (code <= TsscCodeWords.TimeXOR7Bit) { timestamp = DecodeTimestamp(code); code = m_lastPoint.ReadCode(); if (code < TsscCodeWords.Quality2) { throw new Exception($"Expecting code >= {TsscCodeWords.Quality2} Received {code} at position {m_position} with last position { m_lastPosition}"); } } else { timestamp = m_prevTimestamp1; } if (code <= TsscCodeWords.Quality7Bit32) { quality = DecodeQuality(code, nextPoint); code = m_lastPoint.ReadCode(); if (code < TsscCodeWords.Value1) { throw new Exception($"Expecting code >= {TsscCodeWords.Value1} Received {code} at position {m_position} with last position { m_lastPosition}"); } } else { quality = nextPoint.PrevQuality1; } //Since value will almost always change, //This is not put inside a function call. uint valueRaw = 0; if (code == TsscCodeWords.Value1) { valueRaw = nextPoint.PrevValue1; } else if (code == TsscCodeWords.Value2) { valueRaw = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; } else if (code == TsscCodeWords.Value3) { valueRaw = nextPoint.PrevValue3; nextPoint.PrevValue3 = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; } else if (code == TsscCodeWords.ValueZero) { valueRaw = 0; nextPoint.PrevValue3 = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; } else { switch (code) { case TsscCodeWords.ValueXOR4: valueRaw = (uint)ReadBits4() ^ nextPoint.PrevValue1; break; case TsscCodeWords.ValueXOR8: valueRaw = m_data[m_position] ^ nextPoint.PrevValue1; m_position = m_position + 1; break; case TsscCodeWords.ValueXOR12: valueRaw = (uint)ReadBits4() ^ (uint)(m_data[m_position] << 4) ^ nextPoint.PrevValue1; m_position = m_position + 1; break; case TsscCodeWords.ValueXOR16: valueRaw = m_data[m_position] ^ (uint)(m_data[m_position + 1] << 8) ^ nextPoint.PrevValue1; m_position = m_position + 2; break; case TsscCodeWords.ValueXOR20: valueRaw = (uint)ReadBits4() ^ (uint)(m_data[m_position] << 4) ^ (uint)(m_data[m_position + 1] << 12) ^ nextPoint.PrevValue1; m_position = m_position + 2; break; case TsscCodeWords.ValueXOR24: valueRaw = m_data[m_position] ^ (uint)(m_data[m_position + 1] << 8) ^ (uint)(m_data[m_position + 2] << 16) ^ nextPoint.PrevValue1; m_position = m_position + 3; break; case TsscCodeWords.ValueXOR28: valueRaw = (uint)ReadBits4() ^ (uint)(m_data[m_position] << 4) ^ (uint)(m_data[m_position + 1] << 12) ^ (uint)(m_data[m_position + 2] << 20) ^ nextPoint.PrevValue1; m_position = m_position + 3; break; case TsscCodeWords.ValueXOR32: valueRaw = m_data[m_position] ^ (uint)(m_data[m_position + 1] << 8) ^ (uint)(m_data[m_position + 2] << 16) ^ (uint)(m_data[m_position + 3] << 24) ^ nextPoint.PrevValue1; m_position = m_position + 4; break; default: throw new Exception($"Invalid code received {code} at position {m_position} with last position { m_lastPosition}"); } nextPoint.PrevValue3 = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; } value = *(float *)&valueRaw; m_lastPoint = nextPoint; return(true); }
/// <summary> /// Adds the supplied measurement to the stream. If the stream is full, /// this method returns false. /// </summary> /// <param name="id">the id</param> /// <param name="timestamp">the timestamp in ticks</param> /// <param name="quality">the quality</param> /// <param name="value">the value</param> /// <returns>true if successful, false otherwise.</returns> public unsafe bool TryAddMeasurement(ushort id, long timestamp, uint quality, float value) { //if there are fewer than 100 bytes available on the buffer //assume that we cannot add any more. if (m_lastPosition - m_position < 100) return false; TsscPointMetadata point = m_points[id]; if (point == null) { point = new TsscPointMetadata(WriteBits, null, null); point.PrevNextPointId1 = (ushort)(id + 1); m_points[id] = point; } //Note: since I will not know the incoming pointID. The most recent // measurement received will be the one that contains the // coding algorithm for this measurement. Since for the more part // measurements generally have some sort of sequence to them, // this still ends up being a good enough assumption. if (m_lastPoint.PrevNextPointId1 != id) { WritePointIdChange(id); } if (m_prevTimestamp1 != timestamp) { WriteTimestampChange(timestamp); } if (point.PrevQuality1 != quality) { WriteQualityChange(quality, point); } uint valueRaw = *(uint*)&value; if (point.PrevValue1 == valueRaw) { m_lastPoint.WriteCode(TsscCodeWords.Value1); } else if (point.PrevValue2 == valueRaw) { m_lastPoint.WriteCode(TsscCodeWords.Value2); point.PrevValue2 = point.PrevValue1; point.PrevValue1 = valueRaw; } else if (point.PrevValue3 == valueRaw) { m_lastPoint.WriteCode(TsscCodeWords.Value3); point.PrevValue3 = point.PrevValue2; point.PrevValue2 = point.PrevValue1; point.PrevValue1 = valueRaw; } else if (valueRaw == 0) { m_lastPoint.WriteCode(TsscCodeWords.ValueZero); point.PrevValue3 = point.PrevValue2; point.PrevValue2 = point.PrevValue1; point.PrevValue1 = 0; } else { uint bitsChanged = valueRaw ^ point.PrevValue1; if (bitsChanged <= Bits4) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR4); WriteBits((byte)bitsChanged & 15, 4); } else if (bitsChanged <= Bits8) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR8); m_data[m_position] = (byte)bitsChanged; m_position++; } else if (bitsChanged <= Bits12) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR12); WriteBits((byte)bitsChanged & 15, 4); m_data[m_position] = (byte)(bitsChanged >> 4); m_position++; } else if (bitsChanged <= Bits16) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR16); m_data[m_position] = (byte)bitsChanged; m_data[m_position + 1] = (byte)(bitsChanged >> 8); m_position = m_position + 2; } else if (bitsChanged <= Bits20) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR20); WriteBits((byte)bitsChanged & 15, 4); m_data[m_position] = (byte)(bitsChanged >> 4); m_data[m_position + 1] = (byte)(bitsChanged >> 12); m_position = m_position + 2; } else if (bitsChanged <= Bits24) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR24); m_data[m_position] = (byte)bitsChanged; m_data[m_position + 1] = (byte)(bitsChanged >> 8); m_data[m_position + 2] = (byte)(bitsChanged >> 16); m_position = m_position + 3; } else if (bitsChanged <= Bits28) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR28); WriteBits((byte)bitsChanged & 15, 4); m_data[m_position] = (byte)(bitsChanged >> 4); m_data[m_position + 1] = (byte)(bitsChanged >> 12); m_data[m_position + 2] = (byte)(bitsChanged >> 20); m_position = m_position + 3; } else { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR32); m_data[m_position] = (byte)bitsChanged; m_data[m_position + 1] = (byte)(bitsChanged >> 8); m_data[m_position + 2] = (byte)(bitsChanged >> 16); m_data[m_position + 3] = (byte)(bitsChanged >> 24); m_position = m_position + 4; } point.PrevValue3 = point.PrevValue2; point.PrevValue2 = point.PrevValue1; point.PrevValue1 = valueRaw; } m_lastPoint = point; return true; }
/// <summary> /// Adds the supplied measurement to the stream. If the stream is full, /// this method returns false. /// </summary> /// <param name="id">the id</param> /// <param name="timestamp">the timestamp in ticks</param> /// <param name="quality">the quality</param> /// <param name="value">the value</param> /// <returns>true if successful, false otherwise.</returns> public unsafe bool TryAddMeasurement(ushort id, long timestamp, uint quality, float value) { //if there are fewer than 100 bytes available on the buffer //assume that we cannot add any more. if (m_lastPosition - m_position < 100) { return(false); } TsscPointMetadata point = m_points[id]; if (point == null) { point = new TsscPointMetadata(WriteBits, null, null); point.PrevNextPointId1 = (ushort)(id + 1); m_points[id] = point; } //Note: since I will not know the incoming pointID. The most recent // measurement received will be the one that contains the // coding algorithm for this measurement. Since for the more part // measurements generally have some sort of sequence to them, // this still ends up being a good enough assumption. if (m_lastPoint.PrevNextPointId1 != id) { WritePointIdChange(id); } if (m_prevTimestamp1 != timestamp) { WriteTimestampChange(timestamp); } if (point.PrevQuality1 != quality) { WriteQualityChange(quality, point); } uint valueRaw = *(uint *)&value; if (point.PrevValue1 == valueRaw) { m_lastPoint.WriteCode(TsscCodeWords.Value1); } else if (point.PrevValue2 == valueRaw) { m_lastPoint.WriteCode(TsscCodeWords.Value2); point.PrevValue2 = point.PrevValue1; point.PrevValue1 = valueRaw; } else if (point.PrevValue3 == valueRaw) { m_lastPoint.WriteCode(TsscCodeWords.Value3); point.PrevValue3 = point.PrevValue2; point.PrevValue2 = point.PrevValue1; point.PrevValue1 = valueRaw; } else if (valueRaw == 0) { m_lastPoint.WriteCode(TsscCodeWords.ValueZero); point.PrevValue3 = point.PrevValue2; point.PrevValue2 = point.PrevValue1; point.PrevValue1 = 0; } else { uint bitsChanged = valueRaw ^ point.PrevValue1; if (bitsChanged <= Bits4) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR4); WriteBits((byte)bitsChanged & 15, 4); } else if (bitsChanged <= Bits8) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR8); m_data[m_position] = (byte)bitsChanged; m_position++; } else if (bitsChanged <= Bits12) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR12); WriteBits((byte)bitsChanged & 15, 4); m_data[m_position] = (byte)(bitsChanged >> 4); m_position++; } else if (bitsChanged <= Bits16) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR16); m_data[m_position] = (byte)bitsChanged; m_data[m_position + 1] = (byte)(bitsChanged >> 8); m_position = m_position + 2; } else if (bitsChanged <= Bits20) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR20); WriteBits((byte)bitsChanged & 15, 4); m_data[m_position] = (byte)(bitsChanged >> 4); m_data[m_position + 1] = (byte)(bitsChanged >> 12); m_position = m_position + 2; } else if (bitsChanged <= Bits24) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR24); m_data[m_position] = (byte)bitsChanged; m_data[m_position + 1] = (byte)(bitsChanged >> 8); m_data[m_position + 2] = (byte)(bitsChanged >> 16); m_position = m_position + 3; } else if (bitsChanged <= Bits28) { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR28); WriteBits((byte)bitsChanged & 15, 4); m_data[m_position] = (byte)(bitsChanged >> 4); m_data[m_position + 1] = (byte)(bitsChanged >> 12); m_data[m_position + 2] = (byte)(bitsChanged >> 20); m_position = m_position + 3; } else { m_lastPoint.WriteCode(TsscCodeWords.ValueXOR32); m_data[m_position] = (byte)bitsChanged; m_data[m_position + 1] = (byte)(bitsChanged >> 8); m_data[m_position + 2] = (byte)(bitsChanged >> 16); m_data[m_position + 3] = (byte)(bitsChanged >> 24); m_position = m_position + 4; } point.PrevValue3 = point.PrevValue2; point.PrevValue2 = point.PrevValue1; point.PrevValue1 = valueRaw; } m_lastPoint = point; return(true); }
private uint DecodeQuality(int code, TsscPointMetadata nextPoint) { uint quality; if (code == TsscCodeWords.Quality2) { quality = nextPoint.PrevQuality2; } else { quality = Encoding7Bit.ReadUInt32(m_data, ref m_position); } nextPoint.PrevQuality2 = nextPoint.PrevQuality1; nextPoint.PrevQuality1 = quality; return quality; }
/// <summary> /// Reads the next measurement from the stream. If the end of the stream has been encountered, /// return false. /// </summary> /// <param name="id">the id</param> /// <param name="timestamp">the timestamp in ticks</param> /// <param name="quality">the quality</param> /// <param name="value">the value</param> /// <returns>true if successful, false otherwise.</returns> public unsafe bool TryGetMeasurement(out ushort id, out long timestamp, out uint quality, out float value) { TsscPointMetadata nextPoint = null; if (m_position == m_lastPosition && BitStreamIsEmpty) { ClearBitStream(); id = 0; timestamp = 0; quality = 0; value = 0; return false; } //Note: since I will not know the incoming pointID. The most recent // measurement received will be the one that contains the // coding algorithm for this measurement. Since for the more part // measurements generally have some sort of sequence to them, // this still ends up being a good enough assumption. int code = m_lastPoint.ReadCode(); if (code == TsscCodeWords.EndOfStream) { ClearBitStream(); id = 0; timestamp = 0; quality = 0; value = 0; return false; } if (code <= TsscCodeWords.PointIDXOR16) { DecodePointID(code, m_lastPoint); code = m_lastPoint.ReadCode(); if (code < TsscCodeWords.TimeDelta1Forward) throw new Exception($"Expecting code >= {TsscCodeWords.TimeDelta1Forward} Received {code} at position {m_position} with last position { m_lastPosition}"); } id = m_lastPoint.PrevNextPointId1; nextPoint = m_points[m_lastPoint.PrevNextPointId1]; if (nextPoint == null) { nextPoint = new TsscPointMetadata(null, ReadBit, ReadBits5); m_points[id] = nextPoint; nextPoint.PrevNextPointId1 = (ushort)(id + 1); } if (code <= TsscCodeWords.TimeXOR7Bit) { timestamp = DecodeTimestamp(code); code = m_lastPoint.ReadCode(); if (code < TsscCodeWords.Quality2) throw new Exception($"Expecting code >= {TsscCodeWords.Quality2} Received {code} at position {m_position} with last position { m_lastPosition}"); } else { timestamp = m_prevTimestamp1; } if (code <= TsscCodeWords.Quality7Bit32) { quality = DecodeQuality(code, nextPoint); code = m_lastPoint.ReadCode(); if (code < TsscCodeWords.Value1) throw new Exception($"Expecting code >= {TsscCodeWords.Value1} Received {code} at position {m_position} with last position { m_lastPosition}"); } else { quality = nextPoint.PrevQuality1; } //Since value will almost always change, //This is not put inside a function call. uint valueRaw = 0; if (code == TsscCodeWords.Value1) { valueRaw = nextPoint.PrevValue1; } else if (code == TsscCodeWords.Value2) { valueRaw = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; } else if (code == TsscCodeWords.Value3) { valueRaw = nextPoint.PrevValue3; nextPoint.PrevValue3 = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; } else if (code == TsscCodeWords.ValueZero) { valueRaw = 0; nextPoint.PrevValue3 = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; } else { switch (code) { case TsscCodeWords.ValueXOR4: valueRaw = (uint)ReadBits4() ^ nextPoint.PrevValue1; break; case TsscCodeWords.ValueXOR8: valueRaw = m_data[m_position] ^ nextPoint.PrevValue1; m_position = m_position + 1; break; case TsscCodeWords.ValueXOR12: valueRaw = (uint)ReadBits4() ^ (uint)(m_data[m_position] << 4) ^ nextPoint.PrevValue1; m_position = m_position + 1; break; case TsscCodeWords.ValueXOR16: valueRaw = m_data[m_position] ^ (uint)(m_data[m_position + 1] << 8) ^ nextPoint.PrevValue1; m_position = m_position + 2; break; case TsscCodeWords.ValueXOR20: valueRaw = (uint)ReadBits4() ^ (uint)(m_data[m_position] << 4) ^ (uint)(m_data[m_position + 1] << 12) ^ nextPoint.PrevValue1; m_position = m_position + 2; break; case TsscCodeWords.ValueXOR24: valueRaw = m_data[m_position] ^ (uint)(m_data[m_position + 1] << 8) ^ (uint)(m_data[m_position + 2] << 16) ^ nextPoint.PrevValue1; m_position = m_position + 3; break; case TsscCodeWords.ValueXOR28: valueRaw = (uint)ReadBits4() ^ (uint)(m_data[m_position] << 4) ^ (uint)(m_data[m_position + 1] << 12) ^ (uint)(m_data[m_position + 2] << 20) ^ nextPoint.PrevValue1; m_position = m_position + 3; break; case TsscCodeWords.ValueXOR32: valueRaw = m_data[m_position] ^ (uint)(m_data[m_position + 1] << 8) ^ (uint)(m_data[m_position + 2] << 16) ^ (uint)(m_data[m_position + 3] << 24) ^ nextPoint.PrevValue1; m_position = m_position + 4; break; default: throw new Exception($"Invalid code received {code} at position {m_position} with last position { m_lastPosition}"); } nextPoint.PrevValue3 = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; } value = *(float*)&valueRaw; m_lastPoint = nextPoint; return true; }