Package aisutils :: Module uscg
[hide private]
[frames] | no frames]

Source Code for Module aisutils.uscg

  1  #!/usr/bin/env python 
  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 # for sec2timestamp 
 24  import ais.binary 
 25   
26 -class UscgNmea:
27 - def __init__(self,nmeaStr=None):
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 #if len(nmeaStr)<6: 49 # # FIX: throw exception 50 # sys.stderr.write('Not a AIVDM... too short\n') 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 # See 80_330e_PAS 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 # FIX: make an int if the above is set 67 self.aisChannel = fields[4] # 'A' or 'B' 68 self.contents = fields[5] 69 self.fillbits = int(fields[6].split('*')[0]) 70 self.checksumStr = fields[6].split('*')[1] # FIX: this is a hex string. Convert? 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 # maybe it should throw a parse exception instead? 80 f = fields[i] 81 c = f[0] # first charater determines what the field is 82 if c in ('b','r'): 83 self.station = f # FIX: think we want to keep the code in the first char 84 self.stationTypeCode = self.station[0] 85 continue 86 #break # Found it so ditch the for loop 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 # I don't know what x is 101 self.x = int(f[1:]) 102 continue
103 - def getBitVector(self):
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
110 - def __eq__(self,other):
111 # Try to be smart for speed 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 # FIX: probably should check for the existance of rssi, signalStrength, etc 123 return True
124
125 - def __ne__(self,other):
126 return not self.__eq__(other)
127
128 - def __str__(self):
129 return self.buildNmea()
130
131 - def buildNmea(self):
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)) # Always last 151 return ','.join(parts)
152 153 # def getDriver(self): 154 # ''' 155 # Return the python module that handles this message type 156 # ''' 157 # FIX: where did I do this nicely? 158
159 -class TestUscgNmea(unittest.TestCase):
160 - def testUscgNmea(self):
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 # Hmmm... they look the same
180 181
182 - def testEquality(self):
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 # A whole bunch of mangled fields 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 #m9 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s123,d-119,T12.34567123,r003669958,S4321,1085889680') 194 #m10 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-120,T12.34567123,r003669958,S4321,1085889680') 195 #m11 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T11.34567123,r003669958,S4321,1085889680') 196 m12 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669959,S4321,1085889680') 197 #m13 = UscgNmea('!AIVDM,1,1,,B,15Cjtd0Oj;Jp7ilG7=UkKBoB0<06,0*63,s1234,d-119,T12.34567123,r003669958,S432,1085889680') 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 #self.failUnless(m1!=m9) 209 #self.failUnless(m1!=m10) 210 #self.failUnless(m1!=m11) 211 self.failUnless(m1!=m12) 212 #self.failUnless(m1!=m13) 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 #del success # Hide success from epydoc 246 247 if options.unittest: 248 sys.argv = [sys.argv[0]] 249 if options.verbose: sys.argv.append('-v') 250 unittest.main() 251