use crate::common::{validate_inputs, validate_options};
pub use crate::indicator_types::TIndicatorState;
pub use crate::indicators::roofingfilter::multiplier;
use crate::indicators::{
highpass::output_length as hp_output_length,
roofingfilter::{min_data as rf_min_data, output_length as rf_output_length, State as RfSate},
};
use crate::ring_buffer::fixed_single_buffer::FixedRingBuffer;
use crate::types::{DisplayGroup, DisplayType, IndicatorError, IndicatorType, Info};
use serde::{Deserialize, Serialize};
pub const INPUTS_WIDTH: usize = 1;
pub(crate) const C0: f64 = 0.0962;
pub(crate) const C1: f64 = 0.5769;
pub(crate) const C2: f64 = -0.5769;
pub(crate) const C3: f64 = -0.0962;
#[inline(always)]
pub(crate) fn ht_kernel(buf: &FixedRingBuffer<f64, 7>, gain: f64) -> (f64, f64) {
let q = (C0.mul_add(buf[0], C1 * buf[2]) + C2.mul_add(buf[4], C3 * buf[6])) * gain;
(buf[3], q)
}
pub const OPTIONS_WIDTH: usize = 2;
#[cfg(feature = "simd_assets")]
pub use crate::indicators::simd_indicators::hilberttransform_simd::indicator_by_assets;
#[cfg(feature = "simd_options")]
pub use crate::indicators::simd_indicators::hilberttransform_simd::indicator_by_options;
#[cfg(feature = "simd_assets")]
pub mod by_assets {
pub use crate::indicators::simd_indicators::hilberttransform_simd::indicator_by_assets as indicator;
}
#[cfg(feature = "simd_options")]
pub mod by_options {
pub use crate::indicators::simd_indicators::hilberttransform_simd::indicator_by_options as indicator;
}
pub const INFO: Info = Info {
name: "hilberttransform",
indicator_type: IndicatorType::Math,
full_name: "Hilbert Transform",
inputs: &["real"],
options: &["ss_period, hp_period"],
outputs: &["in_phase", "quadrature"],
optional_outputs: &["roofing", "highpass"],
display_groups: &[DisplayGroup {
offset: None,
id: "hilberttransform",
label: "Hilbert Transform",
display_type: DisplayType::Indicator,
outputs: &["in_phase", "quadrature", "roofing", "highpass"],
}],
};
#[derive(Serialize, Deserialize)]
pub struct State {
pub buffer: FixedRingBuffer<f64, 7>,
pub rf_state: RfSate,
}
impl State {
pub fn init_state(
real: &[f64],
periods: (usize, usize),
multipliers: ((f64, f64, f64), (f64, f64)),
optional_outputs: (&mut [f64], &mut [f64]),
) -> State {
let (rf_line, hp_line) = optional_outputs;
let mut buffer = FixedRingBuffer::new();
let mut rf_state = RfSate::init_state(real, periods, multipliers, hp_line);
let mut i = periods.0.max(periods.1);
while buffer.len() < buffer.capacity() {
let (rf, hp) = rf_state.calc(real[i], multipliers);
buffer.push(rf);
crate::init_store_optional_outputs!(i, real.len(),
rf_line => rf,
hp_line => hp
);
i += 1;
}
Self { buffer, rf_state }
}
#[inline(always)]
pub fn calc_transform(&mut self, real: f64) -> (f64, f64) {
self.buffer.push(real);
ht_kernel(&self.buffer, 1.0)
}
#[inline(always)]
pub unsafe fn calc_transform_unchecked(&mut self, real: f64) -> (f64, f64) {
self.buffer.push_unchecked(real);
ht_kernel(&self.buffer, 1.0)
}
#[inline(always)]
pub fn calc(
&mut self,
real: f64,
multipliers: ((f64, f64, f64), (f64, f64)),
) -> (f64, f64, f64, f64) {
let (rf, hp) = self.rf_state.calc(real, multipliers);
let (i, q) = self.calc_transform(rf);
(i, q, rf, hp)
}
#[inline(always)]
pub unsafe fn calc_unchecked(
&mut self,
real: f64,
multipliers: ((f64, f64, f64), (f64, f64)),
) -> (f64, f64, f64, f64) {
let (rf, hp) = self.rf_state.calc(real, multipliers);
let (i, q) = self.calc_transform_unchecked(rf);
(i, q, rf, hp)
}
}
#[derive(Serialize, Deserialize)]
pub struct IndicatorState {
state: State,
multipliers: ((f64, f64, f64), (f64, f64)),
}
impl IndicatorState {
pub fn new(state: State, multipliers: ((f64, f64, f64), (f64, f64))) -> Self {
Self { state, multipliers }
}
}
impl TIndicatorState<INPUTS_WIDTH> for IndicatorState {
fn batch_indicator(
&mut self,
inputs: &[&[f64]; INPUTS_WIDTH],
optional_outputs: Option<&[bool]>,
) -> Result<Vec<Vec<f64>>, IndicatorError> {
validate_inputs(inputs, 1)?;
let (mut p_line, mut q_line, (mut rf_line, mut hp_line)) = {
let len = inputs[0].len();
(
crate::uninit_vec!(f64, len),
crate::uninit_vec!(f64, len),
crate::init_optional_outputs_eff!(
optional_outputs, &[false, false],
rf_line: len,
hp_line: len
),
)
};
cycle(
inputs[0],
&mut self.state,
self.multipliers,
&mut p_line,
&mut q_line,
(&mut rf_line, &mut hp_line),
);
Ok(vec![p_line, q_line, rf_line, hp_line])
}
}
pub fn min_data(options: &[f64]) -> usize {
rf_min_data(options) + 7
}
pub fn output_length(data_len: usize, options: &[f64]) -> usize {
data_len - min_data(options) + 1
}
pub fn indicator(
inputs: &[&[f64]; INPUTS_WIDTH],
options: &[f64; OPTIONS_WIDTH],
optional_outputs: Option<&[bool]>,
) -> Result<(Vec<Vec<f64>>, IndicatorState), IndicatorError> {
validate_options(options)?;
let periods = (options[0] as usize, options[1] as usize);
let multipliers = multiplier(periods);
validate_inputs(inputs, min_data(options))?;
let (mut p_line, mut q_line, (mut rf_line, mut hp_line)) = {
let len = inputs[0].len();
let capacity = output_length(len, options);
(
crate::uninit_vec!(f64, capacity),
crate::uninit_vec!(f64, capacity),
crate::init_optional_outputs_eff!(
optional_outputs, &[false, false],
rf_line: rf_output_length(len, options),
hp_line: hp_output_length(len, &[periods.1 as f64])
),
)
};
let mut state = State::init_state(
inputs[0],
periods,
multipliers,
(&mut rf_line, &mut hp_line),
);
let optional_outputs = {
let offset = crate::slice_outputs_start!(p_line.len(), rf_line, hp_line);
(&mut rf_line[offset.0..], &mut hp_line[offset.1..])
};
let real = {
let from = periods.0.max(periods.1) + 7;
&inputs[0][from..]
};
cycle(
real,
&mut state,
multipliers,
&mut p_line,
&mut q_line,
optional_outputs,
);
Ok((
vec![p_line, q_line, rf_line, hp_line],
IndicatorState::new(state, multipliers),
))
}
fn cycle(
real: &[f64],
state: &mut State,
multipliers: ((f64, f64, f64), (f64, f64)),
p_line: &mut [f64],
q_line: &mut [f64],
optional_outputs: (&mut [f64], &mut [f64]),
) {
let (rf_line, hp_line) = optional_outputs;
let (has_optional, want_rf, want_hp) = crate::calc_want_flags!(rf_line, hp_line);
for i in 0..real.len() {
let (p, q, rf, hp) = unsafe { state.calc_unchecked(*real.get_unchecked(i), multipliers) };
unsafe {
*p_line.get_unchecked_mut(i) = p;
*q_line.get_unchecked_mut(i) = q;
};
if has_optional {
crate::store_optional_outputs!(i,
want_rf, rf_line => rf,
want_hp, hp_line => hp
);
}
}
}