@rfkit/spectrum-analyzer 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +22 -30
  2. package/core/LevelStreamAnalyzer/constants.d.ts +0 -1
  3. package/core/LevelStreamAnalyzer/index.d.ts +0 -38
  4. package/core/LevelStreamAnalyzer/types.d.ts +0 -1
  5. package/core/SeriesManager/constants.d.ts +2 -3
  6. package/core/SeriesManager/index.d.ts +0 -88
  7. package/core/SeriesManager/types.d.ts +0 -1
  8. package/core/SpectrumAnalyzer/constants.d.ts +6 -7
  9. package/core/SpectrumAnalyzer/errors.d.ts +2 -6
  10. package/core/SpectrumAnalyzer/index.d.ts +2 -184
  11. package/core/SpectrumAnalyzer/tools.d.ts +0 -36
  12. package/core/SpectrumAnalyzer/types.d.ts +4 -5
  13. package/core/WaterfallAnalyzer/index.d.ts +0 -4
  14. package/core/WaterfallAnalyzer/types.d.ts +0 -7
  15. package/index.d.ts +2 -2
  16. package/index.js +1 -1
  17. package/package.json +1 -1
  18. package/wasm/core/LevelStreamAnalyzer/constants.d.ts +0 -1
  19. package/wasm/core/LevelStreamAnalyzer/index.d.ts +0 -38
  20. package/wasm/core/LevelStreamAnalyzer/types.d.ts +0 -1
  21. package/wasm/core/SeriesManager/constants.d.ts +2 -3
  22. package/wasm/core/SeriesManager/index.d.ts +0 -88
  23. package/wasm/core/SeriesManager/types.d.ts +0 -1
  24. package/wasm/core/SpectrumAnalyzer/constants.d.ts +6 -7
  25. package/wasm/core/SpectrumAnalyzer/errors.d.ts +2 -6
  26. package/wasm/core/SpectrumAnalyzer/index.d.ts +2 -184
  27. package/wasm/core/SpectrumAnalyzer/tools.d.ts +0 -36
  28. package/wasm/core/SpectrumAnalyzer/types.d.ts +4 -5
  29. package/wasm/core/WaterfallAnalyzer/index.d.ts +0 -4
  30. package/wasm/core/WaterfallAnalyzer/types.d.ts +0 -7
  31. package/wasm/index.d.ts +2 -2
  32. package/wasm/index.js +1 -1
  33. package/wasm/src-rust/pkg/rfkit_spectrum_analyzer_wasm.js +2 -2
  34. package/wasm/src-rust/pkg/rfkit_spectrum_analyzer_wasm_bg.wasm +0 -0
  35. package/core/LevelStreamAnalyzer/constants.d.ts.map +0 -1
  36. package/core/LevelStreamAnalyzer/index.d.ts.map +0 -1
  37. package/core/LevelStreamAnalyzer/types.d.ts.map +0 -1
  38. package/core/SeriesManager/constants.d.ts.map +0 -1
  39. package/core/SeriesManager/index.d.ts.map +0 -1
  40. package/core/SeriesManager/types.d.ts.map +0 -1
  41. package/core/SpectrumAnalyzer/constants.d.ts.map +0 -1
  42. package/core/SpectrumAnalyzer/errors.d.ts.map +0 -1
  43. package/core/SpectrumAnalyzer/index.d.ts.map +0 -1
  44. package/core/SpectrumAnalyzer/tools.d.ts.map +0 -1
  45. package/core/SpectrumAnalyzer/types.d.ts.map +0 -1
  46. package/core/WaterfallAnalyzer/index.d.ts.map +0 -1
  47. package/core/WaterfallAnalyzer/types.d.ts.map +0 -1
  48. package/index.d.ts.map +0 -1
  49. package/wasm/core/LevelStreamAnalyzer/constants.d.ts.map +0 -1
  50. package/wasm/core/LevelStreamAnalyzer/index.d.ts.map +0 -1
  51. package/wasm/core/LevelStreamAnalyzer/types.d.ts.map +0 -1
  52. package/wasm/core/SeriesManager/constants.d.ts.map +0 -1
  53. package/wasm/core/SeriesManager/index.d.ts.map +0 -1
  54. package/wasm/core/SeriesManager/types.d.ts.map +0 -1
  55. package/wasm/core/SpectrumAnalyzer/constants.d.ts.map +0 -1
  56. package/wasm/core/SpectrumAnalyzer/errors.d.ts.map +0 -1
  57. package/wasm/core/SpectrumAnalyzer/index.d.ts.map +0 -1
  58. package/wasm/core/SpectrumAnalyzer/tools.d.ts.map +0 -1
  59. package/wasm/core/SpectrumAnalyzer/types.d.ts.map +0 -1
  60. package/wasm/core/WaterfallAnalyzer/index.d.ts.map +0 -1
  61. package/wasm/core/WaterfallAnalyzer/types.d.ts.map +0 -1
  62. package/wasm/index.d.ts.map +0 -1
