rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
servo_test.h File Reference

The automatic servo test: a servo stepped through supply voltages, its current measured at rest, moving and holding, its travel timed from the supply's current, then the voltage walked down until it no longer moves. More...

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "servo_move.h"
#include "servo_str.h"
#include "servo_test_io.h"

Go to the source code of this file.

Data Structures

struct  servo_test_meter_t
 What reads the servo's current, for what its travel times are worth. More...
struct  servo_test_cfg_t
 Configuration of a run: the plan, the servo, the limits and the settings the report shows. More...
struct  servo_test_mean_t
 A mean, kept as a sum. More...
struct  servo_test_step_t
 One voltage step's results. More...
struct  servo_test_t
struct  servo_test_meter_now_t
 The servo rail's meter as one poll of the panel decided it (servo_source.h). More...

Macros

#define SERVO_TEST_STEPS_MAX   4u
 The characterisation steps a run can carry: 4.8, 6.0, 7.4, 8.4V.
#define SERVO_TEST_HV_ABOVE_V   6.0f
 Above this a step is an HV (high voltage) step: a standard servo's rating is 4.8 to 6.0V.
#define SERVO_TEST_BROWNOUT_START_V   5.0f
 The brown-out walk: where it starts, how far each step goes down, and the lowest voltage it asks for.
#define SERVO_TEST_BROWNOUT_STEP_V   0.2f
#define SERVO_TEST_BROWNOUT_FLOOR_V   3.0f
#define SERVO_TEST_BROWNOUT_MAX   11u
 The steps from START to FLOOR, both included.
#define SERVO_TEST_BROWNOUT_MOVES   2u
 Moves at each brown-out voltage: centre to the high end, then to the low end.
#define SERVO_TEST_MOVE_MIN_A   SERVO_MOVE_MIN_A
 The smallest threshold.
#define SERVO_TEST_NOISE_K   SERVO_MOVE_NOISE_K
#define SERVO_TEST_BAND_A   SERVO_MOVE_BAND_A
 Back within this of the holding level, and not above it by the threshold, is the move arrived.
#define SERVO_TEST_IDLE_MS   1000u
#define SERVO_TEST_HOLD_MIN_MS   600u
 The shortest hold measured at an end, whatever DWELL says: at 10 readings a second, six readings.
#define SERVO_TEST_TRAVEL_TIMEOUT_MS   SERVO_MOVE_TIMEOUT_MS
 A move not arrived this long after its command is late; a meter's lag is added, since its readings show an arrival that much later.
#define SERVO_TEST_SET_TOL_V   0.05f
 A set point counts as taken once read back within this.
#define SERVO_TEST_SET_TIMEOUT_MS   3000u
#define SERVO_TEST_STALE_MS   1500u
 No new reading for this long ends the run: SUPPLY_LINK_STALE_MS.
#define SERVO_TEST_STALL_ABORT_MS   1000u
 Above STALL AT for this long ends the run.
#define SERVO_TEST_CC_ABORT_MS   1000u
 The supply in constant current for this long ends the run.
#define SERVO_TEST_WIN_STALE_MS   200u
 A run on the INA3221: no window for this long ends it, 4 windows of 50ms.
#define SERVO_TEST_WIN_MS   50u
 One window of the INA3221: SENSE_WINDOW_MS.
#define SERVO_TEST_NEG_IDLE_A   0.020f
 A run whose idle current is below minus this says the shunt is fitted the other way round.
#define SERVO_TEST_INA3221_END_UV   163800u
 The INA3221's shunt voltage at the end of its range, in microvolts: code 4095 at 40 uV.
#define SERVO_TEST_MOVES_MAX   1000u
 The most moves a step makes, LENGTH BY TIME included.
#define SERVO_TEST_ENC_TOL_COUNTS   12u
#define SERVO_TEST_ENC_TOL_DEG   1.0547f
 The same in degrees: 12 counts of 360 / 4096.
