import numpy as np class LtSpiceRaw(object): def __init__(self, variables, values_first, values_rest): self.variables = variables self.values_first = values_first self.values_rest = values_rest def get_values(self, variable): return self.values[variable.idx] def get_variable(self, idx=None, expression=None): if idx is not None: return self.variables[idx] if expression is not None: v = [v for v in self.variables if v.expression == expression] if len(v) != 1: raise Exception('Unknown variable: ' + str(v)) return v[0] raise Exception('idx or expression must be given') def to_pandas(self): import pandas data = [] for (i, v) in enumerate(self.values_first): data.append(pandas.Series(v, dtype='float64')) data = {} for (i, v) in enumerate(self.values_first): data[self.variables[i].expression] = v return pandas.DataFrame(data=data) class LtSpiceVariable(object): def __init__(self, idx, expression, kind): self.idx = idx self.expression = expression self.kind = kind class LtSpiceReader(object): def __init__(self, f): self.f = f self.mode = 'header' self.headers = {} self.variables = [] self.values = [] def read_header(self, line): sep = line.find(':') key = line[0:sep] value = line[sep + 1:].strip() # print("key='{}', value='{}'".format(key, str(value))) if key == 'Binary': self.set_binary_mode() elif key == 'Variables': self.no_variables = int(self.headers['No. Variables']) self.mode = 'variables' else: self.headers[key] = value def read_variable(self, line): parts = line.split('\t') idx = int(parts[1]) expression = parts[2] kind = parts[3] self.variables.append(LtSpiceVariable(idx, expression, kind)) if len(self.variables) == self.no_variables: self.mode = 'header' def set_binary_mode(self): self.mode = 'binary' def read_binary(self): pos = self.f.tell() no_points = int(self.headers['No. Points']) no_variables = int(self.headers['No. Variables']) # print("no_points={}, no_variables={}".format(no_points, no_variables)) if True: self.values = np.zeros((no_variables, no_points), dtype='float32') for p in range(no_points): self.values[0][p] = np.fromfile(self.f, count=1, dtype='float64') row = np.fromfile(self.f, count=no_variables - 1, dtype='float32'); for v in range(0, len(row)): self.values[v + 1][p] = row[v] else: self.values = np.zeros((no_points, no_variables), dtype='float64') for p in range(no_points): self.values[p][0] = np.fromfile(self.f, count=1, dtype='float64') row = np.fromfile(self.f, count=no_variables - 1, dtype='float32'); for v in range(0, len(row)): self.values[p][v + 1] = row[v] def read(self): while True: line = self.f.readline(); if len(line) == 0: break self.f.read(1) # The data is utf16 encoded so read the extra null byte # if this wasn't the last line .readline() includes the newline if line[-1] == '\n' or line[-1] == 0x0a: line = line[:-1] # print("len(line)={}, line={}".format(len(line), str(line))) line = line.decode(encoding="utf-16") if self.mode == 'header': self.read_header(line) elif self.mode == 'variables': self.read_variable(line) # The binary data can't be read with readline() if self.mode == 'binary': # self.f.read() self.read_binary() break if len(self.variables) == 0: raise Exception("Something didn't quite work when parsing file, no variables found") return LtSpiceRaw(self.variables, self.values, []) def read_ltspice_raw(filename): with open(filename, mode="rb") as f: r = LtSpiceReader(f) return r.read()