|
rcbench
Servo and ESC test bench: panel, IOmcu and shared C
|
The automatic servo test. See servo_test.h for the method. More...
#include "servo_test.h"#include <math.h>#include <stdio.h>#include <stdlib.h>#include <string.h>Data Structures | |
| struct | sample_t |
| One reading of the run's meter: a reading of the supply, or a window. More... | |
Functions | |
| static void | mean_add (servo_test_mean_t *m, float v) |
| Add a reading to a mean. | |
| static float | mean_of (const servo_test_mean_t *m) |
| Mean of the readings added so far. | |
| static float | enc_dir_deg (const servo_test_t *t, uint16_t raw) |
| The angle in the commanded direction: with REVERSE on, the horn turns the other way for the same command, and the angle is negated. | |
| float | servo_test_enc_deg (uint16_t raw, uint16_t centre) |
raw from centre on the circle, degrees, -180 to just under 180: 360 / 4096 a count. The difference of any two counts the shortest way round. | |
| float | servo_test_enc_wrap (float deg) |
deg on the circle: -180 to just under 180. | |
| bool | servo_test_enc_end (const servo_test_t *t, const servo_test_step_t *s, unsigned end, float *deg, float *err) |
The mean settled end angle of step s at end (0 low, 1 high) into deg, degrees from cfg.enc_centre in the commanded direction, -180 to just under 180, and its error against cfg.enc_cmd_deg[end] into err, the same range; either may be NULL. | |
| void | servo_test_meter_pdmini (servo_test_meter_t *m) |
| Describe the PD mini as the run meter. | |
| void | servo_test_meter_model (servo_test_meter_t *m) |
| The panel's model of the supply: no lag of its own and no repeats, and its travel times held to what a run on the PD mini can check. | |
| void | servo_test_meter_ina3221 (servo_test_meter_t *m, uint16_t shunt_dmohm) |
An INA3221 channel on a shunt of shunt_dmohm, in 0.1mOhm (1000 for 0.1Ohm, which ends the range at 1.638A), named "INA3221" until the caller names the channel, read in 50ms windows: a change of current shows in the window that closes after it, up to 50ms later, and the panel times a move from the windows, so travel times are an upper bound. | |
| static long | whole_ma (float a) |
| A current in whole mA, held to a range every reading lies in. | |
| bool | servo_test_over_a (float a, float limit_a) |
Whether a is above limit_a: both in whole mA, a by its magnitude. The one comparison the verdict and the report make of a current against IDLE CURRENT, HOLD CURRENT and STALL AT. | |
| bool | servo_test_stall_unreachable (const servo_test_cfg_t *cfg, bool *by_limit, bool *by_range) |
Whether STALL AT cannot be reached in a run of cfg: at or above the current limit, where the supply holds the current (by_limit), or at or above the end of the meter's range (by_range). | |
| static bool | on_windows (const servo_test_t *t) |
| Tell whether the run reads the INA3221 windows. | |
| static void | set_threshold (servo_test_step_t *s) |
| The step's threshold, once its idle readings are in: the larger of the smallest and SERVO_TEST_NOISE_K times their standard deviation. | |
| void | servo_test_init (servo_test_t *t) |
| Clear a run state to idle. | |
| bool | servo_test_running (const servo_test_t *t) |
| Tell whether a run is under way. | |
| uint32_t | servo_test_travel_window_ms (const servo_test_t *t) |
How long a move of t may take before it is late, or unseen: SERVO_TEST_TRAVEL_TIMEOUT_MS plus the meter's lag, 3300ms on the PD mini. | |
| static void | put_line (servo_test_t *t, servo_test_out_t kind, const char *text) |
| Queue a line for the card; a CSV row is counted as lost when the outbox is full. | |
| servo_test_out_t | servo_test_peek (servo_test_t *t, const char **text) |
The next item for the card, without taking it: OPEN, the CSV's lines, then once the run is over the report's (if REPORT) and END. text is the line, valid until the next call. NONE while nothing waits. | |
| void | servo_test_pop (servo_test_t *t) |
| The item servo_test_peek() gave has been taken. | |
| bool | servo_test_drained (const servo_test_t *t) |
| Whether everything of the last run has been handed over. | |
| static servo_test_step_t * | cur (servo_test_t *t) |
| The step running. | |
| static bool | enc_raw_at (const servo_test_t *t, uint32_t at_ms, uint16_t *out) |
The count at at_ms, by servo_test_enc_at()'s rule. | |
| static void | enc_close (servo_test_t *t) |
| The move the encoder was judging is over: counted into its step. | |
| static void | enc_open (servo_test_t *t, uint8_t end, bool counted, uint32_t now_ms) |
A move to end is commanded at now_ms: the encoder judges it from the angle read before. | |
| static void | finish (servo_test_t *t, servo_test_abort_t why, uint32_t now_ms) |
| The run ends: the output off and the servo let go, whatever ended it. | |
| void | servo_test_abort (servo_test_t *t, servo_test_abort_t why, uint32_t now_ms) |
End a running run for why: the output is asked off and the servo let go at the next servo_test_step(). | |
| void | servo_test_meter_now (servo_test_t *t, const servo_test_meter_now_t *m, uint32_t now_ms) |
The meter as a poll decided it, for every answer the caller has: with each window, each supply sample and each frame. A run on the INA3221 holds the change count of its start (cfg.meter_changes). It ends, as servo_test_abort() ends a run, at the first answer that is not the INA3221 or carries another count: the meter changed under it, whatever it is now. The reason is m->dropped when that drop came after the start, and SERVO_TEST_AB_INA_METER otherwise. Nothing for a run on another meter, which goes on with the meter it started with. | |
| static void | command (servo_test_t *t, uint16_t us, uint32_t now_ms) |
| Command a pulse width. | |
| static void | begin_step (servo_test_t *t, uint8_t idx, uint32_t now_ms) |
The voltage step idx begins: its set point, the servo at its centre, and on the first step the output on. | |
| static void | begin_move (servo_test_t *t, uint8_t end, bool counted, uint32_t now_ms) |
A move to end end: counted or not, measured against that end's holding level, or the idle current while it is not known. | |
| static void | begin_hold (servo_test_t *t, uint32_t from_ms) |
| Begin holding at an end. | |
| static void | end_move (servo_test_t *t, bool arrived, uint32_t at_ms) |
The move is over: arrived at at_ms, or timed out. | |
| static uint32_t | hold_ms (const servo_test_t *t) |
| Length of the hold at an end, ms. | |
| static void | begin_brownout (servo_test_t *t, float v, uint32_t now_ms) |
Append the brown-out step at v and begin it. | |
| static void | end_step (servo_test_t *t, uint32_t now_ms) |
| End the step: begin the next one, or finish the run. | |
| static void | end_hold (servo_test_t *t, uint32_t now_ms) |
| The hold at an end is over: its level, and the next move or the step's end. | |
| servo_test_start_t | servo_test_start (servo_test_t *t, const servo_test_cfg_t *cfg, uint32_t now_ms, const servo_test_reading_t *last, float v_min, float v_max) |
Start a run at now_ms with the supply as last read it and its set points' range v_min to v_max. | |
| static void | log_row (servo_test_t *t, const sample_t *m, uint8_t mode, uint16_t position_us, const char *tail) |
One row of the CSV, for a reading: the columns every run has, the encoder's two where it is on, and the meter's six in tail. | |
| static void | measure (servo_test_t *t, const sample_t *m) |
| A new reading of the meter, measured by the phase it falls in. | |
| static void | count_step (servo_test_t *t, uint16_t was, uint16_t now) |
A reading count that moved from was to now: what the meter took since, and what of it never reached the run. | |
| void | servo_test_reading (servo_test_t *t, const servo_test_reading_t *r, uint16_t position_us) |
A sample of the supply, with position_us the horn's measured pulse width or 0 for none. | |
| void | servo_test_window (servo_test_t *t, const servo_test_win_t *w, uint16_t position_us) |
A window of INA3221 CH1, each number once and in order, with position_us as servo_test_reading() takes it. | |
| bool | servo_test_enc_at (const servo_test_t *t, uint32_t at_ms, float *deg) |
The angle at at_ms, degrees from cfg.enc_centre: the newest kept reading taken at or before at_ms (a signed difference, so a reading taken later than the row does not count), when it is no older than SERVO_TEST_ENC_STALE_MS. False when there is none. | |
| void | servo_test_encoder (servo_test_t *t, const servo_test_enc_t *e) |
| A reading of the output encoder, each one once. Judged against the move under way; kept as the angle the CSV and the SERVO screen show. | |
| void | servo_test_step (servo_test_t *t, uint32_t now_ms, const servo_test_in_t *in, servo_test_do_t *out) |
One pass at now_ms: the timers, and what the run wants done since the last pass into out. | |
| servo_test_verdict_t | servo_test_verdict (const servo_test_t *t) |
| Verdict of a run. | |
| unsigned | servo_test_steps_planned (const servo_test_t *t) |
| Steps planned, the brown-out counted as one, and the one running, 1-based, for "step 2 of 3". | |
| unsigned | servo_test_step_now (const servo_test_t *t) |
| Number of the step running. | |
| bool | servo_test_max_idle (const servo_test_t *t, float *a) |
| The highest, the longest and the lowest of a run, over its characterisation steps; false when none was measured. A current is the one of the largest magnitude, with its sign. | |
| bool | servo_test_max_hold (const servo_test_t *t, float *a) |
| Highest holding current of a run. | |
| bool | servo_test_max_travel (const servo_test_t *t, uint32_t *ms) |
| Longest travel time of a run. | |
| bool | servo_test_brownout (const servo_test_t *t, float *moved_v, bool *stopped) |
| The lowest brown-out voltage the servo moved at, and whether one below it showed none. | |
| bool | servo_test_negative_at_rest (const servo_test_t *t) |
| Whether a step's idle current reads below -SERVO_TEST_NEG_IDLE_A: the shunt is fitted the other way round. | |
| bool | servo_test_rates (const servo_test_t *t, float *per_s, float *module_per_s, uint32_t *interval_ms) |
| Readings that reached the run per second, the supply's own per second, and the mean interval between two readings; false before two. | |
The automatic servo test. See servo_test.h for the method.
SPDX-License-Identifier: MIT
|
static |
Append the brown-out step at v and begin it.
| t | Run state. |
| v | Set voltage of the step, V. |
| now_ms | Time of the start, ms. |
|
static |
Begin holding at an end.
| t | Run state. |
| from_ms | Start of the hold, ms. |
|
static |
A move to end end: counted or not, measured against that end's holding level, or the idle current while it is not known.
| t | Run state. |
| end | 0 for the low end, 1 for the high end. |
| counted | true when the move counts. |
| now_ms | Time of the command, ms. |
|
static |
The voltage step idx begins: its set point, the servo at its centre, and on the first step the output on.
| t | Run state. |
| idx | Index of the step to begin. |
| now_ms | Time of the start, ms. |
|
static |
Command a pulse width.
| t | Run state. |
| us | Pulse width to command, us. |
| now_ms | Time of the command, ms. |
|
static |
A reading count that moved from was to now: what the meter took since, and what of it never reached the run.
| t | Run state. |
| was | Count before. |
| now | Count now. |
|
static |
The step running.
| t | Run state. |
|
static |
The move the encoder was judging is over: counted into its step.
| t | Run state. |
|
static |
The angle in the commanded direction: with REVERSE on, the horn turns the other way for the same command, and the angle is negated.
| t | Run state. |
| raw | Encoder count, 0 to 4095. |
|
static |
A move to end is commanded at now_ms: the encoder judges it from the angle read before.
| t | Run state. |
| end | 0 for the low end, 1 for the high end. |
| counted | true when the move counts. |
| now_ms | Time of the command, ms. |
|
static |
The count at at_ms, by servo_test_enc_at()'s rule.
| t | Run state. |
| at_ms | Time of the row, ms. |
| out | Out: count of the newest reading in reach. |
|
static |
The hold at an end is over: its level, and the next move or the step's end.
| t | Run state. |
| now_ms | Time of the end, ms. |
|
static |
The move is over: arrived at at_ms, or timed out.
| t | Run state. |
| arrived | true when the move arrived. |
| at_ms | Time the move ended, ms. |
|
static |
End the step: begin the next one, or finish the run.
| t | Run state. |
| now_ms | Time of the end, ms. |
|
static |
The run ends: the output off and the servo let go, whatever ended it.
| t | Run state. |
| why | Reason for the end of the run. |
| now_ms | Time of the end, ms. |
|
static |
Length of the hold at an end, ms.
| t | Run state. |
|
static |
One row of the CSV, for a reading: the columns every run has, the encoder's two where it is on, and the meter's six in tail.
| t | Run state. |
| m | Reading. |
| mode | Supply mode, 0 off, 1 CV, 2 CC; other values print no mode. |
| position_us | Measured pulse width, us; 0 for none. |
| tail | Columns after the meter word. |
|
static |
Add a reading to a mean.
| m | Mean to add to. |
| v | Value to add. |
|
static |
Mean of the readings added so far.
| m | Mean to read. |
|
static |
A new reading of the meter, measured by the phase it falls in.
| t | Run state. |
| m | Reading of the meter. |
|
static |
Tell whether the run reads the INA3221 windows.
| t | Run state. |
|
static |
Queue a line for the card; a CSV row is counted as lost when the outbox is full.
| t | Run state. |
| kind | Kind of the line. |
| text | Line text. |
| void servo_test_abort | ( | servo_test_t * | t, |
| servo_test_abort_t | why, | ||
| uint32_t | now_ms ) |
End a running run for why: the output is asked off and the servo let go at the next servo_test_step().
Nothing for a run not running.
| t | Run state. NULL is ignored. |
| why | Reason the run ends, reported as ABORTED. |
| now_ms | Time of the abort, ms. |
| bool servo_test_brownout | ( | const servo_test_t * | t, |
| float * | moved_v, | ||
| bool * | stopped ) |
The lowest brown-out voltage the servo moved at, and whether one below it showed none.
| t | Run state. NULL gives false. |
| moved_v | Out: lowest brown-out set point at which the servo moved, V. NULL to skip. |
| stopped | Out: true when a brown-out step that ran showed no movement. NULL to skip. |
| bool servo_test_drained | ( | const servo_test_t * | t | ) |
Whether everything of the last run has been handed over.
| t | Run state. |
| bool servo_test_enc_at | ( | const servo_test_t * | t, |
| uint32_t | at_ms, | ||
| float * | deg ) |
The angle at at_ms, degrees from cfg.enc_centre: the newest kept reading taken at or before at_ms (a signed difference, so a reading taken later than the row does not count), when it is no older than SERVO_TEST_ENC_STALE_MS. False when there is none.
| t | Run state. |
| at_ms | Time of the row, ms. |
| deg | Out: angle, degrees from the centre count. |
| float servo_test_enc_deg | ( | uint16_t | raw, |
| uint16_t | centre ) |
raw from centre on the circle, degrees, -180 to just under 180: 360 / 4096 a count. The difference of any two counts the shortest way round.
| raw | Encoder count, 0 to 4095. |
| centre | Centre count, 0 to 4095. |
| bool servo_test_enc_end | ( | const servo_test_t * | t, |
| const servo_test_step_t * | s, | ||
| unsigned | end, | ||
| float * | deg, | ||
| float * | err ) |
The mean settled end angle of step s at end (0 low, 1 high) into deg, degrees from cfg.enc_centre in the commanded direction, -180 to just under 180, and its error against cfg.enc_cmd_deg[end] into err, the same range; either may be NULL.
The mean is taken on the circle, from the first end angle of the step. False when no move settled there. End angles of one end that lie 180 degrees or more apart have no mean that says anything.
| t | Run state. |
| s | Step whose settled moves are averaged. |
| end | 0 for the low end, 1 for the high end. |
| deg | Out: mean end angle, degrees. NULL to skip. |
| err | Out: angle error, degrees. NULL to skip. |
end; false when none did, or for a NULL argument or an end above 1. | float servo_test_enc_wrap | ( | float | deg | ) |
deg on the circle: -180 to just under 180.
| deg | Angle, degrees. |
| void servo_test_encoder | ( | servo_test_t * | t, |
| const servo_test_enc_t * | e ) |
A reading of the output encoder, each one once. Judged against the move under way; kept as the angle the CSV and the SERVO screen show.
A reading that is not valid, or that carries gap, ends the angle's validity before it. Nothing is judged without cfg.enc_on.
| t | Run state. |
| e | Encoder reading. NULL is ignored. |
| void servo_test_init | ( | servo_test_t * | t | ) |
Clear a run state to idle.
| t | Run state to clear. NULL is ignored. |
| bool servo_test_max_hold | ( | const servo_test_t * | t, |
| float * | a ) |
Highest holding current of a run.
| t | Run state. |
| a | Out: holding current of largest magnitude, with its sign. NULL to skip. |
| bool servo_test_max_idle | ( | const servo_test_t * | t, |
| float * | a ) |
The highest, the longest and the lowest of a run, over its characterisation steps; false when none was measured. A current is the one of the largest magnitude, with its sign.
| t | Run state. |
| a | Out: idle current of largest magnitude, with its sign. NULL to skip. |
| bool servo_test_max_travel | ( | const servo_test_t * | t, |
| uint32_t * | ms ) |
Longest travel time of a run.
| t | Run state. |
| ms | Out: longest travel time, ms. NULL to skip. |
| void servo_test_meter_ina3221 | ( | servo_test_meter_t * | m, |
| uint16_t | shunt_dmohm ) |
An INA3221 channel on a shunt of shunt_dmohm, in 0.1mOhm (1000 for 0.1Ohm, which ends the range at 1.638A), named "INA3221" until the caller names the channel, read in 50ms windows: a change of current shows in the window that closes after it, up to 50ms later, and the panel times a move from the windows, so travel times are an upper bound.
| m | Meter description to fill. NULL is ignored. |
| shunt_dmohm | Shunt resistance, 0.1mOhm. |
| void servo_test_meter_model | ( | servo_test_meter_t * | m | ) |
The panel's model of the supply: no lag of its own and no repeats, and its travel times held to what a run on the PD mini can check.
| m | Meter description to fill. NULL is ignored. |
| void servo_test_meter_now | ( | servo_test_t * | t, |
| const servo_test_meter_now_t * | m, | ||
| uint32_t | now_ms ) |
The meter as a poll decided it, for every answer the caller has: with each window, each supply sample and each frame. A run on the INA3221 holds the change count of its start (cfg.meter_changes). It ends, as servo_test_abort() ends a run, at the first answer that is not the INA3221 or carries another count: the meter changed under it, whatever it is now. The reason is m->dropped when that drop came after the start, and SERVO_TEST_AB_INA_METER otherwise. Nothing for a run on another meter, which goes on with the meter it started with.
| t | Run state. |
| m | The answer of the poll. NULL is ignored. |
| now_ms | Time of the poll, ms. |
| void servo_test_meter_pdmini | ( | servo_test_meter_t * | m | ) |
Describe the PD mini as the run meter.
| m | Meter description to fill. NULL is ignored. |
| bool servo_test_negative_at_rest | ( | const servo_test_t * | t | ) |
Whether a step's idle current reads below -SERVO_TEST_NEG_IDLE_A: the shunt is fitted the other way round.
| t | Run state. |
| bool servo_test_over_a | ( | float | a, |
| float | limit_a ) |
Whether a is above limit_a: both in whole mA, a by its magnitude. The one comparison the verdict and the report make of a current against IDLE CURRENT, HOLD CURRENT and STALL AT.
| a | Current, A. |
| limit_a | Limit, A. |
| servo_test_out_t servo_test_peek | ( | servo_test_t * | t, |
| const char ** | text ) |
The next item for the card, without taking it: OPEN, the CSV's lines, then once the run is over the report's (if REPORT) and END. text is the line, valid until the next call. NONE while nothing waits.
| t | Run state. NULL gives NONE. |
| text | Out: text of the item; empty when none. |
| void servo_test_pop | ( | servo_test_t * | t | ) |
The item servo_test_peek() gave has been taken.
| t | Run state. |
| bool servo_test_rates | ( | const servo_test_t * | t, |
| float * | per_s, | ||
| float * | module_per_s, | ||
| uint32_t * | interval_ms ) |
Readings that reached the run per second, the supply's own per second, and the mean interval between two readings; false before two.
| t | Run state. NULL gives false. |
| per_s | Out: readings per second that reached the run. NULL to skip. |
| module_per_s | Out: the supply's own samples per second. NULL to skip. |
| interval_ms | Out: mean interval between two readings, ms. NULL to skip. |
| void servo_test_reading | ( | servo_test_t * | t, |
| const servo_test_reading_t * | r, | ||
| uint16_t | position_us ) |
A sample of the supply, with position_us the horn's measured pulse width or 0 for none.
One that repeats the last reading's sample count is checked for the supply's state and measures nothing. In a run on the INA3221 no sample measures: each is the supply's state alone.
| t | Run state. |
| r | The sample. NULL is ignored. |
| position_us | Horn pulse width, us; 0 for none. |
| bool servo_test_running | ( | const servo_test_t * | t | ) |
Tell whether a run is under way.
| t | Run state. NULL gives false. |
| bool servo_test_stall_unreachable | ( | const servo_test_cfg_t * | cfg, |
| bool * | by_limit, | ||
| bool * | by_range ) |
Whether STALL AT cannot be reached in a run of cfg: at or above the current limit, where the supply holds the current (by_limit), or at or above the end of the meter's range (by_range).
Either may be NULL. Compared in whole mA.
| cfg | Configuration of the run. NULL gives false. |
| by_limit | Out: true when STALL AT is at or above the current limit. NULL to skip. |
| by_range | Out: true when the meter is the INA3221 and STALL AT is at or above its range. NULL to skip. |
| servo_test_start_t servo_test_start | ( | servo_test_t * | t, |
| const servo_test_cfg_t * | cfg, | ||
| uint32_t | now_ms, | ||
| const servo_test_reading_t * | last, | ||
| float | v_min, | ||
| float | v_max ) |
Start a run at now_ms with the supply as last read it and its set points' range v_min to v_max.
Refused, and nothing changed, for no step and no brown-out, a step outside the range, a supply that does not answer, or ends not either side of the centre. A run that starts hands over OPEN and the CSV header first.
| t | Run state to start. NULL is refused as NO_STEPS. |
| cfg | Configuration of the run, copied into the state. NULL is refused as NO_STEPS. |
| now_ms | Time of the start, ms. |
| last | The supply as last read. NULL or offline is refused as NO_SUPPLY. |
| v_min | Lowest set point of the supply, V. |
| v_max | Highest set point of the supply, V. |
| void servo_test_step | ( | servo_test_t * | t, |
| uint32_t | now_ms, | ||
| const servo_test_in_t * | in, | ||
| servo_test_do_t * | out ) |
One pass at now_ms: the timers, and what the run wants done since the last pass into out.
| t | Run state. NULL is ignored. |
| now_ms | Time of the pass, ms. |
| in | What the caller knows: armed and the voltage cap. NULL keeps the cap as it was and does not disarm the run. |
| out | Out: what the run wants done. NULL is ignored. |
| unsigned servo_test_step_now | ( | const servo_test_t * | t | ) |
Number of the step running.
| t | Run state. NULL gives 0. |
| unsigned servo_test_steps_planned | ( | const servo_test_t * | t | ) |
Steps planned, the brown-out counted as one, and the one running, 1-based, for "step 2 of 3".
| t | Run state. NULL gives 0. |
| uint32_t servo_test_travel_window_ms | ( | const servo_test_t * | t | ) |
How long a move of t may take before it is late, or unseen: SERVO_TEST_TRAVEL_TIMEOUT_MS plus the meter's lag, 3300ms on the PD mini.
| t | Run state. NULL uses no meter lag. |
| servo_test_verdict_t servo_test_verdict | ( | const servo_test_t * | t | ) |
Verdict of a run.
| t | Run state. |
| void servo_test_window | ( | servo_test_t * | t, |
| const servo_test_win_t * | w, | ||
| uint16_t | position_us ) |
A window of INA3221 CH1, each number once and in order, with position_us as servo_test_reading() takes it.
The reading of a run on the INA3221, and nothing to any other run. A window without current or without voltage samples, and one that repeats the last number, measures nothing and does not count as a window that arrived.
| t | Run state. |
| w | The window. NULL is ignored. |
| position_us | Horn pulse width, us; 0 for none. |
|
static |
The step's threshold, once its idle readings are in: the larger of the smallest and SERVO_TEST_NOISE_K times their standard deviation.
| s | Step whose idle readings are in. |
|
static |
A current in whole mA, held to a range every reading lies in.
| a | Current, A. |