1
2 __version__ = '$Revision: 2275 $'.split()[1]
3 __date__ = '$Date: 2006-07-10 16:22:35 -0400 (Mon, 10 Jul 2006) $'.split()[1]
4 __author__ = 'Kurt Schwehr'
5
6 __doc__ = '''
7 Connect to a socket and forward what is received to another port.
8 Filter to a list of AIS receivers/basestations.
9
10 @author: '''+__author__+'''
11 @version: ''' + __version__ +'''
12 @copyright: 2006
13 @var __date__: Date of last svn commit
14 @undocumented: __version__ __author__ __doc__ myparser
15 @status: under development
16 @license: GPL v2
17 @since: Jan 2008
18 '''
19
20 import datetime
21 import unittest
22 import sys
23 import ais.sqlhelp
24 import ais.binary
25
28 '''
29 Fields:
30 - rssi ('s'): relative signal strength indicator
31 - signalStrength ('d') - signal strendth in dBm
32 - timeOfArrival ('T') - time of arrive from receiver - seconds within the minute
33 - slotNumber ('S') - Receive slot number
34 - station ('r' or 'b') - station name or id that received the message
35 - stationTypeCode - first letter of the station name indicating 'b'asestation or 'r'eceive only (I think)
36 - cg_sec - receive time of the message from the logging software. Unix UTC second timestamp
37 - timestamp - python datetime object in UTC derived from the cg_sec
38
39 @todo: parse the other fields?
40
41 @see: Maritime navigation and radiocommunication equipment and
42 systems - Digital interfaces - Part 100: Single talker
43 and multiple listeners - Extra requirements to IEC
44 61162-1 for the UAIS. (80_330e_PAS) Draft...
45
46 '''
47 if None!=nmeaStr:
48
49
50
51
52 fields = nmeaStr.split(',')
53 self.cg_sec=float(fields[-1])
54 self.timestamp = datetime.datetime.utcfromtimestamp(self.cg_sec)
55 self.sqlTimestampStr = ais.sqlhelp.sec2timestamp(self.cg_sec)
56
57
58 self.nmeaType=fields[0][1:]
59 self.totalSentences = int(fields[1])
60 self.sentenceNum = int(fields[2])
61 tmp = fields[3]
62 if len(tmp)>0:
63 self.sequentialMsgId = int(tmp)
64 else:
65 self.sequentialMsgId = None
66
67 self.aisChannel = fields[4]
68 self.contents = fields[5]
69 self.fillbits = int(fields[6].split('*')[0])
70 self.checksumStr = fields[6].split('*')[1]
71
72 if self.sentenceNum==1:
73 self.msgTypeChar=fields[5][0]
74 else:
75 self.msgTypeChar=None
76
77 for i in range(len(fields)-1,5,-1):
78 if len(fields[i])==0:
79 continue
80 f = fields[i]
81 c = f[0]
82 if c in ('b','r'):
83 self.station = f
84 self.stationTypeCode = self.station[0]
85 continue
86
87 if c == 's':
88 self.rssi=int(f[1:])
89 continue
90 if c == 'd':
91 self.signalStrength = int(f[1:])
92 continue
93 if c == 'T':
94 self.timeOfArrival = float(f[1:])
95 continue
96 if c == 'S':
97 self.slotNumber = int(f[1:])
98 continue
99 if c == 'x':
100
101 self.x = int(f[1:])
102 continue
104 '''
105 @return: bits for the payload (even if this is a multipart)
106 @rtype: BitVector
107 '''
108 return ais.binary.ais6tobitvec(self.contents)
109
111
112 if self.cg_sec != other.cg_sec: return False
113 if self.sentenceNum != other.sentenceNum: return False
114 if self.totalSentences != other.totalSentences: return False
115 if self.sequentialMsgId != other.sequentialMsgId: return False
116 if self.aisChannel != other.aisChannel: return False
117 if self.checksumStr != other.checksumStr: return False
118 if self.fillbits != other.fillbits: return False
119 if self.station != other.station: return False
120 if self.contents != other.contents: return False
121
122
123 return True
124
126 return not self.__eq__(other)
127
130
132 '''Use the values in this message to reconstruct a single line nmea string'''
133
134 parts=['!'+self.nmeaType,str(self.totalSentences),str(self.sentenceNum)]
135 if self.sequentialMsgId is None:
136 parts.append('')
137 else:
138 parts.append(str(self.sequentialMsgId))
139 parts.append(self.aisChannel)
140 parts.append(self.contents)
141 parts.append(str(self.fillbits)+'*'+self.checksumStr)
142
143 if 'rssi' in self.__dict__: parts.append('s'+str(self.rssi))
144 if 'signalStrength' in self.__dict__: parts.append('d'+str(self.signalStrength))
145 if 'timeOfArrival' in self.__dict__: parts.append('T'+str(self.timeOfArrival))
146 if 'slotNumber' in self.__dict__: parts.append('S'+str(self.slotNumber))
147 if 'x' in self.__dict__: parts.append('x'+str(self.x))
148
149 if self.station: parts.append(self.station)
150 parts.append(str(self.cg_sec))
151 return ','.join(parts)
152
153
154
155
156
157
158
161 un = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
162
163 self.failUnlessEqual(un.nmeaType,'AIVDM')
164 self.failUnlessEqual(un.totalSentences,1)
165 self.failUnlessEqual(un.sentenceNum,1)
166 self.failUnlessEqual(un.sequentialMsgId,None)
167 self.failUnlessEqual(un.aisChannel,'B')
168 self.failUnlessEqual(un.fillbits,0)
169 self.failUnlessEqual(un.checksumStr,'63')
170
171 self.failUnlessEqual(un.rssi,1234)
172 self.failUnlessEqual(un.signalStrength,-119)
173 self.failUnlessEqual(un.timeOfArrival,12.34567123)
174 self.failUnlessEqual(un.slotNumber,4321)
175 self.failUnlessEqual(un.station,'r003669958')
176 self.failUnlessEqual(un.stationTypeCode,'r')
177 self.failUnlessEqual(un.cg_sec,float(1085889680))
178 print un.timestamp
179 print un.sqlTimestampStr
180
181
183 m1 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
184 m1same = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
185
186 m2 = UscgNmea('!AIVDM,2,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
187 m3 = UscgNmea('!AIVDM,1,2,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
188 m4 = UscgNmea('!AIVDM,1,1,7,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
189 m5 = UscgNmea('!AIVDM,1,1,,A,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
190 m6 = UscgNmea('!AIVDM,1,1,,B,25Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
191 m7 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,1*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
192 m8 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*64,s1234,d-119,T12.34567123,r003669958,S4321,1085889680')
193
194
195
196 m12 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669959,S4321,1085889680')
197
198 m14 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S4321,1085889681')
199 self.failUnless(m1==m1)
200 self.failUnless(m1==m1same)
201 self.failUnless(m1!=m2)
202 self.failUnless(m1!=m3)
203 self.failUnless(m1!=m4)
204 self.failUnless(m1!=m5)
205 self.failUnless(m1!=m6)
206 self.failUnless(m1!=m7)
207 self.failUnless(m1!=m8)
208
209
210
211 self.failUnless(m1!=m12)
212
213 self.failUnless(m1!=m14)
214
215
216
217 if __name__=='__main__':
218
219 from optparse import OptionParser
220 parser = OptionParser(usage="%prog [options]",
221 version="%prog "+__version__)
222
223 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true'
224 ,help='run the documentation tests')
225 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true'
226 ,help='run the unit tests')
227 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true'
228 ,help='Make the test output verbose')
229
230 (options,args) = parser.parse_args()
231
232 if options.doctest:
233 success=True
234 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
235 sys.argv= [sys.argv[0]]
236 if options.verbose: sys.argv.append('-v')
237 import doctest
238 numfail,numtests=doctest.testmod()
239 if numfail==0: print 'ok'
240 else:
241 print 'FAILED'
242 success=False
243
244 if not success: sys.exit('Something Failed')
245
246
247 if options.unittest:
248 sys.argv = [sys.argv[0]]
249 if options.verbose: sys.argv.append('-v')
250 unittest.main()
251