15 #include "nmrrelaxfit.h"
16 #include "ui_nmrrelaxform.h"
18 #include "pulserdriver.h"
21 #include "graphwidget.h"
24 REGISTER_TYPE(
XDriverList, NMRT1,
"NMR relaxation measurement");
26 #include <QPushButton>
30 const char XNMRT1::P1DIST_LINEAR[] =
"Linear";
31 const char XNMRT1::P1DIST_LOG[] =
"Log";
32 const char XNMRT1::P1DIST_RECIPROCAL[] =
"Reciprocal";
34 const char XNMRT1::P1STRATEGY_RANDOM[] =
"Random";
35 const char XNMRT1::P1STRATEGY_FLATTEN[] =
"Flatten";
42 const shared_ptr<XNMRT1> &owner)
43 :
XFuncPlot(name, runtime, tr, graph), m_item(item), m_owner(owner)
46 virtual double func(
double t)
const {
47 shared_ptr<XNMRT1> owner = m_owner.lock();
50 shared_ptr<XRelaxFunc> func1 = shot[ *m_item];
53 double it1 = shot[ *owner].m_params[0];
54 double c = shot[ *owner].m_params[1];
55 double a = shot[ *owner].m_params[2];
56 func1->relax( &f, &df, t, it1);
60 shared_ptr<XItemNode < XRelaxFuncList, XRelaxFunc > > m_item;
61 weak_ptr<XNMRT1> m_owner;
64 XNMRT1::XNMRT1(
const char *name,
bool runtime,
65 Transaction &tr_meas,
const shared_ptr<XMeasure> &meas)
69 dynamic_pointer_cast<
XDriver>(shared_from_this()))),
71 dynamic_pointer_cast<
XDriver>(shared_from_this()))),
73 "Pulser", false, ref(tr_meas), meas->drivers(), true)),
75 "NMRPulseAnalyzer1", false, ref(tr_meas), meas->drivers(), true)),
77 "NMRPulseAnalyzer2", false, ref(tr_meas), meas->drivers())),
78 m_active(create<
XBoolNode>(
"Active", false)),
79 m_autoPhase(create<
XBoolNode>(
"AutoPhase", false)),
80 m_mInftyFit(create<
XBoolNode>(
"MInftyFit", false)),
81 m_absFit(create<
XBoolNode>(
"AbsFit", false)),
82 m_trackPeak(create<
XBoolNode>(
"TrackPeakFreq", false)),
87 m_phase(create<
XDoubleNode>(
"Phase", false,
"%.2f")),
89 m_windowFunc(create<
XComboNode>(
"WindowFunc", false, true)),
90 m_autoWindow(create<
XBoolNode>(
"AutoWindow", false)),
91 m_windowWidth(create<
XComboNode>(
"WindowWidth", false, true)),
92 m_mode(create<
XComboNode>(
"Mode", false, true)),
93 m_smoothSamples(create<
XUIntNode>(
"SmoothSamples", false)),
94 m_p1Strategy(create<
XComboNode>(
"P1Strategy", false, true)),
95 m_p1Dist(create<
XComboNode>(
"P1Dist", false, true)),
98 m_fitStatus(create<
XStringNode>(
"FitStatus", true)),
102 m_wave(create<
XWaveNGraph>(
"Wave", true, m_form->m_graph, m_form->m_edDump, m_form->m_tbDump, m_form->m_btnDump)) {
105 m_relaxFunc = create<XItemNode < XRelaxFuncList, XRelaxFunc > >(
106 tr,
"RelaxFunc",
false, ref(tr), m_relaxFuncs,
true);
109 m_form->m_btnClear->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogResetButton));
110 m_form->m_btnResetFit->setIcon(QApplication::style()->standardIcon(QStyle::SP_BrowserReload));
112 m_form->setWindowTitle(i18n(
"NMR Relaxation Measurement - ") +
getLabel() );
114 meas->scalarEntries()->insert(tr_meas, t1inv());
115 meas->scalarEntries()->insert(tr_meas, t1invErr());
122 tr[ *m_windowFunc] = SpectrumSolverWrapper::WINDOW_FUNC_HAMMING;
123 m_windowWidthList = {0.25, 0.5, 1.0, 1.5, 2.0};
124 tr[ *m_windowWidth].add({
"25%",
"50%",
"100%",
"150%",
"200%"});
125 tr[ *m_windowWidth] = 2;
127 const char *labels[] = {
"P1 [ms] or 2Tau [us]",
"Intens [V]",
128 "Weight [1/V]",
"Abs [V]",
"Re [V]",
"Im [V]"};
129 tr[ *m_wave].setColCount(6, labels);
130 tr[ *m_wave].insertPlot(i18n(
"Relaxation"), 0, 1, -1, 2);
131 tr[ *m_wave].insertPlot(i18n(
"Out-of-Phase"), 0, 5, -1, 2);
132 shared_ptr<XAxis> axisx = tr[ *m_wave].axisx();
133 shared_ptr<XAxis> axisy = tr[ *m_wave].axisy();
134 tr[ *axisx->logScale()] =
true;
135 tr[ *axisy->label()] = i18n(
"Relaxation");
136 tr[ *tr[ *m_wave].plot(0)->drawLines()] =
false;
137 tr[ *tr[ *m_wave].plot(1)->drawLines()] =
false;
138 tr[ *tr[ *m_wave].plot(1)->intensity()] = 0.6;
139 shared_ptr<XFuncPlot> plot3 = m_wave->graph()->plots()->create<
XRelaxFuncPlot>(
140 tr,
"FittedCurve",
true, ref(tr), m_wave->graph(),
141 relaxFunc(), static_pointer_cast<
XNMRT1>(shared_from_this()));
142 tr[ *plot3->label()] = i18n(
"Fitted Curve");
143 tr[ *plot3->axisX()] = axisx;
144 tr[ *plot3->axisY()] = axisy;
145 tr[ *plot3->drawPoints()] =
false;
146 tr[ *plot3->pointColor()] = clBlue;
147 tr[ *plot3->lineColor()] = clBlue;
151 tr[ *m_wave].clearPoints();
153 tr[ *mode()].add({
"T1 Measurement",
"T2 Measurement",
"St.E. Measurement"});
154 tr[ *mode()] = MEAS_T1;
156 tr[ *p1Strategy()].add({P1STRATEGY_RANDOM, P1STRATEGY_FLATTEN});
157 tr[ *p1Strategy()] = 1;
159 tr[ *p1Dist()].add({P1DIST_LINEAR, P1DIST_LOG, P1DIST_RECIPROCAL});
162 tr[ *relaxFunc()].str(
XString(
"NMR I=1/2"));
164 tr[ *p1Max()] = 100.0;
165 tr[ *autoPhase()] =
true;
166 tr[ *autoWindow()] =
true;
167 tr[ *mInftyFit()] =
true;
168 tr[ *trackPeak()] =
false;
169 tr[ *smoothSamples()] = 40;
172 m_form->m_dblPhase->setRange( -360.0, 360.0);
173 m_form->m_dblPhase->setSingleStep(10.0);
176 xqcon_create<XQLineEditConnector>(m_p1Min, m_form->m_edP1Min),
177 xqcon_create<XQLineEditConnector>(m_p1Max, m_form->m_edP1Max),
178 xqcon_create<XQLineEditConnector>(m_p1Next, m_form->m_edP1Next),
179 xqcon_create<XQDoubleSpinBoxConnector>(m_phase, m_form->m_dblPhase, m_form->m_slPhase),
180 xqcon_create<XQLineEditConnector>(m_freq, m_form->m_edFreq),
181 xqcon_create<XQToggleButtonConnector>(m_autoWindow, m_form->m_ckbAutoWindow),
182 xqcon_create<XQComboBoxConnector>(m_windowFunc, m_form->m_cmbWindowFunc,
Snapshot( *m_windowFunc)),
183 xqcon_create<XQComboBoxConnector>(m_windowWidth, m_form->m_cmbWindowWidth,
Snapshot( *m_windowWidth)),
184 xqcon_create<XQLineEditConnector>(m_smoothSamples, m_form->m_edSmoothSamples),
185 xqcon_create<XQComboBoxConnector>(m_p1Strategy, m_form->m_cmbP1Strategy,
Snapshot( *m_p1Strategy)),
186 xqcon_create<XQComboBoxConnector>(m_p1Dist, m_form->m_cmbP1Dist,
Snapshot( *m_p1Dist)),
187 xqcon_create<XQButtonConnector>(m_clearAll, m_form->m_btnClear),
188 xqcon_create<XQButtonConnector>(m_resetFit, m_form->m_btnResetFit),
189 xqcon_create<XQToggleButtonConnector>(m_active, m_form->m_ckbActive),
190 xqcon_create<XQToggleButtonConnector>(m_autoPhase, m_form->m_ckbAutoPhase),
191 xqcon_create<XQToggleButtonConnector>(m_mInftyFit, m_form->m_ckbMInftyFit),
192 xqcon_create<XQToggleButtonConnector>(m_absFit, m_form->m_ckbAbsFit),
193 xqcon_create<XQToggleButtonConnector>(m_trackPeak, m_form->m_ckbTrackPeak),
194 xqcon_create<XQTextBrowserConnector>(m_fitStatus, m_form->m_txtFitStatus),
195 xqcon_create<XQComboBoxConnector>(m_relaxFunc, m_form->m_cmbRelaxFunc,
Snapshot( *m_relaxFuncs)),
196 xqcon_create<XQComboBoxConnector>(m_mode, m_form->m_cmbMode,
Snapshot( *m_mode)),
197 xqcon_create<XQComboBoxConnector>(m_pulser, m_form->m_cmbPulser, ref(tr_meas)),
198 xqcon_create<XQComboBoxConnector>(m_pulse1, m_form->m_cmbPulse1, ref(tr_meas)),
199 xqcon_create<XQComboBoxConnector>(m_pulse2, m_form->m_cmbPulse2, ref(tr_meas))
203 m_lsnOnActiveChanged = tr[ *active()].onValueChanged().connectWeakly(
204 shared_from_this(), &XNMRT1::onActiveChanged);
205 m_lsnOnP1CondChanged = tr[ *p1Max()].onValueChanged().connectWeakly(
206 shared_from_this(), &XNMRT1::onP1CondChanged);
207 for(
auto &&x: std::vector<shared_ptr<XValueNodeBase>>(
208 {p1Min(), p1Strategy(), p1Dist(), smoothSamples()}))
209 tr[ *x].onValueChanged().connect(m_lsnOnP1CondChanged);
210 m_lsnOnCondChanged = tr[ *phase()].onValueChanged().connectWeakly(
211 shared_from_this(), &XNMRT1::onCondChanged);
212 for(
auto &&x: std::vector<shared_ptr<XValueNodeBase>>(
213 {mInftyFit(), absFit(), relaxFunc(), autoPhase(), freq(), autoWindow(),
214 windowFunc(), windowWidth(), mode()}))
215 tr[ *x].onValueChanged().connect(m_lsnOnCondChanged);
217 m_lsnOnClearAll = tr[ *m_clearAll].onTouch().connectWeakly(
218 shared_from_this(), &XNMRT1::onClearAll);
219 m_lsnOnResetFit = tr[ *m_resetFit].onTouch().connectWeakly(
220 shared_from_this(), &XNMRT1::onResetFit);
225 m_form->showNormal();
231 trans( *this).m_timeClearRequested = XTime::now();
235 XNMRT1::distributeP1(
const Snapshot &shot,
double uniform_x_0_to_1) {
236 double p1min = shot[ *p1Min()];
237 double p1max = shot[ *p1Max()];
239 if(shot[ *p1Dist()].to_str() == P1DIST_LINEAR)
240 p1 = (1-(uniform_x_0_to_1)) * p1min + (uniform_x_0_to_1) * p1max;
241 else if(shot[ *p1Dist()].to_str() == P1DIST_LOG)
242 p1 = p1min * exp((uniform_x_0_to_1) * log(p1max/p1min));
245 p1 =1/((1-uniform_x_0_to_1)/p1min + (uniform_x_0_to_1)/p1max);
247 p1 = llrint(p1 * 1e4) / 1e4;
254 double x = randMT19937();
255 double p1min = shot[ *p1Min()];
256 double p1max = shot[ *p1Max()];
257 if((p1min <= 0) || (p1min >= p1max)) {
258 gErrPrint(i18n(
"Invalid P1Min or P1Max."));
261 tr[ *
this].m_params[0] = 1.0 / distributeP1(shot, x);
262 tr[ *
this].m_params[1] = 0.1;
263 tr[ *
this].m_params[2] = 0.0;
271 if(shot[ *p1Strategy()] .to_str() == P1STRATEGY_RANDOM) {
272 x_0_to_1 = randMT19937();
276 int samples = shot[ *
this].m_sumpts.size();
282 double p1min = shot[ *p1Min()];
283 double p1max = shot[ *p1Max()];
284 int lb = 0, ub = samples;
285 double k_0 = samples / log(p1max/p1min);
286 int idx_p1next = lrint(log(shot[ *p1Next()] / p1min) * k_0);
287 idx_p1next = std::min(std::max(idx_p1next, 0), samples);
288 bool p1dist_linear = (shot[ *p1Dist()].to_str() == P1DIST_LINEAR);
289 bool p1dist_log = (shot[ *p1Dist()].to_str() == P1DIST_LOG);
290 const auto &sumpts = shot[ *
this].m_sumpts;
296 double xlb = exp(lb / k_0) * p1min;
297 double xub = exp(ub / k_0) * p1min;
300 xhalf = (xlb + xub) / 2;
302 xhalf = 1.0/((1/xlb + 1/xub) / 2);
303 mid = lrint(log(xhalf / p1min) * k_0);
305 assert((mid >= lb) && (mid <= ub));
307 for(
int idx = lb; idx < mid; ++idx)
308 isigma_0 += sumpts[idx].isigma;
310 for(
int idx = mid; idx < ub; ++idx)
311 isigma_1 += sumpts[idx].isigma;
312 if((lb <= idx_p1next) && (mid > idx_p1next))
314 if((mid <= idx_p1next) && (ub > idx_p1next))
316 if(isigma_0 == isigma_1) {
317 if(randMT19937() < 0.5)
323 if(isigma_0 < isigma_1)
329 x_0_to_1 = (double)lb / samples;
335 tr[ *p1Next()] = distributeP1(shot, x_0_to_1);
336 tr[ *p1AltNext()] = distributeP1(shot, 1 - x_0_to_1);
343 double p1min = shot[ *p1Min()];
344 double p1max = shot[ *p1Max()];
345 if((p1min <= 0) || (p1min >= p1max)) {
346 gErrPrint(i18n(
"Invalid P1Min or P1Max."));
357 (node == mode().
get()) ||
358 (node == freq().
get())) {
359 trans( *this).m_timeClearRequested = XTime::now();
365 const std::vector< std::complex<double> >&wave,
int origin,
double cf,
366 std::deque<std::complex<double> > &value_by_cond) {
369 value_by_cond.clear();
370 std::deque<FFT::twindowfunc> funcs;
371 m_solver->windowFuncs(funcs);
374 for(std::deque<double>::const_iterator wit = m_windowWidthList.begin(); wit != m_windowWidthList.end(); wit++) {
375 for(std::deque<FFT::twindowfunc>::iterator fit = funcs.begin(); fit != funcs.end(); fit++) {
376 if(shot_this[ *
this].m_convolutionCache.size() <= idx) {
377 tr[ *
this].m_convolutionCache.push_back(
378 std::make_shared<Payload::ConvolutionCache>());
380 shared_ptr<Payload::ConvolutionCache> cache = tr[ *
this].m_convolutionCache[idx];
381 if((cache->windowwidth != *wit) || (cache->origin != origin) ||
382 (cache->windowfunc != *fit) || (cache->cfreq != cf) ||
383 (cache->wave.size() != wave.size())) {
384 cache->windowwidth = *wit;
385 cache->origin = origin;
386 cache->windowfunc = *fit;
389 cache->wave.resize(wave.size());
390 double wk = 1.0 / FFTSolver::windowLength(wave.size(), -origin, *wit);
391 for(
int i = 0; i < (int)wave.size(); i++) {
392 double w = ( *fit)((i - origin) * wk) / (double)wave.size();
393 cache->wave[i] = std::polar(w, -2.0*M_PI*cf*(i - origin));
398 std::complex<double> z(0.0);
399 for(
int i = 0; i < (int)cache->wave.size(); i++) {
400 z += wave[i] * cache->wave[i];
405 value_by_cond.push_back(z);
414 shared_ptr<XPulser> pulser__ = shot_this[ *pulser()];
415 shared_ptr<XNMRPulseAnalyzer> pulse1__ = shot_this[ *pulse1()];
416 shared_ptr<XNMRPulseAnalyzer> pulse2__ = shot_this[ *pulse2()];
417 if( !pulser__ || !pulse1__)
return false;
418 if(emitter ==
this)
return true;
419 if(emitter == pulser__.get())
return false;
420 assert((emitter == pulse1__.get()) || (emitter == pulse2__.get()));
422 const Snapshot &shot_pulse1((emitter == pulse1__.get()) ? shot_emitter : shot_others);
423 const Snapshot &shot_pulse2((emitter == pulse2__.get()) ? shot_emitter : shot_others);
425 if(shot_others[ *pulser__].time() > shot_pulse1[ *pulse1__].time())
return false;
427 switch(shot_others[ *pulser__].combMode()) {
429 if(emitter == pulse2__.get())
432 case XPulser::N_COMB_MODE_COMB_ALT:
433 case XPulser::N_COMB_MODE_P1_ALT:
435 m_statusPrinter->printError(i18n(
"2 Pulse Analyzers needed."));
438 if(shot_pulse1[ *pulse1__].time() != shot_pulse2[ *pulse2__].time())
return false;
446 XDriver *emitter)
throw (XRecordError&) {
449 double p1min = shot_this[ *p1Min()];
450 double p1max = shot_this[ *p1Max()];
452 if((p1min <= 0) || (p1min >= p1max)) {
453 throw XRecordError(i18n(
"Invalid P1Min or P1Max."), __FILE__, __LINE__);
456 int samples = shot_this[ *smoothSamples()];
458 throw XRecordError(i18n(
"Invalid # of Samples."), __FILE__, __LINE__);
460 if(samples >= 100000) {
461 m_statusPrinter->printWarning(i18n(
"Too many Samples."),
true);
464 int mode__ = shot_this[ *mode()];
465 shared_ptr<XNMRPulseAnalyzer> pulse1__ = shot_this[ *pulse1()];
466 shared_ptr<XNMRPulseAnalyzer> pulse2__ = shot_this[ *pulse2()];
467 const Snapshot &shot_pulse1((emitter == pulse1__.get()) ? shot_emitter : shot_others);
468 const Snapshot &shot_pulse2((emitter == pulse2__.get()) ? shot_emitter : shot_others);
470 shared_ptr<XPulser> pulser__ = shot_this[ *pulser()];
471 const Snapshot &shot_pulser(shot_others);
473 if(shot_pulser[ *pulser__].time()) {
481 if((shot_pulser[ *pulser__].tau() != shot_pulser[ *pulser__].combPT()) ||
482 (shot_pulser[ *pulser__].combNum() != 2) ||
483 ( !shot_pulser[ *pulser__->conserveStEPhase()]) ||
484 (shot_pulser[ *pulser__].pw1() != 0.0) ||
485 (shot_pulser[ *pulser__].pw2() != shot_pulser[ *pulser__].combPW()))
486 m_statusPrinter->printWarning(i18n(
"Strange St.E. settings."));
492 if( emitter !=
this) {
494 assert( shot_pulse1[ *pulse1__].time() );
495 assert( shot_pulser[ *pulser__].time() );
496 assert( emitter != pulser__.get() );
498 if(shot_pulse1[ *pulse1__->exAvgIncr()]) {
499 m_statusPrinter->printWarning(i18n(
"Do NOT use incremental avg. Skipping."));
500 throw XSkippedRecordError(__FILE__, __LINE__);
503 std::deque<std::complex<double> > cmp1, cmp2;
504 double cfreq = shot_this[ *freq()] * 1e3 * shot_pulse1[ *pulse1__].interval();
505 if(shot_this[ *trackPeak()]) {
506 if(((mode__ == MEAS_T1) && (shot_pulser[ *pulser__].combP1() > distributeP1(shot_this, 0.66))) ||
507 ((mode__ == MEAS_T2) && (shot_pulser[ *pulser__].combP1() < distributeP1(shot_this, 0.33))) ||
508 shot_this[ *
this].m_sumpts.empty()) {
509 tr[ *freq()] = (double)shot_pulse1[ *pulse1__->entryPeakFreq()->value()];
510 tr.unmark(m_lsnOnCondChanged);
514 analyzeSpectrum(tr, shot_pulse1[ *pulse1__].wave(),
515 shot_pulse1[ *pulse1__].waveFTPos(), cfreq, cmp1);
518 analyzeSpectrum(tr, shot_pulse2[ *pulse2__].wave(),
519 shot_pulse2[ *pulse2__].waveFTPos(), cfreq, cmp2);
520 pt2.value_by_cond.resize(cmp2.size());
522 pt1.value_by_cond.resize(cmp1.size());
523 switch(shot_pulser[ *pulser__].combMode()) {
525 throw XRecordError(i18n(
"Unknown Comb Mode!"), __FILE__, __LINE__);
526 case XPulser::N_COMB_MODE_COMB_ALT:
527 if(mode__ != MEAS_T1)
throw XRecordError(i18n(
"Use T1 mode!"), __FILE__, __LINE__);
529 pt1.p1 = shot_pulser[ *pulser__].combP1();
530 for(
int i = 0; i < cmp1.size(); i++)
531 pt1.value_by_cond[i] = (cmp1[i] - cmp2[i]) / cmp1[i];
532 tr[ *
this].m_pts.push_back(pt1);
534 case XPulser::N_COMB_MODE_P1_ALT:
535 if(mode__ == MEAS_T2)
536 throw XRecordError(i18n(
"Do not use T2 mode!"), __FILE__, __LINE__);
538 pt1.p1 = shot_pulser[ *pulser__].combP1();
539 std::copy(cmp1.begin(), cmp1.end(), pt1.value_by_cond.begin());
540 tr[ *
this].m_pts.push_back(pt1);
541 pt2.p1 = shot_pulser[ *pulser__].combP1Alt();
542 std::copy(cmp2.begin(), cmp2.end(), pt2.value_by_cond.begin());
543 tr[ *
this].m_pts.push_back(pt2);
545 case XPulser::N_COMB_MODE_ON:
546 if(mode__ != MEAS_T2) {
547 pt1.p1 = shot_pulser[ *pulser__].combP1();
548 std::copy(cmp1.begin(), cmp1.end(), pt1.value_by_cond.begin());
549 tr[ *
this].m_pts.push_back(pt1);
552 m_statusPrinter->printWarning(i18n(
"T2 mode with comb pulse!"));
553 case XPulser::N_COMB_MODE_OFF:
554 if(mode__ != MEAS_T2) {
555 m_statusPrinter->printWarning(i18n(
"Do not use T1 mode! Skipping."));
556 throw XSkippedRecordError(__FILE__, __LINE__);
559 pt1.p1 = 2.0 * shot_pulser[ *pulser__].tau();
560 std::copy(cmp1.begin(), cmp1.end(), pt1.value_by_cond.begin());
561 tr[ *
this].m_pts.push_back(pt1);
566 tr[ *
this].m_sumpts.clear();
568 if(shot_this[ *
this].m_timeClearRequested > shot_pulse1[ *pulse1__].timeAwared()) {
569 tr[ *
this].m_pts.clear();
570 tr[ *m_wave].clearPoints();
571 tr[ *m_fitStatus] =
"";
572 trans( *pulse1__->avgClear()).touch();
574 trans( *pulse2__->avgClear()).touch();
575 throw XSkippedRecordError(__FILE__, __LINE__);
578 shared_ptr<XRelaxFunc> func = shot_this[ *relaxFunc()];
580 throw XRecordError(i18n(
"Please select relaxation func."), __FILE__, __LINE__);
583 tr[ *
this].m_sumpts.resize(samples);
584 auto &sumpts(tr[ *
this].m_sumpts);
588 dummy.
c = 0; dummy.p1 = 0; dummy.isigma = 0;
589 dummy.
value_by_cond.resize(shot_this[ *
this].m_convolutionCache.size());
590 std::fill(tr[ *
this].m_sumpts.begin(), tr[ *
this].m_sumpts.end(), dummy);
591 double k = shot_this[ *
this].m_sumpts.size() / log(p1max/p1min);
592 auto pts_begin(shot_this[ *
this].m_pts.begin());
593 auto pts_end(shot_this[ *
this].m_pts.end());
594 int sum_size = (int)shot_this[ *
this].m_sumpts.size();
595 for(
auto it = pts_begin; it != pts_end; it++) {
596 int idx = lrint(log(it->p1 / p1min) * k);
597 if((idx < 0) || (idx >= sum_size))
continue;
600 if(mode__ == MEAS_ST_E)
601 p1 += 3 * shot_pulser[ *pulser__].tau() * 1e-3;
602 sumpts[idx].isigma += 1;
603 sumpts[idx].p1 += p1;
604 for(
unsigned int i = 0; i < it->value_by_cond.size(); i++)
605 sumpts[idx].value_by_cond[i] += it->value_by_cond[i];
609 std::deque<std::complex<double> > sum_c(
610 shot_this[ *
this].m_convolutionCache.size()), corr(shot_this[ *
this].m_convolutionCache.size());
613 for(
auto it = shot_this[ *
this].m_sumpts.begin();
614 it != shot_this[ *
this].m_sumpts.end(); it++) {
615 if(it->isigma == 0)
continue;
616 double t = log10(it->p1 / it->isigma);
617 for(
unsigned int i = 0; i < it->value_by_cond.size(); i++) {
618 sum_c[i] += it->value_by_cond[i];
619 corr[i] += it->value_by_cond[i] * t;
621 sum_t += t * it->isigma;
626 for(
unsigned int i = 0; i < corr.size(); i++) {
627 corr[i] -= sum_c[i]*sum_t/(double)n;
628 corr[i] *= ((mode__ == MEAS_T1) ? 1 : -1);
631 bool absfit__ = shot_this[ *absFit()];
633 std::deque<double> phase_by_cond(corr.size(), shot_this[ *phase()] / 180.0 * M_PI);
636 for(
unsigned int i = 0; i < corr.size(); i++) {
637 if(shot_this[ *autoPhase()]) {
638 phase_by_cond[i] = std::arg(corr[i]);
640 if(shot_this[ *autoWindow()]) {
641 double sn2 = absfit__ ? std::abs(corr[i]) : std::real(corr[i] * std::polar(1.0, -phase_by_cond[i]));
642 sn2 = sn2 * sn2 / shot_this[ *
this].m_convolutionCache[i]->power;
651 for(std::deque<shared_ptr<Payload::ConvolutionCache> >::const_iterator it
652 = shot_this[ *
this].m_convolutionCache.begin();
653 it != shot_this[ *
this].m_convolutionCache.end(); ++it) {
654 if((m_windowWidthList[std::max(0, (
int)shot_this[ *windowWidth()])] == ( *it)->windowwidth) &&
655 (m_solver->windowFunc(shot_this) == ( *it)->windowfunc)) {
661 if(cond >= (shot_this[ *
this].m_convolutionCache.size())) {
662 throw XSkippedRecordError(__FILE__, __LINE__);
664 double ph = phase_by_cond[cond];
665 if(shot_this[ *autoPhase()]) {
666 tr[ *phase()] = ph / M_PI * 180;
667 tr.unmark(m_lsnOnCondChanged);
669 if(shot_this[ *autoWindow()]) {
670 for(
unsigned int i = 0; i < m_windowWidthList.size(); i++) {
671 if(m_windowWidthList[i] == shot_this[ *
this].m_convolutionCache[cond]->windowwidth)
672 tr[ *windowWidth()] = i;
674 std::deque<FFT::twindowfunc> funcs;
675 m_solver->windowFuncs(funcs);
676 for(
unsigned int i = 0; i < funcs.size(); i++) {
677 if(funcs[i] == shot_this[ *
this].m_convolutionCache[cond]->windowfunc)
678 tr[ *windowFunc()] = i;
680 tr.unmark(m_lsnOnCondChanged);
682 std::complex<double> cph(std::polar(1.0, -phase_by_cond[cond]));
683 for(
auto it = sumpts.begin(); it != sumpts.end(); it++) {
684 if(it->isigma == 0)
continue;
685 it->p1 = it->p1 / it->isigma;
686 it->c = it->value_by_cond[cond] * cph / (double)it->isigma;
687 it->var = (absfit__) ? std::abs(it->c) : std::real(it->c);
688 it->isigma = sqrt(it->isigma);
691 tr[ *m_fitStatus] = iterate(tr, func, 4);
693 t1inv()->value(tr, 1000.0 * shot_this[ *
this].m_params[0]);
694 t1invErr()->value(tr, 1000.0 * shot_this[ *
this].m_errors[0]);
697 m_isPulserControlRequested = (emitter !=
this);
700 XNMRT1::setNextP1(
const Snapshot &shot) {
701 shared_ptr<XPulser> pulser__ = shot[ *pulser()];
704 switch(shot[ *mode()]) {
707 tr[ *pulser__->combP1()] = (double)shot[ *p1Next()];
708 tr[ *pulser__->combP1Alt()] = (double)shot[ *p1AltNext()];
711 tr[ *pulser__->tau()] = shot[ *p1Next()] / 2.0;
722 if( !shot[ *
this].time()) {
724 tr[ *m_wave].clearPoints();
730 if(shot[ *active()] && m_isPulserControlRequested.compare_set_strong((
int)
true, (
int)
false)) {
736 switch(shot[ *mode()]) {
744 label =
"T+tau [ms]";
747 tr[ *m_wave].setLabel(0, label.c_str());
748 tr[ *tr[ *m_wave].axisx()->label()] = label;
749 tr[ *m_wave].setRowCount(shot[ *
this].m_sumpts.size());
750 double *colp1 = tr[ *m_wave].cols(0);
751 double *colval = tr[ *m_wave].cols(1);
752 double *colabs = tr[ *m_wave].cols(3);
753 double *colre = tr[ *m_wave].cols(4);
754 double *colim = tr[ *m_wave].cols(5);
755 double *colisigma = tr[ *m_wave].cols(2);
757 for(
auto it = shot[ *
this].m_sumpts.begin();
758 it != shot[ *
this].m_sumpts.end(); it++) {
759 if(it->isigma == 0) {
768 colabs[i] = std::abs(it->c);
769 colre[i] = std::real(it->c);
770 colim[i] = std::imag(it->c);
773 colisigma[i] = it->isigma;
776 m_wave->drawGraph(tr);
783 if(shot_this[ *active()]) {
784 const shared_ptr<XPulser> pulser__ = shot_this[ *pulser()];
785 const shared_ptr<XNMRPulseAnalyzer> pulse1__ = shot_this[ *pulse1()];
786 const shared_ptr<XNMRPulseAnalyzer> pulse2__ = shot_this[ *pulse2()];
788 onClearAll(shot_this, m_clearAll.get());
789 if( !pulser__ || !pulse1__) {
790 gErrPrint(
getLabel() +
": " + i18n(
"No pulser or No NMR Pulse Analyzer."));
797 ((shot_pulser[ *pulser__->combMode()] == XPulser::N_COMB_MODE_COMB_ALT) ||
798 (shot_pulser[ *pulser__->combMode()] == XPulser::N_COMB_MODE_P1_ALT))) {
800 tr[ *pulse2__->fromTrig()] =
801 shot_pulse1[ *pulse1__->fromTrig()] + shot_pulser[ *pulser__->altSep()];
802 tr[ *pulse2__->width()] = (double)shot_pulse1[ *pulse1__->width()];
803 tr[ *pulse2__->phaseAdv()] = (double)shot_pulse1[ *pulse1__->phaseAdv()];
804 tr[ *pulse2__->bgPos()] =
805 shot_pulse1[ *pulse1__->bgPos()] + shot_pulser[ *pulser__->altSep()];
806 tr[ *pulse2__->bgWidth()] = (double)shot_pulse1[ *pulse1__->bgWidth()];
807 tr[ *pulse2__->fftPos()] =
808 shot_pulse1[ *pulse1__->fftPos()] + shot_pulser[ *pulser__->altSep()];
809 tr[ *pulse2__->fftLen()] = (int)shot_pulse1[ *pulse1__->fftLen()];
810 tr[ *pulse2__->windowFunc()] = (int)shot_pulse1[ *pulse1__->windowFunc()];
811 tr[ *pulse2__->usePNR()] = (bool)shot_pulse1[ *pulse1__->usePNR()];
812 tr[ *pulse2__->pnrSolverList()] = (int)shot_pulse1[ *pulse1__->pnrSolverList()];
813 tr[ *pulse2__->solverList()] = (int)shot_pulse1[ *pulse1__->solverList()];
814 tr[ *pulse2__->numEcho()] = (int)shot_pulse1[ *pulse1__->numEcho()];
815 tr[ *pulse2__->echoPeriod()] = (double)shot_pulse1[ *pulse1__->echoPeriod()];
818 setNextP1(shot_this);