![JAR search and dependency download from the Maven repository](/logo.png)
jais.AISSentence Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jais Show documentation
Show all versions of jais Show documentation
Java NMEA AIS decoding library
/*
* Copyright 2016-2019 Jonathan Machen {@literal }.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jais;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidParameterException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jais.messages.AISMessageDecoder;
import jais.messages.enums.Manufacturer;
import jais.messages.enums.SentenceType;
import jais.messages.enums.Talker;
import lombok.Getter;
import lombok.Setter;
/**
*
* @author Jonathan Machen {@literal }
*/
@Getter
@Setter
public final class AISSentence implements Sentence {
// reserved characters
public final static char ENCAP_START = '!';
public final static char PARAM_START = '$';
public final static char CHECKSUM_DELIMITER = '*';
public final static char FIELD_DELIMITER = ',';
public final static char HEX_DELIMITER = '^';
public final static char RESERVED_DELIMITER = '~';
private final static String DEFAULT_SOURCE = "UNSPECIFIED";
private final static double CHANNEL_A_FREQUENCY_IN_MHZ = 161.975;
private final static double CHANNEL_B_FREQUENCY_IN_MHZ = 162.025;
public final static String PREAMBLE = "([!|$])([A-Z0-9]{1,2})(([A-Z]{2})([A-Z]))";
public final static Pattern PREAMBLE_PATTERN = Pattern.compile(PREAMBLE);
public final static Pattern SENTENCE_PATTERN = Pattern
.compile("(" + TagBlock.TAGBLOCK_STRING + ")?(" + PREAMBLE + "(.*))");
public final static int PREAMBLE_GROUPS = 5;
public final static Charset DEFAULT_CHARSET = StandardCharsets.US_ASCII;
private TagBlock tagBlock;
private Preamble preamble;
private byte[] source;
private byte[] type;
private int fragmentCount = 1;
private int fragmentNumber = 1;
private int sequentialMessageId = -1;
private char radioChannelCode;
private final byte[] unparsedSentence;
private byte[] payload;
private byte[] sentenceWithoutTagBlock;
private int fillBits;
private byte[] checksum;
private final long timeReceived = ZonedDateTime.now(ZoneOffset.UTC.normalized()).toInstant().toEpochMilli();
private byte[][] sentenceParts;
private boolean parsed = false;
private final SentenceType sentenceType = SentenceType.NMEA_AIS;
/**
* Constructor
*
* @param unparsedSentence byte[] composed of the characters from the original
* non-decoded String representing a complete or partial AIS
* message
*/
public AISSentence(byte[] unparsedSentence) {
this(unparsedSentence, ByteArrayUtils.str2bArray(DEFAULT_SOURCE));
}
/**
* Constructor
*
* @param unparsedSentence byte[] composed of the characters from the original
* non-decoded String representing a complete or partial AIS
* message
* @param source byte[] for the named source of the AIS sentence
*/
public AISSentence(byte[] unparsedSentence, byte[] source) {
this.unparsedSentence = ByteArrayUtils.trimByteArray(unparsedSentence);
this.source = ByteArrayUtils.trimByteArray(source);
}
/**
* Constructor
*
* @param rawSentence String representing the original 6 bit encoded String
* representing a complete or
* partial AIS message
*/
public AISSentence(String rawSentence) {
this(rawSentence, DEFAULT_SOURCE);
}
/**
* Constructor
*
* @param unparsedSentence String representing the original 6 bit encoded String
* representing a complete or
* partial AIS message
* @param source String representing the named source of this AIS sentence
*/
public AISSentence(String unparsedSentence, String source) {
this.unparsedSentence = ByteArrayUtils.str2bArray(unparsedSentence);
this.source = ByteArrayUtils.str2bArray(Objects.requireNonNullElse(source, DEFAULT_SOURCE));
}
/**
*
*/
@Override
public void parse() {
if (this.sentenceParts[0] != null) this.preamble = new Preamble(this.sentenceParts[0]);
this.validatePreamble();
this.process();
}
/**
* Validates the AIS sentence preamble against a regular expression constant
*
* @return boolean indicating whether or not the preamble is valid
*/
private boolean validatePreamble() {
if (this.sentenceParts == null) {
return false;
} else if (this.sentenceParts.length == 0) {
return false;
} else if (this.preamble == null && this.sentenceParts[0] == null) {
return false;
} else {
return validatePreamble(Preamble.parse(this.sentenceParts[0]));
}
}
/**
* Validates that the provided Preamble object is non-null and does not contain
* null fields
*
* @param p Preamble object to evaluate for validity
* @return boolean indicating whether or not the preamble is valid
*/
private static boolean validatePreamble(Preamble p) {
return ((p != null) && (p.talker != null) && (p.format != null));
}
/**
* Validates the AIS sentence preamble against a regular expression constant
*
* @param preambleStr String preamble to evaluate for validity
* @return boolean indicating whether or not the preamble is valid
*/
public static boolean validatePreamble(String preambleStr) {
return validatePreamble(Preamble.parse(preambleStr));
}
/**
* Determines whether or not a TagBlock was parsed from this AISsentence
*
* @see jais.TagBlock
*
* @return boolean representing whether or not this sentence has a TagBlock
*/
public boolean hasTagBlock() {
return (this.tagBlock != null);
}
/**
* Validates the contents of the sentence and breaks it into its constituent
* parts
*
* @return an AISSentence that is the product of the processing
*/
public AISSentence process() {
return process(false);
}
/**
* Validates the contents of the sentence and breaks it into its constituent
* parts, optionally generates a TagBlock
* for the resulting AISsentence @see jais.TagBlock
*
* @param addTagBlock boolean flag indicating whether or not a TagBlock should
* be pre-pended to the sentence
* @see jais.TagBlock
* @return a reference to the current AISsentence object
* malformed
*/
public AISSentence process(boolean addTagBlock) {
String unparsedSentence;
if (this.unparsedSentence == null) {
return this;
} else if (this.unparsedSentence.length == 0) {
return this;
} else {
unparsedSentence = ByteArrayUtils.bArray2Str(ByteArrayUtils.trimByteArray(this.unparsedSentence));
}
Matcher m = TagBlock.TAGBLOCK_PATTERN.matcher(unparsedSentence);
if (m.find()) {
if (this.source == null || this.source.length == 0) {
this.tagBlock = TagBlock.parse(m.group(0));
this.source = (this.tagBlock.getSource() != null) ?
this.tagBlock.getSource() : AISSentence.DEFAULT_SOURCE.getBytes();
} else
this.tagBlock = TagBlock.parse(m.group(0));
unparsedSentence = unparsedSentence.substring(m.end());
this.sentenceWithoutTagBlock = ByteArrayUtils.str2bArray(unparsedSentence);
} else if (addTagBlock) {
if (this.source != null && this.source.length != 0)
this.tagBlock = TagBlock.build(this.source);
this.sentenceWithoutTagBlock = this.unparsedSentence;
} else {
// no TagBlock found and addTagBlock is false
this.sentenceWithoutTagBlock = this.unparsedSentence;
}
if (this.sentenceParts == null || this.sentenceParts.length < 7)
this.sentenceParts = ByteArrayUtils.fastSplit(this.sentenceWithoutTagBlock, FIELD_DELIMITER);
switch (this.sentenceParts.length) {
case 10:
case 9:
case 8:
case 7:
if (this.sentenceParts[6] != null && this.sentenceParts[6].length > 0) {
byte[] firstByte = { this.sentenceParts[6][0] };
this.fillBits = ByteArrayUtils.getInt(firstByte, 0);
int csIndex = ByteArrayUtils.indexOf(this.sentenceParts[6], CHECKSUM_DELIMITER);
if (csIndex != -1) {
byte[] checksumBytes = Arrays.copyOfRange(
this.sentenceParts[6], csIndex + 1, this.sentenceParts[6].length);
if (checksumBytes.length > 0) {
this.checksum = ByteArrayUtils.trimByteArray(checksumBytes);
}
}
}
case 6:
this.payload = this.sentenceParts[5]; // the 6-bit encoded string
case 5:
if (this.sentenceParts[4] != null && this.sentenceParts[4].length > 0)
this.radioChannelCode = ByteArrayUtils.bArray2cArray(this.sentenceParts[4])[0];
case 4:
if (this.sentenceParts[3] != null && this.sentenceParts[3].length > 0)
this.sequentialMessageId = Integer.parseInt(ByteArrayUtils.bArray2Str(this.sentenceParts[3]));
case 3:
if(this.sentenceParts[2] != null && this.sentenceParts[2].length > 0)
this.fragmentNumber = Integer.parseInt(ByteArrayUtils.bArray2Str(this.sentenceParts[2]));
case 2:
if(this.sentenceParts[1] != null && this.sentenceParts[1].length > 0)
this.fragmentCount = Integer.parseInt(ByteArrayUtils.bArray2Str(this.sentenceParts[1]));
case 1:
this.preamble = new Preamble(this.sentenceParts[0]);
break;
default:
}
this.parsed = true;
return this;
}
/**
* Checks the validity of the current AIS sentence by analyzing the length of
* its
* String representation, the number of comma separated fields it
* contains, whether or not it has a valid preamble, whether or not it contains
* any invalid characters, and whether or not it has a valid
* checksum
*
* @return a boolean value representing the validity of this AISsentence
*/
public boolean isValid() {
try {
// so we don't throw NPEs over the failure to split the raw String
if (this.sentenceParts == null)
process();
if (this.sentenceWithoutTagBlock.length > 82)
return false; // invalid sentence length
if (this.sentenceParts.length == 0)
return false; // split failed
if (this.sentenceParts.length != 7)
return false; // invalid number of csv fields
if (!validatePreamble())
return false; // invalid preamble
// check for bad characters in binary string
for (char c : ByteArrayUtils.bArray2cArray(this.sentenceParts[5])) {
// is this character within an accepted range?
if (!((c <= AISMessageDecoder.CHAR_RANGE_A_MAX && c >= AISMessageDecoder.CHAR_RANGE_A_MIN)
|| (c <= AISMessageDecoder.CHAR_RANGE_B_MAX && c >= AISMessageDecoder.CHAR_RANGE_B_MIN))) {
// sentence contains an invalid character
return false;
}
}
// if we don't have any bad characters validate the checksum
int csIndex = ByteArrayUtils.indexOf(this.sentenceWithoutTagBlock, CHECKSUM_DELIMITER) + 1;
if (csIndex > 0) {
// validate checksum
if (!validateChecksum(this.sentenceWithoutTagBlock, this.checksum)) {
return false;
}
} else {
return false;
}
} catch (Exception e) {
return false;
}
return true;
}
/**
* Generates a valid checksum based on the provided char []
*
* @param source the source char [] for which you wish to generate a checksum
* @return a generated int checksum for the provided char []
*/
private static int generateChecksum(char[] source) {
int crc = 0;
for (char aSource : source)
crc ^= aSource;
return crc;
}
/**
* Generates a valid checksum based on the provided String
*
* @param sourceString the source String fro which you wish to generate a
* checksum
* @return a generated int checksum for the provided String
*/
public static String generateChecksumString(String sourceString) {
String hexString = Integer.toHexString(generateChecksum(sourceString.toCharArray()));
hexString = (hexString.length() == 1) ? "0" + hexString : hexString;
return hexString;
}
/**
* Attempts to parse a checksum from the provided String and generates a new one
* if the parsing operation is unsuccessful
*
* @param data the AIS sentence string for which you wish to parse the checksum
* @return the int checksum for the provided string
*/
private static int getChecksum(String data) {
int index = data.indexOf(String.valueOf(CHECKSUM_DELIMITER));
if (index > -1) {
return getChecksum(data, 1, data.indexOf((String.valueOf(CHECKSUM_DELIMITER))));
} else {
return getChecksum(data, 1, data.length());
}
}
/**
* Generates a checksum for the provided byte []
*
* @param bytes the byte [] from which you wish to extract a checksum
* @return the int checksum for the provided byte []
*/
private static int getChecksum(byte[] bytes) {
return AISSentence
.generateChecksum(ByteArrayUtils
.bArray2cArray(Arrays.copyOfRange(bytes, 1,
ByteArrayUtils.indexOf(bytes, CHECKSUM_DELIMITER))));
}
/**
* Generates a checksum for the substring (based on int startFrom and int endAt
* indices) of String genString
*
* @param genString the String for which you wish to generate a checksum
* @param startFrom the int start index of the substring
* @param endAt the int end index of the substring
* @return the int form of the checksum
*/
public static int getChecksum(String genString, int startFrom, int endAt) {
if (endAt <= startFrom || endAt > genString.length())
return -1;
return AISSentence.generateChecksum(genString.substring(startFrom, endAt).toCharArray());
}
/**
* Validates the provided checksum (byte [] sentenceChecksum) by generating a
* new
* checksum for byte [] data and comparing them
*
* @param data the byte [] to which the provided sentenceChecksum
* should
* apply
* @param sentenceChecksum a byte [] representation of the checksum to be
* validated
* @return a boolean representing the validity of the checksum
*/
private static boolean validateChecksum(byte[] data, byte[] sentenceChecksum) {
long calcChecksum;
long pktChecksum;
byte[] trimmed = ByteArrayUtils.trimByteArray(data);
try {
calcChecksum = getChecksum(trimmed);
} catch (NumberFormatException nfe) {
return false;
}
try {
pktChecksum = Long.parseUnsignedLong(ByteArrayUtils.bArray2Str(sentenceChecksum), 16);
} catch (NumberFormatException nfe) {
return false;
}
return (pktChecksum == calcChecksum);
}
/**
* A utility method that enables binary decoding even when the binary string is
* all we have
*
* @param rawData the binary String from an AIS sentence String which has no
* prefix or suffix
* @return a generated String representation of a complete AIS sentence (with
* prefix, suffix, checksum, etc)
*/
public static String createSentenceStringFrompayload(String rawData) {
String sentenceString = "!AIVDM,1,1,,A," + rawData + ",0*";
sentenceString += Integer.toHexString(AISSentence.getChecksum(sentenceString));
return sentenceString;
}
/**
* A utility method that creates an AISsentence object based solely on the 6-bit
* encoded String from an AIS sentence
* String
*
* @param rawData The binary encoded String
* @return an AISsentence object based on the provided binary string
*/
public static AISSentence createFromPayload(String rawData) {
return createFromPayload(rawData, null);
}
/**
* Generates an AISsentence object from a raw 6-bit encoded String and a String
* representing the data source
*
* @param rawData The binary encoded String
* @param source A string representing the source from which this sentence
* originated
* @return an AISsentence object based on the provided binary string
*/
public static AISSentence createFromPayload(String rawData, String source) {
if (source == null)
source = "UNKNOWN";
return new AISSentence(createSentenceStringFrompayload(rawData), source);
}
/**
* Generates a String representation of the AISsentence with a pre-pended
* TagBlock
* String which contains only
* the source and time stamp values of the AISsentence object
*
* @return a String representation of the AISsentence with a pre-pended TagBlock
* String
*/
public String generateTagBlockSentenceString() {
TagBlock tb = new TagBlock();
tb.setSource(this.source);
tb.setTimestamp(this.timeReceived);
return generateTagBlockSentenceString(this.unparsedSentence, tb);
}
/**
* Generates a String representation of the AISsentence with a pre-pended
* TagBlock
* String containing the source and time stamp values of the
* AISsentence object as well as the text provided at method invocation
*
* @param text the byte array we wish to use to construct a TagBlock String
* @return a String representing the TagBlock contents
*/
public String generateTagBlockSentenceString(byte[] text) {
TagBlock tb = new TagBlock();
tb.setSource(this.source);
tb.setTimestamp(this.timeReceived);
tb.setTextStr(text);
return generateTagBlockSentenceString(this.unparsedSentence, tb);
}
/**
* Generates a String representation of the AISsentence with its pre-pended
* TagBlock String as already defined
*
* @param rawSentence A byte array containing representing the binary AIS
* sentence
* String
* @param tb The TagBlock object we wish to prepend to the AIS sentence
* @return A String representation of the concatenated TagBlock and AIS sentence
*/
private static String generateTagBlockSentenceString(byte[] rawSentence, TagBlock tb) {
return tb.toString() + ByteArrayUtils.bArray2Str(rawSentence);
}
/**
* Returns the actual numeric frequency (as a double) indicated by the
* this.radioChannelCode
*
* @return a double representing the numeric frequency on which this message was
* broadcast
*/
public double getRadioChannelFrequencyInMhz() {
return switch (this.radioChannelCode) {
case 'a' -> CHANNEL_A_FREQUENCY_IN_MHZ;
case 'b' -> CHANNEL_B_FREQUENCY_IN_MHZ;
default -> 0d;
};
}
/**
* Returns the contents of the non-decoded binary string as a byte []
*
* @return the raw binary string in the form of a byte array
*/
public byte[] getPayloadAsByteArray() {
return this.payload;
}
/**
* Returns the body of the AIS sentence as a byte []
*
* @return the raw body (binary string portion) of the sentence in the form of a
* byte array
*/
public byte[] getsentenceWithoutTagBlockAsByteArray() {
return this.sentenceWithoutTagBlock;
}
/**
* Returns the time at which this AISsentence object was instantiated for the
* specified ZoneOffset
*
* @param offset The ZoneOffset for calculating the time since epoch value of
* the time at which this sentence was received
* @return a ZonedDateTime object representing the time at which this AIS
* sentence
* was received
*/
public ZonedDateTime getTimeReceived(ZoneOffset offset) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(this.timeReceived), offset);
}
/**
* Returns the time at which this AISsentence object was instantiated for the
* specified ZoneId
*
* @param zone the ZoneId which we want to use to calculate the ZonedDateTime
* value
* @return the calculated ZonedDateTime value
*/
public ZonedDateTime getTimeReceived(ZoneId zone) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(this.timeReceived), zone);
}
/**
* Returns the time at which this AISsentence object was presumably sent based
* on
* the timestamp contained within the TagBlock
*
* @return a long representation of the timestamp at which the sentence was sent
*/
public long getTimeSent() {
if (hasTagBlock())
return this.tagBlock.getTimestamp();
return 0;
}
/**
* Returns the source of the AISsentence as a byte []
*
* @return a byte array representation of the name of the sentence source
*/
public byte[] getSource() {
return this.source;
}
/**
* Sets the source of the AISsentence to the provided byte [] value
*
* @param source sets the name of the source of this sentence as a byte array
*/
public void setSource(byte[] source) {
this.source = source;
}
/**
* Takes a String parameter and divides it into one or more distinct AISSentece
* objects
*
* @param sentence String to split or Truncate into multiple AISSentence objects
* @return Optional AISSentence[] which may be empty if no valid AISSentences
* could be found
*/
public static Optional splitOrTruncate(String sentence) {
if (sentence != null && sentence.length() > 0) {
int index = getSentenceTruncIndex(sentence);
if (index > -1) {
String[] sentStrs = ByteArrayUtils.fastSplit(sentence, '!');
List sList = Arrays.stream(sentStrs).map(AISSentence::new).toList();
AISSentence[] sentences = new AISSentence[sList.size()];
return Optional.of(sList.toArray(sentences));
}
}
return Optional.empty();
}
/**
* Truncates the provided StringBuilder object based on the index returned by
* {@link #getSentenceTruncIndex( String s )}
*
* @param sb the StringBuilder from which we want to produce a truncated String
* @return a truncated String
*/
public static String truncateSentence(StringBuilder sb) {
return truncateSentence(sb.toString());
}
/**
* Truncates the provided String object based on the index returned by
* {@link #getSentenceTruncIndex( String s )}
*
* @param s a String we wish to truncate
* @return the substring produced by truncating the provided String
*/
public static String truncateSentence(String s) {
int truncIndex = AISSentence.getSentenceTruncIndex(s);
String substring = null;
if (truncIndex != -1) {
substring = s.substring(0, truncIndex);
}
return substring;
}
/**
*
* @param s Determines the character index at which this String should be
* truncated based on the String contents
* @return the calculated index at which truncation should occur
*/
private static int getSentenceTruncIndex(String s) {
int truncIndex = 0;
Matcher m = AISSentence.PREAMBLE_PATTERN.matcher(s);
if (m.find()) {
if (s.contains("\n")) {
truncIndex = s.indexOf("\n");
} else if (s.contains("\r")) {
truncIndex = s.indexOf("\r");
} else if (m.find()) {
truncIndex = s.indexOf(m.group(0), 1);
} else {
truncIndex = -1;
}
}
return truncIndex;
}
/**
* Combines two or more AISsentence objects into a single non-decoded AIS
* message
* and returns the results as a byte []
*
* @param sentences one or more AISsentence objects that we wish to combine into
* a single binary string for decoding
* @return a byte [] representation of the concatenated AIS binary strings
*/
public static byte[] concatenate(AISSentence... sentences) {
if (sentences.length == 1) {
if (!sentences[0].isParsed())
sentences[0].process();
return sentences[0].getPayloadAsByteArray();
}
byte[] compositeMsg = null;
for (AISSentence sentence : sentences) {
if (sentence == null) {
continue;
} else if (!sentence.isParsed())
sentence.process();
byte[] bytes = sentence.getPayloadAsByteArray();
if (compositeMsg == null)
compositeMsg = bytes;
else {
byte[] temp = new byte[compositeMsg.length + bytes.length];
System.arraycopy(compositeMsg, 0, temp, 0, compositeMsg.length);
System.arraycopy(bytes, 0, temp, compositeMsg.length, bytes.length);
compositeMsg = temp;
}
}
return compositeMsg;
}
/**
* An override of Object.equals()
*
* @param o the object against which we will perform our comparison
* @return the boolean result of comparing the the provided object to the
* current one
*/
@Override
public boolean equals(Object o) {
if (o == null)
return false;
if (!(o instanceof AISSentence that))
return false;
if (that.getUnparsedSentence() == null)
return false;
return (Arrays.equals(that.getUnparsedSentence(), this.unparsedSentence)
&& Arrays.equals(that.getSource(), this.source));
}
/**
* An override of Object.hashCode()
*
* @return an int representing a hashcode
*/
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + (this.unparsedSentence != null ? Arrays.hashCode(this.unparsedSentence) : 0);
hash = 79 * hash + (this.source != null ? Arrays.hashCode(this.source) : 0);
return hash;
}
/**
* Returns a HashMap representation of the AISsentence fields
*
* @return a HashMap representation of the AISsentence
*/
public HashMap toMap() {
HashMap sentenceMap = new HashMap<>();
sentenceMap.put("tagblock", this.tagBlock);
sentenceMap.put("preamble", this.preamble);
sentenceMap.put("raw_message", this.payload);
sentenceMap.put("unparsed_sentence", this.unparsedSentence);
sentenceMap.put("time_received", this.timeReceived);
sentenceMap.put("source", this.source);
sentenceMap.put("fragment_count", this.fragmentCount);
sentenceMap.put("fragment_number", this.fragmentNumber);
sentenceMap.put("sequential_message_id", this.sequentialMessageId);
sentenceMap.put("radio_channel_code", this.radioChannelCode);
sentenceMap.put("checksum", this.checksum);
sentenceMap.put("fillbits", this.fillBits);
return sentenceMap;
}
/**
* An object representation of an AISsentence preamble
*
*/
public static class Preamble {
public final byte[] rawPreamble;
public char firstChar;
public boolean isEncapsulated;
public Talker talker;
public boolean isProprietary;
public Manufacturer manufacturer;
public byte[] format;
public boolean isQuery;
public byte[] parsed;
/**
*
* @param rawPreamble a byte [] representation of the AISsentence preamble
*/
public Preamble(byte[] rawPreamble) {
this.rawPreamble = rawPreamble;
}
/**
* Populates the fields of this Preamble object based on the parsing of it's
* rawPreamble byte [] and returns this Preamble object
*
* @return a Preamble object
*/
public Preamble parse() {
return parse(this, ByteArrayUtils.bArray2Str(rawPreamble));
}
/**
* Returns a Preamble object based on the parsing of the provided raw preamble
* byte []
*
* @param rawPreamble the unparsed preamble in byte array form
* @return a Preamble object based on parsing the provided rawPreamble
*/
public static Preamble parse(byte[] rawPreamble) {
return parse(ByteArrayUtils.bArray2Str(rawPreamble));
}
/**
* Returns a Preamble object based on the parsing of the provided raw preamble
* String
*
* @param rawPreamble a String representation of the unparsed preamble
* @return a Preamble object
*/
public static Preamble parse(String rawPreamble) {
return parse(new Preamble(ByteArrayUtils.str2bArray(rawPreamble)), rawPreamble);
}
/**
* Parses the provided rawPreamble String and populates the fields of the
* provided Preamble object before returning it
*
* @param p the Preamble we wish to populate
* @param rawPreamble the raw String we wish to parse in order to build our
* Preamble object
* @return the completed Preamble object
*/
public static Preamble parse(Preamble p, String rawPreamble) {
Matcher m = PREAMBLE_PATTERN.matcher(rawPreamble);
if (m.find()) {
String parsed = m.group(0);
p.parsed = ByteArrayUtils.str2bArray(parsed);
p.firstChar = m.group(1).charAt(0);
if (p.firstChar == '!')
p.isEncapsulated = true;
else if (m.group(1).equals("$"))
p.isEncapsulated = false;
else {
p.isEncapsulated = false;
}
if (m.group(3).startsWith("P")) {
String mString = (m.group(3)).toUpperCase();
if (!Manufacturer.isKnown(mString))
return null;
p.talker = Talker.P;
try {
p.manufacturer = Manufacturer.valueOf(mString);
} catch (InvalidParameterException ipe) {
// There is no Manufacturer that matches
return null;
}
} else if (Talker.isKnown(m.group(2).toUpperCase())) {
p.talker = Talker.valueOf(m.group(2).toUpperCase());
} else {
p.talker = null;
}
p.format = ByteArrayUtils.str2bArray(m.group(4));
p.isQuery = m.group(5).equals("Q");
}
return p;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy