P3 wave 1: extract measurement simulator/calibration/commands + CONTRACT
src/simulation/simulator.js random-walk generator (was simulateInput inline)
src/calibration/calibrator.js calibrate + isStable + evaluateRepeatability,
using generalFunctions/stats. NB: isStable
tautology preserved verbatim — see
OPEN_QUESTIONS.md 2026-05-10 for the bug.
src/commands/ registry + handlers (canonical names from start)
CONTRACT.md inputs/outputs/events surface
77 basic tests pass (62 pre-refactor + 15 new across the three new files).
specificClass.js / nodeClass.js untouched — integration is P3 wave 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
60
src/simulation/simulator.js
Normal file
60
src/simulation/simulator.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Simulator — random-walk driver for the measurement input.
|
||||
*
|
||||
* Lifted verbatim from Measurement.simulateInput. The orchestrator decides
|
||||
* what to do with the returned value (originally written to `inputValue`),
|
||||
* so this module owns nothing but the walk and its bounds.
|
||||
*/
|
||||
class Simulator {
|
||||
constructor({ config, logger } = {}) {
|
||||
if (!config || !config.scaling) {
|
||||
throw new Error('Simulator requires { config.scaling }');
|
||||
}
|
||||
this.config = config;
|
||||
this.logger = logger || { warn() {}, info() {}, debug() {}, error() {} };
|
||||
|
||||
const s = config.scaling;
|
||||
this.inputRange = Math.abs(s.inputMax - s.inputMin);
|
||||
this.processRange = Math.abs(s.absMax - s.absMin);
|
||||
this.simValue = 0;
|
||||
}
|
||||
|
||||
step() {
|
||||
const s = this.config.scaling;
|
||||
const sign = Math.random() < 0.5 ? -1 : 1;
|
||||
let maxStep;
|
||||
|
||||
if (s.enabled) {
|
||||
// Step size scales with the live input window; fall back to 1 so a
|
||||
// collapsed range still wanders instead of freezing at zero.
|
||||
maxStep = this.inputRange > 0 ? this.inputRange * 0.05 : 1;
|
||||
if (this.simValue < s.inputMin || this.simValue > s.inputMax) {
|
||||
this.logger.warn(`Simulated value ${this.simValue} is outside of input range constraining between min=${s.inputMin} and max=${s.inputMax}`);
|
||||
this.simValue = _constrain(this.simValue, s.inputMin, s.inputMax);
|
||||
}
|
||||
} else {
|
||||
maxStep = this.processRange > 0 ? this.processRange * 0.05 : 1;
|
||||
if (this.simValue < s.absMin || this.simValue > s.absMax) {
|
||||
this.logger.warn(`Simulated value ${this.simValue} is outside of abs range constraining between min=${s.absMin} and max=${s.absMax}`);
|
||||
this.simValue = _constrain(this.simValue, s.absMin, s.absMax);
|
||||
}
|
||||
}
|
||||
|
||||
this.simValue += sign * Math.random() * maxStep;
|
||||
return this.simValue;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.simValue = 0;
|
||||
}
|
||||
|
||||
get current() {
|
||||
return this.simValue;
|
||||
}
|
||||
}
|
||||
|
||||
function _constrain(v, lo, hi) {
|
||||
return Math.min(Math.max(v, lo), hi);
|
||||
}
|
||||
|
||||
module.exports = Simulator;
|
||||
Reference in New Issue
Block a user