Skip to content

Commit ba90160

Browse files
committed
update to v1.3.0
1 parent 4c6d6de commit ba90160

File tree

15 files changed

+1341
-473
lines changed

15 files changed

+1341
-473
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2019-2020 iMAR Navigation GmbH
1+
Copyright (c) 2019-2023 iMAR Navigation GmbH
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy
44
of this software and associated documentation files (the "Software"), to deal

ixcom/cmdline.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77

88

99
class TextFileParser(ixcom.parser.MessageParser):
10-
def __init__(self, outputfile, skip_parameter=None, print_request = True):
10+
def __init__(self, outputfile, skip_parameter=list(), print_request = True):
1111
super().__init__()
12+
self.ignore_output = False
1213
self.outputfile = outputfile
1314
self.print_request = print_request
1415
self.messageSearcher.disableCRC = False
@@ -34,13 +35,13 @@ def __handle_loglist2(self, message):
3435

3536
def handle_message(self, message, from_device):
3637
zeile = "Header Time: %.4f\n" % (message.header.get_time())
37-
if self.skipParameter is None:
38-
self.write_output(zeile)
39-
elif message.data['parameterID'] in self.skipParameter:
40-
return
4138
if message.header.msgID == ixcom.data.MessageID.PARAMETER:
42-
self.parameterList.append((message.data['parameterID'], message))
43-
return
39+
parameter_id = message.data['parameterID']
40+
if not (parameter_id in ixcom.data.ParameterPayloadDictionary) or \
41+
parameter_id in self.skipParameter:
42+
return
43+
self.write_output(zeile)
44+
self.parameterList.append((parameter_id, message))
4445
if message.payload.data["action"] == 0:
4546
zeile = "Parameter: %s\n" % message.payload.get_name()
4647
else:
@@ -106,7 +107,8 @@ def __convert_key(self, key, d):
106107
self.write_output(zeile)
107108

108109
def write_output(self, line):
109-
self.outputfile.write('\t'*self._indent_level+line)
110+
if not self.ignore_output:
111+
self.outputfile.write('\t'*self._indent_level+line)
110112

111113
def parse_file(self, inputfile):
112114
while True:
@@ -136,7 +138,7 @@ def xcom_lookup(argv = None):
136138
sysname = client.get_parameter(19).payload.data['str'].decode('utf-8').split('\0')[0]
137139
imutype = client.get_parameter(107).payload.data["type"]
138140
fwversion = client.get_parameter(5).payload.data['str'].decode('utf-8').split('\0')[0]
139-
if imutype is not 255:
141+
if imutype != 255:
140142
print("%s (%s, FW %s): ssh://root@%s, ftp://%s" % (data[:-1].decode('utf-8'), sysname, fwversion, ip, ip))
141143
client.close_channel()
142144
except:
@@ -155,14 +157,16 @@ def configdump2txt(argv=None):
155157
parser.add_argument('-o', '--output', metavar='output_filename', type=argparse.FileType('wt'), help='Filename of the output file', default=sys.stdout)
156158
args = parser.parse_args(args=argv)
157159
xcomparser = TextFileParser(args.output, skip_parameter=[917]) # skip parxcom_loglist2(917)
160+
xcomparser.ignore_output = True
158161
xcomparser.parse_file(args.input_file)
162+
xcomparser.ignore_output = False
159163
xcomparser.write_parameter()
160164

161165

