waveform-analysis 0.1.37 → 0.1.38

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.
package/README.md CHANGED
@@ -96,37 +96,37 @@ const data = ref<WaveformData>({
96
96
 
97
97
  ### Props
98
98
 
99
- | Prop | 类型 | 默认值 | 说明 |
100
- | --------------------------------- | ------------------------------------------- | ------------------------------------------------------- | ---------------------------------- |
101
- | `data` | `WaveformData` | 必填 | 波形数据 |
102
- | `displayMode` | `'independent' \| 'separated' \| 'compact'` | `'independent'` | 图框布局 |
103
- | `overlayMode` | `'single-axis' \| 'multi-axis'` | `'single-axis'` | 叠加曲线的 Y 轴模式 |
104
- | `timeUnit` | `'s' \| 'ms'` | `'ms'` | 坐标轴和 tooltip 展示单位 |
105
- | `xLabel` / `yLabel` | `string` | `时间(timeUnit)` / `'幅值'` | 坐标轴名称 |
106
- | `lineColor` | `string` | `'#0960bd'` | 单波形默认颜色 |
107
- | `width` / `height` | `number` | 自适应 | 组件总尺寸,单位为 CSS 像素 |
108
- | `zoomable` / `showTooltip` | `boolean` | `true` / `true` | 缩放和数值 tooltip 开关 |
109
- | `pannable` | `boolean` | `false` | 空格拖拽平移开关 |
110
- | `minZoomSpan` | `number` | 未设置 | 最小缩放跨度,使用原始 X 数据单位 |
111
- | `minVisiblePoints` | `number` | `0` | 缩放后至少保留的不同 X 坐标数 |
99
+ | Prop | 类型 | 默认值 | 说明 |
100
+ | --------------------------------- | ------------------------------------------- | ------------------------------------------------------- | --------------------------------------------- |
101
+ | `data` | `WaveformData` | 必填 | 波形数据 |
102
+ | `displayMode` | `'independent' \| 'separated' \| 'compact'` | `'independent'` | 图框布局 |
103
+ | `overlayMode` | `'single-axis' \| 'multi-axis'` | `'single-axis'` | 叠加曲线的 Y 轴模式 |
104
+ | `timeUnit` | `'s' \| 'ms'` | `'ms'` | 坐标轴和 tooltip 展示单位 |
105
+ | `xLabel` / `yLabel` | `string` | `时间(timeUnit)` / `'幅值'` | 坐标轴名称 |
106
+ | `lineColor` | `string` | `'#0960bd'` | 单波形默认颜色 |
107
+ | `width` / `height` | `number` | 自适应 | 组件总尺寸,单位为 CSS 像素 |
108
+ | `zoomable` / `showTooltip` | `boolean` | `true` / `true` | 缩放和数值 tooltip 开关 |
109
+ | `pannable` | `boolean` | `false` | 空格拖拽平移开关 |
110
+ | `minZoomSpan` | `number` | 未设置 | 最小缩放跨度,使用原始 X 数据单位 |
111
+ | `minVisiblePoints` | `number` | `0` | 缩放后至少保留的不同 X 坐标数 |
112
112
  | `initialXDomain` | `[number, number]` | 未设置 | 所有图框的初始 X 范围(可超出数据,空白显示) |
113
113
  | `initialXDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置初始范围(可超出数据) |
114
- | `xDomainStrategy` | `WaveformXDomainStrategy` | `{ type: 'data' }` | 自动 X 轴视口范围策略 |
115
- | `yDomain` | `[number, number]` | 未设置 | 所有波形的固定 Y 轴范围 |
116
- | `yDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置固定范围 |
117
- | `grid` | `WaveformGridOptions` | `{ rowCount: 2, columnCount: 1, showPagination: true }` | 网格和分页 |
118
- | `axes` | `WaveformAxesOptions` | 轴线均显示 | X/Y 轴基线显隐与 X 轴 label 格式化 |
119
- | `rendering` | `WaveformRenderingOptions` | `{}` | 降采样与点/误差棒间距 |
120
- | `title` / `legend` / `frameStyle` | 对应公开类型 | 未设置 | 标题、图例和图框样式 |
121
- | `frameNumber` | `string \| number` | 未设置 | 图框水印内容 |
122
- | `zeroLine` | `WaveformZeroLineOptions` | `{ visible: false }` | 零值参考线显隐与样式 |
123
- | `cleanView` | `boolean` | `false` | 保留波形、图框和刻度的净图模式 |
124
- | `presentationMode` | `boolean` | `false` | 禁用绘图区交互的展示模式 |
125
- | `annotations` | `WaveformAnnotation[]` | `[]` | 受控标注数据 |
126
- | `annotationsVisible` | `boolean` | `true` | 标注图层显隐 |
127
- | `interactionMode` | `'zoom' \| 'annotation'` | `'zoom'` | 左键交互模式 |
128
- | `hiddenSeriesIds` | `string[]` | 未设置 | 受控隐藏系列 ID |
129
- | `defaultHiddenSeriesIds` | `string[]` | `[]` | 非受控模式的初始隐藏系列 |
114
+ | `xDomainStrategy` | `WaveformXDomainStrategy` | `{ type: 'data' }` | 自动 X 轴视口范围策略 |
115
+ | `yDomain` | `[number, number]` | 未设置 | 所有波形的固定 Y 轴范围 |
116
+ | `yDomains` | `Record<string, [number, number]>` | 未设置 | 按 track/series ID 配置固定范围 |
117
+ | `grid` | `WaveformGridOptions` | `{ rowCount: 2, columnCount: 1, showPagination: true }` | 网格和分页 |
118
+ | `axes` | `WaveformAxesOptions` | 轴线均显示 | X/Y 轴基线显隐与 X 轴 label 格式化 |
119
+ | `rendering` | `WaveformRenderingOptions` | `{}` | 降采样与点/误差棒间距 |
120
+ | `title` / `legend` / `frameStyle` | 对应公开类型 | 未设置 | 标题、图例和图框样式 |
121
+ | `frameNumber` | `string \| number` | 未设置 | 图框水印内容 |
122
+ | `zeroLine` | `WaveformZeroLineOptions` | `{ visible: false }` | 零值参考线显隐与样式 |
123
+ | `cleanView` | `boolean` | `false` | 保留波形、图框和刻度的净图模式 |
124
+ | `presentationMode` | `boolean` | `false` | 禁用绘图区交互的展示模式 |
125
+ | `annotations` | `WaveformAnnotation[]` | `[]` | 受控标注数据 |
126
+ | `annotationsVisible` | `boolean` | `true` | 标注图层显隐 |
127
+ | `interactionMode` | `'zoom' \| 'annotation'` | `'zoom'` | 左键交互模式 |
128
+ | `hiddenSeriesIds` | `string[]` | 未设置 | 受控隐藏系列 ID |
129
+ | `defaultHiddenSeriesIds` | `string[]` | `[]` | 非受控模式的初始隐藏系列 |
130
130
 
131
131
  所有公开类型均可从包入口导入,例如 `WaveformData`、`WaveformSeries`、
132
132
  `WaveformAnnotation`、`WaveformLineStyle`、`WaveformRenderingOptions`、
@@ -415,7 +415,8 @@ Demo 左侧控制面板提供标题实时预览,可配置标题名称、显隐
415
415
 
416
416
  ### 图例与曲线显隐
417
417
 
418
- `legend.backgroundColor` 设置多曲线图例的背景颜色。该字段接受任意有效 CSS 颜色值,
418
+ 每个图框独立管理自己的图例:图框内有两条或更多曲线时显示图例,只有一条曲线时不显示。
419
+ `legend.backgroundColor` 设置图例的背景颜色。该字段接受任意有效 CSS 颜色值,
419
420
  可通过 `rgba(...)` 或 `hsla(...)` 中的 alpha 通道调整透明度:
420
421
 
421
422
  ```vue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waveform-analysis",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/types/index.d.ts",
package/src/App.test.ts CHANGED
@@ -235,7 +235,7 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
235
235
  tracks.map((track) =>
236
236
  track.findAll('.waveform-chart__series').map((item) => item.attributes('data-series-name')),
237
237
  ),
238
- ).toEqual([['正弦基波'], ['谐波扰动'], ['阻尼振荡'], ['阶跃响应']])
238
+ ).toEqual([['正弦基波'], ['谐波扰动', '谐波对比'], ['阻尼振荡'], ['阶跃响应']])
239
239
  expect(
240
240
  tracks
241
241
  .slice(1)
@@ -253,6 +253,7 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
253
253
  expect(simulatedSeries.map((item) => item.name)).toEqual([
254
254
  '正弦基波',
255
255
  '谐波扰动',
256
+ '谐波对比',
256
257
  '阻尼振荡',
257
258
  '阶跃响应',
258
259
  '脉冲响应',
@@ -321,7 +322,6 @@ describe('App workspace layout', { timeout: 20_000 }, () => {
321
322
  const styleSelect = frameControls.findAllComponents(Select)[0]
322
323
 
323
324
  expect(colorPickers).toHaveLength(2)
324
-
325
325
  const initialFrames = wrapper.findAll('.waveform-chart__plot-frame')
326
326
  expect(initialFrames.length > 1 && initialFrames.every((frame) => frame.attributes('stroke-width') === '2')).toBe(true)
327
327
 
@@ -16,13 +16,30 @@ function annotationEditorExists() {
16
16
  }
17
17
 
18
18
  describe('WaveformChart', () => {
19
+ it('shows legends when a track contains at least two series', async () => {
20
+ const oneSeries = visibilitySeries()
21
+ if (oneSeries.kind === 'series') oneSeries.series = oneSeries.series.slice(0, 1)
22
+ const withoutLegend = await mountSizedChart(oneSeries, {
23
+ grid: { rowCount: 1, columnCount: 1 },
24
+ })
25
+ expect(withoutLegend.findAll('.waveform-chart__legend')).toHaveLength(0)
26
+
27
+ const twoSeries = visibilitySeries()
28
+ if (twoSeries.kind === 'series') twoSeries.series = twoSeries.series.slice(0, 2)
29
+ const withLegend = await mountSizedChart(twoSeries, {
30
+ grid: { rowCount: 1, columnCount: 1 },
31
+ })
32
+ expect(withLegend.findAll('.waveform-chart__legend')).toHaveLength(1)
33
+ expect(withLegend.find('.waveform-chart__legend').attributes('data-position')).toBe('top-right')
34
+ })
35
+
19
36
  it('resolves legend positions by stable track id across pages', async () => {
20
37
  const wrapper = await mountSizedChart(
21
38
  {
22
39
  kind: 'series',
23
- series: Array.from({ length: 8 }, (_, index) => ({
40
+ series: Array.from({ length: 12 }, (_, index) => ({
24
41
  id: `series-${index}`,
25
- trackId: `frame-${Math.floor(index / 2)}`,
42
+ trackId: `frame-${Math.floor(index / 3)}`,
26
43
  name: `series ${index}`,
27
44
  data: {
28
45
  kind: 'points' as const,
@@ -75,9 +92,9 @@ describe('WaveformChart', () => {
75
92
  const wrapper = await mountSizedChart(
76
93
  {
77
94
  kind: 'series',
78
- series: Array.from({ length: 4 }, (_, index) => ({
95
+ series: Array.from({ length: 6 }, (_, index) => ({
79
96
  id: `series-${index}`,
80
- trackId: `frame-${Math.floor(index / 2)}`,
97
+ trackId: `frame-${Math.floor(index / 3)}`,
81
98
  name: `series ${index}`,
82
99
  data: {
83
100
  kind: 'points',
@@ -112,7 +129,7 @@ describe('WaveformChart', () => {
112
129
  })
113
130
 
114
131
  const items = wrapper.findAll('.waveform-chart__legend-item')
115
- expect(items).toHaveLength(2)
132
+ expect(items).toHaveLength(3)
116
133
  expect(items.every((item) => item.attributes('disabled') !== undefined)).toBe(true)
117
134
  expect(wrapper.get('.waveform-legend__panel').classes()).not.toContain(
118
135
  'waveform-legend__panel--interactive',
@@ -128,7 +145,7 @@ describe('WaveformChart', () => {
128
145
  overlayMode: 'multi-axis',
129
146
  })
130
147
 
131
- expect(wrapper.findAll('.waveform-chart__axis--y')).toHaveLength(2)
148
+ expect(wrapper.findAll('.waveform-chart__axis--y')).toHaveLength(3)
132
149
  expect(wrapper.find('[data-annotation-id="high-note"]').exists()).toBe(true)
133
150
  const annotationLayer = wrapper.get('.waveform-annotation-layer').element
134
151
  const legendLayer = wrapper.get('.waveform-chart__legend-layer').element
@@ -143,8 +160,8 @@ describe('WaveformChart', () => {
143
160
 
144
161
  expect(
145
162
  wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
146
- ).toEqual(['low'])
147
- expect(wrapper.findAll('.waveform-chart__axis--y')).toHaveLength(1)
163
+ ).toEqual(['low', 'mid'])
164
+ expect(wrapper.findAll('.waveform-chart__axis--y')).toHaveLength(2)
148
165
  expect(wrapper.find('[data-annotation-id="high-note"]').exists()).toBe(false)
149
166
  expect(wrapper.findAll('.waveform-chart__legend-item')[1].classes()).toContain('is-hidden')
150
167
  expect(wrapper.findAll('.waveform-chart__legend-item')[1].attributes('aria-pressed')).toBe(
@@ -170,12 +187,12 @@ describe('WaveformChart', () => {
170
187
  )
171
188
  flushAnimationFrames()
172
189
  await flushPromises()
173
- expect(wrapper.findAll('.waveform-chart__tooltip-series')).toHaveLength(1)
190
+ expect(wrapper.findAll('.waveform-chart__tooltip-series')).toHaveLength(2)
174
191
  expect(wrapper.get('.waveform-chart__tooltip-series').text()).toContain('低量程')
175
192
 
176
193
  await wrapper.findAll('.waveform-chart__legend-item')[1].trigger('click')
177
194
  await flushPromises()
178
- expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(2)
195
+ expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(3)
179
196
  expect(wrapper.find('[data-annotation-id="high-note"]').exists()).toBe(true)
180
197
  expect(wrapper.emitted('series-visibility-change')?.at(-1)).toEqual([
181
198
  { seriesId: 'high', visible: true, hiddenSeriesIds: [] },
@@ -191,20 +208,20 @@ describe('WaveformChart', () => {
191
208
 
192
209
  expect(
193
210
  wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
194
- ).toEqual(['low'])
211
+ ).toEqual(['low', 'mid'])
195
212
  await wrapper.findAll('.waveform-chart__legend-item')[0].trigger('click')
196
213
  expect(wrapper.emitted('update:hidden-series-ids')?.at(-1)).toEqual([
197
214
  ['high', 'temporarily-absent', 'low'],
198
215
  ])
199
216
  expect(
200
217
  wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
201
- ).toEqual(['low'])
218
+ ).toEqual(['low', 'mid'])
202
219
 
203
220
  await wrapper.setProps({ hiddenSeriesIds: ['low'] })
204
221
  await flushPromises()
205
222
  expect(
206
223
  wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
207
- ).toEqual(['high'])
224
+ ).toEqual(['high', 'mid'])
208
225
  })
209
226
 
210
227
  it('retains uncontrolled visibility by stable ID and clears removed IDs', async () => {
@@ -223,7 +240,7 @@ describe('WaveformChart', () => {
223
240
  await flushPromises()
224
241
  expect(
225
242
  wrapper.findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
226
- ).toEqual(['low'])
243
+ ).toEqual(['mid', 'low'])
227
244
 
228
245
  await wrapper.setProps({
229
246
  data: {
@@ -234,12 +251,12 @@ describe('WaveformChart', () => {
234
251
  await flushPromises()
235
252
  await wrapper.setProps({ data: original })
236
253
  await flushPromises()
237
- expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(2)
254
+ expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(3)
238
255
  })
239
256
 
240
257
  it('keeps a recoverable legend and stops chart interaction when every series is hidden', async () => {
241
258
  const wrapper = await mountSizedChart(visibilitySeries(), {
242
- defaultHiddenSeriesIds: ['low', 'high'],
259
+ defaultHiddenSeriesIds: ['low', 'high', 'mid'],
243
260
  grid: { rowCount: 1, columnCount: 1 },
244
261
  legend: { interactive: true },
245
262
  overlayMode: 'multi-axis',
@@ -248,7 +265,7 @@ describe('WaveformChart', () => {
248
265
  expect(wrapper.findAll('.waveform-chart__line')).toHaveLength(0)
249
266
  expect(wrapper.findAll('.waveform-chart__axis')).toHaveLength(0)
250
267
  expect(wrapper.findAll('.waveform-chart__overlay')).toHaveLength(0)
251
- expect(wrapper.findAll('.waveform-chart__legend-item')).toHaveLength(2)
268
+ expect(wrapper.findAll('.waveform-chart__legend-item')).toHaveLength(3)
252
269
  expect(wrapper.get('.waveform-track__no-visible-series').text()).toBe('暂无可见曲线')
253
270
 
254
271
  await wrapper.findAll('.waveform-chart__legend-item')[0].trigger('click')
@@ -177,6 +177,18 @@ describe('WaveformChart', () => {
177
177
  ],
178
178
  },
179
179
  },
180
+ {
181
+ id: 'reference',
182
+ trackId: 'frame-1',
183
+ name: 'REF_CH_1',
184
+ data: {
185
+ kind: 'points',
186
+ points: [
187
+ { x: 0, y: 0.25 },
188
+ { x: 1, y: 1.25 },
189
+ ],
190
+ },
191
+ },
180
192
  ],
181
193
  },
182
194
  { frameNumber: 1, grid: { rowCount: 2, columnCount: 1 } },
@@ -186,7 +198,7 @@ describe('WaveformChart', () => {
186
198
  expect(tracks).toHaveLength(2)
187
199
  expect(
188
200
  tracks[0].findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
189
- ).toEqual(['primary', 'comparison'])
201
+ ).toEqual(['primary', 'comparison', 'reference'])
190
202
  expect(
191
203
  tracks[1].findAll('.waveform-chart__line').map((line) => line.attributes('data-series-id')),
192
204
  ).toEqual(['second-frame'])
@@ -203,12 +215,13 @@ describe('WaveformChart', () => {
203
215
  expect(legend.findAll('.waveform-chart__legend-item').map((item) => item.text())).toEqual([
204
216
  'BT2_2M',
205
217
  'TEST_CH_1',
218
+ 'REF_CH_1',
206
219
  ])
207
220
  expect(
208
221
  legend
209
222
  .findAll('.waveform-legend__swatch')
210
223
  .map((swatch) => swatch.get('path').attributes('stroke')),
211
- ).toEqual(['#0960bd', '#2ca02c'])
224
+ ).toEqual(['#0960bd', '#2ca02c', '#d62728'])
212
225
  expect(wrapper.findAll('.waveform-chart__watermark').map((item) => item.text())).toEqual([
213
226
  '1',
214
227
  '2',
@@ -232,10 +245,11 @@ describe('WaveformChart', () => {
232
245
  await flushPromises()
233
246
 
234
247
  const tooltipSeries = wrapper.findAll('.waveform-chart__tooltip-series')
235
- expect(tooltipSeries).toHaveLength(2)
248
+ expect(tooltipSeries).toHaveLength(3)
236
249
  expect(tooltipSeries.map((item) => item.text())).toEqual([
237
250
  expect.stringContaining('BT2_2M'),
238
251
  expect.stringContaining('TEST_CH_1'),
252
+ expect.stringContaining('REF_CH_1'),
239
253
  ])
240
254
  })
241
255
 
@@ -268,6 +282,18 @@ describe('WaveformChart', () => {
268
282
  ],
269
283
  },
270
284
  },
285
+ {
286
+ id: 'third',
287
+ trackId: 'shared',
288
+ name: 'third',
289
+ data: {
290
+ kind: 'points',
291
+ points: [
292
+ { x: 0, y: 2 },
293
+ { x: 1, y: 3 },
294
+ ],
295
+ },
296
+ },
271
297
  ],
272
298
  },
273
299
  { grid: { rowCount: 1, columnCount: 1 } },
@@ -42,6 +42,19 @@ const presentationData: WaveformData = {
42
42
  ],
43
43
  },
44
44
  },
45
+ {
46
+ id: 'mid',
47
+ trackId: 'shared',
48
+ name: '中量程',
49
+ data: {
50
+ kind: 'points',
51
+ points: [
52
+ { x: 0, y: 50 },
53
+ { x: 1, y: 100 },
54
+ { x: 2, y: 75 },
55
+ ],
56
+ },
57
+ },
45
58
  {
46
59
  id: 'other',
47
60
  name: '其他通道',
@@ -150,9 +163,9 @@ describe('WaveformChart presentation mode', () => {
150
163
  cancelable: true,
151
164
  }),
152
165
  )
153
- wrapper.get('.waveform-chart__svg').element.dispatchEvent(
154
- new MouseEvent('dblclick', { bubbles: true, cancelable: true }),
155
- )
166
+ wrapper
167
+ .get('.waveform-chart__svg')
168
+ .element.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, cancelable: true }))
156
169
  await wrapper.get('[data-annotation-id="note"]').trigger('contextmenu', {
157
170
  clientX: width / 2,
158
171
  clientY: height / 2,
@@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'
3
3
  import { createSimulatedWaveformData } from './simulatedWaveforms'
4
4
 
5
5
  describe('simulated waveform data', () => {
6
- it('creates deterministic, finite six-channel data', () => {
6
+ it('creates deterministic, finite seven-channel data with a two-series second frame', () => {
7
7
  const first = createSimulatedWaveformData()
8
8
  const second = createSimulatedWaveformData()
9
9
 
@@ -11,8 +11,12 @@ describe('simulated waveform data', () => {
11
11
  expect(first.kind).toBe('series')
12
12
  if (first.kind !== 'series') return
13
13
 
14
- expect(first.series).toHaveLength(6)
15
- expect(new Set(first.series.map((series) => series.id)).size).toBe(6)
14
+ expect(first.series).toHaveLength(7)
15
+ expect(new Set(first.series.map((series) => series.id)).size).toBe(7)
16
+ const secondFrame = first.series.filter(
17
+ (series) => series.trackId === 'simulated-harmonic-frame',
18
+ )
19
+ expect(secondFrame).toHaveLength(2)
16
20
  expect(new Set(first.series.map((series) => series.shotNo))).toEqual(new Set(['13300']))
17
21
  first.series.forEach((series) => {
18
22
  expect(series.data.kind).toBe('points')
@@ -13,7 +13,7 @@ type ErrorGenerator = (
13
13
 
14
14
  interface SimulatedSeriesDefinition extends Pick<
15
15
  WaveformSeries,
16
- 'id' | 'shotNo' | 'name' | 'unit' | 'lineType' | 'pointType' | 'errorBar'
16
+ 'id' | 'trackId' | 'shotNo' | 'name' | 'unit' | 'color' | 'lineType' | 'pointType' | 'errorBar'
17
17
  > {
18
18
  signal: SignalGenerator
19
19
  errors?: ErrorGenerator
@@ -60,6 +60,7 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
60
60
  },
61
61
  {
62
62
  id: 'simulated-harmonic',
63
+ trackId: 'simulated-harmonic-frame',
63
64
  shotNo: '13300',
64
65
  name: '谐波扰动',
65
66
  unit: 'V',
@@ -68,6 +69,18 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
68
69
  signal: (time) =>
69
70
  0.9 * Math.sin(TWO_PI * 0.55 * time) + 0.28 * Math.sin(TWO_PI * 2.2 * time + 0.4),
70
71
  },
72
+ {
73
+ id: 'simulated-harmonic-reference',
74
+ trackId: 'simulated-harmonic-frame',
75
+ shotNo: '13300',
76
+ name: '谐波对比',
77
+ unit: 'V',
78
+ color: '#2ca02c',
79
+ lineType: 'linear',
80
+ pointType: 'none',
81
+ signal: (time) =>
82
+ 0.62 * Math.sin(TWO_PI * 0.55 * time + 0.55) + 0.18 * Math.sin(TWO_PI * 2.2 * time - 0.2),
83
+ },
71
84
  {
72
85
  id: 'simulated-damped',
73
86
  shotNo: '13300',
@@ -58,6 +58,18 @@ export function visibilitySeries(): WaveformData {
58
58
  ],
59
59
  },
60
60
  },
61
+ {
62
+ id: 'mid',
63
+ trackId: 'shared-frame',
64
+ name: '中量程',
65
+ data: {
66
+ kind: 'points',
67
+ points: [
68
+ { x: 0, y: 100 },
69
+ { x: 1, y: 200 },
70
+ ],
71
+ },
72
+ },
61
73
  ],
62
74
  }
63
75
  }