Last active
October 24, 2015 04:04
-
-
Save geowurster/c3d566955fd442cf1638 to your computer and use it in GitHub Desktop.
libais parsing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import ais.tag_block | |
| import ais.vdm | |
| import ais | |
| import json | |
| import lru # pip install lru-dict | |
| import collections | |
| FIELD_MAP = { | |
| 'positional_accuracy': {'tag': 'body', 'key': 'accuracy'}, | |
| : 'addressed', | |
| : 'aid_type', | |
| 'ais_version': {'tag': 'body', 'key': 'ais_version'}, | |
| : 'alt', | |
| : 'app_id', | |
| : 'assigned', | |
| : 'band', | |
| : 'band_a', | |
| : 'band_b', | |
| 'callsign': {'tag': 'body', 'key': 'callsign'}, | |
| : 'channel_a', | |
| : 'channel_b', | |
| 'cog': {'tag': 'body', 'key': 'course'}, | |
| : 'cs', | |
| 'ack_dac': {'tag': 'body', 'key': 'dac'}, | |
| : 'data', | |
| 'day': {'tag': 'body', 'key': 'day'}, | |
| : 'dest1', | |
| : 'dest2', | |
| 'mmsi_dst': {'tag': 'body', 'key': 'dest_mmsi'}, | |
| 'destination': {'tag': 'body', 'key': 'destination'}, | |
| : 'display', | |
| 'draught': {'tag': 'body', 'key': 'draught'}, | |
| : 'dsc', | |
| 'dte': {'tag': 'body', 'key': 'dte'}, | |
| : 'epfd', | |
| 'req_fi': {'tag': 'body', 'key': 'fid'}, | |
| : 'gnss', | |
| 'true_heading': {'tag': 'body', 'key': 'heading'}, | |
| 'hour': {'tag': 'body', 'key': 'hour'}, | |
| 'imo_num': {'tag': 'body', 'key': 'imo'}, | |
| : 'increment1', | |
| : 'increment2', | |
| : 'increment3', | |
| : 'increment4', | |
| : 'interval', | |
| 'y': {'tag': 'body', 'key': 'lat'}, | |
| 'x' : {'tag': 'body', 'key': 'lon'}, | |
| 'special_manoeuvre': {'tag': 'body', 'key': 'maneuver'}, | |
| 'minute': {'tag': 'body', 'key': 'minute'}, | |
| 'mmsi': {'tag': 'body', 'key': 'mmsi'}, | |
| 'spare': {'tag': 'spare', 'key': 'spare'}, | |
| : 'mmsi1', | |
| : 'mmsi2', | |
| : 'mmsi3', | |
| : 'mmsi4', | |
| : 'mmsiseq1', | |
| : 'mmsiseq2', | |
| : 'mmsiseq3', | |
| : 'mmsiseq4', | |
| : 'model', | |
| 'month': {'tag': 'body', 'key': 'month'}, | |
| : 'mothership_mmsi', | |
| : 'msg22', | |
| : 'name', | |
| : 'ne_lat', | |
| : 'ne_lon', | |
| : 'number1', | |
| : 'number2', | |
| : 'number3', | |
| : 'number4', | |
| : 'off_position', | |
| : 'offset1', | |
| : 'offset1_1', | |
| : 'offset1_2', | |
| : 'offset2', | |
| : 'offset2_1', | |
| : 'offset3', | |
| : 'offset4', | |
| : 'partno', | |
| : 'power', | |
| : 'quiet', | |
| 'radio': {'tag': 'body', 'key': 'radio'}, | |
| 'raim': {'tag': 'body', 'key': 'raim'}, | |
| : 'regional', | |
| 'repeat': {'tag': 'body', 'key': 'repeat'}, | |
| : 'reserved', | |
| 'retransmit': {'tag': 'body', 'key': 'retransmit'}, | |
| 'timestamp': {'tag': 'body', 'key': 'second'}, | |
| 'msg_seq': {'tag': 'body', 'key': 'seqno'}, | |
| : 'serial', | |
| : 'ship_type', | |
| 'name': {'tag': 'body', 'key': 'shipname'}, | |
| 'type_and_cargo': {'tag': 'body', 'key': 'shiptype'}, | |
| 'sog': {'tag': 'body', 'key': 'speed'}, | |
| : 'station_type', | |
| 'nav_status': {'tag': 'body', 'key': 'status'}, | |
| : 'structured', | |
| : 'sw_lat', | |
| : 'sw_lon', | |
| : 'text', | |
| : 'timeout1', | |
| : 'timeout2', | |
| : 'timeout3', | |
| : 'timeout4', | |
| 'dim_a': {'tag': 'body', 'key': 'to_bow'}, | |
| 'to_port': {'tag': 'body', 'key': 'to_port'}, | |
| 'dim_d': {'tag': 'body', 'key': 'to_starboard'}, | |
| 'dim_b': {'tag': 'body', 'key': 'to_stern'}, | |
| 'rot': {'tag': 'body', 'key': 'turn'}, | |
| : 'txrx', | |
| 'id': {'tag': 'body', 'key': 'type'}, | |
| : 'type1_1', | |
| : 'type1_2', | |
| : 'type2_1', | |
| : 'vendorid', | |
| : 'virtual_aid', | |
| 'year': {'tag': 'body', 'key': 'year'}, | |
| : 'zonesize: | |
| } | |
| # TODO: Are dim_a|b|c|d mapped correctly? | |
| def d(t): | |
| return json.dumps(t, indent=4, sort_keys=True) | |
| CACHE = lru.LRU(1000) | |
| def in_cache(val): | |
| try: | |
| CACHE[val] | |
| return True | |
| except KeyError: | |
| return False | |
| with open('100k.nmea') as f: | |
| for line in f: | |
| # We can always parse these | |
| tagblock = ais.tag_block.Parse(line) | |
| payload = ais.vdm.Parse(tagblock['payload']) | |
| sen_tot = int(payload['sen_tot']) | |
| # Single line message. Parse! | |
| if sen_tot is 1: | |
| try: | |
| body = ais.decode(payload['body'], int(payload['fill_bits'])) | |
| msg = { | |
| 'tagblock': tagblock, | |
| 'payload': payload, | |
| 'body': body | |
| } | |
| except ais.DecodeError: | |
| msg = None | |
| print("======= ERRROR SINGLE =======") | |
| else: | |
| sen_num = int(payload['sen_num']) | |
| seq_id = int(payload['seq_id']) | |
| # Sequence not in CACHE | |
| if not in_cache(seq_id): | |
| CACHE[seq_id] = {sen_num: {'tagblock': tagblock, 'payload': payload}} | |
| else: | |
| CACHE[seq_id][sen_num] = {'tagblock': tagblock, 'payload': payload} | |
| # Last message in a sequence | |
| if sen_num is sen_tot: | |
| sen_data = CACHE[seq_id] | |
| # Manual dump is cheaper than LRU | |
| del CACHE[seq_id] | |
| # Build the NMEA sentence | |
| nmea = '' | |
| for k in sorted(sen_data.keys()): | |
| nmea += sen_data[k]['payload']['body'] | |
| # Parse NMEA | |
| try: | |
| body = ais.decode(nmea, payload['fill_bits']) | |
| msg = { | |
| 'tagblock': tagblock, | |
| 'payload': payload, | |
| 'body': body | |
| } | |
| except ais.DecodeError: | |
| msg = None | |
| print("======= ERRROR MULTI =======") | |
| # Only processed part of a multiline message - keep going | |
| continue | |
| print(d(msg)) | |
| gpsd = { | |
| 'type': ['id'], | |
| 'course': ['cog'], | |
| 'mmsi': ['mmsi'], | |
| 'status': ['nav_status'], | |
| 'turn': ['rot'], | |
| 'lon': ['x'], | |
| 'lat': ['y'], | |
| 'speed': ['sog'], | |
| 'repeat': ['repeat'], | |
| 'heading': ['true_heading'], | |
| 'raim': ['raim'], | |
| 'maneuver': ['special_manoeuvre'], | |
| 'spare': ['spare'], | |
| 'second': ['timestamp'], | |
| 'accuracy': ['position_accuracy'], | |
| 'body': { | |
| 'rot_over_range': false, | |
| 'slot_timeout': 1, | |
| 'sync_state': 0, | |
| 'utc_hour': 23, | |
| 'utc_min': 59, | |
| 'utc_spare': 0, | |
| }, | |
| 'payload': { | |
| 'body': '15SimR001Q6jmk9pwgp<Rr1n06sd', | |
| 'chan': 'A', | |
| 'checksum': '66', | |
| 'fill_bits': 0, | |
| 'sen_num': 1, | |
| 'sen_tot': 1, | |
| 'seq_id': null, | |
| 'talker': 'AI', | |
| 'vdm': '!AIVDM,1,1,,A,15SimR001Q6jmk9pwgp<Rr1n06sd,0*66', | |
| 'vdm_type': 'VDM" | |
| }, | |
| 'tagblock': { | |
| 'dest': null, | |
| 'group': null, | |
| 'group_id': null, | |
| 'line_num': null, | |
| 'metadata': 's:rORBCOMM109,q:u,c:1420070400,T:2015-01-01 00.00.00*57', | |
| 'payload': '!AIVDM,1,1,,A,15SimR001Q6jmk9pwgp<Rr1n06sd,0*66', | |
| 'quality': 'u', | |
| 'rcvr': 'rORBCOMM109', | |
| 'rel_time': null, | |
| 'sentence_num': null, | |
| 'sentence_tot': null, | |
| 'tag_checksum': '57', | |
| 'text': null, | |
| 'text_date': '2015-01-01 00.00.00', | |
| 'time': 1420070400 | |
| } | |
| } | |
| break | |
| FIELDS_BY_TYPE = { | |
| '1': [ | |
| 'accuracy', 'course', 'heading', 'lat', 'lon', 'maneuver', 'mmsi', | |
| 'radio', 'raim', 'repeat', 'second', 'speed', 'status', 'turn', 'type' | |
| ], | |
| '2': [ | |
| 'accuracy', 'course', 'heading', 'lat', 'lon', 'maneuver', 'mmsi', | |
| 'radio', 'raim', 'repeat', 'second', 'speed', 'status', 'turn', 'type' | |
| ], | |
| '3': [ | |
| 'accuracy', 'course', 'heading', 'lat', 'lon', 'maneuver', 'mmsi', | |
| 'radio', 'raim', 'repeat', 'second', 'speed', 'status', 'turn', 'type' | |
| ], | |
| '4': [ | |
| 'accuracy', 'day', 'epfd', 'hour', 'lat', 'lon', 'minute', 'mmsi', | |
| 'month', 'radio', 'raim', 'repeat', 'second', 'type', 'year' | |
| ], | |
| '5': [ | |
| 'ais_version', 'callsign', 'day', 'destination', 'draught', 'dte', | |
| 'epfd', 'hour', 'imo', 'minute', 'mmsi', 'month', 'repeat', 'shipname', | |
| 'shiptype', 'to_bow', 'to_port', 'to_starboard', 'to_stern', 'type' | |
| ], | |
| '6': [ | |
| 'dac', 'data', 'day', 'dest_mmsi', 'destination', 'draught', 'dte', | |
| 'epfd', 'fid', 'hour', 'minute', 'mmsi', 'month', 'repeat', | |
| 'retransmit', 'seqno', 'to_port', 'to_starboard', 'type' | |
| ], | |
| '7': [ | |
| 'day', 'destination', 'draught', 'dte', 'epfd', 'hour', 'minute', | |
| 'mmsi', 'mmsi1', 'mmsi2', 'mmsi3', 'mmsi4', 'mmsiseq1', 'mmsiseq2', | |
| 'mmsiseq3', 'mmsiseq4', 'month', 'repeat', 'type' | |
| ], | |
| '8': [ | |
| 'assigned', 'course', 'dac', 'data', 'destination', 'draught', 'dte', | |
| 'dte', 'fid', 'lat', 'minute', 'mmsi', 'radio', 'raim', 'regional', | |
| 'repeat', 'second', 'type' | |
| ], | |
| '9': [ | |
| 'accuracy', 'alt', 'assigned', 'course', 'destination', 'draught', | |
| 'dte', 'dte', 'lat', 'lon', 'minute', 'mmsi', 'radio', 'raim', | |
| 'regional', 'repeat', 'second', 'speed', 'type' | |
| ], | |
| '10': [ | |
| 'assigned', 'course', 'dest_mmsi', 'destination', 'draught', 'dte', | |
| 'dte', 'lat', 'lon', 'minute', 'mmsi', 'radio', 'raim', 'regional', | |
| 'repeat', 'second', 'type' | |
| ], | |
| '11': [ | |
| 'accuracy', 'day', 'epfd', 'hour', 'lat', 'lon', 'minute', 'mmsi', | |
| 'month', 'radio', 'raim', 'repeat', 'second', 'type', 'year' | |
| ], | |
| '12': [ | |
| 'assigned', 'course', 'dest_mmsi', 'destination', 'draught', 'dte', | |
| 'dte', 'minute', 'mmsi', 'radio', 'raim', 'regional', 'repeat', | |
| 'retransmit', 'second', 'seqno', 'text', 'type' | |
| ], | |
| '13': [ | |
| 'day', 'destination', 'draught', 'dte', 'epfd', 'hour', 'minute', | |
| 'mmsi', 'mmsi1', 'mmsi2', 'mmsi3', 'mmsi4', 'mmsiseq1', 'mmsiseq2', | |
| 'mmsiseq3', 'mmsiseq4', 'month', 'repeat', 'type' | |
| ], | |
| '14': [ | |
| 'assigned', 'course', 'destination', 'draught', 'dte', 'dte', 'minute', | |
| 'mmsi', 'radio', 'raim', 'regional', 'repeat', 'retransmit', 'second', | |
| 'text', 'text', 'type' | |
| ], | |
| '15': [ | |
| 'destination', 'draught', 'dte', 'minute', 'mmsi', 'mmsi1', 'mmsi2', | |
| 'offset1_1', 'offset1_2', 'offset2_1', 'radio', 'repeat', 'type', | |
| 'type1_1', 'type1_2', 'type2_1' | |
| ], | |
| '16': [ | |
| 'destination', 'draught', 'dte', 'increment1', 'minute', 'mmsi', | |
| 'mmsi1', 'mmsi2', 'mmsi2', 'offset1', 'offset2', 'offset2_1', 'radio', | |
| 'repeat', 'type', 'type2_1' | |
| ], | |
| '17': [ | |
| 'data', 'lat', 'lon', 'mmsi', 'repeat', 'type' | |
| ], | |
| '18': [ | |
| 'accuracy', 'assigned', 'band', 'course', 'cs', 'display', 'dsc', | |
| 'heading', 'lat', 'lon', 'mmsi', 'msg22', 'radio', 'raim', 'regional', | |
| 'repeat', 'reserved', 'second', 'speed', 'type' | |
| ], | |
| '19': [ | |
| 'accuracy', 'assigned', 'course', 'dte', 'epfd', 'heading', 'lat', | |
| 'lon', 'mmsi', 'raim', 'regional', 'repeat', 'reserved', 'second', | |
| 'shipname', 'shiptype', 'speed', 'to_bow', 'to_port', 'to_starboard', | |
| 'to_stern', 'type' | |
| ], | |
| '20': [ | |
| 'assigned', 'dte', 'increment1', 'increment2', 'increment3', | |
| 'increment4', 'mmsi', 'number1', 'number2', 'number3', 'number4', | |
| 'offset1', 'offset2', 'offset3', 'offset4', 'repeat', 'timeout1', | |
| 'timeout2', 'timeout3', 'timeout4', 'type' | |
| ], | |
| '21': [ | |
| 'accuracy', 'aid_type', 'assigned', 'assigned', 'epfd', 'lat', 'lon', | |
| 'mmsi', 'name', 'off_position', 'raim', 'regional', 'repeat', 'second', | |
| 'to_bow', 'to_port', 'to_starboard', 'to_stern', 'type', 'virtual_aid' | |
| ], | |
| '22': [ | |
| 'addressed', 'assigned', 'band_a', 'band_b', 'channel_a', 'channel_b', | |
| 'dest1', 'dest2', 'mmsi', 'ne_lat', 'ne_lon', 'power', 'repeat', | |
| 'sw_lat', 'sw_lon', 'txrx', 'type', 'zonesize' | |
| ], | |
| '23': [ | |
| 'assigned', 'band_a', 'band_b', 'interval', 'mmsi', 'ne_lat', 'ne_lon', | |
| 'quiet', 'repeat', 'ship_type', 'station_type', 'sw_lat', 'sw_lon', 'txrx', | |
| 'type', 'zonesize' | |
| ], | |
| '24': [ | |
| 'assigned', 'callsign', 'mmsi', 'model', 'mothership_mmsi', 'partno', | |
| 'repeat', 'serial', 'shipname', 'shiptype', 'to_bow', 'to_port', | |
| 'to_starboard', 'to_stern', 'type', 'vendorid', 'zonesize' | |
| ], | |
| '25': [ | |
| 'addressed', 'app_id', 'assigned', 'callsign', 'data', 'dest_mmsi', | |
| 'mmsi', 'model', 'mothership_mmsi', 'repeat', 'serial', 'structured', | |
| 'to_bow', 'to_port', 'to_starboard', 'to_stern', 'type', 'zonesize' | |
| ], | |
| '26': [ | |
| 'addressed', 'app_id', 'assigned', 'callsign', 'data', 'dest_mmsi', | |
| 'mmsi', 'mothership_mmsi', 'radio', 'repeat', 'serial', 'structured', | |
| 'to_bow', 'to_port', 'to_starboard', 'to_stern', 'type', 'zonesize' | |
| ], | |
| '27': [ | |
| 'accuracy', 'assigned', 'course', 'gnss', 'lat', 'lon', 'mmsi', | |
| 'mothership_mmsi', 'raim', 'repeat', 'speed', 'status', 'to_port', | |
| 'to_starboard', 'to_stern', 'type', 'zonesize' | |
| ] | |
| } | |
| TYPE_BY_FIELD = collections.defaultdict(set) | |
| for t, f in FIELDS_BY_TYPE.items(): | |
| TYPE_BY_FIELD[f].add(f) | |
| TYPE_BY_FIELD = {k: tuple(sorted(v)) for k, v in TYPE_BY_FIELD.items()} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment