From 5a8207c9d18ff88d6c762b8c57d57d9c4a2ff8db Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Sat, 8 Jul 2017 22:35:29 +0200 Subject: o Start on some electronics engineering code. --- .gitignore | 7 ++ README.md | 7 ++ demo/DMP2305U.mod | 25 +++++ demo/reverse-polarity/reverse-polarity_normal.asc | 46 +++++++++ demo/reverse-polarity/reverse-polarity_normal.plt | 26 +++++ .../reverse-polarity/reverse-polarity_reversed.asc | 46 +++++++++ ee/__init__.py | 109 +++++++++++++++++++++ ee/tools/__init__.py | 0 ee/tools/read_ltspice_raw.py | 27 +++++ requirements.txt | 6 ++ 10 files changed, 299 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 demo/DMP2305U.mod create mode 100644 demo/reverse-polarity/reverse-polarity_normal.asc create mode 100644 demo/reverse-polarity/reverse-polarity_normal.plt create mode 100644 demo/reverse-polarity/reverse-polarity_reversed.asc create mode 100644 ee/__init__.py create mode 100644 ee/tools/__init__.py create mode 100644 ee/tools/read_ltspice_raw.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7e58a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.pyc +env + +# LTSpice +demo/*/*.net +demo/*/*.raw +demo/*/*.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..1bcc212 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# LTSpice binary format + +## 'real' data + +The data are a sequence of chunks, one chunk for each point. Each chunk +contain values for all variables. The first variable is a float64, +all the others are float32. diff --git a/demo/DMP2305U.mod b/demo/DMP2305U.mod new file mode 100644 index 0000000..3935b20 --- /dev/null +++ b/demo/DMP2305U.mod @@ -0,0 +1,25 @@ +*---------- DMP2305U Spice Model ---------- +.SUBCKT DMP2305U 10 20 30 +* TERMINALS: D G S +M1 1 2 3 3 PMOS L = 1E-006 W = 1E-006 +RD 10 1 0.03653 +RS 30 3 0.001 +RG 20 2 23 +CGS 2 3 6.954E-010 +EGD 12 30 2 1 1 +VFB 14 30 0 +FFB 2 1 VFB 1 +CGD 13 14 8.85E-010 +R1 13 30 1 +D1 13 12 DLIM +DDG 14 15 DCGD +R2 12 15 1 +D2 30 15 DLIM +DSD 10 3 DSUB +.MODEL PMOS PMOS LEVEL = 3 U0 = 400 VMAX = 1.385E+005 ETA = 0.001 ++ TOX = 6E-008 NSUB = 1E+016 KP = 24.44 KAPPA = 0.8442 VTO = -0.8978 +.MODEL DCGD D CJO = 3.468E-010 VJ = 0.2064 M = 0.3945 +.MODEL DSUB D IS = 4.536E-009 N = 1.339 RS = 0.05752 BV = 30 CJO = 2.406E-011 VJ = 1.677E-012 M = 0.8 +.MODEL DLIM D IS = 0.0001 +.ENDS +*Diodes DMP2305U Spice Model v1.0 Last Revised 2011/6/20 \ No newline at end of file diff --git a/demo/reverse-polarity/reverse-polarity_normal.asc b/demo/reverse-polarity/reverse-polarity_normal.asc new file mode 100644 index 0000000..1a15aff --- /dev/null +++ b/demo/reverse-polarity/reverse-polarity_normal.asc @@ -0,0 +1,46 @@ +Version 4 +SHEET 1 2056 680 +WIRE 320 -208 144 -208 +WIRE 544 -208 416 -208 +WIRE 656 -208 544 -208 +WIRE 144 -160 144 -208 +WIRE 544 -96 544 -208 +WIRE 656 -96 656 -208 +WIRE 144 -48 144 -80 +WIRE 144 -32 144 -48 +WIRE 144 80 144 48 +WIRE 400 80 400 -160 +WIRE 400 80 144 80 +WIRE 544 80 544 -32 +WIRE 544 80 400 80 +WIRE 656 80 656 -16 +WIRE 656 80 544 80 +WIRE 144 112 144 80 +FLAG 144 112 0 +FLAG 144 -208 Bat +FLAG 656 -208 Load +FLAG 144 -48 BatI +SYMBOL pmos 320 -160 R270 +SYMATTR InstName M1 +SYMATTR Value DMP2305U +SYMATTR Prefix X +SYMBOL res 160 -64 R180 +WINDOW 0 36 76 Left 2 +WINDOW 3 36 40 Left 2 +SYMATTR InstName Rint +SYMATTR Value {Rint} +SYMBOL voltage 144 -48 R0 +WINDOW 3 24 44 Left 2 +WINDOW 123 0 0 Left 2 +WINDOW 39 0 0 Left 2 +SYMATTR Value 3.3 +SYMATTR InstName V1 +SYMBOL res 640 -112 R0 +SYMATTR InstName Rload +SYMATTR Value 1000 +SYMBOL cap 528 -96 R0 +SYMATTR InstName Cdec +SYMATTR Value 10µ +TEXT 120 192 Left 2 !.include ../DMP2305U.mod +TEXT 120 224 Left 2 !.op 1us +TEXT 120 248 Left 2 !.step param Rint list 25 125 200\n* http://data.energizer.com/pdfs/cr2032.pdf diff --git a/demo/reverse-polarity/reverse-polarity_normal.plt b/demo/reverse-polarity/reverse-polarity_normal.plt new file mode 100644 index 0000000..1ae4c52 --- /dev/null +++ b/demo/reverse-polarity/reverse-polarity_normal.plt @@ -0,0 +1,26 @@ +[Operating Point] +{ + Npanes: 2 + Active Pane: 1 + { + traces: 1 {524291,0,"V(load)"} + X: (' ',0,25,20,200) + Y[0]: (' ',2,2.7,0.05,3.25) + Y[1]: ('p',0,1e+308,5e-012,-1e+308) + Volts: (' ',0,0,2,2.7,0.05,3.25) + Log: 0 0 0 + PltMag: 1 + PltPhi: 1 0 + }, + { + traces: 2 {336592898,0,"Ix(M1:G)"} {524292,1,"(V(bat)-V(load))*I(Rload)"} + X: (' ',0,25,20,200) + Y[0]: ('p',0,-3e-010,5e-012,-2.45e-010) + Y[1]: ('n',0,4.5e-007,1e-008,5.8e-007) + Amps: ('p',0,0,0,-3e-010,5e-012,-2.45e-010) + Units: "W" ('n',0,0,2,4.5e-007,1e-008,5.8e-007) + Log: 0 0 0 + PltMag: 1 + PltPhi: 1 0 + } +} diff --git a/demo/reverse-polarity/reverse-polarity_reversed.asc b/demo/reverse-polarity/reverse-polarity_reversed.asc new file mode 100644 index 0000000..b0e4d0c --- /dev/null +++ b/demo/reverse-polarity/reverse-polarity_reversed.asc @@ -0,0 +1,46 @@ +Version 4 +SHEET 1 2056 680 +WIRE 320 -208 144 -208 +WIRE 544 -208 416 -208 +WIRE 656 -208 544 -208 +WIRE 144 -160 144 -208 +WIRE 544 -96 544 -208 +WIRE 656 -96 656 -208 +WIRE 144 -48 144 -80 +WIRE 144 -32 144 -48 +WIRE 144 80 144 48 +WIRE 400 80 400 -160 +WIRE 400 80 144 80 +WIRE 544 80 544 -32 +WIRE 544 80 400 80 +WIRE 656 80 656 -16 +WIRE 656 80 544 80 +WIRE 144 112 144 80 +FLAG 144 112 0 +FLAG 144 -208 Bat +FLAG 656 -208 Load +FLAG 144 -48 BatI +SYMBOL pmos 320 -160 R270 +SYMATTR InstName M1 +SYMATTR Value DMP2305U +SYMATTR Prefix X +SYMBOL res 160 -64 R180 +WINDOW 0 36 76 Left 2 +WINDOW 3 36 40 Left 2 +SYMATTR InstName Rint +SYMATTR Value {Rint} +SYMBOL voltage 144 64 R180 +WINDOW 3 24 44 Left 2 +WINDOW 123 0 0 Left 2 +WINDOW 39 0 0 Left 2 +SYMATTR Value 3.3 +SYMATTR InstName V1 +SYMBOL res 640 -112 R0 +SYMATTR InstName Rload +SYMATTR Value 1000 +SYMBOL cap 528 -96 R0 +SYMATTR InstName Cdec +SYMATTR Value 10µ +TEXT 120 192 Left 2 !.include ../DMP2305U.mod +TEXT 120 224 Left 2 !.op 1us +TEXT 120 248 Left 2 !.step param Rint list 25 125 200\n* http://data.energizer.com/pdfs/cr2032.pdf diff --git a/ee/__init__.py b/ee/__init__.py new file mode 100644 index 0000000..5c26c0a --- /dev/null +++ b/ee/__init__.py @@ -0,0 +1,109 @@ +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): + v = [v for v in self.variables if v.expression == variable] + if len(v) != 1: + raise Error('Unknown variable: ' + str(variable)) + return self.values[v[0].idx] + +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 = {} + + 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.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() + # Skip an extra '\0' + self.f.seek(pos + 1) + 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 + + if line[0] == 0: + line = line[1:] + if line[-1] == 10: + line = line[:-1] +# print("len(line)={}, line={}".format(len(line), str(line))) + line = str(line, 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 + + 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 new file mode 100644 index 0000000..e69de29 diff --git a/ee/tools/read_ltspice_raw.py b/ee/tools/read_ltspice_raw.py new file mode 100644 index 0000000..0b3b7e1 --- /dev/null +++ b/ee/tools/read_ltspice_raw.py @@ -0,0 +1,27 @@ +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)) + +xs = raw.get_values('V(load)') + +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) + +with open("ltspice.png", "wb") as f: + plt.savefig(f, format="png") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5308763 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +sympy==1.0 +mpmath==0.19 +pkg-resources==0.0.0 +numpy==1.13.1 +parsec==3.3 +matplotlib==2.0.2 -- cgit v1.2.3