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

Locale-tolerant numeric parsing with unit suffixes. More...

#include <stdbool.h>
#include <stddef.h>

Go to the source code of this file.

Data Structures

struct  log_value_t
 A cell split into its parts. digits keeps its separators. More...
struct  log_votes_t
 Running tally of the evidence seen so far. More...
struct  log_unit_tally_t
 Tally of unit suffixes seen in one column, in first-seen order. More...

Macros

#define LOG_DIGITS_MAX   40
 Longest numeric body and unit suffix the parser reads in one cell.
#define LOG_UNIT_MAX   12
 Longest unit suffix the parser reads in one cell, characters.
#define LOG_UNIT_SLOTS   4
 Tally of unit suffixes seen in one column, in first-seen order.

Enumerations

enum  log_conv_t { LOG_CONV_AUTO = 0 , LOG_CONV_DE , LOG_CONV_EN }
 Decimal convention of a file. More...
enum  log_ambig_t { LOG_AMBIG_THOUSANDS = 0 , LOG_AMBIG_DECIMAL }
 What a wholly ambiguous file defaults to. More...
enum  log_evidence_t { LOG_EV_NONE = 0 , LOG_EV_AMBIGUOUS , LOG_EV_DE , LOG_EV_EN }
 What one numeric string proves about the convention. More...

Functions

bool log_split_value (const char *raw, log_value_t *out)
 Split a raw cell into numeric text, exponent and unit.
log_evidence_t log_evidence_of (const char *digits)
 Inspect one already-split numeric string and report what it proves.
void log_votes_reset (log_votes_t *v)
 Clear the tally.
void log_votes_add (log_votes_t *v, const char *raw)
 Feed one raw cell.
log_conv_t log_votes_result (const log_votes_t *v, log_ambig_t fallback, bool *confident, bool *conflict)
 Decide the convention from the tally.
bool log_is_well_formed (const char *digits, log_conv_t convention)
 Is digits well formed under convention?
bool log_parse_with (const char *raw, log_conv_t convention, double *value, char *unit, size_t unit_size)
 Parse one cell with a known convention.
bool log_parse_loose (const char *raw, log_ambig_t fallback, double *value)
 Parse a standalone value, inferring the convention from it alone.
void log_unit_tally_reset (log_unit_tally_t *t)
 Clear a unit tally.
void log_unit_tally_add (log_unit_tally_t *t, const char *unit)
 Add a unit suffix to a tally.
void log_unit_dominant (const log_unit_tally_t *t, char *out, size_t out_size, bool *mixed)
 The most common non-empty suffix. mixed reports more than one.
void log_unit_from_header (const char *header, char *name, size_t name_size, char *unit, size_t unit_size)
 Pull a unit out of a column header: "voltage (V)", "current [A]", "speed km/h", "power (W)". "gyroADC[0]" keeps its index – that is an axis number, not a unit.
double log_time_unit_scale (const char *unit)
 Seconds per unit for a time column, or 0 when the name is not a time unit.

Detailed Description

Locale-tolerant numeric parsing with unit suffixes.

"1,234" is ambiguous: German reads 1.234, English reads 1234. Guessing per value is unsafe, because the same column would then parse inconsistently: "1,234" as 1234 on one row and "10,23" as 10.23 on the next implies two conventions in one column, which no exporter produces.

So the convention is decided per file, from every value in it. Most files contain at least one value that settles it:

"1.234,56" both separators -> the last one is the decimal (German) "1,234.56" both separators -> the last one is the decimal (English) "10,23" one, 2 trailing -> not a thousands group (German) "1.5" one, 1 trailing -> not a thousands group (English) "1.234.567" repeated -> that separator groups (German) "1,234" one, 3 trailing -> ambiguous, no evidence

Only when every value is ambiguous does the fallback apply.

The evidence gatherer is a streaming accumulator rather than a function over an array: the firmware reads logs off a card and never holds the whole file. Values arrive once, are voted on, and are forgotten.

Pure C, no ESP-IDF.

SPDX-License-Identifier: MIT

Enumeration Type Documentation

◆ log_ambig_t

What a wholly ambiguous file defaults to.

Enumerator
LOG_AMBIG_THOUSANDS 

"1,234" -> 1234

LOG_AMBIG_DECIMAL 

"1,234" -> 1.234

◆ log_conv_t

enum log_conv_t

Decimal convention of a file.

Enumerator
LOG_CONV_AUTO 

