1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2026-present FERS Contributors (see AUTHORS.md).
4import { describe, expect, test } from 'bun:test';
6 normalizeSimulationOutputMetadata,
7 type RawSimulationOutputMetadata,
8 type SimulationOutputMetadata,
9} from './simulationProgressStore';
13 simulation_name: 'Metadata Fixture',
14 output_directory: '/tmp/fers',
19} satisfies Omit<SimulationOutputMetadata, 'files'>;
21describe('simulation output metadata', () => {
22 test('keeps file-backed FMCW waveform metadata', () => {
23 const metadata: SimulationOutputMetadata = {
28 receiver_name: 'File FMCW Rx',
30 path: '/tmp/fers/file-fmcw.h5',
34 sample_end_exclusive: 4,
36 min_pulse_length_samples: 0,
37 max_pulse_length_samples: 0,
38 uniform_pulse_length: true,
40 streaming_segments: [],
42 waveform_shape: 'file',
46 start_frequency_offset: 0,
47 sampled_duration: 250e-6,
57 normalizeSimulationOutputMetadata(metadata).files[0].fmcw
59 waveform_shape: 'file',
60 sampled_duration: 250e-6,
65 test('keeps FMCW metadata and streaming segments', () => {
66 const metadata: SimulationOutputMetadata = {
71 receiver_name: 'FMCW Rx',
73 path: '/tmp/fers/fmcw.h5',
77 sample_end_exclusive: 128,
79 min_pulse_length_samples: 0,
80 max_pulse_length_samples: 0,
81 uniform_pulse_length: true,
89 sample_end_exclusive: 128,
90 first_chirp_start_time: 0,
91 emitted_chirp_count: 4,
95 chirp_bandwidth: 20e6,
96 chirp_duration: 250e-6,
99 chirp_rate_signed: -80e9,
100 chirp_direction: 'down',
101 start_frequency_offset: 0,
107 transmitter_name: 'FMCW Tx',
109 waveform_name: 'FMCW Wave',
110 carrier_frequency: 10e9,
111 chirp_bandwidth: 20e6,
112 chirp_duration: 250e-6,
113 chirp_period: 500e-6,
115 chirp_rate_signed: -80e9,
116 chirp_direction: 'down',
117 start_frequency_offset: 0,
123 first_chirp_start_time: 0,
124 emitted_chirp_count: 4,
135 normalizeSimulationOutputMetadata(metadata).files[0]
138 streaming_segments: [
140 first_chirp_start_time: 0,
141 emitted_chirp_count: 4,
145 chirp_bandwidth: 20e6,
146 chirp_duration: 250e-6,
147 chirp_period: 500e-6,
148 chirp_direction: 'down',
156 first_chirp_start_time: 0,
157 emitted_chirp_count: 4,
165 test('adapts legacy cw_segments metadata to streaming_segments', () => {
166 const legacyMetadata = {
171 receiver_name: 'CW Rx',
173 path: '/tmp/fers/cw.h5',
177 sample_end_exclusive: 64,
179 min_pulse_length_samples: 0,
180 max_pulse_length_samples: 0,
181 uniform_pulse_length: true,
189 sample_end_exclusive: 64,
194 } satisfies RawSimulationOutputMetadata;
196 const normalizedFile =
197 normalizeSimulationOutputMetadata(legacyMetadata).files[0];
199 expect(normalizedFile.streaming_segments).toHaveLength(1);
200 expect(normalizedFile.fmcw_sources).toEqual([]);
201 expect('cw_segments' in normalizedFile).toBe(false);
204 test('normalizes mixed sampling-rate metadata', () => {
208 sampling_rates: [1e6, 2e6],
212 receiver_name: 'Rx 1',
214 path: '/tmp/fers/rx1.h5',
218 sample_end_exclusive: 64,
220 min_pulse_length_samples: 0,
221 max_pulse_length_samples: 0,
222 uniform_pulse_length: true,
224 streaming_segments: [],
228 receiver_name: 'Rx 2',
230 path: '/tmp/fers/rx2.h5',
234 sample_end_exclusive: 128,
236 min_pulse_length_samples: 0,
237 max_pulse_length_samples: 0,
238 uniform_pulse_length: true,
240 streaming_segments: [],
241 fmcw_dechirp_mode: 'none',
242 fmcw_dechirp_reference_source: 'none',
245 } satisfies RawSimulationOutputMetadata;
247 const normalized = normalizeSimulationOutputMetadata(metadata);
249 expect(normalized.sampling_rate).toBeNull();
250 expect(normalized.sampling_rates).toEqual([1e6, 2e6]);
251 expect(normalized.files.map((file) => file.sampling_rate)).toEqual([
256 test('preserves VITA49 metadata section', () => {
261 endpoint: '127.0.0.1:4991',
262 endpoint_host: '127.0.0.1',
264 epoch_unix_nanoseconds: '1700000000123456789',
265 class_id: '0xFA52530001000101',
267 max_udp_payload: 1400,
275 reference_frequency: 9.6e9,
277 samples_emitted: 512,
281 late_data_packet_count: 0,
282 late_context_packet_count: 0,
283 context_packet_count: 2,
284 first_sample_time: 0,
287 integer_seconds: 1700000000,
288 fractional_picoseconds: 123456789000,
291 integer_seconds: 1700000010,
292 fractional_picoseconds: 123456789000,
297 } satisfies RawSimulationOutputMetadata;
299 expect(normalizeSimulationOutputMetadata(metadata).vita49).toEqual(