#define SERVO_TEST_ENC_HOLD_MS   100u
 The angle is still when its STILL_MS reaches this.
#define SERVO_TEST_ENC_MOVED_DEG   2.0f
 A move has moved when the angle is further than this from where it started.
#define SERVO_TEST_ENC_STALE_MS   500u
 The angle read before a command is the move's start while younger than this.
#define SERVO_TEST_PDMINI_LAG_MS   300u
 The PD mini's own meter: read every 102 to 106ms, a change of current shows about 300ms later (median 0.31s over two runs, 2026-10-07), and its value often repeats over several readings.
#define SERVO_TEST_STEP_SLOTS   (SERVO_TEST_STEPS_MAX + SERVO_TEST_BROWNOUT_MAX)
#define SERVO_TEST_OUTBOX   16u
 Log rows waiting for the card.

Enumerations

enum  servo_test_meter_kind_t { SERVO_TEST_METER_PDMINI = 0 , SERVO_TEST_METER_INA3221 , SERVO_TEST_METER_MODEL }
 Which meter a run reads. More...
enum  servo_test_ina_t {
  SERVO_TEST_INA_NONE = 0 , SERVO_TEST_INA_OLD , SERVO_TEST_INA_NOT_HELD , SERVO_TEST_INA_SILENT ,
  SERVO_TEST_INA_NO_WINDOW , SERVO_TEST_INA_RESET , SERVO_TEST_INA_SETTLING , SERVO_TEST_INA_MODEL ,
  SERVO_TEST_INA_COUNT
}
 Why a run reads the PD mini with the INA3221 on in SETUP: the condition of servo_source.h that does not hold. More...
enum  servo_test_state_t { SERVO_TEST_IDLE = 0 , SERVO_TEST_RUNNING , SERVO_TEST_DONE }
enum  servo_test_phase_t {
  SERVO_TEST_PH_NONE = 0 , SERVO_TEST_PH_SET , SERVO_TEST_PH_SETTLE , SERVO_TEST_PH_IDLE ,
  SERVO_TEST_PH_MOVE , SERVO_TEST_PH_HOLD
}
enum  servo_test_abort_t {
  SERVO_TEST_AB_NONE = 0 , SERVO_TEST_AB_STOP , SERVO_TEST_AB_DISARMED , SERVO_TEST_AB_LINK ,
  SERVO_TEST_AB_LEFT , SERVO_TEST_AB_OPERATOR , SERVO_TEST_AB_SETTINGS , SERVO_TEST_AB_TOUCH ,
  SERVO_TEST_AB_SUPPLY_LOST , SERVO_TEST_AB_TRIPPED , SERVO_TEST_AB_SUPPLY_OFF , SERVO_TEST_AB_STALE ,
  SERVO_TEST_AB_NOT_ON , SERVO_TEST_AB_SET_NOT_TAKEN , SERVO_TEST_AB_CAP , SERVO_TEST_AB_STALL ,
  SERVO_TEST_AB_CC , SERVO_TEST_AB_WIN_STALE , SERVO_TEST_AB_INA_RESET , SERVO_TEST_AB_INA_SILENT ,
  SERVO_TEST_AB_INA_NO_WINDOW , SERVO_TEST_AB_INA_SETUP , SERVO_TEST_AB_INA_METER , SERVO_TEST_AB_COUNT
}
enum  servo_test_verdict_t { SERVO_TEST_PASS = 0 , SERVO_TEST_FAIL , SERVO_TEST_ABORTED , SERVO_TEST_NOT_MEASURABLE }
enum  servo_test_start_t {
  SERVO_TEST_START_OK = 0 , SERVO_TEST_START_NO_STEPS , SERVO_TEST_START_ABOVE_CAP , SERVO_TEST_START_NO_SUPPLY ,
  SERVO_TEST_START_BAD_ENDS
}
 Why a run would not start. More...