162166
def monitor2xcom(argv = None):
163167
import re
164168
import binascii
165-
parser = argparse.ArgumentParser(description='Converts xcom binary config dump files to other representations')
169+
parser = argparse.ArgumentParser(description='Converts raw messages in monitor log files to readable text')
166170
parser.add_argument('input_file', metavar='', type=argparse.FileType('rt'), nargs='?',
167171
help='Name of the monitor file', default = 'monitor.log')
168172
parser.add_argument('-o', '--output', metavar='output_filename', type=argparse.FileType('wt'),

ixcom/crc16.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
try:
2+
import fastcrc.crc16
3+
fastcrc_installed = True
4+
except ImportError:
5+
fastcrc_installed = False
6+
7+
18
__crc16_table = [
29
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
310
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
@@ -39,6 +46,9 @@ def __crc16(data, crc, table):
3946
crc = ((crc << 8) & 0xff00) ^ table[((crc >> 8) & 0xff)^byte]
4047
return crc & 0xffff
4148

42-
43-
def crc16xmodem(data, crc=0):
44-
return __crc16(data, crc, __crc16_table)
49+
if fastcrc_installed:
50+
def crc16xmodem(data, crc=0):
51+
return fastcrc.crc16.xmodem(data)
52+
else:
53+
def crc16xmodem(data, crc=0):
54+
return __crc16(data, crc, __crc16_table)

ixcom/data.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
from .commands import *
33
from .parameters import *
44
from .messages import *
5+
from .plugin_messages import *
56

67
try:
78
from ixcom_internal.parameters import *
89
from ixcom_internal.messages import *
10+
from ixcom_internal.plugin_messages import *
911
except ImportError as e:
1012
pass

ixcom/exceptions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ class ClientTimeoutError(XcomError):
1616
pass
1717

1818
class CommunicationError(XcomError):
19+
pass
20+
21+
class EndOfConfig(XcomError):
1922
pass

ixcom/grep.py

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .parser import MessageParser, MessageSearcher
99
from . import data
10+
from .exceptions import EndOfConfig
1011

1112
def get_item_len(item):
1213
if isinstance(item, (list, tuple)):
@@ -44,6 +45,41 @@ def parameter_callback(msg, from_device):
4445
parser.messageSearcher.process_bytes(f.read())
4546
return config
4647

48+
def read_file_for_config(filename='iXCOMstream.bin'):
49+
parameter_bytes = io.BytesIO(b'')
50+
message_searcher = MessageSearcher(disable_crc = True)
51+
52+
53+
def message_callback(in_bytes):
54+
if not in_bytes:
55+
return
56+
message_id = int(in_bytes[1])
57+
if message_id == data.MessageID.PARAMETER:
58+
parameter_bytes.write(in_bytes)
59+
else:
60+
raise EndOfConfig()
61+
62+
config = {}
63+
def parameter_callback(msg, from_device):
64+
if msg.header.msgID == data.MessageID.PARAMETER:
65+
config[msg.payload.get_name()] = msg.data
66+
67+
68+
message_searcher.add_callback(message_callback)
69+
70+
with open(filename, 'rb') as f:
71+
try:
72+
message_searcher.process_buffer_unsafe(f.read())
73+
except EndOfConfig:
74+
pass
75+
parameter_bytes.seek(0, os.SEEK_SET)
76+
parser = MessageParser()
77+
parser.nothrow = True
78+
parser.add_callback(parameter_callback)
79+
parser.messageSearcher.process_bytes(parameter_bytes.read())
80+
81+
return config
82+
4783
def read_file(filename='iXCOMstream.bin'):
4884
message_bytes_dict = dict()
4985
result = dict()
@@ -53,6 +89,9 @@ def message_callback(in_bytes):
5389
if not in_bytes:
5490
return
5591
message_id = int(in_bytes[1])
92+
if message_id == data.MessageID.PLUGIN:
93+
plugin_id = int(in_bytes[16]) + (int(in_bytes[17]) << 8)
94+
message_id = 0x100 + plugin_id
5695
if message_id not in message_bytes_dict:
5796
message_bytes_dict[message_id] = io.BytesIO(b'')
5897
message_bytes_dict[message_id].write(in_bytes)
@@ -69,17 +108,31 @@ def parameter_callback(msg, from_device):
69108

70109
for msg_id in message_bytes_dict:
71110
message_bytes_dict[msg_id].seek(0, os.SEEK_SET)
72-
73111
if msg_id < 0xFD:
74112
msg = data.getMessageWithID(msg_id)
75-
result[msg.payload.get_name()] = parse_message_from_buffer(msg_id, message_bytes_dict[msg_id])
113+
try:
114+
if msg:
115+
result[msg.payload.get_name()] = parse_message_from_buffer(msg_id, message_bytes_dict[msg_id])
116+
else:
117+
data.handle_undefined_message(msg_id)
118+
except:
119+
print(f"Error: Message with ID: {msg_id} could not be parsed!")
76120
elif msg_id == data.MessageID.PARAMETER:
77121
parser = MessageParser()
78122
parser.nothrow = True
79123
parser.add_callback(parameter_callback)
80124
parser.messageSearcher.process_bytes(message_bytes_dict[msg_id].read())
81125
result['config'] = config
82-
126+
elif msg_id > 0xFF:
127+
plugin_message_id = msg_id - 0x100
128+
msg = data.getPluginMessageWithID(plugin_message_id)
129+
try:
130+
if msg:
131+
result[msg.payload.get_name()] = parse_message_from_buffer(msg_id, message_bytes_dict[msg_id])
132+
else:
133+
data.handle_undefined_plugin_message(plugin_message_id)
134+
except:
135+
print(f"Error: Plugin Message with ID: {plugin_message_id} could not be parsed!")
83136
return result
84137

85138
def parse_message_from_file(messageID, filename = None):
@@ -88,7 +141,7 @@ def parse_message_from_file(messageID, filename = None):
88141
fname = hex(messageID).upper()+'.bin'
89142
else:
90143
fname = filename
91-
if messageID is not 0x19:
144+
if messageID != 0x19:
92145
msg_size = msg.size()
93146
else:
94147
with open(fname, mode='rb') as f:
@@ -120,18 +173,39 @@ def parse_message_from_file(messageID, filename = None):
120173
def parse_message_from_buffer(messageID, buffer):
121174

122175
def add_time(ret):
123-
return append_fields(ret, 'gpstime', ret['time_of_week_sec'] + 1e-6*ret['time_of_week_usec'], usemask=False)
124-
125-
msg = data.getMessageWithID(messageID)
176+
return append_fields(ret, 'gpstime', ret['time_of_week_sec'] + 1e-6 * ret['time_of_week_usec'], usemask=False)
177+
if messageID > 0xFF:
178+
plugin_message_id = messageID - 0x100
179+
msg = data.getPluginMessageWithID(plugin_message_id)
180+
else:
181+
msg = data.getMessageWithID(messageID)
126182
if not msg or get_item_len(msg) == 0:
127-
print('ignored')
183+
print(f'ignored Message with ID: {messageID}')
128184
return None
129185

130-
if messageID != data.SYSSTAT_Payload.message_id:
186+
if msg.payload.get_varsize_arg_from_bytes is None:
131187
dtype = np.dtype(msg.get_numpy_dtype())
132188
nlen = int(np.floor(len(buffer.getbuffer())/ dtype.itemsize))
133-
return add_time(np.frombuffer(buffer.getbuffer(), dtype,count=nlen))
189+
return add_time(np.frombuffer(buffer.getbuffer(), dtype, count=nlen))
134190
else:
191+
_next_header = 0
192+
_msg_length = 16
193+
ret = []
194+
while _next_header + _msg_length < len(buffer.getbuffer()):
195+
buffer.seek(_next_header)
196+
msg.header.from_bytes(buffer.read(16))
197+
_msg_length = msg.header.msgLength
198+
_varsize_arg = msg.payload.get_varsize_arg_from_bytes(buffer.read(_msg_length - 16 - 4))
199+
if messageID > 0xFF:
200+
msg = data.getPluginMessageWithID(plugin_message_id,_varsize_arg)
201+
else:
202+
msg = data.getMessageWithID(messageID,_varsize_arg)
203+
dtype = np.dtype(msg.get_numpy_dtype())
204+
buffer.seek(_next_header)
205+
ret.append(add_time(np.frombuffer(buffer.read(_msg_length), dtype, count=1)))
206+
_next_header += _msg_length
207+
return ret # normal ndarray not possible because of variable dtypes -> return list
208+
135209
def iterate(buffer):
136210
start = 0
137211
while True:
@@ -140,7 +214,7 @@ def iterate(buffer):
140214
msg.from_bytes(current)
141215
dtype = msg.get_numpy_dtype()
142216
start += msg.header.msgLength
143-
yield add_time(np.frombuffer(current, dtype, count=1))
217+
return add_time(np.frombuffer(current, dtype, count=1))
144218
except:
145219
return
146220
return np.hstack(iterate(buffer))
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
"created": "2018-07-11",
3+
"domain": "public",
4+
"payload": [
5+
{
6+
"fieldSize": "12",
7+
"fieldOffset": "16",
8+
"var": "accel",
9+
"format": "3f",
10+
"fieldName": "Accel",
11+
"type": "3f",
12+
"description": "Calibrated acceleration along IMU x-, y-, and z-axis in \\ensuremath{\\frac{m}{s^2}}."
13+
},
14+
{
15+
"fieldSize": "12",
16+
"fieldOffset": "28",
17+
"var": "gyro",
18+
"format": "3f",
19+
"fieldName": "Gyro",
20+
"type": "3f",
21+
"description": "Calibrated angular rate along IMU x-, y-, and z-axis in \\ensuremath{\\frac{\\textit{rad}}{s}}."
22+
},
23+
{
24+
"fieldSize": "12",
25+
"fieldOffset": "40",
26+
"var": "rpy",
27+
"format": "3f",
28+
"fieldName": "RPY",
29+
"type": "3f",
30+
"description": "Roll, pitch and yaw in \\textit{rad}."
31+
},
32+
{
33+
"fieldSize": "12",
34+
"fieldOffset": "52",
35+
"var": "vel",
36+
"format": "3f",
37+
"fieldName": "Vel",
38+
"type": "3f",
39+
"description": "Velocity along x-, y- and z-axis in \\ensuremath{\\frac{m}{s}}. The reference frame can be selected via the PARDAT\\_VEL parameter (ENU, NED, ECEF or body frame)."
40+
},
41+
{
42+
"fieldSize": "16",
43+
"fieldOffset": "64",
44+
"var": "pos",
45+
"format": "2d",
46+
"fieldName": "Pos",
47+
"type": "2d",
48+
"description": "Longitude and latitude in \\textit{rad}. The selected values are masked in the DATSEL field."
49+
},
50+
{
51+
"fieldSize": "4",
52+
"fieldOffset": "80",
53+
"var": "altitude",
54+
"format": "f",
55+
"fieldName": "Altidude",
56+
"type": "f",
57+
"description": "Altitude/Height in \\ensuremath{m}. The content (WGS or MSL or Baro based) can be selected via the PARDAT\\_POS parameter. The selected value is masked in the DATSEL field."
58+
},
59+
{
60+
"fieldSize": "2",
61+
"fieldOffset": "84",
62+
"var": "undulation",
63+
"format": "h",
64+
"fieldName": "Undulation",
65+
"type": "h",
66+
"description": "Relationship between the geoid and the ellipsoid in \\ensuremath{cm}."
67+
},
68+
{
69+
"fieldSize": "2",
70+
"fieldOffset": "86",
71+
"var": "data_selection",
72+
"format": "H",
73+
"fieldName": "Data_Selection",
74+
"type": "H",
75+
"description": "Data selection mask. This field contains the selected data sources for this mes- sage"
76+
}
77+
],
78+
"name": "INSSOL",
79+
"version": "0.0.0",
80+
"synopsis": "Integrated navigation solution including IMU data in a selectable frame",
81+
"rate": "full",
82+
"id": "0x03",
83+
"type": "message",
84+
"revision": "svn 116",
85+
"description": ""
86+
}

0 commit comments

Comments
 (0)