node-red-contrib-signal-gen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # node-red-contrib-rpo-signal-gen
2
+
3
+ A Node-RED node: **Signal Generator**
4
+
5
+ Generates periodic waveforms (sine, square, sawtooth, triangle, noise) at a configurable frequency and amplitude.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install node-red-contrib-rpo-signal-gen
11
+ ```
12
+
13
+ Or via the Node-RED Palette Manager.
14
+
15
+ ## Inputs
16
+
17
+ - **payload** (string): `"start"`, `"stop"`, or `"reset"` to control generation
18
+
19
+ ## Outputs
20
+
21
+ - **Output 1**: Current waveform sample value
22
+
23
+ ## Configuration
24
+
25
+ | Parameter | Description |
26
+ |-----------|-------------|
27
+ | Waveform | sine, square, sawtooth, triangle, noise |
28
+ | Frequency (Hz) | Waveform frequency |
29
+ | Amplitude | Peak amplitude |
30
+ | Offset | DC offset |
31
+ | Interval (ms) | Output interval |
32
+ | Auto-start | Start on deploy |
33
+
34
+ ## License
35
+
36
+ MIT
@@ -0,0 +1,25 @@
1
+ {
2
+ "signal-gen": {
3
+ "label": "Signalgenerator",
4
+ "waveform": "Wellenform",
5
+ "waveform_opt": {
6
+ "sine": "Sinus",
7
+ "square": "Rechteck",
8
+ "sawtooth": "Sägezahn",
9
+ "triangle": "Dreieck",
10
+ "noise": "Rauschen"
11
+ },
12
+ "frequency": "Frequenz (Hz)",
13
+ "amplitude": "Amplitude",
14
+ "offset": "Offset",
15
+ "intervalMs": "Intervall (ms)",
16
+ "autoStart": "Auto-Start",
17
+ "tip": "Generiert periodische Wellenformen. 'start', 'stop' oder 'reset' senden zum Steuern.",
18
+ "input": {
19
+ "payload": "'start', 'stop' oder 'reset' zur Steuerung"
20
+ },
21
+ "output": {
22
+ "payload": "Aktueller Wellenformwert"
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "signal-gen": {
3
+ "label": "Signal Generator",
4
+ "waveform": "Waveform",
5
+ "waveform_opt": {
6
+ "sine": "Sine",
7
+ "square": "Square",
8
+ "sawtooth": "Sawtooth",
9
+ "triangle": "Triangle",
10
+ "noise": "Noise"
11
+ },
12
+ "frequency": "Frequency (Hz)",
13
+ "amplitude": "Amplitude",
14
+ "offset": "Offset",
15
+ "intervalMs": "Interval (ms)",
16
+ "autoStart": "Auto-start",
17
+ "tip": "Generates periodic waveforms. Send 'start', 'stop', or 'reset' to control.",
18
+ "input": {
19
+ "payload": "'start', 'stop', or 'reset' to control generation"
20
+ },
21
+ "output": {
22
+ "payload": "Current waveform value"
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "signal-gen": {
3
+ "label": "Generador de señal",
4
+ "waveform": "Forma de onda",
5
+ "waveform_opt": {
6
+ "sine": "Seno",
7
+ "square": "Cuadrada",
8
+ "sawtooth": "Diente de sierra",
9
+ "triangle": "Triángulo",
10
+ "noise": "Ruido"
11
+ },
12
+ "frequency": "Frecuencia (Hz)",
13
+ "amplitude": "Amplitud",
14
+ "offset": "Desplazamiento",
15
+ "intervalMs": "Intervalo (ms)",
16
+ "autoStart": "Auto-inicio",
17
+ "tip": "Genera formas de onda periódicas. Envíe 'start', 'stop' o 'reset' para controlar.",
18
+ "input": {
19
+ "payload": "'start', 'stop' o 'reset' para controlar la generación"
20
+ },
21
+ "output": {
22
+ "payload": "Valor actual de la forma de onda"
23
+ }
24
+ }
25
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "node-red-contrib-signal-gen",
3
+ "version": "1.0.0",
4
+ "description": "Signal generator - sine, square, sawtooth, triangle and random test waveforms",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/Wobi848/node-red-contrib-rpo-suite.git"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/Wobi848/node-red-contrib-rpo-suite/issues"
11
+ },
12
+ "files": [
13
+ "signal-gen.js",
14
+ "signal-gen.html",
15
+ "locales",
16
+ "examples"
17
+ ],
18
+ "keywords": [
19
+ "node-red",
20
+ "signal",
21
+ "generator",
22
+ "sine",
23
+ "waveform",
24
+ "test"
25
+ ],
26
+ "author": "sr.rpo",
27
+ "license": "MIT",
28
+ "node-red": {
29
+ "version": "1.0.0",
30
+ "nodes": {
31
+ "signal-gen": "signal-gen.js"
32
+ }
33
+ },
34
+ "engines": {
35
+ "node": ">=14.0.0"
36
+ },
37
+ "scripts": {
38
+ "test": "mocha \"test/**/*_spec.js\" --exit"
39
+ },
40
+ "devDependencies": {
41
+ "mocha": "^10.2.0",
42
+ "node-red": "^3.1.0",
43
+ "node-red-node-test-helper": "^0.3.6"
44
+ }
45
+ }
@@ -0,0 +1,100 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('signal-gen', {
3
+ category: 'rpo',
4
+ color: '#D4600A',
5
+ defaults: {
6
+ "name": {
7
+ "value": ""
8
+ },
9
+ "waveform": {
10
+ "value": "sine"
11
+ },
12
+ "amplitude": {
13
+ "value": 10
14
+ },
15
+ "offset": {
16
+ "value": 0
17
+ },
18
+ "period": {
19
+ "value": 60
20
+ },
21
+ "periodUnit": {
22
+ "value": "s"
23
+ },
24
+ "interval": {
25
+ "value": 1000
26
+ },
27
+ "decimals": {
28
+ "value": 2
29
+ },
30
+ "autoStart": {
31
+ "value": true
32
+ }
33
+ },
34
+ inputs: 1,
35
+ outputs: 1,
36
+ icon: 'font-awesome/fa-line-chart',
37
+ label: function() { return this.name || 'signal-gen'; },
38
+ labelStyle: function() { return this.name ? 'node_label_italic' : ''; }
39
+ });
40
+ </script>
41
+
42
+ <script type="text/html" data-template-name="signal-gen">
43
+ <div class="form-row">
44
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
45
+ <input type="text" id="node-input-name" placeholder="Name">
46
+ </div>
47
+ <div class="form-row">
48
+ <label for="node-input-waveform"><i class="fa fa-line-chart"></i> Waveform</label>
49
+ <select id="node-input-waveform" style="width:70%">
50
+ <option value="sine">Sine</option>
51
+ <option value="square">Square</option>
52
+ <option value="sawtooth">Sawtooth</option>
53
+ <option value="triangle">Triangle</option>
54
+ <option value="random">Random</option>
55
+ </select>
56
+ </div>
57
+ <div class="form-row">
58
+ <label for="node-input-amplitude"><i class="fa fa-arrows-v"></i> Amplitude</label>
59
+ <input type="number" id="node-input-amplitude" style="width:70%">
60
+ </div>
61
+ <div class="form-row">
62
+ <label for="node-input-offset"><i class="fa fa-arrows-h"></i> Offset</label>
63
+ <input type="number" id="node-input-offset" style="width:70%">
64
+ </div>
65
+ <div class="form-row">
66
+ <label for="node-input-period"><i class="fa fa-clock-o"></i> Period</label>
67
+ <input type="number" id="node-input-period" min="1" style="width:50%">
68
+ <select id="node-input-periodUnit" style="width:18%; margin-left:4px">
69
+ <option value="s">s</option>
70
+ <option value="min">min</option>
71
+ </select>
72
+ </div>
73
+ <div class="form-row">
74
+ <label for="node-input-interval"><i class="fa fa-refresh"></i> Interval (ms)</label>
75
+ <input type="number" id="node-input-interval" min="100" style="width:70%">
76
+ </div>
77
+ <div class="form-row">
78
+ <label for="node-input-decimals"><i class="fa fa-sort-numeric-asc"></i> Decimal Places</label>
79
+ <input type="number" id="node-input-decimals" min="0" max="4" style="width:70%">
80
+ </div>
81
+ <div class="form-row">
82
+ <label for="node-input-autoStart"><i class="fa fa-play"></i> Auto Start</label>
83
+ <input type="checkbox" id="node-input-autoStart" style="width:auto;margin-top:4px">
84
+ </div>
85
+ </script>
86
+
87
+ <script type="text/html" data-help-name="signal-gen">
88
+ <p>Generates test signals: Sine, Square, Sawtooth, Triangle, or Random — at a configurable interval.</p>
89
+ <h3>Input (optional)</h3>
90
+ <dl class="message-properties">
91
+ <dt>payload <span class="property-type">string</span></dt><dd><code>"start"</code> / <code>"stop"</code> / <code>"reset"</code></dd>
92
+ </dl>
93
+ <h3>Output</h3>
94
+ <dl class="message-properties">
95
+ <dt>payload <span class="property-type">number</span></dt><dd>Current signal value</dd>
96
+ <dt>signalGen <span class="property-type">object</span></dt><dd>{ waveform, value, phase, amplitude, offset, period, t }</dd>
97
+ </dl>
98
+ <h3>Formula (Sine)</h3>
99
+ <pre>offset + amplitude × sin(2π × phase)</pre>
100
+ </script>
package/signal-gen.js ADDED
@@ -0,0 +1,82 @@
1
+ module.exports = function(RED) {
2
+ function SignalGenNode(config) {
3
+ RED.nodes.createNode(this, config);
4
+ var node = this;
5
+
6
+ node.waveform = config.waveform || 'sine';
7
+ node.amplitude = parseFloat(config.amplitude) || 10;
8
+ node.offset = parseFloat(config.offset) || 0;
9
+ node.period = parseFloat(config.period) || 60;
10
+ if (config.periodUnit === 'min') node.period *= 60;
11
+ node.interval = parseInt(config.interval, 10) || 1000;
12
+ node.decimals = parseInt(config.decimals, 10);
13
+ if (isNaN(node.decimals) || node.decimals < 0) node.decimals = 2;
14
+ node.autoStart = config.autoStart !== false;
15
+
16
+ node.running = false;
17
+ node.timer = null;
18
+ node.startTime = null;
19
+
20
+ node.status({ fill:'grey', shape:'dot', text:'STOPPED' });
21
+ if (node.autoStart) startGen(node);
22
+
23
+ node.on('input', function(msg) {
24
+ if (msg.payload === 'start') { startGen(node); return; }
25
+ if (msg.payload === 'stop') { stopGen(node); return; }
26
+ if (msg.payload === 'reset') { node.startTime = Date.now(); return; }
27
+ });
28
+
29
+ node.on('close', function() { stopGen(node); });
30
+
31
+ function startGen(n) {
32
+ if (n.running) return;
33
+ n.running = true;
34
+ n.startTime = Date.now();
35
+ n.timer = setInterval(function() { tick(n); }, n.interval);
36
+ n.status({ fill:'green', shape:'dot', text:'RUNNING ' + n.waveform });
37
+ }
38
+
39
+ function stopGen(n) {
40
+ n.running = false;
41
+ if (n.timer) { clearInterval(n.timer); n.timer = null; }
42
+ n.status({ fill:'grey', shape:'dot', text:'STOPPED' });
43
+ }
44
+
45
+ function tick(n) {
46
+ var t = (Date.now() - n.startTime) / 1000;
47
+ var phase = (t % n.period) / n.period;
48
+ var val;
49
+
50
+ switch (n.waveform) {
51
+ case 'sine':
52
+ val = n.offset + n.amplitude * Math.sin(2 * Math.PI * phase);
53
+ break;
54
+ case 'square':
55
+ val = phase < 0.5 ? n.offset + n.amplitude : n.offset - n.amplitude;
56
+ break;
57
+ case 'sawtooth':
58
+ val = n.offset - n.amplitude + (2 * n.amplitude * phase);
59
+ break;
60
+ case 'triangle':
61
+ val = phase < 0.5
62
+ ? n.offset - n.amplitude + (4 * n.amplitude * phase)
63
+ : n.offset + n.amplitude - (4 * n.amplitude * (phase - 0.5));
64
+ break;
65
+ case 'random':
66
+ val = n.offset - n.amplitude + (2 * n.amplitude * Math.random());
67
+ break;
68
+ default:
69
+ val = 0;
70
+ }
71
+
72
+ val = parseFloat(val.toFixed(n.decimals));
73
+ n.status({ fill:'green', shape:'dot', text:'RUNNING ' + n.waveform + ' ' + val });
74
+ n.send({
75
+ payload: val,
76
+ signalGen: { waveform:n.waveform, value:val, phase:parseFloat(phase.toFixed(4)), amplitude:n.amplitude, offset:n.offset, period:n.period, t:parseFloat(t.toFixed(2)) }
77
+ });
78
+ }
79
+ }
80
+
81
+ RED.nodes.registerType('signal-gen', SignalGenNode);
82
+ };