@@ -7,9 +7,6 @@ export default class WaterfallAnalyzer {
7
7
  private samplingOptions?;
8
8
  private snapshot;
9
9
  constructor(config?: WaterfallAnalyzerConfig);
10
- /**
11
- * 替换完整瀑布矩阵。仅复制外层数组,调用方在分析器持有期间不得修改帧内容。
12
- */
13
10
  setData(frames: readonly TimestampedFloat32Array[]): Readonly<WaterfallSnapshot>;
14
11
  appendFrame(source: TimestampedFloat32Array): Readonly<WaterfallSnapshot>;
15
12
  updateSamplingRange(options: WaterfallSamplingOptions): Readonly<WaterfallSnapshot>;
@@ -19,4 +16,3 @@ export default class WaterfallAnalyzer {
19
16
  private getSourceSize;
20
17
  private publishSnapshot;
21
18
  }
22
- //# sourceMappingURL=index.d.ts.map
@@ -11,24 +11,17 @@ export interface WaterfallSourceSize {
11
11
  readonly height: number;
12
12
  }
13
13
  export interface WaterfallSnapshot {
14
- /** 分析器持有的可见采样帧,未降采样时可能借用原始帧或其视图;调用方只读。 */
15
14
  readonly data: readonly TimestampedFloat32Array[];
16
- /**
17
- * 原始帧的零拷贝只读借用。调用方不得修改外层数组或帧内容。
18
- */
19
15
  readonly rawData: readonly TimestampedFloat32Array[];
20
16
  readonly range: {
21
17
  readonly x: WaterfallIndexRange;
22
18
  readonly y: WaterfallIndexRange;
23
19
  };
24
20
  readonly sourceSize: WaterfallSourceSize;
25
- /** 每个 X 输出 bin 的代表中心列,并非该 bin 最大值的真实来源列;调用方只读。 */
26
21
  readonly columnIndices: Uint32Array;
27
- /** 每个 Y 输出行对应的真实源行;调用方只读。 */
28
22
  readonly rowIndices: Uint32Array;
29
23
  }
30
24
  export interface WaterfallAnalyzerConfig {
31
25
  maxFrames?: number;
32
26
  onWaterfallUpdate?: (snapshot: Readonly<WaterfallSnapshot>) => void;
33
27
  }
34
- //# sourceMappingURL=types.d.ts.map
package/index.d.ts CHANGED
@@ -6,8 +6,8 @@ export type { SeriesChangeCallback, SeriesConfig, SeriesEventData, SeriesSubscri
6
6
  export { GraphicType, OrientationType, SeriesEventType } from './core/SeriesManager/types';
7
7
  export { ERROR_MESSAGES, SPECTRUM } from './core/SpectrumAnalyzer/constants';
8
8
  export { DataValidationError, IndexOutOfBoundsError, SpectrumError } from './core/SpectrumAnalyzer/errors';
9
- export type { BandwidthConfig, CursorIndexData, FrequencySegment, ProcessInput, ProcessingConfig, ProcessingOptions, ScanSegment, SpectrumConfig, SpectrumData, SpectrumOutputData, SpectrumRawData, TemplateOverData, ThresholdDataOptions, TimestampedFloat32Array } from './core/SpectrumAnalyzer/types';
9
+ export type { BandwidthConfig, CursorIndexData, ExtraDataOptions, FrequencySegment, PeakPoint, PeakStats, ProcessInput, ProcessingConfig, ProcessingOptions, ScanSegment, SpectrumConfig, SpectrumData, SpectrumOutputData, SpectrumRawData, TemplateOverData, ThresholdDataOptions, TimestampedFloat32Array } from './core/SpectrumAnalyzer/types';
10
+ export { ExtraDataMode } from './core/SpectrumAnalyzer/types';
10
11
  export type { WaterfallAnalyzerConfig, WaterfallIndexRange, WaterfallSamplingOptions, WaterfallSnapshot, WaterfallSourceSize } from './core/WaterfallAnalyzer/types';
11
12
  export { LevelStreamAnalyzer, SpectrumAnalyzer, WaterfallAnalyzer };
12
13
  export { default as SeriesManager } from './core/SeriesManager';
13
- //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1 +1 @@
1
- const LEVEL_STREAM={DEFAULT_CACHE_TIME:15e3,DEFAULT_GRANULARITY:10,DEFAULT_RANGE:[-20,100]};const DEFAULT_LEVEL_STREAM_CONFIG={cacheTime:LEVEL_STREAM.DEFAULT_CACHE_TIME,granularity:LEVEL_STREAM.DEFAULT_GRANULARITY,range:LEVEL_STREAM.DEFAULT_RANGE,maxPoints:void 0,onLevelStreamUpdate:data=>{}};class LevelStreamAnalyzer{config;spectrumData=[];probabilityData=new Map;count=0;constructor(config){this.config={...DEFAULT_LEVEL_STREAM_CONFIG,...config}}reset(){this.spectrumData=[];this.probabilityData=new Map;this.count=0}setConfig(config){this.config={...DEFAULT_LEVEL_STREAM_CONFIG,...this.config,...config};if(config.cacheTime&&this.config.cacheTime!==config.cacheTime||config.granularity&&this.config.granularity!==config.granularity||void 0!==config.maxPoints&&this.config.maxPoints!==config.maxPoints)this.reset();if(config.range)this.outputData()}process(level,timestamp){this.removeExpiredData();this.addNewData(level,timestamp);this.updateProbability(level);this.outputData()}updateProbability(level){const{granularity}=this.config;this.count++;const bin=Math.round(level/granularity)*granularity;let binCount=this.probabilityData.get(bin)||0;binCount++;this.probabilityData.set(bin,binCount)}getAll(){return this.probabilityData}removeExpiredData(){const{cacheTime,maxPoints}=this.config;if(0===this.spectrumData.length)return;if(void 0!==maxPoints&&maxPoints>0){if(this.spectrumData.length>maxPoints)this.spectrumData=this.spectrumData.slice(-maxPoints);return}const now=Date.now();if(cacheTime<=0){this.spectrumData=[];return}let left=0;let right=this.spectrumData.length-1;while(left<=right){const mid=Math.floor((left+right)/2);if(now-this.spectrumData[mid].timestamp>=cacheTime)left=mid+1;else right=mid-1}this.spectrumData=this.spectrumData.slice(left)}addNewData(level,timestamp){this.spectrumData.push({value:level,timestamp})}outputData(){const{range,granularity,onLevelStreamUpdate}=this.config;const rangeMin=range[0];const rangeMax=range[1];const probabilityRangeData=new Array(Math.round((rangeMax-rangeMin)/granularity)+1).fill(0);for(const[bin,count]of this.probabilityData)if(bin>=rangeMin&&bin<=rangeMax){const index=Math.round((rangeMax-bin)/granularity);if(this.count>0)probabilityRangeData[index]=count/this.count*100}onLevelStreamUpdate?.({probabilityRangeData:new Float32Array(probabilityRangeData),spectrumData:new Float32Array(this.spectrumData.map(item=>item.value)),timestampData:new Float64Array(this.spectrumData.map(item=>item.timestamp))})}}const SPECTRUM={INITIAL_VALUE:Number.NaN,WATERFALL_MAX_FRAMES:100,OUTPUT_POINTS:1001};const PEAK_DETECTION={MAX_PEAKS:2};const RF_UNIT=null;const FLUORESCENCE={UPDATE_INTERVAL:1,LEVEL_MIN:-20,LEVEL_MAX:140,LEVEL_RANGE:161};const DEFAULT_SPECTRUM_CONFIG={maxPoints:SPECTRUM.OUTPUT_POINTS,waterfallMaxFrames:SPECTRUM.WATERFALL_MAX_FRAMES,initialValue:SPECTRUM.INITIAL_VALUE,processing:{enableWaterfall:false,enableMetrics:false,enableFluorescence:false,enablePeakStats:false,fluorescenceUpdateInterval:FLUORESCENCE.UPDATE_INTERVAL},peakDetection:{maxPeaks:PEAK_DETECTION.MAX_PEAKS},outputPoints:SPECTRUM.OUTPUT_POINTS,outputRange:{start:0,end:SPECTRUM.OUTPUT_POINTS},templateTolerance:0};const ERROR_MESSAGES={EMPTY_SEGMENTS:"频段配置不能为空",INVALID_CONFIG:"必须且只能配置 segments 或 bandwidthConfig 其中之一",EMPTY_BANDWIDTH:"bandwidthConfig 不能为空",INVALID_SEGMENT:index=>`无效的段索引: ${index}`,INDEX_OUT_OF_BOUNDS:index=>`索引超出范围: ${index}`,INVALID_ANTENNA_FACTOR_LENGTH:points=>`天线因子数据长度必须等于实时数据长度 (${points})`,INVALID_ANTENNA_FACTOR:"天线因子数据必须是有效的正数",INVALID_SAMPLING_RANGE:"采样范围无效",INVALID_MAX_POINTS:"点数必须大于0",INVALID_LENGTH:expected=>`频率占用度数据长度不匹配,期望长度为 ${expected}`};class SpectrumError extends Error{code;details;constructor(message,code,details){super(message),this.code=code,this.details=details;this.name="SpectrumError"}}class DataValidationError extends SpectrumError{constructor(message,details){super(message,"DATA_VALIDATION_ERROR",details)}}class IndexOutOfBoundsError extends SpectrumError{index;constructor(message,index,details){super(message,"INDEX_OUT_OF_BOUNDS_ERROR",{index,...details||{}}),this.index=index}}const arrayKeepAttribute=(source,target)=>{if(void 0!==source.max)target.max=source.max;if(void 0!==source.maxIndex)target.maxIndex=source.maxIndex;if(void 0!==source.timestamp)target.timestamp=source.timestamp;if(void 0!==source.progress)target.progress=source.progress};const clamp=(value,min,max)=>{if(value<=min)return min;if(value>=max)return max;return value};const leftToIndex=(left,length)=>{if(length<=1)return 0;const safeLeft=clamp(left,0,100);return Math.max(0,Math.min(length-1,Math.ceil(length*safeLeft/100)-1))};const resolveCursorIndexByLeft=(left,outputLength,maxPoints)=>{if(!Number.isFinite(left)||outputLength<=0||maxPoints<=0)return;const safeLeft=clamp(Number(left),0,100);return{left:safeLeft,sampledIndex:leftToIndex(safeLeft,outputLength),globalIndex:leftToIndex(safeLeft,maxPoints)}};const resample=({realData,antennaFactorData,antennaFactorSwitch=false,outputPoints})=>{const realDataLength=realData.length;const isLessThanDataLength=realDataLength<=outputPoints;const outputLength=isLessThanDataLength?realDataLength:outputPoints;const srcIndexCache=new Uint32Array(outputLength);const realOutputData=new Float32Array(outputLength);realOutputData.timestamp=realData.timestamp;if(isLessThanDataLength){if(!antennaFactorSwitch){for(let i=0;i<realDataLength;i++)srcIndexCache[i]=i;realOutputData.set(realData);return{realOutputData,srcIndexCache}}for(let i=0;i<realDataLength;i++){srcIndexCache[i]=i;realOutputData[i]=realData[i]+antennaFactorData[i]}return{realOutputData,srcIndexCache}}const ratio=realDataLength/outputPoints;let pos=ratio/2;for(let i=0;i<outputLength;i++){const start=Math.floor(pos);const end=Math.min(Math.floor(pos+ratio),realDataLength);let maxValue=realData[start];let maxIndex=start;for(let j=start+1;j<end;j++)if(realData[j]>maxValue){maxValue=realData[j];maxIndex=j}realOutputData[i]=antennaFactorSwitch?maxValue+antennaFactorData[maxIndex]:maxValue;srcIndexCache[i]=maxIndex;pos+=ratio}return{realOutputData,srcIndexCache}};const resampleMultiple=({antennaFactorData,antennaFactorSwitch=false,outputPoints,realData,maxData,minData,avgData,templateData,backgroundNoiseData,thresholdData,fluorescenceData,occupancyData,enablePeakStats=false,maxPeaks=2,outputRangeStart=0})=>{const realDataLength=realData.length;const isLessThanDataLength=realDataLength<=outputPoints;const outputLength=isLessThanDataLength?realDataLength:outputPoints;const hasMaxData=maxData&&maxData.length>0;const hasMinData=minData&&minData.length>0;const hasAvgData=avgData&&avgData.length>0;const hasTemplateData=templateData&&templateData.length>0;const hasOccupancyData=occupancyData&&occupancyData.length>0;const hasBackgroundNoiseData=backgroundNoiseData&&backgroundNoiseData.length>0;const hasThresholdData=thresholdData&&thresholdData.length>0;const hasFluorescenceStats=fluorescenceData&&fluorescenceData.length>0;const candidatePeaks=[];const applyAntennaFactor=antennaFactorSwitch?(value,index)=>value+antennaFactorData[index]:value=>value;const realOutputData=new Float32Array(outputLength);realOutputData.timestamp=realData.timestamp;const maxOutputData=new Float32Array(hasMaxData?outputLength:0);const minOutputData=new Float32Array(hasMinData?outputLength:0);const avgOutputData=new Float32Array(hasAvgData?outputLength:0);const templateOutputData=new Float32Array(hasTemplateData?outputLength:0);const occupancyOutputData=new Float32Array(hasOccupancyData?outputLength:0);const backgroundNoiseOutputData=new Float32Array(hasBackgroundNoiseData?outputLength:0);const thresholdOutputData=new Float32Array(hasThresholdData?outputLength:0);let fluorescenceOutputData=new Array(hasFluorescenceStats?outputLength:0).fill(null);const srcIndexCache=new Uint32Array(outputLength);if(isLessThanDataLength){for(let i=0;i<realDataLength;i++)srcIndexCache[i]=i;if(antennaFactorSwitch)for(let i=0;i<realDataLength;i++){const antennaFactor=antennaFactorData[i];realOutputData[i]=realData[i]+antennaFactor;if(hasMaxData)maxOutputData[i]=maxData[i]+antennaFactor;if(hasMinData)minOutputData[i]=minData[i]+antennaFactor;if(hasAvgData)avgOutputData[i]=avgData[i]+antennaFactor;if(hasTemplateData)templateOutputData[i]=templateData[i]+antennaFactor;if(hasOccupancyData)occupancyOutputData[i]=occupancyData[i];if(hasBackgroundNoiseData)backgroundNoiseOutputData[i]=backgroundNoiseData[i]+antennaFactor;if(hasThresholdData)thresholdOutputData[i]=thresholdData[i]+antennaFactor;if(hasFluorescenceStats){const fluorescence=fluorescenceData[i];const newFluorescence=new Map;for(const[key,value]of fluorescence)newFluorescence.set(key+antennaFactor,value);fluorescenceOutputData[i]=newFluorescence}}else{realOutputData.set(realData);if(hasMaxData)maxOutputData.set(maxData);if(hasMinData)minOutputData.set(minData);if(hasAvgData)avgOutputData.set(avgData);if(hasTemplateData)templateOutputData.set(templateData);if(hasOccupancyData)occupancyOutputData.set(occupancyData);if(hasBackgroundNoiseData)backgroundNoiseOutputData.set(backgroundNoiseData);if(hasThresholdData)thresholdOutputData.set(thresholdData);if(hasFluorescenceStats)fluorescenceOutputData=fluorescenceData}if(enablePeakStats)for(let i=0;i<realDataLength;i++){const value=realOutputData[i];if(Number.isFinite(value))candidatePeaks.push({index:i,srcIndex:i+outputRangeStart,value})}}else{const ratio=realDataLength/outputPoints;let pos=ratio/2;for(let i=0;i<outputLength;i++){const start=Math.floor(pos);const end=Math.min(Math.floor(pos+ratio),realDataLength);let realMaxVal=realData[start];let realMaxIdx=start;let maxVal=hasMaxData?maxData[start]:0;let maxIdx=start;let minVal=hasMinData?minData[start]:0;let minIdx=start;let occupancyMaxVal=hasOccupancyData?occupancyData[start]:0;for(let j=start+1;j<end;j++){if(realData[j]>realMaxVal){realMaxVal=realData[j];realMaxIdx=j}if(hasMaxData&&maxData[j]>maxVal){maxVal=maxData[j];maxIdx=j}if(hasMinData&&minData[j]<minVal){minVal=minData[j];minIdx=j}if(hasOccupancyData&&occupancyData[j]>occupancyMaxVal)occupancyMaxVal=occupancyData[j]}const finalValue=applyAntennaFactor(realMaxVal,realMaxIdx);realOutputData[i]=finalValue;srcIndexCache[i]=realMaxIdx;if(enablePeakStats&&Number.isFinite(finalValue))candidatePeaks.push({index:i,srcIndex:realMaxIdx+outputRangeStart,value:finalValue});if(hasMaxData)maxOutputData[i]=applyAntennaFactor(maxVal,maxIdx);if(hasMinData)minOutputData[i]=applyAntennaFactor(minVal,minIdx);if(hasAvgData){const avgValue=avgData[realMaxIdx];avgOutputData[i]=applyAntennaFactor(avgValue,realMaxIdx)}if(hasTemplateData){const templateValue=templateData[realMaxIdx];templateOutputData[i]=applyAntennaFactor(templateValue,realMaxIdx)}if(hasOccupancyData)occupancyOutputData[i]=occupancyMaxVal;if(hasBackgroundNoiseData){const backgroundNoiseValue=backgroundNoiseData[realMaxIdx];backgroundNoiseOutputData[i]=applyAntennaFactor(backgroundNoiseValue,realMaxIdx)}if(hasThresholdData){const thresholdValue=thresholdData[realMaxIdx];thresholdOutputData[i]=applyAntennaFactor(thresholdValue,realMaxIdx)}if(hasFluorescenceStats){const fluorescence=fluorescenceData[realMaxIdx];const newFluorescence=new Map;for(const[key,value]of fluorescence)newFluorescence.set(applyAntennaFactor(key,realMaxIdx),value);fluorescenceOutputData[i]=newFluorescence}pos+=ratio}}if(enablePeakStats&&candidatePeaks.length>0){candidatePeaks.sort((a,b)=>b.value-a.value);const finalPeaks=candidatePeaks.slice(0,maxPeaks);realOutputData.peaks={peaks:finalPeaks,timestamp:realData.timestamp}}return{realOutputData,maxOutputData,minOutputData,avgOutputData,templateOutputData,occupancyOutputData,backgroundNoiseOutputData,thresholdOutputData,srcIndexCache,fluorescenceOutputData}};const resampleExtraData=(extraData,antennaFactorData,antennaFactorSwitch,srcIndexCache,getOutputData)=>{if(!extraData||0===Object.keys(extraData).length)return{};const extraOutputData={};const extraDataKeys=Object.keys(extraData);const outputLength=srcIndexCache.length;for(let i=0;i<extraDataKeys.length;i++)extraOutputData[extraDataKeys[i]]=new Float32Array(outputLength);for(let k=0;k<extraDataKeys.length;k++){const key=extraDataKeys[k];const sourceArray=getOutputData(extraData[key]);const outputArray=extraOutputData[key];if(antennaFactorSwitch)for(let i=0;i<outputLength;i++){const srcIndex=srcIndexCache[i];outputArray[i]=sourceArray[srcIndex]+antennaFactorData[srcIndex]}else for(let i=0;i<outputLength;i++){const srcIndex=srcIndexCache[i];outputArray[i]=sourceArray[srcIndex]}}return extraOutputData};const findExceedingDatasCore=({realData,maxData,minData,templateData,tolerance=0,startIndex=0,endIndex,usePreallocation=false})=>{const{timestamp}=realData;const actualEndIndex=endIndex??realData.length;const segments=[];let start=-1;let sum=0;let count=0;if(usePreallocation&&0===startIndex&&actualEndIndex===realData.length){const exceedingFlags=new Uint8Array(realData.length);for(let i=0;i<realData.length;i++)exceedingFlags[i]=realData[i]>templateData[i]+tolerance?1:0;for(let i=0;i<realData.length;i++)if(exceedingFlags[i]){if(-1===start){start=i;sum=realData[i];count=1}else{sum+=realData[i];count++}}else if(-1!==start){const end=i-1;if(end-start+1>=2){const avgValue=sum/count;const middleIndex=start+(end-start>>1);segments.push({maxValue:maxData[middleIndex],minValue:minData[middleIndex],avgValue,timestamp,startIndex:start,endIndex:end})}start=-1}}else for(let i=startIndex;i<Math.min(actualEndIndex,realData.length);i++){const isExceeding=realData[i]>templateData[i]+tolerance;if(isExceeding){if(-1===start){start=i;sum=realData[i];count=1}else{sum+=realData[i];count++}}else if(-1!==start){const end=i-1;if(end-start+1>=2){const avgValue=sum/count;const middleIndex=start+(end-start>>1);segments.push({maxValue:maxData[middleIndex],minValue:minData[middleIndex],avgValue,timestamp,startIndex:start,endIndex:end})}start=-1}}if(-1!==start){const end=actualEndIndex-1;if(end-start+1>=2){const avgValue=sum/count;const middleIndex=start+(end-start>>1);segments.push({maxValue:maxData[middleIndex],minValue:minData[middleIndex],avgValue,timestamp,startIndex:start,endIndex:end})}}return segments};const findExceedingDatas=({realData,maxData,minData,templateData,tolerance=0})=>{if(!templateData||0===templateData.length)return[];return findExceedingDatasCore({realData,maxData,minData,templateData,tolerance,usePreallocation:true})};const findExceedingDatasIncremental=({realData,maxData,minData,templateData,tolerance=0,startIndex,endIndex,previousSegments=[]})=>{if(!templateData||0===templateData.length||startIndex>=endIndex)return previousSegments;const newSegments=findExceedingDatasCore({realData,maxData,minData,templateData,tolerance,startIndex,endIndex});return[...previousSegments,...newSegments]};function isAllValid(arr){for(let i=0;i<arr.length;i++)if(!Number.isFinite(arr[i]))return false;return true}var types_ExtraDataMode=/*#__PURE__*/function(ExtraDataMode){ExtraDataMode["REPLACE"]="replace";ExtraDataMode["MERGE"]="merge";ExtraDataMode["CLEAR"]="clear";return ExtraDataMode}({});var SpectrumAnalyzer_PendingDataType=/*#__PURE__*/null;class SpectrumAnalyzer{config=DEFAULT_SPECTRUM_CONFIG;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"]:false,["template"]:false,["backgroundNoise"]:false,["threshold"]:false,["extra"]:false};hasPendingData=false;hasProcessedData=false;isProcessing=false;onSpectrumUpdate;constructor(config){const{onSpectrumUpdate,maxPoints}=config;this.onSpectrumUpdate=onSpectrumUpdate;this.config={...DEFAULT_SPECTRUM_CONFIG,...config,processing:{...DEFAULT_SPECTRUM_CONFIG.processing,...config.processing},peakDetection:{...DEFAULT_SPECTRUM_CONFIG.peakDetection,...config.peakDetection},outputRange:{start:config.outputRange?.start??0,end:config.outputRange?.end??maxPoints??SPECTRUM.OUTPUT_POINTS}};this.reset()}process({data,timestamp,segmentOffset=0,offset=0,fluorescenceData,fluorescenceMaxCount}){const processStartTime=performance.now();try{if(!data?.length)return;this.isProcessing=true;this.hasProcessedData=true;const index=this.resolveGlobalIndex(segmentOffset,offset);if(this.segments.length)this.scanProgress=(index+data.length)/this.config.maxPoints;else if(data.length!==this.config.maxPoints)this.updateMaxPoints(data.length);if(fluorescenceData&&void 0!==fluorescenceMaxCount){this.fluorescenceData=fluorescenceData;this.fluorescenceMaxCount=fluorescenceMaxCount}this.validateInput(data,index);const{maxPoints}=this.config;const endIndex=index+data.length;this.lastIndex=endIndex;const isOver=endIndex>=maxPoints||endIndex<this.lastIndex;this.realData.set(data,index);this.realData.timestamp=timestamp;this.processDataPoints(data,index,isOver);const processedData=this.resampleDataSeries();if(isOver&&processedData.realData)this.updateWaterfallData(this.realData,processedData.realData);const templateOverData=this.updateTemplateOverData(isOver,isOver?void 0:{startIndex:index,endIndex:index+data.length});const pendingData=this.processPendingData();this.notifySpectrumUpdate({...processedData,...pendingData||{},extraData:this.extraOutputData,waterfallData:this.waterfallOutputData,scanProgress:this.scanProgress,processTimes:this.processTimes,...templateOverData&&{templateOverData}})}catch(error){throw error instanceof Error?error:new Error(String(error))}finally{this.isProcessing=false;this.lastProcessTime=performance.now()-processStartTime}}initializeSegments(segments){if(!segments?.length)throw new DataValidationError(ERROR_MESSAGES.EMPTY_SEGMENTS);const prevMaxPoints=this.config.maxPoints;let totalPoints=0;this.segments=segments.map(segment=>{const{startFrequency,stopFrequency,stepFrequency}=segment;const frequencyRange=stopFrequency-startFrequency;const stepCount=1e3*frequencyRange/stepFrequency;const pointCount=Math.round(stepCount)+1;const startIndex=totalPoints;totalPoints+=pointCount;return{startFrequency,stopFrequency,stepFrequency,pointCount,startIndex}});this.updateMaxPoints(totalPoints,false);if(prevMaxPoints===totalPoints){this.config={...this.config,outputRange:{start:0,end:totalPoints}};this.reset(false)}}setAntennaFactor(d){const{antennaFactorData,config:{maxPoints}}=this;let data=new Float32Array(d);if(data.length!==maxPoints){data=new Float32Array(data).subarray(0,maxPoints);console.warn(ERROR_MESSAGES.INVALID_ANTENNA_FACTOR_LENGTH(maxPoints))}let hasInvalid=false;for(let i=0;i<data.length;i++){const value=data[i];const isNiceFinite=Number.isFinite(value)&&value>0;if(!isNiceFinite)hasInvalid=true;antennaFactorData[i]=isNiceFinite?value:0}if(hasInvalid)console.warn(ERROR_MESSAGES.INVALID_ANTENNA_FACTOR);if(this.antennaFactorSwitch)this.setAntennaFactorSwitch(this.antennaFactorSwitch)}setAntennaFactorSwitch(newAntennaFactorSwitch){const isChange=this.antennaFactorSwitch!==newAntennaFactorSwitch;this.antennaFactorSwitch=newAntennaFactorSwitch;if(isChange){const processedData=this.resampleDataSeries();this.resampleWaterfallOutputData();this.notifySpectrumUpdate({...processedData,waterfallData:this.waterfallOutputData})}}updateProcessing(next){const{processing:prev}=this.config;const newProcessing={...prev,...void 0!==next.enableMetrics&&{enableMetrics:next.enableMetrics},...void 0!==next.enableFluorescence&&{enableFluorescence:next.enableFluorescence},...void 0!==next.enablePeakStats&&{enablePeakStats:next.enablePeakStats},...void 0!==next.fluorescenceUpdateInterval&&{fluorescenceUpdateInterval:next.fluorescenceUpdateInterval}};if(newProcessing.enableMetrics===prev.enableMetrics&&newProcessing.enableFluorescence===prev.enableFluorescence&&newProcessing.enablePeakStats===prev.enablePeakStats&&newProcessing.fluorescenceUpdateInterval===prev.fluorescenceUpdateInterval)return;this.config={...this.config,processing:newProcessing};if(newProcessing.enableMetrics&&!prev.enableMetrics){const needInit=0===this.maxData.length||0===this.minData.length||0===this.avgData.length;if(needInit){this.allocateMetricsBuffers();this.clearMetricsBuffers()}}if(newProcessing.enableFluorescence&&!prev.enableFluorescence){if(0===this.fluorescenceData.length)this.fluorescenceData=new Uint32Array(this.config.maxPoints*FLUORESCENCE.LEVEL_RANGE)}const processedData=this.resampleDataSeries();this.extraOutputData=resampleExtraData(this.extraData,this.antennaFactorData,this.antennaFactorSwitch,this.srcIndexCache,this.getOutputData.bind(this));this.notifySpectrumUpdate({...processedData,extraData:this.extraOutputData})}clearMetricsData(){const needAllocate=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&&needAllocate){const outputLength=this.realOutputData?.length??this.config.outputPoints;this.allocateMetricsBuffers(outputLength)}this.clearMetricsBuffers();this.processTimes=0;this.notifySpectrumUpdate({maxData:this.maxOutputData,minData:this.minOutputData,avgData:this.avgOutputData,processTimes:0})}allocateMetricsBuffers(outputLengthOverride){const{maxPoints,outputPoints,processing:{enableMetrics}}=this.config;const rawLength=enableMetrics?maxPoints:0;let outputLength=0;if(enableMetrics){const desired=outputLengthOverride??outputPoints;outputLength=Math.max(0,Math.floor(desired));outputLength=Math.min(outputLength,outputPoints)}this.maxData=new Float32Array(rawLength);this.minData=new Float32Array(rawLength);this.avgData=new Float32Array(rawLength);this.maxOutputData=new Float32Array(outputLength);this.minOutputData=new Float32Array(outputLength);this.avgOutputData=new Float32Array(outputLength)}clearMetricsBuffers(){this.maxData.fill(SPECTRUM.INITIAL_VALUE);this.minData.fill(SPECTRUM.INITIAL_VALUE);this.avgData.fill(SPECTRUM.INITIAL_VALUE);this.maxData.allValid=false;this.minData.allValid=false;this.avgData.allValid=false;this.maxOutputData.fill(SPECTRUM.INITIAL_VALUE);this.minOutputData.fill(SPECTRUM.INITIAL_VALUE);this.avgOutputData.fill(SPECTRUM.INITIAL_VALUE)}setWaterfallData(newWaterfallData){if(!(newWaterfallData?.[0]?.length>0))return;const{processing}=this.config;let{waterfallMaxFrames}=this.config;if(!processing.enableWaterfall)return;waterfallMaxFrames=newWaterfallData.length;this.config={...this.config,waterfallMaxFrames};this.waterfallData=newWaterfallData;this.resampleWaterfallOutputData();this.notifySpectrumUpdate({waterfallData:this.waterfallOutputData})}setFluorescenceData(fluorescenceData,fluorescenceMaxCount){if(!this.config.processing.enableFluorescence)return;if(!fluorescenceData||0===fluorescenceData.length)return;const expectedLength=this.config.maxPoints*FLUORESCENCE.LEVEL_RANGE;if(fluorescenceData.length!==expectedLength)return;this.fluorescenceData=fluorescenceData;this.fluorescenceMaxCount=fluorescenceMaxCount;const outputLength=this.config.outputRange.end-this.config.outputRange.start;const fluorescenceOutput=this.getFluorescenceOutputData(outputLength);this.notifySpectrumUpdate({fluorescenceData:fluorescenceOutput,fluorescenceMaxCount:this.fluorescenceMaxCount})}setRealData(data){if(!(data?.length>0)||data.length!==this.config.maxPoints)return;const newData=new Float32Array(data.length);newData.set(data);newData.timestamp=data.timestamp;this.realData=newData;const processedData=this.resampleDataSeries();this.notifySpectrumUpdate(processedData)}setMaxData(data){if(!(data?.length>0)||data.length!==this.config.maxPoints)return;const newData=new Float32Array(data.length);newData.set(data);this.maxData=newData;this.maxData.allValid=isAllValid(this.maxData);const processedData=this.resampleDataSeries();this.notifySpectrumUpdate(processedData)}setMinData(data){if(!(data?.length>0)||data.length!==this.config.maxPoints)return;const newData=new Float32Array(data.length);newData.set(data);this.minData=newData;this.minData.allValid=isAllValid(this.minData);const processedData=this.resampleDataSeries();this.notifySpectrumUpdate(processedData)}setAvgData(data){if(!(data?.length>0)||data.length!==this.config.maxPoints)return;const newData=new Float32Array(data.length);newData.set(data);this.avgData=newData;this.avgData.allValid=isAllValid(this.avgData);const processedData=this.resampleDataSeries();this.notifySpectrumUpdate(processedData)}setOccupancyData(data){if(!data||0===data.length){this.occupancyData=new Float32Array(0);this.setPendingFlag("occupancy",false);return}this.occupancyData=new Float32Array(data);if(!this.hasProcessedData||this.isProcessing){this.setPendingFlag("occupancy",true);return}const processedData=this.resampleDataSeries();this.notifySpectrumUpdate({occupancyData:processedData.occupancyData});this.setPendingFlag("occupancy",false)}setExtraData(data,mode=types_ExtraDataMode.MERGE){if(mode===types_ExtraDataMode.CLEAR||mode===types_ExtraDataMode.REPLACE){this.extraData={};this.extraOutputData={};this.setPendingFlag("extra",false)}if(mode===types_ExtraDataMode.CLEAR)return;if(!data)return;for(const[key,value]of Object.entries(data)){if(!value||0===value.length){delete this.extraData[key];continue}this.extraData[key]=new Float32Array(value)}if(!this.hasProcessedData||this.isProcessing){this.setPendingFlag("extra",true);return}this.extraOutputData=resampleExtraData(this.extraData,this.antennaFactorData,this.antennaFactorSwitch,this.srcIndexCache,this.getOutputData.bind(this));this.notifySpectrumUpdate({extraData:this.extraOutputData});this.setPendingFlag("extra",false)}reset(preserveProcessedFlag=false){const{maxPoints,waterfallMaxFrames,processing:{enableFluorescence}}=this.config;this.antennaFactorData=new Float32Array(maxPoints);const realData=new Float32Array(maxPoints);realData.timestamp="";realData.fill(SPECTRUM.INITIAL_VALUE);this.realData=realData;this.allocateMetricsBuffers();this.clearMetricsBuffers();this.templateData=new Float32Array;this.occupancyData=new Float32Array;this.backgroundNoiseData=new Float32Array;this.clearThresholdDataState();this.waterfallData=Array.from({length:waterfallMaxFrames},()=>{const frame=new Float32Array;frame.timestamp="";frame.fill(SPECTRUM.INITIAL_VALUE);return frame});this.waterfallOutputData=Array.from({length:waterfallMaxFrames},()=>{const frame=new Float32Array;frame.timestamp="";frame.fill(SPECTRUM.INITIAL_VALUE);return frame});const outputPoints=this.config.outputPoints;this.realOutputData=new Float32Array(outputPoints).fill(SPECTRUM.INITIAL_VALUE);this.templateOutputData=new Float32Array;this.occupancyOutputData=new Float32Array;this.backgroundNoiseOutputData=new Float32Array;this.fluorescenceData=enableFluorescence?new Uint32Array(maxPoints*FLUORESCENCE.LEVEL_RANGE):new Uint32Array(0);this.fluorescenceMaxCount=0;this.fluorescenceUpdateCounter=0;this.extraData={};this.extraOutputData={};this.srcIndexCache=new Uint32Array(maxPoints);this.scanProgress=0;this.lastIndex=0;this.processTimes=0;this.lastProcessTime=0;this.cachedExceedingDatas=[];this.pendingDataFlags={["occupancy"]:false,["template"]:false,["backgroundNoise"]:false,["threshold"]:false,["extra"]:false};this.hasPendingData=false;if(!preserveProcessedFlag)this.hasProcessedData=false;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;const{realData}=this.resampleDataSeries();if(!realData)return;return realData.peaks}updateSamplingRange(start,end){if(start<0||start>=end)throw new DataValidationError(ERROR_MESSAGES.INVALID_SAMPLING_RANGE);this.config={...this.config,outputRange:{start:Math.floor(start),end:Math.ceil(end)}};const processedData=this.resampleDataSeries();this.resampleWaterfallOutputData();this.extraOutputData=resampleExtraData(this.extraData,this.antennaFactorData,this.antennaFactorSwitch,this.srcIndexCache,this.getOutputData.bind(this));this.notifySpectrumUpdate({...processedData,extraData:this.extraOutputData,waterfallData:this.waterfallOutputData});return this.srcIndexCache}setTemplateData(data,templateTolerance){if(!data||0===data.length){this.templateData=new Float32Array(0);this.setPendingFlag("template",false);return}if(void 0!==templateTolerance)this.config.templateTolerance=templateTolerance;this.templateData=new Float32Array(data);if(!this.hasProcessedData||this.isProcessing){this.setPendingFlag("template",true);return}this.notifySpectrumUpdate({templateData:this.resampleSingleData(this.templateData),templateOverData:this.updateTemplateOverData(true)});this.setPendingFlag("template",false)}setBackgroundNoiseData(data){if(!data||0===data.length){this.backgroundNoiseData=new Float32Array(0);this.setPendingFlag("backgroundNoise",false);return}this.backgroundNoiseData=new Float32Array(data);if(!this.hasProcessedData||this.isProcessing){this.setPendingFlag("backgroundNoise",true);return}this.notifySpectrumUpdate({backgroundNoiseData:this.resampleSingleData(this.backgroundNoiseData)});this.setPendingFlag("backgroundNoise",false)}setThresholdData(data,options){if(!data||0===data.length){this.clearThresholdDataState();return}const sourceData=data instanceof Float32Array?data:new Float32Array(data);if(options){const index=this.resolveGlobalIndex(options.segmentOffset??0,options.offset??0);this.validateInput(sourceData,index);if(this.thresholdData.length!==this.config.maxPoints){const next=new Float32Array(this.config.maxPoints);next.fill(SPECTRUM.INITIAL_VALUE);if(this.thresholdData.length>0)next.set(this.thresholdData.subarray(0,this.config.maxPoints));this.thresholdData=next}this.thresholdData.set(sourceData,index)}else{const normalized=new Float32Array(this.config.maxPoints);normalized.fill(SPECTRUM.INITIAL_VALUE);normalized.set(sourceData.subarray(0,this.config.maxPoints));this.thresholdData=normalized}if(!this.hasProcessedData||this.isProcessing){this.setPendingFlag("threshold",true);return}const thresholdOutputData=this.resampleSingleData(this.thresholdData);this.thresholdOutputData=thresholdOutputData;this.notifySpectrumUpdate({thresholdData:thresholdOutputData});this.setPendingFlag("threshold",false)}updateTemplateOverData(forceFullCalculation=false,incrementalRange){if(!this.templateData||0===this.templateData.length){this.cachedExceedingDatas=[];return[]}let templateOverData;if(forceFullCalculation||!incrementalRange){templateOverData=findExceedingDatas({realData:this.realData,maxData:this.maxData,minData:this.minData,templateData:this.templateData,tolerance:this.config.templateTolerance});this.cachedExceedingDatas=templateOverData}else{this.cachedExceedingDatas=findExceedingDatasIncremental({realData:this.realData,maxData:this.maxData,minData:this.minData,templateData:this.templateData,tolerance:this.config.templateTolerance,startIndex:incrementalRange.startIndex,endIndex:incrementalRange.endIndex,previousSegments:this.cachedExceedingDatas});templateOverData=this.cachedExceedingDatas}return templateOverData}resolveGlobalIndex(segmentOffset=0,offset=0){if(this.segments.length){const segment=this.segments[segmentOffset];if(!segment)throw new DataValidationError(ERROR_MESSAGES.INVALID_SEGMENT(segmentOffset));return segment.startIndex+offset}return offset}clearThresholdDataState(){this.thresholdData=new Float32Array(0);this.thresholdOutputData=new Float32Array(0);this.setPendingFlag("threshold",false)}resampleDataSeries(){const{antennaFactorData,antennaFactorSwitch,maxData,minData,avgData,templateData,occupancyData,backgroundNoiseData,thresholdData,config:{maxPoints,outputPoints}}=this;const activeAntennaFactorData=antennaFactorSwitch?antennaFactorData:new Float32Array(maxPoints);const{realOutputData,srcIndexCache,maxOutputData,minOutputData,avgOutputData,templateOutputData,occupancyOutputData,backgroundNoiseOutputData,thresholdOutputData}=resampleMultiple({antennaFactorData:activeAntennaFactorData,antennaFactorSwitch,outputPoints,realData:this.getOutputData(),maxData:maxData?.length>0?this.getOutputData(maxData):void 0,minData:minData?.length>0?this.getOutputData(minData):void 0,avgData:avgData&&avgData.length>0?this.getOutputData(avgData):void 0,templateData:templateData&&templateData.length>0?this.getOutputData(templateData):void 0,occupancyData:occupancyData&&occupancyData.length>0?this.getOutputData(occupancyData):void 0,backgroundNoiseData:backgroundNoiseData&&backgroundNoiseData.length>0?this.getOutputData(backgroundNoiseData):void 0,thresholdData:thresholdData&&thresholdData.length>0?this.getOutputData(thresholdData):void 0,enablePeakStats:this.config.processing.enablePeakStats,maxPeaks:this.config.peakDetection.maxPeaks,outputRangeStart:this.config.outputRange.start});this.srcIndexCache=srcIndexCache;this.realOutputData=realOutputData;this.maxOutputData=maxOutputData||new Float32Array;this.minOutputData=minOutputData||new Float32Array;this.avgOutputData=avgOutputData||new Float32Array;this.templateOutputData=templateOutputData||new Float32Array;this.occupancyOutputData=occupancyOutputData||new Float32Array;this.backgroundNoiseOutputData=backgroundNoiseOutputData||new Float32Array;this.thresholdOutputData=thresholdOutputData||new Float32Array;const fluorescenceOutput=this.getFluorescenceOutputData(realOutputData.length);return{realData:realOutputData,maxData:maxOutputData,minData:minOutputData,avgData:avgOutputData,templateData:templateOutputData,occupancyData:occupancyOutputData,backgroundNoiseData:backgroundNoiseOutputData,thresholdData:thresholdOutputData,fluorescenceData:fluorescenceOutput,fluorescenceMaxCount:this.fluorescenceMaxCount,extraData:this.extraOutputData,srcIndexCache}}resampleWaterfallOutputData(){const{antennaFactorData,antennaFactorSwitch,waterfallData,config:{maxPoints,processing:{enableWaterfall},outputPoints}}=this;if(!enableWaterfall)return;const activeAntennaFactorData=antennaFactorSwitch?antennaFactorData:new Float32Array(maxPoints);this.waterfallOutputData=waterfallData.map(frame=>{const realData=this.getOutputData(frame);const{realOutputData}=resampleMultiple({realData,antennaFactorData:activeAntennaFactorData,antennaFactorSwitch,outputPoints});return realOutputData})}updateWaterfallData(data,outputData){const{waterfallMaxFrames,processing}=this.config;if(!processing.enableWaterfall)return;if(this.waterfallData.length>=waterfallMaxFrames){this.waterfallData.shift();this.waterfallOutputData.shift()}const newData=new Float32Array(data.length);newData.set(data);newData.timestamp=data.timestamp;this.waterfallData.push(newData);this.waterfallOutputData.push(outputData)}updateMaxPoints(maxPoints,preserveProcessedFlag=true){if(this.config.maxPoints===maxPoints)return;this.config={...this.config,maxPoints,outputRange:{start:0,end:maxPoints}};this.reset(preserveProcessedFlag)}validateInput(data,index){if(index<0||index+data.length>this.config.maxPoints)throw new IndexOutOfBoundsError(ERROR_MESSAGES.INDEX_OUT_OF_BOUNDS(index),index)}processDataPoints(data,index,isOver){const{maxData,minData,avgData,config:{processing:{enableMetrics}}}=this;if(enableMetrics&&isOver){this.processTimes+=1;if(!maxData.allValid)maxData.allValid=isAllValid(maxData);if(!minData.allValid)minData.allValid=isAllValid(minData);if(!avgData.allValid)avgData.allValid=isAllValid(avgData)}const length=data.length;if(this.config.processing.enableFluorescence)for(let i=0;i<length;i++)this.updateFluorescenceStats(index+i,data[i]);if(!enableMetrics)return;let allValid=true;for(let i=0;i<length;i++)if(!Number.isFinite(data[i])){allValid=false;break}for(let i=0;i<length;i++){const dataIndex=index+i;const value=data[i];if(!allValid&&!Number.isFinite(value))continue;const oldMax=maxData[dataIndex];if(value>oldMax||!maxData.allValid&&Number.isNaN(oldMax))maxData[dataIndex]=value;const oldMin=minData[dataIndex];if(value<oldMin||!minData.allValid&&Number.isNaN(oldMin))minData[dataIndex]=value;const oldAvg=avgData[dataIndex];if(0===this.processTimes||Number.isNaN(oldAvg))avgData[dataIndex]=value;else avgData[dataIndex]=oldAvg+(value-oldAvg)/(this.processTimes+1)}}updateFluorescenceStats(dataIndex,value){if(!this.config.processing.enableFluorescence||!this.fluorescenceData)return;if(!Number.isFinite(value))return;const level=Math.round(value);if(level<FLUORESCENCE.LEVEL_MIN||level>FLUORESCENCE.LEVEL_MAX)return;const index=dataIndex*FLUORESCENCE.LEVEL_RANGE+(level-FLUORESCENCE.LEVEL_MIN);const newCount=++this.fluorescenceData[index];if(newCount>this.fluorescenceMaxCount)this.fluorescenceMaxCount=newCount}setPendingFlag(type,value){this.pendingDataFlags[type]=value;this.hasPendingData=this.pendingDataFlags["occupancy"]||this.pendingDataFlags["template"]||this.pendingDataFlags["backgroundNoise"]||this.pendingDataFlags["threshold"]||this.pendingDataFlags["extra"]}resampleSingleData(sourceData){const{srcIndexCache}=this;const{outputRange:{start,end}}=this.config;const slicedSourceData=sourceData.subarray(start,end);const outputLength=srcIndexCache.length;const outputData=new Float32Array(outputLength);for(let i=0;i<outputLength;i++)outputData[i]=slicedSourceData[srcIndexCache[i]];return outputData}processPendingData(){if(!this.hasPendingData)return null;const pendingOutput={};if(this.pendingDataFlags["occupancy"]&&this.occupancyData.length>0){const processedData=this.resampleDataSeries();pendingOutput.occupancyData=processedData.occupancyData;this.setPendingFlag("occupancy",false)}if(this.pendingDataFlags["template"]&&this.templateData.length>0){pendingOutput.templateData=this.resampleSingleData(this.templateData);pendingOutput.templateOverData=this.updateTemplateOverData(true);this.setPendingFlag("template",false)}if(this.pendingDataFlags["backgroundNoise"]&&this.backgroundNoiseData.length>0){pendingOutput.backgroundNoiseData=this.resampleSingleData(this.backgroundNoiseData);this.setPendingFlag("backgroundNoise",false)}if(this.pendingDataFlags["threshold"]&&this.thresholdData.length>0){const thresholdOutputData=this.resampleSingleData(this.thresholdData);this.thresholdOutputData=thresholdOutputData;pendingOutput.thresholdData=thresholdOutputData;this.setPendingFlag("threshold",false)}if(this.pendingDataFlags["extra"]&&Object.keys(this.extraData).length>0){this.extraOutputData=resampleExtraData(this.extraData,this.antennaFactorData,this.antennaFactorSwitch,this.srcIndexCache,this.getOutputData.bind(this));pendingOutput.extraData=this.extraOutputData;this.setPendingFlag("extra",false)}return pendingOutput}notifySpectrumUpdate(processedData){this.onSpectrumUpdate?.(processedData)}calculateMemoryUsage(){const arraySize=this.config.maxPoints*Float32Array.BYTES_PER_ELEMENT;const baseArrayMemory=4*arraySize;const rawWaterfallMemory=this.waterfallData.reduce((sum,frame)=>sum+frame.length*Float32Array.BYTES_PER_ELEMENT,0);const outputWaterfallMemory=this.waterfallOutputData.reduce((sum,frame)=>sum+frame.length*Float32Array.BYTES_PER_ELEMENT,0);return baseArrayMemory+rawWaterfallMemory+outputWaterfallMemory}getOutputData(data){const{outputRange:{start,end}}=this.config;const sourceData=data??this.realData;const outputData=sourceData.subarray(start,end);outputData.timestamp=sourceData.timestamp;return outputData}getFluorescenceOutputData(outputLength){const{processing:{enableFluorescence},outputRange:{start:outputRangeStart,end:outputRangeEnd}}=this.config;if(!enableFluorescence||0===this.fluorescenceData.length)return;if(outputLength<=0)return;const outputData=new Uint32Array(outputLength*FLUORESCENCE.LEVEL_RANGE);const srcLength=outputRangeEnd-outputRangeStart;const ratio=srcLength/outputLength;for(let i=0;i<outputLength;i++){const srcIndex=Math.floor(outputRangeStart+(i+.5)*ratio);const srcBase=srcIndex*FLUORESCENCE.LEVEL_RANGE;const dstBase=i*FLUORESCENCE.LEVEL_RANGE;for(let level=0;level<FLUORESCENCE.LEVEL_RANGE;level++)outputData[dstBase+level]=this.fluorescenceData[srcBase+level]}return outputData}getAllRawData(left){const{realOutputData,maxOutputData,minOutputData,avgOutputData,templateOutputData,occupancyOutputData,backgroundNoiseOutputData,thresholdOutputData,waterfallOutputData,extraOutputData,srcIndexCache}=this;realOutputData.timestamp=this.realData.timestamp;const rawData={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};const cursorIndex=resolveCursorIndexByLeft(left,realOutputData.length,this.config.maxPoints);return{realData:realOutputData,maxData:maxOutputData,minData:minOutputData,avgData:avgOutputData,templateData:templateOutputData,occupancyData:occupancyOutputData,backgroundNoiseData:backgroundNoiseOutputData,thresholdData:thresholdOutputData,fluorescenceData:this.getFluorescenceOutputData(realOutputData.length),waterfallData:waterfallOutputData,extraData:extraOutputData,srcIndexCache,rawData,cursorIndex}}}const createEmptySnapshot=()=>({data:[],rawData:[],range:{x:[0,0],y:[0,0]},sourceSize:{width:0,height:0},columnIndices:new Uint32Array(0),rowIndices:new Uint32Array(0)});const copyFrame=source=>{const frame=new Float32Array(source);frame.timestamp=source.timestamp;if(void 0!==source.allValid)frame.allValid=source.allValid;if(source.peaks)frame.peaks={...source.peaks,peaks:source.peaks.peaks.map(peak=>({...peak}))};return frame};const validateOutputSize=(value,name)=>{if(!Number.isInteger(value)||value<=0)throw new DataValidationError(`${name} 必须是正整数`)};const normalizeRange=(range,extent,name)=>{const[rawStart,rawEnd]=range;if(!Number.isFinite(rawStart)||!Number.isFinite(rawEnd)||rawStart>=rawEnd)throw new DataValidationError(`${name} 采样范围无效`);const start=Math.max(0,Math.min(extent,Math.floor(rawStart)));const end=Math.max(0,Math.min(extent,Math.ceil(rawEnd)));if(start>=end)throw new DataValidationError(`${name} 采样范围超出数据边界`);return[start,end]};const reconcileRange=(range,previousExtent,nextExtent)=>{if(0===range[0]&&range[1]===previousExtent)return[0,nextExtent];const start=Math.min(range[0],nextExtent-1);const end=Math.max(start+1,Math.min(range[1],nextExtent));return[start,end]};const reconcileYRange=(range,previousExtent,nextExtent)=>{if(0===range[0]&&range[1]===previousExtent)return[0,nextExtent];if(range[1]===previousExtent){const span=Math.min(range[1]-range[0],nextExtent);return[nextExtent-span,nextExtent]}return reconcileRange(range,previousExtent,nextExtent)};const reconcileAppendedRange=(range,previousExtent,nextExtent,droppedItems)=>{if(0===range[0]&&range[1]===previousExtent)return[0,nextExtent];const span=Math.min(range[1]-range[0],nextExtent);if(range[1]===previousExtent)return[nextExtent-span,nextExtent];const shiftedStart=range[0]-droppedItems;const start=Math.max(0,Math.min(shiftedStart,nextExtent-span));return[start,start+span]};const createCenterIndices=(range,outputSize)=>{const[start,end]=range;const length=end-start;const resultLength=Math.min(length,outputSize);const indices=new Uint32Array(resultLength);if(length<=outputSize){for(let index=0;index<resultLength;index++)indices[index]=start+index;return indices}for(let index=0;index<resultLength;index++){const binStart=Math.floor(index*length/resultLength);const binEnd=Math.floor((index+1)*length/resultLength);indices[index]=start+Math.min(binEnd-1,Math.floor((binStart+binEnd)/2))}return indices};const isSameRange=(left,right)=>left[0]===right[0]&&left[1]===right[1];const isSameIndices=(left,right)=>{if(left.length!==right.length)return false;for(let index=0;index<left.length;index++)if(left[index]!==right[index])return false;return true};const maxPoolFrame=(source,range,outputWidth)=>{const slicedFrame=source.subarray(range[0],range[1]);const sourceLength=slicedFrame.length;const outputLength=Math.min(sourceLength,outputWidth);if(sourceLength<=outputWidth){if(0===range[0]&&range[1]===source.length)return source;slicedFrame.timestamp=source.timestamp;return slicedFrame}const output=new Float32Array(outputLength);output.timestamp=source.timestamp;for(let outputIndex=0;outputIndex<outputLength;outputIndex++){const start=Math.floor(outputIndex*sourceLength/outputLength);const end=Math.floor((outputIndex+1)*sourceLength/outputLength);let maxValue=Number.NaN;for(let sourceIndex=start;sourceIndex<end;sourceIndex++){const value=slicedFrame[sourceIndex];if(Number.isFinite(value)&&(!Number.isFinite(maxValue)||value>maxValue))maxValue=value}output[outputIndex]=maxValue}return output};class WaterfallAnalyzer{maxFrames;onWaterfallUpdate;sourceData=[];samplingOptions;snapshot=createEmptySnapshot();constructor(config={}){if(void 0!==config.maxFrames&&(!Number.isInteger(config.maxFrames)||config.maxFrames<=0))throw new DataValidationError("maxFrames 必须是正整数");this.maxFrames=config.maxFrames;this.onWaterfallUpdate=config.onWaterfallUpdate}setData(frames){if(0===frames.length){this.sourceData=[];this.samplingOptions=void 0;return this.publishSnapshot(createEmptySnapshot())}const width=frames[0].length;if(0===width)throw new DataValidationError("瀑布图帧不能为空");for(const frame of frames)if(frame.length!==width)throw new DataValidationError("瀑布图数据必须是等宽矩阵");const previousSize=this.snapshot.sourceSize;let nextData=[...frames];if(void 0!==this.maxFrames&&nextData.length>this.maxFrames)nextData=nextData.slice(-this.maxFrames);let nextSamplingOptions=this.samplingOptions;if(nextSamplingOptions&&previousSize.width>0&&previousSize.height>0)nextSamplingOptions={...nextSamplingOptions,x:reconcileRange(nextSamplingOptions.x,previousSize.width,width),y:reconcileYRange(nextSamplingOptions.y,previousSize.height,nextData.length)};const nextSnapshot=this.createSnapshot(nextData,nextSamplingOptions);this.sourceData=nextData;this.samplingOptions=nextSamplingOptions;return this.publishSnapshot(nextSnapshot)}appendFrame(source){if(0===source.length)throw new DataValidationError("瀑布图帧不能为空");const width=this.sourceData[0]?.length;if(void 0!==width&&source.length!==width)throw new DataValidationError("追加帧宽度必须与现有瀑布图一致");const previousHeight=this.sourceData.length;const nextData=[...this.sourceData,copyFrame(source)];let droppedFrames=0;if(void 0!==this.maxFrames&&nextData.length>this.maxFrames){droppedFrames=nextData.length-this.maxFrames;nextData.splice(0,droppedFrames)}let nextSamplingOptions=this.samplingOptions;if(nextSamplingOptions&&previousHeight>0)nextSamplingOptions={...nextSamplingOptions,y:reconcileAppendedRange(nextSamplingOptions.y,previousHeight,nextData.length,droppedFrames)};const nextSnapshot=this.createSnapshot(nextData,nextSamplingOptions);this.sourceData=nextData;this.samplingOptions=nextSamplingOptions;return this.publishSnapshot(nextSnapshot)}updateSamplingRange(options){const sourceSize=this.getSourceSize(this.sourceData);if(0===sourceSize.width||0===sourceSize.height)throw new DataValidationError("设置采样范围前必须先设置瀑布图数据");validateOutputSize(options.outputWidth,"outputWidth");validateOutputSize(options.outputHeight,"outputHeight");const nextOptions={x:normalizeRange(options.x,sourceSize.width,"X"),y:normalizeRange(options.y,sourceSize.height,"Y"),outputWidth:options.outputWidth,outputHeight:options.outputHeight};const nextSnapshot=this.createSnapshot(this.sourceData,nextOptions);this.samplingOptions=nextOptions;return this.publishSnapshot(nextSnapshot)}getSnapshot(){return this.snapshot}createSnapshot(sourceData,options){const sourceSize=this.getSourceSize(sourceData);if(0===sourceSize.width||0===sourceSize.height)return createEmptySnapshot();const xRange=options?.x??[0,sourceSize.width];const yRange=options?.y??[0,sourceSize.height];const outputWidth=options?.outputWidth??xRange[1]-xRange[0];const outputHeight=options?.outputHeight??yRange[1]-yRange[0];const columnIndices=createCenterIndices(xRange,outputWidth);const rowIndices=createCenterIndices(yRange,outputHeight);const reusableRows=this.createReusableRows(xRange,columnIndices);const data=Array.from(rowIndices,rowIndex=>{const source=sourceData[rowIndex];return reusableRows.get(source)??maxPoolFrame(source,xRange,outputWidth)});return{data,rawData:sourceData,range:{x:xRange,y:yRange},sourceSize,columnIndices,rowIndices}}createReusableRows(xRange,columnIndices){const previousSnapshot=this.snapshot;if(!isSameRange(previousSnapshot.range.x,xRange)||!isSameIndices(previousSnapshot.columnIndices,columnIndices))return new Map;const reusableRows=new Map;for(let outputIndex=0;outputIndex<previousSnapshot.rowIndices.length;outputIndex++){const source=previousSnapshot.rawData[previousSnapshot.rowIndices[outputIndex]];const output=previousSnapshot.data[outputIndex];if(source&&output)reusableRows.set(source,output)}return reusableRows}getSourceSize(sourceData){return{width:sourceData[0]?.length??0,height:sourceData.length}}publishSnapshot(snapshot){this.snapshot=snapshot;this.onWaterfallUpdate?.(snapshot);return snapshot}}var types_OrientationType=/*#__PURE__*/function(OrientationType){OrientationType["Horizontal"]="horizontal";OrientationType["Vertical"]="vertical";return OrientationType}({});var types_GraphicType=/*#__PURE__*/function(GraphicType){GraphicType["Circle"]="circle";GraphicType["Rect"]="rect";GraphicType["Line"]="line";GraphicType["Stepline"]="stepline";GraphicType["Bar"]="bar";GraphicType["Area"]="area";return GraphicType}({});var types_SeriesEventType=/*#__PURE__*/function(SeriesEventType){SeriesEventType["PropertyChanged"]="propertyChanged";SeriesEventType["SeriesAdded"]="seriesAdded";SeriesEventType["SeriesRemoved"]="seriesRemoved";SeriesEventType["SeriesCleared"]="seriesCleared";return SeriesEventType}({});const DEFAULT_SERIES_CONFIG={thickness:1,orientation:types_OrientationType.Horizontal,display:true,type:types_GraphicType.Line,color:"#00000000",label:""};class SeriesManager{seriesMap=new Map;changeCallbacks=[];subscribers=new Map;constructor(initialSeries=[]){this.initializeSeries(initialSeries)}initializeSeries(seriesConfigs){for(const config of seriesConfigs)this.addSeries(config)}addSeries(config){const fullConfig={...DEFAULT_SERIES_CONFIG,...config};this.seriesMap.set(config.name,fullConfig);this.emitEvent({type:types_SeriesEventType.SeriesAdded,name:config.name,series:fullConfig})}getSeries(name){return this.seriesMap.get(name)}getAllSeries(){return Array.from(this.seriesMap.values())}getAllConfigs(){const configs={};for(const[name,config]of this.seriesMap)configs[name]={...config};return configs}setSeriesProperty(name,property,value){const series=this.seriesMap.get(name);if(!series)return false;if("name"===property)return false;const oldValue=series[property];const updatedSeries={...series,[property]:value};this.seriesMap.set(name,updatedSeries);this.notifyChange(name,property,value);this.emitEvent({type:types_SeriesEventType.PropertyChanged,name,property,value,oldValue,series:updatedSeries});return true}removeSeries(name){const series=this.seriesMap.get(name);if(!series)return false;const deleted=this.seriesMap.delete(name);if(deleted)this.emitEvent({type:types_SeriesEventType.SeriesRemoved,name,series});return deleted}hasSeries(name){return this.seriesMap.has(name)}onChange(callback){this.changeCallbacks.push(callback)}offChange(callback){const index=this.changeCallbacks.indexOf(callback);if(index>-1)this.changeCallbacks.splice(index,1)}notifyChange(name,property,value){for(const callback of this.changeCallbacks)callback(name,property,value)}clear(){this.seriesMap.clear();this.emitEvent({type:types_SeriesEventType.SeriesCleared})}size(){return this.seriesMap.size}subscribe(subscriber,options={}){this.subscribers.set(subscriber,options);if(options.immediate){for(const series of this.seriesMap.values())if(this.shouldNotifySubscriber(subscriber,{type:types_SeriesEventType.SeriesAdded,name:series.name,series}))subscriber({type:types_SeriesEventType.SeriesAdded,name:series.name,series})}return()=>{this.subscribers.delete(subscriber)}}unsubscribe(subscriber){this.subscribers.delete(subscriber)}emitEvent(event){for(const[subscriber]of this.subscribers)if(this.shouldNotifySubscriber(subscriber,event))subscriber(event)}shouldNotifySubscriber(subscriber,event){const options=this.subscribers.get(subscriber);if(!options)return false;if(options.eventTypes&&!options.eventTypes.includes(event.type))return false;if(options.seriesNames&&event.name&&!options.seriesNames.includes(event.name))return false;return true}}export{DataValidationError,ERROR_MESSAGES,types_GraphicType as GraphicType,IndexOutOfBoundsError,LevelStreamAnalyzer,types_OrientationType as OrientationType,SPECTRUM,types_SeriesEventType as SeriesEventType,SeriesManager,SpectrumAnalyzer,SpectrumError,WaterfallAnalyzer};
1
+ 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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rfkit/spectrum-analyzer",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "A high-performance spectrum analyzer library for RF signal processing, supporting real-time spectrum analysis, waterfall display, and multi-segment frequency scanning",
6
6
  "exports": {
@@ -10,4 +10,3 @@ export declare const DEFAULT_LEVEL_STREAM_CONFIG: {
10
10
  maxPoints: undefined;
11
11
  onLevelStreamUpdate: (data: Map<number, number>) => void;
12
12
  };
13
- //# sourceMappingURL=constants.d.ts.map
@@ -1,11 +1,4 @@
1
1
  import type { LevelStreamConfig } from './types';
2
- /**
3
- * [SYNC-WASM] 电平流分析器
4
- * @sync src-rust/src/level_stream/core.rs:WasmLevelStreamAnalyzer
5
- *
6
- * 用于统计电平流数据的分析器。
7
- * 支持设置缓存时间、颗粒度和统计范围,并提供数据处理和概率计算功能。
8
- */
9
2
  export default class LevelStreamAnalyzer {
10
3
  protected config: Required<Omit<LevelStreamConfig, 'maxPoints'>> & {
11
4
  maxPoints?: number;
@@ -14,43 +7,12 @@ export default class LevelStreamAnalyzer {
14
7
  private probabilityData;
15
8
  private count;
16
9
  constructor(config: LevelStreamConfig);
17
- /**
18
- * 重置分析器的状态,清空频谱数据和概率数据,但保留配置信息。
19
- */
20
10
  reset(): void;
21
11
  setConfig(config: Partial<LevelStreamConfig>): void;
22
- /**
23
- * 处理一个新的电平值,更新频谱数据并重新计算概率。
24
- * @param level 新的电平值
25
- * @param timestamp 时间戳
26
- */
27
12
  process(level: number, timestamp: number): void;
28
- /**
29
- * 更新新数据对应的区间概率
30
- * @param level 新的电平值
31
- */
32
13
  private updateProbability;
33
- /**
34
- * 查询所有统计的概率数据。
35
- * @returns 概率数据的 Map 对象
36
- */
37
14
  getAll(): Map<number, number>;
38
- /**
39
- * 移除过期的频谱数据。
40
- * 支持两种模式:
41
- * 1. 点模式(maxPoints):保留最新的 N 个点
42
- * 2. 时间模式(cacheTime):保留指定时间范围内的数据
43
- */
44
15
  private removeExpiredData;
45
- /**
46
- * 添加新的电平值到频谱数据中。
47
- * @param level 新的电平值
48
- * @param timestamp 时间戳
49
- */
50
16
  private addNewData;
51
- /**
52
- * 输出符合统计范围的概率数据。
53
- */
54
17
  private outputData;
55
18
  }
56
- //# sourceMappingURL=index.d.ts.map
@@ -10,4 +10,3 @@ export interface LevelStreamOutputData {
10
10
  spectrumData: Float32Array;
11
11
  timestampData: Float64Array;
12
12
  }
13
- //# sourceMappingURL=types.d.ts.map
@@ -4,7 +4,6 @@ export declare const DEFAULT_SERIES_CONFIG: {
4
4
  readonly orientation: OrientationType.Horizontal;
5
5
  readonly display: true;
6
6
  readonly type: GraphicType.Line;
7
- readonly color: "#00000000";
8
- readonly label: "";
7
+ readonly color: '#00000000';
8
+ readonly label: '';
9
9
  };
10
- //# sourceMappingURL=constants.d.ts.map
@@ -1,112 +1,24 @@
1
1
  import type { SeriesChangeCallback, SeriesConfig, SeriesSubscriber, SubscriptionOptions } from './types';
2
- /**
3
- * Series 处理工具,用于管理图形系列的配置
4
- * 支持初始化、属性设置、变更回调和订阅功能
5
- */
6
2
  export default class SeriesManager {
7
3
  private seriesMap;
8
4
  private changeCallbacks;
9
5
  private subscribers;
10
- /**
11
- * 构造函数,支持初始化系列配置
12
- * @param initialSeries 初始系列配置数组
13
- */
14
6
  constructor(initialSeries?: SeriesConfig[]);
15
- /**
16
- * 初始化系列配置
17
- * @param seriesConfigs 系列配置数组
18
- */
19
7
  private initializeSeries;
20
- /**
21
- * 添加新的系列配置
22
- * @param config 系列配置
23
- */
24
8
  addSeries(config: SeriesConfig): void;
25
- /**
26
- * 获取指定名称的系列配置
27
- * @param name 系列名称
28
- * @returns 系列配置或 undefined
29
- */
30
9
  getSeries(name: string): SeriesConfig | undefined;
31
- /**
32
- * 获取所有系列配置
33
- * @returns 所有系列配置的数组
34
- */
35
10
  getAllSeries(): SeriesConfig[];
36
- /**
37
- * 获取当前所有配置
38
- * @returns 包含所有系列配置的对象,以系列名为键
39
- */
40
11
  getAllConfigs(): Record<string, SeriesConfig>;
41
- /**
42
- * 设置指定系列的某个属性
43
- * @param name 系列名称
44
- * @param property 属性名
45
- * @param value 属性值
46
- * @returns 是否设置成功
47
- */
48
12
  setSeriesProperty<K extends keyof SeriesConfig>(name: string, property: K, value: SeriesConfig[K]): boolean;
49
- /**
50
- * 删除指定名称的系列
51
- * @param name 系列名称
52
- * @returns 是否删除成功
53
- */
54
13
  removeSeries(name: string): boolean;
55
- /**
56
- * 检查系列是否存在
57
- * @param name 系列名称
58
- * @returns 是否存在
59
- */
60
14
  hasSeries(name: string): boolean;
61
- /**
62
- * 添加变更回调函数
63
- * @param callback 回调函数
64
- */
65
15
  onChange(callback: SeriesChangeCallback): void;
66
- /**
67
- * 移除变更回调函数
68
- * @param callback 要移除的回调函数
69
- */
70
16
  offChange(callback: SeriesChangeCallback): void;
71
- /**
72
- * 通知所有回调函数属性变更
73
- * @param name 系列名称
74
- * @param property 属性名
75
- * @param value 新值
76
- */
77
17
  private notifyChange;
78
- /**
79
- * 清空所有系列配置
80
- */
81
18
  clear(): void;
82
- /**
83
- * 获取系列数量
84
- * @returns 系列数量
85
- */
86
19
  size(): number;
87
- /**
88
- * 订阅系列变更事件
89
- * @param subscriber 订阅回调函数
90
- * @param options 订阅选项
91
- * @returns 取消订阅的函数
92
- */
93
20
  subscribe(subscriber: SeriesSubscriber, options?: SubscriptionOptions): () => void;
94
- /**
95
- * 取消订阅
96
- * @param subscriber 要取消的订阅回调函数
97
- */
98
21
  unsubscribe(subscriber: SeriesSubscriber): void;
99
- /**
100
- * 发送事件给所有订阅者
101
- * @param event 事件数据
102
- */
103
22
  private emitEvent;
104
- /**
105
- * 判断是否应该通知订阅者
106
- * @param subscriber 订阅者
107
- * @param event 事件数据
108
- * @returns 是否应该通知
109
- */
110
23
  private shouldNotifySubscriber;
111
24
  }
112
- //# sourceMappingURL=index.d.ts.map
@@ -40,4 +40,3 @@ export interface SubscriptionOptions {
40
40
  eventTypes?: SeriesEventType[];
41
41
  seriesNames?: string[];
42
42
  }
43
- //# sourceMappingURL=types.d.ts.map
@@ -38,15 +38,14 @@ export declare const DEFAULT_SPECTRUM_CONFIG: {
38
38
  readonly templateTolerance: 0;
39
39
  };
40
40
  export declare const ERROR_MESSAGES: {
41
- readonly EMPTY_SEGMENTS: "频段配置不能为空";
42
- readonly INVALID_CONFIG: "必须且只能配置 segments 或 bandwidthConfig 其中之一";
43
- readonly EMPTY_BANDWIDTH: "bandwidthConfig 不能为空";
41
+ readonly EMPTY_SEGMENTS: '频段配置不能为空';
42
+ readonly INVALID_CONFIG: '必须且只能配置 segments 或 bandwidthConfig 其中之一';
43
+ readonly EMPTY_BANDWIDTH: 'bandwidthConfig 不能为空';
44
44
  readonly INVALID_SEGMENT: (index: number) => string;
45
45
  readonly INDEX_OUT_OF_BOUNDS: (index: number) => string;
46
46
  readonly INVALID_ANTENNA_FACTOR_LENGTH: (points: number) => string;
47
- readonly INVALID_ANTENNA_FACTOR: "天线因子数据必须是有效的正数";
48
- readonly INVALID_SAMPLING_RANGE: "采样范围无效";
49
- readonly INVALID_MAX_POINTS: "点数必须大于0";
47
+ readonly INVALID_ANTENNA_FACTOR: '天线因子数据必须是有效的正数';
48
+ readonly INVALID_SAMPLING_RANGE: '采样范围无效';
49
+ readonly INVALID_MAX_POINTS: '点数必须大于0';
50
50
  readonly INVALID_LENGTH: (expected: number) => string;
51
51
  };
52
- //# sourceMappingURL=constants.d.ts.map
@@ -1,16 +1,12 @@
1
1
  export declare class SpectrumError extends Error {
2
2
  readonly code: string;
3
- readonly details?: unknown | undefined;
4
- constructor(message: string, code: string, details?: unknown | undefined);
3
+ readonly details?: unknown;
4
+ constructor(message: string, code: string, details?: unknown);
5
5
  }
6
6
  export declare class DataValidationError extends SpectrumError {
7
7
  constructor(message: string, details?: unknown);
8
8
  }
9
- /**
10
- * 索引越界错误
11
- */
12
9
  export declare class IndexOutOfBoundsError extends SpectrumError {
13
10
  readonly index: number;
14
11
  constructor(message: string, index: number, details?: unknown);
15
12
  }
16
- //# sourceMappingURL=errors.d.ts.map