pulseplot
Version:
Pulse data viewer JS library
138 lines (126 loc) • 48.5 kB
JavaScript
(function(k,P){typeof exports=="object"&&typeof module<"u"?P(exports):typeof define=="function"&&define.amd?define(["exports"],P):(k=typeof globalThis<"u"?globalThis:k||self,P(k.pulseplot={}))})(this,function(k){"use strict";/**
Determine divisor and SI prefix.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2019
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/const P=[{name:"yotta",scale:1e24,prefix:"Y"},{name:"zetta",scale:1e21,prefix:"Z"},{name:"exa",scale:1e18,prefix:"E"},{name:"peta",scale:1e15,prefix:"P"},{name:"tera",scale:1e12,prefix:"T"},{name:"giga",scale:1e9,prefix:"G"},{name:"mega",scale:1e6,prefix:"M"},{name:"kilo",scale:1e3,prefix:"k"},{name:"",scale:1,prefix:""},{name:"milli",scale:.001,prefix:"m"},{name:"micro",scale:1e-6,prefix:"µ"},{name:"nano",scale:1e-9,prefix:"n"},{name:"pico",scale:1e-12,prefix:"p"},{name:"femto",scale:1e-15,prefix:"f"},{name:"atto",scale:1e-18,prefix:"a"},{name:"zepto",scale:1e-21,prefix:"z"},{name:"yocto",scale:1e-24,prefix:"y"}];function Z(r,t=10){if(r==0)return P[8];r=r/t;for(let s=0;s<P.length;++s)if(r>=P[s].scale)return P[s];return P[P.length-1]}const E=[{name:"year",scale:31557513,prefix:"Y"},{name:"month",scale:2635200,prefix:"M"},{name:"day",scale:86400,prefix:"D"},{name:"hour",scale:3600,prefix:"h"},{name:"minute",scale:60,prefix:"m"},{name:"second",scale:1,prefix:"s"},{name:"milli",scale:.001,prefix:"ms"},{name:"micro",scale:1e-6,prefix:"µs"},{name:"nano",scale:1e-9,prefix:"ns"},{name:"pico",scale:1e-12,prefix:"ps"},{name:"femto",scale:1e-15,prefix:"fs"},{name:"atto",scale:1e-18,prefix:"as"},{name:"zepto",scale:1e-21,prefix:"zs"},{name:"yocto",scale:1e-24,prefix:"ys"}];function et(r,t=10){if(r==0)return E[8];r=r/t;for(let s=0;s<E.length;++s)if(r>=E[s].scale)return E[s];return E[E.length-1]}/**
@file Various utils.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2019
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/function I(r){return r&&(typeof r=="string"?document.querySelector(r):r)}function X(r,t){if(!t||typeof t!="string")return t;if(r[t])return r[t];const s=t.toLowerCase();for(let e in r)if(e.toLowerCase()==s)return r[e];for(let e in r)if(e.toLowerCase().startsWith(s))return r[e];return null}function st(r){var t=new DOMParser().parseFromString(r,"text/html");return t.body.textContent||""}/**
@file DropZone JS.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2019
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/class Y{constructor(t,s){t=t||"#dropZone",t=I(t),this.dropZone=t,this.fileLoader=s,this.inputEls=[],this.dragEnteredEls=[],window.addEventListener("dragenter",this),window.addEventListener("dragleave",this),window.addEventListener("dropleave",this),["dragenter","dragover","dragleave","drop"].forEach(i=>this.dropZone.addEventListener(i,this,!1))}addInput(t){t=t||"#inputfile",t=I(t),t.addEventListener("change",this,!1),this.inputEls.push(t)}destroy(){window.removeEventListener("dragenter",this,!1),window.removeEventListener("dragleave",this,!1),window.removeEventListener("dropleave",this,!1),["dragenter","dragover","dragleave","drop"].forEach(s=>this.dropZone.removeEventListener(s,this,!1));for(let s of this.inputEls)s.removeEventListener("change",this,!1)}allowDrag(t){t.dataTransfer.dropEffect="copy",t.preventDefault()}handleDrop(t){t.preventDefault(),this.handleFileSelect(t)}handleFileSelect(t){t.stopPropagation(),t.preventDefault();const s="dataTransfer"in t?t.dataTransfer.files:t.target.files;for(let e=0,i;i=s[e];e++){const n=new FileReader;n.onload=l=>{i.fileBuffer=l.target.result,this.fileLoader(i)},n.readAsArrayBuffer(i)}}handleEvent(t){const s=t.type;if(typeof this[s]=="function")return t.preventDefault(),this[s](t)}dragenter(t){this.dragEnteredEls.push(t.target),document.documentElement.classList.add("dragdrop"),this.dropZone.classList.add("active")}dragover(t){this.dropZone.classList.add("hover"),this.allowDrag(t)}dragleave(t){const s=this.dragEnteredEls.indexOf(t.target);s>-1&&this.dragEnteredEls.splice(s,1),this.dragEnteredEls.indexOf(this.dropZone)<0&&this.dropZone.classList.remove("hover"),this.dragEnteredEls.length===0&&(document.documentElement.classList.remove("dragdrop"),this.dropZone.classList.remove("active"))}dropleave(){this.dragEnteredEls.splice(0),this.dropZone.classList.remove("hover"),this.dropZone.classList.remove("active"),document.documentElement.classList.remove("dragdrop")}drop(t){const s=new Event("dropleave");window.dispatchEvent(s),this.handleDrop(t)}change(t){this.handleFileSelect(t)}}/**
@file Hexbuffer JS.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2020
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/function $(r,t=2){return r=Math.round(r),(r+65536).toString(16).substr(-t).toUpperCase()}class z{constructor(t=""){this.fromString(t)}fromString(t){this.line=t.replace(/\s/g,""),this.index=0}hasNibble(){return this.index+1<=this.line.length}hasByte(){return this.index+2<=this.line.length}hasWord(){return this.index+4<=this.line.length}peekNibble(){return parseInt(this.line.substr(this.index,1),16)}peekByte(){return parseInt(this.line.substr(this.index,2),16)}peekWord(){return parseInt(this.line.substr(this.index,4),16)}getNibble(){const t=parseInt(this.line.substr(this.index,1),16);return this.index+=1,t}getByte(){const t=parseInt(this.line.substr(this.index,2),16);return this.index+=2,t}getWord(){const t=parseInt(this.line.substr(this.index,4),16);return this.index+=4,t}pushNibble(t){this.line+=$(t,1)}pushByte(t){this.line+=$(t,2)}pushWord(t){this.line+=$(t,4)}}/**
@file Histogram JS.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2020
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/const it=16;class nt{constructor(t){typeof t<"u"?(this.count=1,this.sum=t,this.mean=t,this.devi=0,this.min=t,this.max=t):(this.count=0,this.sum=0,this.mean=null,this.devi=0,this.min=null,this.max=null)}add(t){this.count++,this.sum+=t,this.mean=this.sum/this.count,this.min=this.min===null?t:Math.min(t,this.min),this.max=this.max===null?t:Math.max(t,this.max),this.devi=(this.max-this.min)/2}fuse(t){this.count+=t.count,this.sum+=t.sum,this.mean=this.sum/this.count,this.min=Math.min(this.min,t.min),this.max=Math.max(this.max,t.max),this.devi=(this.max-this.min)/2}contains(t){return t>=this.min&&t<=this.max}}class H{constructor(t,s=.2){this.bins=[],this.histogram_sum(t,s)}get length(){return this.bins.length}histogram_sum(t,s=.2){const e=t.length;for(let i=0;i<e;++i){let n;for(n=0;n<this.bins.length;++n){const l=t[i],o=this.bins[n].mean;if(Math.abs(l-o)<s*Math.max(l,o)){this.bins[n].add(t[i]);break}}n==this.bins.length&&n<it&&this.bins.push(new nt(t[i]))}}delete_bin(t){this.bins.splice(t,1)}swap_bins(t,s){if(t<this.bins.length&&s<this.bins.length){const e=this.bins[t];this.bins[t]=this.bins[s],this.bins[s]=e}}sort_mean(){if(!(this.bins.length<2))for(let t=0;t<this.bins.length-1;++t)for(let s=t+1;s<this.bins.length;++s)this.bins[s].mean<this.bins[t].mean&&this.swap_bins(s,t)}sort_count(){if(!(this.bins.length<2))for(let t=0;t<this.bins.length-1;++t)for(let s=t+1;s<this.bins.length;++s)this.bins[s].count<this.bins[t].count&&this.swap_bins(s,t)}fuse_bins(t=.2){if(!(this.bins.length<2))for(let s=0;s<this.bins.length-1;++s)for(let e=s+1;e<this.bins.length;++e){const i=this.bins[s].mean,n=this.bins[e].mean;Math.abs(i-n)<t*Math.max(i,n)&&(this.bins[s].fuse(this.bins[e]),this.delete_bin(e),e--)}}trim_bins(t=0){for(let s=0;s<this.bins.length;++s)this.bins[s].mean<=t&&this.delete_bin(s)}find_bin_index(t){for(let s=0;s<this.bins.length;++s)if(this.bins[s].contains(t))return s;return-1}console_print(){for(let t=0;t<this.bins.length;++t){const s=this.bins[t];console.log(`[${t}] ${s.count} × ${s.mean.toFixed(1)} ±${s.devi.toFixed(1)} µs [${s.min};${s.max}]`)}}string_print(t=", "){let s=[];for(let e=0;e<this.bins.length;++e){const i=this.bins[e];s.push(`${i.count}× ${i.mean.toFixed(1)} <small>±${i.devi.toFixed(1)}</small> µs`)}return s.join(t)}}class lt{constructor(t,s=.2){this.analyse_pulses(t,s),this.create_rfraw(t)}analyse_pulses(t,s,e=.2){this.pulses=[],this.gaps=[],this.periods=[],this.pulse_sum=0,this.gap_sum=0;for(let n=0;n<t.length-2;n+=2){const l=t[n],o=t[n+1];this.pulses.push(l),this.gaps.push(o),this.periods.push(l+o),this.pulse_sum+=l,this.gap_sum+=o}const i=t[t.length-2];this.pulses.push(i),this.pulse_sum+=i,this.pulse_gap_ratio=this.pulse_sum/this.gap_sum,this.pulse_gap_skew=this.pulse_gap_ratio-1,this.hist_pulses=new H(this.pulses,e),this.hist_gaps=new H(this.gaps,e),this.hist_periods=new H(this.periods,e),this.hist_timings=new H(t,e),this.hist_pulses.trim_bins(e),this.hist_gaps.trim_bins(e),this.hist_periods.trim_bins(e),this.hist_timings.trim_bins(e),this.hist_pulses.fuse_bins(e),this.hist_gaps.fuse_bins(e),this.hist_periods.fuse_bins(e),this.hist_timings.fuse_bins(e)}guess(){const t=this.hist_pulses,s=this.hist_gaps,e=this.hist_periods;if(t.sort_mean(),s.sort_mean(),t.bins.length>0&&t.bins[0].mean==0&&t.delete_bin(0),this.pulses.length==1)return{name:"Single pulse detected. Probably Frequency Shift Keying or just noise..."};if(t.length==1&&s.length==1)return{name:"Un-modulated signal. Maybe a preamble..."};if(t.length==1&&s.length>1)return{name:"Pulse Position Modulation with fixed pulse width",modulation:"PPM",short:s.bins[0].mean,long:s.bins[1].mean,gap:s.bins[1].max*1.2,reset:s.bins[s.length-1].max*1.2};if(t.length==2&&s.length==1){const i=t.bins[0].mean,n=t.bins[1].mean;return{name:"Pulse Width Modulation with fixed gap",modulation:"PWM",short:i,long:n,tolerance:(n-i)*.4,reset:s.bins[s.length-1].max*1.2}}else if(t.length==2&&s.length==2&&e.length==1){const i=t.bins[0].mean,n=t.bins[1].mean;return{name:"Pulse Width Modulation with fixed period",modulation:"PWM",short:i,long:n,tolerance:(n-i)*.4,reset:s.bins[s.length-1].max*1.2}}else if(t.length==2&&s.length==2&&e.length==3){const i=t.bins[0].mean;return{name:"Manchester coding (PCM)",modulation:"MC",short:i,long:i,reset:s.bins[s.length-1].max*1.2}}else if(t.length==2&&s.length>=3){const i=t.bins[0].mean,n=t.bins[1].mean;return{name:"Pulse Width Modulation with multiple packets",modulation:"PWM",short:i,long:n,gap:s.bins[1].max*1.2,tolerance:(n-i)*.4,reset:s.bins[s.length-1].max*1.2}}else{if(t.length>=3&&s.length>=3&&Math.abs(t.bins[1].mean-2*t.bins[0].mean)<=t.bins[0].mean/8&&Math.abs(t.bins[2].mean-3*t.bins[0].mean)<=t.bins[0].mean/8&&Math.abs(s.bins[0].mean-t.bins[0].mean)<=t.bins[0].mean/8&&Math.abs(s.bins[1].mean-2*t.bins[0].mean)<=t.bins[0].mean/8&&Math.abs(s.bins[2].mean-3*t.bins[0].mean)<=t.bins[0].mean/8)return{name:"Pulse Code Modulation (Not Return to Zero)",modulation:"PCM",short:t.bins[0].mean,long:t.bins[0].mean,reset:t.bins[0].mean*1024};if(t.length==3){t.sort_count();const i=t.bins[1].mean,n=t.bins[2].mean,l=i<n?i:n,o=i<n?n:i;return{name:"Pulse Width Modulation with sync/delimiter",modulation:"PWM",short:l,long:o,sync:t.bins[0].mean,reset:s.bins[s.length-1].max*1.2}}else return{name:"No clue..."}}}create_rfraw(t){const s=this.hist_timings;if(s.bins.length<1||s.bins.length>8||t.length>494)return"";let e=new z;for(let l of s.bins)e.pushWord(l.mean);for(let l=0;l<t.length-1;l+=2){const o=t[l],a=t[l+1],h=s.find_bin_index(o);h>=0?e.pushNibble(h|8):o==0||console.error("RfRaw encoding mark bucket not found:",o,h);const c=s.find_bin_index(a);c>=0?e.pushNibble(c):a==0||console.error("RfRaw encoding space bucket not found:",a,c)}e.line.length%2&&e.pushNibble(0),e.pushByte(85);let i=new z;i.pushByte(170),i.pushByte(176),i.pushByte(2+e.line.length/2-1),i.pushByte(s.bins.length),i.pushByte(1);let n=new z;n.pushByte(170),n.pushByte(177),n.pushByte(s.bins.length),this.rfrawB0=i.line+e.line,this.rfrawB1=n.line+e.line}console_log(){const t=this.guess();console.log("Analyzing pulses..."),console.log(`Total count: ${this.pulses.length}`),console.log("Pulse width distribution:"),this.hist_pulses.console_print(),console.log("Gap width distribution:"),this.hist_gaps.console_print(),console.log("Pulse period distribution:"),this.hist_periods.console_print(),console.log("Pulse timing distribution:"),this.hist_timings.console_print(),console.log(`DC bias (Pulse/Gap skew): ${(this.pulse_gap_skew*100).toFixed(1)}`),console.log("Guessing modulation:"),console.log(t)}print_plain(t){const s=this.guess();t.innerHTML=`
<div>Pulses: ${this.hist_pulses.string_print()}</div>
<div>Gaps: ${this.hist_gaps.string_print()}</div>
<div>Periods: ${this.hist_periods.string_print()}</div>
<div>Timings: ${this.hist_timings.string_print()}</div>
<div>${s.name}</div>
`}print(t,s){const e=this.guess();t&&(t.innerHTML=`<table>
<tr><th>Pulses</th><td>${this.hist_pulses.string_print("</td><td>")}</td></tr>
<tr><th>Gaps</th><td>${this.hist_gaps.string_print("</td><td>")}</td></tr>
<tr><th>Periods</th><td>${this.hist_periods.string_print("</td><td>")}</td></tr>
<tr><th>Timings</th><td>${this.hist_timings.string_print("</td><td>")}</td></tr>
</table>
`),s&&(s.innerHTML=`
<div><small>DC bias (Pulse/Gap skew): ${(this.pulse_gap_skew*100).toFixed(1)}%</small><br>
Guessing modulation: <strong>${e.name}</strong><br>
modulation: <strong>${e.modulation}</strong>
short: <strong>${e.short?e.short.toFixed(1):"-"}</strong>
long: <strong>${e.long?e.long.toFixed(1):"-"}</strong>
sync: <strong>${e.sync?e.sync.toFixed(1):"-"}</strong>
gap: <strong>${e.gap?e.gap.toFixed(1):"-"}</strong>
reset: <strong>${e.reset?e.reset.toFixed(1):"-"}</strong><br>
<small>RfRaw (rx): <strong>${this.rfrawB1?this.rfrawB1:"-"}</strong></small><br>
<small>RfRaw (tx): <strong>${this.rfrawB0?this.rfrawB0:"-"}</strong></small>
</div>
`)}}/**
@file Bitbuffer JS.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2020
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/class M{constructor(t=[],s=0){Array.isArray(t)?(this.bytes=t,this.len=s||t.length*8):this.fromString(t)}fromString(t){this.bytes=[],this.len=0;let s=-1;if(t=t.trim(),t.startsWith("{")){const e=t.indexOf("}");if(e<0)return;s=parseInt(t.slice(1),10),t=t.slice(e+1)}t.startsWith("0x")&&(t=t.slice(2));for(let e of t){const i=parseInt(e,16);this.pushNibble(i)}s>=0&&(this.len=s)}pushZero(){this.push(0)}pushOne(){this.push(1)}pushSymbol(t){t=="0"?this.push(0):t=="1"&&this.push(1)}push(t){t=t?128:0,this.bytes[~~(this.len/8)]|=t>>this.len%8,this.len+=1}pushNibble(t){for(let s=3;s>=0;--s)this.push(t>>s&1)}pushByte(t){for(let s=7;s>=0;--s)this.push(t>>s&1)}pushBreak(){const t=~~((this.len+7)/8);this.bytes[t]=-1,this.len=(t+1)*8}toBitArray(){let t=[];for(let s=0;s<this.len;++s){const i=(this.bytes[~~(s/8)]||0)>>7-s%8&1;t.push(i)}return t}toHexString(){let t=`{${this.len}}`;for(let s=0;s<this.len;s+=8){const e=this.bytes[~~(s/8)]||0;e<0?t+=" / ":(t+=" ",t+=(e>>4).toString(16).toUpperCase(),s+4<this.len&&(t+=(e&15).toString(16).toUpperCase()))}return t}}/**
@file Pulse Slicer JS.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2020
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/function G(r,t){return t.modulation=="PCM"?ot(r,t):t.modulation=="MC"?ct(r,t):t.modulation=="PPM"?ht(r,t):t.modulation=="PWM"?ft(r,t):t.modulation=="DM"?ut(r,t):t.modulation=="NRZI"?dt(r,t):t.modulation=="CMI"?pt(r,t):t.modulation=="PIWM"?mt(r,t):[]}function ot(r,t){return!t.long||t.long==t.short?at(r,t):rt(r,t)}function at(r,t){const s=t.short,e=t.gap,i=new M;let n=[],l=0;for(let o=0;o<r.length;o+=1){const a=1-o%2,h=r[o];if(e&&h>e)i.pushBreak();else{const c=~~(h/s+.5);for(let f=0;f<c;++f)n.push([l+h/c*f,l+h/c*(f+1),a]),i.push(a)}l+=h}return{hints:n,bits:i}}function rt(r,t){const s=t.short,e=t.long,i=t.gap,n=s*.5,l=s*1.5,o=new M;let a=[],h=0;for(let c=0;c<r.length;c+=2){const f=r[c],p=r[c+1];if(f<n||f>l){o.pushBreak(),h+=f+p;continue}let g=f*e/s,b=p+f-g;if(b<e/2&&(g=f+p,b=0),a.push([h,h+g,"1"]),o.pushOne(),h+=g,i&&p>i){o.pushBreak(),h+=b;continue}const x=~~(b/e+.5);for(let y=0;y<x;++y)a.push([h+b*y/x,h+b*(y+1)/x,"0"]),o.pushZero();h+=b}return{hints:a,bits:o}}function ht(r,t){const s=t.short,e=t.long,i=t.sync,n=t.gap,l=s*.5,o=s*1.5,a=e*.5,h=e*1.5,c=i*.5,f=i*1.5,p=new M;let g=[],b=0;for(let x=0;x<r.length;x+=2){const y=r[x],_=r[x+1],D=b;b+=y+_,_>l&&_<o?(g.push([D,b,"1"]),p.pushOne()):_>a&&_<h?(g.push([D,b,"0"]),p.pushZero()):_>c&&_<f?(g.push([D,b,"X"]),p.pushBreak()):n&&_>n&&p.pushBreak()}return{hints:g,bits:p}}function ft(r,t){const s=t.short,e=t.long,i=t.sync,n=t.gap,l=s*.5,o=s*1.5,a=e*.5,h=e*1.5,c=i*.5,f=i*1.5,p=new M;let g=[],b=0;for(let x=0;x<r.length;x+=2){const y=r[x],_=r[x+1],D=b;let B=b+y+_;_>n&&(B=b+y+n),b+=y+_,y>l&&y<o?(g.push([D,B,"1"]),p.pushOne()):y>a&&y<h?(g.push([D,B,"0"]),p.pushZero()):y>c&&y<f&&(g.push([D,B,"X"]),p.pushBreak()),n&&_>n&&p.pushBreak()}return{hints:g,bits:p}}function j(r,t,s){for(let e=t;e<r.length;e+=2){if(~~(r[e]/s+.5)>1)return 0;if(~~(r[e+1]/s+.5)>1)return 1}return 0}function ct(r,t){const s=t.short,e=new M;let i=[],n=j(r,0,s),l=0,o=0;for(let a=0;a<r.length;a+=2){const h=r[a],c=~~(h/s+.5),f=r[a+1],p=~~(f/s+.5);c==1?(n?o=l:(i.push([o,l+h,"0"]),e.pushZero(),o=l+h),n=!n):c==2?(n?(e.pushBreak(),o=l+h/2):(i.push([o,l+h/2,"0"]),e.pushZero(),o=l+h/2),n=!1):c>2&&(n||(i.push([o,l+h/c,"0"]),e.pushZero()),o=l+h-h/c,e.pushBreak(),n=j(r,a+1,s)),p==1?(n?o=l+h:(i.push([o,l+h+f,"1"]),e.pushOne(),o=l+h+f),n=!n):p==2?(n?(e.pushBreak(),o=l+h+f/2):(i.push([o,l+h+f/2,"1"]),e.pushOne(),o=l+h+f/2),n=!1):p>2&&(n||(i.push([o,l+h+f/p,"1"]),e.pushOne()),o=l+h+f-f/p,e.pushBreak(),n=j(r,a+1,s)),l+=h+f}return{hints:i,bits:e}}function ut(r,t){const s=t.short,e=new M;let i=[],n=0,l=null;for(let o=0;o<r.length;o+=2){const a=r[o],h=~~(a/s+.5),c=r[o+1],f=~~(c/s+.5);!l&&h==1&&f==1?(i.push([n,n+a+c,"0"]),e.pushZero()):h==1&&f==1?(i.push([l,n+a,"0"]),e.pushZero(),l=n+a):l&&h==1&&f==2?(i.push([l,n+a,"0"]),e.pushZero(),i.push([n+a,n+a+c,"1"]),e.pushOne(),l=null):h==2&&f==1?(i.push([n,n+a,"1"]),e.pushOne(),l=n+a):h==2&&f==2?(i.push([n,n+a,"1"]),e.pushOne(),i.push([n+a,n+a+c,"1"]),e.pushOne()):!l&&h==1?(i.push([n,n+a+s,"0"]),e.pushZero(),e.pushBreak()):!l&&h==2?(i.push([n,n+a,"1"]),e.pushOne(),e.pushBreak()):(l&&(i.push([l,l+s*2,"0"]),e.pushZero()),l=null,e.pushBreak()),n+=a+c}return{hints:i,bits:e}}function dt(r,t){const s=t.short,e=new M;let i=[],n=0,l=0;for(let o=0;o<r.length;o+=1){const a=r[o],h=~~(a/s+.5);l&&(i.push([l,n+s/2,"1"]),e.pushOne()),l=n+s/2;for(let c=1;c<h;++c)i.push([l,l+a/h,"0"]),e.pushZero(),l+=a/h;n+=a}return{hints:i,bits:e}}function pt(r,t){const s=t.short,e=new M;let i=[],n=0,l=null;for(let o=0;o<r.length;o+=2){const a=r[o],h=~~(a/s+.5),c=r[o+1],f=~~(c/s+.5);h==1&&f==1?(l||(l=n-a),i.push([l,n+a,"0"]),e.pushZero(),l=n+a):h==1&&f==2?(l||(l=n-a),i.push([l,n+a,"0"]),e.pushZero(),l=n+a+c,i.push([n+a,l,"1"]),e.pushOne()):h==1&&f==3?(l||(l=n-a),i.push([l,n+a,"0"]),e.pushZero(),l=n+a+c*2/3,i.push([n+a,l,"1"]),e.pushOne()):h==2&&f==1?(i.push([l,n+a,"1"]),e.pushOne(),l=n+a):h==2&&f==2?(i.push([l,n+a,"1"]),e.pushOne(),l=n+a+c,i.push([n+a,l,"1"]),e.pushOne()):h==2&&f==3?(i.push([l,n+a,"1"]),e.pushOne(),l=n+a+c*2/3,i.push([n+a,l,"1"]),e.pushOne()):h==3&&f==1?(i.push([l,n+a/3,"0"]),e.pushZero(),i.push([n+a/3,n+a,"1"]),e.pushOne(),l=n+a):h==3&&f==2?(i.push([l,n+a/3,"0"]),e.pushZero(),i.push([n+a/3,n+a,"1"]),e.pushOne(),l=n+a+c,i.push([n+a,l,"1"]),e.pushOne()):h==3&&f==3?(i.push([l,n+a/3,"0"]),e.pushZero(),i.push([n,n+a/3,"1"]),e.pushOne(),i.push([n+a/3,n+a,"1"]),e.pushOne(),i.push([n+a,n+a+c*3/2,"1"]),e.pushOne(),l=n+a+c*3/2):h==1?(i.push([l,n+a,"0"]),e.pushZero(),e.pushBreak(),l=n+a):h==2?(i.push([l,n+a,"1"]),e.pushOne(),e.pushBreak(),l=n+a):e.pushBreak(),n+=a+c}return{hints:i,bits:e}}function mt(r,t){const s=t.short,e=new M;let i=[],n=0;for(let l=0;l<r.length;l+=1){const o=r[l],a=~~(o/s+.5);a==1?(i.push([n,n+o,"1"]),e.pushOne()):a==2?(i.push([n,n+o,"0"]),e.pushZero()):e.pushBreak(),n+=o}return{hints:i,bits:e}}/**
@file Pulse Builder JS.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2020
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/function gt(r){return Array.isArray(r)?r:r.split(/\D/).map(s=>parseInt(s,10))}class bt{constructor(t){Object.assign(this,t),this.build()}build(){this.pulses=[],this.warmup_raw&&this.addRaw(this,this.warmup_raw);const t=this.repeat||1;for(let s=0;s<t;++s){if(this.gap&&s>0&&(this.pulses.push(0),this.pulses.push(this.gap)),this.preamble){const i=new M(this.preamble);this.addCode(this,i)}if(this.preamble_raw&&this.addRaw(this,this.preamble_raw),this.sync_raw&&this.addRaw(this,this.sync_raw),this.syncword){const i=new M(this.syncword);this.addCode(this,i)}const e=new M(this.payload);if(this.addCode(this,e),this.postamble){const i=new M(this.postamble);this.addCode(this,i)}this.postamble_raw&&this.addRaw(this,this.postamble_raw)}return this.coalesce(),this.jitter&&this.addJitter(this),this}addCode(t,s){return t.modulation=="PCM"?this.buildPCM(t,s):t.modulation=="MC"?this.buildMC(t,s):t.modulation=="PPM"?this.buildPPM(t,s):t.modulation=="PWM"?this.buildPWM(t,s):t.modulation=="DM"?this.buildDM(t,s):t.modulation=="NRZI"?this.buildNRZI(t,s):t.modulation=="CMI"?this.buildCMI(t,s):t.modulation=="PIWM"?this.buildPIWM(t,s):console.log("Unknown modulation!"),t}addRaw(t,s){return s=gt(s),s.length%2&&s.push(0),t.pulses=t.pulses.concat(s),t}coalesce(){let t=[],s=0;for(let e=0;e<this.pulses.length;e+=2){const i=this.pulses[e]||0,n=this.pulses[e+1]||0;!i&&!n||(!s&&!i&&t.length?t[t.length-1]+=~~n:n?(t.push(~~(i+s)),t.push(~~n),s=0):s+=i)}return s&&(t.push(~~s),t.push(0)),this.pulses=t,this}addJitter(t){const s=t.jitter;let e=[];for(let i=0;i<t.pulses.length;++i){const n=t.pulses[i];if(n==0)e.push(n);else if(s<1){const l=Math.random()*2*s+1-s;e.push(~~(n*l))}else{const l=Math.random()*2*s-s;e.push(~~(n+l))}}return t.pulses=e,t}buildPCM(t,s){return!t.long||t.long==t.short?this.buildNRZ(t,s):this.buildRZ(t,s)}buildNRZ(t,s){const e=t.short;let i=t.pulses;const n=s.toBitArray();for(let l=0;l<n.length;++l)n[l]?(i.push(e),i.push(0)):(i.push(0),i.push(e));return t}buildRZ(t,s){const e=t.short,i=t.long,n=i-e;let l=t.pulses;const o=s.toBitArray();for(let a=0;a<o.length;++a)o[a]?(l.push(e),l.push(n)):(l.push(0),l.push(i));return t}buildPPM(t,s){const e=t.pulse||~~(t.short/3),i=t.short,n=t.long;let l=t.pulses;const o=s.toBitArray();for(let a=0;a<o.length;++a)l.push(e),o[a]?l.push(i):l.push(n);return t}buildPWM(t,s){const e=t.short,i=t.long,n=t.short_gap||t.long,l=t.long_gap||t.short;let o=t.pulses;const a=s.toBitArray();for(let h=0;h<a.length;++h)a[h]?(o.push(e),o.push(n)):(o.push(i),o.push(l));return t}buildMC(t,s){const e=t.short;let i=t.pulses;const n=s.toBitArray();for(let l=0;l<n.length;++l)n[l]?(i.push(e),i.push(e)):(i.push(0),i.push(e),i.push(e),i.push(0));return t}buildDM(t,s){const e=t.short;let i=t.pulses;const n=s.toBitArray();let l=!1;for(let o=0;o<n.length;++o){const a=n[o];!a&&!l?(i.push(e),i.push(e)):!a&&l?(i.push(0),i.push(e),i.push(e),i.push(0)):a&&!l?(i.push(e*2),i.push(0),l=!l):a&&l&&(i.push(0),i.push(e*2),l=!l)}return t}buildNRZI(t,s){const e=t.short;let i=t.pulses;const n=s.toBitArray();let l=!n[0];for(let o=0;o<n.length;++o)n[o]&&(l=!l),l?(i.push(e),i.push(0)):(i.push(0),i.push(e));return t}buildCMI(t,s){const e=t.short;let i=t.pulses;const n=s.toBitArray();let l=!1;for(let o=0;o<n.length;++o)n[o]?l?(i.push(0),i.push(e*2),l=!l):(i.push(e*2),i.push(0),l=!l):(i.push(0),i.push(e),i.push(e),i.push(0));return t}buildPIWM(t,s){const e=t.short;let i=t.pulses;const n=s.toBitArray();for(let l=0;l<n.length;++l)n[l]?i.push(e):i.push(e*2);return n.length%2&&i.push(e*3),t}}/**
@file RfRaw JS.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2020
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/class R{static isRfRaw(t){return t.trim().startsWith("AA")}static getPulses(t){let s=[];t=t.replace(/5\s*5[+\s]+A\s*A\s*B/g,"55;AAB");const e=t.split(";");for(let i of e){const n=R.getCodePulses(i);s=s.concat(n)}return s}static getCodePulses(t){console.log("parsing rfraw data: ",t);let s=[];const e=new z(t);if(e.getByte()!=170)return s;const n=e.getByte();let l,o,a;if(n==177)o=e.getByte();else if(n==176)l=e.getByte(),o=e.getByte(),a=e.getByte();else return s;let h=[];for(let g=0;g<o;++g)h[g]=e.getWord();console.log(`buckets: ${o} (len ${l} repeats ${a}): `,h);let c=!1;const f=e.index;for(;e.hasNibble();)if(e.getNibble()>7){c=!0;break}if(e.index=f,console.log("RfRaw v3 format: ",c),!c){for(s.push(0);e.hasNibble()&&e.peekByte()!=85;){const g=e.getNibble();if(g>o)break;s.push(h[g])}return s.push(0),a?new Array(a).fill(s).flat():s}let p=!0;for(;e.hasNibble()&&e.peekByte()!=85;){const g=e.getNibble(),b=g&7;if(b>o)break;g&8?(p||s.push(0),s.push(h[b]),p=!1):(p&&s.push(0),s.push(h[b]),p=!0)}return p||s.push(0),a?new Array(a).fill(s).flat():s}}/**
@file BroadlinkRM JS.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2025
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/class N{static isBroadlinkRM(t){try{bin=atob(t)}catch{return!1}if(bin.length<4)return!1;const s=bin.charCodeAt(0);return s==178||s==177||s==215?!0:(s==38,!1)}static decodeData(t){let s={pulses:[],frequency:0,repeats:0,errors:""},e="";try{e=atob(t)}catch{return s.errors="Invalid Base64",console.warn(s.errors),s}if(e.length<4)return s.errors="Invalid data header",console.warn(s.errors),s;const i=e.charCodeAt(0);if(i==178||i==177)s.frequency=43392e4;else if(i==215)s.frequency=315e6;else return i==38?(s.errors="Unsupported code type",console.warn(s.errors,i),s):(s.errors=`Invalid code type (${i.toString(16)})`,console.warn(s.errors,i),s);s.repeats=e.charCodeAt(1),(e.charCodeAt(2)|e.charCodeAt(3)<<8)+2!=e.length&&(s.errors="Code length mismatch",console.warn(s.errors));for(let l=4;l<e.length;l++){let o=e.charCodeAt(l);o==0&&(o=e.charCodeAt(++l)|e.charCodeAt(++l)<<8),s.pulses.push(o)}return s}}/**
@file Pulse Data Viewer JS.
@author Christian W. Zuckschwerdt <zany@triq.net>
@copyright Christian W. Zuckschwerdt, 2020
@license
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
*/function wt(r){return new Promise((t,s)=>{var e=new FileReader;e.onerror=()=>{s(e.error)},e.onload=()=>{t(e.result)},e.readAsText(r)})}window.File&&window.FileReader&&window.FileList&&window.Blob||alert("The File APIs are not fully supported in this browser.");let U;window.addEventListener("resize",function(){clearTimeout(U),U=setTimeout(function(){const r=new Event("debouncedResize");window.dispatchEvent(r)},250)},!1);const xt={PCM:"PCM",PWM:"PWM",PPM:"PPM",MC:"MC",DM:"DM",NRZI:"NRZI",CMI:"CMI",PIWM:"PIWM"},yt={ManchesterGET:"Manchester-GET",ManchesterIEEE:"Manchester-IEEE",DifferentialManchester:"Differential-Manchester"},_t=[{text:"off",value:""},{text:"PCM",value:"PCM"},{text:"PWM",value:"PWM"},{text:"PPM",value:"PPM"},{text:"MC",value:"MC"},{text:"DM",value:"DM"},{text:"NRZI",value:"NRZI"},{text:"CMI",value:"CMI"},{text:"PIWM",value:"PIWM"}],vt=[{text:"off",value:""},{text:"Manchester G.E.Thomas",value:"ManchesterGET"},{text:"Manchester IEEE",value:"ManchesterIEEE"},{text:"Differential Manchester",value:"DifferentialManchester"}];class Mt{static get slicerOptions(){return _t}static get codingOptions(){return vt}constructor(t={}){this.constrain={histogram:i=>parseInt(i,10)||0,slicer:i=>X(xt,i)||"",coding:i=>X(yt,i)||""};const s={width:0,scroll:0,zoom:1};this.setTheme(t.theme);const e=this.heightDefaults(t.height);t=Object.assign({},s,e,t),this.tag=Math.random().toString().substring(2),this.parent=I(t.parent),this.data=t.data||{},this.width=t.width||this.parent.clientWidth,this.height=t.height,this.yHi=t.yHi,this.yLo=t.yLo,this.yHintLo=t.yHintLo,this.yHintHi=t.yHintHi,this.yHintText=t.yHintText,this.scroll=t.scroll,this.zoom=t.zoom,this.slicer=t.slicer,this.renderInfo=t.renderInfo,this.opts=t,t.data&&this.setData(t.data,t.fileinfo),t.unlockable&&this.enableUnlockable(),t.scrollzoom&&this.enableScrollZoom(),window.ResizeObserver?(this.resizeObserver=new ResizeObserver(()=>{this.redrawCanvas()}),this.resizeObserver.observe(this.parent)):window.addEventListener("debouncedResize",this,!1)}setTheme(t){let s={hiStroke:"#3c3",hiFill:"rgba(51,204,51,0.1)",hiLine:3,hiDash:[],loStroke:"#c33",loFill:"rgba(204,204,204,0.1)",loLine:3,loDash:[],edgeStroke:"#ccc",edgeLine:1,edgeDash:[],textFill:"#666",dotFill:"#999",hintStroke:"#aaf",hintDash:[3,2],hintLine:1,hintAltStroke:"#f99",hintAltDash:[3,2],hintAltLine:3,hintFill:"#448",timeLabelFill:"#333",timeMinorFill:"#CCC",timeMajorFill:"#666",font:"10px sans-serif"};const e={};return e.dark={hiStroke:"#b0b",hiFill:"rgba(191,0,191,0.2)",hiLine:3,hiDash:[],loStroke:"#27c",loFill:"rgba(102,102,102,0.2)",loLine:3,loDash:[],edgeStroke:"#555",edgeLine:1,edgeDash:[],textFill:"#ccc",dotFill:"#555",hintStroke:"#555",hintDash:[3,2],hintLine:1,hintAltStroke:"#c55",hintAltDash:[3,2],hintAltLine:3,hintFill:"#ccc",timeLabelFill:"#DDD",timeMinorFill:"#666",timeMajorFill:"#999",font:"10px sans-serif"},typeof t=="string"&&e[t]&&(s=e[t]),this.theme=Object.assign({},s,t),this.redrawCanvas()}heightDefaults(t=120){const s=parseInt(this.theme.font,10)||10,e=22+~~(t/10),i=1+~~(t/10);return{height:t,yHi:e+i,yLo:t-s-i,yHintLo:t-s,yHintHi:e,yHintText:t}}enableUnlockable(){if(this.unlockable)return;this.unlockable=!0;const t=this.canvasNode;["dblclick"].forEach(e=>t.addEventListener(e,this,!1))}disableUnlockable(){if(!this.unlockable)return;this.unlockable=!1;const t=this.canvasNode;["dblclick"].forEach(e=>t.removeEventListener(e,this,!1))}enableScrollZoom(){if(this.unlocked)return;this.unlocked=!0;const t=this.canvasNode;let s=["mousedown","mousemove","mouseup","wheel"];this.unlockable||s.push("dblclick"),"ontouchstart"in window&&(s=s.concat("touchstart","touchmove","touchend")),s.forEach(e=>t.addEventListener(e,this,!1))}disableScrollZoom(){if(!this.unlocked)return;this.unlocked=!1;const t=this.canvasNode;let s=["mousedown","mousemove","mouseup","wheel"];this.unlockable||s.push("dblclick"),"ontouchstart"in window&&(s=s.concat("touchstart","touchmove","touchend")),s.forEach(e=>t.removeEventListener(e,this,!1))}destroy(){this.dropZone&&this.dropZone.destroy(),this.resizeObserver&&this.resizeObserver.disconnect(),window.removeEventListener("debouncedResize",this,!1),this.disableScrollZoom()}createDropZone(t){t=t||this.parent.getElementsByClassName("dropzone")[0];const s=this.setData.bind(this);this.dropZone=new Y(t,s)}setSlicer(t){if(t&&t.modulation?this.slicer=t:this.data&&this.data.modulation?this.slicer=this.data:this.slicer=this.guess,!this.data.pulses||!this.data.pulses.length)return;const s=G(this.data.pulses,this.slicer);if(this.data.hints=s.hints,this.data.bits=s.bits,s.bits){const e=this.timingsNode,i=this.messagesNode;this.analyzer.print(e,i),console.log(s.bits.toHexString()),i&&(i.innerHTML+=`<div>Bits: <strong>${s.bits.toHexString()}</strong></div>`)}this.redrawCanvas()}setOption(t,s){return this[t]=this.constrain[t](s),this.processData()}setOptions(t){for(let s in t)this[s]=this.constrain[s](t[s]);return this.processData()}setData(t,s){t instanceof Blob?wt(t).then(e=>{this.data=this.parseData(e,t),this.processData()}):Array.isArray(t)?(this.data={pulses:t},this.processData()):typeof t=="string"?(this.data=this.parseData(t,s),this.processData()):t.pulses?(this.data=t,this.processData()):(this.data=new bt(t),this.processData())}parseData(t,s={}){let e={name:s.name||null,type:s.type||null,size:s.size||t.length,lastModifiedDate:s.lastModifiedDate,lastModified:s.lastModified,pulses:[],format:"OOK",rate:25e4,scale:1};const i=t.split(`
`);for(let n of i){if(!n)continue;if(n.startsWith(";timescale")){e.timescale=n.slice(11);const o=parseFloat(e.timescale);e.timescale.endsWith("ns")?e.scale=o*.001:e.timescale.endsWith("us")?e.scale=o:e.timescale.endsWith("ms")?e.scale=o*1e3:e.timescale.endsWith("s")&&(e.scale=o*1e6)}if(n.startsWith(";created")&&(e.created=n.slice(9)),n.startsWith(";ook")&&(e.format="OOK"),n.startsWith(";fsk")&&(e.format="FSK"),n.startsWith(";freq1")&&(e.freq1=n.slice(7)),n.startsWith(";freq2")&&(e.freq2=n.slice(7)),n.startsWith(";samplerate")&&(e.rate=n.slice(12)),n.startsWith(";centerfreq")&&(e.centerfreq=n.slice(12)),n.startsWith(";sampledepth")&&(e.sampledepth=n.slice(13)),n.startsWith(";range")&&(e.range=n.slice(7)),n.startsWith(";rssi")&&(e.rssi=n.slice(6)),n.startsWith(";snr")&&(e.snr=n.slice(5)),n.startsWith(";noise")&&(e.noise=n.slice(7)),n.startsWith(";slicer")&&(e.slicer=n.slice(8)),n.startsWith(";"))continue;if(R.isRfRaw(n)){const o=R.getPulses(n);e.pulses=e.pulses.concat(o);continue}if(N.isBroadlinkRM(n)){const o=N.decodeData(n);e.pulses=e.pulses.concat(o.pulses);continue}const l=n.split(" ");for(let o of l)if(o.length){const a=parseInt(o,10);e.pulses.push(a*e.scale)}}if(e.freq1=parseFloat(e.freq1),e.freq2=parseFloat(e.freq2),e.rate=parseFloat(e.rate),e.centerfreq=e.centerfreq&&parseFloat(e.centerfreq),e.sampledepth=e.sampledepth&&parseFloat(e.sampledepth),e.range=e.range&&parseFloat(e.range),e.rssi=e.rssi&&parseFloat(e.rssi),e.snr=e.snr&&parseFloat(e.snr),e.noise=e.noise&&parseFloat(e.noise),e.slicer){const n=e.slicer.split(":");e.modulation=n[0],e.short=n[1]&&parseFloat(n[1]),e.long=n[2]&&parseFloat(n[2]),e.sync=n[3]&&parseFloat(n[3]),e.gap=n[4]&&parseFloat(n[4])}return e}get canvasNode(){return this.parent.getElementsByClassName("pulseplot-canvas")[0]||this.parent.getElementsByTagName("canvas")[0]}get fileinfoNode(){return this.parent.getElementsByClassName("pulseplot-fileinfo")[0]||this.parent.getElementsByClassName("fileinfo")[0]}get timingsNode(){return this.parent.getElementsByClassName("pulseplot-timings")[0]}get messagesNode(){return this.parent.getElementsByClassName("pulseplot-messages")[0]}buildInfo(){if(!this.renderInfo&&!this.fileinfoNode)return;const t=this.data||{},s=t.format,e=t.rate,i=Z(e,10),n=t.width/1e6,l=et(n,10),o=t.width/1e6*e,a=t.pulses||[],h=t.lastModifiedDate||new Date(t.lastModified||0),c=[];if(c.push({text:"File name",value:st(t.name)}),c.push({text:"File type",value:t.type||"n/a"}),c.push({text:"File size",value:`${t.size} bytes`}),c.push({text:"Last modified",value:h.toISOString()}),c.push({text:"Sample format",value:s}),c.push({text:"No. of samples",value:`${o.toFixed(0)} S`}),e>1&&c.push({text:"Sample rate",value:`${e/i.scale}${i.prefix}`}),c.push({text:"No. of pulses",value:`${a.length/2} ×`}),e>1&&c.push({text:"Length (time)",value:`${(n/l.scale).toFixed(2)} ${l.prefix}`}),this.renderInfo)this.renderInfo(c);else{let f="";for(let g of c)f+=`<span title="${g.text}">${g.value}</span>`;const p=this.fileinfoNode;p.innerHTML=f}}handleEvent(t){const s=t.type;if(typeof this[s]=="function")return this[s](t)}wheel(t){let s=!1;t.deltaX&&(this.scroll+=t.deltaX*10,s=!0);const e=.5,i=this.data.width/this.width*10;let n;t.deltaY<0?n=this.zoom*(1+t.deltaY*-.2):n=this.zoom/(1+t.deltaY*.2),n<e&&(n=e),n>i&&(n=i),this.zoom!=n&&(this.scroll=(this.scroll+t.layerX)/this.zoom*n-t.layerX,this.zoom=n,s=!0),s&&this.redrawCanvas(),t.preventDefault()}dblclick(t){this.unlockable&&!this.unlocked?this.enableScrollZoom():this.unlockable&&this.unlocked?this.disableScrollZoom():(this.zoom!=1||this.scroll!=0)&&(this.scroll=0,this.zoom=1,this.redrawCanvas()),t.preventDefault()}mousedown(t){this.dragX=t.offsetX,this.dragY=t.offsetY,this.dragOrig=this.scroll,this.isDrag=!0,t.preventDefault()}mouseup(t){this.isDrag=!1,t.preventDefault()}mousemove(t){this.isDrag&&(this.redrawScroll(this.dragX-t.offsetX,this.dragY-t.offsetY),t.stopPropagation(),t.preventDefault())}touchstart(t){const s=t.target.getBoundingClientRect(),e=t.targetTouches[0],i=e.clientX-s.left,n=e.clientY-s.top;this.dragX=i,this.dragY=n,this.dragOrig=this.scroll,this.isDrag=!0,t.targetTouches.length>1&&t.preventDefault()}touchcancel(t){t.targetTouches.length<1&&(this.isDrag=!1,this.dragZoom=!1)}touchend(t){t.targetTouches.length<1&&(this.isDrag=!1,this.dragZoom=!1)}touchmove(t){const s=t.target.getBoundingClientRect();let e=t.targetTouches[0];if(!this.dragZoom&&t.targetTouches.length==1){const i=e.clientX-s.left,n=e.clientY-s.top;this.redrawScroll(this.dragX-i,this.dragY-n)}else if(t.targetTouches.length==2){this.isDrag=!1;const i=t.targetTouches[1],n=e.clientX<i.clientX?i.clientX-e.clientX:e.clientX-i.clientX;this.dragZoom||(this.dragZoom=this.zoom,this.dragDist=n);const l=(e.clientX+i.clientX)/2,o=n/this.dragDist,a=this.dragZoom*o;this.scroll=(this.scroll+l)/this.zoom*a-l,this.zoom=a,this.redrawCanvas(),t.preventDefault()}t.stopPropagation()}debouncedResize(){this.width=this.opts.width||this.parent.clientWidth,this.drawCanvas()}processData(){if(!this.data.pulses||!this.data.pulses.length)return;let t=0;for(let l=0;l<this.data.pulses.length;++l)t+=this.data.pulses[l];this.data.width=t;const s=this.timingsNode,e=this.messagesNode,i=new lt(this.data.pulses);i.console_log(),i.print(s,e),this.analyzer=i,this.guess=i.guess(),this.slicer||(this.data.modulation?this.slicer=this.data:this.slicer=this.guess);const n=G(this.data.pulses,this.slicer);this.data.hints=n.hints,this.data.bits=n.bits,n.bits&&(console.log(n.bits.toHexString()),e&&(e.innerHTML+=`<div>Bits: <strong>${n.bits.toHexString()}</strong></div>`)),this.buildInfo(),this.drawCanvas()}redrawScroll(t){this.scroll=this.dragOrig+t,this.redrawCanvas()}redrawCanvas(){window.requestAnimationFrame(this.drawCanvas.bind(this))}drawCanvas(){this.width=this.opts.width||this.parent.clientWidth;const t=this.width,s=this.height,e=this.canvasNode;e.width=t,e.height=s;const i=e.getContext("2d"),n=-this.scroll,l=t*this.zoom/this.data.width,o=this.yHi+.5,a=this.yLo+.5;i.font=this.theme.font;const h=i.measureText("8"),c=h.width,f=h.actualBoundingBoxAscent+h.actualBoundingBoxDescent,p=this.data.width/1e6/this.zoom,g=n/t*p,b=Z(p,10),x=p/b.scale,y=g/b.scale;i.fillStyle=this.theme.timeLabelFill,i.fillText(`t[${b.prefix}s]`,32,f),this.data.time&&i.fillText(`${this.data.time}`,100,f),(this.data.format||this.data.mod)&&i.fillText(`${(this.data.format||this.data.mod).toUpperCase()}`,300,f),this.data.pulses&&i.fillText(`${this.data.pulses.length/2} pulses`,330,f);const _=this.data.rate_Hz||this.data.samplerate_Hz||this.data.rate||this.data.samplerate;if(_){const u=Z(_,10);i.fillText(`${_/u.scale} ${u.prefix}Hz`,400,f)}const D=this.data.freq_Hz||this.data.centerfreq_Hz||this.data.centerfreq,B=this.data.freq1_Hz||this.data.freq1;if(B){const u=Z(B,10);i.fillText(`${(B/u.scale).toFixed(3)} ${u.prefix}Hz`,500,f)}if(D&&B){i.fillStyle=this.theme.timeMinorFill,i.fillRect(700,~~(f/2),100,1),i.fillStyle=this.theme.timeLabelFill;const u=(B-this.data.freq_Hz)*100/_+50;i.lineWidth=this.theme.hiLine,i.strokeStyle=this.theme.hiStroke,i.beginPath(),i.moveTo(700+u,0),i.lineTo(700+u,f),i.stroke()}const S=this.data.freq2_Hz||this.data.freq2;if(S){const u=Z(S,10);i.fillText(`${(S/u.scale).toFixed(3)} ${u.prefix}Hz`,600,f)}if(D&&S){const u=(S-this.data.freq_Hz)*100/_+50;i.lineWidth=this.theme.loLine,i.strokeStyle=this.theme.loStroke,i.beginPath(),i.moveTo(700+u,0),i.lineTo(700+u,f),i.stroke()}const J=this.data.range_dB||this.data.range||66,A=this.data.rssi_dB||this.data.rssi,V=this.data.snr_dB||this.data.snr,O=this.data.noise_dB||this.data.noise;A&&V&&O&&(i.lineWidth=1,i.strokeStyle=this.theme.loStroke,i.beginPath(),i.moveTo(900,f),i.lineTo(1e3,f),i.stroke(),i.lineWidth=this.theme.hiLine,i.strokeStyle=this.theme.hiStroke,i.beginPath(),i.moveTo(1e3+O*100/J,f),i.lineTo(1e3+A*100/J,f),i.stroke(),i.fillText(`RSSI ${O.toFixed(1)} ${V.toFixed(1)} ${A.toFixed(1)} dB`,900,f));const Pt=t/85;let T=x/Pt;T=~~(T/5)*5,T<1&&(T=1);const Q=t/x,tt=18;i.fillStyle=this.theme.timeMinorFill;for(let u=y;u<x;u+=T/5){u>=x-T&&(u=x);const d=~~(u*Q);i.fillRect(d,tt+10,1,5)}i.fillStyle=this.theme.timeMajorFill;for(let u=y;u<x;u+=T){u>=x-T&&(u=x);const d=~~(u*Q);i.fillRect(d,tt+5,1,10);const w=(u-y).toFixed(0);let v=d-3*w.length;u>=x&&(v=d-6*c),i.fillText(w,v,20)}i.lineWidth=this.theme.hintLine,i.strokeStyle=this.theme.hintStroke,i.setLineDash(this.theme.hintDash),i.beginPath();let C;for(let u=0;this.data.hints&&u<this.data.hints.length;u+=1){const d=this.data.hints[u],w=d[0]*l+n,v=d[1]*l+n;C!=w&&w>=0&&w<t&&(i.moveTo(~~w-.5,this.yHintLo-.5),i.lineTo(~~w-.5,this.yHintHi+.5)),v>=0&&v<t&&(i.moveTo(~~v-.5,this.yHintLo-.5),i.lineTo(~~v-.5,this.yHintHi+.5)),C=v}i.stroke(),i.setLineDash([]),i.lineWidth=this.theme.hintAltLine,i.strokeStyle=this.theme.hintAltStroke,i.setLineDash(this.theme.hintAltDash),i.beginPath(),C=null;for(let u=0;this.data.hints&&u<this.data.hints.length;u+=1){const d=this.data.hints[u],w=d[0]*l+n,v=d[1]*l+n;C!=w&&(C&&C>=0&&C<t&&(i.moveTo(~~C-.5,this.yHintLo-.5),i.lineTo(~~C-.5,this.yHintHi+.5)),w>=0&&w<t&&(i.moveTo(~~w-.5,this.yHintLo-.5),i.lineTo(~~w-.5,this.yHintHi+.5))),C=v}i.stroke(),i.setLineDash([]),i.fillStyle=this.theme.hintFill,i.beginPath();for(let u=0;this.data.hints&&u<this.data.hints.length;u+=1){const d=this.data.hints[u],w=d[0]*l+n,v=d[1]*l+n,F=d[2],L=v-w;if(L>c){const q=w+L/2;q>=0&&q<t&&i.fillText(F,q-5,this.yHintText)}}if(i.stroke(),!this.data.pulses||!this.data.pulses.length)return;let m=n;i.lineWidth=this.theme.hiLine,i.strokeStyle=this.theme.hiStroke,i.setLineDash(this.theme.hiDash),i.fillStyle=this.theme.hiFill,i.beginPath();for(let u=0;u<this.data.pulses.length;u+=2){const d=m;m+=this.data.pulses[u]*l,(d>=0&&d<t||m>=0&&m<t||d<0&&m>t)&&(i.fillRect(~~d,o,~~(m-d),a-o),i.moveTo(~~d-1,o),i.lineTo(~~m+0,o)),m+=this.data.pulses[u+1]*l}i.stroke(),m=n,i.lineWidth=this.theme.loLine,i.strokeStyle=this.theme.loStroke,i.setLineDash(this.theme.loDash),i.fillStyle=this.theme.loFill,i.beginPath();for(let u=0;u<this.data.pulses.length;u+=2){m+=this.data.pulses[u]*l;const d=m;m+=this.data.pulses[u+1]*l,(d>=0&&d<t||m>=0&&m<t||d<0&&m>t)&&(i.fillRect(~~d,o,~~(m-d),a-o),i.moveTo(~~d-1,a),i.lineTo(~~m+0,a))}i.stroke(),m=n,i.lineWidth=this.theme.edgeLine,i.strokeStyle=this.theme.edgeStroke,i.setLineDash(this.theme.edgeDash),i.beginPath();for(let u=0;u<this.data.pulses.length;u+=1)m>=0&&m<t&&(i.moveTo(~~m-.5,a-.5),i.lineTo(~~m-.5,o+.5)),m+=this.data.pulses[u]*l;i.stroke(),m=n,i.fillStyle=this.theme.textFill;const Dt=o+(a-o+f)/2;for(let u=0;u<this.data.pulses.length;u+=1){const d=this.data.pulses[u],w=d*l;if(w>30){const v=m+w/2;v>=0&&v<t&&i.fillText(d,v-10,Dt)}m+=w}const W=1e6/this.data.rate*l;if(m=n,W>2){const u=W>4?2:1;i.fillStyle=this.theme.dotFill;for(let d=0;d<this.data.pulses.length;d+=2){const w=this.data.pulses[d]*l;for(let F=0;F<w-W;F+=W){const L=~~(m+F);L>=0&&L<t&&i.fillRect(L,o+.5,u,3)}m+=w;const v=this.data.pulses[d+1]*l;for(let F=0;F<v-W;F+=W){const L=~~(m+F);L>=0&&L<t&&i.fillRect(L,a-3.5,u,3)}m+=v}}}}function kt(){let r=[];for(let t in K){const s=K[t];r.push({title:s.name,key:t,value:s})}return r}const K={thgr221_mc:{name:"THGR221 MC",format:"OOK",rate:25e4,timescale:"1us",modulation:"MC",warmup_raw:!1,preamble:"fffffe",preamble_raw:!1,syncword:!1,sync_raw:!1,payload:"be28512a95880d82e453fffffebe28512a95880d82e453fffffebe28512a95880d82e452",payload_invert:!1,postamble:!1,postamble_raw:!1,short:500,gap:0,repeat:1,jitter:5},vailant_dm:{name:"Vailant DM",format:"OOK",rate:25e4,timescale:"1us",modulation:"DM",warmup_raw:!1,preamble:!1,preamble_raw:!1,syncword:!1,sync_raw:!1,payload:"{130}ffff814990fffbfffffed2ff4136807fc",payload_invert:!1,postamble:!1,postamble_raw:[0,16764],short:830,gap:0,repeat:2,jitter:10},fody_pcm:{name:"Fody PCM (NRZ)",format:"FSK",rate:25e4,timescale:"1us",modulation:"PCM",warmup_raw:!1,preamble:"00aaaaaaaaaa",preamble_raw:!1,syncword:"2dd4",sync_raw:!1,payload:"f4f5edffffffffffedc8ffffff0b0a12000000000012370000000000",payload_invert:!1,postamble:!1,postamble_raw:!1,short:488,long:!1,gap:0,repeat:1,jitter:4},pcm_rz:{name:"PCM (RZ) test",format:"OOK",rate:25e4,timescale:"1us",modulation:"PCM",warmup_raw:!1,preamble:"aaaaaaa9",preamble_raw:!1,syncword:!1,sync_raw:!1,payload:"a0ff",payload_invert:!1,postamble:!1,postamble_raw:!1,short:500,long:1e3,gap:0,repeat:1,jitter:10},proove_ppm:{name:"Proove PPM",format:"OOK",rate:25e4,timescale:"1us",modulation:"PPM",warmup_raw:!1,preamble:!1,preamble_raw:[280,2600],syncword:!1,sync_raw:!1,payload:"59A6A9AA66666955",payload_invert:!1,postamble:!1,postamble_raw:!1,pulse:260,short:270,long:1300,gap:1e4,repeat:15,jitter:.05},bresser3ch_pwm:{name:"Bresser-3ch PWM",format:"OOK",rate:25e4,timescale:"1us",modulation:"PWM",warmup_raw:!1,preamble:!1,preamble_raw:[750,700,750,700,750,700,750,700],syncword:!1,sync_raw:!1,payload:"e8d9f3bf76",payload_invert:!1,postamble:!1,postamble_raw:!1,short:260,long:500,sync:750,short_gap:470,long_gap:227,gap:1e3,repeat:15,jitter:.05},pwm_fixedgap:{name:"PWM (fixed gap) test",format:"OOK",rate:25e4,timescale:"1us",modulation:"PWM",warmup_raw:!1,preamble:"aaaaaaa9",preamble_raw:!1,syncword:!1,sync_raw:!1,payload:"a0ff",payload_invert:!1,postamble:!1,postamble_raw:!1,short:500,long:1e3,short_gap:500,long_gap:500,gap:0,repeat:1,jitter:.05},klimalogg_nrzi:{name:"Klimalogg NRZI",format:"OOK",rate:25e4,timescale:"1us",modulation:"NRZI",warmup_raw:!1,preamble:"067ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",preamble_raw:!1,syncword:!1,sync_raw:!1,payload:"619fe6600fffc3c333fcffcff30fffc3ffffc33333fcffffffffff8007e0",payload_invert:!1,postamble:!1,postamble_raw:[0,1e4],short:12,long:!1,gap:0,repeat:1,jitter:2}};k.BroadlinkRM=N,k.DropZone=Y,k.Pulseplot=Mt,k.RfRaw=R,k.getDemos=kt,Object.defineProperty(k,Symbol.toStringTag,{value:"Module"})});
//# sourceMappingURL=pulseplot.umd.cjs.map