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 "ui_SignalDesigner.h"
6#include "SignalDesigner.h"
7
8namespace DynExpModule
9{
11 : QModuleWidget(Owner, parent),
12 ui(std::make_unique<Ui::SignalDesigner>()),
13 PulsesContextMenu(new QMenu(this)),
14 AddPulseAction(nullptr), RemovePulseAction(nullptr), ClearPulsesAction(nullptr)
15 {
16 ui->setupUi(this);
17
18 ui->TWPulses->setItemPrototype(new Util::NumericSortingTableWidgetItem());
19 ui->TWPulses->setItemDelegateForColumn(0, new Util::NumericOnlyItemDelegate(this, 0));
20 connect(ui->TWPulses, &QTableWidget::itemChanged, this, &SignalDesignerWidget::OnPulsesChanged);
21
22 AddPulseAction = PulsesContextMenu->addAction("&New Pulse", QKeySequence(Qt::Key_N), this, &SignalDesignerWidget::OnAddPulse);
23 addAction(AddPulseAction); // for shortcuts
24 RemovePulseAction = PulsesContextMenu->addAction("&Delete selected Pulse(s)", QKeySequence(Qt::Key_Delete), this, &SignalDesignerWidget::OnRemovePulse);
25 addAction(RemovePulseAction); // for shortcuts
27 }
28
30 {
31 bool Changed = PulsesChanged;
32 PulsesChanged = false;
33
34 return Changed;
35 }
36
42 {
43 if (ModuleData->IsUIInitialized())
44 return;
45
46 // Get caps from instrument.
47 ModuleData->MinFuncDesc = ModuleData->GetFuncGen()->GetMinCaps();
48 ModuleData->MaxFuncDesc = ModuleData->GetFuncGen()->GetMaxCaps();
49 ModuleData->DefaultFuncDesc = ModuleData->GetFuncGen()->GetParamDefaults();
50 ModuleData->WaveformCaps = ModuleData->GetFuncGen()->GetWaveformCaps();
51 ModuleData->TriggerCaps = ModuleData->GetFuncGen()->GetTriggerCaps();
52
53 // Read current configuration from instrument.
54 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::FunctionGenerator>(ModuleData->GetFuncGen()->GetInstrumentData());
55
56 if (InstrData->GetCurrentWaveformType() != DynExpInstr::FunctionGeneratorDefs::WaveformTypes::None)
57 {
58 ModuleData->CurrentWaveform = InstrData->GetCurrentWaveformType();
59 ModuleData->CurrentFrequencyInHz = InstrData->GetCurrentFrequencyInHz();
60 ModuleData->CurrentAmplitude = InstrData->GetCurrentAmplitude();
61 ModuleData->CurrentOffset = InstrData->GetCurrentOffset();
62 ModuleData->CurrentPhaseInRad = InstrData->GetCurrentPhaseInRad();
63 ModuleData->CurrentDutyCycle = InstrData->GetCurrentDutyCycle();
64 ModuleData->CurrentPulses = InstrData->GetCurrentPulses();
65 }
66 else
67 {
68 if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Sine) ||
70 ModuleData->GetFuncGen()->GetValueUnit() != DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel))
72 else if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Rect) ||
75 else if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Ramp))
77 else if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Pulse))
79
80 ModuleData->CurrentFrequencyInHz = ModuleData->DefaultFuncDesc.FrequencyInHz;
81 ModuleData->CurrentAmplitude = ModuleData->DefaultFuncDesc.Amplitude;
82 ModuleData->CurrentOffset = ModuleData->DefaultFuncDesc.Offset;
83 ModuleData->CurrentPhaseInRad = 0;
84 ModuleData->CurrentDutyCycle = .5;
85 ModuleData->CurrentPulses.Reset();
86 }
87
88 ModuleData->CurrentTriggerMode = InstrData->GetCurrentTriggerMode();
89 ModuleData->CurrentTriggerEdge = InstrData->GetCurrentTriggerEdge();
90 ModuleData->CurrentAutostart = InstrData->GetShouldAutostart();
91
92 // Block value changed signals while initializing UI.
93 const QSignalBlocker SBFrequencyInHzBlocker(ui->SBFrequencyInHz);
94 const QSignalBlocker SBAmplitudeBlocker(ui->SBAmplitude);
95 const QSignalBlocker SBYOffsetBlocker(ui->SBYOffset);
96 const QSignalBlocker SBPhaseInDegreeBlocker(ui->SBPhaseInDegree);
97 const QSignalBlocker SBDutyCycleBlocker(ui->SBDutyCycle);
98 const QSignalBlocker CBAutostartBlocker(ui->CBAutostart);
99 const QSignalBlocker TWPulsesBlocker(ui->TWPulses);
100 const QSignalBlocker CBTriggerModeBlocker(ui->CBTriggerMode);
101 const QSignalBlocker CBTriggerEdgeBlocker(ui->CBTriggerEdge);
102 const QSignalBlocker BPersistBlocker(ui->BPersist);
103
104 // Adjust available waveform types and select appropriate one.
105 auto SigTypeListView = qobject_cast<QListView*>(ui->CBSignalType->view());
106 {
107 const QSignalBlocker CBSignalTypeBlocker(ui->CBSignalType);
108
109 // TODO: This solution is quick & dirty...
110 SigTypeListView->setRowHidden(ui->CBSignalType->findText("Sine", Qt::MatchFlag::MatchContains),
111 (!ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Sine) &&
113 || ModuleData->GetFuncGen()->GetValueUnit() == DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel);
114 SigTypeListView->setRowHidden(ui->CBSignalType->findText("Rect", Qt::MatchFlag::MatchContains),
115 !ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Rect) &&
117 SigTypeListView->setRowHidden(ui->CBSignalType->findText("Ramp", Qt::MatchFlag::MatchContains),
118 (!ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Ramp) &&
120 || ModuleData->GetFuncGen()->GetValueUnit() == DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel);
121 SigTypeListView->setRowHidden(ui->CBSignalType->findText("Pulse", Qt::MatchFlag::MatchContains),
122 !ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Pulse) &&
124 } // CBSignalTypeBlocker destroyed here.
125
126 int CBSignalTypeIndex = -1;
127 if (ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Sine &&
128 !SigTypeListView->isRowHidden(ui->CBSignalType->findText("Sine", Qt::MatchFlag::MatchContains)))
129 CBSignalTypeIndex = ui->CBSignalType->findText("Sine", Qt::MatchFlag::MatchContains);
130 else if (ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Rect &&
131 !SigTypeListView->isRowHidden(ui->CBSignalType->findText("Rect", Qt::MatchFlag::MatchContains)))
132 CBSignalTypeIndex = ui->CBSignalType->findText("Rect", Qt::MatchFlag::MatchContains);
133 else if (ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Ramp &&
134 !SigTypeListView->isRowHidden(ui->CBSignalType->findText("Ramp", Qt::MatchFlag::MatchContains)))
135 CBSignalTypeIndex = ui->CBSignalType->findText("Ramp", Qt::MatchFlag::MatchContains);
136 else if (ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Pulse &&
137 !SigTypeListView->isRowHidden(ui->CBSignalType->findText("Pulse", Qt::MatchFlag::MatchContains)))
138 CBSignalTypeIndex = ui->CBSignalType->findText("Pulse", Qt::MatchFlag::MatchContains);
139
140 const auto RowHiddenFunc = [SigTypeListView](int i) { return SigTypeListView->isRowHidden(i); };
141 const auto SignalTypeIndexRange = std::views::iota(0, ui->CBSignalType->count());
142 if (std::ranges::all_of(SignalTypeIndexRange, RowHiddenFunc))
143 for (auto* Widget : this->findChildren<QWidget*>())
144 Widget->setEnabled(false);
145 else if (CBSignalTypeIndex < 0)
146 {
147 auto FirstAvailable = std::ranges::find_if(SignalTypeIndexRange, RowHiddenFunc);
148
149 if (FirstAvailable != SignalTypeIndexRange.end())
150 {
151 // CBSignalType is intended to emit QComboBox::currentTextChanged() here.
152 ui->CBSignalType->setCurrentIndex(*FirstAvailable);
153 emit ui->CBSignalType->currentTextChanged(ui->CBSignalType->currentText());
154 }
155 }
156 else
157 {
158 const QSignalBlocker CBSignalTypeBlocker(ui->CBSignalType);
159 ui->CBSignalType->setCurrentIndex(CBSignalTypeIndex);
160 }
161
162 // Frequency
163 ui->SBFrequencyInHz->setMinimum(ModuleData->MinFuncDesc.FrequencyInHz);
164 ui->SBFrequencyInHz->setMaximum(ModuleData->MaxFuncDesc.FrequencyInHz);
165 ui->SBFrequencyInHz->setValue(ModuleData->CurrentFrequencyInHz);
166
167 // Amplitude
168 if (ModuleData->GetFuncGen()->GetValueUnit() != DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel)
169 {
170 ui->SBAmplitude->setMinimum(ModuleData->MinFuncDesc.Amplitude);
171 ui->SBAmplitude->setMaximum(ModuleData->MaxFuncDesc.Amplitude);
172 ui->SBAmplitude->setValue(ModuleData->CurrentAmplitude);
173 ui->SBAmplitude->setSuffix(QString(" ") + ModuleData->GetFuncGen()->GetValueUnitStr());
174 }
175
176 // Offset
177 if ((ModuleData->MinFuncDesc.Offset == 0 && ModuleData->MaxFuncDesc.Offset == 0)
178 || ModuleData->GetFuncGen()->GetValueUnit() == DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel)
179 {
180 ui->LYOffset->setVisible(false);
181 ui->SBYOffset->setVisible(false);
182 }
183 else
184 {
185 ui->LYOffset->setVisible(true);
186 ui->SBYOffset->setVisible(true);
187 ui->SBYOffset->setMinimum(ModuleData->MinFuncDesc.Offset);
188 ui->SBYOffset->setMaximum(ModuleData->MaxFuncDesc.Offset);
189 ui->SBYOffset->setValue(ModuleData->CurrentOffset);
190 ui->SBYOffset->setSuffix(QString(" ") + ModuleData->GetFuncGen()->GetValueUnitStr());
191 }
192
193 // Phase
194 if (ModuleData->GetFuncGen()->IsPhaseAdjustable())
195 ui->SBPhaseInDegree->setValue(ModuleData->CurrentPhaseInRad / std::numbers::pi * 180.0);
196
197 // Duty cycle
198 ui->SBDutyCycle->setValue(ModuleData->CurrentDutyCycle * 100.0);
199
200 // Pulses
201 ui->TWPulses->clear();
202 ui->TWPulses->setRowCount(0);
203 ui->TWPulses->setHorizontalHeaderLabels({ "Time [us]", "Value [" + QString(ModuleData->GetFuncGen()->GetValueUnitStr()) + "]" });
204 auto OldDelegate = ui->TWPulses->itemDelegateForColumn(1);
205 ui->TWPulses->setItemDelegateForColumn(1, ModuleData->GetFuncGen()->GetValueUnit() == DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel ?
207 if (OldDelegate)
208 OldDelegate->deleteLater();
209 for (const auto& Pulse : ModuleData->CurrentPulses.Pulses)
210 {
211 ui->TWPulses->insertRow(ui->TWPulses->rowCount());
212 ui->TWPulses->setItem(ui->TWPulses->rowCount() - 1, 0, new Util::NumericSortingTableWidgetItem(QString::fromStdString(Util::ToStr(Pulse.first * std::micro::den))));
213 ui->TWPulses->setItem(ui->TWPulses->rowCount() - 1, 1, new Util::NumericSortingTableWidgetItem(QString::fromStdString(Util::ToStr(Pulse.second))));
214 }
215
216 // Trigger
217 // TODO: This solution is quick & dirty...
218 int CBTriggerModeIndex = 0;
220 CBTriggerModeIndex = ui->CBTriggerMode->findText("Continuous", Qt::MatchFlag::MatchContains);
222 CBTriggerModeIndex = ui->CBTriggerMode->findText("Single", Qt::MatchFlag::MatchContains);
224 CBTriggerModeIndex = ui->CBTriggerMode->findText("Step", Qt::MatchFlag::MatchContains);
226 CBTriggerModeIndex = ui->CBTriggerMode->findText("Manual", Qt::MatchFlag::MatchContains);
227 ui->CBTriggerMode->setCurrentIndex(CBTriggerModeIndex);
228
229 ui->CBTriggerEdge->setCurrentIndex(ModuleData->CurrentTriggerEdge == DynExpInstr::FunctionGeneratorDefs::TriggerDescType::TriggerEdgeType::Fall ?
230 ui->CBTriggerEdge->findText("Fall", Qt::MatchFlag::MatchContains) : ui->CBTriggerEdge->findText("Rise", Qt::MatchFlag::MatchContains));
231 ui->GBTrigger->setVisible(ModuleData->TriggerCaps.Test(DynExpInstr::FunctionGenerator::TriggerCapsType::CanConfigure));
232 ui->BForce->setVisible(ModuleData->TriggerCaps.Test(DynExpInstr::FunctionGenerator::TriggerCapsType::CanForce));
233
234 // Autostart, persist
235 ui->CBAutostart->setChecked(ModuleData->CurrentAutostart);
236 ui->BPersist->setChecked(ModuleData->CurrentPersistParameters);
237
238 ModuleData->SetUIInitialized();
239 }
240
242 {
243 PulsesContextMenu->exec(ui->TWPulses->mapToGlobal(Position));
244 }
245
247 {
248 ui->TWPulses->insertRow(ui->TWPulses->rowCount());
249 }
250
252 {
253 auto SelectedItems = ui->TWPulses->selectedItems();
254 QSet<int> SelectedRows;
255
256 // Add selected rows.
257 for (const auto Item : SelectedItems)
258 SelectedRows.insert(Item->row());
259
260 // Add empty rows.
261 for (int i = 0; i < ui->TWPulses->rowCount(); ++i)
262 if (!ui->TWPulses->item(i, 0) && !ui->TWPulses->item(i, 1))
263 SelectedRows.insert(i);
264
265 auto RowsToRemove = SelectedRows.values();
266
267 // Remove bottommost rows first to not change the order.
268 std::sort(RowsToRemove.begin(), RowsToRemove.end(), std::greater{});
269 for (const auto Row : RowsToRemove)
270 ui->TWPulses->removeRow(Row);
271 }
272
274 {
275 ui->TWPulses->clearContents();
276 ui->TWPulses->setRowCount(0);
277 }
278
280 {
281 ui->TWPulses->sortItems(0, Qt::SortOrder::AscendingOrder);
282
283 std::vector<double> PulseStarts, PulseAmplitudes;
284 for (int i = 0; i < ui->TWPulses->rowCount(); ++i)
285 {
286 bool ok = true;
287 if (!ui->TWPulses->item(i, 0))
288 continue;
289 double Start = Util::GetDefaultQtLocale().toDouble(ui->TWPulses->item(i, 0)->text(), &ok);
290 if (!ok)
291 continue;
292
293 if (!ui->TWPulses->item(i, 1))
294 continue;
295 double Amplitude = Util::GetDefaultQtLocale().toDouble(ui->TWPulses->item(i, 1)->text(), &ok);
296 if (!ok)
297 continue;
298
299 PulseStarts.push_back(Start / std::micro::den);
300 PulseAmplitudes.push_back(Amplitude);
301 }
302
303 Pulses = { PulseStarts, PulseAmplitudes };
304 PulsesChanged = true;
305 }
306
308 {
309 // Update CurrentSourceIndex first since GetFuncGen() depends on it.
310 CurrentFuncGenIndex = Index;
311
312 UIInitialized = false;
313 }
314
319
345
347 {
348 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance.ModuleDataGetter());
349 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::FunctionGenerator>(ModuleData->GetFuncGen()->GetInstrumentData());
350 ModuleData->CurrentStreamSize = InstrData->GetSampleStream()->GetStreamSizeWrite();
351
353 }
354
358
359 std::unique_ptr<DynExp::QModuleWidget> SignalDesigner::MakeUIWidget()
360 {
361 auto Widget = std::make_unique<SignalDesignerWidget>(*this);
362
363 Connect(Widget->GetUI()->CBSource, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SignalDesigner::OnSourceChanged);
364 Connect(Widget->GetUI()->CBSignalType, &QComboBox::currentTextChanged, this, &SignalDesigner::OnSignalTypeChanged);
365 Connect(Widget->GetUI()->SBStreamSize, QOverload<int>::of(&QSpinBox::valueChanged), this, &SignalDesigner::OnStreamSizeChanged);
366 Connect(Widget->GetUI()->PBResetStreamSize, &QPushButton::clicked, this, &SignalDesigner::OnResetStreamSize);
367 Connect(Widget->GetUI()->SBFrequencyInHz, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnFrequencyChanged);
368 Connect(Widget->GetUI()->SBPhaseInDegree, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnPhaseChanged);
369 Connect(Widget->GetUI()->SBAmplitude, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnAmplitudeChanged);
370 Connect(Widget->GetUI()->SBYOffset, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnOffsetChanged);
371 Connect(Widget->GetUI()->SBDutyCycle, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &SignalDesigner::OnDutyCycleChanged);
372 Connect(Widget->GetUI()->CBTriggerMode, &QComboBox::currentTextChanged, this, &SignalDesigner::OnTriggerModeChanged);
373 Connect(Widget->GetUI()->CBTriggerEdge, &QComboBox::currentTextChanged, this, &SignalDesigner::OnTriggerEdgeChanged);
374 Connect(Widget->GetUI()->CBAutostart, &QCheckBox::checkStateChanged, this, &SignalDesigner::OnAutostartChanged);
375 Connect(Widget->GetUI()->BPersist, &QPushButton::clicked, this, &SignalDesigner::OnPersistParametersClicked);
376 Connect(Widget->GetUI()->BStart, &QPushButton::clicked, this, &SignalDesigner::OnStart);
377 Connect(Widget->GetUI()->BStop, &QPushButton::clicked, this, &SignalDesigner::OnStop);
378 Connect(Widget->GetUI()->BForce, &QPushButton::clicked, this, &SignalDesigner::OnForceTrigger);
379
380 return Widget;
381 }
382
383 void SignalDesigner::UpdateUIChild(const ModuleBase::ModuleDataGetterType& ModuleDataGetter)
384 {
385 auto Widget = GetWidget<SignalDesignerWidget>();
386 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(ModuleDataGetter());
387
388 if (!Widget->GetUI()->CBSource->count())
389 {
390 const QSignalBlocker CBSourceBlocker(Widget->GetUI()->CBSource);
391
392 for (const auto& FuncGenLabel : ModuleData->GetFuncGenLabels())
393 Widget->GetUI()->CBSource->addItem(QIcon(ModuleData->GetFuncGenIconPath().data()), QString::fromStdString(FuncGenLabel));
394
395 Widget->GetUI()->CBSource->setCurrentIndex(0);
396 Widget->GetUI()->CBSource->setEnabled(Widget->GetUI()->CBSource->count() > 1);
397 }
398
399 Widget->InitializeUI(ModuleData);
400
401 if (!Widget->GetUI()->SBStreamSize->hasFocus() && ModuleData->CurrentStreamSize <= std::numeric_limits<int>::max())
402 {
403 const QSignalBlocker SBStreamSizeBlocker(Widget->GetUI()->SBStreamSize);
404 Widget->GetUI()->SBStreamSize->setValue(static_cast<int>(ModuleData->CurrentStreamSize));
405 }
406
407 // Layout changes involving CurrentWaveform come here.
408 Widget->GetUI()->LFrequency->setVisible(ModuleData->CurrentWaveform != DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Pulse);
409 Widget->GetUI()->SBFrequencyInHz->setVisible(ModuleData->CurrentWaveform != DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Pulse);
410 Widget->GetUI()->LPhaseInDegree->setVisible(ModuleData->GetFuncGen()->IsPhaseAdjustable() &&
412 Widget->GetUI()->SBPhaseInDegree->setVisible(ModuleData->GetFuncGen()->IsPhaseAdjustable() &&
414 Widget->GetUI()->LAmplitude->setVisible(ModuleData->GetFuncGen()->GetValueUnit() != DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel &&
416 Widget->GetUI()->SBAmplitude->setVisible(ModuleData->GetFuncGen()->GetValueUnit() != DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel &&
418 Widget->GetUI()->LDutyCycle->setVisible(ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Rect ||
420 Widget->GetUI()->SBDutyCycle->setVisible(ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Rect ||
422 Widget->GetUI()->LDutyCycle->setText(ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Ramp ?
423 "Rise/fall ratio" : "Duty cycle");
424 Widget->GetUI()->TWPulses->setVisible(ModuleData->CurrentWaveform == DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Pulse);
425
426 if (Widget->HavePulsesChanged())
427 {
428 ModuleData->CurrentPulses = Widget->GetPulses();
430 }
431 }
432
434 {
435 ModuleData->GetFuncGen()->Clear();
436
438 ModuleData->GetFuncGen()->SetSineFunction({ ModuleData->CurrentFrequencyInHz,
439 ModuleData->CurrentAmplitude, ModuleData->CurrentOffset, ModuleData->CurrentPhaseInRad },
440 ModuleData->CurrentPersistParameters, ModuleData->CurrentAutostart || Autostart);
442 ModuleData->GetFuncGen()->SetRectFunction({ ModuleData->CurrentFrequencyInHz,
443 ModuleData->CurrentAmplitude, ModuleData->CurrentOffset, ModuleData->CurrentPhaseInRad, ModuleData->CurrentDutyCycle },
444 ModuleData->CurrentPersistParameters, ModuleData->CurrentAutostart || Autostart);
446 ModuleData->GetFuncGen()->SetRampFunction({ ModuleData->CurrentFrequencyInHz,
447 ModuleData->CurrentAmplitude, ModuleData->CurrentOffset, ModuleData->CurrentPhaseInRad, ModuleData->CurrentDutyCycle },
448 ModuleData->CurrentPersistParameters, ModuleData->CurrentAutostart || Autostart);
450 {
451 ModuleData->CurrentPulses.Offset = ModuleData->CurrentOffset;
452 ModuleData->GetFuncGen()->SetPulseFunction(ModuleData->CurrentPulses,
453 ModuleData->CurrentPersistParameters, ModuleData->CurrentAutostart || Autostart);
454 }
455 }
456
458 {
459 auto ModuleParams = DynExp::dynamic_Params_cast<SignalDesigner>(Instance->ParamsGetter());
460 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
461
462 ModuleData->LockFunctionGenerators(Instance, ModuleParams->FunctionGenerator);
463 }
464
466 {
467 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
468
469 ModuleData->UnlockFunctionGenerators(Instance);
470 }
471
473 {
474 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
475
476 ModuleData->SetCurrentFuncGenIndex(Index);
477 }
478
480 {
481 // TODO: This solution is quick & dirty...
482 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
483 if (Text.contains("Sine", Qt::CaseSensitivity::CaseInsensitive))
485 else if (Text.contains("Ramp", Qt::CaseSensitivity::CaseInsensitive))
487 else if (Text.contains("Rect", Qt::CaseSensitivity::CaseInsensitive))
489 else if (Text.contains("Pulse", Qt::CaseSensitivity::CaseInsensitive))
491 else
493
495 }
496
498 {
499 if (Value < 1)
500 return;
501
502 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
503 ModuleData->GetFuncGen()->SetStreamSize(Value);
504
506 }
507
509 {
510 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
511 ModuleData->GetFuncGen()->ResetStreamSize();
512
514 }
515
517 {
518 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
519 ModuleData->CurrentFrequencyInHz = Value;
520
522 }
523
524 void SignalDesigner::OnPhaseChanged(DynExp::ModuleInstance* Instance, double Value) const
525 {
526 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
527 ModuleData->CurrentPhaseInRad = Value / 180.0 * std::numbers::pi;
528
530 }
531
533 {
534 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
535 ModuleData->CurrentAmplitude = Value;
536
538 }
539
540 void SignalDesigner::OnOffsetChanged(DynExp::ModuleInstance* Instance, double Value) const
541 {
542 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
543 ModuleData->CurrentOffset = Value;
544
546 }
547
549 {
550 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
551 ModuleData->CurrentDutyCycle = Value / 100.0;
552
554 }
555
557 {
558 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
559
561 }
562
564 {
565 // TODO: This solution is quick & dirty...
566 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
567 if (Text.contains("Single", Qt::CaseSensitivity::CaseInsensitive))
569 else if (Text.contains("Step", Qt::CaseSensitivity::CaseInsensitive))
571 else if (Text.contains("Manual", Qt::CaseSensitivity::CaseInsensitive))
573 else
575
576 ModuleData->GetFuncGen()->SetTrigger({ ModuleData->CurrentTriggerMode, ModuleData->CurrentTriggerEdge }, ModuleData->CurrentPersistParameters);
577 }
578
580 {
581 // TODO: This solution is quick & dirty...
582 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
583 if (Text.contains("Fall", Qt::CaseSensitivity::CaseInsensitive))
585 else
587
588 ModuleData->GetFuncGen()->SetTrigger({ ModuleData->CurrentTriggerMode, ModuleData->CurrentTriggerEdge }, ModuleData->CurrentPersistParameters);
589 }
590
591 void SignalDesigner::OnAutostartChanged(DynExp::ModuleInstance* Instance, Qt::CheckState State) const
592 {
593 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
594 ModuleData->CurrentAutostart = State == Qt::CheckState::Checked;
595
597 }
598
600 {
601 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
602 ModuleData->CurrentPersistParameters = Value;
603
605 }
606
608 {
609 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
610
612 }
613
615 {
616 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
617
618 ModuleData->GetFuncGen()->Stop();
619 }
620
622 {
623 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalDesigner>(Instance->ModuleDataGetter());
624
625 ModuleData->GetFuncGen()->ForceTrigger();
626 }
627}
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.
std::unique_ptr< Ui::SignalDesigner > ui
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 OnAutostartChanged(DynExp::ModuleInstance *Instance, Qt::CheckState State) 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 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:788
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:827
Refer to ParamsBase::dispatch_tag.
Definition Module.h:191
Defines data for a thread belonging to a ModuleBase instance. Refer to RunnableInstance.
Definition Module.h:840
const ModuleBase::ModuleDataGetterType ModuleDataGetter
Getter for module's data. Refer to ModuleBase::ModuleDataGetterType.
Definition Module.h:872
Refer to ParamsBase::dispatch_tag.
Definition Object.h:2018
QModuleWidget * Widget
User interface widget belonging to the module.
Definition Module.h:1807
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:1816
const Object::ParamsGetterType ParamsGetter
Invoke to obtain the parameters (derived from ParamsBase) of Owner.
Definition Object.h:3710
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:688
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.