1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2008-01-31 $'.split()[1]
5 __author__ = 'xmlbinmsg'
6
7 __doc__='''
8
9 Autogenerated python functions to serialize/deserialize binary messages.
10
11 Generated by: ../scripts/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 'Pool':'10',
486 'Gauge':'11',
487 'Unknown/Unavailable':'31',
488 }
489
490 datumDecodeLut = {
491 '0':'MLLW',
492 '1':'IGLD-85',
493 '2':'WaterDepth',
494 '3':'STND',
495 '4':'MHW',
496 '5':'MSL',
497 '6':'NGVD',
498 '7':'NAVD',
499 '8':'WGS-84',
500 '9':'LAT',
501 '10':'Pool',
502 '11':'Gauge',
503 '31':'Unknown/Unavailable',
504 }
505
506 sourceEncodeLut = {
507 'No data':'0',
508 'Realtime sensor data':'1',
509 'Raw realtime sensor data - no validation':'2',
510 'Predicted water level - tidal data generated by using harmonic analysis':'3',
511 'Forecast water level - tidal data generated by use of a hydrodynamic model':'4',
512 'Reserved':'5',
513 'Reserved':'6',
514 'Reserved':'7',
515 }
516
517 sourceDecodeLut = {
518 '0':'No data',
519 '1':'Realtime sensor data',
520 '2':'Raw realtime sensor data - no validation',
521 '3':'Predicted water level - tidal data generated by using harmonic analysis',
522 '4':'Forecast water level - tidal data generated by use of a hydrodynamic model',
523 '5':'Reserved',
524 '6':'Reserved',
525 '7':'Reserved',
526 }
527
528
529
530
531
532 dbTableName='waterlevel'
533 'Database table name'
534
535 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None
536 ,addCoastGuardFields=True
537 ,dbType='postgres'
538 ):
539 '''
540 Return the SQL CREATE command for this message type
541 @param outfile: file like object to print to.
542 @param fields: which fields to put in the create. Defaults to all.
543 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
544 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
545 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
546 @type addCoastGuardFields: bool
547 @return: sql create string
548 @rtype: str
549
550 @see: sqlCreate
551 '''
552
553 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))
554
555 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
556 '''
557 Return the sqlhelp object to create the table.
558
559 @param fields: which fields to put in the create. Defaults to all.
560 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
561 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
562 @type addCoastGuardFields: bool
563 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
564 @return: An object that can be used to generate a return
565 @rtype: sqlhelp.create
566 '''
567 if None == fields: fields = fieldList
568 import sqlhelp
569 c = sqlhelp.create('waterlevel',dbType=dbType)
570 c.addPrimaryKey()
571 if 'MessageID' in fields: c.addInt ('MessageID')
572 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator')
573 if 'UserID' in fields: c.addInt ('UserID')
574 if 'Spare' in fields: c.addInt ('Spare')
575 if 'dac' in fields: c.addInt ('dac')
576 if 'fid' in fields: c.addInt ('fid')
577 if 'month' in fields: c.addInt ('month')
578 if 'day' in fields: c.addInt ('day')
579 if 'hour' in fields: c.addInt ('hour')
580 if 'min' in fields: c.addInt ('min')
581 if 'stationid' in fields: c.addVarChar('stationid',7)
582 if 'waterlevel' in fields: c.addInt ('waterlevel')
583 if 'datum' in fields: c.addInt ('datum')
584 if 'sigma' in fields: c.addInt ('sigma')
585 if 'source' in fields: c.addInt ('source')
586
587 if addCoastGuardFields:
588
589
590
591
592
593 c.addVarChar('cg_r',15)
594 c.addInt('cg_sec')
595
596 c.addTimestamp('cg_timestamp')
597
598 return c
599
600 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
601 '''
602 Return the SQL INSERT command for this message type
603 @param params: dictionary of values keyed by field name
604 @param outfile: file like object to print to.
605 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields
606 @return: sql create string
607 @rtype: str
608
609 @see: sqlCreate
610 '''
611 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))
612
613
614 -def sqlInsert(params,extraParams=None,dbType='postgres'):
615 '''
616 Give the SQL INSERT statement
617 @param params: dict keyed by field name of values
618 @param extraParams: any extra fields that you have created beyond the normal ais message fields
619 @rtype: sqlhelp.insert
620 @return: insert class instance
621 @todo: allow optional type checking of params?
622 @warning: this will take invalid keys happily and do what???
623 '''
624 import sqlhelp
625 i = sqlhelp.insert('waterlevel',dbType=dbType)
626
627 if dbType=='postgres':
628 finished = []
629 for key in params:
630 if key in finished:
631 continue
632
633 if key not in toPgFields and key not in fromPgFields:
634 if type(params[key])==Decimal: i.add(key,float(params[key]))
635 else: i.add(key,params[key])
636 else:
637 if key in fromPgFields:
638 val = params[key]
639
640 i.addPostGIS(key,val)
641 finished.append(key)
642 else:
643
644 pgName = toPgFields[key]
645
646 valStr=pgTypes[pgName]+'('
647 vals = []
648 for nonPgKey in fromPgFields[pgName]:
649 vals.append(str(params[nonPgKey]))
650 finished.append(nonPgKey)
651 valStr+=' '.join(vals)+')'
652 i.addPostGIS(pgName,valStr)
653 else:
654 for key in params:
655 if type(params[key])==Decimal: i.add(key,float(params[key]))
656 else: i.add(key,params[key])
657
658 if None != extraParams:
659 for key in extraParams:
660 i.add(key,extraParams[key])
661
662 return i
663
664
665
666
667
670 '''
671 Return the LaTeX definition table for this message type
672 @param outfile: file like object to print to.
673 @type outfile: file obj
674 @return: LaTeX table string via the outfile
675 @rtype: str
676
677 '''
678 o = outfile
679
680 o.write('''
681 \\begin{table}%[htb]
682 \\centering
683 \\begin{tabular}{|l|c|l|}
684 \\hline
685 Parameter & Number of bits & Description
686 \\\\ \\hline\\hline
687 MessageID & 6 & AIS message number. Must be 8 \\\\ \hline
688 RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline
689 UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline
690 Spare & 2 & Reserved for definition by a regional authority. \\\\ \hline
691 dac & 10 & Designated Area Code \\\\ \hline
692 fid & 6 & Functional Identifier \\\\ \hline
693 month & 4 & Time the measurement represents month 1..12 \\\\ \hline
694 day & 5 & Time the measurement represents day of the month 1..31 \\\\ \hline
695 hour & 5 & Time the measurement represents UTC hours 0..23 \\\\ \hline
696 min & 6 & Time the measurement represents minutes \\\\ \hline
697 stationid & 42 & Character identifier of the station. Usually a number. \\\\ \hline
698 waterlevel & 16 & Water level in centimeters \\\\ \hline
699 datum & 5 & What reference datum applies to the value \\\\ \hline
700 sigma & 7 & Standard deviation of 1 second samples used to compute the water level height. FIX: is this the correct description of sigma? \\\\ \hline
701 source & 3 & How the water level was derived\\\\ \\hline \\hline
702 Total bits & 149 & Appears to take 1 slot with 19 pad bits to fill the last slot \\\\ \\hline
703 \\end{tabular}
704 \\caption{AIS message number 8: Water level report. This is }
705 \\label{tab:waterlevel}
706 \\end{table}
707 ''')
708
709
710
711
712
713 -def textDefinitionTable(outfile=sys.stdout
714 ,delim='\t'
715 ):
716 '''
717 Return the text definition table for this message type
718 @param outfile: file like object to print to.
719 @type outfile: file obj
720 @return: text table string via the outfile
721 @rtype: str
722
723 '''
724 o = outfile
725 o.write('''Parameter'''+delim+'Number of bits'''+delim+'''Description
726 MessageID'''+delim+'''6'''+delim+'''AIS message number. Must be 8
727 RepeatIndicator'''+delim+'''2'''+delim+'''Indicated how many times a message has been repeated
728 UserID'''+delim+'''30'''+delim+'''Unique ship identification number (MMSI)
729 Spare'''+delim+'''2'''+delim+'''Reserved for definition by a regional authority.
730 dac'''+delim+'''10'''+delim+'''Designated Area Code
731 fid'''+delim+'''6'''+delim+'''Functional Identifier
732 month'''+delim+'''4'''+delim+'''Time the measurement represents month 1..12
733 day'''+delim+'''5'''+delim+'''Time the measurement represents day of the month 1..31
734 hour'''+delim+'''5'''+delim+'''Time the measurement represents UTC hours 0..23
735 min'''+delim+'''6'''+delim+'''Time the measurement represents minutes
736 stationid'''+delim+'''42'''+delim+'''Character identifier of the station. Usually a number.
737 waterlevel'''+delim+'''16'''+delim+'''Water level in centimeters
738 datum'''+delim+'''5'''+delim+'''What reference datum applies to the value
739 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?
740 source'''+delim+'''3'''+delim+'''How the water level was derived
741 Total bits'''+delim+'''149'''+delim+'''Appears to take 1 slot with 19 pad bits to fill the last slot''')
742
743
744
745
746
747 import unittest
749 '''Return a params file base on the testvalue tags.
750 @rtype: dict
751 @return: params based on testvalue tags
752 '''
753 params = {}
754 params['MessageID'] = 8
755 params['RepeatIndicator'] = 1
756 params['UserID'] = 1193046
757 params['Spare'] = 0
758 params['dac'] = 366
759 params['fid'] = 63
760 params['month'] = 2
761 params['day'] = 28
762 params['hour'] = 23
763 params['min'] = 45
764 params['stationid'] = 'A234567'
765 params['waterlevel'] = -97
766 params['datum'] = 0
767 params['sigma'] = 97
768 params['source'] = 2
769
770 return params
771
773 '''Use testvalue tag text from each type to build test case the waterlevel message'''
775
776 params = testParams()
777 bits = encode(params)
778 r = decode(bits)
779
780
781 self.failUnlessEqual(r['MessageID'],params['MessageID'])
782 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
783 self.failUnlessEqual(r['UserID'],params['UserID'])
784 self.failUnlessEqual(r['Spare'],params['Spare'])
785 self.failUnlessEqual(r['dac'],params['dac'])
786 self.failUnlessEqual(r['fid'],params['fid'])
787 self.failUnlessEqual(r['month'],params['month'])
788 self.failUnlessEqual(r['day'],params['day'])
789 self.failUnlessEqual(r['hour'],params['hour'])
790 self.failUnlessEqual(r['min'],params['min'])
791 self.failUnlessEqual(r['stationid'],params['stationid'])
792 self.failUnlessEqual(r['waterlevel'],params['waterlevel'])
793 self.failUnlessEqual(r['datum'],params['datum'])
794 self.failUnlessEqual(r['sigma'],params['sigma'])
795 self.failUnlessEqual(r['source'],params['source'])
796
798 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
799 help='decode a "waterlevel" AIS message')
800 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
801 help='encode a "waterlevel" AIS message')
802 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
803 ,help='Field parameter value [default: %default]')
804 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
805 ,help='Field parameter value [default: %default]')
806 parser.add_option('--month-field', dest='monthField',metavar='uint',type='int'
807 ,help='Field parameter value [default: %default]')
808 parser.add_option('--day-field', dest='dayField',metavar='uint',type='int'
809 ,help='Field parameter value [default: %default]')
810 parser.add_option('--hour-field', dest='hourField',metavar='uint',type='int'
811 ,help='Field parameter value [default: %default]')
812 parser.add_option('--min-field', dest='minField',metavar='uint',type='int'
813 ,help='Field parameter value [default: %default]')
814 parser.add_option('--stationid-field', dest='stationidField',default='@@@@@@@',metavar='aisstr6',type='string'
815 ,help='Field parameter value [default: %default]')
816 parser.add_option('--waterlevel-field', dest='waterlevelField',default=-32768,metavar='int',type='int'
817 ,help='Field parameter value [default: %default]')
818 parser.add_option('--datum-field', dest='datumField',default=31,metavar='uint',type='int'
819 ,help='Field parameter value [default: %default]')
820 parser.add_option('--sigma-field', dest='sigmaField',default=127,metavar='uint',type='int'
821 ,help='Field parameter value [default: %default]')
822 parser.add_option('--source-field', dest='sourceField',default=0,metavar='uint',type='int'
823 ,help='Field parameter value [default: %default]')
824
825
826 if __name__=='__main__':
827
828 from optparse import OptionParser
829 parser = OptionParser(usage="%prog [options]",
830 version="%prog "+__version__)
831
832 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
833 help='run the documentation tests')
834 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
835 help='run the unit tests')
836 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
837 help='Make the test output verbose')
838
839
840
841 typeChoices = ('binary','nmeapayload','nmea')
842 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType'
843 ,default='nmeapayload'
844 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]')
845
846
847 outputChoices = ('std','html','csv','sql' )
848 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType'
849 ,default='std'
850 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]')
851
852 parser.add_option('-o','--output',dest='outputFileName',default=None,
853 help='Name of the python file to write [default: stdout]')
854
855 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append',
856 choices=fieldList,
857 help='Which fields to include in the output. Currently only for csv output [default: all]')
858
859 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true',
860 help='Print the field name for csv')
861
862 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true',
863 help='Print out an sql create command for the table.')
864
865 parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true',
866 help='Print a LaTeX table of the type')
867
868 parser.add_option('--text-table',dest='textDefinitionTable',default=False,action='store_true',
869 help='Print delimited table of the type (for Word table importing)')
870 parser.add_option('--delimt-text-table',dest='delimTextDefinitionTable',default='\t'
871 ,help='Delimiter for text table [default: \'%default\'](for Word table importing)')
872
873
874 dbChoices = ('sqlite','postgres')
875 parser.add_option('-D','--db-type',dest='dbType',default='postgres'
876 ,choices=dbChoices,type='choice'
877 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]')
878
879 addMsgOptions(parser)
880
881 (options,args) = parser.parse_args()
882 success=True
883
884 if options.doctest:
885 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
886 sys.argv= [sys.argv[0]]
887 if options.verbose: sys.argv.append('-v')
888 import doctest
889 numfail,numtests=doctest.testmod()
890 if numfail==0: print 'ok'
891 else:
892 print 'FAILED'
893 success=False
894
895 if not success: sys.exit('Something Failed')
896 del success
897
898 if options.unittest:
899 sys.argv = [sys.argv[0]]
900 if options.verbose: sys.argv.append('-v')
901 unittest.main()
902
903 outfile = sys.stdout
904 if None!=options.outputFileName:
905 outfile = file(options.outputFileName,'w')
906
907
908 if options.doEncode:
909
910 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
911 if None==options.UserIDField: parser.error("missing value for UserIDField")
912 if None==options.monthField: parser.error("missing value for monthField")
913 if None==options.dayField: parser.error("missing value for dayField")
914 if None==options.hourField: parser.error("missing value for hourField")
915 if None==options.minField: parser.error("missing value for minField")
916 if None==options.stationidField: parser.error("missing value for stationidField")
917 if None==options.waterlevelField: parser.error("missing value for waterlevelField")
918 if None==options.datumField: parser.error("missing value for datumField")
919 if None==options.sigmaField: parser.error("missing value for sigmaField")
920 if None==options.sourceField: parser.error("missing value for sourceField")
921 msgDict={
922 'MessageID': '8',
923 'RepeatIndicator': options.RepeatIndicatorField,
924 'UserID': options.UserIDField,
925 'Spare': '0',
926 'dac': '366',
927 'fid': '63',
928 'month': options.monthField,
929 'day': options.dayField,
930 'hour': options.hourField,
931 'min': options.minField,
932 'stationid': options.stationidField,
933 'waterlevel': options.waterlevelField,
934 'datum': options.datumField,
935 'sigma': options.sigmaField,
936 'source': options.sourceField,
937 }
938
939 bits = encode(msgDict)
940 if 'binary'==options.ioType: print str(bits)
941 elif 'nmeapayload'==options.ioType:
942
943 print "bitLen",len(bits)
944 bitLen=len(bits)
945 if bitLen%6!=0:
946 bits = bits + BitVector(size=(6 - (bitLen%6)))
947 print "result:",binary.bitvectoais6(bits)[0]
948
949
950
951 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability")
952 else: sys.exit('ERROR: unknown ioType. Help!')
953
954
955 if options.sqlCreate:
956 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType)
957
958 if options.latexDefinitionTable:
959 latexDefinitionTable(outfile)
960
961
962 if options.textDefinitionTable:
963 textDefinitionTable(outfile,options.delimTextDefinitionTable)
964
965 if options.printCsvfieldList:
966
967 if None == options.fieldList: options.fieldList = fieldList
968 import StringIO
969 buf = StringIO.StringIO()
970 for field in options.fieldList:
971 buf.write(field+',')
972 result = buf.getvalue()
973 if result[-1] == ',': print result[:-1]
974 else: print result
975
976 if options.doDecode:
977 if len(args)==0: args = sys.stdin
978 for msg in args:
979 bv = None
980
981 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'):
982
983
984 bv = binary.ais6tobitvec(msg.split(',')[5])
985 else:
986
987 binaryMsg=True
988 for c in msg:
989 if c not in ('0','1'):
990 binaryMsg=False
991 break
992 if binaryMsg:
993 bv = BitVector(bitstring=msg)
994 else:
995 bv = binary.ais6tobitvec(msg)
996
997 printFields(decode(bv)
998 ,out=outfile
999 ,format=options.outputType
1000 ,fieldList=options.fieldList
1001 ,dbType=options.dbType
1002 )
1003