pub struct StaticWindow { /* private fields */ }Expand description
The initialization window, accumulated one sample at a time.
Eskf::initialize needs only what the window reduces to: sums,
peaks, the span, the barometer’s scatter and the first and last GNSS velocity. So the samples
are folded in as they arrive rather than buffered. A buffered window at the default
Initialization::min_duration is more RAM than a Cortex-M0 has; this one is the same size at
any rate and any length (measured).
let mut filter = Eskf::default();
let config = *filter.config();
let mut window = StaticWindow::new();
let mut i = 0;
while !window.is_long_enough(&config) {
i += 1;
// A refused sample leaves the window as it was: drop it and go on.
if window.push(sample(i)).is_err() {
continue;
}
// Waiting for stillness: a vehicle that moved starts the wait over.
if !window.is_at_rest(&config) {
window = StaticWindow::new();
}
}
assert_eq!(filter.initialize(&window)?, Alignment::Static);Waiting for stillness restarts the window, as above; is_at_rest says
why it cannot slide. A buffered slice can, at the cost of the buffer, and
try_extend or TryFrom builds a window from one.
Each push costs a few dozen floating-point operations, some in f64
(counted). On a core with no floating-point unit every one is a library call, inside the loop
that is already reading the IMU: the price of not buffering, paid only until the window commits.
level_scatter and BaroReadings say why their sums need f64. For the averages it is
precaution: summing a few thousand readings near γ in f32 costs on the order of 10⁻⁵ rad of
tilt, against a 0.02 rad prior.
Implementations§
Source§impl StaticWindow
impl StaticWindow
Sourcepub fn push(&mut self, sample: StaticSample) -> Result<(), SampleRefusal>
pub fn push(&mut self, sample: StaticSample) -> Result<(), SampleRefusal>
Add one sample to the window.
Every check initialization makes on raw input is here, so that nothing downstream
can be handed an unusable interval, a clock that does not run forward, or a value
that is not a number. A refused sample leaves the window as it was, so the caller
can drop it and go on, or start over. The clock is checked because it starts at the
last sample’s time: a window timed anywhere but forward would start it somewhere the
samples do not describe, and the first predict would coast the whole flight so far.
§Errors
SampleRefusal, naming which check the sample failed.
Sourcepub fn try_extend(
&mut self,
samples: impl IntoIterator<Item = StaticSample>,
) -> Result<(), SampleRefusal>
pub fn try_extend( &mut self, samples: impl IntoIterator<Item = StaticSample>, ) -> Result<(), SampleRefusal>
Push every sample samples yields, in order, stopping at the first one refused.
The samples before it stay in the window, and the refused one and those after it are
not taken. A caller building one start from a buffer discards the window on an error,
as TryFrom does: a sample the buffer cannot supply says it is not the window the
caller thought it was.
§Errors
As push, for the first sample refused.
Sourcepub fn is_at_rest(&self, config: &Config) -> bool
pub fn is_at_rest(&self, config: &Config) -> bool
Whether the window’s peak motion so far is within the tolerances of a still one.
The test Alignment::Static and the barometric reference rest on, asked of the
peaks alone, so it is cheap after every sample where
alignment_of measures the whole window. An empty window
has not moved.
A window only grows, and peaks do not fall, so once this is false it stays false: an application waiting for stillness starts a new window rather than sliding this one.
Sourcepub fn is_long_enough(&self, config: &Config) -> bool
pub fn is_long_enough(&self, config: &Config) -> bool
Whether the window holds a sample and spans Initialization::min_duration: the
condition a collection loop runs until.
The sample is asked for as well as the span, so that a min_duration of zero still
ends the loop on a window Eskf::initialize accepts rather
than on an empty one it refuses.
Sourcepub fn span(&self) -> Seconds
pub fn span(&self) -> Seconds
The time the window’s samples integrated: the sum of their angle intervals, and what
Initialization::min_duration is checked against.
Integrated time rather than the distance between the first and last timestamps, because it is what the window observed: a window a logger dropped samples from covers the samples it kept, and a stillness test over time nobody measured would pass on nothing. The angle interval rather than the velocity one for no reason but one: the two cover the same sample and differ only by when a driver closed each integral.
Summed in f64: in f32, 100 intervals of 20 ms come to 1.9999987 s, and a window of
exactly min_duration would read as too short by the rounding. That is
data/flight.csv’s window: summed in f32, it starts short.
Sourcepub fn noise(&self, config: &Config) -> Option<WindowNoise>
pub fn noise(&self, config: &Config) -> Option<WindowNoise>
The white noise this window measured on each sensor, a floor under the noise the
filter should be told; WindowNoise says why a floor. Equation (8″).
On the window rather than the filter, because the figure is for building a
Config and so comes before any filter exists; it reads the same tolerances
is_at_rest does. Measured and
reported, never applied: a figure changes the filter only if the caller writes it into
the Config (GOALS.md differentiator 7, where derived is not adaptive).
None unless the window is at rest, the test the barometric reference rests on, and
spans WindowNoise::MIN_READINGS of the WindowNoise::BLOCKs the IMU’s densities
are read over: a moving window’s scatter is its motion. The barometer’s figure takes
the same count of its own readings.
Trait Implementations§
Source§impl Clone for StaticWindow
impl Clone for StaticWindow
Source§impl Debug for StaticWindow
impl Debug for StaticWindow
Source§impl Default for StaticWindow
impl Default for StaticWindow
Source§impl TryFrom<&[StaticSample]> for StaticWindow
A window from samples already buffered, through StaticWindow::try_extend: the first
refused refuses the window. An empty slice is an empty window, which
Eskf::initialize refuses as InitError::NoSamples.
impl TryFrom<&[StaticSample]> for StaticWindow
A window from samples already buffered, through StaticWindow::try_extend: the first
refused refuses the window. An empty slice is an empty window, which
Eskf::initialize refuses as InitError::NoSamples.
Source§type Error = SampleRefusal
type Error = SampleRefusal
Source§fn try_from(samples: &[StaticSample]) -> Result<Self, SampleRefusal>
fn try_from(samples: &[StaticSample]) -> Result<Self, SampleRefusal>
Auto Trait Implementations§
impl Freeze for StaticWindow
impl RefUnwindSafe for StaticWindow
impl Send for StaticWindow
impl Sync for StaticWindow
impl Unpin for StaticWindow
impl UnsafeUnpin for StaticWindow
impl UnwindSafe for StaticWindow
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.