@rfkit/spectrum-analyzer 0.1.16 → 0.1.19

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.
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/core/LevelStreamAnalyzer/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY;;;;CAIf,CAAC;AAGX,eAAO,MAAM,2BAA2B;;;;gCAIV,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;CAIhD,CAAA"}
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;;;;gCAIV,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;CAIhD,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { LevelStreamConfig } from "./types";
1
+ import type { LevelStreamConfig } from './types';
2
2
  /**
3
3
  * 用于统计电平流数据的分析器。
4
4
  * 支持设置缓存时间、颗粒度和统计范围,并提供数据处理和概率计算功能。
@@ -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;IAEpC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAC;IAE9C,OAAO,CAAC,YAAY,CAA8C;IAElE,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,CAAC,KAAK,CAAK;gBAEN,MAAM,EAAE,iBAAiB;IAQrC;;OAEG;IACI,KAAK,IAAI,IAAI;IAOb,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAgB1D;;;OAGG;IACI,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOnC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACI,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAIpC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA0BzB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACH,OAAO,CAAC,UAAU;CAsBrB"}
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,iBAAiB,CAAC,CAAC;IAE9C,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;IAmB1D;;;OAGG;IACI,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOnC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACI,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAIpC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA0BzB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACH,OAAO,CAAC,UAAU;CA0BnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/LevelStreamAnalyzer/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxB,mBAAmB,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,qBAAqB;IAClC,oBAAoB,EAAE,YAAY,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;CAC9B"}
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,mBAAmB,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC5D;AAED,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,EAAE,YAAY,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;CAC5B"}
@@ -1,4 +1,4 @@
1
- import type { isValidFloat32Array, ProcessInput, ScanSegment, SpectrumConfig, SpectrumData, SpectrumOutputData, TimestampedFloat32Array } from './types';
1
+ import type { isValidFloat32Array, ProcessInput, ScanSegment, SpectrumConfig, SpectrumData, SpectrumOutputData, TemplateOverData, TimestampedFloat32Array } from './types';
2
2
  export default class SpectrumAnalyzer {
3
3
  protected config: Required<Omit<SpectrumConfig, 'segments' | 'bandwidthConfig' | 'onSpectrumUpdate'>>;
4
4
  private segments;
@@ -8,6 +8,7 @@ export default class SpectrumAnalyzer {
8
8
  protected maxData: isValidFloat32Array;
9
9
  protected minData: isValidFloat32Array;
10
10
  protected avgData: isValidFloat32Array;
11
+ protected templateData: Float32Array;
11
12
  protected waterfallData: TimestampedFloat32Array[];
12
13
  protected srcIndexCache: Uint32Array;
13
14
  protected realOutputData: Float32Array;
@@ -16,13 +17,14 @@ export default class SpectrumAnalyzer {
16
17
  protected lastIndex: number;
17
18
  protected processTimes: number;
18
19
  protected lastProcessTime: number;
20
+ protected cachedExceedingDatas: TemplateOverData[];
19
21
  private readonly onSpectrumUpdate?;
20
22
  constructor(config: Partial<SpectrumConfig>);
21
23
  /**
22
24
  * 处理输入数据,包括验证、拼接、统计、采样等操作。
23
25
  * @param {ProcessInput} input - 输入数据,包含数据、时间戳、分段偏移和偏移量。
24
26
  */
25
- process({ data, timestamp, segmentOffset, offset, }: ProcessInput): void;
27
+ process({ data, timestamp, segmentOffset, offset }: ProcessInput): void;
26
28
  /**
27
29
  * 初始化扫描分段信息,并更新最大点数。
28
30
  * @param {ScanSegment[]} segments - 扫描分段信息数组。
@@ -74,6 +76,18 @@ export default class SpectrumAnalyzer {
74
76
  * @returns {Uint32Array} - 输出值对应的原始数据索引数组。
75
77
  */
76
78
  updateSamplingRange(start: number, end: number): Uint32Array;
