use crate::error::WasmError;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn butter(order: u32, cutoff: f64, btype: &str, fs: f64) -> Result<JsValue, JsValue> {
if order == 0 {
return Err(
WasmError::InvalidParameter("Filter order must be positive".to_string()).into(),
);
}
if fs <= 0.0 {
return Err(
WasmError::InvalidParameter("Sampling frequency must be positive".to_string()).into(),
);
}
let nyquist = fs / 2.0;
let normalized_cutoff = cutoff / nyquist;
if normalized_cutoff <= 0.0 || normalized_cutoff >= 1.0 {
return Err(WasmError::InvalidParameter(
"Normalized cutoff frequency must be in the range (0, 1) exclusive".to_string(),
)
.into());
}
let filter_type_str = match btype {
"lowpass" | "low" | "lp" => "lowpass",
"highpass" | "high" | "hp" => "highpass",
"bandpass" | "band" | "bp" => "bandpass",
"bandstop" | "bs" | "notch" => "bandstop",
other => {
return Err(WasmError::InvalidParameter(format!(
"Unknown filter type '{}'. Use 'lowpass', 'highpass', 'bandpass', or 'bandstop'",
other
))
.into());
}
};
let (b_coeffs, a_coeffs) =
scirs2_signal::filter::butter(order as usize, normalized_cutoff, filter_type_str).map_err(
|e| WasmError::ComputationError(format!("Butterworth design failed: {}", e)),
)?;
let result = serde_json::json!({
"b": b_coeffs,
"a": a_coeffs,
});
serde_wasm_bindgen::to_value(&result)
.map_err(|e| WasmError::SerializationError(e.to_string()).into())
}
#[wasm_bindgen]
pub fn convolve(a: &[f64], b: &[f64], mode: &str) -> Result<Vec<f64>, JsValue> {
if a.is_empty() || b.is_empty() {
return Err(
WasmError::InvalidParameter("Input arrays must not be empty".to_string()).into(),
);
}
validate_conv_mode(mode)?;
scirs2_signal::convolve(a, b, mode)
.map_err(|e| WasmError::ComputationError(format!("Convolution failed: {}", e)).into())
}
#[wasm_bindgen]
pub fn correlate(a: &[f64], b: &[f64], mode: &str) -> Result<Vec<f64>, JsValue> {
if a.is_empty() || b.is_empty() {
return Err(
WasmError::InvalidParameter("Input arrays must not be empty".to_string()).into(),
);
}
validate_conv_mode(mode)?;
scirs2_signal::correlate(a, b, mode)
.map_err(|e| WasmError::ComputationError(format!("Correlation failed: {}", e)).into())
}
#[wasm_bindgen]
pub fn hanning(n: usize) -> Result<Vec<f64>, JsValue> {
if n == 0 {
return Err(
WasmError::InvalidParameter("Window length must be positive".to_string()).into(),
);
}
scirs2_signal::window::hann(n, true)
.map_err(|e| WasmError::ComputationError(format!("Hanning window failed: {}", e)).into())
}
#[wasm_bindgen]
pub fn hamming(n: usize) -> Result<Vec<f64>, JsValue> {
if n == 0 {
return Err(
WasmError::InvalidParameter("Window length must be positive".to_string()).into(),
);
}
scirs2_signal::window::hamming(n, true)
.map_err(|e| WasmError::ComputationError(format!("Hamming window failed: {}", e)).into())
}
#[wasm_bindgen]
pub fn blackman(n: usize) -> Result<Vec<f64>, JsValue> {
if n == 0 {
return Err(
WasmError::InvalidParameter("Window length must be positive".to_string()).into(),
);
}
scirs2_signal::window::blackman(n, true)
.map_err(|e| WasmError::ComputationError(format!("Blackman window failed: {}", e)).into())
}
#[wasm_bindgen]
pub fn firwin(n: usize, cutoff: f64, fs: f64) -> Result<Vec<f64>, JsValue> {
if n < 3 {
return Err(
WasmError::InvalidParameter("Number of taps must be at least 3".to_string()).into(),
);
}
if fs <= 0.0 {
return Err(
WasmError::InvalidParameter("Sampling frequency must be positive".to_string()).into(),
);
}
let nyquist = fs / 2.0;
let normalized_cutoff = cutoff / nyquist;
if normalized_cutoff <= 0.0 || normalized_cutoff >= 1.0 {
return Err(WasmError::InvalidParameter(
"Normalized cutoff frequency must be in the range (0, 1) exclusive".to_string(),
)
.into());
}
scirs2_signal::filter::firwin(n, normalized_cutoff, "hamming", true)
.map_err(|e| WasmError::ComputationError(format!("FIR filter design failed: {}", e)).into())
}
#[wasm_bindgen]
pub fn lfilter(b: &[f64], a: &[f64], x: &[f64]) -> Result<Vec<f64>, JsValue> {
if b.is_empty() {
return Err(WasmError::InvalidParameter(
"Numerator coefficients must not be empty".to_string(),
)
.into());
}
if a.is_empty() {
return Err(WasmError::InvalidParameter(
"Denominator coefficients must not be empty".to_string(),
)
.into());
}
if x.is_empty() {
return Err(
WasmError::InvalidParameter("Input signal must not be empty".to_string()).into(),
);
}
scirs2_signal::filter::lfilter(b, a, x)
.map_err(|e| WasmError::ComputationError(format!("lfilter failed: {}", e)).into())
}
fn validate_conv_mode(mode: &str) -> Result<(), JsValue> {
match mode {
"full" | "same" | "valid" => Ok(()),
other => Err(WasmError::InvalidParameter(format!(
"Unknown mode '{}'. Use 'full', 'same', or 'valid'",
other
))
.into()),
}
}