# <http://www.gnu.org/licenses/>.
"""Driver for JPK ForceRobot's velocity clamp data format.
+
+This driver is based on JPK's :file:`JPKForceSpec.txt` version 0.12.
+The specs are freely available from JPK, just email support@jpk.com.
"""
import os.path
import numpy
from .. import curve as curve
-from .. import experiment as experiment
from ..util.util import Closing as Closing
from ..util.si import join_data_label, split_data_label
from . import Driver as Driver
for i in range(len([p for p in f.namelist()
if p.endswith('segment-header.properties')])):
segments.append(self._zip_segment(f, path, info, zip_info, i))
- if zip_info['file-format-version'] not in ['0.5']:
+ if zip_info['file-format-version'] not in ['0.%d' % i
+ for i in range(12)]:
raise NotImplementedError(
'JPK file version %s not supported (yet).'
% zip_info['file-format-version'])
- for name in ['approach', 'retract']:
- if len([s for s in segments if s.info['name'] == name]) == 0:
- raise ValueError(
- 'No segment for %s in %s, only %s'
- % (name, path, [s.info['name'] for s in segments]))
curve_info = self._zip_translate_params(zip_info,
segments[0].info['raw info'])
- for segment in segments:
+ for segment in segments: # HACK, should use curve-level spring constant
segment.info['spring constant (N/m)'] = \
curve_info['spring constant (N/m)']
return (segments, curve_info)
prop_file.close()
expected_shape = (int(prop['force-segment-header']['num-points']),)
channels = []
+ if 'list' not in prop['channels']:
+ prop['channels'] = {'list': prop['channels'].split()}
for chan in prop['channels']['list']:
chan_info = prop['channel'][chan]
- channels.append(self._zip_channel(zipfile, index, chan, chan_info))
+ channels.append(self._zip_channel(
+ zipfile, index, chan, chan_info))
if channels[-1].shape != expected_shape:
- raise NotImplementedError(
- 'Channel %d:%s in %s has strange shape %s != %s'
- % (index, chan, zipfile.path,
- channels[-1].shape, expected_shape))
+ raise NotImplementedError(
+ 'Channel %d:%s in %s has strange shape %s != %s'
+ % (index, chan, zipfile.path,
+ channels[-1].shape, expected_shape))
+ if len(channels) > 0:
+ shape = (len(channels[0]), len(channels))
+ dtype = channels[0].dtype
+ else: # no channels for this data block
+ shape = (0,0)
+ dtype = numpy.float32
d = curve.Data(
- shape=(len(channels[0]), len(channels)),
- dtype=channels[0].dtype,
+ shape=shape,
+ dtype=dtype,
info=self._zip_translate_segment_params(prop))
for i,chan in enumerate(channels):
d[:,i] = chan
def _zip_translate_params(self, params, chan_info):
info = {
'raw info':params,
- 'filetype':self.name,
#'time':self._time_from_TODO(raw_info[]),
}
- # TODO: distinguish between force clamp and velocity clamp
- # experiments. Note that the JPK file format is flexible
- # enough to support mixed experiments (i.e. both force clamp
- # and velocity clamp segments in a single experiment), but I
- # have no idea what sort of analysis such experiments would
- # require ;).
- info['experiment'] = experiment.VelocityClamp()
force_unit = chan_info['channel']['vDeflection']['conversion-set']['conversion']['force']['scaling']['unit']['unit']
assert force_unit == 'N', force_unit
force_base = chan_info['channel']['vDeflection']['conversion-set']['conversion']['force']['base-calibration-slot']
assert force_base == 'distance', force_base
dist_unit = chan_info['channel']['vDeflection']['conversion-set']['conversion']['distance']['scaling']['unit']['unit']
assert dist_unit == 'm', dist_unit
+ distance_base = chan_info['channel']['vDeflection']['conversion-set']['conversion']['distance']['base-calibration-slot']
+ assert distance_base == 'volts', distance_base
+ # Assume volts unit is V, but it is not specified in the JPK
+ # file format.
force_mult = float(
chan_info['channel']['vDeflection']['conversion-set']['conversion']['force']['scaling']['multiplier'])
+ sens_mult = float(
+ chan_info['channel']['vDeflection']['conversion-set']['conversion']['distance']['scaling']['multiplier'])
info['spring constant (N/m)'] = force_mult
+ info['z piezo sensitivity (m/V)'] = sens_mult
return info
def _zip_translate_segment_params(self, params):
return info
def _read_old(self, path, info):
- raise NotImplementedError('No old-style JPK files were available for testing, please send us yours: %s' % path)
+ raise NotImplementedError(
+ "Early JPK files (pre-zip) are not supported by Hooke. Please "
+ "use JPK's `out2jpk-force` script to convert your old files "
+ "to a more recent format before loading them with Hooke.")