UNPKG

biquadjs

Version:

Create arbitrary biquad filters to apply to signals. Created for real time biosensing, works in most general cases.

2 lines (1 loc) 5.9 kB
(()=>{var c=class{idx;sps;bandpassLower;bandpassUpper;useSMA4;last4;filtered;trimOutliers;outlierTolerance;useNotch50;useNotch60;useLowpass;lowpassHz;useHighpass;highpassHz;useBandpass;useDCBlock;DCBresonance;useScaling;offset;scalar;notch50;notch60;lp1;bp1;dcb;hp1;constructor(s={sps:512,useSMA4:!1,useNotch50:!1,useNotch60:!1,useLowpass:!1,lowpassHz:100,useHighpass:!1,highpassHz:3,useBandpass:!1,bandpassLower:3,bandpassUpper:45,useDCBlock:!1,DCBresonance:.995,trimOutliers:!1,outlierTolerance:.2,useScaling:!1,scalar:1}){this.idx=0,this.sps=s.sps,this.bandpassLower=s.bandpassLower?s.bandpassLower:3,this.bandpassUpper=s.bandpassUpper?s.bandpassUpper:45,this.useSMA4=s.useSMA4,this.last4=[],this.useNotch50=s.useNotch50,this.useNotch60=s.useNotch60,this.useLowpass=s.useLowpass,this.lowpassHz=s.lowpassHz?s.lowpassHz:100,this.useHighpass=s.useHighpass,this.highpassHz=s.highpassHz?s.highpassHz:3,this.useBandpass=s.useBandpass,this.useDCBlock=s.useDCBlock,this.DCBresonance=s.DCBresonance?s.DCBresonance:.995,this.useScaling=s.useScaling,this.scalar=s.scalar,this.trimOutliers=s.trimOutliers,this.outlierTolerance=s.outlierTolerance,this.offset=s.offset;let t=this.sps;this.notch50=[n(50,t,2),n(100,t,2)],this.notch60=[n(60,t,2),n(120,t,2)],this.lp1=[new e("lowpass",this.lowpassHz,t),new e("lowpass",this.lowpassHz,t),new e("lowpass",this.lowpassHz,t),new e("lowpass",this.lowpassHz,t)],this.hp1=[new e("highpass",this.highpassHz,t)],this.bp1=[r(this.bandpassLower,this.bandpassUpper,t,1),r(this.bandpassLower,this.bandpassUpper,t,1),r(this.bandpassLower,this.bandpassUpper,t,1),r(this.bandpassLower,this.bandpassUpper,t,1)],this.dcb=new o(s.DCBresonance?s.DCBresonance:.995)}reset(s=this.sps){this.notch50=[n(50,s,2),n(100,s,2)],this.notch60=[n(60,s,2),n(120,s,2)],this.lp1=[new e("lowpass",this.lowpassHz,s),new e("lowpass",this.lowpassHz,s),new e("lowpass",this.lowpassHz,s),new e("lowpass",this.lowpassHz,s)],this.hp1=[new e("highpass",this.highpassHz,s),new e("highpass",this.highpassHz,s),new e("highpass",this.highpassHz,s),new e("highpass",this.highpassHz,s)],this.bp1=[r(this.bandpassLower,this.bandpassUpper,s,1),r(this.bandpassLower,this.bandpassUpper,s,1),r(this.bandpassLower,this.bandpassUpper,s,1),r(this.bandpassLower,this.bandpassUpper,s,1)],this.dcb=new o(this.DCBresonance)}setBandpass(s=this.bandpassLower,t=this.bandpassUpper,h=this.sps){this.bandpassLower=s,this.bandpassUpper=t,this.bp1=[r(s,t,h),r(s,t,h),r(s,t,h),r(s,t,h)]}apply(s=0){let t=s;return this.useScaling===!0&&(t*=this.scalar),this.filtered&&this.trimOutliers&&this.outlierTolerance&&Math.abs(t-this.filtered)>this.outlierTolerance&&(t=this.filtered),this.useDCBlock===!0&&(t=this.dcb.applyFilter(t)),this.useSMA4===!0&&(this.last4||(this.last4=[]),this.last4.length<4?this.last4.push(t):(t=this.last4.reduce((h,a)=>h+a)/this.last4.length,this.last4.shift(),this.last4.push(t))),this.useNotch50===!0&&this.notch50.forEach((h,a)=>{t=h.applyFilter(t)}),this.useNotch60===!0&&this.notch60.forEach((h,a)=>{t=h.applyFilter(t)}),this.useLowpass===!0&&this.lp1.forEach((h,a)=>{t=h.applyFilter(t)}),this.useHighpass===!0&&this.hp1.forEach((h,a)=>{t=h.applyFilter(t)}),this.useBandpass===!0&&this.bp1.forEach((h,a)=>{t=h.applyFilter(t)}),this.filtered=t,this.offset&&(t+=this.offset),this.idx++,t}},e=class{type;freq;sps;Q;dbGain;a0=0;a1=0;a2=0;b0=0;b1=0;b2=0;x1=0;x2=0;y1=0;y2=0;constructor(s,t,h,a=1/Math.sqrt(2),i=0){if(["lowpass","highpass","bandpass","notch","peak","lowshelf","highshelf"].indexOf(s)<0){console.error("Valid types: 'lowpass','highpass','bandpass','notch','peak','lowshelf','highshelf'");return}this.type=s,this.freq=t,this.sps=h,this.Q=a,this.dbGain=i;let p=Math.pow(10,i/40),b=2*Math.PI*t/h,u=Math.sin(b),w=Math.cos(b),m=u/(2*a),f=Math.sqrt(p+p);this[s](p,u,w,m,f),this.b0/=this.a0,this.b1/=this.a0,this.b2/=this.a0,this.a1/=this.a0,this.a2/=this.a0}lowpass(s,t,h,a,i){this.b0=(1-h)*.5,this.b1=1-h,this.b2=(1-h)*.5,this.a0=1+a,this.a1=-2*h,this.a2=1-a}highpass(s,t,h,a,i){this.b0=(1+h)*.5,this.b1=-(1+h),this.b2=(1+h)*.5,this.a0=1+a,this.a1=-2*h,this.a2=1-a}bandpass(s,t,h,a,i){this.b0=a,this.b1=0,this.b2=-a,this.a0=1+a,this.a1=-2*h,this.a2=1-a}notch(s,t,h,a,i){this.b0=1,this.b1=-2*h,this.b2=1,this.a0=1+a,this.a1=-2*h,this.a2=1-a}peak(s,t,h,a,i){this.b0=1+a*s,this.b1=-2*h,this.b2=1-a*s,this.a0=1+a/s,this.a1=-2*h,this.a2=1-a/s}lowshelf(s,t,h,a,i){this.b0=s*(s+1-(s-1)*h+i*t),this.b1=2*s*(s-1-(s+1)*h),this.b2=s*(s+1-(s-1)*h-i*t),this.a0=s+1+(s+1)*h+i*t,this.a1=2*(s-1+(s+1)*h),this.a2=s+1+(s-1)*h-i*t}highshelf(s,t,h,a,i){this.b0=s*(s+1+(s-1)*h+i*t),this.b1=2*s*(s-1+(s+1)*h),this.b2=s*(s+1-(s-1)*h-i*t),this.a0=s+1-(s+1)*h-i*t,this.a1=2*(s-1-(s+1)*h),this.a2=s+1-(s-1)*h-i*t}applyFilter(s){let t=this.b0*s+this.b1*this.x1+this.b2*this.x2-this.a1*this.y1-this.a2*this.y2;return this.x2=this.x1,this.x1=s,this.y2=this.y1,this.y1=t,t}zResult(s){try{let t=Math.pow(Math.sin(Math.PI*s*2/(2*this.sps)),2);return(Math.pow(this.b0+this.b1+this.b2,2)-4*(this.b0*this.b1+4*this.b0*this.b2+this.b1*this.b2)*t+16*this.b0*this.b2*t*t)/(Math.pow(1+this.a1+this.a2,2)-4*(this.a1+4*this.a2+this.a1*this.a2)*t+16*this.a2*t*t)}catch{return-200}}static calcCenterFrequency(s,t){return(s+t)/2}static calcBandwidth(s,t){return t-this.calcCenterFrequency(s,t)}static calcBandpassQ(s,t,h=Math.pow(10,Math.floor(Math.log10(s)))){return h*Math.sqrt((s-t)*(s+t))/(2*t)}static calcNotchQ(s,t,h=Math.pow(10,Math.floor(Math.log10(s)))){return h*s*t/Math.sqrt((s-t)*(s+t))}},o=class{r;x1;x2;y1;y2;constructor(s=.995){this.r=s,this.y1=this.y2=this.x1=this.x2=0}applyFilter(s){this.x2=this.x1,this.x1=s;let t=this.x1-this.x2+this.r*this.y1;return this.y2=this.y1,this.y1=t,t}},n=(l,s,t)=>new e("notch",l,s,e.calcNotchQ(l,t),0),r=(l,s,t,h=Math.pow(10,Math.floor(Math.log10(e.calcCenterFrequency(l,s)))))=>new e("bandpass",e.calcCenterFrequency(l,s),t,e.calcBandpassQ(e.calcCenterFrequency(l,s),e.calcBandwidth(l,s),h),0);})();