FERS 0.1.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
fmcwModeConfig.ts
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2026-present FERS Contributors (see AUTHORS.md).
3
4export type DechirpMode = 'none' | 'physical' | 'ideal';
5export type DechirpReferenceSource = 'attached' | 'transmitter' | 'custom';
6
7export type DechirpReference =
8 | { source: 'attached' }
9 | { source: 'transmitter'; transmitter_name?: string }
10 | { source: 'custom'; waveform_name?: string };
11
12export type ReceiverFmcwModeConfig = {
13 dechirp_mode?: DechirpMode;
14 dechirp_reference?: DechirpReference;
15 if_sample_rate?: number;
16 if_filter_bandwidth?: number;
17 if_filter_transition_width?: number;
18};
19
20export type FmcwIfChainConfig = Pick<
21 ReceiverFmcwModeConfig,
22 'if_sample_rate' | 'if_filter_bandwidth' | 'if_filter_transition_width'
23>;
24
25export const DECHIRP_MODE_OPTIONS: ReadonlyArray<{
26 value: DechirpMode;
27 label: string;
28}> = [
29 { value: 'none', label: 'None' },
30 { value: 'physical', label: 'Physical' },
31 { value: 'ideal', label: 'Ideal' },
32];
33
34export const DECHIRP_REFERENCE_SOURCE_OPTIONS: ReadonlyArray<{
35 value: DechirpReferenceSource;
36 label: string;
37}> = [
38 { value: 'attached', label: 'Attached' },
39 { value: 'transmitter', label: 'Transmitter' },
40 { value: 'custom', label: 'Custom Waveform' },
41];
42
43const DECHIRP_MODES = new Set<DechirpMode>(['none', 'physical', 'ideal']);
44const DECHIRP_REFERENCE_SOURCES = new Set<DechirpReferenceSource>([
45 'attached',
46 'transmitter',
47 'custom',
48]);
49
50export const FMCW_WAVEFORM_TYPES = [
51 'fmcw_from_file',
52 'fmcw_linear_chirp',
53 'fmcw_triangle',
54] as const;
55
56export type FmcwWaveformType = (typeof FMCW_WAVEFORM_TYPES)[number];
57
58export function isFmcwWaveformType(
59 waveformType: string | undefined
60): waveformType is FmcwWaveformType {
61 return FMCW_WAVEFORM_TYPES.includes(waveformType as FmcwWaveformType);
62}
63
64export function isRecord(value: unknown): value is Record<string, unknown> {
65 return typeof value === 'object' && value !== null && !Array.isArray(value);
66}
67
68export function isDechirpMode(value: unknown): value is DechirpMode {
69 return typeof value === 'string' && DECHIRP_MODES.has(value as DechirpMode);
70}
71
72export function isDechirpReferenceSource(
73 value: unknown
74): value is DechirpReferenceSource {
75 return (
76 typeof value === 'string' &&
77 DECHIRP_REFERENCE_SOURCES.has(value as DechirpReferenceSource)
78 );
79}
80
81export function getDechirpMode(config: unknown): DechirpMode {
82 if (!isRecord(config)) {
83 return 'none';
84 }
85 return isDechirpMode(config.dechirp_mode) ? config.dechirp_mode : 'none';
86}
87
88export function getDechirpReferenceSource(
89 reference: unknown
90): DechirpReferenceSource {
91 if (!isRecord(reference)) {
92 return 'attached';
93 }
94 return isDechirpReferenceSource(reference.source)
95 ? reference.source
96 : 'attached';
97}
98
99export function getFmcwIfChainConfig(config: unknown): FmcwIfChainConfig {
100 if (!isRecord(config)) {
101 return {};
102 }
103
104 const ifChainConfig: FmcwIfChainConfig = {};
105 if (typeof config.if_sample_rate === 'number') {
106 ifChainConfig.if_sample_rate = config.if_sample_rate;
107 }
108 if (typeof config.if_filter_bandwidth === 'number') {
109 ifChainConfig.if_filter_bandwidth = config.if_filter_bandwidth;
110 }
111 if (typeof config.if_filter_transition_width === 'number') {
112 ifChainConfig.if_filter_transition_width =
113 config.if_filter_transition_width;
114 }
115 return ifChainConfig;
116}
117
118export function createDechirpReference(
119 source: DechirpReferenceSource,
120 currentReference?: unknown
121): DechirpReference {
122 const current = isRecord(currentReference) ? currentReference : {};
123 switch (source) {
124 case 'attached':
125 return { source };
126 case 'transmitter':
127 return {
128 source,
129 ...(typeof current.transmitter_name === 'string'
130 ? { transmitter_name: current.transmitter_name }
131 : {}),
132 };
133 case 'custom':
134 return {
135 source,
136 ...(typeof current.waveform_name === 'string'
137 ? { waveform_name: current.waveform_name }
138 : {}),
139 };
140 }
141}
142
143export function normalizeFmcwModeConfig(
144 config: unknown
145): ReceiverFmcwModeConfig {
146 if (!isRecord(config)) {
147 return {};
148 }
149
150 const dechirpMode = getDechirpMode(config);
151 if (dechirpMode === 'none') {
152 return {};
153 }
154
155 const reference = createDechirpReference(
156 getDechirpReferenceSource(config.dechirp_reference),
157 config.dechirp_reference
158 );
159
160 return {
161 dechirp_mode: dechirpMode,
162 dechirp_reference: reference,
163 ...getFmcwIfChainConfig(config),
164 };
165}
166
167export function createFmcwModeConfig(
168 dechirpMode: DechirpMode,
169 currentConfig?: unknown
170): ReceiverFmcwModeConfig {
171 if (dechirpMode === 'none') {
172 return {};
173 }
174
175 const current = normalizeFmcwModeConfig(currentConfig);
176 return {
177 dechirp_mode: dechirpMode,
178 dechirp_reference:
179 current.dechirp_reference ?? createDechirpReference('attached'),
180 ...getFmcwIfChainConfig(current),
181 };
182}