Using .NET wrapper for KlvLib

In this article, I'll show how to use a .NET wrapper for KlvLib library.

KlvLib SDK is a C++ library for encoding and decoding Klv data packets. If you need to use it in C#, here is how you can do that.

There is a  KlvLibWr.dll  wrapper ( available on request ) that will help us to do the job.

Let's assume we have to KLV encode a hypothetical MMS data packet. The MMS (MISB Minimum Metadata Set) consists of metadata elements taken from MISB Standard 0601.X to enable the minimum functionality required for both Discovery & Retrieval of source imagery and Situational Awareness Product for ISR mission accomplishment.

We'll use a sample data set from the MISB902 Standard, so it would be easy to check the results.

Tag NameValueInterpretationTLV Hex Bytes
2 UNIX Time Stamp 1231798102000000Mon Jan 12 2009 22:08:22 (UTC) 02 08 00 04 60 50 58 4E 01 80
5Platform Heading Angle 0x71C2159.9744 Degrees05 02 71 C2
6Platform Pitch Angle0xFD3D -0.4315251 Degrees 06 02 FD 3D
7Platform Roll Angle0x08B83.405814 Degrees07 02 08 B8
13Sensor Latitude0x5595B66D60.17682296 Degrees0D 04 55 95 B6 6D
14Sensor Longitude 0x5B5360C4 128.42675904 Degrees0E 04 5B 53 60 C4
15Sensor True Altitude 0xC221 14190.72 Meters 0F 02 C2 21
16Sensor Horizontal FoV0xCD9C144.5713 Degrees10 02 CD 9C
17Sensor Vertical FoV 0xD917152.6436 Degrees 11 02 D9 17
18Sensor Rel. Azimuth Angle0x724A0A20160.71921147 Degrees12 04 72 4A 0A 20
19Sensor Rel. Elevation Angle0x87F84B86-168.79232483 Degrees13 04 87 F8 4B 86
20 Sensor Rel. Roll Angle0x00000000 0.0 Degrees14 04 00 00 00 00
21 Slant Range 0x0383092668590.98 Meters15 04 03 83 09 26
22Target Width0x1281722.8199 Meters16 02 12 81
23 Frame Center Latitude0xF101A229-10.54238863 Degrees17 04 F1 01 A2 29
24Frame Center Longitude0x14BC082B29.15789012 Degrees18 04 14 BC 08 2B
25Frame Center Elevation0x34F33216.037 Meters19 02 34 F3
65UAS LDS Version 0x02 MISB Standard 0601.2 41 01 02
1Checksum0xC84C0xC84C01 02 C8 4C

First, we add a reference to the KlvLibWr.dll to our project along with the corresponding using statement:

using KlvLibWr;

Next, let's define a KlvObject class:

class KlvObject
 {
     public byte Tag { get; set; }
     public object ValueObject { get; set; }
     public byte[] ValueBuf { get; set; }

     public KlvObject()
     {
        Tag = 0;
        ValueObject = null;
        ValueBuf = null; 
     }

      public KlvObject(byte tag, object valueObject)
     {
        Tag = tag;
        ValueObject = valueObject;
        ValueBuf = (byte[])ValueObject;
     }
 }

and fill a list with with the items of this type.

 // Fill KlvObjectList with data ( taken from MISB 0902 sample )
 List<KlvObject> KlvObjectList = new List<KlvObject>();
 KlvObjectList.Add(new KlvObject(0x2, new byte[] { 0x00, 0x04, 0x60, 0x50, 0x58, 0x4E, 0x01, 0x80 }));
 KlvObjectList.Add(new KlvObject(0x5, new byte[] { 0x71, 0xC2 }));
 KlvObjectList.Add(new KlvObject(0x6, new byte[] { 0xFD, 0x3D }));
 KlvObjectList.Add(new KlvObject(0x7, new byte[] { 0x08, 0xB8 }));
 KlvObjectList.Add(new KlvObject(0xD, new byte[] { 0x55, 0x95, 0xB6, 0x6D }));
 KlvObjectList.Add(new KlvObject(0xE, new byte[] { 0x5B, 0x53, 0x60, 0xC4 }));
 KlvObjectList.Add(new KlvObject(0xF, new byte[] { 0xC2, 0x21 }));
 KlvObjectList.Add(new KlvObject(0x10, new byte[] { 0xCD, 0x9C }));
 KlvObjectList.Add(new KlvObject(0x11, new byte[] { 0xD9, 0x17 })); 
 KlvObjectList.Add(new KlvObject(0x12, new byte[] { 0x72, 0x4A, 0x0A, 0x20 })); 
 KlvObjectList.Add(new KlvObject(0x13, new byte[] { 0x87, 0xF8, 0x4B, 0x86 }));
 KlvObjectList.Add(new KlvObject(0x14, new byte[] { 0x00, 0x00, 0x00, 0x00 }));
 KlvObjectList.Add(new KlvObject(0x15, new byte[] { 0x03, 0x83, 0x09, 0x26 }));
 KlvObjectList.Add(new KlvObject(0x16, new byte[] { 0x12, 0x81 }));
 KlvObjectList.Add(new KlvObject(0x17, new byte[] { 0xF1, 0x01, 0xA2, 0x29 }));
 KlvObjectList.Add(new KlvObject(0x18, new byte[] { 0x14, 0xBC, 0x08, 0x2B }));
 KlvObjectList.Add(new KlvObject(0x19, new byte[] { 0x34, 0xF3 }));
 KlvObjectList.Add(new KlvObject(0x41, new byte[] { 0x02 }));

