modewise_operations/
cwt.rs1use modewise_cwt::Cwt;
2use modewise_pipeline::{
3 Artifact, ArtifactKind, Inputs, Operation, OperationDescriptor, Outputs, Port, resolve_params,
4};
5use modewise_spectral::{FrequencyGrid, GridScale, TimeFrequencyTransform};
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9
10use crate::input;
11
12#[derive(Serialize, Deserialize, JsonSchema)]
13#[serde(deny_unknown_fields)]
14struct Params {
15 f_min: f64,
17 f_max: f64,
19 n_bins: usize,
21 #[serde(default = "six")]
23 omega0: f64,
24 #[serde(default)]
26 rectify: bool,
27}
28
29fn six() -> f64 {
30 6.0
31}
32
33impl Params {
34 fn method(&self) -> Result<Cwt, String> {
36 let grid = FrequencyGrid::new(self.f_min, self.f_max, self.n_bins, GridScale::Log)
37 .map_err(|e| e.to_string())?;
38 Cwt::new(grid, self.omega0, self.rectify).map_err(|e| e.to_string())
39 }
40}
41
42pub struct CwtOperation;
44
45impl Operation for CwtOperation {
46 fn summary(&self) -> &'static str {
47 "Morlet continuous wavelet transform power on log-spaced frequencies, with a cone of influence (docs/time-frequency.md)."
48 }
49
50 fn params_schema(&self) -> serde_json::Value {
51 modewise_pipeline::schema_of::<Params>()
52 }
53
54 fn descriptor(&self) -> OperationDescriptor {
55 OperationDescriptor {
56 name: "cwt",
57 version: env!("CARGO_PKG_VERSION"),
58 inputs: &[Port {
59 name: "signal",
60 kinds: &[ArtifactKind::Series],
61 }],
62 outputs: &[Port {
63 name: "power",
64 kinds: &[ArtifactKind::TimeFrequency],
65 }],
66 }
67 }
68
69 fn resolve(&self, params: Value) -> Result<Value, String> {
70 let (typed, resolved) = resolve_params::<Params>(params)?;
71 typed.method()?;
72 Ok(resolved)
73 }
74
75 fn run(&self, params: &Value, inputs: &Inputs) -> Result<Outputs, String> {
76 let p: Params = serde_json::from_value(params.clone()).map_err(|e| e.to_string())?;
77 let Artifact::Series(signal) = input(inputs, "signal")? else {
78 return Err("input \"signal\" must be a series".into());
79 };
80 let map = p.method()?.transform(signal).map_err(|e| e.to_string())?;
81 Ok(Outputs::from([(
82 "power".into(),
83 Artifact::TimeFrequency(map),
84 )]))
85 }
86}