summaryrefslogtreecommitdiff
path: root/core/signal_processor.py
diff options
context:
space:
mode:
authorChristian Kolset <christian.kolset@gmail.com>2026-06-03 12:09:08 -0600
committerChristian Kolset <christian.kolset@gmail.com>2026-06-03 12:09:08 -0600
commit0fa8f9652f6deedb792966ec2b1632cb143c54f6 (patch)
tree324e7521e044050433699835798e3325455eb64d /core/signal_processor.py
parent23c669b3c6fb3bc805887cef00d6c37756e5c41e (diff)
Improve Channels window: split layout + derivative w.r.t. selector
- Split PipelineTab into Physical/Virtual sections via QSplitter - Each section has independent scroll area, header, and add controls - Add "With Respect To" control for derivative/second_derivative virtual channels: Time (dy/dt) or Channel (dy/dx) - When Channel selected, x variable picker appears for any physical or derived channel - signal_processor computes dy/dx using channel inputs when deriv_wrt=channel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'core/signal_processor.py')
-rw-r--r--core/signal_processor.py53
1 files changed, 35 insertions, 18 deletions
diff --git a/core/signal_processor.py b/core/signal_processor.py
index 723de66..df80dbb 100644
--- a/core/signal_processor.py
+++ b/core/signal_processor.py
@@ -442,28 +442,45 @@ class SignalProcessor(QObject):
return dc._fn(inputs, t)
elif dc.kind == "derivative":
- # derivative of source[0]
- src = dc.sources[0] if dc.sources else None
- if src is None: return None
+ if not dc.sources: return None
state = dc.params.setdefault("_state", {})
- prev_t = state.get("t"); prev_v = state.get("v")
- state["t"] = t; state["v"] = inputs[0]
- if prev_t is None or t == prev_t: return 0.0
- return (inputs[0] - prev_v) / (t - prev_t)
+ prev_v = state.get("v")
+ state["v"] = inputs[0]
+ if dc.params.get("deriv_wrt") == "channel":
+ if len(inputs) < 2: return None
+ prev_x = state.get("x")
+ state["x"] = inputs[1]
+ if prev_x is None or inputs[1] == prev_x: return 0.0
+ return (inputs[0] - prev_v) / (inputs[1] - prev_x)
+ else:
+ prev_t = state.get("t")
+ state["t"] = t
+ if prev_t is None or t == prev_t: return 0.0
+ return (inputs[0] - prev_v) / (t - prev_t)
elif dc.kind == "second_derivative":
- # second derivative — derivative of derivative
- src = dc.sources[0] if dc.sources else None
- if src is None: return None
+ if not dc.sources: return None
state = dc.params.setdefault("_state", {})
- prev_t = state.get("t"); prev_v = state.get("v")
- cur_vel = 0.0
- if prev_t is not None and t != prev_t:
- cur_vel = (inputs[0] - prev_v) / (t - prev_t)
- prev_vel = state.get("vel", 0.0)
- state["t"] = t; state["v"] = inputs[0]; state["vel"] = cur_vel
- if prev_t is None or t == prev_t: return 0.0
- return (cur_vel - prev_vel) / (t - prev_t)
+ prev_v = state.get("v")
+ if dc.params.get("deriv_wrt") == "channel":
+ if len(inputs) < 2: return None
+ prev_x = state.get("x")
+ cur_vel = 0.0
+ if prev_x is not None and inputs[1] != prev_x:
+ cur_vel = (inputs[0] - prev_v) / (inputs[1] - prev_x)
+ prev_vel = state.get("vel", 0.0)
+ state["x"] = inputs[1]; state["v"] = inputs[0]; state["vel"] = cur_vel
+ if prev_x is None or inputs[1] == prev_x: return 0.0
+ return (cur_vel - prev_vel) / (inputs[1] - prev_x)
+ else:
+ prev_t = state.get("t")
+ cur_vel = 0.0
+ if prev_t is not None and t != prev_t:
+ cur_vel = (inputs[0] - prev_v) / (t - prev_t)
+ prev_vel = state.get("vel", 0.0)
+ state["t"] = t; state["v"] = inputs[0]; state["vel"] = cur_vel
+ if prev_t is None or t == prev_t: return 0.0
+ return (cur_vel - prev_vel) / (t - prev_t)
elif dc.kind == "power":
# V * I