gcode_parse.c: review decfloat_to_int() again.
Finally, significant simplifications worked out while keeping micrometer accuracy for up to 4290 mm.
This commit is contained in:
parent
a6e2a10f10
commit
34ab76fe6e
|
|
@ -32,54 +32,43 @@ GCODE_COMMAND next_target __attribute__ ((__section__ (".bss")));
|
|||
decfloat_to_int() is the weakest subject to variable overflow. For evaluation, we assume a build room of +-1000 mm and STEPS_PER_MM_x between 1.000 and 4096. Accordingly for metric units:
|
||||
|
||||
df->mantissa: +-0..1048075 (20 bit - 500 for rounding)
|
||||
df->exponent: 0, 2, 3 or 4 (10 bit)
|
||||
multiplicand / denominator: 20..4194303 / 1000 (22 bit - 10 bit) or
|
||||
0..4095 / 1 (12 bit - 0 bit)
|
||||
df->exponent: 0, 2, 3, 4 or 5 (10 bit)
|
||||
multiplicand: 1000 (10 bit)
|
||||
|
||||
imperial units:
|
||||
|
||||
df->mantissa: +-0..32267 (15 bit - 500 for rounding)
|
||||
df->exponent: 0, 2, 3 or 4 (10 bit)
|
||||
multiplicand: 1..105000 (17 bit)
|
||||
denominator: 1 or 10 ( 4 bit)
|
||||
df->exponent: 0, 2, 3, 4 or 5 (10 bit)
|
||||
multiplicand: 25400 (15 bit)
|
||||
*/
|
||||
// accordingly:
|
||||
#define DECFLOAT_EXP_MAX 4
|
||||
#define DECFLOAT_MANT_MM_MAX 1048075
|
||||
#define DECFLOAT_MANT_IN_MAX 32267
|
||||
// decfloat_to_int() can handle a bit more:
|
||||
#define DECFLOAT_EXP_MAX 3 // more is pointless, as 1 um is our presision
|
||||
// (2^^32 - 1) / multiplicand - powers[DECFLOAT_EXP_MAX] / 2 =
|
||||
// 4294967295 / 1000 - 5000 =
|
||||
#define DECFLOAT_MANT_MM_MAX 4289967 // = 4290 mm
|
||||
// 4294967295 / 25400 - 5000 =
|
||||
#define DECFLOAT_MANT_IN_MAX 164093 // = 164 inches = 4160 mm
|
||||
|
||||
/*
|
||||
utility functions
|
||||
*/
|
||||
extern const uint32_t powers[]; // defined in sermsg.c
|
||||
|
||||
// TODO: When the new approach to pass distances in micrometers instead of step
|
||||
// numbers stays, this should be replaced by a simplified version.
|
||||
/// convert a floating point input value into an integer with appropriate scaling.
|
||||
/// \param *df pointer to floating point structure that holds fp value to convert
|
||||
/// \param multiplicand multiply by this amount during conversion to integer
|
||||
/// \param divide_by_1000 divide by 1000 during conversion to integer
|
||||
///
|
||||
/// lots of work has been done in exploring this function's limitations in terms of overflow and rounding
|
||||
/// this work may not be finished
|
||||
static int32_t decfloat_to_int(decfloat *df, uint32_t multiplicand, uint8_t divide_by_1000) {
|
||||
static int32_t decfloat_to_int(decfloat *df, uint32_t multiplicand) {
|
||||
uint32_t r = df->mantissa;
|
||||
uint8_t e = df->exponent;
|
||||
uint32_t rnew1, rnew2;
|
||||
|
||||
// e=1 means we've seen a decimal point but no digits after it, and e=2 means we've seen a decimal point with one digit so it's too high by one if not zero
|
||||
if (e)
|
||||
e--;
|
||||
|
||||
if (divide_by_1000) {
|
||||
rnew1 = r * (multiplicand / 1000);
|
||||
rnew2 = (r * (multiplicand % 1000) + (1000 / 2)) / 1000;
|
||||
r = rnew1 + rnew2;
|
||||
}
|
||||
else {
|
||||
r *= multiplicand;
|
||||
}
|
||||
|
||||
r *= multiplicand;
|
||||
if (e)
|
||||
r = (r + powers[e] / 2) / powers[e];
|
||||
|
||||
|
|
@ -111,42 +100,42 @@ void gcode_parse_char(uint8_t c) {
|
|||
break;
|
||||
case 'X':
|
||||
if (next_target.option_inches)
|
||||
next_target.target.X = decfloat_to_int(&read_digit, 25400, 1);
|
||||
next_target.target.X = decfloat_to_int(&read_digit, 25400);
|
||||
else
|
||||
next_target.target.X = decfloat_to_int(&read_digit, 1000, 0);
|
||||
next_target.target.X = decfloat_to_int(&read_digit, 1000);
|
||||
if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
|
||||
serwrite_int32(next_target.target.X);
|
||||
break;
|
||||
case 'Y':
|
||||
if (next_target.option_inches)
|
||||
next_target.target.Y = decfloat_to_int(&read_digit, 25400, 1);
|
||||
next_target.target.Y = decfloat_to_int(&read_digit, 25400);
|
||||
else
|
||||
next_target.target.Y = decfloat_to_int(&read_digit, 1000, 0);
|
||||
next_target.target.Y = decfloat_to_int(&read_digit, 1000);
|
||||
if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
|
||||
serwrite_int32(next_target.target.Y);
|
||||
break;
|
||||
case 'Z':
|
||||
if (next_target.option_inches)
|
||||
next_target.target.Z = decfloat_to_int(&read_digit, 25400, 1);
|
||||
next_target.target.Z = decfloat_to_int(&read_digit, 25400);
|
||||
else
|
||||
next_target.target.Z = decfloat_to_int(&read_digit, 1000, 0);
|
||||
next_target.target.Z = decfloat_to_int(&read_digit, 1000);
|
||||
if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
|
||||
serwrite_int32(next_target.target.Z);
|
||||
break;
|
||||
case 'E':
|
||||
if (next_target.option_inches)
|
||||
next_target.target.E = decfloat_to_int(&read_digit, 25400, 1);
|
||||
next_target.target.E = decfloat_to_int(&read_digit, 25400);
|
||||
else
|
||||
next_target.target.E = decfloat_to_int(&read_digit, 1000, 0);
|
||||
next_target.target.E = decfloat_to_int(&read_digit, 1000);
|
||||
if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
|
||||
serwrite_uint32(next_target.target.E);
|
||||
break;
|
||||
case 'F':
|
||||
// just use raw integer, we need move distance and n_steps to convert it to a useful value, so wait until we have those to convert it
|
||||
if (next_target.option_inches)
|
||||
next_target.target.F = decfloat_to_int(&read_digit, 25400, 1);
|
||||
next_target.target.F = decfloat_to_int(&read_digit, 25400);
|
||||
else
|
||||
next_target.target.F = decfloat_to_int(&read_digit, 1, 0);
|
||||
next_target.target.F = decfloat_to_int(&read_digit, 1);
|
||||
if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
|
||||
serwrite_uint32(next_target.target.F);
|
||||
break;
|
||||
|
|
@ -155,17 +144,17 @@ void gcode_parse_char(uint8_t c) {
|
|||
// cosmetically this should be done in the temperature section,
|
||||
// but it takes less code, less memory and loses no precision if we do it here instead
|
||||
if ((next_target.M == 104) || (next_target.M == 109) || (next_target.M == 140))
|
||||
next_target.S = decfloat_to_int(&read_digit, 4, 0);
|
||||
next_target.S = decfloat_to_int(&read_digit, 4);
|
||||
// if this is heater PID stuff, multiply by PID_SCALE because we divide by PID_SCALE later on
|
||||
else if ((next_target.M >= 130) && (next_target.M <= 132))
|
||||
next_target.S = decfloat_to_int(&read_digit, PID_SCALE, 0);
|
||||
next_target.S = decfloat_to_int(&read_digit, PID_SCALE);
|
||||
else
|
||||
next_target.S = decfloat_to_int(&read_digit, 1, 0);
|
||||
next_target.S = decfloat_to_int(&read_digit, 1);
|
||||
if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
|
||||
serwrite_uint16(next_target.S);
|
||||
break;
|
||||
case 'P':
|
||||
next_target.P = decfloat_to_int(&read_digit, 1, 0);
|
||||
next_target.P = decfloat_to_int(&read_digit, 1);
|
||||
if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
|
||||
serwrite_uint16(next_target.P);
|
||||
break;
|
||||
|
|
@ -175,12 +164,12 @@ void gcode_parse_char(uint8_t c) {
|
|||
serwrite_uint8(next_target.T);
|
||||
break;
|
||||
case 'N':
|
||||
next_target.N = decfloat_to_int(&read_digit, 1, 0);
|
||||
next_target.N = decfloat_to_int(&read_digit, 1);
|
||||
if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
|
||||
serwrite_uint32(next_target.N);
|
||||
break;
|
||||
case '*':
|
||||
next_target.checksum_read = decfloat_to_int(&read_digit, 1, 0);
|
||||
next_target.checksum_read = decfloat_to_int(&read_digit, 1);
|
||||
if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
|
||||
serwrite_uint8(next_target.checksum_read);
|
||||
break;
|
||||
|
|
@ -276,7 +265,7 @@ void gcode_parse_char(uint8_t c) {
|
|||
default:
|
||||
// can't do ranges in switch..case, so process actual digits here.
|
||||
if (c >= '0' && c <= '9') {
|
||||
if (read_digit.exponent < DECFLOAT_EXP_MAX &&
|
||||
if (read_digit.exponent < DECFLOAT_EXP_MAX + 1 &&
|
||||
((next_target.option_inches == 0 &&
|
||||
read_digit.mantissa < DECFLOAT_MANT_MM_MAX) ||
|
||||
(next_target.option_inches &&
|
||||
|
|
|
|||
Loading…
Reference in New Issue