Skip to content

Commit

Permalink
Put unit knowledge in DFFormat class
Browse files Browse the repository at this point in the history
  • Loading branch information
shancock884 committed Jan 23, 2024
1 parent de1ba54 commit 0517029
Showing 1 changed file with 81 additions and 68 deletions.
149 changes: 81 additions & 68 deletions DFReader.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ def __init__(self, type, name, flen, format, columns, oldfmt=None):
self.format = format
self.columns = columns.split(',')
self.instance_field = None
self.unit_ids = None
self.mult_ids = None
self.units = None

if self.columns == ['']:
self.columns = []
Expand Down Expand Up @@ -125,32 +124,64 @@ def __init__(self, type, name, flen, format, columns, oldfmt=None):
if self.msg_fmts[i] == 'a':
self.a_indexes.append(i)

# If this format was alrady defined, copy over units and instance info
if oldfmt is not None:
self.set_unit_ids(oldfmt.unit_ids)
self.set_mult_ids(oldfmt.mult_ids)

def set_unit_ids(self, unit_ids):
'''set unit IDs string from FMTU'''
self.units = oldfmt.units
if oldfmt.instance_field is not None:
self.set_instance_field(self.colhash[oldfmt.instance_field])

def set_instance_field(self, instance_idx):
'''set up the instance field for this format'''
self.instance_field = self.columns[instance_idx]
# work out offset and length of instance field in message
pre_fmt = self.format[:instance_idx]
pre_sfmt = ""
for c in pre_fmt:
(s, mul, type) = FORMAT_TO_STRUCT[c]
pre_sfmt += s
self.instance_ofs = struct.calcsize(pre_sfmt)
(ifmt,) = self.format[instance_idx]
self.instance_len = struct.calcsize(ifmt)

def set_unit_ids(self, unit_ids, unit_lookup):
'''apply unit IDs string from FMTU'''
if unit_ids is None:
return
self.unit_ids = unit_ids
# Does this unit string define an instance field?
instance_idx = unit_ids.find('#')
if instance_idx != -1:
self.instance_field = self.columns[instance_idx]
# work out offset and length of instance field in message
pre_fmt = self.format[:instance_idx]
pre_sfmt = ""
for c in pre_fmt:
(s, mul, type) = FORMAT_TO_STRUCT[c]
pre_sfmt += s
self.instance_ofs = struct.calcsize(pre_sfmt)
(ifmt,) = self.format[instance_idx]
self.instance_len = struct.calcsize(ifmt)


def set_mult_ids(self, mult_ids):
'''set mult IDs string from FMTU'''
self.mult_ids = mult_ids
self.set_instance_field(instance_idx)
# Build the units array from the IDs
self.units = [""]*len(self.columns)
for i in range(len(self.columns)):
if i < len(unit_ids):
if unit_ids[i] in unit_lookup:
self.units[i] = unit_lookup[unit_ids[i]]

def set_mult_ids(self, mult_ids, mult_lookup):
'''apply mult IDs string from FMTU'''
# Update the units based on the multiplier
for i in range(len(self.units)):
# If the format has its own multiplier, ignore the unit one
# and just return the base unit, and if no unit is specified
# there is nothing to adjust
if self.msg_mults[i] is None and self.units[i] != "":
# Get the unit multiplier from the lookup table
if mult_ids[i] in mult_lookup:
unitmult = mult_lookup[mult_ids[i]]
# Combine the multipler and unit to derive the real unit
if unitmult in MULT_TO_PREFIX:
self.units[i] = MULT_TO_PREFIX[unitmult]+self.units[i]
else:
self.units[i] = "%.4g.%s" % (unitmult, self.units[i])

def get_unit(self, col):
'''Return the unit for the specified field'''
if self.units is None:
return ""
else:
idx = self.colhash[col]
return self.units[idx]