Functions

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.
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.
void servo_test_init (servo_test_t *t)
 Clear a run state to idle.
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.
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.
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.
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.
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.
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_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.
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().
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.
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_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).
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.
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.
servo_str_t servo_test_phase_str (servo_test_phase_t ph)
 The entries the names below come from, for a caller with its own table.
servo_str_t servo_test_abort_str (servo_test_abort_t why)
 Table entry for an abort reason.
servo_str_t servo_test_verdict_str (servo_test_verdict_t v)
 Table entry for a verdict.
const char * servo_test_phase_name (servo_test_phase_t ph)
 English name of a phase.
const char * servo_test_abort_name (servo_test_abort_t why)
 English name of an abort reason.
const char * servo_test_verdict_name (servo_test_verdict_t v)
 English name of a verdict.
const char * servo_test_start_name (servo_test_start_t why)
 English name of a start refusal.
const char * servo_test_csv_header (void)
 The CSV's header row: the 13 columns of every run, then the meter's six – meter, window, current max, current min, voltage min, clipped.
const char * servo_test_csv_header_enc (void)
 And with the encoder's two columns, angle_deg and travel_angle_ms, between the two groups.
const char * servo_test_meter_word (uint8_t kind)
 The word a row's meter column carries: INA3221, PDMINI or MODEL.
bool servo_test_stall_note (const servo_test_cfg_t *cfg, char *buf, size_t n)
 The line that says STALL AT cannot be reached in a run of cfg, in the language of cfg->text, into buf: against the current limit where that is the reason, else against the meter's range (servo_test_stall_unreachable()). False, and buf empty, when a reading can pass STALL AT.
bool servo_report_line (const servo_test_t *t, unsigned idx, char *buf, size_t n)
 Line idx of the report into buf, in the language of t->cfg.text; false past the last.

Detailed Description

The automatic servo test: a servo stepped through supply voltages, its current measured at rest, moving and holding, its travel timed from the supply's current, then the voltage walked down until it no longer moves.

The servo is a PWM (pulse-width modulation) servo on the bench's surface outputs, and the supply is the one SUPPLY drives: the PD mini, or the panel's model of it. Nothing measures the horn, so every result is read off the servo rail's current, at the rate its meter reports it.

The meter. A run reads one meter from its start to its end, the one its configuration names (cfg.meter.kind):

PD mini the supply's own readings (servo_test_reading()), about 10 a second. The model stands in for it with no IOmcu. INA3221 the 50ms windows of the INA3221's CH1 (servo_test_window()), 20 a second: each one's mean current and mean bus voltage are the reading, its highest and lowest 1ms sample give the peak, its lowest bus voltage the step's lowest voltage. The supply is still read for its state – answering, output, trip, set point, mode – and none of its voltages or currents is used or logged.

A reading is a point in time on the PD mini and 50ms long on the INA3221 (cfg.meter.span_ms). IDLE and HOLD take the windows that lie wholly inside them: a window that began before the phase is in neither. The window open at a command is MOVE's. Moves are timed by the panel from the windows as from the supply's readings, so a travel time on the INA3221 is late by up to one window and one poll, an upper bound, and TRAVEL TIME is not checked.

Signed current. A negative current is a reading. The limits, the peak and STALL AT compare its magnitude, in whole mA (servo_test_over_a()), and the CSV keeps the sign. A clipped window – a 1ms sample at an end of the INA3221's range – is a reading as well: its figures are the values the part gave, a lower bound, and the report counts such windows.

Per voltage step, in this order:

SET the set point asked, and waited for until the supply reads it back (SERVO_TEST_SET_TOL_V), at most SERVO_TEST_SET_TIMEOUT_MS; the first step also waits for the output to read on; SETTLE the TEST page's SETTLE, with the servo at its centre; IDLE SERVO_TEST_IDLE_MS at the centre: the idle current, and its noise, the standard deviation of those readings. The step's threshold is the larger of SERVO_TEST_MOVE_MIN_A and SERVO_TEST_NOISE_K times that noise; MOVE a step command to the far end. A move shows movement when a reading lies more than the threshold from the level before the command. It has arrived at the first reading back within SERVO_TEST_BAND_A of that end's holding level, after a reading of the movement more than the threshold above that level; the travel time runs from the command to that reading. A destination held harder than the servo moves, an end pushing on a stop, is never passed: there the move has arrived at the first of two readings in a row within SERVO_TEST_BAND_A of the level and of each other, after movement. A move with movement that does not arrive in SERVO_TEST_TRAVEL_TIMEOUT_MS, plus the meter's lag (servo_test_travel_window_ms()), is late; one that shows no movement in that time is unseen: not timed and not late. The rules are servo_move.h's, fed one reading at a time; HOLD the longer of DWELL and SERVO_TEST_HOLD_MIN_MS at that end: the holding current there, and the holding level the next move to that end falls back to.

A step's first two moves, from the centre to the low end and on to the high end, are not counted: they measure each end's holding level. The counted moves go end to end from there, for MOVEMENTS moves or TEST TIME, as LENGTH BY says. Before an end's holding level is known the idle current stands in for it.

The brown-out test follows: from SERVO_TEST_BROWNOUT_START_V (or the cap, if lower) down in SERVO_TEST_BROWNOUT_STEP_V steps, each one SET, SETTLE and IDLE as above and then SERVO_TEST_BROWNOUT_MOVES moves, centre to the high end and back to the low end. A voltage shows no movement when no reading of any of those moves lies more than that voltage's threshold from the level before its command; the walk stops there, or at the floor, which is the last step when it lies off the step's grid.

The output encoder. With an AS5600 on the horn shaft (cfg.enc_on) the run also reads the angle, from servo_test_encoder(), and reports it beside the current's figures; the current's rules above are unchanged and the angle decides nothing: not the verdict, not a limit. For each move, from its command to the next:

moved the angle leaves SERVO_TEST_ENC_MOVED_DEG of the angle read before the command, when that reading is younger than SERVO_TEST_ENC_STALE_MS. The distance is taken on the circle, the shortest way round, 0 to 180 degrees; settled after it has moved, a reading whose STILL_MS is at least SERVO_TEST_ENC_HOLD_MS and whose still time began after the command: the angle has stayed within SERVO_TEST_ENC_TOL_COUNTS counts (1.05 degrees) of an anchor for that long. The travel time is the start of that stillness minus the command, which the IOmcu's 2ms sample interval resolves whatever the polling interval. It includes the command's way from the panel to the pin, up to one poll interval and one PWM frame, not measured; end angle the last reading of a settled move, from the centre count (cfg.enc_centre); the angle error is that minus the commanded angle of the end (cfg.enc_cmd_deg).

The circle. The sensor's count wraps from 4095 to 0 and every angle is a position on one turn, so no two angles are subtracted or averaged as plain numbers. An angle is the count less the centre count taken the shortest way, -180 to just under +180 degrees. A step's end angles at one end are averaged as offsets from the first of them, each offset the shortest way, and the mean is put back on the circle (servo_test_enc_end()); the angle error is wrapped to the same range. Two readings 2 counts apart either side of the half turn from the centre are 0.18 degrees apart, and ends read at +179.9 and -179.9 degrees average to 180, shown as -180.0.

More than 180 degrees. One turn is all the sensor tells apart. A servo whose ends lie more than 180 degrees apart is reported correctly while each end lies within 180 degrees of the centre: the two ends are never subtracted from each other. An end further than 180 degrees from the centre is reported 360 degrees off (+200 as -160); its angle error is still right while the commanded angle names the same position, since the error is wrapped. A move is "moved" by the shortest way between its start and the reading, so a move that ends within SERVO_TEST_ENC_MOVED_DEG of a whole turn from its start reads unmoved, and the direction of a move is not judged. Turns are not counted: a winch servo's travel of several turns is not measured.

