waveform-analysis 0.1.71 → 0.1.73
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 +260 -40
- package/dist/assets/{workerRuntime-o5An0M-W.js → workerRuntime-5p-EI-GL.js} +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.js +3874 -2778
- package/dist/style.css +1 -1
- package/dist/types/components/WaveformChart.vue.d.ts +24 -6
- package/dist/types/components/annotation/WaveformAnnotationLayer.vue.d.ts +2 -0
- package/dist/types/components/annotation/useWaveformChartAnnotations.d.ts +0 -1
- package/dist/types/components/controls/WaveformToolbar.vue.d.ts +7 -0
- package/dist/types/components/controls/chartExportOptions.d.ts +5 -0
- package/dist/types/components/controls/chartSnapshot.d.ts +2 -0
- package/dist/types/components/controls/snapshotTitle.d.ts +2 -0
- package/dist/types/components/controls/toolbarRegistry.d.ts +21 -0
- package/dist/types/components/controls/useChartCommands.d.ts +42 -0
- package/dist/types/components/controls/useChartImageExport.d.ts +17 -0
- package/dist/types/components/controls/useControlMode.d.ts +8 -0
- package/dist/types/components/core/latestTaskScheduler.d.ts +1 -1
- package/dist/types/components/core/layout.d.ts +6 -3
- package/dist/types/components/core/niceScale.d.ts +34 -0
- package/dist/types/components/core/trackLayoutBuilder.d.ts +4 -1
- package/dist/types/components/core/trackPagination.d.ts +1 -1
- package/dist/types/components/core/types.d.ts +5 -0
- package/dist/types/components/core/useWaveformChartController.d.ts +24 -6
- package/dist/types/components/core/useWaveformChartLifecycle.d.ts +2 -2
- package/dist/types/components/core/useWaveformData.d.ts +2 -0
- package/dist/types/components/core/useWaveformLayout.d.ts +4 -1
- package/dist/types/components/core/useWaveformMeasurements.d.ts +2 -0
- package/dist/types/components/core/useWaveformRenderSampling.d.ts +1 -0
- package/dist/types/components/core/useWaveformViewportRestore.d.ts +5 -0
- package/dist/types/components/core/waveformChartTypes.d.ts +5 -0
- package/dist/types/components/core/waveformSamplingSession.d.ts +24 -0
- package/dist/types/components/core/yAxisLabelStyle.d.ts +3 -0
- package/dist/types/components/core/yAxisLayoutMetrics.d.ts +2 -1
- package/dist/types/components/core/yAxisPadding.d.ts +7 -0
- package/dist/types/components/core/yAxisTitleLayout.d.ts +2 -0
- package/dist/types/components/interaction/useExtremaTickViewport.d.ts +5 -0
- package/dist/types/components/interaction/useWaveformViewport.d.ts +1 -0
- package/dist/types/components/rendering/WaveformLegend.vue.d.ts +2 -2
- package/dist/types/components/rendering/WaveformTrack.vue.d.ts +2 -3
- package/dist/types/components/rendering/WaveformTrackAxes.vue.d.ts +1 -1
- package/dist/types/components/rendering/WaveformTrackBackdrop.vue.d.ts +1 -0
- package/dist/types/components/rendering/compactFrame.d.ts +5 -0
- package/dist/types/components/rendering/yAxisTickVisibility.d.ts +11 -0
- package/dist/types/core/lineBoundary.d.ts +4 -0
- package/dist/types/core/rendering.d.ts +4 -0
- package/dist/types/core/renderingStrategies.d.ts +2 -0
- package/dist/types/core/workerSampling/outputBoundaries.d.ts +4 -0
- package/dist/types/data/simulatedWaveforms.d.ts +1 -1
- package/dist/types/data/sx2Channel.d.ts +10 -0
- package/dist/types/demo/DemoYAxisPaddingControls.vue.d.ts +11 -0
- package/dist/types/demo/types.d.ts +7 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/types/chart.d.ts +32 -8
- package/dist/types/types/controls.d.ts +69 -0
- package/dist/types/types/index.d.ts +2 -1
- package/dist/types/utils/formatters.d.ts +1 -0
- package/package.json +7 -6
- package/src/App.vue +61 -14
- package/src/components/WaveformChart.css +10 -1
- package/src/components/WaveformChart.vue +6 -0
- package/src/components/WaveformChartView.vue +27 -26
- package/src/components/annotation/WaveformAnnotationLayer.vue +6 -1
- package/src/components/annotation/useWaveformChartAnnotations.ts +7 -18
- package/src/components/controls/WaveformToolbar.vue +247 -0
- package/src/components/controls/chartExportOptions.ts +32 -0
- package/src/components/controls/chartSnapshot.ts +132 -0
- package/src/components/controls/snapshotTitle.ts +73 -0
- package/src/components/controls/toolbarRegistry.ts +92 -0
- package/src/components/controls/useChartCommands.ts +294 -0
- package/src/components/controls/useChartImageExport.ts +155 -0
- package/src/components/controls/useControlMode.ts +30 -0
- package/src/components/core/latestTaskScheduler.ts +14 -2
- package/src/components/core/layout.ts +49 -36
- package/src/components/core/niceScale.ts +151 -0
- package/src/components/core/trackLayoutBuilder.ts +41 -17
- package/src/components/core/trackPagination.ts +3 -3
- package/src/components/core/types.ts +5 -0
- package/src/components/core/useWaveformChartController.ts +94 -3
- package/src/components/core/useWaveformChartLifecycle.ts +11 -111
- package/src/components/core/useWaveformData.ts +4 -0
- package/src/components/core/useWaveformLayout.ts +25 -5
- package/src/components/core/useWaveformMeasurements.ts +63 -0
- package/src/components/core/useWaveformRenderSampling.ts +80 -59
- package/src/components/core/useWaveformViewportRestore.ts +105 -0
- package/src/components/core/useWaveformYAxisLayout.ts +3 -2
- package/src/components/core/waveformChartTypes.ts +5 -0
- package/src/components/core/waveformSamplingSession.ts +49 -0
- package/src/components/core/workerSamplingClient.ts +38 -6
- package/src/components/core/yAxisLabelStyle.ts +10 -0
- package/src/components/core/yAxisLayoutMetrics.ts +11 -9
- package/src/components/core/yAxisPadding.ts +60 -0
- package/src/components/core/yAxisTitleLayout.ts +11 -12
- package/src/components/interaction/useExtremaTickViewport.ts +43 -0
- package/src/components/interaction/useWaveformViewport.ts +11 -2
- package/src/components/rendering/WaveformTrack.css +1 -2
- package/src/components/rendering/WaveformTrack.vue +5 -12
- package/src/components/rendering/WaveformTrackAxes.vue +66 -19
- package/src/components/rendering/WaveformTrackBackdrop.vue +8 -3
- package/src/components/rendering/compactFrame.ts +31 -0
- package/src/components/rendering/yAxisTickVisibility.ts +156 -0
- package/src/core/lineBoundary.ts +24 -0
- package/src/core/rendering.ts +48 -25
- package/src/core/renderingStrategies.ts +19 -13
- package/src/core/waveformPointSource.ts +23 -1
- package/src/core/workerSampling/outputBoundaries.ts +32 -0
- package/src/core/workerSampling/repository.ts +26 -18
- package/src/core/workerSampling/repositoryDataset.ts +33 -1
- package/src/core/workerSampling/workerRuntime.ts +13 -3
- package/src/data/simulatedWaveforms.ts +67 -2
- package/src/data/sx2Channel.ts +315 -0
- package/src/demo/DemoChartHost.vue +5 -1
- package/src/demo/DemoGridFrameControls.vue +2 -0
- package/src/demo/DemoViewControls.vue +5 -2
- package/src/demo/DemoYAxisPaddingControls.vue +53 -0
- package/src/demo/embedBridge.ts +85 -0
- package/src/demo/types.ts +7 -0
- package/src/index.ts +19 -0
- package/src/types/chart.ts +34 -8
- package/src/types/controls.ts +80 -0
- package/src/types/index.ts +18 -0
- package/src/utils/formatters.ts +2 -1
- package/src/views/WasmSamplingDemo.vue +8 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pointSourceFromPoints, type WaveformPointSource } from './waveformPointSource'
|
|
1
2
|
import type { WaveformPoint } from '../types'
|
|
2
3
|
import type { ResolvedWaveformRenderingOptions } from './renderingOptions'
|
|
3
4
|
|
|
@@ -7,6 +8,7 @@ export interface VisiblePointRange {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export interface RenderablePointSelectionContext {
|
|
11
|
+
source?: WaveformPointSource
|
|
10
12
|
points: WaveformPoint[]
|
|
11
13
|
range: VisiblePointRange
|
|
12
14
|
domain: [number, number]
|
|
@@ -25,17 +27,15 @@ function selectionBounds(range: VisiblePointRange, pointCount: number) {
|
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
function pushUniquePoint(target: WaveformPoint[], point: WaveformPoint | undefined) {
|
|
29
|
-
if (point && target[target.length - 1] !== point) target.push(point)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
30
|
export const completePointSelectionStrategy: RenderablePointSelectionStrategy = (context) => {
|
|
33
|
-
const
|
|
34
|
-
|
|
31
|
+
const source = context.source ?? pointSourceFromPoints(context.points)
|
|
32
|
+
const { start, end } = selectionBounds(context.range, source.length)
|
|
33
|
+
return source.pointsInRange(start, end)
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
export const peakPreservingPointSelectionStrategy: RenderablePointSelectionStrategy = (context) => {
|
|
38
|
-
const {
|
|
37
|
+
const { range, domain, width, options } = context
|
|
38
|
+
const points = context.source ?? pointSourceFromPoints(context.points)
|
|
39
39
|
const { start, end } = selectionBounds(range, points.length)
|
|
40
40
|
const visibleCount = end - start
|
|
41
41
|
if (visibleCount <= 0) return []
|
|
@@ -48,6 +48,12 @@ export const peakPreservingPointSelectionStrategy: RenderablePointSelectionStrat
|
|
|
48
48
|
)
|
|
49
49
|
const bucketCount = Math.max(1, Math.floor(maximumPointCount / 4))
|
|
50
50
|
const result: WaveformPoint[] = []
|
|
51
|
+
let lastSelectedIndex = -1
|
|
52
|
+
const pushIndex = (index: number) => {
|
|
53
|
+
if (index < 0 || index >= points.length || index === lastSelectedIndex) return
|
|
54
|
+
result.push(points.pointAt(index)!)
|
|
55
|
+
lastSelectedIndex = index
|
|
56
|
+
}
|
|
51
57
|
const span = domainEnd - domainStart || 1
|
|
52
58
|
const bucketIndexes = Array.from({ length: 4 }, () => -1)
|
|
53
59
|
let activeBucket = -1
|
|
@@ -82,13 +88,13 @@ export const peakPreservingPointSelectionStrategy: RenderablePointSelectionStrat
|
|
|
82
88
|
bucketIndexes[position + 1] = value
|
|
83
89
|
}
|
|
84
90
|
for (let index = 0; index < count; index += 1) {
|
|
85
|
-
|
|
91
|
+
pushIndex(bucketIndexes[index])
|
|
86
92
|
}
|
|
87
93
|
}
|
|
88
94
|
|
|
89
|
-
|
|
95
|
+
pushIndex(start)
|
|
90
96
|
for (let index = range.start; index < range.end; index += 1) {
|
|
91
|
-
const point = points
|
|
97
|
+
const point = points.pointAt(index)!
|
|
92
98
|
const bucket = Math.min(
|
|
93
99
|
bucketCount - 1,
|
|
94
100
|
Math.max(0, Math.floor(((point.x - domainStart) / span) * bucketCount)),
|
|
@@ -103,11 +109,11 @@ export const peakPreservingPointSelectionStrategy: RenderablePointSelectionStrat
|
|
|
103
109
|
continue
|
|
104
110
|
}
|
|
105
111
|
lastIndex = index
|
|
106
|
-
if (point.y < points
|
|
107
|
-
if (point.y > points
|
|
112
|
+
if (point.y < points.pointAt(minimumIndex)!.y) minimumIndex = index
|
|
113
|
+
if (point.y > points.pointAt(maximumIndex)!.y) maximumIndex = index
|
|
108
114
|
}
|
|
109
115
|
flushBucket()
|
|
110
|
-
|
|
116
|
+
pushIndex(end - 1)
|
|
111
117
|
return result
|
|
112
118
|
}
|
|
113
119
|
|
|
@@ -288,6 +288,26 @@ function compactSamples(data: Extract<SingleWaveformData, { kind: 'typed-samples
|
|
|
288
288
|
function compactPoints(data: Extract<SingleWaveformData, { kind: 'typed-points' }>) {
|
|
289
289
|
if (!typedPointsAreValid(data))
|
|
290
290
|
return new CompactWaveformPointSource(new Float64Array(), new Float64Array())
|
|
291
|
+
// Validate once before the common dense/sorted path; always take private copies.
|
|
292
|
+
let denseOrdered = true
|
|
293
|
+
for (let index = 0; index < data.x.length; index += 1) {
|
|
294
|
+
if (
|
|
295
|
+
!Number.isFinite(data.x[index]) ||
|
|
296
|
+
!Number.isFinite(data.y[index]) ||
|
|
297
|
+
(index > 0 && data.x[index]! < data.x[index - 1]!)
|
|
298
|
+
) {
|
|
299
|
+
denseOrdered = false
|
|
300
|
+
break
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (denseOrdered)
|
|
304
|
+
return new CompactWaveformPointSource(
|
|
305
|
+
data.x.slice(),
|
|
306
|
+
data.y.slice(),
|
|
307
|
+
data.error?.slice(),
|
|
308
|
+
data.lowerError?.slice(),
|
|
309
|
+
data.upperError?.slice(),
|
|
310
|
+
)
|
|
291
311
|
const indexes: number[] = []
|
|
292
312
|
let ordered = true
|
|
293
313
|
let previousX = Number.NEGATIVE_INFINITY
|
|
@@ -356,11 +376,13 @@ export function lazyPointArray(source: WaveformPointSource): WaveformPoint[] {
|
|
|
356
376
|
}
|
|
357
377
|
return point
|
|
358
378
|
}
|
|
359
|
-
|
|
379
|
+
// Keep the compatibility facade sparse: no point-sized backing array is allocated for compact data.
|
|
380
|
+
return new Proxy<WaveformPoint[]>([], {
|
|
360
381
|
get(target, property, receiver) {
|
|
361
382
|
if (typeof property === 'string' && /^(0|[1-9]\d*)$/.test(property)) {
|
|
362
383
|
return valueAt(Number(property))
|
|
363
384
|
}
|
|
385
|
+
if (property === 'length') return source.length
|
|
364
386
|
return Reflect.get(target, property, receiver)
|
|
365
387
|
},
|
|
366
388
|
has(target, property) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { WaveformPointRange } from '../waveformPointSource'
|
|
2
|
+
import type { WorkerSamplingOutput } from './protocol'
|
|
3
|
+
import { datasetLength, datasetXAt, type StoredDataset } from './repositoryDataset'
|
|
4
|
+
|
|
5
|
+
export function addOutputBoundaries(
|
|
6
|
+
output: WorkerSamplingOutput,
|
|
7
|
+
range: WaveformPointRange,
|
|
8
|
+
dataset: StoredDataset,
|
|
9
|
+
): WorkerSamplingOutput {
|
|
10
|
+
const length = datasetLength(dataset)
|
|
11
|
+
if (range.start === range.end && (range.start === 0 || range.end === length)) return output
|
|
12
|
+
const prefix = range.start > 0 ? [range.start - 1] : []
|
|
13
|
+
const suffix = range.end < length ? [range.end] : []
|
|
14
|
+
if (output.kind === 'source-indexes') {
|
|
15
|
+
const values = Array.from(output.sourceIndexes)
|
|
16
|
+
return {
|
|
17
|
+
kind: 'source-indexes',
|
|
18
|
+
sourceIndexes: Uint32Array.from([...prefix, ...values, ...suffix]),
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const x = Array.from(output.x)
|
|
22
|
+
const y = Array.from(output.y)
|
|
23
|
+
if (prefix.length) {
|
|
24
|
+
x.unshift(datasetXAt(dataset, prefix[0]!))
|
|
25
|
+
y.unshift(dataset.y[prefix[0]!]!)
|
|
26
|
+
}
|
|
27
|
+
if (suffix.length) {
|
|
28
|
+
x.push(datasetXAt(dataset, suffix[0]!))
|
|
29
|
+
y.push(dataset.y[suffix[0]!]!)
|
|
30
|
+
}
|
|
31
|
+
return { kind: 'aggregates', x: Float64Array.from(x), y: Float64Array.from(y) }
|
|
32
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addOutputBoundaries } from './outputBoundaries'
|
|
1
2
|
import type { WaveformPoint } from '../../types'
|
|
2
3
|
import type { WorkerSamplingDataset } from '../waveformPointSource'
|
|
3
4
|
import { javascriptSamplingBackend } from './javascriptBackend'
|
|
@@ -194,13 +195,17 @@ export class WorkerSamplingRepository {
|
|
|
194
195
|
requestId,
|
|
195
196
|
request,
|
|
196
197
|
'ok',
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
198
|
+
addOutputBoundaries(
|
|
199
|
+
{
|
|
200
|
+
kind: 'source-indexes',
|
|
201
|
+
sourceIndexes: Uint32Array.from(
|
|
202
|
+
{ length: visiblePointCount },
|
|
203
|
+
(_, index) => range.start + index,
|
|
204
|
+
),
|
|
205
|
+
},
|
|
206
|
+
range,
|
|
207
|
+
dataset,
|
|
208
|
+
),
|
|
204
209
|
'raw',
|
|
205
210
|
'raw',
|
|
206
211
|
visiblePointCount,
|
|
@@ -256,18 +261,21 @@ export class WorkerSamplingRepository {
|
|
|
256
261
|
)
|
|
257
262
|
: undefined
|
|
258
263
|
: dataset.index?.sample(range.start, range.end, strategy, target)
|
|
259
|
-
const output =
|
|
264
|
+
const output = addOutputBoundaries(
|
|
260
265
|
indexed ??
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
266
|
+
outputWithGlobalIndexes(
|
|
267
|
+
backend.sample({
|
|
268
|
+
x: datasetXRange(dataset, range.start, range.end),
|
|
269
|
+
y: dataset.y.subarray(range.start, range.end),
|
|
270
|
+
points: dataset.points?.slice(range.start, range.end) ?? [],
|
|
271
|
+
strategy,
|
|
272
|
+
targetPointCount: target,
|
|
273
|
+
}),
|
|
274
|
+
range.start,
|
|
275
|
+
),
|
|
276
|
+
range,
|
|
277
|
+
dataset,
|
|
278
|
+
)
|
|
271
279
|
this.cache.set(key, output)
|
|
272
280
|
if (dataset.wasmDatasetHandle !== undefined && isWasmDatasetBackend(backend)) {
|
|
273
281
|
dataset.wasmIndexBytes = backend.datasetIndexBytes(dataset.wasmDatasetHandle)
|
|
@@ -69,8 +69,16 @@ export function isWasmDatasetBackend(
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function copyFiniteNumericValues(sourceX: Float64Array, sourceY: Float32Array | Float64Array) {
|
|
72
|
-
const indexes: number[] = []
|
|
73
72
|
const length = Math.min(sourceX.length, sourceY.length)
|
|
73
|
+
let denseOrdered = sourceX.length === sourceY.length
|
|
74
|
+
for (let index = 0; index < length && denseOrdered; index += 1) {
|
|
75
|
+
denseOrdered =
|
|
76
|
+
Number.isFinite(sourceX[index]) &&
|
|
77
|
+
Number.isFinite(sourceY[index]) &&
|
|
78
|
+
(index === 0 || sourceX[index]! >= sourceX[index - 1]!)
|
|
79
|
+
}
|
|
80
|
+
if (denseOrdered) return { x: sourceX.slice(), y: Float64Array.from(sourceY) }
|
|
81
|
+
const indexes: number[] = []
|
|
74
82
|
for (let index = 0; index < length; index += 1) {
|
|
75
83
|
if (Number.isFinite(sourceX[index]) && Number.isFinite(sourceY[index])) indexes.push(index)
|
|
76
84
|
}
|
|
@@ -120,6 +128,30 @@ export function createStoredDataset(
|
|
|
120
128
|
},
|
|
121
129
|
}
|
|
122
130
|
}
|
|
131
|
+
// Compact, already-valid sample messages need neither an index array nor a sort.
|
|
132
|
+
if (!numeric.sourceIndexes && numeric.values.every(Number.isFinite)) {
|
|
133
|
+
const y = Float64Array.from(numeric.values)
|
|
134
|
+
let min = Infinity
|
|
135
|
+
let max = -Infinity
|
|
136
|
+
for (const value of y) {
|
|
137
|
+
min = Math.min(min, value)
|
|
138
|
+
max = Math.max(max, value)
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
y,
|
|
142
|
+
sampleStartTime: numeric.startTime,
|
|
143
|
+
sampleRate: numeric.sampleRate,
|
|
144
|
+
revision,
|
|
145
|
+
metrics: {
|
|
146
|
+
inputPointCount: y.length,
|
|
147
|
+
validPointCount: y.length,
|
|
148
|
+
xDomain: y.length
|
|
149
|
+
? [numeric.startTime, numeric.startTime + (y.length - 1) / numeric.sampleRate]
|
|
150
|
+
: null,
|
|
151
|
+
yDomain: y.length ? [min, max] : null,
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
}
|
|
123
155
|
const indexes = Array.from({ length: numeric.values.length }, (_, index) => index).filter(
|
|
124
156
|
(index) => Number.isFinite(numeric.values[index]),
|
|
125
157
|
)
|
|
@@ -31,10 +31,20 @@ function addWasmError(response: WorkerSamplingResponse): WorkerSamplingResponse
|
|
|
31
31
|
|
|
32
32
|
const workerScope = globalThis as typeof globalThis & {
|
|
33
33
|
onmessage: ((event: MessageEvent<WorkerSamplingRequest>) => void) | null
|
|
34
|
-
postMessage: (
|
|
34
|
+
postMessage: (
|
|
35
|
+
message: WorkerSamplingResponse | { type: 'request-error'; requestId: number; message: string },
|
|
36
|
+
) => void
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
workerScope.onmessage = async (event: MessageEvent<WorkerSamplingRequest>) => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
try {
|
|
41
|
+
if (requiresWasm(event.data)) await initializeWasmBackend()
|
|
42
|
+
workerScope.postMessage(addWasmError(repository.handle(event.data)))
|
|
43
|
+
} catch (error) {
|
|
44
|
+
workerScope.postMessage({
|
|
45
|
+
type: 'request-error',
|
|
46
|
+
requestId: event.data.requestId,
|
|
47
|
+
message: error instanceof Error ? error.message : String(error),
|
|
48
|
+
})
|
|
49
|
+
}
|
|
40
50
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { WaveformData, WaveformPoint, WaveformSeries } from '../types'
|
|
2
2
|
import { recordedDampedPoints } from './recordedDampedWaveform'
|
|
3
|
+
import { sx2Channel } from './sx2Channel'
|
|
3
4
|
|
|
4
5
|
const POINT_COUNT = 1_000
|
|
5
6
|
const START_TIME = -5
|
|
@@ -140,14 +141,78 @@ const seriesDefinitions: SimulatedSeriesDefinition[] = [
|
|
|
140
141
|
signal: (time, noise) => 0.7 * Math.sin(TWO_PI * 0.45 * time) + noise * 0.36,
|
|
141
142
|
errors: () => ({ error: 0.12 }),
|
|
142
143
|
},
|
|
144
|
+
{
|
|
145
|
+
id: 'simulated-mega-voltage',
|
|
146
|
+
shotNo: '13300',
|
|
147
|
+
name: '百万量级电压',
|
|
148
|
+
unit: 'V',
|
|
149
|
+
lineType: 'linear',
|
|
150
|
+
pointType: 'none',
|
|
151
|
+
signal: (time) => 2.4e6 * Math.sin(TWO_PI * 0.6 * time),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: 'simulated-micro-current',
|
|
155
|
+
shotNo: '13300',
|
|
156
|
+
name: '微量级电流',
|
|
157
|
+
unit: 'A',
|
|
158
|
+
lineType: 'linear',
|
|
159
|
+
pointType: 'none',
|
|
160
|
+
signal: (time, noise) => 3.2e-6 * Math.sin(TWO_PI * 0.45 * time) + noise * 2e-7,
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: 'simulated-giga-pressure',
|
|
164
|
+
shotNo: '13300',
|
|
165
|
+
name: '十亿量级压力脉冲',
|
|
166
|
+
unit: 'Pa',
|
|
167
|
+
lineType: 'linear',
|
|
168
|
+
pointType: 'none',
|
|
169
|
+
signal: (time) => 1.8e9 + 4e8 * Math.exp(-((time - 0.75) ** 2) / 0.4),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 'simulated-nano-displacement',
|
|
173
|
+
shotNo: '13300',
|
|
174
|
+
name: '纳量级阻尼位移',
|
|
175
|
+
unit: 'm',
|
|
176
|
+
lineType: 'linear',
|
|
177
|
+
pointType: 'none',
|
|
178
|
+
signal: (time) => 6e-9 * Math.exp(-(time + 5) * 0.2) * Math.cos(TWO_PI * 0.8 * time),
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: `recorded-${sx2Channel.chnl_id}`,
|
|
182
|
+
shotNo: String(sx2Channel.shot),
|
|
183
|
+
name: sx2Channel.chnl,
|
|
184
|
+
unit: sx2Channel.dat_unit,
|
|
185
|
+
lineType: 'linear',
|
|
186
|
+
pointType: 'none',
|
|
187
|
+
signal: sx2Channel.data.map((value, index) => [sx2Channel.time[index]!, value]),
|
|
188
|
+
},
|
|
143
189
|
]
|
|
144
190
|
|
|
145
|
-
export function createSimulatedWaveformData(): WaveformData {
|
|
191
|
+
export function createSimulatedWaveformData(trackCount?: number): WaveformData {
|
|
146
192
|
const noise = createSeededNoise(0x5eed1234)
|
|
147
193
|
const errorNoise = createSeededNoise(0xe220a11)
|
|
194
|
+
const definitions = [...seriesDefinitions]
|
|
195
|
+
const existingTrackCount = new Set(definitions.map((series) => series.trackId ?? series.id)).size
|
|
196
|
+
const extraTrackCount =
|
|
197
|
+
trackCount !== undefined && Number.isFinite(trackCount)
|
|
198
|
+
? Math.max(0, Math.min(100, Math.floor(trackCount)) - existingTrackCount)
|
|
199
|
+
: 0
|
|
200
|
+
for (let index = 0; index < extraTrackCount; index += 1) {
|
|
201
|
+
const channel = existingTrackCount + index + 1
|
|
202
|
+
definitions.push({
|
|
203
|
+
id: `simulated-extra-${channel}`,
|
|
204
|
+
name: `模拟通道 ${channel}`,
|
|
205
|
+
unit: 'V',
|
|
206
|
+
lineType: 'linear',
|
|
207
|
+
pointType: 'none',
|
|
208
|
+
signal: (time) =>
|
|
209
|
+
(1 + index * 0.3) * Math.sin(TWO_PI * (0.2 + index * 0.15) * time + index) +
|
|
210
|
+
0.2 * Math.cos(TWO_PI * 1.4 * time),
|
|
211
|
+
})
|
|
212
|
+
}
|
|
148
213
|
return {
|
|
149
214
|
kind: 'series',
|
|
150
|
-
series:
|
|
215
|
+
series: definitions.map(({ signal, errors, minimumX, ...series }) => {
|
|
151
216
|
const points = createPoints(signal, noise, errors, errorNoise)
|
|
152
217
|
return {
|
|
153
218
|
...series,
|