nmrpulse.cpp
1 /***************************************************************************
2  Copyright (C) 2002-2015 Kentaro Kitagawa
3  kitagawa@phys.s.u-tokyo.ac.jp
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  You should have received a copy of the GNU Library General
11  Public License and a list of authors along with this program;
12  see the files COPYING and AUTHORS.
13 ***************************************************************************/
14 //---------------------------------------------------------------------------
15 #include "nmrpulse.h"
16 #include "ui_nmrpulseform.h"
17 #include "freqestleastsquare.h"
18 
19 #include "icon.h"
20 #include "graph.h"
21 #include "graphwidget.h"
22 #include "ui_graphnurlform.h"
23 #include "analyzer.h"
24 #include "xnodeconnector.h"
25 
26 REGISTER_TYPE(XDriverList, NMRPulseAnalyzer, "NMR FID/echo analyzer");
27 
28 //---------------------------------------------------------------------------
29 XNMRPulseAnalyzer::XNMRPulseAnalyzer(const char *name, bool runtime,
30  Transaction &tr_meas, const shared_ptr<XMeasure> &meas) :
31  XSecondaryDriver(name, runtime, ref(tr_meas), meas),
32  m_entryPeakAbs(create<XScalarEntry>("PeakAbs", false,
33  dynamic_pointer_cast<XDriver>(shared_from_this()))),
34  m_entryPeakFreq(create<XScalarEntry>("PeakFreq", false,
35  dynamic_pointer_cast<XDriver>(shared_from_this()))),
36  m_dso(create<XItemNode<XDriverList, XDSO> >("DSO", false, ref(tr_meas), meas->drivers(), true)),
37  m_fromTrig(create<XDoubleNode>("FromTrig", false)),
38  m_width(create<XDoubleNode>("Width", false)),
39  m_phaseAdv(create<XDoubleNode>("PhaseAdv", false)),
40  m_usePNR(create<XBoolNode>("UsePNR", false)),
41  m_pnrSolverList(create<XComboNode>("PNRSpectrumSolver", false, true)),
42  m_solverList(create<XComboNode>("SpectrumSolver", false, true)),
43  m_bgPos(create<XDoubleNode>("BGPos", false)),
44  m_bgWidth(create<XDoubleNode>("BGWidth", false)),
45  m_fftPos(create<XDoubleNode>("FFTPos", false)),
46  m_fftLen(create<XUIntNode>("FFTLen", false)),
47  m_windowFunc(create<XComboNode>("WindowFunc", false, true)),
48  m_windowWidth(create<XDoubleNode>("WindowLength", false)),
49  m_difFreq(create<XDoubleNode>("DIFFreq", false)),
50  m_exAvgIncr(create<XBoolNode>("ExAvgIncr", false)),
51  m_extraAvg(create<XUIntNode>("ExtraAvg", false)),
52  m_numEcho(create<XUIntNode>("NumEcho", false)),
53  m_echoPeriod(create<XDoubleNode>("EchoPeriod", false)),
54  m_spectrumShow(create<XTouchableNode>("SpectrumShow", true)),
55  m_avgClear(create<XTouchableNode>("AvgClear", true)),
56  m_picEnabled(create<XBoolNode>("PICEnabled", false)),
57  m_pulser(create<XItemNode<XDriverList, XPulser> >("Pulser", false, ref(tr_meas), meas->drivers(), true)),
58  m_form(new FrmNMRPulse),
59  m_statusPrinter(XStatusPrinter::create(m_form.get())),
60  m_spectrumForm(new FrmGraphNURL(m_form.get(), Qt::Window)), m_waveGraph(create<XWaveNGraph>("Wave", true,
61  m_form->m_graph, m_form->m_edDump, m_form->m_tbDump, m_form->m_btnDump)),
62  m_ftWaveGraph(create<XWaveNGraph>("Spectrum", true, m_spectrumForm.get())),
63  m_solver(create<SpectrumSolverWrapper>("SpectrumSolverWrapper", true, m_solverList, m_windowFunc, m_windowWidth)),
64  m_solverPNR(create<SpectrumSolverWrapper>("PNRSpectrumSolverWrapper", true, m_pnrSolverList, shared_ptr<XComboNode>(), shared_ptr<XDoubleNode>(), true)) {
65  m_form->m_btnAvgClear->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogResetButton));
66  m_form->m_btnSpectrum->setIcon( *g_pIconGraph);
67 
68  connect(dso());
69  connect(pulser());
70 
71  meas->scalarEntries()->insert(tr_meas, entryPeakAbs());
72  meas->scalarEntries()->insert(tr_meas, entryPeakFreq());
73 
74  iterate_commit([=](Transaction &tr){
75  tr[ *m_pnrSolverList].str(XString(SpectrumSolverWrapper::SPECTRUM_SOLVER_LS_MDL));
76  tr[ *fromTrig()] = -0.005;
77  tr[ *width()] = 0.02;
78  tr[ *bgPos()] = 0.03;
79  tr[ *bgWidth()] = 0.03;
80  tr[ *fftPos()] = 0.004;
81  tr[ *fftLen()] = 16384;
82  tr[ *numEcho()] = 1;
83  tr[ *windowFunc()].str(XString(SpectrumSolverWrapper::WINDOW_FUNC_DEFAULT));
84  tr[ *windowWidth()] = 100.0;
85  });
86 
87  m_form->setWindowTitle(i18n("NMR Pulse - ") + getLabel() );
88 
89  m_spectrumForm->setWindowTitle(i18n("NMR-Spectrum - ") + getLabel() );
90 
91  m_form->m_dblWindowWidth->setRange(3.0, 200.0);
92  m_form->m_dblWindowWidth->setSingleStep(1.0);
93  m_form->m_numExtraAvg->setRange(0, 100000);
94  m_form->m_dblPhaseAdv->setRange(-180.0, 180.0);
95  m_form->m_dblPhaseAdv->setSingleStep(10.0);
96 
97  m_conUIs = {
98  xqcon_create<XQButtonConnector>(m_avgClear, m_form->m_btnAvgClear),
99  xqcon_create<XQButtonConnector>(m_spectrumShow, m_form->m_btnSpectrum),
100  xqcon_create<XQLineEditConnector>(fromTrig(), m_form->m_edPos),
101  xqcon_create<XQLineEditConnector>(width(), m_form->m_edWidth),
102  xqcon_create<XQDoubleSpinBoxConnector>(phaseAdv(), m_form->m_dblPhaseAdv, m_form->m_slPhaseAdv),
103  xqcon_create<XQToggleButtonConnector>(usePNR(), m_form->m_ckbPNR),
104  xqcon_create<XQComboBoxConnector>(pnrSolverList(), m_form->m_cmbPNRSolver, Snapshot( *pnrSolverList())),
105  xqcon_create<XQComboBoxConnector>(solverList(), m_form->m_cmbSolver, Snapshot( *solverList())),
106  xqcon_create<XQLineEditConnector>(bgPos(), m_form->m_edBGPos),
107  xqcon_create<XQLineEditConnector>(bgWidth(), m_form->m_edBGWidth),
108  xqcon_create<XQLineEditConnector>(fftPos(), m_form->m_edFFTPos),
109  xqcon_create<XQLineEditConnector>(fftLen(), m_form->m_edFFTLen),
110  xqcon_create<XQSpinBoxUnsignedConnector>(extraAvg(), m_form->m_numExtraAvg),
111  xqcon_create<XQToggleButtonConnector>(exAvgIncr(), m_form->m_ckbIncrAvg),
112  xqcon_create<XQSpinBoxUnsignedConnector>(numEcho(), m_form->m_numEcho),
113  xqcon_create<XQLineEditConnector>(echoPeriod(), m_form->m_edEchoPeriod),
114  xqcon_create<XQComboBoxConnector>(windowFunc(), m_form->m_cmbWindowFunc, Snapshot( *windowFunc())),
115  xqcon_create<XQDoubleSpinBoxConnector>(windowWidth(), m_form->m_dblWindowWidth, m_form->m_slWindowWIdth),
116  xqcon_create<XQLineEditConnector>(difFreq(), m_form->m_edDIFFreq),
117  xqcon_create<XQToggleButtonConnector>(m_picEnabled, m_form->m_ckbPICEnabled),
118  xqcon_create<XQComboBoxConnector>(m_pulser, m_form->m_cmbPulser, ref(tr_meas)),
119  xqcon_create<XQComboBoxConnector>(dso(), m_form->m_cmbDSO, ref(tr_meas))
120  };
121 
122  waveGraph()->iterate_commit([=](Transaction &tr){
123  const char *labels[] = { "Time [ms]", "IFFT Re [V]", "IFFT Im [V]", "DSO CH1[V]", "DSO CH2[V]"};
124  tr[ *waveGraph()].setColCount(5, labels);
125  tr[ *waveGraph()].insertPlot(labels[1], 0, 1);
126  tr[ *waveGraph()].insertPlot(labels[2], 0, 2);
127  tr[ *waveGraph()].insertPlot(labels[3], 0, 3);
128  tr[ *waveGraph()].insertPlot(labels[4], 0, 4);
129  tr[ *tr[ *waveGraph()].axisy()->label()] = i18n("Intens. [V]");
130  tr[ *tr[ *waveGraph()].plot(0)->label()] = i18n("IFFT Re.");
131  tr[ *tr[ *waveGraph()].plot(0)->drawPoints()] = false;
132  tr[ *tr[ *waveGraph()].plot(0)->lineColor()] = QColor(0xcc, 0x00, 0x80).rgb();
133  tr[ *tr[ *waveGraph()].plot(0)->intensity()] = 2.0;
134  tr[ *tr[ *waveGraph()].plot(1)->label()] = i18n("IFFT Im.");
135  tr[ *tr[ *waveGraph()].plot(1)->drawPoints()] = false;
136  tr[ *tr[ *waveGraph()].plot(1)->intensity()] = 2.0;
137  tr[ *tr[ *waveGraph()].plot(1)->lineColor()] = QColor(0x00, 170, 0x00).rgb();
138  tr[ *tr[ *waveGraph()].plot(2)->label()] = i18n("DSO CH1");
139  tr[ *tr[ *waveGraph()].plot(2)->drawPoints()] = false;
140  tr[ *tr[ *waveGraph()].plot(2)->lineColor()] = QColor(0xff, 0xa0, 0x00).rgb();
141  tr[ *tr[ *waveGraph()].plot(2)->intensity()] = 0.3;
142  tr[ *tr[ *waveGraph()].plot(3)->label()] = i18n("DSO CH2");
143  tr[ *tr[ *waveGraph()].plot(3)->drawPoints()] = false;
144  tr[ *tr[ *waveGraph()].plot(3)->lineColor()] = QColor(0x00, 0xa0, 0xff).rgb();
145  tr[ *tr[ *waveGraph()].plot(3)->intensity()] = 0.3;
146  tr[ *waveGraph()].clearPoints();
147  });
148  ftWaveGraph()->iterate_commit([=](Transaction &tr){
149  const char *labels[] = { "Freq. [kHz]", "Re. [V]", "Im. [V]",
150  "Abs. [V]", "Phase [deg]", "Dark. [V]" };
151  tr[ *ftWaveGraph()].setColCount(6, labels);
152  tr[ *ftWaveGraph()].insertPlot(labels[3], 0, 3);
153  tr[ *ftWaveGraph()].insertPlot(labels[4], 0, -1, 4);
154  tr[ *ftWaveGraph()].insertPlot(labels[5], 0, 5);
155  tr[ *tr[ *ftWaveGraph()].axisy()->label()] = i18n("Intens. [V]");
156  tr[ *tr[ *ftWaveGraph()].plot(0)->label()] = i18n("abs.");
157  tr[ *tr[ *ftWaveGraph()].plot(0)->drawBars()] = true;
158  tr[ *tr[ *ftWaveGraph()].plot(0)->drawLines()] = true;
159  tr[ *tr[ *ftWaveGraph()].plot(0)->drawPoints()] = false;
160  tr[ *tr[ *ftWaveGraph()].plot(0)->intensity()] = 0.5;
161  tr[ *tr[ *ftWaveGraph()].plot(1)->label()] = i18n("phase");
162  tr[ *tr[ *ftWaveGraph()].plot(1)->drawPoints()] = false;
163  tr[ *tr[ *ftWaveGraph()].plot(1)->intensity()] = 0.3;
164  tr[ *tr[ *ftWaveGraph()].plot(2)->label()] = i18n("dark");
165  tr[ *tr[ *ftWaveGraph()].plot(2)->drawBars()] = false;
166  tr[ *tr[ *ftWaveGraph()].plot(2)->drawLines()] = true;
167  tr[ *tr[ *ftWaveGraph()].plot(2)->lineColor()] = QColor(0xa0, 0xa0, 0x00).rgb();
168  tr[ *tr[ *ftWaveGraph()].plot(2)->drawPoints()] = false;
169  tr[ *tr[ *ftWaveGraph()].plot(2)->intensity()] = 0.5;
170  {
171  shared_ptr<XXYPlot> plot = ftWaveGraph()->graph()->plots()->create<XXYPlot>(
172  tr, "Peaks", true, ref(tr), ftWaveGraph()->graph());
173  m_peakPlot = plot;
174  tr[ *plot->label()] = i18n("Peaks");
175  tr[ *plot->axisX()] = tr[ *ftWaveGraph()].axisx();
176  tr[ *plot->axisY()] = tr[ *ftWaveGraph()].axisy();
177  tr[ *plot->drawPoints()] = false;
178  tr[ *plot->drawLines()] = false;
179  tr[ *plot->drawBars()] = true;
180  tr[ *plot->intensity()] = 0.5;
181  tr[ *plot->displayMajorGrid()] = false;
182  tr[ *plot->pointColor()] = QColor(0x40, 0x40, 0xa0).rgb();
183  tr[ *plot->barColor()] = QColor(0x40, 0x40, 0xa0).rgb();
184  tr[ *plot->clearPoints()].setUIEnabled(false);
185  tr[ *plot->maxCount()].setUIEnabled(false);
186  }
187  tr[ *ftWaveGraph()].clearPoints();
188  });
189 
190  iterate_commit([=](Transaction &tr){
191  m_lsnOnAvgClear = tr[ *m_avgClear].onTouch().connectWeakly(
192  shared_from_this(), &XNMRPulseAnalyzer::onAvgClear);
193  m_lsnOnSpectrumShow = tr[ *m_spectrumShow].onTouch().connectWeakly(
194  shared_from_this(), &XNMRPulseAnalyzer::onSpectrumShow,
195  XListener::FLAG_MAIN_THREAD_CALL | XListener::FLAG_AVOID_DUP);
196 
197  m_lsnOnCondChanged = tr[ *fromTrig()].onValueChanged().connectWeakly(
198  shared_from_this(), &XNMRPulseAnalyzer::onCondChanged);
199  tr[ *width()].onValueChanged().connect(m_lsnOnCondChanged);
200  tr[ *phaseAdv()].onValueChanged().connect(m_lsnOnCondChanged);
201  tr[ *usePNR()].onValueChanged().connect(m_lsnOnCondChanged);
202  tr[ *pnrSolverList()].onValueChanged().connect(m_lsnOnCondChanged);
203  tr[ *solverList()].onValueChanged().connect(m_lsnOnCondChanged);
204  tr[ *bgPos()].onValueChanged().connect(m_lsnOnCondChanged);
205  tr[ *bgWidth()].onValueChanged().connect(m_lsnOnCondChanged);
206  tr[ *fftPos()].onValueChanged().connect(m_lsnOnCondChanged);
207  tr[ *fftLen()].onValueChanged().connect(m_lsnOnCondChanged);
208  // extraAvg()].onValueChanged().connect(m_lsnOnCondChanged);
209  tr[ *exAvgIncr()].onValueChanged().connect(m_lsnOnCondChanged);
210  tr[ *numEcho()].onValueChanged().connect(m_lsnOnCondChanged);
211  tr[ *echoPeriod()].onValueChanged().connect(m_lsnOnCondChanged);
212  tr[ *windowFunc()].onValueChanged().connect(m_lsnOnCondChanged);
213  tr[ *windowWidth()].onValueChanged().connect(m_lsnOnCondChanged);
214  tr[ *difFreq()].onValueChanged().connect(m_lsnOnCondChanged);
215  });
216 }
217 XNMRPulseAnalyzer::~XNMRPulseAnalyzer() {
218 }
219 void XNMRPulseAnalyzer::onSpectrumShow(const Snapshot &shot, XTouchableNode *) {
220  m_spectrumForm->showNormal();
221  m_spectrumForm->raise();
222 }
224  m_form->showNormal();
225  m_form->raise();
226 }
227 
228 void XNMRPulseAnalyzer::backgroundSub(Transaction &tr,
229  std::vector<std::complex<double> > &wave,
230  int pos, int length, int bgpos, int bglength) {
231  Snapshot &shot(tr);
232 
233  std::complex<double> bg = 0;
234  if(bglength) {
235  double normalize = 0.0;
236  for(int i = 0; i < bglength; i++) {
237  double z = 1.0;
238  if( !shot[ *usePNR()])
239  z = FFT::windowFuncHamming( (double)i / bglength - 0.5);
240  bg += z * wave[pos + i + bgpos];
241  normalize += z;
242  }
243  bg /= normalize;
244  }
245 
246  for(int i = 0; i < wave.size(); i++) {
247  wave[i] -= bg;
248  }
249 
250  SpectrumSolver &solverPNR(tr[ *m_solverPNR].solver());
251  if(bglength) {
252  if(shot[ *usePNR()]) {
253  int dnrlength = FFT::fitLength((bglength + bgpos) * 4);
254  std::vector<std::complex<double> > memin(bglength), memout(dnrlength);
255  for(unsigned int i = 0; i < bglength; i++) {
256  memin[i] = wave[pos + i + bgpos];
257  }
258  try {
259  solverPNR.exec(memin, memout, bgpos, 0.5e-2, &FFT::windowFuncRect, 1.0);
260  int imax = std::min((int)wave.size() - pos, (int)memout.size());
261  for(unsigned int i = 0; i < imax; i++) {
262  wave[i + pos] -= solverPNR.ifft()[i];
263  }
264  }
265  catch (XKameError &e) {
266  e.print();
267 // throw XSkippedRecordError(e.msg(), __FILE__, __LINE__);
268  }
269  }
270  }
271 }
272 void XNMRPulseAnalyzer::rotNFFT(Transaction &tr, int ftpos, double ph,
273  std::vector<std::complex<double> > &wave,
274  std::vector<std::complex<double> > &ftwave) {
275  Snapshot &shot(tr);
276 
277  int length = wave.size();
278  //phase advance
279  std::complex<double> cph(std::polar(1.0, ph));
280  for(int i = 0; i < length; i++) {
281  wave[i] *= cph;
282  }
283 
284  int fftlen = ftwave.size();
285  //fft
286  std::vector<std::complex<double> > fftout(fftlen);
287  SpectrumSolver &solver(tr[ *m_solver].solver());
288  FFT::twindowfunc wndfunc = m_solver->windowFunc(shot);
289  double wndwidth = shot[ *windowWidth()] / 100.0;
290  try {
291  solver.exec(wave, fftout, -ftpos, 0.3e-2, wndfunc, wndwidth);
292  }
293  catch (XKameError &e) {
294  throw XSkippedRecordError(e.msg(), __FILE__, __LINE__);
295  }
296 
297  std::copy(fftout.begin(), fftout.end(), ftwave.begin());
298 
299  if(solver.isFT()) {
300  std::vector<double> weight;
301  SpectrumSolver::window(length, -ftpos, wndfunc, wndwidth, weight);
302  double w = 0;
303  for(int i = 0; i < length; i++)
304  w += weight[i] * weight[i];
305  tr[ *this].m_ftWavePSDCoeff = w/(double)length;
306  }
307  else {
308  tr[ *this].m_ftWavePSDCoeff = 1.0;
309  }
310 }
311 void XNMRPulseAnalyzer::onAvgClear(const Snapshot &shot, XTouchableNode *) {
312  trans( *this).m_timeClearRequested = XTime::now();
313 
314  Snapshot shot_this( *this);
315  requestAnalysis();
316 
317  const shared_ptr<XDSO> dso__ = shot_this[ *dso()];
318  if(dso__)
319  trans( *dso__->restart()).touch(); //Restart averaging in DSO.
320 }
321 void XNMRPulseAnalyzer::onCondChanged(const Snapshot &shot, XValueNodeBase *node) {
322  if(node == exAvgIncr().get())
323  trans( *extraAvg()) = 0;
324  if((node == numEcho().get()) || (node == difFreq().get()) || (node == exAvgIncr().get()))
325  onAvgClear(shot, avgClear().get());
326  else
327  requestAnalysis();
328 }
330  const Snapshot &shot_emitter, const Snapshot &shot_others,
331  XDriver *emitter) const {
332  const shared_ptr<XPulser> pulse__ = shot_this[ *pulser()];
333  if (emitter == pulse__.get())
334  return false;
335  const shared_ptr<XDSO> dso__ = shot_this[ *dso()];
336  if( !dso__)
337  return false;
338  // //Request for clear.
339  // if(m_timeClearRequested > dso__->timeAwared()) return true;
340  // if(pulse__ && (dso__->timeAwared() < pulse__->time())) return false;
341  return true;
342 }
343 void XNMRPulseAnalyzer::analyze(Transaction &tr, const Snapshot &shot_emitter,
344  const Snapshot &shot_others,
345  XDriver *emitter) throw (XRecordError&) {
346  const Snapshot &shot_this(tr);
347  const shared_ptr<XDSO> dso__ = shot_this[ *dso()];
348  assert(dso__);
349 
350  const Snapshot &shot_dso((emitter == dso__.get()) ? shot_emitter : shot_others);
351  assert(shot_dso[ *dso__].time() );
352 
353  if(shot_dso[ *dso__].numChannels() < 1) {
354  throw XSkippedRecordError(i18n("No record in DSO"), __FILE__, __LINE__);
355  }
356  if(shot_dso[ *dso__].numChannels() < 2) {
357  throw XSkippedRecordError(i18n("Two channels needed in DSO"), __FILE__, __LINE__);
358  }
359  if( !shot_dso[ *dso__->singleSequence()]) {
360  m_statusPrinter->printWarning(i18n("Use sequential average in DSO."));
361  }
362  int dso_len = shot_dso[ *dso__].length();
363 
364  double interval = shot_dso[ *dso__].timeInterval();
365  if (interval <= 0) {
366  throw XSkippedRecordError(i18n("Invalid time interval in waveforms."), __FILE__, __LINE__);
367  }
368  int pos = lrint(shot_this[ *fromTrig()] *1e-3 / interval + shot_dso[ *dso__].trigPos());
369  double starttime = (pos - shot_dso[ *dso__].trigPos()) * interval;
370  if(pos >= dso_len) {
371  throw XSkippedRecordError(i18n("Position beyond waveforms."), __FILE__, __LINE__);
372  }
373  if(pos < 0) {
374  throw XSkippedRecordError(i18n("Position beyond waveforms."), __FILE__, __LINE__);
375  }
376  int length = lrint( shot_this[ *width()] / 1000 / interval);
377  if(pos + length >= dso_len) {
378  throw XSkippedRecordError(i18n("Invalid length."), __FILE__, __LINE__);
379  }
380  if(length <= 0) {
381  throw XSkippedRecordError(i18n("Invalid length."), __FILE__, __LINE__);
382  }
383 
384  int bgpos = lrint((shot_this[ *bgPos()] - shot_this[ *fromTrig()]) / 1000 / interval);
385  if(pos + bgpos >= dso_len) {
386  throw XSkippedRecordError(i18n("Position for BG. sub. beyond waveforms."), __FILE__, __LINE__);
387  }
388  if(bgpos < 0) {
389  throw XSkippedRecordError(i18n("Position for BG. sub. beyond waveforms."), __FILE__, __LINE__);
390  }
391  int bglength = lrint(shot_this[ *bgWidth()] / 1000 / interval);
392  if(pos + bgpos + bglength >= dso_len) {
393  throw XSkippedRecordError(i18n("Invalid Length for BG. sub."), __FILE__, __LINE__);
394  }
395  if(bglength < 0) {
396  throw XSkippedRecordError(i18n("Invalid Length for BG. sub."), __FILE__, __LINE__);
397  }
398 
399  shared_ptr<XPulser> pulse__(shot_this[ *pulser()]);
400 
401  int echoperiod = lrint(shot_this[ *echoPeriod()] / 1000 /interval);
402  int numechoes = shot_this[ *numEcho()];
403  numechoes = std::max(1, numechoes);
404  bool bg_after_last_echo = (echoperiod < bgpos + bglength);
405 
406  if(bglength && (bglength < length * (bg_after_last_echo ? numechoes : 1) * 3))
407  m_statusPrinter->printWarning(i18n("Maybe, length for BG. sub. is too short."));
408 
409  if((bgpos < length + (bg_after_last_echo ? (echoperiod * (numechoes - 1)) : 0))
410  && (bgpos + bglength > 0))
411  m_statusPrinter->printWarning(i18n("Maybe, position for BG. sub. is overrapped against echoes"), true);
412 
413  if(numechoes > 1) {
414  if(pos + echoperiod * (numechoes - 1) + length >= dso_len) {
415  throw XSkippedRecordError(i18n("Invalid Multiecho settings."), __FILE__, __LINE__);
416  }
417  if(echoperiod < length) {
418  throw XSkippedRecordError(i18n("Invalid Multiecho settings."), __FILE__, __LINE__);
419  }
420  if( !bg_after_last_echo) {
421  if(bgpos + bglength > echoperiod) {
422  throw XSkippedRecordError(i18n("Invalid Multiecho settings."), __FILE__, __LINE__);
423  }
424  if(pos + echoperiod * (numechoes - 1) + bgpos + bglength >= dso_len) {
425  throw XSkippedRecordError(i18n("Invalid Multiecho settings."), __FILE__, __LINE__);
426  }
427  }
428  if(pulse__) {
429  if((numechoes > shot_others[ *pulse__].echoNum()) ||
430  (fabs(shot_this[ *echoPeriod()] * 1e3 / (shot_others[ *pulse__].tau() * 2.0) - 1.0) > 1e-4)) {
431  m_statusPrinter->printWarning(i18n("Invalid Multiecho settings."), true);
432  }
433  }
434  }
435  if(pulse__) {
436  if(shot_others[ *pulse__].isPulseAnalyzerMode()) {
437  m_statusPrinter->printWarning(i18n("Built-In Network Analyzer Mode."), false);
438  throw XSkippedRecordError(__FILE__, __LINE__);
439  }
440  }
441 
442  if((shot_this[ *this].m_startTime != starttime) || (length != shot_this[ *this].m_waveWidth)) {
443  double t = length * interval * 1e3;
444  tr[ *tr[ *waveGraph()].axisx()->autoScale()] = false;
445  tr[ *tr[ *waveGraph()].axisx()->minValue()] = starttime * 1e3 - t * 0.3;
446  tr[ *tr[ *waveGraph()].axisx()->maxValue()] = starttime * 1e3 + t * 1.3;
447  }
448  tr[ *this].m_waveWidth = length;
449  bool skip = (shot_this[ *this].m_timeClearRequested > shot_dso[ *dso__].timeAwared());
450  bool avgclear = skip;
451 
452  if(interval != shot_this[ *this].m_interval) {
453  //[sec]
454  tr[ *this].m_interval = interval;
455  avgclear = true;
456  }
457  if(shot_this[ *this].m_startTime != starttime) {
458  //[sec]
459  tr[ *this].m_startTime = starttime;
460  avgclear = true;
461  }
462 
463  if(length > (int)shot_this[ *this].m_waveSum.size()) {
464  avgclear = true;
465  }
466  tr[ *this].m_wave.resize(length);
467  tr[ *this].m_waveSum.resize(length);
468  int fftlen = FFT::fitLength(shot_this[ *fftLen()]);
469  if(fftlen != shot_this[ *this].m_darkPSD.size()) {
470  avgclear = true;
471  }
472  if(length > fftlen) {
473  throw XSkippedRecordError(i18n("FFT length is too short."), __FILE__, __LINE__);
474  }
475  tr[ *this].m_darkPSD.resize(fftlen);
476  tr[ *this].m_darkPSDSum.resize(fftlen);
477  std::fill(tr[ *this].m_wave.begin(), tr[ *this].m_wave.end(), std::complex<double>(0.0));
478 
479  // Phase Inversion Cycling
480  bool picenabled = shot_this[ *m_picEnabled];
481  bool inverted = false;
482  if(picenabled && ( !pulse__ || !shot_others[ *pulse__].time())) {
483  picenabled = false;
484  gErrPrint(getLabel() + ": " + i18n("No active pulser!"));
485  }
486  if(pulse__) {
487  inverted = shot_others[ *pulse__].invertPhase();
488  }
489 
490  int avgnum = std::max((unsigned int)shot_this[ *extraAvg()], 1u) * (picenabled ? 2 : 1);
491 
492  if( !shot_this[ *exAvgIncr()] && (avgnum <= shot_this[ *this].m_avcount)) {
493  avgclear = true;
494  }
495  if(avgclear) {
496  std::fill(tr[ *this].m_waveSum.begin(), tr[ *this].m_waveSum.end(), std::complex<double>(0.0));
497  std::fill(tr[ *this].m_darkPSDSum.begin(), tr[ *this].m_darkPSDSum.end(), 0.0);
498  tr[ *this].m_avcount = 0;
499  if(shot_this[ *exAvgIncr()]) {
500  tr[ *extraAvg()] = 0;
501  }
502  }
503 
504  if(skip) {
505  throw XSkippedRecordError(__FILE__, __LINE__);
506  }
507 
508  tr[ *this].m_dsoWave.resize(dso_len);
509  std::complex<double> *dsowave( &tr[ *this].m_dsoWave[0]);
510  {
511  const double *rawwavecos, *rawwavesin = NULL;
512  assert(shot_dso[ *dso__].numChannels() );
513  rawwavecos = shot_dso[ *dso__].wave(0);
514  rawwavesin = shot_dso[ *dso__].wave(1);
515  for(unsigned int i = 0; i < dso_len; i++) {
516  dsowave[i] = std::complex<double>(rawwavecos[i], rawwavesin[i]) * (inverted ? -1.0 : 1.0);
517  }
518  }
519  tr[ *this].m_dsoWaveStartPos = pos;
520 
521  //Background subtraction or dynamic noise reduction
522  if(bg_after_last_echo)
523  backgroundSub(tr, tr[ *this].m_dsoWave, pos, length, bgpos, bglength);
524  for(int i = 1; i < numechoes; i++) {
525  int rpos = pos + i * echoperiod;
526  for(int j = 0;
527  j < ( !bg_after_last_echo ? std::max(bgpos + bglength, length) : length); j++) {
528  int k = rpos + j;
529  assert(k < dso_len);
530  if(i == 1)
531  dsowave[pos + j] /= (double)numechoes;
532  dsowave[pos + j] += dsowave[k] / (double)numechoes;
533  }
534  }
535  //Background subtraction or dynamic noise reduction
536  if( !bg_after_last_echo)
537  backgroundSub(tr, tr[ *this].m_dsoWave, pos, length, bgpos, bglength);
538 
539  std::complex<double> *wavesum( &tr[ *this].m_waveSum[0]);
540  double *darkpsdsum( &tr[ *this].m_darkPSDSum[0]);
541  //Incremental/Sequential average.
542  if((emitter == dso__.get()) || ( !shot_this[ *this].m_avcount)) {
543  for(int i = 0; i < length; i++) {
544  wavesum[i] += dsowave[pos + i];
545  }
546  if(bglength) {
547  //Estimates power spectral density in the background.
548  if( !shot_this[ *this].m_ftDark ||
549  (shot_this[ *this].m_ftDark->length() != shot_this[ *this].m_darkPSD.size())) {
550  tr[ *this].m_ftDark.reset(new FFT(-1, shot_this[ *fftLen()]));
551  }
552  std::vector<std::complex<double> > darkin(fftlen, 0.0), darkout(fftlen);
553  int bginplen = std::min(bglength, fftlen);
554  double normalize = 0.0;
555  //Twists background not to be affected by the dc subtraction.
556  for(int i = 0; i < bginplen; i++) {
557  double tw = sin(2.0*M_PI*i/(double)bginplen);
558  darkin[i] = dsowave[pos + i + bgpos] * tw;
559  normalize += tw * tw;
560  }
561  normalize = 1.0 / normalize * interval;
562  tr[ *this].m_ftDark->exec(darkin, darkout);
563  //Convolution for the rectangular window.
564  for(int i = 0; i < fftlen; i++) {
565  darkin[i] = std::norm(darkout[i]) * normalize;
566  }
567  tr[ *this].m_ftDark->exec(darkin, darkout); //FT of PSD.
568  std::vector<std::complex<double> > sigma2(darkout);
569  std::fill(darkin.begin(), darkin.end(), std::complex<double>(0.0));
570  double x = sqrt(1.0 / length / fftlen);
571  for(int i = 0; i < length; i++) {
572  darkin[i] = x;
573  }
574  tr[ *this].m_ftDark->exec(darkin, darkout); //FT of rect. window.
575  for(int i = 0; i < fftlen; i++) {
576  darkin[i] = std::norm(darkout[i]);
577  }
578  tr[ *this].m_ftDark->exec(darkin, darkout); //FT of norm of (FT of rect. window).
579  for(int i = 0; i < fftlen; i++) {
580  darkin[i] = std::conj(darkout[i] * sigma2[i]);
581  }
582  tr[ *this].m_ftDark->exec(darkin, darkout); //Convolution.
583  normalize = 1.0 / fftlen;
584  for(int i = 0; i < fftlen; i++) {
585  darkpsdsum[i] += std::real(darkout[i]) * normalize; //[V^2/Hz]
586  }
587  }
588  tr[ *this].m_avcount++;
589  if( shot_this[ *exAvgIncr()]) {
590  tr[ *extraAvg()] = shot_this[ *this].m_avcount;
591  }
592  }
593  std::complex<double> *wave( &tr[ *this].m_wave[0]);
594  double normalize = 1.0 / shot_this[ *this].m_avcount;
595  for(int i = 0; i < length; i++) {
596  wave[i] = wavesum[i] * normalize;
597  }
598  double darknormalize = normalize * normalize;
599  if(bg_after_last_echo)
600  darknormalize /= (double)numechoes;
601  double *darkpsd( &tr[ *this].m_darkPSD[0]);
602  for(int i = 0; i < fftlen; i++) {
603  darkpsd[i] = darkpsdsum[i] * darknormalize;
604  }
605  int ftpos = lrint( shot_this[ *fftPos()] * 1e-3 / interval + shot_dso[ *dso__].trigPos() - pos);
606 
607  if(shot_this[ *difFreq()] != 0.0) {
608  //Digital IF.
609  double omega = -2.0 * M_PI * shot_this[ *difFreq()] * 1e3 * interval;
610  for(int i = 0; i < length; i++) {
611  wave[i] *= std::polar(1.0, omega * (i - ftpos));
612  }
613  }
614 
615  // if((windowfunc != &windowFuncRect) && (abs(ftpos - length/2) > length*0.1))
616  // m_statusPrinter->printWarning(i18n("FFTPos is off-centered for window func."));
617  double ph = shot_this[ *phaseAdv()] * M_PI / 180;
618  tr[ *this].m_waveFTPos = ftpos;
619  //[Hz]
620  tr[ *this].m_dFreq = 1.0 / fftlen / interval;
621  tr[ *this].m_ftWave.resize(fftlen);
622 
623  rotNFFT(tr, ftpos, ph, tr[ *this].m_wave, tr[ *this].m_ftWave); //Generates a new SpectrumSolver.
624  const SpectrumSolver &solver(shot_this[ *m_solver].solver());
625  if(solver.peaks().size()) {
626  entryPeakAbs()->value(tr,
627  solver.peaks()[0].first / (double)shot_this[ *this].m_wave.size());
628  double x = solver.peaks()[0].second;
629  x = (x > fftlen / 2) ? (x - fftlen) : x;
630  entryPeakFreq()->value(tr,
631  0.001 * x * shot_this[ *this].m_dFreq);
632  }
633 
634  m_isPulseInversionRequested = picenabled && (shot_this[ *this].m_avcount % 2 == 1) && (emitter == dso__.get());
635 
636  if( !shot_this[ *exAvgIncr()] && (avgnum != shot_this[ *this].m_avcount))
637  throw XSkippedRecordError(__FILE__, __LINE__);
638 }
640  iterate_commit_while([=](Transaction &tr)->bool{
641  Snapshot &shot(tr);
642  if(shot[ *this].time() && shot[ *this].m_avcount)
643  return false;
644 
645  tr[ *ftWaveGraph()].clearPoints();
646  tr[ *waveGraph()].clearPoints();
647  tr[ *m_peakPlot->maxCount()] = 0;
648  return true;
649  });
650 
651  if(m_isPulseInversionRequested.compare_set_strong((int)true, (int)false)) {
652  shared_ptr<XPulser> pulse__ = shot[ *pulser()];
653  if(pulse__) {
654  pulse__->iterate_commit([=](Transaction &tr){
655  if(tr[ *pulse__].time()) {
656  tr[ *pulse__->invertPhase()] = !tr[ *pulse__->invertPhase()];
657  }
658  });
659  }
660  }
661 
662  iterate_commit([=](Transaction &tr){
663  Snapshot &shot(tr);
664  const SpectrumSolver &solver(shot[ *m_solver].solver());
665 
666  int ftsize = shot[ *this].m_ftWave.size();
667 
668  tr[ *ftWaveGraph()].setRowCount(ftsize);
669  double normalize = 1.0 / shot[ *this].m_wave.size();
670  double darknormalize =
671  shot[ *this].m_ftWavePSDCoeff / (shot[ *this].m_wave.size() * shot[ *this].interval());
672  double dfreq = shot[ *this].m_dFreq;
673  const double *darkpsd( &shot[ *this].m_darkPSD[0]);
674  const std::complex<double> *ftwave( &shot[ *this].m_ftWave[0]);
675  double *colf(tr[ *ftWaveGraph()].cols(0));
676  double *colr(tr[ *ftWaveGraph()].cols(1));
677  double *coli(tr[ *ftWaveGraph()].cols(2));
678  double *colabs(tr[ *ftWaveGraph()].cols(3));
679  double *colarg(tr[ *ftWaveGraph()].cols(4));
680  double *coldark(tr[ *ftWaveGraph()].cols(5));
681  for (int i = 0; i < ftsize; i++) {
682  int j = (i - ftsize/2 + ftsize) % ftsize;
683  colf[i] = 0.001 * (i - ftsize/2) * dfreq;
684  std::complex<double> z = ftwave[j] * normalize;
685  colr[i] = std::real(z);
686  coli[i] = std::imag(z);
687  colabs[i] = std::abs(z);
688  colarg[i] = std::arg(z) / M_PI * 180;
689  coldark[i] = sqrt(darkpsd[j] * darknormalize);
690  }
691  const std::vector<std::pair<double, double> > &peaks(solver.peaks());
692  int peaks_size = peaks.size();
693  tr[ *m_peakPlot->maxCount()] = peaks_size;
694  auto &points(tr[ *m_peakPlot].points());
695  points.resize(peaks_size);
696  for(int i = 0; i < peaks_size; i++) {
697  double x = peaks[i].second;
698  x = (x > ftsize / 2) ? (x - ftsize) : x;
699  points[i] = XGraph::ValPoint(0.001 * x * dfreq, peaks[i].first * normalize);
700  }
701  ftWaveGraph()->drawGraph(tr);
702 
703  int length = shot[ *this].m_dsoWave.size();
704  const std::complex<double> *dsowave( &shot[ *this].m_dsoWave[0]);
705  if(solver.ifft().size() < ftsize)
706  return; //solver has been failed.
707  const std::complex<double> *ifft( &solver.ifft()[0]);
708  int dsowavestartpos = shot[ *this].m_dsoWaveStartPos;
709  double interval = shot[ *this].m_interval;
710  double starttime = shot[ *this].startTime();
711  int waveftpos = shot[ *this].m_waveFTPos;
712  tr[ *waveGraph()].setRowCount(length);
713  double *colt(tr[ *waveGraph()].cols(0));
714  double *colfr(tr[ *waveGraph()].cols(1));
715  double *colfi(tr[ *waveGraph()].cols(2));
716  double *colrr(tr[ *waveGraph()].cols(3));
717  double *colri(tr[ *waveGraph()].cols(4));
718  for (int i = 0; i < length; i++) {
719  int j = i - dsowavestartpos;
720  colt[i] = (starttime + j * interval) * 1e3;
721  if(abs(j) < ftsize / 2) {
722  j = (j - waveftpos + ftsize) % ftsize;
723  colfr[i] = std::real(ifft[j]);
724  colfi[i] = std::imag(ifft[j]);
725  }
726  else {
727  colfr[i] = 0.0;
728  colfi[i] = 0.0;
729  }
730  colrr[i] = dsowave[i].real();
731  colri[i] = dsowave[i].imag();
732  }
733  waveGraph()->drawGraph(tr);
734  });
735 }
736 

Generated for KAME4 by  doxygen 1.8.3