Skip to content

Instantly share code, notes, and snippets.

@atx
Last active June 17, 2020 20:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atx/5193168cfd5f2fe07f95175f91ac03f6 to your computer and use it in GitHub Desktop.
Save atx/5193168cfd5f2fe07f95175f91ac03f6 to your computer and use it in GitHub Desktop.
Hacky script which combines `sigrok-cli` and `dpsctl.py` to calibrate OpenDPS. Will likely need modifications before use. Note: The current calibration is currently untested, it looks like the DPS needs nonzero load resistor in order to measure current and I do not have any at hand.
#! /usr/bin/env python3
import argparse
import subprocess
import pathlib
import time
import scipy.stats
import numpy as np
DAC_MAX = 2**12 - 1
def to_base_units(val, unit_scaled):
if len(unit_scaled) == 1:
return val, unit_scaled
assert len(unit_scaled) == 2
FACTORS = {
"m": 1e-3
}
factor = unit_scaled[0]
unit = unit_scaled[1]
return FACTORS[factor] * val, unit
def sigrok_measure(driver):
# My sigrok-cli returns weird exit codes, hence check_output does not work
p = subprocess.Popen(
["sigrok-cli", "--driver", driver, "-O", "analog", "--samples", "1"],
stderr=subprocess.PIPE, stdout=subprocess.PIPE
)
output, error = p.communicate()
_, val_raw, unit_raw, *_ = output.split()
return to_base_units(float(val_raw), unit_raw.decode())
class DPSCTL:
def __init__(self, path, dev):
self.path = path
self.dev = dev
def call(self, *args):
return subprocess.check_output(
[self.path, "-d", self.dev] + list(args)
)
def set_param(self, name, value):
output = self.call("-p", f"{name}={value}")
assert output.endswith(b"ok\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-c", "--dpsctl", type=pathlib.Path)
parser.add_argument("-d", "--dpsctl-dev", required=True)
parser.add_argument("-s", "--sigrok-driver", required=True)
parser.add_argument("-t", "--type", choices=["voltage", "current"], required=True)
parser.add_argument("-n", "--sample-count", type=int, default=50)
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--dump", type=pathlib.Path)
args = parser.parse_args()
ctl = DPSCTL(args.dpsctl, args.dpsctl_dev)
# This disables various limits
ctl.call("--screen", "settings")
ctl.call("-o", "on")
if args.type == "voltage":
par_dac = "V_DAC"
par_dac_other = "A_DAC"
par_adc = "V_ADC"
par_out_adc = "VOUT_ADC"
units = "V"
elif args.type == "current":
par_dac = "A_DAC"
par_dac_other = "V_DAC"
par_adc = "A_ADC"
par_out_adc = "IOUT_ADC"
units = "A"
ctl.set_param(par_dac, 0)
ctl.set_param(par_dac_other, DAC_MAX)
time.sleep(3) # This can take a long time if we are at the other side
dacs_full = np.arange(0, DAC_MAX, DAC_MAX / args.sample_count, dtype=np.int32)
dacs = []
out_real = []
out_measured = []
for i, v_dac in enumerate(dacs_full):
ctl.set_param(par_dac, v_dac)
time.sleep(1.0)
v, u = sigrok_measure(args.sigrok_driver)
assert u == units, "Wrong units!"
print(v_dac, v)
dacs.append(v_dac)
out_real.append(v)
cal_report = map(str.strip, ctl.call("-cr").decode().split("\n"))
for line in cal_report:
if not line.startswith(par_out_adc):
continue
out_measured.append(int(line.split()[-1]))
# The current range does not work from 0, so we have to cut the beginning
# too
slopes = np.diff(out_real) / np.diff(dacs)
i_first, i_last = np.where(abs(slopes) > 0.005)[0][[0, -1]]
i_last += 1
out_measured = out_measured[i_first:i_last]
out_real = out_real[i_first:i_last]
dacs = dacs[i_first:i_last]
if args.dump:
with args.dump.open("w") as f:
for v_dac, v_out, v_adc in zip(dacs, out_real, out_measured):
f.write(f"{v_dac} {v_out} {v_adc}\n")
out_real = np.array(out_real)*1e3 # Should be in mV
dac_k, dac_c, *_ = scipy.stats.linregress(out_real, dacs)
print(f"DAC K = {dac_k} C = {dac_c}")
adc_k, adc_c, *_ = scipy.stats.linregress(out_measured, out_real)
print(f"ADC K = {adc_k} C = {adc_c}")
if not args.dry_run:
ctl.call(
"-c",
f"{par_dac}_K={dac_k}",
f"{par_dac}_C={dac_c}"
)
ctl.call(
"-c",
f"{par_adc}_K={adc_k}",
f"{par_adc}_C={adc_c}"
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment