#!/usr/bin/env python

"""Autogenerated python functions to serialize/deserialize binary messages.

Need to then wrap these functions with the outer AIS packet and then
convert the whole binary blob to a NMEA string.  Those functions are
not currently provided in this file.

serialize: python to ais binary
deserialize: ais binary to python

The generated code uses translators.py, binary.py, and aisstring.py
which should be packaged with the resulting files.

TODO(schwehr): Put in a description of the message here with fields and types.
"""
import sys
from decimal import Decimal
import unittest

from aisutils.BitVector import BitVector

from aisutils import aisstring
from aisutils import binary
from aisutils import sqlhelp
from aisutils import uscg


fieldList = (
    'MessageID',
    'RepeatIndicator',
    'UserID',
    'Spare',
    'ChanA',
    'ChanB',
    'TxRxMode',
    'power',
    'corner1_lon',
    'corner1_lat',
    'corner2_lon',
    'corner2_lat',
    'IndicatorType',
    'ChanABandwidth',
    'ChanBBandwidth',
    'TransZoneSize',
    'Spare2',
)

fieldListPostgres = (
    'MessageID',
    'RepeatIndicator',
    'UserID',
    'Spare',
    'ChanA',
    'ChanB',
    'TxRxMode',
    'power',
    'corner1',    # PostGIS data type
    'corner2',    # PostGIS data type
    'IndicatorType',
    'ChanABandwidth',
    'ChanBBandwidth',
    'TransZoneSize',
    'Spare2',
)

toPgFields = {
    'corner1_lon':'corner1',
    'corner1_lat':'corner1',
    'corner2_lon':'corner2',
    'corner2_lat':'corner2',
}
"""
Go to the Postgis field names from the straight field name
"""

fromPgFields = {
    'corner1':('corner1_lon','corner1_lat',),
    'corner2':('corner2_lon','corner2_lat',),
}
"""
Go from the Postgis field names to the straight field name
"""

pgTypes = {
    'corner1':'POINT',
    'corner2':'POINT',
}
"""
Lookup table for each postgis field name to get its type.
"""

def encode(params, validate=False):
    '''Create a ChanMngmt binary message payload to pack into an AIS Msg ChanMngmt.

    Fields in params:
      - MessageID(uint): AIS message number.  Must be 22 (field automatically set to "22")
      - RepeatIndicator(uint): Indicated how many times a message has been repeated
      - UserID(uint): Unique ship identification number (MMSI)
      - Spare(uint): Not used.  Should be set to zero. (field automatically set to "0")
      - ChanA(uint): Channel number from ITU-R M.1084 Annex 4
      - ChanB(uint): Channel number from ITU-R M.1084 Annex 4
      - TxRxMode(uint): FIX: find the description
      - power(uint): FIX: put in a description
      - corner1_lon(decimal): north-east corner of area for assignment  longitude of corner
      - corner1_lat(decimal): north-east corner of area for assignment  latitude of corner
      - corner2_lon(decimal): south-west corner of area for assignment  longitude of corner
      - corner2_lat(decimal): south-west corner of area for assignment  latitude of corner
      - IndicatorType(uint): FIX: put in a description
      - ChanABandwidth(uint): FIX: put in a description
      - ChanBBandwidth(uint): FIX: put in a description
      - TransZoneSize(uint): FIX: put in a description
      - Spare2(uint): Not used.  Should be set to zero. (field automatically set to "0")
    @param params: Dictionary of field names/values.  Throws a ValueError exception if required is missing
    @param validate: Set to true to cause checking to occur.  Runs slower.  FIX: not implemented.
    @rtype: BitVector
    @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8
    @note: The returned bits may not be 6 bit aligned.  It is up to you to pad out the bits.
    '''

    bvList = []
    bvList.append(binary.setBitVectorSize(BitVector(intVal=22),6))
    if 'RepeatIndicator' in params:
        bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2))
    else:
        bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['ChanA']),12))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['ChanB']),12))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['TxRxMode']),4))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['power']),1))
    if 'corner1_lon' in params:
        bvList.append(binary.bvFromSignedInt(int(Decimal(params['corner1_lon'])*Decimal('600')),18))
    else:
        bvList.append(binary.bvFromSignedInt(108600,18))
    if 'corner1_lat' in params:
        bvList.append(binary.bvFromSignedInt(int(Decimal(params['corner1_lat'])*Decimal('600')),17))
    else:
        bvList.append(binary.bvFromSignedInt(108600,17))
    if 'corner2_lon' in params:
        bvList.append(binary.bvFromSignedInt(int(Decimal(params['corner2_lon'])*Decimal('600')),18))
    else:
        bvList.append(binary.bvFromSignedInt(108600,18))
    if 'corner2_lat' in params:
        bvList.append(binary.bvFromSignedInt(int(Decimal(params['corner2_lat'])*Decimal('600')),17))
    else:
        bvList.append(binary.bvFromSignedInt(108600,17))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['IndicatorType']),1))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['ChanABandwidth']),1))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['ChanBBandwidth']),1))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['TransZoneSize']),3))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=0),23))

    return binary.joinBV(bvList)

def decode(bv, validate=False):
    '''Unpack a ChanMngmt message.

    Fields in params:
      - MessageID(uint): AIS message number.  Must be 22 (field automatically set to "22")
      - RepeatIndicator(uint): Indicated how many times a message has been repeated
      - UserID(uint): Unique ship identification number (MMSI)
      - Spare(uint): Not used.  Should be set to zero. (field automatically set to "0")
      - ChanA(uint): Channel number from ITU-R M.1084 Annex 4
      - ChanB(uint): Channel number from ITU-R M.1084 Annex 4
      - TxRxMode(uint): FIX: find the description
      - power(uint): FIX: put in a description
      - corner1_lon(decimal): north-east corner of area for assignment  longitude of corner
      - corner1_lat(decimal): north-east corner of area for assignment  latitude of corner
      - corner2_lon(decimal): south-west corner of area for assignment  longitude of corner
      - corner2_lat(decimal): south-west corner of area for assignment  latitude of corner
      - IndicatorType(uint): FIX: put in a description
      - ChanABandwidth(uint): FIX: put in a description
      - ChanBBandwidth(uint): FIX: put in a description
      - TransZoneSize(uint): FIX: put in a description
      - Spare2(uint): Not used.  Should be set to zero. (field automatically set to "0")
    @type bv: BitVector
    @param bv: Bits defining a message
    @param validate: Set to true to cause checking to occur.  Runs slower.  FIX: not implemented.
    @rtype: dict
    @return: params
    '''

    #Would be nice to check the bit count here..
    #if validate:
    #    assert (len(bv)==FIX: SOME NUMBER)
    r = {}
    r['MessageID']=22
    r['RepeatIndicator']=int(bv[6:8])
    r['UserID']=int(bv[8:38])
    r['Spare']=0
    r['ChanA']=int(bv[40:52])
    r['ChanB']=int(bv[52:64])
    r['TxRxMode']=int(bv[64:68])
    r['power']=int(bv[68:69])
    r['corner1_lon']=Decimal(binary.signedIntFromBV(bv[69:87]))/Decimal('600')
    r['corner1_lat']=Decimal(binary.signedIntFromBV(bv[87:104]))/Decimal('600')
    r['corner2_lon']=Decimal(binary.signedIntFromBV(bv[104:122]))/Decimal('600')
    r['corner2_lat']=Decimal(binary.signedIntFromBV(bv[122:139]))/Decimal('600')
    r['IndicatorType']=int(bv[139:140])
    r['ChanABandwidth']=int(bv[140:141])
    r['ChanBBandwidth']=int(bv[141:142])
    r['TransZoneSize']=int(bv[142:145])
    r['Spare2']=0
    return r

def decodeMessageID(bv, validate=False):
    return 22

def decodeRepeatIndicator(bv, validate=False):
    return int(bv[6:8])

def decodeUserID(bv, validate=False):
    return int(bv[8:38])

def decodeSpare(bv, validate=False):
    return 0

def decodeChanA(bv, validate=False):
    return int(bv[40:52])

def decodeChanB(bv, validate=False):
    return int(bv[52:64])

def decodeTxRxMode(bv, validate=False):
    return int(bv[64:68])

def decodepower(bv, validate=False):
    return int(bv[68:69])

def decodecorner1_lon(bv, validate=False):
    return Decimal(binary.signedIntFromBV(bv[69:87]))/Decimal('600')

def decodecorner1_lat(bv, validate=False):
    return Decimal(binary.signedIntFromBV(bv[87:104]))/Decimal('600')

def decodecorner2_lon(bv, validate=False):
    return Decimal(binary.signedIntFromBV(bv[104:122]))/Decimal('600')

def decodecorner2_lat(bv, validate=False):
    return Decimal(binary.signedIntFromBV(bv[122:139]))/Decimal('600')

def decodeIndicatorType(bv, validate=False):
    return int(bv[139:140])

def decodeChanABandwidth(bv, validate=False):
    return int(bv[140:141])

def decodeChanBBandwidth(bv, validate=False):
    return int(bv[141:142])

def decodeTransZoneSize(bv, validate=False):
    return int(bv[142:145])

def decodeSpare2(bv, validate=False):
    return 0


