DynExp
Highly flexible laboratory automation for dynamically changing experiments.
ODMR.cpp
Go to the documentation of this file.
1 // This file is part of DynExp.
2 
3 #include "stdafx.h"
4 #include "ODMR.h"
5 
7 {
9  {
10  Init();
11  }
12 
13  unsigned long long ODMRData::GetSweepNumberSteps() const noexcept
14  {
16  return 0;
17 
18  return std::floor(std::abs(SweepSeriesStop - SweepSeriesStart) / SweepSeriesStep) + 1;
19  }
20 
21  std::stringstream ODMRData::AssembleCSVHeader(double RFPower, double RFModulationDepth, double AuxAnalogOutValue, bool IsRFOffResonance)
22  {
23  std::stringstream CSVData;
24 
25  CSVData << std::setprecision(6);
26  CSVData << "RFPower = " << RFPower << " " << GetRFGenerator()->GetValueUnitStr() << "\n";
27  CSVData << "RFCenterFreq = " << RFCenterFreq << " Hz\n";
28  CSVData << "RFFreqSpan = " << RFFreqSpan << " Hz\n";
29  CSVData << "RFFreqSpacing = " << RFFreqSpacing << " Hz\n";
30  CSVData << "RFDwellTime = " << RFDwellTime << " s\n";
31 
32  CSVData << "RFModulation = ";
33  switch (RFModulation)
34  {
35  case RFModulationType::None: CSVData << "none\n"; break;
36  case RFModulationType::Sine: CSVData << "sine\n"; break;
37  case RFModulationType::Pulse: CSVData << "pulse\n"; break;
38  default: CSVData << "\n";
39  }
40  CSVData << "RFModulationFreq = " << RFModulationFreq << " Hz\n";
41  CSVData << "RFModulationDepth = " << RFModulationDepth << " Hz\n";
42 
43  CSVData << "ODMRSamplingRate = " << ODMRSamplingRate << " samples/s\n";
44  CSVData << "CurrentSaveIndex = " << CurrentSaveIndex << "\n";
45 
46  CSVData << "IsRFOffResonance = " << (IsRFOffResonance ? "yes" : "no") << "\n";
47  CSVData << "SensitivityEnabled = " << (SensitivityEnabled ? "yes" : "no") << "\n";
48  CSVData << "SensitivityOncePerSweep = " << (SensitivityOncePerSweep ? "yes" : "no") << "\n";
49  CSVData << "SensitivityOffResonanceEnabled = " << (SensitivityOffResonanceEnabled ? "yes" : "no") << "\n";
50  CSVData << "SensitivityResonanceFreq = " << SensitivityResonanceFreq << " Hz\n";
51  CSVData << "SensitivityOffResonanceFreq = " << SensitivityOffResonanceFreq << " Hz\n";
52  CSVData << "SensitivityResonanceSpan = " << SensitivityResonanceSpan << " Hz\n";
53  CSVData << "SensitivitySamplingRate = " << SensitivitySamplingRate << " samples/s\n";
54  CSVData << "SensitivityDuration = " << SensitivityDuration << " s\n";
55 
56  CSVData << "SensitivityAnalysisEnabled = " << (SensitivityAnalysisEnabled ? "yes" : "no") << "\n";
57  CSVData << "GyromagneticRatio = " << GyromagneticRatio << " Hz/T\n";
58 
59  CSVData << "SweepSeriesEnabled = " << (SweepSeriesEnabled ? "yes" : "no") << "\n";
60  CSVData << "SweepSeries = ";
61  switch (SweepSeries)
62  {
63  case SweepSeriesType::RFModulationDepth: CSVData << "RFModulationDepth\n"; break;
64  case SweepSeriesType::RFPower: CSVData << "RFPower\n"; break;
65  case SweepSeriesType::AnalogOut: CSVData << "AnalogOut\n"; break;
66  default: CSVData << "\n";
67  }
68  CSVData << "SweepSeriesStart = " << SweepSeriesStart << "\n";
69  CSVData << "SweepSeriesStop = " << SweepSeriesStop << "\n";
70  CSVData << "SweepSeriesStep = " << SweepSeriesStep << "\n";
71  CSVData << "SweepSeriesRetrace = " << (SweepSeriesRetrace ? "yes" : "no") << "\n";
72  CSVData << "SweepSeriesAdvanceLastValue = " << (SweepSeriesAdvanceLastValue ? "yes" : "no") << "\n";
73  CSVData << "CurrentSweepIndex = " << CurrentSweepIndex << "\n";
74  CSVData << "AuxAnalogOutValue = " << AuxAnalogOutValue << "\n";
75 
77  {
78  auto& LockinInstr = dynamic_cast<const DynExpInstr::LockinAmplifier&>(*GetSignalDetector());
79  auto LockinData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::LockinAmplifier>(GetSignalDetector()->GetInstrumentData());
80 
81  auto Sensitivity = LockinData->GetSensitivity();
82  auto TimeConstant = LockinData->GetTimeConstant();
83  unsigned int FilterOrder = LockinData->GetFilterOrder();
84 
85  CSVData << "LockinSensitivity = " << Sensitivity << " " << LockinInstr.GetSensitivityUnitString() << "\n";
86  CSVData << "LockinTimeConstant = " << TimeConstant << " s\n";
87  CSVData << "LockinFilterOrder = " << FilterOrder << "\n";
88  }
89 
90  CSVData << "HEADER_END\n";
91 
92  return CSVData;
93  }
94 
96  {
97  RFPower = -10;
98  RFAutoEnabled = true;
99  RFCenterFreq = 2.87e9;
100  RFFreqSpan = 100e6;
101  RFFreqSpacing = 10e3;
102  RFDwellTime = 10e-3;
103 
105  RFModulationFreq = 10e3;
106  RFModulationDepth = 1e6;
107 
108  ODMRSamplingRate = .1e3;
109 
111  SaveDataPath = "";
112  CurrentSaveIndex = 0;
113  AutosaveEnabled = false;
114 
115  SensitivityEnabled = false;
116  SensitivityOncePerSweep = false;
118  SensitivityResonanceFreq = 2.87e9;
123 
125  GyromagneticRatio = 28e9;
126 
127  SweepSeriesEnabled = false;
129  SweepSeriesStart = 1e3;
130  SweepSeriesStop = 10e3;
131  SweepSeriesStep = .1e3;
132  SweepSeriesRetrace = true;
134  CurrentSweepIndex = 0;
135 
139 
141  AuxAnalogOutMinValue = 0.0;
142  AuxAnalogOutMaxValue = 1.0;
143 
144  AcquisitionTime = 0.0;
145 
148 
149  Features = {};
150  }
151 
152  ODMR::ODMR::ODMR(const std::thread::id OwnerThreadID, DynExp::ParamsBasePtrType&& Params)
153  : QModuleBase(OwnerThreadID, std::move(Params)),
154  StateMachine(InitializingState, ReadyState,
155  MeasurementSeriesStepState, MeasurementSeriesInitState,
156  ODMRTraceInitState, ODMRTraceWaitState, ODMRTraceFinishState,
157  SensitivityInitState, SensitivityWaitState, SensitivityFinishState)
158  {
159  }
160 
161  std::chrono::milliseconds ODMR::ODMR::GetMainLoopDelay() const
162  {
163  auto CurrentState = StateMachine.GetCurrentState()->GetState();
164 
165  // If doing some measurement, run much faster.
166  if (CurrentState == StateType::Ready)
167  return std::chrono::milliseconds(30);
168  else
169  return std::chrono::milliseconds(2);
170  }
171 
173  {
174  try
175  {
176  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
177 
179  {
180  auto LockinData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::LockinAmplifier>(ModuleData->GetSignalDetector()->GetInstrumentData());
181 
182  ModuleData->AcquisitionTime = LockinData->GetAcquisitionTime();
183  } // LockinData unlocked here.
184 
185  StateMachine.Invoke(*this, Instance);
186 
188  }
189  catch (const Util::TimeoutException& e)
190  {
191  if (NumFailedUpdateAttempts++ >= 3)
192  Instance.GetOwner().SetWarning(e);
193  }
194 
196  }
197 
198  void ODMR::ResetImpl(dispatch_tag<QModuleBase>)
199  {
200  StateMachine.SetCurrentState(StateType::Initializing);
201 
202  NextRFPower = 0;
205 
207  }
208 
209  std::unique_ptr<DynExp::QModuleWidget> ODMR::MakeUIWidget()
210  {
211  auto Widget = std::make_unique<ODMRWidget>(*this);
212 
213  Connect(Widget->GetUI().SBRFPower, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnRFPowerChanged);
214  Connect(Widget->GetUI().CBRFAutoEnable, &QCheckBox::stateChanged, this, &ODMR::OnRFAutoEnableClicked);
215  Connect(Widget->GetUI().SBRFCenter, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnRFCenterFreqChanged);
216  Connect(Widget->GetUI().SBRFSpan, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnRFFreqSpanChanged);
217  Connect(Widget->GetUI().SBRFFreqSpacing, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnRFFreqSpacingChanged);
218  Connect(Widget->GetUI().SBRFDwellTime, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnRFDwellTimeChanged);
219 
220  Connect(Widget->GetUI().RBRFModulationTypeNone, &QRadioButton::clicked, this, &ODMR::OnRFModNoneClicked);
221  Connect(Widget->GetUI().RBRFModulationTypeSine, &QRadioButton::clicked, this, &ODMR::OnRFModSineClicked);
222  Connect(Widget->GetUI().RBRFModulationTypePulse, &QRadioButton::clicked, this, &ODMR::OnRFModPulseClicked);
223  Connect(Widget->GetUI().SBRFModulationFreq, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnRFModFreqChanged);
224  Connect(Widget->GetUI().SBRFModulationDepth, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnRFModDepthChanged);
225 
226  Connect(Widget->GetUI().SBDataAcquisitionODMRSamplingRate, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnODMRSamplingRateChanged);
227 
228  Connect(Widget->GetUI().LESaveDataPath, &QLineEdit::textChanged, this, &ODMR::OnSavePathChanged);
229  Connect(Widget->GetUI().SBSaveDataCurrentIndex, QOverload<int>::of(&QSpinBox::valueChanged), this, &ODMR::OnSaveIndexChanged);
230  Connect(Widget->GetUI().CBSaveDataEnable, &QCheckBox::stateChanged, this, &ODMR::OnAutosaveClicked);
231 
232  Connect(Widget->GetUI().CBSensitivityEnable, &QCheckBox::stateChanged, this, &ODMR::OnRecordSensitivityClicked);
233  Connect(Widget->GetUI().CBSensitivityOncePerSweep, &QCheckBox::stateChanged, this, &ODMR::OnRecordSensitivityOncePerSweepClicked);
234  Connect(Widget->GetUI().CBSensitivityOffResEnable, &QCheckBox::stateChanged, this, &ODMR::OnRecordSensitivityOffResonanceClicked);
235  Connect(Widget->GetUI().SBSensitivityFreq, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnSensitivityResonanceFreqChanged);
236  Connect(Widget->GetUI().SBSensitivityOffResFreq, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnSensitivityOffResonanceFreqChanged);
237  Connect(Widget->GetUI().SBSensitivitySpan, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnSensitivityResonanceSpanChanged);
238  Connect(Widget->GetUI().SBSensitivitySamplingRate, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnSensitivitySamplingRateChanged);
239  Connect(Widget->GetUI().SBSensitivityDuration, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnSensitivityDurationChanged);
240 
241  Connect(Widget->GetUI().GBSensitivityAnalysis, &QGroupBox::toggled, this, &ODMR::OnEnableSensitivityAnalysisClicked);
242  Connect(Widget->GetUI().SBGyromagneticRatio, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnGyromagneticRatioChanged);
243 
244  Connect(Widget->GetUI().CBParamSweepEnable, &QCheckBox::stateChanged, this, &ODMR::OnEnableSweepSeriesClicked);
245  Connect(Widget->GetUI().CBParamSweepType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ODMR::OnSweepSeriesParamChanged);
246  Connect(Widget->GetUI().SBParamSweepStart, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnSweepSeriesStartChanged);
247  Connect(Widget->GetUI().SBParamSweepStop, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnSweepSeriesStopChanged);
248  Connect(Widget->GetUI().SBParamSweepStep, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ODMR::OnSweepSeriesStepChanged);
249  Connect(Widget->GetUI().CBParamSweepRetrace, &QCheckBox::stateChanged, this, &ODMR::OnSweepSeriesRetraceClicked);
250  Connect(Widget->GetUI().CBParamSweepAdvanceLastValue, &QCheckBox::stateChanged, this, &ODMR::OnSweepSeriesAdvanceLastValueClicked);
251 
252  Connect(Widget->GetUI().BStart, &QPushButton::clicked, this, &ODMR::OnStartClicked);
253  Connect(Widget->GetUI().BStartSensitivity, &QPushButton::clicked, this, &ODMR::OnStartSensitivityClicked);
254  Connect(Widget->GetUI().BStop, &QPushButton::clicked, this, &ODMR::OnStopClicked);
255  Connect(Widget->GetUI().BRFOn, &QPushButton::clicked, this, &ODMR::OnRFOnClicked);
256  Connect(Widget->GetUI().BRFOff, &QPushButton::clicked, this, &ODMR::OnRFOffClicked);
257 
258  return Widget;
259  }
260 
261  void ODMR::UpdateUIChild(const ModuleBase::ModuleDataGetterType& ModuleDataGetter)
262  {
263  auto Widget = GetWidget<ODMRWidget>();
264  ODMRPlotType ODMRPlot;
265  SensitivityPlotType SensitivityPlot;
266 
267  {
268  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(ModuleDataGetter());
269 
270  if (StateMachine.GetCurrentState()->GetState() == StateType::Initializing)
271  return;
272 
273  Widget->InitializeUI(ModuleData);
274  Widget->SetUIState(StateMachine.GetCurrentState(), ModuleData);
275  Widget->UpdateUIData(ModuleData);
276 
277  if (ModuleData->ODMRPlot.HasChanged)
278  {
279  ODMRPlot = ModuleData->ODMRPlot;
280  ModuleData->ODMRPlot.HasChanged = false;
281  }
282  if (ModuleData->SensitivityPlot.HasChanged)
283  {
284  SensitivityPlot = ModuleData->SensitivityPlot;
285  ModuleData->SensitivityPlot.HasChanged = false;
286  }
287  } // ModuleData unlocked here (heavy plot operations follow).
288 
289  if (ODMRPlot.HasChanged)
290  {
291  Widget->UpdateODMRPlot(ODMRPlot);
292  ConnectChartWidgets(Widget->GetODMRDataSeries());
293  }
294  if (SensitivityPlot.HasChanged)
295  Widget->UpdateSensitivityPlot(SensitivityPlot);
296  }
297 
298  void ODMR::InitSweepValues(Util::SynchronizedPointer<ModuleDataType>& ModuleData)
299  {
300  NextRFPower = ModuleData->RFPower;
301  NextRFModulationDepth = ModuleData->RFModulationDepth;
302  NextAuxAnalogOutValue = (ModuleData->AuxAnalogOutMinValue <= 0 && ModuleData->AuxAnalogOutMaxValue >= 0) ? 0 : ModuleData->AuxAnalogOutMinValue;
303 
304  if (!ModuleData->SweepSeriesEnabled || !ModuleData->GetSweepNumberSteps())
305  return;
306 
307  decltype(ModuleData->CurrentSweepIndex) SweepIndex = ModuleData->CurrentSweepIndex;
308  if (ModuleData->SweepSeriesAdvanceLastValue && SweepIndex == 1)
309  SweepIndex = ModuleData->GetSweepNumberSteps() - 1;
310  else if (ModuleData->SweepSeriesAdvanceLastValue && SweepIndex > 1)
311  --SweepIndex;
312 
313  switch (ModuleData->SweepSeries)
314  {
316  NextRFModulationDepth = (ModuleData->SweepSeriesStart + SweepIndex * std::abs(ModuleData->SweepSeriesStep)) * 1e3;
317  break;
319  NextRFPower = ModuleData->SweepSeriesStart + SweepIndex * std::abs(ModuleData->SweepSeriesStep);
320  break;
322  NextAuxAnalogOutValue = ModuleData->SweepSeriesStart + SweepIndex * std::abs(ModuleData->SweepSeriesStep);
323  break;
324  }
325  }
326 
327  void ODMR::InitRFGenerator(double Frequency, bool EnableRF, Util::SynchronizedPointer<ModuleDataType>& ModuleData) const
328  {
329  ModuleData->GetRFGenerator()->SetSineFunction({ Frequency, NextRFPower, 0, 0 }, false, EnableRF);
330  ModuleData->GetRFGenerator()->SetModulation({
335  ModuleData->RFModulationFreq,
339  });
340  }
341 
342  void ODMR::SetAuxAnalogOutValue(Util::SynchronizedPointer<ModuleDataType>& ModuleData) const
343  {
345  ModuleData->GetAuxAnalogOut()->SetSync(NextAuxAnalogOutValue);
346  }
347 
348  void ODMR::WaitUntilReadyAndTrigger(Util::SynchronizedPointer<ModuleDataType>& ModuleData) const
349  {
350  // Trigger on rising edge, so set to 0 now.
351  ModuleData->GetTrigger()->SetSync(0);
352 
353  // Wait to ensure that trigger signal does not come too fast
354  std::this_thread::sleep_for(std::chrono::milliseconds(200));
355 
356  // Prepare acquisition.
357  ModuleData->GetSignalDetector()->Stop();
358  ModuleData->GetSignalDetector()->Clear();
359  ModuleData->GetSignalDetector()->Start();
360 
361  // Wait until every involved instrument is ready.
362  DynExp::WaitForInstruments(*ModuleData->GetRFGenerator(), *ModuleData->GetSignalDetector(), *ModuleData->GetTrigger());
363 
364  // Trigger RF sweep and data acquisition now.
365  ModuleData->GetTrigger()->SetSync(1);
366  }
367 
368  void ODMR::ConnectChartWidgets(QLineSeries* ODMRLineSeries)
369  {
370  if (!ODMRLineSeries)
371  return;
372 
373  Connect(ODMRLineSeries, &QXYSeries::hovered, this, &ODMR::OnODMRChartHovered);
374  Connect(ODMRLineSeries, &QXYSeries::clicked, this, &ODMR::OnODMRChartClicked);
375  }
376 
377  void ODMR::OnInit(DynExp::ModuleInstance* Instance) const
378  {
379  auto ModuleParams = DynExp::dynamic_Params_cast<ODMR>(Instance->ParamsGetter());
380  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
381 
382  Instance->LockObject(ModuleParams->RFGenerator, ModuleData->GetRFGenerator());
383  Instance->LockObject(ModuleParams->SignalDetector, ModuleData->GetSignalDetector());
384  Instance->LockObject(ModuleParams->TriggerOut, ModuleData->GetTrigger());
385 
386  ModuleData->RFGeneratorMinFuncDesc = ModuleData->GetRFGenerator()->GetMinCaps();
387  ModuleData->RFGeneratorMaxFuncDesc = ModuleData->GetRFGenerator()->GetMaxCaps();
388  ModuleData->RFGeneratorDefaultFuncDesc = ModuleData->GetRFGenerator()->GetParamDefaults();
389 
390  if (dynamic_cast<const DynExpInstr::LockinAmplifier*>(ModuleData->GetSignalDetector().get()))
392  else if (dynamic_cast<const DynExpInstr::AnalogIn*>(ModuleData->GetSignalDetector().get()))
394  ModuleData->GetSignalDetector()->Stop();
395 
396  if (ModuleParams->AuxAnalogOut.ContainsID())
397  {
398  Instance->LockObject(ModuleParams->AuxAnalogOut, ModuleData->GetAuxAnalogOut());
400 
401  ModuleData->AuxAnalogOutValueUnit = ModuleData->GetAuxAnalogOut()->GetValueUnit();
402  ModuleData->AuxAnalogOutMinValue = ModuleData->GetAuxAnalogOut()->GetUserMinValue();
403  ModuleData->AuxAnalogOutMaxValue = ModuleData->GetAuxAnalogOut()->GetUserMaxValue();
404  }
405  }
406 
407  void ODMR::OnExit(DynExp::ModuleInstance* Instance) const
408  {
409  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
410 
411  Instance->UnlockObject(ModuleData->GetRFGenerator());
412  Instance->UnlockObject(ModuleData->GetSignalDetector());
413  Instance->UnlockObject(ModuleData->GetTrigger());
414  Instance->UnlockObject(ModuleData->GetAuxAnalogOut());
415  }
416 
417  void ODMR::OnRFPowerChanged(DynExp::ModuleInstance* Instance, double Value) const
418  {
419  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
420  ModuleData->RFPower = Value;
421  }
422 
423  void ODMR::OnRFAutoEnableClicked(DynExp::ModuleInstance* Instance, int Checked) const
424  {
425  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
426  ModuleData->RFAutoEnabled = Checked;
427  }
428 
429  void ODMR::OnRFCenterFreqChanged(DynExp::ModuleInstance* Instance, double Value) const
430  {
431  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
432  ModuleData->RFCenterFreq = Value * 1e6;
433  }
434 
435  void ODMR::OnRFFreqSpanChanged(DynExp::ModuleInstance* Instance, double Value) const
436  {
437  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
438  ModuleData->RFFreqSpan = Value * 1e6;
439  }
440 
441  void ODMR::OnRFFreqSpacingChanged(DynExp::ModuleInstance* Instance, double Value) const
442  {
443  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
444  ModuleData->RFFreqSpacing = Value * 1e3;
445  }
446 
447  void ODMR::OnRFDwellTimeChanged(DynExp::ModuleInstance* Instance, double Value) const
448  {
449  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
450  ModuleData->RFDwellTime = Value * 1e-3;
451  }
452 
453  void ODMR::OnRFModNoneClicked(DynExp::ModuleInstance* Instance, bool Checked) const
454  {
455  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
456 
457  if (Checked)
459  }
460 
461  void ODMR::OnRFModSineClicked(DynExp::ModuleInstance* Instance, bool Checked) const
462  {
463  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
464 
465  if (Checked)
467  }
468 
469  void ODMR::OnRFModPulseClicked(DynExp::ModuleInstance* Instance, bool Checked) const
470  {
471  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
472 
473  if (Checked)
475  }
476 
477  void ODMR::OnRFModFreqChanged(DynExp::ModuleInstance* Instance, double Value) const
478  {
479  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
480  ModuleData->RFModulationFreq = Value * 1e3;
481  }
482 
483  void ODMR::OnRFModDepthChanged(DynExp::ModuleInstance* Instance, double Value) const
484  {
485  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
486  ModuleData->RFModulationDepth = Value * 1e3;
487  }
488 
489  void ODMR::OnODMRSamplingRateChanged(DynExp::ModuleInstance* Instance, double Value) const
490  {
491  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
492  ModuleData->ODMRSamplingRate = Value;
493  }
494 
495  void ODMR::OnSavePathChanged(DynExp::ModuleInstance* Instance, QString Path) const
496  {
497  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
498  ModuleData->SaveDataPath = Path.toStdString();
499  }
500 
501  void ODMR::OnSaveIndexChanged(DynExp::ModuleInstance* Instance, int Index) const
502  {
503  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
504  ModuleData->CurrentSaveIndex = Index;
505  }
506 
507  void ODMR::OnAutosaveClicked(DynExp::ModuleInstance* Instance, int Checked) const
508  {
509  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
510  ModuleData->AutosaveEnabled = Checked;
511  }
512 
513  void ODMR::OnRecordSensitivityClicked(DynExp::ModuleInstance* Instance, int Checked) const
514  {
515  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
516  ModuleData->SensitivityEnabled = Checked;
517  }
518 
519  void ODMR::OnRecordSensitivityOncePerSweepClicked(DynExp::ModuleInstance* Instance, int Checked) const
520  {
521  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
522  ModuleData->SensitivityOncePerSweep = Checked;
523  }
524 
525  void ODMR::OnRecordSensitivityOffResonanceClicked(DynExp::ModuleInstance* Instance, int Checked) const
526  {
527  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
528  ModuleData->SensitivityOffResonanceEnabled = Checked;
529  }
530 
531  void ODMR::OnSensitivityResonanceFreqChanged(DynExp::ModuleInstance* Instance, double Value) const
532  {
533  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
534  ModuleData->SensitivityResonanceFreq = Value * 1e6;
535  }
536 
537  void ODMR::OnSensitivityOffResonanceFreqChanged(DynExp::ModuleInstance* Instance, double Value) const
538  {
539  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
540  ModuleData->SensitivityOffResonanceFreq = Value * 1e6;
541  }
542 
543  void ODMR::OnSensitivityResonanceSpanChanged(DynExp::ModuleInstance* Instance, double Value) const
544  {
545  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
546  ModuleData->SensitivityResonanceSpan = Value * 1e6;
547  }
548 
549  void ODMR::OnSensitivitySamplingRateChanged(DynExp::ModuleInstance* Instance, double Value) const
550  {
551  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
552  ModuleData->SensitivitySamplingRate = Value;
553  }
554 
555  void ODMR::OnSensitivityDurationChanged(DynExp::ModuleInstance* Instance, double Value) const
556  {
557  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
558  ModuleData->SensitivityDuration = Value;
559  }
560 
561  void ODMR::OnEnableSensitivityAnalysisClicked(DynExp::ModuleInstance* Instance, bool Checked) const
562  {
563  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
564  ModuleData->SensitivityAnalysisEnabled = Checked;
565  }
566 
567  void ODMR::OnGyromagneticRatioChanged(DynExp::ModuleInstance* Instance, double Value) const
568  {
569  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
570  ModuleData->GyromagneticRatio = Value * 1e6;
571  }
572 
573  void ODMR::OnEnableSweepSeriesClicked(DynExp::ModuleInstance* Instance, int Checked) const
574  {
575  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
576  ModuleData->SweepSeriesEnabled = Checked;
577  }
578 
579  void ODMR::OnSweepSeriesParamChanged(DynExp::ModuleInstance* Instance, int Index) const
580  {
581  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
582 
583  switch (Index)
584  {
585  case 0: ModuleData->SweepSeries = ODMRData::SweepSeriesType::RFModulationDepth; break;
586  case 1: ModuleData->SweepSeries = ODMRData::SweepSeriesType::RFPower; break;
587  case 2:
590  break;
591  }
592  }
593 
594  void ODMR::OnSweepSeriesStartChanged(DynExp::ModuleInstance* Instance, double Value) const
595  {
596  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
597  ModuleData->SweepSeriesStart = Value;
598  }
599 
600  void ODMR::OnSweepSeriesStopChanged(DynExp::ModuleInstance* Instance, double Value) const
601  {
602  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
603  ModuleData->SweepSeriesStop = Value;
604  }
605 
606  void ODMR::OnSweepSeriesStepChanged(DynExp::ModuleInstance* Instance, double Value) const
607  {
608  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
609  ModuleData->SweepSeriesStep = Value;
610  }
611 
612  void ODMR::OnSweepSeriesRetraceClicked(DynExp::ModuleInstance* Instance, int Checked) const
613  {
614  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
615  ModuleData->SweepSeriesRetrace = Checked;
616  }
617 
618  void ODMR::OnSweepSeriesAdvanceLastValueClicked(DynExp::ModuleInstance* Instance, int Checked) const
619  {
620  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
621  ModuleData->SweepSeriesAdvanceLastValue = Checked;
622  }
623 
624  void ODMR::OnStartClicked(DynExp::ModuleInstance* Instance, bool) const
625  {
626  if (!IsReadyState())
627  return;
628 
630 
631  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
633  }
634 
635  void ODMR::OnStartSensitivityClicked(DynExp::ModuleInstance* Instance, bool) const
636  {
637  if (!IsReadyState())
638  return;
639 
641 
642  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
644  }
645 
646  void ODMR::OnStopClicked(DynExp::ModuleInstance* Instance, bool) const
647  {
648  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
649 
650  if (ModuleData->RFAutoEnabled)
651  ModuleData->GetRFGenerator()->Stop();
652  ModuleData->GetSignalDetector()->Stop();
653 
654  StateMachine.ResetContext();
655  StateMachine.SetCurrentState(StateType::Ready);
656  }
657 
658  void ODMR::OnRFOnClicked(DynExp::ModuleInstance* Instance, bool) const
659  {
660  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
661  ModuleData->GetRFGenerator()->Start();
662  }
663 
664  void ODMR::OnRFOffClicked(DynExp::ModuleInstance* Instance, bool) const
665  {
666  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
667  ModuleData->GetRFGenerator()->Stop();
668  }
669 
670  void ODMR::OnODMRChartHovered(DynExp::ModuleInstance* Instance, QPointF Point, bool State) const
671  {
672  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
673  ModuleData->ODMRPlot.SelectedPoint = State ? Point : QPointF();
674  }
675 
676  void ODMR::OnODMRChartClicked(DynExp::ModuleInstance* Instance, QPointF Point) const
677  {
678  if (Point.isNull())
679  return;
680 
681  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance->ModuleDataGetter());
682  ModuleData->GetRFGenerator()->SetSineFunction({ Point.x() * 1e9 , ModuleData->RFPower, 0, 0});
683  }
684 
686  {
687  return StateType::Ready;
688  }
689 
691  {
692  return StateType::Ready;
693  }
694 
695  StateType ODMR::MeasurementSeriesInitFunc(DynExp::ModuleInstance& Instance)
696  {
697  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
698 
699  ModuleData->CurrentSweepIndex = 0;
701 
703  {
704  auto LockinAmplifier = ModuleData->GetLockinAmplifier();
706  LockinAmplifier->SetTriggerEdge(DynExpInstr::LockinAmplifierDefs::TriggerEdgeType::Rise);
707  LockinAmplifier->SetEnable(true);
708  }
709 
711  }
712 
713  StateType ODMR::MeasurementSeriesStepFunc(DynExp::ModuleInstance& Instance)
714  {
715  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
716 
717  if (!ModuleData->SweepSeriesEnabled || ++ModuleData->CurrentSweepIndex >= ModuleData->GetSweepNumberSteps())
718  {
719  ++ModuleData->CurrentSaveIndex;
720 
721  if (ModuleData->SweepSeriesEnabled && ModuleData->SweepSeriesRetrace)
722  {
723  ModuleData->CurrentSweepIndex = 0;
725 
727  InitRFGenerator(ModuleData->GetRFStartFreq(), false, ModuleData);
728  }
729  if (ModuleData->RFAutoEnabled)
730  ModuleData->GetRFGenerator()->Stop();
731 
732  return StateType::Ready;
733  }
734 
736 
738  }
739 
740  StateType ODMR::ODMRTraceInitFunc(DynExp::ModuleInstance& Instance)
741  {
742  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
743 
745  InitRFGenerator(ModuleData->GetRFStartFreq(), ModuleData->RFAutoEnabled, ModuleData);
746  ModuleData->GetRFGenerator()->SetSweep({
748  ModuleData->GetRFStartFreq(),
749  ModuleData->GetRFStopFreq(),
750  ModuleData->RFFreqSpacing,
751  ModuleData->RFDwellTime * 1000,
752  true
753  });
754  ModuleData->GetRFGenerator()->SetTrigger({
757  });
758 
760  {
761  auto LockinAmplifier = ModuleData->GetLockinAmplifier();
762  LockinAmplifier->SetStreamSize(ModuleData->RFDwellTime * ModuleData->GetNumSamples() * ModuleData->ODMRSamplingRate);
763  LockinAmplifier->SetSamplingRate(ModuleData->ODMRSamplingRate);
764  }
765 
767 
769  }
770 
771  StateType ODMR::ODMRTraceWaitFunc(DynExp::ModuleInstance& Instance)
772  {
773  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
774 
775  return ModuleData->GetSignalDetector()->HasFinished() ? StateType::ODMRTraceFinish : StateType::ODMRTraceWait;
776  }
777 
778  StateType ODMR::ODMRTraceFinishFunc(DynExp::ModuleInstance& Instance)
779  {
780  std::vector<std::tuple<double, double, double>> SaveSamples;
781  bool Save = false;
782  bool PerformSensitivityMeasurement = false;
783  double RFStartFreq{};
784  decltype(ODMRData::RFDwellTime) RFDwellTime{};
785  decltype(ODMRData::RFFreqSpacing) RFFreqSpacing{};
786  bool IsBasicSampleTimeUsed{};
787  double SamplingRate{ .0 };
789  decltype(ODMRPlotType::DataPoints) ODMRDataPoints;
790  decltype(ODMRPlotType::DataPointsMinValues) ODMRDataPointsMinValues = { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
791  decltype(ODMRPlotType::DataPointsMaxValues) ODMRDataPointsMaxValues = { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
792  decltype(ODMRData::SensitivityResonanceFreq) SensitivityResonanceFreq{};
793  decltype(ODMRData::SensitivityResonanceSpan) SensitivityResonanceSpan{};
794  decltype(ODMRPlotType::FitParams) ODMRFitParams{};
795  decltype(ODMRPlotType::FitPoints) ODMRFitPoints{};
796  std::vector<double> FitXData;
797  std::vector<double> FitYData;
798  std::string Filename;
799  std::string ValueUnitStr;
800  std::stringstream CSVData;
801 
802  {
803  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
804 
805  if (!ModuleData->GetSignalDetector()->CanRead())
807 
808  Save = ModuleData->AutosaveEnabled;
809  PerformSensitivityMeasurement = ModuleData->SensitivityEnabled && (!ModuleData->SensitivityOncePerSweep || !ModuleData->CurrentSweepIndex);
810  RFStartFreq = ModuleData->GetRFStartFreq();
811  RFDwellTime = ModuleData->RFDwellTime;
812  RFFreqSpacing = ModuleData->RFFreqSpacing;
813  SensitivityResonanceFreq = ModuleData->SensitivityResonanceFreq;
814  SensitivityResonanceSpan = ModuleData->SensitivityResonanceSpan;
815 
816  auto SignalDetectorData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::DataStreamInstrument>(ModuleData->GetSignalDetector()->GetInstrumentData());
817  SignalDetectorData->GetSampleStream()->SeekBeg(std::ios_base::in);
818  ODMRSamples = SignalDetectorData->GetSampleStream()->ReadBasicSamples(SignalDetectorData->GetSampleStream()->GetStreamSizeRead());
819 
820  IsBasicSampleTimeUsed = SignalDetectorData->GetSampleStream()->IsBasicSampleTimeUsed();
821  if (!IsBasicSampleTimeUsed && ModuleData->TestFeature(ODMRData::FeatureType::AnalogInDetection))
822  {
823  SamplingRate = ModuleData->GetAnalogIn()->GetNumericSampleStreamParams().SamplingRate;
824 
825  if (SamplingRate == .0)
826  throw Util::InvalidDataException("The analog input channel's sampling rate must not be 0.");
827  }
828 
829  } // ModuleData unlocked here (heavy calculation follows).
830 
831  for (size_t i = 0; i < ODMRSamples.size(); ++i)
832  {
833  const auto& Sample = ODMRSamples[i];
834 
835  auto Frequency = RFStartFreq + (IsBasicSampleTimeUsed ? Sample.Time : (static_cast<double>(i) / SamplingRate)) / RFDwellTime * RFFreqSpacing;
836  ODMRDataPoints.push_back({ Frequency / 1e9, Sample.Value });
837  ODMRDataPointsMinValues = { std::min(ODMRDataPointsMinValues.x(), Frequency / 1e9), std::min(ODMRDataPointsMinValues.y(), Sample.Value) };
838  ODMRDataPointsMaxValues = { std::max(ODMRDataPointsMaxValues.x(), Frequency / 1e9), std::max(ODMRDataPointsMaxValues.y(), Sample.Value) };
839  if (Save)
840  SaveSamples.emplace_back(std::make_tuple(Frequency, Sample.Time, Sample.Value));
841 
842  if (PerformSensitivityMeasurement &&
843  Frequency >= SensitivityResonanceFreq - SensitivityResonanceSpan / 2 &&
844  Frequency <= SensitivityResonanceFreq + SensitivityResonanceSpan / 2)
845  {
846  FitXData.push_back(Frequency - SensitivityResonanceFreq);
847  FitYData.push_back(Sample.Value);
848  }
849  }
850 
851  if (PerformSensitivityMeasurement)
852  {
853  double c0{0}, c1{0}, cov00{0}, cov01{0}, cov11{0}, chisq{0};
854  gsl_fit_linear(FitXData.data(), 1, FitYData.data(), 1, FitXData.size(), &c0, &c1, &cov00, &cov01, &cov11, &chisq);
855  ODMRFitParams = { c0, c1 };
856 
857  for (const auto& f : FitXData)
858  ODMRFitPoints.push_back({ (f + SensitivityResonanceFreq) / 1e9, c0 + c1 * f });
859  }
860 
861  {
862  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
863 
864  ModuleData->ODMRPlot.DataPoints = std::move(ODMRDataPoints);
865  ModuleData->ODMRPlot.DataPointsMinValues = ODMRDataPointsMinValues;
866  ModuleData->ODMRPlot.DataPointsMaxValues = ODMRDataPointsMaxValues;
867  ModuleData->ODMRPlot.FitParams = ODMRFitParams;
868  ModuleData->ODMRPlot.FitPoints = ODMRFitPoints;
869  ModuleData->ODMRPlot.HasChanged = true;
870 
871  Filename = Util::RemoveExtFromPath(ModuleData->SaveDataPath) + "_ODMR" + Util::ToStr(ModuleData->CurrentSaveIndex) + "_Sweep" + Util::ToStr(ModuleData->CurrentSweepIndex) + ".csv";
872  ValueUnitStr = ModuleData->GetSignalDetector()->GetValueUnitStr();
873 
874  if (Save)
875  CSVData = ModuleData->AssembleCSVHeader(NextRFPower, NextRFModulationDepth, NextAuxAnalogOutValue, false);
876  } // ModuleData unlocked here.
877 
878  if (Save)
879  {
880  CSVData << "f(Hz);t(s);Value(" << ValueUnitStr << ")\n";
881  for (const auto& Sample : SaveSamples)
882  CSVData << std::get<0>(Sample) << ";" << std::get<1>(Sample) << ";" << std::get<2>(Sample) << "\n";
883 
884  Util::SaveToFile(QString::fromStdString(Filename), CSVData.str());
885  }
886 
887  return PerformSensitivityMeasurement ? StateType::SensitivityInit : StateType::MeasurementSeriesStep;
888  }
889 
890  StateType ODMR::SensitivityInitFunc(DynExp::ModuleInstance& Instance)
891  {
892  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
893 
894  // No sensitivity calculation if detection is not performed with a lock-in amplifer since in this case it is not possible
895  // to measure the ODMR signal's derivative directly.
897  {
898  StateMachine.ResetContext();
900  }
901 
903  ModuleData->GetRFGenerator()->SetSweep({}); // Disable sweep.
904  ModuleData->GetRFGenerator()->SetTrigger({}); // Disable trigger.
905  auto RFFreq = StateMachine.GetContext() == &SensitivityOffResonanceContext ? ModuleData->SensitivityOffResonanceFreq : ModuleData->SensitivityResonanceFreq;
906  InitRFGenerator(RFFreq, ModuleData->RFAutoEnabled, ModuleData);
907 
908  auto LockinAmplifier = ModuleData->GetLockinAmplifier();
909  LockinAmplifier->SetStreamSize(ModuleData->SensitivityDuration * ModuleData->SensitivitySamplingRate);
910  LockinAmplifier->SetSamplingRate(ModuleData->SensitivitySamplingRate);
911 
913 
915  }
916 
917  StateType ODMR::SensitivityWaitFunc(DynExp::ModuleInstance& Instance)
918  {
919  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
920 
921  return ModuleData->GetSignalDetector()->HasFinished() ? StateType::SensitivityFinish : StateType::SensitivityWait;
922  }
923 
924  StateType ODMR::SensitivityFinishFunc(DynExp::ModuleInstance& Instance)
925  {
926  std::vector<std::tuple<double, double>> SaveSamples;
928  bool Save = false;
929  bool SensitivityOffResonanceEnabled = false;
930  bool SensitivityAnalysisEnabled = false;
931  decltype(ODMRPlotType::FitParams) ODMRFitParams{};
932  decltype(ODMRData::GyromagneticRatio) GyromagneticRatio{};
934  std::vector<std::complex<double>> SensitivityASD;
935  decltype(SensitivityPlotType::DataPoints) SensitivityDataPoints;
936  decltype(SensitivityPlotType::DataPointsMinValues) SensitivityDataPointsMinValues = { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
937  decltype(SensitivityPlotType::DataPointsMaxValues) SensitivityDataPointsMaxValues = { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
938  std::string Filename;
939  std::string FilenamePrefix;
940  std::string ValueUnitStr;
941  std::stringstream CSVData;
942 
943  {
944  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
945 
946  if (!ModuleData->GetSignalDetector()->CanRead())
948 
949  MeasurementMode = ModuleData->MeasurementMode;
950  Save = ModuleData->AutosaveEnabled;
951  SensitivityOffResonanceEnabled = ModuleData->SensitivityOffResonanceEnabled;
952  SensitivityAnalysisEnabled = ModuleData->SensitivityAnalysisEnabled;
953  ODMRFitParams = ModuleData->ODMRPlot.FitParams;
954  GyromagneticRatio = ModuleData->GyromagneticRatio;
955 
956  auto SignalDetectorData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::DataStreamInstrument>(ModuleData->GetSignalDetector()->GetInstrumentData());
957  SignalDetectorData->GetSampleStream()->SeekBeg(std::ios_base::in);
958  SensitivitySamples = SignalDetectorData->GetSampleStream()->ReadBasicSamples(SignalDetectorData->GetSampleStream()->GetStreamSizeRead());
959  } // ModuleData unlocked here (heavy calculation follows).
960 
961  for (const auto& Sample : SensitivitySamples)
962  {
963  SaveSamples.emplace_back(std::make_tuple(Sample.Time, Sample.Value));
964 
965  if (MeasurementMode == ODMRData::MeasurementModeType::All && StateMachine.GetContext() != &SensitivityOffResonanceContext)
966  SensitivityASD.emplace_back(Sample.Value / std::get<1>(ODMRFitParams) / GyromagneticRatio, 0);
967  }
968 
969  if (SensitivityASD.size() > 2 && SensitivityAnalysisEnabled && StateMachine.GetContext() != &SensitivityOffResonanceContext)
970  {
971  const auto Length = static_cast<double>(SensitivityASD.size());
972 
973  SensitivityASD = Util::FFT(SensitivityASD);
974  std::transform(SensitivityASD.cbegin(), SensitivityASD.cend(), SensitivityASD.begin(), [Length](auto x) { return std::abs(x / Length); });
975 
976  // Make single-sided spectrum
977  SensitivityASD.erase(SensitivityASD.cbegin() + SensitivityASD.size() / 2 + 1, SensitivityASD.cend());
978  std::transform(SensitivityASD.cbegin() + 1, SensitivityASD.cend(), SensitivityASD.begin() + 1, [Length](auto x) { return 2.0 * x; });
979 
980  // Assume SensitivitySamples sorted by Time. Assume samples equally spaced in time.
981  double TimeSamplingRate = 1 / (std::abs(SensitivitySamples.back().Time - SensitivitySamples.front().Time) / (SensitivitySamples.size() - 1));
982  std::vector<double> Frequencies(Length / 2 + 1, 0);
983  std::iota(Frequencies.begin(), Frequencies.end(), 0);
984  std::transform(Frequencies.cbegin(), Frequencies.cend(), Frequencies.begin(), [TimeSamplingRate, Length](auto x) { return TimeSamplingRate * x / Length; });
985 
986  double FrequencySamplingRate = std::abs(Frequencies.back() - Frequencies.front()) / (Frequencies.size() - 1);
987  std::transform(SensitivityASD.cbegin(), SensitivityASD.cend(), SensitivityASD.begin(), [FrequencySamplingRate](auto x) { return x / std::sqrt(FrequencySamplingRate); });
988 
989  if (SensitivityASD.size() == Frequencies.size())
990  for (size_t i = 1; i < SensitivityASD.size(); ++i) // Ignore 0 Hz frequency component
991  {
992  SensitivityDataPoints.push_back({ Frequencies[i], SensitivityASD[i].real() });
993  SensitivityDataPointsMinValues = { std::min(SensitivityDataPointsMinValues.x(), SensitivityDataPoints.back().x()),
994  std::min(SensitivityDataPointsMinValues.y(), SensitivityDataPoints.back().y()) };
995  SensitivityDataPointsMaxValues = { std::max(SensitivityDataPointsMaxValues.x(), SensitivityDataPoints.back().x()),
996  std::max(SensitivityDataPointsMaxValues.y(), SensitivityDataPoints.back().y()) };
997  }
998  }
999 
1000  {
1001  auto ModuleData = DynExp::dynamic_ModuleData_cast<ODMR>(Instance.ModuleDataGetter());
1002 
1003  if (StateMachine.GetContext() != &SensitivityOffResonanceContext)
1004  {
1005  ModuleData->SensitivityPlot.DataPoints = std::move(SensitivityDataPoints);
1006  ModuleData->SensitivityPlot.DataPointsMinValues = SensitivityDataPointsMinValues;
1007  ModuleData->SensitivityPlot.DataPointsMaxValues = SensitivityDataPointsMaxValues;
1008  ModuleData->SensitivityPlot.HasChanged = true;
1009 
1010  FilenamePrefix = "Sensitivity";
1011  }
1012  else
1013  FilenamePrefix = "OffResSensitivity";
1014 
1015  Filename = Util::RemoveExtFromPath(ModuleData->SaveDataPath) + "_" + FilenamePrefix + Util::ToStr(ModuleData->CurrentSaveIndex) + "_Sweep" + Util::ToStr(ModuleData->CurrentSweepIndex) + ".csv";
1016  ValueUnitStr = ModuleData->GetSignalDetector()->GetValueUnitStr();
1017 
1018  if (Save)
1020  } // ModuleData unlocked here.
1021 
1022  if (Save)
1023  {
1024  CSVData << "t(s);Value(" << ValueUnitStr << ")\n";
1025  CSVData << std::setprecision(12);
1026  for (const auto& Sample : SaveSamples)
1027  CSVData << std::get<0>(Sample) << ";" << std::get<1>(Sample) << "\n";
1028 
1029  Util::SaveToFile(QString::fromStdString(Filename), CSVData.str());
1030  }
1031 
1032  if (SensitivityOffResonanceEnabled)
1033  {
1034  if (StateMachine.GetContext() == &SensitivityOffResonanceContext)
1035  StateMachine.ResetContext();
1036  else
1037  {
1040  }
1041  }
1042 
1044  }
1045 }
Implementation of a module to perform optically detected magnetic resonance (ODMR) measurements.
Meta instrument for a single analog input port based on the data stream and generic input port meta i...
Definition: AnalogIn.h:136
std::vector< BasicSample > BasicSampleListType
Type of a list containing data stream samples of type BasicSample.
Meta instrument for a lock-in amplifier based on the data stream meta instrument.
bool SweepSeriesAdvanceLastValue
Definition: ODMR.h:89
DynExpInstr::DataStreamInstrumentData::ValueType AuxAnalogOutMinValue
Definition: ODMR.h:97
double SensitivityResonanceFreq
Definition: ODMR.h:74
std::stringstream AssembleCSVHeader(double RFPower, double RFModulationDepth, double AuxAnalogOutValue, bool IsRFOffResonance)
Definition: ODMR.cpp:21
bool TestFeature(const std::array< FeatureType, N > &Flags) const
Definition: ODMR.h:35
DynExpInstr::DataStreamInstrumentData::UnitType AuxAnalogOutValueUnit
Definition: ODMR.h:96
SweepSeriesType SweepSeries
Definition: ODMR.h:84
std::string SaveDataPath
Definition: ODMR.h:67
double SensitivitySamplingRate
Definition: ODMR.h:77
unsigned long long CurrentSweepIndex
Definition: ODMR.h:90
SensitivityPlotType SensitivityPlot
Definition: ODMR.h:103
DynExpInstr::FunctionGeneratorDefs::FunctionDescType RFGeneratorMaxFuncDesc
Definition: ODMR.h:93
double SensitivityResonanceSpan
Definition: ODMR.h:76
Util::FeatureTester< FeatureType > Features
Definition: ODMR.h:116
double SensitivityOffResonanceFreq
Definition: ODMR.h:75
unsigned long long GetSweepNumberSteps() const noexcept
Definition: ODMR.cpp:13
auto & GetSignalDetector() noexcept
Definition: ODMR.h:41
unsigned int CurrentSaveIndex
Definition: ODMR.h:68
MeasurementModeType MeasurementMode
Definition: ODMR.h:66
auto & GetRFGenerator() noexcept
Definition: ODMR.h:40
DynExpInstr::FunctionGeneratorDefs::FunctionDescType RFGeneratorDefaultFuncDesc
Definition: ODMR.h:94
ODMRPlotType ODMRPlot
Definition: ODMR.h:102
RFModulationType RFModulation
Definition: ODMR.h:60
DynExpInstr::FunctionGeneratorDefs::FunctionDescType RFGeneratorMinFuncDesc
Definition: ODMR.h:92
bool SensitivityOffResonanceEnabled
Definition: ODMR.h:73
DynExpInstr::DataStreamInstrumentData::ValueType AuxAnalogOutMaxValue
Definition: ODMR.h:98
void ResetImpl(dispatch_tag< QModuleDataBase >) override final
Definition: ODMR.cpp:8
size_t NumFailedUpdateAttempts
Definition: ODMR.h:284
void InitRFGenerator(double Frequency, bool EnableRF, Util::SynchronizedPointer< ModuleDataType > &ModuleData) const
Definition: ODMR.cpp:327
void OnSweepSeriesStopChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:600
void OnRFModDepthChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:483
void ConnectChartWidgets(QLineSeries *ODMRLineSeries)
Definition: ODMR.cpp:368
Util::StateMachine< StateMachineStateType > StateMachine
Definition: ODMR.h:278
void WaitUntilReadyAndTrigger(Util::SynchronizedPointer< ModuleDataType > &ModuleData) const
Definition: ODMR.cpp:348
void OnStopClicked(DynExp::ModuleInstance *Instance, bool) const
Definition: ODMR.cpp:646
void OnRFAutoEnableClicked(DynExp::ModuleInstance *Instance, int Checked) const
Definition: ODMR.cpp:423
void OnRFModNoneClicked(DynExp::ModuleInstance *Instance, bool Checked) const
Definition: ODMR.cpp:453
void OnRFFreqSpanChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:435
void OnGyromagneticRatioChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:567
std::chrono::milliseconds GetMainLoopDelay() const override final
Specifies in which time intervals the module's event queue runs to handle pending events.
Definition: ODMR.cpp:161
void OnRecordSensitivityOffResonanceClicked(DynExp::ModuleInstance *Instance, int Checked) const
Definition: ODMR.cpp:525
bool IsReadyState() const noexcept
Definition: ODMR.h:181
void OnODMRSamplingRateChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:489
void OnEnableSweepSeriesClicked(DynExp::ModuleInstance *Instance, int Checked) const
Definition: ODMR.cpp:573
void OnRFOnClicked(DynExp::ModuleInstance *Instance, bool) const
Definition: ODMR.cpp:658
double NextAuxAnalogOutValue
Definition: ODMR.h:282
void OnRFModPulseClicked(DynExp::ModuleInstance *Instance, bool Checked) const
Definition: ODMR.cpp:469
StateType ReadyStateFunc(DynExp::ModuleInstance &Instance)
Definition: ODMR.cpp:690
ODMR(const std::thread::id OwnerThreadID, DynExp::ParamsBasePtrType &&Params)
Definition: ODMR.cpp:152
void OnSavePathChanged(DynExp::ModuleInstance *Instance, QString Path) const
Definition: ODMR.cpp:495
void OnSweepSeriesStepChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:606
void OnSensitivityResonanceSpanChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:543
void OnRecordSensitivityClicked(DynExp::ModuleInstance *Instance, int Checked) const
Definition: ODMR.cpp:513
void OnSensitivityOffResonanceFreqChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:537
StateType InitializingStateFunc(DynExp::ModuleInstance &Instance)
Definition: ODMR.cpp:685
double NextRFModulationDepth
Definition: ODMR.h:281
void OnSweepSeriesRetraceClicked(DynExp::ModuleInstance *Instance, int Checked) const
Definition: ODMR.cpp:612
void SetAuxAnalogOutValue(Util::SynchronizedPointer< ModuleDataType > &ModuleData) const
Definition: ODMR.cpp:342
void OnSaveIndexChanged(DynExp::ModuleInstance *Instance, int Index) const
Definition: ODMR.cpp:501
void OnODMRChartHovered(DynExp::ModuleInstance *Instance, QPointF Point, bool State) const
Definition: ODMR.cpp:670
void OnODMRChartClicked(DynExp::ModuleInstance *Instance, QPointF Point) const
Definition: ODMR.cpp:676
void OnRFFreqSpacingChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:441
void OnRFModFreqChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:477
void OnAutosaveClicked(DynExp::ModuleInstance *Instance, int Checked) const
Definition: ODMR.cpp:507
void OnRFDwellTimeChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:447
void OnStartClicked(DynExp::ModuleInstance *Instance, bool) const
Definition: ODMR.cpp:624
void InitSweepValues(Util::SynchronizedPointer< ModuleDataType > &ModuleData)
Definition: ODMR.cpp:298
void OnSensitivitySamplingRateChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:549
void OnSensitivityResonanceFreqChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:531
void OnRFModSineClicked(DynExp::ModuleInstance *Instance, bool Checked) const
Definition: ODMR.cpp:461
void OnSweepSeriesAdvanceLastValueClicked(DynExp::ModuleInstance *Instance, int Checked) const
Definition: ODMR.cpp:618
void OnStartSensitivityClicked(DynExp::ModuleInstance *Instance, bool) const
Definition: ODMR.cpp:635
void OnRFOffClicked(DynExp::ModuleInstance *Instance, bool) const
Definition: ODMR.cpp:664
void OnSweepSeriesParamChanged(DynExp::ModuleInstance *Instance, int Index) const
Definition: ODMR.cpp:579
void OnEnableSensitivityAnalysisClicked(DynExp::ModuleInstance *Instance, bool Checked) const
Definition: ODMR.cpp:561
void OnRFPowerChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:417
void OnRecordSensitivityOncePerSweepClicked(DynExp::ModuleInstance *Instance, int Checked) const
Definition: ODMR.cpp:519
void OnSweepSeriesStartChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:594
const Util::StateMachineContext< StateMachineStateType > SensitivityOffResonanceContext
Definition: ODMR.h:275
void OnRFCenterFreqChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:429
void OnSensitivityDurationChanged(DynExp::ModuleInstance *Instance, double Value) const
Definition: ODMR.cpp:555
const std::unique_ptr< ModuleDataType > ModuleData
Module data belonging to this ModuleBase instance.
Definition: Module.h:743
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
void UnlockObject(LinkedObjectWrapperContainer< ObjectT > &ObjectWrapperContainer)
Unlocks an Object instance stored in the LinkedObjectWrapperContainer ObjectWrapperContainer....
Definition: Object.h:3570
void LockObject(const ParamsBase::Param< ObjectLink< ObjectT >> &LinkParam, LinkedObjectWrapperContainer< ObjectT > &ObjectWrapperContainer, std::chrono::milliseconds Timeout=ObjectLinkBase::LockObjectTimeoutDefault)
Locks an Object instance referenced by a parameter LinkParam of type ParamsBase::Param< ObjectLink< O...
Definition: Object.h:3554
const auto & GetOwner() const noexcept
Returns Owner.
Definition: Object.h:3524
Data to operate on is invalid for a specific purpose. This indicates a corrupted data structure or fu...
Definition: Exception.h:163
Pointer to lock a class derived from ISynchronizedPointerLockable for synchronizing between threads....
Definition: Util.h:170
Thrown when an operation timed out before it could be completed, especially used for locking shared d...
Definition: Exception.h:261
@ Rise
Trigger on rising edge.
@ ExternSingle
Run once after an external trigger signal has been detected.
std::tuple< double, double > FitParams
Definition: ODMRWidget.h:41
constexpr auto Save
void WaitForInstruments(InstrTs &... Instruments)
Blocks until every instrument passed to the function as a reference parameter has arrived at a synchr...
Definition: Instrument.h:1234
std::unique_ptr< ParamsBase > ParamsBasePtrType
Alias for a pointer to the parameter system base class ParamsBase.
Definition: Object.h:1807
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
bool SaveToFile(const QString &Filename, std::string_view Text)
Saves a std::string_view to a file (using QFile). Creates a new file or truncates an existing file's ...
Definition: QtUtil.cpp:236
std::vector< std::complex< double > > FFT(const std::vector< std::complex< double >> &Data, bool InverseTransform)
Computes the Fast Fourier Transform (FFT) a vector of complex values.
Definition: Util.cpp:220
auto RemoveExtFromPath(std::string Path)
Removes the filename's extension from a path.
Definition: Util.h:853
Accumulates include statements to provide a precompiled header.
@ Pulse
Pulse (binary) modulation switching the affected quantity between two values.
@ Frequency
Modulation affecting the waveform's frequency.