Compare commits

...

5 commits

Author SHA1 Message Date
Stephan I. Böttcher
68fa884b4c turbo.py: wire up baud 2024-10-13 16:06:10 +02:00
Stephan I. Böttcher
4b4773bdd1 turbo.py: baud, python compat, clock reprocessing 2024-10-13 14:01:45 +02:00
Stephan I. Böttcher
b3d97179c0 Merge branch 'master' of codeberg.org:SiB64/turbo_weather 2024-10-13 13:59:33 +02:00
Stephan I. Böttcher
7bd1a95232 linear_regression: reset when x is nonmonotonous with decay 2024-10-13 13:58:09 +02:00
Stephan I. Böttcher
2fd6ba81f5 linear_regression.py: -l, fixes 2024-10-13 13:53:59 +02:00
2 changed files with 61 additions and 36 deletions

View file

@ -1,21 +1,24 @@
#! /usr/bin/python3 #! /usr/bin/python3
import numpy import sys, numpy
class linreg: class linreg:
def __init__(self, p=1, ny=1, xoff=0, yoff=0, decay=None, xdecay=None): def __init__(self, p=1, ny=1,
xoff=0, yoff=0,
decay=None, xdecay=None,
logscale=False):
self.P = p self.P = p
self.p = numpy.arange(2*p+1) self.p = numpy.arange(2*p+1)
self.x = numpy.zeros((2*p+1,)) self.x = numpy.zeros((2*p+1,))
self.y = numpy.zeros((ny, p+1)) self.y = numpy.zeros((ny, p+1))
self.yy = numpy.zeros((ny,)) self.yy = numpy.zeros((ny,))
self.w = None
self.xoff = xoff self.xoff = xoff
self.yoff = yoff self.yoff = yoff
self.decay = decay self.decay = decay
self.xdecay = xdecay self.xdecay = xdecay
self.N = 0 self.N = 0
self.logscale = logscale
Ai = numpy.arange(p+1) Ai = numpy.arange(p+1)
self.Ai = Ai+Ai.reshape((-1,1)) self.Ai = Ai+Ai.reshape((-1,1))
@ -37,10 +40,16 @@ class linreg:
def add(self, x, y, w=1.0, decay=None): def add(self, x, y, w=1.0, decay=None):
y = numpy.array(y, dtype=float).reshape((-1,)) y = numpy.array(y, dtype=float).reshape((-1,))
if self.logscale:
y = numpy.log(y)
if decay is None: if decay is None:
if self.N and self.xdecay is not None: if self.N and self.xdecay is not None:
decay = 1 - numpy.exp((self.lastx - x)/self.xdecay) if x <= self.lastx:
decay = 1
self.N = 0
else:
decay = 1 - numpy.exp((self.lastx - x)/self.xdecay)
else: else:
decay = self.decay decay = self.decay
self.lastx = x self.lastx = x
@ -78,16 +87,18 @@ class linreg:
return return
try: try:
return numpy.linalg.solve(self.x[self.Ai], self.y[...,numpy.newaxis])[...,0] return numpy.linalg.solve(self.x[self.Ai], self.y[...,numpy.newaxis])[...,0]
except: except numpy.linalg.LinAlgError:
print(self.x[self.Ai], self.y) print(self.N, self.x[self.Ai], self.y, file=sys.stderr)
raise
def solve_p(self, p): def solve_p(self, p):
if self.N <= p or p > self.P: if self.N <= p or p > self.P:
return return
Ai = self.Ai[:p+1, :p+1] Ai = self.Ai[:p+1, :p+1]
y = self.y[:, :p+1, numpy.newaxis] y = self.y[:, :p+1, numpy.newaxis]
return numpy.linalg.solve(self.x[Ai], y) try:
return numpy.linalg.solve(self.x[Ai], y)
except numpy.linalg.LinAlgError:
print(self.x[Ai], y, file=sys.stderr)
def transform_x(self, x): def transform_x(self, x):
p = numpy.power(-x, self.p).reshape((-1,1)) p = numpy.power(-x, self.p).reshape((-1,1))
@ -99,10 +110,11 @@ class linreg:
def transform_y(self, y): def transform_y(self, y):
self.yy += self.x[0]*y*y - 2*y*self.y[:,0] self.yy += self.x[0]*y*y - 2*y*self.y[:,0]
self.y -= y * self.x[:self.P+1] self.y -= y[:,numpy.newaxis] * self.x[:self.P+1]
def print_result(self, r, fmt="%.4g"): def print_result(self, r, fmt="%.12g"):
if r is None: if r is None:
print("")
return return
dx = self.xoff dx = self.xoff
dy = self.yoff dy = self.yoff
@ -110,9 +122,13 @@ class linreg:
dx = self.x_origin dx = self.x_origin
if dy is True: if dy is True:
dy = self.y_origin dy = self.y_origin
r[:,0] += dy r[:,0] += dy
if self.logscale:
r[:,0] = numpy.exp(r[:,0])
dy = numpy.exp(dy)
dy += numpy.zeros(self.yy.shape)
for i, rr in enumerate(r): for i, rr in enumerate(r):
print(i, fmt % dx, " ".join([fmt % rrr for rrr in rr])) print(i, fmt % dx, fmt % dy[i], " ".join([fmt % rrr for rrr in rr]))
def strtonum(s): def strtonum(s):
try: try:
@ -124,13 +140,9 @@ def strtonum(s):
def main(): def main():
import sys, getopt, fileinput import sys, getopt, fileinput
options, files = getopt.gnu_getopt(sys.argv[1:], "p:n:x:y:w:d:X:Y:D:T:I") options, files = getopt.gnu_getopt(sys.argv[1:], "lp:n:x:y:w:d:X:Y:D:T:I")
p = 1 p = 1
ny = 1 ny = 1
xoff = 0
yoff = 0
decay = None
xdecay = None
ix = 0 ix = 0
iy = 1 iy = 1
iw = None iw = None
@ -138,26 +150,30 @@ def main():
w = 1.0 w = 1.0
d = None d = None
I = False I = False
args = {}
for o, v in options: for o, v in options:
if o=="-p": if o=="-p":
p = int(v) args["p"] = int(v)
if o=="-n": if o=="-n":
ny = int(v) ny = int(v)
args["ny"] = ny
if o=="-X": if o=="-X":
if v=="R": if v=="R":
xoff = True args["xoff"] = True
else: else:
xoff = int(v) args["xoff"] = int(v)
if o=="-Y": if o=="-Y":
if v=="R": if v=="R":
xoff = True args["xoff"] = True
yoff = True args["yoff"] = True
else: else:
yoff = int(v) args["yoff"] = int(v)
if o=="-D": if o=="-D":
decay = float(v) args["decay"] = float(v)
if o=="-T": if o=="-T":
xdecay = float(v) args["xdecay"] = float(v)
if o=="-l":
args["logscale"] = True
if o=="-x": if o=="-x":
ix = int(v) ix = int(v)
if o=="-y": if o=="-y":
@ -169,7 +185,7 @@ def main():
if o=="-I": if o=="-I":
I = True I = True
LR = linreg(p=p, ny=ny, xoff=xoff, yoff=yoff, decay=decay, xdecay=xdecay) LR = linreg(**args)
for l in fileinput.input(files): for l in fileinput.input(files):
ll = l.split() ll = l.split()
@ -184,10 +200,10 @@ def main():
continue continue
LR.add(x, y, w, d) LR.add(x, y, w, d)
if I: if I:
print_result(LR.solve()) LR.print_result(LR.solve())
if not I: if not I:
print_result(LR.solve()) LR.print_result(LR.solve())
if __name__=="__main__": if __name__=="__main__":
main() main()

