@rfkit/spectrum-analyzer 0.1.0 → 0.1.2

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.
@@ -0,0 +1,12 @@
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
+ onLevelStreamUpdate: (data: Map<number, number>) => void;
11
+ };
12
+ //# 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;AAGX,eAAO,MAAM,2BAA2B;;;;gCAIV,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;CAIhD,CAAA"}
@@ -0,0 +1,46 @@
1
+ import type { LevelStreamConfig } from "./types";
2
+ /**
3
+ * 用于统计电平流数据的分析器。
4
+ * 支持设置缓存时间、颗粒度和统计范围,并提供数据处理和概率计算功能。
5
+ */
6
+ export default class LevelStreamAnalyzer {
7
+ protected config: Required<LevelStreamConfig>;
8
+ private spectrumData;
9
+ private probabilityData;
10
+ private count;
11
+ constructor(config: LevelStreamConfig);
12
+ /**
13
+ * 重置分析器的状态,清空频谱数据和概率数据,但保留配置信息。
14
+ */
15
+ reset(): void;
16
+ setConfig(config: Partial<LevelStreamConfig>): void;
17
+ /**
18
+ * 处理一个新的电平值,更新频谱数据并重新计算概率。
19
+ * @param level 新的电平值
20
+ */
21
+ process(level: number): void;
22
+ /**
23
+ * 更新新数据对应的区间概率
24
+ * @param level 新的电平值
25
+ */
26
+ private updateProbability;
27
+ /**
28
+ * 查询所有统计的概率数据。
29
+ * @returns 概率数据的 Map 对象
30
+ */
31
+ getAll(): Map<number, number>;
32
+ /**
33
+ * 移除过期的频谱数据。
34
+ */
35
+ private removeExpiredData;
36
+ /**
37
+ * 添加新的电平值到频谱数据中。
38
+ * @param level 新的电平值
39
+ */
40
+ private addNewData;
41
+ /**
42
+ * 输出符合统计范围的概率数据。
43
+ */
44
+ private outputData;
45
+ }
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,11 @@
1
+ export interface LevelStreamConfig {
2
+ cacheTime: number;
3
+ granularity: number;
4
+ range: [number, number];
5
+ onLevelStreamUpdate: (data: LevelStreamOutputData) => void;
6
+ }
7
+ export interface LevelStreamOutputData {
8
+ probabilityRangeData: Float32Array;
9
+ spectrumData: Float32Array;
10
+ }
11
+ //# 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;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,12 +1,12 @@
1
1
  export declare const SPECTRUM: {
2
- readonly INITIAL_VALUE: 0;
2
+ readonly INITIAL_VALUE: number;
3
3
  readonly WATERFALL_MAX_FRAMES: 100;
4
4
  readonly OUTPUT_POINTS: 1001;
5
5
  };
