From 8bc7acb857b01b35afcede723eea952295b610f3 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 21 Apr 2011 16:45:59 -0400 Subject: [PATCH] Massive rewrite (v 0.6) to base everything on Cython and revamped pypiezo. --- calibcant/calibrate.py | 585 ++++++++++++++++------------------------- 1 file changed, 228 insertions(+), 357 deletions(-) diff --git a/calibcant/calibrate.py b/calibcant/calibrate.py index 54279a5..e8b7293 100644 --- a/calibcant/calibrate.py +++ b/calibcant/calibrate.py @@ -18,30 +18,26 @@ # License along with calibcant. If not, see # . -""" -Aquire and analyze cantilever calibration data. - -W. Trevor King Dec. 2007-Jan. 2008 +"""Acquire and analyze cantilever calibration data. -GPL BOILERPLATE +The relevent physical quantities are: +* Vzp_out Output z-piezo voltage (what we generate) +* Vzp Applied z-piezo voltage (after external ZPGAIN) +* Zp The z-piezo position +* Zcant The cantilever vertical deflection +* Vphoto The photodiode vertical deflection voltage (what we measure) +* Fcant The force on the cantilever +* T The temperature of the cantilever and surrounding solution +* (another thing we measure or guess) +* k_b Boltzmann's constant -The relevent physical quantities are : - Vzp_out Output z-piezo voltage (what we generate) - Vzp Applied z-piezo voltage (after external ZPGAIN) - Zp The z-piezo position - Zcant The cantilever vertical deflection - Vphoto The photodiode vertical deflection voltage (what we measure) - Fcant The force on the cantilever - T The temperature of the cantilever and surrounding solution - (another thing we measure or guess) - k_b Boltzmann's constant +Which are related by the parameters: -Which are related by the parameters : - zpGain Vzp_out / Vzp - zpSensitivity Zp / Vzp - photoSensitivity Vphoto / Zcant - k_cant Fcant / Zcant +* zpGain Vzp_out / Vzp +* zpSensitivity Zp / Vzp +* photoSensitivity Vphoto / Zcant +* k_cant Fcant / Zcant Cantilever calibration assumes a pre-calibrated z-piezo (zpSensitivity) and a amplifier (zpGain). In our lab, the z-piezo is @@ -49,21 +45,31 @@ calibrated by imaging a calibration sample, which has features with well defined sizes, and the gain is set with a knob on the Nanoscope. photoSensitivity is measured by bumping the cantilever against the -surface, where Zp = Zcant (see bump_aquire() and the bump_analyze +surface, where Zp = Zcant (see bump_acquire() and the bump_analyze submodule). k_cant is measured by watching the cantilever vibrate in free solution -(see the vib_aquire() and the vib_analyze submodule). The average +(see the vib_acquire() and the vib_analyze submodule). The average energy of the cantilever in the vertical direction is given by the equipartition theorem. - 1/2 k_b T = 1/2 k_cant - so k_cant = k_b T / Zcant**2 - but Zcant = Vphoto / photoSensitivity - so k_cant = k_b T * photoSensitivty**2 / + +.. math:: \frac{1}{2} k_b T = \frac{1}{2} k_cant + +so + +.. math:: k_cant = \frac{k_b T}{Zcant**2} + +but + +.. math:: Zcant = \frac{Vphoto}{photoSensitivity} + +so + +.. math:: k_cant = \frac{k_b T * photoSensitivty^2}{} We measured photoSensitivity with the surface bumps. We can either measure T using an external function (see temperature.py), or just -estimate it (see T_aquire() and the T_analyze submodule). Guessing +estimate it (see T_acquire() and the T_analyze submodule). Guessing room temp ~22 deg C is actually fairly reasonable. Assuming the actual fluid temperature is within +/- 5 deg, the error in the spring constant k_cant is within 5/273.15 ~= 2%. A time series of Vphoto @@ -73,351 +79,216 @@ the average variance . We do all these measurements a few times to estimate statistical errors. -The functions are layed out in the families: - bump_*(), vib_*(), T_*(), and calib_*() -where calib_{save|load|analyze}() deal with derived data, not -real-world data. - -For each family, * can be any of : - aquire get real-world data - save store real-world data to disk - load get real-world data from disk - analyze interperate the real-world data. - plot show a nice graphic to convince people we're working :p - load_analyze_tweaked - read a file with a list of paths to previously saved - real world data load each file using *_load(), analyze - using *_analyze(), and optionally plot using *_plot(). - Intended for re-processing old data. -A family name without any _* extension (e.g. bump()), runs *_aquire(), - *_save(), *_analyze(), *_plot(). - -We also define the two positioning functions: - move_just_onto_surface() and move_far_from_surface() -which make automating the calibration procedure more straightforward. -""" +The functions are layed out in the families:: -import numpy -import time + bump_*(), vib_*(), T_*(), and calib_*() -import FFT_tools -import piezo.z_piezo_utils as z_piezo_utils -from splittable_kwargs import splittableKwargsFunction, \ - make_splittable_kwargs_function +For each family, * can be any of: -from . import common -from . import config -from . import bump_analyze -from . import T_analyze -from . import vib_analyze -from . import analyze +* acquire get real-world data +* save store real-world data to disk +* load get real-world data from disk +* analyze interperate the real-world data. +* plot show a nice graphic to convince people we're working :p +A family name without any `_*` extension (e.g. `bump()`), runs +`*_acquire()`, `*_analyze()`, and `*_save()`. `*_analyze()` will run +`*_plot()` if `matplotlib` is set in `calibcant.base_config`. +""" -# bump family +from numpy import zeros as _zeros +from numpy import float as _float +from time import sleep as _sleep -@splittableKwargsFunction() -def bump_aquire(zpiezo, push_depth=200, npoints=1024, push_speed=1000) : - """ - Ramps closer push_depth and returns to the original position. - Inputs: - zpiezo an opened zpiezo.zpiezo instance - push_depth distance to approach, in nm - npoints number points during the approach and during the retreat - push_speed piezo speed during approach and retreat, in nm/s - Returns the aquired ramp data dictionary, with data in DAC/ADC bits. - """ - # generate the bump output - nm_per_step = float(push_depth) / npoints - freq = push_speed / nm_per_step # freq is sample frequency in Hz - start_pos = zpiezo.curPos() - pos_dist = zpiezo.pos_nm2out(push_depth) - zpiezo.pos_nm2out(0) - close_pos = start_pos + pos_dist - appr = numpy.linspace(start_pos, close_pos, npoints) - retr = numpy.linspace(close_pos, start_pos, npoints) - out = numpy.concatenate((appr, retr)) - # run the bump, and measure deflection - if config.TEXT_VERBOSE : - print "Bump %g nm at %g nm/s" % (push_depth, push_speed) - data = zpiezo.ramp(out, freq) - return data - -@splittableKwargsFunction(bump_aquire, - (bump_analyze.bump_save, 'data'), - (bump_analyze.bump_analyze, 'data')) -def bump(**kwargs): - """ - Wrapper around bump_aquire(), bump_save(), bump_analyze() - """ - bump_aquire_kwargs,bump_save_kwargs,bump_analyze_kwargs = \ - bump._splitargs(bump, kwargs) - data = bump_aquire(**bump_aquire_kwargs) - bump_analyze.bump_save(data, **bump_save_kwargs) - photoSensitivity = bump_analyze.bump_analyze(data, **bump_analyze_kwargs) - return photoSensitivity - -# T family. -# Fairly stubby, since a one shot Temp measurement is a common thing. -# We just wrap that to provide a consistent interface. - -@splittableKwargsFunction() -def T_aquire(get_T=None) : - """ - Measure the current temperature of the sample, - or, if get_T == None, fake it by returning config.DEFAULT_TEMP - """ - if get_T == None : - if config.TEXT_VERBOSE : - print "Fake temperature %g" % config.DEFAULT_TEMP - return config.DEFAULT_TEMP - else : - if config.TEXT_VERBOSE : - print "Measure temperature" - return get_T() - -@splittableKwargsFunction(T_aquire, - (T_analyze.T_save, 'T'), - (T_analyze.T_analyze, 'T')) -def T(**kwargs): - """ - Wrapper around T_aquire(), T_save(), T_analyze(), T_plot() - """ - T_aquire_kwargs,T_save_kwargs,T_analyze_kwargs = \ - T._splitargs(T, kwargs) - T_raw = T_aquire(**T_aquire_kwargs) - T_analyze.T_save(T_raw, **T_save_kwargs) - T_ret = T_analyze.T_analyze(T_raw, **T_analyze_kwargs) # returns array - return T_ret[0] +from . import LOG as _LOG -# vib family +from .bump import bump as _bump +from .T import T as _T +from .vib import vib as _vib +from .analyze import calib_analyze as _calib_analyze +from .analyze import calib_save as _calib_save -@splittableKwargsFunction() -def vib_aquire(zpiezo, time=1, freq=50e3) : - """ - Record data for TIME seconds at FREQ Hz from ZPIEZO at it's current position. - """ - # round up to the nearest power of two, for efficient FFT-ing - nsamps = FFT_tools.ceil_pow_of_two(time*freq) - time = nsamps / freq - # take some data, keeping the position voltage at it's current value - out = numpy.ones((nsamps,), dtype=numpy.uint16) * zpiezo.curPos() - if config.TEXT_VERBOSE : - print "get %g seconds of data" % time - data = zpiezo.ramp(out, freq) - data['sample frequency Hz'] = numpy.array([freq]) - return data - -@splittableKwargsFunction(vib_aquire, - (vib_analyze.vib_save, 'data'), - (vib_analyze.vib_analyze, 'deflection_bits', 'freq')) -def vib(**kwargs) : - """ - Wrapper around vib_aquire(), vib_save(), vib_analyze() - """ - vib_aquire_kwargs,vib_save_kwargs,vib_analyze_kwargs = \ - vib._splitargs(vib, kwargs) - data = vib_aquire(**vib_aquire_kwargs) - vib_analyze.vib_save(data, **vib_save_kwargs) - freq = data['sample frequency Hz'] - deflection_bits = data['Deflection input'] - Vphoto_var = vib_analyze.vib_analyze(deflection_bits=deflection_bits, - freq=freq, **vib_analyze_kwargs) - return Vphoto_var - -# A few positioning functions, so we can run bump_aquire() and vib_aquire() -# with proper spacing relative to the surface. - -@splittableKwargsFunction() -def move_just_onto_surface(stepper, zpiezo, Depth_nm=-50, setpoint=2) : - """ - Uses z_piezo_utils.getSurfPos() to pinpoint the position of the - surface. Adjusts the stepper position as required to get within - stepper_tol nm of the surface. Then set Vzp to place the - cantilever Depth_nm onto the surface. Negative Depth_nm values - will place the cantilever that many nm _off_ the surface. - - If getSurfPos() fails to find the surface, backs off (for safety) - and steps in (without moving the zpiezo) until Vphoto > setpoint. - """ - stepper_tol = 250 # nm, generous estimate of the fullstep stepsize - - if config.TEXT_VERBOSE : - print "moving just onto surface" - # Zero the piezo - if config.TEXT_VERBOSE : - print "zero the z piezo output" - zpiezo.jumpToPos(zpiezo.pos_nm2out(0)) - # See if we're near the surface already - if config.TEXT_VERBOSE : - print "See if we're starting near the surface" - try : - dist = zpiezo.pos_out2nm( \ - z_piezo_utils.getSurfPos(zpiezo, zpiezo.def_V2in(setpoint)) - ) - except (z_piezo_utils.tooClose, z_piezo_utils.poorFit), string : - if config.TEXT_VERBOSE : - print "distance failed with: ", string - print "Back off 200 half steps" - # Back away 200 steps - stepper.step_rel(-400) - stepper.step_rel(200) - sp = zpiezo.def_V2in(setpoint) # sp = setpoint in bits - zpiezo.updateInputs() - cd = zpiezo.curDef() # cd = current deflection in bits - if config.TEXT_VERBOSE : - print "Single stepping approach" - while cd < sp : - if config.TEXT_VERBOSE : - print "deflection %g < setpoint %g. step closer" % (cd, sp) - stepper.step_rel(2) # Full step in - zpiezo.updateInputs() - cd = zpiezo.curDef() - # Back off two steps (protecting against backlash) - if config.TEXT_VERBOSE : - print "Step back 4 half steps to get off the setpoint" - stepper.step_rel(-200) - stepper.step_rel(196) - # get the distance to the surface - zpiezo.updateInputs() - if config.TEXT_VERBOSE : - print "get surf pos, with setpoint %g (%d)" % (setpoint, zpiezo.def_V2in(setpoint)) - for i in range(20) : # HACK, keep stepping back until we get a distance - try : - dist = zpiezo.pos_out2nm( \ - z_piezo_utils.getSurfPos(zpiezo,zpiezo.def_V2in(setpoint))) - except (z_piezo_utils.tooClose, z_piezo_utils.poorFit), string : - stepper.step_rel(-200) - stepper.step_rel(198) - continue - break - if i >= 19 : - print "tried %d times, still too close! bailing" % i - print "probably an invalid setpoint." - raise Exception, "weirdness" - if config.TEXT_VERBOSE : - print 'distance to surface ', dist, ' nm' - # fine tune the stepper position - while dist < -stepper_tol : # step back if we need to - stepper.step_rel(-200) - stepper.step_rel(198) - dist = zpiezo.pos_out2nm( \ - z_piezo_utils.getSurfPos(zpiezo, zpiezo.def_V2in(setpoint))) - if config.TEXT_VERBOSE : - print 'distance to surface ', dist, ' nm, step back' - while dist > stepper_tol : # and step forward if we need to - stepper.step_rel(2) - dist = zpiezo.pos_out2nm( \ - z_piezo_utils.getSurfPos(zpiezo, zpiezo.def_V2in(setpoint))) - if config.TEXT_VERBOSE : - print 'distance to surface ', dist, ' nm, step closer' - # now adjust the zpiezo to place us just onto the surface - target = dist + Depth_nm - zpiezo.jumpToPos(zpiezo.pos_nm2out(target)) - # and we're there :) - if config.TEXT_VERBOSE : - print "We're %g nm into the surface" % Depth_nm - -@splittableKwargsFunction() -def move_far_from_surface(stepper, um_back=50) : - """ - Step back a specified number of microns. - (uses very rough estimate of step distance at the moment) - """ - step_nm = 100 - steps = int(um_back*1000/step_nm) - print "step back %d steps" % steps - stepper.step_rel(-steps) - - -# and finally, the calib family - -@splittableKwargsFunction((move_just_onto_surface, 'stepper', 'zpiezo'), - (bump, 'zpiezo', 'log_dir', 'Vphoto_in2V'), - (move_far_from_surface, 'stepper'), - (T, 'log_dir'), - (vib, 'zpiezo', 'log_dir', 'Vphoto_in2V'), - (analyze.calib_save, 'bumps','Ts','vibs','log_dir')) -def calib_aquire(stepper, zpiezo, num_bumps=10, num_Ts=10, num_vibs=20, - log_dir=config.LOG_DIR, Vphoto_in2V=config.Vphoto_in2V, - **kwargs): + +def move_far_from_surface(stepper, distance): + """Step back approximately `distance` meters. """ - Aquire data for calibrating a cantilever in one function. - return (bump, T, vib), each of which is an array. - Inputs : - stepper a stepper.stepper_obj for coarse Z positioning - zpiezo a z_piezo.z_piezo for fine positioning and deflection readin - num_bumps number of 'bumps' (see Outputs) - num_temps number of 'Ts' (see Outputs) - num_vibs number of 'vib's (see Outputs) - log_dir directory to log data to. Default 'None' disables logging. - Vphoto_in2V function to convert photodiode input bits to Volts - - + other kwargs. Run calib_aquire._kwargs(calib_aquire) to see - all options. Run calib_aquire._childSplittables to see a list - of kwarg functions that this function calls. - - Outputs (all are arrays of recorded data) : - bumps measured (V_photodiode / nm_tip) proportionality constant - Ts measured temperature (K) - vibs measured V_photodiode variance in free solution + steps = int(distance/stepper.step_size) + _LOG.info('step back %d steps (~%g m)' % (steps, distance)) + stepper.step_relative(-steps) + +def calib_acquire(afm, calibration_config, bump_config, temperature_config, + vibration_config, filename=None, group='/'): + """Acquire data for calibrating a cantilever in one function. + + Inputs: + afm a pyafm.AFM instance + calibration_config a .config._CalibrationConfig instance + bump_config a .config._BumpConfig instance + temperature_config a .config._TConfig instance + vibration_config a .config._VibrationConfig instance + + Outputs (all are arrays of recorded data): + bumps measured (V_photodiode / nm_tip) proportionality constant + Ts measured temperature (K) + vibs measured V_photodiode variance (Volts**2) in free solution + + The temperatures are collected after moving far from the surface + but before and vibrations are measured to give everything time to + settle after the big move. """ - move_just_onto_surface_kwargs,bump_kwargs,move_far_from_surface_kwargs, \ - T_kwargs,vib_kwargs,calib_save_kwargs = \ - calib_aquire._splitargs(calib_aquire, kwargs) - # get bumps - bumps = numpy.zeros((num_bumps,), dtype=numpy.float) - for i in range(num_bumps) : - move_just_onto_surface(stepper, zpiezo, **move_just_onto_surface_kwargs) - bumps[i] = bump(zpiezo=zpiezo, log_dir=log_dir, - Vphoto_in2V=Vphoto_in2V, **bump_kwargs) - if config.TEXT_VERBOSE : - print bumps - - move_far_from_surface(stepper, **move_far_from_surface_kwargs) - - # get Ts - Ts = numpy.zeros((num_Ts,), dtype=numpy.float) - for i in range(num_Ts) : - Ts[i] = T(**T_kwargs) - time.sleep(1) # wait a bit to get an independent temperature measure - print Ts + assert group.endswith('/'), group + + bumps = _zeros((calibration_config['num-bumps'],), dtype=_float) + for i in range(calibration_config['num-bumps']): + _LOG.info('acquire bump %d of %d' % (i, calibration_config['num-bumps'])) + bumps[i] = _bump(afm=afm, bump_config=bump_config, + filename=filename, group='%sbump/%d/' % (group, i)) + _LOG.debug('bumps: %s' % bumps) + + move_far_from_surface( + afm.stepper, distance=calibration_config['vibration-spacing']) + + Ts = _zeros((calibration_config['num-temperatures'],), dtype=_float) + for i in range(calibration_config['num-temperatures']): + _LOG.info('acquire T %d of %d' + % (i, calibration_config['num-temperatures'])) + Ts[i] = _T( + get_T=afm.get_temperature, temperature_config=temperature_config, + filename=filename, group='%stemperature/%d/' % (group, i)) + _sleep(calibration_config['temperature-sleep']) + _LOG.debug('temperatures: %s' % Ts) # get vibs - vibs = numpy.zeros((num_vibs,), dtype=numpy.float) - for i in range(num_vibs) : - vibs[i] = vib(zpiezo=zpiezo, log_dir=log_dir, Vphoto_in2V=Vphoto_in2V, - **vib_kwargs) - print vibs - - analyze.calib_save(bumps, Ts, vibs, log_dir, **calib_save_kwargs) - + vibs = _zeros((calibration_config['num-vibrations'],), dtype=_float) + for i in range(calibration_config['num-vibrations']): + vibs[i] = _vib( + piezo=afm.piezo, vibration_config=vibration_config, + filename=filename, group='%svibration/%d/' % (group, i)) + _LOG.debug('vibrations: %s' % vibs) + return (bumps, Ts, vibs) +def calib(afm, calibration_config, bump_config, temperature_config, + vibration_config, filename=None, group='/'): + """Calibrate a cantilever in one function. -@splittableKwargsFunction( \ - (calib_aquire, 'log_dir'), - (analyze.calib_analyze, 'bumps','Ts','vibs')) -def calib(log_dir=config.LOG_DIR, **kwargs) : - """ - Calibrate a cantilever in one function. - The I-don't-care-about-the-details black box version :p. - return (k, k_s) Inputs: - (see calib_aquire()) - Outputs : - k cantilever spring constant (in N/m, or equivalently nN/nm) - k_s standard deviation in our estimate of k - Notes : - See get_calibration_data() for the data aquisition code - See analyze_calibration_data() for the analysis code + (see `calib_acquire()`) + + Outputs: + k cantilever spring constant (in N/m, or equivalently nN/nm) + k_s standard deviation in our estimate of k + + >>> import os + >>> from pprint import pprint + >>> import tempfile + >>> from pycomedi.device import Device + >>> from pycomedi.subdevice import StreamingSubdevice + >>> from pycomedi.channel import AnalogChannel, DigitalChannel + >>> from pycomedi.constant import AREF, IO_DIRECTION, SUBDEVICE_TYPE, UNIT + >>> from pypiezo.afm import AFMPiezo + >>> from pypiezo.base import PiezoAxis, InputChannel + >>> from pypiezo.config import (HDF5_ChannelConfig, HDF5_AxisConfig, + ... pprint_HDF5) + >>> from stepper import Stepper + >>> from pyafm import AFM + >>> from .config import (HDF5_CalibrationConfig, HDF5_BumpConfig, + ... HDF5_TemperatureConfig, HDF5_VibrationConfig) + >>> from .analyze import calib_load_all + + >>> fd,filename = tempfile.mkstemp(suffix='.h5', prefix='calibcant-') + >>> os.close(fd) + + >>> d = Device('/dev/comedi0') + >>> d.open() + + Setup an `AFMPiezo` instance. + + >>> s_in = d.find_subdevice_by_type(SUBDEVICE_TYPE.ai, + ... factory=StreamingSubdevice) + >>> s_out = d.find_subdevice_by_type(SUBDEVICE_TYPE.ao, + ... factory=StreamingSubdevice) + + >>> axis_channel = s_out.channel( + ... 0, factory=AnalogChannel, aref=AREF.ground) + >>> input_channel = s_in.channel(0, factory=AnalogChannel, aref=AREF.diff) + >>> for chan in [axis_channel, input_channel]: + ... chan.range = chan.find_range(unit=UNIT.volt, min=-10, max=10) + + We set the minimum voltage for the `z` axis to -9 (a volt above + the minimum possible voltage) to help with testing + `.get_surface_position`. Without this minimum voltage, small + calibration errors could lead to a railed -10 V input for the + first few surface approaching steps, which could lead to an + `EdgeKink` error instead of a `FlatFit` error. + + >>> axis_config = HDF5_AxisConfig(filename, '/bump/config/z/axis') + >>> axis_config.update( + ... {'gain':20, 'sensitivity':8e-9, 'minimum':-9}) + >>> axis_channel_config = HDF5_ChannelConfig( + ... filename, '/bump/config/z/channel') + >>> input_channel_config = HDF5_ChannelConfig( + ... filename, '/bump/config/deflection/channel') + + >>> a = PiezoAxis(axis_config=axis_config, + ... axis_channel_config=axis_channel_config, + ... axis_channel=axis_channel, name='z') + >>> a.setup_config() + + >>> c = InputChannel( + ... channel_config=input_channel_config, channel=input_channel, + ... name='deflection') + >>> c.setup_config() + + >>> piezo = AFMPiezo(axes=[a], input_channels=[c]) + + Setup a `stepper` instance. + + >>> s_d = d.find_subdevice_by_type(SUBDEVICE_TYPE.dio) + >>> d_channels = [s_d.channel(i, factory=DigitalChannel) + ... for i in (0, 1, 2, 3)] + >>> for chan in d_channels: + ... chan.dio_config(IO_DIRECTION.output) + + >>> def write(value): + ... s_d.dio_bitfield(bits=value, write_mask=2**4-1) + + >>> stepper = Stepper(write=write) + + Setup an `AFM` instance. + + >>> afm = AFM(piezo, stepper) + + Test calibration: + + >>> calibration_config = HDF5_CalibrationConfig( + ... filename=filename, group='/bump/config/calibration/') + >>> bump_config = HDF5_BumpConfig( + ... filename=filename, group='/bump/config/bump/') + >>> temperature_config = HDF5_TemperatureConfig( + ... filename=filename, group='/bump/config/temperature/') + >>> vibration_config = HDF5_VibrationConfig( + ... filename=filename, group='/bump/config/vibration') + >>> calib(afm, calibration_config, bump_config, temperature_config, + ... vibration_config, filename=filename, group='/') + TODO: replace skipped example data with real-world values + >>> pprint_HDF5(filename) # doctest: +ELLIPSIS, +REPORT_UDIFF + >>> everything = calib_load_all(filename, '/') + >>> pprint(everything) + + Close the Comedi device. + + >>> d.close() + + Cleanup our temporary config file. + + os.remove(filename) """ - calib_aquire_kwargs,calib_analyze_kwargs = \ - calib._splitargs(calib, kwargs) - a, T, vib = calib_aquire(**calib_aquire_kwargs) - k,k_s,ps2_m, ps2_s,T_m,T_s,one_o_Vp2_m,one_o_Vp2_s = \ - analyze.calib_analyze(a, T, vib, **calib_analyze_kwargs) - analyze.calib_save_analysis(k, k_s, ps2_m, ps2_s, T_m, T_s, - one_o_Vp2_m, one_o_Vp2_s, log_dir) + bumps, Ts, vibs = calib_acquire( + afm, calibration_config, bump_config, temperature_config, + vibration_config, filename=filename, group=group) + # TODO: convert vib units? + k,k_s = _calib_analyze(bumps, Ts, vibs) + _calib_save(filename, group=group+'calibration/', bumps=bumps, Ts=Ts, + vibs=vibs, calibration_config=calibration_config, k=k, k_s=k_s) return (k, k_s) - - - -- 2.26.2