diff --git a/.github/workflows/jdk11.yml b/.github/workflows/jdk11.yml index 9f5e08225..c2189a051 100644 --- a/.github/workflows/jdk11.yml +++ b/.github/workflows/jdk11.yml @@ -74,11 +74,26 @@ jobs: with: file: ./st0903vtrack/target/site/jacoco/jacoco.xml flags: unittests-st0903-vtrack + - name: Upload ST 1002 implementation test results to Codecov + uses: codecov/codecov-action@v1.5.0 + with: + file: ./st1002/target/site/jacoco/jacoco.xml + flags: unittests-st1002 + - name: Upload ST 1010 implementation test results to Codecov + uses: codecov/codecov-action@v1.5.0 + with: + file: ./st1010/target/site/jacoco/jacoco.xml + flags: unittests-st1010 - name: Upload ST 1108 implementation test results to Codecov uses: codecov/codecov-action@v1.5.0 with: file: ./st1108/target/site/jacoco/jacoco.xml flags: unittests-st1108 + - name: Upload ST 1202 implementation test results to Codecov + uses: codecov/codecov-action@v1.5.0 + with: + file: ./st1202/target/site/jacoco/jacoco.xml + flags: unittests-st1202 - name: Upload ST 1206 implementation test results to Codecov uses: codecov/codecov-action@v1.5.0 with: diff --git a/README.md b/README.md index 1fc39f4fb..e4a8c5db8 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,11 @@ The table below lists the status of currently-supported standards: | ST 0808 | Ancillary Text Metadata Sets | Implemented as of ST 0808.2. Local Set support only, no universal set support. Deprecated by MISB. | | | ST 0809 | Meteorological Metadata Local Set | Implemented as of ST 0809.2. No interoperability testing. | | | ST 0903 | Video Moving Target Indicator and Track Metadata | VMTI and VTrack Local Sets implemented as of ST 0903.5. We also support pre-ST0903.4 files. | | +| ST 1002 | Range Motion Imagery | Partly implemented as of ST 1002.2. No interoperability testing. | | +| ST 1010 | Generalized Standard Deviation and Correlation Coefficient Metadata | Partly implemented as of ST 1010.3. No support for ST 1201 formatted standard deviation values. | | | ST 1108 | Motion Imagery Interpretability and Quality Metadata | Implemented as of ST 1108.3. ST 1108.2 and earlier is also supported. No interoperability testing. | | | ST 1201 | Floating Point to Integer Mapping | Fully implemented per ST 1201.5. | | +| ST 1202 | Generalized Transformation Parameters | Mostly implemented as of ST 1202.2. | | | ST 1204 | Motion Imagery Identification System (MIIS) Core Identifier | Implemented as of ST 1204.3. | | | ST 1206 | Synthetic Aperture Radar (SAR) Motion Imagery Metadata | Implemented as of ST 1206.1. Unit tests only, no interoperability testing. | | | ST 1301 | Motion Imagery Identification System (MIIS) - Augmentation Identifiers | Implemented as of ST 1301.2. Validated with CMITT. | | diff --git a/api/src/main/java/org/jmisb/api/common/KlvParseException.java b/api/src/main/java/org/jmisb/api/common/KlvParseException.java index 3b05de492..df4328515 100644 --- a/api/src/main/java/org/jmisb/api/common/KlvParseException.java +++ b/api/src/main/java/org/jmisb/api/common/KlvParseException.java @@ -4,6 +4,7 @@ /** Indicates an error occurred during metadata parsing. */ public class KlvParseException extends Exception { + /** Internal store for inner exception. */ private final byte[] buffer; /** diff --git a/examples/README.md b/examples/README.md index 076900851..a980275de 100644 --- a/examples/README.md +++ b/examples/README.md @@ -52,6 +52,10 @@ This example considered complete. See [its README](parserplugin/README.md) for m It does a console dump of the raw KLV in a file to standard output. In this context, "raw" is the de-multiplexed stream content. This example is considered complete. See [its README](rawklv/README.md) for more information. +## rangeimagegenerator + +It generates a test file with video and ST 1002 Range Image KLV metadata. This example is a work-in-progress. + ## systemout It does a console dump of the KLV metadata in a file to standard output (`System.out` in Java, hence the name). diff --git a/examples/pom.xml b/examples/pom.xml index a08d52291..80494a090 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -23,6 +23,7 @@ rawklv annotations timetransfer + rangeimagegenerator diff --git a/examples/rangeimagegenerator/pom.xml b/examples/rangeimagegenerator/pom.xml new file mode 100644 index 000000000..88e6f0551 --- /dev/null +++ b/examples/rangeimagegenerator/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + org.jmisb + examples + 2.0.0-SNAPSHOT + + rangeimagegenerator + jar + Range image generator example + Example code that generates ST 1002 range image test files. + + org.jmisb.examples.rangeimagegenerator.GeneratorCLI + + + + org.jmisb + jmisb-api-ffmpeg + ${project.version} + + + org.slf4j + slf4j-simple + + + commons-cli + commons-cli + + + org.jmisb + st1002 + ${project.version} + + + + + + com.theoryinpractise + googleformatter-maven-plugin + + + org.apache.maven.plugins + maven-shade-plugin + + + + \ No newline at end of file diff --git a/examples/rangeimagegenerator/src/main/java/module-info.java b/examples/rangeimagegenerator/src/main/java/module-info.java new file mode 100644 index 000000000..5634dedc5 --- /dev/null +++ b/examples/rangeimagegenerator/src/main/java/module-info.java @@ -0,0 +1,11 @@ +module org.jmisb.examples.rangeimagegenerator { + requires org.jmisb.api.ffmpeg; + requires org.jmisb.api; + requires org.jmisb.core; + requires org.jmisb.st1002; + requires org.jmisb.st1010; + requires org.jmisb.st1202; + requires commons.cli; + requires java.desktop; + requires org.slf4j; +} diff --git a/examples/rangeimagegenerator/src/main/java/org/jmisb/examples/rangeimagegenerator/Generator.java b/examples/rangeimagegenerator/src/main/java/org/jmisb/examples/rangeimagegenerator/Generator.java new file mode 100644 index 000000000..04b25a9f8 --- /dev/null +++ b/examples/rangeimagegenerator/src/main/java/org/jmisb/examples/rangeimagegenerator/Generator.java @@ -0,0 +1,211 @@ +package org.jmisb.examples.rangeimagegenerator; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.UUID; +import javax.imageio.ImageIO; +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.st1204.CoreIdentifier; +import org.jmisb.api.video.CodecIdentifier; +import org.jmisb.api.video.IVideoFileOutput; +import org.jmisb.api.video.KlvFormat; +import org.jmisb.api.video.MetadataFrame; +import org.jmisb.api.video.VideoFileOutput; +import org.jmisb.api.video.VideoFrame; +import org.jmisb.api.video.VideoOutputOptions; +import org.jmisb.core.video.TimingUtils; +import org.jmisb.st1002.GeneralizedTransformation; +import org.jmisb.st1002.IRangeImageMetadataValue; +import org.jmisb.st1002.RangeImageCompressionMethod; +import org.jmisb.st1002.RangeImageEnumerations; +import org.jmisb.st1002.RangeImageLocalSet; +import org.jmisb.st1002.RangeImageMetadataKey; +import org.jmisb.st1002.RangeImageSource; +import org.jmisb.st1002.RangeImageryDataType; +import org.jmisb.st1002.ST1002PrecisionTimeStamp; +import org.jmisb.st1002.ST1002VersionNumber; +import org.jmisb.st1002.SinglePointRangeMeasurement; +import org.jmisb.st1002.SinglePointRangeMeasurementColumn; +import org.jmisb.st1002.SinglePointRangeMeasurementRow; +import org.jmisb.st1010.SDCC; +import org.jmisb.st1202.Denominator_X; +import org.jmisb.st1202.Denominator_Y; +import org.jmisb.st1202.GeneralizedTransformationLocalSet; +import org.jmisb.st1202.GeneralizedTransformationParametersKey; +import org.jmisb.st1202.IGeneralizedTransformationMetadataValue; +import org.jmisb.st1202.SDCC_FLP; +import org.jmisb.st1202.ST1202DocumentVersion; +import org.jmisb.st1202.TransformationEnumeration; +import org.jmisb.st1202.X_Numerator_Constant; +import org.jmisb.st1202.X_Numerator_X; +import org.jmisb.st1202.X_Numerator_Y; +import org.jmisb.st1202.Y_Numerator_Constant; +import org.jmisb.st1202.Y_Numerator_X; +import org.jmisb.st1202.Y_Numerator_Y; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Generator { + + private static Logger LOG = LoggerFactory.getLogger(Generator.class); + private final int width = 1280; + private final int height = 960; + private final int bitRate = 500_000; + private final int gopSize = 30; + private final double frameRate = 15.0; + private final double frameDuration = 1.0 / frameRate; + private final int duration = 60; + private KlvFormat klvFormat = KlvFormat.Synchronous; + private CodecIdentifier codec = CodecIdentifier.H264; + private String filename = "rangeimage.ts"; + + public Generator() throws KlvParseException {} + + public void setKlvFormat(KlvFormat klvFormat) { + this.klvFormat = klvFormat; + } + + public void setCodec(CodecIdentifier codec) { + this.codec = codec; + } + + public void setOutputFile(String filename) { + this.filename = filename; + } + + public void generate() throws KlvParseException { + showConfiguration(); + CoreIdentifier coreIdentifier = new CoreIdentifier(); + coreIdentifier.setMinorUUID(UUID.randomUUID()); + coreIdentifier.setVersion(1); + + try (IVideoFileOutput output = + new VideoFileOutput( + new VideoOutputOptions( + width, height, bitRate, frameRate, gopSize, klvFormat, codec))) { + output.open(filename); + + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); + try { + image = ImageIO.read(new File("test1280.jpg")); + } catch (IOException e) { + // TODO: log + } + + final long numFrames = duration * Math.round(frameRate); + long startTime = System.currentTimeMillis(); + double pts = 1000.0 * System.currentTimeMillis(); // Close enough for this. + for (long i = 0; i < numFrames; ++i) { + output.addVideoFrame(new VideoFrame(image, pts * 1.0e-6)); + SortedMap values = new TreeMap<>(); + values.put( + RangeImageMetadataKey.PrecisionTimeStamp, + new ST1002PrecisionTimeStamp((long) pts)); + values.put(RangeImageMetadataKey.DocumentVersion, new ST1002VersionNumber(2)); + values.put( + RangeImageMetadataKey.RangeImageEnumerations, + new RangeImageEnumerations( + RangeImageCompressionMethod.NO_COMPRESSION, + RangeImageryDataType.PERSPECTIVE, + RangeImageSource.RANGE_SENSOR)); + values.put( + RangeImageMetadataKey.SinglePointRangeMeasurement, + new SinglePointRangeMeasurement(8000)); + values.put( + RangeImageMetadataKey.SinglePointRangeMeasurementRowCoordinate, + new SinglePointRangeMeasurementRow(403)); + values.put( + RangeImageMetadataKey.SinglePointRangeMeasurementColumnCoordinate, + new SinglePointRangeMeasurementColumn(803)); + Map + st1202values = new TreeMap<>(); + st1202values.put( + GeneralizedTransformationParametersKey.X_Numerator_x, new X_Numerator_X(0)); + st1202values.put( + GeneralizedTransformationParametersKey.X_Numerator_y, new X_Numerator_Y(0)); + st1202values.put( + GeneralizedTransformationParametersKey.X_Numerator_Constant, + new X_Numerator_Constant(0)); + st1202values.put( + GeneralizedTransformationParametersKey.Y_Numerator_x, new Y_Numerator_X(0)); + st1202values.put( + GeneralizedTransformationParametersKey.Y_Numerator_y, new Y_Numerator_Y(0)); + st1202values.put( + GeneralizedTransformationParametersKey.Y_Numerator_Constant, + new Y_Numerator_Constant(0)); + st1202values.put( + GeneralizedTransformationParametersKey.Denominator_x, new Denominator_X(0)); + st1202values.put( + GeneralizedTransformationParametersKey.Denominator_y, new Denominator_Y(0)); + st1202values.put( + GeneralizedTransformationParametersKey.DocumentVersion, + new ST1202DocumentVersion(2)); + SDCC sdcc = new SDCC(); + sdcc.setValues( + new double[][] { + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0} + }); + st1202values.put(GeneralizedTransformationParametersKey.SDCC, new SDCC_FLP(sdcc)); + st1202values.put( + GeneralizedTransformationParametersKey.TransformationEnumeration, + TransformationEnumeration.CHILD_PARENT); + values.put( + RangeImageMetadataKey.GeneralizedTransformationLocalSet, + new GeneralizedTransformation( + new GeneralizedTransformationLocalSet(st1202values))); + RangeImageLocalSet localSet = new RangeImageLocalSet(values); + output.addMetadataFrame(new MetadataFrame(localSet, pts)); + pts += frameDuration * 1.0e6; + long elapsedTime = System.currentTimeMillis() - startTime; + long requiredElapsedTime = (long) ((i + 1) * frameDuration * 1000.0); + long waitTime = requiredElapsedTime - elapsedTime; + if (waitTime > 0) { + TimingUtils.shortWait(waitTime); + } + } + + } catch (IOException e) { + LOG.error("Failed to write file", e); + } + } + + private void showConfiguration() { + System.out.println("Generating with configuration:"); + System.out.println(toString()); + } + + @Override + public String toString() { + return "Generator{" + + "width=" + + width + + ", height=" + + height + + ", bitRate=" + + bitRate + + ", gopSize=" + + gopSize + + ", frameRate=" + + frameRate + + ", frameDuration=" + + frameDuration + + ", duration=" + + duration + + ",\nklvFormat=" + + klvFormat + + ",\nfilename=" + + filename + + '}'; + } +} diff --git a/examples/rangeimagegenerator/src/main/java/org/jmisb/examples/rangeimagegenerator/GeneratorCLI.java b/examples/rangeimagegenerator/src/main/java/org/jmisb/examples/rangeimagegenerator/GeneratorCLI.java new file mode 100644 index 000000000..76860ce9f --- /dev/null +++ b/examples/rangeimagegenerator/src/main/java/org/jmisb/examples/rangeimagegenerator/GeneratorCLI.java @@ -0,0 +1,57 @@ +package org.jmisb.examples.rangeimagegenerator; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.video.CodecIdentifier; + +/** Range image file generator */ +public class GeneratorCLI { + + /** @param args the command line arguments */ + public static void main(String[] args) throws KlvParseException { + final Options commandLineOptions = new Options(); + commandLineOptions.addOption(new Option("o", "outputFile", true, "Output file name")); + commandLineOptions.addOption( + new Option(null, "coding", true, "The video coding to use (H.264 or H.265)")); + commandLineOptions.addOption(new Option("h", "help", false, "Show help message")); + CommandLineParser commandLineParser = new DefaultParser(); + CommandLine commandLine; + try { + commandLine = commandLineParser.parse(commandLineOptions, args); + Generator generator = new Generator(); + if (commandLine.hasOption("h")) { + showHelp(commandLineOptions); + return; + } + if (commandLine.hasOption("coding")) { + String codecName = commandLine.getOptionValue("coding"); + if (codecName.equalsIgnoreCase("H.264") || codecName.equalsIgnoreCase("H264")) { + generator.setCodec(CodecIdentifier.H264); + } else if (codecName.equalsIgnoreCase("H.265") + || codecName.equalsIgnoreCase("H265")) { + generator.setCodec(CodecIdentifier.H265); + } + } + if (commandLine.hasOption("outputFileBase")) { + generator.setOutputFile(commandLine.getOptionValue("outputFile")); + } + generator.generate(); + } catch (ParseException ex) { + showHelp(commandLineOptions); + } + } + + private static void showHelp(final Options commandLineOptions) { + String header = "Generate test MISB ST 1002 range image files\n\n"; + String footer = + "\nPlease report issues at /~https://github.com/WestRidgeSystems/jmisb/issues"; + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("generator", header, commandLineOptions, footer, true); + } +} diff --git a/examples/rangeimagegenerator/test1280.jpg b/examples/rangeimagegenerator/test1280.jpg new file mode 100644 index 000000000..da768be03 Binary files /dev/null and b/examples/rangeimagegenerator/test1280.jpg differ diff --git a/impl/pom.xml b/impl/pom.xml index 05b17ddbc..980f8c6a5 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -44,6 +44,16 @@ st0809 ${project.version} + + org.jmisb + st1002 + ${project.version} + + + org.jmisb + st1010 + ${project.version} + org.jmisb st1108 diff --git a/impl/src/main/java/module-info.java b/impl/src/main/java/module-info.java index 8aeed2650..04c20bab0 100644 --- a/impl/src/main/java/module-info.java +++ b/impl/src/main/java/module-info.java @@ -5,6 +5,7 @@ requires transitive org.jmisb.st0602; requires transitive org.jmisb.st0808; requires transitive org.jmisb.st0809; + requires transitive org.jmisb.st1010; requires transitive org.jmisb.st1108; requires transitive org.jmisb.st1206; requires transitive org.jmisb.st1301; diff --git a/pom.xml b/pom.xml index b2c032948..ed4e1e50d 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,10 @@ st0809 st0903 st0903vtrack + st1002 + st1010 st1108 + st1202 st1206 st1301 st1403 @@ -64,7 +67,6 @@ viewer elevation examples - @@ -571,6 +573,7 @@ true 11 + true diff --git a/st0601/pom.xml b/st0601/pom.xml index 0d88c323c..060267c45 100644 --- a/st0601/pom.xml +++ b/st0601/pom.xml @@ -30,6 +30,11 @@ st0903 ${project.version} + + ${project.groupId} + st1002 + ${project.version} + ${project.groupId} st1206 diff --git a/st0601/src/main/java/module-info.java b/st0601/src/main/java/module-info.java index bacb494f6..9b3fefe46 100644 --- a/st0601/src/main/java/module-info.java +++ b/st0601/src/main/java/module-info.java @@ -9,6 +9,7 @@ requires org.jmisb.st0102; requires org.jmisb.st0806; requires transitive org.jmisb.st0903; + requires transitive org.jmisb.st1002; requires org.jmisb.st1206; requires org.jmisb.st1601; requires org.jmisb.st1602; diff --git a/st0601/src/main/java/org/jmisb/st0601/NestedRangeImageLocalSet.java b/st0601/src/main/java/org/jmisb/st0601/NestedRangeImageLocalSet.java new file mode 100644 index 000000000..51e6868a2 --- /dev/null +++ b/st0601/src/main/java/org/jmisb/st0601/NestedRangeImageLocalSet.java @@ -0,0 +1,84 @@ +package org.jmisb.st0601; + +import java.util.Set; +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.IKlvKey; +import org.jmisb.api.klv.INestedKlvValue; +import org.jmisb.st1002.IRangeImageMetadataValue; +import org.jmisb.st1002.RangeImageLocalSet; + +/** + * Range Image Local Set (ST 0601 Item 97). + * + *

From ST: + * + *

+ * + *

The Range Image Local Set item allows users to include the Range Image LS (MISB ST 1002) + * within MISB ST 0601. Range Motion Imagery is a temporal sequence of range images. Each range + * image is a collection of range measurements from a sensor to target scene. A range measurement is + * the distance (e.g., meters) from an object (or area) in the scene to the sensor. The KLV + * structures of this standard are intended to allow for flexibility, efficient packing, and future + * extensions. Range Motion Imagery can be used standalone, or in collaboration with other Motion + * Imagery. + * + *

See MISB ST 1002 for generation and usage requirements. + * + *

+ */ +public class NestedRangeImageLocalSet implements IUasDatalinkValue, INestedKlvValue { + private final RangeImageLocalSet rangeImageLocalSet; + + /** + * Create from value. + * + * @param rangeImage the Range Image data as a local set + */ + public NestedRangeImageLocalSet(RangeImageLocalSet rangeImage) { + this.rangeImageLocalSet = rangeImage; + } + + /** + * Create from encoded bytes. + * + * @param bytes The byte array + * @throws KlvParseException if the input is invalid + */ + public NestedRangeImageLocalSet(byte[] bytes) throws KlvParseException { + this.rangeImageLocalSet = RangeImageLocalSet.fromNestedBytes(bytes, 0, bytes.length); + } + + @Override + public byte[] getBytes() { + return this.rangeImageLocalSet.frameMessage(true); + } + + @Override + public String getDisplayableValue() { + return "[Range Image]"; + } + + @Override + public String getDisplayName() { + return "Range Image"; + } + + /** + * Get the Range Image data. + * + * @return the Range Image data as a local set + */ + public RangeImageLocalSet getRangeImage() { + return this.rangeImageLocalSet; + } + + @Override + public IRangeImageMetadataValue getField(IKlvKey tag) { + return this.rangeImageLocalSet.getField(tag); + } + + @Override + public Set getIdentifiers() { + return this.rangeImageLocalSet.getIdentifiers(); + } +} diff --git a/st0601/src/main/java/org/jmisb/st0601/UasDatalinkFactory.java b/st0601/src/main/java/org/jmisb/st0601/UasDatalinkFactory.java index 0989ab227..932709d80 100644 --- a/st0601/src/main/java/org/jmisb/st0601/UasDatalinkFactory.java +++ b/st0601/src/main/java/org/jmisb/st0601/UasDatalinkFactory.java @@ -214,8 +214,7 @@ public static IUasDatalinkValue createValue(UasDatalinkTag tag, byte[] bytes) case TargetWidthExtended: return new TargetWidthExtended(bytes); case RangeImage: - // TODO Implement ST 1002 - return new OpaqueValue(bytes); + return new NestedRangeImageLocalSet(bytes); case Georegistration: return new NestedGeoRegistrationLocalSet(bytes); case CompositeImaging: diff --git a/st0601/src/main/java/org/jmisb/st0601/UasDatalinkTag.java b/st0601/src/main/java/org/jmisb/st0601/UasDatalinkTag.java index 4b98152c7..9779cf455 100644 --- a/st0601/src/main/java/org/jmisb/st0601/UasDatalinkTag.java +++ b/st0601/src/main/java/org/jmisb/st0601/UasDatalinkTag.java @@ -318,7 +318,8 @@ public enum UasDatalinkTag implements IKlvKey { /** Tag 96; Target width within sensor field of view; Value is a {@link TargetWidthExtended}. */ TargetWidthExtended(96), /** - * Tag 97; MISB ST 1002 Range Imaging Local Set metadata items; Value is a {@link OpaqueValue}. + * Tag 97; MISB ST 1002 Range Imaging Local Set metadata items; Value is a {@link + * NestedRangeImageLocalSet}. */ RangeImage(97), /** diff --git a/st0601/src/test/java/org/jmisb/st0601/NestedRangeImageLocalSetTest.java b/st0601/src/test/java/org/jmisb/st0601/NestedRangeImageLocalSetTest.java new file mode 100644 index 000000000..6667cb8b4 --- /dev/null +++ b/st0601/src/test/java/org/jmisb/st0601/NestedRangeImageLocalSetTest.java @@ -0,0 +1,121 @@ +package org.jmisb.st0601; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import org.jmisb.api.common.KlvParseException; +import org.jmisb.st1002.IRangeImageMetadataValue; +import org.jmisb.st1002.RangeImageLocalSet; +import org.jmisb.st1002.RangeImageMetadataKey; +import org.jmisb.st1002.ST1002PrecisionTimeStamp; +import org.jmisb.st1002.ST1002VersionNumber; +import org.jmisb.st1002.SinglePointRangeMeasurement; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class NestedRangeImageLocalSetTest { + + private final byte[] localSetAsByteArray = + new byte[] { + 0x01, + 0x08, + 0x00, + 0x05, + (byte) 0xf2, + (byte) 0xf6, + 0x17, + 0x66, + 0x25, + 0x00, + 0x0b, + 0x01, + 0x02, + 0x0d, + 0x08, + 0x40, + (byte) 0xa5, + (byte) 0xe8, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x15, + 0x02, + (byte) 0x72, + (byte) 0x41 + }; + + @Test + public void testConstructFromLocalSet() { + NestedRangeImageLocalSet uut = new NestedRangeImageLocalSet(makeLocalSet()); + Assert.assertNotNull(uut); + checkLocalSetValues(uut); + assertEquals(uut.getBytes(), localSetAsByteArray); + } + + @Test + public void testConstructFromBytes() throws KlvParseException { + NestedRangeImageLocalSet localSetFromBytes = + new NestedRangeImageLocalSet(localSetAsByteArray); + Assert.assertNotNull(localSetFromBytes); + checkLocalSetValues(localSetFromBytes); + } + + @Test + public void testFactory() throws KlvParseException { + IUasDatalinkValue value = + UasDatalinkFactory.createValue(UasDatalinkTag.RangeImage, localSetAsByteArray); + Assert.assertTrue(value instanceof NestedRangeImageLocalSet); + NestedRangeImageLocalSet localSet = (NestedRangeImageLocalSet) value; + Assert.assertNotNull(localSet); + checkLocalSetValues(localSet); + } + + private void checkLocalSetValues(NestedRangeImageLocalSet nestedLocalSet) { + Assert.assertEquals(nestedLocalSet.getDisplayName(), "Range Image"); + Assert.assertEquals(nestedLocalSet.getDisplayableValue(), "[Range Image]"); + assertTrue(nestedLocalSet.getRangeImage() instanceof RangeImageLocalSet); + assertEquals(nestedLocalSet.getRangeImage().getIdentifiers().size(), 3); + assertEquals(nestedLocalSet.getIdentifiers().size(), 3); + assertTrue( + nestedLocalSet + .getIdentifiers() + .containsAll( + Set.of( + RangeImageMetadataKey.DocumentVersion, + RangeImageMetadataKey.PrecisionTimeStamp, + RangeImageMetadataKey.SinglePointRangeMeasurement))); + IRangeImageMetadataValue docVersion = + nestedLocalSet.getField(RangeImageMetadataKey.DocumentVersion); + assertTrue(docVersion instanceof ST1002VersionNumber); + assertEquals(docVersion.getDisplayName(), "Version Number"); + assertEquals(docVersion.getDisplayableValue(), "ST 1002.2"); + IRangeImageMetadataValue name = + nestedLocalSet.getField(RangeImageMetadataKey.PrecisionTimeStamp); + assertTrue(name instanceof ST1002PrecisionTimeStamp); + assertEquals(name.getDisplayName(), "Precision Time Stamp"); + assertEquals(name.getDisplayableValue(), "1674513652000000"); + IRangeImageMetadataValue version = + nestedLocalSet.getField(RangeImageMetadataKey.SinglePointRangeMeasurement); + assertTrue(version instanceof SinglePointRangeMeasurement); + assertEquals(version.getDisplayName(), "Single Point Range Measurement"); + assertEquals(version.getDisplayableValue(), "2804.100 m"); + } + + private RangeImageLocalSet makeLocalSet() { + SortedMap map = new TreeMap<>(); + map.put(RangeImageMetadataKey.DocumentVersion, new ST1002VersionNumber(2)); + map.put( + RangeImageMetadataKey.PrecisionTimeStamp, + new ST1002PrecisionTimeStamp(1674513652000000L)); + map.put( + RangeImageMetadataKey.SinglePointRangeMeasurement, + new SinglePointRangeMeasurement(2804.1)); + RangeImageLocalSet localSet = new RangeImageLocalSet(map); + return localSet; + } +} diff --git a/st1002/checkstyle.xml b/st1002/checkstyle.xml new file mode 100644 index 000000000..41e751804 --- /dev/null +++ b/st1002/checkstyle.xml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/st1002/checkstyle_suppressions.xml b/st1002/checkstyle_suppressions.xml new file mode 100644 index 000000000..97784b708 --- /dev/null +++ b/st1002/checkstyle_suppressions.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/st1002/pom.xml b/st1002/pom.xml new file mode 100644 index 000000000..f67f9b07e --- /dev/null +++ b/st1002/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + org.jmisb + jmisb + 2.0.0-SNAPSHOT + + st1002 + 2.0.0-SNAPSHOT + ST 1002 Range Motion Imagery + jmisb implementation of ST 1002 Range Motion Imagery + + + org.jmisb + jmisb-core + ${project.version} + + + org.jmisb + jmisb-api + ${project.version} + + + org.jmisb + st1202 + ${project.version} + + + org.slf4j + slf4j-api + + + org.testng + testng + test + + + com.github.valfirst + slf4j-test + test + + + + + + + com.theoryinpractise + googleformatter-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + testng-unit.xml + + + + surefire.testng.verbose + ${surefire.verbosity} + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + edu.berkeley.cs.jqf + jqf-maven-plugin + + + + + + + generate-sbom-cyclonedx + + + performRelease + true + + + + + + org.cyclonedx + cyclonedx-maven-plugin + + + + + + diff --git a/st1002/src/main/java/module-info.java b/st1002/src/main/java/module-info.java new file mode 100644 index 000000000..10efd59e4 --- /dev/null +++ b/st1002/src/main/java/module-info.java @@ -0,0 +1,43 @@ +/** + * MISB ST 1002 Range Motion Imagery implementation for jmisb. + * + *

This module provides an implementation of the MISB ST 1002 standard, which describes Range + * Motion Imagery, its format and supporting metadata. + * + *

Range Motion Imagery is a temporal sequence of Range Images. Each Range Image is a collection + * of Range Measurements from a sensor to target scene. A Range Measurement is the distance (e.g., + * meters) from an object (or area) in the scene to the sensor. The KLV structures of this Standard + * are intended to allow for flexibility, efficient packing, and future extensions. Range Motion + * Imagery can be used standalone or in collaboration with other Motion Imagery. MISB ST 1107 Metric + * Geopositioning Metadata Set provides the basis for collaborating with other Motion Imagery types. + * + *

This standard describes the: + * + *

    + *
  • Perspective Range Motion Imagery and Depth Range Motion Imagery; + *
  • the collection methods of Range Motion Imagery; + *
  • the formats used for storing or transmitting Range Motion Imagery; + *
  • the supporting metadata needed for Range Motion Imagery including: + *
      + *
    • temporal + *
    • uncertainty, and + *
    • compression parameters; + *
    + * and + *
  • the alignment to Collaborative Imagery. + *
+ */ +@SuppressWarnings("module") // That is not a version number - its a document number. +module org.jmisb.st1002 { + requires org.jmisb.api; + requires org.jmisb.st1202; + requires org.slf4j; + + uses org.jmisb.api.klv.IMisbMessageFactory; + + // If this is updated, ensure src/main/resources/META-INF/services is updated + provides org.jmisb.api.klv.IMisbMessageFactory with + org.jmisb.st1002.RangeImageLocalSetFactory; + + exports org.jmisb.st1002; +} diff --git a/st1002/src/main/java/org/jmisb/st1002/GeneralizedTransformation.java b/st1002/src/main/java/org/jmisb/st1002/GeneralizedTransformation.java new file mode 100644 index 000000000..ce1ed917d --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/GeneralizedTransformation.java @@ -0,0 +1,85 @@ +package org.jmisb.st1002; + +import java.util.Set; +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.IKlvKey; +import org.jmisb.api.klv.IKlvValue; +import org.jmisb.api.klv.INestedKlvValue; +import org.jmisb.st1202.GeneralizedTransformationLocalSet; + +/** + * Generalized Transformation (ST 1002 Local Set Item 19). + * + *

The Generalized Transformation is a mathematical transformation used to project information, + * points, or lines from one image plane into a second image plane. The use of the Generalized + * Transformation Local Set, MISB ST 1202, is for specific cases when aligning a Range Image to a + * Collaborative Sensors Image. In this case, the Range Image is said to be a child of the + * Collaborative Image (i.e., the parent image), so the Child-Parent Transformation (CPT) + * enumeration 2 defined in Table 1 of MISB ST 1202, is used. + * + *

The boresighted imaging case is a hardware solution, where multiple focal plane arrays are + * simultaneously imaging through a single aperture. The hardware limits the variation in the + * perspective centres and the principal axis of the system to be coincident. Therefore, the + * three-dimensional scene is imaged on two, dependent, focal plane arrays, where the dependency is + * in the geometry. These focal planes do not necessarily have the same orientation or identical + * pixel sizes. Also, the magnification of the two optical paths can be different causing different + * image scales. These effects can be sufficiently modelled using the Generalized Transformation. + * + *

Co-Boresighted
+ * sensors + * + *

When the Range Imagery does not meet the requirements for being co-boresighted, the Range + * Imagery is called non-boresighted, and a transformation is needed to align the Range Imagery with + * the Collaborative Imagery. + * + *

Non-Boresighted sensors + */ +public class GeneralizedTransformation implements IRangeImageMetadataValue, INestedKlvValue { + + private final GeneralizedTransformationLocalSet localSet; + + /** + * Create from encoded bytes. + * + * @param localset value part of the nested local set, without UniversalLabel or overall length + */ + public GeneralizedTransformation(final GeneralizedTransformationLocalSet localset) { + this.localSet = localset; + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array for the nested local set. + * @throws KlvParseException if parsing fails + */ + public GeneralizedTransformation(byte[] bytes) throws KlvParseException { + this.localSet = new GeneralizedTransformationLocalSet(bytes); + } + + @Override + public byte[] getBytes() { + return localSet.frameMessage(true); + } + + @Override + public String getDisplayName() { + return "Generalized Transformation Local Set"; + } + + @Override + public String getDisplayableValue() { + return localSet.displayHeader(); + } + + @Override + public IKlvValue getField(IKlvKey tag) { + return localSet.getField(tag); + } + + @Override + public Set getIdentifiers() { + return localSet.getIdentifiers(); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/IRangeImageMetadataValue.java b/st1002/src/main/java/org/jmisb/st1002/IRangeImageMetadataValue.java new file mode 100644 index 000000000..aaff4563f --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/IRangeImageMetadataValue.java @@ -0,0 +1,17 @@ +package org.jmisb.st1002; + +import org.jmisb.api.klv.IKlvValue; + +/** + * ST 1002 metadata value. + * + *

All ST 1002 Range Image Local Set values implement this interface. + */ +public interface IRangeImageMetadataValue extends IKlvValue { + /** + * Get the encoded bytes. + * + * @return The encoded byte array + */ + byte[] getBytes(); +} diff --git a/st1002/src/main/java/org/jmisb/st1002/NumberOfSectionsInX.java b/st1002/src/main/java/org/jmisb/st1002/NumberOfSectionsInX.java new file mode 100644 index 000000000..555855a02 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/NumberOfSectionsInX.java @@ -0,0 +1,84 @@ +package org.jmisb.st1002; + +import org.jmisb.api.klv.Ber; +import org.jmisb.api.klv.BerDecoder; +import org.jmisb.api.klv.BerEncoder; +import org.jmisb.api.klv.BerField; + +/** + * Number of Sections in X (ST 1002 Range Image Local Set Tag 17). + * + *

Range Imagery Data is a rectangular array of Range Measurements. Range Imagery Data can be + * formatted in whole or in separate parts, where each part is called a Section. Sections are + * rectangular areas that when combined form the full image. Each Section can be compressed to + * provide the most efficient transmission and storage. ST 1002.2 requires Sections to be in simple + * layout. A simple Section layout divides the image into either horizontal or vertical strips. All + * horizontal strips have the same width as the full image but can vary in height as needed as + * illustrated below. + * + *

Five
+ * horizontal sections + * + *

All vertical strips have the same height as the full image but can vary in width as + * illustrated below. + * + *

Three
+ * vertical sections + * + *

When the Range Image is formatted into separate Sections, the Range Image Local Set will + * contain multiple Section Data Variable Length Packs. Specifically, the number of Section Data + * Variable Length Packs will be the Number of Sections in X multiplied by the Number of Sections in + * Y. + * + * @see NumberOfSectionsInY + */ +public class NumberOfSectionsInX implements IRangeImageMetadataValue { + + private final int value; + + /** + * Create from value. + * + * @param numberOfSections the number of Sections, at least 1 + */ + public NumberOfSectionsInX(int numberOfSections) { + if (numberOfSections < 1) { + throw new IllegalArgumentException("ST 1002 Number of Sections in X must be positive"); + } + this.value = numberOfSections; + } + + /** + * Create from encoded bytes. + * + * @param bytes Byte array containing BER-OID formatted value number + */ + public NumberOfSectionsInX(byte[] bytes) { + BerField berField = BerDecoder.decode(bytes, 0, true); + this.value = berField.getValue(); + } + + /** + * Get the number of sections. + * + * @return the number of sections + */ + public int getNumberOfSections() { + return value; + } + + @Override + public byte[] getBytes() { + return BerEncoder.encode(value, Ber.OID); + } + + @Override + public String getDisplayName() { + return "Number of Sections in X"; + } + + @Override + public String getDisplayableValue() { + return String.format("%d", value); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/NumberOfSectionsInY.java b/st1002/src/main/java/org/jmisb/st1002/NumberOfSectionsInY.java new file mode 100644 index 000000000..4fd168030 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/NumberOfSectionsInY.java @@ -0,0 +1,84 @@ +package org.jmisb.st1002; + +import org.jmisb.api.klv.Ber; +import org.jmisb.api.klv.BerDecoder; +import org.jmisb.api.klv.BerEncoder; +import org.jmisb.api.klv.BerField; + +/** + * Number of Sections in Y (ST 1002 Range Image Local Set Tag 18). + * + *

Range Imagery Data is a rectangular array of Range Measurements. Range Imagery Data can be + * formatted in whole or in separate parts, where each part is called a Section. Sections are + * rectangular areas that when combined form the full image. Each Section can be compressed to + * provide the most efficient transmission and storage. ST 1002.2 requires Sections to be in simple + * layout. A simple Section layout divides the image into either horizontal or vertical strips. All + * horizontal strips have the same width as the full image but can vary in height as needed as + * illustrated below. + * + *

Five
+ * horizontal sections + * + *

All vertical strips have the same height as the full image but can vary in width as + * illustrated below. + * + *

Three
+ * vertical sections + * + *

When the Range Image is formatted into separate Sections, the Range Image Local Set will + * contain multiple Section Data Variable Length Packs. Specifically, the number of Section Data + * Variable Length Packs will be the Number of Sections in X multiplied by the Number of Sections in + * Y. + * + * @see NumberOfSectionsInX + */ +public class NumberOfSectionsInY implements IRangeImageMetadataValue { + + private final int value; + + /** + * Create from value. + * + * @param numberOfSections the number of Sections, at least 1 + */ + public NumberOfSectionsInY(int numberOfSections) { + if (numberOfSections < 1) { + throw new IllegalArgumentException("ST 1002 Number of Sections in Y must be positive"); + } + this.value = numberOfSections; + } + + /** + * Create from encoded bytes. + * + * @param bytes Byte array containing BER-OID formatted value number + */ + public NumberOfSectionsInY(byte[] bytes) { + BerField berField = BerDecoder.decode(bytes, 0, true); + this.value = berField.getValue(); + } + + /** + * Get the number of sections. + * + * @return the number of sections + */ + public int getNumberOfSections() { + return value; + } + + @Override + public byte[] getBytes() { + return BerEncoder.encode(value, Ber.OID); + } + + @Override + public String getDisplayName() { + return "Number of Sections in Y"; + } + + @Override + public String getDisplayableValue() { + return String.format("%d", value); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/RangeImageCompressionMethod.java b/st1002/src/main/java/org/jmisb/st1002/RangeImageCompressionMethod.java new file mode 100644 index 000000000..a6a532a18 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/RangeImageCompressionMethod.java @@ -0,0 +1,64 @@ +package org.jmisb.st1002; + +/** + * Range Image Compression Method. + * + *

This enumeration describes the available compression used for reducing the number of bytes of + * the range image. There are currently only two valid values - no compression, and planar fit. See + * ST 1002.2 Section 7 for a description of planar fit. + * + * @see RangeImageEnumerations + */ +public enum RangeImageCompressionMethod { + /** + * Unknown value. + * + *

This is not a valid compression method, and indicates a problem with decoding. + */ + UNKNOWN(-1, "Unknown"), + /** No compression. */ + NO_COMPRESSION(0, "No Compression"), + /** Planar fit. */ + PLANAR_FIT(1, "Planar Fit"); + + /** + * Look up Range Image Compression Method by encoded value. + * + * @param value the encoded (integer) value + * @return the corresponding enumeration value + */ + public static RangeImageCompressionMethod lookup(int value) { + for (RangeImageCompressionMethod method : values()) { + if (method.getEncodedValue() == value) { + return method; + } + } + return UNKNOWN; + } + + private final int encodedValue; + private final String textDescription; + + private RangeImageCompressionMethod(int value, String description) { + this.encodedValue = value; + this.textDescription = description; + } + + /** + * Encoded value. + * + * @return integer representation of the enumeration + */ + public int getEncodedValue() { + return encodedValue; + } + + /** + * Text description. + * + * @return human readable description of the enumeration meaning. + */ + public String getTextDescription() { + return textDescription; + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/RangeImageEnumerations.java b/st1002/src/main/java/org/jmisb/st1002/RangeImageEnumerations.java new file mode 100644 index 000000000..d2a4466e8 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/RangeImageEnumerations.java @@ -0,0 +1,137 @@ +package org.jmisb.st1002; + +import org.jmisb.api.klv.Ber; +import org.jmisb.api.klv.BerDecoder; +import org.jmisb.api.klv.BerEncoder; +import org.jmisb.api.klv.BerField; + +/** + * Range Image Enumerations (ST 1002 Range Image Local Set Tag 12). + * + *

Range Image Enumerations is a set of enumerated values encoded as a BER-OID integer value. + * Range Image Enumerations contains three separate enumerated values: + * + *

    + *
  • Range Image Source, + *
  • Range Image Data Type, and + *
  • Range Image Compression Method. + *
+ * + *

Range Image Source declares how the Range Imagery was created, either from a Range Sensor or + * Computationally Extracted, as described in Section 5. This enumeration has two values, so it + * consumes one bit of the Range Image Enumerations value. + * + *

Range Imagery Data Type declares the type of Range Imagery, either Perspective Range Image or + * Depth Range Image. To allow for further types to be defined in the future this enumeration has + * eight values (0 through 7), where currently only two values are defined: 0=Perspective Range + * Image and 1=Depth Range Image. + * + *

Range Imagery Compression Method declares the method of compression used for reducing the + * number of bytes of the Range Image. One method of compression is called Planar Fit, described in + * ST 1002.2 Section 7. To allow for further compression techniques to be defined in the future, + * this enumeration has eight values (0 through 7), where currently only two values are defined: + * 0=No Compression and 1=Planar Fit. + */ +public class RangeImageEnumerations implements IRangeImageMetadataValue { + + private final RangeImageCompressionMethod compressionMethod; + private final RangeImageryDataType dataType; + private final RangeImageSource rangeImageSource; + private static final int COMPRESSION_METHOD_MASK = 0b00000111; + private static final int DATA_TYPE_BIT_SHIFT = 3; + private static final int DATA_TYPE_MASK = 0b00111000; + private static final int SOURCE_BIT_SHIFT = 6; + private static final int SOURCE_MASK = 0b01000000; + + /** + * Construct from values. + * + * @param compressionMethod the compression method enumeration value + * @param dataType the range imagery data type enumeration value + * @param rangeImageSource the range image source enumeration value + */ + public RangeImageEnumerations( + RangeImageCompressionMethod compressionMethod, + RangeImageryDataType dataType, + RangeImageSource rangeImageSource) { + this.compressionMethod = compressionMethod; + this.dataType = dataType; + this.rangeImageSource = rangeImageSource; + } + + /** + * Construct from encoded bytes. + * + * @param bytes byte array (currently only length 1) + */ + public RangeImageEnumerations(byte[] bytes) { + BerField ber = BerDecoder.decode(bytes, 0, true); + int value = ber.getValue(); + this.compressionMethod = + RangeImageCompressionMethod.lookup(value & COMPRESSION_METHOD_MASK); + this.dataType = + RangeImageryDataType.lookup((value & DATA_TYPE_MASK) >> DATA_TYPE_BIT_SHIFT); + this.rangeImageSource = RangeImageSource.lookup((value & SOURCE_MASK) >> SOURCE_BIT_SHIFT); + } + + @Override + public byte[] getBytes() { + if (compressionMethod == RangeImageCompressionMethod.UNKNOWN) { + throw new IllegalArgumentException("Range Image Compression Method cannot be UNKNOWN"); + } + if (dataType == RangeImageryDataType.UNKNOWN) { + throw new IllegalArgumentException("Range Imagery Data Type cannot be UNKNOWN"); + } + if (rangeImageSource == RangeImageSource.UNKNOWN) { + throw new IllegalArgumentException("Range Image Source cannot be UNKNOWN"); + } + int value = 0; + value += compressionMethod.getEncodedValue(); + value += (dataType.getEncodedValue() << DATA_TYPE_BIT_SHIFT); + value += (rangeImageSource.getEncodedValue() << SOURCE_BIT_SHIFT); + return BerEncoder.encode(value, Ber.OID); + } + + @Override + public String getDisplayName() { + return "Range Image Enumerations"; + } + + @Override + public String getDisplayableValue() { + StringBuilder sb = new StringBuilder(); + sb.append(rangeImageSource.getTextDescription()); + sb.append(" | "); + sb.append(dataType.getTextDescription()); + sb.append(" | "); + sb.append(compressionMethod.getTextDescription()); + return sb.toString(); + } + + /** + * Range image compression method. + * + * @return the range image compression method as an enumerated value. + */ + public RangeImageCompressionMethod getCompressionMethod() { + return this.compressionMethod; + } + + /** + * Range imagery data type. + * + * @return the range imagery data type as an enumerated value. + */ + public RangeImageryDataType getDataType() { + return dataType; + } + + /** + * Range image source. + * + * @return the range image source as an enumerated value. + */ + public RangeImageSource getRangeImageSource() { + return rangeImageSource; + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/RangeImageLocalSet.java b/st1002/src/main/java/org/jmisb/st1002/RangeImageLocalSet.java new file mode 100644 index 000000000..a8087d921 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/RangeImageLocalSet.java @@ -0,0 +1,222 @@ +package org.jmisb.st1002; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import org.jmisb.api.common.InvalidDataHandler; +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.ArrayBuilder; +import org.jmisb.api.klv.BerDecoder; +import org.jmisb.api.klv.BerField; +import org.jmisb.api.klv.CrcCcitt; +import org.jmisb.api.klv.IKlvKey; +import org.jmisb.api.klv.IMisbMessage; +import org.jmisb.api.klv.LdsField; +import org.jmisb.api.klv.LdsParser; +import org.jmisb.api.klv.UniversalLabel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Range Image Local Set. + * + *

This is the core ST 1002 Local Set. + */ +public class RangeImageLocalSet implements IMisbMessage { + + private static final int CRC16_LENGTH = 2; + + /** + * Universal label for Range Image Local Set. + * + *

See ST 1002.2 Table 2. + */ + public static final UniversalLabel RangeImageLocalSetUl = + new UniversalLabel( + new byte[] { + 0x06, 0x0E, 0x2B, 0x34, 0x02, 0x0B, 0x01, 0x01, 0x0E, 0x01, 0x03, 0x03, + 0x0C, 0x00, 0x00, 0x00 + }); + + private static final Logger LOGGER = LoggerFactory.getLogger(RangeImageLocalSet.class); + + /** + * Create a {@link IRangeImageMetadataValue} instance from encoded bytes. + * + * @param tag The tag defining the value type + * @param bytes Encoded bytes + * @return The new instance + * @throws KlvParseException if the parsing of the encoded bytes fails + */ + static IRangeImageMetadataValue createValue(RangeImageMetadataKey tag, byte[] bytes) + throws KlvParseException { + switch (tag) { + case PrecisionTimeStamp: + return new ST1002PrecisionTimeStamp(bytes); + case DocumentVersion: + return new ST1002VersionNumber(bytes); + case RangeImageEnumerations: + return new RangeImageEnumerations(bytes); + case SinglePointRangeMeasurement: + return new SinglePointRangeMeasurement(bytes); + case SinglePointRangeMeasurementUncertainty: + return new SinglePointRangeMeasurementUncertainty(bytes); + case SinglePointRangeMeasurementRowCoordinate: + return new SinglePointRangeMeasurementRow(bytes); + case SinglePointRangeMeasurementColumnCoordinate: + return new SinglePointRangeMeasurementColumn(bytes); + case NumberOfSectionsInX: + return new NumberOfSectionsInX(bytes); + case NumberOfSectionsInY: + return new NumberOfSectionsInY(bytes); + case GeneralizedTransformationLocalSet: + return new GeneralizedTransformation(bytes); + default: + LOGGER.info("Unknown Range Image Metadata tag: {}", tag); + } + return null; + } + + /** Map containing all elements in the message. */ + private final SortedMap map = new TreeMap<>(); + + /** + * Create the local set from the given key/value pairs. + * + * @param values Tag/value pairs to be included in the local set + */ + public RangeImageLocalSet(Map values) { + map.putAll(values); + } + + /** + * Build a Range Image Local Set from encoded bytes. + * + *

The encoding is assumed to be nested (i.e. it does not have the Universal Label or length + * parts). You can use the constructor taking a byte array for the non-nested case. + * + * @param bytes the bytes to build from + * @param offset the index into the {@code bytes} array to start parsing from + * @param len the number of bytes to parse (starting at {@code offset}. + * @return local set corresponding to the provided bytes + * @throws KlvParseException if parsing fails + */ + public static RangeImageLocalSet fromNestedBytes(final byte[] bytes, int offset, int len) + throws KlvParseException { + return new RangeImageLocalSet(parseValues(bytes, offset, len)); + } + + /** + * Build a Range Image Local Set from encoded bytes. + * + * @param bytes the bytes to build from + * @throws KlvParseException if parsing fails + */ + public RangeImageLocalSet(final byte[] bytes) throws KlvParseException { + int offset = UniversalLabel.LENGTH; + BerField len = BerDecoder.decode(bytes, offset, false); + offset += len.getLength(); + SortedMap values = + parseValues(bytes, offset, len.getValue()); + map.putAll(values); + } + + private static SortedMap parseValues( + final byte[] bytes, int offset, int len) throws KlvParseException { + SortedMap map = new TreeMap<>(); + List fields = LdsParser.parseFields(bytes, offset, len); + for (LdsField field : fields) { + RangeImageMetadataKey key = RangeImageMetadataKey.getKey(field.getTag()); + switch (key) { + case Undefined: + LOGGER.info("Unknown Range Image Metadata tag: {}", field.getTag()); + break; + case CRC16CCITT: + if (!CrcCcitt.verify(bytes, field.getData())) { + InvalidDataHandler handler = InvalidDataHandler.getInstance(); + handler.handleInvalidChecksum(LOGGER, "Bad checksum"); + } + break; + case SectionDataVLP: + SectionData sectionData = new SectionData(field.getData()); + if (!map.containsKey(RangeImageMetadataKey.SectionDataVLP)) { + map.put(RangeImageMetadataKey.SectionDataVLP, new SectionDataList()); + } + SectionDataList sectionDataList = + (SectionDataList) map.get(RangeImageMetadataKey.SectionDataVLP); + sectionDataList.add(sectionData); + break; + default: + IRangeImageMetadataValue value = createValue(key, field.getData()); + map.put(key, value); + } + } + return map; + } + + @Override + public byte[] frameMessage(boolean isNested) { + ArrayBuilder builder = new ArrayBuilder(); + for (RangeImageMetadataKey tag : map.keySet()) { + if (tag.equals(RangeImageMetadataKey.Undefined)) { + // We can't serialise this + continue; + } + if (tag.equals(RangeImageMetadataKey.CRC16CCITT)) { + // handled at the end. + continue; + } + if (tag.equals(RangeImageMetadataKey.SectionDataVLP)) { + SectionDataList sectionDataList = (SectionDataList) getField(tag); + List sectionDataPacks = sectionDataList.getPacks(); + for (SectionData sectionData : sectionDataPacks) { + try { + byte[] valueBytes = sectionData.getBytes(); + builder.appendAsOID(tag.getIdentifier()); + builder.appendAsBerLength(valueBytes.length); + builder.append(valueBytes); + } catch (KlvParseException ex) { + LOGGER.warn("Failed to serialize ST 1002 Section Data:" + ex.getMessage()); + } + } + } else { + builder.appendAsOID(tag.getIdentifier()); + byte[] valueBytes = getField(tag).getBytes(); + builder.appendAsBerLength(valueBytes.length); + builder.append(valueBytes); + } + } + builder.appendAsOID(RangeImageMetadataKey.CRC16CCITT.getIdentifier()); + builder.appendAsBerLength(CRC16_LENGTH); + if (!isNested) { + builder.prependLengthPlus(2); + builder.prepend(RangeImageLocalSetUl); + } + CrcCcitt crc = new CrcCcitt(); + crc.addData(builder.toBytes()); + builder.append(crc.getCrc()); + return builder.toBytes(); + } + + @Override + public Set getIdentifiers() { + return map.keySet(); + } + + @Override + public IRangeImageMetadataValue getField(IKlvKey key) { + return map.get((RangeImageMetadataKey) key); + } + + @Override + public UniversalLabel getUniversalLabel() { + return RangeImageLocalSetUl; + } + + @Override + public String displayHeader() { + return "ST 1002 Range Image"; + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/RangeImageLocalSetFactory.java b/st1002/src/main/java/org/jmisb/st1002/RangeImageLocalSetFactory.java new file mode 100644 index 000000000..6e87d17cb --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/RangeImageLocalSetFactory.java @@ -0,0 +1,19 @@ +package org.jmisb.st1002; + +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.IMisbMessageFactory; +import org.jmisb.api.klv.UniversalLabel; + +/** Factory method for RangeImageLocalSet. */ +public class RangeImageLocalSetFactory implements IMisbMessageFactory { + + @Override + public RangeImageLocalSet create(byte[] bytes) throws KlvParseException { + return new RangeImageLocalSet(bytes); + } + + @Override + public UniversalLabel getUniversalLabel() { + return RangeImageLocalSet.RangeImageLocalSetUl; + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/RangeImageMetadataKey.java b/st1002/src/main/java/org/jmisb/st1002/RangeImageMetadataKey.java new file mode 100644 index 000000000..43aac09f8 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/RangeImageMetadataKey.java @@ -0,0 +1,84 @@ +package org.jmisb.st1002; + +import java.util.HashMap; +import java.util.Map; +import org.jmisb.api.klv.IKlvKey; + +/** + * ST 1002 tags - description and numbers. + * + *

These tags are used to identify each element in the local set. + */ +public enum RangeImageMetadataKey implements IKlvKey { + /** Unknown key. This should not be created. */ + Undefined(0), + /** Range Image Precision Time Stamp. */ + PrecisionTimeStamp(1), + /** Document Version. */ + DocumentVersion(11), + /** Range Image Enumerations. */ + RangeImageEnumerations(12), + /** Single Point Range Measurement (SPRM). */ + SinglePointRangeMeasurement(13), + /** + * Single Point Range Measurement (SPRM) Uncertainty. + * + *

Range measurement uncertainty. + */ + SinglePointRangeMeasurementUncertainty(14), + /** + * Single Point Range Measurement (SPRM) Row Coordinate. + * + *

Measured Row Coordinate for Range. + */ + SinglePointRangeMeasurementRowCoordinate(15), + /** + * Single Point Range Measurement (SPRM) Column Coordinate. + * + *

Measured Column Coordinate for Range. + */ + SinglePointRangeMeasurementColumnCoordinate(16), + /** Number of Sections in X. */ + NumberOfSectionsInX(17), + /** Number of Sections in Y. */ + NumberOfSectionsInY(18), + /** Generalized Transformation Local Set. */ + GeneralizedTransformationLocalSet(19), + /** Section Data Variable Length Pack. */ + SectionDataVLP(20), + /** CRC-16-CCITT. */ + CRC16CCITT(21); + + private final int tag; + + private static final Map tagTable = new HashMap<>(); + + static { + for (RangeImageMetadataKey key : values()) { + tagTable.put(key.tag, key); + } + } + + private RangeImageMetadataKey(int tag) { + this.tag = tag; + } + + /** + * Get the tag value associated with this enumeration value. + * + * @return integer tag value for the metadata key + */ + public int getIdentifier() { + return tag; + } + + /** + * Look up the metadata key by tag identifier. + * + * @param tag the integer tag value to look up + * @return corresponding metadata key + */ + public static RangeImageMetadataKey getKey(int tag) { + return tagTable.getOrDefault(tag, Undefined); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/RangeImageSource.java b/st1002/src/main/java/org/jmisb/st1002/RangeImageSource.java new file mode 100644 index 000000000..6fba55562 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/RangeImageSource.java @@ -0,0 +1,69 @@ +package org.jmisb.st1002; + +/** + * Range Image Source. + * + *

This enumeration describes how the Range Imagery was created, either from a range sensor (e.g. + * laser range finder, or LIDAR) or computationally extracted. + * + * @see RangeImageEnumerations + */ +public enum RangeImageSource { + /** + * Unknown value. + * + *

This is not a valid image source, and indicates a problem with decoding. + */ + UNKNOWN(-1, "Unknown"), + /** Computationally extracted. */ + COMPUTATIONALLY_EXTRACTED(0, "Computationally Extracted"), + /** Range sensor. */ + RANGE_SENSOR(1, "Range Sensor"); + + /** + * Look up Range Image Source by encoded value. + * + *

This looks up the basic value (without any bit shifting applied, so Range Sensor is {@code + * 0b1}). + * + * @param value the encoded (integer) value + * @return the corresponding enumeration value + */ + public static RangeImageSource lookup(int value) { + for (RangeImageSource source : values()) { + if (source.getEncodedValue() == value) { + return source; + } + } + return UNKNOWN; + } + + private final int encodedValue; + private final String textDescription; + + private RangeImageSource(int value, String description) { + this.encodedValue = value; + this.textDescription = description; + } + + /** + * Encoded value. + * + *

This looks up the basic value (without any bit shifting applied, so Range Sensor is {@code + * 0b1}). + * + * @return integer representation of the enumeration + */ + public int getEncodedValue() { + return encodedValue; + } + + /** + * Text description. + * + * @return human readable description of the enumeration meaning. + */ + public String getTextDescription() { + return textDescription; + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/RangeImageryDataType.java b/st1002/src/main/java/org/jmisb/st1002/RangeImageryDataType.java new file mode 100644 index 000000000..dd51433f8 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/RangeImageryDataType.java @@ -0,0 +1,78 @@ +package org.jmisb.st1002; + +/** + * Range Imagery Data Type. + * + *

This enumeration describes the range imagery, either Perspective Range Image or Depth Range + * Image. Other types may be added in the future, however these two are the only valid values as of + * ST 1002.2. + * + *

* + * + *

Perspective
+ * Range Image Example + * + *

Depth Range Image
+ * Example + * + * @see RangeImageEnumerations + */ +public enum RangeImageryDataType { + /** + * Unknown value. + * + *

This is not a valid data type, and indicates a problem with decoding. + */ + UNKNOWN(-1, "Unknown"), + /** Perspective range image. */ + PERSPECTIVE(0, "Perspective Range Image"), + /** Depth range image. */ + DEPTH(1, "Depth Range Image"); + + /** + * Look up Range Imagery Data Type by encoded value. + * + *

This looks up the basic value (without any bit shifting applied, so Depth is {@code + * 0b001}). + * + * @param value the encoded (integer) value + * @return the corresponding enumeration value + */ + public static RangeImageryDataType lookup(int value) { + for (RangeImageryDataType dataType : values()) { + if (dataType.getEncodedValue() == value) { + return dataType; + } + } + return UNKNOWN; + } + + private final int encodedValue; + private final String textDescription; + + private RangeImageryDataType(int value, String description) { + this.encodedValue = value; + this.textDescription = description; + } + + /** + * Encoded value. + * + *

This looks up the basic value (without any bit shifting applied, so Depth is {@code + * 0b001}). + * + * @return integer representation of the enumeration + */ + public int getEncodedValue() { + return encodedValue; + } + + /** + * Text description. + * + * @return human readable description of the enumeration meaning. + */ + public String getTextDescription() { + return textDescription; + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/ST1002PrecisionTimeStamp.java b/st1002/src/main/java/org/jmisb/st1002/ST1002PrecisionTimeStamp.java new file mode 100644 index 000000000..f9a195b53 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/ST1002PrecisionTimeStamp.java @@ -0,0 +1,61 @@ +package org.jmisb.st1002; + +import java.time.LocalDateTime; +import org.jmisb.api.klv.st0603.ST0603TimeStamp; + +/** + * Range Image Precision Time Stamp (ST 1002 Range Image Local Set Tag 1). + * + *

The Range Image Precision Time Stamp is the time when the measurements of the Range Image + * occurred. This time information is used to coordinate the Range Image with other sources of data, + * such as a collaborative sensors image or other sensor data. The time value is an invocation of + * the MISP Precision Time Stamp, a 64-bit unsigned integer that represents the number of + * microseconds since midnight of January 1st 1970 without leap-seconds, as defined in MISB ST 0603 + * and detailed in the Motion Imagery Handbook. + * + *

The Range Image Precision Time Stamp is required in all Range Image Local Sets, and must be + * the first item. Positioning the Precision Time Stamp tag as the first item facilitates rapidly + * checking whether the Local Set matches the desired time for processing a collaborative image. + */ +public class ST1002PrecisionTimeStamp extends ST0603TimeStamp implements IRangeImageMetadataValue { + /** + * Create from value. + * + * @param microseconds Microseconds since the epoch + */ + public ST1002PrecisionTimeStamp(long microseconds) { + super(microseconds); + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array, length of 8 bytes. + */ + public ST1002PrecisionTimeStamp(byte[] bytes) { + super(bytes); + if (bytes.length < 8) { + throw new IllegalArgumentException( + this.getDisplayName() + " encoding is an 8-byte unsigned int"); + } + } + + /** + * Create from {@code LocalDateTime}. + * + * @param dateTime The date and time + */ + public ST1002PrecisionTimeStamp(LocalDateTime dateTime) { + super(dateTime); + } + + @Override + public final String getDisplayName() { + return "Precision Time Stamp"; + } + + @Override + public byte[] getBytes() { + return getBytesFull(); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/ST1002VersionNumber.java b/st1002/src/main/java/org/jmisb/st1002/ST1002VersionNumber.java new file mode 100644 index 000000000..ea34e82b0 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/ST1002VersionNumber.java @@ -0,0 +1,77 @@ +package org.jmisb.st1002; + +import org.jmisb.api.klv.Ber; +import org.jmisb.api.klv.BerDecoder; +import org.jmisb.api.klv.BerEncoder; +import org.jmisb.api.klv.BerField; + +/** + * Version Number (ST 1002 Range Image Local Set Tag 11). + * + *

The version number is the same of the minor version number of the standard document. For + * example, with MISB ST 1002.1, the version number value is {@code 1} and with ST 1002.2, the + * version number value is {@code 2}. + */ +public class ST1002VersionNumber implements IRangeImageMetadataValue { + + private final int version; + + /** + * The currently supported revision is 1002.2. + * + *

This may be useful in the constructor. + */ + public static final short ST_VERSION_NUMBER = 2; + + /** + * Create from value. + * + *

The current version is available as {@link #ST_VERSION_NUMBER}. + * + * @param versionNumber The version number + */ + public ST1002VersionNumber(int versionNumber) { + if (versionNumber < 0) { + throw new IllegalArgumentException("ST 1002 Version Number cannot be negative"); + } + this.version = versionNumber; + } + + /** + * Create from encoded bytes. + * + * @param bytes Byte array containing BER-OID formatted version number + */ + public ST1002VersionNumber(byte[] bytes) { + BerField berField = BerDecoder.decode(bytes, 0, true); + this.version = berField.getValue(); + } + + /** + * Get the document version number. + * + * @return The version number + */ + public int getVersion() { + return version; + } + + @Override + public byte[] getBytes() { + return BerEncoder.encode(version, Ber.OID); + } + + @Override + public String getDisplayName() { + return "Version Number"; + } + + @Override + public String getDisplayableValue() { + if (version == 0) { + return "ST 1002"; + } else { + return "ST 1002." + version; + } + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/SectionData.java b/st1002/src/main/java/org/jmisb/st1002/SectionData.java new file mode 100644 index 000000000..397a95bd1 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/SectionData.java @@ -0,0 +1,316 @@ +package org.jmisb.st1002; + +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.ArrayBuilder; +import org.jmisb.api.klv.Ber; +import org.jmisb.api.klv.BerDecoder; +import org.jmisb.api.klv.BerEncoder; +import org.jmisb.api.klv.BerField; +import org.jmisb.api.klv.IKlvValue; +import org.jmisb.api.klv.st1303.MDAPDecoder; +import org.jmisb.api.klv.st1303.NaturalFormatEncoder; +import org.jmisb.core.klv.PrimitiveConverter; + +/** + * Section Data Variable Length Pack. + * + *

Section data, along with its supporting information is formatted in a Variable Length Pack + * (VLP) (see SMPTE ST 336) called the Section Data VLP. The information in each Section Data VLP + * includes Section coordinates, Section data array, uncertainty values, and optional compression + * parameters. The optional compression parameters are truncated from the VLP (along with their + * lengths) as a group (in a similar fashion to MISB RP 0701 Floating Length Packs). The figure + * below illustrates a Section Data VLP with each item in the VLP prefixed with its item length. The + * green items indicate required values (along with their lengths in blue); the pink item can be + * zero-length; and the yellow items can be truncated (along with their lengths). + * + *

Section Data
+ * Variable Length Pack + */ +public class SectionData implements IKlvValue { + + private final int sectionNumberX; + private final int sectionNumberY; + private final double[][] arrayOfMeasuredValues; + private final double[][] arrayOfUncertaintyValues; + private final double planeXScaleFactor; + private final double planeYScaleFactor; + private final double planeConstantValue; + + /** + * Create from values. + * + *

This constructor assumes the planar fit compression factors are not included. + * + * @param x the section number X value + * @param y the section number Y value + * @param measurements the array of measurements + * @param uncertainties the array of uncertainties (can be null) + */ + public SectionData(int x, int y, double[][] measurements, double[][] uncertainties) { + this.sectionNumberX = x; + this.sectionNumberY = y; + this.arrayOfMeasuredValues = measurements.clone(); + if (uncertainties == null) { + this.arrayOfUncertaintyValues = null; + } else { + this.arrayOfUncertaintyValues = uncertainties.clone(); + } + this.planeXScaleFactor = 0.0; + this.planeYScaleFactor = 0.0; + this.planeConstantValue = 0.0; + } + + /** + * Create from values. + * + * @param x the section number X value + * @param y the section number Y value + * @param measurements the array of measurements + * @param uncertainties the array of uncertainties (can be null) + * @param planeXscale the plane X scale factor + * @param planeYscale the plane Y scale factor + * @param planeConstant the plane constant value + */ + public SectionData( + int x, + int y, + double[][] measurements, + double[][] uncertainties, + double planeXscale, + double planeYscale, + double planeConstant) { + this.sectionNumberX = x; + this.sectionNumberY = y; + this.arrayOfMeasuredValues = measurements.clone(); + if (uncertainties == null) { + this.arrayOfUncertaintyValues = null; + } else { + this.arrayOfUncertaintyValues = uncertainties.clone(); + } + this.planeXScaleFactor = planeXscale; + this.planeYScaleFactor = planeYscale; + this.planeConstantValue = planeConstant; + } + + /** + * Create from encoded bytes. + * + *

The encoded byte array is assumed to start from the first item length ({@code L1} in the + * diagram above, and does not include the overall VLP Key or Length field. + * + * @param bytes the encoded byte array + * @throws KlvParseException if parsing fails + */ + public SectionData(byte[] bytes) throws KlvParseException { + MDAPDecoder mdapDecoder = new MDAPDecoder(); + int offset = 0; + BerField l1field = BerDecoder.decode(bytes, offset, false); + offset += l1field.getLength(); + BerField sectionXfield = BerDecoder.decode(bytes, offset, true); + offset += sectionXfield.getLength(); + sectionNumberX = sectionXfield.getValue(); + BerField l2field = BerDecoder.decode(bytes, offset, false); + offset += l2field.getLength(); + BerField sectionYfield = BerDecoder.decode(bytes, offset, true); + offset += sectionYfield.getLength(); + sectionNumberY = sectionYfield.getValue(); + BerField l3field = BerDecoder.decode(bytes, offset, false); + offset += l3field.getLength(); + this.arrayOfMeasuredValues = mdapDecoder.decodeFloatingPoint2D(bytes, offset); + offset += l3field.getValue(); + BerField l4field = BerDecoder.decode(bytes, offset, false); + offset += l4field.getLength(); + if (l4field.getValue() > 0) { + this.arrayOfUncertaintyValues = mdapDecoder.decodeFloatingPoint2D(bytes, offset); + offset += l4field.getValue(); + } else { + this.arrayOfUncertaintyValues = null; + } + if (offset < bytes.length) { + BerField l5field = BerDecoder.decode(bytes, offset, false); + offset += l5field.getLength(); + if (l5field.getValue() == 4) { + this.planeXScaleFactor = PrimitiveConverter.toFloat32(bytes, offset); + } else if (l5field.getValue() == 8) { + this.planeXScaleFactor = PrimitiveConverter.toFloat64(bytes, offset); + } else { + throw new KlvParseException( + "unsupported Plane X Scale Factor size: " + l5field.getValue()); + } + offset += l5field.getValue(); + } else { + this.planeXScaleFactor = 0.0; + } + if (offset < bytes.length) { + BerField l6field = BerDecoder.decode(bytes, offset, false); + offset += l6field.getLength(); + if (l6field.getValue() == 4) { + this.planeYScaleFactor = PrimitiveConverter.toFloat32(bytes, offset); + } else if (l6field.getValue() == 8) { + this.planeYScaleFactor = PrimitiveConverter.toFloat64(bytes, offset); + } else { + throw new KlvParseException( + "unsupported Plane Y Scale Factor size: " + l6field.getValue()); + } + offset += l6field.getValue(); + } else { + this.planeYScaleFactor = 0.0; + } + + if (offset < bytes.length) { + BerField l7field = BerDecoder.decode(bytes, offset, false); + offset += l7field.getLength(); + if (l7field.getValue() == 4) { + this.planeConstantValue = PrimitiveConverter.toFloat32(bytes, offset); + } else if (l7field.getValue() == 8) { + this.planeConstantValue = PrimitiveConverter.toFloat64(bytes, offset); + } else { + throw new KlvParseException( + "unsupported Plane Constant size: " + l7field.getValue()); + } + offset += l7field.getValue(); + } else { + this.planeConstantValue = 0.0; + } + } + + /** + * Copy Constructor. + * + * @param other the section data instance to copy values from + */ + public SectionData(SectionData other) { + this.sectionNumberX = other.getSectionNumberX(); + this.sectionNumberY = other.getSectionNumberY(); + this.arrayOfMeasuredValues = other.getArrayOfMeasuredValues().clone(); + if (other.getArrayOfUncertaintyValues() == null) { + this.arrayOfUncertaintyValues = null; + } else { + this.arrayOfUncertaintyValues = other.getArrayOfUncertaintyValues().clone(); + } + this.planeXScaleFactor = other.getPlaneXScaleFactor(); + this.planeYScaleFactor = other.getPlaneYScaleFactor(); + this.planeConstantValue = other.getPlaneConstantValue(); + } + + /** + * Serialise the Section Data to encoded byte array. + * + * @return the byte array + * @throws KlvParseException if serialisation fails. + */ + public byte[] getBytes() throws KlvParseException { + ArrayBuilder builder = new ArrayBuilder(); + byte[] sectionNumberXbytes = BerEncoder.encode(sectionNumberX, Ber.OID); + builder.appendAsBerLength(sectionNumberXbytes.length); + builder.append(sectionNumberXbytes); + byte[] sectionNumberYbytes = BerEncoder.encode(sectionNumberY, Ber.OID); + builder.appendAsBerLength(sectionNumberYbytes.length); + builder.append(sectionNumberYbytes); + NaturalFormatEncoder mdapEncoder = new NaturalFormatEncoder(); + byte[] arrayOfMeasuredValuesBytes = mdapEncoder.encode(arrayOfMeasuredValues); + builder.appendAsBerLength(arrayOfMeasuredValuesBytes.length); + builder.append(arrayOfMeasuredValuesBytes); + if (arrayOfUncertaintyValues == null) { + builder.appendAsBerLength(0); + } else { + byte[] arrayOfUncertaintyValuesBytes = mdapEncoder.encode(arrayOfUncertaintyValues); + builder.appendAsBerLength(arrayOfUncertaintyValuesBytes.length); + builder.append(arrayOfUncertaintyValuesBytes); + } + if ((this.planeXScaleFactor == 0.0) + && (this.planeYScaleFactor == 0.0) + && (this.planeConstantValue == 0.0)) { + // just omit the rest. + } else { + builder.appendAsBerLength(Double.BYTES); + builder.appendAsFloat64Primitive(this.planeXScaleFactor); + builder.appendAsBerLength(Double.BYTES); + builder.appendAsFloat64Primitive(this.planeYScaleFactor); + builder.appendAsBerLength(Double.BYTES); + builder.appendAsFloat64Primitive(this.planeConstantValue); + } + return builder.toBytes(); + } + + /** + * Section Number in X direction. + * + * @return section number as an integer + */ + public int getSectionNumberX() { + return sectionNumberX; + } + + /** + * Section Number in Y direction. + * + * @return section number as an integer. + */ + public int getSectionNumberY() { + return sectionNumberY; + } + + /** + * Array of measured values. + * + * @return the measured values as a 2-dimensional array + */ + public double[][] getArrayOfMeasuredValues() { + return arrayOfMeasuredValues.clone(); + } + /** + * Array of uncertainty values. + * + * @return the uncertainty values as a 2-dimensional array, or null if not provided. + */ + public double[][] getArrayOfUncertaintyValues() { + if (arrayOfUncertaintyValues == null) { + return null; + } + return arrayOfUncertaintyValues.clone(); + } + + /** + * Plane X scale factor. + * + *

This is used for planar fit compression. See ST 1002.2 Section 7. + * + * @return the scale factor as a double. + */ + public double getPlaneXScaleFactor() { + return planeXScaleFactor; + } + + /** + * Plane Y scale factor. + * + *

This is used for planar fit compression. See ST 1002.2 Section 7. + * + * @return the scale factor as a double. + */ + public double getPlaneYScaleFactor() { + return planeYScaleFactor; + } + + /** + * Plane Constant value. + * + *

This is used for planar fit compression. See ST 1002.2 Section 7. + * + * @return the scale factor as a double. + */ + public double getPlaneConstantValue() { + return planeConstantValue; + } + + @Override + public String getDisplayName() { + return "Section Data"; + } + + @Override + public String getDisplayableValue() { + return String.format("Section [%d,%d]", this.sectionNumberX, this.sectionNumberY); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/SectionDataIdentifierKey.java b/st1002/src/main/java/org/jmisb/st1002/SectionDataIdentifierKey.java new file mode 100644 index 000000000..15d9ca4f4 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/SectionDataIdentifierKey.java @@ -0,0 +1,78 @@ +package org.jmisb.st1002; + +import org.jmisb.api.klv.IKlvKey; + +/** Pseudo-key item for Section Data identifier. */ +public class SectionDataIdentifierKey implements IKlvKey, Comparable { + + private final int sectionX; + private final int sectionY; + + /** + * Constructor. + * + *

Section numbers in the simple layout (mandated by ST 1002.2) only vary in one direction (X + * for horizontal composition, Y for vertical composition). + * + * @param x the section number X identifier. + * @param y the section number Y identifier. + */ + public SectionDataIdentifierKey(final int x, final int y) { + this.sectionX = x; + this.sectionY = y; + } + + @Override + public int getIdentifier() { + return sectionX + sectionY - 1; + } + + /** + * Section Number X value. + * + * @return the section number as an integer + */ + public int getSectionX() { + return sectionX; + } + + /** + * Section Number Y value. + * + * @return the section number as an integer + */ + public int getSectionY() { + return sectionY; + } + + @Override + public int compareTo(SectionDataIdentifierKey other) { + return Integer.compare(getIdentifier(), other.getIdentifier()); + } + + @Override + public int hashCode() { + int hash = 3; + hash = 29 * hash + this.sectionX; + hash = 29 * hash + this.sectionY; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final SectionDataIdentifierKey other = (SectionDataIdentifierKey) obj; + if (this.sectionX != other.sectionX) { + return false; + } + return this.sectionY == other.sectionY; + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/SectionDataList.java b/st1002/src/main/java/org/jmisb/st1002/SectionDataList.java new file mode 100644 index 000000000..bfbf859cf --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/SectionDataList.java @@ -0,0 +1,84 @@ +package org.jmisb.st1002; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import org.jmisb.api.klv.IKlvKey; +import org.jmisb.api.klv.IKlvValue; +import org.jmisb.api.klv.INestedKlvValue; + +/** + * Section Data list (ST 1002 Range Image Local Set Tag 20). + * + *

Section data, along with its supporting information is formatted in a Variable Length Pack + * (VLP) (see SMPTE ST 336) called the Section Data VLP. + * + *

Section data is repeatable within the Range Image Local Set. {@code SectionDataList} + * represents zero or more Section Data VLPs. + * + * @see NumberOfSectionsInX + * @see NumberOfSectionsInY + */ +public class SectionDataList implements IRangeImageMetadataValue, INestedKlvValue { + + private final List vlps = new ArrayList<>(); + + @Override + public byte[] getBytes() { + throw new UnsupportedOperationException("Should not be called"); + } + + @Override + public String getDisplayName() { + return "Section Data"; + } + + @Override + public String getDisplayableValue() { + return "[VLPs]"; + } + + @Override + public IKlvValue getField(IKlvKey tag) { + SectionDataIdentifierKey key = (SectionDataIdentifierKey) tag; + for (SectionData vlp : vlps) { + if ((vlp.getSectionNumberX() == key.getSectionX()) + && (vlp.getSectionNumberY() == key.getSectionY())) { + return vlp; + } + } + return null; + } + + @Override + public Set getIdentifiers() { + SortedSet identifiers = new TreeSet<>(); + vlps.forEach( + (SectionData vlp) -> { + identifiers.add( + new SectionDataIdentifierKey( + vlp.getSectionNumberX(), vlp.getSectionNumberY())); + }); + return identifiers; + } + + /** + * Add a Section Data VLP to the list. + * + * @param sectionData the data to add + */ + public void add(SectionData sectionData) { + this.vlps.add(new SectionData(sectionData)); + } + + /** + * Get the Section Data Variable Length Packs. + * + * @return copy of the packs as a list. + */ + public List getPacks() { + return new ArrayList<>(vlps); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurement.java b/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurement.java new file mode 100644 index 000000000..a83592711 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurement.java @@ -0,0 +1,77 @@ +package org.jmisb.st1002; + +import org.jmisb.api.common.KlvParseException; +import org.jmisb.core.klv.PrimitiveConverter; + +/** + * Single Point Range Measurement (ST 1002 Local Set Item 13). + * + *

The Single Point Range Measurement (SPRM) is the measure of distance (in metres) from either + * the principle point, or backplane of a Collaborative Sensor through the image plane to a point in + * the scene. + * + *

These measurement concepts are shown in the following images, which are from MISB ST 1002.2. + * Note that the red line closer to the terrain (green area) represents the focal plane, and that + * the images are unlikely to be to scale in most scenarios. + * + *

Perspective
+ * Range Image Example + * + *

Depth Range Image
+ * Example + * + *

This value can be either a 32-bit or 64-bit IEEE floating point value. + * + * @see SinglePointRangeMeasurementUncertainty + */ +public class SinglePointRangeMeasurement implements IRangeImageMetadataValue { + + private final double value; + + /** + * Create from value. + * + * @param range the range in metres. + */ + public SinglePointRangeMeasurement(double range) { + this.value = range; + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 or 8 + * @throws KlvParseException if the byte array is not of the correct length + */ + public SinglePointRangeMeasurement(byte[] bytes) throws KlvParseException { + try { + this.value = PrimitiveConverter.toFloat64(bytes); + } catch (IllegalArgumentException ex) { + throw new KlvParseException("Single Point Range Measurement is of length 4 or 8 bytes"); + } + } + + @Override + public final String getDisplayName() { + return "Single Point Range Measurement"; + } + + /** + * Get the range. + * + * @return range in metres. + */ + public double getRange() { + return this.value; + } + + @Override + public byte[] getBytes() { + return PrimitiveConverter.float64ToBytes(value); + } + + @Override + public String getDisplayableValue() { + return String.format("%.3f m", this.value); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurementColumn.java b/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurementColumn.java new file mode 100644 index 000000000..3612aaffa --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurementColumn.java @@ -0,0 +1,75 @@ +package org.jmisb.st1002; + +import org.jmisb.api.common.KlvParseException; +import org.jmisb.core.klv.PrimitiveConverter; + +/** + * Single Point Range Measurement Column (ST 1002 Local Set Item 16). + * + *

The Single Point Range Measurement is not necessarily measured directly through the centre of + * the Collaborative Image; therefore, the location within the image needs to be indicated. The + * Single Point Range Measurement Row and Column Coordinates are the coordinates within the + * Collaborative Sensor’s Image where the measurement was taken. + * + *

Single
+ * Point Range Measurement + * + *

These values are either 32-bit or 64-bit IEEE floating point values. If the Row and Column + * values are omitted from the Range Image Local Set, then the default values are set to the centre + * of the Collaborative Sensor's Image. + * + * @see SinglePointRangeMeasurement + * @see SinglePointRangeMeasurementRow + */ +public class SinglePointRangeMeasurementColumn implements IRangeImageMetadataValue { + + private final double value; + + /** + * Create from value. + * + * @param row the row coordinate in pixels. + */ + public SinglePointRangeMeasurementColumn(double row) { + this.value = row; + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 or 8 + * @throws KlvParseException if the byte array is not of the correct length + */ + public SinglePointRangeMeasurementColumn(byte[] bytes) throws KlvParseException { + try { + this.value = PrimitiveConverter.toFloat64(bytes); + } catch (IllegalArgumentException ex) { + throw new KlvParseException( + "Single Point Range Measurement Column Coordinate is of length 4 or 8 bytes"); + } + } + + @Override + public final String getDisplayName() { + return "Single Point Range Measurement Column"; + } + + /** + * Get the column coordinate. + * + * @return column coordinate in pixels. + */ + public double getColumn() { + return this.value; + } + + @Override + public byte[] getBytes() { + return PrimitiveConverter.float64ToBytes(value); + } + + @Override + public String getDisplayableValue() { + return String.format("%.1f", this.value); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurementRow.java b/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurementRow.java new file mode 100644 index 000000000..ebd85de9b --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurementRow.java @@ -0,0 +1,75 @@ +package org.jmisb.st1002; + +import org.jmisb.api.common.KlvParseException; +import org.jmisb.core.klv.PrimitiveConverter; + +/** + * Single Point Range Measurement Row (ST 1002 Local Set Item 15). + * + *

The Single Point Range Measurement is not necessarily measured directly through the centre of + * the Collaborative Image; therefore, the location within the image needs to be indicated. The + * Single Point Range Measurement Row and Column Coordinates are the coordinates within the + * Collaborative Sensor’s Image where the measurement was taken. + * + *

Single
+ * Point Range Measurement + * + *

These values are either 32-bit or 64-bit IEEE floating point values. If the Row and Column + * values are omitted from the Range Image Local Set, then the default values are set to the centre + * of the Collaborative Sensor's Image. + * + * @see SinglePointRangeMeasurement + * @see SinglePointRangeMeasurementColumn + */ +public class SinglePointRangeMeasurementRow implements IRangeImageMetadataValue { + + private final double value; + + /** + * Create from value. + * + * @param row the row coordinate in pixels. + */ + public SinglePointRangeMeasurementRow(double row) { + this.value = row; + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 or 8 + * @throws KlvParseException if the byte array is not of the correct length + */ + public SinglePointRangeMeasurementRow(byte[] bytes) throws KlvParseException { + try { + this.value = PrimitiveConverter.toFloat64(bytes); + } catch (IllegalArgumentException ex) { + throw new KlvParseException( + "Single Point Range Measurement Row Coordinate is of length 4 or 8 bytes"); + } + } + + @Override + public final String getDisplayName() { + return "Single Point Range Measurement Row"; + } + + /** + * Get the row coordinate. + * + * @return row coordinate in pixels. + */ + public double getRow() { + return this.value; + } + + @Override + public byte[] getBytes() { + return PrimitiveConverter.float64ToBytes(value); + } + + @Override + public String getDisplayableValue() { + return String.format("%.1f", this.value); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurementUncertainty.java b/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurementUncertainty.java new file mode 100644 index 000000000..8505d0412 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/SinglePointRangeMeasurementUncertainty.java @@ -0,0 +1,67 @@ +package org.jmisb.st1002; + +import org.jmisb.api.common.KlvParseException; +import org.jmisb.core.klv.PrimitiveConverter; + +/** + * Single Point Range Measurement Uncertainty (ST 1002 Local Set Item 14). + * + *

The Single Point Range Measurement Uncertainty is the uncertainty (sigma,σ) of the Single + * Point Range Measurement data, in metres, along the measured vector from either the perspective + * centre or depth range measurement backplane to the scene. This value can be either a 32-bit or + * 64-bit IEEE floating point value. + * + * @see SinglePointRangeMeasurement + */ +public class SinglePointRangeMeasurementUncertainty implements IRangeImageMetadataValue { + + private final double value; + + /** + * Create from value. + * + * @param uncertainty the range uncertainty in metres. + */ + public SinglePointRangeMeasurementUncertainty(double uncertainty) { + this.value = uncertainty; + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 or 8 + * @throws KlvParseException if the byte array is not of the correct length + */ + public SinglePointRangeMeasurementUncertainty(byte[] bytes) throws KlvParseException { + try { + this.value = PrimitiveConverter.toFloat64(bytes); + } catch (IllegalArgumentException ex) { + throw new KlvParseException( + "Single Point Range Measurement Uncertainty is of length 4 or 8 bytes"); + } + } + + @Override + public final String getDisplayName() { + return "Single Point Range Measurement Uncertainty"; + } + + /** + * Get the uncertainty. + * + * @return range uncertainty (one standard deviation) in metres. + */ + public double getRange() { + return this.value; + } + + @Override + public byte[] getBytes() { + return PrimitiveConverter.float64ToBytes(value); + } + + @Override + public String getDisplayableValue() { + return String.format("%.3f m", this.value); + } +} diff --git a/st1002/src/main/java/org/jmisb/st1002/package-info.java b/st1002/src/main/java/org/jmisb/st1002/package-info.java new file mode 100644 index 000000000..49e1cbbc0 --- /dev/null +++ b/st1002/src/main/java/org/jmisb/st1002/package-info.java @@ -0,0 +1,17 @@ +/** + * ST 1002: Range Motion Imagery. + * + *

This standard describes Range Motion Imagery, its format and supporting metadata. Range Motion + * Imagery is a temporal sequence of Range Images. Each Range Image is a collection of Range + * Measurements from a sensor to target scene. A Range Measurement is the distance (e.g., meters) + * from an object (or area) in the scene to the sensor. The KLV structures of this Standard are + * intended to allow for flexibility, efficient packing, and future extensions. Range Motion Imagery + * can be used standalone or in collaboration with other Motion Imagery. MISB ST 1107 Metric + * Geopositioning Metadata Set provides the basis for collaborating with other Motion Imagery types. + * + *

This standard describes the: Perspective Range Motion Imagery and Depth Range Motion Imagery; + * the collection methods of Range Motion Imagery; the formats used for storing or transmitting + * Range Motion Imagery; the supporting metadata needed for Range Motion Imagery including, + * temporal, uncertainty, and compression parameters; and the alignment to Collaborative Imagery. + */ +package org.jmisb.st1002; diff --git a/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/coboresightedsensors.png b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/coboresightedsensors.png new file mode 100644 index 000000000..6bf944c20 Binary files /dev/null and b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/coboresightedsensors.png differ diff --git a/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/depthrangeimage.png b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/depthrangeimage.png new file mode 100644 index 000000000..bff190ee8 Binary files /dev/null and b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/depthrangeimage.png differ diff --git a/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/fivehorizontalsections.png b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/fivehorizontalsections.png new file mode 100644 index 000000000..6d58a060f Binary files /dev/null and b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/fivehorizontalsections.png differ diff --git a/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/nonboresightedsensors.png b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/nonboresightedsensors.png new file mode 100644 index 000000000..1dd860df9 Binary files /dev/null and b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/nonboresightedsensors.png differ diff --git a/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/perspectiverangeimage.png b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/perspectiverangeimage.png new file mode 100644 index 000000000..fe0877acb Binary files /dev/null and b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/perspectiverangeimage.png differ diff --git a/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/sectiondatavlp.png b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/sectiondatavlp.png new file mode 100644 index 000000000..e4d3db69d Binary files /dev/null and b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/sectiondatavlp.png differ diff --git a/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/singlepointrangemeasurement.png b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/singlepointrangemeasurement.png new file mode 100644 index 000000000..e6b1eb765 Binary files /dev/null and b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/singlepointrangemeasurement.png differ diff --git a/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/threeverticalsections.png b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/threeverticalsections.png new file mode 100644 index 000000000..c421a09e2 Binary files /dev/null and b/st1002/src/main/javadoc/org/jmisb/st1002/doc-files/threeverticalsections.png differ diff --git a/st1002/src/main/resources/META-INF/services/org.jmisb.api.klv.IMisbMessageFactory b/st1002/src/main/resources/META-INF/services/org.jmisb.api.klv.IMisbMessageFactory new file mode 100644 index 000000000..349abc422 --- /dev/null +++ b/st1002/src/main/resources/META-INF/services/org.jmisb.api.klv.IMisbMessageFactory @@ -0,0 +1 @@ +org.jmisb.st1002.RangeImageLocalSetFactory diff --git a/st1002/src/test/java/org/jmisb/st1002/GeneralizedTransformationTest.java b/st1002/src/test/java/org/jmisb/st1002/GeneralizedTransformationTest.java new file mode 100644 index 000000000..cade31b05 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/GeneralizedTransformationTest.java @@ -0,0 +1,87 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; +import org.jmisb.api.common.KlvParseException; +import org.jmisb.st1202.GeneralizedTransformationLocalSet; +import org.jmisb.st1202.GeneralizedTransformationParametersKey; +import org.jmisb.st1202.IGeneralizedTransformationMetadataValue; +import org.jmisb.st1202.ST1202DocumentVersion; +import org.testng.annotations.Test; + +/** Tests for ST 1002 Generalized Transformation (ST 1002 Tag 19). */ +public class GeneralizedTransformationTest { + + @Test + public void testConstructFromValueMap() throws KlvParseException { + Map + values = new HashMap<>(); + values.put( + GeneralizedTransformationParametersKey.DocumentVersion, + new ST1202DocumentVersion(2)); + GeneralizedTransformationLocalSet localSet = new GeneralizedTransformationLocalSet(values); + + GeneralizedTransformation uut = new GeneralizedTransformation(localSet); + assertEquals(uut.getBytes(), new byte[] {0x0a, 0x01, 0x02}); + assertEquals(uut.getDisplayName(), "Generalized Transformation Local Set"); + assertEquals(uut.getDisplayableValue(), "ST 1202 Generalized Transformation Local Set"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.DocumentVersion)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayName(), + "Document Version"); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayableValue(), + "ST 1202.2"); + } + + @Test + public void testConstructFromEncodedBytes() throws KlvParseException { + GeneralizedTransformation uut = + new GeneralizedTransformation(new byte[] {0x0a, 0x01, 0x02}); + assertEquals(uut.getBytes(), new byte[] {0x0a, 0x01, 0x02}); + assertEquals(uut.getDisplayName(), "Generalized Transformation Local Set"); + assertEquals(uut.getDisplayableValue(), "ST 1202 Generalized Transformation Local Set"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.DocumentVersion)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayName(), + "Document Version"); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayableValue(), + "ST 1202.2"); + } + + @Test + public void testFactoryEncodedBytes() throws KlvParseException { + IRangeImageMetadataValue value = + RangeImageLocalSet.createValue( + RangeImageMetadataKey.GeneralizedTransformationLocalSet, + new byte[] {0x0a, 0x01, 0x02}); + assertTrue(value instanceof GeneralizedTransformation); + GeneralizedTransformation uut = (GeneralizedTransformation) value; + assertEquals(uut.getBytes(), new byte[] {0x0a, 0x01, 0x02}); + assertEquals(uut.getDisplayName(), "Generalized Transformation Local Set"); + assertEquals(uut.getDisplayableValue(), "ST 1202 Generalized Transformation Local Set"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.DocumentVersion)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayName(), + "Document Version"); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayableValue(), + "ST 1202.2"); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/LoggerChecks.java b/st1002/src/test/java/org/jmisb/st1002/LoggerChecks.java new file mode 100644 index 000000000..306f52ca6 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/LoggerChecks.java @@ -0,0 +1,63 @@ +package org.jmisb.st1002; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import java.util.List; +import org.slf4j.helpers.MessageFormatter; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import uk.org.lidalia.slf4jext.Level; + +/** + * Superclass for logging checks in a test case. + * + *

The concept is that there is a test logger that should always contain no log messages after a + * test has been run. That gets verified in the AfterMethod so if there is a message, the logger + * needs to be checked and clear()ed before the test method returns. + * + *

Only ERROR, WARN and INFO levels are checked. DEBUG and lower are implementation detail. + * + *

The subclass is responsible for initialising the LOGGER correctly by calling the super + * constructor with the class that is creating the log messages. + */ +public abstract class LoggerChecks { + protected TestLogger LOGGER; + + public LoggerChecks(Class T) { + LOGGER = TestLoggerFactory.getTestLogger(T); + LOGGER.setEnabledLevelsForAllThreads(Level.ERROR, Level.WARN, Level.INFO); + } + + @BeforeMethod + public void clearLogger() { + LOGGER.clear(); + } + + @AfterMethod + public void checkLogger() { + verifyNoLoggerMessages(); + } + + protected void verifyNoLoggerMessages() { + if (LOGGER.getLoggingEvents().size() > 0) { + List events = LOGGER.getLoggingEvents(); + for (LoggingEvent event : events) { + System.out.println(event.getLevel().name() + ": " + event.getMessage().toString()); + } + } + Assert.assertEquals(LOGGER.getLoggingEvents().size(), 0); + } + + protected void verifySingleLoggerMessage(final String expectedMessage) { + Assert.assertEquals(LOGGER.getLoggingEvents().size(), 1); + LoggingEvent event = LOGGER.getLoggingEvents().get(0); + Assert.assertEquals(event.getArguments().size(), 1); + String message = + MessageFormatter.format(event.getMessage(), event.getArguments().get(0)) + .getMessage(); + Assert.assertEquals(message, expectedMessage); + LOGGER.clear(); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/NumberOfSectionsInXTest.java b/st1002/src/test/java/org/jmisb/st1002/NumberOfSectionsInXTest.java new file mode 100644 index 000000000..ccbdd6415 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/NumberOfSectionsInXTest.java @@ -0,0 +1,82 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for ST 1002 Number of Sections in X (ST 1002 Tag 17). */ +public class NumberOfSectionsInXTest { + @Test + public void testConstructFromValue() { + NumberOfSectionsInX uut = new NumberOfSectionsInX(1); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x01}); + assertEquals(uut.getDisplayName(), "Number of Sections in X"); + assertEquals(uut.getDisplayableValue(), "1"); + assertEquals(uut.getNumberOfSections(), 1); + } + + @Test + public void testConstructFromValue127() { + NumberOfSectionsInX uut = new NumberOfSectionsInX(127); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x7f}); + assertEquals(uut.getDisplayName(), "Number of Sections in X"); + assertEquals(uut.getDisplayableValue(), "127"); + assertEquals(uut.getNumberOfSections(), 127); + } + + @Test + public void testConstructFromValue129() { + NumberOfSectionsInX uut = new NumberOfSectionsInX(129); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x81, (byte) 0x01}); + assertEquals(uut.getDisplayName(), "Number of Sections in X"); + assertEquals(uut.getDisplayableValue(), "129"); + assertEquals(uut.getNumberOfSections(), 129); + } + + @Test + public void testConstructFromEncodedBytes() { + NumberOfSectionsInX uut = new NumberOfSectionsInX(new byte[] {(byte) 0x02}); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x02}); + assertEquals(uut.getDisplayName(), "Number of Sections in X"); + assertEquals(uut.getDisplayableValue(), "2"); + assertEquals(uut.getNumberOfSections(), 2); + } + + @Test + public void testConstructFromEncodedBytes127() { + NumberOfSectionsInX uut = new NumberOfSectionsInX(new byte[] {(byte) 0x7F}); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x7F}); + assertEquals(uut.getDisplayName(), "Number of Sections in X"); + assertEquals(uut.getDisplayableValue(), "127"); + assertEquals(uut.getNumberOfSections(), 127); + } + + @Test + public void testConstructFromEncodedBytes255() { + NumberOfSectionsInX uut = new NumberOfSectionsInX(new byte[] {(byte) 0x81, (byte) 0x7F}); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x81, (byte) 0x7F}); + assertEquals(uut.getDisplayName(), "Number of Sections in X"); + assertEquals(uut.getDisplayableValue(), "255"); + assertEquals(uut.getNumberOfSections(), 255); + } + + @Test + public void testFactoryEncodedBytes() throws KlvParseException { + IRangeImageMetadataValue value = + RangeImageLocalSet.createValue( + RangeImageMetadataKey.NumberOfSectionsInX, new byte[] {(byte) 0x02}); + assertTrue(value instanceof NumberOfSectionsInX); + NumberOfSectionsInX uut = (NumberOfSectionsInX) value; + assertEquals(uut.getBytes(), new byte[] {(byte) 0x02}); + assertEquals(uut.getDisplayName(), "Number of Sections in X"); + assertEquals(uut.getDisplayableValue(), "2"); + assertEquals(uut.getNumberOfSections(), 2); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testTooSmall() { + new NumberOfSectionsInX(0); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/NumberOfSectionsInYTest.java b/st1002/src/test/java/org/jmisb/st1002/NumberOfSectionsInYTest.java new file mode 100644 index 000000000..0527d76e4 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/NumberOfSectionsInYTest.java @@ -0,0 +1,82 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for ST 1002 Number of Sections in Y (ST 1002 Tag 18). */ +public class NumberOfSectionsInYTest { + @Test + public void testConstructFromValue() { + NumberOfSectionsInY uut = new NumberOfSectionsInY(1); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x01}); + assertEquals(uut.getDisplayName(), "Number of Sections in Y"); + assertEquals(uut.getDisplayableValue(), "1"); + assertEquals(uut.getNumberOfSections(), 1); + } + + @Test + public void testConstructFromValue127() { + NumberOfSectionsInY uut = new NumberOfSectionsInY(127); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x7f}); + assertEquals(uut.getDisplayName(), "Number of Sections in Y"); + assertEquals(uut.getDisplayableValue(), "127"); + assertEquals(uut.getNumberOfSections(), 127); + } + + @Test + public void testConstructFromValue129() { + NumberOfSectionsInY uut = new NumberOfSectionsInY(129); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x81, (byte) 0x01}); + assertEquals(uut.getDisplayName(), "Number of Sections in Y"); + assertEquals(uut.getDisplayableValue(), "129"); + assertEquals(uut.getNumberOfSections(), 129); + } + + @Test + public void testConstructFromEncodedBytes() { + NumberOfSectionsInY uut = new NumberOfSectionsInY(new byte[] {(byte) 0x02}); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x02}); + assertEquals(uut.getDisplayName(), "Number of Sections in Y"); + assertEquals(uut.getDisplayableValue(), "2"); + assertEquals(uut.getNumberOfSections(), 2); + } + + @Test + public void testConstructFromEncodedBytes127() { + NumberOfSectionsInY uut = new NumberOfSectionsInY(new byte[] {(byte) 0x7F}); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x7F}); + assertEquals(uut.getDisplayName(), "Number of Sections in Y"); + assertEquals(uut.getDisplayableValue(), "127"); + assertEquals(uut.getNumberOfSections(), 127); + } + + @Test + public void testConstructFromEncodedBytes255() { + NumberOfSectionsInY uut = new NumberOfSectionsInY(new byte[] {(byte) 0x81, (byte) 0x7F}); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x81, (byte) 0x7F}); + assertEquals(uut.getDisplayName(), "Number of Sections in Y"); + assertEquals(uut.getDisplayableValue(), "255"); + assertEquals(uut.getNumberOfSections(), 255); + } + + @Test + public void testFactoryEncodedBytes() throws KlvParseException { + IRangeImageMetadataValue value = + RangeImageLocalSet.createValue( + RangeImageMetadataKey.NumberOfSectionsInY, new byte[] {(byte) 0x02}); + assertTrue(value instanceof NumberOfSectionsInY); + NumberOfSectionsInY uut = (NumberOfSectionsInY) value; + assertEquals(uut.getBytes(), new byte[] {(byte) 0x02}); + assertEquals(uut.getDisplayName(), "Number of Sections in Y"); + assertEquals(uut.getDisplayableValue(), "2"); + assertEquals(uut.getNumberOfSections(), 2); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testTooSmall() { + new NumberOfSectionsInY(0); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/PrecisionTimeStampTest.java b/st1002/src/test/java/org/jmisb/st1002/PrecisionTimeStampTest.java new file mode 100644 index 000000000..d1c4e9634 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/PrecisionTimeStampTest.java @@ -0,0 +1,134 @@ +package org.jmisb.st1002; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; +import org.jmisb.api.common.KlvParseException; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class PrecisionTimeStampTest { + // Example from MISB ST 0601 doc + @Test + public void testExample() { + // Convert byte[] -> value + byte[] bytes = + new byte[] { + (byte) 0x00, + (byte) 0x04, + (byte) 0x59, + (byte) 0xf4, + (byte) 0xA6, + (byte) 0xaa, + (byte) 0x4a, + (byte) 0xa8 + }; + ST1002PrecisionTimeStamp pts = new ST1002PrecisionTimeStamp(bytes); + Assert.assertEquals(pts.getDisplayName(), "Precision Time Stamp"); + Assert.assertEquals(pts.getDisplayableValue(), "1224807209913000"); + LocalDateTime dateTime = pts.getDateTime(); + + Assert.assertEquals(dateTime.getYear(), 2008); + Assert.assertEquals(dateTime.getMonth(), Month.OCTOBER); + Assert.assertEquals(dateTime.getDayOfMonth(), 24); + Assert.assertEquals(dateTime.getHour(), 0); + Assert.assertEquals(dateTime.getMinute(), 13); + Assert.assertEquals(dateTime.getSecond(), 29); + Assert.assertEquals(dateTime.getNano(), 913000000); + + // Convert value -> byte[] + long microseconds = dateTime.toInstant(ZoneOffset.UTC).toEpochMilli() * 1000; + ST1002PrecisionTimeStamp pts2 = new ST1002PrecisionTimeStamp(microseconds); + Assert.assertEquals(pts2.getDisplayName(), "Precision Time Stamp"); + Assert.assertEquals( + pts2.getBytes(), + new byte[] { + (byte) 0x00, + (byte) 0x04, + (byte) 0x59, + (byte) 0xf4, + (byte) 0xA6, + (byte) 0xaa, + (byte) 0x4a, + (byte) 0xa8 + }); + + Assert.assertEquals(microseconds, 1224807209913000L); + Assert.assertEquals(pts2.getDisplayableValue(), "1224807209913000"); + } + + @Test + public void testFactoryExample() throws KlvParseException { + byte[] bytes = + new byte[] { + (byte) 0x00, + (byte) 0x04, + (byte) 0x59, + (byte) 0xf4, + (byte) 0xA6, + (byte) 0xaa, + (byte) 0x4a, + (byte) 0xa8 + }; + IRangeImageMetadataValue v = + RangeImageLocalSet.createValue(RangeImageMetadataKey.PrecisionTimeStamp, bytes); + Assert.assertTrue(v instanceof ST1002PrecisionTimeStamp); + Assert.assertEquals(v.getDisplayName(), "Precision Time Stamp"); + ST1002PrecisionTimeStamp pts = (ST1002PrecisionTimeStamp) v; + Assert.assertEquals(pts.getDisplayableValue(), "1224807209913000"); + LocalDateTime dateTime = pts.getDateTime(); + + Assert.assertEquals(dateTime.getYear(), 2008); + Assert.assertEquals(dateTime.getMonth(), Month.OCTOBER); + Assert.assertEquals(dateTime.getDayOfMonth(), 24); + Assert.assertEquals(dateTime.getHour(), 0); + Assert.assertEquals(dateTime.getMinute(), 13); + Assert.assertEquals(dateTime.getSecond(), 29); + Assert.assertEquals(dateTime.getNano(), 913000000); + } + + @Test + public void testNow() { + LocalDateTime now = LocalDateTime.now(); + ST1002PrecisionTimeStamp pts = new ST1002PrecisionTimeStamp(now); + Assert.assertEquals(pts.getDisplayName(), "Precision Time Stamp"); + Assert.assertEquals(pts.getDateTime().getDayOfMonth(), now.getDayOfMonth()); + Assert.assertEquals(pts.getDateTime().getHour(), now.getHour()); + long ptsMicroseconds = pts.getMicroseconds(); + long nowMicroseconds = + ChronoUnit.MICROS.between(Instant.EPOCH, now.toInstant(ZoneOffset.UTC)); + Assert.assertEquals(ptsMicroseconds, nowMicroseconds); + } + + @Test + public void testMinAndMax() { + ST1002PrecisionTimeStamp pts = new ST1002PrecisionTimeStamp(0L); + Assert.assertEquals(pts.getDisplayName(), "Precision Time Stamp"); + Assert.assertEquals(pts.getDateTime().getYear(), 1970); + Assert.assertEquals(pts.getDateTime().getMonth(), Month.JANUARY); + Assert.assertEquals(pts.getDateTime().getDayOfMonth(), 1); + Assert.assertEquals(pts.getDisplayableValue(), "0"); + + // Create max value and ensure no exception is thrown + pts = new ST1002PrecisionTimeStamp(Long.MAX_VALUE); + Assert.assertEquals(pts.getDisplayName(), "Precision Time Stamp"); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testTooSmall() { + new ST1002PrecisionTimeStamp(-1); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testTooBig() { + // Oct 12, 2263 at 08:30 + new ST1002PrecisionTimeStamp(LocalDateTime.of(2263, 10, 12, 8, 30)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void badArrayLength() { + new ST1002PrecisionTimeStamp(new byte[] {0x00, 0x00, 0x00, 0x00}); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/RangeImageCompressionMethodTest.java b/st1002/src/test/java/org/jmisb/st1002/RangeImageCompressionMethodTest.java new file mode 100644 index 000000000..d37116ba4 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/RangeImageCompressionMethodTest.java @@ -0,0 +1,45 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +/** Unit tests for RangeImageCompressionMethod. */ +public class RangeImageCompressionMethodTest { + + @Test + public void checkNoCompression() { + RangeImageCompressionMethod uut = RangeImageCompressionMethod.NO_COMPRESSION; + assertEquals(uut.getEncodedValue(), 0); + assertEquals(uut.getTextDescription(), "No Compression"); + } + + @Test + public void checkPlanarFit() { + RangeImageCompressionMethod uut = RangeImageCompressionMethod.PLANAR_FIT; + assertEquals(uut.getEncodedValue(), 1); + assertEquals(uut.getTextDescription(), "Planar Fit"); + } + + @Test + public void checkLookupNoCompression() { + RangeImageCompressionMethod uut = RangeImageCompressionMethod.lookup(0); + assertEquals(uut.getEncodedValue(), 0); + assertEquals(uut.getTextDescription(), "No Compression"); + } + + @Test + public void checkLookupPlanar() { + RangeImageCompressionMethod uut = RangeImageCompressionMethod.lookup(1); + assertEquals(uut.getEncodedValue(), 1); + assertEquals(uut.getTextDescription(), "Planar Fit"); + } + + @Test + public void checkLookupUnknownValue() { + RangeImageCompressionMethod uut = RangeImageCompressionMethod.lookup(2); + assertEquals(uut, RangeImageCompressionMethod.UNKNOWN); + assertEquals(uut.getEncodedValue(), -1); + assertEquals(uut.getTextDescription(), "Unknown"); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/RangeImageDataTypeTest.java b/st1002/src/test/java/org/jmisb/st1002/RangeImageDataTypeTest.java new file mode 100644 index 000000000..1187d5623 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/RangeImageDataTypeTest.java @@ -0,0 +1,45 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +/** Unit tests for RangeImageDataType. */ +public class RangeImageDataTypeTest { + + @Test + public void checkPerspective() { + RangeImageryDataType uut = RangeImageryDataType.PERSPECTIVE; + assertEquals(uut.getEncodedValue(), 0); + assertEquals(uut.getTextDescription(), "Perspective Range Image"); + } + + @Test + public void checkDepth() { + RangeImageryDataType uut = RangeImageryDataType.DEPTH; + assertEquals(uut.getEncodedValue(), 1); + assertEquals(uut.getTextDescription(), "Depth Range Image"); + } + + @Test + public void checkLookupPerspective() { + RangeImageryDataType uut = RangeImageryDataType.lookup(0); + assertEquals(uut.getEncodedValue(), 0); + assertEquals(uut.getTextDescription(), "Perspective Range Image"); + } + + @Test + public void checkLookupDepth() { + RangeImageryDataType uut = RangeImageryDataType.lookup(1); + assertEquals(uut.getEncodedValue(), 1); + assertEquals(uut.getTextDescription(), "Depth Range Image"); + } + + @Test + public void checkLookupUnknownValue() { + RangeImageryDataType uut = RangeImageryDataType.lookup(2); + assertEquals(uut, RangeImageryDataType.UNKNOWN); + assertEquals(uut.getEncodedValue(), -1); + assertEquals(uut.getTextDescription(), "Unknown"); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/RangeImageEnumerationsTest.java b/st1002/src/test/java/org/jmisb/st1002/RangeImageEnumerationsTest.java new file mode 100644 index 000000000..ca29c645b --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/RangeImageEnumerationsTest.java @@ -0,0 +1,88 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for ST 1002 Range Image Enumerations (ST 1002 Tag 12). */ +public class RangeImageEnumerationsTest { + @Test + public void testConstructFromValue() { + RangeImageEnumerations uut = + new RangeImageEnumerations( + RangeImageCompressionMethod.PLANAR_FIT, + RangeImageryDataType.DEPTH, + RangeImageSource.RANGE_SENSOR); + assertEquals(uut.getBytes(), new byte[] {(byte) 0b01001001}); + assertEquals(uut.getDisplayName(), "Range Image Enumerations"); + assertEquals(uut.getDisplayableValue(), "Range Sensor | Depth Range Image | Planar Fit"); + assertEquals(uut.getCompressionMethod(), RangeImageCompressionMethod.PLANAR_FIT); + assertEquals(uut.getDataType(), RangeImageryDataType.DEPTH); + assertEquals(uut.getRangeImageSource(), RangeImageSource.RANGE_SENSOR); + } + + @Test + public void testConstructFromBytes() { + RangeImageEnumerations uut = new RangeImageEnumerations(new byte[] {(byte) 0b01001001}); + assertEquals(uut.getBytes(), new byte[] {(byte) 0b01001001}); + assertEquals(uut.getDisplayName(), "Range Image Enumerations"); + assertEquals(uut.getDisplayableValue(), "Range Sensor | Depth Range Image | Planar Fit"); + assertEquals(uut.getCompressionMethod(), RangeImageCompressionMethod.PLANAR_FIT); + assertEquals(uut.getDataType(), RangeImageryDataType.DEPTH); + assertEquals(uut.getRangeImageSource(), RangeImageSource.RANGE_SENSOR); + } + + @Test + public void testFactoryEncodedBytes() throws KlvParseException { + IRangeImageMetadataValue value = + RangeImageLocalSet.createValue( + RangeImageMetadataKey.RangeImageEnumerations, + new byte[] {(byte) 0b01001001}); + assertTrue(value instanceof RangeImageEnumerations); + RangeImageEnumerations uut = (RangeImageEnumerations) value; + assertEquals(uut.getBytes(), new byte[] {(byte) 0b01001001}); + assertEquals(uut.getDisplayName(), "Range Image Enumerations"); + assertEquals(uut.getDisplayableValue(), "Range Sensor | Depth Range Image | Planar Fit"); + assertEquals(uut.getCompressionMethod(), RangeImageCompressionMethod.PLANAR_FIT); + assertEquals(uut.getDataType(), RangeImageryDataType.DEPTH); + assertEquals(uut.getRangeImageSource(), RangeImageSource.RANGE_SENSOR); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Range Image Source cannot be UNKNOWN") + public void testSerialisationBadSource() { + RangeImageEnumerations uut = + new RangeImageEnumerations( + RangeImageCompressionMethod.NO_COMPRESSION, + RangeImageryDataType.DEPTH, + RangeImageSource.UNKNOWN); + uut.getBytes(); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Range Imagery Data Type cannot be UNKNOWN") + public void testSerialisationBadDataType() { + RangeImageEnumerations uut = + new RangeImageEnumerations( + RangeImageCompressionMethod.NO_COMPRESSION, + RangeImageryDataType.UNKNOWN, + RangeImageSource.COMPUTATIONALLY_EXTRACTED); + uut.getBytes(); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Range Image Compression Method cannot be UNKNOWN") + public void testSerialisationBadCompressionMethod() { + RangeImageEnumerations uut = + new RangeImageEnumerations( + RangeImageCompressionMethod.UNKNOWN, + RangeImageryDataType.PERSPECTIVE, + RangeImageSource.COMPUTATIONALLY_EXTRACTED); + uut.getBytes(); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/RangeImageLocalSetTest.java b/st1002/src/test/java/org/jmisb/st1002/RangeImageLocalSetTest.java new file mode 100644 index 000000000..2b726be9f --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/RangeImageLocalSetTest.java @@ -0,0 +1,358 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.*; + +import java.time.LocalDateTime; +import java.time.Month; +import java.util.HashMap; +import java.util.Map; +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for the ST 1002 Range Image Local Set. */ +public class RangeImageLocalSetTest extends LoggerChecks { + + public RangeImageLocalSetTest() { + super(RangeImageLocalSet.class); + } + + @Test + public void parse() throws KlvParseException { + final byte[] bytes = + new byte[] { + (byte) 0x06, + (byte) 0x0E, + (byte) 0x2B, + (byte) 0x34, + (byte) 0x02, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x01, + (byte) 0x0E, + (byte) 0x01, + (byte) 0x03, + (byte) 0x03, + (byte) 0x0C, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x07, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x02, + (byte) 0x15, + (byte) 0x02, + (byte) 0x32, + (byte) 0x3E + }; + RangeImageLocalSetFactory factory = new RangeImageLocalSetFactory(); + assertEquals( + factory.getUniversalLabel().getBytes(), + new byte[] { + 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x0b, 0x01, 0x01, 0x0e, 0x01, 0x03, 0x03, 0x0c, + 0x00, 0x00, 0x00 + }); + RangeImageLocalSet localSet = factory.create(bytes); + assertNotNull(localSet); + assertEquals(localSet.displayHeader(), "ST 1002 Range Image"); + assertEquals(localSet.getUniversalLabel(), RangeImageLocalSet.RangeImageLocalSetUl); + assertEquals(localSet.getIdentifiers().size(), 1); + } + + @Test + public void parseWithUnknown() throws KlvParseException { + final byte[] bytes = + new byte[] { + (byte) 0x06, + (byte) 0x0E, + (byte) 0x2B, + (byte) 0x34, + (byte) 0x02, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x01, + (byte) 0x0E, + (byte) 0x01, + (byte) 0x03, + (byte) 0x03, + (byte) 0x0C, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x0a, + (byte) 0x7F, + (byte) 0x01, + (byte) 0x00, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x02, + (byte) 0x15, + (byte) 0x02, + (byte) 0x05, + (byte) 0xc7 + }; + RangeImageLocalSetFactory factory = new RangeImageLocalSetFactory(); + assertEquals( + factory.getUniversalLabel().getBytes(), + new byte[] { + 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x0b, 0x01, 0x01, 0x0e, 0x01, 0x03, 0x03, 0x0c, + 0x00, 0x00, 0x00 + }); + verifyNoLoggerMessages(); + RangeImageLocalSet localSet = factory.create(bytes); + verifySingleLoggerMessage("Unknown Range Image Metadata tag: 127"); + assertNotNull(localSet); + assertEquals(localSet.displayHeader(), "ST 1002 Range Image"); + assertEquals(localSet.getUniversalLabel(), RangeImageLocalSet.RangeImageLocalSetUl); + assertEquals(localSet.getIdentifiers().size(), 1); + } + + @Test + public void fromNestedBytes() throws KlvParseException { + final byte[] bytes = + new byte[] { + (byte) 0x06, + (byte) 0x0E, + (byte) 0x2B, + (byte) 0x34, + (byte) 0x02, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x01, + (byte) 0x0E, + (byte) 0x01, + (byte) 0x03, + (byte) 0x03, + (byte) 0x0C, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x07, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x02, + (byte) 0x15, + (byte) 0x02, + (byte) 0x32, + (byte) 0x3E + }; + RangeImageLocalSet localSet = + RangeImageLocalSet.fromNestedBytes( + new byte[] {0x0B, 0x01, 0x2, 0x15, 0x02, (byte) 0xd9, 0x21}, 0, 7); + assertNotNull(localSet); + assertEquals(localSet.displayHeader(), "ST 1002 Range Image"); + assertEquals(localSet.getUniversalLabel(), RangeImageLocalSet.RangeImageLocalSetUl); + assertEquals(localSet.getIdentifiers().size(), 1); + assertEquals(localSet.frameMessage(false), bytes); + assertEquals( + localSet.frameMessage(true), + new byte[] {0x0B, 0x01, 0x2, 0x15, 0x02, (byte) 0xd9, 0x21}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void badChecksum() throws KlvParseException { + byte[] bytes = + new byte[] { + (byte) 0x06, + (byte) 0x0E, + (byte) 0x2B, + (byte) 0x34, + (byte) 0x02, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x01, + (byte) 0x0E, + (byte) 0x01, + (byte) 0x03, + (byte) 0x03, + (byte) 0x0C, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x07, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x02, + (byte) 0x15, + (byte) 0x02, + (byte) 0x31, + (byte) 0x3E + }; + verifyNoLoggerMessages(); + RangeImageLocalSetFactory factory = new RangeImageLocalSetFactory(); + factory.create(bytes); + verifySingleLoggerMessage("Bad checksum"); + } + + @Test + public void buildFromValues() throws KlvParseException { + + Map values = new HashMap<>(); + values.put(RangeImageMetadataKey.DocumentVersion, new ST1002VersionNumber(2)); + RangeImageLocalSet localSet = new RangeImageLocalSet(values); + assertNotNull(localSet); + assertEquals(localSet.displayHeader(), "ST 1002 Range Image"); + assertEquals(localSet.getUniversalLabel(), RangeImageLocalSet.RangeImageLocalSetUl); + assertEquals(localSet.getIdentifiers().size(), 1); + final byte[] expectedBytes = + new byte[] { + (byte) 0x06, + (byte) 0x0E, + (byte) 0x2B, + (byte) 0x34, + (byte) 0x02, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x01, + (byte) 0x0E, + (byte) 0x01, + (byte) 0x03, + (byte) 0x03, + (byte) 0x0C, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x07, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x02, + (byte) 0x15, + (byte) 0x02, + (byte) 0x32, + (byte) 0x3E + }; + assertEquals(localSet.frameMessage(false), expectedBytes); + assertEquals( + localSet.frameMessage(true), + new byte[] {0x0B, 0x01, 0x2, 0x15, 0x02, (byte) 0xd9, 0x21}); + } + + @Test + public void buildFromValuesWithCRCAndUnknown() throws KlvParseException { + final byte[] bytes = + new byte[] { + (byte) 0x06, + (byte) 0x0E, + (byte) 0x2B, + (byte) 0x34, + (byte) 0x02, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x01, + (byte) 0x0E, + (byte) 0x01, + (byte) 0x03, + (byte) 0x03, + (byte) 0x0C, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x07, + (byte) 0x0B, + (byte) 0x01, + (byte) 0x02, + (byte) 0x15, + (byte) 0x02, + (byte) 0x32, + (byte) 0x3E + }; + Map values = new HashMap<>(); + values.put( + RangeImageMetadataKey.Undefined, + new IRangeImageMetadataValue() { + @Override + public byte[] getBytes() { + throw new UnsupportedOperationException("Should not be called"); + } + + @Override + public String getDisplayName() { + return "bogus name"; + } + + @Override + public String getDisplayableValue() { + return "bogus value"; + } + }); + values.put( + RangeImageMetadataKey.CRC16CCITT, + new IRangeImageMetadataValue() { + @Override + public byte[] getBytes() { + throw new UnsupportedOperationException("Should not be called"); + } + + @Override + public String getDisplayName() { + throw new UnsupportedOperationException("Should not be called"); + } + + @Override + public String getDisplayableValue() { + throw new UnsupportedOperationException("Should not be called"); + } + }); + values.put(RangeImageMetadataKey.DocumentVersion, new ST1002VersionNumber(2)); + + RangeImageLocalSet localSet = new RangeImageLocalSet(values); + assertNotNull(localSet); + assertEquals(localSet.frameMessage(false), bytes); + assertEquals( + localSet.frameMessage(true), + new byte[] {0x0B, 0x01, 0x2, 0x15, 0x02, (byte) 0xd9, 0x21}); + } + + @Test + public void createUnknown() throws KlvParseException { + verifyNoLoggerMessages(); + IRangeImageMetadataValue value = + RangeImageLocalSet.createValue(RangeImageMetadataKey.Undefined, new byte[] {0x01}); + verifySingleLoggerMessage("Unknown Range Image Metadata tag: Undefined"); + assertNull(value); + } + + @Test + public void buildFromValuesWithSectionData() throws KlvParseException { + Map values = new HashMap<>(); + values.put( + RangeImageMetadataKey.PrecisionTimeStamp, + new ST1002PrecisionTimeStamp(LocalDateTime.of(2023, Month.JANUARY, 25, 19, 50))); + values.put(RangeImageMetadataKey.DocumentVersion, new ST1002VersionNumber(2)); + values.put( + RangeImageMetadataKey.Undefined, + new IRangeImageMetadataValue() { + @Override + public byte[] getBytes() { + throw new UnsupportedOperationException("Should not be called."); + } + + @Override + public String getDisplayName() { + throw new UnsupportedOperationException("Should not be called."); + } + + @Override + public String getDisplayableValue() { + throw new UnsupportedOperationException("Should not be called."); + } + }); + SectionDataList sectionDataList = new SectionDataList(); + sectionDataList.add( + new SectionData(1, 1, new double[][] {{100.0, 200.0}, {150.0, 250.0}}, null)); + sectionDataList.add( + new SectionData(1, 2, new double[][] {{300.0, 400.0}, {350.0, 450.0}}, null)); + values.put(RangeImageMetadataKey.SectionDataVLP, sectionDataList); + values.put(RangeImageMetadataKey.NumberOfSectionsInX, new NumberOfSectionsInX(1)); + values.put(RangeImageMetadataKey.NumberOfSectionsInY, new NumberOfSectionsInX(2)); + RangeImageLocalSet localSet = new RangeImageLocalSet(values); + assertNotNull(localSet); + assertEquals(localSet.displayHeader(), "ST 1002 Range Image"); + assertEquals(localSet.getUniversalLabel(), RangeImageLocalSet.RangeImageLocalSetUl); + assertEquals(localSet.getIdentifiers().size(), 6); + byte[] bytes = localSet.frameMessage(false); + RangeImageLocalSet parsedLocalSet = new RangeImageLocalSet(bytes); + // Undefined entry got dropped out. + assertEquals(parsedLocalSet.getIdentifiers().size(), 5); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/RangeImageSourceTest.java b/st1002/src/test/java/org/jmisb/st1002/RangeImageSourceTest.java new file mode 100644 index 000000000..59317fea2 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/RangeImageSourceTest.java @@ -0,0 +1,45 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +/** Unit tests for RangeImageSource. */ +public class RangeImageSourceTest { + + @Test + public void checkComputationallyExtracted() { + RangeImageSource uut = RangeImageSource.COMPUTATIONALLY_EXTRACTED; + assertEquals(uut.getEncodedValue(), 0); + assertEquals(uut.getTextDescription(), "Computationally Extracted"); + } + + @Test + public void checkRangeSensor() { + RangeImageSource uut = RangeImageSource.RANGE_SENSOR; + assertEquals(uut.getEncodedValue(), 1); + assertEquals(uut.getTextDescription(), "Range Sensor"); + } + + @Test + public void checkLookupComputationallyExtracted() { + RangeImageSource uut = RangeImageSource.lookup(0); + assertEquals(uut.getEncodedValue(), 0); + assertEquals(uut.getTextDescription(), "Computationally Extracted"); + } + + @Test + public void checkLookupRangeSensor() { + RangeImageSource uut = RangeImageSource.lookup(1); + assertEquals(uut.getEncodedValue(), 1); + assertEquals(uut.getTextDescription(), "Range Sensor"); + } + + @Test + public void checkLookupUnknownValue() { + RangeImageSource uut = RangeImageSource.lookup(2); + assertEquals(uut, RangeImageSource.UNKNOWN); + assertEquals(uut.getEncodedValue(), -1); + assertEquals(uut.getTextDescription(), "Unknown"); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/ST1002VersionNumberTest.java b/st1002/src/test/java/org/jmisb/st1002/ST1002VersionNumberTest.java new file mode 100644 index 000000000..5815a5018 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/ST1002VersionNumberTest.java @@ -0,0 +1,92 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for ST 1002 Version Number (ST 1002 Tag 11). */ +public class ST1002VersionNumberTest { + @Test + public void testConstructFromValue() { + ST1002VersionNumber version = new ST1002VersionNumber(1); + assertEquals(version.getBytes(), new byte[] {(byte) 0x01}); + assertEquals(version.getDisplayName(), "Version Number"); + assertEquals(version.getDisplayableValue(), "ST 1002.1"); + assertEquals(version.getVersion(), 1); + } + + @Test + public void testConstructFromValue127() { + ST1002VersionNumber version = new ST1002VersionNumber(127); + assertEquals(version.getBytes(), new byte[] {(byte) 0x7f}); + assertEquals(version.getDisplayName(), "Version Number"); + assertEquals(version.getDisplayableValue(), "ST 1002.127"); + assertEquals(version.getVersion(), 127); + } + + @Test + public void testConstructFromValue129() { + ST1002VersionNumber version = new ST1002VersionNumber(129); + assertEquals(version.getBytes(), new byte[] {(byte) 0x81, (byte) 0x01}); + assertEquals(version.getDisplayName(), "Version Number"); + assertEquals(version.getDisplayableValue(), "ST 1002.129"); + assertEquals(version.getVersion(), 129); + } + + @Test + public void testConstructFromEncodedBytes() { + ST1002VersionNumber version = new ST1002VersionNumber(new byte[] {(byte) 0x02}); + assertEquals(version.getBytes(), new byte[] {(byte) 0x02}); + assertEquals(version.getDisplayName(), "Version Number"); + assertEquals(version.getDisplayableValue(), "ST 1002.2"); + assertEquals(version.getVersion(), 2); + } + + @Test + public void testConstructFromEncodedBytes0() { + ST1002VersionNumber version = new ST1002VersionNumber(new byte[] {(byte) 0x00}); + assertEquals(version.getBytes(), new byte[] {(byte) 0x00}); + assertEquals(version.getDisplayName(), "Version Number"); + assertEquals(version.getDisplayableValue(), "ST 1002"); + assertEquals(version.getVersion(), 0); + } + + @Test + public void testConstructFromEncodedBytes127() { + ST1002VersionNumber version = new ST1002VersionNumber(new byte[] {(byte) 0x7F}); + assertEquals(version.getBytes(), new byte[] {(byte) 0x7F}); + assertEquals(version.getDisplayName(), "Version Number"); + assertEquals(version.getDisplayableValue(), "ST 1002.127"); + assertEquals(version.getVersion(), 127); + } + + @Test + public void testConstructFromEncodedBytes255() { + ST1002VersionNumber version = + new ST1002VersionNumber(new byte[] {(byte) 0x81, (byte) 0x7F}); + assertEquals(version.getBytes(), new byte[] {(byte) 0x81, (byte) 0x7F}); + assertEquals(version.getDisplayName(), "Version Number"); + assertEquals(version.getDisplayableValue(), "ST 1002.255"); + assertEquals(version.getVersion(), 255); + } + + @Test + public void testFactoryEncodedBytes() throws KlvParseException { + IRangeImageMetadataValue value = + RangeImageLocalSet.createValue( + RangeImageMetadataKey.DocumentVersion, new byte[] {(byte) 0x02}); + assertTrue(value instanceof ST1002VersionNumber); + ST1002VersionNumber version = (ST1002VersionNumber) value; + assertEquals(version.getBytes(), new byte[] {(byte) 0x02}); + assertEquals(version.getDisplayName(), "Version Number"); + assertEquals(version.getDisplayableValue(), "ST 1002.2"); + assertEquals(version.getVersion(), 2); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testTooSmall() { + new ST1002VersionNumber(-1); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/SectionDataIdentifierKeyTest.java b/st1002/src/test/java/org/jmisb/st1002/SectionDataIdentifierKeyTest.java new file mode 100644 index 000000000..2fb4ae91c --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/SectionDataIdentifierKeyTest.java @@ -0,0 +1,56 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.*; + +import org.testng.annotations.Test; + +/** Unit tests for SectionDataIdentifierKey. */ +public class SectionDataIdentifierKeyTest { + + @Test + public void checkDisplayName() { + SectionDataIdentifierKey uut = new SectionDataIdentifierKey(3, 1); + assertEquals(uut.getIdentifier(), 3); + } + + @Test + public void checkSectionNumbers() { + SectionDataIdentifierKey uut = new SectionDataIdentifierKey(3, 1); + assertEquals(uut.getSectionX(), 3); + assertEquals(uut.getSectionY(), 1); + } + + @Test + public void checkSectionNumbersY() { + SectionDataIdentifierKey uut = new SectionDataIdentifierKey(1, 3); + assertEquals(uut.getSectionX(), 1); + assertEquals(uut.getSectionY(), 3); + } + + @Test + public void checkHash() { + SectionDataIdentifierKey uut = new SectionDataIdentifierKey(3, 1); + assertEquals(uut.hashCode(), 2611); + } + + @Test + public void checkEquals() { + SectionDataIdentifierKey uut = new SectionDataIdentifierKey(3, 1); + assertFalse(uut.equals(null)); + assertTrue(uut.equals(new SectionDataIdentifierKey(3, 1))); + assertFalse(uut.equals(new SectionDataIdentifierKey(2, 1))); + assertFalse(uut.equals(new SectionDataIdentifierKey(1, 3))); + assertFalse(uut.equals(new SectionDataIdentifierKey(3, 3))); + assertFalse(uut.equals(3)); + assertTrue(uut.equals(uut)); + } + + @Test + public void checkComparison() { + SectionDataIdentifierKey uut1 = new SectionDataIdentifierKey(1, 3); + SectionDataIdentifierKey uut2 = new SectionDataIdentifierKey(1, 2); + assertEquals(uut1.compareTo(uut2), 1); + assertEquals(uut2.compareTo(uut1), -1); + assertEquals(uut1.compareTo(uut1), 0); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/SectionDataListTest.java b/st1002/src/test/java/org/jmisb/st1002/SectionDataListTest.java new file mode 100644 index 000000000..d36c4e6f3 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/SectionDataListTest.java @@ -0,0 +1,61 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.*; + +import java.util.Set; +import org.jmisb.api.klv.IKlvKey; +import org.testng.annotations.Test; + +/** Unit tests for SectionDataList. */ +public class SectionDataListTest { + + @Test + public void checkDisplayName() { + SectionDataList uut = new SectionDataList(); + assertEquals(uut.getDisplayName(), "Section Data"); + } + + @Test + public void checkDisplayableValue() { + SectionDataList uut = new SectionDataList(); + assertEquals(uut.getDisplayableValue(), "[VLPs]"); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void checkBadGetBytesOperation() { + SectionDataList uut = new SectionDataList(); + uut.getBytes(); + } + + @Test + public void checkWithNoElements() { + SectionDataList uut = new SectionDataList(); + assertTrue(uut.getPacks().isEmpty()); + assertTrue(uut.getIdentifiers().isEmpty()); + SectionDataIdentifierKey someKeyNotPresent = new SectionDataIdentifierKey(1, 1); + assertNull(uut.getField(someKeyNotPresent)); + } + + @Test + public void checkWithTwoSectionDataElements() { + SectionDataList uut = new SectionDataList(); + SectionData sectionData1 = new SectionData(1, 1, new double[][] {}, null); + uut.add(sectionData1); + SectionData sectionData2 = new SectionData(1, 2, new double[][] {}, null); + uut.add(sectionData2); + assertFalse(uut.getPacks().isEmpty()); + Set identifiers = uut.getIdentifiers(); + assertEquals(identifiers.size(), 2); + int totalYnumbers = 0; + for (var key : identifiers) { + var field = uut.getField(key); + assertTrue(field instanceof SectionData); + SectionData sectionData = (SectionData) field; + assertEquals(sectionData.getSectionNumberX(), 1); + totalYnumbers += sectionData.getSectionNumberY(); + } + assertEquals(totalYnumbers, 3); + SectionDataIdentifierKey someKeyNotPresent = new SectionDataIdentifierKey(2, 1); + assertNull(uut.getField(someKeyNotPresent)); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/SectionDataTest.java b/st1002/src/test/java/org/jmisb/st1002/SectionDataTest.java new file mode 100644 index 000000000..9c1bbce41 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/SectionDataTest.java @@ -0,0 +1,838 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.*; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Unit tests for Section Data. */ +public class SectionDataTest { + + public SectionDataTest() {} + + @Test + public void checkConstructFromBytes() throws KlvParseException { + byte[] bytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x3F, + (byte) 0xB9, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A, + 0x3F, + (byte) 0xC9, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }; + SectionData uut = new SectionData(bytes); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertEquals(uut.getArrayOfUncertaintyValues(), new double[][] {{0.1}, {0.2}}); + assertEquals(uut.getBytes(), bytes); + assertEquals(uut.getDisplayName(), "Section Data"); + assertEquals(uut.getDisplayableValue(), "Section [5,1]"); + } + + @Test + public void checkConstructFromBytesNoUncertainties() throws KlvParseException { + byte[] bytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00, + 0x08, + 0x40, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0x40, + 0x54, + (byte) 0xC7, + (byte) 0xAE, + 0x14, + 0x7A, + (byte) 0xE1, + 0x48, + 0x08, + 0x40, + 0x29, + (byte) 0xF5, + (byte) 0xC2, + (byte) 0x8F, + 0x5C, + 0x28, + (byte) 0xF6 + }; + SectionData uut = new SectionData(bytes); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertNull(uut.getArrayOfUncertaintyValues()); + assertEquals(uut.getPlaneXScaleFactor(), 12.0); + assertEquals(uut.getPlaneYScaleFactor(), 83.12); + assertEquals(uut.getPlaneConstantValue(), 12.98); + assertEquals(uut.getBytes(), bytes); + } + + @Test + public void checkConstructFromBytesNoScaleFactor() throws KlvParseException { + byte[] bytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00, + 0x08, + 0x40, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0x40, + 0x54, + (byte) 0xC7, + (byte) 0xAE, + 0x14, + 0x7A, + (byte) 0xE1, + 0x48 + }; + SectionData uut = new SectionData(bytes); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertNull(uut.getArrayOfUncertaintyValues()); + assertEquals(uut.getPlaneXScaleFactor(), 12.0); + assertEquals(uut.getPlaneYScaleFactor(), 83.12); + assertEquals(uut.getPlaneConstantValue(), 0.0); + assertEquals(uut.getDisplayName(), "Section Data"); + assertEquals(uut.getDisplayableValue(), "Section [5,1]"); + } + + @Test + public void checkConstructFromBytesNoYScale() throws KlvParseException { + byte[] bytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00, + 0x08, + 0x40, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + }; + SectionData uut = new SectionData(bytes); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertNull(uut.getArrayOfUncertaintyValues()); + assertEquals(uut.getPlaneXScaleFactor(), 12.0); + assertEquals(uut.getPlaneYScaleFactor(), 0.0); + assertEquals(uut.getPlaneConstantValue(), 0.0); + } + + @Test + public void checkConstructFromBytesNoXScale() throws KlvParseException { + byte[] bytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00, + }; + SectionData uut = new SectionData(bytes); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertNull(uut.getArrayOfUncertaintyValues()); + assertEquals(uut.getPlaneXScaleFactor(), 0.0); + assertEquals(uut.getPlaneYScaleFactor(), 0.0); + assertEquals(uut.getPlaneConstantValue(), 0.0); + assertEquals(uut.getBytes(), bytes); + } + + @Test(expectedExceptions = KlvParseException.class) + public void checkConstructFromBytesBadXScaleLength() throws KlvParseException { + byte[] bytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00, + 0x07, + 0x40, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0x40, + 0x54, + (byte) 0xC7, + (byte) 0xAE, + 0x14, + 0x7A, + (byte) 0xE1, + 0x48, + 0x08, + 0x40, + 0x29, + (byte) 0xF5, + (byte) 0xC2, + (byte) 0x8F, + 0x5C, + 0x28, + (byte) 0xF6 + }; + new SectionData(bytes); + } + + @Test(expectedExceptions = KlvParseException.class) + public void checkConstructFromBytesBadYScaleLength() throws KlvParseException { + byte[] bytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00, + 0x08, + 0x40, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x07, + 0x40, + 0x54, + (byte) 0xC7, + (byte) 0xAE, + 0x14, + 0x7A, + 0x48, + 0x08, + 0x40, + 0x29, + (byte) 0xF5, + (byte) 0xC2, + (byte) 0x8F, + 0x5C, + 0x28, + (byte) 0xF6 + }; + new SectionData(bytes); + } + + @Test(expectedExceptions = KlvParseException.class) + public void checkConstructFromBytesBadConstantLength() throws KlvParseException { + byte[] bytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00, + 0x08, + 0x40, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0x40, + 0x54, + (byte) 0xC7, + (byte) 0xAE, + 0x14, + 0x7A, + (byte) 0xE1, + 0x48, + 0x07, + 0x40, + 0x29, + (byte) 0xF5, + (byte) 0xC2, + (byte) 0x8F, + 0x5C, + 0x28 + }; + new SectionData(bytes); + } + + @Test + public void checkConstructFromBytesFloat() throws KlvParseException { + byte[] bytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00, + 0x04, + 0x41, + 0x40, + 0x00, + 0x00, + 0x04, + 0x42, + (byte) 0xA6, + 0x3D, + 0x71, + 0x04, + 0x41, + 0x4F, + (byte) 0xAE, + 0x14 + }; + SectionData uut = new SectionData(bytes); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertNull(uut.getArrayOfUncertaintyValues()); + assertEquals(uut.getPlaneXScaleFactor(), 12.0, 0.00001); + assertEquals(uut.getPlaneYScaleFactor(), 83.12, 0.00001); + assertEquals(uut.getPlaneConstantValue(), 12.98, 0.00001); + } + + @Test + public void checkConstructFromValues() throws KlvParseException { + SectionData uut = + new SectionData( + 5, 1, new double[][] {{2.4}, {-8.67}}, new double[][] {{0.1}, {0.2}}); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertEquals(uut.getArrayOfUncertaintyValues(), new double[][] {{0.1}, {0.2}}); + byte[] expectedBytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x3F, + (byte) 0xB9, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A, + 0x3F, + (byte) 0xC9, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }; + + assertEquals(uut.getBytes(), expectedBytes); + } + + @Test + public void checkConstructFromValuesNullUncertainties() throws KlvParseException { + SectionData uut = new SectionData(5, 1, new double[][] {{2.4}, {-8.67}}, null); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertNull(uut.getArrayOfUncertaintyValues()); + byte[] expectedBytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00 + }; + + assertEquals(uut.getBytes(), expectedBytes); + SectionData copy = new SectionData(uut); + assertEquals(copy.getSectionNumberX(), 5); + assertEquals(copy.getSectionNumberY(), 1); + assertEquals(copy.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertNull(copy.getArrayOfUncertaintyValues()); + assertEquals(copy.getPlaneXScaleFactor(), 0.0); + assertEquals(copy.getPlaneYScaleFactor(), 0.0); + assertEquals(copy.getPlaneConstantValue(), 0.0); + assertEquals(copy.getDisplayName(), "Section Data"); + assertEquals(copy.getDisplayableValue(), "Section [5,1]"); + } + + @Test + public void checkConstructFromValuesWithPlanar() throws KlvParseException { + SectionData uut = + new SectionData(5, 1, new double[][] {{2.4}, {-8.67}}, null, 12.0, 83.12, 12.98); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertNull(uut.getArrayOfUncertaintyValues()); + assertEquals(uut.getPlaneXScaleFactor(), 12.0); + assertEquals(uut.getPlaneYScaleFactor(), 83.12); + assertEquals(uut.getPlaneConstantValue(), 12.98); + byte[] expectedBytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x00, + 0x08, + 0x40, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0x40, + 0x54, + (byte) 0xC7, + (byte) 0xAE, + 0x14, + 0x7A, + (byte) 0xE1, + 0x48, + 0x08, + 0x40, + 0x29, + (byte) 0xF5, + (byte) 0xC2, + (byte) 0x8F, + 0x5C, + 0x28, + (byte) 0xF6 + }; + assertEquals(uut.getBytes(), expectedBytes); + } + + @Test + public void checkConstructFromValuesWithUncertaintiesAndPlanar() throws KlvParseException { + SectionData uut = + new SectionData( + 5, + 1, + new double[][] {{2.4}, {-8.67}}, + new double[][] {{0.1}, {0.2}}, + 12.0, + 83.12, + 12.98); + assertEquals(uut.getSectionNumberX(), 5); + assertEquals(uut.getSectionNumberY(), 1); + assertEquals(uut.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertEquals(uut.getArrayOfUncertaintyValues(), new double[][] {{0.1}, {0.2}}); + assertEquals(uut.getPlaneXScaleFactor(), 12.0); + assertEquals(uut.getPlaneYScaleFactor(), 83.12); + assertEquals(uut.getPlaneConstantValue(), 12.98); + byte[] expectedBytes = + new byte[] { + 0x01, + 0x05, + 0x01, + 0x01, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x40, + 0x03, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + 0x33, + (byte) 0xc0, + 0x21, + 0x57, + 0x0a, + 0x3d, + 0x70, + (byte) 0xa3, + (byte) 0xd7, + 0x15, + 0x02, + 0x02, + 0x01, + 0x08, + 0x01, + 0x3F, + (byte) 0xB9, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A, + 0x3F, + (byte) 0xC9, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A, + 0x08, + 0x40, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0x40, + 0x54, + (byte) 0xC7, + (byte) 0xAE, + 0x14, + 0x7A, + (byte) 0xE1, + 0x48, + 0x08, + 0x40, + 0x29, + (byte) 0xF5, + (byte) 0xC2, + (byte) 0x8F, + 0x5C, + 0x28, + (byte) 0xF6 + }; + assertEquals(uut.getBytes(), expectedBytes); + assertEquals(uut.getDisplayName(), "Section Data"); + assertEquals(uut.getDisplayableValue(), "Section [5,1]"); + SectionData copy = new SectionData(uut); + assertEquals(copy.getSectionNumberX(), 5); + assertEquals(copy.getSectionNumberY(), 1); + assertEquals(copy.getArrayOfMeasuredValues(), new double[][] {{2.4}, {-8.67}}); + assertEquals(copy.getArrayOfUncertaintyValues(), new double[][] {{0.1}, {0.2}}); + assertEquals(copy.getPlaneXScaleFactor(), 12.0); + assertEquals(copy.getPlaneYScaleFactor(), 83.12); + assertEquals(copy.getPlaneConstantValue(), 12.98); + assertEquals(copy.getDisplayName(), "Section Data"); + assertEquals(copy.getDisplayableValue(), "Section [5,1]"); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementColumnTest.java b/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementColumnTest.java new file mode 100644 index 000000000..5cee9ac1f --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementColumnTest.java @@ -0,0 +1,128 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for SinglePointRangeMeasurementColumn. */ +public class SinglePointRangeMeasurementColumnTest { + + @Test + public void fromValue() { + SinglePointRangeMeasurementColumn uut = new SinglePointRangeMeasurementColumn(984.2); + assertEquals(uut.getColumn(), 984.2, 0.000000001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Column"); + assertEquals(uut.getDisplayableValue(), "984.2"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + } + + @Test + public void fromBytesFloat() throws KlvParseException { + SinglePointRangeMeasurementColumn uut = + new SinglePointRangeMeasurementColumn( + new byte[] {(byte) 0x44, (byte) 0x76, (byte) 0x0C, (byte) 0xCD}); + assertEquals(uut.getColumn(), 984.2, 0.0001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Column"); + assertEquals(uut.getDisplayableValue(), "984.2"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0xA0, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }); + } + + @Test + public void fromBytesDouble() throws KlvParseException { + SinglePointRangeMeasurementColumn uut = + new SinglePointRangeMeasurementColumn( + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + assertEquals(uut.getColumn(), 984.2, 0.000000001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Column"); + assertEquals(uut.getDisplayableValue(), "984.2"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IRangeImageMetadataValue v = + RangeImageLocalSet.createValue( + RangeImageMetadataKey.SinglePointRangeMeasurementColumnCoordinate, + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + assertTrue(v instanceof SinglePointRangeMeasurementColumn); + SinglePointRangeMeasurementColumn uut = (SinglePointRangeMeasurementColumn) v; + assertEquals(uut.getColumn(), 984.2, 0.000000001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Column"); + assertEquals(uut.getDisplayableValue(), "984.2"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testBadLength3() throws KlvParseException { + new SinglePointRangeMeasurementColumn(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testBadLength5() throws KlvParseException { + new SinglePointRangeMeasurementColumn(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementRowTest.java b/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementRowTest.java new file mode 100644 index 000000000..098fbe7cf --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementRowTest.java @@ -0,0 +1,128 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for SinglePointRangeMeasurementRow. */ +public class SinglePointRangeMeasurementRowTest { + + @Test + public void fromValue() { + SinglePointRangeMeasurementRow uut = new SinglePointRangeMeasurementRow(984.2); + assertEquals(uut.getRow(), 984.2, 0.000000001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Row"); + assertEquals(uut.getDisplayableValue(), "984.2"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + } + + @Test + public void fromBytesFloat() throws KlvParseException { + SinglePointRangeMeasurementRow uut = + new SinglePointRangeMeasurementRow( + new byte[] {(byte) 0x44, (byte) 0x76, (byte) 0x0C, (byte) 0xCD}); + assertEquals(uut.getRow(), 984.2, 0.0001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Row"); + assertEquals(uut.getDisplayableValue(), "984.2"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0xA0, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }); + } + + @Test + public void fromBytesDouble() throws KlvParseException { + SinglePointRangeMeasurementRow uut = + new SinglePointRangeMeasurementRow( + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + assertEquals(uut.getRow(), 984.2, 0.000000001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Row"); + assertEquals(uut.getDisplayableValue(), "984.2"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IRangeImageMetadataValue v = + RangeImageLocalSet.createValue( + RangeImageMetadataKey.SinglePointRangeMeasurementRowCoordinate, + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + assertTrue(v instanceof SinglePointRangeMeasurementRow); + SinglePointRangeMeasurementRow uut = (SinglePointRangeMeasurementRow) v; + assertEquals(uut.getRow(), 984.2, 0.000000001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Row"); + assertEquals(uut.getDisplayableValue(), "984.2"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x8E, + (byte) 0xC1, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9A + }); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testBadLength3() throws KlvParseException { + new SinglePointRangeMeasurementRow(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testBadLength5() throws KlvParseException { + new SinglePointRangeMeasurementRow(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementTest.java b/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementTest.java new file mode 100644 index 000000000..95b40d020 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementTest.java @@ -0,0 +1,119 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for SinglePointRangeMeasurement. */ +public class SinglePointRangeMeasurementTest { + + @Test + public void fromValue() { + SinglePointRangeMeasurement uut = new SinglePointRangeMeasurement(12.0f); + assertEquals(uut.getRange(), 12.0f); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement"); + assertEquals(uut.getDisplayableValue(), "12.000 m"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x28, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }); + } + + @Test + public void fromBytesFloat() throws KlvParseException { + SinglePointRangeMeasurement uut = + new SinglePointRangeMeasurement( + new byte[] {(byte) 0x41, (byte) 0x40, (byte) 0x00, (byte) 0x00}); + assertEquals(uut.getRange(), 12.0f); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement"); + assertEquals(uut.getDisplayableValue(), "12.000 m"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x28, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }); + } + + @Test + public void fromBytesDouble() throws KlvParseException { + SinglePointRangeMeasurement uut = + new SinglePointRangeMeasurement( + new byte[] { + (byte) 0x40, + (byte) 0x28, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }); + assertEquals(uut.getRange(), 12.0f); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement"); + assertEquals(uut.getDisplayableValue(), "12.000 m"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x28, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IRangeImageMetadataValue v = + RangeImageLocalSet.createValue( + RangeImageMetadataKey.SinglePointRangeMeasurement, + new byte[] {(byte) 0x41, (byte) 0x40, (byte) 0x00, (byte) 0x00}); + assertTrue(v instanceof SinglePointRangeMeasurement); + SinglePointRangeMeasurement uut = (SinglePointRangeMeasurement) v; + assertEquals(uut.getRange(), 12.0f); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement"); + assertEquals(uut.getDisplayableValue(), "12.000 m"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x40, + (byte) 0x28, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testBadLength3() throws KlvParseException { + new SinglePointRangeMeasurement(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testBadLength5() throws KlvParseException { + new SinglePointRangeMeasurement(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } +} diff --git a/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementUncertaintyTest.java b/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementUncertaintyTest.java new file mode 100644 index 000000000..86e943aa0 --- /dev/null +++ b/st1002/src/test/java/org/jmisb/st1002/SinglePointRangeMeasurementUncertaintyTest.java @@ -0,0 +1,120 @@ +package org.jmisb.st1002; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for SinglePointRangeMeasurementUncertainty. */ +public class SinglePointRangeMeasurementUncertaintyTest { + + @Test + public void fromValue() { + SinglePointRangeMeasurementUncertainty uut = + new SinglePointRangeMeasurementUncertainty(0.15); + assertEquals(uut.getRange(), 0.15); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Uncertainty"); + assertEquals(uut.getDisplayableValue(), "0.150 m"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x3F, + (byte) 0xC3, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33 + }); + } + + @Test + public void fromBytesFloat() throws KlvParseException { + SinglePointRangeMeasurementUncertainty uut = + new SinglePointRangeMeasurementUncertainty( + new byte[] {(byte) 0x3E, (byte) 0x19, (byte) 0x99, (byte) 0x9A}); + assertEquals(uut.getRange(), 0.15, 0.00000001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Uncertainty"); + assertEquals(uut.getDisplayableValue(), "0.150 m"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x3F, + (byte) 0xC3, + (byte) 0x33, + (byte) 0x33, + (byte) 0x40, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }); + } + + @Test + public void fromBytesDouble() throws KlvParseException { + SinglePointRangeMeasurementUncertainty uut = + new SinglePointRangeMeasurementUncertainty( + new byte[] { + (byte) 0x3F, + (byte) 0xC3, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33 + }); + assertEquals(uut.getRange(), 0.15); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Uncertainty"); + assertEquals(uut.getDisplayableValue(), "0.150 m"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x3F, + (byte) 0xC3, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33 + }); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IRangeImageMetadataValue v = + RangeImageLocalSet.createValue( + RangeImageMetadataKey.SinglePointRangeMeasurementUncertainty, + new byte[] {(byte) 0x3E, (byte) 0x19, (byte) 0x99, (byte) 0x9A}); + assertTrue(v instanceof SinglePointRangeMeasurementUncertainty); + SinglePointRangeMeasurementUncertainty uut = (SinglePointRangeMeasurementUncertainty) v; + assertEquals(uut.getRange(), 0.15, 0.00000001); + assertEquals(uut.getDisplayName(), "Single Point Range Measurement Uncertainty"); + assertEquals(uut.getDisplayableValue(), "0.150 m"); + assertEquals( + uut.getBytes(), + new byte[] { + (byte) 0x3F, + (byte) 0xC3, + (byte) 0x33, + (byte) 0x33, + (byte) 0x40, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testBadLength3() throws KlvParseException { + new SinglePointRangeMeasurementUncertainty(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testBadLength5() throws KlvParseException { + new SinglePointRangeMeasurementUncertainty(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } +} diff --git a/st1002/testng-unit.xml b/st1002/testng-unit.xml new file mode 100644 index 000000000..9aff492c4 --- /dev/null +++ b/st1002/testng-unit.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/st1010/SpotBugsFilter.xml b/st1010/SpotBugsFilter.xml new file mode 100644 index 000000000..5e55494a7 --- /dev/null +++ b/st1010/SpotBugsFilter.xml @@ -0,0 +1,3 @@ + + + diff --git a/st1010/checkstyle.xml b/st1010/checkstyle.xml new file mode 100644 index 000000000..41e751804 --- /dev/null +++ b/st1010/checkstyle.xml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/st1010/checkstyle_suppressions.xml b/st1010/checkstyle_suppressions.xml new file mode 100644 index 000000000..97784b708 --- /dev/null +++ b/st1010/checkstyle_suppressions.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/st1010/pom.xml b/st1010/pom.xml new file mode 100644 index 000000000..230fda152 --- /dev/null +++ b/st1010/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + org.jmisb + jmisb + 2.0.0-SNAPSHOT + + st1010 + 2.0.0-SNAPSHOT + ST 1010 SDCC + + + ${project.groupId} + jmisb-api + ${project.version} + + + org.slf4j + slf4j-api + + + org.testng + testng + test + + + com.github.valfirst + slf4j-test + test + + + + + + + com.theoryinpractise + googleformatter-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + testng-unit.xml + + + + surefire.testng.verbose + ${surefire.verbosity} + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + SpotBugsFilter.xml + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + edu.berkeley.cs.jqf + jqf-maven-plugin + + + + + + + generate-sbom-cyclonedx + + + performRelease + true + + + + + + org.cyclonedx + cyclonedx-maven-plugin + + + + + + \ No newline at end of file diff --git a/st1010/src/main/java/module-info.java b/st1010/src/main/java/module-info.java new file mode 100644 index 000000000..d319682ce --- /dev/null +++ b/st1010/src/main/java/module-info.java @@ -0,0 +1,20 @@ +/** + * ST 1010: Generalized Standard Deviation and Correlation Coefficient Metadata. + * + *

This standard (ST) defines a bit-efficient method for transmitting standard deviation and + * correlation coefficient data. In support of this method a Standard Deviation and Correlation + * Coefficient Floating Length Pack (SDCC-FLP) construct is defined. The construct leverages the + * symmetry of the variance-covariance matrix and the fixed data range of the correlation + * coefficients to reduce the number of bytes transmitted, in effect compressing the data. This + * method is, therefore, not extendable to more generic matrix cases. This ST is dependent on + * context from an invoking standard (or other document), called a Parent Document, which provides a + * list of values (random variables) that can have corresponding standard deviation and correlation + * coefficients; this list of random variables is called the Source List. + */ +@SuppressWarnings("module") // That is not a version number - its a document number. +module org.jmisb.st1010 { + requires org.jmisb.api; + requires org.slf4j; + + exports org.jmisb.st1010; +} diff --git a/st1010/src/main/java/org/jmisb/st1010/EncodingFormat.java b/st1010/src/main/java/org/jmisb/st1010/EncodingFormat.java new file mode 100644 index 000000000..dc3fbba94 --- /dev/null +++ b/st1010/src/main/java/org/jmisb/st1010/EncodingFormat.java @@ -0,0 +1,60 @@ +package org.jmisb.st1010; + +import org.jmisb.api.common.KlvParseException; + +/** + * Encoding format for Standard Deviation and Correlation Coefficient values. + * + *

ST 1010 supports the encoding of values in two formats (at least in Mode 2 parsing) - IEEE 754 + * floating point, and ST 1201 integer encoding. This enumeration allows identification of the + * parsing format. + * + *

In Mode 1 parsing, the format for standard deviation are externally specified (in the invoking + * specification document), but the correlation coefficients are always in ST 1201 format. + */ +public enum EncodingFormat { + /** + * Value is encoded in an IEEE 754 floating point format. + * + *

The supported formats are per MISB ST 0107. + */ + IEEE(0), + /** + * Value is encoded in MISB ST 1201 format. + * + *

The encoding is IMAPB(-1.0, 1.0). + */ + ST1201(1); + + private EncodingFormat(int value) { + v = value; + } + + private final int v; + + /** + * Get the value associated with this encoding format. + * + * @return 1 if the format is ST1201, otherwise 0 to mean IEEE format. + */ + public int getValue() { + return v; + } + + /** + * Get the encoding from an integer value. + * + * @param v the value to look up (0 or 1) + * @return the corresponding EncodingFormat for the value. + * @throws KlvParseException if the value is out of range. + */ + public static EncodingFormat getEncoding(int v) throws KlvParseException { + if (v == 1) { + return EncodingFormat.ST1201; + } else if (v == 0) { + return EncodingFormat.IEEE; + } else { + throw new KlvParseException("The only encoding format values are 0 or 1, got " + v); + } + } +} diff --git a/st1010/src/main/java/org/jmisb/st1010/SDCC.java b/st1010/src/main/java/org/jmisb/st1010/SDCC.java new file mode 100644 index 000000000..f359b0392 --- /dev/null +++ b/st1010/src/main/java/org/jmisb/st1010/SDCC.java @@ -0,0 +1,187 @@ +package org.jmisb.st1010; + +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import org.jmisb.api.klv.IKlvKey; +import org.jmisb.api.klv.INestedKlvValue; + +/** + * Standard Deviation and Correlation Coefficient Matrix. + * + *

An instance of this class represents the "full" version of the Standard Deviation and Cross + * Correlation (SDCC) matrix. The standard deviation values (error estimates) are on the main + * diagonal. The off-diagonal values are the estimates of correlation between the errors on + * corresponding rows / columns. The SDCC is symmetrical about the main diagonal. + * + *

As an example, consider the case where a local set (such as ST 0601) has Sensor Latitude and + * Sensor Longitude information, and there is an estimate of the error (at the one + * standard-deviation level) - say based on the GPS sensor performance and satellite geometry + * (Horizontal Dilution of Precision - HDOP). In this situation, the latitude and longitude parts of + * the position error are probably fairly closely related (and would have cross-correlation values + * approaching 1). SDCC provides the structure to represent this information. + */ +public class SDCC implements INestedKlvValue { + private int stdDevLength = Float.BYTES; + private int corrCoefLength = 3; + private EncodingFormat stdDevFormat = EncodingFormat.IEEE; + private EncodingFormat corrCoefFormat = EncodingFormat.ST1201; + private double[][] values = new double[0][0]; + + /** + * Constructor. + * + *

This will produce an empty SDCC, initialised to use four-byte IEEE format for standard + * deviations, and three-byte ST 1201 for correlation coefficients. + */ + public SDCC() {} + + /** + * Copy constructor. + * + * @param other the object to copy from. + */ + public SDCC(SDCC other) { + this.stdDevLength = other.getStandardDeviationLength(); + this.corrCoefLength = other.getCorrelationCoefficientLength(); + this.stdDevFormat = other.getStandardDeviationFormat(); + this.corrCoefFormat = other.getCorrelationCoefficientFormat(); + this.values = other.getValues().clone(); + } + + /** + * Get the encoded standard deviation length. + * + * @return the length of each encoded value. + */ + public int getStandardDeviationLength() { + return stdDevLength; + } + + /** + * Set the length of each encoded standard deviation value. + * + *

Note that the format needs to be consistent with the standard deviation format. In + * particular, {@code EncodingFormat.IEEE} requires that the length be 2, 4 or 8 bytes, while + * {@code EncodingFormat.ST1201} requires that the length be consistent with the IMAP encoding. + * + *

All standard deviation values in an SDCC instance need to have the same length. + * + * @param length the length in bytes + */ + public void setStandardDeviationLength(int length) { + this.stdDevLength = length; + } + + /** + * Get the encoded correlation coefficient value length. + * + * @return the length of each encoded value. + */ + public int getCorrelationCoefficientLength() { + return corrCoefLength; + } + + /** + * Set the length of each encoded correlation coefficient value. + * + *

Note that the format needs to be consistent with the correlation coefficient format. In + * particular, {@code EncodingFormat.IEEE} requires that the length be 2, 4 or 8 bytes, while + * {@code EncodingFormat.ST1201} requires that the length be consistent with the IMAP encoding. + * + *

All correlation coefficients in an SDCC instance need to have the same length. + * + * @param length the length in bytes + */ + public void setCorrelationCoefficientLength(int length) { + this.corrCoefLength = length; + } + + /** + * Get the standard deviation encoding format. + * + * @return the format for encoding standard deviations. + */ + public EncodingFormat getStandardDeviationFormat() { + return stdDevFormat; + } + + /** + * Set the standard deviation encoding format. + * + *

Note that the format needs to be consistent with the standard deviation length. + * + *

Also, use of {@code EncodingFormat.ST1201} requires specifying the IMAP encoding for the + * standard deviation value. + * + * @param format the format to use for encoding standard deviation values. + */ + public void setStandardDeviationFormat(EncodingFormat format) { + this.stdDevFormat = format; + } + + /** + * Get the correlation coefficient encoding format. + * + * @return the format for encoding correlation coefficients. + */ + public EncodingFormat getCorrelationCoefficientFormat() { + return corrCoefFormat; + } + + /** + * Set the correlation coefficient encoding format. + * + *

Note that the format needs to be consistent with the correlation coefficient length. + * + * @param format the format to use for encoding correlation coefficients. + */ + public void setCorrelationCoefficientFormat(EncodingFormat format) { + this.corrCoefFormat = format; + } + + /** + * Get the reconstructed matrix values. + * + *

The matrix will have the standard deviations on the main diagonal, and the correlation + * coefficients symmetrical about the main diagonal. Any sparse entries from the encoded format + * will be filled with zeros. + * + * @return the reconstructed matrix. + */ + public double[][] getValues() { + return values.clone(); + } + + /** + * Set the matrix values. + * + *

The matrix should have the standard deviations on the main diagonal, and the correlation + * coefficients symmetrical about the main diagonal. + * + * @param values the matrix values. + */ + public void setValues(double[][] values) { + this.values = values.clone(); + } + + @Override + public Set getIdentifiers() { + SortedSet identifiers = new TreeSet<>(); + for (int rowNumber = 0; rowNumber < values.length; rowNumber++) { + for (int columnNumber = 0; columnNumber < values[rowNumber].length; columnNumber++) { + identifiers.add( + new SDCCValueIdentifierKey( + rowNumber, columnNumber, values[rowNumber].length)); + } + } + return identifiers; + } + + @Override + public SDCCValueWrap getField(IKlvKey identifier) { + SDCCValueIdentifierKey key = (SDCCValueIdentifierKey) identifier; + double value = values[key.getRow()][key.getColumn()]; + return new SDCCValueWrap(key.getRow(), key.getColumn(), value); + } +} diff --git a/st1010/src/main/java/org/jmisb/st1010/SDCCParser.java b/st1010/src/main/java/org/jmisb/st1010/SDCCParser.java new file mode 100644 index 000000000..2769cd42a --- /dev/null +++ b/st1010/src/main/java/org/jmisb/st1010/SDCCParser.java @@ -0,0 +1,189 @@ +package org.jmisb.st1010; + +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.BerDecoder; +import org.jmisb.api.klv.BerField; +import org.jmisb.api.klv.st1201.FpEncoder; +import org.jmisb.api.klv.st1201.OutOfRangeBehaviour; +import org.jmisb.core.klv.PrimitiveConverter; + +/** + * Parsing support for {@link SDCC} instances. + * + *

This turns an ST 1010 encoded byte array into an SDCC structure. + * + *

Note that parsing of an SDCC instance that uses Mode 1 parse control requires out-of-band + * information (defined in the invoking standards document) on the encoding of standard deviation + * values (i.e. either ST 1201 IMAP, or IEEE-754 float). + */ +public class SDCCParser { + + private EncodingFormat standardDeviationFormat = EncodingFormat.IEEE; + + /** + * Constructor. + * + *

Creates a new SDCC Parser, initialized to use IEEE format for standard deviation encoding + * (applicable to Mode 1). Use {@link setStandardDeviationFormat} prior to calling {@link parse} + * if required. + */ + public SDCCParser() {} + + /** + * Get the standard deviation encoding format. + * + * @return the encoding format as an enumerated value. + */ + public EncodingFormat getStandardDeviationFormat() { + return standardDeviationFormat; + } + + /** + * Set the standard deviation encoding format. + * + *

This is only required for "Mode 1" parsing. + * + * @param format the encoding format to use + */ + public void setStandardDeviationFormat(EncodingFormat format) { + this.standardDeviationFormat = format; + } + + /** + * Parse a byte array into an SDCC structure. + * + *

This will automatically detect and handle both Mode 1 and Mode 2 parse control modes. + * + * @param bytes the ST 1010 encoded data + * @return SDCC equivalent to the {@code bytes} data. + * @throws KlvParseException if the byte array could not be parsed. + */ + public SDCC parse(byte[] bytes) throws KlvParseException { + int offset = 0; + BerField berMatrix = BerDecoder.decode(bytes, offset, true); + int matrixSize = berMatrix.getValue(); + offset += berMatrix.getLength(); + SDCCParserContext parserContext; + int modeByte1 = bytes[offset]; + offset += 1; + if (isLastByte(modeByte1)) { + parserContext = initialiseMode1(modeByte1); + } else { + int modeByte2 = bytes[offset]; + offset += 1; + parserContext = initialiseMode2(modeByte1, modeByte2); + } + byte[] bitVector = new byte[0]; + if ((matrixSize > 0) && parserContext.isSparseMode()) { + bitVector = parseBitVector(bytes, offset, matrixSize); + offset += bitVector.length; + } + double[][] values = new double[matrixSize][matrixSize]; + for (int r = 0; r < values.length; r++) { + if (parserContext.getSdcc().getStandardDeviationFormat().equals(EncodingFormat.IEEE)) { + values[r][r] = + PrimitiveConverter.toFloat64( + bytes, + offset, + parserContext.getSdcc().getStandardDeviationLength()); + } else { + throw new UnsupportedOperationException( + "Parsing of ST 1201 encoded SDCC Standard Deviation values needs to be implemented"); + } + offset += parserContext.getSdcc().getStandardDeviationLength(); + } + + if (parserContext.getSdcc().getCorrelationCoefficientLength() > 0) { + FpEncoder correlationCoefficientDecoder = + new FpEncoder( + -1.0, + 1.0, + parserContext.getSdcc().getCorrelationCoefficientLength(), + OutOfRangeBehaviour.Default); + int bitVectorOffset = 7; + int bitVectorValidByte = 0; + for (int r = 0; r < matrixSize; r++) { + for (int c = r + 1; c < matrixSize; c++) { + if (parserContext.isSparseMode() + && ((bitVector[bitVectorValidByte] & (1 << bitVectorOffset)) == 0)) { + values[r][c] = 0.0; + } else { + if (parserContext + .getSdcc() + .getCorrelationCoefficientFormat() + .equals(EncodingFormat.IEEE)) { + values[r][c] = + PrimitiveConverter.toFloat64( + bytes, + offset, + parserContext + .getSdcc() + .getCorrelationCoefficientLength()); + } else { + values[r][c] = correlationCoefficientDecoder.decode(bytes, offset); + } + offset += parserContext.getSdcc().getCorrelationCoefficientLength(); + } + bitVectorOffset -= 1; + if (bitVectorOffset < 0) { + bitVectorOffset = 7; + bitVectorValidByte += 1; + } + values[c][r] = values[r][c]; + } + } + } + parserContext.getSdcc().setValues(values); + return parserContext.getSdcc(); + } + + private byte[] parseBitVector(byte[] bytes, int offset, int matrixSize) { + int numCoefficients = matrixSize * (matrixSize - 1); + int numBits = numCoefficients / 2; + int numBytes = (numBits + 7) / Byte.SIZE; + byte[] bitVector = new byte[numBytes]; + for (int i = 0; i < bitVector.length; i++) { + bitVector[i] = bytes[i + offset]; + } + return bitVector; + } + + private SDCCParserContext initialiseMode2(int modeByte1, int modeByte2) + throws KlvParseException { + // Mode 2 parse control + SDCCParserContext parserContext = new SDCCParserContext(); + if (!isLastByte(modeByte2)) { + throw new KlvParseException( + String.format( + "ST 1010 parsing only supports 1 and 2 byte mode selection, got 0x%02x, 0x%02x", + modeByte1 & 0xFF, modeByte2 & 0xFF)); + } + parserContext.setSparseMode((modeByte1 & 0x20) == 0x20); + int cf = (modeByte1 & 0x10) >> 4; + SDCC sdcc = new SDCC(); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.getEncoding(cf)); + sdcc.setCorrelationCoefficientLength(modeByte1 & 0x0F); + int sf = (modeByte2 & 0x10) >> 4; + sdcc.setStandardDeviationFormat(EncodingFormat.getEncoding(sf)); + sdcc.setStandardDeviationLength(modeByte2 & 0x0F); + parserContext.setSdcc(sdcc); + return parserContext; + } + + private SDCCParserContext initialiseMode1(int modeByte1) { + // Mode 1 parse control + SDCCParserContext parserContext = new SDCCParserContext(); + SDCC sdcc = new SDCC(); + sdcc.setStandardDeviationLength((modeByte1 & 0x70) >> 4); + parserContext.setSparseMode((modeByte1 & 0x08) == 0x08); + sdcc.setCorrelationCoefficientLength(modeByte1 & 0x07); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + sdcc.setStandardDeviationFormat(getStandardDeviationFormat()); + parserContext.setSdcc(sdcc); + return parserContext; + } + + private static boolean isLastByte(int modeByte1) { + return (modeByte1 & 0x80) == 0x00; + } +} diff --git a/st1010/src/main/java/org/jmisb/st1010/SDCCParserContext.java b/st1010/src/main/java/org/jmisb/st1010/SDCCParserContext.java new file mode 100644 index 000000000..2d471f16e --- /dev/null +++ b/st1010/src/main/java/org/jmisb/st1010/SDCCParserContext.java @@ -0,0 +1,49 @@ +package org.jmisb.st1010; + +/** + * Context for {@link SDCC} Parsing. + * + *

This is used by the {@link SDCCParser} implementation, and is an internal implementation + * detail. + */ +class SDCCParserContext { + + private boolean sparseMode; + private SDCC sdcc; + + /** + * Get whether the byte array being parsed uses sparse mode. + * + * @return true if sparse mode is used, false if sparse mode is not used. + */ + public boolean isSparseMode() { + return sparseMode; + } + + /** + * Set whether the byte array being parsed uses spare mode. + * + * @param sparseMode true if sparse mode is used, false if sparse mode is not used. + */ + public void setSparseMode(boolean sparseMode) { + this.sparseMode = sparseMode; + } + + /** + * Get the SDCC matrix. + * + * @return the SDCC matrix. + */ + public SDCC getSdcc() { + return sdcc; + } + + /** + * Set the SDCC matrix. + * + * @param sdcc the matrix. + */ + public void setSdcc(SDCC sdcc) { + this.sdcc = sdcc; + } +} diff --git a/st1010/src/main/java/org/jmisb/st1010/SDCCSerialiser.java b/st1010/src/main/java/org/jmisb/st1010/SDCCSerialiser.java new file mode 100644 index 000000000..088a6b81b --- /dev/null +++ b/st1010/src/main/java/org/jmisb/st1010/SDCCSerialiser.java @@ -0,0 +1,265 @@ +package org.jmisb.st1010; + +import org.jmisb.api.klv.ArrayBuilder; +import org.jmisb.api.klv.Ber; +import org.jmisb.api.klv.BerEncoder; +import org.jmisb.api.klv.st1201.FpEncoder; +import org.jmisb.api.klv.st1201.OutOfRangeBehaviour; +import org.jmisb.core.klv.PrimitiveConverter; + +/** + * Serialization support for {@link SDCC} instances. + * + *

ST 1010 has two encoding formats, referred to as "Parse Modes". Mode 1 is more compact but + * Mode 2 parse control is more flexible, and is required by some standards, and therefore is the + * default. Use {@link setPreferMode1} to select which mode is preferred. There are modes and bit + * lengths that cannot be supported in Mode 1 control. In particular, specifying lengths > 7, or + * {@code EncodingFormat.IEEE} for correlation coefficients will result in Mode 2 being used + * (irrespective of the preferred mode). + * + *

ST 1010 also supports not encoding correlation coefficients that are 0.0 (i.e. no + * correlation), using "sparse mode". This requires adding a bit mask which is not always required. + * By default, sparse mode will be used if it is more efficient (i.e. would save bytes overall). It + * is also possible to disable use of sparse mode using {@link setSparseEnabled}. The result is that + * sparse mode is only used if both it would be more efficient and it is not disabled. + */ +public class SDCCSerialiser { + private boolean preferMode1 = false; + private boolean sparseEnabled = true; + private static final byte NOT_FINAL_BYTE = (byte) (1 << 7); + private static final byte FINAL_BYTE = 0; + private static final byte MODE1_SLEN_SHIFT = 4; + private static final byte MODE1_SPARSE_SHIFT = 3; + private static final byte MODE2_SPARSE_SHIFT = 5; + private static final byte CORRELATION_COEFFICIENT_FORMAT_SHIFT = 4; + private static final byte STANDARD_DEVIATION_FORMAT_SHIFT = 4; + private static final int MAX_MODE1_CORRELATION_COEFFICIENT_LEN = 7; + private static final int MAX_MODE1_STANDARD_DEVIATION_LEN = 7; + + /** + * Get whether encoding should prefer Mode 1 parse control. + * + * @return true if encoding will prefer Mode 1 parse control. + */ + public boolean isPreferMode1() { + return preferMode1; + } + + /** + * Get whether sparse representation is allowed. + * + *

SDCC can reduce space by omitting the encoding of uncorrelated variable covariances (i.e. + * 0 values). See MISB ST 1010.3 for more information. + * + * @return true if sparse encoding is enabled, otherwise false. + */ + public boolean isSparseEnabled() { + return sparseEnabled; + } + + /** + * Set whether sparse representation is allowed. + * + *

SDCC can reduce space by omitting the encoding of uncorrelated variable covariances (i.e. + * 0 values). See MISB ST 1010.3 for more information. + * + *

When serializing, sparse representation will be used if it is enabled and would be more + * efficient than serializing the 0 values (i.e. save bytes). If it is not enabled (set to + * false), full values will be used. If it is not more efficient, full values will be used. + * + *

Sparse representation is enabled by default. + * + * @param sparseEnabled true to enable sparse representation, false to disable sparse + * representation. + */ + public void setSparseEnabled(boolean sparseEnabled) { + this.sparseEnabled = sparseEnabled; + } + + /** + * Set whether encoding should prefer Mode 1 parse control. + * + *

In general, Mode 2 parse control is more flexible, and is required by some standards, and + * therefore is the default. In addition, there are modes and bit lengths that cannot be + * supported in Mode 1 control. In particular, specifying lengths > 7, or {@code + * EncodingFormat.IEEE} for correlation coefficients will result in Mode 2 being used, + * irrespective of this setting. + * + *

If this option is enabled, and the required encoding can work with Mode 1, it will be + * used. + * + * @param preferMode1 true to enable Mode 1 parse control as an option, false to disable Mode 1 + * parse control. + */ + public void setPreferMode1(boolean preferMode1) { + this.preferMode1 = preferMode1; + } + + /** + * Serialize a Standard Deviation and Cross Correlation instance to bytes. + * + * @param sdcc the matrix of values to encode + * @return the corresponding byte array representation + */ + public byte[] encode(SDCC sdcc) { + if (sdcc.getValues().length != sdcc.getValues()[0].length) { + throw new IllegalArgumentException("SDCC requires a square input array"); + } + ArrayBuilder arrayBuilder = new ArrayBuilder(); + byte[] matrixBytes = BerEncoder.encode(sdcc.getValues().length, Ber.OID); + arrayBuilder.append(matrixBytes); + boolean usingSparse = isSparseEnabled() && sparseWouldSaveSpace(sdcc); + arrayBuilder.append(getModeBytes(sdcc, usingSparse)); + byte[] bitVector = new byte[0]; + if (usingSparse) { + bitVector = new byte[getBitVectorLength(sdcc)]; + // The values will get filled in as we work through the coefficients + arrayBuilder.append(bitVector); + } + for (int r = 0; r < sdcc.getValues().length; r++) { + // TODO: ST1201 + double standardDeviation = sdcc.getValues()[r][r]; + switch (sdcc.getStandardDeviationLength()) { + case 4: + arrayBuilder.append( + PrimitiveConverter.float32ToBytes((float) standardDeviation)); + break; + case 8: + arrayBuilder.append(PrimitiveConverter.float64ToBytes(standardDeviation)); + break; + default: + throw new IllegalArgumentException( + "SDCC IEEE format length 4 and 8 is currently supported. More work required for 2 byte. Other lengths not sensible."); + } + } + FpEncoder correlationCoefficientEncoder = + new FpEncoder( + -1.0, + 1.0, + sdcc.getCorrelationCoefficientLength(), + OutOfRangeBehaviour.Default); + int bitVectorOffset = 7; + int bitVectorValidByte = 0; + for (int r = 0; r < sdcc.getValues().length; r++) { + for (int c = r + 1; c < sdcc.getValues().length; c++) { + + double coef = sdcc.getValues()[r][c]; + if (usingSparse) { + if (coef != 0.0) { + bitVector[bitVectorValidByte] |= 1 << bitVectorOffset; + } + bitVectorOffset -= 1; + if (bitVectorOffset < 0) { + bitVectorValidByte += 1; + bitVectorOffset = 7; + } + if (coef == 0.0) { + continue; + } + } + if (sdcc.getCorrelationCoefficientFormat().equals(EncodingFormat.IEEE)) { + switch (sdcc.getCorrelationCoefficientLength()) { + case 4: + arrayBuilder.append(PrimitiveConverter.float32ToBytes((float) coef)); + break; + case 8: + arrayBuilder.append(PrimitiveConverter.float64ToBytes(coef)); + break; + default: + throw new IllegalArgumentException( + "SDCC IEEE format length 4 and 8 is currently supported. More work required for 2 byte. Other lengths not sensible."); + } + } else { + arrayBuilder.append(correlationCoefficientEncoder.encode(coef)); + } + } + } + return arrayBuilder.toBytes(); + } + + private byte[] getModeBytes(SDCC sdcc, boolean usingSparse) { + if (isPreferMode1() && isMode1Compatible(sdcc)) { + return getModeBytesMode1(sdcc, usingSparse); + } + return getModeBytesMode2(sdcc, usingSparse); + } + + private byte[] getModeBytesMode1(SDCC sdcc, boolean usingSparse) { + byte[] parseControl = new byte[1]; + parseControl[0] = FINAL_BYTE; + byte slen = (byte) (sdcc.getStandardDeviationLength() & 0x07); + parseControl[0] |= (slen << MODE1_SLEN_SHIFT); + if (usingSparse) { + parseControl[0] |= 1 << MODE1_SPARSE_SHIFT; + } + byte clen = (byte) (sdcc.getCorrelationCoefficientLength() & 0x07); + parseControl[0] |= clen; + return parseControl; + } + + private byte[] getModeBytesMode2(SDCC sdcc, boolean usingSparse) { + byte[] parseControl = new byte[2]; + parseControl[0] = NOT_FINAL_BYTE; + parseControl[1] = FINAL_BYTE; + int numCoefficients = getNumCoefficients(sdcc); + if (numCoefficients != 0) { + if (usingSparse) { + parseControl[0] |= 1 << MODE2_SPARSE_SHIFT; + } + parseControl[0] |= + (sdcc.getCorrelationCoefficientFormat().getValue() + << CORRELATION_COEFFICIENT_FORMAT_SHIFT); + byte clen = (byte) (sdcc.getCorrelationCoefficientLength() & 0x0F); + parseControl[0] |= clen; + } + parseControl[1] |= + (sdcc.getStandardDeviationFormat().getValue() << STANDARD_DEVIATION_FORMAT_SHIFT); + byte slen = (byte) (sdcc.getStandardDeviationLength() & 0x0F); + parseControl[1] |= slen; + return parseControl; + } + + private int getNumCoefficients(SDCC sdcc) { + return (sdcc.getValues().length * (sdcc.getValues().length - 1)); + } + + private boolean sparseWouldSaveSpace(SDCC sdcc) { + int numZeros = 0; + for (int r = 0; r < sdcc.getValues().length; r++) { + for (int c = r + 1; c < sdcc.getValues().length; c++) { + if (sdcc.getValues()[r][c] == 0.0) { + numZeros += 1; + } + } + } + return (sdcc.getCorrelationCoefficientLength() * numZeros > getBitVectorLength(sdcc)); + } + + /** + * Get the length of the BitVector. + * + * @param sdcc the SDCC to calculate for + * @return the length in bytes + */ + private int getBitVectorLength(SDCC sdcc) { + int numBits = getNumCoefficients(sdcc) / 2; + int numBytes = (numBits + 7) / Byte.SIZE; + return numBytes; + } + + private boolean isMode1Compatible(SDCC sdcc) { + if (sdcc.getCorrelationCoefficientFormat().equals(EncodingFormat.IEEE)) { + return false; + } + if (sdcc.getStandardDeviationFormat().equals(EncodingFormat.ST1201)) { + return false; + } + if (sdcc.getCorrelationCoefficientLength() > MAX_MODE1_CORRELATION_COEFFICIENT_LEN) { + return false; + } + if (sdcc.getStandardDeviationLength() > MAX_MODE1_STANDARD_DEVIATION_LEN) { + return false; + } + return true; + } +} diff --git a/st1010/src/main/java/org/jmisb/st1010/SDCCValueIdentifierKey.java b/st1010/src/main/java/org/jmisb/st1010/SDCCValueIdentifierKey.java new file mode 100644 index 000000000..b322ecae9 --- /dev/null +++ b/st1010/src/main/java/org/jmisb/st1010/SDCCValueIdentifierKey.java @@ -0,0 +1,86 @@ +package org.jmisb.st1010; + +import java.util.Comparator; +import org.jmisb.api.klv.IKlvKey; + +/** + * Pseudo-key item for SDCC value identification. + * + *

Each identifier corresponds to one item in the SDCC value matrix (i.e. a unique row/column + * combination). + */ +public class SDCCValueIdentifierKey implements IKlvKey, Comparable { + + private final int row; + private final int column; + private final int numColumns; + + /** + * Constructor. + * + * @param row row identifier. + * @param column column identifier. + * @param numColumns + */ + public SDCCValueIdentifierKey(final int row, final int column, final int numColumns) { + this.row = row; + this.column = column; + this.numColumns = numColumns; + } + + /** + * Row number. + * + * @return row number as an integer value, zero base. + */ + public int getRow() { + return row; + } + + /** + * Column number. + * + * @return column number as an integer value, zero base. + */ + public int getColumn() { + return column; + } + + @Override + public int getIdentifier() { + return row * numColumns + column; + } + + @Override + public int compareTo(SDCCValueIdentifierKey t) { + return Comparator.comparingInt(SDCCValueIdentifierKey::getRow) + .thenComparingInt(SDCCValueIdentifierKey::getColumn) + .compare(this, t); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 67 * hash + this.row; + hash = 67 * hash + this.column; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final SDCCValueIdentifierKey other = (SDCCValueIdentifierKey) obj; + if (this.row != other.row) { + return false; + } + return this.column == other.column; + } +} diff --git a/st1010/src/main/java/org/jmisb/st1010/SDCCValueWrap.java b/st1010/src/main/java/org/jmisb/st1010/SDCCValueWrap.java new file mode 100644 index 000000000..5cedf5cd1 --- /dev/null +++ b/st1010/src/main/java/org/jmisb/st1010/SDCCValueWrap.java @@ -0,0 +1,65 @@ +package org.jmisb.st1010; + +import org.jmisb.api.klv.IKlvValue; + +/** + * Wrapper for an item in an SDCC matrix. + * + *

This acts as an adapter (facade) around the double value, to present it as an IKlvValue. + */ +public class SDCCValueWrap implements IKlvValue { + + private final int row; + private final int column; + private final double value; + + /** + * Constructor. + * + * @param row the row number + * @param column the column number + * @param value the value + */ + public SDCCValueWrap(final int row, final int column, final double value) { + this.row = row; + this.column = column; + this.value = value; + } + + @Override + public String getDisplayName() { + return String.format("[%d][%d]", row, column); + } + + @Override + public String getDisplayableValue() { + return String.format("%.3f", value); + } + + /** + * Row index. + * + * @return the row index of the value + */ + public int getRow() { + return row; + } + + /** + * Column index. + * + * @return the column index of the value + */ + public int getColumn() { + return column; + } + + /** + * Value. + * + * @return the wrapped value, as a double. + */ + public double getValue() { + return value; + } +} diff --git a/st1010/src/main/java/org/jmisb/st1010/package-info.java b/st1010/src/main/java/org/jmisb/st1010/package-info.java new file mode 100644 index 000000000..681e71c67 --- /dev/null +++ b/st1010/src/main/java/org/jmisb/st1010/package-info.java @@ -0,0 +1,17 @@ +/** + * ST 1010: Generalized Standard Deviation and Correlation Coefficient Metadata. + * + *

This standard (ST) defines a bit-efficient method for transmitting standard deviation and + * correlation coefficient data. + * + *

In support of this method, a Standard Deviation and Correlation Coefficient Floating Length + * Pack (SDCC-FLP) construct is defined. The construct leverages the symmetry of the + * variance-covariance matrix and the fixed data range of the correlation coefficients to reduce the + * number of bytes transmitted, in effect compressing the data. This method is, therefore, not + * extendable to more generic matrix cases. + * + *

This ST is dependent on context from an invoking standard (or other document), called a Parent + * Document, which provides a list of values (random variables) that can have corresponding standard + * deviation and correlation coefficients; this list of random variables is called the Source List. + */ +package org.jmisb.st1010; diff --git a/st1010/src/test/java/org/jmisb/st1010/EncodingFormatTest.java b/st1010/src/test/java/org/jmisb/st1010/EncodingFormatTest.java new file mode 100644 index 000000000..3677c9e86 --- /dev/null +++ b/st1010/src/test/java/org/jmisb/st1010/EncodingFormatTest.java @@ -0,0 +1,48 @@ +package org.jmisb.st1010; + +import static org.testng.Assert.*; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Unit tests for the EncodingFormat enum. */ +public class EncodingFormatTest { + + public EncodingFormatTest() {} + + @Test + public void checkST1201() { + EncodingFormat uut = EncodingFormat.ST1201; + assertEquals(uut.getValue(), 1); + } + + @Test + public void checkST1201Lookup() throws KlvParseException { + EncodingFormat uut = EncodingFormat.getEncoding(1); + assertEquals(uut, EncodingFormat.ST1201); + assertEquals(uut.getValue(), 1); + } + + @Test + public void checkIEEE() { + EncodingFormat uut = EncodingFormat.IEEE; + assertEquals(uut.getValue(), 0); + } + + @Test + public void checkIEEELookup() throws KlvParseException { + EncodingFormat uut = EncodingFormat.getEncoding(0); + assertEquals(uut, EncodingFormat.IEEE); + assertEquals(uut.getValue(), 0); + } + + @Test(expectedExceptions = KlvParseException.class) + public void checkBadLookup2() throws KlvParseException { + EncodingFormat.getEncoding(2); + } + + @Test(expectedExceptions = KlvParseException.class) + public void checkBadLookupNeg() throws KlvParseException { + EncodingFormat.getEncoding(-1); + } +} diff --git a/st1010/src/test/java/org/jmisb/st1010/Mode1ParseControlTest.java b/st1010/src/test/java/org/jmisb/st1010/Mode1ParseControlTest.java new file mode 100644 index 000000000..5b44c13a8 --- /dev/null +++ b/st1010/src/test/java/org/jmisb/st1010/Mode1ParseControlTest.java @@ -0,0 +1,22 @@ +package org.jmisb.st1010; + +import static org.testng.Assert.*; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +public class Mode1ParseControlTest { + + public Mode1ParseControlTest() {} + + @Test + public void check4B() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + parser.setStandardDeviationFormat(EncodingFormat.IEEE); + SDCC result = parser.parse(new byte[] {0x00, 0x4B}); + assertEquals(result.getStandardDeviationLength(), 4); + assertEquals(result.getCorrelationCoefficientLength(), 3); + assertEquals(result.getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(result.getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + } +} diff --git a/st1010/src/test/java/org/jmisb/st1010/Mode2ParseControlTest.java b/st1010/src/test/java/org/jmisb/st1010/Mode2ParseControlTest.java new file mode 100644 index 000000000..06bd3080a --- /dev/null +++ b/st1010/src/test/java/org/jmisb/st1010/Mode2ParseControlTest.java @@ -0,0 +1,31 @@ +package org.jmisb.st1010; + +import static org.testng.Assert.*; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +public class Mode2ParseControlTest { + + public Mode2ParseControlTest() {} + + @Test + public void checkB308() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + SDCC result = parser.parse(new byte[] {(byte) 0x00, (byte) 0xB3, (byte) 0x08}); + assertEquals(result.getStandardDeviationLength(), 8); + assertEquals(result.getCorrelationCoefficientLength(), 3); + assertEquals(result.getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(result.getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + } + + @Test + public void check8413() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + SDCC result = parser.parse(new byte[] {(byte) 0x00, (byte) 0x84, (byte) 0x13}); + assertEquals(result.getStandardDeviationLength(), 3); + assertEquals(result.getCorrelationCoefficientLength(), 4); + assertEquals(result.getStandardDeviationFormat(), EncodingFormat.ST1201); + assertEquals(result.getCorrelationCoefficientFormat(), EncodingFormat.IEEE); + } +} diff --git a/st1010/src/test/java/org/jmisb/st1010/SDCCParserTest.java b/st1010/src/test/java/org/jmisb/st1010/SDCCParserTest.java new file mode 100644 index 000000000..3c3b1b747 --- /dev/null +++ b/st1010/src/test/java/org/jmisb/st1010/SDCCParserTest.java @@ -0,0 +1,236 @@ +package org.jmisb.st1010; + +import static org.testng.Assert.*; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +public class SDCCParserTest { + + public SDCCParserTest() {} + + @Test + public void checkSingleStandardDeviation() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + SDCC result = + parser.parse( + new byte[] { + (byte) 0x01, + (byte) 0x80, + (byte) 0x04, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD + }); + assertEquals(result.getStandardDeviationLength(), 4); + assertEquals(result.getCorrelationCoefficientLength(), 0); + assertEquals(result.getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(result.getCorrelationCoefficientFormat(), EncodingFormat.IEEE); + assertValuesAreEqual(result.getValues(), new double[][] {{3.2}}); + } + + @Test( + expectedExceptions = KlvParseException.class, + expectedExceptionsMessageRegExp = + "ST 1010 parsing only supports 1 and 2 byte mode selection, got 0x80, 0x84") + public void checkBadModeControl() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + parser.parse( + new byte[] { + (byte) 0x01, + (byte) 0x80, + (byte) 0x84, + (byte) 0x00, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD + }); + } + + @Test + public void checkSingleStandardDeviationDouble() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + SDCC result = + parser.parse( + new byte[] { + (byte) 0x01, + (byte) 0x90, + (byte) 0x08, + (byte) 0x40, + (byte) 0x2A, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66 + }); + assertEquals(result.getStandardDeviationLength(), 8); + assertEquals(result.getCorrelationCoefficientLength(), 0); + assertEquals(result.getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(result.getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + assertValuesAreEqual(result.getValues(), new double[][] {{13.2}}); + } + + @Test + public void checkTwoVariable() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + SDCC result = + parser.parse( + new byte[] { + (byte) 0x02, + (byte) 0x84, + (byte) 0x04, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x3e, + (byte) 0xfa, + (byte) 0xe1, + (byte) 0x48 + }); + assertEquals(result.getStandardDeviationLength(), 4); + assertEquals(result.getCorrelationCoefficientLength(), 4); + assertEquals(result.getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(result.getCorrelationCoefficientFormat(), EncodingFormat.IEEE); + assertValuesAreEqual(result.getValues(), new double[][] {{3.2, 0.49}, {0.49, 16.2}}); + } + + @Test + public void checkTwoVariableMode1() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + parser.setStandardDeviationFormat(EncodingFormat.IEEE); + SDCC result = + parser.parse( + new byte[] { + (byte) 0x02, + (byte) 0x42, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x5f, + (byte) 0x5c + }); + assertEquals(result.getStandardDeviationLength(), 4); + assertEquals(result.getCorrelationCoefficientLength(), 2); + assertEquals(result.getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(result.getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + assertValuesAreEqual(result.getValues(), new double[][] {{3.2, 0.49}, {0.49, 16.2}}); + } + + private void assertValuesAreEqual(double[][] actual, double[][] expected) { + assertEquals(actual.length, expected.length); + if (expected.length > 0) { + assertEquals(actual[0].length, expected[0].length); + for (int r = 0; r < expected.length; r++) { + for (int c = 0; c < expected[r].length; c++) { + assertEquals(actual[r][c], expected[r][c], 0.00001); + } + } + } + } + + @Test + public void checkThreeVariableSparse() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + SDCC result = + parser.parse( + new byte[] { + (byte) 0x03, + (byte) 0xB3, + (byte) 0x04, + (byte) 0b01000000, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x43, + (byte) 0x6a, + (byte) 0x33, + (byte) 0x33, + (byte) 0x5f, + (byte) 0x5c, + (byte) 0x28 + }); + assertEquals(result.getStandardDeviationLength(), 4); + assertEquals(result.getCorrelationCoefficientLength(), 3); + assertEquals(result.getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(result.getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + assertValuesAreEqual( + result.getValues(), + new double[][] { + {3.2, 0.0, 0.49}, + {0.0, 16.2, 0.0}, + {0.49, 0.0, 234.2} + }); + } + + @Test + public void checkFiveVariableSparse() throws KlvParseException { + SDCCParser parser = new SDCCParser(); + SDCC result = + parser.parse( + new byte[] { + (byte) 0x05, + (byte) 0xB3, + (byte) 0x04, + (byte) 0b01000000, + (byte) 0b10000000, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x43, + (byte) 0x6a, + (byte) 0x33, + (byte) 0x33, + (byte) 0x3e, + (byte) 0xa3, + (byte) 0xd7, + (byte) 0x0a, + (byte) 0x40, + (byte) 0xc9, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x5f, + (byte) 0x5c, + (byte) 0x28, + (byte) 0x80, + (byte) 0x00, + (byte) 0x00 + }); + assertEquals(result.getStandardDeviationLength(), 4); + assertEquals(result.getCorrelationCoefficientLength(), 3); + assertEquals(result.getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(result.getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + assertValuesAreEqual( + result.getValues(), + new double[][] { + {3.2, 0.0, 0.49, 0.0, 0.0}, + {0.0, 16.2, 0.0, 0.0, 0.0}, + {0.49, 0.0, 234.2, 0.0, 1.0}, + {0.0, 0.0, 0.0, 0.32, 0.0}, + {0.0, 0.0, 1.0, 0.0, 6.3} + }); + } +} diff --git a/st1010/src/test/java/org/jmisb/st1010/SDCCTest.java b/st1010/src/test/java/org/jmisb/st1010/SDCCTest.java new file mode 100644 index 000000000..18a49fca6 --- /dev/null +++ b/st1010/src/test/java/org/jmisb/st1010/SDCCTest.java @@ -0,0 +1,97 @@ +package org.jmisb.st1010; + +import static org.testng.Assert.*; + +import org.testng.annotations.Test; + +/** Unit tests for SDCC. */ +public class SDCCTest { + + public SDCCTest() {} + + @Test + public void checkDefaults() { + SDCC uut = new SDCC(); + assertEquals(uut.getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + assertEquals(uut.getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(uut.getCorrelationCoefficientLength(), 3); + assertEquals(uut.getStandardDeviationLength(), 4); + assertEquals(uut.getValues().length, 0); + } + + @Test + public void checkSetterAndCopy() { + SDCC uut = new SDCC(); + uut.setCorrelationCoefficientFormat(EncodingFormat.IEEE); + uut.setStandardDeviationFormat(EncodingFormat.ST1201); + uut.setCorrelationCoefficientLength(8); + uut.setStandardDeviationLength(3); + uut.setValues(new double[][] {{5.3, 0.2}, {0.2, 1.6}}); + assertEquals(uut.getCorrelationCoefficientFormat(), EncodingFormat.IEEE); + assertEquals(uut.getStandardDeviationFormat(), EncodingFormat.ST1201); + assertEquals(uut.getCorrelationCoefficientLength(), 8); + assertEquals(uut.getStandardDeviationLength(), 3); + assertEquals(uut.getValues().length, 2); + assertEquals(uut.getValues()[0].length, 2); + assertEquals(uut.getValues()[0][0], 5.3); + assertEquals(uut.getValues()[0][1], 0.2); + assertEquals(uut.getValues()[1][0], 0.2); + assertEquals(uut.getValues()[1][1], 1.6); + assertEquals(uut.getIdentifiers().size(), 4); + boolean found00 = false; + boolean found01 = false; + boolean found10 = false; + boolean found11 = false; + for (SDCCValueIdentifierKey identifier : uut.getIdentifiers()) { + if ((identifier.getRow() == 0) && (identifier.getColumn() == 0)) { + found00 = true; + assertEquals(uut.getField(identifier).getDisplayName(), "[0][0]"); + assertEquals(uut.getField(identifier).getDisplayableValue(), "5.300"); + assertEquals(uut.getField(identifier).getRow(), 0); + assertEquals(uut.getField(identifier).getColumn(), 0); + assertEquals(uut.getField(identifier).getValue(), 5.3, 0.0001); + } + if ((identifier.getRow() == 0) && (identifier.getColumn() == 1)) { + found01 = true; + assertEquals(uut.getField(identifier).getDisplayName(), "[0][1]"); + assertEquals(uut.getField(identifier).getDisplayableValue(), "0.200"); + assertEquals(uut.getField(identifier).getRow(), 0); + assertEquals(uut.getField(identifier).getColumn(), 1); + assertEquals(uut.getField(identifier).getValue(), 0.2, 0.0001); + } + if ((identifier.getRow() == 1) && (identifier.getColumn() == 0)) { + found10 = true; + assertEquals(uut.getField(identifier).getDisplayName(), "[1][0]"); + assertEquals(uut.getField(identifier).getDisplayableValue(), "0.200"); + assertEquals(uut.getField(identifier).getRow(), 1); + assertEquals(uut.getField(identifier).getColumn(), 0); + assertEquals(uut.getField(identifier).getValue(), 0.2, 0.0001); + } + + if ((identifier.getRow() == 1) && (identifier.getColumn() == 1)) { + found11 = true; + assertEquals(uut.getField(identifier).getDisplayName(), "[1][1]"); + assertEquals(uut.getField(identifier).getDisplayableValue(), "1.600"); + assertEquals(uut.getField(identifier).getRow(), 1); + assertEquals(uut.getField(identifier).getColumn(), 1); + assertEquals(uut.getField(identifier).getValue(), 1.6, 0.0001); + } + } + assertTrue(found00); + assertTrue(found01); + assertTrue(found10); + assertTrue(found11); + SDCC copy = new SDCC(uut); + assertEquals(copy.getCorrelationCoefficientFormat(), EncodingFormat.IEEE); + assertEquals(copy.getStandardDeviationFormat(), EncodingFormat.ST1201); + assertEquals(copy.getCorrelationCoefficientLength(), 8); + assertEquals(copy.getStandardDeviationLength(), 3); + assertEquals(copy.getValues().length, 2); + assertEquals(copy.getValues()[0].length, 2); + assertEquals(copy.getValues()[0][0], 5.3); + assertEquals(copy.getValues()[0][1], 0.2); + assertEquals(copy.getValues()[1][0], 0.2); + assertEquals(copy.getValues()[1][1], 1.6); + assertEquals(uut.getIdentifiers().size(), 4); + } +} diff --git a/st1010/src/test/java/org/jmisb/st1010/SDCCValueIdentifierKeyTest.java b/st1010/src/test/java/org/jmisb/st1010/SDCCValueIdentifierKeyTest.java new file mode 100644 index 000000000..d9e87f9c4 --- /dev/null +++ b/st1010/src/test/java/org/jmisb/st1010/SDCCValueIdentifierKeyTest.java @@ -0,0 +1,49 @@ +package org.jmisb.st1010; + +import static org.testng.Assert.*; + +import org.testng.annotations.Test; + +/** Unit tests for SDCCValueIdentifierKey. */ +public class SDCCValueIdentifierKeyTest { + + @Test + public void checkIdentifier() { + SDCCValueIdentifierKey uut = new SDCCValueIdentifierKey(3, 1, 4); + assertEquals(uut.getIdentifier(), 13); + } + + @Test + public void checkRowAndColumn() { + SDCCValueIdentifierKey uut = new SDCCValueIdentifierKey(3, 1, 4); + assertEquals(uut.getRow(), 3); + assertEquals(uut.getColumn(), 1); + } + + @Test + public void checkHash() { + SDCCValueIdentifierKey uut = new SDCCValueIdentifierKey(3, 1, 4); + assertEquals(uut.hashCode(), 22647); + } + + @Test + public void checkEquals() { + SDCCValueIdentifierKey uut = new SDCCValueIdentifierKey(3, 1, 4); + assertFalse(uut.equals(null)); + assertTrue(uut.equals(new SDCCValueIdentifierKey(3, 1, 4))); + assertFalse(uut.equals(new SDCCValueIdentifierKey(2, 1, 4))); + assertFalse(uut.equals(new SDCCValueIdentifierKey(1, 3, 4))); + assertFalse(uut.equals(new SDCCValueIdentifierKey(3, 3, 4))); + assertFalse(uut.equals(3)); + assertTrue(uut.equals(uut)); + } + + @Test + public void checkComparison() { + SDCCValueIdentifierKey uut1 = new SDCCValueIdentifierKey(1, 3, 4); + SDCCValueIdentifierKey uut2 = new SDCCValueIdentifierKey(1, 2, 4); + assertEquals(uut1.compareTo(uut2), 1); + assertEquals(uut2.compareTo(uut1), -1); + assertEquals(uut1.compareTo(uut1), 0); + } +} diff --git a/st1010/src/test/java/org/jmisb/st1010/SDCCWriterTest.java b/st1010/src/test/java/org/jmisb/st1010/SDCCWriterTest.java new file mode 100644 index 000000000..0592ae1e0 --- /dev/null +++ b/st1010/src/test/java/org/jmisb/st1010/SDCCWriterTest.java @@ -0,0 +1,563 @@ +package org.jmisb.st1010; + +import static org.testng.Assert.*; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Unit tests for SDCCWriter */ +public class SDCCWriterTest { + + public SDCCWriterTest() {} + + @Test + public void checkSingleStandardDeviation() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{3.2}}); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + SDCCSerialiser writer = new SDCCSerialiser(); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x01, + (byte) 0x80, + (byte) 0x04, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD + }); + } + + @Test + public void checkSingleStandardDeviationDouble() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{13.2}}); + sdcc.setStandardDeviationLength(8); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + SDCCSerialiser writer = new SDCCSerialiser(); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x01, + (byte) 0x80, + (byte) 0x08, + (byte) 0x40, + (byte) 0x2A, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66 + }); + } + + @Test + public void checkSingleStandardDeviationDoubleMode1Fallback() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{13.2}}); + sdcc.setStandardDeviationLength(8); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.setPreferMode1(true); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x01, + (byte) 0x80, + (byte) 0x08, + (byte) 0x40, + (byte) 0x2A, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66, + (byte) 0x66 + }); + } + + @Test + public void checkTwoVariable() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{3.2, 0.49}, {0.49, 16.2}}); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(4); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.IEEE); + SDCCSerialiser writer = new SDCCSerialiser(); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x02, + (byte) 0x84, + (byte) 0x04, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x3e, + (byte) 0xfa, + (byte) 0xe1, + (byte) 0x48 + }); + } + + @Test + public void checkTwoVariableST1201() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{3.2, 0.49}, {0.49, 16.2}}); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(3); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + SDCCSerialiser writer = new SDCCSerialiser(); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x02, + (byte) 0x93, + (byte) 0x04, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x5f, + (byte) 0x5c, + (byte) 0x28, + }); + } + + @Test + public void checkTwoVariableST1201Mode1Fallback() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{3.2, 0.49}, {0.49, 16.2}}); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(8); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.setSparseEnabled(false); + writer.setPreferMode1(true); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x02, + (byte) 0x98, + (byte) 0x04, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x5f, + (byte) 0x5c, + (byte) 0x28, + (byte) 0xf5, + (byte) 0xc2, + (byte) 0x8f, + (byte) 0x5c, + (byte) 0x00 + }); + } + + @Test + public void checkTwoVariableIEEEDoubleCovariance() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{3.2, 0.49}, {0.49, 16.2}}); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(8); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.IEEE); + SDCCSerialiser writer = new SDCCSerialiser(); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x02, + (byte) 0x88, + (byte) 0x04, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9A, + (byte) 0x3F, + (byte) 0xDF, + (byte) 0x5C, + (byte) 0x28, + (byte) 0xF5, + (byte) 0xC2, + (byte) 0x8F, + (byte) 0x5C + }); + } + + @Test + public void checkTwoVariableIEEEDoubleCovarianceMode1Fallback() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{3.2, 0.49}, {0.49, 16.2}}); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(8); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.IEEE); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.setPreferMode1(true); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x02, + (byte) 0x88, + (byte) 0x04, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9A, + (byte) 0x3F, + (byte) 0xDF, + (byte) 0x5C, + (byte) 0x28, + (byte) 0xF5, + (byte) 0xC2, + (byte) 0x8F, + (byte) 0x5C + }); + } + + @Test + public void checkTwoVariableMode1() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{3.2, 0.49}, {0.49, 16.2}}); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(2); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.setPreferMode1(true); + writer.setSparseEnabled(false); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x02, + (byte) 0x42, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x5f, + (byte) 0x5c + }); + assertTrue(writer.isPreferMode1()); + } + + @Test + public void checkThreeVariableNotSparse() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues( + new double[][] { + {3.2, 0.0, 0.49}, + {0.0, 16.2, 0.0}, + {0.49, 0.0, 234.2} + }); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(3); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.setSparseEnabled(false); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x03, + (byte) 0x93, + (byte) 0x04, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x43, + (byte) 0x6a, + (byte) 0x33, + (byte) 0x33, + (byte) 0x40, + (byte) 0x00, + (byte) 0x00, + (byte) 0x5f, + (byte) 0x5c, + (byte) 0x28, + (byte) 0x40, + (byte) 0x00, + (byte) 0x00, + }); + } + + @Test + public void checkThreeVariableSparse() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues( + new double[][] { + {3.2, 0.0, 0.49}, + {0.0, 16.2, 0.0}, + {0.49, 0.0, 234.2} + }); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(3); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.setSparseEnabled(true); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x03, + (byte) 0xB3, + (byte) 0x04, + (byte) 0b01000000, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x43, + (byte) 0x6a, + (byte) 0x33, + (byte) 0x33, + (byte) 0x5f, + (byte) 0x5c, + (byte) 0x28 + }); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "SDCC requires a square input array") + public void checkNonSquareValues() { + SDCC sdcc = new SDCC(); + sdcc.setValues( + new double[][] { + {3.2, 0.0, 0.49}, + {0.0, 16.2, 0.0} + }); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.encode(sdcc); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = + "SDCC IEEE format length 4 and 8 is currently supported. More work required for 2 byte. Other lengths not sensible.") + public void checkIEEEBadLengthStandardDeviation() { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{3.2, 0.0, 0.49}, {0.0, 16.2, 0.0}, {0.49, 0.0, 234.2}}); + sdcc.setStandardDeviationLength(3); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(4); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.IEEE); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.encode(sdcc); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = + "SDCC IEEE format length 4 and 8 is currently supported. More work required for 2 byte. Other lengths not sensible.") + public void checkIEEEBadLengthCorrelationCoefficients() { + SDCC sdcc = new SDCC(); + sdcc.setValues(new double[][] {{3.2, 0.0, 0.49}, {0.0, 16.2, 0.0}, {0.49, 0.0, 234.2}}); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(3); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.IEEE); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.encode(sdcc); + } + + @Test + public void checkThreeVariableSparseDefault() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues( + new double[][] { + {3.2, 0.0, 0.49}, + {0.0, 16.2, 0.0}, + {0.49, 0.0, 234.2} + }); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(3); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + SDCCSerialiser writer = new SDCCSerialiser(); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x03, + (byte) 0xB3, + (byte) 0x04, + (byte) 0b01000000, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x43, + (byte) 0x6a, + (byte) 0x33, + (byte) 0x33, + (byte) 0x5f, + (byte) 0x5c, + (byte) 0x28 + }); + } + + @Test + public void checkFiveVariableSparse() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues( + new double[][] { + {3.2, 0.0, 0.49, 0.0, 0.0}, + {0.0, 16.2, 0.0, 0.0, 0.0}, + {0.49, 0.0, 234.2, 0.0, 1.0}, + {0.0, 0.0, 0.0, 0.32, 0.0}, + {0.0, 0.0, 1.0, 0.0, 6.3} + }); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(3); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + SDCCSerialiser writer = new SDCCSerialiser(); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x05, + (byte) 0xB3, + (byte) 0x04, + (byte) 0b01000000, + (byte) 0b10000000, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x43, + (byte) 0x6a, + (byte) 0x33, + (byte) 0x33, + (byte) 0x3e, + (byte) 0xa3, + (byte) 0xd7, + (byte) 0x0a, + (byte) 0x40, + (byte) 0xc9, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x5f, + (byte) 0x5c, + (byte) 0x28, + (byte) 0x80, + (byte) 0x00, + (byte) 0x00 + }); + } + + @Test + public void checkFiveVariableSparseMode1() throws KlvParseException { + SDCC sdcc = new SDCC(); + sdcc.setValues( + new double[][] { + {3.2, 0.0, 0.49, 0.0, 0.0}, + {0.0, 16.2, 0.0, 0.0, 0.0}, + {0.49, 0.0, 234.2, 0.0, 1.0}, + {0.0, 0.0, 0.0, 0.32, 0.0}, + {0.0, 0.0, 1.0, 0.0, 6.3} + }); + sdcc.setStandardDeviationLength(4); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setCorrelationCoefficientLength(3); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + SDCCSerialiser writer = new SDCCSerialiser(); + writer.setPreferMode1(true); + byte[] encoded = writer.encode(sdcc); + assertEquals( + encoded, + new byte[] { + (byte) 0x05, + (byte) 0x4B, + (byte) 0b01000000, + (byte) 0b10000000, + (byte) 0x40, + (byte) 0x4C, + (byte) 0xCC, + (byte) 0xCD, + (byte) 0x41, + (byte) 0x81, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x43, + (byte) 0x6a, + (byte) 0x33, + (byte) 0x33, + (byte) 0x3e, + (byte) 0xa3, + (byte) 0xd7, + (byte) 0x0a, + (byte) 0x40, + (byte) 0xc9, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x5f, + (byte) 0x5c, + (byte) 0x28, + (byte) 0x80, + (byte) 0x00, + (byte) 0x00 + }); + } +} diff --git a/st1010/testng-unit.xml b/st1010/testng-unit.xml new file mode 100644 index 000000000..0c28a01b9 --- /dev/null +++ b/st1010/testng-unit.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/st1202/SpotBugsFilter.xml b/st1202/SpotBugsFilter.xml new file mode 100644 index 000000000..5e55494a7 --- /dev/null +++ b/st1202/SpotBugsFilter.xml @@ -0,0 +1,3 @@ + + + diff --git a/st1202/checkstyle.xml b/st1202/checkstyle.xml new file mode 100644 index 000000000..41e751804 --- /dev/null +++ b/st1202/checkstyle.xml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/st1202/checkstyle_suppressions.xml b/st1202/checkstyle_suppressions.xml new file mode 100644 index 000000000..97784b708 --- /dev/null +++ b/st1202/checkstyle_suppressions.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/st1202/pom.xml b/st1202/pom.xml new file mode 100644 index 000000000..f74bc1dd1 --- /dev/null +++ b/st1202/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + org.jmisb + jmisb + 2.0.0-SNAPSHOT + + st1202 + jar + ST 1202 Generalized Transformation Parameters + + + ${project.groupId} + jmisb-api + ${project.version} + + + ${project.groupId} + st1010 + ${project.version} + + + org.slf4j + slf4j-api + + + org.testng + testng + test + + + com.github.valfirst + slf4j-test + test + + + + + + + com.theoryinpractise + googleformatter-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + testng-unit.xml + + + + surefire.testng.verbose + ${surefire.verbosity} + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + SpotBugsFilter.xml + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + edu.berkeley.cs.jqf + jqf-maven-plugin + + + + + + + generate-sbom-cyclonedx + + + performRelease + true + + + + + + org.cyclonedx + cyclonedx-maven-plugin + + + + + + \ No newline at end of file diff --git a/st1202/src/main/java/module-info.java b/st1202/src/main/java/module-info.java new file mode 100644 index 000000000..d27c9343f --- /dev/null +++ b/st1202/src/main/java/module-info.java @@ -0,0 +1,28 @@ +/** + * MISB ST 1202.2 Generalized Transformation Parameters. + * + *

This standard describes a generalized method of transforming two-dimensional data (or points) + * from one coordinate system into a second two-dimensional coordinate system. This Generalized + * Transformation may be used for various image-to-image transformations such as an affine + * transformation by simply equating some parameters to be equal to zero. In addition, this + * Generalized Transformation may describe some homographic-like transformations. + * + *

This standard defines three items: + * + *

    + *
  • The different methods of implementation and constraints that need to be enforced to + * maintain certain transformation relationships. + *
  • The mandatory method of uncertainty propagation to be implemented on systems where + * uncertainty information is needed. + *
  • The KLV Local Set (LS) that represents all the parameters for the Generalized + * Transformation. + *
+ */ +@SuppressWarnings("module") // That is not a version number - its a document number. +module org.jmisb.st1202 { + requires org.jmisb.api; + requires org.jmisb.st1010; + requires org.slf4j; + + exports org.jmisb.st1202; +} diff --git a/st1202/src/main/java/org/jmisb/st1202/AbstractGeneralizedTransformationMetadataValue.java b/st1202/src/main/java/org/jmisb/st1202/AbstractGeneralizedTransformationMetadataValue.java new file mode 100644 index 000000000..7af8ace13 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/AbstractGeneralizedTransformationMetadataValue.java @@ -0,0 +1,69 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; +import org.jmisb.core.klv.PrimitiveConverter; + +/** + * Abstract shared representation for 4 byte float values. + * + *