6
6
  export declare const DEFAULT_SPECTRUM_CONFIG: {
7
7
  readonly maxPoints: 1001;
8
8
  readonly waterfallMaxFrames: 100;
9
- readonly initialValue: 0;
9
+ readonly initialValue: number;
10
10
  readonly processing: {
11
11
  readonly enableWaterfall: false;
12
12
  readonly enableMetrics: false;
@@ -23,7 +23,8 @@ export declare const ERROR_MESSAGES: {
23
23
  readonly EMPTY_BANDWIDTH: "bandwidthConfig 不能为空";
24
24
  readonly INVALID_SEGMENT: (index: number) => string;
25
25
  readonly INDEX_OUT_OF_BOUNDS: (index: number) => string;
26
- readonly INVALID_ANTENNA_FACTOR: (points: number) => string;
26
+ readonly INVALID_ANTENNA_FACTOR_LENGTH: (points: number) => string;
27
+ readonly INVALID_ANTENNA_FACTOR: "天线因子数据必须是有效的正数";
27
28
  readonly INVALID_SAMPLING_RANGE: "采样范围无效";
28
29
  readonly INVALID_MAX_POINTS: "点数必须大于0";
29
30
  readonly INVALID_LENGTH: (expected: number) => string;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ;;;;CAIX,CAAC;AAEX,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;CAa1B,CAAC;AAEX,eAAO,MAAM,cAAc;;;;sCAIA,MAAM;0CACF,MAAM;qDACK,MAAM;;;;wCAKnB,MAAM;CAEzB,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;aAGpB,IAAI,EAAE,MAAM;aACZ,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,YAAA;CAKpC;AAED,qBAAa,mBAAoB,SAAQ,aAAa;gBACxC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;CAG/C;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,aAAa;aAGpC,KAAK,EAAE,MAAM;gBAD7B,OAAO,EAAE,MAAM,EACC,KAAK,EAAE,MAAM,EAC7B,OAAO,CAAC,EAAE,OAAO;CAOpB"}
@@ -0,0 +1,113 @@
1
+ import type { isValidFloat32Array, ProcessInput, ScanSegment, SpectrumConfig, SpectrumOutputData, TimestampedFloat32Array } from './types';
2
+ export default class SpectrumAnalyzer {
3
+ protected config: Required<Omit<SpectrumConfig, 'segments' | 'bandwidthConfig' | 'onSpectrumUpdate'>>;
4
+ private segments;
5
+ protected antennaFactorData: Float32Array;
6
+ protected antennaFactorSwitch: boolean;
7
+ protected realData: TimestampedFloat32Array;
8
+ protected maxData: isValidFloat32Array;
9
+ protected minData: isValidFloat32Array;
10
+ protected avgData: isValidFloat32Array;
11
+ protected waterfallData: TimestampedFloat32Array[];
12
+ protected srcIndexCache: Uint32Array;
13
+ protected realOutputData: Float32Array;
14
+ protected waterfallOutputData: TimestampedFloat32Array[];
15
+ protected scanProgress: number;
16
+ protected lastIndex: number;
17
+ protected processTimes: number;
18
+ protected lastProcessTime: number;
19
+ private readonly onSpectrumUpdate?;
20
+ constructor(config: Partial<SpectrumConfig>);
21
+ /**
22
+ * 处理输入数据,包括验证、拼接、统计、采样等操作。
23
+ * @param {ProcessInput} input - 输入数据,包含数据、时间戳、分段偏移和偏移量。
24
+ */
25
+ process({ data, timestamp, segmentOffset, offset, }: ProcessInput): void;
26
+ /**
27
+ * 初始化扫描分段信息,并更新最大点数。
28
+ * @param {ScanSegment[]} segments - 扫描分段信息数组。
29
+ */
30
+ initializeSegments(segments: ScanSegment[]): void;
31
+ /**
32
+ * 设置天线因子数据,并根据开关状态重新输出数据。
33
+ * @param {Float32Array} data - 天线因子数据,默认为根据最大点数创建的数组。
34
+ */
35
+ setAntennaFactor(data: Float32Array | number[]): void;
36
+ /**
37
+ * 设置天线因子开关,并根据开关状态重新输出数据。
38
+ * @param {boolean} newAntennaFactorSwitch - 新的天线因子开关状态。
39
+ */
40
+ setAntennaFactorSwitch(newAntennaFactorSwitch: boolean): void;
41
+ /**
42
+ * 重置频谱分析器的状态,包括数据和进度。
43
+ */
44
+ reset(): void;
45
+ /**
46
+ * 获取当前频谱分析器的缓存数据。
47
+ * @returns {Readonly<SpectrumOutputData>} - 包含实时数据、最大值、最小值、平均值、瀑布图数据和处理次数的对象。
48
+ */
49
+ getData(): Readonly<SpectrumOutputData>;
50
+ /**
51
+ * 获取频谱分析器的性能指标,包括处理时间、数据点数、瀑布图帧数、初始化状态和内存使用情况。
52
+ * @returns {Readonly<Record<string, number | boolean>>} - 包含性能指标的对象。
53
+ */
54
+ getPerformanceMetrics(): Readonly<Record<string, number | boolean>>;
55
+ /**
56
+ * 更新采样范围,并重新输出数据。
57
+ * @param {number} start - 采样范围的起始位置。
58
+ * @param {number} end - 采样范围的结束位置。
59
+ * @returns {Uint32Array} - 输出值对应的原始数据索引数组。
60
+ */
61
+ updateSamplingRange(start: number, end: number): Uint32Array;
62
+ /**
63
+ * 对数据进行重采样,生成频谱输出数据。
64
+ * @returns {Readonly<SpectrumOutputData>} - 包含重采样后的实时数据、最大值、最小值和平均值的对象。
65
+ */
66
+ protected resampleDataSeries(): Readonly<SpectrumOutputData>;
67
+ /**
68
+ * 对瀑布图输出数据进行重采样。
69
+ */
70
+ protected resampleWaterfallOutputData(): void;
71
+ /**
72
+ * 更新瀑布图数据,包括添加新数据和移除旧数据。
73
+ * @param {TimestampedFloat32Array} data - 输入的实时数据。
74
+ * @param {TimestampedFloat32Array} outputData - 输出的实时数据。
75
+ */
76
+ protected updateWaterfallData(data: TimestampedFloat32Array, outputData: TimestampedFloat32Array): void;
77
+ /**
78
+ * 更新最大点数,并重置。
79
+ * @param {number} maxPoints - 新的最大点数。
80
+ */
81
+ protected updateMaxPoints(maxPoints: number): void;
82
+ /**
83
+ * 验证输入数据的索引是否在有效范围内。
84
+ * @param {Float32Array} data - 输入数据。
85
+ * @param {number} index - 数据的起始索引。
86
+ */
87
+ protected validateInput(data: Float32Array, index: number): void;
88
+ /**
89
+ * 统计计算,处理数据点,更新统计值(最大值、最小值、平均值)。
90
+ * @param {Float32Array} data - 输入数据。
91
+ * @param {number} index - 数据的起始索引。
92
+ * @param {boolean} isOver - 数据是否处理完成。
93
+ */
94
+ private processDataPoints;
95
+ private isAllValid;
96
+ /**
97
+ * 通知频谱数据更新,调用回调函数。
98
+ * @param {SpectrumOutputData} processedData - 处理后的频谱数据。
99
+ */
100
+ private notifySpectrumUpdate;
101
+ /**
102
+ * 计算当前频谱分析器的内存使用情况。
103
+ * @returns {number} - 内存使用量(字节)。
104
+ */
105
+ private calculateMemoryUsage;
106
+ /**
107
+ * 获取输出数据,根据配置的输出范围截取数据。
108
+ * @param {TimestampedFloat32Array} [data] - 可选的输入数据,默认为实时数据。
109
+ * @returns {Readonly<TimestampedFloat32Array>} - 输出数据。
110
+ */
111
+ private getOutputData;
112
+ }
113
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/SpectrumAnalyzer/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,SAAS,CAAC;AAKjB,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC,SAAS,CAAC,MAAM,EAAE,QAAQ,CACxB,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,iBAAiB,GAAG,kBAAkB,CAAC,CAC1E,CAA2B;IAC5B,OAAO,CAAC,QAAQ,CAAqB;IAErC,SAAS,CAAC,iBAAiB,EAAG,YAAY,CAAC;IAC3C,SAAS,CAAC,mBAAmB,EAAG,OAAO,CAAC;IAExC,SAAS,CAAC,QAAQ,EAAG,uBAAuB,CAAC;IAC7C,SAAS,CAAC,OAAO,EAAG,mBAAmB,CAAC;IACxC,SAAS,CAAC,OAAO,EAAG,mBAAmB,CAAC;IACxC,SAAS,CAAC,OAAO,EAAG,mBAAmB,CAAC;IACxC,SAAS,CAAC,aAAa,EAAG,uBAAuB,EAAE,CAAC;IAEpD,SAAS,CAAC,aAAa,EAAG,WAAW,CAAC;IAEtC,SAAS,CAAC,cAAc,EAAG,YAAY,CAAC;IACxC,SAAS,CAAC,mBAAmB,EAAG,uBAAuB,EAAE,CAAC;IAC1D,SAAS,CAAC,YAAY,EAAG,MAAM,CAAC;IAEhC,SAAS,CAAC,SAAS,EAAG,MAAM,CAAC;IAC7B,SAAS,CAAC,YAAY,EAAG,MAAM,CAAC;IAChC,SAAS,CAAC,eAAe,EAAG,MAAM,CAAC;IAEnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAqC;gBAE3D,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC;IAqB3C;;;OAGG;IACI,OAAO,CAAC,EACb,IAAI,EACJ,SAAS,EACT,aAAiB,EACjB,MAAU,GACX,EAAE,YAAY,GAAG,IAAI;IAoDtB;;;OAGG;IACI,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI;IAwBxD;;;OAGG;IACI,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,GAAG,IAAI;IA+B5D;;;OAGG;IACI,sBAAsB,CAAC,sBAAsB,EAAE,OAAO,GAAG,IAAI;IAapE;;OAEG;IACI,KAAK,IAAI,IAAI;IAgDpB;;;OAGG;IACI,OAAO,IAAI,QAAQ,CAAC,kBAAkB,CAAC;IAa9C;;;OAGG;IACI,qBAAqB,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IAU1E;;;;;OAKG;IACI,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW;IAiBnE;;;OAGG;IACH,SAAS,CAAC,kBAAkB,IAAI,QAAQ,CAAC,kBAAkB,CAAC;IA0E5D;;OAEG;IACH,SAAS,CAAC,2BAA2B,IAAI,IAAI;IA+B7C;;;;OAIG;IACH,SAAS,CAAC,mBAAmB,CAC3B,IAAI,EAAE,uBAAuB,EAC7B,UAAU,EAAE,uBAAuB,GAClC,IAAI;IAsBP;;;OAGG;IACH,SAAS,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAalD;;;;OAIG;IACH,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAShE;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAkDzB,OAAO,CAAC,UAAU;IASlB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAW5B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAiB5B;;;;OAIG;IACH,OAAO,CAAC,aAAa;CAetB"}
@@ -10,7 +10,12 @@ export declare const arrayKeepAttribute: (source: Float32Array & {
10
10
  timestamp?: number;
11
11
  progress?: number;
12
12
  }) => void;
13
- export declare const resample: (data: TimestampedFloat32Array, antennaFactorData: Float32Array, interval: number) => {
13
+ export declare const resample: ({ realData, antennaFactorData, antennaFactorSwitch, outputPoints, }: {
14
+ realData: TimestampedFloat32Array;
15
+ antennaFactorData: Float32Array;
16
+ antennaFactorSwitch?: boolean;
17
+ outputPoints: number;
18
+ }) => {
14
19
  realOutputData: TimestampedFloat32Array;
15
20
  srcIndexCache: Uint32Array;
16
21
  };
@@ -0,0 +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;AAGF,eAAO,MAAM,QAAQ,GACnB,qEAKG;IACD,QAAQ,EAAE,uBAAuB,CAAC;IAClC,iBAAiB,EAAE,YAAY,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;CACtB,KACA;IAAE,cAAc,EAAE,uBAAuB,CAAC;IAAC,aAAa,EAAE,WAAW,CAAA;CA8DvE,CAAC"}
@@ -19,7 +19,6 @@ export interface ProcessingConfig {
19
19
  export interface SpectrumConfig {
20
20
  maxPoints?: number;
21
21
  waterfallMaxFrames?: number;
22
- initialValue?: number;
23
22
  processing?: Partial<ProcessingConfig>;
24
23
  outputPoints?: number;
25
24
  outputRange?: {
@@ -39,7 +38,10 @@ export interface BandwidthConfig {
39
38
  frequency: number;
40
39
  bandwidth: number;
41
40
  }
42
- export type TimestampedFloat32Array = Float32Array & {
41
+ export type isValidFloat32Array = Float32Array & {
42
+ allValid?: boolean;
43
+ };
44
+ export type TimestampedFloat32Array = isValidFloat32Array & {
43
45
  timestamp: number;
44
46
  };
45
47
  export interface SpectrumOutputData {
@@ -0,0 +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,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,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,kBAAkB,KAAK,IAAI,CAAC;CACvD;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;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAClF,MAAM,WAAW,kBAAkB;IACjC,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;IAE1C,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC,qBAAqB,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAElD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
package/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- import SpectrumAnalyzer from './core';
2
- export type { ProcessInput, ProcessingConfig, SpectrumConfig, FrequencySegment, BandwidthConfig, TimestampedFloat32Array, SpectrumOutputData, } from './core/types';
3
- export { SpectrumError, DataValidationError, IndexOutOfBoundsError, } from './core/errors';
4
- export { SPECTRUM, ERROR_MESSAGES } from './core/constants';
5
- export default SpectrumAnalyzer;
1
+ import SpectrumAnalyzer from './core/SpectrumAnalyzer';
2
+ import LevelStreamAnalyzer from './core/LevelStreamAnalyzer';
3
+ export type { ProcessInput, ProcessingConfig, SpectrumConfig, FrequencySegment, BandwidthConfig, TimestampedFloat32Array, SpectrumOutputData, } 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';
7
+ export { SpectrumAnalyzer, LevelStreamAnalyzer };
6
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,QAAQ,CAAC;AAGtC,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAG5D,eAAe,gBAAgB,CAAC"}
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,GACnB,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"}
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=(data,antennaFactorData,interval)=>{const outputLength=data.length<=interval?data.length:interval;const srcIndexCache=new Uint32Array(outputLength);const hasAntennaFactor=antennaFactorData?.length>0;if(interval<=0||data.length<=interval){if(!hasAntennaFactor){for(let i=0;i<data.length;i++)srcIndexCache[i]=i;return{realOutputData:data,srcIndexCache}}const realOutputData=new Float32Array(data.length);realOutputData.timestamp=data.timestamp;for(let i=0;i<data.length;i++){srcIndexCache[i]=i;realOutputData[i]=data[i]+antennaFactorData[i]}return{realOutputData,srcIndexCache}}const ratio=data.length/interval;const realOutputData=new Float32Array(outputLength);realOutputData.timestamp=data.timestamp;let pos=ratio/2;for(let i=0;i<outputLength;i++){const start=Math.floor(pos);const end=Math.min(Math.floor(pos+ratio),data.length);let maxValue=data[start];let maxIndex=start;for(let j=start+1;j<end;j++)if(data[j]>maxValue){maxValue=data[j];maxIndex=j}realOutputData[i]=hasAntennaFactor?maxValue+antennaFactorData[maxIndex]:maxValue;srcIndexCache[i]=maxIndex;pos+=ratio}return{realOutputData,srcIndexCache}};const SPECTRUM={INITIAL_VALUE:0,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:points=>`天线因子数据长度必须等于 maxPoints (${points})`,INVALID_SAMPLING_RANGE:"采样范围无效",INVALID_MAX_POINTS:"点数必须大于0",INVALID_LENGTH:expected=>`频率占用度数据长度不匹配,期望长度为 ${expected}`};class SpectrumAnalyzer{config=DEFAULT_SPECTRUM_CONFIG;segments=[];antennaFactorData;realData;sumData;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}}}process({data,timestamp,segmentOffset=0,offset=0}){const processStartTime=performance.now();try{if(0===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);const processedData=this.resampleDataSeries();if(isOver){this.updateWaterfallData(this.realData,processedData.realData);this.processTimes+=1}this.notifySpectrumUpdate(processedData)}catch(error){throw error instanceof Error?error:new Error(String(error))}finally{this.lastProcessTime=performance.now()-processStartTime}}setOccupancyData(data){const{maxPoints}=this.config;if(data.length!==maxPoints)throw new DataValidationError(ERROR_MESSAGES.INVALID_LENGTH(maxPoints));const{config:{outputRange:{start}}}=this;const len=this.srcIndexCache.length;new Float32Array(data);const occupancyOutputData=new Float32Array(len);for(let i=0;i<len;i++){const dataIndex=this.srcIndexCache[i]+start;occupancyOutputData[i]=data[dataIndex]}return occupancyOutputData}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(data){const{maxPoints}=this.config;if(data.length!==maxPoints)throw new DataValidationError(ERROR_MESSAGES.INVALID_ANTENNA_FACTOR(maxPoints));this.antennaFactorData=new Float32Array(data)}reset(){const{maxPoints,waterfallMaxFrames}=this.config;this.antennaFactorData=new Float32Array(maxPoints);const realData=new Float32Array(maxPoints);realData.timestamp=0;this.realData=realData;this.sumData=new Float32Array(maxPoints);this.maxData=new Float32Array(maxPoints);this.minData=new Float32Array(maxPoints);this.avgData=new Float32Array(maxPoints);this.waterfallData=Array.from({length:waterfallMaxFrames},()=>new Float32Array);this.waterfallOutputData=Array.from({length:waterfallMaxFrames},()=>new Float32Array);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})}getData(){const{realData,maxData,minData,avgData,waterfallData,processTimes}=this;return{realData,maxData,minData,avgData,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||end>this.config.maxPoints||start>=end)return;this.config={...this.config,outputRange:{start:Math.floor(start),end:Math.ceil(end)}};const processedData=this.resampleDataSeries();this.resampleWaterfallOutputData();this.notifySpectrumUpdate(processedData)}resampleDataSeries(){const{antennaFactorData,maxData,minData,avgData,config:{outputPoints,processing:{enableMetrics},outputRange:{start}}}=this;const{realOutputData,srcIndexCache}=resample(this.getOutputData(),antennaFactorData,outputPoints);this.srcIndexCache=srcIndexCache;if(!enableMetrics)return{realData:realOutputData,maxData:new Float32Array,minData:new Float32Array,avgData:new Float32Array};const len=srcIndexCache.length;const maxOutputData=new Float32Array(len);const minOutputData=new Float32Array(len);const avgOutputData=new Float32Array(len);for(let i=0;i<len;i++){const dataIndex=srcIndexCache[i]+start;maxOutputData[i]=maxData[dataIndex];minOutputData[i]=minData[dataIndex];avgOutputData[i]=avgData[dataIndex]}return{realData:realOutputData,maxData:maxOutputData,minData:minOutputData,avgData:avgOutputData}}resampleWaterfallOutputData(){const{antennaFactorData,waterfallData,config:{processing:{enableWaterfall},outputPoints}}=this;if(!enableWaterfall)return;this.waterfallOutputData=waterfallData.map(frame=>{const{realOutputData}=resample(this.getOutputData(frame),antennaFactorData,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){const length=data.length;const{sumData,maxData,minData,avgData,antennaFactorData}=this;const times=this.processTimes;const hasAntennaFactor=antennaFactorData?.length>0;if(0===times){for(let i=0;i<length;i++){const dataIndex=index+i;const value=hasAntennaFactor?data[i]+antennaFactorData[dataIndex]:data[i];sumData[dataIndex]=value;maxData[dataIndex]=value;minData[dataIndex]=value;avgData[dataIndex]=value}return}for(let i=0;i<length;i++){const dataIndex=index+i;const value=hasAntennaFactor?data[i]+antennaFactorData[dataIndex]:data[i];const oldAvg=avgData[dataIndex];sumData[dataIndex]+=value;maxData[dataIndex]=value>maxData[dataIndex]?value:maxData[dataIndex];minData[dataIndex]=value<minData[dataIndex]?value:minData[dataIndex];avgData[dataIndex]=oldAvg+(value-oldAvg)/(times+1)}}notifySpectrumUpdate(processedData){this.onSpectrumUpdate?.({...processedData,waterfallData:this.waterfallOutputData,processTimes:this.processTimes,realOriginalData:this.realData,waterfallOriginalData:this.waterfallData,scanProgress:this.scanProgress})}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}}const src=SpectrumAnalyzer;export{DataValidationError,ERROR_MESSAGES,IndexOutOfBoundsError,SPECTRUM,SpectrumError,src as default};
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 SPECTRUM={INITIAL_VALUE: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)}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(data){const{antennaFactorData,config:{maxPoints}}=this;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 isFinite=Number.isFinite(value)&&value>0;if(!isFinite)hasInvalid=true;antennaFactorData[i]=isFinite?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)}}reset(){const{maxPoints,waterfallMaxFrames}=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(maxPoints).fill(SPECTRUM.INITIAL_VALUE);this.minData=new Float32Array(maxPoints).fill(SPECTRUM.INITIAL_VALUE);this.avgData=new Float32Array(maxPoints).fill(SPECTRUM.INITIAL_VALUE);this.waterfallData=Array.from({length:waterfallMaxFrames},()=>{const frame=new Float32Array(maxPoints);frame.timestamp=0;frame.fill(SPECTRUM.INITIAL_VALUE);return frame});this.waterfallOutputData=Array.from({length:waterfallMaxFrames},()=>{const frame=new Float32Array(maxPoints);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})}getData(){const{realData,maxData,minData,avgData,waterfallData,processTimes}=this;return{realData,maxData,minData,avgData,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);return this.srcIndexCache}resampleDataSeries(){const{antennaFactorData,antennaFactorSwitch,maxData,minData,avgData,config:{maxPoints,outputPoints,processing:{enableMetrics},outputRange:{start}}}=this;const activeAntennaFactorData=antennaFactorSwitch?antennaFactorData:new Float32Array(maxPoints);const realData=this.getOutputData();const{realOutputData,srcIndexCache}=resample({realData,antennaFactorData:activeAntennaFactorData,antennaFactorSwitch,outputPoints});this.srcIndexCache=srcIndexCache;if(!enableMetrics)return{realData:realOutputData,maxData:new Float32Array,minData:new Float32Array,avgData:new Float32Array};const len=srcIndexCache.length;const minOutputData=new Float32Array(len);const avgOutputData=new Float32Array(len);for(let i=0;i<len;i++){const dataIndex=srcIndexCache[i]+start;minOutputData[i]=minData[dataIndex]+activeAntennaFactorData[dataIndex];avgOutputData[i]=avgData[dataIndex]+activeAntennaFactorData[dataIndex]}const maxDataWithTimestamp=maxData;maxDataWithTimestamp.timestamp=realOutputData.timestamp;const outputDataMax=this.getOutputData(maxDataWithTimestamp);const{realOutputData:maxOutputData}=resample({realData:outputDataMax,antennaFactorData:activeAntennaFactorData,antennaFactorSwitch,outputPoints});return{realData:realOutputData,maxData:maxOutputData,minData:minOutputData,avgData:avgOutputData}}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}=resample({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}=this;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,waterfallData:this.waterfallOutputData,processTimes:this.processTimes,realOriginalData:this.realData,waterfallOriginalData:this.waterfallData,scanProgress:this.scanProgress})}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}}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};
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "module": "index.js",
5
5
  "author": "Hxgh",
