private uint DecodeQuality(int code, TsscPointMetadata nextPoint) { uint quality = code == TsscCodeWords.Quality2 ? nextPoint.PrevQuality2 : Encoding7Bit.ReadUInt32(m_data, ref m_position); nextPoint.PrevQuality2 = nextPoint.PrevQuality1; nextPoint.PrevQuality1 = quality; return(quality); }
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(); m_lastPoint = new(null, ReadBit, ReadBits5); m_data = Array.Empty <byte>(); 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) { switch (code) { case TsscCodeWords.PointIDXOR4: lastPoint.PrevNextPointId1 = ReadBits4() ^ lastPoint.PrevNextPointId1; break; case TsscCodeWords.PointIDXOR8: lastPoint.PrevNextPointId1 = m_data[m_position] ^ lastPoint.PrevNextPointId1; m_position += 1; break; case TsscCodeWords.PointIDXOR12: lastPoint.PrevNextPointId1 = ReadBits4() ^ (m_data[m_position] << 4) ^ lastPoint.PrevNextPointId1; m_position += 1; break; case TsscCodeWords.PointIDXOR16: lastPoint.PrevNextPointId1 = m_data[m_position] ^ (m_data[m_position + 1] << 8) ^ lastPoint.PrevNextPointId1; m_position += 2; break; case TsscCodeWords.PointIDXOR20: lastPoint.PrevNextPointId1 = ReadBits4() ^ (m_data[m_position] << 4) ^ (m_data[m_position + 1] << 12) ^ lastPoint.PrevNextPointId1; m_position += 2; break; case TsscCodeWords.PointIDXOR24: lastPoint.PrevNextPointId1 = m_data[m_position] ^ (m_data[m_position + 1] << 8) ^ (m_data[m_position + 2] << 16) ^ lastPoint.PrevNextPointId1; m_position += 3; break; case TsscCodeWords.PointIDXOR32: lastPoint.PrevNextPointId1 = m_data[m_position] ^ (m_data[m_position + 1] << 8) ^ (m_data[m_position + 2] << 16) ^ (m_data[m_position + 3] << 24) ^ lastPoint.PrevNextPointId1; m_position += 4; break; default: throw new($"Invalid code received {code} at position {m_position} with last position {m_lastPosition}"); } }
/// <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 int id, out long timestamp, out uint quality, out float value) { 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(); switch (code) { case TsscCodeWords.EndOfStream: { ClearBitStream(); id = 0; timestamp = 0; quality = 0; value = 0; return(false); } case <= TsscCodeWords.PointIDXOR32: { DecodePointID(code, m_lastPoint); code = m_lastPoint.ReadCode(); if (code < TsscCodeWords.TimeDelta1Forward) { throw new($"Expecting code >= {TsscCodeWords.TimeDelta1Forward} Received {code} at position {m_position} with last position { m_lastPosition}"); } break; } } id = m_lastPoint.PrevNextPointId1; TsscPointMetadata nextPoint = m_points[m_lastPoint.PrevNextPointId1]; if (nextPoint is null) { nextPoint = new(null, ReadBit, ReadBits5); m_points[id] = nextPoint; nextPoint.PrevNextPointId1 = id + 1; } if (code <= TsscCodeWords.TimeXOR7Bit) { timestamp = DecodeTimestamp(code); code = m_lastPoint.ReadCode(); if (code < TsscCodeWords.Quality2) { throw new($"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($"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; switch (code) { case TsscCodeWords.Value1: valueRaw = nextPoint.PrevValue1; break; case TsscCodeWords.Value2: valueRaw = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; break; case TsscCodeWords.Value3: valueRaw = nextPoint.PrevValue3; nextPoint.PrevValue3 = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; break; case TsscCodeWords.ValueZero: valueRaw = 0; nextPoint.PrevValue3 = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; break; default: switch (code) { case TsscCodeWords.ValueXOR4: valueRaw = (uint)ReadBits4() ^ nextPoint.PrevValue1; break; case TsscCodeWords.ValueXOR8: valueRaw = m_data[m_position] ^ nextPoint.PrevValue1; m_position += 1; break; case TsscCodeWords.ValueXOR12: valueRaw = (uint)ReadBits4() ^ (uint)(m_data[m_position] << 4) ^ nextPoint.PrevValue1; m_position += 1; break; case TsscCodeWords.ValueXOR16: valueRaw = m_data[m_position] ^ (uint)(m_data[m_position + 1] << 8) ^ nextPoint.PrevValue1; 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 += 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 += 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 += 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 += 4; break; default: throw new($"Invalid code received {code} at position {m_position} with last position { m_lastPosition}"); } nextPoint.PrevValue3 = nextPoint.PrevValue2; nextPoint.PrevValue2 = nextPoint.PrevValue1; nextPoint.PrevValue1 = valueRaw; break; } 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(int 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 is null) { point = new(WriteBits, null, null) { PrevNextPointId1 = 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; switch (bitsChanged) { case <= Bits4: m_lastPoint.WriteCode(TsscCodeWords.ValueXOR4); WriteBits((byte)bitsChanged & 15, 4); break; case <= Bits8: m_lastPoint.WriteCode(TsscCodeWords.ValueXOR8); m_data[m_position] = (byte)bitsChanged; m_position++; break; case <= Bits12: m_lastPoint.WriteCode(TsscCodeWords.ValueXOR12); WriteBits((byte)bitsChanged & 15, 4); m_data[m_position] = (byte)(bitsChanged >> 4); m_position++; break; case <= Bits16: m_lastPoint.WriteCode(TsscCodeWords.ValueXOR16); m_data[m_position] = (byte)bitsChanged; m_data[m_position + 1] = (byte)(bitsChanged >> 8); m_position += 2; break; case <= 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 += 2; break; case <= 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 += 3; break; case <= 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 += 3; break; default: 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 += 4; break; } point.PrevValue3 = point.PrevValue2; point.PrevValue2 = point.PrevValue1; point.PrevValue1 = valueRaw; } m_lastPoint = point; return(true); }