modewise_operations/
reconstruct.rs

1use modewise_pipeline::{
2    Artifact, ArtifactKind, Inputs, Operation, OperationDescriptor, Outputs, Port, resolve_params,
3};
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8use crate::input;
9
10#[derive(Serialize, Deserialize, JsonSchema)]
11#[serde(deny_unknown_fields)]
12struct Params {
13    /// Mode indices to sum, counted from the lowest frequency.
14    modes: Vec<usize>,
15}
16
17/// Sums chosen modes back into a signal, for example to keep one frequency band.
18pub struct ReconstructOperation;
19
20impl Operation for ReconstructOperation {
21    fn summary(&self) -> &'static str {
22        "Sums chosen modes of a decomposition back into a series, for example to keep one band."
23    }
24
25    fn params_schema(&self) -> serde_json::Value {
26        modewise_pipeline::schema_of::<Params>()
27    }
28
29    fn descriptor(&self) -> OperationDescriptor {
30        OperationDescriptor {
31            name: "reconstruct",
32            version: env!("CARGO_PKG_VERSION"),
33            inputs: &[Port {
34                name: "modes",
35                kinds: &[ArtifactKind::Decomposition],
36            }],
37            outputs: &[Port {
38                name: "signal",
39                kinds: &[ArtifactKind::Series],
40            }],
41        }
42    }
43
44    fn resolve(&self, params: Value) -> Result<Value, String> {
45        resolve_params::<Params>(params).map(|(_, resolved)| resolved)
46    }
47
48    fn run(&self, params: &Value, inputs: &Inputs) -> Result<Outputs, String> {
49        let p: Params = serde_json::from_value(params.clone()).map_err(|e| e.to_string())?;
50        let Artifact::Decomposition(modes) = input(inputs, "modes")? else {
51            return Err("input \"modes\" must be a decomposition".into());
52        };
53        let signal = modes.reconstruct(&p.modes).map_err(|e| e.to_string())?;
54        Ok(Outputs::from([("signal".into(), Artifact::Series(signal))]))
55    }
56}