6
6
  "license": "MIT",
7
- "version": "0.1.0",
7
+ "version": "0.1.2",
8
8
  "private": false,
9
9
  "keywords": [
10
10
  "spectrum-analyzer",
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/core/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ;;;;CAIX,CAAC;AAEX,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;CAa1B,CAAC;AAEX,eAAO,MAAM,cAAc;;;;sCAIA,MAAM;0CACF,MAAM;8CACF,MAAM;;;wCAIZ,MAAM;CAEzB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;aAGpB,IAAI,EAAE,MAAM;aACZ,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,YAAA;CAKpC;AAED,qBAAa,mBAAoB,SAAQ,aAAa;gBACxC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;CAG/C;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,aAAa;aAGpC,KAAK,EAAE,MAAM;gBAD7B,OAAO,EAAE,MAAM,EACC,KAAK,EAAE,MAAM,EAC7B,OAAO,CAAC,EAAE,OAAO;CAOpB"}
package/core/index.d.ts DELETED
@@ -1,39 +0,0 @@
1
- import type { ProcessInput, ScanSegment, SpectrumConfig, SpectrumOutputData, TimestampedFloat32Array } from './types';
2
- export default class SpectrumAnalyzer {
3
- protected config: Required<Omit<SpectrumConfig, 'segments' | 'bandwidthConfig' | 'onSpectrumUpdate'>>;
4
- private segments;
5
- protected antennaFactorData: Float32Array;
6
- protected realData: TimestampedFloat32Array;
7
- protected sumData: Float32Array;
8
- protected maxData: Float32Array;
9
- protected minData: Float32Array;
10
- protected avgData: Float32Array;
11
- protected waterfallData: TimestampedFloat32Array[];
12
- protected srcIndexCache: Uint32Array;
13
- protected realOutputData: Float32Array;
14
- protected waterfallOutputData: TimestampedFloat32Array[];
15
- protected scanProgress: number;
16
- protected lastIndex: number;
17
- protected processTimes: number;
18
- protected lastProcessTime: number;
19
- private readonly onSpectrumUpdate?;
20
- constructor(config: Partial<SpectrumConfig>);
21
- process({ data, timestamp, segmentOffset, offset, }: ProcessInput): void;
22
- setOccupancyData(data: Float32Array): Float32Array;
23
- initializeSegments(segments: ScanSegment[]): void;
24
- setAntennaFactor(data: Float32Array): void;
25
- reset(): void;
26
- getData(): Readonly<SpectrumOutputData>;
27
- getPerformanceMetrics(): Readonly<Record<string, number | boolean>>;
28
- updateSamplingRange(start: number, end: number): void;
29
- protected resampleDataSeries(): Readonly<SpectrumOutputData>;
30
- protected resampleWaterfallOutputData(): void;
31
- protected updateWaterfallData(data: TimestampedFloat32Array, outputData: TimestampedFloat32Array): void;
32
- protected updateMaxPoints(maxPoints: number): void;
33
- protected validateInput(data: Float32Array, index: number): void;
34
- private processDataPoints;
35
- private notifySpectrumUpdate;
36
- private calculateMemoryUsage;
37
- private getOutputData;
38
- }
39
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,SAAS,CAAC;AAKjB,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC,SAAS,CAAC,MAAM,EAAE,QAAQ,CACxB,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,iBAAiB,GAAG,kBAAkB,CAAC,CAC1E,CAA2B;IAC5B,OAAO,CAAC,QAAQ,CAAqB;IAErC,SAAS,CAAC,iBAAiB,EAAG,YAAY,CAAC;IAC3C,SAAS,CAAC,QAAQ,EAAG,uBAAuB,CAAC;IAC7C,SAAS,CAAC,OAAO,EAAG,YAAY,CAAC;IACjC,SAAS,CAAC,OAAO,EAAG,YAAY,CAAC;IACjC,SAAS,CAAC,OAAO,EAAG,YAAY,CAAC;IACjC,SAAS,CAAC,OAAO,EAAG,YAAY,CAAC;IACjC,SAAS,CAAC,aAAa,EAAG,uBAAuB,EAAE,CAAC;IAEpD,SAAS,CAAC,aAAa,EAAG,WAAW,CAAC;IAEtC,SAAS,CAAC,cAAc,EAAG,YAAY,CAAC;IACxC,SAAS,CAAC,mBAAmB,EAAG,uBAAuB,EAAE,CAAC;IAC1D,SAAS,CAAC,YAAY,EAAG,MAAM,CAAC;IAEhC,SAAS,CAAC,SAAS,EAAG,MAAM,CAAC;IAC7B,SAAS,CAAC,YAAY,EAAG,MAAM,CAAC;IAChC,SAAS,CAAC,eAAe,EAAG,MAAM,CAAC;IAEnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAqC;gBAE3D,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC;IAmBpC,OAAO,CAAC,EACb,IAAI,EACJ,SAAS,EACT,aAAiB,EACjB,MAAU,GACX,EAAE,YAAY,GAAG,IAAI;IAqDf,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY;IAwBlD,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI;IAwBjD,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAa1C,KAAK,IAAI,IAAI;IAuCb,OAAO,IAAI,QAAQ,CAAC,kBAAkB,CAAC;IAavC,qBAAqB,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IAUnE,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAa5D,SAAS,CAAC,kBAAkB,IAAI,QAAQ,CAAC,kBAAkB,CAAC;IAmD5D,SAAS,CAAC,2BAA2B,IAAI,IAAI;IAqB7C,SAAS,CAAC,mBAAmB,CAC3B,IAAI,EAAE,uBAAuB,EAC7B,UAAU,EAAE,uBAAuB,GAClC,IAAI;IAsBP,SAAS,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAalD,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAShE,OAAO,CAAC,iBAAiB;IAqCzB,OAAO,CAAC,oBAAoB;IAW5B,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,aAAa;CActB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/core/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,GACnB,MAAM,uBAAuB,EAC7B,mBAAmB,YAAY,EAC/B,UAAU,MAAM,KACf;IAAE,cAAc,EAAE,uBAAuB,CAAC;IAAC,aAAa,EAAE,WAAW,CAAA;CAiEvE,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/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,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,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,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,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,kBAAkB,KAAK,IAAI,CAAC;CACvD;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,uBAAuB,GAAG,YAAY,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAC3E,MAAM,WAAW,kBAAkB;IACjC,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;IAE1C,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC,qBAAqB,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAElD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}