This should have been multiple commits, but it isn't. Sue me. This
change has two main goals:
1. Sample feedback at the beginning of the control loop iteration so
that it is always up-to-date when we are computing the actual drive
outputs. This means we're doing twice the amount of communication
with the labjack (previously, setting the output and reading the
feedback was done with a singe command). However, this makes the
loop structure much more standard, and it means that we aren't
constantly operating on feedback that is stale by one loop
interval.
2. Sample feedback into a (configurable size) buffer. This lets us
operate on aggregated feedback rather than on a single instantaneous
data point. Right now, feedback is computed as a moving average,
which acts as a rudimentary low-pass filter, reducing spurious
single-loop actions due to feedback spikes or other noise. However,
the other reason to aggregate some backwards data is that it will
let us do automatic stall detection in a simple way, although that
is not currently done.
On an unloaded rotator, this fixes the rapid relay toggling without
needing to implement debouncing.
An additional configuration validation should probably be added: the
rotator rotates in azimuth about 6 deg per second. If the control loop
speed is too slow or the controller angle tolerance is small enough,
then the controller will never be able settle (imagine that the loop
interval is 1 second. That means the rotator will move approximately 6
degrees every loop iteration, so it will always overshoot.
There's not really any analogous concept for azimuth. This is for a
specific piece of hardware, so there's no real point in making it more
generic. This is respected at the 180 degree point as well, for
software that can handle flipped rotation configurations.
This doesn't currently play well with the elevation offset setting,
which should be applied after this clamping operation. Either this
needs to be moved to the API layer or (more appropriately) the input
range validation needs to move in the controller.