Skip to content

Instantly share code, notes, and snippets.

@gabonator
Created August 15, 2023 15:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabonator/634b441c0dc35fe0f34a85dc4d20b72a to your computer and use it in GitHub Desktop.
Save gabonator/634b441c0dc35fe0f34a85dc4d20b72a to your computer and use it in GitHub Desktop.
rete experiment with OOK signal processing
<script src="https://cdn.jsdelivr.net/npm/rete@1.5.2/build/rete.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-vue-render-plugin@0.5.1/build/vue-render-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-connection-plugin@0.9.0/build/connection-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/alight@0.14.1/alight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-area-plugin@0.2.1/build/area-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-context-menu-plugin@0.6.0/build/context-menu-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-comment-plugin@0.3.0/build/comment-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-history-plugin@0.1.0/build/history-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-connection-mastery-plugin@0.1.0/build/connection-mastery-plugin.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/histogram-bellcurve.js"></script>
<style>
html, body {
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
}
.node .control input, .node .input-control input {
width: 140px;
}
select, input {
width: 100%;
border-radius: 30px;
background-color: white;
padding: 2px 6px;
border: 1px solid #999;
font-size: 110%;
width: 170px;
}
.note {
position: absolute;
top: 0;
left: 0;
background: #7ca2ba;
color: white;
width: 100%;
text-align: center;
padding: 0.5em;
font-family: sans-serif;
z-index: 1;
a {
color: #eee;
}
}
#chart {
width: 800px;
height: 400px;
position: absolute;
left: -650px;
top: 400px;
border:1px #f00 solid;
display: none;
}
</style>
<div id="rete"></div>
<div id="chart"></div>
<script>
var numSocket = new Rete.Socket('Number value');
var arrSocket = new Rete.Socket('Array value');
var VueNumControl = {
props: ['readonly', 'defaultVal', 'emitter', 'ikey', 'getData', 'putData'],
template: '<input type="number" :readonly="readonly" :value="value" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>',
data() {
return {
value: 0,
}
},
methods: {
change(e){
this.value = +e.target.value;
this.update();
},
update() {
if (this.ikey)
this.putData(this.ikey, this.value)
this.emitter.trigger('process');
}
},
mounted() {
// this.value = this.getData(this.ikey);
let val = this.getData(this.ikey);
this.value = val === undefined ? this.defaultVal : val;
this.update();
}
}
var VueStrControl = {
props: ['readonly', 'defaultVal', 'emitter', 'ikey', 'getData', 'putData'],
template: '<input :readonly="readonly" :value="value" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>',
data() {
return {
value: "nic",
}
},
methods: {
change(e){
console.log("neda sa");
// this.value = +e.target.value;
this.update();
},
update() {
if (this.ikey)
this.putData(this.ikey, this.value)
this.emitter.trigger('process');
}
},
mounted() {
let val = this.getData(this.ikey);
this.value = val === undefined ? this.defaultVal : val;
this.update();
}
}
var VueCheckControl = {
props: ['readonly', 'defaultVal', 'message', 'label', 'emitter', 'ikey', 'getData', 'putData'],
template: '<div style="font-family: sans-serif;"><input style="width:32px" type="checkbox" :readonly="readonly" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>{{message}}</div>',
// TODO: checked
data() {
return {
value: false,
message: "ahoj",
}
},
methods: {
change(e){
this.value = e.target.checked;
this.update();
},
update() {
if (this.ikey)
this.putData(this.ikey, this.value)
// this.checked = this.value ? "checked" : "";
this.emitter.trigger('process');
}
},
mounted() {
let val = this.getData(this.ikey);
this.value = val === undefined ? this.defaultVal : val;
this.update();
}
}
var VueOptionControl = {
props: ['readonly', 'defaultVal', 'message', 'label', 'emitter', 'ikey', 'getData', 'putData'],
template: '<div style="font-family: sans-serif;"><input style="width:32px" type="checkbox" :readonly="readonly" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>{{message}}</div>',
// TODO: checked
data() {
return {
value: false,
message: "ahoj",
}
},
methods: {
change(e){
this.value = e.target.checked;
this.update();
},
update() {
if (this.ikey)
this.putData(this.ikey, this.value)
// this.checked = this.value ? "checked" : "";
this.emitter.trigger('process');
}
},
mounted() {
let val = this.getData(this.ikey);
this.value = val === undefined ? this.defaultVal : val;
this.update();
}
}
var VueTextControl = {
props: ['readonly', 'emitter', 'ikey', 'getData', 'putData'],
//template: '<input type="text" :readonly="readonly" :value="value" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>',
template: '<textarea style="width:100%; height:80px" :readonly="readonly" :value="value" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop=""/>',
data() {
return {
value: "init text",
}
},
methods: {
change(e){
this.value = e.target.value;
this.update();
},
update() {
if (this.ikey)
this.putData(this.ikey, this.value)
this.emitter.trigger('process');
}
},
mounted() {
this.value = this.getData(this.ikey);
this.update();
}
}
class NumControl extends Rete.Control {
constructor(emitter, key, readonly, defaultVal) {
super(key);
this.component = VueNumControl;
this.props = { emitter, ikey: key, readonly, defaultVal };
}
setValue(val) {
this.vueContext.value = val;
}
}
class StrControl extends Rete.Control {
constructor(emitter, key, readonly, defaultVal) {
super(key);
this.component = VueStrControl;
this.props = { emitter, ikey: key, readonly, defaultVal };
}
setValue(val) {
this.vueContext.value = val;
}
}
class CheckControl extends Rete.Control {
constructor(emitter, key, readonly, defaultVal/*, message*/) {
super(key);
this.component = VueCheckControl;
this.props = { emitter, ikey: key, readonly, defaultVal/*, message:message*/ };
}
setValue(val) {
this.vueContext.value = val;
}
}
class OptionControl extends Rete.Control {
constructor(emitter, key, readonly, defaultVal, options) {
super(key);
this.component = VueOptionControl;
this.props = { emitter, ikey: key, readonly, defaultVal, options };
}
setValue(val) {
this.vueContext.value = val;
}
}
class TextControl extends Rete.Control {
constructor(emitter, key, readonly) {
super(key);
this.component = VueTextControl;
this.props = { emitter, ikey: key, readonly};
}
setValue(val) {
this.vueContext.value = val;
}
}
class NumConstant extends Rete.Component {
constructor(){
super("Input signal");
}
builder(node) {
this.out1 = new Rete.Output('data', "Pulse", arrSocket);
this.mydata = {data:[300,600,280,600,320,560,320,600,320,600,320,580,320,600,280,600,320,580,320,580,300,640,280,600,320,580,280,340,580,320,600,580,300,620,300,600,300,600,300,320,600,580,320,580,320,600,320,300,560,22580,300,580,280,600,320,620,300,580,320,580,300,600,320,600,300,600,300,600,300,600,300,620,300,600,300,600,300,300,620,280,640,560,300,620,280,620,280,620,280,340,600,580,320,580,340,560,320,300,600,23000,280,580,300,620,260,640,280,600,300,600,300,600,320,600,300,600,320,580,320,600,280,620,280,600,300,640,280,320,580,320,580,600,300,620,260,620,300,600,300,340,580,600,300,600,320,580,320,320,580,22540,320,580,280,620,300,600,300,620,300,580,300,600,300,620,300,600,300,600,280,600,320,620,300,580,300,600,340,280,600,300,620,580,300,620,300,580,320,600,280,320,580,620,320,600,280,600,320,300,580,498500,280,620,280,580,340,600,300,600,300,600,300,600,320,580,340,580,320,560,300,620,280,620,300,600,300,580,340,300,600,320,580,580,320,600,300,600,300,620,300,300,580,620,320,560,320,320,580,600,320,22600,260,600,300,620,300,580,320,600,300,620,280,620,280,620,260,620,300,600,340,560,320,600,300,600,300,620,300,300,600,320,600,580,300,600,280,620,300,620,280,340,580,600,280,620,280,340,580,580,320,23060,280,600,280,600,300,600,320,560,320,600,320,580,320,580,300,620,300,600,300,600,320,580,320,600,280,620,280,340,600,280,600,600,320,560,320,600,320,580,320,300,600,600,320,560,320,340,580,600,280,22580,280,600,280,620,300,600,300,600,320,580,300,620,280,620,280,600,300,600,320,580,340,580,300,620,280,620,300,320,580,340,560,600,300,600,320,600,280,600,300,340,600,600,280,600,300,320,580,620,300]};
return node.addOutput(this.out1);
}
worker(node, inputs, outputs) {
outputs['data'] = this.mydata;
this.out1.name = `Pulse ${this.mydata.data.length}p / ${(this.mydata.data.reduce((a,b)=>a+b, 0)/1000).toFixed(1)}ms`;
this.out1.node.update();
}
}
class Quantize extends Rete.Component {
constructor(){
super("Quantize");
}
builder(node) {
var inp = new Rete.Input('in', "Pulse", arrSocket);
this.out = new Rete.Output('out', "Pulse", arrSocket);
var len = new NumControl(this.editor, 'len');
len.props.defaultVal = 300;
return node
.addInput(inp)
.addControl(len)
.addOutput(this.out);
}
worker(node, inputs, outputs) {
var arr = inputs['in'].length ? [...inputs['in'][0].data] : [];
var q = node.data.len;
for (var i=0; i<arr.length; i++)
arr[i] = Math.floor((arr[i]+q/2)/q)*q;
outputs['out'] = {data:arr};
this.out.name = `Pulse ${arr.length}p / ${(arr.reduce((a,b)=>a+b, 0)/1000).toFixed(1)}ms`;
this.out.node.update();
}
}
class Filter extends Rete.Component {
constructor(){
super("Filter");
}
builder(node) {
var inp = new Rete.Input('in', "Pulse", arrSocket);
this.out = new Rete.Output('out', "Pulse", arrSocket);
var condition = new StrControl(this.editor, 'condition');
condition.props.defaultVal = "x < 5000";
var breakOn = new CheckControl(this.editor, 'break');
breakOn.props.defaultVal = false;
breakOn.props.message = "break on condition";
return node
.addInput(inp)
.addControl(condition)
.addControl(breakOn)
.addOutput(this.out);
}
worker(node, inputs, outputs) {
var arr = inputs['in'].length ? [...inputs['in'][0].data] : [];
var q = node.data.len;
var newarr = [];
console.log(node.data.condition, node.data.break);
var filterCode = eval(`(x, i) => ${node.data.condition}`);
for (var i=0; i<arr.length; i++)
{
if (filterCode(arr[i], i))
newarr.push(arr[i])
else if (node.data.break)
break;
}
outputs['out'] = {data:newarr};
this.out.name = `${node.data.break} Pulse ${newarr.length}p / ${(newarr.reduce((a,b)=>a+b, 0)/1000).toFixed(1)}ms`;
this.out.node.update();
}
}
class Histogram extends Rete.Component {
constructor(){
super("Histogram");
}
builder(node) {
var inp1 = new Rete.Input('arr', "Pulse", arrSocket);
return node
.addInput(inp1);
}
worker(node, inputs, outputs) {
var n1 = inputs['arr'].length ? inputs['arr'][0].data : [];
ShowHistogram(n1);
}
}
class SubArray extends Rete.Component {
constructor(){
super("SubArray");
}
builder(node) {
var inp = new Rete.Input('in', "Pulse", arrSocket);
this.out = new Rete.Output('out', "Pulse", arrSocket);
var begin = new NumControl(this.editor, 'begin');
this.len = new NumControl(this.editor, 'len');
var tillEnd = new CheckControl(this.editor, 'tillEnd');
tillEnd.props.defaultVal = false;
tillEnd.props.message = "all till end";
this.len.props.readonly = true;
begin.props.defaultVal = 1;
this.len.props.defaultVal = 10;
return node
.addInput(inp)
.addControl(begin)
.addControl(this.len)
.addControl(tillEnd)
.addOutput(this.out);
}
worker(node, inputs, outputs) {
var arr = inputs['in'].length ? [...inputs['in'][0].data] : [];
if (node.data.tillEnd)
arr = arr.slice(node.data.begin)
else
arr = arr.slice(node.data.begin, node.data.begin + node.data.len)
outputs['out'] = {data:arr};
this.out.name = `Pulse ${arr.length}p / ${(arr.reduce((a,b)=>a+b, 0)/1000).toFixed(1)}ms`;
this.out.node.update();
}
}
class Pairs extends Rete.Component {
constructor(){
super("Pairs");
}
builder(node) {
var inp = new Rete.Input('in', "Pulse", arrSocket);
this.out = new Rete.Output('out', "Pulse", arrSocket);
return node
.addInput(inp)
.addOutput(this.out);
}
worker(node, inputs, outputs) {
var arr = inputs['in'].length ? [...inputs['in'][0].data] : [];
if (arr.length % 2 != 0)
{
outputs['out'] = {data:[]}
this.out.name = `Error!`;
this.out.node.update();
throw "not pairs!"
}
var arrout = []
for (var i=0; i<arr.length; i+=2)
arrout.push(`${arr[i]},${arr[i+1]}`);
outputs['out'] = {data:arrout};
this.out.name = `Array of ${arrout.length} pairs`;
this.out.node.update();
}
}
class LookupTable extends Rete.Component {
constructor(){
super("Lookup table");
}
builder(node) {
var inp = new Rete.Input('in', "Pulse", arrSocket);
this.out = new Rete.Output('out', "Pulse", arrSocket);
return node
.addInput(inp)
.addOutput(this.out);
}
worker(node, inputs, outputs) {
var arr = inputs['in'].length ? [...inputs['in'][0].data] : [];
if (arr.length % 2 != 0)
{
outputs['out'] = {data:[]}
this.out.name = `Error!`;
this.out.node.update();
throw "not pairs!"
}
var arrout = []
for (var i=0; i<arr.length; i++)
if (arr[i] == "300,600")
arrout.push("1")
else if (arr[i] == "600,300")
arrout.push("0")
else
arrout.push("?")
outputs['out'] = {data:arrout};
this.out.name = `Array of ${arrout.length} symbols`;
this.out.node.update();
}
}
class ToInteger extends Rete.Component {
constructor(){
super("To integer");
// TODO: uint8_t, uint16_t, uint32_t
// TODO: LSB/MSB
}
builder(node) {
var inp = new Rete.Input('in', "Pulse", arrSocket);
this.out = new Rete.Output('out', "Pulse", arrSocket);
var firstbit = new OptionControl(this.editor, 'msblsbfirst');
firstbit.props.title = "First bit"
firstbit.props.options = ["MSB", "LSB"];
firstbit.props.defaultVal = "MSB";
var datatype = new OptionControl(this.editor, 'datatype');
datatype.props.title = "Data type"
datatype.props.options = ["uint8_t", "uint16_t", "uint32_t"];
datatype.props.defaultVal = "uint32_t";
return node
.addInput(inp)
.addControl(firstbit)
.addControl(datatype)
.addOutput(this.out);
}
worker(node, inputs, outputs) {
var arr = inputs['in'].length ? [...inputs['in'][0].data] : [];
var uint = 0;
for (var i=0; i<arr.length; i++)
{
if (arr[i] === "0" || arr[i] === 0)
{
} else if (arr[i] === "1" || arr[i] === 1)
{
uint |= 1<<(arr.length-1-i)
} else {
outputs['out'] = {data:[]}
this.out.name = `Error!`;
this.out.node.update();
throw "wrong data"
}
}
var digits = (arr.length+3)>>2;
var arrout = ["0x"+(("00000000"+uint.toString(16)).substr(-digits))]
outputs['out'] = {data:arrout};
this.out.name = `uint32_t [${arrout.length}]`;
this.out.node.update();
}
}
class Print extends Rete.Component {
constructor(){
super("Print");
}
builder(node) {
var inp = new Rete.Input('in', "Pulse", arrSocket);
this.print = new TextControl(this.editor, 'print');
return node.addInput(inp).addControl(this.print);
}
worker(node, inputs, outputs) {
var arr = inputs['in'].length ? [...inputs['in'][0].data] : [];
console.log(arr);
this.editor.nodes.find(n => n.id == node.id).controls.get('print').setValue(arr.join(", "));
}
}
class NumComponent extends Rete.Component {
constructor(){
super("Number");
}
builder(node) {
var out1 = new Rete.Output('num', "Number", numSocket);
return node.addControl(new NumControl(this.editor, 'num')).addOutput(out1);
}
worker(node, inputs, outputs) {
outputs['num'] = node.data.num;
}
}
class AddComponent extends Rete.Component {
constructor(){
super("Add");
}
builder(node) {
var inp1 = new Rete.Input('num',"Number", numSocket);
var inp2 = new Rete.Input('num2', "Number2", numSocket);
var out = new Rete.Output('num', "Number", numSocket);
this.inp1 = inp1
inp1.addControl(new NumControl(this.editor, 'num'))
inp2.addControl(new NumControl(this.editor, 'num2'))
return node
.addInput(inp1)
.addInput(inp2)
.addControl(new NumControl(this.editor, 'preview', true))
.addOutput(out);
}
worker(node, inputs, outputs) {
var n1 = inputs['num'].length?inputs['num'][0]:node.data.num1;
var n2 = inputs['num2'].length?inputs['num2'][0]:node.data.num2;
var sum = n1 + n2;
this.inp1.name = "ahoj" + sum;
this.inp1.node.update();
this.editor.nodes.find(n => n.id == node.id).controls.get('preview').setValue(sum);
outputs['num'] = sum;
}
}
(async () => {
var container = document.querySelector('#rete');
var components = [new NumComponent(), new AddComponent(), new NumConstant(), new SubArray(), new Quantize(),
new Histogram(), new Filter(), new Pairs(), new LookupTable(), new Print(), new ToInteger()];
var editor = new Rete.NodeEditor('demo@0.1.0', container);
editor.use(ConnectionPlugin.default);
editor.use(VueRenderPlugin.default);
editor.use(ContextMenuPlugin.default);
editor.use(AreaPlugin);
editor.use(CommentPlugin.default);
editor.use(HistoryPlugin);
editor.use(ConnectionMasteryPlugin.default);
var engine = new Rete.Engine('demo@0.1.0');
components.map(c => {
editor.register(c);
engine.register(c);
});
var nd = await components[2].createNode()
var nf = await components[6].createNode({break:true})
var nq = await components[4].createNode()
var nh = await components[5].createNode()
var ns = await components[3].createNode({tillEnd:true}) // subarray
var np = await components[7].createNode() // pairs
var nl = await components[8].createNode() // loopkup
var nx = await components[9].createNode() // print
var ni = await components[10].createNode() // to integer
nd.position = [100, 200];
nf.position = [350, 200];
nq.position = [600, 200];
nh.position = [850, 200];
ns.position = [100, 400];
np.position = [350, 400];
nl.position = [600, 400];
nx.position = [1100, 400];
ni.position = [850, 400];
editor.addNode(nd);
editor.addNode(nf);
editor.addNode(nq);
editor.addNode(nh);
editor.addNode(ns);
editor.addNode(np);
editor.addNode(nl);
editor.addNode(nx);
editor.addNode(ni);
editor.connect(nd.outputs.get('data'), nf.inputs.get('in'));
editor.connect(nf.outputs.get('out'), nq.inputs.get('in'));
editor.connect(nq.outputs.get('out'), nh.inputs.get('arr'));
editor.connect(nq.outputs.get('out'), ns.inputs.get('in'));
editor.connect(ns.outputs.get('out'), np.inputs.get('in'));
editor.connect(np.outputs.get('out'), nl.inputs.get('in'));
editor.connect(nl.outputs.get('out'), ni.inputs.get('in'));
editor.connect(ni.outputs.get('out'), nx.inputs.get('in'));
editor.on('process nodecreated noderemoved connectioncreated connectionremoved', async () => {
console.log('process');
await engine.abort();
await engine.process(editor.toJSON());
});
editor.view.resize();
AreaPlugin.zoomAt(editor);
editor.trigger('process');
})();
function ShowHistogram(data)
{
if (data.length == 0)
{
document.querySelector("#chart").style.display = "none";
return;
} else {
document.querySelector("#chart").style.display = "block";
}
var data1 = [];
var data2 = [];
for (var i=0; i<data.length; i++)
{
if (i%2 == 0)
data1.push(data[i]);
else
data2.push(data[i]);
}
Highcharts.chart('chart', {
title: {
text: 'Highcharts Histogram'
},
xAxis: [{
title: { text: 'Data' },
alignTicks: false,
}, {
title: { text: 'Histogram' },
alignTicks: false,
opposite: true,
// min:minVal
}],
yAxis: [{
title: { text: 'Data' }
}, {
title: { text: 'Histogram' },
opposite: true
}],
/*
plotOptions: {
histogram: {
accessibility: {
point: {
valueDescriptionFormat: '{index}. {point.x:.3f} to {point.x2:.3f}, {point.y}.'
}
}
}
},
*/
series: [{
name: 'Histogram',
type: 'histogram',
xAxis: 1 ,
yAxis: 1,
baseSeries: "s1",
zIndex: -1,
color: "rgba(255, 0, 0, 0.5)",
binsNumber:20
}, {
name: 'Histogram',
type: 'histogram',
xAxis: 1,
yAxis: 1,
baseSeries: "s2",
zIndex: -1,
color: "rgba(0, 0, 255, 0.5)",
binsNumber:20
},
{
name: 'Data',
type: 'scatter',
data: data1,
id: 's1',
marker: {
radius: 1.5
}, visible:false
},{
name: 'Data',
type: 'scatter',
data: data2,
id: 's2',
marker: {
radius: 1.5
}, visible:false
}]
});
(function(H) {
H.Pointer.prototype.drag = function(e) {
var container = this.chart.container.parentElement;
container.style.left = container.offsetLeft + e.movementX + 'px';
container.style.top = container.offsetTop + e.movementY + 'px';
}
})(Highcharts);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment