@rfkit/spectrum-analyzer 0.1.42 → 0.1.43

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 (37) hide show
  1. package/README.md +86 -10
  2. package/core/LevelStreamAnalyzer/index.d.ts +3 -0
  3. package/core/LevelStreamAnalyzer/index.d.ts.map +1 -1
  4. package/core/SpectrumAnalyzer/index.d.ts.map +1 -1
  5. package/core/SpectrumAnalyzer/tools.d.ts +21 -0
  6. package/core/SpectrumAnalyzer/tools.d.ts.map +1 -1
  7. package/core/SpectrumAnalyzer/types.d.ts +1 -1
  8. package/core/SpectrumAnalyzer/types.d.ts.map +1 -1
  9. package/index.js +1 -1
  10. package/package.json +14 -3
  11. package/wasm/core/LevelStreamAnalyzer/constants.d.ts +13 -0
  12. package/wasm/core/LevelStreamAnalyzer/constants.d.ts.map +1 -0
  13. package/wasm/core/LevelStreamAnalyzer/index.d.ts +56 -0
  14. package/wasm/core/LevelStreamAnalyzer/index.d.ts.map +1 -0
  15. package/wasm/core/LevelStreamAnalyzer/types.d.ts +13 -0
  16. package/wasm/core/LevelStreamAnalyzer/types.d.ts.map +1 -0
  17. package/wasm/core/SeriesManager/constants.d.ts +10 -0
  18. package/wasm/core/SeriesManager/constants.d.ts.map +1 -0
  19. package/wasm/core/SeriesManager/index.d.ts +112 -0
  20. package/wasm/core/SeriesManager/index.d.ts.map +1 -0
  21. package/wasm/core/SeriesManager/types.d.ts +43 -0
  22. package/wasm/core/SeriesManager/types.d.ts.map +1 -0
  23. package/wasm/core/SpectrumAnalyzer/constants.d.ts +34 -0
  24. package/wasm/core/SpectrumAnalyzer/constants.d.ts.map +1 -0
  25. package/wasm/core/SpectrumAnalyzer/errors.d.ts +16 -0
  26. package/wasm/core/SpectrumAnalyzer/errors.d.ts.map +1 -0
  27. package/wasm/core/SpectrumAnalyzer/index.d.ts +207 -0
  28. package/wasm/core/SpectrumAnalyzer/index.d.ts.map +1 -0
  29. package/wasm/core/SpectrumAnalyzer/tools.d.ts +89 -0
  30. package/wasm/core/SpectrumAnalyzer/tools.d.ts.map +1 -0
  31. package/wasm/core/SpectrumAnalyzer/types.d.ts +89 -0
  32. package/wasm/core/SpectrumAnalyzer/types.d.ts.map +1 -0
  33. package/wasm/index.d.ts +11 -0
  34. package/wasm/index.d.ts.map +1 -0
  35. package/wasm/index.js +1 -0
  36. package/wasm/src-rust/pkg/rfkit_spectrum_analyzer_wasm.js +2 -0
  37. package/wasm/src-rust/pkg/rfkit_spectrum_analyzer_wasm_bg.wasm +0 -0
package/README.md CHANGED
@@ -1,26 +1,102 @@
1
- # rfkit-spectrum-analyzer
2
- > A high-performance spectrum analyzer library for RF signal processing, supporting real-time spectrum analysis, waterfall display, and multi-segment frequency scanning.
1
+ # @rfkit/spectrum-analyzer
3
2
 
4
- ## Setup
3
+ 高性能 RF 频谱分析库,支持实时频谱分析、瀑布图显示和多段频率扫描。
4
+
5
+ 提供 **JS 版本** 和 **WASM 高性能版本** 两种选择。
6
+
7
+ ## 安装
5
8
 
6
9
  ```bash
7
10
  pnpm add @rfkit/spectrum-analyzer
8
11
  ```
9
12
 