View file

@ -6,6 +6,7 @@ import pressure, ntc, linear_regression, cmdsocket
options, files = getopt.gnu_getopt(sys.argv[1:], "xF:s:o:c", ["debug", "tty=", "noise", "clock", "socket=", "output="]) options, files = getopt.gnu_getopt(sys.argv[1:], "xF:s:o:c", ["debug", "tty=", "noise", "clock", "socket=", "output="])
tty = None tty = None
baud = 2400
socket = None socket = None
out = None out = None
@ -29,6 +30,10 @@ for o,v in options:
if tty: if tty:
raise ValueError("can only do one tty") raise ValueError("can only do one tty")
tty = v tty = v
v = v.split(",", 1)
if v[1:]:
tty = v[0]
baud = int(v[1])
do_clock = True do_clock = True
if o in "--clock": if o in "--clock":
@ -62,7 +67,7 @@ if len(files)==1:
tty = files[0] tty = files[0]
if tty: if tty:
inp = serial.Serial(port=tty, baudrate=2400) inp = serial.Serial(port=tty, baudrate=baud)
else: else:
inp = fileinput.input(files, mode="rb") inp = fileinput.input(files, mode="rb")
@ -77,10 +82,7 @@ def noise(line, prefix="x"):
if not do_noise: if not do_noise:
return False return False
trunc = "" trunc = ""
if len(line)>40: print(prefix, repr(line), file=out)
line = line[:40]
trunc = ""
print(prefix, repr(line), trunc, file=out)
return False return False
def echo(line, *a): def echo(line, *a):
@ -149,13 +151,20 @@ def emit_voltages(Tcpu, Vcpu, Vbat, dVntc, Vntc, cVntc, Vrf, cVrf):
freq = linear_regression.linreg(p=2, xoff=True, yoff=True, xdecay=600) freq = linear_regression.linreg(p=2, xoff=True, yoff=True, xdecay=600)
def clock(line): def clock(line):
ll = line.split()
try: try:
c = int(line.split()[1], 16) c = int(ll[1], 16)
except: except:
return noise(line, "t") return noise(line, "t")
s = None s = None
t = None
try:
t = float(ll[3])
except:
pass
if do_clock: if do_clock:
t = time.time() t = time.time()
if t is not None:
freq.add(t, c) freq.add(t, c)
s = freq.solve() s = freq.solve()
if s is None: if s is None:
@ -369,7 +378,7 @@ def emit_sigrow(c, cc):
if len(cc)<34: if len(cc)<34:
return return
for r,v in SIGROW.items(): for r,v in SIGROW.items():
print(f"s {r} {" ".join([f"{i:02x}" for i in cc[v[0]:v[0]+v[1]]])}") print(f"s {r} {' '.join([f'{i:02x}' for i in cc[v[0]:v[0]+v[1]]])}")
i = devid() i = devid()
if i in DEVID: if i in DEVID:
print(f"s DEVICE 0x{i:06x} {DEVID[i]} SN {bytes(cc[3:13])}") print(f"s DEVICE 0x{i:06x} {DEVID[i]} SN {bytes(cc[3:13])}")