adxl345: Support recording data from multiple ADXL345's in one run, and more. (#5224)

Add PROBE and CHIP to TEST_RESONANCES

Since it's possible to specify more than one chip
in TEST_RESONANCES the CHIP parameter has been
renamed to CHIPS

Signed-off-by: Mikkel Schmidt <mikkel.schmidt@gmail.com>
This commit is contained in:
Mikkel Schmidt 2022-05-24 01:56:58 +02:00 committed by GitHub
parent c7e0372c5d
commit af38d708cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 24 deletions

View File

@ -896,23 +896,28 @@ all enabled accelerometer chips.
#### TEST_RESONANCES
`TEST_RESONANCES AXIS=<axis> OUTPUT=<resonances,raw_data>
[NAME=<name>] [FREQ_START=<min_freq>] [FREQ_END=<max_freq>]
[HZ_PER_SEC=<hz_per_sec>] [INPUT_SHAPING=[<0:1>]]`: Runs the resonance
[HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
[POINT=x,y,z] [INPUT_SHAPING=[<0:1>]]`: Runs the resonance
test in all configured probe points for the requested "axis" and
measures the acceleration using the accelerometer chips configured for
the respective axis. "axis" can either be X or Y, or specify an
arbitrary direction as `AXIS=dx,dy`, where dx and dy are floating
point numbers defining a direction vector (e.g. `AXIS=X`, `AXIS=Y`, or
`AXIS=1,-1` to define a diagonal direction). Note that `AXIS=dx,dy`
and `AXIS=-dx,-dy` is equivalent. If `INPUT_SHAPING=0` or not set
(default), disables input shaping for the resonance testing, because
and `AXIS=-dx,-dy` is equivalent. `adxl345_chip_name` can be one or
more configured adxl345 chip,delimited with comma, for example
`CHIPS="adxl345, adxl345 rpi"`. Note that `adxl345` can be omitted from
named adxl345 chips. If POINT is specified it will override the point(s)
configured in `[resonance_tester]`. If `INPUT_SHAPING=0` or not set(default),
disables input shaping for the resonance testing, because
it is not valid to run the resonance testing with the input shaper
enabled. `OUTPUT` parameter is a comma-separated list of which outputs
will be written. If `raw_data` is requested, then the raw
accelerometer data is written into a file or a series of files
`/tmp/raw_data_<axis>_[<point>_]<name>.csv` with (`<point>_` part of
the name generated only if more than 1 probe point is configured). If
`resonances` is specified, the frequency response is calculated
(across all probe points) and written into
`/tmp/raw_data_<axis>_[<chip_name>_][<point>_]<name>.csv` with
(`<point>_` part of the name generated only if more than 1 probe point
is configured or POINT is specified). If `resonances` is specified, the
frequency response is calculated (across all probe points) and written into
`/tmp/resonances_<axis>_<name>.csv` file. If unset, OUTPUT defaults to
`resonances`, and NAME defaults to the current time in
"YYYYMMDD_HHMMSS" format.

View File

@ -147,15 +147,21 @@ class ResonanceTester:
(chip_axis, self.printer.lookup_object(chip_name))
for chip_axis, chip_name in self.accel_chip_names]
def _run_test(self, gcmd, axes, helper, raw_name_suffix=None):
def _run_test(self, gcmd, axes, helper, raw_name_suffix=None,
accel_chips=None, test_point=None):
toolhead = self.printer.lookup_object('toolhead')
calibration_data = {axis: None for axis in axes}
self.test.prepare_test(gcmd)
test_points = self.test.get_start_test_points()
if test_point is not None:
test_points = [test_point]
else:
test_points = self.test.get_start_test_points()
for point in test_points:
toolhead.manual_move(point, self.move_speed)
if len(test_points) > 1:
if len(test_points) > 1 or test_point is not None:
gcmd.respond_info(
"Probing point (%.3f, %.3f, %.3f)" % tuple(point))
for axis in axes:
@ -165,29 +171,36 @@ class ResonanceTester:
gcmd.respond_info("Testing axis %s" % axis.get_name())
raw_values = []
for chip_axis, chip in self.accel_chips:
if axis.matches(chip_axis):
if accel_chips is None:
for chip_axis, chip in self.accel_chips:
if axis.matches(chip_axis):
aclient = chip.start_internal_client()
raw_values.append((chip_axis, aclient, chip.name))
else:
for chip in accel_chips:
aclient = chip.start_internal_client()
raw_values.append((chip_axis, aclient))
raw_values.append((axis, aclient, chip.name))
# Generate moves
self.test.run_test(axis, gcmd)
for chip_axis, aclient in raw_values:
for chip_axis, aclient, chip_name in raw_values:
aclient.finish_measurements()
if raw_name_suffix is not None:
raw_name = self.get_filename(
'raw_data', raw_name_suffix, axis,
point if len(test_points) > 1 else None)
point if len(test_points) > 1 else None,
chip_name if accel_chips is not None else None,)
aclient.write_to_file(raw_name)
gcmd.respond_info(
"Writing raw accelerometer data to "
"%s file" % (raw_name,))
if helper is None:
continue
for chip_axis, aclient in raw_values:
for chip_axis, aclient, chip_name in raw_values:
if not aclient.has_valid_samples():
raise gcmd.error(
"%s-axis accelerometer measured no data" % (
chip_axis,))
"accelerometer '%s' measured no data" % (
chip_name,))
new_data = helper.process_accelerometer_data(aclient)
if calibration_data[axis] is None:
calibration_data[axis] = new_data
@ -198,6 +211,28 @@ class ResonanceTester:
def cmd_TEST_RESONANCES(self, gcmd):
# Parse parameters
axis = _parse_axis(gcmd, gcmd.get("AXIS").lower())
accel_chips = gcmd.get("CHIPS", None)
test_point = gcmd.get("POINT", None)
if test_point:
test_coords = test_point.split(',')
if len(test_coords) != 3:
raise gcmd.error("Invalid POINT parameter, must be 'x,y,z'")
try:
test_point = [float(p.strip()) for p in test_coords]
except ValueError:
raise gcmd.error("Invalid POINT parameter, must be 'x,y,z'"
" where x, y and z are valid floating point numbers")
if accel_chips:
parsed_chips = []
for chip_name in accel_chips.split(','):
if "adxl345" in chip_name:
chip_lookup_name = chip_name.strip()
else:
chip_lookup_name = "adxl345 " + chip_name.strip();
chip = self.printer.lookup_object(chip_lookup_name)
parsed_chips.append(chip)
outputs = gcmd.get("OUTPUT", "resonances").lower().split(',')
for output in outputs:
@ -221,10 +256,13 @@ class ResonanceTester:
data = self._run_test(
gcmd, [axis], helper,
raw_name_suffix=name_suffix if raw_output else None)[axis]
raw_name_suffix=name_suffix if raw_output else None,
accel_chips=parsed_chips if accel_chips else None,
test_point=test_point)[axis]
if csv_output:
csv_name = self.save_calibration_data('resonances', name_suffix,
helper, axis, data)
helper, axis, data,
point=test_point)
gcmd.respond_info(
"Resonances data written to %s file" % (csv_name,))
cmd_SHAPER_CALIBRATE_help = (
@ -287,7 +325,8 @@ class ResonanceTester:
for chip_axis, aclient in raw_values:
if not aclient.has_valid_samples():
raise gcmd.error(
"%s-axis accelerometer measured no data" % (chip_axis,))
"%s-axis accelerometer measured no data" % (
chip_axis,))
data = helper.process_accelerometer_data(aclient)
vx = data.psd_x.mean()
vy = data.psd_y.mean()
@ -299,18 +338,22 @@ class ResonanceTester:
def is_valid_name_suffix(self, name_suffix):
return name_suffix.replace('-', '').replace('_', '').isalnum()
def get_filename(self, base, name_suffix, axis=None, point=None):
def get_filename(self, base, name_suffix, axis=None,
point=None, chip_name=None):
name = base
if axis:
name += '_' + axis.get_name()
if chip_name:
name += '_' + chip_name.replace(" ", "_")
if point:
name += "_%.3f_%.3f_%.3f" % (point[0], point[1], point[2])
name += '_' + name_suffix
return os.path.join("/tmp", name + ".csv")
def save_calibration_data(self, base_name, name_suffix, shaper_calibrate,
axis, calibration_data, all_shapers=None):
output = self.get_filename(base_name, name_suffix, axis)
axis, calibration_data,
all_shapers=None, point=None):
output = self.get_filename(base_name, name_suffix, axis, point)
shaper_calibrate.save_calibration_data(output, calibration_data,
all_shapers)
return output