1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2007-04-07 $'.split()[1]
5 __author__ = 'xmlbinmsg'
6
7 __doc__='''
8
9 Autogenerated python functions to serialize/deserialize binary messages.
10
11 Generated by: ./aisxmlbinmsg2py.py
12
13 Need to then wrap these functions with the outer AIS packet and then
14 convert the whole binary blob to a NMEA string. Those functions are
15 not currently provided in this file.
16
17 serialize: python to ais binary
18 deserialize: ais binary to python
19
20 The generated code uses translators.py, binary.py, and aisstring.py
21 which should be packaged with the resulting files.
22
23
24 @requires: U{epydoc<http://epydoc.sourceforge.net/>} > 3.0alpha3
25 @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>}
26
27 @author: '''+__author__+'''
28 @version: ''' + __version__ +'''
29 @var __date__: Date of last svn commit
30 @undocumented: __version__ __author__ __doc__ parser
31 @status: under development
32 @license: Generated code has no license
33 @todo: FIX: put in a description of the message here with fields and types.
34 '''
35
36 import sys
37 from decimal import Decimal
38 from BitVector import BitVector
39
40 import binary, aisstring
41
42
43 TrueBV = BitVector(bitstring="1")
44 "Why always rebuild the True bit? This should speed things up a bunch"
45 FalseBV = BitVector(bitstring="0")
46 "Why always rebuild the False bit? This should speed things up a bunch"
47
48
49 fieldList = (
50 'MessageID',
51 'RepeatIndicator',
52 'UserID',
53 'Spare',
54 'dac',
55 'fid',
56 'efid',
57 'month',
58 'day',
59 'hour',
60 'min',
61 'sec',
62 'stationid',
63 'longitude',
64 'latitude',
65 'timetoexpire',
66 'radius',
67 'heading',
68 'numWhales',
69 )
70
71 fieldListPostgres = (
72 'MessageID',
73 'RepeatIndicator',
74 'UserID',
75 'Spare',
76 'dac',
77 'fid',
78 'efid',
79 'month',
80 'day',
81 'hour',
82 'min',
83 'sec',
84 'stationid',
85 'whale',
86 'timetoexpire',
87 'radius',
88 'heading',
89 'numWhales',
90 )
91
92 toPgFields = {
93 'longitude':'whale',
94 'latitude':'whale',
95 }
96 '''
97 Go to the Postgis field names from the straight field name
98 '''
99
100 fromPgFields = {
101 'whale':('longitude','latitude',),
102 }
103 '''
104 Go from the Postgis field names to the straight field name
105 '''
106
107 pgTypes = {
108 'whale':'POINT',
109 }
110 '''
111 Lookup table for each postgis field name to get its type.
112 '''
113
114 -def encode(params, validate=False):
115 '''Create a whalenotice binary message payload to pack into an AIS Msg whalenotice.
116
117 Fields in params:
118 - MessageID(uint): AIS message number. Must be 8 (field automatically set to "8")
119 - RepeatIndicator(uint): Indicated how many times a message has been repeated
120 - UserID(uint): Unique ship identification number (MMSI)
121 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0")
122 - dac(uint): Designated Area Code (field automatically set to "366")
123 - fid(uint): Functional Identifier (field automatically set to "1")
124 - efid(uint): extended functional identifier (dac+fid+id defines which message) (field automatically set to "2")
125 - month(uint): Time whale was last recorded. month 1..12
126 - day(uint): Time whale was last recorded. day of the month 1..31
127 - hour(uint): Time whale was last recorded. UTC hours 0..23
128 - min(uint): Time whale was last recorded. minutes
129 - sec(uint): Time whale was last recorded. seconds
130 - stationid(aisstr6): Character identifier of the station. Usually a number.
131 - longitude(decimal): Where was the whale located. Center of the restricted speed zone. East West location
132 - latitude(decimal): Where was the whale located. Center of the restricted speed zone. North South location
133 - timetoexpire(uint): Seconds from the time tag until this notice expires
134 - radius(uint): Distance of the required exclusion/slow zone
135 - heading(uint): Direction the whale is traveling if known
136 - numWhales(uint): Number of whales detected
137 @param params: Dictionary of field names/values. Throws a ValueError exception if required is missing
138 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
139 @rtype: BitVector
140 @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8
141 @note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits.
142 '''
143
144 bvList = []
145 bvList.append(binary.setBitVectorSize(BitVector(intVal=8),6))
146 if 'RepeatIndicator' in params:
147 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2))
148 else:
149 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
150 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30))
151 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
152 bvList.append(binary.setBitVectorSize(BitVector(intVal=366),10))
153 bvList.append(binary.setBitVectorSize(BitVector(intVal=1),6))
154 bvList.append(binary.setBitVectorSize(BitVector(intVal=2),12))
155 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['month']),4))
156 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['day']),5))
157 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['hour']),5))
158 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['min']),6))
159 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['sec']),6))
160 if 'stationid' in params:
161 bvList.append(aisstring.encode(params['stationid'],42))
162 else:
163 bvList.append(aisstring.encode('@@@@@@@',42))
164 if 'longitude' in params:
165 bvList.append(binary.bvFromSignedInt(int(Decimal(params['longitude'])*Decimal('600000')),28))
166 else:
167 bvList.append(binary.bvFromSignedInt(108600000,28))
168 if 'latitude' in params:
169 bvList.append(binary.bvFromSignedInt(int(Decimal(params['latitude'])*Decimal('600000')),27))
170 else:
171 bvList.append(binary.bvFromSignedInt(54600000,27))
172 if 'timetoexpire' in params:
173 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['timetoexpire']),16))
174 else:
175 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),16))
176 if 'radius' in params:
177 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['radius']),16))
178 else:
179 bvList.append(binary.setBitVectorSize(BitVector(intVal=65534),16))
180 if 'heading' in params:
181 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['heading']),9))
182 else:
183 bvList.append(binary.setBitVectorSize(BitVector(intVal=511),9))
184 if 'numWhales' in params:
185 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['numWhales']),8))
186 else:
187 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),8))
188
189 return binary.joinBV(bvList)
190
191 -def decode(bv, validate=False):
192 '''Unpack a whalenotice message
193
194 Fields in params:
195 - MessageID(uint): AIS message number. Must be 8 (field automatically set to "8")
196 - RepeatIndicator(uint): Indicated how many times a message has been repeated
197 - UserID(uint): Unique ship identification number (MMSI)
198 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0")
199 - dac(uint): Designated Area Code (field automatically set to "366")
200 - fid(uint): Functional Identifier (field automatically set to "1")
201 - efid(uint): extended functional identifier (dac+fid+id defines which message) (field automatically set to "2")
202 - month(uint): Time whale was last recorded. month 1..12
203 - day(uint): Time whale was last recorded. day of the month 1..31
204 - hour(uint): Time whale was last recorded. UTC hours 0..23
205 - min(uint): Time whale was last recorded. minutes
206 - sec(uint): Time whale was last recorded. seconds
207 - stationid(aisstr6): Character identifier of the station. Usually a number.
208 - longitude(decimal): Where was the whale located. Center of the restricted speed zone. East West location
209 - latitude(decimal): Where was the whale located. Center of the restricted speed zone. North South location
210 - timetoexpire(uint): Seconds from the time tag until this notice expires
211 - radius(uint): Distance of the required exclusion/slow zone
212 - heading(uint): Direction the whale is traveling if known
213 - numWhales(uint): Number of whales detected
214 @type bv: BitVector
215 @param bv: Bits defining a message
216 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
217 @rtype: dict
218 @return: params
219 '''
220
221
222
223
224 r = {}
225 r['MessageID']=8
226 r['RepeatIndicator']=int(bv[6:8])
227 r['UserID']=int(bv[8:38])
228 r['Spare']=0
229 r['dac']=366
230 r['fid']=1
231 r['efid']=2
232 r['month']=int(bv[68:72])
233 r['day']=int(bv[72:77])
234 r['hour']=int(bv[77:82])
235 r['min']=int(bv[82:88])
236 r['sec']=int(bv[88:94])
237 r['stationid']=aisstring.decode(bv[94:136])
238 r['longitude']=Decimal(binary.signedIntFromBV(bv[136:164]))/Decimal('600000')
239 r['latitude']=Decimal(binary.signedIntFromBV(bv[164:191]))/Decimal('600000')
240 r['timetoexpire']=int(bv[191:207])
241 r['radius']=int(bv[207:223])
242 r['heading']=int(bv[223:232])
243 r['numWhales']=int(bv[232:240])
244 return r
245
248
251
254
257
260
263
266
268 return int(bv[68:72])
269
271 return int(bv[72:77])
272
274 return int(bv[77:82])
275
277 return int(bv[82:88])
278
280 return int(bv[88:94])
281
284
287
290
292 return int(bv[191:207])
293
295 return int(bv[207:223])
296
298 return int(bv[223:232])
299
301 return int(bv[232:240])
302
303
305 out.write("<h3>whalenotice<h3>\n")
306 out.write("<table border=\"1\">\n")
307 out.write("<tr bgcolor=\"orange\">\n")
308 out.write("<th align=\"left\">Field Name</th>\n")
309 out.write("<th align=\"left\">Type</th>\n")
310 out.write("<th align=\"left\">Value</th>\n")
311 out.write("<th align=\"left\">Value in Lookup Table</th>\n")
312 out.write("<th align=\"left\">Units</th>\n")
313 out.write("\n")
314 out.write("<tr>\n")
315 out.write("<td>MessageID</td>\n")
316 out.write("<td>uint</td>\n")
317 if 'MessageID' in params:
318 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
319 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
320 out.write("</tr>\n")
321 out.write("\n")
322 out.write("<tr>\n")
323 out.write("<td>RepeatIndicator</td>\n")
324 out.write("<td>uint</td>\n")
325 if 'RepeatIndicator' in params:
326 out.write(" <td>"+str(params['RepeatIndicator'])+"</td>\n")
327 if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut:
328 out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>")
329 else:
330 out.write("<td><i>Missing LUT entry</i></td>")
331 out.write("</tr>\n")
332 out.write("\n")
333 out.write("<tr>\n")
334 out.write("<td>UserID</td>\n")
335 out.write("<td>uint</td>\n")
336 if 'UserID' in params:
337 out.write(" <td>"+str(params['UserID'])+"</td>\n")
338 out.write(" <td>"+str(params['UserID'])+"</td>\n")
339 out.write("</tr>\n")
340 out.write("\n")
341 out.write("<tr>\n")
342 out.write("<td>Spare</td>\n")
343 out.write("<td>uint</td>\n")
344 if 'Spare' in params:
345 out.write(" <td>"+str(params['Spare'])+"</td>\n")
346 out.write(" <td>"+str(params['Spare'])+"</td>\n")
347 out.write("</tr>\n")
348 out.write("\n")
349 out.write("<tr>\n")
350 out.write("<td>dac</td>\n")
351 out.write("<td>uint</td>\n")
352 if 'dac' in params:
353 out.write(" <td>"+str(params['dac'])+"</td>\n")
354 out.write(" <td>"+str(params['dac'])+"</td>\n")
355 out.write("</tr>\n")
356 out.write("\n")
357 out.write("<tr>\n")
358 out.write("<td>fid</td>\n")
359 out.write("<td>uint</td>\n")
360 if 'fid' in params:
361 out.write(" <td>"+str(params['fid'])+"</td>\n")
362 out.write(" <td>"+str(params['fid'])+"</td>\n")
363 out.write("</tr>\n")
364 out.write("\n")
365 out.write("<tr>\n")
366 out.write("<td>efid</td>\n")
367 out.write("<td>uint</td>\n")
368 if 'efid' in params:
369 out.write(" <td>"+str(params['efid'])+"</td>\n")
370 out.write(" <td>"+str(params['efid'])+"</td>\n")
371 out.write("</tr>\n")
372 out.write("\n")
373 out.write("<tr>\n")
374 out.write("<td>month</td>\n")
375 out.write("<td>uint</td>\n")
376 if 'month' in params:
377 out.write(" <td>"+str(params['month'])+"</td>\n")
378 out.write(" <td>"+str(params['month'])+"</td>\n")
379 out.write("</tr>\n")
380 out.write("\n")
381 out.write("<tr>\n")
382 out.write("<td>day</td>\n")
383 out.write("<td>uint</td>\n")
384 if 'day' in params:
385 out.write(" <td>"+str(params['day'])+"</td>\n")
386 out.write(" <td>"+str(params['day'])+"</td>\n")
387 out.write("</tr>\n")
388 out.write("\n")
389 out.write("<tr>\n")
390 out.write("<td>hour</td>\n")
391 out.write("<td>uint</td>\n")
392 if 'hour' in params:
393 out.write(" <td>"+str(params['hour'])+"</td>\n")
394 out.write(" <td>"+str(params['hour'])+"</td>\n")
395 out.write("</tr>\n")
396 out.write("\n")
397 out.write("<tr>\n")
398 out.write("<td>min</td>\n")
399 out.write("<td>uint</td>\n")
400 if 'min' in params:
401 out.write(" <td>"+str(params['min'])+"</td>\n")
402 out.write(" <td>"+str(params['min'])+"</td>\n")
403 out.write("</tr>\n")
404 out.write("\n")
405 out.write("<tr>\n")
406 out.write("<td>sec</td>\n")
407 out.write("<td>uint</td>\n")
408 if 'sec' in params:
409 out.write(" <td>"+str(params['sec'])+"</td>\n")
410 out.write(" <td>"+str(params['sec'])+"</td>\n")
411 out.write("</tr>\n")
412 out.write("\n")
413 out.write("<tr>\n")
414 out.write("<td>stationid</td>\n")
415 out.write("<td>aisstr6</td>\n")
416 if 'stationid' in params:
417 out.write(" <td>"+str(params['stationid'])+"</td>\n")
418 out.write(" <td>"+str(params['stationid'])+"</td>\n")
419 out.write("</tr>\n")
420 out.write("\n")
421 out.write("<tr>\n")
422 out.write("<td>longitude</td>\n")
423 out.write("<td>decimal</td>\n")
424 if 'longitude' in params:
425 out.write(" <td>"+str(params['longitude'])+"</td>\n")
426 out.write(" <td>"+str(params['longitude'])+"</td>\n")
427 out.write("<td>degrees</td>\n")
428 out.write("</tr>\n")
429 out.write("\n")
430 out.write("<tr>\n")
431 out.write("<td>latitude</td>\n")
432 out.write("<td>decimal</td>\n")
433 if 'latitude' in params:
434 out.write(" <td>"+str(params['latitude'])+"</td>\n")
435 out.write(" <td>"+str(params['latitude'])+"</td>\n")
436 out.write("<td>degrees</td>\n")
437 out.write("</tr>\n")
438 out.write("\n")
439 out.write("<tr>\n")
440 out.write("<td>timetoexpire</td>\n")
441 out.write("<td>uint</td>\n")
442 if 'timetoexpire' in params:
443 out.write(" <td>"+str(params['timetoexpire'])+"</td>\n")
444 out.write(" <td>"+str(params['timetoexpire'])+"</td>\n")
445 out.write("<td>seconds</td>\n")
446 out.write("</tr>\n")
447 out.write("\n")
448 out.write("<tr>\n")
449 out.write("<td>radius</td>\n")
450 out.write("<td>uint</td>\n")
451 if 'radius' in params:
452 out.write(" <td>"+str(params['radius'])+"</td>\n")
453 out.write(" <td>"+str(params['radius'])+"</td>\n")
454 out.write("<td>m</td>\n")
455 out.write("</tr>\n")
456 out.write("\n")
457 out.write("<tr>\n")
458 out.write("<td>heading</td>\n")
459 out.write("<td>uint</td>\n")
460 if 'heading' in params:
461 out.write(" <td>"+str(params['heading'])+"</td>\n")
462 out.write(" <td>"+str(params['heading'])+"</td>\n")
463 out.write("<td>degrees true north</td>\n")
464 out.write("</tr>\n")
465 out.write("\n")
466 out.write("<tr>\n")
467 out.write("<td>numWhales</td>\n")
468 out.write("<td>uint</td>\n")
469 if 'numWhales' in params:
470 out.write(" <td>"+str(params['numWhales'])+"</td>\n")
471 out.write(" <td>"+str(params['numWhales'])+"</td>\n")
472 out.write("</tr>\n")
473 out.write("</table>\n")
474
475
477 '''KML (Keyhole Markup Language) for Google Earth, but without the header/footer'''
478 out.write("\ <Placemark>\n")
479 out.write("\t <name>"+str(params['stationsid'])+"</name>\n")
480 out.write("\t\t<description>\n")
481 import StringIO
482 buf = StringIO.StringIO()
483 printHtml(params,buf)
484 import cgi
485 out.write(cgi.escape(buf.getvalue()))
486 out.write("\t\t</description>\n")
487 out.write("\t\t<styleUrl>#m_ylw-pushpin_copy0</styleUrl>\n")
488 out.write("\t\t<Point>\n")
489 out.write("\t\t\t<coordinates>")
490 out.write(str(params['longitude']))
491 out.write(',')
492 out.write(str(params['latitude']))
493 out.write(",0</coordinates>\n")
494 out.write("\t\t</Point>\n")
495 out.write("\t</Placemark>\n")
496
497 -def printFields(params, out=sys.stdout, format='std', fieldList=None, dbType='postgres'):
498 '''Print a whalenotice message to stdout.
499
500 Fields in params:
501 - MessageID(uint): AIS message number. Must be 8 (field automatically set to "8")
502 - RepeatIndicator(uint): Indicated how many times a message has been repeated
503 - UserID(uint): Unique ship identification number (MMSI)
504 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0")
505 - dac(uint): Designated Area Code (field automatically set to "366")
506 - fid(uint): Functional Identifier (field automatically set to "1")
507 - efid(uint): extended functional identifier (dac+fid+id defines which message) (field automatically set to "2")
508 - month(uint): Time whale was last recorded. month 1..12
509 - day(uint): Time whale was last recorded. day of the month 1..31
510 - hour(uint): Time whale was last recorded. UTC hours 0..23
511 - min(uint): Time whale was last recorded. minutes
512 - sec(uint): Time whale was last recorded. seconds
513 - stationid(aisstr6): Character identifier of the station. Usually a number.
514 - longitude(decimal): Where was the whale located. Center of the restricted speed zone. East West location
515 - latitude(decimal): Where was the whale located. Center of the restricted speed zone. North South location
516 - timetoexpire(uint): Seconds from the time tag until this notice expires
517 - radius(uint): Distance of the required exclusion/slow zone
518 - heading(uint): Direction the whale is traveling if known
519 - numWhales(uint): Number of whales detected
520 @param params: Dictionary of field names/values.
521 @param out: File like object to write to
522 @rtype: stdout
523 @return: text to out
524 '''
525
526 if 'std'==format:
527 out.write("whalenotice:\n")
528 if 'MessageID' in params: out.write(" MessageID: "+str(params['MessageID'])+"\n")
529 if 'RepeatIndicator' in params: out.write(" RepeatIndicator: "+str(params['RepeatIndicator'])+"\n")
530 if 'UserID' in params: out.write(" UserID: "+str(params['UserID'])+"\n")
531 if 'Spare' in params: out.write(" Spare: "+str(params['Spare'])+"\n")
532 if 'dac' in params: out.write(" dac: "+str(params['dac'])+"\n")
533 if 'fid' in params: out.write(" fid: "+str(params['fid'])+"\n")
534 if 'efid' in params: out.write(" efid: "+str(params['efid'])+"\n")
535 if 'month' in params: out.write(" month: "+str(params['month'])+"\n")
536 if 'day' in params: out.write(" day: "+str(params['day'])+"\n")
537 if 'hour' in params: out.write(" hour: "+str(params['hour'])+"\n")
538 if 'min' in params: out.write(" min: "+str(params['min'])+"\n")
539 if 'sec' in params: out.write(" sec: "+str(params['sec'])+"\n")
540 if 'stationid' in params: out.write(" stationid: "+str(params['stationid'])+"\n")
541 if 'longitude' in params: out.write(" longitude: "+str(params['longitude'])+"\n")
542 if 'latitude' in params: out.write(" latitude: "+str(params['latitude'])+"\n")
543 if 'timetoexpire' in params: out.write(" timetoexpire: "+str(params['timetoexpire'])+"\n")
544 if 'radius' in params: out.write(" radius: "+str(params['radius'])+"\n")
545 if 'heading' in params: out.write(" heading: "+str(params['heading'])+"\n")
546 if 'numWhales' in params: out.write(" numWhales: "+str(params['numWhales'])+"\n")
547 elif 'csv'==format:
548 if None == options.fieldList:
549 options.fieldList = fieldList
550 needComma = False;
551 for field in fieldList:
552 if needComma: out.write(',')
553 needComma = True
554 if field in params:
555 out.write(str(params[field]))
556
557 out.write("\n")
558 elif 'html'==format:
559 printHtml(params,out)
560 elif 'sql'==format:
561 sqlInsertStr(params,out,dbType=dbType)
562 elif 'kml'==format:
563 printKml(params,out)
564 elif 'kml-full'==format:
565 out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
566 out.write("<kml xmlns=\"http://earth.google.com/kml/2.1\">\n")
567 out.write("<Document>\n")
568 out.write(" <name>whalenotice</name>\n")
569 printKml(params,out)
570 out.write("</Document>\n")
571 out.write("</kml>\n")
572 else:
573 print "ERROR: unknown format:",format
574 assert False
575
576 return
577
578 RepeatIndicatorEncodeLut = {
579 'default':'0',
580 'do not repeat any more':'3',
581 }
582
583 RepeatIndicatorDecodeLut = {
584 '0':'default',
585 '3':'do not repeat any more',
586 }
587
588
589
590
591
592 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None
593 ,addCoastGuardFields=True
594 ,dbType='postgres'
595 ):
596 '''
597 Return the SQL CREATE command for this message type
598 @param outfile: file like object to print to.
599 @param fields: which fields to put in the create. Defaults to all.
600 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
601 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
602 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
603 @type addCoastGuardFields: bool
604 @return: sql create string
605 @rtype: str
606
607 @see: sqlCreate
608 '''
609
610 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))
611
612 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
613 '''
614 Return the sqlhelp object to create the table.
615
616 @param fields: which fields to put in the create. Defaults to all.
617 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
618 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
619 @type addCoastGuardFields: bool
620 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
621 @return: An object that can be used to generate a return
622 @rtype: sqlhelp.create
623 '''
624 if None == fields: fields = fieldList
625 import sqlhelp
626 c = sqlhelp.create('whalenotice',dbType=dbType)
627 c.addPrimaryKey()
628 if 'MessageID' in fields: c.addInt ('MessageID')
629 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator')
630 if 'UserID' in fields: c.addInt ('UserID')
631 if 'Spare' in fields: c.addInt ('Spare')
632 if 'dac' in fields: c.addInt ('dac')
633 if 'fid' in fields: c.addInt ('fid')
634 if 'efid' in fields: c.addInt ('efid')
635 if 'month' in fields: c.addInt ('month')
636 if 'day' in fields: c.addInt ('day')
637 if 'hour' in fields: c.addInt ('hour')
638 if 'min' in fields: c.addInt ('min')
639 if 'sec' in fields: c.addInt ('sec')
640 if 'stationid' in fields: c.addVarChar('stationid',7)
641 if dbType != 'postgres':
642 if 'longitude' in fields: c.addDecimal('longitude',8,5)
643 if dbType != 'postgres':
644 if 'latitude' in fields: c.addDecimal('latitude',8,5)
645 if 'timetoexpire' in fields: c.addInt ('timetoexpire')
646 if 'radius' in fields: c.addInt ('radius')
647 if 'heading' in fields: c.addInt ('heading')
648 if 'numWhales' in fields: c.addInt ('numWhales')
649
650 if addCoastGuardFields:
651
652
653
654
655
656 c.addVarChar('cg_r',15)
657 c.addInt('cg_sec')
658
659 c.addTimestamp('cg_timestamp')
660
661 if dbType == 'postgres':
662 c.addPostGIS('whale','POINT',2);
663
664 return c
665
666 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
667 '''
668 Return the SQL INSERT command for this message type
669 @param params: dictionary of values keyed by field name
670 @param outfile: file like object to print to.
671 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields
672 @return: sql create string
673 @rtype: str
674
675 @see: sqlCreate
676 '''
677 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))
678
679
680 -def sqlInsert(params,extraParams=None,dbType='postgres'):
681 '''
682 Give the SQL INSERT statement
683 @param params: dict keyed by field name of values
684 @param extraParams: any extra fields that you have created beyond the normal ais message fields
685 @rtype: sqlhelp.insert
686 @return: insert class instance
687 @todo: allow optional type checking of params?
688 @warning: this will take invalid keys happily and do what???
689 '''
690 import sqlhelp
691 i = sqlhelp.insert('whalenotice',dbType=dbType)
692
693 if dbType=='postgres':
694 finished = []
695 for key in params:
696 if key in finished:
697 continue
698
699 if key not in toPgFields and key not in fromPgFields:
700 if type(params[key])==Decimal: i.add(key,float(params[key]))
701 else: i.add(key,params[key])
702 else:
703 if key in fromPgFields:
704 val = params[key]
705
706 i.addPostGIS(key,val)
707 finished.append(key)
708 else:
709
710 pgName = toPgFields[key]
711
712 valStr=pgTypes[pgName]+'('
713 vals = []
714 for nonPgKey in fromPgFields[pgName]:
715 vals.append(str(params[nonPgKey]))
716 finished.append(nonPgKey)
717 valStr+=' '.join(vals)+')'
718 i.addPostGIS(pgName,valStr)
719 else:
720 for key in params:
721 if type(params[key])==Decimal: i.add(key,float(params[key]))
722 else: i.add(key,params[key])
723
724 if None != extraParams:
725 for key in extraParams:
726 i.add(key,extraParams[key])
727
728 return i
729
730
731
732
733
736 '''
737 Return the LaTeX definition table for this message type
738 @param outfile: file like object to print to.
739 @type outfile: file obj
740 @return: LaTeX table string via the outfile
741 @rtype: str
742
743 '''
744 o = outfile
745
746 o.write('''
747 \\begin{table}%[htb]
748 \\centering
749 \\begin{tabular}{|l|c|l|}
750 \\hline
751 Parameter & Number of bits & Description
752 \\\\ \\hline\\hline
753 MessageID & 6 & AIS message number. Must be 8 \\\\ \hline
754 RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline
755 UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline
756 Spare & 2 & Reserved for definition by a regional authority. \\\\ \hline
757 dac & 10 & Designated Area Code \\\\ \hline
758 fid & 6 & Functional Identifier \\\\ \hline
759 efid & 12 & extended functional identifier (dac+fid+id defines which message) \\\\ \hline
760 month & 4 & Time whale was last recorded. month 1..12 \\\\ \hline
761 day & 5 & Time whale was last recorded. day of the month 1..31 \\\\ \hline
762 hour & 5 & Time whale was last recorded. UTC hours 0..23 \\\\ \hline
763 min & 6 & Time whale was last recorded. minutes \\\\ \hline
764 sec & 6 & Time whale was last recorded. seconds \\\\ \hline
765 stationid & 42 & Character identifier of the station. Usually a number. \\\\ \hline
766 longitude & 28 & Where was the whale located. Center of the restricted speed zone. East West location \\\\ \hline
767 latitude & 27 & Where was the whale located. Center of the restricted speed zone. North South location \\\\ \hline
768 timetoexpire & 16 & Seconds from the time tag until this notice expires \\\\ \hline
769 radius & 16 & Distance of the required exclusion/slow zone \\\\ \hline
770 heading & 9 & Direction the whale is traveling if known \\\\ \hline
771 numWhales & 8 & Number of whales detected\\\\ \\hline \\hline
772 Total bits & 240 & Appears to take 2 slots with 184 pad bits to fill the last slot \\\\ \\hline
773 \\end{tabular}
774 \\caption{AIS message number 8: Endangered whale notification binary message.}
775 \\label{tab:whalenotice}
776 \\end{table}
777 ''')
778
779
780
781
782
783 -def textDefinitionTable(outfile=sys.stdout
784 ,delim='\t'
785 ):
786 '''
787 Return the text definition table for this message type
788 @param outfile: file like object to print to.
789 @type outfile: file obj
790 @return: text table string via the outfile
791 @rtype: str
792
793 '''
794 o = outfile
795 o.write('''Parameter'''+delim+'Number of bits'''+delim+'''Description
796 MessageID'''+delim+'''6'''+delim+'''AIS message number. Must be 8
797 RepeatIndicator'''+delim+'''2'''+delim+'''Indicated how many times a message has been repeated
798 UserID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI)
799 Spare'''+delim+'''2'''+delim+'''Reserved for definition by a regional authority.
800 dac'''+delim+'''10'''+delim+'''Designated Area Code
801 fid'''+delim+'''6'''+delim+'''Functional Identifier
802 efid'''+delim+'''12'''+delim+'''extended functional identifier (dac+fid+id defines which message)
803 month'''+delim+'''4'''+delim+'''Time whale was last recorded. month 1..12
804 day'''+delim+'''5'''+delim+'''Time whale was last recorded. day of the month 1..31
805 hour'''+delim+'''5'''+delim+'''Time whale was last recorded. UTC hours 0..23
806 min'''+delim+'''6'''+delim+'''Time whale was last recorded. minutes
807 sec'''+delim+'''6'''+delim+'''Time whale was last recorded. seconds
808 stationid'''+delim+'''42'''+delim+'''Character identifier of the station. Usually a number.
809 longitude'''+delim+'''28'''+delim+'''Where was the whale located. Center of the restricted speed zone. East West location
810 latitude'''+delim+'''27'''+delim+'''Where was the whale located. Center of the restricted speed zone. North South location
811 timetoexpire'''+delim+'''16'''+delim+'''Seconds from the time tag until this notice expires
812 radius'''+delim+'''16'''+delim+'''Distance of the required exclusion/slow zone
813 heading'''+delim+'''9'''+delim+'''Direction the whale is traveling if known
814 numWhales'''+delim+'''8'''+delim+'''Number of whales detected
815 Total bits'''+delim+'''240'''+delim+'''Appears to take 2 slots with 184 pad bits to fill the last slot''')
816
817
818
819
820
821 import unittest
823 '''Return a params file base on the testvalue tags.
824 @rtype: dict
825 @return: params based on testvalue tags
826 '''
827 params = {}
828 params['MessageID'] = 8
829 params['RepeatIndicator'] = 1
830 params['UserID'] = 1193046
831 params['Spare'] = 0
832 params['dac'] = 366
833 params['fid'] = 1
834 params['efid'] = 2
835 params['month'] = 2
836 params['day'] = 28
837 params['hour'] = 23
838 params['min'] = 45
839 params['sec'] = 58
840 params['stationid'] = 'A234567'
841 params['longitude'] = Decimal('-122.16328055555556')
842 params['latitude'] = Decimal('37.424458333333334')
843 params['timetoexpire'] = 1
844 params['radius'] = 5000
845 params['heading'] = 35
846 params['numWhales'] = 2
847
848 return params
849
851 '''Use testvalue tag text from each type to build test case the whalenotice message'''
853
854 params = testParams()
855 bits = encode(params)
856 r = decode(bits)
857
858
859 self.failUnlessEqual(r['MessageID'],params['MessageID'])
860 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
861 self.failUnlessEqual(r['UserID'],params['UserID'])
862 self.failUnlessEqual(r['Spare'],params['Spare'])
863 self.failUnlessEqual(r['dac'],params['dac'])
864 self.failUnlessEqual(r['fid'],params['fid'])
865 self.failUnlessEqual(r['efid'],params['efid'])
866 self.failUnlessEqual(r['month'],params['month'])
867 self.failUnlessEqual(r['day'],params['day'])
868 self.failUnlessEqual(r['hour'],params['hour'])
869 self.failUnlessEqual(r['min'],params['min'])
870 self.failUnlessEqual(r['sec'],params['sec'])
871 self.failUnlessEqual(r['stationid'],params['stationid'])
872 self.failUnlessAlmostEqual(r['longitude'],params['longitude'],5)
873 self.failUnlessAlmostEqual(r['latitude'],params['latitude'],5)
874 self.failUnlessEqual(r['timetoexpire'],params['timetoexpire'])
875 self.failUnlessEqual(r['radius'],params['radius'])
876 self.failUnlessEqual(r['heading'],params['heading'])
877 self.failUnlessEqual(r['numWhales'],params['numWhales'])
878
880 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
881 help='decode a "whalenotice" AIS message')
882 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
883 help='encode a "whalenotice" AIS message')
884 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
885 ,help='Field parameter value [default: %default]')
886 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
887 ,help='Field parameter value [default: %default]')
888 parser.add_option('--month-field', dest='monthField',metavar='uint',type='int'
889 ,help='Field parameter value [default: %default]')
890 parser.add_option('--day-field', dest='dayField',metavar='uint',type='int'
891 ,help='Field parameter value [default: %default]')
892 parser.add_option('--hour-field', dest='hourField',metavar='uint',type='int'
893 ,help='Field parameter value [default: %default]')
894 parser.add_option('--min-field', dest='minField',metavar='uint',type='int'
895 ,help='Field parameter value [default: %default]')
896 parser.add_option('--sec-field', dest='secField',metavar='uint',type='int'
897 ,help='Field parameter value [default: %default]')
898 parser.add_option('--stationid-field', dest='stationidField',default='@@@@@@@',metavar='aisstr6',type='string'
899 ,help='Field parameter value [default: %default]')
900 parser.add_option('--longitude-field', dest='longitudeField',default=Decimal('181'),metavar='decimal',type='string'
901 ,help='Field parameter value [default: %default]')
902 parser.add_option('--latitude-field', dest='latitudeField',default=Decimal('91'),metavar='decimal',type='string'
903 ,help='Field parameter value [default: %default]')
904 parser.add_option('--timetoexpire-field', dest='timetoexpireField',default=0,metavar='uint',type='int'
905 ,help='Field parameter value [default: %default]')
906 parser.add_option('--radius-field', dest='radiusField',default=65534,metavar='uint',type='int'
907 ,help='Field parameter value [default: %default]')
908 parser.add_option('--heading-field', dest='headingField',default=511,metavar='uint',type='int'
909 ,help='Field parameter value [default: %default]')
910 parser.add_option('--numWhales-field', dest='numWhalesField',default=0,metavar='uint',type='int'
911 ,help='Field parameter value [default: %default]')
912
913
914 if __name__=='__main__':
915
916 from optparse import OptionParser
917 parser = OptionParser(usage="%prog [options]",
918 version="%prog "+__version__)
919
920 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
921 help='run the documentation tests')
922 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
923 help='run the unit tests')
924 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
925 help='Make the test output verbose')
926
927
928
929 typeChoices = ('binary','nmeapayload','nmea')
930 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType'
931 ,default='nmeapayload'
932 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]')
933
934
935 outputChoices = ('std','html','csv','sql' , 'kml','kml-full')
936 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType'
937 ,default='std'
938 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]')
939
940 parser.add_option('-o','--output',dest='outputFileName',default=None,
941 help='Name of the python file to write [default: stdout]')
942
943 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append',
944 choices=fieldList,
945 help='Which fields to include in the output. Currently only for csv output [default: all]')
946
947 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true',
948 help='Print the field name for csv')
949
950 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true',
951 help='Print out an sql create command for the table.')
952
953 parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true',
954 help='Print a LaTeX table of the type')
955
956 parser.add_option('--text-table',dest='textDefinitionTable',default=False,action='store_true',
957 help='Print delimited table of the type (for Word table importing)')
958 parser.add_option('--delimt-text-table',dest='delimTextDefinitionTable',default='\t'
959 ,help='Delimiter for text table [default: \'%default\'](for Word table importing)')
960
961
962 dbChoices = ('sqlite','postgres')
963 parser.add_option('-D','--db-type',dest='dbType',default='postgres'
964 ,choices=dbChoices,type='choice'
965 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]')
966
967 addMsgOptions(parser)
968
969 (options,args) = parser.parse_args()
970 success=True
971
972 if options.doctest:
973 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
974 sys.argv= [sys.argv[0]]
975 if options.verbose: sys.argv.append('-v')
976 import doctest
977 numfail,numtests=doctest.testmod()
978 if numfail==0: print 'ok'
979 else:
980 print 'FAILED'
981 success=False
982
983 if not success: sys.exit('Something Failed')
984 del success
985
986 if options.unittest:
987 sys.argv = [sys.argv[0]]
988 if options.verbose: sys.argv.append('-v')
989 unittest.main()
990
991 outfile = sys.stdout
992 if None!=options.outputFileName:
993 outfile = file(options.outputFileName,'w')
994
995
996 if options.doEncode:
997
998 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
999 if None==options.UserIDField: parser.error("missing value for UserIDField")
1000 if None==options.monthField: parser.error("missing value for monthField")
1001 if None==options.dayField: parser.error("missing value for dayField")
1002 if None==options.hourField: parser.error("missing value for hourField")
1003 if None==options.minField: parser.error("missing value for minField")
1004 if None==options.secField: parser.error("missing value for secField")
1005 if None==options.stationidField: parser.error("missing value for stationidField")
1006 if None==options.longitudeField: parser.error("missing value for longitudeField")
1007 if None==options.latitudeField: parser.error("missing value for latitudeField")
1008 if None==options.timetoexpireField: parser.error("missing value for timetoexpireField")
1009 if None==options.radiusField: parser.error("missing value for radiusField")
1010 if None==options.headingField: parser.error("missing value for headingField")
1011 if None==options.numWhalesField: parser.error("missing value for numWhalesField")
1012 msgDict={
1013 'MessageID': '8',
1014 'RepeatIndicator': options.RepeatIndicatorField,
1015 'UserID': options.UserIDField,
1016 'Spare': '0',
1017 'dac': '366',
1018 'fid': '1',
1019 'efid': '2',
1020 'month': options.monthField,
1021 'day': options.dayField,
1022 'hour': options.hourField,
1023 'min': options.minField,
1024 'sec': options.secField,
1025 'stationid': options.stationidField,
1026 'longitude': options.longitudeField,
1027 'latitude': options.latitudeField,
1028 'timetoexpire': options.timetoexpireField,
1029 'radius': options.radiusField,
1030 'heading': options.headingField,
1031 'numWhales': options.numWhalesField,
1032 }
1033
1034 bits = encode(msgDict)
1035 if 'binary'==options.ioType: print str(bits)
1036 elif 'nmeapayload'==options.ioType:
1037
1038 print "bitLen",len(bits)
1039 bitLen=len(bits)
1040 if bitLen%6!=0:
1041 bits = bits + BitVector(size=(6 - (bitLen%6)))
1042 print "result:",binary.bitvectoais6(bits)[0]
1043
1044
1045
1046 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability")
1047 else: sys.exit('ERROR: unknown ioType. Help!')
1048
1049
1050 if options.sqlCreate:
1051 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType)
1052
1053 if options.latexDefinitionTable:
1054 latexDefinitionTable(outfile)
1055
1056
1057 if options.textDefinitionTable:
1058 textDefinitionTable(outfile,options.delimTextDefinitionTable)
1059
1060 if options.printCsvfieldList:
1061
1062 if None == options.fieldList: options.fieldList = fieldList
1063 import StringIO
1064 buf = StringIO.StringIO()
1065 for field in options.fieldList:
1066 buf.write(field+',')
1067 result = buf.getvalue()
1068 if result[-1] == ',': print result[:-1]
1069 else: print result
1070
1071 if options.doDecode:
1072 for msg in args:
1073 bv = None
1074
1075 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'):
1076
1077
1078 bv = binary.ais6tobitvec(msg.split(',')[5])
1079 else:
1080
1081 binaryMsg=True
1082 for c in msg:
1083 if c not in ('0','1'):
1084 binaryMsg=False
1085 break
1086 if binaryMsg:
1087 bv = BitVector(bitstring=msg)
1088 else:
1089 bv = binary.ais6tobitvec(msg)
1090
1091 printFields(decode(bv)
1092 ,out=outfile
1093 ,format=options.outputType
1094 ,fieldList=options.fieldList
1095 ,dbType=options.dbType
1096 )
1097