"""Define some variables to configure the package for a particular lab
and workflow."""
-import piezo.z_piezo as z_piezo
-
-
-DEFAULT_TEMP = 22 # assume 22 deg C
-LOG_DATA = True # quietly grab all real-world data and log to LOG_DIR
-LOG_DIR = '${DEFAULT}/calibrate_cantilever'
-GNUFIT_DATA_BASE='./calibrate_cantilever_fitdata'
-TEXT_VERBOSE = True # for debugging
-GNUPLOT_VERBOSE = True # turn on fit check plotting
-PYLAB_VERBOSE = True # turn on plotting
-PYLAB_INTERACTIVE = True # select between draw() and show() for flushing plots
-BASE_FIGNUM = 20 # to avoid writing to already existing figures
-
-
-# HACK
-# make sure you make a system note (add_system_note) if you change
-# these in case you don't have access to a z_piezo for conversion
-# functions
-
-# zpGain zpiezo applied voltage per output Volt
-zpGain = z_piezo.DEFAULT_GAIN
-# zpSensitivity nm zpiezo response per applied Volt
-zpSensitivity = z_piezo.DEFAULT_SENSITIVITY
-# Vzp_out2V function that converts output DAC bits to Volts
-Vzp_out2V = z_piezo.DEFAULT_VZP_OUT_2_VOLTS
-# Vphoto_in2V function that converts input ADC bits to Volts
-Vphoto_in2V = z_piezo.DEFAULT_VPHOTO_IN_2_VOLTS
-# zeroVphoto_bits ADC bit output for a 0V input
-zeroVphoto_bits = z_piezo.DEFAULT_ZERO_PHOTODIODE_BITS
+import logging as _logging
+import os.path as _os_path
+import sys as _sys
+
+from FFT_tools import window_hann as _window_hann
+from pypiezo.config import (
+ _ChoiceSetting, _BooleanSetting, _IntegerSetting, _FloatSetting,
+ _FloatListSetting, _Config, _BackedConfig, _HDF5Config, _YAMLConfig)
+
+from . import LOG as _LOG
+
+
+class _BaseConfig (_Config):
+ "Configure `calibcant` module operation"
+ settings = [
+ _ChoiceSetting(
+ name='log-level',
+ help='Module logging level.',
+ default=_logging.WARN,
+ choices=[
+ ('critical', _logging.CRITICAL),
+ ('error', _logging.ERROR),
+ ('warn', _logging.WARN),
+ ('info', _logging.INFO),
+ ('debug', _logging.DEBUG),
+ ]),
+ _BooleanSetting(
+ name='syslog',
+ help='Log to syslog (otherwise log to stderr).',
+ default=False),
+ _BooleanSetting(
+ name='matplotlib',
+ help='Plot piezo motion using `matplotlib`.',
+ default=False),
+ _FloatSetting(
+ name='temperature',
+ help=('Default temperature for thermal calibration in degrees '
+ 'Celsius.'),
+ default=22),
+ ]
+
+class _TemperatureUnit (object):
+ pass
+class Celsius (_TemperatureUnit):
+ pass
+class Kelvin (_TemperatureUnit):
+ pass
+
+class _TemperatureConfig (_Config):
+ "Configure `calibcant` temperature operation"
+ settings = [
+ _ChoiceSetting(
+ name='units',
+ help='Units of raw temperature measurements.',
+ default=Celsius,
+ choices=[
+ ('Celsius', Celsius),
+ ('Kelvin', Kelvin),
+ ]),
+ _BooleanSetting(
+ name='default',
+ help=('The temperature values are defaults (vs. real '
+ 'measurements).'),
+ default=True),
+ ]
+
+class _BumpModel (object):
+ pass
+class Linear (_BumpModel):
+ pass
+class Quadratic (_BumpModel):
+ pass
+
+class _BumpConfig (_Config):
+ "Configure `calibcant` bump operation"
+ settings = [
+ _FloatSetting(
+ name='initial-position',
+ help=('Position relative to surface for start of bump in meters. '
+ 'Should be less than zero to ensure non-contact region '
+ 'before you hit the surface.'),
+ default=-50e-9),
+ _FloatSetting(
+ name='setpoint',
+ help=('Maximum deflection in volts in case of stepper positioning '
+ 'to achieve the initial position.'),
+ default=2.0),
+ _IntegerSetting(
+ name='far-steps',
+ help=('Number of stepper steps to move "far" away from the '
+ 'surface. For possible stepper adjustments while initially '
+ 'locating the surface.'),
+ default=200),
+ _FloatSetting(
+ name='push-depth',
+ help='Distance to approach in meters.',
+ default=200e-9),
+ _FloatSetting(
+ name='push-speed',
+ help='Approach/retract speed in meters/second.',
+ default=1e-6),
+ _FloatSetting(
+ name='samples',
+ help='Number of samples during approach and during retreat.',
+ default=1024),
+ _ChoiceSetting(
+ name='model',
+ help='Bump deflection model.',
+ default=Quadratic,
+ choices=[
+ ('linear', Linear),
+ ('quadratic', Quadratic),
+ ]),
+ ]
+
+class _VibrationModel (object):
+ pass
+class Variance (_VibrationModel):
+ pass
+class BreitWigner (_VibrationModel):
+ pass
+class OffsetBreitWigner (_VibrationModel):
+ pass
+
+class _VibrationConfig (_Config):
+ "Configure `calibcant` vibration operation"
+ settings = [
+ _FloatSetting(
+ name='frequency',
+ help='Sampling frequency in Hz.',
+ default=50e3),
+ _FloatSetting(
+ name='sample-time',
+ help=('Aquisition time in seconds. This is rounded up as required '
+ 'so the number of samples will be an integer power of two.'),
+ default=1),
+ _ChoiceSetting(
+ name='model',
+ help='Vibration model.',
+ default=BreitWigner,
+ choices=[
+ ('variance', Variance),
+ ('Breit-Wigner', BreitWigner),
+ ('offset Breit-Wigner', OffsetBreitWigner),
+ ]),
+ _IntegerSetting(
+ name='chunk-size',
+ help='FFT chunk size (for PSD fits).',
+ default=2048),
+ _BooleanSetting(
+ name='overlap',
+ help='Overlap FFT chunks (for PSD fits).'),
+ _ChoiceSetting(
+ name='window',
+ help='FFT chunk window (for PSD fits).',
+ default=_window_hann,
+ choices=[
+ ('Hann', _window_hann),
+ ]),
+ _FloatSetting(
+ name='minimum-fit-frequency',
+ help='Lower bound of Lorentzian fitting region.',
+ default=500.),
+ _FloatSetting(
+ name='maximum-fit-frequency',
+ help='Upper bound of Lorentzian fitting region.',
+ default=25e3),
+ ]
+
+
+class _CalibrationConfig (_Config):
+ "Configure a full `calibcant` calibration run"
+ settings = [
+ _IntegerSetting(
+ name='num-bumps',
+ help='Number of surface bumps.',
+ default=10),
+ _IntegerSetting(
+ name='num-temperatures',
+ help='Number of temperature measurements.',
+ default=10),
+ _IntegerSetting(
+ name='num-vibrations',
+ help='Number of thermal vibration measurements.',
+ default=20),
+ _FloatSetting(
+ name='temperature-sleep',
+ help=('Time between temperature measurements (in seconds) to get '
+ 'independent measurements when reading from slow sensors.'),
+ default=1),
+ _FloatSetting(
+ name='vibration-spacing',
+ help=('Approximate distance from the surface in meters for the '
+ 'vibration measurements. This should be large enough that '
+ 'surface effects are negligable.'),
+ default=50e-6),
+ ]
+
+
+# Define HDF5- and YAML-backed subclasses of the basic _Config types.
+for name,obj in locals().items():
+ if (obj != _Config and
+ type(obj) == type and
+ issubclass(obj, _Config) and
+ not issubclass(obj, _BackedConfig)):
+ for prefix,base in [('HDF5', _HDF5Config), ('YAML', _YAMLConfig)]:
+ _name = '%s%s' % (prefix, name)
+ _bases = (base, obj)
+ _dict = {}
+ _class = type(_name, _bases, _dict)
+ setattr(_sys.modules[__name__], _name, _class)
+
+del name, obj, prefix, base, _name, _bases, _dict, _class
+
+
+def find_base_config():
+ "Return the best `_BaseConfig` match after scanning the filesystem"
+ _LOG.info('looking for base_config file')
+ user_basepath = _os_path.join(_os_path.expanduser('~'), '.calibcantrc')
+ system_basepath = _os_path.join('/etc', 'calibcant', 'config')
+ distributed_basepath = _os_path.join(
+ '/usr', 'share', 'calibcant', 'config')
+ for basepath in [user_basepath, system_basepath, distributed_basepath]:
+ for (extension, config) in [('.h5', HDF5_BaseConfig),
+ ('.yaml', YAML_BaseConfig)]:
+ filename = basepath + extension
+ if _os_path.exists(filename):
+ _LOG.info('base_config file found at %s' % filename)
+ base_config = config(filename)
+ base_config.load()
+ return base_config
+ else:
+ _LOG.debug('no base_config file at %s' % filename)
+ _LOG.info('new base_config file at %s' % filename)
+ basepath = user_basepath
+ filename = basepath + extension
+ return config(filename)