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_linear_chirp',
52 'fmcw_triangle',
53] as const;
54
55export type FmcwWaveformType = (typeof FMCW_WAVEFORM_TYPES)[number];
56
57export function isFmcwWaveformType(
58 waveformType: string | undefined
59): waveformType is FmcwWaveformType {
60 return FMCW_WAVEFORM_TYPES.includes(waveformType as FmcwWaveformType);
61}
62
63export function isRecord(value: unknown): value is Record<string, unknown> {
64 return typeof value === 'object' && value !== null && !Array.isArray(value);
65}
66
67export function isDechirpMode(value: unknown): value is DechirpMode {
68 return typeof value === 'string' && DECHIRP_MODES.has(value as DechirpMode);
69}
70
71export function isDechirpReferenceSource(
72 value: unknown
73): value is DechirpReferenceSource {
74 return (
75 typeof value === 'string' &&
76 DECHIRP_REFERENCE_SOURCES.has(value as DechirpReferenceSource)
77 );
78}
79
80export function getDechirpMode(config: unknown): DechirpMode {
81 if (!isRecord(config)) {
82 return 'none';
83 }
84 return isDechirpMode(config.dechirp_mode) ? config.dechirp_mode : 'none';
85}
86
87export function getDechirpReferenceSource(
88 reference: unknown
89): DechirpReferenceSource {
90 if (!isRecord(reference)) {
91 return 'attached';
92 }
93 return isDechirpReferenceSource(reference.source)
94 ? reference.source
95 : 'attached';
96}
97
98export function getFmcwIfChainConfig(config: unknown): FmcwIfChainConfig {
99 if (!isRecord(config)) {
100 return {};
101 }
102
103 const ifChainConfig: FmcwIfChainConfig = {};
104 if (typeof config.if_sample_rate === 'number') {
105 ifChainConfig.if_sample_rate = config.if_sample_rate;
106 }
107 if (typeof config.if_filter_bandwidth === 'number') {
108 ifChainConfig.if_filter_bandwidth = config.if_filter_bandwidth;
109 }
110 if (typeof config.if_filter_transition_width === 'number') {
111 ifChainConfig.if_filter_transition_width =
112 config.if_filter_transition_width;
113 }
114 return ifChainConfig;
115}
116
117export function createDechirpReference(
118 source: DechirpReferenceSource,
119 currentReference?: unknown
120): DechirpReference {
121 const current = isRecord(currentReference) ? currentReference : {};
122 switch (source) {
123 case 'attached':
124 return { source };
125 case 'transmitter':
126 return {
127 source,
128 ...(typeof current.transmitter_name === 'string'
129 ? { transmitter_name: current.transmitter_name }
130 : {}),
131 };
132 case 'custom':
133 return {
134 source,
135 ...(typeof current.waveform_name === 'string'
136 ? { waveform_name: current.waveform_name }
137 : {}),
138 };
139 }
140}
141
142export function normalizeFmcwModeConfig(
143 config: unknown
144): ReceiverFmcwModeConfig {
145 if (!isRecord(config)) {
146 return {};
147 }
148
149 const dechirpMode = getDechirpMode(config);
150 if (dechirpMode === 'none') {
151 return {};
152 }
153
154 const reference = createDechirpReference(
155 getDechirpReferenceSource(config.dechirp_reference),
156 config.dechirp_reference
157 );
158
159 return {
160 dechirp_mode: dechirpMode,
161 dechirp_reference: reference,
162 ...getFmcwIfChainConfig(config),
163 };
164}
165
166export function createFmcwModeConfig(
167 dechirpMode: DechirpMode,
168 currentConfig?: unknown
169): ReceiverFmcwModeConfig {
170 if (dechirpMode === 'none') {
171 return {};
172 }
173
174 const current = normalizeFmcwModeConfig(currentConfig);
175 return {
176 dechirp_mode: dechirpMode,
177 dechirp_reference:
178 current.dechirp_reference ?? createDechirpReference('attached'),
179 ...getFmcwIfChainConfig(current),
180 };
181}