View Javadoc
1   package au.gov.amsa.util.nmea;
2   
3   import java.util.LinkedHashMap;
4   import java.util.List;
5   import java.util.Map.Entry;
6   import java.util.regex.Matcher;
7   import java.util.regex.Pattern;
8   
9   /**
10   * Bean to carry NMEA fields.
11   * 
12   * @author dxm
13   * 
14   */
15  public class NmeaMessage {
16  
17      private final LinkedHashMap<String, String> tags;
18      private final List<String> items;
19      private final Talker talker;
20      private final SentenceInfo sentenceInfo;
21      private final String checksum;
22  
23      /**
24       * Constructor.
25       * 
26       * @param tags
27       *            is a list of tags from the tag block section of an NMEA
28       *            message
29       * @param items
30       *            is the list of columns from the NMEA message (not including
31       *            the tag block) but including the checksum on the final column.
32       */
33      public NmeaMessage(LinkedHashMap<String, String> tags, List<String> items, String checksum) {
34          this.tags = tags;
35          this.items = items;
36          if (!items.isEmpty() && items.get(0).length() >= 3)
37              this.talker = NmeaUtil.getTalker((items.get(0).substring(1, 3)));
38          else
39              talker = Talker.UNKNOWN;
40          this.sentenceInfo = getSentenceInfo(tags, items);
41          this.checksum = checksum;
42      }
43  
44      /**
45       * Returns the 's:' value from the tag block.
46       * 
47       * @return
48       */
49      public String getSource() {
50          return tags.get("s");
51      }
52  
53      /**
54       * Returns the 'c:' value from the tag block times 1000 to convert to
55       * millis. Returns null if not present.
56       * 
57       * @return
58       */
59      public Long getUnixTimeMillis() {
60          String time = tags.get("c");
61          if (time == null)
62              return null;
63          else
64              try {
65                  return Long.parseLong(time) * 1000;
66              } catch (NumberFormatException e) {
67                  return null;
68              }
69      }
70  
71      /**
72       * Returns the 'd:' value from the tag block.
73       * 
74       * @return
75       */
76      public String getDestination() {
77          return tags.get("d");
78      }
79  
80      /**
81       * Returns the 'g:' value from the tag block.
82       * 
83       * @return
84       */
85      public String getSentenceGroupingFromTagBlock() {
86          return tags.get("g");
87      }
88  
89      /**
90       * Returns the 'n:' value from the tag block.
91       * 
92       * @return
93       */
94      public Integer getLineCount() {
95          String count = tags.get("n");
96          if (count == null)
97              return null;
98          else
99              return Integer.parseInt(count);
100     }
101 
102     /**
103      * Returns the 'r:' value from the tag block times 1000 to convert to
104      * millis.
105      * 
106      * @return
107      */
108     public Long getRelativeTimeMillis() {
109         String time = tags.get("r");
110         if (time == null)
111             return null;
112         else
113             return Long.parseLong(time) * 1000;
114     }
115 
116     /**
117      * Returns the 't:' value from the tag block.
118      * 
119      * @return
120      */
121     public String getText() {
122         return tags.get("t");
123     }
124 
125     /**
126      * Returns a list of the NMEA items from the columns after the tag block.
127      * 
128      * @return
129      */
130     public List<String> getItems() {
131         return items;
132     }
133 
134     public LinkedHashMap<String, String> getTags() {
135         return tags;
136     }
137 
138     public Talker getTalker() {
139         return talker;
140     }
141 
142     public String toLine() {
143         return NmeaUtil.createNmeaLine(tags, items);
144     }
145 
146     public Integer getSentenceNumber() {
147         if (sentenceInfo != null)
148             return sentenceInfo.number;
149         else
150             return null;
151     }
152 
153     public Integer getSentenceCount() {
154         if (sentenceInfo != null)
155             return sentenceInfo.count;
156         else
157             return null;
158     }
159 
160     public String getSentenceGroupId() {
161         if (sentenceInfo != null)
162             return sentenceInfo.id;
163         else
164             return null;
165     }
166 
167     /**
168      * Returns a recalculated checksum.
169      * 
170      * @return calculated checksum
171      */
172     public String calculateChecksum() {
173         return NmeaUtil.getChecksum(NmeaUtil.createNmeaLine(tags, items));
174     }
175 
176     public String getChecksum() {
177         return checksum;
178     }
179 
180     private static final Pattern sentenceTagPattern = Pattern
181             .compile("([1-9][0-9]*)G([1-9][0-9]*)");
182 
183     private static SentenceInfo getSentenceInfo(LinkedHashMap<String, String> tags,
184             List<String> items) {
185         try {
186             String g = tags.get("g");
187             if (g != null) {
188                 // old style NMEA 4.0.0 sentence grouping tag
189                 String[] parts = g.split("-");
190                 if (parts.length < 3)
191                     throw new NmeaMessageParseException("not enough parts available in g tag");
192                 int number = Integer.parseInt(parts[0]);
193                 int count = Integer.parseInt(parts[1]);
194                 String id = parts[2];
195                 return new SentenceInfo(number, count, id);
196 
197             } else {
198                 // look for new style NMEA 4.1.0 sentence grouping tag (e.g.
199                 // 1G2)
200                 for (Entry<String, String> entry : tags.entrySet()) {
201                     Matcher matcher = sentenceTagPattern.matcher(entry.getKey());
202                     if (matcher.matches()) {
203                         int number = Integer.parseInt(matcher.group(1));
204                         int count = Integer.parseInt(matcher.group(2));
205                         String id = entry.getValue();
206                         // found it so return the sentence info
207                         return new SentenceInfo(number, count, id);
208                     }
209                 }
210                 // didn't find the grouping tag
211                 if (items.size() > 2 && isEncapsulationSentence(items)) {
212                     int number = Integer.parseInt(items.get(2));
213                     int count = Integer.parseInt(items.get(1));
214                     String id = items.get(3);
215                     return new SentenceInfo(number, count, id);
216                 } else
217                     return null;
218             }
219         } catch (NumberFormatException e) {
220             throw new NmeaMessageParseException(e.getMessage(), e);
221         }
222     }
223 
224     private static boolean isEncapsulationSentence(List<String> items) {
225         return items.size() > 0 && items.get(0).startsWith("!");
226     }
227 
228     private static class SentenceInfo {
229         int number;
230         int count;
231         String id;
232 
233         public SentenceInfo(int number, int count, String id) {
234             super();
235             this.number = number;
236             this.count = count;
237             this.id = id;
238         }
239     }
240 
241     public boolean isSingleSentence() {
242         return getSentenceCount() == null || getSentenceCount() == 1;
243     }
244 
245     public Long getArrivalTimeMillis() {
246         String time = tags.get("a");
247         if (time == null)
248             return null;
249         else
250             try {
251                 return Long.parseLong(time);
252             } catch (NumberFormatException e) {
253                 return null;
254             }
255     }
256 
257 }