Since so much of the local set is common, this is a single representation. + */ +public abstract class AbstractGeneralizedTransformationMetadataValue + implements IGeneralizedTransformationMetadataValue { + + private final float value; + private final String displayName; + + /** + * Create from value. + * + * @param value the value as a float. + * @param displayName the human-readable display name for the value + */ + protected AbstractGeneralizedTransformationMetadataValue( + final float value, final String displayName) { + this.value = value; + this.displayName = displayName; + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 + * @param displayName the human-readable display name for the value + * @throws KlvParseException if the byte array is not of the correct length + */ + protected AbstractGeneralizedTransformationMetadataValue(byte[] bytes, final String displayName) + throws KlvParseException { + try { + this.value = PrimitiveConverter.toFloat32(bytes); + this.displayName = displayName; + } catch (IllegalArgumentException ex) { + throw new KlvParseException(getDisplayName() + " is of length 4 bytes"); + } + } + + @Override + public final String getDisplayName() { + return displayName; + } + + /** + * Get the value. + * + * @return value as a floating point value. + */ + public float getValue() { + return this.value; + } + + @Override + public byte[] getBytes() { + return PrimitiveConverter.float32ToBytes(value); + } + + @Override + public String getDisplayableValue() { + return String.format("%.3f", this.value); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/Denominator_X.java b/st1202/src/main/java/org/jmisb/st1202/Denominator_X.java new file mode 100644 index 000000000..8ea3926ae --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/Denominator_X.java @@ -0,0 +1,28 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; + +/** Denominator - x factor (ST 1202 Local Set Item 7). */ +public class Denominator_X extends AbstractGeneralizedTransformationMetadataValue { + + private static final String DISPLAY_NAME = "Denominator - x factor"; + + /** + * Create from value. + * + * @param value the value as a float. + */ + public Denominator_X(float value) { + super(value, DISPLAY_NAME); + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 + * @throws KlvParseException if the byte array is not of the correct length + */ + public Denominator_X(byte[] bytes) throws KlvParseException { + super(bytes, DISPLAY_NAME); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/Denominator_Y.java b/st1202/src/main/java/org/jmisb/st1202/Denominator_Y.java new file mode 100644 index 000000000..3decf444a --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/Denominator_Y.java @@ -0,0 +1,28 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; + +/** Denominator - y factor (ST 1202 Local Set Item 8). */ +public class Denominator_Y extends AbstractGeneralizedTransformationMetadataValue { + + private static final String DISPLAY_NAME = "Denominator - y factor"; + + /** + * Create from value. + * + * @param value the value as a float. + */ + public Denominator_Y(float value) { + super(value, DISPLAY_NAME); + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 + * @throws KlvParseException if the byte array is not of the correct length + */ + public Denominator_Y(byte[] bytes) throws KlvParseException { + super(bytes, DISPLAY_NAME); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/GeneralizedTransformationLocalSet.java b/st1202/src/main/java/org/jmisb/st1202/GeneralizedTransformationLocalSet.java new file mode 100644 index 000000000..13e9fcba8 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/GeneralizedTransformationLocalSet.java @@ -0,0 +1,156 @@ +package org.jmisb.st1202; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.ArrayBuilder; +import org.jmisb.api.klv.IKlvKey; +import org.jmisb.api.klv.IMisbMessage; +import org.jmisb.api.klv.LdsField; +import org.jmisb.api.klv.LdsParser; +import org.jmisb.api.klv.UniversalLabel; +import org.jmisb.st1010.SDCC; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Generalized Transformation Local Set. + * + *

This is the core ST 1202 Local Set. + */ +public class GeneralizedTransformationLocalSet implements IMisbMessage { + + /** + * Universal label for Generalized Transformation Local Set. + * + *

See ST 1202.2 Table 2. + */ + public static final UniversalLabel GeneralizedTransformationLocalSetUl = + new UniversalLabel( + new byte[] { + 0x06, 0x0E, 0x2B, 0x34, 0x02, 0x0B, 0x01, 0x01, 0x0E, 0x01, 0x03, 0x05, + 0x05, 0x00, 0x00, 0x00 + }); + + private static final Logger LOGGER = + LoggerFactory.getLogger(GeneralizedTransformationLocalSet.class); + + /** + * Create a {@link IGeneralizedTransformationMetadataValue} instance from encoded bytes. + * + * @param tag The tag defining the value type + * @param bytes Encoded bytes + * @return The new instance + * @throws KlvParseException if the parsing of the encoded bytes fails + */ + static IGeneralizedTransformationMetadataValue createValue( + GeneralizedTransformationParametersKey tag, byte[] bytes) throws KlvParseException { + switch (tag) { + case X_Numerator_x: + return new X_Numerator_X(bytes); + case X_Numerator_y: + return new X_Numerator_Y(bytes); + case X_Numerator_Constant: + return new X_Numerator_Constant(bytes); + case Y_Numerator_x: + return new Y_Numerator_X(bytes); + case Y_Numerator_y: + return new Y_Numerator_Y(bytes); + case Y_Numerator_Constant: + return new Y_Numerator_Constant(bytes); + case Denominator_x: + return new Denominator_X(bytes); + case Denominator_y: + return new Denominator_Y(bytes); + case SDCC: + return new SDCC_FLP(bytes); + case DocumentVersion: + return new ST1202DocumentVersion(bytes); + case TransformationEnumeration: + return TransformationEnumeration.fromBytes(bytes); + default: + LOGGER.info("Unknown Generalized Transformation Metadata tag: {}", tag); + } + return null; + } + + /** Map containing all elements in the message. */ + private final SortedMap< + GeneralizedTransformationParametersKey, IGeneralizedTransformationMetadataValue> + map = new TreeMap<>(); + + /** + * Create the local set from the given key/value pairs. + * + * @param values Tag/value pairs to be included in the local set + */ + public GeneralizedTransformationLocalSet( + Map + values) { + map.putAll(values); + } + + /** + * Build a Generalized Transformation Local Set from encoded bytes. + * + *

This assumes the byte array is always nested (see requirement ST 1202.1-09). + * + * @param bytes the bytes to build from + * @throws KlvParseException if parsing fails + */ + public GeneralizedTransformationLocalSet(final byte[] bytes) throws KlvParseException { + List fields = LdsParser.parseFields(bytes, 0, bytes.length); + for (LdsField field : fields) { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(field.getTag()); + switch (key) { + case Undefined: + LOGGER.info("Unknown Generalized Transformation tag: {}", field.getTag()); + break; + default: + IGeneralizedTransformationMetadataValue value = + createValue(key, field.getData()); + map.put(key, value); + } + } + } + + @Override + public byte[] frameMessage(boolean isNested) { + if (!isNested) { + throw new IllegalArgumentException( + "Generalized Tranformation Local Set must be nested"); + } + ArrayBuilder builder = new ArrayBuilder(); + for (GeneralizedTransformationParametersKey tag : map.keySet()) { + builder.appendAsOID(tag.getIdentifier()); + byte[] valueBytes = getField(tag).getBytes(); + builder.appendAsBerLength(valueBytes.length); + builder.append(valueBytes); + } + return builder.toBytes(); + } + + @Override + public Set getIdentifiers() { + return map.keySet(); + } + + @Override + public IGeneralizedTransformationMetadataValue getField(IKlvKey key) { + return map.get((GeneralizedTransformationParametersKey) key); + } + + @Override + public UniversalLabel getUniversalLabel() { + return GeneralizedTransformationLocalSetUl; + } + + @Override + public String displayHeader() { + return "ST 1202 Generalized Transformation Local Set"; + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/GeneralizedTransformationParametersKey.java b/st1202/src/main/java/org/jmisb/st1202/GeneralizedTransformationParametersKey.java new file mode 100644 index 000000000..c8fc695e7 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/GeneralizedTransformationParametersKey.java @@ -0,0 +1,148 @@ +package org.jmisb.st1202; + +import java.util.HashMap; +import java.util.Map; +import org.jmisb.api.klv.IKlvKey; + +/** + * ST 1202 tags - description and numbers. + * + *

This enumeration maps the Generalized Transformation Local Set tag values to a name, and names + * to tag values. It conceptually corresponds to the Tag ID and Name columns in ST 1202.2 Table 2. + */ +public enum GeneralizedTransformationParametersKey implements IKlvKey { + /** + * Unknown key. + * + *

This should not be created. It does not correspond to any known ST 1202 tag / key. + */ + Undefined(0), + + /** + * x Equation Numerator - x factor. + * + *

ST 1202 Local Set Tag 1. + * + *

This is {code A} in ST 1202.2 Equation 1. + */ + X_Numerator_x(1), + + /** + * x Equation Numerator - y factor. + * + *

ST 1202 Local Set Tag 2. + * + *

This is {code B} in ST 1202.2 Equation 1. + */ + X_Numerator_y(2), + + /** + * x Equation Numerator - constant factor. + * + *

ST 1202 Local Set Tag 3. + * + *

This is {code C} in ST 1202.2 Equation 1. + */ + X_Numerator_Constant(3), + + /** + * y Equation Numerator - x factor. + * + *

ST 1202 Local Set Tag 4. + * + *

This is {code D} in ST 1202.2 Equation 2. + */ + Y_Numerator_x(4), + + /** + * y Equation Numerator - y factor. + * + *

ST 1202 Local Set Tag 5. + * + *

This is {code E} in ST 1202.2 Equation 2. + */ + Y_Numerator_y(5), + + /** + * y Equation Numerator - constant factor. + * + *

ST 1202 Local Set Tag 6. + * + *

This is {code F} in ST 1202.2 Equation 2. + */ + Y_Numerator_Constant(6), + + /** + * Denominator - x factor. + * + *

ST 1202 Local Set Tag 7. + * + *

This is {code G} in ST 1202.2 Equations 1 and 2. + */ + Denominator_x(7), + + /** + * Denominator - y factor. + * + *

ST 1202 Local Set Tag 8. + * + *

This is {code H} in ST 1202.2 Equations 1 and 2. + */ + Denominator_y(8), + + /** + * Standard Deviation and Correlation Coefficients (SDCC). + * + *

ST 1202 Local Set Tag 9. + */ + SDCC(9), + + /** + * Document Version. + * + *

ST 1202 Local Set Tag 10. + */ + DocumentVersion(10), + + /** + * Transformation Enumeration. + * + *

ST 1202 Local Set Tag 11. + */ + TransformationEnumeration(11); + + private final int tag; + + private static final Map tagTable = + new HashMap<>(); + + static { + for (GeneralizedTransformationParametersKey key : values()) { + tagTable.put(key.tag, key); + } + } + + private GeneralizedTransformationParametersKey(int tag) { + this.tag = tag; + } + + /** + * Get the tag value associated with this enumeration value. + * + * @return integer tag value for the local set identifier + */ + @Override + public int getIdentifier() { + return tag; + } + + /** + * Look up the key by tag identifier. + * + * @param tag the integer tag value to look up + * @return corresponding local set key + */ + public static GeneralizedTransformationParametersKey getKey(int tag) { + return tagTable.getOrDefault(tag, Undefined); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/IGeneralizedTransformationMetadataValue.java b/st1202/src/main/java/org/jmisb/st1202/IGeneralizedTransformationMetadataValue.java new file mode 100644 index 000000000..b8b0254c7 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/IGeneralizedTransformationMetadataValue.java @@ -0,0 +1,19 @@ +package org.jmisb.st1202; + +import org.jmisb.api.klv.IKlvValue; + +/** + * ST 1202 Generalized Transformation Local Set value. + * + *

All Generalized Transformation Local Set values implement this interface. Users are unlikely + * to need to implement this interface. Instead, use one of the implementations either directly, or + * via the Local Set implementation. + */ +public interface IGeneralizedTransformationMetadataValue extends IKlvValue { + /** + * Get the encoded bytes. + * + * @return The encoded byte array + */ + byte[] getBytes(); +} diff --git a/st1202/src/main/java/org/jmisb/st1202/SDCC_FLP.java b/st1202/src/main/java/org/jmisb/st1202/SDCC_FLP.java new file mode 100644 index 000000000..829972b46 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/SDCC_FLP.java @@ -0,0 +1,91 @@ +package org.jmisb.st1202; + +import java.util.Set; +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.IKlvKey; +import org.jmisb.api.klv.IKlvValue; +import org.jmisb.api.klv.INestedKlvValue; +import org.jmisb.st1010.SDCC; +import org.jmisb.st1010.SDCCParser; +import org.jmisb.st1010.SDCCSerialiser; + +/** + * Standard Deviation and Correlation Coefficient (SDCC) Floating Length Pack (FLP). + * + *

In many applications, the knowledge of the uncertainty of all estimated values is critical to + * understand the performance of a system. Thus, it is desirable to provide a means to propagate the + * uncertainty information of the transformation parameters. The Generalized Transformation LS + * utilizes the format described in MISB ST 1010 for transmitting the standard deviation and + * correlation coefficient information. + */ +public class SDCC_FLP implements IGeneralizedTransformationMetadataValue, INestedKlvValue { + + /** Underlying SDCC value. */ + private SDCC sdcc; + + /** + * Construct from value. + * + * @param value the SDCC instance to copy values from + */ + public SDCC_FLP(final SDCC value) { + this.sdcc = new SDCC(value); + } + /** + * Construct from encoded bytes. + * + * @param bytes encoded byte array, per ST 1010. + * @throws KlvParseException if parsing fails + */ + public SDCC_FLP(byte[] bytes) throws KlvParseException { + SDCCParser parser = new SDCCParser(); + this.sdcc = parser.parse(bytes); + } + + @Override + public byte[] getBytes() { + SDCCSerialiser serialiser = new SDCCSerialiser(); + serialiser.setPreferMode1(true); + serialiser.setSparseEnabled(false); + return serialiser.encode(sdcc); + } + + @Override + public String getDisplayName() { + return "Standard Deviation and Correlation Coefficients"; + } + + @Override + public String getDisplayableValue() { + // TODO: see if we can do better + return "[SDCC]"; + } + + /** + * Get the standard deviation and correlation coefficient object. + * + * @return copy of the ST 1010 SDCC object. + */ + public SDCC getSDCC() { + return new SDCC(sdcc); + } + + /** + * Get the standard deviation and correlation coefficient matrix. + * + * @return copy of the ST 1010 SDCC values matrix. + */ + public double[][] getSDCCMatrix() { + return sdcc.getValues(); + } + + @Override + public IKlvValue getField(IKlvKey identifier) { + return sdcc.getField(identifier); + } + + @Override + public Set getIdentifiers() { + return sdcc.getIdentifiers(); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/ST1202DocumentVersion.java b/st1202/src/main/java/org/jmisb/st1202/ST1202DocumentVersion.java new file mode 100644 index 000000000..d7044c01d --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/ST1202DocumentVersion.java @@ -0,0 +1,78 @@ +package org.jmisb.st1202; + +import org.jmisb.core.klv.PrimitiveConverter; + +/** + * ST 1202 Document Version (ST 1202 Generalized Transformation Local Set Tag 10). + * + *

The version number is the same of the minor version number of the standard document. For + * example, with MISB ST 1202.1, the version number value is {@code 1} and with ST 1202.2, the + * version number value is {@code 2}. + * + *

This implementation assumes the document version is UINT8 encoded - the standard states + * BER-OID encoded, but also states 1 byte, and a valid range of 0 to 255. That is inconsistent, + * although it makes no practical difference for feasible version numbers. MISB have indicated that + * a future update (assuming there ever is one) to ST 1202 would change this to UINT8. + */ +public class ST1202DocumentVersion implements IGeneralizedTransformationMetadataValue { + + private final int version; + + /** + * The currently supported revision is 1202.2. + * + *

This may be useful in the constructor. + */ + public static final short ST_VERSION_NUMBER = 2; + + /** + * Create from value. + * + *

The current version is available as {@link #ST_VERSION_NUMBER}. + * + * @param versionNumber The version number + */ + public ST1202DocumentVersion(int versionNumber) { + if (versionNumber < 0) { + throw new IllegalArgumentException("ST 1202 Document Version cannot be negative"); + } + this.version = versionNumber; + } + + /** + * Create from encoded bytes. + * + * @param bytes Byte array containing UINT8 formatted version number + */ + public ST1202DocumentVersion(byte[] bytes) { + this.version = PrimitiveConverter.toUint8(bytes); + } + + /** + * Get the document version number. + * + * @return The version number + */ + public int getVersion() { + return version; + } + + @Override + public byte[] getBytes() { + return PrimitiveConverter.uint8ToBytes((short) version); + } + + @Override + public String getDisplayName() { + return "Document Version"; + } + + @Override + public String getDisplayableValue() { + if (version == 0) { + return "ST 1202"; + } else { + return "ST 1202." + version; + } + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/TransformationEnumeration.java b/st1202/src/main/java/org/jmisb/st1202/TransformationEnumeration.java new file mode 100644 index 000000000..af768f802 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/TransformationEnumeration.java @@ -0,0 +1,157 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; + +/** Transformation type enumeration. */ +public enum TransformationEnumeration implements IGeneralizedTransformationMetadataValue { + /** + * Unknown value. + * + *

This is not a valid enumeration value, and typically indicates a problem with decoding + * (e.g. bad data or implementation bug). + */ + UNKNOWN(-1, "UNKNOWN", ""), + + /** + * Other – No Defined Transformation. + * + *

An enumeration value equal to {@code 0} implies the transformation type is not defined; + * however, this does not prevent the user from exploiting the information contained within the + * Generalized Transformation LS. + */ + OTHER(0, "Other - No Defined Transformation (NDT)", ""), + + /** + * Chipping Transformation. + * + *

An enumeration value equal to {@code 1} signifies the transmitted image is a chip (or + * sub-region) from a larger image. Examples of a chipped image are: 1) a sub-region of an image + * that may be digitally enlarged (zoom); 2) a sub-region of an image selected to reduce + * bandwidth, or to provide higher quality within the sub-region. + */ + CHIPPING(1, "Chipping Transformation (CT)", "px"), + + /** + * Child-Parent Transformation. + * + *

An enumeration value equal to {@code 2} indicates the transformation of a child focal + * plane array (FPA) to its parent FPA (e.g. example defined in MISB ST 1002). This CPT is a + * plane-to-plane transformation used to transform between FPA's in image space. + */ + CHILD_PARENT(2, "Child-Parent Transformation (CPT)", "mm"), + + /** + * Default Pixel-Space to Image-Space Transformation. + * + *

An enumeration value equal to {@code 3} is the default pixel-space to image-space + * transformation. + */ + DPIT(3, "Default Pixel-Space to Image-Space Transformation (DPIT)", "mm"), + + /** + * Optical Transformation. + * + *

An enumeration value equal to {@code 4} indicates the pixel data of an image is a + * translation, rotation, scale or skew from the originating FPA to final optical focal plane. + * This may occur when the originating FPA is a subset of an entire optical focal plane. An + * example is a Combined Composite Focal Plane Array (CCFPA) sensor, where multiple focal plane + * array detectors combine to image a single optical focal plane. This optical transformation is + * a plane-to-plane transformation to transform from FPA to the optical image plane. In addition + * to providing a transformation from FPA to CCFPA, the optical transformation may also support + * the effects of Coudé paths or Fast Steering Mirrors (FSM). Coudé path and FSM effects may + * mimic that of the transformation between FPA and CCFPA. They may also differ, however, by + * translating, rotating, scaling or skewing the optical image plane. + */ + OPTICAL(4, "Optical Transformation (OT)", "mm"); + + /** + * Get the enumeration value for a byte array. + * + * @param bytes the byte array, length 1 + * @return the corresponding enumeration value, or UNKNOWN if there is no matching enumeration + * value. + * @throws KlvParseException if the parsing fails (e.g. wrong length byte array) + */ + static TransformationEnumeration fromBytes(byte[] bytes) throws KlvParseException { + if (bytes.length != 1) { + throw new KlvParseException( + "Transformation Enumeration should be encoded as a 1 byte array"); + } + int value = bytes[0]; + return TransformationEnumeration.lookup(value); + } + + /** + * Look up enumeration value by encoded value. + * + * @param value the encoded (integer) value + * @return the corresponding enumeration value, or UNKNOWN if the value did not match any entry + */ + public static TransformationEnumeration lookup(int value) { + for (TransformationEnumeration transformationType : values()) { + if (transformationType.getEncodedValue() == value) { + return transformationType; + } + } + return UNKNOWN; + } + + private final int encodedValue; + private final String description; + private final String units; + + private TransformationEnumeration( + final int encodedValue, final String description, final String units) { + this.encodedValue = encodedValue; + this.description = description; + this.units = units; + } + + @Override + public byte[] getBytes() { + if (this == UNKNOWN) { + throw new IllegalArgumentException( + "Cannot serialise UNKNOWN Transformation Enumeration"); + } + return new byte[] {(byte) encodedValue}; + } + + @Override + public String getDisplayName() { + return "Transformation Enumeration"; + } + + @Override + public String getDisplayableValue() { + return description; + } + + /** + * Encoded value. + * + * @return integer encoded value for the enumeration + */ + public int getEncodedValue() { + return encodedValue; + } + + /** + * Text description. + * + * @return String containing human-readable enumeration description. + */ + public String getDescription() { + return description; + } + + /** + * Transformation units. + * + *

The transformation units as provided in ST 1202.2 Table 1. + * + * @return units as a text string (blank if "None"). + */ + public String getUnits() { + return units; + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/X_Numerator_Constant.java b/st1202/src/main/java/org/jmisb/st1202/X_Numerator_Constant.java new file mode 100644 index 000000000..d8560b8d9 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/X_Numerator_Constant.java @@ -0,0 +1,28 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; + +/** x Equation Numerator - Constant factor (ST 1202 Local Set Item 3). */ +public class X_Numerator_Constant extends AbstractGeneralizedTransformationMetadataValue { + + private static final String DISPLAY_NAME = "x Equation Numerator - Constant factor"; + + /** + * Create from value. + * + * @param value the value as a float. + */ + public X_Numerator_Constant(float value) { + super(value, DISPLAY_NAME); + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 + * @throws KlvParseException if the byte array is not of the correct length + */ + public X_Numerator_Constant(byte[] bytes) throws KlvParseException { + super(bytes, DISPLAY_NAME); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/X_Numerator_X.java b/st1202/src/main/java/org/jmisb/st1202/X_Numerator_X.java new file mode 100644 index 000000000..cd3f4bf5e --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/X_Numerator_X.java @@ -0,0 +1,28 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; + +/** x Equation Numerator - x factor (ST 1202 Local Set Item 1). */ +public class X_Numerator_X extends AbstractGeneralizedTransformationMetadataValue { + + private static final String DISPLAY_NAME = "x Equation Numerator - x factor"; + + /** + * Create from value. + * + * @param value the value as a float. + */ + public X_Numerator_X(float value) { + super(value, DISPLAY_NAME); + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 + * @throws KlvParseException if the byte array is not of the correct length + */ + public X_Numerator_X(byte[] bytes) throws KlvParseException { + super(bytes, DISPLAY_NAME); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/X_Numerator_Y.java b/st1202/src/main/java/org/jmisb/st1202/X_Numerator_Y.java new file mode 100644 index 000000000..a4d0ed074 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/X_Numerator_Y.java @@ -0,0 +1,28 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; + +/** x Equation Numerator - y factor (ST 1202 Local Set Item 2). */ +public class X_Numerator_Y extends AbstractGeneralizedTransformationMetadataValue { + + private static final String DISPLAY_NAME = "x Equation Numerator - y factor"; + + /** + * Create from value. + * + * @param value the value as a float. + */ + public X_Numerator_Y(float value) { + super(value, DISPLAY_NAME); + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 + * @throws KlvParseException if the byte array is not of the correct length + */ + public X_Numerator_Y(byte[] bytes) throws KlvParseException { + super(bytes, DISPLAY_NAME); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/Y_Numerator_Constant.java b/st1202/src/main/java/org/jmisb/st1202/Y_Numerator_Constant.java new file mode 100644 index 000000000..5242c141b --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/Y_Numerator_Constant.java @@ -0,0 +1,28 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; + +/** y Equation Numerator - Constant factor (ST 1202 Local Set Item 6). */ +public class Y_Numerator_Constant extends AbstractGeneralizedTransformationMetadataValue { + + private static final String DISPLAY_NAME = "y Equation Numerator - Constant factor"; + + /** + * Create from value. + * + * @param value the value as a float. + */ + public Y_Numerator_Constant(float value) { + super(value, DISPLAY_NAME); + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 + * @throws KlvParseException if the byte array is not of the correct length + */ + public Y_Numerator_Constant(byte[] bytes) throws KlvParseException { + super(bytes, DISPLAY_NAME); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/Y_Numerator_X.java b/st1202/src/main/java/org/jmisb/st1202/Y_Numerator_X.java new file mode 100644 index 000000000..cd62ce6ca --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/Y_Numerator_X.java @@ -0,0 +1,28 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; + +/** y Equation Numerator - x factor (ST 1202 Local Set Item 4). */ +public class Y_Numerator_X extends AbstractGeneralizedTransformationMetadataValue { + + private static final String DISPLAY_NAME = "y Equation Numerator - x factor"; + + /** + * Create from value. + * + * @param value the value as a float. + */ + public Y_Numerator_X(float value) { + super(value, DISPLAY_NAME); + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 + * @throws KlvParseException if the byte array is not of the correct length + */ + public Y_Numerator_X(byte[] bytes) throws KlvParseException { + super(bytes, DISPLAY_NAME); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/Y_Numerator_Y.java b/st1202/src/main/java/org/jmisb/st1202/Y_Numerator_Y.java new file mode 100644 index 000000000..ea1ec41a0 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/Y_Numerator_Y.java @@ -0,0 +1,28 @@ +package org.jmisb.st1202; + +import org.jmisb.api.common.KlvParseException; + +/** y Equation Numerator - y factor (ST 1202 Local Set Item 5). */ +public class Y_Numerator_Y extends AbstractGeneralizedTransformationMetadataValue { + + private static final String DISPLAY_NAME = "y Equation Numerator - y factor"; + + /** + * Create from value. + * + * @param value the value as a float. + */ + public Y_Numerator_Y(float value) { + super(value, DISPLAY_NAME); + } + + /** + * Create from encoded bytes. + * + * @param bytes Encoded byte array of length 4 + * @throws KlvParseException if the byte array is not of the correct length + */ + public Y_Numerator_Y(byte[] bytes) throws KlvParseException { + super(bytes, DISPLAY_NAME); + } +} diff --git a/st1202/src/main/java/org/jmisb/st1202/package-info.java b/st1202/src/main/java/org/jmisb/st1202/package-info.java new file mode 100644 index 000000000..34174d2b2 --- /dev/null +++ b/st1202/src/main/java/org/jmisb/st1202/package-info.java @@ -0,0 +1,10 @@ +/** + * MISB ST 1202.2 Generalized Transformation Parameters. + * + *

This standard describes a generalized method of transforming two-dimensional data (or points) + * from one coordinate system into a second two-dimensional coordinate system. This Generalized + * Transformation may be used for various image-to-image transformations such as an affine + * transformation by simply equating some parameters to be equal to zero. In addition, this + * Generalized Transformation may describe some homographic-like transformations. + */ +package org.jmisb.st1202; diff --git a/st1202/src/test/java/org/jmisb/st1202/Denominator_X_Test.java b/st1202/src/test/java/org/jmisb/st1202/Denominator_X_Test.java new file mode 100644 index 000000000..455ed191e --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/Denominator_X_Test.java @@ -0,0 +1,62 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for Denominator_X. */ +public class Denominator_X_Test { + + @Test + public void fromValue() { + Denominator_X uut = new Denominator_X(0.0f); + assertEquals(uut.getValue(), 0.0f); + assertEquals(uut.getDisplayName(), "Denominator - x factor"); + assertEquals(uut.getDisplayableValue(), "0.000"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytes() throws KlvParseException { + Denominator_X uut = + new Denominator_X(new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertEquals(uut.getValue(), 0.0f); + assertEquals(uut.getDisplayName(), "Denominator - x factor"); + assertEquals(uut.getDisplayableValue(), "0.000"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IGeneralizedTransformationMetadataValue v = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.Denominator_x, + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertTrue(v instanceof Denominator_X); + Denominator_X uut = (Denominator_X) v; + assertEquals(uut.getValue(), 0.0f); + assertEquals(uut.getDisplayName(), "Denominator - x factor"); + assertEquals(uut.getDisplayableValue(), "0.000"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooShort() throws KlvParseException { + new Denominator_X(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooLong() throws KlvParseException { + new Denominator_X(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testNotDouble() throws KlvParseException { + new Denominator_X(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/Denominator_Y_Test.java b/st1202/src/test/java/org/jmisb/st1202/Denominator_Y_Test.java new file mode 100644 index 000000000..d1331933c --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/Denominator_Y_Test.java @@ -0,0 +1,62 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for Denominator_Y. */ +public class Denominator_Y_Test { + + @Test + public void fromValue() { + Denominator_Y uut = new Denominator_Y(0.2f); + assertEquals(uut.getValue(), 0.2f); + assertEquals(uut.getDisplayName(), "Denominator - y factor"); + assertEquals(uut.getDisplayableValue(), "0.200"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3E, (byte) 0x4C, (byte) 0xCC, (byte) 0xCD}); + } + + @Test + public void fromBytes() throws KlvParseException { + Denominator_Y uut = + new Denominator_Y(new byte[] {(byte) 0x3E, (byte) 0x4C, (byte) 0xCC, (byte) 0xCD}); + assertEquals(uut.getValue(), 0.2f); + assertEquals(uut.getDisplayName(), "Denominator - y factor"); + assertEquals(uut.getDisplayableValue(), "0.200"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3E, (byte) 0x4C, (byte) 0xCC, (byte) 0xCD}); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IGeneralizedTransformationMetadataValue v = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.Denominator_y, + new byte[] {(byte) 0x3E, (byte) 0x4C, (byte) 0xCC, (byte) 0xCD}); + assertTrue(v instanceof Denominator_Y); + Denominator_Y uut = (Denominator_Y) v; + assertEquals(uut.getValue(), 0.2f); + assertEquals(uut.getDisplayName(), "Denominator - y factor"); + assertEquals(uut.getDisplayableValue(), "0.200"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3E, (byte) 0x4C, (byte) 0xCC, (byte) 0xCD}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooShort() throws KlvParseException { + new Denominator_Y(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooLong() throws KlvParseException { + new Denominator_Y(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testNotDouble() throws KlvParseException { + new Denominator_Y(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/GeneralizedTransformationLocalSetTest.java b/st1202/src/test/java/org/jmisb/st1202/GeneralizedTransformationLocalSetTest.java new file mode 100644 index 000000000..c9c389975 --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/GeneralizedTransformationLocalSetTest.java @@ -0,0 +1,351 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.*; + +import java.util.HashMap; +import java.util.Map; +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Unit tests for Generalized Transformation Local Set implementation. */ +public class GeneralizedTransformationLocalSetTest extends LoggerChecks { + + private final byte[] bytesAll = + new byte[] { + 0x01, + 0x04, + 0x3F, + (byte) 0x80, + 0x00, + 0x00, + 0x02, + 0x04, + 0x40, + 0x00, + 0x00, + 0x00, + 0x03, + 0x04, + 0x40, + 0x40, + 0x00, + 0x00, + 0x04, + 0x04, + 0x40, + (byte) 0x80, + 0x00, + 0x00, + 0x05, + 0x04, + 0x40, + (byte) 0xa0, + 0x00, + 0x00, + 0x06, + 0x04, + 0x40, + (byte) 0xc0, + 0x00, + 0x00, + 0x07, + 0x04, + 0x40, + (byte) 0xe0, + 0x00, + 0x00, + 0x08, + 0x04, + 0x41, + 0x00, + 0x00, + 0x00, + 0x09, + 90, + 0x08, + 0b01000010, + (byte) 0x3d, + (byte) 0xcc, + (byte) 0xcc, + (byte) 0xcd, + (byte) 0x3e, + (byte) 0x4c, + (byte) 0xcc, + (byte) 0xcd, + (byte) 0x3e, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x3e, + (byte) 0xcc, + (byte) 0xcc, + (byte) 0xcd, + (byte) 0x3f, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x3f, + (byte) 0x19, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x3f, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x3f, + (byte) 0x4c, + (byte) 0xcc, + (byte) 0xcd, + (byte) 0x40, + (byte) 0xa3, + (byte) 0x41, + (byte) 0x47, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + 0x0a, + 0x01, + 0x02, + 0x0b, + 0x01, + 0x02 + }; + + public GeneralizedTransformationLocalSetTest() { + super(GeneralizedTransformationLocalSet.class); + } + + @Test + public void checkFromBytesSimple() throws KlvParseException { + // This is not a reasonable local set, just serves for testing + byte[] bytes = new byte[] {0x0a, 0x01, 0x02}; + GeneralizedTransformationLocalSet uut = new GeneralizedTransformationLocalSet(bytes); + verifyNoLoggerMessages(); + assertEquals( + uut.getUniversalLabel(), + GeneralizedTransformationLocalSet.GeneralizedTransformationLocalSetUl); + assertEquals(uut.displayHeader(), "ST 1202 Generalized Transformation Local Set"); + assertEquals(uut.getIdentifiers().size(), 1); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.DocumentVersion)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayableValue(), + "ST 1202.2"); + assertEquals(uut.frameMessage(true), bytes); + verifyNoLoggerMessages(); + } + + @Test + public void checkFromBytesWithUnknown() throws KlvParseException { + // This is not a reasonable local set, just serves for testing + byte[] bytes = new byte[] {0x7f, 0x01, 0x02, 0x0a, 0x01, 0x02}; + GeneralizedTransformationLocalSet uut = new GeneralizedTransformationLocalSet(bytes); + this.verifySingleLoggerMessage("Unknown Generalized Transformation tag: 127"); + assertEquals( + uut.getUniversalLabel(), + GeneralizedTransformationLocalSet.GeneralizedTransformationLocalSetUl); + assertEquals(uut.displayHeader(), "ST 1202 Generalized Transformation Local Set"); + assertEquals(uut.getIdentifiers().size(), 1); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.DocumentVersion)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayableValue(), + "ST 1202.2"); + assertEquals(uut.frameMessage(true), new byte[] {0x0a, 0x01, 0x02}); + verifyNoLoggerMessages(); + } + + @Test + public void checkFromValues() throws KlvParseException { + Map map = + new HashMap<>(); + map.put( + GeneralizedTransformationParametersKey.DocumentVersion, + new ST1202DocumentVersion(2)); + GeneralizedTransformationLocalSet uut = new GeneralizedTransformationLocalSet(map); + verifyNoLoggerMessages(); + assertEquals( + uut.getUniversalLabel(), + GeneralizedTransformationLocalSet.GeneralizedTransformationLocalSetUl); + assertEquals(uut.displayHeader(), "ST 1202 Generalized Transformation Local Set"); + assertEquals(uut.getIdentifiers().size(), 1); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.DocumentVersion)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayableValue(), + "ST 1202.2"); + assertEquals(uut.frameMessage(true), new byte[] {0x0a, 0x01, 0x02}); + verifyNoLoggerMessages(); + } + + @Test + public void checkFromBytesAll() throws KlvParseException { + verifyNoLoggerMessages(); + GeneralizedTransformationLocalSet uut = new GeneralizedTransformationLocalSet(bytesAll); + verifyNoLoggerMessages(); + assertEquals( + uut.getUniversalLabel(), + GeneralizedTransformationLocalSet.GeneralizedTransformationLocalSetUl); + assertEquals(uut.displayHeader(), "ST 1202 Generalized Transformation Local Set"); + assertEquals(uut.getIdentifiers().size(), 11); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.X_Numerator_x)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.X_Numerator_x) + .getDisplayableValue(), + "1.000"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.X_Numerator_y)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.X_Numerator_y) + .getDisplayableValue(), + "2.000"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.X_Numerator_Constant)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.X_Numerator_Constant) + .getDisplayableValue(), + "3.000"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.Y_Numerator_x)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.Y_Numerator_x) + .getDisplayableValue(), + "4.000"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.Y_Numerator_y)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.Y_Numerator_y) + .getDisplayableValue(), + "5.000"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.Y_Numerator_Constant)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.Y_Numerator_Constant) + .getDisplayableValue(), + "6.000"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.Denominator_x)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.Denominator_x) + .getDisplayableValue(), + "7.000"); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.Denominator_y)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.Denominator_y) + .getDisplayableValue(), + "8.000"); + assertTrue(uut.getIdentifiers().contains(GeneralizedTransformationParametersKey.SDCC)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.SDCC).getDisplayableValue(), + "[SDCC]"); + assertTrue(uut.getField(GeneralizedTransformationParametersKey.SDCC) instanceof SDCC_FLP); + SDCC_FLP sdccFlp = (SDCC_FLP) uut.getField(GeneralizedTransformationParametersKey.SDCC); + assertEquals(sdccFlp.getSDCCMatrix()[0][0], 0.1, 0.0000001); + assertEquals(sdccFlp.getSDCCMatrix()[0][1], 0.01, 0.001); + assertEquals(sdccFlp.getSDCCMatrix()[1][0], 0.01, 0.001); + assertEquals(sdccFlp.getSDCCMatrix()[7][7], 0.8, 0.0000001); + assertTrue( + uut.getIdentifiers() + .contains(GeneralizedTransformationParametersKey.DocumentVersion)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.DocumentVersion) + .getDisplayableValue(), + "ST 1202.2"); + assertTrue( + uut.getIdentifiers() + .contains( + GeneralizedTransformationParametersKey.TransformationEnumeration)); + assertEquals( + uut.getField(GeneralizedTransformationParametersKey.TransformationEnumeration) + .getDisplayableValue(), + "Child-Parent Transformation (CPT)"); + assertEquals(uut.frameMessage(true), bytesAll); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void badSerialisationNonNested() { + Map map = + new HashMap<>(); + map.put( + GeneralizedTransformationParametersKey.DocumentVersion, + new ST1202DocumentVersion(2)); + GeneralizedTransformationLocalSet uut = new GeneralizedTransformationLocalSet(map); + uut.frameMessage(false); + } + + @Test + public void lookupUnknown() throws KlvParseException { + verifyNoLoggerMessages(); + IGeneralizedTransformationMetadataValue uut = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.Undefined, new byte[] {0x03}); + this.verifySingleLoggerMessage( + "Unknown Generalized Transformation Metadata tag: Undefined"); + assertNull(uut); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/GeneralizedTransformationParametersKeyTest.java b/st1202/src/test/java/org/jmisb/st1202/GeneralizedTransformationParametersKeyTest.java new file mode 100644 index 000000000..8a7a3a095 --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/GeneralizedTransformationParametersKeyTest.java @@ -0,0 +1,113 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +/** Unit tests for GeneralizedTransformationParametersKey. */ +public class GeneralizedTransformationParametersKeyTest { + + @Test + public void Enum0Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(0); + assertEquals(key, GeneralizedTransformationParametersKey.Undefined); + assertEquals(key.getIdentifier(), 0); + } + + @Test + public void EnumUnknownTest() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(999); + assertEquals(key, GeneralizedTransformationParametersKey.Undefined); + assertEquals(key.getIdentifier(), 0); + } + + @Test + public void Enum1Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(1); + assertEquals(key, GeneralizedTransformationParametersKey.X_Numerator_x); + assertEquals(key.getIdentifier(), 1); + } + + @Test + public void Enum2Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(2); + assertEquals(key, GeneralizedTransformationParametersKey.X_Numerator_y); + assertEquals(key.getIdentifier(), 2); + } + + @Test + public void Enum3Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(3); + assertEquals(key, GeneralizedTransformationParametersKey.X_Numerator_Constant); + assertEquals(key.getIdentifier(), 3); + } + + @Test + public void Enum4Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(4); + assertEquals(key, GeneralizedTransformationParametersKey.Y_Numerator_x); + assertEquals(key.getIdentifier(), 4); + } + + @Test + public void Enum5Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(5); + assertEquals(key, GeneralizedTransformationParametersKey.Y_Numerator_y); + assertEquals(key.getIdentifier(), 5); + } + + @Test + public void Enum6Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(6); + assertEquals(key, GeneralizedTransformationParametersKey.Y_Numerator_Constant); + assertEquals(key.getIdentifier(), 6); + } + + @Test + public void Enum7Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(7); + assertEquals(key, GeneralizedTransformationParametersKey.Denominator_x); + assertEquals(key.getIdentifier(), 7); + } + + @Test + public void Enum8Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(8); + assertEquals(key, GeneralizedTransformationParametersKey.Denominator_y); + assertEquals(key.getIdentifier(), 8); + } + + @Test + public void Enum9Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(9); + assertEquals(key, GeneralizedTransformationParametersKey.SDCC); + assertEquals(key.getIdentifier(), 9); + } + + @Test + public void Enum10Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(10); + assertEquals(key, GeneralizedTransformationParametersKey.DocumentVersion); + assertEquals(key.getIdentifier(), 10); + } + + @Test + public void Enum11Test() { + GeneralizedTransformationParametersKey key = + GeneralizedTransformationParametersKey.getKey(11); + assertEquals(key, GeneralizedTransformationParametersKey.TransformationEnumeration); + assertEquals(key.getIdentifier(), 11); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/LoggerChecks.java b/st1202/src/test/java/org/jmisb/st1202/LoggerChecks.java new file mode 100644 index 000000000..a68731645 --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/LoggerChecks.java @@ -0,0 +1,63 @@ +package org.jmisb.st1202; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import java.util.List; +import org.slf4j.helpers.MessageFormatter; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import uk.org.lidalia.slf4jext.Level; + +/** + * Superclass for logging checks in a test case. + * + *

The concept is that there is a test logger that should always contain no log messages after a + * test has been run. That gets verified in the AfterMethod so if there is a message, the logger + * needs to be checked and clear()ed before the test method returns. + * + *

Only ERROR, WARN and INFO levels are checked. DEBUG and lower are implementation detail. + * + *

The subclass is responsible for initialising the LOGGER correctly by calling the super + * constructor with the class that is creating the log messages. + */ +public abstract class LoggerChecks { + protected TestLogger LOGGER; + + public LoggerChecks(Class T) { + LOGGER = TestLoggerFactory.getTestLogger(T); + LOGGER.setEnabledLevelsForAllThreads(Level.ERROR, Level.WARN, Level.INFO); + } + + @BeforeMethod + public void clearLogger() { + LOGGER.clear(); + } + + @AfterMethod + public void checkLogger() { + verifyNoLoggerMessages(); + } + + protected void verifyNoLoggerMessages() { + if (LOGGER.getLoggingEvents().size() > 0) { + List events = LOGGER.getLoggingEvents(); + for (LoggingEvent event : events) { + System.out.println(event.getLevel().name() + ": " + event.getMessage().toString()); + } + } + Assert.assertEquals(LOGGER.getLoggingEvents().size(), 0); + } + + protected void verifySingleLoggerMessage(final String expectedMessage) { + Assert.assertEquals(LOGGER.getLoggingEvents().size(), 1); + LoggingEvent event = LOGGER.getLoggingEvents().get(0); + Assert.assertEquals(event.getArguments().size(), 1); + String message = + MessageFormatter.format(event.getMessage(), event.getArguments().get(0)) + .getMessage(); + Assert.assertEquals(message, expectedMessage); + LOGGER.clear(); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/SDCC_FLP_Test.java b/st1202/src/test/java/org/jmisb/st1202/SDCC_FLP_Test.java new file mode 100644 index 000000000..d0b6ad3e9 --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/SDCC_FLP_Test.java @@ -0,0 +1,232 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.jmisb.api.klv.IKlvKey; +import org.jmisb.st1010.EncodingFormat; +import org.jmisb.st1010.SDCC; +import org.jmisb.st1010.SDCCValueIdentifierKey; +import org.jmisb.st1010.SDCCValueWrap; +import org.testng.annotations.Test; + +/** Tests for ST 1202 SDCC Floating Length Pack. */ +public class SDCC_FLP_Test { + private final double[][] matrix = + new double[][] { + {0.10, 0.01, 0.02, 0.00, 0.00, 0.00, 0.00, 0.00}, + {0.01, 0.20, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00}, + {0.02, 0.00, 0.30, 0.00, 0.00, 0.00, 0.00, 0.00}, + {0.00, 0.00, 0.00, 0.40, 0.00, 0.00, 0.00, 0.00}, + {0.00, 0.00, 0.00, 0.00, 0.50, 0.00, 0.00, 0.00}, + {0.00, 0.00, 0.00, 0.00, 0.00, 0.60, 0.00, 0.00}, + {0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.70, 0.00}, + {0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.80}, + }; + + private final byte[] sdccBytes = + new byte[] { + 0x08, + 0b01000010, + (byte) 0x3d, + (byte) 0xcc, + (byte) 0xcc, + (byte) 0xcd, + (byte) 0x3e, + (byte) 0x4c, + (byte) 0xcc, + (byte) 0xcd, + (byte) 0x3e, + (byte) 0x99, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x3e, + (byte) 0xcc, + (byte) 0xcc, + (byte) 0xcd, + (byte) 0x3f, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x3f, + (byte) 0x19, + (byte) 0x99, + (byte) 0x9a, + (byte) 0x3f, + (byte) 0x33, + (byte) 0x33, + (byte) 0x33, + (byte) 0x3f, + (byte) 0x4c, + (byte) 0xcc, + (byte) 0xcd, + (byte) 0x40, + (byte) 0xa3, + (byte) 0x41, + (byte) 0x47, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + (byte) 0x40, + (byte) 0x00, + }; + + @Test + public void testConstructFromSdccValue() { + SDCC sdcc = new SDCC(); + sdcc.setStandardDeviationFormat(EncodingFormat.IEEE); + sdcc.setStandardDeviationLength(Float.BYTES); + sdcc.setCorrelationCoefficientFormat(EncodingFormat.ST1201); + sdcc.setCorrelationCoefficientLength(2); + sdcc.setValues(matrix); + SDCC_FLP uut = new SDCC_FLP(sdcc); + assertEquals(uut.getDisplayName(), "Standard Deviation and Correlation Coefficients"); + assertEquals(uut.getDisplayableValue(), "[SDCC]"); + assertEquals(uut.getBytes(), sdccBytes); + assertEquals(uut.getSDCC().getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(uut.getSDCC().getStandardDeviationLength(), 4); + assertEquals(uut.getSDCC().getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + assertEquals(uut.getSDCC().getCorrelationCoefficientLength(), 2); + assertEquals(uut.getSDCC().getValues(), matrix); + assertEquals(uut.getSDCCMatrix(), matrix); + assertEquals(uut.getIdentifiers().size(), 64); + } + + @Test + public void testConstructFromBytes() throws KlvParseException { + SDCC_FLP uut = new SDCC_FLP(sdccBytes); + assertEquals(uut.getDisplayName(), "Standard Deviation and Correlation Coefficients"); + assertEquals(uut.getDisplayableValue(), "[SDCC]"); + assertEquals(uut.getBytes(), sdccBytes); + assertEquals(uut.getSDCC().getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(uut.getSDCC().getStandardDeviationLength(), 4); + assertEquals(uut.getSDCC().getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + assertEquals(uut.getSDCC().getCorrelationCoefficientLength(), 2); + assertEquals(uut.getSDCC().getValues().length, matrix.length); + assertEquals(uut.getSDCC().getValues()[0].length, matrix[0].length); + double[][] values = uut.getSDCC().getValues(); + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[i].length; j++) { + if (i == j) { + assertEquals(values[i][j], matrix[i][j], 0.0000001); + } else { + assertEquals(values[i][j], matrix[i][j], 0.0001); + } + } + } + values = uut.getSDCCMatrix(); + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[i].length; j++) { + if (i == j) { + assertEquals(values[i][j], matrix[i][j], 0.0000001); + } else { + assertEquals(values[i][j], matrix[i][j], 0.0001); + } + } + } + assertEquals(uut.getIdentifiers().size(), 64); + } + + @Test + public void testFactoryEncodedBytes() throws KlvParseException { + IGeneralizedTransformationMetadataValue value = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.SDCC, sdccBytes); + assertTrue(value instanceof SDCC_FLP); + SDCC_FLP uut = (SDCC_FLP) value; + assertEquals(uut.getDisplayName(), "Standard Deviation and Correlation Coefficients"); + assertEquals(uut.getDisplayableValue(), "[SDCC]"); + assertEquals(uut.getBytes(), sdccBytes); + assertEquals(uut.getSDCC().getStandardDeviationFormat(), EncodingFormat.IEEE); + assertEquals(uut.getSDCC().getStandardDeviationLength(), 4); + assertEquals(uut.getSDCC().getCorrelationCoefficientFormat(), EncodingFormat.ST1201); + assertEquals(uut.getSDCC().getCorrelationCoefficientLength(), 2); + assertEquals(uut.getSDCC().getValues().length, matrix.length); + assertEquals(uut.getSDCC().getValues()[0].length, matrix[0].length); + assertEquals(uut.getSDCC().getValues().length, matrix.length); + assertEquals(uut.getSDCC().getValues()[0].length, matrix[0].length); + double[][] values = uut.getSDCC().getValues(); + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[i].length; j++) { + if (i == j) { + assertEquals(values[i][j], matrix[i][j], 0.0000001); + } else { + assertEquals(values[i][j], matrix[i][j], 0.0001); + } + } + } + values = uut.getSDCCMatrix(); + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[i].length; j++) { + if (i == j) { + assertEquals(values[i][j], matrix[i][j], 0.0000001); + } else { + assertEquals(values[i][j], matrix[i][j], 0.0001); + } + } + } + assertEquals(uut.getIdentifiers().size(), 64); + for (IKlvKey identifier : uut.getIdentifiers()) { + assertTrue(identifier instanceof SDCCValueIdentifierKey); + SDCCValueIdentifierKey id = (SDCCValueIdentifierKey) identifier; + int row = id.getRow(); + int column = id.getColumn(); + var fieldValue = uut.getField(id); + assertTrue(fieldValue instanceof SDCCValueWrap); + SDCCValueWrap wrappedValue = (SDCCValueWrap) fieldValue; + if (row == column) { + assertEquals(wrappedValue.getValue(), matrix[row][column], 0.0000001); + } else { + assertEquals(wrappedValue.getValue(), matrix[row][column], 0.0001); + } + } + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/ST1202DocumentVersionTest.java b/st1202/src/test/java/org/jmisb/st1202/ST1202DocumentVersionTest.java new file mode 100644 index 000000000..326284caa --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/ST1202DocumentVersionTest.java @@ -0,0 +1,74 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for ST 1202 Document Version. */ +public class ST1202DocumentVersionTest { + @Test + public void testConstructFromValue() { + ST1202DocumentVersion version = new ST1202DocumentVersion(1); + assertEquals(version.getBytes(), new byte[] {(byte) 0x01}); + assertEquals(version.getDisplayName(), "Document Version"); + assertEquals(version.getDisplayableValue(), "ST 1202.1"); + assertEquals(version.getVersion(), 1); + } + + @Test + public void testConstructFromValue127() { + ST1202DocumentVersion version = new ST1202DocumentVersion(127); + assertEquals(version.getBytes(), new byte[] {(byte) 0x7f}); + assertEquals(version.getDisplayName(), "Document Version"); + assertEquals(version.getDisplayableValue(), "ST 1202.127"); + assertEquals(version.getVersion(), 127); + } + + @Test + public void testConstructFromEncodedBytes() { + ST1202DocumentVersion version = new ST1202DocumentVersion(new byte[] {(byte) 0x02}); + assertEquals(version.getBytes(), new byte[] {(byte) 0x02}); + assertEquals(version.getDisplayName(), "Document Version"); + assertEquals(version.getDisplayableValue(), "ST 1202.2"); + assertEquals(version.getVersion(), 2); + } + + @Test + public void testConstructFromEncodedBytes0() { + ST1202DocumentVersion version = new ST1202DocumentVersion(new byte[] {(byte) 0x00}); + assertEquals(version.getBytes(), new byte[] {(byte) 0x00}); + assertEquals(version.getDisplayName(), "Document Version"); + assertEquals(version.getDisplayableValue(), "ST 1202"); + assertEquals(version.getVersion(), 0); + } + + @Test + public void testConstructFromEncodedBytes127() { + ST1202DocumentVersion version = new ST1202DocumentVersion(new byte[] {(byte) 0x7F}); + assertEquals(version.getBytes(), new byte[] {(byte) 0x7F}); + assertEquals(version.getDisplayName(), "Document Version"); + assertEquals(version.getDisplayableValue(), "ST 1202.127"); + assertEquals(version.getVersion(), 127); + } + + @Test + public void testFactoryEncodedBytes() throws KlvParseException { + IGeneralizedTransformationMetadataValue value = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.DocumentVersion, + new byte[] {(byte) 0x02}); + assertTrue(value instanceof ST1202DocumentVersion); + ST1202DocumentVersion version = (ST1202DocumentVersion) value; + assertEquals(version.getBytes(), new byte[] {(byte) 0x02}); + assertEquals(version.getDisplayName(), "Document Version"); + assertEquals(version.getDisplayableValue(), "ST 1202.2"); + assertEquals(version.getVersion(), 2); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testTooSmall() { + new ST1202DocumentVersion(-1); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/TransformationEnumerationTest.java b/st1202/src/test/java/org/jmisb/st1202/TransformationEnumerationTest.java new file mode 100644 index 000000000..d6714ab20 --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/TransformationEnumerationTest.java @@ -0,0 +1,106 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for ST 1202 Transformation Enumeration. */ +public class TransformationEnumerationTest { + @Test + public void testConstructFromValue() { + TransformationEnumeration uut = TransformationEnumeration.CHIPPING; + assertEquals(uut.getBytes(), new byte[] {(byte) 0x01}); + assertEquals(uut.getDisplayName(), "Transformation Enumeration"); + assertEquals(uut.getDisplayableValue(), "Chipping Transformation (CT)"); + assertEquals(uut.getEncodedValue(), 1); + assertEquals(uut.getDescription(), "Chipping Transformation (CT)"); + assertEquals(uut.getUnits(), "px"); + } + + @Test + public void testConstructFromEncodedBytes() throws KlvParseException { + TransformationEnumeration uut = + TransformationEnumeration.fromBytes(new byte[] {(byte) 0x02}); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x02}); + assertEquals(uut.getDisplayName(), "Transformation Enumeration"); + assertEquals(uut.getDisplayableValue(), "Child-Parent Transformation (CPT)"); + assertEquals(uut.getEncodedValue(), 2); + assertEquals(uut.getDescription(), "Child-Parent Transformation (CPT)"); + assertEquals(uut.getUnits(), "mm"); + } + + @Test + public void testConstructFromEncodedBytes4() throws KlvParseException { + TransformationEnumeration uut = + TransformationEnumeration.fromBytes(new byte[] {(byte) 0x04}); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x04}); + assertEquals(uut.getDisplayName(), "Transformation Enumeration"); + assertEquals(uut.getDisplayableValue(), "Optical Transformation (OT)"); + assertEquals(uut.getEncodedValue(), 4); + assertEquals(uut.getDescription(), "Optical Transformation (OT)"); + assertEquals(uut.getUnits(), "mm"); + } + + @Test + public void testFactoryEncodedBytes() throws KlvParseException { + IGeneralizedTransformationMetadataValue value = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.TransformationEnumeration, + new byte[] {(byte) 0x02}); + assertTrue(value instanceof TransformationEnumeration); + TransformationEnumeration uut = (TransformationEnumeration) value; + assertEquals(uut.getDisplayName(), "Transformation Enumeration"); + assertEquals(uut.getDisplayableValue(), "Child-Parent Transformation (CPT)"); + assertEquals(uut.getEncodedValue(), 2); + assertEquals(uut.getDescription(), "Child-Parent Transformation (CPT)"); + assertEquals(uut.getUnits(), "mm"); + } + + @Test( + expectedExceptions = KlvParseException.class, + expectedExceptionsMessageRegExp = + "Transformation Enumeration should be encoded as a 1 byte array") + public void testConstructFromEncodedBytesTooLong() throws KlvParseException { + TransformationEnumeration.fromBytes(new byte[] {(byte) 0x01, (byte) 0x02}); + } + + @Test( + expectedExceptions = KlvParseException.class, + expectedExceptionsMessageRegExp = + "Transformation Enumeration should be encoded as a 1 byte array") + public void testConstructFromEncodedBytesTooShort() throws KlvParseException { + TransformationEnumeration.fromBytes(new byte[] {}); + } + + @Test + public void testLookup() throws KlvParseException { + TransformationEnumeration uut = TransformationEnumeration.lookup(4); + assertEquals(uut, TransformationEnumeration.OPTICAL); + assertEquals(uut.getBytes(), new byte[] {(byte) 0x04}); + assertEquals(uut.getDisplayName(), "Transformation Enumeration"); + assertEquals(uut.getDisplayableValue(), "Optical Transformation (OT)"); + assertEquals(uut.getEncodedValue(), 4); + assertEquals(uut.getDescription(), "Optical Transformation (OT)"); + assertEquals(uut.getUnits(), "mm"); + } + + @Test + public void testLookupUnknown() throws KlvParseException { + TransformationEnumeration uut = TransformationEnumeration.lookup(5); + assertEquals(uut, TransformationEnumeration.UNKNOWN); + } + + @Test() + public void testSerialiseOther() throws KlvParseException { + assertEquals(TransformationEnumeration.OTHER.getBytes(), new byte[] {(byte) 0x00}); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Cannot serialise UNKNOWN Transformation Enumeration") + public void testSerialiseUnknown() throws KlvParseException { + TransformationEnumeration.UNKNOWN.getBytes(); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/X_Numerator_Constant_Test.java b/st1202/src/test/java/org/jmisb/st1202/X_Numerator_Constant_Test.java new file mode 100644 index 000000000..963eb5599 --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/X_Numerator_Constant_Test.java @@ -0,0 +1,63 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for X_Numerator_Constant. */ +public class X_Numerator_Constant_Test { + + @Test + public void fromValue() { + X_Numerator_Constant uut = new X_Numerator_Constant(262.5f); + assertEquals(uut.getValue(), 262.5f); + assertEquals(uut.getDisplayName(), "x Equation Numerator - Constant factor"); + assertEquals(uut.getDisplayableValue(), "262.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x43, (byte) 0x83, (byte) 0x40, (byte) 0x00}); + } + + @Test + public void fromBytes() throws KlvParseException { + X_Numerator_Constant uut = + new X_Numerator_Constant( + new byte[] {(byte) 0x43, (byte) 0x83, (byte) 0x40, (byte) 0x00}); + assertEquals(uut.getValue(), 262.5f); + assertEquals(uut.getDisplayName(), "x Equation Numerator - Constant factor"); + assertEquals(uut.getDisplayableValue(), "262.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x43, (byte) 0x83, (byte) 0x40, (byte) 0x00}); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IGeneralizedTransformationMetadataValue v = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.X_Numerator_Constant, + new byte[] {(byte) 0x43, (byte) 0x83, (byte) 0x40, (byte) 0x00}); + assertTrue(v instanceof X_Numerator_Constant); + X_Numerator_Constant uut = (X_Numerator_Constant) v; + assertEquals(uut.getValue(), 262.5f); + assertEquals(uut.getDisplayName(), "x Equation Numerator - Constant factor"); + assertEquals(uut.getDisplayableValue(), "262.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x43, (byte) 0x83, (byte) 0x40, (byte) 0x00}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooShort() throws KlvParseException { + new X_Numerator_Constant(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooLong() throws KlvParseException { + new X_Numerator_Constant(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testNotDouble() throws KlvParseException { + new X_Numerator_Constant(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/X_Numerator_X_Test.java b/st1202/src/test/java/org/jmisb/st1202/X_Numerator_X_Test.java new file mode 100644 index 000000000..c9a5003b0 --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/X_Numerator_X_Test.java @@ -0,0 +1,62 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for X_Numerator_X. */ +public class X_Numerator_X_Test { + + @Test + public void fromValue() { + X_Numerator_X uut = new X_Numerator_X(0.5f); + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "x Equation Numerator - x factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytes() throws KlvParseException { + X_Numerator_X uut = + new X_Numerator_X(new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "x Equation Numerator - x factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IGeneralizedTransformationMetadataValue v = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.X_Numerator_x, + new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertTrue(v instanceof X_Numerator_X); + X_Numerator_X uut = (X_Numerator_X) v; + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "x Equation Numerator - x factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooShort() throws KlvParseException { + new X_Numerator_X(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooLong() throws KlvParseException { + new X_Numerator_X(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testNotDouble() throws KlvParseException { + new X_Numerator_X(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/X_Numerator_Y_Test.java b/st1202/src/test/java/org/jmisb/st1202/X_Numerator_Y_Test.java new file mode 100644 index 000000000..feed67621 --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/X_Numerator_Y_Test.java @@ -0,0 +1,62 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for X_Numerator_Y. */ +public class X_Numerator_Y_Test { + + @Test + public void fromValue() { + X_Numerator_Y uut = new X_Numerator_Y(0.5f); + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "x Equation Numerator - y factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytes() throws KlvParseException { + X_Numerator_Y uut = + new X_Numerator_Y(new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "x Equation Numerator - y factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IGeneralizedTransformationMetadataValue v = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.X_Numerator_y, + new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertTrue(v instanceof X_Numerator_Y); + X_Numerator_Y uut = (X_Numerator_Y) v; + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "x Equation Numerator - y factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooShort() throws KlvParseException { + new X_Numerator_Y(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooLong() throws KlvParseException { + new X_Numerator_Y(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testNotDouble() throws KlvParseException { + new X_Numerator_Y(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/Y_Numerator_Constant_Test.java b/st1202/src/test/java/org/jmisb/st1202/Y_Numerator_Constant_Test.java new file mode 100644 index 000000000..a5623206b --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/Y_Numerator_Constant_Test.java @@ -0,0 +1,63 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for Y_Numerator_Constant. */ +public class Y_Numerator_Constant_Test { + + @Test + public void fromValue() { + Y_Numerator_Constant uut = new Y_Numerator_Constant(556.25f); + assertEquals(uut.getValue(), 556.25f); + assertEquals(uut.getDisplayName(), "y Equation Numerator - Constant factor"); + assertEquals(uut.getDisplayableValue(), "556.250"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x44, (byte) 0x0B, (byte) 0x10, (byte) 0x00}); + } + + @Test + public void fromBytes() throws KlvParseException { + Y_Numerator_Constant uut = + new Y_Numerator_Constant( + new byte[] {(byte) 0x44, (byte) 0x0B, (byte) 0x10, (byte) 0x00}); + assertEquals(uut.getValue(), 556.25f); + assertEquals(uut.getDisplayName(), "y Equation Numerator - Constant factor"); + assertEquals(uut.getDisplayableValue(), "556.250"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x44, (byte) 0x0B, (byte) 0x10, (byte) 0x00}); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IGeneralizedTransformationMetadataValue v = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.Y_Numerator_Constant, + new byte[] {(byte) 0x44, (byte) 0x0B, (byte) 0x10, (byte) 0x00}); + assertTrue(v instanceof Y_Numerator_Constant); + Y_Numerator_Constant uut = (Y_Numerator_Constant) v; + assertEquals(uut.getValue(), 556.25f); + assertEquals(uut.getDisplayName(), "y Equation Numerator - Constant factor"); + assertEquals(uut.getDisplayableValue(), "556.250"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x44, (byte) 0x0B, (byte) 0x10, (byte) 0x00}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooShort() throws KlvParseException { + new Y_Numerator_Constant(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooLong() throws KlvParseException { + new Y_Numerator_Constant(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testNotDouble() throws KlvParseException { + new Y_Numerator_Constant(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/Y_Numerator_X_Test.java b/st1202/src/test/java/org/jmisb/st1202/Y_Numerator_X_Test.java new file mode 100644 index 000000000..55387f0e9 --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/Y_Numerator_X_Test.java @@ -0,0 +1,62 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for Y_Numerator_X. */ +public class Y_Numerator_X_Test { + + @Test + public void fromValue() { + Y_Numerator_X uut = new Y_Numerator_X(0.5f); + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "y Equation Numerator - x factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytes() throws KlvParseException { + Y_Numerator_X uut = + new Y_Numerator_X(new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "y Equation Numerator - x factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IGeneralizedTransformationMetadataValue v = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.Y_Numerator_x, + new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertTrue(v instanceof Y_Numerator_X); + Y_Numerator_X uut = (Y_Numerator_X) v; + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "y Equation Numerator - x factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooShort() throws KlvParseException { + new Y_Numerator_X(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooLong() throws KlvParseException { + new Y_Numerator_X(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testNotDouble() throws KlvParseException { + new Y_Numerator_X(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); + } +} diff --git a/st1202/src/test/java/org/jmisb/st1202/Y_Numerator_Y_Test.java b/st1202/src/test/java/org/jmisb/st1202/Y_Numerator_Y_Test.java new file mode 100644 index 000000000..14e04deee --- /dev/null +++ b/st1202/src/test/java/org/jmisb/st1202/Y_Numerator_Y_Test.java @@ -0,0 +1,62 @@ +package org.jmisb.st1202; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.jmisb.api.common.KlvParseException; +import org.testng.annotations.Test; + +/** Tests for Y_Numerator_Y. */ +public class Y_Numerator_Y_Test { + + @Test + public void fromValue() { + Y_Numerator_Y uut = new Y_Numerator_Y(0.5f); + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "y Equation Numerator - y factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytes() throws KlvParseException { + Y_Numerator_Y uut = + new Y_Numerator_Y(new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "y Equation Numerator - y factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test + public void fromBytesFactory() throws KlvParseException { + IGeneralizedTransformationMetadataValue v = + GeneralizedTransformationLocalSet.createValue( + GeneralizedTransformationParametersKey.Y_Numerator_y, + new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + assertTrue(v instanceof Y_Numerator_Y); + Y_Numerator_Y uut = (Y_Numerator_Y) v; + assertEquals(uut.getValue(), 0.5f); + assertEquals(uut.getDisplayName(), "y Equation Numerator - y factor"); + assertEquals(uut.getDisplayableValue(), "0.500"); + assertEquals( + uut.getBytes(), new byte[] {(byte) 0x3f, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooShort() throws KlvParseException { + new Y_Numerator_Y(new byte[] {0x01, 0x02, 0x03}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testTooLong() throws KlvParseException { + new Y_Numerator_Y(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}); + } + + @Test(expectedExceptions = KlvParseException.class) + public void testNotDouble() throws KlvParseException { + new Y_Numerator_Y(new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); + } +} diff --git a/st1202/testng-unit.xml b/st1202/testng-unit.xml new file mode 100644 index 000000000..43b67fd60 --- /dev/null +++ b/st1202/testng-unit.xml @@ -0,0 +1,10 @@ + + + + + + + + + +