|
|
|
Sponsored Link •
|
|
Advertisement
|
Signal, which is a simple typedef for a built-in integral
type. Some variation in the signal is to be expected, depending on
location, atmospheric conditions and so on; but if a more substantial
change occurs then the new value must be stored in RAM and written to
flash memory. That's what this function does.
/**
* Handle a signal update.
* If a change in the signal is detected, store the new value
* and write it to flash.
*/
void
signalUpdate(Signal update, Signal & stored)
{
int const tolerance = 10;
bool const changed
= update > stored + tolerance
||
update < stored - tolerance;
if (changed)
{
flashWriteSignal(update);
stored = update;
}
}
Maybe this function isn't perfect—perhaps the detection of change and
subsequent action should be subfunctions, and certainly the numeric literal
requires explanation—but it looks sound enough. In fact, it is so
apparently sensible that the Talk-To-Me engineers are surprised when field
tests reveal problems.
signalUpdate()
function is to blame.
int const tolerance = 20; // Used to be 10, but this caused
// too many calls to flashWriteSignal()
The changes are made, a patch is issued, field tests resume. Unfortunately,
the problem persists—if anything, it seems worse.
There is only one way out:
if (changed)
{
// flashWriteSignal(update); Avoid excessive writes to flash
// Works around driver problems.
stored = update;
}
This rather drastic step means that the Talk-To-Me will lose settings when
power-cycled, which isn't ideal, but at least a show-stopping defect can be
downgraded to medium priority. Flaky hardware drivers get the blame. Field
tests continue. The signalUpdate() function is left in this rather messy
state.
void
signalUpdate(Signal update, Signal & stored)
{
int const tolerance = 20; // Used to be 10
if (signalsDifferent(update, stored, tolerance))
{
// flashWriteSignal(update); Avoid excessive writes to flash
stored = update;
}
}
In a new file, then, we have:
....
/**
* @return True if the input signals differ by more than the supplied
* tolerance, false otherwise.
*/
bool
signalsDifferent(Signal s1, Signal s2, int tol)
{
return
s1 > s2 + tol || s1 < s2 - tol;
}
The associated unit tests read:
void testSignalsDifferentBoundaryCase()
{
assert(!signalsDifferent(0, 0, 0));
}
void testSignalsDifferentTypicalSignal()
{
assert(!signalsDifferent(70, 80, 10));
assert(!signalsDifferent(80, 70, 10));
}
void testSignalsDifferentPoorSignal()
{
assert(!signalsDifferent(10, 10, 20));
}
testSignalsDifferentPoorSignal() test case fails. At last the real
problem is evident.
For the purposes of this article I have been withholding a critical
detail: namely which built-in integral type a Signal is.
Inspecting the relevant header file shows that:
/**
* Typedef used to represent signal quality as a percentage.
* 100 represents perfect signal, 0 no signal.
*/
typedef unsigned Signal;
So, the expression in signalsDifferent(10, 10, 20) evaluates:
10u > 10u + 20 || 10u < 10u - 20
Now, when you subtract an int from an unsigned
both values are promoted to unsigned and the result is
unsigned. So, 10u - 20 is a very big unsigned
number. Our expression is therefore equivalent to:
false || true
which is of course true.
In fact, signalsDifferent() returns true
whenever the second input Signal dips below the tolerance.
So, if the signal quality drops below 20%, flash memory is written to
every time the driver is polled. Hence the bug reported during field
tests.
/**
* Typedef used to represent signal quality as a percentage.
* 100 represents perfect signal, 0 no signal.
*/
typedef int Signal;
The code worked just fine back then. Unfortunately the C++ type system
offered no help when someone decided that an unsigned was
more appropriate for a value in the range [0, 100]. No compiler warnings
were, in this case, bad news.
Perhaps it would be more accurate to say that the C type system failed to help since this is one of those areas where C++ finds itself compromised and constrained by its C heritage. The integral promotion rules are well and truly entrenched in the common core of the two languages.
|
Sponsored Links
|