79
+ /**
80
+ * 设置模板数据
81
+ * @param {Float32Array | number[]} data - 模板数据
82
+ */
83
+ setTemplateData(data?: Float32Array | number[]): void;
84
+ /**
85
+ * 统一的超出数据段计算和缓存更新方法
86
+ * @param forceFullCalculation - 是否强制进行全量计算
87
+ * @param incrementalRange - 增量计算的范围
88
+ * @returns 计算得到的超出数据段
89
+ */
90
+ private updateTemplateOverData;
77
91
  /**
78
92
  * 对数据进行重采样,生成频谱输出数据。
79
93
  * @returns {Readonly<SpectrumOutputData>} - 包含重采样后的实时数据、最大值、最小值和平均值的对象。
@@ -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,4 +1,4 @@
1
- import type { TimestampedFloat32Array } from './types';
1
+ import type { TemplateOverData, TimestampedFloat32Array } from './types';
2
2
  export declare const arrayKeepAttribute: (source: Float32Array & {
3
3
  max?: number;
4
4
  maxIndex?: number;
@@ -10,7 +10,7 @@ export declare const arrayKeepAttribute: (source: Float32Array & {
10
10
  timestamp?: number;
11
11
  progress?: number;
12
12
  }) => void;
13
- export declare const resample: ({ realData, antennaFactorData, antennaFactorSwitch, outputPoints, }: {
13
+ export declare const resample: ({ realData, antennaFactorData, antennaFactorSwitch, outputPoints }: {
14
14
  realData: TimestampedFloat32Array;
15
15
  antennaFactorData: Float32Array;
16
16
  antennaFactorSwitch?: boolean;
@@ -19,20 +19,36 @@ export declare const resample: ({ realData, antennaFactorData, antennaFactorSwit
19
19
  realOutputData: TimestampedFloat32Array;
20
20
  srcIndexCache: Uint32Array;
21
21
  };
22
- export declare const resampleMultiple: ({ antennaFactorData, antennaFactorSwitch, outputPoints, realData, maxData, minData, avgData, }: {
22
+ export declare const resampleMultiple: ({ antennaFactorData, antennaFactorSwitch, outputPoints, realData, maxData, minData, avgData, templateData }: {
23
23
  antennaFactorData: Float32Array;
24
24
  antennaFactorSwitch?: boolean;
25
25
  outputPoints: number;
26
26
  realData: TimestampedFloat32Array;
27
- enableMetrics?: boolean;
28
27
  maxData?: Float32Array;
29
28
  minData?: Float32Array;
30
29
  avgData?: Float32Array;
30
+ templateData?: Float32Array;
31
31
  }) => {
32
32
  realOutputData: TimestampedFloat32Array;
33
33
  maxOutputData: Float32Array;
34
34
  minOutputData: Float32Array;
35
35
  avgOutputData: Float32Array;
36
+ templateOutputData: Float32Array;
36
37
  srcIndexCache: Uint32Array;
37
38
  };
39
+ export declare const findExceedingDatas: ({ realData, maxData, minData, templateData }: {
40
+ realData: TimestampedFloat32Array;
41
+ maxData: Float32Array;
42
+ minData: Float32Array;
43
+ templateData: Float32Array;
44
+ }) => TemplateOverData[];
45
+ export declare const findExceedingDatasIncremental: ({ realData, maxData, minData, templateData, startIndex, endIndex, previousSegments }: {
46
+ realData: TimestampedFloat32Array;
47
+ maxData: Float32Array;
48
+ minData: Float32Array;
49
+ templateData: Float32Array;
50
+ startIndex: number;
51
+ endIndex: number;
52
+ previousSegments?: TemplateOverData[];
53
+ }) => TemplateOverData[];
38
54
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAGvD,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,YAAY,GAAG;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,EACD,QAAQ,YAAY,GAAG;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,SAMF,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,qEAKtB;IACD,QAAQ,EAAE,uBAAuB,CAAC;IAClC,iBAAiB,EAAE,YAAY,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;CACtB,KAAG;IAAE,cAAc,EAAE,uBAAuB,CAAC;IAAC,aAAa,EAAE,WAAW,CAAA;CA6DxE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,gGAS9B;IACD,iBAAiB,EAAE,YAAY,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,KAAG;IACF,cAAc,EAAE,uBAAuB,CAAC;IACxC,aAAa,EAAE,YAAY,CAAC;IAC5B,aAAa,EAAE,YAAY,CAAC;IAC5B,aAAa,EAAE,YAAY,CAAC;IAC5B,aAAa,EAAE,WAAW,CAAC;CA0I5B,CAAC"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAGzE,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,YAAY,GAAG;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,EACD,QAAQ,YAAY,GAAG;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,SAMF,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,oEAKtB;IACD,QAAQ,EAAE,uBAAuB,CAAC;IAClC,iBAAiB,EAAE,YAAY,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;CACtB,KAAG;IAAE,cAAc,EAAE,uBAAuB,CAAC;IAAC,aAAa,EAAE,WAAW,CAAA;CA6DxE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,6GAS9B;IACD,iBAAiB,EAAE,YAAY,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,KAAG;IACF,cAAc,EAAE,uBAAuB,CAAC;IACxC,aAAa,EAAE,YAAY,CAAC;IAC5B,aAAa,EAAE,YAAY,CAAC;IAC5B,aAAa,EAAE,YAAY,CAAC;IAC5B,kBAAkB,EAAE,YAAY,CAAC;IACjC,aAAa,EAAE,WAAW,CAAC;CAgI5B,CAAC;AA+IF,eAAO,MAAM,kBAAkB,GAAI,8CAKhC;IACD,QAAQ,EAAE,uBAAuB,CAAC;IAClC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,YAAY,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;CAC5B,KAAG,gBAAgB,EAanB,CAAC;AAGF,eAAO,MAAM,6BAA6B,GAAI,sFAQ3C;IACD,QAAQ,EAAE,uBAAuB,CAAC;IAClC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,YAAY,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACvC,KAAG,gBAAgB,EAgBnB,CAAC"}
@@ -49,11 +49,21 @@ export interface SpectrumData {
49
49
  maxData: Float32Array;
50
50
  minData: Float32Array;
51
51
  avgData: Float32Array;
52
+ templateData: Float32Array;
52
53
  waterfallData?: TimestampedFloat32Array[];
53
54
  }
54
55
  export interface SpectrumOutputData extends SpectrumData {
55
56
  srcIndexCache: Uint32Array;
56
57
  scanProgress?: number;
57
58
  processTimes?: number;
59
+ templateOverData?: TemplateOverData[];
60
+ }
61
+ export interface TemplateOverData {
62
+ maxValue: number;
63
+ minValue: number;
64
+ avgValue: number;
65
+ timestamp: number;
66
+ startIndex: number;
67
+ endIndex: number;
58
68
  }
59
69
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC9B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAC;CAChE;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AACxE,MAAM,MAAM,uBAAuB,GAAG,mBAAmB,GAAG;IAC1D,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,uBAAuB,CAAC;IAClC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,YAAY,CAAC;IACtB,aAAa,CAAC,EAAE,uBAAuB,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,aAAa,EAAE,WAAW,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC9B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAC;CAChE;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AACxE,MAAM,MAAM,uBAAuB,GAAG,mBAAmB,GAAG;IAC1D,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,uBAAuB,CAAC;IAClC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,YAAY,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,CAAC,EAAE,uBAAuB,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,aAAa,EAAE,WAAW,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACvC;AAGD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB"}
package/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import SpectrumAnalyzer from './core/SpectrumAnalyzer';
2
1
  import LevelStreamAnalyzer from './core/LevelStreamAnalyzer';
3
- export type { ProcessInput, ProcessingConfig, SpectrumConfig, FrequencySegment, BandwidthConfig, TimestampedFloat32Array, SpectrumOutputData, SpectrumData, } from './core/SpectrumAnalyzer/types';
4
- export type { LevelStreamConfig, LevelStreamOutputData, } from './core/LevelStreamAnalyzer/types';
5
- export { SpectrumError, DataValidationError, IndexOutOfBoundsError, } from './core/SpectrumAnalyzer/errors';
6
- export { SPECTRUM, ERROR_MESSAGES } from './core/SpectrumAnalyzer/constants';
2
+ import SpectrumAnalyzer from './core/SpectrumAnalyzer';
3
+ export type { LevelStreamConfig, LevelStreamOutputData } from './core/LevelStreamAnalyzer/types';
4
+ export { ERROR_MESSAGES, SPECTRUM } from './core/SpectrumAnalyzer/constants';
5
+ export { DataValidationError, IndexOutOfBoundsError, SpectrumError } from './core/SpectrumAnalyzer/errors';
6
+ export type { BandwidthConfig, FrequencySegment, ProcessInput, ProcessingConfig, SpectrumConfig, SpectrumData, SpectrumOutputData, TimestampedFloat32Array } from './core/SpectrumAnalyzer/types';
7
7
  export { SpectrumAnalyzer, LevelStreamAnalyzer };
8
8
  //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,yBAAyB,CAAC;AACvD,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAG7D,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,YAAY,GACb,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EACV,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAG7E,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAC7D,OAAO,gBAAgB,MAAM,yBAAyB,CAAC;AAEvD,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACtB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAG7E,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACd,MAAM,gCAAgC,CAAC;AAExC,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC"}
package/index.js CHANGED
@@ -1 +1 @@
1
- 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})=>{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 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 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}else{realOutputData.set(realData);if(hasMaxData)maxOutputData.set(maxData);if(hasMinData)minOutputData.set(minData);if(hasAvgData)avgOutputData.set(avgData)}}else{const ratio=realDataLength/outputPoints;let pos=ratio/2;const findMaxInRange=(data,start,end)=>{let maxVal=data[start];let maxIdx=start;for(let j=start+1;j<end;j++)if(data[j]>maxVal){maxVal=data[j];maxIdx=j}return{maxVal,maxIdx}};const findMinInRange=(data,start,end)=>{let minVal=data[start];let minIdx=start;for(let j=start+1;j<end;j++)if(data[j]<minVal){minVal=data[j];minIdx=j}return{minVal,minIdx}};for(let i=0;i<outputLength;i++){const start=Math.floor(pos);const end=Math.min(Math.floor(pos+ratio),realDataLength);const{maxVal:maxValue,maxIdx:maxIndex}=findMaxInRange(realData,start,end);realOutputData[i]=antennaFactorSwitch?maxValue+antennaFactorData[maxIndex]:maxValue;srcIndexCache[i]=maxIndex;if(hasMaxData){const{maxVal:maxDataValue,maxIdx:maxDataIndex}=findMaxInRange(maxData,start,end);maxOutputData[i]=antennaFactorSwitch?maxDataValue+antennaFactorData[maxDataIndex]:maxDataValue}if(hasMinData){const{minVal:minValue,minIdx:minIndex}=findMinInRange(minData,start,end);minOutputData[i]=antennaFactorSwitch?minValue+antennaFactorData[minIndex]:minValue}if(hasAvgData){const avgValue=avgData[maxIndex];avgOutputData[i]=antennaFactorSwitch?avgValue+antennaFactorData[maxIndex]:avgValue}pos+=ratio}}return{realOutputData,maxOutputData,minOutputData,srcIndexCache,avgOutputData}};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},outputPoints:SPECTRUM.OUTPUT_POINTS,outputRange:{start:0,end:SPECTRUM.OUTPUT_POINTS}};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 SpectrumAnalyzer{config=DEFAULT_SPECTRUM_CONFIG;segments=[];antennaFactorData;antennaFactorSwitch;realData;maxData;minData;avgData;waterfallData;srcIndexCache;realOutputData;waterfallOutputData;scanProgress;lastIndex;processTimes;lastProcessTime;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;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)this.updateWaterfallData(this.realData,processedData.realData);this.notifySpectrumUpdate({...processedData,waterfallData:this.waterfallOutputData,scanProgress:this.scanProgress,processTimes:this.processTimes})}catch(error){throw error instanceof Error?error:new Error(String(error))}finally{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 pointCount=Math.floor((stopFrequency-startFrequency)*1e3/stepFrequency)+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=this.isAllValid(this.maxData);const processedData=this.resampleDataSeries();this.notifySpectrumUpdate(processedData)}reset(){const{maxPoints,waterfallMaxFrames,processing:{enableMetrics}}=this.config;this.antennaFactorData=new Float32Array(maxPoints);const realData=new Float32Array(maxPoints);realData.timestamp=0;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.waterfallData=Array.from({length:waterfallMaxFrames},()=>{const frame=new Float32Array;frame.timestamp=0;frame.fill(SPECTRUM.INITIAL_VALUE);return frame});this.waterfallOutputData=Array.from({length:waterfallMaxFrames},()=>{const frame=new Float32Array;frame.timestamp=0;frame.fill(SPECTRUM.INITIAL_VALUE);return frame});this.srcIndexCache=new Uint32Array(maxPoints);this.scanProgress=0;this.lastIndex=0;this.processTimes=0;this.lastProcessTime=0;this.notifySpectrumUpdate({realData:this.realData,maxData:this.maxData,minData:this.minData,avgData:this.avgData,srcIndexCache:this.srcIndexCache,processTimes:0,scanProgress:0})}getData(){const{realData,maxData,minData,avgData,srcIndexCache,waterfallData,processTimes}=this;return{realData,maxData,minData,avgData,srcIndexCache,waterfallData,processTimes}}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.notifySpectrumUpdate({...processedData,waterfallData:this.waterfallOutputData});return this.srcIndexCache}resampleDataSeries(){const{antennaFactorData,antennaFactorSwitch,maxData,minData,avgData,config:{maxPoints,outputPoints,processing:{enableMetrics}}}=this;const activeAntennaFactorData=antennaFactorSwitch?antennaFactorData:new Float32Array(maxPoints);const{realOutputData,srcIndexCache,maxOutputData,minOutputData,avgOutputData}=resampleMultiple({antennaFactorData:activeAntennaFactorData,antennaFactorSwitch,outputPoints,realData:this.getOutputData(),enableMetrics,maxData:this.getOutputData(maxData),minData:this.getOutputData(minData),avgData:this.getOutputData(avgData)});this.srcIndexCache=srcIndexCache;return{realData:realOutputData,maxData:maxOutputData,minData:minOutputData,avgData:avgOutputData,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()}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=this.isAllValid(maxData);if(!minData.allValid)minData.allValid=this.isAllValid(minData);if(!avgData.allValid)avgData.allValid=this.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)}}isAllValid(arr){for(let i=0;i<arr.length;i++)if(!Number.isFinite(arr[i]))return false;return true}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}getAllRawData(){const{realData,maxData,minData,avgData,waterfallData}=this;return{realData,maxData,minData,avgData,waterfallData}}}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,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)this.reset();if(config.range)this.outputData()}process(level){this.removeExpiredData();this.addNewData(level);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 now=Date.now();const{cacheTime}=this.config;if(cacheTime<=0){this.spectrumData=[];return}if(0===this.spectrumData.length)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){const now=Date.now();this.spectrumData.push({value:level,timestamp:now})}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))})}}export{DataValidationError,ERROR_MESSAGES,IndexOutOfBoundsError,LevelStreamAnalyzer,SPECTRUM,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,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)this.reset();if(config.range)this.outputData()}process(level){this.removeExpiredData();this.addNewData(level);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 now=Date.now();const{cacheTime}=this.config;if(cacheTime<=0){this.spectrumData=[];return}if(0===this.spectrumData.length)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){const now=Date.now();this.spectrumData.push({value:level,timestamp:now})}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))})}}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},outputPoints:SPECTRUM.OUTPUT_POINTS,outputRange:{start:0,end:SPECTRUM.OUTPUT_POINTS}};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})=>{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 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 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}else{realOutputData.set(realData);if(hasMaxData)maxOutputData.set(maxData);if(hasMinData)minOutputData.set(minData);if(hasAvgData)avgOutputData.set(avgData);if(hasTemplateData)templateOutputData.set(templateData)}}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)}pos+=ratio}}return{realOutputData,maxOutputData,minOutputData,avgOutputData,templateOutputData,srcIndexCache}};const findExceedingDatasCore=({realData,maxData,minData,templateData,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]?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];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})=>{if(!templateData||0===templateData.length)return[];return findExceedingDatasCore({realData,maxData,minData,templateData,usePreallocation:true})};const findExceedingDatasIncremental=({realData,maxData,minData,templateData,startIndex,endIndex,previousSegments=[]})=>{if(!templateData||0===templateData.length||startIndex>=endIndex)return previousSegments;const newSegments=findExceedingDatasCore({realData,maxData,minData,templateData,startIndex,endIndex});return[...previousSegments,...newSegments]};class SpectrumAnalyzer{config=DEFAULT_SPECTRUM_CONFIG;segments=[];antennaFactorData;antennaFactorSwitch;realData;maxData;minData;avgData;templateData;waterfallData;srcIndexCache;realOutputData;waterfallOutputData;scanProgress;lastIndex;processTimes;lastProcessTime;cachedExceedingDatas=[];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;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)this.updateWaterfallData(this.realData,processedData.realData);const templateOverData=this.updateTemplateOverData(isOver,isOver?void 0:{startIndex:index,endIndex:index+data.length});this.notifySpectrumUpdate({...processedData,waterfallData:this.waterfallOutputData,scanProgress:this.scanProgress,processTimes:this.processTimes,...templateOverData&&{templateOverData}})}catch(error){throw error instanceof Error?error:new Error(String(error))}finally{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 pointCount=Math.floor((stopFrequency-startFrequency)*1e3/stepFrequency)+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=this.isAllValid(this.maxData);const processedData=this.resampleDataSeries();this.notifySpectrumUpdate(processedData)}reset(){const{maxPoints,waterfallMaxFrames,processing:{enableMetrics}}=this.config;this.antennaFactorData=new Float32Array(maxPoints);const realData=new Float32Array(maxPoints);realData.timestamp=0;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.waterfallData=Array.from({length:waterfallMaxFrames},()=>{const frame=new Float32Array;frame.timestamp=0;frame.fill(SPECTRUM.INITIAL_VALUE);return frame});this.waterfallOutputData=Array.from({length:waterfallMaxFrames},()=>{const frame=new Float32Array;frame.timestamp=0;frame.fill(SPECTRUM.INITIAL_VALUE);return frame});this.srcIndexCache=new Uint32Array(maxPoints);this.scanProgress=0;this.lastIndex=0;this.processTimes=0;this.lastProcessTime=0;this.cachedExceedingDatas=[];this.notifySpectrumUpdate({realData:this.realData,maxData:this.maxData,minData:this.minData,avgData:this.avgData,templateData:this.templateData,srcIndexCache:this.srcIndexCache,processTimes:0,scanProgress:0})}getData(){const{realData,maxData,minData,avgData,templateData,srcIndexCache,waterfallData,processTimes}=this;return{realData,maxData,minData,avgData,templateData,srcIndexCache,waterfallData,processTimes}}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.notifySpectrumUpdate({...processedData,waterfallData:this.waterfallOutputData});return this.srcIndexCache}setTemplateData(data){if(!data||0===data.length){this.templateData=new Float32Array(0);return}this.templateData=new Float32Array(data);const{srcIndexCache,config:{outputPoints}}=this;const templateOutputData=new Float32Array(outputPoints);for(let i=0;i<outputPoints;i++)templateOutputData[i]=this.templateData[srcIndexCache[i]];const templateOverData=this.updateTemplateOverData(true);this.notifySpectrumUpdate({templateData:templateOutputData,templateOverData})}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});this.cachedExceedingDatas=templateOverData}else{this.cachedExceedingDatas=findExceedingDatasIncremental({realData:this.realData,maxData:this.maxData,minData:this.minData,templateData:this.templateData,startIndex:incrementalRange.startIndex,endIndex:incrementalRange.endIndex,previousSegments:this.cachedExceedingDatas});templateOverData=this.cachedExceedingDatas}return templateOverData}resampleDataSeries(){const{antennaFactorData,antennaFactorSwitch,maxData,minData,avgData,templateData,config:{maxPoints,outputPoints}}=this;const activeAntennaFactorData=antennaFactorSwitch?antennaFactorData:new Float32Array(maxPoints);const{realOutputData,srcIndexCache,maxOutputData,minOutputData,avgOutputData,templateOutputData}=resampleMultiple({antennaFactorData:activeAntennaFactorData,antennaFactorSwitch,outputPoints,realData:this.getOutputData(),maxData:this.getOutputData(maxData),minData:this.getOutputData(minData),avgData:this.getOutputData(avgData),templateData:this.getOutputData(templateData)});this.srcIndexCache=srcIndexCache;return{realData:realOutputData,maxData:maxOutputData,minData:minOutputData,avgData:avgOutputData,templateData:templateOutputData,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()}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=this.isAllValid(maxData);if(!minData.allValid)minData.allValid=this.isAllValid(minData);if(!avgData.allValid)avgData.allValid=this.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)}}isAllValid(arr){for(let i=0;i<arr.length;i++)if(!Number.isFinite(arr[i]))return false;return true}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}getAllRawData(){const{realData,maxData,minData,avgData,templateData,waterfallData}=this;return{realData,maxData,minData,avgData,templateData,waterfallData}}}export{DataValidationError,ERROR_MESSAGES,IndexOutOfBoundsError,LevelStreamAnalyzer,SPECTRUM,SpectrumAnalyzer,SpectrumError};
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "types": "index.d.ts",
6
6
  "author": "Hxgh",
7
7
  "license": "MIT",
8
- "version": "0.1.16",
8
+ "version": "0.1.19",
9
9
  "private": false,
10
10
  "keywords": [
11
11
  "spectrum-analyzer",
@@ -18,4 +18,4 @@
18
18
  "antenna-factor",
19
19
  "frequency-scanning"
20
20
  ]
21
- }
21
+ }