UNPKG

@rfkit/spectrum-analyzer

Version:

A high-performance spectrum analyzer library for RF signal processing, supporting real-time spectrum analysis, waterfall display, and multi-segment frequency scanning

1 lines 35.7 kB
let t={DEFAULT_CACHE_TIME:15e3,DEFAULT_GRANULARITY:10,DEFAULT_RANGE:[-20,100]},e={cacheTime:t.DEFAULT_CACHE_TIME,granularity:t.DEFAULT_GRANULARITY,range:t.DEFAULT_RANGE,maxPoints:void 0,onLevelStreamUpdate:t=>{}};class a{config;spectrumData=[];probabilityData=new Map;count=0;constructor(t){this.config={...e,...t}}reset(){this.spectrumData=[],this.probabilityData=new Map,this.count=0}setConfig(t){let a=void 0!==t.cacheTime&&this.config.cacheTime!==t.cacheTime||void 0!==t.granularity&&this.config.granularity!==t.granularity||void 0!==t.maxPoints&&this.config.maxPoints!==t.maxPoints;this.config={...e,...this.config,...t},a&&this.reset(),t.range&&this.outputData()}process(t,e){this.removeExpiredData(),this.addNewData(t,e),this.updateProbability(t),this.outputData()}updateProbability(t){let{granularity:e}=this.config;this.count++;let a=Math.round(t/e)*e,s=this.probabilityData.get(a)||0;s++,this.probabilityData.set(a,s)}getAll(){return this.probabilityData}removeExpiredData(){let{cacheTime:t,maxPoints:e}=this.config;if(0===this.spectrumData.length)return;if(void 0!==e&&e>0){this.spectrumData.length>e&&(this.spectrumData=this.spectrumData.slice(-e));return}let a=Date.now();if(t<=0){this.spectrumData=[];return}let s=0,i=this.spectrumData.length-1;for(;s<=i;){let e=Math.floor((s+i)/2);a-this.spectrumData[e].timestamp>=t?s=e+1:i=e-1}this.spectrumData=this.spectrumData.slice(s)}addNewData(t,e){this.spectrumData.push({value:t,timestamp:e})}outputData(){let{range:t,granularity:e,onLevelStreamUpdate:a}=this.config,s=t[0],i=t[1],r=Array(Math.round((i-s)/e)+1).fill(0);for(let[t,a]of this.probabilityData)if(t>=s&&t<=i){let s=Math.round((i-t)/e);this.count>0&&(r[s]=a/this.count*100)}a?.({probabilityRangeData:new Float32Array(r),spectrumData:new Float32Array(this.spectrumData.map(t=>t.value)),timestampData:new Float64Array(this.spectrumData.map(t=>t.timestamp))})}}let s={INITIAL_VALUE:0/0,WATERFALL_MAX_FRAMES:100,OUTPUT_POINTS:1001},i={MAX_PEAKS:2},r=null,n={UPDATE_INTERVAL:1,LEVEL_MIN:-20,LEVEL_MAX:140,LEVEL_RANGE:161},l={maxPoints:s.OUTPUT_POINTS,waterfallMaxFrames:s.WATERFALL_MAX_FRAMES,initialValue:s.INITIAL_VALUE,processing:{enableWaterfall:!1,enableMetrics:!1,enableFluorescence:!1,enablePeakStats:!1,fluorescenceUpdateInterval:n.UPDATE_INTERVAL},peakDetection:{maxPeaks:i.MAX_PEAKS},outputPoints:s.OUTPUT_POINTS,outputRange:{start:0,end:s.OUTPUT_POINTS},templateTolerance:0},h={EMPTY_SEGMENTS:"频段配置不能为空",INVALID_CONFIG:"必须且只能配置 segments 或 bandwidthConfig 其中之一",EMPTY_BANDWIDTH:"bandwidthConfig 不能为空",INVALID_SEGMENT:t=>`无效的段索引: ${t}`,INDEX_OUT_OF_BOUNDS:t=>`索引超出范围: ${t}`,INVALID_ANTENNA_FACTOR_LENGTH:t=>`天线因子数据长度必须等于实时数据长度 (${t})`,INVALID_ANTENNA_FACTOR:"天线因子数据必须是有效的正数",INVALID_SAMPLING_RANGE:"采样范围无效",INVALID_MAX_POINTS:"点数必须大于0",INVALID_LENGTH:t=>`频率占用度数据长度不匹配,期望长度为 ${t}`};class o extends Error{code;details;constructor(t,e,a){super(t),this.code=e,this.details=a,this.name="SpectrumError"}}class u extends o{constructor(t,e){super(t,"DATA_VALIDATION_ERROR",e)}}class c extends o{index;constructor(t,e,a){super(t,"INDEX_OUT_OF_BOUNDS_ERROR",{index:e,...a||{}}),this.index=e}}let p=(t,e)=>{void 0!==t.max&&(e.max=t.max),void 0!==t.maxIndex&&(e.maxIndex=t.maxIndex),void 0!==t.timestamp&&(e.timestamp=t.timestamp),void 0!==t.progress&&(e.progress=t.progress)},g=(t,e,a)=>t<=e?e:t>=a?a:t,D=(t,e)=>e<=1?0:Math.max(0,Math.min(e-1,Math.ceil(e*g(t,0,100)/100)-1)),m=(t,e,a)=>{if(!Number.isFinite(t)||e<=0||a<=0)return;let s=g(Number(t),0,100);return{left:s,sampledIndex:D(s,e),globalIndex:D(s,a)}},f=({realData:t,antennaFactorData:e,antennaFactorSwitch:a=!1,outputPoints:s})=>{let i=t.length,r=i<=s,n=r?i:s,l=new Uint32Array(n),h=new Float32Array(n);if(h.timestamp=t.timestamp,r){if(!a){for(let t=0;t<i;t++)l[t]=t;return h.set(t),{realOutputData:h,srcIndexCache:l}}for(let a=0;a<i;a++)l[a]=a,h[a]=t[a]+e[a];return{realOutputData:h,srcIndexCache:l}}let o=i/s,u=o/2;for(let s=0;s<n;s++){let r=Math.floor(u),n=Math.min(Math.floor(u+o),i),c=t[r],p=r;for(let e=r+1;e<n;e++)t[e]>c&&(c=t[e],p=e);h[s]=a?c+e[p]:c,l[s]=p,u+=o}return{realOutputData:h,srcIndexCache:l}},d=({antennaFactorData:t,antennaFactorSwitch:e=!1,outputPoints:a,realData:s,maxData:i,minData:r,avgData:n,templateData:l,backgroundNoiseData:h,thresholdData:o,fluorescenceData:u,occupancyData:c,enablePeakStats:p=!1,maxPeaks:g=2,outputRangeStart:D=0})=>{let m=s.length,f=m<=a,d=f?m:a,x=i&&i.length>0,A=r&&r.length>0,y=n&&n.length>0,F=l&&l.length>0,w=c&&c.length>0,E=h&&h.length>0,b=o&&o.length>0,I=u&&u.length>0,S=[],N=e?(e,a)=>e+t[a]:t=>t,P=new Float32Array(d);P.timestamp=s.timestamp;let O=new Float32Array(x?d:0),M=new Float32Array(A?d:0),T=new Float32Array(y?d:0),v=new Float32Array(F?d:0),L=new Float32Array(w?d:0),_=new Float32Array(E?d:0),U=new Float32Array(b?d:0),R=Array(I?d:0).fill(null),V=new Uint32Array(d);if(f){for(let t=0;t<m;t++)V[t]=t;if(e)for(let e=0;e<m;e++){let a=t[e];if(P[e]=s[e]+a,x&&(O[e]=i[e]+a),A&&(M[e]=r[e]+a),y&&(T[e]=n[e]+a),F&&(v[e]=l[e]+a),w&&(L[e]=c[e]),E&&(_[e]=h[e]+a),b&&(U[e]=o[e]+a),I){let t=u[e],s=new Map;for(let[e,i]of t)s.set(e+a,i);R[e]=s}}else P.set(s),x&&O.set(i),A&&M.set(r),y&&T.set(n),F&&v.set(l),w&&L.set(c),E&&_.set(h),b&&U.set(o),I&&(R=u);if(p)for(let t=0;t<m;t++){let e=P[t];Number.isFinite(e)&&S.push({index:t,srcIndex:t+D,value:e})}}else{let t=m/a,e=t/2;for(let a=0;a<d;a++){let g=Math.floor(e),f=Math.min(Math.floor(e+t),m),d=s[g],k=g,C=x?i[g]:0,G=g,W=A?r[g]:0,B=g,z=w?c[g]:0;for(let t=g+1;t<f;t++)s[t]>d&&(d=s[t],k=t),x&&i[t]>C&&(C=i[t],G=t),A&&r[t]<W&&(W=r[t],B=t),w&&c[t]>z&&(z=c[t]);let H=N(d,k);if(P[a]=H,V[a]=k,p&&Number.isFinite(H)&&S.push({index:a,srcIndex:k+D,value:H}),x&&(O[a]=N(C,G)),A&&(M[a]=N(W,B)),y){let t=n[k];T[a]=N(t,k)}if(F){let t=l[k];v[a]=N(t,k)}if(w&&(L[a]=z),E){let t=h[k];_[a]=N(t,k)}if(b){let t=o[k];U[a]=N(t,k)}if(I){let t=u[k],e=new Map;for(let[a,s]of t)e.set(N(a,k),s);R[a]=e}e+=t}}return p&&S.length>0&&(S.sort((t,e)=>e.value-t.value),P.peaks={peaks:S.slice(0,g),timestamp:s.timestamp}),{realOutputData:P,maxOutputData:O,minOutputData:M,avgOutputData:T,templateOutputData:v,occupancyOutputData:L,backgroundNoiseOutputData:_,thresholdOutputData:U,srcIndexCache:V,fluorescenceOutputData:R}},x=(t,e,a,s,i)=>{if(!t||0===Object.keys(t).length)return{};let r={},n=Object.keys(t),l=s.length;for(let t=0;t<n.length;t++)r[n[t]]=new Float32Array(l);for(let h=0;h<n.length;h++){let o=n[h],u=i(t[o]),c=r[o];if(a)for(let t=0;t<l;t++){let a=s[t];c[t]=u[a]+e[a]}else for(let t=0;t<l;t++){let e=s[t];c[t]=u[e]}}return r},A=({realData:t,maxData:e,minData:a,templateData:s,tolerance:i=0,startIndex:r=0,endIndex:n,usePreallocation:l=!1})=>{let{timestamp:h}=t,o=n??t.length,u=[],c=-1,p=0,g=0;if(l&&0===r&&o===t.length){let r=new Uint8Array(t.length);for(let e=0;e<t.length;e++)r[e]=+(t[e]>s[e]+i);for(let s=0;s<t.length;s++)if(r[s])-1===c?(c=s,p=t[s],g=1):(p+=t[s],g++);else if(-1!==c){let t=s-1;if(t-c+1>=2){let s=p/g,i=c+(t-c>>1);u.push({maxValue:e[i],minValue:a[i],avgValue:s,timestamp:h,startIndex:c,endIndex:t})}c=-1}}else for(let n=r;n<Math.min(o,t.length);n++)if(t[n]>s[n]+i)-1===c?(c=n,p=t[n],g=1):(p+=t[n],g++);else if(-1!==c){let t=n-1;if(t-c+1>=2){let s=p/g,i=c+(t-c>>1);u.push({maxValue:e[i],minValue:a[i],avgValue:s,timestamp:h,startIndex:c,endIndex:t})}c=-1}if(-1!==c){let t=o-1;if(t-c+1>=2){let s=p/g,i=c+(t-c>>1);u.push({maxValue:e[i],minValue:a[i],avgValue:s,timestamp:h,startIndex:c,endIndex:t})}}return u},y=({realData:t,maxData:e,minData:a,templateData:s,tolerance:i=0})=>s&&0!==s.length?A({realData:t,maxData:e,minData:a,templateData:s,tolerance:i,usePreallocation:!0}):[],F=({realData:t,maxData:e,minData:a,templateData:s,tolerance:i=0,startIndex:r,endIndex:n,previousSegments:l=[]})=>!s||0===s.length||r>=n?l:[...l,...A({realData:t,maxData:e,minData:a,templateData:s,tolerance:i,startIndex:r,endIndex:n})];function w(t){for(let e=0;e<t.length;e++)if(!Number.isFinite(t[e]))return!1;return!0}var E=/*#__PURE__*/function(t){return t.REPLACE="replace",t.MERGE="merge",t.CLEAR="clear",t}({}),b=/*#__PURE__*/null;class I{config=l;segments=[];antennaFactorData;antennaFactorSwitch;realData;maxData;minData;avgData;templateData;occupancyData;backgroundNoiseData;thresholdData;waterfallData;srcIndexCache;realOutputData;maxOutputData;minOutputData;avgOutputData;templateOutputData;occupancyOutputData;backgroundNoiseOutputData;thresholdOutputData;waterfallOutputData;scanProgress;lastIndex;processTimes;lastProcessTime;cachedExceedingDatas=[];fluorescenceData;fluorescenceMaxCount=0;fluorescenceUpdateCounter=0;extraData={};extraOutputData={};pendingDataFlags={occupancy:!1,template:!1,backgroundNoise:!1,threshold:!1,extra:!1};hasPendingData=!1;hasProcessedData=!1;isProcessing=!1;onSpectrumUpdate;constructor(t){let{onSpectrumUpdate:e,maxPoints:a}=t;this.onSpectrumUpdate=e,this.config={...l,...t,processing:{...l.processing,...t.processing},peakDetection:{...l.peakDetection,...t.peakDetection},outputRange:{start:t.outputRange?.start??0,end:t.outputRange?.end??a??s.OUTPUT_POINTS}},this.reset()}process({data:t,timestamp:e,segmentOffset:a=0,offset:s=0,fluorescenceData:i,fluorescenceMaxCount:r}){let n=performance.now();try{if(!t?.length)return;this.isProcessing=!0,this.hasProcessedData=!0;let n=this.resolveGlobalIndex(a,s);this.segments.length?this.scanProgress=(n+t.length)/this.config.maxPoints:t.length!==this.config.maxPoints&&this.updateMaxPoints(t.length),i&&void 0!==r&&(this.fluorescenceData=i,this.fluorescenceMaxCount=r),this.validateInput(t,n);let{maxPoints:l}=this.config,h=n+t.length;this.lastIndex=h;let o=h>=l||h<this.lastIndex;this.realData.set(t,n),this.realData.timestamp=e,this.processDataPoints(t,n,o);let u=this.resampleDataSeries();o&&u.realData&&this.updateWaterfallData(this.realData,u.realData);let c=this.updateTemplateOverData(o,o?void 0:{startIndex:n,endIndex:n+t.length}),p=this.processPendingData();this.notifySpectrumUpdate({...u,...p||{},extraData:this.extraOutputData,waterfallData:this.waterfallOutputData,scanProgress:this.scanProgress,processTimes:this.processTimes,...c&&{templateOverData:c}})}catch(t){throw t instanceof Error?t:Error(String(t))}finally{this.isProcessing=!1,this.lastProcessTime=performance.now()-n}}initializeSegments(t){if(!t?.length)throw new u(h.EMPTY_SEGMENTS);let e=this.config.maxPoints,a=0;this.segments=t.map(t=>{let{startFrequency:e,stopFrequency:s,stepFrequency:i}=t,r=Math.round(1e3*(s-e)/i)+1,n=a;return a+=r,{startFrequency:e,stopFrequency:s,stepFrequency:i,pointCount:r,startIndex:n}}),this.updateMaxPoints(a,!1),e===a&&(this.config={...this.config,outputRange:{start:0,end:a}},this.reset(!1))}setAntennaFactor(t){let{antennaFactorData:e,config:{maxPoints:a}}=this,s=new Float32Array(t);s.length!==a&&(s=new Float32Array(s).subarray(0,a),console.warn(h.INVALID_ANTENNA_FACTOR_LENGTH(a)));let i=!1;for(let t=0;t<s.length;t++){let a=s[t],r=Number.isFinite(a)&&a>0;r||(i=!0),e[t]=r?a:0}i&&console.warn(h.INVALID_ANTENNA_FACTOR),this.antennaFactorSwitch&&this.setAntennaFactorSwitch(this.antennaFactorSwitch)}setAntennaFactorSwitch(t){let e=this.antennaFactorSwitch!==t;if(this.antennaFactorSwitch=t,e){let t=this.resampleDataSeries();this.resampleWaterfallOutputData(),this.notifySpectrumUpdate({...t,waterfallData:this.waterfallOutputData})}}updateProcessing(t){let{processing:e}=this.config,a={...e,...void 0!==t.enableMetrics&&{enableMetrics:t.enableMetrics},...void 0!==t.enableFluorescence&&{enableFluorescence:t.enableFluorescence},...void 0!==t.enablePeakStats&&{enablePeakStats:t.enablePeakStats},...void 0!==t.fluorescenceUpdateInterval&&{fluorescenceUpdateInterval:t.fluorescenceUpdateInterval}};if(a.enableMetrics===e.enableMetrics&&a.enableFluorescence===e.enableFluorescence&&a.enablePeakStats===e.enablePeakStats&&a.fluorescenceUpdateInterval===e.fluorescenceUpdateInterval)return;this.config={...this.config,processing:a},a.enableMetrics&&!e.enableMetrics&&(0===this.maxData.length||0===this.minData.length||0===this.avgData.length)&&(this.allocateMetricsBuffers(),this.clearMetricsBuffers()),a.enableFluorescence&&!e.enableFluorescence&&0===this.fluorescenceData.length&&(this.fluorescenceData=new Uint32Array(this.config.maxPoints*n.LEVEL_RANGE));let s=this.resampleDataSeries();this.extraOutputData=x(this.extraData,this.antennaFactorData,this.antennaFactorSwitch,this.srcIndexCache,this.getOutputData.bind(this)),this.notifySpectrumUpdate({...s,extraData:this.extraOutputData})}clearMetricsData(){let t=0===this.maxData.length||0===this.minData.length||0===this.avgData.length||0===this.maxOutputData.length||0===this.minOutputData.length||0===this.avgOutputData.length;if(this.config.processing.enableMetrics&&t){let t=this.realOutputData?.length??this.config.outputPoints;this.allocateMetricsBuffers(t)}this.clearMetricsBuffers(),this.processTimes=0,this.notifySpectrumUpdate({maxData:this.maxOutputData,minData:this.minOutputData,avgData:this.avgOutputData,processTimes:0})}allocateMetricsBuffers(t){let{maxPoints:e,outputPoints:a,processing:{enableMetrics:s}}=this.config,i=s?e:0,r=0;s&&(r=Math.min(r=Math.max(0,Math.floor(t??a)),a)),this.maxData=new Float32Array(i),this.minData=new Float32Array(i),this.avgData=new Float32Array(i),this.maxOutputData=new Float32Array(r),this.minOutputData=new Float32Array(r),this.avgOutputData=new Float32Array(r)}clearMetricsBuffers(){this.maxData.fill(s.INITIAL_VALUE),this.minData.fill(s.INITIAL_VALUE),this.avgData.fill(s.INITIAL_VALUE),this.maxData.allValid=!1,this.minData.allValid=!1,this.avgData.allValid=!1,this.maxOutputData.fill(s.INITIAL_VALUE),this.minOutputData.fill(s.INITIAL_VALUE),this.avgOutputData.fill(s.INITIAL_VALUE)}setWaterfallData(t){if(!(t?.[0]?.length>0))return;let{processing:e}=this.config,{waterfallMaxFrames:a}=this.config;e.enableWaterfall&&(a=t.length,this.config={...this.config,waterfallMaxFrames:a},this.waterfallData=t,this.resampleWaterfallOutputData(),this.notifySpectrumUpdate({waterfallData:this.waterfallOutputData}))}setFluorescenceData(t,e){if(!this.config.processing.enableFluorescence||!t||0===t.length)return;let a=this.config.maxPoints*n.LEVEL_RANGE;if(t.length!==a)return;this.fluorescenceData=t,this.fluorescenceMaxCount=e;let s=this.config.outputRange.end-this.config.outputRange.start,i=this.getFluorescenceOutputData(s);this.notifySpectrumUpdate({fluorescenceData:i,fluorescenceMaxCount:this.fluorescenceMaxCount})}setRealData(t){if(!(t?.length>0)||t.length!==this.config.maxPoints)return;let e=new Float32Array(t.length);e.set(t),e.timestamp=t.timestamp,this.realData=e;let a=this.resampleDataSeries();this.notifySpectrumUpdate(a)}setMaxData(t){if(!(t?.length>0)||t.length!==this.config.maxPoints)return;let e=new Float32Array(t.length);e.set(t),this.maxData=e,this.maxData.allValid=w(this.maxData);let a=this.resampleDataSeries();this.notifySpectrumUpdate(a)}setMinData(t){if(!(t?.length>0)||t.length!==this.config.maxPoints)return;let e=new Float32Array(t.length);e.set(t),this.minData=e,this.minData.allValid=w(this.minData);let a=this.resampleDataSeries();this.notifySpectrumUpdate(a)}setAvgData(t){if(!(t?.length>0)||t.length!==this.config.maxPoints)return;let e=new Float32Array(t.length);e.set(t),this.avgData=e,this.avgData.allValid=w(this.avgData);let a=this.resampleDataSeries();this.notifySpectrumUpdate(a)}setOccupancyData(t){if(!t||0===t.length){this.occupancyData=new Float32Array(0),this.setPendingFlag("occupancy",!1);return}if(this.occupancyData=new Float32Array(t),!this.hasProcessedData||this.isProcessing)return void this.setPendingFlag("occupancy",!0);let e=this.resampleDataSeries();this.notifySpectrumUpdate({occupancyData:e.occupancyData}),this.setPendingFlag("occupancy",!1)}setExtraData(t,e=E.MERGE,a){if(e===E.CLEAR){this.extraData={},this.extraOutputData={},this.setPendingFlag("extra",!1);return}if(!t){e===E.REPLACE&&(this.extraData={},this.extraOutputData={},this.setPendingFlag("extra",!1));return}let i=Object.entries(t).map(([t,e])=>[t,e?.length?new Float32Array(e):null]),r=a?this.resolveGlobalIndex(a.segmentOffset??0,a.offset??0):0;if(a)for(let[,t]of i)t&&this.validateInput(t,r);for(let[t,n]of(e===E.REPLACE&&(this.extraData={},this.extraOutputData={},this.setPendingFlag("extra",!1)),i)){if(!n){delete this.extraData[t];continue}if(!a){this.extraData[t]=n;continue}let e=this.extraData[t];if(e?.length!==this.config.maxPoints){let a=new Float32Array(this.config.maxPoints);a.fill(s.INITIAL_VALUE),e?.length&&a.set(e.subarray(0,this.config.maxPoints)),e=a,this.extraData[t]=e}e.set(n,r)}!this.hasProcessedData||this.isProcessing?this.setPendingFlag("extra",!0):(this.extraOutputData=x(this.extraData,this.antennaFactorData,this.antennaFactorSwitch,this.srcIndexCache,this.getOutputData.bind(this)),this.notifySpectrumUpdate({extraData:this.extraOutputData}),this.setPendingFlag("extra",!1))}reset(t=!1){let{maxPoints:e,waterfallMaxFrames:a,processing:{enableFluorescence:i}}=this.config;this.antennaFactorData=new Float32Array(e);let r=new Float32Array(e);r.timestamp="",r.fill(s.INITIAL_VALUE),this.realData=r,this.allocateMetricsBuffers(),this.clearMetricsBuffers(),this.templateData=new Float32Array,this.occupancyData=new Float32Array,this.backgroundNoiseData=new Float32Array,this.clearThresholdDataState(),this.waterfallData=Array.from({length:a},()=>{let t=new Float32Array;return t.timestamp="",t.fill(s.INITIAL_VALUE),t}),this.waterfallOutputData=Array.from({length:a},()=>{let t=new Float32Array;return t.timestamp="",t.fill(s.INITIAL_VALUE),t});let l=this.config.outputPoints;this.realOutputData=new Float32Array(l).fill(s.INITIAL_VALUE),this.templateOutputData=new Float32Array,this.occupancyOutputData=new Float32Array,this.backgroundNoiseOutputData=new Float32Array,this.fluorescenceData=new Uint32Array(i?e*n.LEVEL_RANGE:0),this.fluorescenceMaxCount=0,this.fluorescenceUpdateCounter=0,this.extraData={},this.extraOutputData={},this.srcIndexCache=new Uint32Array(e),this.scanProgress=0,this.lastIndex=0,this.processTimes=0,this.lastProcessTime=0,this.cachedExceedingDatas=[],this.pendingDataFlags={occupancy:!1,template:!1,backgroundNoise:!1,threshold:!1,extra:!1},this.hasPendingData=!1,t||(this.hasProcessedData=!1),this.notifySpectrumUpdate({realData:this.realData,maxData:this.maxData,minData:this.minData,avgData:this.avgData,templateData:this.templateData,backgroundNoiseData:this.backgroundNoiseData,thresholdData:this.thresholdData,extraData:this.extraData,srcIndexCache:this.srcIndexCache,processTimes:0,scanProgress:0})}getPerformanceMetrics(){return{lastProcessTime:this.lastProcessTime,dataPoints:this.config.maxPoints,waterfallFrames:this.waterfallData.length,isInitialized:this.realData.length>0,memoryUsage:this.calculateMemoryUsage()}}getPeakStats(){if(!this.config.processing.enablePeakStats)return;let{realData:t}=this.resampleDataSeries();if(t)return t.peaks}updateSamplingRange(t,e){if(t<0||t>=e)throw new u(h.INVALID_SAMPLING_RANGE);this.config={...this.config,outputRange:{start:Math.floor(t),end:Math.ceil(e)}};let a=this.resampleDataSeries();return this.resampleWaterfallOutputData(),this.extraOutputData=x(this.extraData,this.antennaFactorData,this.antennaFactorSwitch,this.srcIndexCache,this.getOutputData.bind(this)),this.notifySpectrumUpdate({...a,extraData:this.extraOutputData,waterfallData:this.waterfallOutputData}),this.srcIndexCache}setTemplateData(t,e){if(!t||0===t.length){this.templateData=new Float32Array(0),this.setPendingFlag("template",!1);return}(void 0!==e&&(this.config.templateTolerance=e),this.templateData=new Float32Array(t),!this.hasProcessedData||this.isProcessing)?this.setPendingFlag("template",!0):(this.notifySpectrumUpdate({templateData:this.resampleSingleData(this.templateData),templateOverData:this.updateTemplateOverData(!0)}),this.setPendingFlag("template",!1))}setBackgroundNoiseData(t){if(!t||0===t.length){this.backgroundNoiseData=new Float32Array(0),this.setPendingFlag("backgroundNoise",!1);return}(this.backgroundNoiseData=new Float32Array(t),!this.hasProcessedData||this.isProcessing)?this.setPendingFlag("backgroundNoise",!0):(this.notifySpectrumUpdate({backgroundNoiseData:this.resampleSingleData(this.backgroundNoiseData)}),this.setPendingFlag("backgroundNoise",!1))}setThresholdData(t,e){if(!t||0===t.length)return void this.clearThresholdDataState();let a=t instanceof Float32Array?t:new Float32Array(t);if(e){let t=this.resolveGlobalIndex(e.segmentOffset??0,e.offset??0);if(this.validateInput(a,t),this.thresholdData.length!==this.config.maxPoints){let t=new Float32Array(this.config.maxPoints);t.fill(s.INITIAL_VALUE),this.thresholdData.length>0&&t.set(this.thresholdData.subarray(0,this.config.maxPoints)),this.thresholdData=t}this.thresholdData.set(a,t)}else{let t=new Float32Array(this.config.maxPoints);t.fill(s.INITIAL_VALUE),t.set(a.subarray(0,this.config.maxPoints)),this.thresholdData=t}if(!this.hasProcessedData||this.isProcessing)return void this.setPendingFlag("threshold",!0);let i=this.resampleSingleData(this.thresholdData);this.thresholdOutputData=i,this.notifySpectrumUpdate({thresholdData:i}),this.setPendingFlag("threshold",!1)}updateTemplateOverData(t=!1,e){let a;return this.templateData&&0!==this.templateData.length?(t||!e?(a=y({realData:this.realData,maxData:this.maxData,minData:this.minData,templateData:this.templateData,tolerance:this.config.templateTolerance}),this.cachedExceedingDatas=a):(this.cachedExceedingDatas=F({realData:this.realData,maxData:this.maxData,minData:this.minData,templateData:this.templateData,tolerance:this.config.templateTolerance,startIndex:e.startIndex,endIndex:e.endIndex,previousSegments:this.cachedExceedingDatas}),a=this.cachedExceedingDatas),a):(this.cachedExceedingDatas=[],[])}resolveGlobalIndex(t=0,e=0){if(this.segments.length){let a=this.segments[t];if(!a)throw new u(h.INVALID_SEGMENT(t));return a.startIndex+e}return e}clearThresholdDataState(){this.thresholdData=new Float32Array(0),this.thresholdOutputData=new Float32Array(0),this.setPendingFlag("threshold",!1)}resampleDataSeries(){let{antennaFactorData:t,antennaFactorSwitch:e,maxData:a,minData:s,avgData:i,templateData:r,occupancyData:n,backgroundNoiseData:l,thresholdData:h,config:{maxPoints:o,outputPoints:u}}=this,{realOutputData:c,srcIndexCache:p,maxOutputData:g,minOutputData:D,avgOutputData:m,templateOutputData:f,occupancyOutputData:x,backgroundNoiseOutputData:A,thresholdOutputData:y}=d({antennaFactorData:e?t:new Float32Array(o),antennaFactorSwitch:e,outputPoints:u,realData:this.getOutputData(),maxData:a?.length>0?this.getOutputData(a):void 0,minData:s?.length>0?this.getOutputData(s):void 0,avgData:i&&i.length>0?this.getOutputData(i):void 0,templateData:r&&r.length>0?this.getOutputData(r):void 0,occupancyData:n&&n.length>0?this.getOutputData(n):void 0,backgroundNoiseData:l&&l.length>0?this.getOutputData(l):void 0,thresholdData:h&&h.length>0?this.getOutputData(h):void 0,enablePeakStats:this.config.processing.enablePeakStats,maxPeaks:this.config.peakDetection.maxPeaks,outputRangeStart:this.config.outputRange.start});this.srcIndexCache=p,this.realOutputData=c,this.maxOutputData=g||new Float32Array,this.minOutputData=D||new Float32Array,this.avgOutputData=m||new Float32Array,this.templateOutputData=f||new Float32Array,this.occupancyOutputData=x||new Float32Array,this.backgroundNoiseOutputData=A||new Float32Array,this.thresholdOutputData=y||new Float32Array;let F=this.getFluorescenceOutputData(c.length);return{realData:c,maxData:g,minData:D,avgData:m,templateData:f,occupancyData:x,backgroundNoiseData:A,thresholdData:y,fluorescenceData:F,fluorescenceMaxCount:this.fluorescenceMaxCount,extraData:this.extraOutputData,srcIndexCache:p}}resampleWaterfallOutputData(){let{antennaFactorData:t,antennaFactorSwitch:e,waterfallData:a,config:{maxPoints:s,processing:{enableWaterfall:i},outputPoints:r}}=this;if(!i)return;let n=e?t:new Float32Array(s);this.waterfallOutputData=a.map(t=>{let{realOutputData:a}=d({realData:this.getOutputData(t),antennaFactorData:n,antennaFactorSwitch:e,outputPoints:r});return a})}updateWaterfallData(t,e){let{waterfallMaxFrames:a,processing:s}=this.config;if(!s.enableWaterfall)return;this.waterfallData.length>=a&&(this.waterfallData.shift(),this.waterfallOutputData.shift());let i=new Float32Array(t.length);i.set(t),i.timestamp=t.timestamp,this.waterfallData.push(i),this.waterfallOutputData.push(e)}updateMaxPoints(t,e=!0){this.config.maxPoints!==t&&(this.config={...this.config,maxPoints:t,outputRange:{start:0,end:t}},this.reset(e))}validateInput(t,e){if(e<0||e+t.length>this.config.maxPoints)throw new c(h.INDEX_OUT_OF_BOUNDS(e),e)}processDataPoints(t,e,a){let{maxData:s,minData:i,avgData:r,config:{processing:{enableMetrics:n}}}=this;n&&a&&(this.processTimes+=1,s.allValid||(s.allValid=w(s)),i.allValid||(i.allValid=w(i)),r.allValid||(r.allValid=w(r)));let l=t.length;if(this.config.processing.enableFluorescence)for(let a=0;a<l;a++)this.updateFluorescenceStats(e+a,t[a]);if(!n)return;let h=!0;for(let e=0;e<l;e++)if(!Number.isFinite(t[e])){h=!1;break}for(let a=0;a<l;a++){let n=e+a,l=t[a];if(!h&&!Number.isFinite(l))continue;let o=s[n];(l>o||!s.allValid&&Number.isNaN(o))&&(s[n]=l);let u=i[n];(l<u||!i.allValid&&Number.isNaN(u))&&(i[n]=l);let c=r[n];0===this.processTimes||Number.isNaN(c)?r[n]=l:r[n]=c+(l-c)/(this.processTimes+1)}}updateFluorescenceStats(t,e){if(!this.config.processing.enableFluorescence||!this.fluorescenceData||!Number.isFinite(e))return;let a=Math.round(e);if(a<n.LEVEL_MIN||a>n.LEVEL_MAX)return;let s=t*n.LEVEL_RANGE+(a-n.LEVEL_MIN),i=++this.fluorescenceData[s];i>this.fluorescenceMaxCount&&(this.fluorescenceMaxCount=i)}setPendingFlag(t,e){this.pendingDataFlags[t]=e,this.hasPendingData=this.pendingDataFlags.occupancy||this.pendingDataFlags.template||this.pendingDataFlags.backgroundNoise||this.pendingDataFlags.threshold||this.pendingDataFlags.extra}resampleSingleData(t){let{srcIndexCache:e}=this,{outputRange:{start:a,end:s}}=this.config,i=t.subarray(a,s),r=e.length,n=new Float32Array(r);for(let t=0;t<r;t++)n[t]=i[e[t]];return n}processPendingData(){if(!this.hasPendingData)return null;let t={};if(this.pendingDataFlags.occupancy&&this.occupancyData.length>0&&(t.occupancyData=this.resampleDataSeries().occupancyData,this.setPendingFlag("occupancy",!1)),this.pendingDataFlags.template&&this.templateData.length>0&&(t.templateData=this.resampleSingleData(this.templateData),t.templateOverData=this.updateTemplateOverData(!0),this.setPendingFlag("template",!1)),this.pendingDataFlags.backgroundNoise&&this.backgroundNoiseData.length>0&&(t.backgroundNoiseData=this.resampleSingleData(this.backgroundNoiseData),this.setPendingFlag("backgroundNoise",!1)),this.pendingDataFlags.threshold&&this.thresholdData.length>0){let e=this.resampleSingleData(this.thresholdData);this.thresholdOutputData=e,t.thresholdData=e,this.setPendingFlag("threshold",!1)}return this.pendingDataFlags.extra&&Object.keys(this.extraData).length>0&&(this.extraOutputData=x(this.extraData,this.antennaFactorData,this.antennaFactorSwitch,this.srcIndexCache,this.getOutputData.bind(this)),t.extraData=this.extraOutputData,this.setPendingFlag("extra",!1)),t}notifySpectrumUpdate(t){this.onSpectrumUpdate?.(t)}calculateMemoryUsage(){return 4*(this.config.maxPoints*Float32Array.BYTES_PER_ELEMENT)+this.waterfallData.reduce((t,e)=>t+e.length*Float32Array.BYTES_PER_ELEMENT,0)+this.waterfallOutputData.reduce((t,e)=>t+e.length*Float32Array.BYTES_PER_ELEMENT,0)}getOutputData(t){let{outputRange:{start:e,end:a}}=this.config,s=t??this.realData,i=s.subarray(e,a);return i.timestamp=s.timestamp,i}getFluorescenceOutputData(t){let{processing:{enableFluorescence:e},outputRange:{start:a,end:s}}=this.config;if(!e||0===this.fluorescenceData.length||t<=0)return;let i=new Uint32Array(t*n.LEVEL_RANGE),r=(s-a)/t;for(let e=0;e<t;e++){let t=Math.floor(a+(e+.5)*r)*n.LEVEL_RANGE,s=e*n.LEVEL_RANGE;for(let e=0;e<n.LEVEL_RANGE;e++)i[s+e]=this.fluorescenceData[t+e]}return i}getAllRawData(t){let{realOutputData:e,maxOutputData:a,minOutputData:s,avgOutputData:i,templateOutputData:r,occupancyOutputData:n,backgroundNoiseOutputData:l,thresholdOutputData:h,waterfallOutputData:o,extraOutputData:u,srcIndexCache:c}=this;e.timestamp=this.realData.timestamp;let p={realData:this.realData,maxData:this.maxData,minData:this.minData,avgData:this.avgData,templateData:this.templateData,occupancyData:this.occupancyData,backgroundNoiseData:this.backgroundNoiseData,thresholdData:this.thresholdData,waterfallData:this.waterfallData,extraData:this.extraData},g=m(t,e.length,this.config.maxPoints);return{realData:e,maxData:a,minData:s,avgData:i,templateData:r,occupancyData:n,backgroundNoiseData:l,thresholdData:h,fluorescenceData:this.getFluorescenceOutputData(e.length),waterfallData:o,extraData:u,srcIndexCache:c,rawData:p,cursorIndex:g}}}let S=()=>({data:[],rawData:[],range:{x:[0,0],y:[0,0]},sourceSize:{width:0,height:0},columnIndices:new Uint32Array(0),rowIndices:new Uint32Array(0)}),N=t=>{let e=new Float32Array(t);return e.timestamp=t.timestamp,void 0!==t.allValid&&(e.allValid=t.allValid),t.peaks&&(e.peaks={...t.peaks,peaks:t.peaks.peaks.map(t=>({...t}))}),e},P=(t,e)=>{if(!Number.isInteger(t)||t<=0)throw new u(`${e} 必须是正整数`)},O=(t,e,a)=>{let[s,i]=t;if(!Number.isFinite(s)||!Number.isFinite(i)||s>=i)throw new u(`${a} 采样范围无效`);let r=Math.max(0,Math.min(e,Math.floor(s))),n=Math.max(0,Math.min(e,Math.ceil(i)));if(r>=n)throw new u(`${a} 采样范围超出数据边界`);return[r,n]},M=(t,e,a)=>{if(0===t[0]&&t[1]===e)return[0,a];let s=Math.min(t[0],a-1),i=Math.max(s+1,Math.min(t[1],a));return[s,i]},T=(t,e,a)=>{if(0===t[0]&&t[1]===e)return[0,a];if(t[1]===e){let e=Math.min(t[1]-t[0],a);return[a-e,a]}return M(t,e,a)},v=(t,e,a,s)=>{if(0===t[0]&&t[1]===e)return[0,a];let i=Math.min(t[1]-t[0],a);if(t[1]===e)return[a-i,a];let r=Math.max(0,Math.min(t[0]-s,a-i));return[r,r+i]},L=(t,e)=>{let[a,s]=t,i=s-a,r=Math.min(i,e),n=new Uint32Array(r);if(i<=e){for(let t=0;t<r;t++)n[t]=a+t;return n}for(let t=0;t<r;t++){let e=Math.floor(t*i/r),s=Math.floor((t+1)*i/r);n[t]=a+Math.min(s-1,Math.floor((e+s)/2))}return n},_=(t,e)=>t[0]===e[0]&&t[1]===e[1],U=(t,e)=>{if(t.length!==e.length)return!1;for(let a=0;a<t.length;a++)if(t[a]!==e[a])return!1;return!0},R=(t,e,a)=>{let s=t.subarray(e[0],e[1]),i=s.length,r=Math.min(i,a);if(i<=a)return 0===e[0]&&e[1]===t.length?t:(s.timestamp=t.timestamp,s);let n=new Float32Array(r);n.timestamp=t.timestamp;for(let t=0;t<r;t++){let e=Math.floor(t*i/r),a=Math.floor((t+1)*i/r),l=0/0;for(let t=e;t<a;t++){let e=s[t];Number.isFinite(e)&&(!Number.isFinite(l)||e>l)&&(l=e)}n[t]=l}return n};class V{maxFrames;onWaterfallUpdate;sourceData=[];samplingOptions;snapshot=S();constructor(t={}){if(void 0!==t.maxFrames&&(!Number.isInteger(t.maxFrames)||t.maxFrames<=0))throw new u("maxFrames 必须是正整数");this.maxFrames=t.maxFrames,this.onWaterfallUpdate=t.onWaterfallUpdate}setData(t){if(0===t.length)return this.sourceData=[],this.samplingOptions=void 0,this.publishSnapshot(S());let e=t[0].length;if(0===e)throw new u("瀑布图帧不能为空");for(let a of t)if(a.length!==e)throw new u("瀑布图数据必须是等宽矩阵");let a=this.snapshot.sourceSize,s=[...t];void 0!==this.maxFrames&&s.length>this.maxFrames&&(s=s.slice(-this.maxFrames));let i=this.samplingOptions;i&&a.width>0&&a.height>0&&(i={...i,x:M(i.x,a.width,e),y:T(i.y,a.height,s.length)});let r=this.createSnapshot(s,i);return this.sourceData=s,this.samplingOptions=i,this.publishSnapshot(r)}appendFrame(t){if(0===t.length)throw new u("瀑布图帧不能为空");let e=this.sourceData[0]?.length;if(void 0!==e&&t.length!==e)throw new u("追加帧宽度必须与现有瀑布图一致");let a=this.sourceData.length,s=[...this.sourceData,N(t)],i=0;void 0!==this.maxFrames&&s.length>this.maxFrames&&(i=s.length-this.maxFrames,s.splice(0,i));let r=this.samplingOptions;r&&a>0&&(r={...r,y:v(r.y,a,s.length,i)});let n=this.createSnapshot(s,r);return this.sourceData=s,this.samplingOptions=r,this.publishSnapshot(n)}updateSamplingRange(t){let e=this.getSourceSize(this.sourceData);if(0===e.width||0===e.height)throw new u("设置采样范围前必须先设置瀑布图数据");P(t.outputWidth,"outputWidth"),P(t.outputHeight,"outputHeight");let a={x:O(t.x,e.width,"X"),y:O(t.y,e.height,"Y"),outputWidth:t.outputWidth,outputHeight:t.outputHeight},s=this.createSnapshot(this.sourceData,a);return this.samplingOptions=a,this.publishSnapshot(s)}getSnapshot(){return this.snapshot}createSnapshot(t,e){let a=this.getSourceSize(t);if(0===a.width||0===a.height)return S();let s=e?.x??[0,a.width],i=e?.y??[0,a.height],r=e?.outputWidth??s[1]-s[0],n=e?.outputHeight??i[1]-i[0],l=L(s,r),h=L(i,n),o=this.createReusableRows(s,l);return{data:Array.from(h,e=>{let a=t[e];return o.get(a)??R(a,s,r)}),rawData:t,range:{x:s,y:i},sourceSize:a,columnIndices:l,rowIndices:h}}createReusableRows(t,e){let a=this.snapshot;if(!_(a.range.x,t)||!U(a.columnIndices,e))return new Map;let s=new Map;for(let t=0;t<a.rowIndices.length;t++){let e=a.rawData[a.rowIndices[t]],i=a.data[t];e&&i&&s.set(e,i)}return s}getSourceSize(t){return{width:t[0]?.length??0,height:t.length}}publishSnapshot(t){return this.snapshot=t,this.onWaterfallUpdate?.(t),t}}var k=/*#__PURE__*/function(t){return t.Horizontal="horizontal",t.Vertical="vertical",t}({}),C=/*#__PURE__*/function(t){return t.Circle="circle",t.Rect="rect",t.Line="line",t.Stepline="stepline",t.Bar="bar",t.Area="area",t}({}),G=/*#__PURE__*/function(t){return t.PropertyChanged="propertyChanged",t.SeriesAdded="seriesAdded",t.SeriesRemoved="seriesRemoved",t.SeriesCleared="seriesCleared",t}({});let W={thickness:1,orientation:k.Horizontal,display:!0,type:C.Line,color:"#00000000",label:""};class B{seriesMap=new Map;changeCallbacks=[];subscribers=new Map;constructor(t=[]){this.initializeSeries(t)}initializeSeries(t){for(let e of t)this.addSeries(e)}addSeries(t){let e={...W,...t};this.seriesMap.set(t.name,e),this.emitEvent({type:G.SeriesAdded,name:t.name,series:e})}getSeries(t){return this.seriesMap.get(t)}getAllSeries(){return Array.from(this.seriesMap.values())}getAllConfigs(){let t={};for(let[e,a]of this.seriesMap)t[e]={...a};return t}setSeriesProperty(t,e,a){let s=this.seriesMap.get(t);if(!s||"name"===e)return!1;let i=s[e],r={...s,[e]:a};return this.seriesMap.set(t,r),this.notifyChange(t,e,a),this.emitEvent({type:G.PropertyChanged,name:t,property:e,value:a,oldValue:i,series:r}),!0}removeSeries(t){let e=this.seriesMap.get(t);if(!e)return!1;let a=this.seriesMap.delete(t);return a&&this.emitEvent({type:G.SeriesRemoved,name:t,series:e}),a}hasSeries(t){return this.seriesMap.has(t)}onChange(t){this.changeCallbacks.push(t)}offChange(t){let e=this.changeCallbacks.indexOf(t);e>-1&&this.changeCallbacks.splice(e,1)}notifyChange(t,e,a){for(let s of this.changeCallbacks)s(t,e,a)}clear(){this.seriesMap.clear(),this.emitEvent({type:G.SeriesCleared})}size(){return this.seriesMap.size}subscribe(t,e={}){if(this.subscribers.set(t,e),e.immediate)for(let e of this.seriesMap.values())this.shouldNotifySubscriber(t,{type:G.SeriesAdded,name:e.name,series:e})&&t({type:G.SeriesAdded,name:e.name,series:e});return()=>{this.subscribers.delete(t)}}unsubscribe(t){this.subscribers.delete(t)}emitEvent(t){for(let[e]of this.subscribers)this.shouldNotifySubscriber(e,t)&&e(t)}shouldNotifySubscriber(t,e){let a=this.subscribers.get(t);return!!a&&(!a.eventTypes||!!a.eventTypes.includes(e.type))&&(!a.seriesNames||!e.name||!!a.seriesNames.includes(e.name))}}export{u as DataValidationError,h as ERROR_MESSAGES,c as IndexOutOfBoundsError,a as LevelStreamAnalyzer,s as SPECTRUM,B as SeriesManager,I as SpectrumAnalyzer,o as SpectrumError,V as WaterfallAnalyzer,E as ExtraDataMode,C as GraphicType,k as OrientationType,G as SeriesEventType};