modewise_operations/
stft.rs1use modewise_pipeline::{
2 Artifact, ArtifactKind, Inputs, Operation, OperationDescriptor, Outputs, Port, resolve_params,
3};
4use modewise_spectral::TimeFrequencyTransform;
5use modewise_stft::Stft;
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 window_len: usize,
17 hop: usize,
19}
20
21pub struct StftOperation;
22
23impl Operation for StftOperation {
24 fn summary(&self) -> &'static str {
25 "Short-time Fourier transform power with a periodic Hann window."
26 }
27
28 fn params_schema(&self) -> serde_json::Value {
29 modewise_pipeline::schema_of::<Params>()
30 }
31
32 fn descriptor(&self) -> OperationDescriptor {
33 OperationDescriptor {
34 name: "stft",
35 version: env!("CARGO_PKG_VERSION"),
36 inputs: &[Port {
37 name: "signal",
38 kinds: &[ArtifactKind::Series],
39 }],
40 outputs: &[Port {
41 name: "power",
42 kinds: &[ArtifactKind::TimeFrequency],
43 }],
44 }
45 }
46
47 fn resolve(&self, params: Value) -> Result<Value, String> {
48 let (typed, resolved) = resolve_params::<Params>(params)?;
49 Stft::new(typed.window_len, typed.hop).map_err(|e| e.to_string())?;
50 Ok(resolved)
51 }
52
53 fn run(&self, params: &Value, inputs: &Inputs) -> Result<Outputs, String> {
54 let p: Params = serde_json::from_value(params.clone()).map_err(|e| e.to_string())?;
55 let method = Stft::new(p.window_len, p.hop).map_err(|e| e.to_string())?;
56 let Artifact::Series(signal) = input(inputs, "signal")? else {
57 return Err("input \"signal\" must be a series".into());
58 };
59 let map = method.transform(signal).map_err(|e| e.to_string())?;
60 Ok(Outputs::from([(
61 "power".into(),
62 Artifact::TimeFrequency(map),
63 )]))
64 }
65}