aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Buesch <m@bues.ch>2019-07-21 13:38:14 +0200
committerMichael Buesch <m@bues.ch>2019-07-21 13:38:14 +0200
commit728e95a8b6230363e810dc3a8272e12986a07ebd (patch)
treeddf3cf134cf17d3fe2d12a1e48fd179a3a779752
parent433cbd6ff2940f20f45d32fd0d1993a8c13b1d2f (diff)
downloadxytronic-lf-master.tar.xz
xytronic-lf-master.zip
simulator: Add some circuit simulatorsHEADmaster
Signed-off-by: Michael Buesch <m@bues.ch>
-rw-r--r--firmware/simulator/circuits/pwm.py83
-rw-r--r--firmware/simulator/circuits/rcnet.py102
-rw-r--r--firmware/simulator/circuits/sine.py67
-rw-r--r--firmware/simulator/circuits/voltagedivider.py49
4 files changed, 301 insertions, 0 deletions
diff --git a/firmware/simulator/circuits/pwm.py b/firmware/simulator/circuits/pwm.py
new file mode 100644
index 0000000..dc330f5
--- /dev/null
+++ b/firmware/simulator/circuits/pwm.py
@@ -0,0 +1,83 @@
+"""
+# Xytronic LF-1600
+# Open Source firmware
+# PWM calculation
+#
+# Copyright (c) 2019 Michael Buesch <m@bues.ch>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+"""
+
+__all__ = [
+ "PWMGenerator",
+]
+
+class PWMGenerator(object):
+ __slots__ = (
+ "f",
+ "duty",
+ "cycleT",
+ "out",
+ "__errors",
+ )
+
+ def __init__(self, f, duty=0.0):
+ self.f = max(f, 0.0)
+ self.duty = max(min(duty, 1.0), 0.0)
+ self.cycleT = 0.0
+ self.out = False
+ self.__errors = 0
+
+ @property
+ def cycleDur(self):
+ return 1.0 / self.f
+
+ def calc(self, dt):
+ if self.f:
+ self.cycleT += dt
+ dur = self.cycleDur
+ wrap = False
+ if self.out:
+ if self.cycleT >= dur:
+ if self.duty < 1.0:
+ self.out = False
+ wrap = True
+ else:
+ if self.cycleT >= dur * (1.0 - self.duty):
+ if self.duty > 0.0:
+ self.out = True
+ else:
+ wrap = True
+ if wrap:
+ self.cycleT -= dur
+ if self.cycleT >= dur:
+ if self.__errors < 10:
+ print("The PWM frequency %f Hz is "
+ "too fast. Loosing steps." % (
+ self.f))
+ self.__errors += 1
+ self.cycleT = 0.0
+ return self.out
+
+if __name__ == "__main__":
+ import time
+ pwm = PWMGenerator(f=0.5, duty=0.3)
+ dt = 0.1
+ i = 0.0
+ while 1:
+ out = pwm.calc(dt)
+ print("t=%.1f, out=%d, cycleT=%.3f" % (i, int(out), pwm.cycleT))
+ time.sleep(dt)
+ i += dt
diff --git a/firmware/simulator/circuits/rcnet.py b/firmware/simulator/circuits/rcnet.py
new file mode 100644
index 0000000..352c57a
--- /dev/null
+++ b/firmware/simulator/circuits/rcnet.py
@@ -0,0 +1,102 @@
+"""
+# Xytronic LF-1600
+# Open Source firmware
+# R/C network calculation
+#
+# Copyright (c) 2019 Michael Buesch <m@bues.ch>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+"""
+
+import math
+
+__all__ = [
+ "RCNet",
+]
+
+class RCNet(object):
+ """Discrete R/C network calculation.
+ """
+
+ __slots__ = (
+ "r", # Resistor value, in Ohms.
+ "c", # Capacitor value, in Farad.
+ "v", # Capacitor voltage.
+ "i", # Network current, in Ampere.
+ )
+
+ def __init__(self, r, c, v=None, q=None):
+ self.r = r
+ self.c = c
+ self.v = v or 0.0
+ if q is not None:
+ assert(v is None)
+ self.q = q
+ self.i = 0.0
+
+ @property
+ def q(self):
+ """Get charge, in Coulomb.
+ """
+ return self.c * self.v
+
+ @q.setter
+ def q(self, newQ):
+ """Set charge, in Coulomb.
+ """
+ self.v = newQ / self.c
+
+ def calc(self, vIn, dt):
+ """Calculate the next discrete step.
+ Returns the new C voltage.
+ """
+ c, r = self.c, self.r
+ dv = vIn - self.v
+ fact = math.exp((-1.0 / (r * c)) * dt)
+ self.i = (dv / r) * fact
+ self.v += dv * (1.0 - fact)
+ return self.v
+
+ def __str__(self):
+ return "v=%.3f V, i=%.3f mA, q=%.3f uC" % (
+ self.v,
+ self.i * 1e3,
+ self.q * 1e6)
+
+if __name__ == "__main__":
+ import matplotlib.pyplot as plt
+
+ rc = RCNet(r=(10e3),
+ c=(220e-6),
+ q=0.0)
+
+ dt = 0.01
+ times = [t * dt for t in range(500)]
+ values = []
+ for t in times:
+ rc.calc(vIn=(5.0 if t <= 3.0 else -2.0),
+ dt=dt)
+ print("t=%.3f s, %s" % (t, str(rc)))
+ values.append((rc.q * 1e6, rc.i * 1e3, rc.v))
+
+ fig, ax = plt.subplots(3, sharex=True)
+ ax[0].plot(times, [val[0] for val in values], label="Q (uC)")
+ ax[0].legend()
+ ax[1].plot(times, [val[1] for val in values], label="I (mA)")
+ ax[1].legend()
+ ax[2].plot(times, [val[2] for val in values], label="U (V)")
+ ax[2].legend()
+ ax[2].set_xlabel("t (s)")
+ plt.show()
diff --git a/firmware/simulator/circuits/sine.py b/firmware/simulator/circuits/sine.py
new file mode 100644
index 0000000..d24e8f8
--- /dev/null
+++ b/firmware/simulator/circuits/sine.py
@@ -0,0 +1,67 @@
+"""
+# Xytronic LF-1600
+# Open Source firmware
+# Sine calculation
+#
+# Copyright (c) 2019 Michael Buesch <m@bues.ch>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+"""
+
+import math
+
+__all__ = [
+ "SineGenerator",
+]
+
+class SineGenerator(object):
+ __slots__ = (
+ "f",
+ "peakV",
+ "cycleT",
+ "v",
+ )
+
+ def __init__(self, f, rmsV=5.0):
+ self.f = max(f, 0.0)
+ self.peakV = rmsV * math.sqrt(2)
+ self.cycleT = 0.0
+ self.v = 0.0
+
+ @property
+ def rmsV(self):
+ return self.peakV / math.sqrt(2)
+
+ @property
+ def cycleDur(self):
+ return 1.0 / self.f
+
+ def calc(self, dt):
+ if self.f:
+ self.cycleT += dt
+ self.v = self.peakV * math.sin(math.pi * 2 *
+ (self.cycleT / self.cycleDur))
+ return self.v
+
+if __name__ == "__main__":
+ import time
+ sine = SineGenerator(f=0.5)
+ dt = 0.1
+ i = 0.0
+ while 1:
+ v = sine.calc(dt)
+ print("t=%.1f, out=%.1f" % (i, v))
+ time.sleep(dt)
+ i += dt
diff --git a/firmware/simulator/circuits/voltagedivider.py b/firmware/simulator/circuits/voltagedivider.py
new file mode 100644
index 0000000..b207bf1
--- /dev/null
+++ b/firmware/simulator/circuits/voltagedivider.py
@@ -0,0 +1,49 @@
+"""
+# Xytronic LF-1600
+# Open Source firmware
+# Voltage divider calculation
+#
+# Copyright (c) 2019 Michael Buesch <m@bues.ch>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+"""
+
+
+class VoltageDivider(object):
+ """Voltage divider calculation.
+ """
+
+ __slots__ = (
+ "r1",
+ "r2",
+ "i",
+ )
+
+ def __init__(self, r1, r2, i=0.0):
+ self.r1 = r1
+ self.r2 = r2
+ self.i = i
+
+ @property
+ def u1(self):
+ return self.i * self.r1
+
+ @property
+ def u2(self):
+ return self.i * self.r2
+
+ def calc(self, uIn):
+ self.i = uIn / (self.r1 + self.r2)
+ return self.u2
bues.ch cgit interface