Skip to content

Commit

Permalink
Initial work on extracting time support to st0603 module (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
bradh authored Apr 30, 2020
1 parent 97b245d commit b608f88
Show file tree
Hide file tree
Showing 13 changed files with 485 additions and 123 deletions.
66 changes: 6 additions & 60 deletions api/src/main/java/org/jmisb/api/klv/st0601/PrecisionTimeStamp.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package org.jmisb.api.klv.st0601;

import org.jmisb.core.klv.PrimitiveConverter;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import org.jmisb.api.klv.st0603.ST0603TimeStamp;

/**
* Precision Time Stamp (ST 0601 tag 2)
Expand All @@ -18,23 +14,15 @@
* Resolution: 1 microsecond.
* </blockquote>
*/
public class PrecisionTimeStamp implements IUasDatalinkValue
public class PrecisionTimeStamp extends ST0603TimeStamp implements IUasDatalinkValue
{
// Yes, using a long here means overflow in the year 2262, but there's no unsigned long in java
// and the cost of using something like BigInteger seems unnecessary
private long microseconds;

/**
* Create from value
* @param microseconds Microseconds since the epoch
*/
public PrecisionTimeStamp(long microseconds)
{
if (microseconds < 0)
{
throw new IllegalArgumentException("Precision Timestamp must be in range [0,2^64-1]");
}
this.microseconds = microseconds;
super(microseconds);
}

/**
Expand All @@ -43,58 +31,16 @@ public PrecisionTimeStamp(long microseconds)
*/
public PrecisionTimeStamp(byte[] bytes)
{
if (bytes.length != 8)
{
throw new IllegalArgumentException("Precision Time Stamp encoding is an 8-byte unsigned int");
}
microseconds = PrimitiveConverter.toInt64(bytes);
super(bytes);
}

/**
* Create from {@code LocalDateTime}
* @param localDateTime The UTC date and time
* @param localDateTime The date and time
*/
public PrecisionTimeStamp(LocalDateTime localDateTime)
{
try
{
microseconds = ChronoUnit.MICROS.between(Instant.EPOCH, localDateTime.toInstant(ZoneOffset.UTC));
}
catch (ArithmeticException e)
{
throw new IllegalArgumentException("Precision Timestamp must be before April 11, 2262 23:47:16.854Z");
}
}

/**
* Get the value
* @return Number of microseconds since the epoch
*/
public long getMicroseconds()
{
return microseconds;
}

@Override
public byte[] getBytes()
{
return PrimitiveConverter.int64ToBytes(microseconds);
}

/**
* Get the value as a {@code LocalDateTime}
* @return The UTC date and time
*/
LocalDateTime getLocalDateTime()
{
return LocalDateTime.ofEpochSecond(microseconds / 1000000,
(int)(microseconds % 1000000) * 1000, ZoneOffset.UTC);
}

@Override
public String getDisplayableValue()
{
return "" + microseconds;
super(localDateTime);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package org.jmisb.api.klv.st0903.shared;
package org.jmisb.api.klv.st0603;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import org.jmisb.api.klv.st0903.IVmtiMetadataValue;
import org.jmisb.core.klv.PrimitiveConverter;

/**
* ST0603 Timestamp.
*/
public abstract class ST0603TimeStamp implements IVmtiMetadataValue
public class ST0603TimeStamp
{
// this is unsigned, so methods.
// this is conceptually unsigned, so be careful when manipulating it.
protected long microseconds;

/**
Expand All @@ -24,7 +23,7 @@ public ST0603TimeStamp(long microseconds)
{
if (microseconds < 0)
{
throw new IllegalArgumentException(this.getDisplayName() + " must be in range [0,2^64-1]");
throw new IllegalArgumentException("Timestamp must be in range [0,2^64-1]");
}
this.microseconds = microseconds;
}
Expand All @@ -37,24 +36,25 @@ public ST0603TimeStamp(byte[] bytes)
{
if (bytes.length != 8)
{
throw new IllegalArgumentException(this.getDisplayName() + " encoding is an 8-byte unsigned int");
throw new IllegalArgumentException("Timestamp encoding is an 8-byte unsigned int");
}
microseconds = PrimitiveConverter.toInt64(bytes);
}

/**
* Create from {@code ZonedDateTime}
* @param dateTime The UTC date and time
* Create from {@code DateTime}
* @param dateTime The date and time
*/
public ST0603TimeStamp(ZonedDateTime dateTime)
public ST0603TimeStamp(LocalDateTime dateTime)
{
try
{
microseconds = ChronoUnit.MICROS.between(Instant.EPOCH, dateTime.toInstant());
// TODO: Not really UTC...
microseconds = ChronoUnit.MICROS.between(Instant.EPOCH, dateTime.toInstant(ZoneOffset.UTC));
}
catch (ArithmeticException e)
{
throw new IllegalArgumentException(this.getDisplayName() + " must be before April 11, 2262 23:47:16.854Z");
throw new IllegalArgumentException("Timestamp must be before April 11, 2262 23:47:16.854Z");
}
}

Expand All @@ -68,23 +68,21 @@ public long getMicroseconds()
return microseconds;
}

@Override
public byte[] getBytes()
{
return PrimitiveConverter.int64ToBytes(microseconds);
}

/**
* Get the value as a {@code ZonedDateTime}
* @return The UTC date and time
* Get the value as a {@code LocalDateTime}
* @return The date and time
*/
public ZonedDateTime getDateTime()
public LocalDateTime getDateTime()
{
Instant instant = Instant.ofEpochSecond(microseconds / 1000000, (int) (microseconds % 1000000) * 1000);
return ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"));
// TODO: not really UTC
return LocalDateTime.ofEpochSecond(microseconds / 1000000, (int)(microseconds % 1000000) * 1000, ZoneOffset.UTC);
}

@Override
public String getDisplayableValue()
{
return "" + microseconds;
Expand Down
183 changes: 183 additions & 0 deletions api/src/main/java/org/jmisb/api/klv/st0603/TimeStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package org.jmisb.api.klv.st0603;

/**
* ST0603 Time Status.
* <p>
* From ST0603.5 section 7.4:
* </p>
* <blockquote>
* <p>
* Systems producing a timestamp representing Absolute Time for Motion Imagery
* and metadata will have differing requirements for the accuracy and precision
* of the timestamp. System designers need to specify both the precision and the
* accuracy of the timestamp, so that users of the data understand what can be
* expected in data analysis.
* </p>
* <p>
* The purpose of the Time Status is twofold:
* </p>
* <p>
* 1. Provide information on the reference clock used to produce a timestamp,
* and
* </p>
* <p>
* 2. Provide a bit-efficient timestamp qualifier for use within Motion Imagery.
* </p>
* <p>
* There are cases where a suitable reference for a timestamp is not available,
* or the synchronization to a reference may be temporarily lost. The Time
* Status provides end-user information regarding the reference for the
* timestamp in these instances.
* </p>
* </blockquote>
* <p>
* The Time Status is a set of three flags. The reverse flag depends on the
* discontinuity flag, since it describes the direction of the discontinuity
* (jump).
*/
public class TimeStatus
{
private boolean lockUnknown;
private boolean discontinuity;
private boolean reverse;

private static final byte LOCK_UNKNOWN_BITMASK = (byte)(0b1 << 7);
private static final byte DISCONTINUITY_BITMASK = (byte)(0b1 << 6);
private static final byte REVERSE_BITMASK = (byte)(0b1 << 5);

/**
* Constructor.
*
* This will create a "non-locked, forward linear" status.
*/
public TimeStatus()
{
lockUnknown = true;
discontinuity = false;
reverse = false;
}

/**
* Create from encoded byte.
*
* @param encoded Encoded byte
*/
public TimeStatus(byte encoded)
{
lockUnknown = ((encoded & LOCK_UNKNOWN_BITMASK) == LOCK_UNKNOWN_BITMASK);
discontinuity = ((encoded & DISCONTINUITY_BITMASK) == DISCONTINUITY_BITMASK);
if (discontinuity)
{
reverse = ((encoded & REVERSE_BITMASK) == REVERSE_BITMASK);
}
else
{
reverse = false;
}
}

/**
* Whether the time is locked or not.
* <p>
* Locked means that the internal clock is locked to absolute time reference.
* Not locked also covers the "lock status is unknown" case.
* </p>
* @return true if the time is locked, false if the time is not locked.
*/
public boolean isLocked()
{
return !lockUnknown;
}

/**
* Set whether the time is locked or not.
* <p>
* Locked means that the internal clock is locked to absolute time reference.
* Not locked also covers the "lock status is unknown" case.
* </p>
* @param locked true if the time is locked, false if the time is not locked.
*/
public void setLocked(boolean locked)
{
this.lockUnknown = !locked;
}

/**
* Whether the time is discontinuous.
* <p>
* A discontinuity means that time has not incremented forward in a linear
* fashion. That is, a forward non-linear or reverse jump as a by-product of
* relocking the clock to a reference, or other correction.
* </p>
* @return true if there is a discontinuity, or false if time is
* incrementing in a normal linear fashion.
*/
public boolean isDiscontinuity()
{
return discontinuity;
}

/**
* Set whether the time is discontinuous.
* <p>
* A discontinuity means that time has not incremented forward in a linear
* fashion. That is, a forward non-linear or reverse jump as a by-product of
* relocking the clock to a reference, or other correction.
* </p>
*
* @param discontinuity true if there is a discontinuity, or false if time
* is incrementing in a normal linear fashion.
* @param reverse true if the discontinuity is a reverse jump, or false if
* the discontinuity is a forward jump.
*/
public void setDiscontinuity(final boolean discontinuity, final boolean reverse)
{
this.discontinuity = discontinuity;
if (discontinuity)
{
this.reverse = reverse;
}
else
{
this.reverse = false;
}
}

/**
* Whether the discontinuity is in the reverse direction.
* <p>
* Only meaningful if there is a discontinuity (see isDiscontinuity()).
* </p>
*
* @return true if the discontinuity is reverse direction (step back),
* otherwise false.
*/
public boolean isReverse()
{
return reverse;
}

/**
* Get the encoded value for this TimeStatus.
* <p>
* The encoded value is a set of bit flags in a byte. See ST0603.5 Table 3.
* @return the encoded value, as a single byte.
*/
byte getEncodedValue()
{
byte value = 0b00011111;
if (discontinuity)
{
value |= DISCONTINUITY_BITMASK;
if (reverse)
{
value |= REVERSE_BITMASK;
}
}
if (lockUnknown)
{
value |= LOCK_UNKNOWN_BITMASK;
}
return value;
}
}
Loading

0 comments on commit b608f88

Please sign in to comment.