Add RTA hold and fall mode
This commit is contained in:
+28
-2
@@ -207,6 +207,7 @@ impl RtaPeakHold {
|
||||
sample_rate: u32,
|
||||
config: &PhoenixRtaConfig,
|
||||
) {
|
||||
let previous_clock = self.sample_clock;
|
||||
self.sample_clock = self.sample_clock.saturating_add(advanced_samples as u64);
|
||||
let mode = normalize_rta_peak_hold_mode(&config.rta_peak_hold_mode);
|
||||
let hold_samples = (f64::from(sample_rate.max(1))
|
||||
@@ -242,7 +243,7 @@ impl RtaPeakHold {
|
||||
};
|
||||
if incoming >= self.values[index] {
|
||||
self.values[index] = incoming;
|
||||
if mode == "auto" {
|
||||
if mode == "auto" || mode == "fall" {
|
||||
self.expires_at_sample[index] = self.sample_clock.saturating_add(hold_samples);
|
||||
}
|
||||
} else if mode == "auto" && self.sample_clock >= self.expires_at_sample[index] {
|
||||
@@ -250,6 +251,14 @@ impl RtaPeakHold {
|
||||
// value. There is deliberately no second, continuous peak decay.
|
||||
self.values[index] = incoming;
|
||||
self.expires_at_sample[index] = self.sample_clock.saturating_add(hold_samples);
|
||||
} else if mode == "fall" && self.sample_clock >= self.expires_at_sample[index] {
|
||||
let decay_start = previous_clock.max(self.expires_at_sample[index]);
|
||||
let decay_samples = self.sample_clock.saturating_sub(decay_start);
|
||||
let decay_db = config.rta_peak_decay_db_per_second * decay_samples as f32
|
||||
/ sample_rate.max(1) as f32;
|
||||
self.values[index] = (self.values[index] - decay_db)
|
||||
.max(incoming)
|
||||
.max(RTA_PEAK_FLOOR_DB);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1944,8 +1953,10 @@ fn normalize_rta_integration(value: &str) -> &'static str {
|
||||
fn normalize_rta_peak_hold_mode(value: &str) -> &'static str {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"off" => "off",
|
||||
"fall" => "fall",
|
||||
"manual" => "manual",
|
||||
_ => "auto",
|
||||
"auto" => "auto",
|
||||
_ => "fall",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2317,6 +2328,7 @@ mod tests {
|
||||
#[test]
|
||||
fn rta_peak_hold_has_timed_manual_off_and_reset_semantics() {
|
||||
let mut config = PhoenixRtaConfig::default();
|
||||
config.rta_peak_hold_mode = "auto".to_string();
|
||||
config.rta_peak_hold_seconds = 0.1;
|
||||
let mut hold = RtaPeakHold::new(1);
|
||||
|
||||
@@ -2329,6 +2341,20 @@ mod tests {
|
||||
"timed hold must release directly to current"
|
||||
);
|
||||
|
||||
config.rta_peak_hold_mode = "fall".to_string();
|
||||
config.rta_peak_decay_db_per_second = 20.0;
|
||||
hold.update(&[-10.0], 1, 1_000, &config);
|
||||
hold.update(&[-40.0], 100, 1_000, &config);
|
||||
assert_eq!(
|
||||
hold.values[0], -10.0,
|
||||
"fall mode must first observe the hold time"
|
||||
);
|
||||
hold.update(&[-40.0], 500, 1_000, &config);
|
||||
assert!(
|
||||
(hold.values[0] - -20.0).abs() < 1.0e-6,
|
||||
"fall must use the configured dB/s rate"
|
||||
);
|
||||
|
||||
config.rta_peak_hold_mode = "manual".to_string();
|
||||
hold.update(&[-20.0], 1, 1_000, &config);
|
||||
hold.update(&[-5.0], 1, 1_000, &config);
|
||||
|
||||
+3
-1
@@ -70,6 +70,7 @@ pub struct PhoenixRtaConfig {
|
||||
pub layout: String,
|
||||
pub rta_peak_hold_mode: String,
|
||||
pub rta_peak_hold_seconds: f32,
|
||||
pub rta_peak_decay_db_per_second: f32,
|
||||
pub rta_peak_reset_token: u64,
|
||||
pub input_offset_db_l: f32,
|
||||
pub input_offset_db_r: f32,
|
||||
@@ -103,8 +104,9 @@ impl Default for PhoenixRtaConfig {
|
||||
tau_slow: 1.0,
|
||||
integration: "fast".to_string(),
|
||||
layout: "rtw".to_string(),
|
||||
rta_peak_hold_mode: "auto".to_string(),
|
||||
rta_peak_hold_mode: "fall".to_string(),
|
||||
rta_peak_hold_seconds: 2.5,
|
||||
rta_peak_decay_db_per_second: 20.0,
|
||||
rta_peak_reset_token: 0,
|
||||
input_offset_db_l: -5.0,
|
||||
input_offset_db_r: -5.0,
|
||||
|
||||
+12
-3
@@ -414,8 +414,10 @@ fn normalize_rta_config(mut config: PhoenixRtaConfig) -> PhoenixRtaConfig {
|
||||
.as_str()
|
||||
{
|
||||
"off" => "off".to_string(),
|
||||
"fall" => "fall".to_string(),
|
||||
"manual" => "manual".to_string(),
|
||||
_ => "auto".to_string(),
|
||||
"auto" => "auto".to_string(),
|
||||
_ => "fall".to_string(),
|
||||
};
|
||||
let requested_hold = if config.rta_peak_hold_seconds.is_finite() {
|
||||
config.rta_peak_hold_seconds
|
||||
@@ -431,6 +433,11 @@ fn normalize_rta_config(mut config: PhoenixRtaConfig) -> PhoenixRtaConfig {
|
||||
} else {
|
||||
requested_hold.clamp(0.0, 30.0)
|
||||
};
|
||||
config.rta_peak_decay_db_per_second = if config.rta_peak_decay_db_per_second.is_finite() {
|
||||
config.rta_peak_decay_db_per_second.clamp(1.0, 60.0)
|
||||
} else {
|
||||
20.0
|
||||
};
|
||||
let offset_l = if config.input_offset_db_l.is_finite() {
|
||||
config.input_offset_db_l
|
||||
} else {
|
||||
@@ -792,16 +799,18 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rta_peak_hold_config_is_normalized_without_decay_parameter() {
|
||||
fn rta_peak_hold_config_is_normalized() {
|
||||
let rtw = normalize_rta_config(PhoenixRtaConfig {
|
||||
rta_peak_hold_mode: "MANUAL".to_string(),
|
||||
rta_peak_hold_seconds: 3.7,
|
||||
rta_peak_decay_db_per_second: 25.0,
|
||||
rta_peak_reset_token: 12,
|
||||
layout: "rtw".to_string(),
|
||||
..PhoenixRtaConfig::default()
|
||||
});
|
||||
assert_eq!(rtw.rta_peak_hold_mode, "manual");
|
||||
assert_eq!(rtw.rta_peak_hold_seconds, 4.0);
|
||||
assert_eq!(rtw.rta_peak_decay_db_per_second, 25.0);
|
||||
assert_eq!(rtw.rta_peak_reset_token, 12);
|
||||
|
||||
let extension = normalize_rta_config(PhoenixRtaConfig {
|
||||
@@ -810,7 +819,7 @@ mod tests {
|
||||
layout: "iec".to_string(),
|
||||
..PhoenixRtaConfig::default()
|
||||
});
|
||||
assert_eq!(extension.rta_peak_hold_mode, "auto");
|
||||
assert_eq!(extension.rta_peak_hold_mode, "fall");
|
||||
assert_eq!(extension.rta_peak_hold_seconds, 30.0);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user