only ever an input: "decide for me"

LOG_CONV_DE 

1.234,56

LOG_CONV_EN 

1,234.56

◆ log_evidence_t

What one numeric string proves about the convention.

Enumerator
LOG_EV_NONE 

no separator at all: "42"

LOG_EV_AMBIGUOUS 

one separator, 3 trailing

Function Documentation

◆ log_evidence_of()

log_evidence_t log_evidence_of ( const char * digits)

Inspect one already-split numeric string and report what it proves.

Parameters
digitsNumeric text as log_split_value() gives it.
Returns
The evidence.

◆ log_is_well_formed()

bool log_is_well_formed ( const char * digits,
log_conv_t convention )

Is digits well formed under convention?

Strict on purpose. Stripping the grouping separator and parsing what is left turns the German "1.234,56" into 1.23456 when read as English: a silently wrong number. Rejecting lets the caller surface the mismatch.

Parameters
digitsNumeric text as log_split_value() gives it.
conventionThe convention to check against.
Returns
true when the text is well formed.

◆ log_parse_loose()

bool log_parse_loose ( const char * raw,
log_ambig_t fallback,
double * value )

Parse a standalone value, inferring the convention from it alone.

Parameters
rawThe raw cell text.
fallbackThe default for an ambiguous value.
valueReceives the number.
Returns
true when the value parsed.

◆ log_parse_with()

bool log_parse_with ( const char * raw,
log_conv_t convention,
double * value,
char * unit,
size_t unit_size )

Parse one cell with a known convention.

Parameters
rawThe raw cell text.
conventionThe convention of the file.
valueReceives the number.
unitReceives the unit suffix.
unit_sizeSize of unit, bytes.
Returns
false when the cell does not conform, rather than guessing.

◆ log_split_value()

bool log_split_value ( const char * raw,
log_value_t * out )

Split a raw cell into numeric text, exponent and unit.

Grouping characters that are never decimal points – ASCII space, the apostrophe Swiss exporters use, U+00A0 and U+202F – are removed here, so digits carries only digits, '.' and ','.

Parameters
rawThe raw cell text.
outReceives the parts.
Returns
false when the cell holds no number at all.

◆ log_time_unit_scale()

double log_time_unit_scale ( const char * unit)

Seconds per unit for a time column, or 0 when the name is not a time unit.

Parameters
unitThe unit name.
Returns
Seconds per unit, or 0.

◆ log_unit_dominant()

void log_unit_dominant ( const log_unit_tally_t * t,
char * out,
size_t out_size,
bool * mixed )

The most common non-empty suffix. mixed reports more than one.

Parameters
tThe tally.
outReceives the suffix.
out_sizeSize of out, bytes.
mixedReceives true when more than one suffix was seen.

◆ log_unit_from_header()

void log_unit_from_header ( const char * header,
char * name,
size_t name_size,
char * unit,
size_t unit_size )

Pull a unit out of a column header: "voltage (V)", "current [A]", "speed km/h", "power (W)". "gyroADC[0]" keeps its index – that is an axis number, not a unit.

Parameters
headerThe column header.
nameReceives the name without the unit.
name_sizeSize of name, bytes.
unitReceives the unit.
unit_sizeSize of unit, bytes.

◆ log_unit_tally_add()

void log_unit_tally_add ( log_unit_tally_t * t,
const char * unit )

Add a unit suffix to a tally.

Parameters
tThe tally.
unitThe unit suffix.

◆ log_unit_tally_reset()

void log_unit_tally_reset ( log_unit_tally_t * t)

Clear a unit tally.

Parameters
tThe tally.

◆ log_votes_add()

void log_votes_add ( log_votes_t * v,
const char * raw )

Feed one raw cell.

Cells that hold no number are ignored.

Parameters
vThe tally.
rawThe raw cell text.

◆ log_votes_reset()

void log_votes_reset ( log_votes_t * v)

Clear the tally.

Parameters
vThe tally.

◆ log_votes_result()

log_conv_t log_votes_result ( const log_votes_t * v,
log_ambig_t fallback,
bool * confident,
bool * conflict )

Decide the convention from the tally.

Parameters
vThe tally.
fallbackThe default for a wholly ambiguous file.
confidentset false when nothing proved anything and the fallback had to decide.
conflictset true when both conventions were proven, which means the file itself is inconsistent; the caller reports it rather than silently picking the winner.
Returns
The convention.