10
- ## Use
13
+ ## 使用
14
+
15
+ ### JS 版本(默认)
16
+
11
17
  ```typescript
12
18
  import {
13
19
  SpectrumAnalyzer,
20
+ LevelStreamAnalyzer,
14
21
  type SpectrumConfig,
15
22
  type SpectrumOutputData,
16
- type TimestampedFloat32Array,
17
23
  } from '@rfkit/spectrum-analyzer';
18
24
 
19
- new SpectrumAnalyzer({
20
- processing: {
21
- enableMetrics,
22
- enableWaterfall,
25
+ const analyzer = new SpectrumAnalyzer({
26
+ maxPoints: 2048,
27
+ outputPoints: 1024,
28
+ enableStatistics: true,
29
+ onSpectrumUpdate: (data) => {
30
+ console.log(data.realData);
23
31
  },
24
- onSpectrumUpdate: handleSpectrumUpdate,
25
32
  });
33
+
34
+ // 处理频谱数据
35
+ analyzer.process({ data: Float32Array, timestamp: Date.now() });
26
36
  ```
37
+
38
+ ### WASM 高性能版本
39
+
40
+ WASM 版本 API 与 JS 版本完全一致,仅需在应用启动时初始化一次。
41
+
42
+ ```typescript
43
+ import {
44
+ initWasm,
45
+ SpectrumAnalyzer,
46
+ LevelStreamAnalyzer,
47
+ } from '@rfkit/spectrum-analyzer/wasm';
48
+
49
+ // 应用启动时初始化(仅需一次)
50
+ await initWasm();
51
+
52
+ // 之后使用方式与 JS 版本完全一致
53
+ const analyzer = new SpectrumAnalyzer({
54
+ maxPoints: 2048,
55
+ outputPoints: 1024,
56
+ enableStatistics: true,
57
+ onSpectrumUpdate: (data) => {
58
+ console.log(data.realData);
59
+ },
60
+ });
61
+
62
+ analyzer.process({ data: Float32Array, timestamp: Date.now() });
63
+ ```
64
+
65
+ ## 性能对比
66
+
67
+ | 场景 | JS 版本 | WASM 版本 | 提升 |
68
+ |------|---------|-----------|------|
69
+ | 8192 点数据 | 0.032 ms | 0.020 ms | **35%** |
70
+ | 2000 点 / 300Hz | 0.023 ms | 0.020 ms | **13%** |
71
+
72
+ 两个版本在 300Hz 更新频率下都能轻松满足,CPU 占用 < 1%。
73
+
74
+ ## 核心组件
75
+
76
+ ### SpectrumAnalyzer
77
+
78
+ 频谱分析器,支持:
79
+ - 实时数据处理与重采样
80
+ - 最大值/最小值/平均值统计
81
+ - 瀑布图数据管理
82
+ - 天线因子补偿
83
+ - 模板超限检测
84
+ - 多段频率扫描
85
+
86
+ ### LevelStreamAnalyzer
87
+
88
+ 电平流分析器,支持:
89
+ - 电平概率分布统计
90
+ - 时间/点数缓存模式
91
+
92
+ ## 构建命令
93
+
94
+ ```bash
95
+ pnpm build # 构建 JS 版本
96
+ pnpm build:wasm # 构建 WASM 版本
97
+ pnpm build:all # 构建全部
98
+ ```
99
+
100
+ ## License
101
+
102
+ MIT
@@ -1,5 +1,8 @@
1
1
  import type { LevelStreamConfig } from './types';
