FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
assetSlice.ts
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
3
4import { StateCreator } from 'zustand';
5import { defaultAntenna, defaultTiming, defaultWaveform } from '../defaults';
6import { generateSimId } from '../idUtils';
7import { cleanObject, serializeAntenna, serializeTiming } from '../serializers';
8import { enqueueFullSync, enqueueGranularSync } from '../syncQueue';
9import { Antenna, AssetActions, ScenarioStore } from '../types';
10import { buildScenarioJson } from './backendSlice';
11
12export const createAssetSlice: StateCreator<
13 ScenarioStore,
14 [['zustand/immer', never]],
15 [],
16 AssetActions
17> = (set, get) => ({
18 addWaveform: () => {
19 set((state) => {
20 const id = generateSimId('Waveform');
21 state.waveforms.push({
22 ...defaultWaveform,
23 id,
24 name: `Waveform ${state.waveforms.length + 1}`,
25 });
26 state.isDirty = true;
27 });
28 // libfers has no granular add API for Waveforms — full sync is required.
29 void enqueueFullSync(() => buildScenarioJson(get()));
30 },
31 addTiming: () => {
32 set((state) => {
33 const id = generateSimId('Timing');
34 state.timings.push({
35 ...defaultTiming,
36 id,
37 name: `Timing ${state.timings.length + 1}`,
38 });
39 state.isDirty = true;
40 });
41 // libfers has no granular add API for Timings — full sync is required.
42 void enqueueFullSync(() => buildScenarioJson(get()));
43 },
44 addAntenna: () => {
45 set((state) => {
46 const id = generateSimId('Antenna');
47 state.antennas.push({
48 ...defaultAntenna,
49 id,
50 name: `Antenna ${state.antennas.length + 1}`,
51 });
52 state.isDirty = true;
53 });
54 // libfers has no granular add API for Antennas — full sync is required.
55 void enqueueFullSync(() => buildScenarioJson(get()));
56 },
57 addNoiseEntry: (timingId) => {
58 let touched = false;
59 set((state) => {
60 const timing = state.timings.find((t) => t.id === timingId);
61 if (timing) {
62 timing.noiseEntries.push({
63 id: generateSimId('Timing'),
64 alpha: 0,
65 weight: 0,
66 });
67 state.isDirty = true;
68 touched = true;
69 }
70 });
71 if (touched) {
72 const timing = get().timings.find((t) => t.id === timingId);
73 if (timing) {
74 void enqueueGranularSync(
75 'Timing',
76 timing.id,
77 JSON.stringify(cleanObject(serializeTiming(timing)))
78 );
79 }
80 }
81 },
82 removeNoiseEntry: (timingId, entryId) => {
83 let touched = false;
84 set((state) => {
85 const timing = state.timings.find((t) => t.id === timingId);
86 if (timing) {
87 const index = timing.noiseEntries.findIndex(
88 (e) => e.id === entryId
89 );
90 if (index > -1) {
91 timing.noiseEntries.splice(index, 1);
92 state.isDirty = true;
93 touched = true;
94 }
95 }
96 });
97 if (touched) {
98 const timing = get().timings.find((t) => t.id === timingId);
99 if (timing) {
100 void enqueueGranularSync(
101 'Timing',
102 timing.id,
103 JSON.stringify(cleanObject(serializeTiming(timing)))
104 );
105 }
106 }
107 },
108 setAntennaPattern: (antennaId, newPattern) => {
109 let touched = false;
110 set((state) => {
111 const index = state.antennas.findIndex((a) => a.id === antennaId);
112 if (index === -1) return;
113 delete state.antennaPreviewErrors[antennaId];
114
115 const oldAntenna = state.antennas[index];
116 const baseAntenna = {
117 id: oldAntenna.id,
118 type: oldAntenna.type,
119 name: oldAntenna.name,
120 efficiency: oldAntenna.efficiency,
121 meshScale: oldAntenna.meshScale,
122 design_frequency: oldAntenna.design_frequency,
123 };
124
125 let newAntennaState: Antenna;
126
127 switch (newPattern) {
128 case 'isotropic':
129 newAntennaState = {
130 ...baseAntenna,
131 pattern: 'isotropic',
132 };
133 break;
134 case 'sinc':
135 newAntennaState = {
136 ...baseAntenna,
137 pattern: 'sinc',
138 alpha: 1.0,
139 beta: 1.0,
140 gamma: 2.0,
141 };
142 break;
143 case 'gaussian':
144 newAntennaState = {
145 ...baseAntenna,
146 pattern: 'gaussian',
147 azscale: 1.0,
148 elscale: 1.0,
149 };
150 break;
151 case 'squarehorn':
152 case 'parabolic':
153 newAntennaState = {
154 ...baseAntenna,
155 pattern: newPattern,
156 diameter: 0.5,
157 };
158 break;
159 case 'xml':
160 case 'file':
161 newAntennaState = {
162 ...baseAntenna,
163 pattern: newPattern,
164 filename: '',
165 };
166 break;
167 }
168 state.antennas[index] = newAntennaState;
169 state.isDirty = true;
170 touched = true;
171 });
172 if (touched) {
173 const antenna = get().antennas.find((a) => a.id === antennaId);
174 if (antenna) {
175 void enqueueGranularSync(
176 'Antenna',
177 antenna.id,
178 JSON.stringify(cleanObject(serializeAntenna(antenna)))
179 );
180 }
181 }
182 },
183});