DynExp
Highly flexible laboratory automation for dynamically changing experiments.
Loading...
Searching...
No Matches
SignalDesigner.cpp
Go to the documentation of this file.
1// This file is part of DynExp.
2
3#include "stdafx.h"
4#include "moc_SignalDesigner.cpp"
5#include "SignalDesigner.h"
6
7namespace DynExpModule
8{
10 : QModuleWidget(Owner, parent), PulsesContextMenu(new QMenu(this)),
11 AddPulseAction(nullptr), RemovePulseAction(nullptr), ClearPulsesAction(nullptr)
12 {
13 ui.setupUi(this);
14
15 ui.TWPulses->setItemPrototype(new Util::NumericSortingTableWidgetItem());
16 ui.TWPulses->setItemDelegateForColumn(0, new Util::NumericOnlyItemDelegate(this, 0));
17 connect(ui.TWPulses, &QTableWidget::itemChanged, this, &SignalDesignerWidget::OnPulsesChanged);
18
19 AddPulseAction = PulsesContextMenu->addAction("&New Pulse", this, &SignalDesignerWidget::OnAddPulse, QKeySequence(Qt::Key_N));
20 addAction(AddPulseAction); // for shortcuts
21 RemovePulseAction = PulsesContextMenu->addAction("&Delete selected Pulse(s)", this, &SignalDesignerWidget::OnRemovePulse, QKeySequence(Qt::Key_Delete));
22 addAction(RemovePulseAction); // for shortcuts
24 }
25
27 {
28 bool Changed = PulsesChanged;
29 PulsesChanged = false;
30
31 return Changed;
32 }
33
39 {
40 if (ModuleData->IsUIInitialized())
41 return;
42
43 // Get caps from instrument.
44 ModuleData->MinFuncDesc = ModuleData->GetFuncGen()->GetMinCaps();
45 ModuleData->MaxFuncDesc = ModuleData->GetFuncGen()->GetMaxCaps();
46 ModuleData->DefaultFuncDesc = ModuleData->GetFuncGen()->GetParamDefaults();
47 ModuleData->WaveformCaps = ModuleData->GetFuncGen()->GetWaveformCaps();
48 ModuleData->TriggerCaps = ModuleData->GetFuncGen()->GetTriggerCaps();
49
50 // Read current configuration from instrument.
51 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::FunctionGenerator>(ModuleData->GetFuncGen()->GetInstrumentData());
52
53 if (InstrData->GetCurrentWaveformType() != DynExpInstr::FunctionGeneratorDefs::WaveformTypes::None)
54 {
55 ModuleData->CurrentWaveform = InstrData->GetCurrentWaveformType();
56 ModuleData->CurrentFrequencyInHz = InstrData->GetCurrentFrequencyInHz();
57 ModuleData->CurrentAmplitude = InstrData->GetCurrentAmplitude();
58 ModuleData->CurrentOffset = InstrData->GetCurrentOffset();
59 ModuleData->CurrentPhaseInRad = InstrData->GetCurrentPhaseInRad();
60 ModuleData->CurrentDutyCycle = InstrData->GetCurrentDutyCycle();
61 ModuleData->CurrentPulses = InstrData->GetCurrentPulses();
62 }
63 else
64 {
65 if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Sine) ||
67 ModuleData->GetFuncGen()->GetValueUnit() != DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel))
69 else if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Rect) ||
72 else if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Ramp))
74 else if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Pulse))
76
77 ModuleData->CurrentFrequencyInHz = ModuleData->DefaultFuncDesc.FrequencyInHz;
78 ModuleData->CurrentAmplitude = ModuleData->DefaultFuncDesc.Amplitude;
79 ModuleData->CurrentOffset = ModuleData->DefaultFuncDesc.Offset;
80 ModuleData->CurrentPhaseInRad = 0;
81 ModuleData->CurrentDutyCycle = .5;
82 ModuleData->CurrentPulses.Reset();
83 }
84
85 ModuleData->CurrentTriggerMode = InstrData->GetCurrentTriggerMode();
86 ModuleData->CurrentTriggerEdge = InstrData->GetCurrentTriggerEdge();
87 ModuleData->CurrentAutostart = InstrData->GetShouldAutostart();
88
89 // Block value changed signals while initializing UI.
90 const QSignalBlocker SBFrequencyInHzBlocker(ui.SBFrequencyInHz);
91 const QSignalBlocker SBAmplitudeBlocker(ui.SBAmplitude);
92 const QSignalBlocker SBYOffsetBlocker(ui.SBYOffset);
93 const QSignalBlocker SBPhaseInDegreeBlocker(ui.SBPhaseInDegree);
94 const QSignalBlocker SBDutyCycleBlocker(ui.SBDutyCycle);
95 const QSignalBlocker CBAutostartBlocker(ui.CBAutostart);
96 const QSignalBlocker TWPulsesBlocker(ui.TWPulses);
97 const QSignalBlocker CBTriggerModeBlocker(ui.CBTriggerMode);
98 const QSignalBlocker CBTriggerEdgeBlocker(ui.CBTriggerEdge);
99 const QSignalBlocker BPersistBlocker(ui.BPersist);
100
101 // Adjust available waveform types and select appropriate one.
102 auto SigTypeListView = qobject_cast<QListView*>(ui.CBSignalType->view());
103 {
104 const QSignalBlocker CBSignalTypeBlocker(ui.CBSignalType);
105
106 // TODO: This solution is quick & dirty...
107 SigTypeListView->setRowHidden(ui.CBSignalType->findText("Sine", Qt::MatchFlag::MatchContains),
108 (!ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Sine) &&
110 || ModuleData->GetFuncGen()->GetValueUnit() == DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel);
111 SigTypeListView->setRowHidden(ui.CBSignalType->findText("Rect", Qt::MatchFlag::MatchContains),
112 !ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Rect) &&
114 SigTypeListView->setRowHidden(ui.CBSignalType->findText("Ramp", Qt::MatchFlag::MatchContains),
115 (!ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Ramp) &&
117 || ModuleData->GetFuncGen()->GetValueUnit() == DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel);
118 SigTypeListView->setRowHidden(ui.CBSignalType->findText("Pulse", Qt::MatchFlag::MatchContains),
119 !ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Pulse) &&
121 } // CBSignalTypeBlocker destroyed here.
122
123 int CBSignalTypeIndex = -1;
124 if (ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Sine &&
125 !SigTypeListView->isRowHidden(ui.CBSignalType->findText("Sine", Qt::MatchFlag::MatchContains)))
126 CBSignalTypeIndex = ui.CBSignalType->findText("Sine", Qt::MatchFlag::MatchContains);
127 else if (ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Rect &&
128 !SigTypeListView->isRowHidden(ui.CBSignalType->findText("Rect", Qt::MatchFlag::MatchContains)))
129 CBSignalTypeIndex = ui.CBSignalType->findText("Rect", Qt::MatchFlag::MatchContains);
130 else if (ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Ramp &&
131 !SigTypeListView->isRowHidden(ui.CBSignalType->findText("Ramp", Qt::MatchFlag::MatchContains)))
132 CBSignalTypeIndex = ui.CBSignalType->findText("Ramp", Qt::MatchFlag::MatchContains);
133 else if (ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Pulse &&
134 !SigTypeListView->isRowHidden(ui.CBSignalType->findText("Pulse", Qt::MatchFlag::MatchContains)))
135 CBSignalTypeIndex = ui.CBSignalType->findText("Pulse", Qt::MatchFlag::MatchContains);
136
137 const auto RowHiddenFunc = [SigTypeListView](int i) { return SigTypeListView->isRowHidden(i); };
138 const auto SignalTypeIndexRange = std::views::iota(0, ui.CBSignalType->count());
139 if (std::ranges::all_of(SignalTypeIndexRange, RowHiddenFunc))
140 for (auto* Widget : this->findChildren<QWidget*>())
141 Widget->setEnabled(false);
142 else if (CBSignalTypeIndex < 0)
143 {
144 auto FirstAvailable = std::ranges::find_if(SignalTypeIndexRange, RowHiddenFunc);
145
146 if (FirstAvailable != SignalTypeIndexRange.end())
147 {
148 // CBSignalType is intended to emit QComboBox::currentTextChanged() here.
149 ui.CBSignalType->setCurrentIndex(*FirstAvailable);
150 emit ui.CBSignalType->currentTextChanged(ui.CBSignalType->currentText());
151 }
152 }
153 else
154 {
155 const QSignalBlocker CBSignalTypeBlocker(ui.CBSignalType);
156 ui.CBSignalType->setCurrentIndex(CBSignalTypeIndex);
157 }
158
159 // Frequency
160 ui.SBFrequencyInHz->setMinimum(ModuleData->MinFuncDesc.FrequencyInHz);
161 ui.SBFrequencyInHz->setMaximum(ModuleData->MaxFuncDesc.FrequencyInHz);
162 ui.SBFrequencyInHz->setValue(ModuleData->CurrentFrequencyInHz);
163
164 // Amplitude
165 if (ModuleData->GetFuncGen()->GetValueUnit() != DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel)
166 {
167 ui.SBAmplitude->setMinimum(ModuleData->MinFuncDesc.Amplitude);
168 ui.SBAmplitude->setMaximum(ModuleData->MaxFuncDesc.Amplitude);
169 ui.SBAmplitude->setValue(ModuleData->CurrentAmplitude);
170 ui.SBAmplitude->setSuffix(QString(" ") + ModuleData->GetFuncGen()->GetValueUnitStr());
171 }
172
173 // Offset
174 if ((ModuleData->MinFuncDesc.Offset == 0 && ModuleData->MaxFuncDesc.Offset == 0)
175 || ModuleData->GetFuncGen()->GetValueUnit() == DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel)
176 {
177 ui.LYOffset->setVisible(false);
178 ui.SBYOffset->setVisible(false);
179 }
180 else
181 {
182 ui.LYOffset->setVisible(true);
183 ui.SBYOffset->setVisible(true);
184 ui.SBYOffset->setMinimum(ModuleData->MinFuncDesc.Offset);
185 ui.SBYOffset->setMaximum(ModuleData->MaxFuncDesc.Offset);
186 ui.SBYOffset->setValue(ModuleData->CurrentOffset);
187 ui.SBYOffset->setSuffix(QString(" ") + ModuleData->GetFuncGen()->GetValueUnitStr());
188 }
189
190 // Phase
191 if (ModuleData->GetFuncGen()->IsPhaseAdjustable())
192 ui.SBPhaseInDegree->setValue(ModuleData->CurrentPhaseInRad / std::numbers::pi * 180.0);
193
194 // Duty cycle
195 ui.SBDutyCycle->setValue(ModuleData->CurrentDutyCycle * 100.0);
196
197 // Pulses
198 ui.TWPulses->clear();
199 ui.TWPulses->setRowCount(0);
200 ui.TWPulses->setHorizontalHeaderLabels({ "Time [us]", "Value [" + QString(ModuleData->GetFuncGen()->GetValueUnitStr()) + "]" });
201 auto OldDelegate = ui.TWPulses->itemDelegateForColumn(1);
202 ui.TWPulses->setItemDelegateForColumn(1, ModuleData->GetFuncGen()->GetValueUnit() == DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel ?
204 if (OldDelegate)
205 OldDelegate->deleteLater();
206 for (const auto& Pulse : ModuleData->CurrentPulses.Pulses)
207 {
208 ui.TWPulses->insertRow(ui.TWPulses->rowCount());
209 ui.TWPulses->setItem(ui.TWPulses->rowCount() - 1, 0, new Util::NumericSortingTableWidgetItem(QString::fromStdString(Util::ToStr(Pulse.first * std::micro::den))));
210 ui.TWPulses->setItem(ui.TWPulses->rowCount() - 1, 1, new Util::NumericSortingTableWidgetItem(QString::fromStdString(Util::ToStr(Pulse.second))));
211 }
212
213 // Trigger
214 // TODO: This solution is quick & dirty...
215 int CBTriggerModeIndex = 0;
217 CBTriggerModeIndex = ui.CBTriggerMode->findText("Continuous", Qt::MatchFlag::MatchContains);
219 CBTriggerModeIndex = ui.CBTriggerMode->findText("Single", Qt::MatchFlag::MatchContains);
221 CBTriggerModeIndex = ui.CBTriggerMode->findText("Step", Qt::MatchFlag::MatchContains);
223 CBTriggerModeIndex = ui.CBTriggerMode->findText("Manual", Qt::MatchFlag::MatchContains);
224 ui.CBTriggerMode->setCurrentIndex(CBTriggerModeIndex);
225
226 ui.CBTriggerEdge->setCurrentIndex(ModuleData->CurrentTriggerEdge == DynExpInstr::FunctionGeneratorDefs::TriggerDescType::TriggerEdgeType::Fall ?
227 ui.CBTriggerEdge->findText("Fall", Qt::MatchFlag::MatchContains) : ui.CBTriggerEdge->findText("Rise", Qt::MatchFlag::MatchContains));
228 ui.GBTrigger->setVisible(ModuleData->TriggerCaps.Test(DynExpInstr::FunctionGenerator::TriggerCapsType::CanConfigure));
229 ui.BForce->setVisible(ModuleData->TriggerCaps.Test(DynExpInstr::FunctionGenerator::TriggerCapsType::CanForce));
230
231 // Autostart, persist
232 ui.CBAutostart->setChecked(ModuleData->CurrentAutostart);
233 ui.BPersist->setChecked(ModuleData->CurrentPersistParameters);
234
235 ModuleData->SetUIInitialized();
236 }
237
239 {
240 PulsesContextMenu->exec(ui.TWPulses->mapToGlobal(Position));
241 }
242
244 {
245 ui.TWPulses->insertRow(ui.TWPulses->rowCount());
246 }
247
249 {
250 auto SelectedItems = ui.TWPulses->selectedItems();
251 QSet<int> SelectedRows;
252
253 // Add selected rows.
254 for (const auto Item : SelectedItems)
255 SelectedRows.insert(Item->row());
256
257 // Add empty rows.
258 for (int i = 0; i < ui.TWPulses->rowCount(); ++i)
259 if (!ui.TWPulses->item(i, 0) && !ui.TWPulses->item(i, 1))
260 SelectedRows.insert(i);
261
262 auto RowsToRemove = SelectedRows.values();
263
264 // Remove bottommost rows first to not change the order.
265 std::sort(RowsToRemove.begin(), RowsToRemove.end(), std::greater{});
266 for (const auto Row : RowsToRemove)
267 ui.TWPulses->removeRow(Row);
268 }
269
271 {
272 ui.TWPulses->clearContents();
273 ui.TWPulses->setRowCount(0);
274 }
275
277 {
278 ui.TWPulses->sortItems(0, Qt::SortOrder::AscendingOrder);
279
280 std::vector<double> PulseStarts, PulseAmplitudes;
281 for (int i = 0; i < ui.TWPulses->rowCount(); ++i)
282 {
283 bool ok = true;
284 if (!ui.TWPulses->item(i, 0))
285 continue;
286 double Start = Util::GetDefaultQtLocale().toDouble(ui.TWPulses->item(i, 0)->text(), &ok);
287 if (!ok)
288 continue;
289
290 if (!ui.TWPulses->item(i, 1))
291 continue;
292 double Amplitude = Util::GetDefaultQtLocale().toDouble(ui.TWPulses->item(i, 1)->text(), &ok);
293 if (!ok)
294 continue;
295
296 PulseStarts.push_back(Start / std::micro::den);
297 PulseAmplitudes.push_back(Amplitude);
298 }
299
300 Pulses = { PulseStarts, PulseAmplitudes };
301 PulsesChanged = true;
302 }
303
305 {
306 // Update CurrentSourceIndex first since GetFuncGen() depends on it.
307 CurrentFuncGenIndex = Index;
308
309 UIInitialized = false;
310 }
311
316
342
344 {
345 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance.ModuleDataGetter());
346 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::FunctionGenerator>(ModuleData->GetFuncGen()->GetInstrumentData());
347 ModuleData->CurrentStreamSize = InstrData->GetSampleStream()->GetStreamSizeWrite();
348
350 }
351
355
356 std::unique_ptr<DynExp::QModuleWidget> SignalDesigner::MakeUIWidget()
357 {
358 auto Widget = std::make_unique<SignalDesignerWidget>(*this);
359
360 Connect(Widget->GetUI().CBSource, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SignalDesigner::OnSourceChanged);
361 Connect(Widget->GetUI().CBSignalType, &QComboBox::currentTextChanged, this, &SignalDesigner::OnSignalTypeChanged);
362 Connect(Widget->GetUI().SBStreamSize, QOverload<int>::of(&QSpinBox::valueChanged), this, &SignalDesigner::OnStreamSizeChanged);
363 Connect(Widget->GetUI().PBResetStreamSize, &QPushButton::clicked, this, &SignalDesigner::OnResetStreamSize);
364 Connect(Widget->GetUI().SBFrequencyInHz, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnFrequencyChanged);
365 Connect(Widget->GetUI().SBPhaseInDegree, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnPhaseChanged);
366 Connect(Widget->GetUI().SBAmplitude, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnAmplitudeChanged);
367 Connect(Widget->GetUI().SBYOffset, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnOffsetChanged);
368 Connect(Widget->GetUI().SBDutyCycle, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnDutyCycleChanged);
369 Connect(Widget->GetUI().CBTriggerMode, &QComboBox::currentTextChanged, this, &SignalDesigner::OnTriggerModeChanged);
370 Connect(Widget->GetUI().CBTriggerEdge, &QComboBox::currentTextChanged, this, &SignalDesigner::OnTriggerEdgeChanged);
371 Connect(Widget->GetUI().CBAutostart, &QCheckBox::stateChanged, this, &SignalDesigner::OnAutostartChanged);
372 Connect(Widget->GetUI().BPersist, &QPushButton::clicked, this, &SignalDesigner::OnPersistParametersClicked);
373 Connect(Widget->GetUI().BStart, &QPushButton::clicked, this, &SignalDesigner::OnStart);
374 Connect(Widget->GetUI().BStop, &QPushButton::clicked, this, &SignalDesigner::OnStop);
375 Connect(Widget->GetUI().BForce, &QPushButton::clicked, this, &SignalDesigner::OnForceTrigger);
376
377 return Widget;
378 }
379
380 void SignalDesigner::UpdateUIChild(const ModuleBase::ModuleDataGetterType& ModuleDataGetter)
381 {
382 auto Widget = GetWidget<SignalDesignerWidget>();
383 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(ModuleDataGetter());
384
385 if (!Widget->GetUI().CBSource->count())
386 {
387 const QSignalBlocker CBSourceBlocker(Widget->GetUI().CBSource);
388
389 for (const auto& FuncGenLabel : ModuleData->GetFuncGenLabels())
390 Widget->GetUI().CBSource->addItem(QIcon(ModuleData->GetFuncGenIconPath().data()), QString::fromStdString(FuncGenLabel));
391
392 Widget->GetUI().CBSource->setCurrentIndex(0);
393 Widget->GetUI().CBSource->setEnabled(Widget->GetUI().CBSource->count() > 1);
394 }
395
396 Widget->InitializeUI(ModuleData);
397
398 if (!Widget->GetUI().SBStreamSize->hasFocus() && ModuleData->CurrentStreamSize <= std::numeric_limits<int>::max())
399 {
400 const QSignalBlocker SBStreamSizeBlocker(Widget->GetUI().SBStreamSize);
401 Widget->GetUI().SBStreamSize->setValue(static_cast<int>(ModuleData->CurrentStreamSize));
402 }
403
404 // Layout changes involving CurrentWaveform come here.
405 Widget->GetUI().LFrequency->setVisible(ModuleData->CurrentWaveform != DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Pulse);
406 Widget->GetUI().SBFrequencyInHz->setVisible(ModuleData->CurrentWaveform != DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Pulse);
407 Widget->GetUI().LPhaseInDegree->setVisible(ModuleData->GetFuncGen()->IsPhaseAdjustable() &&
409 Widget->GetUI().SBPhaseInDegree->setVisible(ModuleData->GetFuncGen()->IsPhaseAdjustable() &&
411 Widget->GetUI().LAmplitude->setVisible(ModuleData->GetFuncGen()->GetValueUnit() != DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel &&
413 Widget->GetUI().SBAmplitude->setVisible(ModuleData->GetFuncGen()->GetValueUnit() != DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel &&
415 Widget->GetUI().LDutyCycle->setVisible(ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Rect ||
417 Widget->GetUI().SBDutyCycle->setVisible(ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Rect ||
419 Widget->GetUI().LDutyCycle->setText(ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Ramp ?
420 "Rise/fall ratio" : "Duty cycle");
421 Widget->GetUI().TWPulses->setVisible(ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Pulse);
422
423 if (Widget->HavePulsesChanged())
424 {
425 ModuleData->CurrentPulses = Widget->GetPulses();
427 }
428 }
429
431 {
432 ModuleData->GetFuncGen()->Clear();
433
435 ModuleData->GetFuncGen()->SetSineFunction({ ModuleData->CurrentFrequencyInHz,
436 ModuleData->CurrentAmplitude, ModuleData->CurrentOffset, ModuleData->CurrentPhaseInRad },
437 ModuleData->CurrentPersistParameters, ModuleData->CurrentAutostart || Autostart);
439 ModuleData->GetFuncGen()->SetRectFunction({ ModuleData->CurrentFrequencyInHz,
440 ModuleData->CurrentAmplitude, ModuleData->CurrentOffset, ModuleData->CurrentPhaseInRad, ModuleData->CurrentDutyCycle },
441 ModuleData->CurrentPersistParameters, ModuleData->CurrentAutostart || Autostart);
443 ModuleData->GetFuncGen()->SetRampFunction({ ModuleData->CurrentFrequencyInHz,
444 ModuleData->CurrentAmplitude, ModuleData->CurrentOffset, ModuleData->CurrentPhaseInRad, ModuleData->CurrentDutyCycle },
445 ModuleData->CurrentPersistParameters, ModuleData->CurrentAutostart || Autostart);
447 {
448 ModuleData->CurrentPulses.Offset = ModuleData->CurrentOffset;
449 ModuleData->GetFuncGen()->SetPulseFunction(ModuleData->CurrentPulses,
450 ModuleData->CurrentPersistParameters, ModuleData->CurrentAutostart || Autostart);
451 }
452 }
453
455 {
456 auto ModuleParams = DynExp::dynamic_Params_cast<SignalDesigner>(Instance->ParamsGetter());
457 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
458
459 ModuleData->LockFunctionGenerators(Instance, ModuleParams->FunctionGenerator);
460 }
461
463 {
464 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
465
466 ModuleData->UnlockFunctionGenerators(Instance);
467 }
468
470 {
471 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
472
473 ModuleData->SetCurrentFuncGenIndex(Index);
474 }
475
477 {
478 // TODO: This solution is quick & dirty...
479 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
480 if (Text.contains("Sine", Qt::CaseSensitivity::CaseInsensitive))
482 else if (Text.contains("Ramp", Qt::CaseSensitivity::CaseInsensitive))
484 else if (Text.contains("Rect", Qt::CaseSensitivity::CaseInsensitive))
486 else if (Text.contains("Pulse", Qt::CaseSensitivity::CaseInsensitive))
488 else
490
492 }
493
495 {
496 if (Value < 1)
497 return;
498
499 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
500 ModuleData->GetFuncGen()->SetStreamSize(Value);
501
503 }
504
506 {
507 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
508 ModuleData->GetFuncGen()->ResetStreamSize();
509
511 }
512
514 {
515 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
516 ModuleData->CurrentFrequencyInHz = Value;
517
519 }
520
521 void SignalDesigner::OnPhaseChanged(DynExp::ModuleInstance* Instance, double Value) const
522 {
523 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
524 ModuleData->CurrentPhaseInRad = Value / 180.0 * std::numbers::pi;
525
527 }
528
530 {
531 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
532 ModuleData->CurrentAmplitude = Value;
533
535 }
536
537 void SignalDesigner::OnOffsetChanged(DynExp::ModuleInstance* Instance, double Value) const
538 {
539 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
540 ModuleData->CurrentOffset = Value;
541
543 }
544
546 {
547 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
548 ModuleData->CurrentDutyCycle = Value / 100.0;
549
551 }
552
554 {
555 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
556
558 }
559
561 {
562 // TODO: This solution is quick & dirty...
563 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
564 if (Text.contains("Single", Qt::CaseSensitivity::CaseInsensitive))
566 else if (Text.contains("Step", Qt::CaseSensitivity::CaseInsensitive))
568 else if (Text.contains("Manual", Qt::CaseSensitivity::CaseInsensitive))
570 else
572
573 ModuleData->GetFuncGen()->SetTrigger({ ModuleData->CurrentTriggerMode, ModuleData->CurrentTriggerEdge }, ModuleData->CurrentPersistParameters);
574 }
575
577 {
578 // TODO: This solution is quick & dirty...
579 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
580 if (Text.contains("Fall", Qt::CaseSensitivity::CaseInsensitive))
582 else
584
585 ModuleData->GetFuncGen()->SetTrigger({ ModuleData->CurrentTriggerMode, ModuleData->CurrentTriggerEdge }, ModuleData->CurrentPersistParameters);
586 }
587
589 {
590 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
591 ModuleData->CurrentAutostart = Value == Qt::CheckState::Checked;
592
594 }
595
597 {
598 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
599 ModuleData->CurrentPersistParameters = Value;
600
602 }
603
605 {
606 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
607
609 }
610
612 {
613 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
614
615 ModuleData->GetFuncGen()->Stop();
616 }
617
619 {
620 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
621
622 ModuleData->GetFuncGen()->ForceTrigger();
623 }
624}
Implementation of a module to design waveforms and to store them in data streams of function generato...
@ LogicLevel
Logic level (TTL) units (1 or 0)
@ CanForce
Triggering can be forced (executed by the software).
@ CanConfigure
Trigger settings can be adjusted by the software.
DynExpInstr::FunctionGeneratorDefs::PulsesDescType CurrentPulses
DynExpInstr::FunctionGeneratorDefs::FunctionDescType MaxFuncDesc
DynExpInstr::FunctionGeneratorDefs::TriggerDescType::TriggerEdgeType CurrentTriggerEdge
DynExpInstr::FunctionGeneratorDefs::TriggerDescType::TriggerModeType CurrentTriggerMode
Util::FeatureTester< DynExpInstr::FunctionGenerator::TriggerCapsType > TriggerCaps
DynExpInstr::FunctionGeneratorDefs::WaveformTypes CurrentWaveform
DynExpInstr::FunctionGeneratorDefs::FunctionDescType MinFuncDesc
DynExpInstr::FunctionGeneratorDefs::FunctionDescType DefaultFuncDesc
Util::FeatureTester< DynExpInstr::FunctionGenerator::WaveformCapsType > WaveformCaps
void ResetImpl(dispatch_tag< QModuleDataBase >) override final
SignalDesignerWidget(SignalDesigner &Owner, QModuleWidget *parent=nullptr)
DynExpInstr::FunctionGeneratorDefs::PulsesDescType Pulses
void OnPulsesContextMenuRequested(const QPoint &Position)
void InitializeUI(Util::SynchronizedPointer< SignalDesignerData > &ModuleData)
Layout changes not involving ModuleData->CurrentWaveform come here.
void OnPulsesChanged(QTableWidgetItem *)
void OnPhaseChanged(DynExp::ModuleInstance *Instance, double Value) const
void UpdateUIChild(const ModuleBase::ModuleDataGetterType &ModuleDataGetter) override final
void OnForceTrigger(DynExp::ModuleInstance *Instance, bool) const
void OnTriggerModeChanged(DynExp::ModuleInstance *Instance, QString Text) const
void OnSourceChanged(DynExp::ModuleInstance *Instance, int Index) const
void OnSignalTypeChanged(DynExp::ModuleInstance *Instance, QString Text) const
void OnOffsetChanged(DynExp::ModuleInstance *Instance, double Value) const
Util::DynExpErrorCodes::DynExpErrorCodes ModuleMainLoop(DynExp::ModuleInstance &Instance) override final
Module main loop. The function is executed periodically by the module thread. Also refer to GetMainLo...
void OnTriggerEdgeChanged(DynExp::ModuleInstance *Instance, QString Text) const
void OnExit(DynExp::ModuleInstance *Instance) const override final
This event is triggered right before the module thread terminates (not due to an exception,...
void ResetImpl(dispatch_tag< QModuleBase >) override final
void OnResetStreamSize(DynExp::ModuleInstance *Instance, bool) const
std::unique_ptr< DynExp::QModuleWidget > MakeUIWidget() override final
Used by InitUI() as a factory function for the module's user interface widget. Create the widget here...
void OnInit(DynExp::ModuleInstance *Instance) const override final
This event is triggered right before the module thread starts. Override it to lock instruments this m...
void OnAmplitudeChanged(DynExp::ModuleInstance *Instance, double Value) const
void OnPersistParametersClicked(DynExp::ModuleInstance *Instance, bool Value) const
void OnAutostartChanged(DynExp::ModuleInstance *Instance, int Value) const
void OnStreamSizeChanged(DynExp::ModuleInstance *Instance, int Value) const
void OnDutyCycleChanged(DynExp::ModuleInstance *Instance, double Value) const
void OnStop(DynExp::ModuleInstance *Instance, bool) const
void OnStart(DynExp::ModuleInstance *Instance, bool) const
void OnFrequencyChanged(DynExp::ModuleInstance *Instance, double Value) const
void OnPulsesChanged(DynExp::ModuleInstance *Instance) const
void UpdateWaveform(Util::SynchronizedPointer< SignalDesignerData > &ModuleData, bool Autostart=false) const
const std::unique_ptr< ModuleDataType > ModuleData
Module data belonging to this ModuleBase instance.
Definition Module.h:743
void MakeAndEnqueueEvent(ReceiverType *Receiver, EventType EventFuncPtr, ArgsTs &&...Args) const
Calls MakeEvent() to construct a new event and subsequently enqueues the event into the module's even...
Definition Module.h:780
Refer to ParamsBase::dispatch_tag.
Definition Module.h:189
Defines data for a thread belonging to a ModuleBase instance. Refer to RunnableInstance.
Definition Module.h:793
const ModuleBase::ModuleDataGetterType ModuleDataGetter
Getter for module's data. Refer to ModuleBase::ModuleDataGetterType.
Definition Module.h:825
Refer to ParamsBase::dispatch_tag.
Definition Object.h:2018
QModuleWidget * Widget
User interface widget belonging to the module.
Definition Module.h:1491
void Connect(SenderType *Sender, SignalType Signal, ReceiverType *Receiver, EventType Event)
Uses Qt's connect mechanism to connect a QObject's signal to a DynExp module's event....
Definition Module.h:1500
const Object::ParamsGetterType ParamsGetter
Invoke to obtain the parameters (derived from ParamsBase) of Owner.
Definition Object.h:3671
Implements a QItemDelegate which forces e.g. a QTableWidgetItem's content to be boolean (0 or 1).
Definition QtUtil.h:644
Implements a QItemDelegate which forces e.g. a QTableWidgetItem's content to be numeric (double-preci...
Definition QtUtil.h:618
Implements a QTableWidgetItem which contains numeric content such that table widget items can be nume...
Definition QtUtil.h:590
Pointer to lock a class derived from ISynchronizedPointerLockable for synchronizing between threads....
Definition Util.h:170
@ Pulse
Manually defined pulses.
@ None
Unspecified/arbitrary waveform.
DynExp's module namespace contains the implementation of DynExp modules which extend DynExp's core fu...
constexpr auto Delete
DynExpErrorCodes
DynExp's error codes
Definition Exception.h:22
std::string ToStr(const T &Value, int Precision=-1)
Converts a (numeric) value of type T to a std::string using operator<< of std::stringstream.
Definition Util.h:625
const QLocale & GetDefaultQtLocale()
Returns the default locale properties to be assigned to Qt widgets.
Definition QtUtil.cpp:12
Accumulates include statements to provide a precompiled header.
void Reset()
Removes all pulse segments from Pulses.
@ ExternStep
Advance by a single (sweep) sample after an external trigger signal has been detected.
@ Continuous
Run continuously disabling the trigger.
@ ExternSingle
Run once after an external trigger signal has been detected.