2
2
  /**
3
+ * [SYNC-WASM] 电平流分析器
4
+ * @sync src-rust/src/level_stream/core.rs:WasmLevelStreamAnalyzer
5
+ *
3
6
  * 用于统计电平流数据的分析器。
4
7
  * 支持设置缓存时间、颗粒度和统计范围,并提供数据处理和概率计算功能。
5
8
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/LevelStreamAnalyzer/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAEtC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,GAAG;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE1F,OAAO,CAAC,YAAY,CAA8C;IAElE,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,CAAC,KAAK,CAAK;gBAEN,MAAM,EAAE,iBAAiB;IAQrC;;OAEG;IACI,KAAK,IAAI,IAAI;IAMb,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAoB1D;;;;OAIG;IACI,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAOtD;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACI,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAIpC;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAwCzB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACH,OAAO,CAAC,UAAU;CA6BnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/LevelStreamAnalyzer/index.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAEtC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,GAAG;QACjE,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,OAAO,CAAC,YAAY,CAA8C;IAElE,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,CAAC,KAAK,CAAK;gBAEN,MAAM,EAAE,iBAAiB;IAQrC;;OAEG;IACI,KAAK,IAAI,IAAI;IAMb,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAqB1D;;;;OAIG;IACI,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAOtD;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACI,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAIpC;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAwCzB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACH,OAAO,CAAC,UAAU;CA6BnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/index.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,10 @@
1
+ /**
2
+ * SpectrumAnalyzer 工具函数
3
+ *
4
+ * [SYNC-WASM] 本文件包含需要与 Rust WASM 版本同步维护的核心算法
5
+ * @see src-rust/src/spectrum_analyzer/resampler.rs
6
+ * @see src-rust/src/spectrum_analyzer/template.rs
7
+ */
1
8
  import type { TemplateOverData, TimestampedFloat32Array } from './types';
