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 +36 -0
- package/locales/de/signal-gen.json +25 -0
- package/locales/en/signal-gen.json +25 -0
- package/locales/es/signal-gen.json +25 -0
- package/package.json +45 -0
- package/signal-gen.html +100 -0
- package/signal-gen.js +82 -0
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
|
+
}
|
package/signal-gen.html
ADDED
|
@@ -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
|
+
};
|