/**
* Copyright (c) Codice Foundation
* <p/>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p/>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package org.codice.ddf.libs.klv;
import static java.lang.System.arraycopy;
import static org.codice.ddf.libs.klv.Utilities.intToBytes;
import static org.codice.ddf.libs.klv.Utilities.longToBytes;
import static org.codice.ddf.libs.klv.Utilities.shortToBytes;
import static org.codice.ddf.libs.klv.data.Klv.KeyLength;
import static org.codice.ddf.libs.klv.data.Klv.LengthEncoding;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.collection.IsMapContaining.hasKey;
import static org.hamcrest.number.IsCloseTo.closeTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.codice.ddf.libs.klv.data.numerical.KlvByte;
import org.codice.ddf.libs.klv.data.numerical.KlvDouble;
import org.codice.ddf.libs.klv.data.numerical.KlvFloat;
import org.codice.ddf.libs.klv.data.numerical.KlvInt;
import org.codice.ddf.libs.klv.data.numerical.KlvIntegerEncodedFloatingPoint;
import org.codice.ddf.libs.klv.data.numerical.KlvLong;
import org.codice.ddf.libs.klv.data.numerical.KlvShort;
import org.codice.ddf.libs.klv.data.numerical.KlvUnsignedByte;
import org.codice.ddf.libs.klv.data.numerical.KlvUnsignedShort;
import org.codice.ddf.libs.klv.data.raw.KlvBytes;
import org.codice.ddf.libs.klv.data.set.KlvLocalSet;
import org.codice.ddf.libs.klv.data.text.KlvString;
import org.junit.BeforeClass;
import org.junit.Test;
public class KlvDecoderTest {
private static final String UAS_DATALINK_LOCAL_SET_UNIVERSAL_KEY =
"UAS Datalink Local Set Universal Key";
private static final String TIMESTAMP = "timestamp";
private static final String UAS_LS_VERSION_NUMBER = "UAS LS Version Number";
private static final String PLATFORM_HEADING_ANGLE = "platform heading angle";
private static final String PLATFORM_PITCH_ANGLE = "platform pitch angle";
private static final String PLATFORM_ROLL_ANGLE = "platform roll angle";
private static final String IMAGE_SOURCE_SENSOR = "image source sensor";
private static final String IMAGE_COORDINATE_SYSTEM = "image coordinate system";
private static final String SENSOR_LATITUDE = "sensor latitude";
private static final String SENSOR_LONGITUDE = "sensor longitude";
private static final String SENSOR_TRUE_ALTITUDE = "sensor true altitude";
private static final String SENSOR_HORIZONTAL_FOV = "sensor horizontal fov";
private static final String SENSOR_VERTICAL_FOV = "sensor vertical fov";
private static final String SENSOR_RELATIVE_AZIMUTH_ANGLE = "sensor relative azimuth angle";
private static final String SENSOR_RELATIVE_ELEVATION_ANGLE = "sensor relative elevation angle";
private static final String SENSOR_RELATIVE_ROLL_ANGLE = "sensor relative roll angle";
private static final String SLANT_RANGE = "slant range";
private static final String TARGET_WIDTH = "target width";
private static final String FRAME_CENTER_LATITUDE = "frame center latitude";
private static final String FRAME_CENTER_LONGITUDE = "frame center longitude";
private static final String FRAME_CENTER_ELEVATION = "frame center elevation";
private static final String TARGET_LOCATION_LATITUDE = "target location latitude";
private static final String TARGET_LOCATION_LONGITUDE = "target location longitude";
private static final String TARGET_LOCATION_ELEVATION = "target location elevation";
private static final String PLATFORM_GROUND_SPEED = "platform ground speed";
private static final String GROUND_RANGE = "ground range";
private static final String CHECKSUM = "checksum";
private static final Map<String, Object> EXPECTED_VALUES = new HashMap<>();
private static final Set<KlvDataElement> DATA_ELEMENTS = new HashSet<>();
@BeforeClass
public static void setUpClass() {
// The test KLV is a UAS Datalink Local Set (MISB ST 0601).
EXPECTED_VALUES.put(TIMESTAMP, 1245257585099653L);
EXPECTED_VALUES.put(UAS_LS_VERSION_NUMBER, (byte) 1);
EXPECTED_VALUES.put(PLATFORM_HEADING_ANGLE, 15675);
EXPECTED_VALUES.put(PLATFORM_PITCH_ANGLE, (short) 5504);
EXPECTED_VALUES.put(PLATFORM_ROLL_ANGLE, (short) 338);
EXPECTED_VALUES.put(IMAGE_SOURCE_SENSOR, "EON");
EXPECTED_VALUES.put(IMAGE_COORDINATE_SYSTEM, "Geodetic WGS84");
EXPECTED_VALUES.put(SENSOR_LATITUDE, 1304747195);
EXPECTED_VALUES.put(SENSOR_LONGITUDE, -1314362114);
EXPECTED_VALUES.put(SENSOR_TRUE_ALTITUDE, 8010);
EXPECTED_VALUES.put(SENSOR_HORIZONTAL_FOV, 133);
EXPECTED_VALUES.put(SENSOR_VERTICAL_FOV, 75);
EXPECTED_VALUES.put(SENSOR_RELATIVE_AZIMUTH_ANGLE, 550031997L);
EXPECTED_VALUES.put(SENSOR_RELATIVE_ELEVATION_ANGLE, -52624680);
EXPECTED_VALUES.put(SENSOR_RELATIVE_ROLL_ANGLE, 4273523553L);
EXPECTED_VALUES.put(SLANT_RANGE, 9387617L);
EXPECTED_VALUES.put(TARGET_WIDTH, 457);
EXPECTED_VALUES.put(FRAME_CENTER_LATITUDE, 1306364970);
EXPECTED_VALUES.put(FRAME_CENTER_LONGITUDE, -1312907532);
EXPECTED_VALUES.put(FRAME_CENTER_ELEVATION, 2949);
EXPECTED_VALUES.put(TARGET_LOCATION_LATITUDE, 1306364970);
EXPECTED_VALUES.put(TARGET_LOCATION_LONGITUDE, -1312907532);
EXPECTED_VALUES.put(TARGET_LOCATION_ELEVATION, 2949);
EXPECTED_VALUES.put(PLATFORM_GROUND_SPEED, (short) 46);
EXPECTED_VALUES.put(GROUND_RANGE, 9294889L);
EXPECTED_VALUES.put(CHECKSUM, 7263);
DATA_ELEMENTS.add(new KlvLong(new byte[] {0x02}, TIMESTAMP));
DATA_ELEMENTS.add(new KlvByte(new byte[] {0x41}, UAS_LS_VERSION_NUMBER));
DATA_ELEMENTS.add(new KlvUnsignedShort(new byte[] {0x05}, PLATFORM_HEADING_ANGLE));
DATA_ELEMENTS.add(new KlvShort(new byte[] {0x06}, PLATFORM_PITCH_ANGLE));
DATA_ELEMENTS.add(new KlvShort(new byte[] {0x07}, PLATFORM_ROLL_ANGLE));
DATA_ELEMENTS.add(new KlvString(new byte[] {0x0b}, IMAGE_SOURCE_SENSOR));
DATA_ELEMENTS.add(new KlvString(new byte[] {0x0c}, IMAGE_COORDINATE_SYSTEM));
DATA_ELEMENTS.add(new KlvInt(new byte[] {0x0d}, SENSOR_LATITUDE));
DATA_ELEMENTS.add(new KlvInt(new byte[] {0x0e}, SENSOR_LONGITUDE));
DATA_ELEMENTS.add(new KlvUnsignedShort(new byte[] {0x0f}, SENSOR_TRUE_ALTITUDE));
DATA_ELEMENTS.add(new KlvUnsignedShort(new byte[] {0x10}, SENSOR_HORIZONTAL_FOV));
DATA_ELEMENTS.add(new KlvUnsignedShort(new byte[] {0x11}, SENSOR_VERTICAL_FOV));
DATA_ELEMENTS.add(new KlvLong(new byte[] {0x12}, SENSOR_RELATIVE_AZIMUTH_ANGLE));
DATA_ELEMENTS.add(new KlvInt(new byte[] {0x13}, SENSOR_RELATIVE_ELEVATION_ANGLE));
DATA_ELEMENTS.add(new KlvLong(new byte[] {0x14}, SENSOR_RELATIVE_ROLL_ANGLE));
DATA_ELEMENTS.add(new KlvLong(new byte[] {0x15}, SLANT_RANGE));
// Target width isn't actually a 32-bit int in the UAS Datalink Local Set; it's an unsigned
// 16-bit int. However, this KLV encodes the target width using 4 bytes (for some reason).
DATA_ELEMENTS.add(new KlvInt(new byte[] {0x16}, TARGET_WIDTH));
DATA_ELEMENTS.add(new KlvInt(new byte[] {0x17}, FRAME_CENTER_LATITUDE));
DATA_ELEMENTS.add(new KlvInt(new byte[] {0x18}, FRAME_CENTER_LONGITUDE));
DATA_ELEMENTS.add(new KlvUnsignedShort(new byte[] {0x19}, FRAME_CENTER_ELEVATION));
DATA_ELEMENTS.add(new KlvInt(new byte[] {0x28}, TARGET_LOCATION_LATITUDE));
DATA_ELEMENTS.add(new KlvInt(new byte[] {0x29}, TARGET_LOCATION_LONGITUDE));
DATA_ELEMENTS.add(new KlvUnsignedShort(new byte[] {0x2a}, TARGET_LOCATION_ELEVATION));
DATA_ELEMENTS.add(new KlvUnsignedByte(new byte[] {0x38}, PLATFORM_GROUND_SPEED));
DATA_ELEMENTS.add(new KlvLong(new byte[] {0x39}, GROUND_RANGE));
DATA_ELEMENTS.add(new KlvUnsignedShort(new byte[] {0x01}, CHECKSUM));
}
private KlvContext getKLVContext(final Set<? extends KlvDataElement> dataElements) {
final KlvContext localSetContext = new KlvContext(KeyLength.OneByte,
LengthEncoding.OneByte,
dataElements);
final KlvLocalSet outerSet = new KlvLocalSet(new byte[] {0x06, 0x0E, 0x2B, 0x34, 0x02, 0x0B,
0x01, 0x01, 0x0E, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00},
UAS_DATALINK_LOCAL_SET_UNIVERSAL_KEY,
localSetContext);
final Set<KlvDataElement> outerSetContext = Collections.singleton(outerSet);
return new KlvContext(KeyLength.SixteenBytes, LengthEncoding.BER, outerSetContext);
}
@Test
public void testKLVSet() throws Exception {
byte[] klvBytes;
try (final InputStream inputStream = getClass().getClassLoader()
.getResourceAsStream("testKLV.klv")) {
klvBytes = IOUtils.toByteArray(inputStream);
}
final KlvContext klvContext = getKLVContext(DATA_ELEMENTS);
final Map<String, KlvDataElement> decodedDataElements = new KlvDecoder(klvContext).decode(
klvBytes)
.getDataElements();
assertThat(decodedDataElements.size(), is(1));
assertThat(decodedDataElements, hasKey(UAS_DATALINK_LOCAL_SET_UNIVERSAL_KEY));
final KlvContext localSet = ((KlvLocalSet) decodedDataElements.get(
UAS_DATALINK_LOCAL_SET_UNIVERSAL_KEY)).getValue();
final Map<String, KlvDataElement> localSetDataElements = localSet.getDataElements();
assertThat(localSetDataElements.size(), is(DATA_ELEMENTS.size()));
localSetDataElements.forEach((name, dataElement) -> {
final Object expectedValue = EXPECTED_VALUES.get(name);
assertThat(String.format("%s is not %s", name, expectedValue),
dataElement.getValue(),
is(expectedValue));
});
}
private KlvContext decodeKLV(final KeyLength keyLength, final LengthEncoding lengthEncoding,
final KlvDataElement dataElement, final byte[] encodedBytes)
throws KlvDecodingException {
final KlvContext klvContext = new KlvContext(keyLength, lengthEncoding);
klvContext.addDataElement(dataElement);
return new KlvDecoder(klvContext).decode(encodedBytes);
}
private byte[] getValueBytes(final KeyLength keyLength, final LengthEncoding lengthEncoding,
final byte[] encodedBytes) throws KlvDecodingException {
final byte[] key = Arrays.copyOf(encodedBytes, keyLength.value());
final KlvBytes dataElement = new KlvBytes(key, "test");
final KlvContext decodedKlvContext = decodeKLV(keyLength,
lengthEncoding,
dataElement,
encodedBytes);
return ((KlvBytes) decodedKlvContext.getDataElementByName("test")).getValue();
}
@Test
public void testOneByteKey() throws KlvDecodingException {
final byte[] klvBytes = {7, 3, 9, 8, 7};
final byte[] value = getValueBytes(KeyLength.OneByte, LengthEncoding.OneByte, klvBytes);
assertThat(value, is(new byte[] {9, 8, 7}));
}
@Test
public void testTwoByteKey() throws KlvDecodingException {
final byte[] klvBytes = {-14, 99, 3, -1, 0, 1};
final byte[] value = getValueBytes(KeyLength.TwoBytes, LengthEncoding.OneByte, klvBytes);
assertThat(value, is(new byte[] {-1, 0, 1}));
}
@Test
public void testFourByteKey() throws KlvDecodingException {
final byte[] klvBytes = {-14, 99, -55, 101, 3, -1, 0, 1};
final byte[] value = getValueBytes(KeyLength.FourBytes, LengthEncoding.OneByte, klvBytes);
assertThat(value, is(new byte[] {-1, 0, 1}));
}
@Test
public void testSixteenByteKey() throws KlvDecodingException {
final byte[] klvBytes =
{-14, 99, -55, 101, 22, 0, -9, -45, -55, -1, 77, 89, 112, 17, 18, 19, 3, -1, 0, 1};
final byte[] value = getValueBytes(KeyLength.SixteenBytes,
LengthEncoding.OneByte,
klvBytes);
assertThat(value, is(new byte[] {-1, 0, 1}));
}
@Test
// One-byte length encoding has already been tested in all the key length tests.
public void testTwoByteLengthEncoding() throws KlvDecodingException {
final byte[] expectedValueBytes = new byte[256];
Arrays.fill(expectedValueBytes, (byte) 4);
final byte[] klvBytes = ArrayUtils.addAll(new byte[] {5, 1, 0}, expectedValueBytes);
final byte[] value = getValueBytes(KeyLength.OneByte, LengthEncoding.TwoBytes, klvBytes);
assertThat(value, is(expectedValueBytes));
}
@Test
public void testFourByteLengthEncoding() throws KlvDecodingException {
final byte[] expectedValueBytes = new byte[256];
Arrays.fill(expectedValueBytes, (byte) -2);
final byte[] klvBytes = ArrayUtils.addAll(new byte[] {5, 0, 0, 1, 0}, expectedValueBytes);
final byte[] value = getValueBytes(KeyLength.OneByte, LengthEncoding.FourBytes, klvBytes);
assertThat(value, is(expectedValueBytes));
}
@Test
public void testBERLengthEncodingSingleByte() throws KlvDecodingException {
final byte length = 100;
final byte[] expectedValueBytes = new byte[length];
Arrays.fill(expectedValueBytes, (byte) 101);
final byte[] klvBytes = ArrayUtils.addAll(new byte[] {5, length}, expectedValueBytes);
final byte[] value = getValueBytes(KeyLength.OneByte, LengthEncoding.BER, klvBytes);
assertThat(value, is(expectedValueBytes));
}
@Test
public void testBERLengthEncodingMultipleBytes() throws KlvDecodingException {
final byte length = 55;
final byte[] expectedValueBytes = new byte[length];
Arrays.fill(expectedValueBytes, (byte) -25);
final byte[] klvBytes = ArrayUtils.addAll(new byte[] {5, (byte) 0b10000001, length},
expectedValueBytes);
final byte[] value = getValueBytes(KeyLength.OneByte, LengthEncoding.BER, klvBytes);
assertThat(value, is(expectedValueBytes));
}
@Test
public void testByteValue() throws KlvDecodingException {
final byte[] klvBytes = {-8, 1, -128};
final KlvByte klvByte = new KlvByte(new byte[] {-8}, "test");
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvByte,
klvBytes);
final byte value = ((KlvByte) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is((byte) -128));
}
@Test
public void testUnsignedByteValue() throws KlvDecodingException {
final byte[] klvBytes = {-8, 1, -127};
final KlvUnsignedByte klvUnsignedByte = new KlvUnsignedByte(new byte[] {-8}, "test");
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvUnsignedByte,
klvBytes);
final short value =
((KlvUnsignedByte) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is((short) 129));
}
@Test
public void testShortValue() throws KlvDecodingException {
final byte[] klvBytes = {-8, 2, (byte) 0xB6, 0x1f};
final KlvShort klvShort = new KlvShort(new byte[] {-8}, "test");
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvShort,
klvBytes);
final short value = ((KlvShort) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is((short) -18913));
}
@Test
public void testUnsignedShortValue() throws KlvDecodingException {
final byte[] klvBytes = {-8, 2, (byte) 0xB6, 0x1f};
final KlvUnsignedShort klvUnsignedShort = new KlvUnsignedShort(new byte[] {-8}, "test");
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvUnsignedShort,
klvBytes);
final int value =
((KlvUnsignedShort) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(46623));
}
@Test
public void testIntValue() throws KlvDecodingException {
final byte[] klvBytes = {-8, 4, (byte) 0xAF, 0x69, 0x1E, 0x0F};
final KlvInt klvInt = new KlvInt(new byte[] {-8}, "test");
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvInt,
klvBytes);
final int value = ((KlvInt) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(-1352065521));
}
@Test
public void testUnsignedIntValue() throws KlvDecodingException {
final byte[] klvBytes = {-8, 4, (byte) 0xAF, 0x69, 0x1E, 0x0F};
final KlvLong klvUnsignedInt = new KlvLong(new byte[] {-8}, "test");
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvUnsignedInt,
klvBytes);
final long value = ((KlvLong) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(2942901775L));
}
@Test
public void testLongValue() throws KlvDecodingException {
final byte[] klvBytes =
{-8, 8, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0x50, (byte) 0x96,
(byte) 0xE1, (byte) 0xF1};
final KlvLong klvLong = new KlvLong(new byte[] {-8}, "test");
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvLong,
klvBytes);
final long value = ((KlvLong) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(-2942901775L));
}
@Test
public void testFloatValue() throws KlvDecodingException {
final byte[] klvBytes = {-8, 4, 0x46, (byte) 0xA8, 0x7E, 0x59};
final KlvFloat klvFloat = new KlvFloat(new byte[] {-8}, "test");
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvFloat,
klvBytes);
final float value = ((KlvFloat) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(21567.174f));
}
@Test
public void testDoubleValue() throws KlvDecodingException {
final byte[] klvBytes =
{-8, 8, 0x40, (byte) 0xD5, 0x0F, (byte) 0xCB, 0x21, 0x07, (byte) 0xB7, (byte) 0x84};
final KlvDouble klvDouble = new KlvDouble(new byte[] {-8}, "test");
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvDouble,
klvBytes);
final double value =
((KlvDouble) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(21567.173891));
}
@Test
public void testStringValue() throws KlvDecodingException {
final byte[] klvBytes = {-8, 3, 0x4b, 0x4c, 0x56};
final KlvString klvString = new KlvString(new byte[] {-8},
"test",
StandardCharsets.UTF_8.name());
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvString,
klvBytes);
final String value =
((KlvString) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is("KLV"));
}
@Test
// Example value taken from ST0601.8 Tag 36.
public void testFloatingPointEncodedAsUnsignedByte() throws KlvDecodingException {
final byte[] klvBytes = {-8, 1, (byte) 0xB2};
final KlvUnsignedByte klvUnsignedByte = new KlvUnsignedByte(new byte[] {-8}, "test");
final KlvIntegerEncodedFloatingPoint windSpeed = new KlvIntegerEncodedFloatingPoint(
klvUnsignedByte,
0,
(1 << 8) - 1,
0,
100);
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
windSpeed,
klvBytes);
final double value =
((KlvIntegerEncodedFloatingPoint) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(closeTo(69.80392, 1e-5)));
}
@Test
// Example value taken from ST0601.8 Tag 5.
public void testFloatingPointEncodedAsUnsignedShort() throws KlvDecodingException {
final byte[] klvBytes = {-8, 2, 0x71, (byte) 0xC2};
final KlvUnsignedShort klvUnsignedShort = new KlvUnsignedShort(new byte[] {-8}, "test");
final KlvIntegerEncodedFloatingPoint platformHeadingAngle =
new KlvIntegerEncodedFloatingPoint(klvUnsignedShort, 0, (1 << 16) - 1, 0, 360);
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
platformHeadingAngle,
klvBytes);
final double value =
((KlvIntegerEncodedFloatingPoint) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(closeTo(159.9744, 1e-4)));
}
@Test
// Example value taken from ST0601.8 Tag 7, but for some reason their example value is 3.405814,
// which is wrong.
public void testFloatingPointEncodedAsShort() throws KlvDecodingException {
final byte[] klvBytes = {-8, 2, (byte) 0x08, (byte) 0xB8};
final KlvShort klvShort = new KlvShort(new byte[] {-8}, "test");
final KlvIntegerEncodedFloatingPoint platformRollAngle =
// Short.MIN_VALUE is an "out of range" indicator, so it is not included in the range.
new KlvIntegerEncodedFloatingPoint(klvShort,
Short.MIN_VALUE + 1,
Short.MAX_VALUE,
-50,
50);
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
platformRollAngle,
klvBytes);
final double value =
((KlvIntegerEncodedFloatingPoint) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(closeTo(3.405865, 1e-6)));
}
@Test
// Example value taken from ST0601.8 Tag 18, but for some reason their example value is
// 160.719211474396, which is wrong.
public void testFloatingPointEncodedAsUnsignedInt() throws KlvDecodingException {
final byte[] klvBytes = {-8, 4, 0x72, 0x4A, 0x0A, 0x20};
final KlvLong klvUnsignedInt = new KlvLong(new byte[] {-8}, "test");
final KlvIntegerEncodedFloatingPoint sensorRelativeAzimuth =
new KlvIntegerEncodedFloatingPoint(klvUnsignedInt, 0, (1L << 32) - 1, 0, 360);
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
sensorRelativeAzimuth,
klvBytes);
final double value =
((KlvIntegerEncodedFloatingPoint) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(closeTo(160.719211436975, 1e-12)));
}
@Test
// Example value taken from ST0601.8 Tag 19.
public void testFloatingPointEncodedAsInt() throws KlvDecodingException {
final byte[] klvBytes = {-8, 4, (byte) 0x87, (byte) 0xF8, 0x4B, (byte) 0x86};
final KlvInt klvInt = new KlvInt(new byte[] {-8}, "test");
final KlvIntegerEncodedFloatingPoint sensorRelativeElevationAngle =
new KlvIntegerEncodedFloatingPoint(klvInt,
// Short.MIN_VALUE is an "out of range" indicator, so it is not included in the range.
Integer.MIN_VALUE + 1,
Integer.MAX_VALUE,
-180,
180);
final KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
sensorRelativeElevationAngle,
klvBytes);
final double value =
((KlvIntegerEncodedFloatingPoint) decodedKlvContext.getDataElementByName("test")).getValue();
assertThat(value, is(closeTo(-168.792324833941, 1e-12)));
}
@Test
public void testMissingBytes() {
final byte[] klvBytes = {-8, 4, (byte) 0x87, (byte) 0xF8, 0x4B};
final KlvInt klvInt = new KlvInt(new byte[] {-8}, "test");
try {
decodeKLV(KeyLength.OneByte, LengthEncoding.OneByte, klvInt, klvBytes);
fail("Should have thrown a KlvDecodingException.");
} catch (KlvDecodingException e) {
assertThat(e.getCause(), instanceOf(IndexOutOfBoundsException.class));
}
}
private boolean isErrorIndicatedByte(byte value, Optional<Byte> errorValue)
throws KlvDecodingException {
KlvByte klvByte = new KlvByte(new byte[] {0}, "test", errorValue);
KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvByte,
new byte[] {0, 1, value});
return decodedKlvContext.getDataElementByName("test")
.isErrorIndicated();
}
@Test
public void testIsErrorIndicatedByte() throws KlvDecodingException {
assertThat(isErrorIndicatedByte((byte) 1, Optional.of((byte) 1)), is(true));
assertThat(isErrorIndicatedByte((byte) 1, Optional.of((byte) 2)), is(false));
assertThat(isErrorIndicatedByte((byte) 1, Optional.empty()), is(false));
}
@Test
public void testLongToBytes() {
long longValue = 0x0102030405060708L;
byte[] bytes = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
assertThat(longToBytes(longValue), is(bytes));
}
@Test
public void testIntToBytes() {
int intValue = 0x01020304;
byte[] bytes = {0x01, 0x02, 0x03, 0x04};
assertThat(intToBytes(intValue), is(bytes));
}
@Test
public void testShortToBytes() {
short shortValue = 0x0102;
byte[] bytes = {0x01, 0x02};
assertThat(shortToBytes(shortValue), is(bytes));
}
private boolean isErrorIndicatedDouble(double value, Optional<Double> errorValue)
throws KlvDecodingException {
KlvDouble klvDouble = new KlvDouble(new byte[] {0}, "test", errorValue);
byte[] dataBytes = new byte[10];
dataBytes[0] = 0;
dataBytes[1] = 8;
arraycopy(longToBytes(Double.doubleToLongBits(value)), 0, dataBytes, 2, 8);
return isErrorIndicatedDecode(klvDouble, dataBytes);
}
@Test
public void testIsErrorIndicatedDouble() throws KlvDecodingException {
assertThat(isErrorIndicatedDouble(0, Optional.of(0D)), is(true));
assertThat(isErrorIndicatedDouble(1, Optional.of(0D)), is(false));
assertThat(isErrorIndicatedDouble(1, Optional.empty()), is(false));
}
private boolean isErrorIndicatedFloat(float value, Optional<Float> errorValue)
throws KlvDecodingException {
KlvFloat klvFloat = new KlvFloat(new byte[] {0}, "test", errorValue);
byte[] dataBytes = new byte[6];
dataBytes[0] = 0;
dataBytes[1] = 4;
arraycopy(intToBytes(Float.floatToIntBits(value)), 0, dataBytes, 2, 4);
return isErrorIndicatedDecode(klvFloat, dataBytes);
}
private boolean isErrorIndicatedDecode(KlvDataElement klvDataElement, byte[] dataBytes)
throws KlvDecodingException {
KlvContext decodedKlvContext = decodeKLV(KeyLength.OneByte,
LengthEncoding.OneByte,
klvDataElement,
dataBytes);
return decodedKlvContext.getDataElementByName("test")
.isErrorIndicated();
}
@Test
public void testIsErrorIndicatedFloat() throws KlvDecodingException {
assertThat(isErrorIndicatedFloat(0, Optional.of(0F)), is(true));
assertThat(isErrorIndicatedFloat(1, Optional.of(0F)), is(false));
assertThat(isErrorIndicatedFloat(1, Optional.empty()), is(false));
}
private boolean isErrorIndicatedInt(int value, Optional<Integer> errorValue)
throws KlvDecodingException {
KlvInt klvInt = new KlvInt(new byte[] {0}, "test", errorValue);
byte[] dataBytes = new byte[6];
dataBytes[0] = 0;
dataBytes[1] = 4;
arraycopy(intToBytes(value), 0, dataBytes, 2, 4);
return isErrorIndicatedDecode(klvInt, dataBytes);
}
@Test
public void testIsErrorIndicatedInt() throws KlvDecodingException {
assertThat(isErrorIndicatedInt(0, Optional.of(0)), is(true));
assertThat(isErrorIndicatedInt(1, Optional.of(0)), is(false));
assertThat(isErrorIndicatedInt(1, Optional.empty()), is(false));
}
private boolean isErrorIndicatedIntegerIndicatedFloatingPoint(int value,
Optional<Integer> errorValue) throws KlvDecodingException {
KlvIntegerEncodedFloatingPoint klvIntegerEncodedFloatingPoint =
new KlvIntegerEncodedFloatingPoint(new KlvInt(new byte[] {0}, "test", errorValue),
Integer.MIN_VALUE + 1,
Integer.MAX_VALUE,
-90,
90);
byte[] dataBytes = new byte[6];
dataBytes[0] = 0;
dataBytes[1] = 4;
arraycopy(intToBytes(value), 0, dataBytes, 2, 4);
return isErrorIndicatedDecode(klvIntegerEncodedFloatingPoint, dataBytes);
}
@Test
public void testIsErrorIndicatedIntegerEncodedFloatingPoint() throws KlvDecodingException {
assertThat(isErrorIndicatedIntegerIndicatedFloatingPoint(Integer.MIN_VALUE,
Optional.of(Integer.MIN_VALUE)), is(true));
assertThat(isErrorIndicatedIntegerIndicatedFloatingPoint(Integer.MIN_VALUE + 1,
Optional.of(Integer.MIN_VALUE)), is(false));
assertThat(isErrorIndicatedIntegerIndicatedFloatingPoint(Integer.MIN_VALUE,
Optional.empty()), is(false));
assertThat(isErrorIndicatedIntegerIndicatedFloatingPoint(Integer.MIN_VALUE + 1,
Optional.empty()), is(false));
}
private boolean isErrorIndicatedLong(long value, Optional<Long> errorValue)
throws KlvDecodingException {
KlvLong klvLong = new KlvLong(new byte[] {0}, "test", errorValue);
byte[] dataBytes = new byte[10];
dataBytes[0] = 0;
dataBytes[1] = 8;
arraycopy(longToBytes(value), 0, dataBytes, 2, 8);
return isErrorIndicatedDecode(klvLong, dataBytes);
}
@Test
public void testIsErrorIndicatedLong() throws KlvDecodingException {
assertThat(isErrorIndicatedLong(0L, Optional.of(0L)), is(true));
assertThat(isErrorIndicatedLong(1L, Optional.of(0L)), is(false));
assertThat(isErrorIndicatedLong(1L, Optional.empty()), is(false));
}
private boolean isErrorIndicatedShort(short value, Optional<Short> errorValue)
throws KlvDecodingException {
KlvShort klvShort = new KlvShort(new byte[] {0}, "test", errorValue);
byte[] dataBytes = new byte[4];
dataBytes[0] = 0;
dataBytes[1] = 2;
arraycopy(shortToBytes(value), 0, dataBytes, 2, 2);
return isErrorIndicatedDecode(klvShort, dataBytes);
}
@Test
public void testIsErrorIndicatedShort() throws KlvDecodingException {
assertThat(isErrorIndicatedShort((short) 0, Optional.of((short) 0)), is(true));
assertThat(isErrorIndicatedShort((short) 1, Optional.of((short) 0)), is(false));
assertThat(isErrorIndicatedShort((short) 1, Optional.empty()), is(false));
}
private boolean isErrorIndicatedUnsignedByte(short value, Optional<Short> errorValue)
throws KlvDecodingException {
KlvUnsignedByte klvUnsignedByte = new KlvUnsignedByte(new byte[] {0}, "test", errorValue);
byte[] dataBytes = new byte[3];
dataBytes[0] = 0;
dataBytes[1] = 1;
dataBytes[2] = (byte) (0xFF & value);
return isErrorIndicatedDecode(klvUnsignedByte, dataBytes);
}
@Test
public void testIsErrorIndicatedUnsignedByte() throws KlvDecodingException {
assertThat(isErrorIndicatedUnsignedByte((short) 0, Optional.of((short) 0)), is(true));
assertThat(isErrorIndicatedUnsignedByte((short) 1, Optional.of((short) 0)), is(false));
assertThat(isErrorIndicatedUnsignedByte((short) 1, Optional.empty()), is(false));
}
}