DynExp
Highly flexible laboratory automation for dynamically changing experiments.
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 
7 namespace 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) ||
66  (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::UserDefined) &&
67  ModuleData->GetFuncGen()->GetValueUnit() != DynExpInstr::DataStreamInstrumentData::UnitType::LogicLevel))
68  ModuleData->CurrentWaveform = DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Sine;
69  else if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Rect) ||
71  ModuleData->CurrentWaveform = DynExpInstr::FunctionGeneratorDefs::WaveformTypes::Rect;
72  else if (ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::Ramp))
73  ModuleData->CurrentWaveform = DynExpInstr::FunctionGeneratorDefs::WaveformTypes::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) &&
113  !ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::UserDefined));
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) &&
120  !ModuleData->WaveformCaps.Test(DynExpInstr::FunctionGenerator::WaveformCapsType::UserDefined));
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);
220  else if (ModuleData->CurrentTriggerMode == DynExpInstr::FunctionGeneratorDefs::TriggerDescType::TriggerModeType::ExternStep)
221  CBTriggerModeIndex = ui.CBTriggerMode->findText("Step", Qt::MatchFlag::MatchContains);
222  else if (ModuleData->CurrentTriggerMode == DynExpInstr::FunctionGeneratorDefs::TriggerDescType::TriggerModeType::Manual)
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 
313  {
314  Init();
315  }
316 
318  {
319  UIInitialized = false;
321 
322  MinFuncDesc = {};
323  MaxFuncDesc = {};
324  DefaultFuncDesc = {};
325  WaveformCaps = {};
326  TriggerCaps = {};
327 
330  CurrentAmplitude = 0;
331  CurrentOffset = 0;
332  CurrentPhaseInRad = 0;
333  CurrentDutyCycle = .5;
337  CurrentAutostart = false;
338  CurrentPersistParameters = false;
339 
340  CurrentStreamSize = 1;
341  }
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 
353  {
354  }
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 
513  void SignalDesigner::OnFrequencyChanged(DynExp::ModuleInstance* Instance, double Value) const
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 
529  void SignalDesigner::OnAmplitudeChanged(DynExp::ModuleInstance* Instance, double Value) const
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 
545  void SignalDesigner::OnDutyCycleChanged(DynExp::ModuleInstance* Instance, double Value) const
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))
567  ModuleData->CurrentTriggerMode = DynExpInstr::FunctionGeneratorDefs::TriggerDescType::TriggerModeType::ExternStep;
568  else if (Text.contains("Manual", Qt::CaseSensitivity::CaseInsensitive))
569  ModuleData->CurrentTriggerMode = DynExpInstr::FunctionGeneratorDefs::TriggerDescType::TriggerModeType::Manual;
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 
608  UpdateWaveform(ModuleData, true);
609  }
610 
611  void SignalDesigner::OnStop(DynExp::ModuleInstance* Instance, bool) const
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.
@ Rise
Trigger on rising edge.
@ Fall
Trigger on falling edge.
@ ExternSingle
Run once after an external trigger signal has been detected.
@ Continuous
Run continuously disabling the trigger.
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.