With the current implementation, the data is regressed in DAC/ADC bits
and THEN converted, so we're assuming that both conversions are LINEAR.
- if they aren't, rewrite to convert before the regression.
+ If they aren't, rewrite to convert before the regression.
"""
bump_fit_kwargs,slope_bitspbit2Vpnm_kwargs = \
bump_analyze._splitargs(bump_analyze, kwargs)
**bump_fit_kwargs)
return slope_bitspbit2Vpnm(Vphoto2Vzp_out_bit, **slope_bitspbit2Vpnm_kwargs)
-def limited_quadratic(x, params):
+def limited_linear(x, params):
"""
Model the bump as:
flat region (off-surface)
- quadratic region (in-contact)
+ linear region (in-contact)
flat region (high-voltage-rail)
Parameters:
x_contact (x value for the surface-contact kink)
y_contact (y value for the surface-contact kink)
slope (dy/dx at the surface-contact kink)
- quad (d**2 y / dx**2, allow decreasing sensitivity with increased x)
"""
high_voltage_rail = 2**16 - 1 # bits
- x_contact,y_contact,slope,quad = params
- y = slope*(x-x_contact) + quad*(x-x_contact)**2+ y_contact
+ x_contact,y_contact,slope = params
+ y = slope*(x-x_contact) + y_contact
y = numpy.clip(y, y_contact, high_voltage_rail)
return y
-def limited_quadratic_param_guess(x, y) :
+def limited_linear_param_guess(x, y) :
"""
- Guess rough parameters for a limited_quadratic model. Assumes the
+ Guess rough parameters for a limited_linear model. Assumes the
bump approaches (raising the deflection as it does so) first.
Retracting after the approach is optional. Approximates the contact
position and an on-surface (high) position by finding first crossings
x_contact = float(x[i_low])
x_high = float(x[i_high])
slope = (y_high - y_contact) / (x_high - x_contact)
+ return (x_contact, y_contact, slope)
+
+def limited_linear_sensitivity(params):
+ """
+ Return the estimated sensitivity to small deflections according to
+ limited_linear fit parameters.
+ """
+ slope = params[2]
+ return slope
+
+def limited_quadratic(x, params):
+ """
+ Model the bump as:
+ flat region (off-surface)
+ quadratic region (in-contact)
+ flat region (high-voltage-rail)
+ Parameters:
+ x_contact (x value for the surface-contact kink)
+ y_contact (y value for the surface-contact kink)
+ slope (dy/dx at the surface-contact kink)
+ quad (d**2 y / dx**2, allow decreasing sensitivity with increased x)
+ """
+ high_voltage_rail = 2**16 - 1 # bits
+ x_contact,y_contact,slope,quad = params
+ y = slope*(x-x_contact) + quad*(x-x_contact)**2+ y_contact
+ y = numpy.clip(y, y_contact, high_voltage_rail)
+ return y
+
+def limited_quadratic_param_guess(x, y) :
+ """
+ Guess rough parameters for a limited_quadratic model. Assumes the
+ bump approaches (raising the deflection as it does so) first.
+ Retracting after the approach is optional. Approximates the contact
+ position and an on-surface (high) position by finding first crossings
+ of thresholds 0.3 and 0.7 of the y value's total range. Not the
+ most efficient algorithm, but it seems fairly robust.
+ """
+ x_contact,y_contact,slope = limited_linear_param_guess(x,y)
quad = 0
return (x_contact, y_contact, slope, quad)
@splittableKwargsFunction()
def bump_fit(zpiezo_output_bits, deflection_input_bits,
- paramGuesser=limited_quadratic_param_guess,
+ param_guesser=limited_quadratic_param_guess,
model=limited_quadratic,
sensitivity_from_fit_params=limited_quadratic_sensitivity,
plotVerbose=False) :
y = deflection_input_bits
def residual(p, y, x) :
return model(x, p) - y
- paramGuess = paramGuesser(x, y)
+ param_guess = param_guesser(x, y)
p,cov,info,mesg,ier = \
- scipy.optimize.leastsq(residual, paramGuess, args=(y, x),
+ scipy.optimize.leastsq(residual, param_guess, args=(y, x),
full_output=True, maxfev=int(10e3))
if config.TEXT_VERBOSE :
print "Fitted params:",p
else :
print "Solution did not converge"
if plotVerbose or config.PYLAB_VERBOSE :
- yguess = model(x, paramGuess)
+ yguess = model(x, param_guess)
#yguess = None # Don't print the guess, since I'm convinced it's ok ;).
yfit = model(x, p)
bump_plot(data={"Z piezo output":x, "Deflection input":y},
'and one to analyze tweak files.\n'
'\n'
'Single file mode (the default) :\n'
- 'Scales raw DAC/ADC bit data and fits a straight line.\n'
- 'Returns photodiode sensitivity Vphotodiode/Zcantilever in V/nm.\n'
+ 'Scales raw DAC/ADC bit data and fits a bounded quadratic.\n'
+ 'Returns photodiode sensitivity Vphotodiode/Zcantilever in V/nm, determined by.\n'
+ 'the slope at the kink between the non-contact region and the contact region.\n'
'<input-file> should be whitespace-delimited, 2 column ASCII\n'
'without a header line. e.g: "<zp_DAC>\\t<deflection_ADC>\\n"\n'
'\n'
parser.add_option('-d', '--datalogger-mode', dest='datalogger_mode', action='store_true',
help='Run input files with datalogger.read_dict_of_arrays(). This is useful, for example, to test a single line from a tweakfile.',
default=False)
+ parser.add_option('-q', '--disable-quadratic', dest='quadratic', action='store_false',
+ help='Disable quadratic term in fitting (i.e. use bounded linear fits).',
+ default=True)
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
help='Print lots of debugging information',
default=False)
config.PYLAB_INTERACTIVE = False
config.PYLAB_VERBOSE = options.pylab
config.GNUPLOT_VERBOSE = False
+ if options.quadratic == True:
+ param_guesser = limited_quadratic_param_guess
+ model = limited_quadratic
+ sensitivity_from_fit_params = limited_quadratic_sensitivity
+ else:
+ param_guesser = limited_linear_param_guess
+ model = limited_linear
+ sensitivity_from_fit_params = limited_linear_sensitivity
if options.tweakmode == False :
if options.datalogger_mode:
data = bump_load(ifilename)
else:
data = read_data(ifilename)
- photoSensitivity = bump_analyze(data)
+ photoSensitivity = bump_analyze(data,
+ param_guesser=param_guesser,
+ model=model,
+ sensitivity_from_fit_params=sensitivity_from_fit_params)
print >> ofile, photoSensitivity
else : # tweak file mode
- slopes = bump_load_analyze_tweaked(ifilename)
+ slopes = bump_load_analyze_tweaked(ifilename,
+ param_guesser=param_guesser,
+ model=model,
+ sensitivity_from_fit_params=sensitivity_from_fit_params)
if options.comma_out :
sep = ','
else :