package au.gov.amsa.util.nmea; import java.util.LinkedHashMap; import java.util.List; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Bean to carry NMEA fields. * * @author dxm * */ public class NmeaMessage { private final LinkedHashMap<String, String> tags; private final List<String> items; private final Talker talker; private final SentenceInfo sentenceInfo; private final String checksum; /** * Constructor. * * @param tags * is a list of tags from the tag block section of an NMEA * message * @param items * is the list of columns from the NMEA message (not including * the tag block) but including the checksum on the final column. */ public NmeaMessage(LinkedHashMap<String, String> tags, List<String> items, String checksum) { this.tags = tags; this.items = items; if (!items.isEmpty() && items.get(0).length() >= 3) this.talker = NmeaUtil.getTalker((items.get(0).substring(1, 3))); else talker = Talker.UNKNOWN; this.sentenceInfo = getSentenceInfo(tags, items); this.checksum = checksum; } /** * Returns the 's:' value from the tag block. * * @return */ public String getSource() { return tags.get("s"); } /** * Returns the 'c:' value from the tag block times 1000 to convert to * millis. Returns null if not present. * * @return */ public Long getUnixTimeMillis() { String time = tags.get("c"); if (time == null) return null; else try { return Long.parseLong(time) * 1000; } catch (NumberFormatException e) { return null; } } /** * Returns the 'd:' value from the tag block. * * @return */ public String getDestination() { return tags.get("d"); } /** * Returns the 'g:' value from the tag block. * * @return */ public String getSentenceGroupingFromTagBlock() { return tags.get("g"); } /** * Returns the 'n:' value from the tag block. * * @return */ public Integer getLineCount() { String count = tags.get("n"); if (count == null) return null; else return Integer.parseInt(count); } /** * Returns the 'r:' value from the tag block times 1000 to convert to * millis. * * @return */ public Long getRelativeTimeMillis() { String time = tags.get("r"); if (time == null) return null; else return Long.parseLong(time) * 1000; } /** * Returns the 't:' value from the tag block. * * @return */ public String getText() { return tags.get("t"); } /** * Returns a list of the NMEA items from the columns after the tag block. * * @return */ public List<String> getItems() { return items; } public LinkedHashMap<String, String> getTags() { return tags; } public Talker getTalker() { return talker; } public String toLine() { return NmeaUtil.createNmeaLine(tags, items); } public Integer getSentenceNumber() { if (sentenceInfo != null) return sentenceInfo.number; else return null; } public Integer getSentenceCount() { if (sentenceInfo != null) return sentenceInfo.count; else return null; } public String getSentenceGroupId() { if (sentenceInfo != null) return sentenceInfo.id; else return null; } /** * Returns a recalculated checksum. * * @return calculated checksum */ public String calculateChecksum() { return NmeaUtil.getChecksum(NmeaUtil.createNmeaLine(tags, items)); } public String getChecksum() { return checksum; } private static final Pattern sentenceTagPattern = Pattern .compile("([1-9][0-9]*)G([1-9][0-9]*)"); private static SentenceInfo getSentenceInfo(LinkedHashMap<String, String> tags, List<String> items) { try { String g = tags.get("g"); if (g != null) { // old style NMEA 4.0.0 sentence grouping tag String[] parts = g.split("-"); if (parts.length < 3) throw new NmeaMessageParseException("not enough parts available in g tag"); int number = Integer.parseInt(parts[0]); int count = Integer.parseInt(parts[1]); String id = parts[2]; return new SentenceInfo(number, count, id); } else { // look for new style NMEA 4.1.0 sentence grouping tag (e.g. // 1G2) for (Entry<String, String> entry : tags.entrySet()) { Matcher matcher = sentenceTagPattern.matcher(entry.getKey()); if (matcher.matches()) { int number = Integer.parseInt(matcher.group(1)); int count = Integer.parseInt(matcher.group(2)); String id = entry.getValue(); // found it so return the sentence info return new SentenceInfo(number, count, id); } } // didn't find the grouping tag if (items.size() > 2 && isEncapsulationSentence(items)) { int number = Integer.parseInt(items.get(2)); int count = Integer.parseInt(items.get(1)); String id = items.get(3); return new SentenceInfo(number, count, id); } else return null; } } catch (NumberFormatException e) { throw new NmeaMessageParseException(e.getMessage(), e); } } private static boolean isEncapsulationSentence(List<String> items) { return items.size() > 0 && items.get(0).startsWith("!"); } private static class SentenceInfo { int number; int count; String id; public SentenceInfo(int number, int count, String id) { super(); this.number = number; this.count = count; this.id = id; } } public boolean isSingleSentence() { return getSentenceCount() == null || getSentenceCount() == 1; } public Long getArrivalTimeMillis() { String time = tags.get("a"); if (time == null) return null; else try { return Long.parseLong(time); } catch (NumberFormatException e) { return null; } } }