The magnet. A reading is an angle only while the sensor detects its magnet (STATUS MD, as5600.h); the link hands out no other (sense_link_enc()). A reading without one arrives as not valid with no_magnet set: it ends the angle's validity as any invalid reading does – no angle in the CSV, a move that had not settled not counted – and the report says how often it happened. A reading with the field too weak or too strong (ML, MH) is an angle and is used; the report counts them.

A move that never left the tolerance is unmoved; one that moved and was not still for SERVO_TEST_ENC_HOLD_MS before the next command is late. Only counted moves are reported. A deadband cannot be derived from moves that go end to end, and is reported as not measured.

What reads the current is described to the run (servo_test_meter_t), so a meter whose readings lag and repeat, as the PD mini's do, gives travel times that are an upper bound: TRAVEL TIME is reported against them and not checked.

The run is aborted – the output asked off, the servo let go – by STOP, a disarm, link loss, leaving the screen, the operator taking the servo or changing how it is driven, and touch events lost (the caller's, servo_test_abort()), and by the supply (here): it stops answering, trips, its output goes off, no new reading arrives for SERVO_TEST_STALE_MS, a set point is not read back, a step above the voltage cap in force, a current above STALL AT for SERVO_TEST_STALL_ABORT_MS, or the supply in constant current for SERVO_TEST_CC_ABORT_MS. A run on the INA3221 also ends when no window arrives for SERVO_TEST_WIN_STALE_MS and when the INA3221 stops being the servo rail's meter (servo_test_meter_now()): it never goes on with another meter. An aborted run is reported as ABORTED with its reason and what it measured so far, and nothing fed to it after its end changes either.

Stall. A reading whose magnitude is above STALL AT on a characterisation step fails the run, and readings above it for SERVO_TEST_STALL_ABORT_MS without one at or under it end the run. On the INA3221 the reading is the window's mean and the time counts from the window's start, so 20 windows in a row end it. A supply in constant current holds its current limit: with STALL AT at or above the limit no reading is above STALL AT, and the constant-current rule ends the run instead, whatever STALL AT is. Constant current for less than SERVO_TEST_CC_ABORT_MS fails nothing: the report counts those readings.

A state machine fed readings and stepped with the time, so the host suite runs it against the servo and supply models; it moves nothing itself. What it wants done comes back from servo_test_step(). Its log rows and report lines leave through an outbox (servo_test_peek()), so the card is written by whoever owns the card.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ SERVO_TEST_BROWNOUT_START_V

#define SERVO_TEST_BROWNOUT_START_V   5.0f

The brown-out walk: where it starts, how far each step goes down, and the lowest voltage it asks for.

A supply whose lowest set point is higher stops it there.

◆ SERVO_TEST_MOVE_MIN_A

#define SERVO_TEST_MOVE_MIN_A   SERVO_MOVE_MIN_A

The smallest threshold.

A step's threshold is the larger of this and SERVO_TEST_NOISE_K times its idle noise. A reading more than the threshold from the level before a command is the servo moving, and more than it above an end's holding level is the move not yet there. The move rules are servo_move.h's; these are its constants.

◆ SERVO_TEST_OUTBOX

#define SERVO_TEST_OUTBOX   16u

Log rows waiting for the card.

A row is a reading, 10 to 20 a second.

◆ SERVO_TEST_WIN_STALE_MS

#define SERVO_TEST_WIN_STALE_MS   200u

A run on the INA3221: no window for this long ends it, 4 windows of 50ms.

The limit run (servo_limrun.h) takes the same figure.

Enumeration Type Documentation

◆ servo_test_abort_t

Enumerator
SERVO_TEST_AB_CC 

the supply in constant current

SERVO_TEST_AB_WIN_STALE 

no window for SERVO_TEST_WIN_STALE_MS

SERVO_TEST_AB_INA_RESET 

the part reset itself

SERVO_TEST_AB_INA_SILENT 

it stopped answering

SERVO_TEST_AB_INA_NO_WINDOW 

its windows hold no current

SERVO_TEST_AB_INA_SETUP 

the IOmcu no longer holds its set-up, or it is off in SETUP

SERVO_TEST_AB_INA_METER 

it stopped being the meter, the condition not known

◆ servo_test_ina_t

Why a run reads the PD mini with the INA3221 on in SETUP: the condition of servo_source.h that does not hold.

Enumerator
SERVO_TEST_INA_NONE 

not on in SETUP, or it is the meter

SERVO_TEST_INA_OLD 

the IOmcu is older than 4.11

SERVO_TEST_INA_NOT_HELD 

it does not hold the set-up

SERVO_TEST_INA_SILENT 

the part does not answer

SERVO_TEST_INA_NO_WINDOW 

no fresh window with current

SERVO_TEST_INA_RESET 

it reset itself

SERVO_TEST_INA_SETTLING 

working for less than 1s

SERVO_TEST_INA_MODEL 

the supply is the panel's model

◆ servo_test_meter_kind_t

Which meter a run reads.

Enumerator
SERVO_TEST_METER_PDMINI 

the supply's own readings

SERVO_TEST_METER_INA3221 

CH1's 50ms windows

SERVO_TEST_METER_MODEL 

the panel's model of the supply

◆ servo_test_start_t

Why a run would not start.

Enumerator
SERVO_TEST_START_NO_STEPS 

no step and no brown-out chosen

SERVO_TEST_START_ABOVE_CAP 

a step above the voltage cap

SERVO_TEST_START_NO_SUPPLY 

the supply does not answer

SERVO_TEST_START_BAD_ENDS 

the ends are not either side of the centre

◆ servo_test_state_t

Enumerator
SERVO_TEST_IDLE 

never started

SERVO_TEST_DONE 

finished or aborted

◆ servo_test_verdict_t

Enumerator
SERVO_TEST_NOT_MEASURABLE 

Nothing failed, and a counted move showed no movement: the current cannot tell that servo moving from one standing still.

Function Documentation

◆ servo_report_line()

bool servo_report_line ( const servo_test_t * t,
unsigned idx,
char * buf,
size_t n )

Line idx of the report into buf, in the language of t->cfg.text; false past the last.

Parameters
tRun state.
idxIndex of the line asked for.
bufOut: the line. NULL is refused.
nSize of buf, bytes; 0 is refused.
Returns
true when line idx exists and was written into buf; false past the last line, or for a NULL argument or n of 0.

◆ servo_test_abort()

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.

Parameters
tRun state. NULL is ignored.
whyReason the run ends, reported as ABORTED.
now_msTime of the abort, ms.

◆ servo_test_abort_name()

const char * servo_test_abort_name ( servo_test_abort_t why)

English name of an abort reason.

Parameters
whyAbort reason to name.
Returns
name of the reason.

◆ servo_test_abort_str()

servo_str_t servo_test_abort_str ( servo_test_abort_t why)

Table entry for an abort reason.

Parameters
whyAbort reason to name.
Returns
table entry for the reason.

◆ servo_test_brownout()

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.

Parameters
tRun state. NULL gives false.
moved_vOut: lowest brown-out set point at which the servo moved, V. NULL to skip.
stoppedOut: true when a brown-out step that ran showed no movement. NULL to skip.
Returns
true when a brown-out step moved.

◆ servo_test_csv_header()

const char * servo_test_csv_header ( void )

The CSV's header row: the 13 columns of every run, then the meter's six – meter, window, current max, current min, voltage min, clipped.

Returns
header row of the CSV without the encoder columns.

◆ servo_test_csv_header_enc()

const char * servo_test_csv_header_enc ( void )

And with the encoder's two columns, angle_deg and travel_angle_ms, between the two groups.

Returns
header row of the CSV with the encoder columns.

◆ servo_test_drained()

bool servo_test_drained ( const servo_test_t * t)

Whether everything of the last run has been handed over.

Parameters
tRun state.
Returns
true for NULL, or when the run is not RUNNING, nothing is queued and END was handed over.

◆ servo_test_enc_at()

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.

Parameters
tRun state.
at_msTime of the row, ms.
degOut: angle, degrees from the centre count.
Returns
true when a kept reading no older than SERVO_TEST_ENC_STALE_MS was taken at or before at_ms; deg is set then.

◆ servo_test_enc_end()

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.

Parameters
tRun state.
sStep whose settled moves are averaged.
end0 for the low end, 1 for the high end.
degOut: mean end angle, degrees. NULL to skip.
errOut: angle error, degrees. NULL to skip.
Returns
true when a move settled at end; false when none did, or for a NULL argument or an end above 1.

◆ servo_test_encoder()

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.

Parameters
tRun state.
eEncoder reading. NULL is ignored.

◆ servo_test_init()

void servo_test_init ( servo_test_t * t)

Clear a run state to idle.

Parameters
tRun state to clear. NULL is ignored.

◆ servo_test_max_hold()

bool servo_test_max_hold ( const servo_test_t * t,
float * a )

Highest holding current of a run.

Parameters
tRun state.
aOut: holding current of largest magnitude, with its sign. NULL to skip.
Returns
true when a characterisation step measured holding current.

◆ servo_test_max_idle()

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.

Parameters
tRun state.
aOut: idle current of largest magnitude, with its sign. NULL to skip.
Returns
true when a characterisation step measured idle current.

◆ servo_test_max_travel()

bool servo_test_max_travel ( const servo_test_t * t,
uint32_t * ms )

Longest travel time of a run.

Parameters
tRun state.
msOut: longest travel time, ms. NULL to skip.
Returns
true when a counted move of a characterisation step arrived.

◆ servo_test_meter_ina3221()

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.

Parameters
mMeter description to fill. NULL is ignored.
shunt_dmohmShunt resistance, 0.1mOhm.

◆ servo_test_meter_model()

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.

Parameters
mMeter description to fill. NULL is ignored.

◆ servo_test_meter_now()

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.

Parameters
tRun state.
mThe answer of the poll. NULL is ignored.
now_msTime of the poll, ms.

◆ servo_test_meter_pdmini()

void servo_test_meter_pdmini ( servo_test_meter_t * m)

Describe the PD mini as the run meter.

Parameters
mMeter description to fill. NULL is ignored.

◆ servo_test_meter_word()

const char * servo_test_meter_word ( uint8_t kind)

The word a row's meter column carries: INA3221, PDMINI or MODEL.

Parameters
kindMeter kind, servo_test_meter_kind_t.
Returns
word for the meter column: INA3221, PDMINI or MODEL.

◆ servo_test_negative_at_rest()

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.

Parameters
tRun state.
Returns
true when a step idle mean is below minus SERVO_TEST_NEG_IDLE_A, in whole mA.

◆ servo_test_over_a()

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.

Parameters
aCurrent, A.
limit_aLimit, A.
Returns
true when the magnitude of a, in whole mA, is above limit_a in whole mA.

◆ servo_test_peek()

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.

Parameters
tRun state. NULL gives NONE.
textOut: text of the item; empty when none.
Returns
kind of the next item: queued lines first, then the report lines, then END; NONE while nothing waits.

◆ servo_test_phase_name()

const char * servo_test_phase_name ( servo_test_phase_t ph)

English name of a phase.

Parameters
phPhase to name.
Returns
name of the phase.

◆ servo_test_phase_str()

servo_str_t servo_test_phase_str ( servo_test_phase_t ph)

The entries the names below come from, for a caller with its own table.

Parameters
phPhase to name.
Returns
table entry for the phase.

◆ servo_test_pop()

void servo_test_pop ( servo_test_t * t)

The item servo_test_peek() gave has been taken.

Parameters
tRun state.

◆ servo_test_rates()

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.

Parameters
tRun state. NULL gives false.
per_sOut: readings per second that reached the run. NULL to skip.
module_per_sOut: the supply's own samples per second. NULL to skip.
interval_msOut: mean interval between two readings, ms. NULL to skip.
Returns
true when two readings span time; false before two readings or when they share a time.

◆ servo_test_reading()

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.

Parameters
tRun state.
rThe sample. NULL is ignored.
position_usHorn pulse width, us; 0 for none.

◆ servo_test_running()

bool servo_test_running ( const servo_test_t * t)

Tell whether a run is under way.

Parameters
tRun state. NULL gives false.
Returns
true while the state is RUNNING.

◆ servo_test_stall_note()

bool servo_test_stall_note ( const servo_test_cfg_t * cfg,
char * buf,
size_t n )

The line that says STALL AT cannot be reached in a run of cfg, in the language of cfg->text, into buf: against the current limit where that is the reason, else against the meter's range (servo_test_stall_unreachable()). False, and buf empty, when a reading can pass STALL AT.

Parameters
cfgConfiguration of the run.
bufOut: the line.
nSize of buf, bytes.
Returns
true when the note was written; false when a reading can pass STALL AT.

◆ servo_test_stall_unreachable()

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.

Parameters
cfgConfiguration of the run. NULL gives false.
by_limitOut: true when STALL AT is at or above the current limit. NULL to skip.
by_rangeOut: true when the meter is the INA3221 and STALL AT is at or above its range. NULL to skip.
Returns
true when either holds.

◆ servo_test_start()

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.

Parameters
tRun state to start. NULL is refused as NO_STEPS.
cfgConfiguration of the run, copied into the state. NULL is refused as NO_STEPS.
now_msTime of the start, ms.
lastThe supply as last read. NULL or offline is refused as NO_SUPPLY.
v_minLowest set point of the supply, V.
v_maxHighest set point of the supply, V.
Returns
SERVO_TEST_START_OK when the run started; otherwise the reason it was refused.

◆ servo_test_start_name()

const char * servo_test_start_name ( servo_test_start_t why)

English name of a start refusal.

Parameters
whyRefusal to name.
Returns
name of the refusal.

◆ servo_test_step()

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.

Parameters
tRun state. NULL is ignored.
now_msTime of the pass, ms.
inWhat the caller knows: armed and the voltage cap. NULL keeps the cap as it was and does not disarm the run.
outOut: what the run wants done. NULL is ignored.

◆ servo_test_step_now()

unsigned servo_test_step_now ( const servo_test_t * t)

Number of the step running.

Parameters
tRun state. NULL gives 0.
Returns
1-based number of the step running; 0 for NULL or a run with no steps.

◆ servo_test_steps_planned()

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".

Parameters
tRun state. NULL gives 0.
Returns
steps planned, the brown-out walk counted as one.

◆ servo_test_travel_window_ms()

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.

Parameters
tRun state. NULL uses no meter lag.
Returns
SERVO_TEST_TRAVEL_TIMEOUT_MS plus the meter lag_ms, ms.

◆ servo_test_verdict()

servo_test_verdict_t servo_test_verdict ( const servo_test_t * t)

Verdict of a run.

Parameters
tRun state.
Returns
ABORTED for NULL or an aborted run; FAIL on a stall, a limit exceeded or a timed-out move; NOT_MEASURABLE when a counted move showed no movement; otherwise PASS.

◆ servo_test_verdict_name()

const char * servo_test_verdict_name ( servo_test_verdict_t v)

English name of a verdict.

Parameters
vVerdict to name.
Returns
name of the verdict.

◆ servo_test_verdict_str()

servo_str_t servo_test_verdict_str ( servo_test_verdict_t v)

Table entry for a verdict.

Parameters
vVerdict to name.
Returns
table entry for the verdict.

◆ servo_test_window()

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.

Parameters
tRun state.
wThe window. NULL is ignored.
position_usHorn pulse width, us; 0 for none.