From 2384a4e12cb029cbd6c8595fa9f3c5a666a391da Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Wed, 2 Aug 2017 22:28:17 +0200 Subject: o Importing formatting tools. --- .gitignore | 6 ++ Makefile | 17 +++++ ee/__init__.py | 120 -------------------------------- ee/tools/__init__.py | 0 ee/tools/read_ltspice_raw.py | 31 --------- requirements.txt | 3 + setup.cfg | 9 +++ setup.py | 13 ++++ src/ee/__init__.py | 145 +++++++++++++++++++++++++++++++++++++++ src/ee/formatting/__init__.py | 85 +++++++++++++++++++++++ src/ee/tools/__init__.py | 0 src/ee/tools/read_ltspice_raw.py | 31 +++++++++ test/tests.py | 48 +++++++++++++ tox.ini | 9 +++ 14 files changed, 366 insertions(+), 151 deletions(-) create mode 100644 Makefile delete mode 100644 ee/__init__.py delete mode 100644 ee/tools/__init__.py delete mode 100644 ee/tools/read_ltspice_raw.py create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 src/ee/__init__.py create mode 100644 src/ee/formatting/__init__.py create mode 100644 src/ee/tools/__init__.py create mode 100644 src/ee/tools/read_ltspice_raw.py create mode 100644 test/tests.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 65e9278..53b822f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ *.pyc env +env-* +ee.egg-info +.eggs +.cache *.png @@ -7,3 +11,5 @@ env demo/*/*.net demo/*/*.raw demo/*/*.log + +.tox diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bf7ecc9 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +all: env-python2-packages env-python3-packages + +env-python2: + virtualenv -p python2 env-python2 + +env-python2-packages: env-python2 + env-python2/bin/pip install -r requirements.txt + env-python2/bin/pip install -e . + +env-python3: + virtualenv -p python3 env-python3 + +env-python3-packages: env-python3 + env-python3/bin/pip install -r requirements.txt + env-python3/bin/pip install -e . + +#env-python2/bin/pip install -r requirements.txt diff --git a/ee/__init__.py b/ee/__init__.py deleted file mode 100644 index 08a283c..0000000 --- a/ee/__init__.py +++ /dev/null @@ -1,120 +0,0 @@ -import sympy -import numpy as np - -__all__ = [ - 'read_ltspice_raw' -] - -class LtSpiceRaw(object): - def __init__(self, variables, values): - self.variables = variables - self.values = values - - 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(variable)) - return v[0] - raise Exception('idx or expression must be given') - -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("variables={}, no_points={}, pos={}".format(no_variables, no_points, pos)) - self.values = vs = np.zeros((no_variables, no_points)) - for p in range(no_points): - vs[0][p] = np.fromfile(self.f, count=1, dtype='float64') - pointdata = np.fromfile(self.f, count=no_variables - 1, dtype='float32') -# print("value={:0=0.15e}".format(vs[0][p])) - for v in range(1, no_variables): -# print("p={}, v={}".format(p, v)) -# print(" value={:0=0.15e}".format(pointdata[v - 1])) - vs[v][p] = pointdata[v - 1] - - 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() diff --git a/ee/tools/__init__.py b/ee/tools/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/ee/tools/read_ltspice_raw.py b/ee/tools/read_ltspice_raw.py deleted file mode 100644 index 279fe73..0000000 --- a/ee/tools/read_ltspice_raw.py +++ /dev/null @@ -1,31 +0,0 @@ -import ee -import sys - -raw = ee.read_ltspice_raw(sys.argv[1]) - -#print("Variables:") -#for i, v in enumerate(raw.variables): -# print("{:2}: kind: {:20} expression: {}".format(i, v.kind, v.expression)) - -#for i, v in enumerate(raw.variables): -# print("{:2}: kind: {:20} expression: {}".format(i, v.kind, v.expression)) -# for p in raw.values[i]: -# print(" {}".format(p)) - -x = raw.get_variable(idx = 0) -y = raw.get_variable(expression = 'V(load)') -xs = raw.get_values(y) - -import matplotlib -matplotlib.use('Agg') - -import matplotlib.pyplot as plt -from matplotlib.ticker import FuncFormatter, MaxNLocator -fig = plt.figure() -ax = fig.add_subplot(111) -ax.plot(xs) -ax.set_xlabel(x.expression) -ax.set_ylabel(y.expression) - -with open("ltspice.png", "wb") as f: - plt.savefig(f, format="png") diff --git a/requirements.txt b/requirements.txt index 5308763..3f26a39 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,6 @@ pkg-resources==0.0.0 numpy==1.13.1 parsec==3.3 matplotlib==2.0.2 +pandas==0.20.3 +Pillow==4.2.1 +pytest==3.2.0 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..cd59717 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,9 @@ +[aliases] +test=pytest + +[tool:pytest] +addopts = --verbose +python_files = + test_*.py + *_test.py + tests.py diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e1a428f --- /dev/null +++ b/setup.py @@ -0,0 +1,13 @@ +from distutils.core import setup +from glob import glob +from os.path import basename +from os.path import dirname +from os.path import join +from os.path import splitext +from setuptools import find_packages + +setup(name='ee', + version='1.0', + packages=find_packages('src'), + package_dir={'': 'src'}, + ) diff --git a/src/ee/__init__.py b/src/ee/__init__.py new file mode 100644 index 0000000..a22ecc6 --- /dev/null +++ b/src/ee/__init__.py @@ -0,0 +1,145 @@ +import numpy as np + +__all__ = [ + 'EeError', + 'read_ltspice_raw' +] + +class EeError(Exception): + pass + +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(variable)) + 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() diff --git a/src/ee/formatting/__init__.py b/src/ee/formatting/__init__.py new file mode 100644 index 0000000..3c592ef --- /dev/null +++ b/src/ee/formatting/__init__.py @@ -0,0 +1,85 @@ +#import sympy as sp +import math +from ee import EeError + +__all__ = [ + 'e6', 'e12', 'e24', 'e48', 'e96', 'e192', + 'eng_str', +] + +#class Symbols(object): +# def __init__(self): +# self.tau = sp.symbols('tau'), +# self.r = sp.symbols('r'), +# self.c = sp.symbols('c') +# +#_s = Symbols() + +class ESeries(object): + def __init__(self, series): + self.series = series + + def closest(self, value): + e = math.floor(math.log10(value)) + value = float(value/(10**e)) + + return min(self.series, key=lambda v: abs(v - value)) * 10**e + +# https://en.wikipedia.org/wiki/E-series_of_preferred_numbers +_e_series_24 = [1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1] +_e_series_12 = [v for i, v in enumerate(_e_series_24) if i % 2 == 0] +_e_series_6 = [v for i, v in enumerate(_e_series_24) if i % 4 == 0] + +_e_series_192 = [ + 1.00, 1.01, 1.02, 1.04, 1.05, 1.06, 1.07, 1.09, 1.10, 1.11, + 1.13, 1.14, 1.15, 1.17, 1.18, 1.20, 1.21, 1.23, 1.24, 1.26, + 1.27, 1.29, 1.30, 1.32, 1.33, 1.35, 1.37, 1.38, 1.40, 1.42, + 1.43, 1.45, 1.47, 1.49, 1.50, 1.52, 1.54, 1.56, 1.58, 1.60, + 1.62, 1.64, 1.65, 1.67, 1.69, 1.72, 1.74, 1.76, 1.78, 1.80, + 1.82, 1.84, 1.87, 1.89, 1.91, 1.93, 1.96, 1.98, 2.00, 2.03, + 2.05, 2.08, 2.10, 2.13, 2.15, 2.18, 2.21, 2.23, 2.26, 2.29, + 2.32, 2.34, 2.37, 2.40, 2.43, 2.46, 2.49, 2.52, 2.55, 2.58, + 2.61, 2.64, 2.67, 2.71, 2.74, 2.77, 2.80, 2.84, 2.87, 2.91, + 2.94, 2.98, 3.01, 3.05, 3.09, 3.12, 3.16, 3.20, 3.24, 3.28, + 3.32, 3.36, 3.40, 3.44, 3.48, 3.52, 3.57, 3.61, 3.65, 3.70, + 3.74, 3.79, 3.83, 3.88, 3.92, 3.97, 4.02, 4.07, 4.12, 4.17, + 4.22, 4.27, 4.32, 4.37, 4.42, 4.48, 4.53, 4.59, 4.64, 4.70, + 4.75, 4.81, 4.87, 4.93, 4.99, 5.05, 5.11, 5.17, 5.23, 5.30, + 5.36, 5.42, 5.49, 5.56, 5.62, 5.69, 5.76, 5.83, 5.90, 5.97, + 6.04, 6.12, 6.19, 6.26, 6.34, 6.42, 6.49, 6.57, 6.65, 6.73, + 6.81, 6.90, 6.98, 7.06, 7.15, 7.23, 7.32, 7.41, 7.50, 7.59, + 7.68, 7.77, 7.87, 7.96, 8.06, 8.16, 8.25, 8.35, 8.45, 8.56, + 8.66, 8.76, 8.87, 8.98, 9.09, 9.20, 9.31, 9.42, 9.53, 9.65, + 9.76, 9.88, +] +_e_series_96 = [v for i, v in enumerate(_e_series_192) if i % 2 == 0] +_e_series_48 = [v for i, v in enumerate(_e_series_192) if i % 4 == 0] + +e6 = ESeries(_e_series_6) +e12 = ESeries(_e_series_12) +e24 = ESeries(_e_series_24) +e48 = ESeries(_e_series_48) +e96 = ESeries(_e_series_96) +e192 = ESeries(_e_series_192) + +def e_series_find_closest(value): + e = math.floor(math.log10(value)) + value = float(value/(10**e)) + + return min(series, key=lambda v: abs(v - value)) * 10**e + +def eng_str(value): + big = ['', ' k', ' M', ' G', ' T'] + e_floor = math.floor(math.log10(value)) + + div3 = int(math.floor(e_floor/3)) + suffix = big[div3] + scale = 10**(div3*3) + scaled = value/scale + + if scaled == int(scaled): + return "{:1.0f}{}".format(scaled, suffix) + else: + return "{:1.1f}{}".format(scaled, suffix) + +# return "scaled={:>9}, div3={}, floor(e)={}, scale={}, suffix={}".format(str(scaled), div3, e_floor, scale, suffix) diff --git a/src/ee/tools/__init__.py b/src/ee/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ee/tools/read_ltspice_raw.py b/src/ee/tools/read_ltspice_raw.py new file mode 100644 index 0000000..279fe73 --- /dev/null +++ b/src/ee/tools/read_ltspice_raw.py @@ -0,0 +1,31 @@ +import ee +import sys + +raw = ee.read_ltspice_raw(sys.argv[1]) + +#print("Variables:") +#for i, v in enumerate(raw.variables): +# print("{:2}: kind: {:20} expression: {}".format(i, v.kind, v.expression)) + +#for i, v in enumerate(raw.variables): +# print("{:2}: kind: {:20} expression: {}".format(i, v.kind, v.expression)) +# for p in raw.values[i]: +# print(" {}".format(p)) + +x = raw.get_variable(idx = 0) +y = raw.get_variable(expression = 'V(load)') +xs = raw.get_values(y) + +import matplotlib +matplotlib.use('Agg') + +import matplotlib.pyplot as plt +from matplotlib.ticker import FuncFormatter, MaxNLocator +fig = plt.figure() +ax = fig.add_subplot(111) +ax.plot(xs) +ax.set_xlabel(x.expression) +ax.set_ylabel(y.expression) + +with open("ltspice.png", "wb") as f: + plt.savefig(f, format="png") diff --git a/test/tests.py b/test/tests.py new file mode 100644 index 0000000..a1c4ad7 --- /dev/null +++ b/test/tests.py @@ -0,0 +1,48 @@ +def test_formatting(): + import ee.formatting + import sys +# for x in sys.path: +# print(x) + print('pre') + print(dir(ee.formatting)) + print('post') + ee.formatting.eng_str(10) + from ee.formatting import eng_str + + test_cases = [ + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + ] + test_cases = [ + 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, 137, 138, 139, + ] + test_cases = [ + [5.5, "5.5"], + [55, "55"], + [550, "550"], + [5500, "5.5 k"], + [55000, "55 k"], + [550000, "550 k"], + [5500000, "5.5 M"], + ] + + for [tc, expected] in test_cases: + actual = eng_str(tc) +# status = if expected == actual: 'PASS' else 'FAIL' + status = '' if expected == actual else 'FAIL' + print("{:5} {:10} => expected={:>10}, actual={:>10}".format(status, tc, expected, actual)) + +# for tc in test_cases: +# print("{:10} => e12={:10}".format(tc, e_series_find_closest(tc))) + diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..416365c --- /dev/null +++ b/tox.ini @@ -0,0 +1,9 @@ +[testenv] +changedir=test +deps= + pytest + numpy +commands= + pytest \ + --basetemp={envtmpdir} \ + {posargs} -- cgit v1.2.3