aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2019-01-21 15:35:27 -0500
committerKevin O'Connor <kevin@koconnor.net>2019-01-21 20:27:41 -0500
commitfc946c796c72c83a5c9c753a3ca69fce3b8a88da (patch)
treedf71e4822bc0614a36e7d2d6ac8dab324219f82d
parent99980817c31a33bb4e330c2357a8975a4441fbc4 (diff)
downloadkutter-fc946c796c72c83a5c9c753a3ca69fce3b8a88da.tar.gz
kutter-fc946c796c72c83a5c9c753a3ca69fce3b8a88da.tar.xz
kutter-fc946c796c72c83a5c9c753a3ca69fce3b8a88da.zip
adc_temperature: Split linear interpolation code into its own class
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r--klippy/extras/adc_temperature.py106
1 files changed, 60 insertions, 46 deletions
diff --git a/klippy/extras/adc_temperature.py b/klippy/extras/adc_temperature.py
index d347c222..cb7b0e28 100644
--- a/klippy/extras/adc_temperature.py
+++ b/klippy/extras/adc_temperature.py
@@ -37,61 +37,75 @@ class PrinterADCtoTemperature:
######################################################################
-# Linear voltage sensors
+# Linear interpolation
+######################################################################
+
+# Helper code to perform linear interpolation
+class LinearInterpolate:
+ def __init__(self, samples):
+ self.keys = []
+ self.slopes = []
+ last_key = last_value = None
+ for key, value in sorted(samples):
+ if last_key is None:
+ last_key = key
+ last_value = value
+ continue
+ if key <= last_key:
+ raise ValueError("duplicate value")
+ gain = (value - last_value) / (key - last_key)
+ offset = last_value - last_key * gain
+ if self.slopes and self.slopes[-1] == (gain, offset):
+ continue
+ last_value = value
+ last_key = key
+ self.keys.append(key)
+ self.slopes.append((gain, offset))
+ if not self.keys:
+ raise ValueError("need at least two samples")
+ self.keys.append(9999999999999.)
+ self.slopes.append(self.slopes[-1])
+ def interpolate(self, key):
+ pos = bisect.bisect(self.keys, key)
+ gain, offset = self.slopes[pos]
+ return key * gain + offset
+ def reverse_interpolate(self, value):
+ values = [key * gain + offset for key, (gain, offset) in zip(
+ self.keys, self.slopes)]
+ if values[0] < values[-2]:
+ valid = [i for i in range(len(values)) if values[i] >= value]
+ else:
+ valid = [i for i in range(len(values)) if values[i] <= value]
+ gain, offset = self.slopes[min(valid + [len(values) - 1])]
+ return (value - offset) / gain
+
+
+######################################################################
+# Linear voltage to temperature converter
######################################################################
# Linear style conversion chips calibrated with two temp measurements
-class Linear:
+class LinearVoltage:
def __init__(self, config, params):
- self.adc_samples = []
- self.slope_samples = []
- self.calc_coefficients(config, params)
- def calc_coefficients(self, config, params):
adc_voltage = config.getfloat('adc_voltage', 5., above=0.)
- last_volt = last_temp = None
- for volt, temp in sorted([(v, t) for t, v in params]):
+ samples = []
+ for temp, volt in params:
adc = volt / adc_voltage
if adc < 0. or adc > 1.:
logging.warn("Ignoring adc sample %.3f/%.3f in heater %s",
temp, volt, config.get_name())
continue
- if last_volt is None:
- last_volt = volt
- last_temp = temp
- continue
- if volt <= last_volt:
- raise config.error("adc_temperature duplicate voltage")
- slope = (temp - last_temp) / (volt - last_volt)
- gain = adc_voltage * slope
- offset = last_temp - last_volt * slope
- if self.slope_samples and self.slope_samples[-1] == (gain, offset):
- continue
- last_temp = temp
- last_volt = volt
- self.adc_samples.append(adc)
- self.slope_samples.append((gain, offset))
- if not self.adc_samples:
- raise config.error(
- "adc_temperature needs two volt and temperature measurements")
- self.adc_samples[-1] = 1.
- def calc_temp(self, adc):
- pos = bisect.bisect(self.adc_samples, adc)
- gain, offset = self.slope_samples[pos]
- return read_value * gain + offset
- def calc_adc(self, temp):
- temps = [adc * gain + offset for adc, (gain, offset) in zip(
- self.adc_samples, self.slope_samples)]
- if temps[0] < temps[-1]:
- pos = min([i for i in range(len(temps)) if temps[i] >= temp]
- + [len(temps) - 1])
- else:
- pos = min([i for i in range(len(temps)) if temps[i] <= temp]
- + [len(temps) - 1])
- gain, offset = self.slope_samples[pos]
- return (temp - offset) / gain
+ samples.append((adc, temp))
+ try:
+ li = LinearInterpolate(samples)
+ except ValueError as e:
+ raise config.error("adc_temperature %s in heater %s" % (
+ str(e), config.get_name()))
+ self.calc_temp = li.interpolate
+ self.calc_adc = li.reverse_interpolate
# Custom defined sensors from the config file
-class CustomLinear:
+class CustomLinearVoltage:
def __init__(self, config):
self.name = " ".join(config.get_name().split()[1:])
self.params = []
@@ -102,7 +116,7 @@ class CustomLinear:
v = config.getfloat("voltage%d" % (i,))
self.params.append((t, v))
def create(self, config):
- lv = Linear(config, self.params)
+ lv = LinearVoltage(config, self.params)
return PrinterADCtoTemperature(config, lv)
AD595 = [
@@ -132,10 +146,10 @@ def load_config(config):
pheater = config.get_printer().lookup_object("heater")
for sensor_type, params in [("AD595", AD595), ("PT100 INA826", PT100)]:
func = (lambda config, params=params:
- PrinterADCtoTemperature(config, Linear(config, params)))
+ PrinterADCtoTemperature(config, LinearVoltage(config, params)))
pheater.add_sensor(sensor_type, func)
def load_config_prefix(config):
- linear = CustomLinear(config)
+ linear = CustomLinearVoltage(config)
pheater = config.get_printer().lookup_object("heater")
pheater.add_sensor(linear.name, linear.create)