def __str__(self):
return ("DFFormat(%s,%s,%s,%s)" %
Expand Down Expand Up @@ -260,15 +291,6 @@ def __str__(self):
ret = ret[:-2]
return ret + '}'

def get_unit(self,col):
if self.fmt.unit_ids is None and self.fmt.mult_ids is None:
return ""
i = self.fmt.colhash[col]
fmtmult = self.fmt.msg_mults[i]
unitid = self.fmt.unit_ids[i]
multid = self.fmt.mult_ids[i]
return self._parent.get_derived_unit(fmtmult,unitid,multid)

def dump_verbose(self, f):
is_py3 = sys.version_info >= (3,0)
timestamp = "%s.%03u" % (
Expand All @@ -290,14 +312,16 @@ def dump_verbose(self, f):
except UnicodeDecodeError:
f.write(" %s: %s" % (c, to_string(val)))
# Append the unit to the output
unit = self.get_unit(c)
if unit.startswith("rad"):
unit = self.fmt.get_unit(c)
if unit == "":
# No unit specified - just output the newline
f.write("\n")
elif unit.startswith("rad"):
# For rad or rad/s, add the degrees conversion too
f.write(" %s (%s %s)\n" % (unit, math.degrees(val), unit.replace("rad","deg")))
elif unit != "":
f.write(" %s\n" % (unit))
else:
f.write("\n")
# Append the unit
f.write(" %s\n" % (unit))

def get_msgbuf(self):
'''create a binary message buffer for a message'''
Expand Down Expand Up @@ -549,27 +573,6 @@ def __init__(self):
self.unit_lookup = {}
self.mult_lookup = {}

def get_derived_unit(self,fmtmult,unitid,multid):
# Get the base unit from the lookup table
if unitid in self.unit_lookup:
baseunit = self.unit_lookup[unitid]
else:
return ""
# If the format has its own multiplier, ignore the unit one
# and just return the base unit
if fmtmult is not None:
return baseunit
# Get the unit multiplier from the lookup table
if multid in self.mult_lookup:
unitmult = self.mult_lookup[multid]
else:
return baseunit
# Combine the multipler and unit to create derived unit
if unitmult in MULT_TO_PREFIX:
return MULT_TO_PREFIX[unitmult]+baseunit
else:
return "%.4g.%s" % (unitmult, baseunit)

def _rewind(self):
'''reset state on rewind'''
# be careful not to replace self.messages with a new hash;
Expand Down Expand Up @@ -953,6 +956,8 @@ def init_arrays(self, progress_callback=None):
if mfmt.name == 'MULT':
mult_type = mfmt.type

# Handle FMTU messages by updating the DFFormat class with the
# unit/multiplier information
if fmtu_type is not None and mtype == fmtu_type:
fmt = self.formats[mtype]
body = self.data_map[ofs+3:ofs+mlen]
Expand All @@ -963,10 +968,11 @@ def init_arrays(self, progress_callback=None):
if ftype in self.formats:
fmt2 = self.formats[ftype]
if 'UnitIds' in fmt.colhash:
fmt2.set_unit_ids(null_term(elements[fmt.colhash['UnitIds']]))
fmt2.set_unit_ids(null_term(elements[fmt.colhash['UnitIds']]), self.unit_lookup)
if 'MultIds' in fmt.colhash:
fmt2.set_mult_ids(null_term(elements[fmt.colhash['MultIds']]))
fmt2.set_mult_ids(null_term(elements[fmt.colhash['MultIds']]), self.mult_lookup)

# Handle UNIT messages by updating the unit_lookup dictionary
if unit_type is not None and mtype == unit_type:
fmt = self.formats[mtype]
body = self.data_map[ofs+3:ofs+mlen]
Expand All @@ -975,14 +981,18 @@ def init_arrays(self, progress_callback=None):
elements = list(struct.unpack(fmt.msg_struct, body))
self.unit_lookup[chr(elements[1])] = null_term(elements[2])

# Handle MULT messages by updating the mult_lookup dictionary
if mult_type is not None and mtype == mult_type:
fmt = self.formats[mtype]
body = self.data_map[ofs+3:ofs+mlen]
if len(body)+3 < mlen:
break
elements = list(struct.unpack(fmt.msg_struct, body))
# Round the multiplier to 7 significant figures to remove
# inaccuracies created by float/double conversions
# Even though the multiplier value is logged as a double, the
# values in log files look to be single-precision values that have
# been cast to a double.
# To ensure that the values saved here can be used to index the
# MULT_TO_PREFIX table, we round them to 7 significant decimal digits
mult = float("%.7g" % (elements[2]))
self.mult_lookup[chr(elements[1])] = mult

Expand Down Expand Up @@ -1150,8 +1160,8 @@ def _parse_next(self):
MultIds = elements[2]
if FmtType in self.formats:
fmt = self.formats[FmtType]
fmt.set_unit_ids(UnitIds)
fmt.set_mult_ids(MultIds)
fmt.set_unit_ids(UnitIds, self.unit_lookup)
fmt.set_mult_ids(MultIds, self.mult_lookup)

try:
self._add_msg(m)
Expand Down Expand Up @@ -1391,8 +1401,8 @@ def _parse_next(self):
fmtid = getattr(m, 'FmtType', None)
if fmtid is not None and fmtid in self.id_to_name:
fmtu = self.formats[self.id_to_name[fmtid]]
fmtu.set_unit_ids(getattr(m, 'UnitIds', None))
fmtu.set_mult_ids(getattr(m, 'MultIds', None))
fmtu.set_unit_ids(getattr(m, 'UnitIds', None), self.unit_lookup)
fmtu.set_mult_ids(getattr(m, 'MultIds', None), self.mult_lookup)

if m.get_type() == 'UNIT':
unitid = getattr(m, 'Id', None)
Expand All @@ -1402,8 +1412,11 @@ def _parse_next(self):
if m.get_type() == 'MULT':
multid = getattr(m, 'Id', None)
mult = getattr(m, 'Mult', None)
# Round the multiplier to 7 significant figures to remove
# inaccuracies created by float/double conversions
# Even though the multiplier value is logged as a double, the
# values in log files look to be single-precision values that have
# been cast to a double.
# To ensure that the values saved here can be used to index the
# MULT_TO_PREFIX table, we round them to 7 significant decimal digits
mult = float("%.7g" % (mult))
self.mult_lookup[chr(multid)] = mult

Expand Down

0 comments on commit 0517029

Please sign in to comment.