1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2008-01-16 $'.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 'month',
57 'day',
58 'hour',
59 'min',
60 'stationid',
61 'waterlevel',
62 'datum',
63 'sigma',
64 'source',
65 )
66
67 fieldListPostgres = (
68 'MessageID',
69 'RepeatIndicator',
70 'UserID',
71 'Spare',
72 'dac',
73 'fid',
74 'month',
75 'day',
76 'hour',
77 'min',
78 'stationid',
79 'waterlevel',
80 'datum',
81 'sigma',
82 'source',
83 )
84
85 toPgFields = {
86 }
87 '''
88 Go to the Postgis field names from the straight field name
89 '''
90
91 fromPgFields = {
92 }
93 '''
94 Go from the Postgis field names to the straight field name
95 '''
96
97 pgTypes = {
98 }
99 '''
100 Lookup table for each postgis field name to get its type.
101 '''
102
103 -def encode(params, validate=False):
104 '''Create a waterlevel binary message payload to pack into an AIS Msg waterlevel.
105
106 Fields in params:
107 - MessageID(uint): AIS message number. Must be 8 (field automatically set to "8")
108 - RepeatIndicator(uint): Indicated how many times a message has been repeated
109 - UserID(uint): Unique ship identification number (MMSI)
110 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0")
111 - dac(uint): Designated Area Code (field automatically set to "366")
112 - fid(uint): Functional Identifier (field automatically set to "63")
113 - month(uint): Time the measurement represents month 1..12
114 - day(uint): Time the measurement represents day of the month 1..31
115 - hour(uint): Time the measurement represents UTC hours 0..23
116 - min(uint): Time the measurement represents minutes
117 - stationid(aisstr6): Character identifier of the station. Usually a number.
118 - waterlevel(int): Water level in centimeters
119 - datum(uint): What reference datum applies to the value
120 - sigma(uint): Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma?
121 - source(uint): How the water level was derived
122 @param params: Dictionary of field names/values. Throws a ValueError exception if required is missing
123 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
124 @rtype: BitVector
125 @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8
126 @note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits.
127 '''
128
129 bvList = []
130 bvList.append(binary.setBitVectorSize(BitVector(intVal=8),6))
131 if 'RepeatIndicator' in params:
132 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2))
133 else:
134 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
135 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30))
136 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
137 bvList.append(binary.setBitVectorSize(BitVector(intVal=366),10))
138 bvList.append(binary.setBitVectorSize(BitVector(intVal=63),6))
139 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['month']),4))
140 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['day']),5))
141 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['hour']),5))
142 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['min']),6))
143 if 'stationid' in params:
144 bvList.append(aisstring.encode(params['stationid'],42))
145 else:
146 bvList.append(aisstring.encode('@@@@@@@',42))
147 if 'waterlevel' in params:
148 bvList.append(binary.bvFromSignedInt(params['waterlevel'],16))
149 else:
150 bvList.append(binary.bvFromSignedInt(-32768,16))
151 if 'datum' in params:
152 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['datum']),5))
153 else:
154 bvList.append(binary.setBitVectorSize(BitVector(intVal=31),5))
155 if 'sigma' in params:
156 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['sigma']),7))
157 else:
158 bvList.append(binary.setBitVectorSize(BitVector(intVal=127),7))
159 if 'source' in params:
160 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['source']),3))
161 else:
162 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),3))
163
164 return binary.joinBV(bvList)
165
166 -def decode(bv, validate=False):
167 '''Unpack a waterlevel message
168
169 Fields in params:
170 - MessageID(uint): AIS message number. Must be 8 (field automatically set to "8")
171 - RepeatIndicator(uint): Indicated how many times a message has been repeated
172 - UserID(uint): Unique ship identification number (MMSI)
173 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0")
174 - dac(uint): Designated Area Code (field automatically set to "366")
175 - fid(uint): Functional Identifier (field automatically set to "63")
176 - month(uint): Time the measurement represents month 1..12
177 - day(uint): Time the measurement represents day of the month 1..31
178 - hour(uint): Time the measurement represents UTC hours 0..23
179 - min(uint): Time the measurement represents minutes
180 - stationid(aisstr6): Character identifier of the station. Usually a number.
181 - waterlevel(int): Water level in centimeters
182 - datum(uint): What reference datum applies to the value
183 - sigma(uint): Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma?
184 - source(uint): How the water level was derived
185 @type bv: BitVector
186 @param bv: Bits defining a message
187 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
188 @rtype: dict
189 @return: params
190 '''
191
192
193
194
195 r = {}
196 r['MessageID']=8
197 r['RepeatIndicator']=int(bv[6:8])
198 r['UserID']=int(bv[8:38])
199 r['Spare']=0
200 r['dac']=366
201 r['fid']=63
202 r['month']=int(bv[56:60])
203 r['day']=int(bv[60:65])
204 r['hour']=int(bv[65:70])
205 r['min']=int(bv[70:76])
206 r['stationid']=aisstring.decode(bv[76:118])
207 r['waterlevel']=binary.signedIntFromBV(bv[118:134])
208 r['datum']=int(bv[134:139])
209 r['sigma']=int(bv[139:146])
210 r['source']=int(bv[146:149])
211 return r
212
215
218
221
224
227
230
232 return int(bv[56:60])
233
235 return int(bv[60:65])
236
238 return int(bv[65:70])
239
241 return int(bv[70:76])
242
245
248
250 return int(bv[134:139])
251
253 return int(bv[139:146])
254
256 return int(bv[146:149])
257
258
260 out.write("<h3>waterlevel</h3>\n")
261 out.write("<table border=\"1\">\n")
262 out.write("<tr bgcolor=\"orange\">\n")
263 out.write("<th align=\"left\">Field Name</th>\n")
264 out.write("<th align=\"left\">Type</th>\n")
265 out.write("<th align=\"left\">Value</th>\n")
266 out.write("<th align=\"left\">Value in Lookup Table</th>\n")
267 out.write("<th align=\"left\">Units</th>\n")
268 out.write("\n")
269 out.write("<tr>\n")
270 out.write("<td>MessageID</td>\n")
271 out.write("<td>uint</td>\n")
272 if 'MessageID' in params:
273 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
274 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
275 out.write("</tr>\n")
276 out.write("\n")
277 out.write("<tr>\n")
278 out.write("<td>RepeatIndicator</td>\n")
279 out.write("<td>uint</td>\n")
280 if 'RepeatIndicator' in params:
281 out.write(" <td>"+str(params['RepeatIndicator'])+"</td>\n")
282 if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut:
283 out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>")
284 else:
285 out.write("<td><i>Missing LUT entry</i></td>")
286 out.write("</tr>\n")
287 out.write("\n")
288 out.write("<tr>\n")
289 out.write("<td>UserID</td>\n")
290 out.write("<td>uint</td>\n")
291 if 'UserID' in params:
292 out.write(" <td>"+str(params['UserID'])+"</td>\n")
293 out.write(" <td>"+str(params['UserID'])+"</td>\n")
294 out.write("</tr>\n")
295 out.write("\n")
296 out.write("<tr>\n")
297 out.write("<td>Spare</td>\n")
298 out.write("<td>uint</td>\n")
299 if 'Spare' in params:
300 out.write(" <td>"+str(params['Spare'])+"</td>\n")
301 out.write(" <td>"+str(params['Spare'])+"</td>\n")
302 out.write("</tr>\n")
303 out.write("\n")
304 out.write("<tr>\n")
305 out.write("<td>dac</td>\n")
306 out.write("<td>uint</td>\n")
307 if 'dac' in params:
308 out.write(" <td>"+str(params['dac'])+"</td>\n")
309 out.write(" <td>"+str(params['dac'])+"</td>\n")
310 out.write("</tr>\n")
311 out.write("\n")
312 out.write("<tr>\n")
313 out.write("<td>fid</td>\n")
314 out.write("<td>uint</td>\n")
315 if 'fid' in params:
316 out.write(" <td>"+str(params['fid'])+"</td>\n")
317 out.write(" <td>"+str(params['fid'])+"</td>\n")
318 out.write("</tr>\n")
319 out.write("\n")
320 out.write("<tr>\n")
321 out.write("<td>month</td>\n")
322 out.write("<td>uint</td>\n")
323 if 'month' in params:
324 out.write(" <td>"+str(params['month'])+"</td>\n")
325 out.write(" <td>"+str(params['month'])+"</td>\n")
326 out.write("</tr>\n")
327 out.write("\n")
328 out.write("<tr>\n")
329 out.write("<td>day</td>\n")
330 out.write("<td>uint</td>\n")
331 if 'day' in params:
332 out.write(" <td>"+str(params['day'])+"</td>\n")
333 out.write(" <td>"+str(params['day'])+"</td>\n")
334 out.write("</tr>\n")
335 out.write("\n")
336 out.write("<tr>\n")
337 out.write("<td>hour</td>\n")
338 out.write("<td>uint</td>\n")
339 if 'hour' in params:
340 out.write(" <td>"+str(params['hour'])+"</td>\n")
341 out.write(" <td>"+str(params['hour'])+"</td>\n")
342 out.write("</tr>\n")
343 out.write("\n")
344 out.write("<tr>\n")
345 out.write("<td>min</td>\n")
346 out.write("<td>uint</td>\n")
347 if 'min' in params:
348 out.write(" <td>"+str(params['min'])+"</td>\n")
349 out.write(" <td>"+str(params['min'])+"</td>\n")
350 out.write("</tr>\n")
351 out.write("\n")
352 out.write("<tr>\n")
353 out.write("<td>stationid</td>\n")
354 out.write("<td>aisstr6</td>\n")
355 if 'stationid' in params:
356 out.write(" <td>"+str(params['stationid'])+"</td>\n")
357 out.write(" <td>"+str(params['stationid'])+"</td>\n")
358 out.write("</tr>\n")
359 out.write("\n")
360 out.write("<tr>\n")
361 out.write("<td>waterlevel</td>\n")
362 out.write("<td>int</td>\n")
363 if 'waterlevel' in params:
364 out.write(" <td>"+str(params['waterlevel'])+"</td>\n")
365 out.write(" <td>"+str(params['waterlevel'])+"</td>\n")
366 out.write("<td>cm</td>\n")
367 out.write("</tr>\n")
368 out.write("\n")
369 out.write("<tr>\n")
370 out.write("<td>datum</td>\n")
371 out.write("<td>uint</td>\n")
372 if 'datum' in params:
373 out.write(" <td>"+str(params['datum'])+"</td>\n")
374 if str(params['datum']) in datumDecodeLut:
375 out.write("<td>"+datumDecodeLut[str(params['datum'])]+"</td>")
376 else:
377 out.write("<td><i>Missing LUT entry</i></td>")
378 out.write("</tr>\n")
379 out.write("\n")
380 out.write("<tr>\n")
381 out.write("<td>sigma</td>\n")
382 out.write("<td>uint</td>\n")
383 if 'sigma' in params:
384 out.write(" <td>"+str(params['sigma'])+"</td>\n")
385 out.write(" <td>"+str(params['sigma'])+"</td>\n")
386 out.write("<td>cm</td>\n")
387 out.write("</tr>\n")
388 out.write("\n")
389 out.write("<tr>\n")
390 out.write("<td>source</td>\n")
391 out.write("<td>uint</td>\n")
392 if 'source' in params:
393 out.write(" <td>"+str(params['source'])+"</td>\n")
394 if str(params['source']) in sourceDecodeLut:
395 out.write("<td>"+sourceDecodeLut[str(params['source'])]+"</td>")
396 else:
397 out.write("<td><i>Missing LUT entry</i></td>")
398 out.write("</tr>\n")
399 out.write("</table>\n")
400
401 -def printFields(params, out=sys.stdout, format='std', fieldList=None, dbType='postgres'):
402 '''Print a waterlevel message to stdout.
403
404 Fields in params:
405 - MessageID(uint): AIS message number. Must be 8 (field automatically set to "8")
406 - RepeatIndicator(uint): Indicated how many times a message has been repeated
407 - UserID(uint): Unique ship identification number (MMSI)
408 - Spare(uint): Reserved for definition by a regional authority. (field automatically set to "0")
409 - dac(uint): Designated Area Code (field automatically set to "366")
410 - fid(uint): Functional Identifier (field automatically set to "63")
411 - month(uint): Time the measurement represents month 1..12
412 - day(uint): Time the measurement represents day of the month 1..31
413 - hour(uint): Time the measurement represents UTC hours 0..23
414 - min(uint): Time the measurement represents minutes
415 - stationid(aisstr6): Character identifier of the station. Usually a number.
416 - waterlevel(int): Water level in centimeters
417 - datum(uint): What reference datum applies to the value
418 - sigma(uint): Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma?
419 - source(uint): How the water level was derived
420 @param params: Dictionary of field names/values.
421 @param out: File like object to write to
422 @rtype: stdout
423 @return: text to out
424 '''
425
426 if 'std'==format:
427 out.write("waterlevel:\n")
428 if 'MessageID' in params: out.write(" MessageID: "+str(params['MessageID'])+"\n")
429 if 'RepeatIndicator' in params: out.write(" RepeatIndicator: "+str(params['RepeatIndicator'])+"\n")
430 if 'UserID' in params: out.write(" UserID: "+str(params['UserID'])+"\n")
431 if 'Spare' in params: out.write(" Spare: "+str(params['Spare'])+"\n")
432 if 'dac' in params: out.write(" dac: "+str(params['dac'])+"\n")
433 if 'fid' in params: out.write(" fid: "+str(params['fid'])+"\n")
434 if 'month' in params: out.write(" month: "+str(params['month'])+"\n")
435 if 'day' in params: out.write(" day: "+str(params['day'])+"\n")
436 if 'hour' in params: out.write(" hour: "+str(params['hour'])+"\n")
437 if 'min' in params: out.write(" min: "+str(params['min'])+"\n")
438 if 'stationid' in params: out.write(" stationid: "+str(params['stationid'])+"\n")
439 if 'waterlevel' in params: out.write(" waterlevel: "+str(params['waterlevel'])+"\n")
440 if 'datum' in params: out.write(" datum: "+str(params['datum'])+"\n")
441 if 'sigma' in params: out.write(" sigma: "+str(params['sigma'])+"\n")
442 if 'source' in params: out.write(" source: "+str(params['source'])+"\n")
443 elif 'csv'==format:
444 if None == options.fieldList:
445 options.fieldList = fieldList
446 needComma = False;
447 for field in fieldList:
448 if needComma: out.write(',')
449 needComma = True
450 if field in params:
451 out.write(str(params[field]))
452
453 out.write("\n")
454 elif 'html'==format:
455 printHtml(params,out)
456 elif 'sql'==format:
457 sqlInsertStr(params,out,dbType=dbType)
458 else:
459 print "ERROR: unknown format:",format
460 assert False
461
462 return
463
464 RepeatIndicatorEncodeLut = {
465 'default':'0',
466 'do not repeat any more':'3',
467 }
468
469 RepeatIndicatorDecodeLut = {
470 '0':'default',
471 '3':'do not repeat any more',
472 }
473
474 datumEncodeLut = {
475 'MLLW':'0',
476 'IGLD-85':'1',
477 'WaterDepth':'2',
478 'STND':'3',
479 'MHW':'4',
480 'MSL':'5',
481 'NGVD':'6',
482 'NAVD':'7',
483 'WGS-84':'8',
484 'LAT':'9',
485 }
486
487 datumDecodeLut = {
488 '0':'MLLW',
489 '1':'IGLD-85',
490 '2':'WaterDepth',
491 '3':'STND',
492 '4':'MHW',
493 '5':'MSL',
494 '6':'NGVD',
495 '7':'NAVD',
496 '8':'WGS-84',
497 '9':'LAT',
498 }
499
500 sourceEncodeLut = {
501 'No data':'0',
502 'Realtime sensor data':'1',
503 'Raw realtime sensor data - no validation':'2',
504 'Predicted water level - tidal data generated by using harmonic analysis':'3',
505 'Forecast water level - tidal data generated by use of a hydrodynamic model':'4',
506 'Reserved':'5',
507 'Reserved':'6',
508 'Reserved':'7',
509 }
510
511 sourceDecodeLut = {
512 '0':'No data',
513 '1':'Realtime sensor data',
514 '2':'Raw realtime sensor data - no validation',
515 '3':'Predicted water level - tidal data generated by using harmonic analysis',
516 '4':'Forecast water level - tidal data generated by use of a hydrodynamic model',
517 '5':'Reserved',
518 '6':'Reserved',
519 '7':'Reserved',
520 }
521
522
523
524
525
526 dbTableName='waterlevel'
527 'Database table name'
528
529 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None
530 ,addCoastGuardFields=True
531 ,dbType='postgres'
532 ):
533 '''
534 Return the SQL CREATE command for this message type
535 @param outfile: file like object to print to.
536 @param fields: which fields to put in the create. Defaults to all.
537 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
538 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
539 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
540 @type addCoastGuardFields: bool
541 @return: sql create string
542 @rtype: str
543
544 @see: sqlCreate
545 '''
546
547 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))
548
549 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
550 '''
551 Return the sqlhelp object to create the table.
552
553 @param fields: which fields to put in the create. Defaults to all.
554 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
555 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
556 @type addCoastGuardFields: bool
557 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
558 @return: An object that can be used to generate a return
559 @rtype: sqlhelp.create
560 '''
561 if None == fields: fields = fieldList
562 import sqlhelp
563 c = sqlhelp.create('waterlevel',dbType=dbType)
564 c.addPrimaryKey()
565 if 'MessageID' in fields: c.addInt ('MessageID')
566 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator')
567 if 'UserID' in fields: c.addInt ('UserID')
568 if 'Spare' in fields: c.addInt ('Spare')
569 if 'dac' in fields: c.addInt ('dac')
570 if 'fid' in fields: c.addInt ('fid')
571 if 'month' in fields: c.addInt ('month')
572 if 'day' in fields: c.addInt ('day')
573 if 'hour' in fields: c.addInt ('hour')
574 if 'min' in fields: c.addInt ('min')
575 if 'stationid' in fields: c.addVarChar('stationid',7)
576 if 'waterlevel' in fields: c.addInt ('waterlevel')
577 if 'datum' in fields: c.addInt ('datum')
578 if 'sigma' in fields: c.addInt ('sigma')
579 if 'source' in fields: c.addInt ('source')
580
581 if addCoastGuardFields:
582
583
584
585
586
587 c.addVarChar('cg_r',15)
588 c.addInt('cg_sec')
589
590 c.addTimestamp('cg_timestamp')
591
592 return c
593
594 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
595 '''
596 Return the SQL INSERT command for this message type
597 @param params: dictionary of values keyed by field name
598 @param outfile: file like object to print to.
599 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields
600 @return: sql create string
601 @rtype: str
602
603 @see: sqlCreate
604 '''
605 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))
606
607
608 -def sqlInsert(params,extraParams=None,dbType='postgres'):
609 '''
610 Give the SQL INSERT statement
611 @param params: dict keyed by field name of values
612 @param extraParams: any extra fields that you have created beyond the normal ais message fields
613 @rtype: sqlhelp.insert
614 @return: insert class instance
615 @todo: allow optional type checking of params?
616 @warning: this will take invalid keys happily and do what???
617 '''
618 import sqlhelp
619 i = sqlhelp.insert('waterlevel',dbType=dbType)
620
621 if dbType=='postgres':
622 finished = []
623 for key in params:
624 if key in finished:
625 continue
626
627 if key not in toPgFields and key not in fromPgFields:
628 if type(params[key])==Decimal: i.add(key,float(params[key]))
629 else: i.add(key,params[key])
630 else:
631 if key in fromPgFields:
632 val = params[key]
633
634 i.addPostGIS(key,val)
635 finished.append(key)
636 else:
637
638 pgName = toPgFields[key]
639
640 valStr=pgTypes[pgName]+'('
641 vals = []
642 for nonPgKey in fromPgFields[pgName]:
643 vals.append(str(params[nonPgKey]))
644 finished.append(nonPgKey)
645 valStr+=' '.join(vals)+')'
646 i.addPostGIS(pgName,valStr)
647 else:
648 for key in params:
649 if type(params[key])==Decimal: i.add(key,float(params[key]))
650 else: i.add(key,params[key])
651
652 if None != extraParams:
653 for key in extraParams:
654 i.add(key,extraParams[key])
655
656 return i
657
658
659
660
661
664 '''
665 Return the LaTeX definition table for this message type
666 @param outfile: file like object to print to.
667 @type outfile: file obj
668 @return: LaTeX table string via the outfile
669 @rtype: str
670
671 '''
672 o = outfile
673
674 o.write('''
675 \\begin{table}%[htb]
676 \\centering
677 \\begin{tabular}{|l|c|l|}
678 \\hline
679 Parameter & Number of bits & Description
680 \\\\ \\hline\\hline
681 MessageID & 6 & AIS message number. Must be 8 \\\\ \hline
682 RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline
683 UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline
684 Spare & 2 & Reserved for definition by a regional authority. \\\\ \hline
685 dac & 10 & Designated Area Code \\\\ \hline
686 fid & 6 & Functional Identifier \\\\ \hline
687 month & 4 & Time the measurement represents month 1..12 \\\\ \hline
688 day & 5 & Time the measurement represents day of the month 1..31 \\\\ \hline
689 hour & 5 & Time the measurement represents UTC hours 0..23 \\\\ \hline
690 min & 6 & Time the measurement represents minutes \\\\ \hline
691 stationid & 42 & Character identifier of the station. Usually a number. \\\\ \hline
692 waterlevel & 16 & Water level in centimeters \\\\ \hline
693 datum & 5 & What reference datum applies to the value \\\\ \hline
694 sigma & 7 & Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma? \\\\ \hline
695 source & 3 & How the water level was derived\\\\ \\hline \\hline
696 Total bits & 149 & Appears to take 1 slot with 19 pad bits to fill the last slot \\\\ \\hline
697 \\end{tabular}
698 \\caption{AIS message number 8: Water level report. This is }
699 \\label{tab:waterlevel}
700 \\end{table}
701 ''')
702
703
704
705
706
707 -def textDefinitionTable(outfile=sys.stdout
708 ,delim='\t'
709 ):
710 '''
711 Return the text definition table for this message type
712 @param outfile: file like object to print to.
713 @type outfile: file obj
714 @return: text table string via the outfile
715 @rtype: str
716
717 '''
718 o = outfile
719 o.write('''Parameter'''+delim+'Number of bits'''+delim+'''Description
720 MessageID'''+delim+'''6'''+delim+'''AIS message number. Must be 8
721 RepeatIndicator'''+delim+'''2'''+delim+'''Indicated how many times a message has been repeated
722 UserID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI)
723 Spare'''+delim+'''2'''+delim+'''Reserved for definition by a regional authority.
724 dac'''+delim+'''10'''+delim+'''Designated Area Code
725 fid'''+delim+'''6'''+delim+'''Functional Identifier
726 month'''+delim+'''4'''+delim+'''Time the measurement represents month 1..12
727 day'''+delim+'''5'''+delim+'''Time the measurement represents day of the month 1..31
728 hour'''+delim+'''5'''+delim+'''Time the measurement represents UTC hours 0..23
729 min'''+delim+'''6'''+delim+'''Time the measurement represents minutes
730 stationid'''+delim+'''42'''+delim+'''Character identifier of the station. Usually a number.
731 waterlevel'''+delim+'''16'''+delim+'''Water level in centimeters
732 datum'''+delim+'''5'''+delim+'''What reference datum applies to the value
733 sigma'''+delim+'''7'''+delim+'''Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma?
734 source'''+delim+'''3'''+delim+'''How the water level was derived
735 Total bits'''+delim+'''149'''+delim+'''Appears to take 1 slot with 19 pad bits to fill the last slot''')
736
737
738
739
740
741 import unittest
743 '''Return a params file base on the testvalue tags.
744 @rtype: dict
745 @return: params based on testvalue tags
746 '''
747 params = {}
748 params['MessageID'] = 8
749 params['RepeatIndicator'] = 1
750 params['UserID'] = 1193046
751 params['Spare'] = 0
752 params['dac'] = 366
753 params['fid'] = 63
754 params['month'] = 2
755 params['day'] = 28
756 params['hour'] = 23
757 params['min'] = 45
758 params['stationid'] = 'A234567'
759 params['waterlevel'] = -97
760 params['datum'] = 0
761 params['sigma'] = 97
762 params['source'] = 2
763
764 return params
765
767 '''Use testvalue tag text from each type to build test case the waterlevel message'''
769
770 params = testParams()
771 bits = encode(params)
772 r = decode(bits)
773
774
775 self.failUnlessEqual(r['MessageID'],params['MessageID'])
776 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
777 self.failUnlessEqual(r['UserID'],params['UserID'])
778 self.failUnlessEqual(r['Spare'],params['Spare'])
779 self.failUnlessEqual(r['dac'],params['dac'])
780 self.failUnlessEqual(r['fid'],params['fid'])
781 self.failUnlessEqual(r['month'],params['month'])
782 self.failUnlessEqual(r['day'],params['day'])
783 self.failUnlessEqual(r['hour'],params['hour'])
784 self.failUnlessEqual(r['min'],params['min'])
785 self.failUnlessEqual(r['stationid'],params['stationid'])
786 self.failUnlessEqual(r['waterlevel'],params['waterlevel'])
787 self.failUnlessEqual(r['datum'],params['datum'])
788 self.failUnlessEqual(r['sigma'],params['sigma'])
789 self.failUnlessEqual(r['source'],params['source'])
790
792 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
793 help='decode a "waterlevel" AIS message')
794 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
795 help='encode a "waterlevel" AIS message')
796 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
797 ,help='Field parameter value [default: %default]')
798 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
799 ,help='Field parameter value [default: %default]')
800 parser.add_option('--month-field', dest='monthField',metavar='uint',type='int'
801 ,help='Field parameter value [default: %default]')
802 parser.add_option('--day-field', dest='dayField',metavar='uint',type='int'
803 ,help='Field parameter value [default: %default]')
804 parser.add_option('--hour-field', dest='hourField',metavar='uint',type='int'
805 ,help='Field parameter value [default: %default]')
806 parser.add_option('--min-field', dest='minField',metavar='uint',type='int'
807 ,help='Field parameter value [default: %default]')
808 parser.add_option('--stationid-field', dest='stationidField',default='@@@@@@@',metavar='aisstr6',type='string'
809 ,help='Field parameter value [default: %default]')
810 parser.add_option('--waterlevel-field', dest='waterlevelField',default=-32768,metavar='int',type='int'
811 ,help='Field parameter value [default: %default]')
812 parser.add_option('--datum-field', dest='datumField',default=31,metavar='uint',type='int'
813 ,help='Field parameter value [default: %default]')
814 parser.add_option('--sigma-field', dest='sigmaField',default=127,metavar='uint',type='int'
815 ,help='Field parameter value [default: %default]')
816 parser.add_option('--source-field', dest='sourceField',default=0,metavar='uint',type='int'
817 ,help='Field parameter value [default: %default]')
818
819
820 if __name__=='__main__':
821
822 from optparse import OptionParser
823 parser = OptionParser(usage="%prog [options]",
824 version="%prog "+__version__)
825
826 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
827 help='run the documentation tests')
828 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
829 help='run the unit tests')
830 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
831 help='Make the test output verbose')
832
833
834
835 typeChoices = ('binary','nmeapayload','nmea')
836 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType'
837 ,default='nmeapayload'
838 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]')
839
840
841 outputChoices = ('std','html','csv','sql' )
842 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType'
843 ,default='std'
844 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]')
845
846 parser.add_option('-o','--output',dest='outputFileName',default=None,
847 help='Name of the python file to write [default: stdout]')
848
849 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append',
850 choices=fieldList,
851 help='Which fields to include in the output. Currently only for csv output [default: all]')
852
853 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true',
854 help='Print the field name for csv')
855
856 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true',
857 help='Print out an sql create command for the table.')
858
859 parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true',
860 help='Print a LaTeX table of the type')
861
862 parser.add_option('--text-table',dest='textDefinitionTable',default=False,action='store_true',
863 help='Print delimited table of the type (for Word table importing)')
864 parser.add_option('--delimt-text-table',dest='delimTextDefinitionTable',default='\t'
865 ,help='Delimiter for text table [default: \'%default\'](for Word table importing)')
866
867
868 dbChoices = ('sqlite','postgres')
869 parser.add_option('-D','--db-type',dest='dbType',default='postgres'
870 ,choices=dbChoices,type='choice'
871 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]')
872
873 addMsgOptions(parser)
874
875 (options,args) = parser.parse_args()
876 success=True
877
878 if options.doctest:
879 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
880 sys.argv= [sys.argv[0]]
881 if options.verbose: sys.argv.append('-v')
882 import doctest
883 numfail,numtests=doctest.testmod()
884 if numfail==0: print 'ok'
885 else:
886 print 'FAILED'
887 success=False
888
889 if not success: sys.exit('Something Failed')
890 del success
891
892 if options.unittest:
893 sys.argv = [sys.argv[0]]
894 if options.verbose: sys.argv.append('-v')
895 unittest.main()
896
897 outfile = sys.stdout
898 if None!=options.outputFileName:
899 outfile = file(options.outputFileName,'w')
900
901
902 if options.doEncode:
903
904 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
905 if None==options.UserIDField: parser.error("missing value for UserIDField")
906 if None==options.monthField: parser.error("missing value for monthField")
907 if None==options.dayField: parser.error("missing value for dayField")
908 if None==options.hourField: parser.error("missing value for hourField")
909 if None==options.minField: parser.error("missing value for minField")
910 if None==options.stationidField: parser.error("missing value for stationidField")
911 if None==options.waterlevelField: parser.error("missing value for waterlevelField")
912 if None==options.datumField: parser.error("missing value for datumField")
913 if None==options.sigmaField: parser.error("missing value for sigmaField")
914 if None==options.sourceField: parser.error("missing value for sourceField")
915 msgDict={
916 'MessageID': '8',
917 'RepeatIndicator': options.RepeatIndicatorField,
918 'UserID': options.UserIDField,
919 'Spare': '0',
920 'dac': '366',
921 'fid': '63',
922 'month': options.monthField,
923 'day': options.dayField,
924 'hour': options.hourField,
925 'min': options.minField,
926 'stationid': options.stationidField,
927 'waterlevel': options.waterlevelField,
928 'datum': options.datumField,
929 'sigma': options.sigmaField,
930 'source': options.sourceField,
931 }
932
933 bits = encode(msgDict)
934 if 'binary'==options.ioType: print str(bits)
935 elif 'nmeapayload'==options.ioType:
936
937 print "bitLen",len(bits)
938 bitLen=len(bits)
939 if bitLen%6!=0:
940 bits = bits + BitVector(size=(6 - (bitLen%6)))
941 print "result:",binary.bitvectoais6(bits)[0]
942
943
944
945 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability")
946 else: sys.exit('ERROR: unknown ioType. Help!')
947
948
949 if options.sqlCreate:
950 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType)
951
952 if options.latexDefinitionTable:
953 latexDefinitionTable(outfile)
954
955
956 if options.textDefinitionTable:
957 textDefinitionTable(outfile,options.delimTextDefinitionTable)
958
959 if options.printCsvfieldList:
960
961 if None == options.fieldList: options.fieldList = fieldList
962 import StringIO
963 buf = StringIO.StringIO()
964 for field in options.fieldList:
965 buf.write(field+',')
966 result = buf.getvalue()
967 if result[-1] == ',': print result[:-1]
968 else: print result
969
970 if options.doDecode:
971 if len(args)==0: args = sys.stdin
972 for msg in args:
973 bv = None
974
975 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'):
976
977
978 bv = binary.ais6tobitvec(msg.split(',')[5])
979 else:
980
981 binaryMsg=True
982 for c in msg:
983 if c not in ('0','1'):
984 binaryMsg=False
985 break
986 if binaryMsg:
987 bv = BitVector(bitstring=msg)
988 else:
989 bv = binary.ais6tobitvec(msg)
990
991 printFields(decode(bv)
992 ,out=outfile
993 ,format=options.outputType
994 ,fieldList=options.fieldList
995 ,dbType=options.dbType
996 )
997