Skip to main content

sva_samples/
error.rs

1// Concern: declares the refusals the sampled half reaches, each with its code | Non-concern: the closed-form crate's own codes (sva-formula), rendering a diagnostic (sva-engine) | IO: none
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub enum SampleError {
5    HopOutsideCola {
6        window: usize,
7        hop: usize,
8    },
9    WindowNotPowerOfTwo {
10        window: usize,
11    },
12    WidthMismatch {
13        left: usize,
14        right: usize,
15    },
16    ChannelOutOfRange {
17        k: usize,
18        width: usize,
19    },
20    /// A grid sized from the rate and the fundamental, past what one call may allocate.
21    GridTooLarge {
22        model: &'static str,
23        nodes: usize,
24        ceiling: usize,
25    },
26}
27
28/// What a collapse refuses, per FORMAT 16.3.
29#[derive(Clone, Debug, PartialEq)]
30pub enum CollapseError {
31    NoHorizon,
32    EmptyBand {
33        ceiling: f64,
34        lowest: f64,
35    },
36    SingularInCt {
37        at: f64,
38        order: i32,
39    },
40    NotEvaluable(&'static str),
41    NestedSeries {
42        depth: usize,
43        terms: usize,
44        bound: usize,
45    },
46    LeftAlgebra(&'static str),
47}
48
49impl CollapseError {
50    pub fn code(&self) -> &'static str {
51        match self {
52            CollapseError::NoHorizon => "collapse.no_horizon",
53            CollapseError::EmptyBand { .. } => "collapse.empty_band",
54            CollapseError::SingularInCt { .. } => "collapse.singular_in_ct",
55            CollapseError::NotEvaluable(_) => "collapse.not_evaluable",
56            CollapseError::NestedSeries { .. } => "collapse.series_nesting",
57            CollapseError::LeftAlgebra(_) => "cast.left_algebra",
58        }
59    }
60}
61
62/// One repair per refusal: a window is what a missing horizon wants, and no window brings a
63/// line back under a ceiling.
64impl CollapseError {
65    pub fn help(&self) -> &'static str {
66        match self {
67            CollapseError::NoHorizon => "give the observation a window with --from and --to",
68            CollapseError::EmptyBand { ceiling, .. }
69                if *ceiling < sva_formula::AUDIBLE_CEILING_HZ =>
70            {
71                "this rate's own half is the ceiling: raise --sample-rate past twice the \
72                 lowest line"
73            }
74            CollapseError::EmptyBand { .. } => {
75                "the profile's 20 kHz ceiling is what no rate raises; read it with \
76                 `--as lines`, or bring the line into the band"
77            }
78            CollapseError::SingularInCt { .. } => {
79                "write it inside a convolution, or sample the closed form it multiplies"
80            }
81            CollapseError::NestedSeries { .. } => {
82                "write fewer levels, or a narrower band for the terms to clear"
83            }
84            CollapseError::NotEvaluable(_) | CollapseError::LeftAlgebra(_) => {
85                "write the subterm inside sample(...) to leave A deliberately"
86            }
87        }
88    }
89}
90
91impl std::fmt::Display for CollapseError {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        match self {
94            CollapseError::NoHorizon => {
95                write!(
96                    f,
97                    "a closed form collapses against a horizon. give --from and --to"
98                )
99            }
100            CollapseError::EmptyBand { ceiling, lowest } => write!(
101                f,
102                "every line is at or above the {ceiling} Hz ceiling, the lowest at {lowest} Hz"
103            ),
104            CollapseError::SingularInCt { at, order } => write!(
105                f,
106                "a singularity of order {order} at t = {at} has no value on the grid"
107            ),
108            CollapseError::NotEvaluable(what) => {
109                write!(f, "{what} has no value at a point")
110            }
111            CollapseError::NestedSeries {
112                depth,
113                terms,
114                bound,
115            } => write!(
116                f,
117                "a series nested {depth} deep takes {terms} terms at one instant, past the \
118                 {bound} one expansion holds"
119            ),
120            CollapseError::LeftAlgebra(clause) => write!(f, "the closed form left A. {clause}"),
121        }
122    }
123}
124
125impl SampleError {
126    pub fn code(&self) -> &'static str {
127        match self {
128            SampleError::HopOutsideCola { .. } => "samples.hop_outside_cola",
129            SampleError::WindowNotPowerOfTwo { .. } => "samples.window_not_power_of_two",
130            SampleError::WidthMismatch { .. } => "type.width_mismatch",
131            SampleError::ChannelOutOfRange { .. } => "samples.channel_out_of_range",
132            SampleError::GridTooLarge { .. } => "samples.grid_too_large",
133        }
134    }
135}
136
137impl std::fmt::Display for SampleError {
138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139        match self {
140            SampleError::HopOutsideCola { window, hop } => write!(
141                f,
142                "a window of {window} at a hop of {hop} does not overlap-add to a constant. \
143                 write a hop that divides the window"
144            ),
145            SampleError::WindowNotPowerOfTwo { window } => {
146                write!(f, "a window of {window} is not a power of two")
147            }
148            SampleError::WidthMismatch { left, right } => write!(
149                f,
150                "{left} components meet {right}; an elementwise operation needs equal widths, \
151                 or one side mono"
152            ),
153            SampleError::ChannelOutOfRange { k, width } => {
154                write!(f, "component {k} of a value {width} wide")
155            }
156            SampleError::GridTooLarge {
157                model,
158                nodes,
159                ceiling,
160            } => write!(
161                f,
162                "`{model}` sizes a {nodes}-node grid at this rate, past the {ceiling} one \
163                 call may hold. ask for a higher fundamental, or a lower --sample-rate"
164            ),
165        }
166    }
167}