View Javadoc
1   package au.gov.amsa.ais;
2   
3   import static java.lang.Integer.parseInt;
4   
5   import java.util.Calendar;
6   import java.util.regex.Pattern;
7   
8   import au.gov.amsa.util.nmea.NmeaMessage;
9   import au.gov.amsa.util.nmea.NmeaMessageParseException;
10  import au.gov.amsa.util.nmea.NmeaUtil;
11  
12  import com.google.common.annotations.VisibleForTesting;
13  
14  /**
15   * Parses a custom ExactEarth NMEA line for AMSA so that we can obtain the
16   * actual timestamp of an AIS Position Report (types 1,2,3).
17   * 
18   * @author dxm
19   * 
20   */
21  public class NmeaMessageExactEarthTimestamp {
22  
23  	private final NmeaMessage nmea;
24  	private final long time;
25  	private final String followingSequenceChecksum;
26  
27  	/**
28  	 * Constructor.
29  	 * 
30  	 * @param line
31  	 */
32  	public NmeaMessageExactEarthTimestamp(String line) {
33  		nmea = NmeaUtil.parseNmea(line);
34  		try {
35  			Util.checkArgument(isPghp(line), "not an ExactEarth timestamp: "
36  					+ line);
37  			getFollowingSequenceChecksum();
38  			int year = parseInt(getItem(2));
39  			int month = parseInt(getItem(3));
40  			int day = parseInt(getItem(4));
41  			int hour = parseInt(getItem(5));
42  			int minute = parseInt(getItem(6));
43  			int second = parseInt(getItem(7));
44  			int millisecond = parseInt(getItem(8));
45  			time = getTime(year, month, day, hour, minute, second, millisecond);
46  			followingSequenceChecksum = extractFollowingSequenceChecksum();
47  		} catch (RuntimeException e) {
48  			throw new AisParseException(e);
49  		}
50  	}
51  
52  	private static final Pattern pghpPattern = Pattern
53  			.compile("^(\\\\.*\\\\)?\\$PGHP.*$");
54  
55  	/**
56  	 * Returns true if and only if the given NMEA line is a PGHP line. Remember
57  	 * that the line can start with a tag block.
58  	 * 
59  	 * @param line
60  	 * @return
61  	 */
62  	@VisibleForTesting
63  	static boolean isPghp(String line) {
64  		if (line == null)
65  			return false;
66  		else
67  			// return Pattern.matches("^(\\\\.*\\\\)?\\$PGHP.*$", line);
68  			return pghpPattern.matcher(line).matches();
69  	}
70  
71  	/**
72  	 * Returns true if the given line is a custom ExactEarth timestamp.
73  	 * 
74  	 * @param line
75  	 * @return
76  	 */
77  	public static boolean isExactEarthTimestamp(String line) {
78  		try {
79  			new NmeaMessageExactEarthTimestamp(line);
80  			return true;
81  		} catch (AisParseException e) {
82  			return false;
83  		} catch (NmeaMessageParseException e) {
84  			return false;
85  		}
86  	}
87  
88  	/**
89  	 * Returns the item at index, zero based.
90  	 * 
91  	 * @param index
92  	 * @return
93  	 */
94  	private String getItem(int index) {
95  		return nmea.getItems().get(index);
96  	}
97  
98  	/**
99  	 * Returns the epoch time in ms.
100 	 * 
101 	 * @return
102 	 */
103 	private static long getTime(int year, int month, int day, int hour,
104 			int minute, int second, int millisecond) {
105 		Calendar cal = Calendar.getInstance(Util.TIME_ZONE_UTC);
106 		cal.set(year, month - 1, day, hour, minute, second);
107 		cal.set(Calendar.MILLISECOND, millisecond);
108 		return cal.getTimeInMillis();
109 	}
110 
111 	/**
112 	 * Returns the time in epoch ms.
113 	 * 
114 	 * @return
115 	 */
116 	public long getTime() {
117 		return time;
118 	}
119 
120 	/**
121 	 * Returns the checksum of the message that this timestamp line refers to.
122 	 * 
123 	 * @return
124 	 */
125 	public String getFollowingSequenceChecksum() {
126 		return followingSequenceChecksum;
127 	}
128 
129 	/**
130 	 * Returns the checksum of the message that this timestamp refers to.
131 	 * 
132 	 * @return
133 	 */
134 	private String extractFollowingSequenceChecksum() {
135 		return getItem(13);
136 	}
137 }