forked from asdf1011/libaisdec
/
decode.py
executable file
·136 lines (119 loc) · 4.94 KB
/
decode.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env python
from bdec import DecodeError
from bdec.data import Data
from bdec.field import Field
from bdec.spec import load_specs, LoadError
from bdec.output.instance import encode, decode
from bdec.output.xmlout import to_file
import getopt
from glob import glob
import operator
import os.path
import sys
def usage(program):
print 'Decode aivdm encoded ais files.'
print 'Usage: %s [options] <filename.aivdm> ...'
print
print 'Options:'
print ' -h Show this help.'
class Decoder:
def __init__(self):
spec_dir = os.path.join(os.path.split(__file__)[0], 'spec')
self._fake_6_bit_text_field = Field('6 bit ascii', length=0,
format=Field.TEXT)
try:
self._aivdm = load_specs([os.path.join(spec_dir, 'aivdm.xml')])
self._ais = load_specs(glob(os.path.join(spec_dir, 'ais*.xml')))
except LoadError, ex:
sys.exit(str(ex))
def _filter_6_bit_ascii(self, items):
"""Convert 6-bit ascii decode items into a single text field.
This doesn't change the decoding, but hacks the decoded output
so it displays the data in a nicer fashion.
items -- An iterable list of decoded entries.
return -- A iterable list of decoded entries, 6-bit ascii items
collapsed into a single field."""
iterable = iter(items)
for is_starting, name, entry, data, value in iterable:
if is_starting and entry.name == '6 bit ascii':
# We found a 6-bit ascii string. Get the data and values for
# the child entries, and collapse them into a single result.
yield True, name, self._fake_6_bit_text_field, None, None
text = []
d = Data()
for is_starting, name, entry, data, value in iterable:
if entry.name == '6 bit ascii':
break
elif value is not None and name=='character':
d += data
text += chr(value)
yield False, name, self._fake_6_bit_text_field, d, ''.join(text)
else:
yield is_starting, name, entry, data, value
def _get_crc(self, data):
result = 0
data = data.copy()
# Skip the leading '!'
data.pop(8)
while True:
char = data.pop(8)
if int(char) == ord('*'):
return result
result ^= int(char)
def decode(self, input_file):
# Encode aivdm data back into binary
fragments = []
payload = self._aivdm[1][0]
data = Data(input_file)
while data:
try:
calculated_crc = self._get_crc(data)
packet = decode(self._aivdm[0], data)
if packet.checksum != calculated_crc:
sys.stderr.write('Crc mismatch; expected %02X, but '
'calculated %02X. Skipping packet.\n' %
(packet.checksum, calculated_crc))
continue
bits = encode(payload, list(ord(c.character) for c in packet.payload))
bits = bits.pop(len(bits) - packet.num_fill_bits)
if len(fragments) == packet.fragment_number - 1:
fragments.append(bits)
if len(fragments) == packet.fragment_count:
self._decode_ais(reduce(operator.add, fragments))
fragments = []
else:
sys.stderr.write('Expected fragment number %i, got %i (of %i)\n' % (
len(fragments) + 1, packet.fragment_number, packet.fragment_count))
fragments = []
except DecodeError, ex:
filename, line_number, column_number = self._aivdm[2][ex.entry]
sys.stderr.write('%s[%i] - %s\n' % (filename, line_number, ex))
fragments = []
# Read to the next newline
while data and data.pop(8).text('ascii') != '\n':
pass
def _decode_ais(self, data):
# Decode the ais binary
try:
to_file(self._filter_6_bit_ascii(self._ais[0].decode(data)), sys.stdout)
except DecodeError, ex:
filename, line_number, column_number = self._ais[2][ex.entry]
sys.stderr.write('%s[%i] - %s\n' % (filename, line_number, ex))
def main(args):
try:
opts, args = getopt.getopt(args[1:], 'h', [])
except getopt.GetoptError, ex:
sys.exit(ex)
for opt, value in opts:
if opt == '-h':
usage(args[0])
return 0
else:
raise NotImplementedError(opt)
if not args:
sys.exit("No files to decode. Run '%s -h' for more details.", args[0])
decoder = Decoder()
for filename in args:
decoder.decode(file(filename, 'rb'))
if __name__ == '__main__':
main(sys.argv)