2
9
  export declare const arrayKeepAttribute: (source: Float32Array & {
3
10
  max?: number;
@@ -19,6 +26,13 @@ export declare const resample: ({ realData, antennaFactorData, antennaFactorSwit
19
26
  realOutputData: TimestampedFloat32Array;
20
27
  srcIndexCache: Uint32Array;
21
28
  };
29
+ /**
30
+ * [SYNC-WASM] 多数据系列重采样
31
+ * @sync src-rust/src/spectrum_analyzer/resampler.rs:resample_multiple
32
+ *
33
+ * 从 maxPoints 重采样到 outputPoints,对每个区间取最大值
34
+ * 支持同时处理 real/max/min/avg/template/occupancy/backgroundNoise/fluorescence 数据
35
+ */
22
36
  export declare const resampleMultiple: ({ antennaFactorData, antennaFactorSwitch, outputPoints, realData, maxData, minData, avgData, templateData, backgroundNoiseData, fluorescenceData, occupancyData }: {
23
37
  antennaFactorData: Float32Array;
24
38
  antennaFactorSwitch?: boolean;
@@ -43,10 +57,17 @@ export declare const resampleMultiple: ({ antennaFactorData, antennaFactorSwitch
43
57
  fluorescenceOutputData: Map<number, number>[];
44
58
  };
45
59
  /**
60
+ * [SYNC-WASM] 额外数据重采样
61
+ * @sync src-rust/src/spectrum_analyzer/resampler.rs:resample_extra_data
62
+ *
46
63
  * 专门用于处理 extraData 的重采样,支持天线因子应用
47
64
  * 高性能优化版本:预分配内存,消除重复代码,集成 getOutputData 处理
48
65
  */
49
66
  export declare const resampleExtraData: (extraData: Record<string, Float32Array>, antennaFactorData: Float32Array, antennaFactorSwitch: boolean, srcIndexCache: Uint32Array, getOutputData: (data: Float32Array) => Float32Array) => Record<string, Float32Array>;
67
+ /**
68
+ * [SYNC-WASM] 全量版本:使用预分配优化的完整数据扫描
69
+ * @sync src-rust/src/spectrum_analyzer/template.rs:find_exceeding_datas
70
+ */
50
71
  export declare const findExceedingDatas: ({ realData, maxData, minData, templateData, tolerance }: {
51
72
  realData: TimestampedFloat32Array;
52
73
  maxData: Float32Array;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/tools.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,7 @@ export interface ScanSegment {
12
12
  }
13
13
  export interface ProcessInput {
14
14
  data: Float32Array;
15
- timestamp: any;
15
+ timestamp: number | string | bigint;
16
16
  frequency?: number;
17
17
  segmentOffset?: number;
18
18
  offset?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/types.ts"],"names":[],"mappings":"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"}
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 DEFAULT_SPECTRUM_CONFIG={maxPoints:SPECTRUM.OUTPUT_POINTS,waterfallMaxFrames:SPECTRUM.WATERFALL_MAX_FRAMES,initialValue:SPECTRUM.INITIAL_VALUE,processing:{enableWaterfall:false,enableMetrics:false,enableFluorescence:false},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 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,fluorescenceData,occupancyData})=>{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 hasFluorescenceStats=fluorescenceData&&fluorescenceData.length>0;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);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]+antennaFactor;if(hasBackgroundNoiseData)backgroundNoiseOutputData[i]=backgroundNoiseData[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(hasFluorescenceStats)fluorescenceOutputData=fluorescenceData}}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;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}}realOutputData[i]=applyAntennaFactor(realMaxVal,realMaxIdx);srcIndexCache[i]=realMaxIdx;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){const occupancyValue=occupancyData[realMaxIdx];occupancyOutputData[i]=applyAntennaFactor(occupancyValue,realMaxIdx)}if(hasBackgroundNoiseData){const backgroundNoiseValue=backgroundNoiseData[realMaxIdx];backgroundNoiseOutputData[i]=applyAntennaFactor(backgroundNoiseValue,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}}return{realOutputData,maxOutputData,minOutputData,avgOutputData,templateOutputData,occupancyOutputData,backgroundNoiseOutputData,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;waterfallData;srcIndexCache;realOutputData;waterfallOutputData;scanProgress;lastIndex;processTimes;lastProcessTime;cachedExceedingDatas=[];fluorescenceData;fluorescenceMaxCount=0;extraData={};extraOutputData={};pendingDataFlags={["occupancy"]:false,["template"]:false,["backgroundNoise"]: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},outputRange:{start:config.outputRange?.start??0,end:config.outputRange?.end??maxPoints??SPECTRUM.OUTPUT_POINTS}};this.reset()}process({data,timestamp,segmentOffset=0,offset=0}){const processStartTime=performance.now();try{if(!data?.length)return;this.isProcessing=true;this.hasProcessedData=true;let index=offset;if(this.segments.length){const segment=this.segments[segmentOffset];if(!segment)throw new DataValidationError(ERROR_MESSAGES.INVALID_SEGMENT(segmentOffset));index=segment.startIndex+offset;this.scanProgress=(index+data.length)/this.config.maxPoints}else if(data.length!==this.config.maxPoints)this.updateMaxPoints(data.length);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);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)}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})}}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})}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}this.notifySpectrumUpdate({occupancyData:this.resampleSingleData(this.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:{enableMetrics,enableFluorescence}}=this.config;this.antennaFactorData=new Float32Array(maxPoints);const realData=new Float32Array(maxPoints);realData.timestamp="";realData.fill(SPECTRUM.INITIAL_VALUE);this.realData=realData;this.maxData=new Float32Array(enableMetrics?maxPoints:0).fill(SPECTRUM.INITIAL_VALUE);this.minData=new Float32Array(enableMetrics?maxPoints:0).fill(SPECTRUM.INITIAL_VALUE);this.avgData=new Float32Array(enableMetrics?maxPoints:0).fill(SPECTRUM.INITIAL_VALUE);this.templateData=new Float32Array;this.backgroundNoiseData=new Float32Array;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});this.fluorescenceData=enableFluorescence?Array.from({length:maxPoints},()=>new Map):[];this.fluorescenceMaxCount=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,["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,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()}}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)}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}resampleDataSeries(){const{antennaFactorData,antennaFactorSwitch,maxData,minData,avgData,templateData,occupancyData,backgroundNoiseData,config:{maxPoints,outputPoints}}=this;const activeAntennaFactorData=antennaFactorSwitch?antennaFactorData:new Float32Array(maxPoints);const{realOutputData,srcIndexCache,maxOutputData,minOutputData,avgOutputData,templateOutputData,occupancyOutputData,backgroundNoiseOutputData}=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});this.srcIndexCache=srcIndexCache;return{realData:realOutputData,maxData:maxOutputData,minData:minOutputData,avgData:avgOutputData,templateData:templateOutputData,occupancyData:occupancyOutputData,backgroundNoiseData:backgroundNoiseOutputData,fluorescenceData:this.getFluorescenceOutputData(),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){if(this.config.maxPoints===maxPoints)return;this.config={...this.config,maxPoints,outputRange:{start:0,end:maxPoints}};this.reset(true)}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)return;if(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)}let allValid=true;const length=data.length;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);this.updateFluorescenceStats(dataIndex,value)}}updateFluorescenceStats(dataIndex,value){const{processing}=this.config;if(!processing.enableFluorescence||!this.fluorescenceData)return;if(!Number.isFinite(value))return;const level=Math.round(value);let levelMap=this.fluorescenceData[dataIndex];if(!levelMap){levelMap=new Map;this.fluorescenceData[dataIndex]=levelMap}const currentCount=levelMap.get(level)??0;const newCount=currentCount+1;levelMap.set(level,newCount);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["extra"]}resampleSingleData(sourceData){const{srcIndexCache}=this;const outputLength=srcIndexCache.length;const outputData=new Float32Array(outputLength);for(let i=0;i<outputLength;i++)outputData[i]=sourceData[srcIndexCache[i]];return outputData}processPendingData(){if(!this.hasPendingData)return null;const pendingOutput={};if(this.pendingDataFlags["occupancy"]&&this.occupancyData.length>0){pendingOutput.occupancyData=this.resampleSingleData(this.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["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(data){const{outputRange:{start,end}}=this.config;const sourceData=data??this.fluorescenceData;if(!sourceData||0===sourceData.length)return;return sourceData.slice(start,end)}getAllRawData(){const{realData,maxData,minData,avgData,templateData,occupancyData,backgroundNoiseData,fluorescenceData,waterfallData,extraData,srcIndexCache}=this;return{realData,maxData,minData,avgData,templateData,occupancyData,backgroundNoiseData,fluorescenceData,waterfallData,extraData,srcIndexCache}}}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};
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 DEFAULT_SPECTRUM_CONFIG={maxPoints:SPECTRUM.OUTPUT_POINTS,waterfallMaxFrames:SPECTRUM.WATERFALL_MAX_FRAMES,initialValue:SPECTRUM.INITIAL_VALUE,processing:{enableWaterfall:false,enableMetrics:false,enableFluorescence:false},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 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,fluorescenceData,occupancyData})=>{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 hasFluorescenceStats=fluorescenceData&&fluorescenceData.length>0;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);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(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(hasFluorescenceStats)fluorescenceOutputData=fluorescenceData}}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]}realOutputData[i]=applyAntennaFactor(realMaxVal,realMaxIdx);srcIndexCache[i]=realMaxIdx;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(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}}return{realOutputData,maxOutputData,minOutputData,avgOutputData,templateOutputData,occupancyOutputData,backgroundNoiseOutputData,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;waterfallData;srcIndexCache;realOutputData;waterfallOutputData;scanProgress;lastIndex;processTimes;lastProcessTime;cachedExceedingDatas=[];fluorescenceData;fluorescenceMaxCount=0;extraData={};extraOutputData={};pendingDataFlags={["occupancy"]:false,["template"]:false,["backgroundNoise"]: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},outputRange:{start:config.outputRange?.start??0,end:config.outputRange?.end??maxPoints??SPECTRUM.OUTPUT_POINTS}};this.reset()}process({data,timestamp,segmentOffset=0,offset=0}){const processStartTime=performance.now();try{if(!data?.length)return;this.isProcessing=true;this.hasProcessedData=true;let index=offset;if(this.segments.length){const segment=this.segments[segmentOffset];if(!segment)throw new DataValidationError(ERROR_MESSAGES.INVALID_SEGMENT(segmentOffset));index=segment.startIndex+offset;this.scanProgress=(index+data.length)/this.config.maxPoints}else if(data.length!==this.config.maxPoints)this.updateMaxPoints(data.length);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);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)}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})}}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})}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:{enableMetrics,enableFluorescence}}=this.config;this.antennaFactorData=new Float32Array(maxPoints);const realData=new Float32Array(maxPoints);realData.timestamp="";realData.fill(SPECTRUM.INITIAL_VALUE);this.realData=realData;this.maxData=new Float32Array(enableMetrics?maxPoints:0).fill(SPECTRUM.INITIAL_VALUE);this.minData=new Float32Array(enableMetrics?maxPoints:0).fill(SPECTRUM.INITIAL_VALUE);this.avgData=new Float32Array(enableMetrics?maxPoints:0).fill(SPECTRUM.INITIAL_VALUE);this.templateData=new Float32Array;this.backgroundNoiseData=new Float32Array;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});this.fluorescenceData=enableFluorescence?Array.from({length:maxPoints},()=>new Map):[];this.fluorescenceMaxCount=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,["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,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()}}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)}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}resampleDataSeries(){const{antennaFactorData,antennaFactorSwitch,maxData,minData,avgData,templateData,occupancyData,backgroundNoiseData,config:{maxPoints,outputPoints}}=this;const activeAntennaFactorData=antennaFactorSwitch?antennaFactorData:new Float32Array(maxPoints);const{realOutputData,srcIndexCache,maxOutputData,minOutputData,avgOutputData,templateOutputData,occupancyOutputData,backgroundNoiseOutputData}=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});this.srcIndexCache=srcIndexCache;return{realData:realOutputData,maxData:maxOutputData,minData:minOutputData,avgData:avgOutputData,templateData:templateOutputData,occupancyData:occupancyOutputData,backgroundNoiseData:backgroundNoiseOutputData,fluorescenceData:this.getFluorescenceOutputData(),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){if(this.config.maxPoints===maxPoints)return;this.config={...this.config,maxPoints,outputRange:{start:0,end:maxPoints}};this.reset(true)}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)return;if(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)}let allValid=true;const length=data.length;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);this.updateFluorescenceStats(dataIndex,value)}}updateFluorescenceStats(dataIndex,value){const{processing}=this.config;if(!processing.enableFluorescence||!this.fluorescenceData)return;if(!Number.isFinite(value))return;const level=Math.round(value);let levelMap=this.fluorescenceData[dataIndex];if(!levelMap){levelMap=new Map;this.fluorescenceData[dataIndex]=levelMap}const currentCount=levelMap.get(level)??0;const newCount=currentCount+1;levelMap.set(level,newCount);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["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["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(data){const{outputRange:{start,end}}=this.config;const sourceData=data??this.fluorescenceData;if(!sourceData||0===sourceData.length)return;return sourceData.slice(start,end)}getAllRawData(){const{realData,maxData,minData,avgData,templateData,occupancyData,backgroundNoiseData,fluorescenceData,waterfallData,extraData,srcIndexCache}=this;return{realData,maxData,minData,avgData,templateData,occupancyData,backgroundNoiseData,fluorescenceData,waterfallData,extraData,srcIndexCache}}}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};
package/package.json CHANGED
@@ -1,11 +1,22 @@
1
1
  {
2
2
  "name": "@rfkit/spectrum-analyzer",
3
3
  "description": "A high-performance spectrum analyzer library for RF signal processing, supporting real-time spectrum analysis, waterfall display, and multi-segment frequency scanning",
4
- "module": "index.js",
5
- "types": "index.d.ts",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./index.d.ts",
8
+ "import": "./index.js"
9
+ },
10
+ "./wasm": {
11
+ "types": "./wasm/index.d.ts",
12
+ "import": "./wasm/index.js"
13
+ }
14
+ },
15
+ "module": "./index.js",
16
+ "types": "./index.d.ts",
6
17
  "author": "Hxgh",
7
18
  "license": "MIT",
8
- "version": "0.1.42",
19
+ "version": "0.1.43",
9
20
  "private": false,
10
21
  "keywords": [
11
22
  "spectrum-analyzer",
@@ -0,0 +1,13 @@
1
+ export declare const LEVEL_STREAM: {
2
+ readonly DEFAULT_CACHE_TIME: number;
3
+ readonly DEFAULT_GRANULARITY: 10;
4
+ readonly DEFAULT_RANGE: readonly [-20, 100];
5
+ };
6
+ export declare const DEFAULT_LEVEL_STREAM_CONFIG: {
7
+ cacheTime: number;
8
+ granularity: 10;
9
+ range: readonly [-20, 100];
10
+ maxPoints: undefined;
11
+ onLevelStreamUpdate: (data: Map<number, number>) => void;
12
+ };
13
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/core/LevelStreamAnalyzer/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY;;;;CAIf,CAAC;AAEX,eAAO,MAAM,2BAA2B;;;;;gCAKV,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;CAIhD,CAAC"}
@@ -0,0 +1,56 @@
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
+ export default class LevelStreamAnalyzer {
10
+ protected config: Required<Omit<LevelStreamConfig, 'maxPoints'>> & {
11
+ maxPoints?: number;
12
+ };
13
+ private spectrumData;
14
+ private probabilityData;
15
+ private count;
16
+ constructor(config: LevelStreamConfig);
17
+ /**
18
+ * 重置分析器的状态,清空频谱数据和概率数据,但保留配置信息。
19
+ */
20
+ reset(): void;
21
+ setConfig(config: Partial<LevelStreamConfig>): void;
22
+ /**
23
+ * 处理一个新的电平值,更新频谱数据并重新计算概率。
24
+ * @param level 新的电平值
25
+ * @param timestamp 时间戳
26
+ */
27
+ process(level: number, timestamp: number): void;
28
+ /**
29
+ * 更新新数据对应的区间概率
30
+ * @param level 新的电平值
31
+ */
32
+ private updateProbability;
33
+ /**
34
+ * 查询所有统计的概率数据。
35
+ * @returns 概率数据的 Map 对象
36
+ */
37
+ getAll(): Map<number, number>;
38
+ /**
39
+ * 移除过期的频谱数据。
40
+ * 支持两种模式:
41
+ * 1. 点模式(maxPoints):保留最新的 N 个点
42
+ * 2. 时间模式(cacheTime):保留指定时间范围内的数据
43
+ */
44
+ private removeExpiredData;
45
+ /**
46
+ * 添加新的电平值到频谱数据中。
47
+ * @param level 新的电平值
48
+ * @param timestamp 时间戳
49
+ */
50
+ private addNewData;
51
+ /**
52
+ * 输出符合统计范围的概率数据。
53
+ */
54
+ private outputData;
55
+ }
56
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/LevelStreamAnalyzer/index.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAEtC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,GAAG;QACjE,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,OAAO,CAAC,YAAY,CAA8C;IAElE,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,CAAC,KAAK,CAAK;gBAEN,MAAM,EAAE,iBAAiB;IAQrC;;OAEG;IACI,KAAK,IAAI,IAAI;IAMb,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAqB1D;;;;OAIG;IACI,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAOtD;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACI,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAIpC;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAwCzB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACH,OAAO,CAAC,UAAU;CA6BnB"}
@@ -0,0 +1,13 @@
1
+ export interface LevelStreamConfig {
2
+ cacheTime: number;
3
+ granularity: number;
4
+ range: [number, number];
5
+ maxPoints?: number;
6
+ onLevelStreamUpdate: (data: LevelStreamOutputData) => void;
7
+ }
8
+ export interface LevelStreamOutputData {
9
+ probabilityRangeData: Float32Array;
10
+ spectrumData: Float32Array;
11
+ timestampData: Float64Array;
12
+ }
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/LevelStreamAnalyzer/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC5D;AAED,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,EAAE,YAAY,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;IAE3B,aAAa,EAAE,YAAY,CAAC;CAC7B"}
@@ -0,0 +1,10 @@
1
+ import { GraphicType, OrientationType } from './types';
2
+ export declare const DEFAULT_SERIES_CONFIG: {
3
+ readonly thickness: 1;
4
+ readonly orientation: OrientationType.Horizontal;
5
+ readonly display: true;
6
+ readonly type: GraphicType.Line;
7
+ readonly color: "#00000000";
8
+ readonly label: "";
9
+ };
10
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/core/SeriesManager/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEvD,eAAO,MAAM,qBAAqB;;;;;;;CAOxB,CAAC"}