pub struct AffineTransform { /* private fields */ }Expand description
Invertible i32 affine map y' = (y * gain + offset) / scale.
Arithmetic uses i64 intermediates and nearest, ties-away-from-zero
rounding. Identity (modulo rounding when |scale| ≠ 1) is
gain = scale and offset = 0. A negative gain or scale is allowed
and flips sense.
This type never reads NVM, wraps a transfer, or writes
TransferMetadata. AffineCalibration contains one of these and
delegates its gain/offset/scale arithmetic here.
§Inverse
unapply solves y = (y' * scale - offset) / gain with
the same rounding. Because a zero gain collapses every input onto
offset / scale, new rejects gain == 0 rather than
deferring the failure to unapply.
§Numerical scope
For any i32 y, gain, and offset, the product/sum
y * gain + offset always fits in i64; the same holds for
y' * scale - offset on the inverse path. Both directions still report
AffineOverflow::Overflow when the rounded result does not fit i32.
Both directions round, so unapply(apply(y)) is bounded rather than
exact. A transform that compresses the scale cannot restore what the
forward quantization discarded. At i32 extremes, inverse rounding of a
forward result can land just outside i32, in which case unapply
reports overflow.
Implementations§
Source§impl AffineTransform
impl AffineTransform
Sourcepub const fn new(
gain: i32,
offset: i32,
scale: i32,
) -> Result<Self, AffineTransformError>
pub const fn new( gain: i32, offset: i32, scale: i32, ) -> Result<Self, AffineTransformError>
Construct an invertible affine transform.
§Errors
Returns AffineTransformError::ZeroScale if scale == 0, or
AffineTransformError::ZeroGain if gain == 0. A zero gain maps
every input onto the single value offset / scale and has no inverse.
Examples found in repository?
23pub fn runtime_api_smoke(code: u16, sample: u32) -> (i32, u16, u32, bool) {
24 let transfer = PiecewiseLinearTransfer::new(
25 &SMOKE_INPUTS,
26 &SMOKE_OUTPUTS,
27 MonotonicDirection::Increasing,
28 );
29 let calibrated = match AffineCalibration::new(transfer, 1_001, 0, 1_000) {
30 Ok(value) => value,
31 Err(_) => unreachable!(),
32 };
33 let physical = calibrated.convert(code).unwrap_or_default();
34 let inverse = calibrated.invert(physical).unwrap_or_default();
35
36 let affine = match AffineTransform::new(1_001, 0, 1_000) {
37 Ok(value) => value,
38 Err(_) => unreachable!(),
39 };
40 let _ = affine
41 .apply(physical)
42 .and_then(|value| affine.unapply(value));
43
44 let mut average = MovingAverage::<u32, 1>::new();
45 let filtered = match average.update(sample) {
46 FilterOutput::Ready(value) => value,
47 FilterOutput::WarmingUp { .. } => unreachable!(),
48 };
49 let mut latch = Hysteresis::<u32>::new(100, 200);
50 (physical, inverse, filtered, latch.update(filtered))
51}Sourcepub fn apply(&self, value: i32) -> Result<i32, AffineOverflow>
pub fn apply(&self, value: i32) -> Result<i32, AffineOverflow>
Apply y' = (y * gain + offset) / scale.
Rounding is nearest, ties away from zero.
§Errors
Returns AffineOverflow::Overflow when the rounded result does not
fit in i32.
Examples found in repository?
23pub fn runtime_api_smoke(code: u16, sample: u32) -> (i32, u16, u32, bool) {
24 let transfer = PiecewiseLinearTransfer::new(
25 &SMOKE_INPUTS,
26 &SMOKE_OUTPUTS,
27 MonotonicDirection::Increasing,
28 );
29 let calibrated = match AffineCalibration::new(transfer, 1_001, 0, 1_000) {
30 Ok(value) => value,
31 Err(_) => unreachable!(),
32 };
33 let physical = calibrated.convert(code).unwrap_or_default();
34 let inverse = calibrated.invert(physical).unwrap_or_default();
35
36 let affine = match AffineTransform::new(1_001, 0, 1_000) {
37 Ok(value) => value,
38 Err(_) => unreachable!(),
39 };
40 let _ = affine
41 .apply(physical)
42 .and_then(|value| affine.unapply(value));
43
44 let mut average = MovingAverage::<u32, 1>::new();
45 let filtered = match average.update(sample) {
46 FilterOutput::Ready(value) => value,
47 FilterOutput::WarmingUp { .. } => unreachable!(),
48 };
49 let mut latch = Hysteresis::<u32>::new(100, 200);
50 (physical, inverse, filtered, latch.update(filtered))
51}Sourcepub fn unapply(&self, value: i32) -> Result<i32, AffineOverflow>
pub fn unapply(&self, value: i32) -> Result<i32, AffineOverflow>
Undo y' = (y * gain + offset) / scale, recovering y.
Solves y = (y' * scale - offset) / gain with the same nearest,
ties-away rounding. Offset is subtracted in i64 rather than negated
as i32, so offset == i32::MIN is representable.
Both directions round, so unapply(apply(y)) is bounded rather than
exact: a transform that compresses the scale cannot restore what the
forward quantization discarded.
§Errors
Returns AffineOverflow::Overflow when the rounded result does not
fit in i32.
Examples found in repository?
23pub fn runtime_api_smoke(code: u16, sample: u32) -> (i32, u16, u32, bool) {
24 let transfer = PiecewiseLinearTransfer::new(
25 &SMOKE_INPUTS,
26 &SMOKE_OUTPUTS,
27 MonotonicDirection::Increasing,
28 );
29 let calibrated = match AffineCalibration::new(transfer, 1_001, 0, 1_000) {
30 Ok(value) => value,
31 Err(_) => unreachable!(),
32 };
33 let physical = calibrated.convert(code).unwrap_or_default();
34 let inverse = calibrated.invert(physical).unwrap_or_default();
35
36 let affine = match AffineTransform::new(1_001, 0, 1_000) {
37 Ok(value) => value,
38 Err(_) => unreachable!(),
39 };
40 let _ = affine
41 .apply(physical)
42 .and_then(|value| affine.unapply(value));
43
44 let mut average = MovingAverage::<u32, 1>::new();
45 let filtered = match average.update(sample) {
46 FilterOutput::Ready(value) => value,
47 FilterOutput::WarmingUp { .. } => unreachable!(),
48 };
49 let mut latch = Hysteresis::<u32>::new(100, 200);
50 (physical, inverse, filtered, latch.update(filtered))
51}Trait Implementations§
Source§impl Clone for AffineTransform
impl Clone for AffineTransform
Source§fn clone(&self) -> AffineTransform
fn clone(&self) -> AffineTransform
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for AffineTransform
Source§impl Debug for AffineTransform
impl Debug for AffineTransform
impl Eq for AffineTransform
Source§impl PartialEq for AffineTransform
impl PartialEq for AffineTransform
impl StructuralPartialEq for AffineTransform
Auto Trait Implementations§
impl Freeze for AffineTransform
impl RefUnwindSafe for AffineTransform
impl Send for AffineTransform
impl Sync for AffineTransform
impl Unpin for AffineTransform
impl UnsafeUnpin for AffineTransform
impl UnwindSafe for AffineTransform
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> CheckedAs for T
impl<T> CheckedAs for T
Source§fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
Source§impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
Source§fn checked_cast_from(src: Src) -> Option<Dst>
fn checked_cast_from(src: Src) -> Option<Dst>
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.