Wednesday, August 17, 2011

Writing a TEDS (IEEE 1451.4) parser.

I was tasked recently with writing a parser for TEDS bit stream data. There is an IEEE published paper with an overview of the standard but it doesn't give any examples for implementation. I was finally able to find a paper that gave examples but still had trouble parsing the data. I finally realized there is not only 2 selector bits after the basic TEDS block but selector bits inside the standard template (2 in the case of template 25). Here are my main findings:

  • It depends on the software interface to get the bit-stream, but bits usually start their indexing from left to right. If you are using a language that assumes right to left bit encoding, the stream indices will need to be reversed (eg. 010110002 translates to 00011010= 2610).
  • If you are using an little-endian architecture (eg. x86), then the byte order will need to be converted from big-endian (reverse the byte indices). It is big-endian since the standard is defined for networks and thus has a network byte order.
  • To calculate ConRelRes values, use the formula...
    <start_value> * [1 + 2 * <tolerance>] ^ <teds_value>
    In the published paper, you will see an entry like ConRelRes(5E-7 to 172, +- 0.15%) in the "Data Type (and Range)" field. 5E-7 is the start value, 0.15 is the tolerance and the TEDS value is whatever value gets parsed from the bit stream.

Here are some helper functions to parse TEDS correctly on the .NET platform (x86 systems).
int _streamOffset = 0;

private System.Collections.BitArray _GetBits(System.Collections.BitArray bits, int length)
{
 System.Collections.BitArray result = new System.Collections.BitArray(length);
 for (int index = 0; index <= length - 1; index++) {
  result[index] = bits[_streamOffset + index];
 }
 return result;
}
private byte[] _GetBytes(System.Collections.BitArray bits)
{
 int byteCount = Convert.ToInt32(Math.Ceiling(bits.Count / 8));
 int padCount = (byteCount * 8) - bits.Count;
 byte[] bytes = new byte[byteCount];
 int byteIndex = 0;
 int bitIndex = 0;
 System.Collections.BitArray reverseBits = new System.Collections.BitArray(bits.Count + padCount);

 //byte align new bit array
 for (int index = 0; index <= padCount - 1; index++) {
  reverseBits[index] = false;
 }
 //reverse bits to read from right to left
 for (int index = 0; index <= bits.Length - 1; index++) {
  reverseBits[padCount + index] = bits[bits.Length - 1 - index];
 }

 //create byte array from byte aligned right to left bits
 for (int index = 0; index <= reverseBits.Count - 1; index++) {
  if ((reverseBits[index])) {
   bytes[byteIndex] = bytes[byteIndex] | Convert.ToByte(1 << (7 - bitIndex));
  }

  bitIndex += 1;
  if ((bitIndex == 8)) {
   bitIndex = 0;
   byteIndex += 1;
  }
 }

 return bytes;
}

private bool _GetBoolean(System.Collections.BitArray bits)
{
 bool result = bits[_streamOffset];
 _streamOffset += 1;
 return result;
}
private char _GetChar(System.Collections.BitArray bits, int length)
{
 char result = BitConverter.ToChar(new byte[] {
  0,
  _GetBytes(_GetBits(bits, length))[0]
 }, 0);
 _streamOffset += length;
 return result;
}
private int _GetInteger(System.Collections.BitArray bits, int length)
{
 byte[] intBytes = new byte[] {
  0,
  0,
  0,
  0
 };
 byte[] bytes = _GetBytes(_GetBits(bits, length));
 int result = 0;
 //pad out rest of bytes so it is int32 aligned and convert endianess
 for (int index = 0; index <= bytes.Length - 1; index++) {
  intBytes[index] = bytes[bytes.Length - 1 - index];
 }
 result = Convert.ToInt32(BitConverter.ToUInt32(intBytes, 0));
 _streamOffset += length;
 return result;
}