-
Notifications
You must be signed in to change notification settings - Fork 4
fix(sim): PushButton/Monoflop Reset handling; InputRef output mirroring #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,7 +46,12 @@ macro_rules! passthrough_io_block { | |
| // --------------------------------------------------------------------------- | ||
|
|
||
| /// Input reference — proxy that forwards named inputs to the block graph. | ||
| /// I→Q (digital) and AI→AQ (analog). | ||
| /// | ||
| /// A ref is fed on exactly one side (I or AI) but the Miniserver mirrors the | ||
| /// signal on BOTH outputs: consumers routinely read Q from an AI-fed ref | ||
| /// (r50 corpus: `ref.AI <- mem.AQ` with `monoflop.InputTrigger: ref.Q`). | ||
| /// Q is the digital view (non-zero → 1), AQ the analog value; the unfed | ||
| /// side idles at 0, so combining the two inputs is lossless. | ||
| #[derive(Clone, Copy)] | ||
| pub struct InputRef; | ||
|
|
||
|
|
@@ -60,7 +65,9 @@ impl Block for InputRef { | |
| ) -> Vec<Signal> { | ||
| let i = inputs.first().copied().unwrap_or(0.0); | ||
| let ai = inputs.get(1).copied().unwrap_or(0.0); | ||
| vec![i, ai] | ||
| let q = if i != 0.0 || ai != 0.0 { 1.0 } else { 0.0 }; | ||
| let aq = if ai != 0.0 { ai } else { i }; | ||
| vec![q, aq] | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This behavior is not implemented in |
||
| } | ||
|
|
||
| fn block_type(&self) -> &str { | ||
|
|
@@ -260,12 +267,12 @@ mod tests { | |
| use super::*; | ||
| use crate::blocks::create_block; | ||
| #[test] | ||
| fn input_ref_passthrough() { | ||
| fn input_ref_mirrors_fed_side_to_both_outputs() { | ||
| let mut block = InputRef; | ||
| // I=42, AI=0 → Q=42, AQ=0 | ||
| assert_eq!(block.eval(&[42.0], &[], 0.0, &[]), vec![42.0, 0.0]); | ||
| // I=0, AI=99 → Q=0, AQ=99 | ||
| assert_eq!(block.eval(&[0.0, 99.0], &[], 0.0, &[]), vec![0.0, 99.0]); | ||
| // I=42, AI unfed → Q=1 (digital view), AQ=42 | ||
| assert_eq!(block.eval(&[42.0], &[], 0.0, &[]), vec![1.0, 42.0]); | ||
| // I unfed, AI=99 → Q=1, AQ=99 (AI-fed refs serve Q consumers) | ||
| assert_eq!(block.eval(&[0.0, 99.0], &[], 0.0, &[]), vec![1.0, 99.0]); | ||
| // empty → Q=0, AQ=0 | ||
| assert_eq!(block.eval(&[], &[], 0.0, &[]), vec![0.0, 0.0]); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -252,12 +252,18 @@ impl Block for PushButton { | |
| ) -> Vec<Signal> { | ||
| let trigger = inputs.first().copied().unwrap_or(0.0); | ||
| let force_on = inputs.get(1).copied().unwrap_or(0.0); | ||
| let reset = inputs.get(2).copied().unwrap_or(0.0); | ||
| let disable = inputs.get(3).copied().unwrap_or(0.0); | ||
| let prev_trigger = prev_inputs.first().copied().unwrap_or(0.0); | ||
| let previous = self.is_on; | ||
|
|
||
| if is_high(force_on) { | ||
| // WARNING: Assumed behavior — not validated against Miniserver. | ||
| // Assumption: Reset dominates On; InputDisable gates only the trigger. | ||
| if is_high(reset) { | ||
| self.is_on = false; | ||
| } else if is_high(force_on) { | ||
| self.is_on = true; | ||
| } else if !is_high(prev_trigger) && is_high(trigger) { | ||
| } else if !is_high(disable) && !is_high(prev_trigger) && is_high(trigger) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| self.is_on = !self.is_on; | ||
| } | ||
|
|
||
|
|
@@ -329,10 +335,20 @@ impl Block for PushButton2 { | |
| prev_inputs: &[Signal], | ||
| ) -> Vec<Signal> { | ||
| let trigger = inputs.first().copied().unwrap_or(0.0); | ||
| let reset = inputs.get(2).copied().unwrap_or(0.0); | ||
| let disable = inputs.get(3).copied().unwrap_or(0.0); | ||
| let prev_trigger = prev_inputs.first().copied().unwrap_or(0.0); | ||
| let dc_window = params.first().copied().unwrap_or(0.4).max(0.0); | ||
| let previous = self.is_on; | ||
| let rising = !is_high(prev_trigger) && is_high(trigger); | ||
| // WARNING: Assumed behavior — not validated against Miniserver. | ||
| // Assumption: Reset dominates and cancels a pending double-click; | ||
| // InputDisable gates only the trigger. | ||
| if is_high(reset) { | ||
| self.is_on = false; | ||
| self.awaiting_second = false; | ||
| } | ||
| let rising = | ||
| !is_high(reset) && !is_high(disable) && !is_high(prev_trigger) && is_high(trigger); | ||
| let mut double_click = false; | ||
|
|
||
| if self.awaiting_second { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -574,7 +574,7 @@ fn block_signature( | |
| &["Q1", "Q2", "Q3", "Q4", "AQ"], | ||
| &["Time", "V1", "V2", "V3", "V4"], | ||
| ), | ||
| "Monoflop" => (&["InputTrigger"], &["Q"], &["Time"]), | ||
| "Monoflop" => (&["InputTrigger", "Reset"], &["Q"], &["Time"]), | ||
| "Minmax" => ( | ||
| &["Input1", "Input2", "Input3", "Input4"], | ||
| &["AQmin", "AQmax"], | ||
|
|
@@ -605,12 +605,12 @@ fn block_signature( | |
| &["TimeHigh", "TimeLow"], | ||
| ), | ||
| "PushButton" | "PushButton2" | "PushButton2Sel" => ( | ||
| &["InputTrigger", "On"], | ||
| &["InputTrigger", "On", "Reset", "InputDisable"], | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add regression coverage for the newly exposed connectors. The current |
||
| &["Q", "Qoff", "Qon", "AQ"], | ||
| &["Min", "Max"], | ||
| ), | ||
| "PushButtonSel" => ( | ||
| &["InputTrigger", "InputPos", "Reset"], | ||
| &["InputTrigger", "InputPos", "Reset", "InputDisable"], | ||
| &["AQ"], | ||
| &["Min", "Max", "Step", "Repeat", "Def"], | ||
| ), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ai != 0.0uses the runtime value to infer which connector is fed. A connected analog input can legitimately produce0.0; ifIis also present, this then switchesAQto the digital value instead of preserving the analog zero. Please select the mirrored source from connector connectivity/presence rather than its current value, and add a regression case withAI = 0and nonzeroI.