def printHtml(params, out=sys.stdout):
        out.write("<h3>ChanMngmt</h3>\n")
        out.write("<table border=\"1\">\n")
        out.write("<tr bgcolor=\"orange\">\n")
        out.write("<th align=\"left\">Field Name</th>\n")
        out.write("<th align=\"left\">Type</th>\n")
        out.write("<th align=\"left\">Value</th>\n")
        out.write("<th align=\"left\">Value in Lookup Table</th>\n")
        out.write("<th align=\"left\">Units</th>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>MessageID</td>\n")
        out.write("<td>uint</td>\n")
        if 'MessageID' in params:
            out.write("    <td>"+str(params['MessageID'])+"</td>\n")
            out.write("    <td>"+str(params['MessageID'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>RepeatIndicator</td>\n")
        out.write("<td>uint</td>\n")
        if 'RepeatIndicator' in params:
            out.write("    <td>"+str(params['RepeatIndicator'])+"</td>\n")
            if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut:
                out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>UserID</td>\n")
        out.write("<td>uint</td>\n")
        if 'UserID' in params:
            out.write("    <td>"+str(params['UserID'])+"</td>\n")
            out.write("    <td>"+str(params['UserID'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>Spare</td>\n")
        out.write("<td>uint</td>\n")
        if 'Spare' in params:
            out.write("    <td>"+str(params['Spare'])+"</td>\n")
            out.write("    <td>"+str(params['Spare'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>ChanA</td>\n")
        out.write("<td>uint</td>\n")
        if 'ChanA' in params:
            out.write("    <td>"+str(params['ChanA'])+"</td>\n")
            out.write("    <td>"+str(params['ChanA'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>ChanB</td>\n")
        out.write("<td>uint</td>\n")
        if 'ChanB' in params:
            out.write("    <td>"+str(params['ChanB'])+"</td>\n")
            out.write("    <td>"+str(params['ChanB'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>TxRxMode</td>\n")
        out.write("<td>uint</td>\n")
        if 'TxRxMode' in params:
            out.write("    <td>"+str(params['TxRxMode'])+"</td>\n")
            if str(params['TxRxMode']) in TxRxModeDecodeLut:
                out.write("<td>"+TxRxModeDecodeLut[str(params['TxRxMode'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>power</td>\n")
        out.write("<td>uint</td>\n")
        if 'power' in params:
            out.write("    <td>"+str(params['power'])+"</td>\n")
            if str(params['power']) in powerDecodeLut:
                out.write("<td>"+powerDecodeLut[str(params['power'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>corner1_lon</td>\n")
        out.write("<td>decimal</td>\n")
        if 'corner1_lon' in params:
            out.write("    <td>"+str(params['corner1_lon'])+"</td>\n")
            out.write("    <td>"+str(params['corner1_lon'])+"</td>\n")
        out.write("<td>degrees</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>corner1_lat</td>\n")
        out.write("<td>decimal</td>\n")
        if 'corner1_lat' in params:
            out.write("    <td>"+str(params['corner1_lat'])+"</td>\n")
            out.write("    <td>"+str(params['corner1_lat'])+"</td>\n")
        out.write("<td>degrees</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>corner2_lon</td>\n")
        out.write("<td>decimal</td>\n")
        if 'corner2_lon' in params:
            out.write("    <td>"+str(params['corner2_lon'])+"</td>\n")
            out.write("    <td>"+str(params['corner2_lon'])+"</td>\n")
        out.write("<td>degrees</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>corner2_lat</td>\n")
        out.write("<td>decimal</td>\n")
        if 'corner2_lat' in params:
            out.write("    <td>"+str(params['corner2_lat'])+"</td>\n")
            out.write("    <td>"+str(params['corner2_lat'])+"</td>\n")
        out.write("<td>degrees</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>IndicatorType</td>\n")
        out.write("<td>uint</td>\n")
        if 'IndicatorType' in params:
            out.write("    <td>"+str(params['IndicatorType'])+"</td>\n")
            if str(params['IndicatorType']) in IndicatorTypeDecodeLut:
                out.write("<td>"+IndicatorTypeDecodeLut[str(params['IndicatorType'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>ChanABandwidth</td>\n")
        out.write("<td>uint</td>\n")
        if 'ChanABandwidth' in params:
            out.write("    <td>"+str(params['ChanABandwidth'])+"</td>\n")
            if str(params['ChanABandwidth']) in ChanABandwidthDecodeLut:
                out.write("<td>"+ChanABandwidthDecodeLut[str(params['ChanABandwidth'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>ChanBBandwidth</td>\n")
        out.write("<td>uint</td>\n")
        if 'ChanBBandwidth' in params:
            out.write("    <td>"+str(params['ChanBBandwidth'])+"</td>\n")
            if str(params['ChanBBandwidth']) in ChanBBandwidthDecodeLut:
                out.write("<td>"+ChanBBandwidthDecodeLut[str(params['ChanBBandwidth'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>TransZoneSize</td>\n")
        out.write("<td>uint</td>\n")
        if 'TransZoneSize' in params:
            out.write("    <td>"+str(params['TransZoneSize'])+"</td>\n")
            if str(params['TransZoneSize']) in TransZoneSizeDecodeLut:
                out.write("<td>"+TransZoneSizeDecodeLut[str(params['TransZoneSize'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("<td>nm</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>Spare2</td>\n")
        out.write("<td>uint</td>\n")
        if 'Spare2' in params:
            out.write("    <td>"+str(params['Spare2'])+"</td>\n")
            out.write("    <td>"+str(params['Spare2'])+"</td>\n")
        out.write("</tr>\n")
        out.write("</table>\n")

def printFields(params, out=sys.stdout, format='std', fieldList=None, dbType='postgres'):
    '''Print a ChanMngmt message to stdout.

    Fields in params:
      - MessageID(uint): AIS message number.  Must be 22 (field automatically set to "22")
      - RepeatIndicator(uint): Indicated how many times a message has been repeated
      - UserID(uint): Unique ship identification number (MMSI)
      - Spare(uint): Not used.  Should be set to zero. (field automatically set to "0")
      - ChanA(uint): Channel number from ITU-R M.1084 Annex 4
      - ChanB(uint): Channel number from ITU-R M.1084 Annex 4
      - TxRxMode(uint): FIX: find the description
      - power(uint): FIX: put in a description
      - corner1_lon(decimal): north-east corner of area for assignment  longitude of corner
      - corner1_lat(decimal): north-east corner of area for assignment  latitude of corner
      - corner2_lon(decimal): south-west corner of area for assignment  longitude of corner
      - corner2_lat(decimal): south-west corner of area for assignment  latitude of corner
      - IndicatorType(uint): FIX: put in a description
      - ChanABandwidth(uint): FIX: put in a description
      - ChanBBandwidth(uint): FIX: put in a description
      - TransZoneSize(uint): FIX: put in a description
      - Spare2(uint): Not used.  Should be set to zero. (field automatically set to "0")
    @param params: Dictionary of field names/values.
    @param out: File like object to write to.
    @rtype: stdout
    @return: text to out
    '''

    if 'std'==format:
        out.write("ChanMngmt:\n")
        if 'MessageID' in params: out.write("    MessageID:        "+str(params['MessageID'])+"\n")
        if 'RepeatIndicator' in params: out.write("    RepeatIndicator:  "+str(params['RepeatIndicator'])+"\n")
        if 'UserID' in params: out.write("    UserID:           "+str(params['UserID'])+"\n")
        if 'Spare' in params: out.write("    Spare:            "+str(params['Spare'])+"\n")
        if 'ChanA' in params: out.write("    ChanA:            "+str(params['ChanA'])+"\n")
        if 'ChanB' in params: out.write("    ChanB:            "+str(params['ChanB'])+"\n")
        if 'TxRxMode' in params: out.write("    TxRxMode:         "+str(params['TxRxMode'])+"\n")
        if 'power' in params: out.write("    power:            "+str(params['power'])+"\n")
        if 'corner1_lon' in params: out.write("    corner1_lon:      "+str(params['corner1_lon'])+"\n")
        if 'corner1_lat' in params: out.write("    corner1_lat:      "+str(params['corner1_lat'])+"\n")
        if 'corner2_lon' in params: out.write("    corner2_lon:      "+str(params['corner2_lon'])+"\n")
        if 'corner2_lat' in params: out.write("    corner2_lat:      "+str(params['corner2_lat'])+"\n")
        if 'IndicatorType' in params: out.write("    IndicatorType:    "+str(params['IndicatorType'])+"\n")
        if 'ChanABandwidth' in params: out.write("    ChanABandwidth:   "+str(params['ChanABandwidth'])+"\n")
        if 'ChanBBandwidth' in params: out.write("    ChanBBandwidth:   "+str(params['ChanBBandwidth'])+"\n")
        if 'TransZoneSize' in params: out.write("    TransZoneSize:    "+str(params['TransZoneSize'])+"\n")
        if 'Spare2' in params: out.write("    Spare2:           "+str(params['Spare2'])+"\n")
        elif 'csv'==format:
                if None == options.fieldList:
                        options.fieldList = fieldList
                needComma = False;
                for field in fieldList:
                        if needComma: out.write(',')
                        needComma = True
                        if field in params:
                                out.write(str(params[field]))
                        # else: leave it empty
                out.write("\n")
    elif 'html'==format:
        printHtml(params,out)
    elif 'sql'==format:
                sqlInsertStr(params,out,dbType=dbType)
    else:
        print "ERROR: unknown format:",format
        assert False

    return # Nothing to return

RepeatIndicatorEncodeLut = {
    'default':'0',
    'do not repeat any more':'3',
    } #RepeatIndicatorEncodeLut

RepeatIndicatorDecodeLut = {
    '0':'default',
    '3':'do not repeat any more',
    } # RepeatIndicatorEncodeLut

TxRxModeEncodeLut = {
    'Tx A/Tx B, Rx A/RX B':'0',
    'Tx A, Rx A/Rx B':'1',
    'Tx B, Rx A/Rx B':'2',
    } #TxRxModeEncodeLut

TxRxModeDecodeLut = {
    '0':'Tx A/Tx B, Rx A/RX B',
    '1':'Tx A, Rx A/Rx B',
    '2':'Tx B, Rx A/Rx B',
    } # TxRxModeEncodeLut

powerEncodeLut = {
    'high':'0',
    'low':'1',
    } #powerEncodeLut

powerDecodeLut = {
    '0':'high',
    '1':'low',
    } # powerEncodeLut

IndicatorTypeEncodeLut = {
    'broadcast':'0',
    'addressed':'1',
    } #IndicatorTypeEncodeLut

IndicatorTypeDecodeLut = {
    '0':'broadcast',
    '1':'addressed',
    } # IndicatorTypeEncodeLut

ChanABandwidthEncodeLut = {
    'specified by channel number':'0',
    '12.5kHz':'1',
    } #ChanABandwidthEncodeLut

ChanABandwidthDecodeLut = {
    '0':'specified by channel number',
    '1':'12.5kHz',
    } # ChanABandwidthEncodeLut

ChanBBandwidthEncodeLut = {
    'specified by channel number':'0',
    '12.5kHz':'1',
    } #ChanBBandwidthEncodeLut

ChanBBandwidthDecodeLut = {
    '0':'specified by channel number',
    '1':'12.5kHz',
    } # ChanBBandwidthEncodeLut

TransZoneSizeEncodeLut = {
    '1':'0',
    '2':'1',
    '3':'2',
    '4':'3',
    '5':'4',
    '6':'5',
    '7':'6',
    '8':'7',
    } #TransZoneSizeEncodeLut

TransZoneSizeDecodeLut = {
    '0':'1',
    '1':'2',
    '2':'3',
    '3':'4',
    '4':'5',
    '5':'6',
    '6':'7',
    '7':'8',
    } # TransZoneSizeEncodeLut

######################################################################
# SQL SUPPORT
######################################################################

dbTableName='ChanMngmt'
'Database table name'

def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None
                ,addCoastGuardFields=True
                ,dbType='postgres'
                ):
        """
        Return the SQL CREATE command for this message type
        @param outfile: file like object to print to.
        @param fields: which fields to put in the create.  Defaults to all.
        @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
        @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
        @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
        @type addCoastGuardFields: bool
        @return: sql create string
        @rtype: str

        @see: sqlCreate
        """
        # FIX: should this sqlCreate be the same as in LaTeX (createFuncName) rather than hard coded?
        outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))

def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
    """Return the sqlhelp object to create the table.

    @param fields: which fields to put in the create.  Defaults to all.
    @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
    @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
    @type addCoastGuardFields: bool
    @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
    @return: An object that can be used to generate a return
    @rtype: sqlhelp.create
    """
    if fields is None:
        fields = fieldList
    c = sqlhelp.create('ChanMngmt',dbType=dbType)
    c.addPrimaryKey()
    if 'MessageID' in fields: c.addInt ('MessageID')
    if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator')
    if 'UserID' in fields: c.addInt ('UserID')
    if 'Spare' in fields: c.addInt ('Spare')
    if 'ChanA' in fields: c.addInt ('ChanA')
    if 'ChanB' in fields: c.addInt ('ChanB')
    if 'TxRxMode' in fields: c.addInt ('TxRxMode')
    if 'power' in fields: c.addInt ('power')
    if dbType != 'postgres':
        if 'corner1_lon' in fields: c.addDecimal('corner1_lon',5,2)
    if dbType != 'postgres':
        if 'corner1_lat' in fields: c.addDecimal('corner1_lat',5,2)
    if dbType != 'postgres':
        if 'corner2_lon' in fields: c.addDecimal('corner2_lon',5,2)
    if dbType != 'postgres':
        if 'corner2_lat' in fields: c.addDecimal('corner2_lat',5,2)
    if 'IndicatorType' in fields: c.addInt ('IndicatorType')
    if 'ChanABandwidth' in fields: c.addInt ('ChanABandwidth')
    if 'ChanBBandwidth' in fields: c.addInt ('ChanBBandwidth')
    if 'TransZoneSize' in fields: c.addInt ('TransZoneSize')
    if 'Spare2' in fields: c.addInt ('Spare2')

    if addCoastGuardFields:
        # c.addInt('cg_s_rssi')  # Relative signal strength indicator
        # c.addInt('cg_d_strength')  # dBm receive strength
        # c.addVarChar('cg_x',10)  # Idonno
        c.addInt('cg_t_arrival')  # Receive timestamp from the AIS equipment 'T'
        c.addInt('cg_s_slotnum')  # Slot received in
        c.addVarChar('cg_r',15)  # Receiver station ID  -  should usually be an MMSI, but sometimes is a string
        c.addInt('cg_sec')  # UTC seconds since the epoch

        c.addTimestamp('cg_timestamp') # UTC decoded cg_sec - not actually in the data stream

    if dbType == 'postgres':
        #--- EPSG 4326 : WGS 84
        #INSERT INTO "spatial_ref_sys" ("srid","auth_name","auth_srid","srtext","proj4text") VALUES (4326,'EPSG',4326,'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]','+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ');
        c.addPostGIS('corner1','POINT',2,SRID=4326);
        #--- EPSG 4326 : WGS 84
        #INSERT INTO "spatial_ref_sys" ("srid","auth_name","auth_srid","srtext","proj4text") VALUES (4326,'EPSG',4326,'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]','+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ');
        c.addPostGIS('corner2','POINT',2,SRID=4326);

    return c

def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
        """
        Return the SQL INSERT command for this message type
        @param params: dictionary of values keyed by field name
        @param outfile: file like object to print to.
        @param extraParams: A sequence of tuples containing (name,sql type) for additional fields
        @return: sql create string
        @rtype: str

        @see: sqlCreate
        """
        outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))


def sqlInsert(params,extraParams=None,dbType='postgres'):
        """
        Give the SQL INSERT statement
        @param params: dict keyed by field name of values
        @param extraParams: any extra fields that you have created beyond the normal ais message fields
        @rtype: sqlhelp.insert
        @return: insert class instance
         TODO(schwehr):allow optional type checking of params?
        @warning: this will take invalid keys happily and do what???
        """

        i = sqlhelp.insert('ChanMngmt',dbType=dbType)

        if dbType=='postgres':
                finished = []
                for key in params:
                        if key in finished:
                                continue

                        if key not in toPgFields and key not in fromPgFields:
                                if type(params[key])==Decimal: i.add(key,float(params[key]))
                                else: i.add(key,params[key])
                        else:
                                if key in fromPgFields:
                                        val = params[key]
                                        # Had better be a WKT type like POINT(-88.1 30.321)
                                        i.addPostGIS(key,val)
                                        finished.append(key)
                                else:
                                        # Need to construct the type.
                                        pgName = toPgFields[key]
                                        #valStr='GeomFromText(\''+pgTypes[pgName]+'('
                                        valStr=pgTypes[pgName]+'('
                                        vals = []
                                        for nonPgKey in fromPgFields[pgName]:
                                                vals.append(str(params[nonPgKey]))
                                                finished.append(nonPgKey)
                                        valStr+=' '.join(vals)+')'
                                        i.addPostGIS(pgName,valStr)
        else:
                for key in params:
                        if type(params[key])==Decimal: i.add(key,float(params[key]))
                        else: i.add(key,params[key])

        if None != extraParams:
                for key in extraParams:
                        i.add(key,extraParams[key])

        return i

######################################################################
# LATEX SUPPORT
######################################################################

def latexDefinitionTable(outfile=sys.stdout
                ):
        """
        Return the LaTeX definition table for this message type
        @param outfile: file like object to print to.
        @type outfile: file obj
        @return: LaTeX table string via the outfile
        @rtype: str

        """
        o = outfile

        o.write("""
\\begin{table}%[htb]
\\centering
\\begin{tabular}{|l|c|l|}
\\hline
Parameter & Number of bits & Description
\\\\  \\hline\\hline
MessageID & 6 & AIS message number.  Must be 22 \\\\ \hline
RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline
UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline
Spare & 2 & Not used.  Should be set to zero. \\\\ \hline
ChanA & 12 & Channel number from ITU-R M.1084 Annex 4 \\\\ \hline
ChanB & 12 & Channel number from ITU-R M.1084 Annex 4 \\\\ \hline
TxRxMode & 4 & FIX: find the description \\\\ \hline
power & 1 & FIX: put in a description \\\\ \hline
corner1\_lon & 18 & north-east corner of area for assignment  longitude of corner \\\\ \hline
corner1\_lat & 17 & north-east corner of area for assignment  latitude of corner \\\\ \hline
corner2\_lon & 18 & south-west corner of area for assignment  longitude of corner \\\\ \hline
corner2\_lat & 17 & south-west corner of area for assignment  latitude of corner \\\\ \hline
IndicatorType & 1 & FIX: put in a description \\\\ \hline
ChanABandwidth & 1 & FIX: put in a description \\\\ \hline
ChanBBandwidth & 1 & FIX: put in a description \\\\ \hline
TransZoneSize & 3 & FIX: put in a description \\\\ \hline
Spare2 & 23 & Not used.  Should be set to zero.\\\\ \\hline \\hline
Total bits & 168 & Appears to take 1 slot \\\\ \\hline
\\end{tabular}
\\caption{AIS message number 22: Base station report - F}
\\label{tab:ChanMngmt}
\\end{table}
""")

######################################################################
# Text Definition
######################################################################

def textDefinitionTable(outfile=sys.stdout ,delim='    '):
    """Return the text definition table for this message type

    @param outfile: file like object to print to.
    @type outfile: file obj
    @return: text table string via the outfile
    @rtype: str

    """
    o = outfile
    o.write('Parameter'+delim+'Number of bits'+delim+"""Description
MessageID"""+delim+'6'+delim+"""AIS message number.  Must be 22
RepeatIndicator"""+delim+'2'+delim+"""Indicated how many times a message has been repeated
UserID"""+delim+'30'+delim+"""Unique ship identification number (MMSI)
Spare"""+delim+'2'+delim+"""Not used.  Should be set to zero.
ChanA"""+delim+'12'+delim+"""Channel number from ITU-R M.1084 Annex 4
ChanB"""+delim+'12'+delim+"""Channel number from ITU-R M.1084 Annex 4
TxRxMode"""+delim+'4'+delim+"""FIX: find the description
power"""+delim+'1'+delim+"""FIX: put in a description
corner1_lon"""+delim+'18'+delim+"""north-east corner of area for assignment  longitude of corner
corner1_lat"""+delim+'17'+delim+"""north-east corner of area for assignment  latitude of corner
corner2_lon"""+delim+'18'+delim+"""south-west corner of area for assignment  longitude of corner
corner2_lat"""+delim+'17'+delim+"""south-west corner of area for assignment  latitude of corner
IndicatorType"""+delim+'1'+delim+"""FIX: put in a description
ChanABandwidth"""+delim+'1'+delim+"""FIX: put in a description
ChanBBandwidth"""+delim+'1'+delim+"""FIX: put in a description
TransZoneSize"""+delim+'3'+delim+"""FIX: put in a description
Spare2"""+delim+'23'+delim+"""Not used.  Should be set to zero.
Total bits"""+delim+"""168"""+delim+"""Appears to take 1 slot""")


######################################################################
# UNIT TESTING
######################################################################
def testParams():
    '''Return a params file base on the testvalue tags.
    @rtype: dict
    @return: params based on testvalue tags
    '''
    params = {}
    params['MessageID'] = 22
    params['RepeatIndicator'] = 1
    params['UserID'] = 1193046
    params['Spare'] = 0
    params['ChanA'] = 0
    params['ChanB'] = 0
    params['TxRxMode'] = 1
    params['power'] = 1
    params['corner1_lon'] = Decimal('-122.16')
    params['corner1_lat'] = Decimal('37.42')
    params['corner2_lon'] = Decimal('-122.16')
    params['corner2_lat'] = Decimal('37.42')
    params['IndicatorType'] = 1
    params['ChanABandwidth'] = 1
    params['ChanBBandwidth'] = 1
    params['TransZoneSize'] = 1
    params['Spare2'] = 0

    return params

class TestChanMngmt(unittest.TestCase):
    '''Use testvalue tag text from each type to build test case the ChanMngmt message'''
    def testEncodeDecode(self):

        params = testParams()
        bits   = encode(params)
        r      = decode(bits)

        # Check that each parameter came through ok.
        self.failUnlessEqual(r['MessageID'],params['MessageID'])
        self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
        self.failUnlessEqual(r['UserID'],params['UserID'])
        self.failUnlessEqual(r['Spare'],params['Spare'])
        self.failUnlessEqual(r['ChanA'],params['ChanA'])
        self.failUnlessEqual(r['ChanB'],params['ChanB'])
        self.failUnlessEqual(r['TxRxMode'],params['TxRxMode'])
        self.failUnlessEqual(r['power'],params['power'])
        self.failUnlessAlmostEqual(r['corner1_lon'],params['corner1_lon'],2)
        self.failUnlessAlmostEqual(r['corner1_lat'],params['corner1_lat'],2)
        self.failUnlessAlmostEqual(r['corner2_lon'],params['corner2_lon'],2)
        self.failUnlessAlmostEqual(r['corner2_lat'],params['corner2_lat'],2)
        self.failUnlessEqual(r['IndicatorType'],params['IndicatorType'])
        self.failUnlessEqual(r['ChanABandwidth'],params['ChanABandwidth'])
        self.failUnlessEqual(r['ChanBBandwidth'],params['ChanBBandwidth'])
        self.failUnlessEqual(r['TransZoneSize'],params['TransZoneSize'])
        self.failUnlessEqual(r['Spare2'],params['Spare2'])

def addMsgOptions(parser):
    parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
                help='decode a "ChanMngmt" AIS message')
    parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
                help='encode a "ChanMngmt" AIS message')
    parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--ChanA-field', dest='ChanAField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--ChanB-field', dest='ChanBField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--TxRxMode-field', dest='TxRxModeField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--power-field', dest='powerField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--corner1_lon-field', dest='corner1_lonField',default=Decimal('181'),metavar='decimal',type='string'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--corner1_lat-field', dest='corner1_latField',default=Decimal('181'),metavar='decimal',type='string'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--corner2_lon-field', dest='corner2_lonField',default=Decimal('181'),metavar='decimal',type='string'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--corner2_lat-field', dest='corner2_latField',default=Decimal('181'),metavar='decimal',type='string'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--IndicatorType-field', dest='IndicatorTypeField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--ChanABandwidth-field', dest='ChanABandwidthField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--ChanBBandwidth-field', dest='ChanBBandwidthField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--TransZoneSize-field', dest='TransZoneSizeField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')

def main():
    from optparse import OptionParser
    parser = OptionParser(usage="%prog [options]")

    parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
        help='run the unit tests')
    parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
        help='Make the test output verbose')

    # FIX: remove nmea from binary messages.  No way to build the whole packet?
    # FIX: or build the surrounding msg 8 for a broadcast?
    typeChoices = ('binary','nmeapayload','nmea') # FIX: what about a USCG type message?
    parser.add_option('-t', '--type', choices=typeChoices, type='choice',
        dest='ioType', default='nmeapayload',
        help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]')


    outputChoices = ('std','html','csv','sql' )
    parser.add_option('-T', '--output-type', choices=outputChoices,
        type='choice', dest='outputType', default='std',
        help='What kind of string to output ('+', '.join(outputChoices)+') '
        '[default: %default]')

    parser.add_option('-o','--output',dest='outputFileName',default=None,
        help='Name of the python file to write [default: stdout]')

    parser.add_option('-f', '--fields', dest='fieldList', default=None,
        action='append', choices=fieldList,
        help='Which fields to include in the output.  Currently only for csv '
        'output [default: all]')

    parser.add_option('-p', '--print-csv-field-list', dest='printCsvfieldList',
        default=False,action='store_true',
        help='Print the field name for csv')

    parser.add_option('-c', '--sql-create', dest='sqlCreate', default=False,
        action='store_true',
        help='Print out an sql create command for the table.')

    parser.add_option('--latex-table', dest='latexDefinitionTable',
        default=False,action='store_true',
        help='Print a LaTeX table of the type')

    parser.add_option('--text-table', dest='textDefinitionTable', default=False,
        action='store_true',
        help='Print delimited table of the type (for Word table importing)')

    parser.add_option('--delimt-text-table', dest='delimTextDefinitionTable',
        default='    ',
        help='Delimiter for text table [default: \'%default\'] '
        '(for Word table importing)')

    dbChoices = ('sqlite','postgres')
    parser.add_option('-D', '--db-type', dest='dbType', default='postgres',
        choices=dbChoices,type='choice',
        help='What kind of database ('+', '.join(dbChoices)+') '
        '[default: %default]')

    addMsgOptions(parser)

    options, args = parser.parse_args()

    if options.unittest:
            sys.argv = [sys.argv[0]]
            if options.verbose: sys.argv.append('-v')
            unittest.main()

    outfile = sys.stdout
    if None!=options.outputFileName:
            outfile = file(options.outputFileName,'w')


    if options.doEncode:
        # Make sure all non required options are specified.
        if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
        if None==options.UserIDField: parser.error("missing value for UserIDField")
        if None==options.ChanAField: parser.error("missing value for ChanAField")
        if None==options.ChanBField: parser.error("missing value for ChanBField")
        if None==options.TxRxModeField: parser.error("missing value for TxRxModeField")
        if None==options.powerField: parser.error("missing value for powerField")
        if None==options.corner1_lonField: parser.error("missing value for corner1_lonField")
        if None==options.corner1_latField: parser.error("missing value for corner1_latField")
        if None==options.corner2_lonField: parser.error("missing value for corner2_lonField")
        if None==options.corner2_latField: parser.error("missing value for corner2_latField")
        if None==options.IndicatorTypeField: parser.error("missing value for IndicatorTypeField")
        if None==options.ChanABandwidthField: parser.error("missing value for ChanABandwidthField")
        if None==options.ChanBBandwidthField: parser.error("missing value for ChanBBandwidthField")
        if None==options.TransZoneSizeField: parser.error("missing value for TransZoneSizeField")
    msgDict = {
        'MessageID': '22',
        'RepeatIndicator': options.RepeatIndicatorField,
        'UserID': options.UserIDField,
        'Spare': '0',
        'ChanA': options.ChanAField,
        'ChanB': options.ChanBField,
        'TxRxMode': options.TxRxModeField,
        'power': options.powerField,
        'corner1_lon': options.corner1_lonField,
        'corner1_lat': options.corner1_latField,
        'corner2_lon': options.corner2_lonField,
        'corner2_lat': options.corner2_latField,
        'IndicatorType': options.IndicatorTypeField,
        'ChanABandwidth': options.ChanABandwidthField,
        'ChanBBandwidth': options.ChanBBandwidthField,
        'TransZoneSize': options.TransZoneSizeField,
        'Spare2': '0',
    }

    bits = encode(msgDict)
    if 'binary' == options.ioType:
        print str(bits)
    elif 'nmeapayload'==options.ioType:
        # FIX: figure out if this might be necessary at compile time
        bitLen=len(bits)
        if bitLen % 6 != 0:
            bits = bits + BitVector(size=(6 - (bitLen%6)))  # Pad out to multiple of 6
        print binary.bitvectoais6(bits)[0]

    # FIX: Do not emit this option for the binary message payloads.  Does not make sense.
    elif 'nmea' == options.ioType:
        nmea = uscg.create_nmea(bits)
        print nmea
    else:
        sys.exit('ERROR: unknown ioType.  Help!')


        if options.sqlCreate:
                sqlCreateStr(outfile,options.fieldList,dbType=options.dbType)

        if options.latexDefinitionTable:
                latexDefinitionTable(outfile)

        # For conversion to word tables
        if options.textDefinitionTable:
                textDefinitionTable(outfile,options.delimTextDefinitionTable)

        if options.printCsvfieldList:
                # Make a csv separated list of fields that will be displayed for csv
                if None == options.fieldList: options.fieldList = fieldList
                import StringIO
                buf = StringIO.StringIO()
                for field in options.fieldList:
                        buf.write(field+',')
                result = buf.getvalue()
                if result[-1] == ',': print result[:-1]
                else: print result

        if options.doDecode:
                if len(args)==0: args = sys.stdin
                for msg in args:
                        bv = None

                        if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'):
                                # Found nmea
                                # FIX: do checksum
                                bv = binary.ais6tobitvec(msg.split(',')[5])
                        else: # either binary or nmeapayload... expect mostly nmeapayloads
                                # assumes that an all 0 and 1 string can not be a nmeapayload
                                binaryMsg=True
                                for c in msg:
                                        if c not in ('0','1'):
                                                binaryMsg=False
                                                break
                                if binaryMsg:
                                        bv = BitVector(bitstring=msg)
                                else: # nmeapayload
                                        bv = binary.ais6tobitvec(msg)

                        printFields(decode(bv)
                                    ,out=outfile
                                    ,format=options.outputType
                                    ,fieldList=options.fieldList
                                    ,dbType=options.dbType
                                    )

############################################################
if __name__=='__main__':
    main()

#!/usr/bin/env python

"""Autogenerated python functions to serialize/deserialize binary messages.

Need to then wrap these functions with the outer AIS packet and then
convert the whole binary blob to a NMEA string.  Those functions are
not currently provided in this file.

serialize: python to ais binary
deserialize: ais binary to python

The generated code uses translators.py, binary.py, and aisstring.py
which should be packaged with the resulting files.

TODO(schwehr): Put in a description of the message here with fields and types.
"""
import sys
from decimal import Decimal
import unittest

from aisutils.BitVector import BitVector

from aisutils import aisstring
from aisutils import binary
from aisutils import sqlhelp
from aisutils import uscg


fieldList = (
    'MessageID',
    'RepeatIndicator',
    'UserID',
    'Spare',
    'ChanA',
    'ChanB',
    'TxRxMode',
    'power',
    'corner1_lon',
    'corner1_lat',
    'corner2_lon',
    'corner2_lat',
    'IndicatorType',
    'ChanABandwidth',
    'ChanBBandwidth',
    'TransZoneSize',
    'Spare2',
)

fieldListPostgres = (
    'MessageID',
    'RepeatIndicator',
    'UserID',
    'Spare',
    'ChanA',
    'ChanB',
    'TxRxMode',
    'power',
    'corner1',    # PostGIS data type
    'corner2',    # PostGIS data type
    'IndicatorType',
    'ChanABandwidth',
    'ChanBBandwidth',
    'TransZoneSize',
    'Spare2',
)

toPgFields = {
    'corner1_lon':'corner1',
    'corner1_lat':'corner1',
    'corner2_lon':'corner2',
    'corner2_lat':'corner2',
}
"""
Go to the Postgis field names from the straight field name
"""

fromPgFields = {
    'corner1':('corner1_lon','corner1_lat',),
    'corner2':('corner2_lon','corner2_lat',),
}
"""
Go from the Postgis field names to the straight field name
"""

pgTypes = {
    'corner1':'POINT',
    'corner2':'POINT',
}
"""
Lookup table for each postgis field name to get its type.
"""

def encode(params, validate=False):
    '''Create a ChanMngmt binary message payload to pack into an AIS Msg ChanMngmt.

    Fields in params:
      - MessageID(uint): AIS message number.  Must be 22 (field automatically set to "22")
      - RepeatIndicator(uint): Indicated how many times a message has been repeated
      - UserID(uint): Unique ship identification number (MMSI)
      - Spare(uint): Not used.  Should be set to zero. (field automatically set to "0")
      - ChanA(uint): Channel number from ITU-R M.1084 Annex 4
      - ChanB(uint): Channel number from ITU-R M.1084 Annex 4
      - TxRxMode(uint): FIX: find the description
      - power(uint): FIX: put in a description
      - corner1_lon(decimal): north-east corner of area for assignment  longitude of corner
      - corner1_lat(decimal): north-east corner of area for assignment  latitude of corner
      - corner2_lon(decimal): south-west corner of area for assignment  longitude of corner
      - corner2_lat(decimal): south-west corner of area for assignment  latitude of corner
      - IndicatorType(uint): FIX: put in a description
      - ChanABandwidth(uint): FIX: put in a description
      - ChanBBandwidth(uint): FIX: put in a description
      - TransZoneSize(uint): FIX: put in a description
      - Spare2(uint): Not used.  Should be set to zero. (field automatically set to "0")
    @param params: Dictionary of field names/values.  Throws a ValueError exception if required is missing
    @param validate: Set to true to cause checking to occur.  Runs slower.  FIX: not implemented.
    @rtype: BitVector
    @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8
    @note: The returned bits may not be 6 bit aligned.  It is up to you to pad out the bits.
    '''

    bvList = []
    bvList.append(binary.setBitVectorSize(BitVector(intVal=22),6))
    if 'RepeatIndicator' in params:
        bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2))
    else:
        bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['ChanA']),12))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['ChanB']),12))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['TxRxMode']),4))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['power']),1))
    if 'corner1_lon' in params:
        bvList.append(binary.bvFromSignedInt(int(Decimal(params['corner1_lon'])*Decimal('600')),18))
    else:
        bvList.append(binary.bvFromSignedInt(108600,18))
    if 'corner1_lat' in params:
        bvList.append(binary.bvFromSignedInt(int(Decimal(params['corner1_lat'])*Decimal('600')),17))
    else:
        bvList.append(binary.bvFromSignedInt(108600,17))
    if 'corner2_lon' in params:
        bvList.append(binary.bvFromSignedInt(int(Decimal(params['corner2_lon'])*Decimal('600')),18))
    else:
        bvList.append(binary.bvFromSignedInt(108600,18))
    if 'corner2_lat' in params:
        bvList.append(binary.bvFromSignedInt(int(Decimal(params['corner2_lat'])*Decimal('600')),17))
    else:
        bvList.append(binary.bvFromSignedInt(108600,17))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['IndicatorType']),1))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['ChanABandwidth']),1))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['ChanBBandwidth']),1))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=params['TransZoneSize']),3))
    bvList.append(binary.setBitVectorSize(BitVector(intVal=0),23))

    return binary.joinBV(bvList)

def decode(bv, validate=False):
    '''Unpack a ChanMngmt message.

    Fields in params:
      - MessageID(uint): AIS message number.  Must be 22 (field automatically set to "22")
      - RepeatIndicator(uint): Indicated how many times a message has been repeated
      - UserID(uint): Unique ship identification number (MMSI)
      - Spare(uint): Not used.  Should be set to zero. (field automatically set to "0")
      - ChanA(uint): Channel number from ITU-R M.1084 Annex 4
      - ChanB(uint): Channel number from ITU-R M.1084 Annex 4
      - TxRxMode(uint): FIX: find the description
      - power(uint): FIX: put in a description
      - corner1_lon(decimal): north-east corner of area for assignment  longitude of corner
      - corner1_lat(decimal): north-east corner of area for assignment  latitude of corner
      - corner2_lon(decimal): south-west corner of area for assignment  longitude of corner
      - corner2_lat(decimal): south-west corner of area for assignment  latitude of corner
      - IndicatorType(uint): FIX: put in a description
      - ChanABandwidth(uint): FIX: put in a description
      - ChanBBandwidth(uint): FIX: put in a description
      - TransZoneSize(uint): FIX: put in a description
      - Spare2(uint): Not used.  Should be set to zero. (field automatically set to "0")
    @type bv: BitVector
    @param bv: Bits defining a message
    @param validate: Set to true to cause checking to occur.  Runs slower.  FIX: not implemented.
    @rtype: dict
    @return: params
    '''

    #Would be nice to check the bit count here..
    #if validate:
    #    assert (len(bv)==FIX: SOME NUMBER)
    r = {}
    r['MessageID']=22
    r['RepeatIndicator']=int(bv[6:8])
    r['UserID']=int(bv[8:38])
    r['Spare']=0
    r['ChanA']=int(bv[40:52])
    r['ChanB']=int(bv[52:64])
    r['TxRxMode']=int(bv[64:68])
    r['power']=int(bv[68:69])
    r['corner1_lon']=Decimal(binary.signedIntFromBV(bv[69:87]))/Decimal('600')
    r['corner1_lat']=Decimal(binary.signedIntFromBV(bv[87:104]))/Decimal('600')
    r['corner2_lon']=Decimal(binary.signedIntFromBV(bv[104:122]))/Decimal('600')
    r['corner2_lat']=Decimal(binary.signedIntFromBV(bv[122:139]))/Decimal('600')
    r['IndicatorType']=int(bv[139:140])
    r['ChanABandwidth']=int(bv[140:141])
    r['ChanBBandwidth']=int(bv[141:142])
    r['TransZoneSize']=int(bv[142:145])
    r['Spare2']=0
    return r

def decodeMessageID(bv, validate=False):
    return 22

def decodeRepeatIndicator(bv, validate=False):
    return int(bv[6:8])

def decodeUserID(bv, validate=False):
    return int(bv[8:38])

def decodeSpare(bv, validate=False):
    return 0

def decodeChanA(bv, validate=False):
    return int(bv[40:52])

def decodeChanB(bv, validate=False):
    return int(bv[52:64])

def decodeTxRxMode(bv, validate=False):
    return int(bv[64:68])

def decodepower(bv, validate=False):
    return int(bv[68:69])

def decodecorner1_lon(bv, validate=False):
    return Decimal(binary.signedIntFromBV(bv[69:87]))/Decimal('600')

def decodecorner1_lat(bv, validate=False):
    return Decimal(binary.signedIntFromBV(bv[87:104]))/Decimal('600')

def decodecorner2_lon(bv, validate=False):
    return Decimal(binary.signedIntFromBV(bv[104:122]))/Decimal('600')

def decodecorner2_lat(bv, validate=False):
    return Decimal(binary.signedIntFromBV(bv[122:139]))/Decimal('600')

def decodeIndicatorType(bv, validate=False):
    return int(bv[139:140])

def decodeChanABandwidth(bv, validate=False):
    return int(bv[140:141])

def decodeChanBBandwidth(bv, validate=False):
    return int(bv[141:142])

def decodeTransZoneSize(bv, validate=False):
    return int(bv[142:145])

def decodeSpare2(bv, validate=False):
    return 0


def printHtml(params, out=sys.stdout):
        out.write("<h3>ChanMngmt</h3>\n")
        out.write("<table border=\"1\">\n")
        out.write("<tr bgcolor=\"orange\">\n")
        out.write("<th align=\"left\">Field Name</th>\n")
        out.write("<th align=\"left\">Type</th>\n")
        out.write("<th align=\"left\">Value</th>\n")
        out.write("<th align=\"left\">Value in Lookup Table</th>\n")
        out.write("<th align=\"left\">Units</th>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>MessageID</td>\n")
        out.write("<td>uint</td>\n")
        if 'MessageID' in params:
            out.write("    <td>"+str(params['MessageID'])+"</td>\n")
            out.write("    <td>"+str(params['MessageID'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>RepeatIndicator</td>\n")
        out.write("<td>uint</td>\n")
        if 'RepeatIndicator' in params:
            out.write("    <td>"+str(params['RepeatIndicator'])+"</td>\n")
            if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut:
                out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>UserID</td>\n")
        out.write("<td>uint</td>\n")
        if 'UserID' in params:
            out.write("    <td>"+str(params['UserID'])+"</td>\n")
            out.write("    <td>"+str(params['UserID'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>Spare</td>\n")
        out.write("<td>uint</td>\n")
        if 'Spare' in params:
            out.write("    <td>"+str(params['Spare'])+"</td>\n")
            out.write("    <td>"+str(params['Spare'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>ChanA</td>\n")
        out.write("<td>uint</td>\n")
        if 'ChanA' in params:
            out.write("    <td>"+str(params['ChanA'])+"</td>\n")
            out.write("    <td>"+str(params['ChanA'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>ChanB</td>\n")
        out.write("<td>uint</td>\n")
        if 'ChanB' in params:
            out.write("    <td>"+str(params['ChanB'])+"</td>\n")
            out.write("    <td>"+str(params['ChanB'])+"</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>TxRxMode</td>\n")
        out.write("<td>uint</td>\n")
        if 'TxRxMode' in params:
            out.write("    <td>"+str(params['TxRxMode'])+"</td>\n")
            if str(params['TxRxMode']) in TxRxModeDecodeLut:
                out.write("<td>"+TxRxModeDecodeLut[str(params['TxRxMode'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>power</td>\n")
        out.write("<td>uint</td>\n")
        if 'power' in params:
            out.write("    <td>"+str(params['power'])+"</td>\n")
            if str(params['power']) in powerDecodeLut:
                out.write("<td>"+powerDecodeLut[str(params['power'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>corner1_lon</td>\n")
        out.write("<td>decimal</td>\n")
        if 'corner1_lon' in params:
            out.write("    <td>"+str(params['corner1_lon'])+"</td>\n")
            out.write("    <td>"+str(params['corner1_lon'])+"</td>\n")
        out.write("<td>degrees</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>corner1_lat</td>\n")
        out.write("<td>decimal</td>\n")
        if 'corner1_lat' in params:
            out.write("    <td>"+str(params['corner1_lat'])+"</td>\n")
            out.write("    <td>"+str(params['corner1_lat'])+"</td>\n")
        out.write("<td>degrees</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>corner2_lon</td>\n")
        out.write("<td>decimal</td>\n")
        if 'corner2_lon' in params:
            out.write("    <td>"+str(params['corner2_lon'])+"</td>\n")
            out.write("    <td>"+str(params['corner2_lon'])+"</td>\n")
        out.write("<td>degrees</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>corner2_lat</td>\n")
        out.write("<td>decimal</td>\n")
        if 'corner2_lat' in params:
            out.write("    <td>"+str(params['corner2_lat'])+"</td>\n")
            out.write("    <td>"+str(params['corner2_lat'])+"</td>\n")
        out.write("<td>degrees</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>IndicatorType</td>\n")
        out.write("<td>uint</td>\n")
        if 'IndicatorType' in params:
            out.write("    <td>"+str(params['IndicatorType'])+"</td>\n")
            if str(params['IndicatorType']) in IndicatorTypeDecodeLut:
                out.write("<td>"+IndicatorTypeDecodeLut[str(params['IndicatorType'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>ChanABandwidth</td>\n")
        out.write("<td>uint</td>\n")
        if 'ChanABandwidth' in params:
            out.write("    <td>"+str(params['ChanABandwidth'])+"</td>\n")
            if str(params['ChanABandwidth']) in ChanABandwidthDecodeLut:
                out.write("<td>"+ChanABandwidthDecodeLut[str(params['ChanABandwidth'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>ChanBBandwidth</td>\n")
        out.write("<td>uint</td>\n")
        if 'ChanBBandwidth' in params:
            out.write("    <td>"+str(params['ChanBBandwidth'])+"</td>\n")
            if str(params['ChanBBandwidth']) in ChanBBandwidthDecodeLut:
                out.write("<td>"+ChanBBandwidthDecodeLut[str(params['ChanBBandwidth'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>TransZoneSize</td>\n")
        out.write("<td>uint</td>\n")
        if 'TransZoneSize' in params:
            out.write("    <td>"+str(params['TransZoneSize'])+"</td>\n")
            if str(params['TransZoneSize']) in TransZoneSizeDecodeLut:
                out.write("<td>"+TransZoneSizeDecodeLut[str(params['TransZoneSize'])]+"</td>")
            else:
                out.write("<td><i>Missing LUT entry</i></td>")
        out.write("<td>nm</td>\n")
        out.write("</tr>\n")
        out.write("\n")
        out.write("<tr>\n")
        out.write("<td>Spare2</td>\n")
        out.write("<td>uint</td>\n")
        if 'Spare2' in params:
            out.write("    <td>"+str(params['Spare2'])+"</td>\n")
            out.write("    <td>"+str(params['Spare2'])+"</td>\n")
        out.write("</tr>\n")
        out.write("</table>\n")

def printFields(params, out=sys.stdout, format='std', fieldList=None, dbType='postgres'):
    '''Print a ChanMngmt message to stdout.

    Fields in params:
      - MessageID(uint): AIS message number.  Must be 22 (field automatically set to "22")
      - RepeatIndicator(uint): Indicated how many times a message has been repeated
      - UserID(uint): Unique ship identification number (MMSI)
      - Spare(uint): Not used.  Should be set to zero. (field automatically set to "0")
      - ChanA(uint): Channel number from ITU-R M.1084 Annex 4
      - ChanB(uint): Channel number from ITU-R M.1084 Annex 4
      - TxRxMode(uint): FIX: find the description
      - power(uint): FIX: put in a description
      - corner1_lon(decimal): north-east corner of area for assignment  longitude of corner
      - corner1_lat(decimal): north-east corner of area for assignment  latitude of corner
      - corner2_lon(decimal): south-west corner of area for assignment  longitude of corner
      - corner2_lat(decimal): south-west corner of area for assignment  latitude of corner
      - IndicatorType(uint): FIX: put in a description
      - ChanABandwidth(uint): FIX: put in a description
      - ChanBBandwidth(uint): FIX: put in a description
      - TransZoneSize(uint): FIX: put in a description
      - Spare2(uint): Not used.  Should be set to zero. (field automatically set to "0")
    @param params: Dictionary of field names/values.
    @param out: File like object to write to.
    @rtype: stdout
    @return: text to out
    '''

    if 'std'==format:
        out.write("ChanMngmt:\n")
        if 'MessageID' in params: out.write("    MessageID:        "+str(params['MessageID'])+"\n")
        if 'RepeatIndicator' in params: out.write("    RepeatIndicator:  "+str(params['RepeatIndicator'])+"\n")
        if 'UserID' in params: out.write("    UserID:           "+str(params['UserID'])+"\n")
        if 'Spare' in params: out.write("    Spare:            "+str(params['Spare'])+"\n")
        if 'ChanA' in params: out.write("    ChanA:            "+str(params['ChanA'])+"\n")
        if 'ChanB' in params: out.write("    ChanB:            "+str(params['ChanB'])+"\n")
        if 'TxRxMode' in params: out.write("    TxRxMode:         "+str(params['TxRxMode'])+"\n")
        if 'power' in params: out.write("    power:            "+str(params['power'])+"\n")
        if 'corner1_lon' in params: out.write("    corner1_lon:      "+str(params['corner1_lon'])+"\n")
        if 'corner1_lat' in params: out.write("    corner1_lat:      "+str(params['corner1_lat'])+"\n")
        if 'corner2_lon' in params: out.write("    corner2_lon:      "+str(params['corner2_lon'])+"\n")
        if 'corner2_lat' in params: out.write("    corner2_lat:      "+str(params['corner2_lat'])+"\n")
        if 'IndicatorType' in params: out.write("    IndicatorType:    "+str(params['IndicatorType'])+"\n")
        if 'ChanABandwidth' in params: out.write("    ChanABandwidth:   "+str(params['ChanABandwidth'])+"\n")
        if 'ChanBBandwidth' in params: out.write("    ChanBBandwidth:   "+str(params['ChanBBandwidth'])+"\n")
        if 'TransZoneSize' in params: out.write("    TransZoneSize:    "+str(params['TransZoneSize'])+"\n")
        if 'Spare2' in params: out.write("    Spare2:           "+str(params['Spare2'])+"\n")
        elif 'csv'==format:
                if None == options.fieldList:
                        options.fieldList = fieldList
                needComma = False;
                for field in fieldList:
                        if needComma: out.write(',')
                        needComma = True
                        if field in params:
                                out.write(str(params[field]))
                        # else: leave it empty
                out.write("\n")
    elif 'html'==format:
        printHtml(params,out)
    elif 'sql'==format:
                sqlInsertStr(params,out,dbType=dbType)
    else:
        print "ERROR: unknown format:",format
        assert False

    return # Nothing to return

RepeatIndicatorEncodeLut = {
    'default':'0',
    'do not repeat any more':'3',
    } #RepeatIndicatorEncodeLut

RepeatIndicatorDecodeLut = {
    '0':'default',
    '3':'do not repeat any more',
    } # RepeatIndicatorEncodeLut

TxRxModeEncodeLut = {
    'Tx A/Tx B, Rx A/RX B':'0',
    'Tx A, Rx A/Rx B':'1',
    'Tx B, Rx A/Rx B':'2',
    } #TxRxModeEncodeLut

TxRxModeDecodeLut = {
    '0':'Tx A/Tx B, Rx A/RX B',
    '1':'Tx A, Rx A/Rx B',
    '2':'Tx B, Rx A/Rx B',
    } # TxRxModeEncodeLut

powerEncodeLut = {
    'high':'0',
    'low':'1',
    } #powerEncodeLut

powerDecodeLut = {
    '0':'high',
    '1':'low',
    } # powerEncodeLut

IndicatorTypeEncodeLut = {
    'broadcast':'0',
    'addressed':'1',
    } #IndicatorTypeEncodeLut

IndicatorTypeDecodeLut = {
    '0':'broadcast',
    '1':'addressed',
    } # IndicatorTypeEncodeLut

ChanABandwidthEncodeLut = {
    'specified by channel number':'0',
    '12.5kHz':'1',
    } #ChanABandwidthEncodeLut

ChanABandwidthDecodeLut = {
    '0':'specified by channel number',
    '1':'12.5kHz',
    } # ChanABandwidthEncodeLut

ChanBBandwidthEncodeLut = {
    'specified by channel number':'0',
    '12.5kHz':'1',
    } #ChanBBandwidthEncodeLut

ChanBBandwidthDecodeLut = {
    '0':'specified by channel number',
    '1':'12.5kHz',
    } # ChanBBandwidthEncodeLut

TransZoneSizeEncodeLut = {
    '1':'0',
    '2':'1',
    '3':'2',
    '4':'3',
    '5':'4',
    '6':'5',
    '7':'6',
    '8':'7',
    } #TransZoneSizeEncodeLut

TransZoneSizeDecodeLut = {
    '0':'1',
    '1':'2',
    '2':'3',
    '3':'4',
    '4':'5',
    '5':'6',
    '6':'7',
    '7':'8',
    } # TransZoneSizeEncodeLut

######################################################################
# SQL SUPPORT
######################################################################

dbTableName='ChanMngmt'
'Database table name'

def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None
                ,addCoastGuardFields=True
                ,dbType='postgres'
                ):
        """
        Return the SQL CREATE command for this message type
        @param outfile: file like object to print to.
        @param fields: which fields to put in the create.  Defaults to all.
        @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
        @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
        @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
        @type addCoastGuardFields: bool
        @return: sql create string
        @rtype: str

        @see: sqlCreate
        """
        # FIX: should this sqlCreate be the same as in LaTeX (createFuncName) rather than hard coded?
        outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))

def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
    """Return the sqlhelp object to create the table.

    @param fields: which fields to put in the create.  Defaults to all.
    @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
    @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
    @type addCoastGuardFields: bool
    @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
    @return: An object that can be used to generate a return
    @rtype: sqlhelp.create
    """
    if fields is None:
        fields = fieldList
    c = sqlhelp.create('ChanMngmt',dbType=dbType)
    c.addPrimaryKey()
    if 'MessageID' in fields: c.addInt ('MessageID')
    if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator')
    if 'UserID' in fields: c.addInt ('UserID')
    if 'Spare' in fields: c.addInt ('Spare')
    if 'ChanA' in fields: c.addInt ('ChanA')
    if 'ChanB' in fields: c.addInt ('ChanB')
    if 'TxRxMode' in fields: c.addInt ('TxRxMode')
    if 'power' in fields: c.addInt ('power')
    if dbType != 'postgres':
        if 'corner1_lon' in fields: c.addDecimal('corner1_lon',5,2)
    if dbType != 'postgres':
        if 'corner1_lat' in fields: c.addDecimal('corner1_lat',5,2)
    if dbType != 'postgres':
        if 'corner2_lon' in fields: c.addDecimal('corner2_lon',5,2)
    if dbType != 'postgres':
        if 'corner2_lat' in fields: c.addDecimal('corner2_lat',5,2)
    if 'IndicatorType' in fields: c.addInt ('IndicatorType')
    if 'ChanABandwidth' in fields: c.addInt ('ChanABandwidth')
    if 'ChanBBandwidth' in fields: c.addInt ('ChanBBandwidth')
    if 'TransZoneSize' in fields: c.addInt ('TransZoneSize')
    if 'Spare2' in fields: c.addInt ('Spare2')

    if addCoastGuardFields:
        # c.addInt('cg_s_rssi')  # Relative signal strength indicator
        # c.addInt('cg_d_strength')  # dBm receive strength
        # c.addVarChar('cg_x',10)  # Idonno
        c.addInt('cg_t_arrival')  # Receive timestamp from the AIS equipment 'T'
        c.addInt('cg_s_slotnum')  # Slot received in
        c.addVarChar('cg_r',15)  # Receiver station ID  -  should usually be an MMSI, but sometimes is a string
        c.addInt('cg_sec')  # UTC seconds since the epoch

        c.addTimestamp('cg_timestamp') # UTC decoded cg_sec - not actually in the data stream

    if dbType == 'postgres':
        #--- EPSG 4326 : WGS 84
        #INSERT INTO "spatial_ref_sys" ("srid","auth_name","auth_srid","srtext","proj4text") VALUES (4326,'EPSG',4326,'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]','+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ');
        c.addPostGIS('corner1','POINT',2,SRID=4326);
        #--- EPSG 4326 : WGS 84
        #INSERT INTO "spatial_ref_sys" ("srid","auth_name","auth_srid","srtext","proj4text") VALUES (4326,'EPSG',4326,'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]','+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ');
        c.addPostGIS('corner2','POINT',2,SRID=4326);

    return c

def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
        """
        Return the SQL INSERT command for this message type
        @param params: dictionary of values keyed by field name
        @param outfile: file like object to print to.
        @param extraParams: A sequence of tuples containing (name,sql type) for additional fields
        @return: sql create string
        @rtype: str

        @see: sqlCreate
        """
        outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))


def sqlInsert(params,extraParams=None,dbType='postgres'):
        """
        Give the SQL INSERT statement
        @param params: dict keyed by field name of values
        @param extraParams: any extra fields that you have created beyond the normal ais message fields
        @rtype: sqlhelp.insert
        @return: insert class instance
         TODO(schwehr):allow optional type checking of params?
        @warning: this will take invalid keys happily and do what???
        """

        i = sqlhelp.insert('ChanMngmt',dbType=dbType)

        if dbType=='postgres':
                finished = []
                for key in params:
                        if key in finished:
                                continue

                        if key not in toPgFields and key not in fromPgFields:
                                if type(params[key])==Decimal: i.add(key,float(params[key]))
                                else: i.add(key,params[key])
                        else:
                                if key in fromPgFields:
                                        val = params[key]
                                        # Had better be a WKT type like POINT(-88.1 30.321)
                                        i.addPostGIS(key,val)
                                        finished.append(key)
                                else:
                                        # Need to construct the type.
                                        pgName = toPgFields[key]
                                        #valStr='GeomFromText(\''+pgTypes[pgName]+'('
                                        valStr=pgTypes[pgName]+'('
                                        vals = []
                                        for nonPgKey in fromPgFields[pgName]:
                                                vals.append(str(params[nonPgKey]))
                                                finished.append(nonPgKey)
                                        valStr+=' '.join(vals)+')'
                                        i.addPostGIS(pgName,valStr)
        else:
                for key in params:
                        if type(params[key])==Decimal: i.add(key,float(params[key]))
                        else: i.add(key,params[key])

        if None != extraParams:
                for key in extraParams:
                        i.add(key,extraParams[key])

        return i

######################################################################
# LATEX SUPPORT
######################################################################

def latexDefinitionTable(outfile=sys.stdout
                ):
        """
        Return the LaTeX definition table for this message type
        @param outfile: file like object to print to.
        @type outfile: file obj
        @return: LaTeX table string via the outfile
        @rtype: str

        """
        o = outfile

        o.write("""
\\begin{table}%[htb]
\\centering
\\begin{tabular}{|l|c|l|}
\\hline
Parameter & Number of bits & Description
\\\\  \\hline\\hline
MessageID & 6 & AIS message number.  Must be 22 \\\\ \hline
RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline
UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline
Spare & 2 & Not used.  Should be set to zero. \\\\ \hline
ChanA & 12 & Channel number from ITU-R M.1084 Annex 4 \\\\ \hline
ChanB & 12 & Channel number from ITU-R M.1084 Annex 4 \\\\ \hline
TxRxMode & 4 & FIX: find the description \\\\ \hline
power & 1 & FIX: put in a description \\\\ \hline
corner1\_lon & 18 & north-east corner of area for assignment  longitude of corner \\\\ \hline
corner1\_lat & 17 & north-east corner of area for assignment  latitude of corner \\\\ \hline
corner2\_lon & 18 & south-west corner of area for assignment  longitude of corner \\\\ \hline
corner2\_lat & 17 & south-west corner of area for assignment  latitude of corner \\\\ \hline
IndicatorType & 1 & FIX: put in a description \\\\ \hline
ChanABandwidth & 1 & FIX: put in a description \\\\ \hline
ChanBBandwidth & 1 & FIX: put in a description \\\\ \hline
TransZoneSize & 3 & FIX: put in a description \\\\ \hline
Spare2 & 23 & Not used.  Should be set to zero.\\\\ \\hline \\hline
Total bits & 168 & Appears to take 1 slot \\\\ \\hline
\\end{tabular}
\\caption{AIS message number 22: Base station report - F}
\\label{tab:ChanMngmt}
\\end{table}
""")

######################################################################
# Text Definition
######################################################################

def textDefinitionTable(outfile=sys.stdout ,delim='    '):
    """Return the text definition table for this message type

    @param outfile: file like object to print to.
    @type outfile: file obj
    @return: text table string via the outfile
    @rtype: str

    """
    o = outfile
    o.write('Parameter'+delim+'Number of bits'+delim+"""Description
MessageID"""+delim+'6'+delim+"""AIS message number.  Must be 22
RepeatIndicator"""+delim+'2'+delim+"""Indicated how many times a message has been repeated
UserID"""+delim+'30'+delim+"""Unique ship identification number (MMSI)
Spare"""+delim+'2'+delim+"""Not used.  Should be set to zero.
ChanA"""+delim+'12'+delim+"""Channel number from ITU-R M.1084 Annex 4
ChanB"""+delim+'12'+delim+"""Channel number from ITU-R M.1084 Annex 4
TxRxMode"""+delim+'4'+delim+"""FIX: find the description
power"""+delim+'1'+delim+"""FIX: put in a description
corner1_lon"""+delim+'18'+delim+"""north-east corner of area for assignment  longitude of corner
corner1_lat"""+delim+'17'+delim+"""north-east corner of area for assignment  latitude of corner
corner2_lon"""+delim+'18'+delim+"""south-west corner of area for assignment  longitude of corner
corner2_lat"""+delim+'17'+delim+"""south-west corner of area for assignment  latitude of corner
IndicatorType"""+delim+'1'+delim+"""FIX: put in a description
ChanABandwidth"""+delim+'1'+delim+"""FIX: put in a description
ChanBBandwidth"""+delim+'1'+delim+"""FIX: put in a description
TransZoneSize"""+delim+'3'+delim+"""FIX: put in a description
Spare2"""+delim+'23'+delim+"""Not used.  Should be set to zero.
Total bits"""+delim+"""168"""+delim+"""Appears to take 1 slot""")


######################################################################
# UNIT TESTING
######################################################################
def testParams():
    '''Return a params file base on the testvalue tags.
    @rtype: dict
    @return: params based on testvalue tags
    '''
    params = {}
    params['MessageID'] = 22
    params['RepeatIndicator'] = 1
    params['UserID'] = 1193046
    params['Spare'] = 0
    params['ChanA'] = 0
    params['ChanB'] = 0
    params['TxRxMode'] = 1
    params['power'] = 1
    params['corner1_lon'] = Decimal('-122.16')
    params['corner1_lat'] = Decimal('37.42')
    params['corner2_lon'] = Decimal('-122.16')
    params['corner2_lat'] = Decimal('37.42')
    params['IndicatorType'] = 1
    params['ChanABandwidth'] = 1
    params['ChanBBandwidth'] = 1
    params['TransZoneSize'] = 1
    params['Spare2'] = 0

    return params

class TestChanMngmt(unittest.TestCase):
    '''Use testvalue tag text from each type to build test case the ChanMngmt message'''
    def testEncodeDecode(self):

        params = testParams()
        bits   = encode(params)
        r      = decode(bits)

        # Check that each parameter came through ok.
        self.failUnlessEqual(r['MessageID'],params['MessageID'])
        self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
        self.failUnlessEqual(r['UserID'],params['UserID'])
        self.failUnlessEqual(r['Spare'],params['Spare'])
        self.failUnlessEqual(r['ChanA'],params['ChanA'])
        self.failUnlessEqual(r['ChanB'],params['ChanB'])
        self.failUnlessEqual(r['TxRxMode'],params['TxRxMode'])
        self.failUnlessEqual(r['power'],params['power'])
        self.failUnlessAlmostEqual(r['corner1_lon'],params['corner1_lon'],2)
        self.failUnlessAlmostEqual(r['corner1_lat'],params['corner1_lat'],2)
        self.failUnlessAlmostEqual(r['corner2_lon'],params['corner2_lon'],2)
        self.failUnlessAlmostEqual(r['corner2_lat'],params['corner2_lat'],2)
        self.failUnlessEqual(r['IndicatorType'],params['IndicatorType'])
        self.failUnlessEqual(r['ChanABandwidth'],params['ChanABandwidth'])
        self.failUnlessEqual(r['ChanBBandwidth'],params['ChanBBandwidth'])
        self.failUnlessEqual(r['TransZoneSize'],params['TransZoneSize'])
        self.failUnlessEqual(r['Spare2'],params['Spare2'])

def addMsgOptions(parser):
    parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
                help='decode a "ChanMngmt" AIS message')
    parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
                help='encode a "ChanMngmt" AIS message')
    parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--ChanA-field', dest='ChanAField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--ChanB-field', dest='ChanBField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--TxRxMode-field', dest='TxRxModeField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--power-field', dest='powerField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--corner1_lon-field', dest='corner1_lonField',default=Decimal('181'),metavar='decimal',type='string'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--corner1_lat-field', dest='corner1_latField',default=Decimal('181'),metavar='decimal',type='string'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--corner2_lon-field', dest='corner2_lonField',default=Decimal('181'),metavar='decimal',type='string'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--corner2_lat-field', dest='corner2_latField',default=Decimal('181'),metavar='decimal',type='string'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--IndicatorType-field', dest='IndicatorTypeField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--ChanABandwidth-field', dest='ChanABandwidthField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--ChanBBandwidth-field', dest='ChanBBandwidthField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')
    parser.add_option('--TransZoneSize-field', dest='TransZoneSizeField',metavar='uint',type='int'
        ,help='Field parameter value [default: %default]')

def main():
    from optparse import OptionParser
    parser = OptionParser(usage="%prog [options]")

    parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
        help='run the unit tests')
    parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
        help='Make the test output verbose')

    # FIX: remove nmea from binary messages.  No way to build the whole packet?
    # FIX: or build the surrounding msg 8 for a broadcast?
    typeChoices = ('binary','nmeapayload','nmea') # FIX: what about a USCG type message?
    parser.add_option('-t', '--type', choices=typeChoices, type='choice',
        dest='ioType', default='nmeapayload',
        help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]')


    outputChoices = ('std','html','csv','sql' )
    parser.add_option('-T', '--output-type', choices=outputChoices,
        type='choice', dest='outputType', default='std',
        help='What kind of string to output ('+', '.join(outputChoices)+') '
        '[default: %default]')

    parser.add_option('-o','--output',dest='outputFileName',default=None,
        help='Name of the python file to write [default: stdout]')

    parser.add_option('-f', '--fields', dest='fieldList', default=None,
        action='append', choices=fieldList,
        help='Which fields to include in the output.  Currently only for csv '
        'output [default: all]')

    parser.add_option('-p', '--print-csv-field-list', dest='printCsvfieldList',
        default=False,action='store_true',
        help='Print the field name for csv')

    parser.add_option('-c', '--sql-create', dest='sqlCreate', default=False,
        action='store_true',
        help='Print out an sql create command for the table.')

    parser.add_option('--latex-table', dest='latexDefinitionTable',
        default=False,action='store_true',
        help='Print a LaTeX table of the type')

    parser.add_option('--text-table', dest='textDefinitionTable', default=False,
        action='store_true',
        help='Print delimited table of the type (for Word table importing)')

    parser.add_option('--delimt-text-table', dest='delimTextDefinitionTable',
        default='    ',
        help='Delimiter for text table [default: \'%default\'] '
        '(for Word table importing)')

    dbChoices = ('sqlite','postgres')
    parser.add_option('-D', '--db-type', dest='dbType', default='postgres',
        choices=dbChoices,type='choice',
        help='What kind of database ('+', '.join(dbChoices)+') '
        '[default: %default]')

    addMsgOptions(parser)

    options, args = parser.parse_args()

    if options.unittest:
            sys.argv = [sys.argv[0]]
            if options.verbose: sys.argv.append('-v')
            unittest.main()

    outfile = sys.stdout
    if None!=options.outputFileName:
            outfile = file(options.outputFileName,'w')


    if options.doEncode:
        # Make sure all non required options are specified.
        if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
        if None==options.UserIDField: parser.error("missing value for UserIDField")
        if None==options.ChanAField: parser.error("missing value for ChanAField")
        if None==options.ChanBField: parser.error("missing value for ChanBField")
        if None==options.TxRxModeField: parser.error("missing value for TxRxModeField")
        if None==options.powerField: parser.error("missing value for powerField")
        if None==options.corner1_lonField: parser.error("missing value for corner1_lonField")
        if None==options.corner1_latField: parser.error("missing value for corner1_latField")
        if None==options.corner2_lonField: parser.error("missing value for corner2_lonField")
        if None==options.corner2_latField: parser.error("missing value for corner2_latField")
        if None==options.IndicatorTypeField: parser.error("missing value for IndicatorTypeField")
        if None==options.ChanABandwidthField: parser.error("missing value for ChanABandwidthField")
        if None==options.ChanBBandwidthField: parser.error("missing value for ChanBBandwidthField")
        if None==options.TransZoneSizeField: parser.error("missing value for TransZoneSizeField")
    msgDict = {
        'MessageID': '22',
        'RepeatIndicator': options.RepeatIndicatorField,
        'UserID': options.UserIDField,
        'Spare': '0',
        'ChanA': options.ChanAField,
        'ChanB': options.ChanBField,
        'TxRxMode': options.TxRxModeField,
        'power': options.powerField,
        'corner1_lon': options.corner1_lonField,
        'corner1_lat': options.corner1_latField,
        'corner2_lon': options.corner2_lonField,
        'corner2_lat': options.corner2_latField,
        'IndicatorType': options.IndicatorTypeField,
        'ChanABandwidth': options.ChanABandwidthField,
        'ChanBBandwidth': options.ChanBBandwidthField,
        'TransZoneSize': options.TransZoneSizeField,
        'Spare2': '0',
    }

    bits = encode(msgDict)
    if 'binary' == options.ioType:
        print str(bits)
    elif 'nmeapayload'==options.ioType:
        # FIX: figure out if this might be necessary at compile time
        bitLen=len(bits)
        if bitLen % 6 != 0:
            bits = bits + BitVector(size=(6 - (bitLen%6)))  # Pad out to multiple of 6
        print binary.bitvectoais6(bits)[0]

    # FIX: Do not emit this option for the binary message payloads.  Does not make sense.
    elif 'nmea' == options.ioType:
        nmea = uscg.create_nmea(bits)
        print nmea
    else:
        sys.exit('ERROR: unknown ioType.  Help!')


        if options.sqlCreate:
                sqlCreateStr(outfile,options.fieldList,dbType=options.dbType)

        if options.latexDefinitionTable:
                latexDefinitionTable(outfile)

        # For conversion to word tables
        if options.textDefinitionTable:
                textDefinitionTable(outfile,options.delimTextDefinitionTable)

        if options.printCsvfieldList:
                # Make a csv separated list of fields that will be displayed for csv
                if None == options.fieldList: options.fieldList = fieldList
                import StringIO
                buf = StringIO.StringIO()
                for field in options.fieldList:
                        buf.write(field+',')
                result = buf.getvalue()
                if result[-1] == ',': print result[:-1]
                else: print result

        if options.doDecode:
                if len(args)==0: args = sys.stdin
                for msg in args:
                        bv = None

                        if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'):
                                # Found nmea
                                # FIX: do checksum
                                bv = binary.ais6tobitvec(msg.split(',')[5])
                        else: # either binary or nmeapayload... expect mostly nmeapayloads
                                # assumes that an all 0 and 1 string can not be a nmeapayload
                                binaryMsg=True
                                for c in msg:
                                        if c not in ('0','1'):
                                                binaryMsg=False
                                                break
                                if binaryMsg:
                                        bv = BitVector(bitstring=msg)
                                else: # nmeapayload
                                        bv = binary.ais6tobitvec(msg)

                        printFields(decode(bv)
                                    ,out=outfile
                                    ,format=options.outputType
                                    ,fieldList=options.fieldList
                                    ,dbType=options.dbType
                                    )

############################################################
if __name__=='__main__':
    main()

#!/usr/bin/env python Autogenerated python functions to serialize/deserialize binary messages.  Need to then wrap these functions with the outer AIS packet and then convert the whole binary blob to a NMEA string.  Those functions are not currently provided in this file.  serialize: python to ais binary deserialize: ais binary to python  The generated code uses translators.py, binary.py, and aisstring.py which should be packaged with the resulting files.  TODO(schwehr): Put in a description of the message here with fields and types. # PostGIS data type # PostGIS data type Go to the Postgis field names from the straight field name Go from the Postgis field names to the straight field name Lookup table for each postgis field name to get its type. Create a ChanMngmt binary message payload to pack into an AIS Msg ChanMngmt.      Fields in params:       - MessageID(uint): AIS message number.  Must be 22 (field automatically set to "22")       - RepeatIndicator(uint): Indicated how many times a message has been repeated       - UserID(uint): Unique ship identification number (MMSI)       - Spare(uint): Not used.  Should be set to zero. (field automatically set to "0")       - ChanA(uint): Channel number from ITU-R M.1084 Annex 4       - ChanB(uint): Channel number from ITU-R M.1084 Annex 4       - TxRxMode(uint): FIX: find the description       - power(uint): FIX: put in a description       - corner1_lon(decimal): north-east corner of area for assignment  longitude of corner       - corner1_lat(decimal): north-east corner of area for assignment  latitude of corner       - corner2_lon(decimal): south-west corner of area for assignment  longitude of corner       - corner2_lat(decimal): south-west corner of area for assignment  latitude of corner       - IndicatorType(uint): FIX: put in a description       - ChanABandwidth(uint): FIX: put in a description       - ChanBBandwidth(uint): FIX: put in a description       - TransZoneSize(uint): FIX: put in a description       - Spare2(uint): Not used.  Should be set to zero. (field automatically set to "0")     @param params: Dictionary of field names/values.  Throws a ValueError exception if required is missing     @param validate: Set to true to cause checking to occur.  Runs slower.  FIX: not implemented.     @rtype: BitVector     @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8     @note: The returned bits may not be 6 bit aligned.  It is up to you to pad out the bits. Unpack a ChanMngmt message.      Fields in params:       - MessageID(uint): AIS message number.  Must be 22 (field automatically set to "22")       - RepeatIndicator(uint): Indicated how many times a message has been repeated       - UserID(uint): Unique ship identification number (MMSI)       - Spare(uint): Not used.  Should be set to zero. (field automatically set to "0")       - ChanA(uint): Channel number from ITU-R M.1084 Annex 4       - ChanB(uint): Channel number from ITU-R M.1084 Annex 4       - TxRxMode(uint): FIX: find the description       - power(uint): FIX: put in a description       - corner1_lon(decimal): north-east corner of area for assignment  longitude of corner       - corner1_lat(decimal): north-east corner of area for assignment  latitude of corner       - corner2_lon(decimal): south-west corner of area for assignment  longitude of corner       - corner2_lat(decimal): south-west corner of area for assignment  latitude of corner       - IndicatorType(uint): FIX: put in a description       - ChanABandwidth(uint): FIX: put in a description       - ChanBBandwidth(uint): FIX: put in a description       - TransZoneSize(uint): FIX: put in a description       - Spare2(uint): Not used.  Should be set to zero. (field automatically set to "0")     @type bv: BitVector     @param bv: Bits defining a message     @param validate: Set to true to cause checking to occur.  Runs slower.  FIX: not implemented.     @rtype: dict     @return: params #Would be nice to check the bit count here.. #if validate: #    assert (len(bv)==FIX: SOME NUMBER) Print a ChanMngmt message to stdout.      Fields in params:       - MessageID(uint): AIS message number.  Must be 22 (field automatically set to "22")       - RepeatIndicator(uint): Indicated how many times a message has been repeated       - UserID(uint): Unique ship identification number (MMSI)       - Spare(uint): Not used.  Should be set to zero. (field automatically set to "0")       - ChanA(uint): Channel number from ITU-R M.1084 Annex 4       - ChanB(uint): Channel number from ITU-R M.1084 Annex 4       - TxRxMode(uint): FIX: find the description       - power(uint): FIX: put in a description       - corner1_lon(decimal): north-east corner of area for assignment  longitude of corner       - corner1_lat(decimal): north-east corner of area for assignment  latitude of corner       - corner2_lon(decimal): south-west corner of area for assignment  longitude of corner       - corner2_lat(decimal): south-west corner of area for assignment  latitude of corner       - IndicatorType(uint): FIX: put in a description       - ChanABandwidth(uint): FIX: put in a description       - ChanBBandwidth(uint): FIX: put in a description       - TransZoneSize(uint): FIX: put in a description       - Spare2(uint): Not used.  Should be set to zero. (field automatically set to "0")     @param params: Dictionary of field names/values.     @param out: File like object to write to.     @rtype: stdout     @return: text to out # else: leave it empty # Nothing to return #RepeatIndicatorEncodeLut # RepeatIndicatorEncodeLut #TxRxModeEncodeLut # TxRxModeEncodeLut #powerEncodeLut # powerEncodeLut #IndicatorTypeEncodeLut # IndicatorTypeEncodeLut #ChanABandwidthEncodeLut # ChanABandwidthEncodeLut #ChanBBandwidthEncodeLut # ChanBBandwidthEncodeLut #TransZoneSizeEncodeLut # TransZoneSizeEncodeLut ###################################################################### # SQL SUPPORT ###################################################################### Return the SQL CREATE command for this message type         @param outfile: file like object to print to.         @param fields: which fields to put in the create.  Defaults to all.         @param extraFields: A sequence of tuples containing (name,sql type) for additional fields         @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format         @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')         @type addCoastGuardFields: bool         @return: sql create string         @rtype: str          @see: sqlCreate # FIX: should this sqlCreate be the same as in LaTeX (createFuncName) rather than hard coded? Return the sqlhelp object to create the table.      @param fields: which fields to put in the create.  Defaults to all.     @param extraFields: A sequence of tuples containing (name,sql type) for additional fields     @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format     @type addCoastGuardFields: bool     @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')     @return: An object that can be used to generate a return     @rtype: sqlhelp.create # c.addInt('cg_s_rssi')  # Relative signal strength indicator # c.addInt('cg_d_strength')  # dBm receive strength # c.addVarChar('cg_x',10)  # Idonno # Receive timestamp from the AIS equipment 'T' # Slot received in # Receiver station ID  -  should usually be an MMSI, but sometimes is a string # UTC seconds since the epoch # UTC decoded cg_sec - not actually in the data stream #--- EPSG 4326 : WGS 84 #INSERT INTO "spatial_ref_sys" ("srid","auth_name","auth_srid","srtext","proj4text") VALUES (4326,'EPSG',4326,'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]','+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs '); #--- EPSG 4326 : WGS 84 #INSERT INTO "spatial_ref_sys" ("srid","auth_name","auth_srid","srtext","proj4text") VALUES (4326,'EPSG',4326,'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]','+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs '); Return the SQL INSERT command for this message type         @param params: dictionary of values keyed by field name         @param outfile: file like object to print to.         @param extraParams: A sequence of tuples containing (name,sql type) for additional fields         @return: sql create string         @rtype: str          @see: sqlCreate Give the SQL INSERT statement         @param params: dict keyed by field name of values         @param extraParams: any extra fields that you have created beyond the normal ais message fields         @rtype: sqlhelp.insert         @return: insert class instance          TODO(schwehr):allow optional type checking of params?         @warning: this will take invalid keys happily and do what??? # Had better be a WKT type like POINT(-88.1 30.321) # Need to construct the type. #valStr='GeomFromText(\''+pgTypes[pgName]+'(' ###################################################################### # LATEX SUPPORT ###################################################################### Return the LaTeX definition table for this message type         @param outfile: file like object to print to.         @type outfile: file obj         @return: LaTeX table string via the outfile         @rtype: str \\begin{table}%[htb] \\centering \\begin{tabular}{|l|c|l|} \\hline Parameter & Number of bits & Description \\\\  \\hline\\hline MessageID & 6 & AIS message number.  Must be 22 \\\\ \hline RepeatIndicator & 2 & Indicated how many times a message has been repeated \\\\ \hline UserID & 30 & Unique ship identification number (MMSI) \\\\ \hline Spare & 2 & Not used.  Should be set to zero. \\\\ \hline ChanA & 12 & Channel number from ITU-R M.1084 Annex 4 \\\\ \hline ChanB & 12 & Channel number from ITU-R M.1084 Annex 4 \\\\ \hline TxRxMode & 4 & FIX: find the description \\\\ \hline power & 1 & FIX: put in a description \\\\ \hline corner1\_lon & 18 & north-east corner of area for assignment  longitude of corner \\\\ \hline corner1\_lat & 17 & north-east corner of area for assignment  latitude of corner \\\\ \hline corner2\_lon & 18 & south-west corner of area for assignment  longitude of corner \\\\ \hline corner2\_lat & 17 & south-west corner of area for assignment  latitude of corner \\\\ \hline IndicatorType & 1 & FIX: put in a description \\\\ \hline ChanABandwidth & 1 & FIX: put in a description \\\\ \hline ChanBBandwidth & 1 & FIX: put in a description \\\\ \hline TransZoneSize & 3 & FIX: put in a description \\\\ \hline Spare2 & 23 & Not used.  Should be set to zero.\\\\ \\hline \\hline Total bits & 168 & Appears to take 1 slot \\\\ \\hline \\end{tabular} \\caption{AIS message number 22: Base station report - F} \\label{tab:ChanMngmt} \\end{table} ###################################################################### # Text Definition ###################################################################### Return the text definition table for this message type      @param outfile: file like object to print to.     @type outfile: file obj     @return: text table string via the outfile     @rtype: str Description MessageID AIS message number.  Must be 22 RepeatIndicator Indicated how many times a message has been repeated UserID Unique ship identification number (MMSI) Spare Not used.  Should be set to zero. ChanA Channel number from ITU-R M.1084 Annex 4 ChanB Channel number from ITU-R M.1084 Annex 4 TxRxMode FIX: find the description power FIX: put in a description corner1_lon north-east corner of area for assignment  longitude of corner corner1_lat north-east corner of area for assignment  latitude of corner corner2_lon south-west corner of area for assignment  longitude of corner corner2_lat south-west corner of area for assignment  latitude of corner IndicatorType FIX: put in a description ChanABandwidth FIX: put in a description ChanBBandwidth FIX: put in a description TransZoneSize FIX: put in a description Spare2 Not used.  Should be set to zero. Total bits 168 Appears to take 1 slot ###################################################################### # UNIT TESTING ###################################################################### Return a params file base on the testvalue tags.     @rtype: dict     @return: params based on testvalue tags Use testvalue tag text from each type to build test case the ChanMngmt message # Check that each parameter came through ok. # FIX: remove nmea from binary messages.  No way to build the whole packet? # FIX: or build the surrounding msg 8 for a broadcast? # FIX: what about a USCG type message? # Make sure all non required options are specified. # FIX: figure out if this might be necessary at compile time # Pad out to multiple of 6 # FIX: Do not emit this option for the binary message payloads.  Does not make sense. # For conversion to word tables # Make a csv separated list of fields that will be displayed for csv # Found nmea # FIX: do checksum # either binary or nmeapayload... expect mostly nmeapayloads # assumes that an all 0 and 1 string can not be a nmeapayload # nmeapayload ############################################################