NanoScope.
Photo-sensitivity is measured by bumping the cantilever against the
-surface, where `Zp = Zcant` (see the `bump_*()` family of functions).
-The measured slope Vphoto/Vout is converted to photo-sensitivity via
+surface, where `Zp = Zcant`. The measured slope Vphoto/Vout is
+converted to photo-sensitivity via::
Vphoto/Vzp_out * Vzp_out/Vzp * Vzp/Zp * Zp/Zcant = Vphoto/Zcant
(measured) (1/zp_gain) (1/zp_sensitivity) (1) (photo_sensitivity)
We do all these measurements a few times to estimate statistical
errors.
-
-The functions are layed out in the families:
- bump_*()
-For each family, * can be any of:
- 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()`,
-`*_save()`, `*_analyze()`.
-
-If `package_config['matplotlib']` is `True`, `*_analyze()` will call
-`*_plot()` internally.
"""
import numpy as _numpy
from pypiezo.base import convert_bits_to_meters as _convert_bits_to_meters
from . import LOG as _LOG
-from .bump_analyze import bump_analyze as _bump_analyze
-from .bump_analyze import bump_save as _bump_save
+from .bump_analyze import analyze as _analyze
+from .bump_analyze import save as _save
-def bump_acquire(afm, bump_config):
+def acquire(afm, config):
"""Ramps `push_depth` closer and returns to the original position.
Inputs:
- afm a pyafm.AFM instance
- bump_config a .config._BumpConfig instance
+ afm a pyafm.AFM instance
+ config a .config._BumpConfig instance
Returns the acquired ramp data dictionary, with data in DAC/ADC bits.
"""
afm.move_just_onto_surface(
- depth=bump_config['initial-position'], far=bump_config['far-steps'],
- setpoint=bump_config['setpoint'],
- min_slope_ratio=bump_config['min-slope-ratio'])
+ depth=config['initial-position'], far=config['far-steps'],
+ setpoint=config['setpoint'],
+ min_slope_ratio=config['min-slope-ratio'])
#afm.piezo.jump('z', 32000)
- _LOG.info('bump the surface to a depth of %g m with a setpoint of %g V'
- % (bump_config['push-depth'], bump_config['setpoint']))
+ _LOG.info(
+ 'bump the surface to a depth of {} m with a setpoint of {} V'.format(
+ config['push-depth'], config['setpoint']))
- axis = afm.piezo.axis_by_name(afm.axis_name)
+ axis = afm.piezo.axis_by_name(afm.config['main-axis'])
- start_pos = afm.piezo.last_output[afm.axis_name]
+ start_pos = afm.piezo.last_output[afm.config['main-axis']]
start_pos_m = _convert_bits_to_meters(axis.config, start_pos)
- close_pos_m = start_pos_m + bump_config['push-depth']
+ close_pos_m = start_pos_m + config['push-depth']
close_pos = _convert_meters_to_bits(axis.config, close_pos_m)
- dtype = afm.piezo.channel_dtype(afm.axis_name, direction='output')
+ dtype = afm.piezo.channel_dtype(
+ afm.config['main-axis'], direction='output')
appr = _numpy.linspace(
- start_pos, close_pos, bump_config['samples']).astype(dtype)
+ start_pos, close_pos, config['samples']).astype(dtype)
# switch numpy.append to numpy.concatenate with version 2.0+
out = _numpy.append(appr, appr[::-1])
out = out.reshape((len(out), 1))
# (samples) / (meters) * (meters/second) = (samples/second)
- freq = (bump_config['samples'] / bump_config['push-depth']
- * bump_config['push-speed'])
+ freq = (config['samples'] / config['push-depth']
+ * config['push-speed'])
- data = afm.piezo.ramp(out, freq, output_names=[afm.axis_name],
+ data = afm.piezo.ramp(out, freq, output_names=[afm.config['main-axis']],
input_names=['deflection'])
out = out.reshape((len(out),))
data = data.reshape((data.size,))
- return {afm.axis_name: out, 'deflection': data}
+ return {afm.config['main-axis']: out, 'deflection': data}
-def bump(afm, bump_config, filename, group='/'):
- """Wrapper around bump_acquire(), bump_analyze(), bump_save().
+def run(afm, config, filename, group='/'):
+ """Wrapper around acquire(), analyze(), save().
>>> import os
>>> import tempfile
>>> from h5config.storage.hdf5 import pprint_HDF5
- >>> 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 ChannelConfig, AxisConfig
- >>> from stepper import Stepper
- >>> from pyafm.afm import AFM
+ >>> from pyafm.storage import load_afm
>>> from .config import BumpConfig
>>> 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 = AxisConfig()
- >>> axis_config.update(
- ... {'gain':20, 'sensitivity':8e-9, 'minimum':-9})
- >>> axis_channel_config = ChannelConfig()
- >>> axis_channel_config['name'] = 'z'
- >>> axis_config['channel'] = axis_channel_config
- >>> input_channel_config = ChannelConfig()
- >>> input_channel_config['name'] = 'deflection'
-
- >>> a = PiezoAxis(config=axis_config, axis_channel=axis_channel)
- >>> a.setup_config()
-
- >>> c = InputChannel(config=input_channel_config, channel=input_channel)
- >>> c.setup_config()
-
- >>> piezo = AFMPiezo(axes=[a], inputs=[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)
+ >>> devices = []
+ >>> afm = load_afm()
+ >>> afm.load_from_config(devices=devices)
Test a bump:
- >>> bump_config = BumpConfig()
- >>> bump(afm, bump_config, filename, group='/bump')
- TODO: replace skipped example data with real-world values
+ >>> config = BumpConfig()
+ >>> output = run(afm=afm, config=config, filename=filename, group='/')
+ >>> output # doctest: +SKIP
+ 23265462.3047795
>>> pprint_HDF5(filename) # doctest: +ELLIPSIS, +REPORT_UDIFF
/
- /bump
- /bump/config
- /bump/config/bump
- <HDF5 dataset "far-steps": shape (), type "<i4">
- 200
- <HDF5 dataset "initial-position": shape (), type "<f8">
- -5e-08
- <HDF5 dataset "model": shape (), type "|S9">
- quadratic
- <HDF5 dataset "push-depth": shape (), type "<f8">
- 2e-07
- <HDF5 dataset "push-speed": shape (), type "<f8">
- 1e-06
- <HDF5 dataset "samples": shape (), type "<i4">
- 1024
- <HDF5 dataset "setpoint": shape (), type "<f8">
- 2.0
- /bump/config/deflection
- /bump/config/deflection/channel
- <HDF5 dataset "channel": shape (), type "<i4">
- 0
- <HDF5 dataset "conversion-coefficients": shape (2,), type "<f8">
- [ -1.00000000e+01 3.05180438e-04]
- <HDF5 dataset "conversion-origin": shape (), type "<f8">
- 0.0
- <HDF5 dataset "device": shape (), type "|S12">
- /dev/comedi0
- <HDF5 dataset "inverse-conversion-coefficients": shape (2,), type "<f8">
- [ 0. 3276.75]
- <HDF5 dataset "inverse-conversion-origin": shape (), type "<f8">
- -10.0
- <HDF5 dataset "maxdata": shape (), type "<i8">
- 65535
- <HDF5 dataset "name": shape (), type "|S10">
- deflection
- <HDF5 dataset "range": shape (), type "<i4">
- 0
- <HDF5 dataset "subdevice": shape (), type "<i4">
- 0
- /bump/config/z
- /bump/config/z/axis
- /bump/config/z/axis/channel
- <HDF5 dataset "channel": shape (), type "<i4">
- 0
- <HDF5 dataset "conversion-coefficients": shape (2,), type "<f8">
- [ -1.00000000e+01 3.05180438e-04]
- <HDF5 dataset "conversion-origin": shape (), type "<f8">
- 0.0
- <HDF5 dataset "device": shape (), type "|S12">
- /dev/comedi0
- <HDF5 dataset "inverse-conversion-coefficients": shape (2,), type "<f8">
- [ 0. 3276.75]
- <HDF5 dataset "inverse-conversion-origin": shape (), type "<f8">
- -10.0
- <HDF5 dataset "maxdata": shape (), type "<i8">
- 65535
- <HDF5 dataset "name": shape (), type "|S1">
- z
- <HDF5 dataset "range": shape (), type "<i4">
- 0
- <HDF5 dataset "subdevice": shape (), type "<i4">
- 1
- <HDF5 dataset "gain": shape (), type "<i4">
- 20
- <HDF5 dataset "maximum": shape (), type "<f8">
- 10.0
- <HDF5 dataset "minimum": shape (), type "<i4">
- -9
- <HDF5 dataset "monitor": shape (), type "|S1">
- <BLANKLINE>
- <HDF5 dataset "sensitivity": shape (), type "<f8">
- 8e-09
- <HDF5 dataset "processed": shape (), type "<f8">
+ /config
+ /config/bump
+ <HDF5 dataset "far-steps": shape (), type "<i4">
+ 200
+ <HDF5 dataset "initial-position": shape (), type "<f8">
+ -5e-08
+ <HDF5 dataset "min-slope-ratio": shape (), type "<f8">
+ 10.0
+ <HDF5 dataset "model": shape (), type "|S9">
+ quadratic
+ <HDF5 dataset "push-depth": shape (), type "<f8">
+ 2e-07
+ <HDF5 dataset "push-speed": shape (), type "<f8">
+ 1e-06
+ <HDF5 dataset "samples": shape (), type "<i4">
+ 1024
+ <HDF5 dataset "setpoint": shape (), type "<f8">
+ 2.0
+ /processed
+ <HDF5 dataset "data": shape (), type "<f8">
...
- /bump/raw
- <HDF5 dataset "deflection": shape (2048,), type "<u2">
+ <HDF5 dataset "units": shape (), type "|S3">
+ V/m
+ /raw
+ /raw/deflection
+ <HDF5 dataset "data": shape (2048,), type "<u2">
[...]
- <HDF5 dataset "z": shape (2048,), type "<u2">
+ <HDF5 dataset "units": shape (), type "|S4">
+ bits
+ /raw/z
+ <HDF5 dataset "data": shape (2048,), type "<u2">
[...]
+ <HDF5 dataset "units": shape (), type "|S4">
+ bits
- Close the Comedi device.
+ Close the Comedi devices.
- >>> d.close()
+ >>> for device in devices:
+ ... device.close()
Cleanup our temporary config file.
>>> os.remove(filename)
"""
deflection_channel = afm.piezo.input_channel_by_name('deflection')
- axis = afm.piezo.axis_by_name(afm.axis_name)
+ axis = afm.piezo.axis_by_name(afm.config['main-axis'])
- data = bump_acquire(afm, bump_config)
- photo_sensitivity = _bump_analyze(
- data, bump_config, z_axis_config=axis.config,
+ raw = acquire(afm, config)
+ photo_sensitivity = _analyze(
+ config=config, data=raw, z_axis_config=axis.config,
deflection_channel_config=deflection_channel.config)
- _bump_save(
- filename, group, data, bump_config,
- z_axis_config=axis.config,
- deflection_channel_config=deflection_channel.config,
- processed_bump=photo_sensitivity)
+ _save(filename=filename, group=group, config=config,
+ raw=raw, processed=photo_sensitivity)
return photo_sensitivity