Next, we'll put the code for creating a data buffer into a EncodeDataBuffer function, so we could later reuse it.

byte[] EncodeDataBuffer(List<KlvObject> KlvObjectList)
 {
     byte[] buf = null;

     CKlvEncoderWr klvEncoder = new CKlvEncoderWr();

     klvEncoder.SetOuterKey("060E2B34020B01010E01030101000000"); // ASCII form
     // or in binary form: 
     // klvEncoder.SetOuterKey(new byte[] { 0x06, 0x0E, 0x2B, 0x34, 0x02, 0x0B, 0x01, 0x01, 0x0E, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00 });

     klvEncoder.SetCheckSumKey("01"); // ASCII form
     // or in binary form: 
     // klvEncoder.SetCheckSumKey(new byte[] { 0x01 });

     if (KlvObjectList.Count > 0)
     {
            foreach (var klvObj in KlvObjectList)
            {
                 klvEncoder.AddKlvItem(klvObj.Tag, klvObj.ValueBuf);
            }

           buf = klvEncoder.Encode();
     } 
     return buf;
 }

Now, if we call this method, providing the KlvObjectList array as an argument, we'll get back an encoded buffer:

 // Encode Klv Buffer
 var dataBuffer = EncodeDataBuffer(KlvObjectList);

If we save this buffer and open it with KlvInspector, we'll see a properly formatted MISB 0601.X Klv packet:

The checksum is calculated automatically and set to 0xC8 4C, exactly like in the MISB 0902 standard sample.

Now, let's decode the buffer and see if we can get all our Klv items back (of course, you can load any other klv packet and decode it, but we're going to stick to the data we have).

All we have to do is create a KlvDecoder instance and call its Parse method, providing the buffer.

 // Create decoder instance 
 CKlvDecodeWr klvDecoder = new CKlvDecodeWr();

 // Decode the data buffer and get the array of Klvs 
 CKlvItem[] itemArr = klvDecoder.Parse(dataBuffer, true);

Now, if we examine the returned CKlvItem array, we'll see that all our original values are present there.

Ok, it was really easy to encode and decode a simple Klv packet, but what if we need something more complex? For example, in a real life scenario, you probably need to add some nested Klv data, like Security Metadata Set (defined by MISB 0102.X).

We're not going to discuss a MISB0102 standard here or how to create the Metadata Set buffer - you can use MISB0601Converter for this if you like. Just remember, if you're using KlvLib to create a nested buffer, you should make a separate instance of the encoder.

For the KlvLib, this data is just like any other data buffer. I used KlvInspector to create a sample buffer.

A binary buffer for the above data will be:

01 01 01 02 01 01 03 04 2F 2F 49 4C 04 04 54 65 73 74 0C 01 02 16 02 00 09.

It will have a length of 25 bytes.

So, lets create a new KlvObject with a key 48 (0x30) and length 25 (0x19), filling the value with the security metadata set. Add it to the existing array and encode the buffer.

 byte[] secMetadatSetArr = new byte[] { 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x04, 0x2F, 0x2F, 0x49, 0x4C, 0x04, 0x4, 0x54, 0x65, 0x73, 0x74, 0x0C, 0x01, 0x02, 0x16, 0x02, 0x00, 0x09 };
 KlvObjectList.Add(new KlvObject(0x30, secMetadatSetArr));
 dataBuffer = EncodeDataBuffer(KlvObjectList);

Saving this buffer to a file and opening it with KlvInspector,  will show a properly formatted MISB 0601.X Klv packet where in addition to our old data there is a nested Security Metadata Set.

Decoding it back is exactly the same as shown above.

That's it.

No Comments

Post a Comment