OSCR

Spatial Organization of Morpho-Electric Subtypes of Pyramidal Neuron in the Subiculum.

Code ↔ Paper

2 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 2 matches
  1. [1] § Materials and Methods › Electrophysiology ↔ src/stimfit/gui/doc.cpp, lines 3130–3200 · score 0.75 · 20–80 %, AP rise, duration, linear, decay, baseline
  2. [2] § Results › Electrophysiological Diversity of PCA Assigned Subicular PYN Subtypes ↔ src/stimfit/gui/doc.cpp, lines 3130–3200 · score 0.72 · 20–80 %, AP rise, AP peak, decay

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

C++ · 4,140 lines · 148 KB · GPL-2.0 · 2 matches

  1. // This program is free software; you can redistribute it and/or
  2. // modify it under the terms of the GNU General Public License
  3. // as published by the Free Software Foundation; either version 2
  4. // of the License, or (at your option) any later version.
  5. // This program is distributed in the hope that it will be useful,
  6. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. // GNU General Public License for more details.
  9. // You should have received a copy of the GNU General Public License
  10. // along with this program; if not, write to the Free Software
  11. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  12. // doc.cpp
  13. // The document class, derived from both wxDocument and recording
  14. // 2007-12-27, Christoph Schmidt-Hieber, University of Freiburg
  15. // For compilers that support precompilation, includes "wx/wx.h".
  16. #include <wx/wxprec.h>
  17. #include <wx/progdlg.h>
  18. #include <wx/filename.h>
  19. #ifdef __BORLANDC__
  20. #pragma hdrstop
  21. #endif
  22. #include <numeric>
  23. #include <algorithm>
  24. #include <functional>
  25. #include <queue>
  26. #include <cmath>
  27. #include <limits>
  28. #ifndef WX_PRECOMP
  29. #include <wx/wx.h>
  30. #endif
  31. #if !wxUSE_DOC_VIEW_ARCHITECTURE
  32. #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in setup.h!
  33. #endif
  34. #include "../../libstfio/annotation.h"
  35. #include "./app.h"
  36. #include "./view.h"
  37. #include "./parentframe.h"
  38. #include "./childframe.h"
  39. #include "./dlgs/smalldlgs.h"
  40. #include "./dlgs/fitseldlg.h"
  41. #include "./dlgs/eventdlg.h"
  42. #include "./dlgs/cursorsdlg.h"
  43. #include "./../../libstfnum/stfnum.h"
  44. #include "./../../libstfnum/fit.h"
  45. #include "./../../libstfnum/funclib.h"
  46. #include "./../../libstfnum/measure.h"
  47. #include "./../../libstfio/stfio.h"
  48. #ifdef WITH_PYTHON
  49. #include "./../../pystfio/pystfio.h"
  50. #if PY_MAJOR_VERSION >= 3
  51. #ifndef PyString_Check
  52. #define PyString_Check PyUnicode_Check
  53. #endif
  54. #ifndef PyString_AsString
  55. #define PyString_AsString PyUnicode_AsUTF8
  56. #endif
  57. #ifndef PyString_FromString
  58. #define PyString_FromString PyUnicode_FromString
  59. #endif
  60. #endif
  61. #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
  62. #ifdef __GNUC__
  63. #pragma GCC diagnostic push
  64. #pragma GCC diagnostic ignored "-Wunused-function"
  65. #endif
  66. #include <numpy/arrayobject.h>
  67. #ifdef __GNUC__
  68. #pragma GCC diagnostic pop
  69. #endif
  70. #endif
  71. #include "./usrdlg/usrdlg.h"
  72. #include "./doc.h"
  73. #include "./graph.h"
  74. IMPLEMENT_DYNAMIC_CLASS(wxStfDoc, wxDocument)
  75. BEGIN_EVENT_TABLE( wxStfDoc, wxDocument )
  76. EVT_MENU( ID_SWAPCHANNELS, wxStfDoc::OnSwapChannels )
  77. EVT_MENU( ID_FILEINFO, wxStfDoc::Fileinfo)
  78. EVT_MENU( ID_NEWFROMSELECTEDTHIS, wxStfDoc::OnNewfromselectedThisMenu )
  79. EVT_MENU( ID_MYSELECTALL, wxStfDoc::Selectall )
  80. EVT_MENU( ID_UNSELECTALL, wxStfDoc::Deleteselected )
  81. EVT_MENU( ID_SELECTSOME, wxStfDoc::Selectsome )
  82. EVT_MENU( ID_UNSELECTSOME, wxStfDoc::Unselectsome )
  83. EVT_MENU( ID_SELECT_AND_ADD, wxStfDoc::SelectTracesOfType )
  84. EVT_MENU( ID_SELECT_AND_REMOVE, wxStfDoc::UnselectTracesOfType )
  85. EVT_MENU( ID_CONCATENATE_MULTICHANNEL, wxStfDoc::ConcatenateMultiChannel )
  86. EVT_MENU( ID_BATCH, wxStfDoc::OnAnalysisBatch )
  87. EVT_MENU( ID_INTEGRATE, wxStfDoc::OnAnalysisIntegrate )
  88. EVT_MENU( ID_DIFFERENTIATE, wxStfDoc::OnAnalysisDifferentiate )
  89. EVT_MENU( ID_MULTIPLY, wxStfDoc::Multiply)
  90. EVT_MENU( ID_SUBTRACTBASE, wxStfDoc::SubtractBaseMenu )
  91. EVT_MENU( ID_FIT, wxStfDoc::FitDecay)
  92. EVT_MENU( ID_LFIT, wxStfDoc::LFit)
  93. EVT_MENU( ID_LOG, wxStfDoc::LnTransform)
  94. EVT_MENU( ID_FILTER,wxStfDoc::Filter)
  95. EVT_MENU( ID_POVERN,wxStfDoc::P_over_N)
  96. EVT_MENU( ID_PLOTCRITERION,wxStfDoc::Plotcriterion)
  97. EVT_MENU( ID_PLOTCORRELATION,wxStfDoc::Plotcorrelation)
  98. EVT_MENU( ID_PLOTDECONVOLUTION,wxStfDoc::Plotdeconvolution)
  99. EVT_MENU( ID_EXTRACT,wxStfDoc::MarkEvents )
  100. EVT_MENU( ID_THRESHOLD,wxStfDoc::Threshold)
  101. EVT_MENU( ID_VIEWTABLE, wxStfDoc::Viewtable)
  102. EVT_MENU( ID_EVENT_EXTRACT, wxStfDoc::Extract )
  103. EVT_MENU( ID_EVENT_ERASE, wxStfDoc::InteractiveEraseEvents )
  104. EVT_MENU( ID_EVENT_ADDEVENT, wxStfDoc::AddEvent )
  105. EVT_MENU( ID_ANNOTATION_ADDANNOTATION, wxStfDoc::OnAddAnnotation )
  106. EVT_MENU( ID_ANNOTATION_REMOVEANNOTATION, wxStfDoc::OnRemoveAnnotation )
  107. EVT_MENU( ID_ANNOTATION_ERASEALLANNOTATIONS, wxStfDoc::OnEraseAllAnnotations )
  108. EVT_MENU( ID_ANNOTATION_EXPORTANNOTATIONS, wxStfDoc::OnExportAnnotations )
  109. EVT_MENU( ID_ANNOTATION_IMPORTANNOTATIONS, wxStfDoc::OnImportAnnotations )
  110. EVT_MENU( ID_ANNOTATION_DetectEvents, wxStfDoc::OnExpertDetectEvents )
  111. END_EVENT_TABLE()
  112. double eventThreshold = INT32_MAX;
  113. static const int baseline=100;
  114. // static const double rtfrac = 0.2; // now expressed in percentage, see RTFactor
  115. wxStfDoc::wxStfDoc() :
  116. Recording(), peakAtEnd(false), startFitAtPeak(false), initialized(false),progress(true), Average(0),
  117. latencyStartMode(stf::riseMode),
  118. latencyEndMode(stf::footMode),
  119. latencyWindowMode(stf::defaultMode),
  120. direction(stfnum::both),
  121. #ifdef WITH_PSLOPE
  122. pslopeBegMode(stf::psBeg_manualMode),
  123. pslopeEndMode(stf::psEnd_manualMode),
  124. #endif
  125. baseBeg(0),
  126. baseEnd(0),
  127. peakBeg(0),
  128. peakEnd(0),
  129. fitBeg(0),
  130. fitEnd(0),
  131. baselineMethod(stfnum::mean_sd),
  132. #ifdef WITH_PSLOPE
  133. PSlopeBeg(0),
  134. PSlopeEnd(0),
  135. DeltaT(0),
  136. viewPSlope(true),
  137. #endif
  138. measCursor(0),
  139. ShowRuler(false),
  140. latencyStartCursor(0.0),
  141. latencyEndCursor(0.0),
  142. latency(0.0),
  143. base(0.0),
  144. APBase(0.0),
  145. baseSD(0.0),
  146. threshold(0.0),
  147. slopeForThreshold(20.0),
  148. peak(0.0),
  149. APPeak(0.0),
  150. tLoReal(0),
  151. tHiReal(0),
  152. t50LeftReal(0),
  153. t50RightReal(0),
  154. maxT(0.0),
  155. thrT(-1.0),
  156. maxRiseY(0.0),
  157. maxRiseT(0.0),
  158. maxDecayY(0.0),
  159. maxDecayT(0.0),
  160. maxRise(0.0),
  161. maxDecay(0.0),
  162. t50Y(0.0),
  163. APMaxT(0.0),
  164. APMaxRiseY(0.0),
  165. APMaxRiseT(0.0),
  166. APt50LeftReal(0.0),
  167. APrtLoHi(0.0),
  168. APtLoReal(0.0),
  169. APtHiReal(0.0),
  170. APt0Real(0.0),
  171. #ifdef WITH_PSLOPE
  172. PSlope(0.0),
  173. #endif
  174. rtLoHi(0.0),
  175. InnerLoRT(NAN),
  176. InnerHiRT(NAN),
  177. OuterLoRT(NAN),
  178. OuterHiRT(NAN),
  179. halfDuration(0.0),
  180. slopeRatio(0.0),
  181. t0Real(0.0),
  182. pM(1),
  183. RTFactor(20),
  184. tLoIndex(0),
  185. tHiIndex(0),
  186. t50LeftIndex(0),
  187. t50RightIndex(0),
  188. APt50LeftIndex(0),
  189. APt50RightIndex(0),
  190. APtLoIndex(0),
  191. APtHiIndex(0),
  192. fromBase(true),
  193. viewCrosshair(true),
  194. viewBaseline(true),
  195. viewBaseSD(true),
  196. viewThreshold(false),
  197. viewPeakzero(true),
  198. viewPeakbase(true),
  199. viewPeakthreshold(false),
  200. viewRTLoHi(true),
  201. viewInnerRiseTime(false),
  202. viewOuterRiseTime(false),
  203. viewT50(true),
  204. viewRD(true),
  205. viewSloperise(true),
  206. viewSlopedecay(true),
  207. viewLatency(true),
  208. viewCursors(true),
  209. xzoom(XZoom(0, 0.1, false)),
  210. yzoom(size(), YZoom(500,0.1,false)),
  211. sec_attr(size())
  212. {
  213. for (std::size_t nchannel=0; nchannel < sec_attr.size(); ++nchannel) {
  214. sec_attr[nchannel].resize(at(nchannel).size());
  215. }
  216. }
  217. wxStfDoc::~wxStfDoc()
  218. {}
  219. bool wxStfDoc::OnOpenPyDocument(const wxString& filename) {
  220. progress = false;
  221. bool success = OnOpenDocument( filename );
  222. progress = true;
  223. return success;
  224. }
  225. #ifdef WITH_PYTHON
  226. bool wxStfDoc::LoadTDMS(const std::string& filename, Recording& ReturnData) {
  227. // Grab the Global Interpreter Lock.
  228. wxPyBlock_t blocked = wxPyBeginBlockThreads();
  229. PyObject* stf_mod = PyImport_ImportModule("tdms");
  230. if (!stf_mod) {
  231. PyErr_Print();
  232. #ifdef _STFDEBUG
  233. wxGetApp().ErrorMsg(wxT("Couldn't load tdms.py"));
  234. #endif
  235. wxPyEndBlockThreads(blocked);
  236. return false;
  237. }
  238. PyObject* py_fn = PyString_FromString(filename.c_str());
  239. PyObject* stf_tdms_f = PyObject_GetAttrString(stf_mod, "tdms_open");
  240. PyObject* stf_tdms_args = NULL;
  241. PyObject* stf_tdms_res = NULL;
  242. const auto finish_tdms_load = [&](bool result) {
  243. Py_XDECREF(stf_tdms_res);
  244. Py_XDECREF(stf_tdms_args);
  245. Py_XDECREF(stf_tdms_f);
  246. Py_XDECREF(py_fn);
  247. Py_XDECREF(stf_mod);
  248. wxPyEndBlockThreads(blocked);
  249. return result;
  250. };
  251. if (stf_tdms_f && PyCallable_Check(stf_tdms_f)) {
  252. stf_tdms_args = PyTuple_Pack(1, py_fn);
  253. stf_tdms_res = PyObject_CallObject(stf_tdms_f, stf_tdms_args);
  254. PyErr_Print();
  255. } else {
  256. return finish_tdms_load(false);
  257. }
  258. if (stf_tdms_res == Py_None) {
  259. wxGetApp().ErrorMsg( wxT("nptdms module unavailable. Cannot read tdms files."));
  260. return finish_tdms_load(false);
  261. }
  262. if (!PyTuple_Check(stf_tdms_res)) {
  263. wxGetApp().ErrorMsg(wxT("Return value of tdms_open is not a tuple. Aborting now."));
  264. return finish_tdms_load(false);
  265. }
  266. if (PyTuple_Size(stf_tdms_res) != 2) {
  267. wxGetApp().ErrorMsg( wxT("Return value of tdms_open is not a 2-tuple. Aborting now."));
  268. return finish_tdms_load(false);
  269. }
  270. PyObject* data_list = PyTuple_GetItem(stf_tdms_res, 0);
  271. PyObject* py_dt = PyTuple_GetItem(stf_tdms_res, 1);
  272. double tdmsDt = PyFloat_AsDouble(py_dt);
  273. Py_ssize_t nchannels = PyList_Size(data_list);
  274. ReturnData.resize(nchannels);
  275. int nchannels_nonempty = 0;
  276. for (int nc = 0; nc < nchannels; ++nc) {
  277. PyObject* section_list = PyList_GetItem(data_list, nc);
  278. Py_ssize_t nsections = PyList_Size(section_list);
  279. if (nsections != 0) {
  280. Channel ch(nsections);
  281. for (int ns = 0; ns < nsections; ++ns) {
  282. PyObject* list_item = PyList_GetItem(section_list, ns);
  283. if (!PyArray_Check(list_item)) {
  284. wxGetApp().ErrorMsg( wxT("Expected a numpy array"));
  285. return finish_tdms_load(false);
  286. }
  287. PyArrayObject* np_array = (PyArrayObject*)list_item;
  288. npy_intp* arr_shape = PyArray_DIMS(np_array);
  289. int nsamples = arr_shape[0];
  290. Section sec(nsamples);
  291. double* data = (double*)PyArray_DATA(np_array);
  292. std::copy(&data[0], &data[nsamples], &sec.get_w()[0]);
  293. ch.InsertSection(sec, ns);
  294. }
  295. ReturnData.InsertChannel(ch, nc);
  296. nchannels_nonempty++;
  297. }
  298. }
  299. ReturnData.resize(nchannels_nonempty);
  300. ReturnData.SetXScale(tdmsDt);
  301. return finish_tdms_load(true);
  302. }
  303. #endif // WITH_PYTHON
  304. bool wxStfDoc::OnOpenDocument(const wxString& filename) {
  305. const auto importFromFileType = [&](stfio::filetype type) -> bool {
  306. if (type == stfio::tdms) {
  307. #ifdef WITH_PYTHON
  308. if (!LoadTDMS(stf::wx2std(filename), *this)) {
  309. wxGetApp().ExceptMsg(wxT("Error opening file\n"));
  310. get().clear();
  311. return false;
  312. }
  313. #else
  314. wxGetApp().ExceptMsg(wxT("Error opening file - TDMS requires python \n"));
  315. get().clear();
  316. return false;
  317. #endif
  318. return true;
  319. }
  320. try {
  321. if (progress) {
  322. stf::wxProgressInfo progDlg("Reading file", "Opening file", 100);
  323. stfio::importFile(stf::wx2std(filename), type, *this, wxGetApp().GetTxtImport(), progDlg);
  324. } else {
  325. stfio::StdoutProgressInfo progDlg("Reading file", "Opening file", 100, true);
  326. stfio::importFile(stf::wx2std(filename), type, *this, wxGetApp().GetTxtImport(), progDlg);
  327. }
  328. }
  329. catch (const std::exception& e) {
  330. wxString errorMsg(wxT("Error opening file\n"));
  331. errorMsg += wxString(e.what(), wxConvLocal);
  332. wxGetApp().ExceptMsg(errorMsg);
  333. get().clear();
  334. return false;
  335. }
  336. catch (...) {
  337. wxGetApp().ExceptMsg(wxT("Error opening file\n"));
  338. get().clear();
  339. return false;
  340. }
  341. return true;
  342. };
  343. const auto hasNonEmptyData = [&]() -> bool {
  344. return !get().empty() && !get()[0].get().empty() && !get()[0][0].get().empty();
  345. };
  346. const auto initializePostOpenState = [&]() -> bool {
  347. wxStfParentFrame* pFrame = wxGetApp().GetMainFrame();
  348. if (pFrame == nullptr) {
  349. throw std::runtime_error("pFrame is 0 in wxStfDoc::OnOpenDocument");
  350. }
  351. pFrame->SetSingleChannel( size() <= 1 );
  352. if (InitCursors()!=wxID_OK) {
  353. get().clear();
  354. wxGetApp().ErrorMsg(wxT( "Couldn't initialize cursors\n" ));
  355. return false;
  356. }
  357. if (get().size()>1) {
  358. try {
  359. if (!ChannelSelDlg()) {
  360. wxGetApp().ErrorMsg(wxT( "File is probably empty\n" ));
  361. get().clear();
  362. return false;
  363. }
  364. }
  365. catch (const std::out_of_range& e) {
  366. wxString msg(wxT( "Channel could not be selected:" ));
  367. msg += wxString( e.what(), wxConvLocal );
  368. wxGetApp().ExceptMsg(msg);
  369. get().clear();
  370. return false;
  371. }
  372. }
  373. return true;
  374. };
  375. // Check whether the file exists:
  376. if ( !wxFileName::FileExists( filename ) ) {
  377. wxString msg;
  378. msg << wxT("Couldn't find ") << filename;
  379. wxGetApp().ErrorMsg( msg );
  380. return false;
  381. }
  382. // Store directory:
  383. wxFileName wxfFilename( filename );
  384. wxGetApp().wxWriteProfileString( wxT("Settings"), wxT("Last directory"), wxfFilename.GetPath() );
  385. if (wxDocument::OnOpenDocument(filename)) { //calls base class function
  386. wxString filter(wxT("*.") + wxfFilename.GetExt());
  387. stfio::filetype type = stfio::findType(stf::wx2std(filter));
  388. #if 0 // TODO: backport ascii
  389. if (type==stf::ascii) {
  390. if (!wxGetApp().get_directTxtImport()) {
  391. wxStfTextImportDlg ImportDlg( GetDocumentWindow(),
  392. stf::CreatePreview(filename), 1, false );
  393. if (ImportDlg.ShowModal()!=wxID_OK) {
  394. get().clear();
  395. return false;
  396. }
  397. // store settings in application:
  398. wxGetApp().set_txtImportSettings(ImportDlg.GetTxtImport());
  399. }
  400. }
  401. #endif
  402. if (!importFromFileType(type)) {
  403. return false;
  404. }
  405. if (!hasNonEmptyData()) {
  406. wxGetApp().ErrorMsg(wxT("File is probably empty\n"));
  407. get().clear();
  408. return false;
  409. }
  410. if (!initializePostOpenState()) {
  411. return false;
  412. }
  413. } else {
  414. wxGetApp().ErrorMsg(wxT( "Failure in wxDocument::OnOpenDocument\n" ));
  415. get().clear();
  416. return false;
  417. }
  418. // Make sure curChannel and secondChannel are not out of range
  419. // so that we can use them safely without range checking:
  420. wxString msg(wxT( "Error while checking range:\nParts of the file might be empty\nClosing file now" ));
  421. if (!(get().size()>1)) {
  422. if (cursec().size()==0) {
  423. wxGetApp().ErrorMsg(msg);
  424. get().clear();
  425. return false;
  426. }
  427. } else {
  428. if (cursec().size()==0 || secsec().size()==0) {
  429. wxGetApp().ErrorMsg(msg);
  430. get().clear();
  431. return false;
  432. }
  433. }
  434. wxFileName fn(GetFilename());
  435. SetTitle(fn.GetFullName());
  436. PostInit();
  437. return true;
  438. }
  439. void wxStfDoc::SetData( const Recording& c_Data, const wxStfDoc* Sender, const wxString& title )
  440. {
  441. resize(c_Data.size());
  442. std::copy(c_Data.get().begin(),c_Data.get().end(),get().begin());
  443. CopyAttributes(c_Data);
  444. // Make sure curChannel and curSection are not out of range:
  445. std::out_of_range e("Data empty in wxStimfitDoc::SetData()");
  446. if (get().empty()) {
  447. throw e;
  448. }
  449. wxStfParentFrame* pFrame = wxGetApp().GetMainFrame();
  450. if (pFrame == nullptr) {
  451. throw std::runtime_error("pFrame is 0 in wxStfDoc::SetData");
  452. }
  453. pFrame->SetSingleChannel( size() <= 1 );
  454. // If the title is not a zero string...
  455. if (title != wxT("\0")) {
  456. // ... reset its title ...
  457. SetTitle(title);
  458. }
  459. //Read object variables and ensure correct and appropriate values:
  460. if (Sender!=NULL) {
  461. CopyCursors(*Sender);
  462. SetLatencyBeg( Sender->GetLatencyBeg() );
  463. SetLatencyEnd( Sender->GetLatencyEnd() );
  464. //Get value of the reset latency cursor box
  465. //0=Off, 1=Peak, 2=Rise
  466. SetLatencyStartMode( Sender->GetLatencyStartMode() );
  467. SetLatencyEndMode( Sender->GetLatencyEndMode() );
  468. //SetLatencyWindowMode( Sender->GetLatencyWindowMode() );
  469. #ifdef WITH_PSLOPE
  470. SetPSlopeBegMode ( Sender->GetPSlopeBegMode() );
  471. SetPSlopeEndMode ( Sender->GetPSlopeEndMode() );
  472. #endif
  473. // Update menu checks:
  474. // UpdateMenuCheckmarks();
  475. //Get value of the peak direction dialog box
  476. SetDirection( Sender->GetDirection() );
  477. SetFromBase( Sender->GetFromBase() );
  478. CheckBoundaries();
  479. } else {
  480. if (InitCursors()!=wxID_OK) {
  481. get().clear();
  482. return;
  483. }
  484. }
  485. //Number of channels to display (1 or 2 only!)
  486. if (get().size()>1) {
  487. //Select active channel to be displayed
  488. try {
  489. if (ChannelSelDlg()!=true) {
  490. get().clear();
  491. throw std::runtime_error("Couldn't select channels");
  492. }
  493. }
  494. catch (...) {
  495. throw;
  496. }
  497. }
  498. //Latency Cursor: OFF-Mode only if one channel is selected!
  499. if (!(get().size()>1) &&
  500. GetLatencyStartMode()!=stf::manualMode &&
  501. GetLatencyEndMode()!=stf::manualMode)
  502. {
  503. SetLatencyStartMode(stf::manualMode);
  504. SetLatencyEndMode(stf::manualMode);
  505. // UpdateMenuCheckmarks();
  506. }
  507. // Make sure once again curChannel and curSection are not out of range:
  508. if (!(get().size()>1)) {
  509. if (cursec().size()==0) {
  510. throw e;
  511. }
  512. } else {
  513. if (cursec().size()==0 || secsec().size()==0) {
  514. throw e;
  515. }
  516. }
  517. PostInit();
  518. }
  519. //Dialog box to display the specific settings of the current CFS file.
  520. int wxStfDoc::InitCursors() {
  521. //Get values from .Stimfit and ensure proper settings
  522. SetMeasCursor(wxGetApp().wxGetProfileInt(wxT("Settings"), wxT("MeasureCursor"), 1));
  523. SetMeasRuler( wxGetApp().wxGetProfileInt(wxT("Settings"), wxT("ShowRuler"), 0) );
  524. SetBaseBeg(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("BaseBegin"), 1));
  525. SetBaseEnd(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("BaseEnd"), 20));
  526. int ibase_method = wxGetApp().wxGetProfileInt(wxT("Settings"), wxT("BaselineMethod"),0);
  527. switch (ibase_method) {
  528. case 0: SetBaselineMethod(stfnum::mean_sd); break;
  529. case 1: SetBaselineMethod(stfnum::median_iqr); break;
  530. default: SetBaselineMethod(stfnum::mean_sd);
  531. }
  532. SetPeakBeg(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("PeakBegin"), (int)cursec().size()-100));
  533. SetPeakEnd(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("PeakEnd"), (int)cursec().size()-50));
  534. int iDirection = wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("Direction"),2);
  535. switch (iDirection) {
  536. case 0: SetDirection(stfnum::up); break;
  537. case 1: SetDirection(stfnum::down); break;
  538. case 2: SetDirection(stfnum::both); break;
  539. default: SetDirection(stfnum::undefined_direction);
  540. }
  541. SetFromBase( true ); // reset at every program start wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("FromBase"),1) );
  542. SetPeakAtEnd( wxGetApp().wxGetProfileInt(wxT("Settings"), wxT("PeakAtEnd"), 0));
  543. SetFitBeg(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("FitBegin"), 10));
  544. SetFitEnd(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("FitEnd"), 100));
  545. SetStartFitAtPeak( wxGetApp().wxGetProfileInt(wxT("Settings"), wxT("StartFitAtPeak"), 0));
  546. SetLatencyWindowMode(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("LatencyWindowMode"),1));
  547. SetLatencyBeg(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("LatencyStartCursor"), 0)); /*CSH*/
  548. SetLatencyEnd(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("LatencyEndCursor"), 2)); /*CSH*/
  549. SetLatencyStartMode( wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("LatencyStartMode"),0) );
  550. SetLatencyEndMode( wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("LatencyEndMode"),0) );
  551. // Set corresponding menu checkmarks:
  552. // UpdateMenuCheckmarks();
  553. SetPM(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("PeakMean"),1));
  554. SetRTFactor(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("RTFactor"),20));
  555. wxString wxsSlope = wxGetApp().wxGetProfileString(wxT("Settings"),wxT("Slope"),wxT("20.0"));
  556. double fSlope = 0.0;
  557. wxsSlope.ToDouble(&fSlope);
  558. SetSlopeForThreshold( fSlope );
  559. if (!(get().size()>1) &&
  560. GetLatencyStartMode()!=stf::manualMode &&
  561. GetLatencyEndMode()!=stf::manualMode)
  562. {
  563. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("LatencyStartMode"),stf::manualMode);
  564. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("LatencyEndMode"),stf::manualMode);
  565. SetLatencyStartMode(stf::manualMode);
  566. SetLatencyEndMode(stf::manualMode);
  567. }
  568. #ifdef WITH_PSLOPE
  569. // read PSlope Beg mode from Stimfit registry
  570. int iPSlopeMode = wxGetApp().wxGetProfileInt( wxT("Settings"), wxT("PSlopeStartMode"), stf::psBeg_manualMode );
  571. switch (iPSlopeMode) {
  572. case 0:
  573. SetPSlopeBegMode( stf::psBeg_manualMode );
  574. break;
  575. case 1:
  576. SetPSlopeBegMode( stf::psBeg_footMode );
  577. break;
  578. case 2:
  579. SetPSlopeBegMode( stf::psBeg_thrMode );
  580. break;
  581. case 3:
  582. SetPSlopeBegMode( stf::psBeg_t50Mode );
  583. break;
  584. default:
  585. SetPSlopeBegMode( stf::psBeg_undefined );
  586. }
  587. // read PSlope End mode from Stimfit registry
  588. iPSlopeMode = wxGetApp().wxGetProfileInt( wxT("Settings"), wxT("PSlopeEndMode"), stf::psEnd_manualMode );
  589. switch (iPSlopeMode) {
  590. case 0:
  591. SetPSlopeEndMode( stf::psEnd_manualMode );
  592. break;
  593. case 1:
  594. SetPSlopeEndMode( stf::psEnd_t50Mode );
  595. break;
  596. case 2:
  597. SetPSlopeEndMode( stf::psEnd_DeltaTMode );
  598. break;
  599. case 3:
  600. SetPSlopeEndMode( stf::psEnd_peakMode );
  601. break;
  602. default:
  603. SetPSlopeEndMode( stf::psEnd_undefined );
  604. }
  605. #endif
  606. CheckBoundaries();
  607. return wxID_OK;
  608. } //End SettingsDlg()
  609. void wxStfDoc::PostInit() {
  610. wxStfChildFrame *pFrame = (wxStfChildFrame*)GetDocumentWindow();
  611. if ( pFrame == nullptr ) {
  612. wxGetApp().ErrorMsg( wxT("Zero pointer in wxStfDoc::PostInit") );
  613. return;
  614. }
  615. // Update some vector sizes
  616. sec_attr.resize(size());
  617. for (std::size_t nchannel=0; nchannel < sec_attr.size(); ++nchannel) {
  618. sec_attr[nchannel].resize(at(nchannel).size());
  619. }
  620. yzoom.resize(size());
  621. try {
  622. pFrame->CreateMenuTraces(get().at(GetCurChIndex()).size());
  623. if ( size() > 1 ) {
  624. wxArrayString channelNames;
  625. channelNames.Alloc( size() );
  626. for (std::size_t n_c=0; n_c < size(); ++n_c) {
  627. wxString channelStream;
  628. channelStream << n_c << wxT(" (") << stf::std2wx( at(n_c).GetChannelName() ) << wxT(")");
  629. channelNames.Add( channelStream );
  630. }
  631. pFrame->CreateComboChannels( channelNames );
  632. pFrame->SetChannels( GetCurChIndex(), GetSecChIndex() );
  633. }
  634. }
  635. catch (const std::out_of_range& e) {
  636. wxGetApp().ExceptMsg( wxString( e.what(), wxConvLocal ) );
  637. return;
  638. }
  639. if (GetSR()>1000) {
  640. wxString highSampling;
  641. highSampling << wxT("Sampling rate seems very high (") << GetSR() << wxT(" kHz).\n")
  642. << wxT("Divide by 1000?");
  643. if (wxMessageDialog(
  644. GetDocumentWindow(),
  645. highSampling,
  646. wxT("Adjust sampling rate"),
  647. wxYES_NO
  648. ).ShowModal()==wxID_YES)
  649. {
  650. SetXScale(GetXScale()*1000.0);
  651. }
  652. }
  653. // Read results table settings from registry:
  654. SetViewCrosshair(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewCrosshair"),1)==1);
  655. SetViewBaseline(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewBaseline"),1)==1);
  656. SetViewBaseSD(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewBaseSD"),1)==1);
  657. SetViewThreshold(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewThreshold"),1)==1);
  658. SetViewPeakZero(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewPeakzero"),1)==1);
  659. SetViewPeakBase(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewPeakbase"),1)==1);
  660. SetViewPeakThreshold(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewPeakthreshold"),1)==1);
  661. SetViewRTLoHi(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewRTLoHi"),1)==1);
  662. SetViewInnerRiseTime(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewInnerRiseTime"),1)==1);
  663. SetViewOuterRiseTime(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewOuterRiseTime"),1)==1);
  664. SetViewT50(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewT50"),1)==1);
  665. SetViewRD(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewRD"),1)==1);
  666. SetViewSlopeRise(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewSloperise"),1)==1);
  667. SetViewSlopeDecay(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewSlopedecay"),1)==1);
  668. #ifdef WITH_PSLOPE
  669. //SetViewPSlope(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewPSlope"),1)==1);
  670. SetViewPSlope(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewPSlope"),1)==1);
  671. //SetPSlopeBegMode( wxGetApp().wxGetProfileInt( wxT("Settings"), wxT("PSlopeStartMode"), 1)==1);
  672. #endif
  673. SetViewLatency(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewLatency"),1)==1);
  674. SetViewCursors(wxGetApp().wxGetProfileInt(wxT("Settings"),wxT("ViewCursors"),1)==1);
  675. // refresh the view once we are through:
  676. initialized=true;
  677. pFrame->SetCurTrace(0);
  678. UpdateSelectedButton();
  679. wxGetApp().OnPeakcalcexecMsg();
  680. wxStfParentFrame* parentFrame = wxGetApp().GetMainFrame();
  681. if (parentFrame) {
  682. parentFrame->SetFocus();
  683. }
  684. wxStfView* pView=(wxStfView*)GetFirstView();
  685. if (pView != nullptr) {
  686. wxStfGraph* pGraph = pView->GetGraph();
  687. if (pGraph != nullptr) {
  688. pGraph->Refresh();
  689. pGraph->Enable();
  690. // Set the focus:
  691. pGraph->SetFocus();
  692. }
  693. }
  694. }
  695. //Dialog box to select channel to be displayed
  696. bool wxStfDoc::ChannelSelDlg() {
  697. // Set default channels:
  698. if ( size() < 2 ) {
  699. return false;
  700. }
  701. // SetCurChIndex(); done in Recording constructor
  702. // SetSecCh( 1 );
  703. return true;
  704. } //End ChannelSelDlg()
  705. void wxStfDoc::CheckBoundaries()
  706. {
  707. //Security check base
  708. if (GetBaseBeg() > GetBaseEnd())
  709. {
  710. std::size_t aux=GetBaseBeg();
  711. SetBaseBeg((int)GetBaseEnd());
  712. SetBaseEnd((int)aux);
  713. wxGetApp().ErrorMsg(wxT("Base cursors are reversed,\nthey will be exchanged"));
  714. }
  715. //Security check peak
  716. if (GetPeakBeg() > GetPeakEnd())
  717. {
  718. std::size_t aux=GetPeakBeg();
  719. SetPeakBeg((int)GetPeakEnd());
  720. SetPeakEnd((int)aux);
  721. wxGetApp().ErrorMsg(wxT("Peak cursors are reversed,\nthey will be exchanged"));
  722. }
  723. //Security check decay
  724. if (GetFitBeg() > GetFitEnd())
  725. {
  726. std::size_t aux=GetFitBeg();
  727. SetFitBeg((int)GetFitEnd());
  728. SetFitEnd((int)aux);
  729. wxGetApp().ErrorMsg(wxT("Decay cursors are reversed,\nthey will be exchanged"));
  730. }
  731. if (GetPM() > (int)cursec().size()) {
  732. SetPM((int)cursec().size()-1);
  733. }
  734. if (GetPM() == 0) {
  735. SetPM(1);
  736. }
  737. }
  738. bool wxStfDoc::OnNewDocument() {
  739. // correct caption:
  740. wxString title(GetTitle());
  741. wxStfChildFrame* wnd=(wxStfChildFrame*)GetDocumentWindow();
  742. wnd->SetLabel(title);
  743. // call base class member:
  744. return true;
  745. // return wxDocument::OnNewDocument();
  746. }
  747. void wxStfDoc::Fileinfo(wxCommandEvent& WXUNUSED(event)) {
  748. //Create CFileOpenDlg object 'dlg'
  749. std::ostringstream oss1, oss2;
  750. oss1 << "Number of Channels: " << static_cast<unsigned int>(get().size());
  751. oss2 << "Number of Sweeps: " << static_cast<unsigned int>(get()[GetCurChIndex()].size());
  752. char buf[128];
  753. struct tm t = GetDateTime();
  754. snprintf(buf, 128, "Date:\t%04i-%02i-%02i\nTime:\t%02i:%02i:%02i\n", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
  755. std::string general = buf
  756. + oss1.str() + "\n" + oss2.str() + "\n"
  757. + "Comment:\n" + GetComment();
  758. wxStfFileInfoDlg dlg( GetDocumentWindow(), general, GetFileDescription(),
  759. GetGlobalSectionDescription() );
  760. dlg.ShowModal();
  761. }
  762. bool wxStfDoc::OnCloseDocument() {
  763. if (!get().empty()) {
  764. WriteToReg();
  765. }
  766. // Remove file menu from file menu list:
  767. #ifndef __WXGTK__
  768. wxGetApp().GetDocManager()->GetFileHistory()->RemoveMenu( doc_file_menu );
  769. #endif
  770. // Tell the App:
  771. wxGetApp().CleanupDocument(this);
  772. return wxDocument::OnCloseDocument();
  773. //Note that the base class version will delete all the document's data
  774. }
  775. bool wxStfDoc::SaveAs() {
  776. // Override file save dialog to display only writeable
  777. // file types
  778. wxString filters;
  779. filters += wxT("hdf5 file (*.h5)|*.h5|");
  780. filters += wxT("deprecated: CED filing system (*.dat;*.cfs) (will be disabled in future)|*.dat;*.cfs|");
  781. filters += wxT("deprecated: Axon text file (*.atf) (will be disabled in future)|*.atf|");
  782. filters += wxT("Igor binary wave (*.ibw)|*.ibw|");
  783. filters += wxT("Mantis TDMS file (*.tdms)|*.tdms|");
  784. filters += wxT("Text file series (*.txt)|*.txt|");
  785. #if defined(WITH_BIOSIG)
  786. filters += wxT("GDF file (*.gdf)|*.gdf");
  787. #endif
  788. wxFileDialog SelectFileDialog( GetDocumentWindow(), wxT("Save file"), wxT(""), wxT(""), filters,
  789. wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxFD_PREVIEW );
  790. if(SelectFileDialog.ShowModal()==wxID_OK) {
  791. wxString filename = SelectFileDialog.GetPath();
  792. Recording writeRec(ReorderChannels());
  793. if (writeRec.size() == 0) return false;
  794. try {
  795. stf::wxProgressInfo progDlg("Reading file", "Opening file", 100);
  796. stfio::filetype type;
  797. switch (SelectFileDialog.GetFilterIndex()) {
  798. case 0: type=stfio::hdf5; break;
  799. case 1: type=stfio::cfs; break;
  800. case 2: type=stfio::atf; break;
  801. case 3: type=stfio::igor; break;
  802. case 4: type=stfio::tdms; break;
  803. case 5: type=stfio::ascii; break;
  804. #if defined(WITH_BIOSIG)
  805. default: type=stfio::biosig;
  806. #else
  807. default: type=stfio::hdf5;
  808. #endif
  809. }
  810. return stfio::exportFile(stf::wx2std(filename), type, writeRec, progDlg);
  811. }
  812. catch (const std::runtime_error& e) {
  813. wxGetApp().ExceptMsg(stf::std2wx(e.what()));
  814. return false;
  815. }
  816. } else {
  817. return false;
  818. }
  819. }
  820. Recording wxStfDoc::ReorderChannels() {
  821. // Re-order channels?
  822. std::vector< wxString > channelNames(size());
  823. wxs_it it = channelNames.begin();
  824. for (c_ch_it cit = get().begin();
  825. cit != get().end() && it != channelNames.end();
  826. cit++)
  827. {
  828. *it = stf::std2wx( cit->GetChannelName() );
  829. it++;
  830. }
  831. std::vector<int> channelOrder(size());
  832. if (size()>1) {
  833. wxStfOrderChannelsDlg orderDlg(GetDocumentWindow(),channelNames);
  834. if (orderDlg.ShowModal() != wxID_OK) {
  835. return Recording(0);
  836. }
  837. channelOrder=orderDlg.GetChannelOrder();
  838. } else {
  839. int n_c = 0;
  840. for (int_it orderIt = channelOrder.begin(); orderIt != channelOrder.end(); ++orderIt) {
  841. *orderIt = n_c++;
  842. }
  843. }
  844. Recording writeRec(size());
  845. writeRec.CopyAttributes(*this);
  846. std::size_t n_c = 0;
  847. for (c_int_it cit2 = channelOrder.begin(); cit2 != channelOrder.end(); cit2++) {
  848. writeRec.InsertChannel(get()[*cit2],n_c);
  849. // correct units:
  850. writeRec[n_c++].SetYUnits( at(*cit2).GetYUnits() );
  851. }
  852. return writeRec;
  853. }
  854. bool wxStfDoc::DoSaveDocument(const wxString& filename) {
  855. Recording writeRec(ReorderChannels());
  856. if (writeRec.size() == 0) return false;
  857. try {
  858. stf::wxProgressInfo progDlg("Reading file", "Opening file", 100);
  859. if (stfio::exportFile(stf::wx2std(filename), stfio::hdf5, writeRec, progDlg))
  860. return true;
  861. else
  862. return false;
  863. }
  864. catch (const std::runtime_error& e) {
  865. wxGetApp().ExceptMsg(stf::std2wx(e.what()));
  866. return false;
  867. }
  868. }
  869. void wxStfDoc::WriteToReg() {
  870. //Write file length
  871. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("FirstPoint"), 1);
  872. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("LastPoint"), (int)cursec().size()-1);
  873. //Write cursors
  874. if (!outOfRange(GetBaseBeg()))
  875. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("BaseBegin"), (int)GetBaseBeg());
  876. if (!outOfRange(GetBaseEnd()))
  877. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("BaseEnd"), (int)GetBaseEnd());
  878. if (!outOfRange(GetPeakBeg()))
  879. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("PeakBegin"), (int)GetPeakBeg());
  880. if (!outOfRange(GetPeakEnd()))
  881. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("PeakEnd"), (int)GetPeakEnd());
  882. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("PeakMean"),(int)GetPM());
  883. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("RTFactor"),(int)GetRTFactor());
  884. wxString wxsSlope;
  885. wxsSlope << GetSlopeForThreshold();
  886. wxGetApp().wxWriteProfileString(wxT("Settings"),wxT("Slope"),wxsSlope);
  887. //if (wxGetApp().GetCursorsDialog() != NULL) {
  888. // wxGetApp().wxWriteProfileInt(
  889. // wxT("Settings"),wxT("StartFitAtPeak"),(int)wxGetApp().GetCursorsDialog()->GetStartFitAtPeak()
  890. // );
  891. //}
  892. if (!outOfRange(GetFitBeg()))
  893. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("FitBegin"), (int)GetFitBeg());
  894. if (!outOfRange(GetFitEnd()))
  895. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("FitEnd"), (int)GetFitEnd());
  896. wxGetApp().wxWriteProfileInt( wxT("Settings"),wxT("StartFitAtPeak"),(int)GetStartFitAtPeak() );
  897. if (!outOfRange((size_t)GetLatencyBeg()))
  898. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("LatencyStartCursor"), (int)GetLatencyBeg());
  899. if (!outOfRange((size_t)GetLatencyEnd()))
  900. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("LatencyEndCursor"), (int)GetLatencyEnd());
  901. #ifdef WITH_PSLOPE
  902. if (!outOfRange((size_t)GetPSlopeBeg()))
  903. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("PSlopeStartCursor"), GetPSlopeBeg() );
  904. if (!outOfRange((size_t)GetPSlopeEnd()))
  905. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("PSlopeEndCursor"), GetPSlopeEnd() );
  906. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("PSlopeStartMode"), (int)GetPSlopeBegMode());
  907. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("PSlopeEndMode"), (int)GetPSlopeEndMode());
  908. wxGetApp().wxWriteProfileInt(wxT("Settings"), wxT("DeltaT"), GetDeltaT() );
  909. #endif
  910. // Write Zoom
  911. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("Zoom.xZoom"), (int)GetXZoom().xZoom*100000);
  912. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("Zoom.yZoom"), GetYZoom(GetCurChIndex()).yZoom*100000);
  913. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("Zoom.startPosX"), (int)GetXZoom().startPosX);
  914. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("Zoom.startPosY"), GetYZoom(GetCurChIndex()).startPosY);
  915. if ((get().size()>1))
  916. {
  917. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("Zoom.yZoom2"), (int)GetYZoom(GetSecChIndex()).yZoom*100000);
  918. wxGetApp().wxWriteProfileInt(wxT("Settings"),wxT("Zoom.startPosY2"), GetYZoom(GetSecChIndex()).startPosY);
  919. }
  920. }
  921. bool wxStfDoc::SetSection(std::size_t section){
  922. // Check range:
  923. if (!(get().size()>1)) {
  924. if (section>=get()[GetCurChIndex()].size())
  925. {
  926. wxGetApp().ErrorMsg(wxT("subscript out of range\nwhile calling CStimfitDoc::SetSection()"));
  927. return false;
  928. }
  929. if (get()[GetCurChIndex()][section].size()==0) {
  930. wxGetApp().ErrorMsg(wxT("Section is empty"));
  931. return false;
  932. }
  933. } else {
  934. if (section>=get()[GetCurChIndex()].size() ||
  935. section>=get()[GetSecChIndex()].size())
  936. {
  937. wxGetApp().ErrorMsg(wxT("subscript out of range\nwhile calling CStimfitDoc::SetSection()"));
  938. return false;
  939. }
  940. if (get()[GetCurChIndex()][section].size()==0 ||
  941. get()[GetSecChIndex()][section].size()==0) {
  942. wxGetApp().ErrorMsg(wxT("Section is empty"));
  943. return false;
  944. }
  945. }
  946. CheckBoundaries();
  947. SetCurSecIndex(section);
  948. UpdateSelectedButton();
  949. return true;
  950. }
  951. void wxStfDoc::OnSwapChannels(wxCommandEvent& WXUNUSED(event)) {
  952. if ( size() > 1) {
  953. // Update combo boxes:
  954. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  955. if ( pFrame == nullptr ) {
  956. wxGetApp().ErrorMsg( wxT("Frame is zero in wxStfDoc::SwapChannels"));
  957. return;
  958. }
  959. pFrame->SetChannels( GetSecChIndex(), GetCurChIndex() );
  960. pFrame->UpdateChannels();
  961. }
  962. }
  963. void wxStfDoc::ToggleSelect() {
  964. // get current selection status of this trace:
  965. bool selected = false;
  966. for (c_st_it cit = GetSelectedSections().begin();
  967. cit != GetSelectedSections().end() && !selected;
  968. ++cit) {
  969. if (*cit == GetCurSecIndex()) {
  970. selected = true;
  971. }
  972. }
  973. if (selected) {
  974. Remove();
  975. } else {
  976. Select();
  977. }
  978. }
  979. void wxStfDoc::Select() {
  980. if (GetSelectedSections().size() == get()[GetCurChIndex()].size()) {
  981. wxGetApp().ErrorMsg(wxT("No more traces can be selected\nAll traces are selected"));
  982. return;
  983. }
  984. //control whether trace has already been selected:
  985. bool already=false;
  986. for (c_st_it cit = GetSelectedSections().begin();
  987. cit != GetSelectedSections().end() && !already;
  988. ++cit) {
  989. if (*cit == GetCurSecIndex()) {
  990. already = true;
  991. }
  992. }
  993. //add trace number to selected numbers, print number of selected traces
  994. if (!already) {
  995. SelectTrace(GetCurSecIndex(), baseBeg, baseEnd);
  996. //String output in the trace navigator
  997. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  998. pFrame->SetSelected(GetSelectedSections().size());
  999. } else {
  1000. wxGetApp().ErrorMsg(wxT("Trace is already selected"));
  1001. return;
  1002. }
  1003. Focus();
  1004. }
  1005. void wxStfDoc::Remove() {
  1006. if (UnselectTrace(GetCurSecIndex())) {
  1007. //Message update in the trace navigator
  1008. wxStfChildFrame* pFrame = (wxStfChildFrame*)GetDocumentWindow();
  1009. if (pFrame)
  1010. pFrame->SetSelected(GetSelectedSections().size());
  1011. } else {
  1012. wxGetApp().ErrorMsg(wxT("Trace is not selected"));
  1013. }
  1014. Focus();
  1015. }
  1016. void wxStfDoc::ConcatenateMultiChannel(wxCommandEvent &WXUNUSED(event)) {
  1017. if (GetSelectedSections().empty()) {
  1018. wxGetApp().ErrorMsg(wxT("Select sweeps first"));
  1019. return;
  1020. }
  1021. stf::wxProgressInfo progDlg("Concatenating sections", "Starting...", 100);
  1022. try {
  1023. Recording Concatenated = stfio::concatenate(*this, GetSelectedSections(), progDlg);
  1024. wxGetApp().NewChild(Concatenated,this,wxString(GetTitle()+wxT(", concatenated")));
  1025. } catch (const std::runtime_error& e) {
  1026. wxGetApp().ErrorMsg(wxT("Error concatenating sections:\n") + stf::std2wx(e.what()));
  1027. }
  1028. }
  1029. void wxStfDoc::CreateAverage(
  1030. bool calcSD,
  1031. bool align //align to steepest rise of other channel?
  1032. ) {
  1033. if(GetSelectedSections().empty()) {
  1034. wxGetApp().ErrorMsg(wxT("Select traces first"));
  1035. return;
  1036. }
  1037. wxBusyCursor wc;
  1038. //array indicating how many indices to shift when aligning,
  1039. //has to be filled with zeros:
  1040. std::vector<int> shift(GetSelectedSections().size(),0);
  1041. int shift_size = 0;
  1042. /* Aligned average */
  1043. //find alignment points in the reference (==second) channel:
  1044. if (align) {
  1045. // check that we have more than one channel
  1046. wxStfAlignDlg AlignDlg(GetDocumentWindow(), size()>1);
  1047. if (AlignDlg.ShowModal() != wxID_OK) return;
  1048. //store current section and channel index:
  1049. std::size_t section_old=GetCurSecIndex();
  1050. std::size_t channel_old=GetCurChIndex();
  1051. //initialize the lowest and the highest index:
  1052. std::size_t min_index=0;
  1053. try {
  1054. if (AlignDlg.UseReference())
  1055. min_index=get()[GetSecChIndex()].at(GetSelectedSections().at(0)).size()-1;
  1056. else
  1057. min_index=get()[GetCurChIndex()].at(GetSelectedSections().at(0)).size()-1;
  1058. }
  1059. catch (const std::out_of_range& e) {
  1060. wxString msg(wxT("Error while aligning\nIt is safer to re-start the program\n"));
  1061. msg+=wxString( e.what(), wxConvLocal );
  1062. wxGetApp().ExceptMsg(msg);
  1063. return;
  1064. }
  1065. // swap channels temporarily:
  1066. // if (AlignDlg.UseReference())
  1067. // SetCurChIndex(GetSecChIndex());
  1068. std::size_t max_index=0;
  1069. int_it it = shift.begin();
  1070. //loop through all selected sections:
  1071. for (c_st_it cit = GetSelectedSections().begin();
  1072. cit != GetSelectedSections().end() && it != shift.end();
  1073. cit++)
  1074. {
  1075. //Set the selected section as the current section temporarily:
  1076. SetSection(*cit);
  1077. if (peakAtEnd) {
  1078. SetPeakEnd((int)get()[GetSecChIndex()][*cit].size()-1);
  1079. }
  1080. // Calculate all variables for the current settings
  1081. // APMaxSlopeT will be calculated for the second (==reference)
  1082. // channel, so channels may not be changed!
  1083. try {
  1084. Measure();
  1085. }
  1086. catch (const std::out_of_range& e) {
  1087. Average.resize(0);
  1088. SetSection(section_old);
  1089. SetCurChIndex(channel_old);
  1090. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1091. return;
  1092. }
  1093. std::size_t alignIndex;
  1094. //check whether the current index is a max or a min,
  1095. //and if so, store it:
  1096. switch (AlignDlg.AlignRise()) {
  1097. case 0: // align to peak time
  1098. if (AlignDlg.UseReference())
  1099. alignIndex = lround(GetAPMaxT());
  1100. else
  1101. alignIndex = lround(GetMaxT());
  1102. break;
  1103. case 1: // align to steepest slope time
  1104. if (AlignDlg.UseReference())
  1105. alignIndex = lround(GetAPMaxRiseT());
  1106. else
  1107. alignIndex = lround(GetMaxRiseT());
  1108. break;
  1109. case 2: // align to half amplitude time
  1110. if (AlignDlg.UseReference())
  1111. alignIndex = lround(GetAPT50LeftReal());
  1112. else
  1113. alignIndex = lround(GetT50LeftReal());
  1114. break;
  1115. case 3: // align to onset
  1116. if (AlignDlg.UseReference())
  1117. alignIndex = lround(GetAPT0Real());
  1118. else
  1119. alignIndex = lround(GetT0Real());
  1120. break;
  1121. default:
  1122. wxGetApp().ExceptMsg(wxT("Invalid alignment method"));
  1123. return;
  1124. }
  1125. *it = alignIndex;
  1126. if (alignIndex > max_index) {
  1127. max_index=alignIndex;
  1128. }
  1129. if (alignIndex < min_index) {
  1130. min_index=alignIndex;
  1131. }
  1132. it++;
  1133. }
  1134. //now that max and min indices are known, calculate the number of
  1135. //points that need to be shifted:
  1136. for (int_it shiftIt = shift.begin(); shiftIt != shift.end(); ++shiftIt) {
  1137. (*shiftIt) -= (int)min_index;
  1138. }
  1139. //restore section and channel settings:
  1140. SetSection(section_old);
  1141. SetCurChIndex(channel_old);
  1142. shift_size = (max_index-min_index);
  1143. }
  1144. //number of points in average:
  1145. size_t average_size = cursec().size();
  1146. for (c_st_it sit = GetSelectedSections().begin(); sit != GetSelectedSections().end(); sit++) {
  1147. if (curch().get()[*sit].size() < average_size) {
  1148. average_size = curch().get()[*sit].size();
  1149. }
  1150. }
  1151. average_size -= shift_size;
  1152. //initialize temporary sections and channels:
  1153. Average.resize(size());
  1154. std::size_t n_c = 0;
  1155. for (c_ch_it cit = get().begin(); cit != get().end(); cit++) {
  1156. Section TempSection(average_size), TempSig(average_size);
  1157. try {
  1158. MakeAverage(TempSection, TempSig, n_c, GetSelectedSections(), calcSD, shift);
  1159. }
  1160. catch (const std::out_of_range& e) {
  1161. Average.resize(0);
  1162. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1163. return;
  1164. }
  1165. TempSection.SetXScale(get()[n_c][0].GetXScale()); // set xscale for channel n_c and the only section
  1166. TempSection.SetSectionDescription(stf::wx2std(GetTitle())
  1167. +std::string(", average"));
  1168. Channel TempChannel(TempSection);
  1169. TempChannel.SetChannelName(cit->GetChannelName());
  1170. try {
  1171. Average.InsertChannel(TempChannel,n_c);
  1172. }
  1173. catch (const std::out_of_range& e) {
  1174. Average.resize(0);
  1175. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1176. return;
  1177. }
  1178. n_c++;
  1179. }
  1180. Average.CopyAttributes(*this);
  1181. wxString title;
  1182. title << GetFilename() << wxT(", average of ") << (int)GetSelectedSections().size() << wxT(" traces");
  1183. wxGetApp().NewChild(Average,this,title);
  1184. } //End of CreateAverage(.,.,.)
  1185. void wxStfDoc::FitDecay(wxCommandEvent& WXUNUSED(event)) {
  1186. int fselect=-2;
  1187. wxStfFitSelDlg FitSelDialog(GetDocumentWindow(), this);
  1188. if (FitSelDialog.ShowModal() != wxID_OK) return;
  1189. wxBeginBusyCursor();
  1190. fselect=FitSelDialog.GetFSelect();
  1191. if (outOfRange(GetFitBeg()) || outOfRange(GetFitEnd())) {
  1192. wxGetApp().ErrorMsg(wxT("Subscript out of range in wxStfDoc::FitDecay()"));
  1193. return;
  1194. }
  1195. //number of parameters to be fitted:
  1196. std::size_t n_params=0;
  1197. //number of points:
  1198. std::size_t n_points = GetFitEnd()-GetFitBeg();
  1199. if (n_points<=1) {
  1200. wxGetApp().ErrorMsg(wxT("Check fit limits"));
  1201. return;
  1202. }
  1203. std::string fitInfo;
  1204. try {
  1205. n_params=(int)wxGetApp().GetFuncLib().at(fselect).pInfo.size();
  1206. }
  1207. catch (const std::out_of_range& e) {
  1208. wxString msg(wxT("Could not retrieve function from library:\n"));
  1209. msg+=wxString( e.what(), wxConvLocal );
  1210. wxGetApp().ExceptMsg(msg);
  1211. return;
  1212. }
  1213. Vector_double params ( FitSelDialog.GetInitP() );
  1214. int warning = 0;
  1215. try {
  1216. std::size_t fitSize = GetFitEnd() - GetFitBeg();
  1217. Vector_double x( fitSize );
  1218. //fill array:
  1219. std::copy(&cursec()[GetFitBeg()], &cursec()[GetFitBeg()+fitSize], &x[0]);
  1220. if (params.size() != n_params) {
  1221. throw std::runtime_error("Wrong size of params in wxStfDoc::lmFit()");
  1222. }
  1223. double chisqr = stfnum::lmFit( x, GetXScale(), wxGetApp().GetFuncLib()[fselect],
  1224. FitSelDialog.GetOpts(), FitSelDialog.UseScaling(),
  1225. params, fitInfo, warning );
  1226. SetIsFitted( GetCurChIndex(), GetCurSecIndex(), params, wxGetApp().GetFuncLibPtr(fselect),
  1227. chisqr, GetFitBeg(), GetFitEnd() );
  1228. }
  1229. catch (const std::out_of_range& e) {
  1230. wxGetApp().ExceptMsg( wxString(e.what(), wxConvLocal) );
  1231. return;
  1232. }
  1233. catch (const std::runtime_error& e) {
  1234. wxGetApp().ExceptMsg( wxString(e.what(), wxConvLocal) );
  1235. return;
  1236. }
  1237. // Refresh the graph to show the fit before
  1238. // the dialog pops up:
  1239. wxStfView* pView=(wxStfView*)GetFirstView();
  1240. if (pView!=NULL && pView->GetGraph()!=NULL)
  1241. pView->GetGraph()->Refresh();
  1242. wxStfFitInfoDlg InfoDialog(GetDocumentWindow(), stf::std2wx(fitInfo));
  1243. wxEndBusyCursor();
  1244. InfoDialog.ShowModal();
  1245. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1246. wxString label; label << wxT("Fit, Section #") << (int)GetCurSecIndex()+1;
  1247. try {
  1248. pFrame->ShowTable(sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).bestFit, label);
  1249. }
  1250. catch (std::out_of_range const& e) {
  1251. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1252. }
  1253. }
  1254. void wxStfDoc::LFit(wxCommandEvent& WXUNUSED(event)) {
  1255. wxBusyCursor wc;
  1256. if (outOfRange(GetFitBeg()) || outOfRange(GetFitEnd())) {
  1257. wxGetApp().ErrorMsg(wxT("Subscript out of range in wxStfDoc::FitDecay()"));
  1258. return;
  1259. }
  1260. //number of parameters to be fitted:
  1261. std::size_t n_params=0;
  1262. //number of points:
  1263. std::size_t n_points=GetFitEnd()-GetFitBeg();
  1264. if (n_points<=1) {
  1265. wxGetApp().ErrorMsg(wxT("Check fit limits"));
  1266. return;
  1267. }
  1268. std::string fitInfo;
  1269. n_params=2;
  1270. Vector_double params( n_params );
  1271. //fill array:
  1272. Vector_double x(n_points);
  1273. std::copy(&cursec()[GetFitBeg()], &cursec()[GetFitBeg()+n_points], &x[0]);
  1274. Vector_double t(x.size());
  1275. for (std::size_t n_t=0;n_t<x.size();++n_t) t[n_t]=n_t*GetXScale();
  1276. // Perform the fit:
  1277. double chisqr = stfnum::linFit(t,x,params[0],params[1]);
  1278. try {
  1279. SetIsFitted( GetCurChIndex(), GetCurSecIndex(), params, wxGetApp().GetLinFuncPtr(), chisqr, GetFitBeg(), GetFitEnd() );
  1280. }
  1281. catch (std::out_of_range const& e) {
  1282. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1283. return;
  1284. }
  1285. // Refresh the graph to show the fit before
  1286. // the dialog pops up:
  1287. wxStfView* pView=(wxStfView*)GetFirstView();
  1288. if (pView!=NULL && pView->GetGraph()!=NULL)
  1289. pView->GetGraph()->Refresh();
  1290. fitInfo += "slope = " + std::to_string(params[0]);
  1291. if (params[0] != 0.0)
  1292. fitInfo += "\n1/slope = " + std::to_string(1.0 / params[0]);
  1293. else
  1294. fitInfo += "\n1/slope = undefined (slope = 0)";
  1295. fitInfo += "\ny-intercept = " + std::to_string(params[1]);
  1296. wxStfFitInfoDlg InfoDialog(GetDocumentWindow(), stf::std2wx(fitInfo));
  1297. InfoDialog.ShowModal();
  1298. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1299. wxString label; label << wxT("Fit, Section #") << (int)GetCurSecIndex();
  1300. try {
  1301. pFrame->ShowTable(sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).bestFit, label);
  1302. }
  1303. catch (std::out_of_range const& e) {
  1304. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1305. }
  1306. }
  1307. void wxStfDoc::LnTransform(wxCommandEvent& WXUNUSED(event)) {
  1308. Channel TempChannel(GetSelectedSections().size(), get()[GetCurChIndex()][GetSelectedSections()[0]].size());
  1309. std::size_t n = 0;
  1310. for (c_st_it cit = GetSelectedSections().begin(); cit != GetSelectedSections().end(); cit++) {
  1311. Section TempSection(size());
  1312. std::transform(get()[GetCurChIndex()][*cit].get().begin(),
  1313. get()[GetCurChIndex()][*cit].get().end(),
  1314. TempSection.get_w().begin(),
  1315. #if defined(_WINDOWS) && !defined(__MINGW32__)
  1316. std::logl);
  1317. #else
  1318. (double(*)(double))log);
  1319. #endif
  1320. TempSection.SetXScale(get()[GetCurChIndex()][*cit].GetXScale());
  1321. TempSection.SetSectionDescription( get()[GetCurChIndex()][*cit].GetSectionDescription()+
  1322. ", transformed (ln)");
  1323. try {
  1324. TempChannel.InsertSection(TempSection,n);
  1325. }
  1326. catch (std::out_of_range const& e) {
  1327. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1328. }
  1329. n++;
  1330. }
  1331. if (TempChannel.size()>0) {
  1332. Recording Transformed(TempChannel);
  1333. Transformed.CopyAttributes(*this);
  1334. wxString title(GetTitle());
  1335. title+=wxT(", transformed (ln)");
  1336. wxGetApp().NewChild(Transformed,this,title);
  1337. }
  1338. }
  1339. void wxStfDoc::Viewtable(wxCommandEvent& WXUNUSED(event)) {
  1340. wxBusyCursor wc;
  1341. try {
  1342. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1343. pFrame->ShowTable( CurAsTable(), stf::std2wx( cursec().GetSectionDescription() ) );
  1344. }
  1345. catch (const std::out_of_range& e) {
  1346. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1347. return;
  1348. }
  1349. }
  1350. void wxStfDoc::Multiply(wxCommandEvent& WXUNUSED(event)) {
  1351. if (GetSelectedSections().empty()) {
  1352. wxGetApp().ErrorMsg(wxT("Select traces first"));
  1353. return;
  1354. }
  1355. //insert standard values:
  1356. std::vector<std::string> labels(1);
  1357. Vector_double defaults(labels.size());
  1358. labels[0]="Multiply with:";defaults[0]=1;
  1359. stf::UserInput init(labels,defaults,"Set factor");
  1360. wxStfUsrDlg MultDialog(GetDocumentWindow(),init);
  1361. if (MultDialog.ShowModal()!=wxID_OK) return;
  1362. Vector_double input(MultDialog.readInput());
  1363. if (input.size()!=1) return;
  1364. double factor=input[0];
  1365. try {
  1366. Recording Multiplied = stfio::multiply(*this, GetSelectedSections(), GetCurChIndex(), factor);
  1367. wxGetApp().NewChild(Multiplied, this, wxString(GetTitle()+wxT(", multiplied")));
  1368. } catch (const std::exception& e) {
  1369. wxGetApp().ErrorMsg(wxT("Error during multiplication:\n") + stf::std2wx(e.what()));
  1370. }
  1371. }
  1372. bool wxStfDoc::SubtractBase( ) {
  1373. if (GetSelectedSections().empty()) {
  1374. wxGetApp().ErrorMsg(wxT("Select traces first"));
  1375. return false;
  1376. }
  1377. Channel TempChannel(GetSelectedSections().size(), get()[GetCurChIndex()][GetSelectedSections()[0]].size());
  1378. std::size_t n = 0;
  1379. for (c_st_it cit = GetSelectedSections().begin(); cit != GetSelectedSections().end(); cit++) {
  1380. Section TempSection(stfio::vec_scal_minus(get()[GetCurChIndex()][*cit].get(), GetSelectBase()[n]));
  1381. TempSection.SetXScale(get()[GetCurChIndex()][*cit].GetXScale());
  1382. TempSection.SetSectionDescription( get()[GetCurChIndex()][*cit].GetSectionDescription()+
  1383. ", baseline subtracted");
  1384. try {
  1385. TempChannel.InsertSection(TempSection,n);
  1386. }
  1387. catch (const std::out_of_range& e) {
  1388. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1389. return false;
  1390. }
  1391. n++;
  1392. }
  1393. if (TempChannel.size()>0) {
  1394. Recording SubBase(TempChannel);
  1395. SubBase.CopyAttributes(*this);
  1396. wxString title(GetTitle());
  1397. title+=wxT(", baseline subtracted");
  1398. wxGetApp().NewChild(SubBase,this,title);
  1399. } else {
  1400. wxGetApp().ErrorMsg( wxT("Channel is empty.") );
  1401. return false;
  1402. }
  1403. return true;
  1404. }
  1405. void wxStfDoc::OnAnalysisBatch(wxCommandEvent &WXUNUSED(event)) {
  1406. // event.Skip();
  1407. if (GetSelectedSections().empty())
  1408. {
  1409. wxGetApp().ErrorMsg(wxT("No selected traces"));
  1410. return;
  1411. }
  1412. std::size_t section_old=GetCurSecIndex(); //
  1413. wxStfBatchDlg SaveYtDialog(GetDocumentWindow());
  1414. if (SaveYtDialog.ShowModal()!=wxID_OK) return;
  1415. std::vector<std::string> colTitles;
  1416. //Write the header of the SaveYt file in a string
  1417. if (SaveYtDialog.PrintBase()) {
  1418. colTitles.push_back("Base");
  1419. }
  1420. if (SaveYtDialog.PrintBaseSD()) {
  1421. colTitles.push_back("Base SD");
  1422. }
  1423. if (SaveYtDialog.PrintThreshold()) {
  1424. colTitles.push_back("Slope threshold");
  1425. }
  1426. if (SaveYtDialog.PrintSlopeThresholdTime()) {
  1427. colTitles.push_back("Slope threshold time");
  1428. }
  1429. if (SaveYtDialog.PrintPeakZero()) {
  1430. colTitles.push_back("Peak (from 0)");
  1431. }
  1432. if (SaveYtDialog.PrintPeakBase()) {
  1433. colTitles.push_back("Peak (from baseline)");
  1434. }
  1435. if (SaveYtDialog.PrintPeakThreshold()) {
  1436. colTitles.push_back("Peak (from threshold)");
  1437. }
  1438. if (SaveYtDialog.PrintPeakTime()) {
  1439. colTitles.push_back("Peak time");
  1440. }
  1441. if (SaveYtDialog.PrintRTLoHi()) {
  1442. colTitles.push_back("RT Lo-Hi%");
  1443. }
  1444. if (SaveYtDialog.PrintInnerRTLoHi()) {
  1445. colTitles.push_back("inner Rise Time Lo-Hi%");
  1446. }
  1447. if (SaveYtDialog.PrintOuterRTLoHi()) {
  1448. colTitles.push_back("Outer Rise Time Lo-Hi%");
  1449. }
  1450. if (SaveYtDialog.PrintT50()) {
  1451. colTitles.push_back("duration Amp/2");
  1452. }
  1453. if (SaveYtDialog.PrintT50SE()) {
  1454. colTitles.push_back("start Amp/2");
  1455. colTitles.push_back("end Amp/2");
  1456. }
  1457. if (SaveYtDialog.PrintSlopes()) {
  1458. colTitles.push_back("Max. slope rise");
  1459. colTitles.push_back("Max. slope decay");
  1460. }
  1461. if (SaveYtDialog.PrintSlopeTimes()) {
  1462. colTitles.push_back("Time of max. rise");
  1463. colTitles.push_back("Time of max. decay");
  1464. }
  1465. if (SaveYtDialog.PrintLatencies()) {
  1466. colTitles.push_back("Latency");
  1467. }
  1468. int fselect=-2;
  1469. std::size_t n_params=0;
  1470. wxStfFitSelDlg FitSelDialog(GetDocumentWindow(), this);
  1471. if (SaveYtDialog.PrintFitResults()) {
  1472. while (fselect<0) {
  1473. FitSelDialog.SetNoInput(true);
  1474. if (FitSelDialog.ShowModal()!=wxID_OK) {
  1475. SetSection(section_old);
  1476. return;
  1477. }
  1478. fselect=FitSelDialog.GetFSelect();
  1479. }
  1480. try {
  1481. n_params=(int)wxGetApp().GetFuncLib().at(fselect).pInfo.size();
  1482. }
  1483. catch (const std::out_of_range& e) {
  1484. wxString msg(wxT("Error while retrieving function from library:\n"));
  1485. msg += stf::std2wx(e.what());
  1486. wxGetApp().ExceptMsg(msg);
  1487. SetSection(section_old);
  1488. return;
  1489. }
  1490. for (std::size_t n_pf=0;n_pf<n_params;++n_pf) {
  1491. colTitles.push_back( wxGetApp().GetFuncLib()[fselect].pInfo[n_pf].desc);
  1492. }
  1493. colTitles.push_back("Fit warning code");
  1494. }
  1495. #ifdef WITH_PSLOPE
  1496. if (SaveYtDialog.PrintPSlopes()) {
  1497. colTitles.push_back("pSlope");
  1498. }
  1499. #endif
  1500. if (SaveYtDialog.PrintThr()) {
  1501. colTitles.push_back("# of thr. crossings");
  1502. }
  1503. double crossingThreshold=0.0;
  1504. if (SaveYtDialog.PrintThr()) {
  1505. // Get threshold from user:
  1506. std::ostringstream thrS;
  1507. thrS << "Threshold (" << at(GetCurChIndex()).GetYUnits() << ")";
  1508. stf::UserInput Input( std::vector<std::string>(1, thrS.str()),
  1509. Vector_double (1,0.0), "Set threshold");
  1510. wxStfUsrDlg myDlg( GetDocumentWindow(), Input );
  1511. if (myDlg.ShowModal()!=wxID_OK) {
  1512. return;
  1513. }
  1514. crossingThreshold=myDlg.readInput()[0];
  1515. }
  1516. wxProgressDialog progDlg( wxT("Batch analysis in progress"), wxT("Starting batch analysis"),
  1517. 100, GetDocumentWindow(), wxPD_SMOOTH | wxPD_AUTO_HIDE | wxPD_APP_MODAL );
  1518. stfnum::Table table(GetSelectedSections().size(),colTitles.size());
  1519. for (std::size_t nCol=0;nCol<colTitles.size();++nCol) {
  1520. try {
  1521. table.SetColLabel(nCol,colTitles[nCol]);
  1522. }
  1523. catch (const std::out_of_range& e) {
  1524. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1525. SetSection(section_old);
  1526. return;
  1527. }
  1528. }
  1529. std::size_t n_s = 0;
  1530. for (c_st_it cit = GetSelectedSections().begin(); cit != GetSelectedSections().end(); cit++) {
  1531. wxString progStr;
  1532. progStr << wxT("Processing trace # ") << (int)n_s+1 << wxT(" of ") << (int)GetSelectedSections().size();
  1533. progDlg.Update( (int)((double)n_s/ (double)GetSelectedSections().size()*100.0), progStr );
  1534. SetSection(*cit);
  1535. if (peakAtEnd)
  1536. SetPeakEnd((int)get()[GetCurChIndex()][*cit].size()-1);
  1537. //Calculate all variables for the current settings
  1538. try {
  1539. Measure();
  1540. }
  1541. catch (const std::out_of_range& e) {
  1542. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1543. SetSection(section_old);
  1544. return;
  1545. }
  1546. // Set fit start cursor to new peak if necessary:
  1547. //if (wxGetApp().GetCursorsDialog() != NULL && wxGetApp().GetCursorsDialog()->GetStartFitAtPeak())
  1548. if ( startFitAtPeak )
  1549. SetFitBeg(GetMaxT());
  1550. Vector_double params;
  1551. int fitWarning = 0;
  1552. if (SaveYtDialog.PrintFitResults()) {
  1553. try {
  1554. n_params=(int)wxGetApp().GetFuncLib().at(fselect).pInfo.size();
  1555. }
  1556. catch (const std::out_of_range& e) {
  1557. wxString msg(wxT("Could not retrieve function from library:\n"));
  1558. msg+=wxString( e.what(), wxConvLocal );
  1559. wxGetApp().ExceptMsg(msg);
  1560. return;
  1561. }
  1562. // in this case, initialize parameters from init function,
  1563. // not from user input:
  1564. Vector_double x(GetFitEnd()-GetFitBeg());
  1565. //fill array:
  1566. std::copy(&cursec()[GetFitBeg()], &cursec()[GetFitEnd()], &x[0]);
  1567. params.resize(n_params);
  1568. wxGetApp().GetFuncLib().at(fselect).init( x, GetBase(), GetPeak(), GetRTLoHi(),
  1569. GetHalfDuration(), GetXScale(), params );
  1570. std::string fitInfo;
  1571. try {
  1572. double chisqr = stfnum::lmFit( x, GetXScale(), wxGetApp().GetFuncLib()[fselect],
  1573. FitSelDialog.GetOpts(), FitSelDialog.UseScaling(),
  1574. params, fitInfo, fitWarning );
  1575. SetIsFitted( GetCurChIndex(), GetCurSecIndex(), params, wxGetApp().GetFuncLibPtr(fselect),
  1576. chisqr, GetFitBeg(), GetFitEnd() );
  1577. }
  1578. catch (const std::out_of_range& e) {
  1579. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1580. SetSection(section_old);
  1581. return;
  1582. }
  1583. catch (const std::runtime_error& e) {
  1584. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1585. SetSection(section_old);
  1586. return;
  1587. }
  1588. catch (const std::exception& e) {
  1589. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1590. SetSection(section_old);
  1591. return;
  1592. }
  1593. }
  1594. // count number of threshold crossings if needed:
  1595. std::size_t n_crossings=0;
  1596. if (SaveYtDialog.PrintThr()) {
  1597. n_crossings= stfnum::peakIndices( cursec().get(), crossingThreshold, 0 ).size();
  1598. }
  1599. std::size_t nCol=0;
  1600. //Write the variables of the current channel in a string
  1601. try {
  1602. table.SetRowLabel(n_s, cursec().GetSectionDescription());
  1603. if (SaveYtDialog.PrintBase())
  1604. table.at(n_s,nCol++)=GetBase();
  1605. if (SaveYtDialog.PrintBaseSD())
  1606. table.at(n_s,nCol++)=GetBaseSD();
  1607. if (SaveYtDialog.PrintThreshold())
  1608. table.at(n_s,nCol++)=GetThreshold();
  1609. if (SaveYtDialog.PrintSlopeThresholdTime())
  1610. table.at(n_s,nCol++)=GetThrT()*GetXScale();
  1611. if (SaveYtDialog.PrintPeakZero())
  1612. table.at(n_s,nCol++)=GetPeak();
  1613. if (SaveYtDialog.PrintPeakBase())
  1614. table.at(n_s,nCol++)=GetPeak()-GetBase();
  1615. if (SaveYtDialog.PrintPeakThreshold())
  1616. table.at(n_s,nCol++)=GetPeak()-GetThreshold();
  1617. if (SaveYtDialog.PrintPeakTime())
  1618. table.at(n_s,nCol++)=GetPeakTime()*GetXScale();
  1619. if (SaveYtDialog.PrintRTLoHi())
  1620. table.at(n_s,nCol++)=GetRTLoHi();
  1621. if (SaveYtDialog.PrintInnerRTLoHi())
  1622. table.at(n_s,nCol++)=GetInnerRiseTime();
  1623. if (SaveYtDialog.PrintOuterRTLoHi())
  1624. table.at(n_s,nCol++)=GetOuterRiseTime();
  1625. if (SaveYtDialog.PrintT50())
  1626. table.at(n_s,nCol++)=GetHalfDuration();
  1627. if (SaveYtDialog.PrintT50SE()) {
  1628. table.at(n_s,nCol++)=GetT50LeftReal()*GetXScale();
  1629. table.at(n_s,nCol++)=GetT50RightReal()*GetXScale();
  1630. }
  1631. if (SaveYtDialog.PrintSlopes()) {
  1632. table.at(n_s,nCol++)=GetMaxRise();
  1633. table.at(n_s,nCol++)=GetMaxDecay();
  1634. }
  1635. if (SaveYtDialog.PrintSlopeTimes()) {
  1636. table.at(n_s,nCol++)=GetMaxRiseT()*GetXScale();
  1637. table.at(n_s,nCol++)=GetMaxDecayT()*GetXScale();
  1638. }
  1639. if (SaveYtDialog.PrintLatencies()) {
  1640. table.at(n_s,nCol++)=GetLatency()*GetXScale();
  1641. }
  1642. if (SaveYtDialog.PrintFitResults()) {
  1643. for (std::size_t n_pf=0;n_pf<n_params;++n_pf) {
  1644. table.at(n_s,nCol++)=params[n_pf];
  1645. }
  1646. if (fitWarning != 0) {
  1647. table.at(n_s,nCol++) = (double)fitWarning;
  1648. } else {
  1649. table.SetEmpty(n_s,nCol++);
  1650. }
  1651. }
  1652. #ifdef WITH_PSLOPE
  1653. if (SaveYtDialog.PrintPSlopes()) {
  1654. table.at(n_s,nCol++)=GetPSlope();
  1655. }
  1656. #endif
  1657. if (SaveYtDialog.PrintThr()) {
  1658. table.at(n_s,nCol++)=n_crossings;
  1659. }
  1660. }
  1661. catch (const std::out_of_range& e) {
  1662. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1663. SetSection(section_old);
  1664. return;
  1665. }
  1666. n_s++;
  1667. }
  1668. progDlg.Update(100,wxT("Finished"));
  1669. SetSection(section_old);
  1670. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1671. pFrame->ShowTable(table,wxT("Batch analysis results"));
  1672. }
  1673. void wxStfDoc::OnAnalysisIntegrate(wxCommandEvent &WXUNUSED(event)) {
  1674. double integral_s = 0.0, integral_t = 0.0;
  1675. const std::string units = at(GetCurChIndex()).GetYUnits() + " * " + GetXUnits();
  1676. try {
  1677. integral_s = stfnum::integrate_simpson(cursec().get(),GetFitBeg(),GetFitEnd(),GetXScale());
  1678. integral_t = stfnum::integrate_trapezium(cursec().get(),GetFitBeg(),GetFitEnd(),GetXScale());
  1679. }
  1680. catch (const std::exception& e) {
  1681. wxGetApp().ErrorMsg(wxString( e.what(), wxConvLocal ));
  1682. return;
  1683. }
  1684. stfnum::Table integralTable(6,1);
  1685. try {
  1686. integralTable.SetRowLabel(0, "Trapezium (linear)");
  1687. integralTable.SetRowLabel(1, "Integral (from 0)");
  1688. integralTable.SetRowLabel(2, "Integral (from base)");
  1689. integralTable.SetRowLabel(3, "Simpson (quadratic)");
  1690. integralTable.SetRowLabel(4, "Integral (from 0)");
  1691. integralTable.SetRowLabel(5, "Integral (from base)");
  1692. //integralTable.SetColLabel(0, "Results");
  1693. integralTable.SetColLabel(0, units);
  1694. integralTable.SetEmpty(0,0);
  1695. integralTable.at(1,0) = integral_t;
  1696. integralTable.at(2,0) =
  1697. integral_t - (GetFitEnd()-GetFitBeg())*GetXScale()*GetBase();
  1698. integralTable.SetEmpty(3,0);
  1699. integralTable.at(4,0) = integral_s;
  1700. integralTable.at(5,0) =
  1701. integral_s - (GetFitEnd()-GetFitBeg())*GetXScale()*GetBase();
  1702. }
  1703. catch (const std::out_of_range& e) {
  1704. wxGetApp().ErrorMsg(wxString( e.what(), wxConvLocal ));
  1705. return;
  1706. }
  1707. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1708. pFrame->ShowTable(integralTable,wxT("Integral"));
  1709. try {
  1710. Vector_double quad_p = stfnum::quad(cursec().get(), GetFitBeg(), GetFitEnd());
  1711. SetIsIntegrated(GetCurChIndex(), GetCurSecIndex(), true,GetFitBeg(),GetFitEnd(), quad_p);
  1712. }
  1713. catch (const std::runtime_error& e) {
  1714. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1715. }
  1716. catch (const std::out_of_range& e) {
  1717. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1718. }
  1719. }
  1720. void wxStfDoc::OnAnalysisDifferentiate(wxCommandEvent &WXUNUSED(event)) {
  1721. if (GetSelectedSections().empty()) {
  1722. wxGetApp().ErrorMsg(wxT("Select traces first"));
  1723. return;
  1724. }
  1725. Channel TempChannel(GetSelectedSections().size(), get()[GetCurChIndex()][GetSelectedSections()[0]].size());
  1726. std::size_t n = 0;
  1727. for (c_st_it cit = GetSelectedSections().begin(); cit != GetSelectedSections().end(); cit++) {
  1728. Section TempSection( stfnum::diff( get()[GetCurChIndex()][*cit].get(), GetXScale() ) );
  1729. TempSection.SetXScale(get()[GetCurChIndex()][*cit].GetXScale());
  1730. TempSection.SetSectionDescription( get()[GetCurChIndex()][*cit].GetSectionDescription()+
  1731. ", differentiated");
  1732. try {
  1733. TempChannel.InsertSection(TempSection,n);
  1734. }
  1735. catch (const std::out_of_range& e) {
  1736. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1737. }
  1738. n++;
  1739. }
  1740. if (TempChannel.size()>0) {
  1741. Recording Diff(TempChannel);
  1742. Diff.CopyAttributes(*this);
  1743. Diff[0].SetYUnits(at(GetCurChIndex()).GetYUnits()+" / ms");
  1744. wxString title(GetTitle());
  1745. title+=wxT(", differentiated");
  1746. wxGetApp().NewChild(Diff,this,title);
  1747. }
  1748. }
  1749. bool wxStfDoc::OnNewfromselectedThis( ) {
  1750. if (GetSelectedSections().empty()) {
  1751. wxGetApp().ErrorMsg(wxT("Select traces first"));
  1752. return false;
  1753. }
  1754. Channel TempChannel(GetSelectedSections().size(), get()[GetCurChIndex()][GetSelectedSections()[0]].size());
  1755. std::size_t n = 0;
  1756. for (c_st_it cit = GetSelectedSections().begin(); cit != GetSelectedSections().end(); cit++) {
  1757. // Multiply the valarray in Data:
  1758. Section TempSection(get()[GetCurChIndex()][*cit].get());
  1759. TempSection.SetXScale(get()[GetCurChIndex()][*cit].GetXScale());
  1760. TempSection.SetSectionDescription( get()[GetCurChIndex()][*cit].GetSectionDescription()+
  1761. ", new from selected");
  1762. try {
  1763. TempChannel.InsertSection(TempSection,n);
  1764. }
  1765. catch (std::out_of_range const& e) {
  1766. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  1767. return false;
  1768. }
  1769. n++;
  1770. }
  1771. if (TempChannel.size()>0) {
  1772. Recording Selected(TempChannel);
  1773. Selected.CopyAttributes(*this);
  1774. Selected[0].SetYUnits( at(GetCurChIndex()).GetYUnits() );
  1775. Selected[0].SetChannelName( at(GetCurChIndex()).GetChannelName() );
  1776. wxString title(GetTitle());
  1777. title+=wxT(", new from selected");
  1778. wxGetApp().NewChild(Selected,this,title);
  1779. } else {
  1780. wxGetApp().ErrorMsg( wxT("Channel is empty.") );
  1781. return false;
  1782. }
  1783. return true;
  1784. }
  1785. void wxStfDoc::Selectsome(wxCommandEvent &WXUNUSED(event)) {
  1786. if (GetSelectedSections().size()>0) {
  1787. wxGetApp().ErrorMsg(wxT("Unselect all"));
  1788. return;
  1789. }
  1790. //insert standard values:
  1791. std::vector<std::string> labels(2);
  1792. Vector_double defaults(labels.size());
  1793. labels[0]="Select every x-th trace:";defaults[0]=1;
  1794. labels[1]="Starting with the y-th:";defaults[1]=1;
  1795. stf::UserInput init(labels,defaults,"Select every n-th (1-based)");
  1796. wxStfUsrDlg EveryDialog(GetDocumentWindow(),init);
  1797. if (EveryDialog.ShowModal()!=wxID_OK) return;
  1798. Vector_double input(EveryDialog.readInput());
  1799. if (input.size()!=2) return;
  1800. int everynth=(int)input[0];
  1801. int everystart=(int)input[1];
  1802. //div_t n_selected=div((int)get()[GetCurChIndex()].size(),everynth);
  1803. for (int n=0; n*everynth+everystart-1 < (int)get()[GetCurChIndex()].size(); ++n) {
  1804. try {
  1805. SelectTrace(n*everynth+everystart-1, baseBeg, baseEnd);
  1806. }
  1807. catch (const std::out_of_range& e) {
  1808. wxGetApp().ExceptMsg( wxString::FromAscii(e.what()) );
  1809. }
  1810. }
  1811. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1812. pFrame->SetSelected(GetSelectedSections().size());
  1813. Focus();
  1814. }
  1815. void wxStfDoc::SelectTracesOfType(wxCommandEvent &WXUNUSED(event)) {
  1816. // TODO: dialog should display possible selections
  1817. //insert standard values:
  1818. std::vector<std::string> labels(1);
  1819. Vector_double defaults(labels.size());
  1820. labels[0]="Select Trace of Type";defaults[0]=1;
  1821. stf::UserInput init(labels,defaults,"Select trace of type");
  1822. wxStfUsrDlg EveryDialog(GetDocumentWindow(),init);
  1823. if (EveryDialog.ShowModal()!=wxID_OK) return;
  1824. Vector_double input(EveryDialog.readInput());
  1825. if (input.size()!=1) return;
  1826. int selTyp=(int)input[0];
  1827. for (size_t n=0; n < get()[GetCurChIndex()].size(); ++n) {
  1828. if (GetSectionType(n)==selTyp) SelectTrace(n, baseBeg, baseEnd);
  1829. }
  1830. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1831. pFrame->SetSelected(GetSelectedSections().size());
  1832. Focus();
  1833. }
  1834. void wxStfDoc::UnselectTracesOfType(wxCommandEvent &WXUNUSED(event)) {
  1835. // TODO: dialog should display possible selections
  1836. //insert standard values:
  1837. std::vector<std::string> labels(1);
  1838. Vector_double defaults(labels.size());
  1839. labels[0]="Unselect Traces of Type";defaults[0]=1;
  1840. stf::UserInput init(labels,defaults,"Unselect trace of type");
  1841. wxStfUsrDlg EveryDialog(GetDocumentWindow(),init);
  1842. if (EveryDialog.ShowModal()!=wxID_OK) return;
  1843. Vector_double input(EveryDialog.readInput());
  1844. if (input.size()!=1) return;
  1845. int selTyp=(int)input[0];
  1846. for (int n=0; n < (int)get()[GetCurChIndex()].size(); ++n) {
  1847. if (GetSectionType(n)==selTyp) UnselectTrace(n);
  1848. }
  1849. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1850. pFrame->SetSelected(GetSelectedSections().size());
  1851. Focus();
  1852. }
  1853. void wxStfDoc::Unselectsome(wxCommandEvent &WXUNUSED(event)) {
  1854. if (GetSelectedSections().size() < get()[GetCurChIndex()].size()) {
  1855. wxGetApp().ErrorMsg(wxT("Select all traces first"));
  1856. return;
  1857. }
  1858. //insert standard values:
  1859. std::vector<std::string> labels(2);
  1860. Vector_double defaults(labels.size());
  1861. labels[0]="Unselect every x-th trace:";defaults[0]=1;
  1862. labels[1]="Starting with the y-th:";defaults[1]=1;
  1863. stf::UserInput init(labels,defaults,"Unselect every n-th (1-based)");
  1864. wxStfUsrDlg EveryDialog(GetDocumentWindow(),init);
  1865. if (EveryDialog.ShowModal()!=wxID_OK) return;
  1866. Vector_double input(EveryDialog.readInput());
  1867. if (input.size()!=2) return;
  1868. int everynth=(int)input[0];
  1869. int everystart=(int)input[1];
  1870. //div_t n_unselected=div((int)get()[GetCurChIndex()].size(),everynth);
  1871. for (int n=0; n*everynth+everystart-1 < (int)get()[GetCurChIndex()].size(); ++n) {
  1872. UnselectTrace(n*everynth+everystart-1);
  1873. }
  1874. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1875. pFrame->SetSelected(GetSelectedSections().size());
  1876. Focus();
  1877. }
  1878. void wxStfDoc::Selectall(wxCommandEvent& event) {
  1879. //Make sure all traces are unselected prior to selecting them all:
  1880. if ( !GetSelectedSections().empty() )
  1881. Deleteselected(event);
  1882. for (int n_s=0; n_s<(int)get()[GetCurChIndex()].size(); ++n_s) {
  1883. SelectTrace(n_s, baseBeg, baseEnd);
  1884. }
  1885. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1886. pFrame->SetSelected(GetSelectedSections().size());
  1887. Focus();
  1888. }
  1889. void wxStfDoc::Deleteselected(wxCommandEvent &WXUNUSED(event)) {
  1890. wxStfChildFrame* pFrame=(wxStfChildFrame*)GetDocumentWindow();
  1891. if( !GetSelectedSections().empty() ) {
  1892. GetSelectedSectionsW().clear();
  1893. GetSelectBaseW().clear();
  1894. //Update selected traces string in the trace navigator
  1895. pFrame->SetSelected(GetSelectedSections().size());
  1896. } else {
  1897. wxGetApp().ErrorMsg(wxT("No selected trace to remove"));
  1898. return;
  1899. }
  1900. // refresh the view once we are through:
  1901. if (pFrame->ShowSelected()) {
  1902. wxStfView* pView=(wxStfView*)GetFirstView();
  1903. if (pView!=NULL && pView->GetGraph()!=NULL)
  1904. pView->GetGraph()->Refresh();
  1905. }
  1906. Focus();
  1907. }
  1908. void wxStfDoc::Focus() {
  1909. UpdateSelectedButton();
  1910. // refresh the view once we are through:
  1911. wxStfView* pView=(wxStfView*)GetFirstView();
  1912. if (pView != nullptr && pView->GetGraph() != nullptr) {
  1913. pView->GetGraph()->Enable();
  1914. pView->GetGraph()->SetFocus();
  1915. }
  1916. }
  1917. void wxStfDoc::UpdateSelectedButton() {
  1918. // control whether trace has been selected:
  1919. bool selected=false;
  1920. for (c_st_it cit = GetSelectedSections().begin();
  1921. cit != GetSelectedSections().end() && !selected;
  1922. ++cit) {
  1923. if (*cit == GetCurSecIndex()) {
  1924. selected = true;
  1925. }
  1926. }
  1927. // Set status of selection button:
  1928. wxStfParentFrame* parentFrame = wxGetApp().GetMainFrame();
  1929. if (parentFrame) {
  1930. parentFrame->SetSelectedButton( selected );
  1931. }
  1932. }
  1933. void wxStfDoc::Filter(wxCommandEvent& WXUNUSED(event)) {
  1934. #ifndef TEST_MINIMAL
  1935. if (GetSelectedSections().empty()) {
  1936. wxGetApp().ErrorMsg(wxT("No traces selected"));
  1937. return;
  1938. }
  1939. //--For details on the Fast Fourier Transform see NR in C++, chapters 12 and 13
  1940. std::vector<std::string> windowLabels(2);
  1941. Vector_double windowDefaults(windowLabels.size());
  1942. windowLabels[0]="From point #:";windowDefaults[0]=0;
  1943. windowLabels[1]="To point #:";windowDefaults[1]=(int)cursec().size()-1;
  1944. stf::UserInput initWindow(windowLabels,windowDefaults,"Filter window");
  1945. wxStfUsrDlg FilterWindowDialog(GetDocumentWindow(),initWindow);
  1946. if (FilterWindowDialog.ShowModal()!=wxID_OK) return;
  1947. Vector_double windowInput(FilterWindowDialog.readInput());
  1948. if (windowInput.size()!=2) return;
  1949. int llf=(int)windowInput[0];
  1950. int ulf=(int)windowInput[1];
  1951. wxStfFilterSelDlg FilterSelectDialog(GetDocumentWindow());
  1952. if (FilterSelectDialog.ShowModal()!=wxID_OK) return;
  1953. int fselect=FilterSelectDialog.GetFilterSelect();
  1954. int size=0;
  1955. bool inverse=true;
  1956. switch (fselect) {
  1957. case 1:
  1958. size=3; break;
  1959. case 2:
  1960. case 3:
  1961. size=1;
  1962. break;
  1963. }
  1964. wxStfGaussianDlg FftDialog(GetDocumentWindow());
  1965. Vector_double a(size);
  1966. switch (fselect) {
  1967. case 1:
  1968. if (FftDialog.ShowModal()!=wxID_OK) return;
  1969. a[0]=(int)(FftDialog.Amp()*100000.0)/100000.0; /*amplitude from 0 to 1*/
  1970. a[1]=(int)(FftDialog.Center()*100000.0)/100000.0; /*center in kHz*/
  1971. a[2]=(int)(FftDialog.Width()*100000.0)/100000.0; /*width in kHz*/
  1972. break;
  1973. case 2:
  1974. case 3: {
  1975. //insert standard values:
  1976. std::vector<std::string> labels(1);
  1977. Vector_double defaults(labels.size());
  1978. labels[0]="Cutoff frequency (kHz):";
  1979. defaults[0]=10;
  1980. stf::UserInput init(labels,defaults,"Set frequency");
  1981. wxStfUsrDlg FilterHighLowDialog(GetDocumentWindow(),init);
  1982. if (FilterHighLowDialog.ShowModal()!=wxID_OK) return;
  1983. Vector_double input(FilterHighLowDialog.readInput());
  1984. if (input.size()!=1) return;
  1985. a[0]=(int)(input[0]*100000.0)/100000.0; /*midpoint of sigmoid curve in kHz*/
  1986. break;
  1987. }
  1988. }
  1989. wxBusyCursor wc;
  1990. //--I. Defining the parameters of the filter function
  1991. /*sampling interval in ms*/
  1992. Channel TempChannel(GetSelectedSections().size(), get()[GetCurChIndex()][GetSelectedSections()[0]].size());
  1993. std::size_t n = 0;
  1994. for (c_st_it cit = GetSelectedSections().begin(); cit != GetSelectedSections().end(); cit++) {
  1995. try {
  1996. switch (fselect) {
  1997. case 3: {
  1998. Section FftTemp(stfnum::filter(get()[GetCurChIndex()][*cit].get(),
  1999. llf,ulf,a,(int)GetSR(),stfnum::fgaussColqu,false));
  2000. FftTemp.SetXScale(get()[GetCurChIndex()][*cit].GetXScale());
  2001. FftTemp.SetSectionDescription( get()[GetCurChIndex()][*cit].GetSectionDescription()+
  2002. ", filtered");
  2003. TempChannel.InsertSection(FftTemp, n);
  2004. break;
  2005. }
  2006. case 2: {
  2007. Section FftTemp(stfnum::filter(get()[GetCurChIndex()][*cit].get(),
  2008. llf,ulf,a,(int)GetSR(),stfnum::fbessel4,false));
  2009. FftTemp.SetXScale(get()[GetCurChIndex()][*cit].GetXScale());
  2010. FftTemp.SetSectionDescription( get()[GetCurChIndex()][*cit].GetSectionDescription()+
  2011. ", filtered" );
  2012. TempChannel.InsertSection(FftTemp, n);
  2013. break;
  2014. }
  2015. case 1: {
  2016. Section FftTemp(stfnum::filter(get()[GetCurChIndex()][*cit].get(),
  2017. llf,ulf,a,(int)GetSR(),stfnum::fgauss,inverse));
  2018. FftTemp.SetXScale(get()[GetCurChIndex()][*cit].GetXScale());
  2019. FftTemp.SetSectionDescription( get()[GetCurChIndex()][*cit].GetSectionDescription()+
  2020. std::string(", filtered") );
  2021. TempChannel.InsertSection(FftTemp, n);
  2022. break;
  2023. }
  2024. }
  2025. }
  2026. catch (const std::exception& e) {
  2027. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2028. }
  2029. n++;
  2030. }
  2031. if (TempChannel.size()>0) {
  2032. Recording Fft(TempChannel);
  2033. Fft.CopyAttributes(*this);
  2034. wxGetApp().NewChild(Fft, this,GetTitle()+wxT(", filtered"));
  2035. }
  2036. #endif
  2037. }
  2038. void wxStfDoc::P_over_N(wxCommandEvent& WXUNUSED(event)){
  2039. //insert standard values:
  2040. std::vector<std::string> labels(1);
  2041. Vector_double defaults(labels.size());
  2042. labels[0]="N = (mind polarity!)";defaults[0]=-4;
  2043. stf::UserInput init(labels,defaults,"P over N");
  2044. wxStfUsrDlg PonDialog(GetDocumentWindow(),init);
  2045. if (PonDialog.ShowModal()!=wxID_OK) return;
  2046. Vector_double input(PonDialog.readInput());
  2047. if (input.size()!=1) return;
  2048. int PoN=(int)fabs(input[0]);
  2049. int ponDirection=input[0]<0? -1:1;
  2050. int new_sections=(int)get()[GetCurChIndex()].size()/(PoN+1);
  2051. if (new_sections<1) {
  2052. wxGetApp().ErrorMsg(wxT("Not enough traces for P/n correction"));
  2053. return;
  2054. }
  2055. //File dialog box
  2056. wxBusyCursor wc;
  2057. Channel TempChannel(new_sections);
  2058. //read and PoN
  2059. for (int n_section=0; n_section < new_sections; n_section++) {
  2060. //Section loop
  2061. Section TempSection(get()[GetCurChIndex()][n_section].size());
  2062. TempSection.SetXScale(get()[GetCurChIndex()][n_section].GetXScale());
  2063. for (int n_point=0; n_point < (int)get()[GetCurChIndex()][n_section].size(); n_point++)
  2064. TempSection[n_point]=0.0;
  2065. //Addition of the PoN-values:
  2066. for (int n_PoN=1; n_PoN < PoN+1; n_PoN++)
  2067. for (int n_point=0; n_point < (int)get()[GetCurChIndex()][n_section].size(); n_point++)
  2068. TempSection[n_point] += get()[GetCurChIndex()][n_PoN+(n_section*(PoN+1))][n_point];
  2069. //Subtraction from the original values:
  2070. for (int n_point=0; n_point < (int)get()[GetCurChIndex()][n_section].size(); n_point++)
  2071. TempSection[n_point] = get()[GetCurChIndex()][n_section*(PoN+1)][n_point]-
  2072. TempSection[n_point]*ponDirection;
  2073. std::ostringstream povernLabel;
  2074. povernLabel << GetTitle() << ", #" << n_section << ", P over N";
  2075. TempSection.SetSectionDescription(povernLabel.str());
  2076. try {
  2077. TempChannel.InsertSection(TempSection,n_section);
  2078. }
  2079. catch (const std::out_of_range& e) {
  2080. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2081. }
  2082. }
  2083. if (TempChannel.size()>0) {
  2084. Recording DataPoN(TempChannel);
  2085. DataPoN.CopyAttributes(*this);
  2086. wxGetApp().NewChild(DataPoN,this,GetTitle()+wxT(", p over n subtracted"));
  2087. }
  2088. }
  2089. void wxStfDoc::Plotextraction(stf::extraction_mode mode) {
  2090. std::vector<stf::SectionPointer> sectionList(wxGetApp().GetSectionsWithFits());
  2091. if (sectionList.empty()) {
  2092. wxGetApp().ErrorMsg(
  2093. wxT("You have to create a template first\nby fitting a function to an event") );
  2094. return;
  2095. }
  2096. wxStfEventDlg MiniDialog(GetDocumentWindow(), sectionList, false);
  2097. if (MiniDialog.ShowModal()!=wxID_OK) {
  2098. return;
  2099. }
  2100. int nTemplate=MiniDialog.GetTemplate();
  2101. try {
  2102. Vector_double templateWave(
  2103. sectionList.at(nTemplate).sec_attr.storeFitEnd -
  2104. sectionList.at(nTemplate).sec_attr.storeFitBeg);
  2105. for ( std::size_t n_p=0; n_p < templateWave.size(); n_p++ ) {
  2106. templateWave[n_p] = sectionList.at(nTemplate).sec_attr.fitFunc->func(
  2107. n_p*GetXScale(), sectionList.at(nTemplate).sec_attr.bestFitP);
  2108. }
  2109. wxBusyCursor wc;
  2110. #undef min
  2111. #undef max
  2112. // subtract offset and normalize:
  2113. double fmax = *std::max_element(templateWave.begin(), templateWave.end());
  2114. double fmin = *std::min_element(templateWave.begin(), templateWave.end());
  2115. templateWave = stfio::vec_scal_minus(templateWave, fmax);
  2116. double minim=fabs(fmin);
  2117. templateWave = stfio::vec_scal_div(templateWave, minim);
  2118. std::string section_description, window_title;
  2119. Section TempSection(cursec().get().size());
  2120. switch (mode) {
  2121. case stf::criterion: {
  2122. stf::wxProgressInfo progDlg("Computing detection criterion...", "Computing detection criterion...", 100);
  2123. TempSection = Section(stfnum::detectionCriterion( cursec().get(), templateWave, progDlg));
  2124. section_description = "Detection criterion of ";
  2125. window_title = ", detection criterion";
  2126. break;
  2127. }
  2128. case stf::correlation: {
  2129. stf::wxProgressInfo progDlg("Computing linear correlation...", "Computing linear correlation...", 100);
  2130. TempSection = Section(stfnum::linCorr(cursec().get(), templateWave, progDlg));
  2131. section_description = "Template correlation of ";
  2132. window_title = ", linear correlation";
  2133. break;
  2134. }
  2135. case stf::deconvolution:
  2136. std::string usrInStr[2] = {"Lowpass (kHz)", "Highpass (kHz)"};
  2137. double usrInDbl[2] = {0.5, 0.0001};
  2138. stf::UserInput Input( std::vector<std::string>(usrInStr, usrInStr+2),
  2139. Vector_double (usrInDbl, usrInDbl+2), "Filter settings" );
  2140. wxStfUsrDlg myDlg( GetDocumentWindow(), Input );
  2141. if (myDlg.ShowModal()!=wxID_OK) return;
  2142. Vector_double filter = myDlg.readInput();
  2143. stf::wxProgressInfo progDlg("Computing deconvolution...", "Starting deconvolution...", 100);
  2144. TempSection = Section(stfnum::deconvolve(cursec().get(), templateWave,
  2145. (int)GetSR(), filter[1], filter[0], progDlg));
  2146. section_description = "Template deconvolution from ";
  2147. window_title = ", deconvolution";
  2148. break;
  2149. }
  2150. if (TempSection.size()==0) return;
  2151. TempSection.SetXScale(cursec().GetXScale());
  2152. TempSection.SetSectionDescription(section_description +
  2153. cursec().GetSectionDescription());
  2154. Channel TempChannel(TempSection);
  2155. Recording detCrit(TempChannel);
  2156. detCrit.CopyAttributes(*this);
  2157. wxGetApp().NewChild(detCrit, this, GetTitle() + stf::std2wx(window_title));
  2158. }
  2159. catch (const std::runtime_error& e) {
  2160. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2161. }
  2162. catch (const std::exception& e) {
  2163. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2164. }
  2165. }
  2166. void wxStfDoc::Plotcriterion(wxCommandEvent& WXUNUSED(event)) {
  2167. Plotextraction(stf::criterion);
  2168. }
  2169. void wxStfDoc::Plotcorrelation(wxCommandEvent& WXUNUSED(event)) {
  2170. Plotextraction(stf::correlation);
  2171. }
  2172. void wxStfDoc::Plotdeconvolution(wxCommandEvent& WXUNUSED(event)) {
  2173. Plotextraction(stf::deconvolution);
  2174. }
  2175. void wxStfDoc::MarkEvents(wxCommandEvent& WXUNUSED(event)) {
  2176. std::vector<stf::SectionPointer> sectionList(wxGetApp().GetSectionsWithFits());
  2177. if (sectionList.empty()) {
  2178. wxGetApp().ErrorMsg(
  2179. wxT( "You have to create a template first\nby fitting a function to an event" ) );
  2180. return;
  2181. }
  2182. wxStfEventDlg MiniDialog( GetDocumentWindow(), sectionList, true );
  2183. if ( MiniDialog.ShowModal()!=wxID_OK ) {
  2184. return;
  2185. }
  2186. int nTemplate=MiniDialog.GetTemplate();
  2187. stf::event_polarity_mode polarityMode = MiniDialog.GetPolarityMode();
  2188. try {
  2189. Vector_double rawTemplateWave(
  2190. sectionList.at(nTemplate).sec_attr.storeFitEnd -
  2191. sectionList.at(nTemplate).sec_attr.storeFitBeg);
  2192. for ( std::size_t n_p=0; n_p < rawTemplateWave.size(); n_p++ ) {
  2193. rawTemplateWave[n_p] = sectionList.at(nTemplate).sec_attr.fitFunc->func(
  2194. n_p*GetXScale(), sectionList.at(nTemplate).sec_attr.bestFitP);
  2195. }
  2196. if (rawTemplateWave.empty()) {
  2197. wxGetApp().ErrorMsg(wxT("Error: Template waveform is empty."));
  2198. return;
  2199. }
  2200. auto classifyPolarity = [](const std::vector<double>& data,
  2201. std::size_t left,
  2202. std::size_t right,
  2203. double baselineMean,
  2204. double ambiguityBand) -> int {
  2205. if (data.empty()) {
  2206. return 0;
  2207. }
  2208. if (left >= data.size()) {
  2209. left = data.size()-1;
  2210. }
  2211. if (right >= data.size()) {
  2212. right = data.size()-1;
  2213. }
  2214. if (right < left) {
  2215. return 0;
  2216. }
  2217. double maxDelta = -std::numeric_limits<double>::max();
  2218. double minDelta = std::numeric_limits<double>::max();
  2219. for (std::size_t i = left; i <= right; ++i) {
  2220. const double delta = data[i] - baselineMean;
  2221. if (delta > maxDelta) maxDelta = delta;
  2222. if (delta < minDelta) minDelta = delta;
  2223. }
  2224. const double absMax = fabs(maxDelta);
  2225. const double absMin = fabs(minDelta);
  2226. double dominantDelta = 0.0;
  2227. if (absMax > absMin)
  2228. dominantDelta = maxDelta;
  2229. else if (absMin > absMax)
  2230. dominantDelta = minDelta;
  2231. if (fabs(dominantDelta) <= ambiguityBand) {
  2232. return 0;
  2233. }
  2234. return dominantDelta > 0.0 ? 1 : -1;
  2235. };
  2236. // Compute template polarity from the unnormalized template waveform.
  2237. // Use the average of the first and last point as baseline surrogate:
  2238. // this is robust for fitted template snippets that may not contain much
  2239. // pre-event baseline at the beginning.
  2240. const double templateBaseline =
  2241. 0.5 * (rawTemplateWave.front() + rawTemplateWave.back());
  2242. const int templatePolarity = classifyPolarity(rawTemplateWave, 0,
  2243. rawTemplateWave.size()-1,
  2244. templateBaseline, 0.0);
  2245. auto polarityMatchesSelection = [&](int eventPolarity) -> bool {
  2246. switch (polarityMode) {
  2247. case stf::polarity_positive_only:
  2248. return eventPolarity >= 0;
  2249. case stf::polarity_negative_only:
  2250. return eventPolarity <= 0;
  2251. case stf::polarity_same_as_template:
  2252. return (eventPolarity == 0 || templatePolarity == 0 || eventPolarity == templatePolarity);
  2253. case stf::polarity_both:
  2254. default:
  2255. return true;
  2256. }
  2257. };
  2258. Vector_double templateWave(rawTemplateWave);
  2259. wxBusyCursor wc;
  2260. #undef min
  2261. #undef max
  2262. // subtract offset and normalize:
  2263. double fmax = *std::max_element(templateWave.begin(), templateWave.end());
  2264. double fmin = *std::min_element(templateWave.begin(), templateWave.end());
  2265. templateWave = stfio::vec_scal_minus(templateWave, fmax);
  2266. double minim=fabs(fmin);
  2267. templateWave = stfio::vec_scal_div(templateWave, minim);
  2268. Vector_double detect( cursec().get().size() - templateWave.size() );
  2269. switch (MiniDialog.GetMode()) {
  2270. case stf::criterion: {
  2271. stf::wxProgressInfo progDlg("Computing detection criterion...", "Computing detection criterion...", 100);
  2272. detect=stfnum::detectionCriterion(cursec().get(), templateWave, progDlg);
  2273. break;
  2274. }
  2275. case stf::correlation: {
  2276. stf::wxProgressInfo progDlg("Computing linear correlation...", "Computing linear correlation...", 100);
  2277. detect=stfnum::linCorr(cursec().get(), templateWave, progDlg);
  2278. break;
  2279. }
  2280. case stf::deconvolution:
  2281. std::string usrInStr[2] = {"Lowpass (kHz)", "Highpass (kHz)"};
  2282. double usrInDbl[2] = {0.5, 0.0001};
  2283. stf::UserInput Input( std::vector<std::string>(usrInStr, usrInStr+2),
  2284. Vector_double (usrInDbl, usrInDbl+2), "Filter settings" );
  2285. wxStfUsrDlg myDlg( GetDocumentWindow(), Input );
  2286. if (myDlg.ShowModal()!=wxID_OK) return;
  2287. Vector_double filter = myDlg.readInput();
  2288. stf::wxProgressInfo progDlg("Computing deconvolution...", "Starting deconvolution...", 100);
  2289. detect=stfnum::deconvolve(cursec().get(), templateWave, (int)GetSR(), filter[1], filter[0], progDlg);
  2290. break;
  2291. }
  2292. if (detect.empty()) {
  2293. wxGetApp().ErrorMsg(wxT("Error: Detection criterion is empty."));
  2294. return;
  2295. }
  2296. std::vector<int> startIndices(
  2297. stfnum::peakIndices( detect, MiniDialog.GetThreshold(),
  2298. MiniDialog.GetMinDistance() ) );
  2299. if (startIndices.empty()) {
  2300. wxGetApp().ErrorMsg( wxT( "No events were found. Try to lower the threshold." ) );
  2301. return;
  2302. }
  2303. // erase old events:
  2304. ClearEvents(GetCurChIndex(), GetCurSecIndex());
  2305. wxStfView* pView = (wxStfView*)GetFirstView();
  2306. wxStfGraph* pGraph = pView->GetGraph();
  2307. std::size_t nKept = 0;
  2308. for (c_int_it cit = startIndices.begin(); cit != startIndices.end(); ++cit ) {
  2309. // Find peak in this event:
  2310. double baselineMean=0;
  2311. int baselineCount = 0;
  2312. for ( int n_mean = *cit-baseline;
  2313. n_mean < *cit;
  2314. ++n_mean )
  2315. {
  2316. if (n_mean < 0) {
  2317. baselineMean += cursec().at(0);
  2318. } else {
  2319. baselineMean += cursec().at(n_mean);
  2320. }
  2321. ++baselineCount;
  2322. }
  2323. baselineMean /= (baselineCount > 0 ? baselineCount : 1);
  2324. double baselineVar = 0.0;
  2325. if (baselineCount > 1) {
  2326. for ( int n_mean = *cit-baseline;
  2327. n_mean < *cit;
  2328. ++n_mean )
  2329. {
  2330. const double baselinePoint = (n_mean < 0) ? cursec().at(0) : cursec().at(n_mean);
  2331. const double diff = baselinePoint - baselineMean;
  2332. baselineVar += diff*diff;
  2333. }
  2334. baselineVar /= static_cast<double>(baselineCount - 1);
  2335. }
  2336. const double baselineSd = sqrt(std::max(0.0, baselineVar));
  2337. double peakIndex=0;
  2338. size_t eventl = templateWave.size();
  2339. if (*cit + eventl >= cursec().get().size()) {
  2340. eventl = cursec().get().size()-1- (*cit);
  2341. }
  2342. if (eventl == 0) {
  2343. continue;
  2344. }
  2345. const int eventPolarity = classifyPolarity(
  2346. cursec().get(),
  2347. static_cast<std::size_t>(*cit),
  2348. static_cast<std::size_t>(*cit) + eventl,
  2349. baselineMean,
  2350. baselineSd
  2351. );
  2352. if (!polarityMatchesSelection(eventPolarity)) {
  2353. continue;
  2354. }
  2355. sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList.push_back(
  2356. stf::Event( *cit, 0, templateWave.size(), new wxCheckBox(
  2357. pGraph, -1, wxEmptyString) ) );
  2358. stfnum::peak( cursec().get(), baselineMean, *cit, *cit + eventl,
  2359. 1, stfnum::both, peakIndex );
  2360. if (peakIndex != peakIndex || peakIndex < 0 || peakIndex >= cursec().get().size()) {
  2361. throw std::runtime_error("Error during peak detection (result is NAN)\n");
  2362. }
  2363. // set peak index of this event:
  2364. sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList.back().SetEventPeakIndex((int)peakIndex);
  2365. ++nKept;
  2366. }
  2367. if (nKept == 0) {
  2368. wxGetApp().ErrorMsg(wxT("No events were found that match the selected polarity."));
  2369. }
  2370. if (pGraph != nullptr) {
  2371. pGraph->Refresh();
  2372. }
  2373. }
  2374. catch (std::out_of_range const& e) {
  2375. wxGetApp().ExceptMsg( wxString( e.what(), wxConvLocal ));
  2376. }
  2377. catch (const std::runtime_error& e) {
  2378. wxGetApp().ExceptMsg( wxString( e.what(), wxConvLocal ));
  2379. }
  2380. catch (const std::exception& e) {
  2381. wxGetApp().ExceptMsg( wxString( e.what(), wxConvLocal ));
  2382. }
  2383. }
  2384. void wxStfDoc::Extract( wxCommandEvent& WXUNUSED(event) ) {
  2385. try {
  2386. stfnum::Table events(GetCurrentSectionAttributes().eventList.size(), 2);
  2387. events.SetColLabel(0, "Time of event onset");
  2388. events.SetColLabel(1, "Inter-event interval");
  2389. // using the peak indices (these are the locations of the beginning of an optimal
  2390. // template matching), new sections are created:
  2391. // count non-discarded events:
  2392. std::size_t n_real = 0;
  2393. for (c_event_it cit = GetCurrentSectionAttributes().eventList.begin();
  2394. cit != GetCurrentSectionAttributes().eventList.end(); ++cit) {
  2395. n_real += (int)(!cit->GetDiscard());
  2396. }
  2397. Channel TempChannel2(n_real);
  2398. std::vector<int> peakIndices(n_real);
  2399. n_real = 0;
  2400. c_event_it lastEventIt = GetCurrentSectionAttributes().eventList.begin();
  2401. for (c_event_it it = GetCurrentSectionAttributes().eventList.begin();
  2402. it != GetCurrentSectionAttributes().eventList.end(); ++it) {
  2403. if (!it->GetDiscard()) {
  2404. wxString miniName; miniName << wxT( "Event #" ) << (int)n_real+1;
  2405. events.SetRowLabel(n_real, stf::wx2std(miniName));
  2406. events.at(n_real,0) = (double)it->GetEventStartIndex() / GetSR();
  2407. events.at(n_real,1)=
  2408. ((double)(it->GetEventStartIndex() -
  2409. lastEventIt->GetEventStartIndex())) / GetSR();
  2410. // add some baseline at the beginning and end:
  2411. std::size_t eventSize = it->GetEventSize() + 2*baseline;
  2412. Section TempSection2( eventSize );
  2413. for ( std::size_t n_new = 0; n_new < eventSize; ++n_new ) {
  2414. // make sure index is not out of range:
  2415. int index = it->GetEventStartIndex() + n_new - baseline;
  2416. if (index < 0)
  2417. index = 0;
  2418. if (index >= (int)cursec().size())
  2419. index = cursec().size()-1;
  2420. TempSection2[n_new] = cursec()[index];
  2421. }
  2422. std::ostringstream eventDesc;
  2423. eventDesc << "Extracted event #" << (int)n_real;
  2424. TempSection2.SetSectionDescription(eventDesc.str());
  2425. TempSection2.SetXScale(get()[GetCurChIndex()][GetCurSecIndex()].GetXScale());
  2426. TempChannel2.InsertSection( TempSection2, n_real );
  2427. n_real++;
  2428. lastEventIt = it;
  2429. }
  2430. }
  2431. if (TempChannel2.size()>0) {
  2432. Recording Minis( TempChannel2 );
  2433. Minis.CopyAttributes( *this );
  2434. wxStfDoc* pDoc=wxGetApp().NewChild( Minis, this,
  2435. GetTitle()+wxT(", extracted events") );
  2436. if (pDoc != nullptr) {
  2437. wxStfChildFrame* pChild=(wxStfChildFrame*)pDoc->GetDocumentWindow();
  2438. if (pChild != nullptr) {
  2439. pChild->ShowTable(events,wxT("Extracted events"));
  2440. }
  2441. }
  2442. }
  2443. }
  2444. catch (const std::out_of_range& e) {
  2445. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2446. }
  2447. catch (const std::runtime_error& e) {
  2448. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2449. }
  2450. catch (const std::exception& e) {
  2451. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2452. }
  2453. }
  2454. void wxStfDoc::InteractiveEraseEvents( wxCommandEvent& WXUNUSED(event) ) {
  2455. if (wxMessageDialog( GetDocumentWindow(), wxT("Do you really want to erase all events?"),
  2456. wxT("Erase all events"), wxYES_NO ).ShowModal()==wxID_YES)
  2457. {
  2458. try {
  2459. ClearEvents(GetCurChIndex(), GetCurSecIndex());
  2460. }
  2461. catch (const std::out_of_range& e) {
  2462. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2463. }
  2464. }
  2465. }
  2466. void wxStfDoc::OnAddAnnotation( wxCommandEvent& WXUNUSED(event) ) {
  2467. try {
  2468. // retrieve the position where to add the annotation:
  2469. wxStfView* pView = (wxStfView*)GetFirstView();
  2470. wxStfGraph* pGraph = pView->GetGraph();
  2471. int newStartPos = pGraph->get_eventPos();
  2472. Annotation newAnnotation(newStartPos, 0);
  2473. this->at(GetCurChIndex())[GetCurSecIndex()].AddAnnotation(newAnnotation);
  2474. eventThreshold = INT32_MAX;
  2475. }
  2476. catch (const std::runtime_error& e) {
  2477. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2478. }
  2479. catch (const std::exception& e) {
  2480. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2481. }
  2482. }
  2483. void wxStfDoc::OnRemoveAnnotation( wxCommandEvent& WXUNUSED(event) ) {
  2484. try {
  2485. wxStfView* pView = (wxStfView*)GetFirstView();
  2486. wxStfGraph* pGraph = pView->GetGraph();
  2487. int cursorPos = pGraph->get_eventPos();
  2488. int minDist = INT_MAX;
  2489. std::vector<Annotation> annotationsList = this->at(GetCurChIndex())[GetCurSecIndex()].GetAnnotationList();
  2490. if (annotationsList.empty()) {
  2491. return;
  2492. }
  2493. int distFromCursor;
  2494. std::size_t indexOfMinDist = 0;
  2495. for (std::size_t i = 0; i < annotationsList.size(); ++i) {
  2496. distFromCursor = abs((int)(annotationsList.at(i).GetAnnotationPosition()) - cursorPos);
  2497. if (distFromCursor < minDist ) {
  2498. indexOfMinDist = i;
  2499. minDist = distFromCursor;
  2500. }
  2501. }
  2502. this->at(GetCurChIndex())[GetCurSecIndex()].RemoveAnnotation(indexOfMinDist);
  2503. eventThreshold = INT32_MAX;
  2504. }
  2505. catch (const std::runtime_error& e) {
  2506. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2507. }
  2508. catch (const std::exception& e) {
  2509. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2510. }
  2511. }
  2512. void wxStfDoc::OnEraseAllAnnotations(wxCommandEvent&)
  2513. {
  2514. this->at(GetCurChIndex())[GetCurSecIndex()].EraseAllAnnotations();
  2515. eventThreshold = INT32_MAX;
  2516. }
  2517. void wxStfDoc::OnExportAnnotations(wxCommandEvent&)
  2518. {
  2519. try{
  2520. wxStfView* pView = (wxStfView*)GetFirstView();
  2521. wxStfGraph* pGraph = pView->GetGraph();
  2522. wxString ascFilter = wxT("Annotations (*.asc)|*asc");
  2523. wxFileDialog* exportAnnotationsDialog = new wxFileDialog(pGraph, wxT("Export Annotations"),wxT(""), wxT(""),
  2524. ascFilter, wxFD_SAVE | wxFD_PREVIEW);
  2525. if (exportAnnotationsDialog->ShowModal() == wxID_OK ){
  2526. wxString filepath = exportAnnotationsDialog->GetPath();
  2527. wxFile asc_file;
  2528. if (!asc_file.Open(filepath + ".asc", wxFile::write)) {
  2529. std::cerr << "Error opening file!\n" ;
  2530. return;
  2531. }
  2532. //print a header to the file
  2533. wxFileName fn(GetFilename());
  2534. std::stringstream ss;
  2535. ss << "#STIMFITv0.16.9" << "\n"
  2536. << "#generated from file " << fn.GetFullName() << "\n"
  2537. << "#SampleRate " << GetSR() << "KHz" << "\n";
  2538. wxString line(ss.str());
  2539. asc_file.Write(line);
  2540. std::size_t relativePositionBase = 0;
  2541. for (std::size_t channelIndex = 0; channelIndex < this->size(); channelIndex++){
  2542. for (std::size_t sectionIndex = 0; sectionIndex < this->at(channelIndex).size(); sectionIndex++){
  2543. std::vector<Annotation> annotationsList = this->at(channelIndex)[sectionIndex].GetAnnotationList();
  2544. for (std::size_t annotationIndex = 0; annotationIndex < annotationsList.size(); annotationIndex++){
  2545. std::stringstream annotationStream;
  2546. annotationStream << channelIndex << "\t"
  2547. << relativePositionBase + annotationsList.at(annotationIndex).GetAnnotationPosition() << "\n";
  2548. wxString annotationLine(annotationStream.str());
  2549. asc_file.Write(annotationLine);
  2550. }
  2551. relativePositionBase += this->at(channelIndex)[sectionIndex].GetSectionSize();
  2552. }
  2553. }
  2554. asc_file.Close();
  2555. }
  2556. }
  2557. catch (const std::runtime_error& e) {
  2558. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2559. }
  2560. catch (const std::exception& e) {
  2561. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2562. }
  2563. }
  2564. void wxStfDoc::OnCalculatedThresholdExpertDetectEvents(double calculatedThreshold, std::size_t filterOrder)
  2565. {
  2566. std::vector<double> fullRawDetectionTrace = CalcRawDetectionTrace(filterOrder, 0, cursec().GetSectionSize() - 1);
  2567. std::vector<stf::Event> detectedEvents = DetectEvents(calculatedThreshold, fullRawDetectionTrace);
  2568. }
  2569. void wxStfDoc::OnExpertDetectEvents(wxCommandEvent&)
  2570. {
  2571. try{
  2572. std::size_t filterOrder = round(0.004 * 1000 * GetSR());
  2573. if (eventThreshold == INT32_MAX){
  2574. std::vector<Annotation> positions = cursec().GetAnnotationList();
  2575. if (positions.size() == 0) throw std::out_of_range("No scoring was set by the expert!");
  2576. wxStfView* pView = (wxStfView*)GetFirstView();
  2577. (void)pView;
  2578. double rangeStart = cursec().GetFirstAnnotationPosition();
  2579. double rangeEnd = cursec().GetLastAnnotationPosition();
  2580. double avgEventInterval = (rangeEnd - rangeStart) / (2 * (positions.size() - 1));
  2581. rangeStart -= avgEventInterval;
  2582. rangeEnd += avgEventInterval;
  2583. std::vector<int> idx(rangeEnd - rangeStart + 1);
  2584. std::iota(idx.begin(), idx.end(), rangeStart);
  2585. double delay = 0; // reconsider type
  2586. std::size_t startPoint = cursec().GetFirstAnnotationPosition();
  2587. std::size_t endPoint = cursec().GetLastAnnotationPosition();
  2588. std::vector<int> c = CalcScoringTrace(positions, startPoint, endPoint);
  2589. std::vector<double> d = CalcRawDetectionTrace(filterOrder, startPoint, endPoint);
  2590. double area = CalcAreaUnderCurve(d, c);
  2591. std::vector<double> sortedRawDetectionTrace = SortScoringTraceByRawDetection(d, c);
  2592. std::vector<int> sortedScoringTrace = std::move(c);
  2593. std::pair<std::size_t, double> kappa = CalcMaxKappa(sortedRawDetectionTrace, sortedScoringTrace);
  2594. double calculatedThreshold = sortedRawDetectionTrace[kappa.first];
  2595. double maxKappa = kappa.second;
  2596. std::vector<double> fullRawDetectionTrace = CalcRawDetectionTrace(filterOrder, 0, cursec().GetSectionSize() - 1);
  2597. ClearEvents(GetCurChIndex(), GetCurSecIndex());
  2598. std::vector<stf::Event> detectedEvents = DetectEvents(calculatedThreshold, fullRawDetectionTrace);
  2599. eventThreshold = calculatedThreshold;
  2600. wxString msg;
  2601. msg.Printf("AUC: %g\nKAPPA: %g\nfilterlength: %zu\nThreshold: %g\nDelay: %g",
  2602. area, maxKappa, filterOrder, calculatedThreshold, delay);
  2603. wxMessageBox(msg, "Metadata", wxOK | wxICON_INFORMATION, nullptr);
  2604. }else{ // no new calculation needed
  2605. ClearEvents(GetCurChIndex(), GetCurSecIndex());
  2606. OnCalculatedThresholdExpertDetectEvents(eventThreshold, filterOrder);
  2607. }
  2608. }
  2609. catch (const std::runtime_error& e) {
  2610. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2611. }
  2612. catch (const std::exception& e) {
  2613. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2614. }
  2615. }
  2616. void wxStfDoc::OnImportAnnotations(wxCommandEvent&)
  2617. {
  2618. try{
  2619. wxStfView* pView = (wxStfView*)GetFirstView();
  2620. wxStfGraph* pGraph = pView->GetGraph();
  2621. wxString ascFilter = wxT("Annotations (*.asc)|*asc");
  2622. wxFileDialog* importAnnotationsDialog = new wxFileDialog(pGraph, wxT("Import Annotations"),wxT(""), wxT(""),
  2623. ascFilter, wxFD_SAVE | wxFD_PREVIEW);
  2624. if (importAnnotationsDialog->ShowModal() == wxID_OK ){
  2625. wxString filepath = importAnnotationsDialog->GetPath();
  2626. wxFile asc_file;
  2627. if (!asc_file.Open(filepath, wxFile::read)) {
  2628. std::cerr << "Error opening file!\n" ;
  2629. return;
  2630. }
  2631. wxString content;
  2632. asc_file.ReadAll(&content);
  2633. asc_file.Close();
  2634. wxStringTokenizer lines(content, "\n", wxTOKEN_STRTOK);
  2635. while (lines.HasMoreTokens()) {
  2636. wxString line = lines.GetNextToken().Trim(true).Trim(false);
  2637. if (line.IsEmpty()|| line.StartsWith("#")) continue; //skip comments or header
  2638. wxStringTokenizer tokens(line, "\t", wxTOKEN_STRTOK);
  2639. if (tokens.CountTokens() < 2) continue;
  2640. long channelIndex, relativePosition;
  2641. tokens.GetNextToken().ToLong(&channelIndex);
  2642. tokens.GetNextToken().ToLong(&relativePosition);
  2643. std::size_t sectionSize = this->at(channelIndex)[0].GetSectionSize();
  2644. std::size_t sectionIndex = ceil(relativePosition / sectionSize);
  2645. Annotation annotation(relativePosition % sectionSize, 0);
  2646. this->at(channelIndex)[sectionIndex].AddAnnotation(annotation);
  2647. eventThreshold = INT32_MAX;
  2648. }
  2649. }
  2650. }
  2651. catch (const std::runtime_error& e) {
  2652. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2653. }
  2654. catch (const std::exception& e) {
  2655. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2656. }
  2657. }
  2658. void wxStfDoc::AddEvent( wxCommandEvent& WXUNUSED(event) ) {
  2659. try {
  2660. // retrieve the position where to add the event:
  2661. wxStfView* pView = (wxStfView*)GetFirstView();
  2662. wxStfGraph* pGraph = pView->GetGraph();
  2663. int newStartPos = pGraph->get_eventPos();
  2664. stf::Event newEvent(newStartPos, 0, GetCurrentSectionAttributes().eventList.at(0).GetEventSize(),
  2665. new wxCheckBox(pGraph, -1, wxEmptyString));
  2666. // Find peak in this event:
  2667. double baselineMean=0;
  2668. for ( int n_mean = newStartPos - baseline;
  2669. n_mean < newStartPos;
  2670. ++n_mean )
  2671. {
  2672. if (n_mean < 0) {
  2673. baselineMean += cursec().at(0);
  2674. } else {
  2675. baselineMean += cursec().at(n_mean);
  2676. }
  2677. }
  2678. baselineMean /= baseline;
  2679. double peakIndex=0;
  2680. stfnum::peak( cursec().get(), baselineMean, newStartPos,
  2681. newStartPos + GetCurrentSectionAttributes().eventList.at(0).GetEventSize(), 1,
  2682. stfnum::both, peakIndex );
  2683. // set peak index of last event:
  2684. newEvent.SetEventPeakIndex( (int)peakIndex );
  2685. // find the position in the current event list where the new
  2686. // event should be inserted:
  2687. bool found = false;
  2688. for (event_it it = sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList.begin();
  2689. it != sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList.end(); ++it) {
  2690. if ( (int)(it->GetEventStartIndex()) > newStartPos ) {
  2691. // insert new event before this event, then break:
  2692. sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList.insert( it, newEvent );
  2693. found = true;
  2694. break;
  2695. }
  2696. }
  2697. // if we are at the end of the list, append the event:
  2698. if (!found)
  2699. sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList.push_back( newEvent );
  2700. }
  2701. catch (const std::out_of_range& e) {
  2702. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2703. }
  2704. catch (const std::runtime_error& e) {
  2705. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2706. }
  2707. catch (const std::exception& e) {
  2708. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  2709. }
  2710. }
  2711. void wxStfDoc::Threshold(wxCommandEvent& WXUNUSED(event)) {
  2712. // get threshold from user input:
  2713. Vector_double thresholdValues(0);
  2714. std::ostringstream thrS;
  2715. thrS << "Threshold (" << at(GetCurChIndex()).GetYUnits() << ")";
  2716. stf::UserInput Input( std::vector<std::string>(1, thrS.str()),
  2717. Vector_double (1,0.0), "Set threshold" );
  2718. wxStfUsrDlg myDlg( GetDocumentWindow(), Input );
  2719. if (myDlg.ShowModal()!=wxID_OK) {
  2720. return;
  2721. }
  2722. thresholdValues=myDlg.readInput();
  2723. std::vector<int> startIndices(
  2724. stfnum::peakIndices( cursec().get(), thresholdValues[0], 0 )
  2725. );
  2726. if (startIndices.empty()) {
  2727. wxGetApp().ErrorMsg(
  2728. wxT("Couldn't find any events;\ntry again with lower threshold")
  2729. );
  2730. }
  2731. // clear table from previous detection
  2732. wxStfView* pView=(wxStfView*)GetFirstView();
  2733. wxStfGraph* pGraph = pView->GetGraph();
  2734. sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList.clear();
  2735. for (c_int_it cit = startIndices.begin(); cit != startIndices.end(); ++cit) {
  2736. sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList.push_back(
  2737. stf::Event(*cit, 0, baseline, new wxCheckBox(pGraph, -1, wxEmptyString)));
  2738. }
  2739. // show results in a table:
  2740. stfnum::Table events(GetCurrentSectionAttributes().eventList.size(),2);
  2741. events.SetColLabel( 0, "Time of event peak");
  2742. events.SetColLabel( 1, "Inter-event interval");
  2743. std::size_t n_event = 0;
  2744. c_event_it lastEventCit = GetCurrentSectionAttributes().eventList.begin();
  2745. for (c_event_it cit2 = GetCurrentSectionAttributes().eventList.begin();
  2746. cit2 != GetCurrentSectionAttributes().eventList.end(); ++cit2) {
  2747. wxString eventName; eventName << wxT("Event #") << (int)n_event+1;
  2748. events.SetRowLabel(n_event, stf::wx2std(eventName));
  2749. events.at(n_event,0)= (double)cit2->GetEventStartIndex() / GetSR();
  2750. events.at(n_event,1)=
  2751. ((double)(cit2->GetEventStartIndex() -
  2752. lastEventCit->GetEventStartIndex()) ) / GetSR();
  2753. n_event++;
  2754. lastEventCit = cit2;
  2755. }
  2756. wxStfChildFrame* pChild=(wxStfChildFrame*)GetDocumentWindow();
  2757. if (pChild!=NULL) {
  2758. pChild->ShowTable(events,wxT("Extracted events"));
  2759. }
  2760. }
  2761. //Function calculates the peak and respective measures: base, Lo/Hi rise time
  2762. //half duration, ratio of rise/slope and maximum slope
  2763. void wxStfDoc::Measure( )
  2764. {
  2765. double var=0.0;
  2766. if (cursec().get().size() == 0) return;
  2767. try {
  2768. cursec().at(0);
  2769. }
  2770. catch (const std::out_of_range&) {
  2771. return;
  2772. }
  2773. long windowLength = 1;
  2774. /*
  2775. windowLength (defined in samples) determines the size of the window for computing slopes.
  2776. if the window length larger than 1 is used, a kind of smoothing and low pass filtering is applied.
  2777. If slope estimates from data with different sampling rates should be compared, the
  2778. window should be choosen in such a way that the length in milliseconds is approximately the same.
  2779. This reduces some variability, the slope estimates are more robust and comparible.
  2780. Set window length to 0.05 ms, with a minimum of 1 sample. In this way, all data
  2781. sampled with 20 kHz or lower, will use a 1 sample window, data with a larger sampling rate
  2782. use a window of 0.05 ms for computing the slope.
  2783. */
  2784. windowLength = lround(0.05 * GetSR()); // use window length of about 0.05 ms.
  2785. if (windowLength < 1) windowLength = 1; // use a minimum window length of 1 sample
  2786. //Begin peak and base calculation
  2787. //-------------------------------
  2788. try {
  2789. base=stfnum::base(baselineMethod,var,cursec().get(),baseBeg,baseEnd);
  2790. baseSD=sqrt(var);
  2791. peak=stfnum::peak(cursec().get(),base,
  2792. peakBeg,peakEnd,pM,direction,maxT);
  2793. }
  2794. catch (const std::out_of_range& e) {
  2795. base=0.0;
  2796. baseSD=0.0;
  2797. peak=0.0;
  2798. throw e;
  2799. }
  2800. try {
  2801. threshold = stfnum::threshold( cursec().get(), peakBeg, peakEnd, slopeForThreshold/GetSR(), thrT, windowLength );
  2802. } catch (const std::out_of_range& e) {
  2803. threshold = 0;
  2804. throw e;
  2805. }
  2806. //Begin Lo to Hi% Rise Time calculation
  2807. //-------------------------------------
  2808. // 2009-06-05: reference is either from baseline or from threshold
  2809. double reference = base;
  2810. if (!fromBase && thrT >= 0) {
  2811. reference = threshold;
  2812. }
  2813. double ampl=peak-reference;
  2814. tLoReal=0.0;
  2815. double factor= RTFactor*0.01; /* normalized value */
  2816. InnerLoRT=NAN;
  2817. InnerHiRT=NAN;
  2818. OuterLoRT=NAN;
  2819. OuterHiRT=NAN;
  2820. try {
  2821. // 2008-04-27: changed limits to start from the beginning of the trace
  2822. // 2013-06-16: changed to accept different rise-time proportions
  2823. rtLoHi=stfnum::risetime2(cursec().get(),reference,ampl, (double)0/*(double)baseEnd*/,
  2824. maxT, factor/*0.2*/, InnerLoRT, InnerHiRT, OuterLoRT, OuterHiRT);
  2825. InnerLoRT/=GetSR();
  2826. InnerHiRT/=GetSR();
  2827. OuterLoRT/=GetSR();
  2828. OuterHiRT/=GetSR();
  2829. }
  2830. catch (const std::out_of_range& e) {
  2831. throw e;
  2832. }
  2833. try {
  2834. // 2008-04-27: changed limits to start from the beginning of the trace
  2835. // 2013-06-16: changed to accept different rise-time proportions
  2836. rtLoHi=stfnum::risetime(cursec().get(),reference,ampl, (double)0/*(double)baseEnd*/,
  2837. maxT, factor/*0.2*/, tLoIndex, tHiIndex, tLoReal);
  2838. }
  2839. catch (const std::out_of_range& e) {
  2840. rtLoHi=0.0;
  2841. throw e;
  2842. }
  2843. tHiReal=tLoReal+rtLoHi;
  2844. rtLoHi/=GetSR();
  2845. //Begin Half Duration calculation
  2846. //-------------------------------
  2847. //t50LeftReal=0.0;
  2848. // 2008-04-27: changed limits to start from the beginning of the trace
  2849. // and to stop at the end of the trace
  2850. halfDuration = stfnum::t_half(cursec().get(), reference, ampl, (double)0 /*(double)baseBeg*/,
  2851. (double)cursec().size()-1 /*(double)peakEnd*/,maxT, t50LeftIndex, t50RightIndex, t50LeftReal);
  2852. t50RightReal=t50LeftReal+halfDuration;
  2853. halfDuration/=GetSR();
  2854. t50Y=0.5*ampl + reference;
  2855. //Calculate the beginning of the event by linear extrapolation:
  2856. if (latencyEndMode==stf::footMode) {
  2857. t0Real=tLoReal-(tHiReal-tLoReal)/3.0; // using 20-80% rise time (f/(1-2f) = 0.2/(1-0.4) = 1/3.0)
  2858. } else {
  2859. t0Real=t50LeftReal;
  2860. }
  2861. //Begin Ratio of slopes rise/decay calculation
  2862. //--------------------------------------------
  2863. double left_rise = peakBeg;
  2864. maxRise=stfnum::maxRise(cursec().get(),left_rise,maxT,maxRiseT,maxRiseY,windowLength);
  2865. double t_half_3=t50RightIndex+2.0*(t50RightIndex-t50LeftIndex);
  2866. double right_decay=peakEnd<=t_half_3 ? peakEnd : t_half_3+1;
  2867. maxDecay=stfnum::maxDecay(cursec().get(),maxT,right_decay,maxDecayT,maxDecayY,windowLength);
  2868. //Slope ratio
  2869. if (maxDecay !=0) slopeRatio=maxRise/maxDecay;
  2870. else slopeRatio=0.0;
  2871. maxRise *= GetSR();
  2872. maxDecay *= GetSR();
  2873. if (size()>1) {
  2874. //Calculate the absolute peak of the (AP) Ch2 inbetween the peak boundaries
  2875. //A direction dependent evaluation of the peak as in Ch1 does NOT exist!!
  2876. // endResting is set to 100 points arbitrarily in the pascal version
  2877. // (see measlib.pas) assuming that the resting potential is stable
  2878. // during the first 100 sampling points.
  2879. // const int endResting=100;
  2880. const int searchRange=100;
  2881. double APVar=0.0;
  2882. try {
  2883. // in 2012-11-02: use baseline cursors and not arbitrarily 100 points
  2884. //APBase=stfnum::base(APVar,secsec().get(),0,endResting);
  2885. APBase=stfnum::base(baselineMethod,APVar,secsec().get(), baseBeg, baseEnd ); // use baseline cursors
  2886. //APPeak=stfnum::peak(secsec().get(),APBase,peakBeg,peakEnd,pM,stfnum::up,APMaxT);
  2887. APPeak=stfnum::peak( secsec().get(),APBase ,peakBeg ,peakEnd ,pM,direction ,APMaxT );
  2888. }
  2889. catch (const std::out_of_range& e) {
  2890. APBase=0.0;
  2891. APPeak=0.0;
  2892. throw e;
  2893. }
  2894. //-------------------------------
  2895. //Maximal slope in the rise before the peak
  2896. //----------------------------
  2897. APMaxRiseT=0.0;
  2898. APMaxRiseY=0.0;
  2899. double left_APRise = peakBeg;
  2900. //if (GetLatencyWindowMode() == stf::defaultMode ) {
  2901. left_APRise= APMaxT-searchRange>2.0 ? APMaxT-searchRange : 2.0;
  2902. try {
  2903. stfnum::maxRise(secsec().get(),left_APRise,APMaxT,APMaxRiseT,APMaxRiseY,windowLength);
  2904. }
  2905. catch (const std::out_of_range&) {
  2906. APMaxRiseT=0.0;
  2907. APMaxRiseY=0.0;
  2908. left_APRise = peakBeg;
  2909. }
  2910. //End determination of the region of maximal slope in the second channel
  2911. //----------------------------
  2912. //-------------------------------
  2913. //Half-maximal amplitude
  2914. //----------------------------
  2915. //APt50LeftReal=0.0;
  2916. //std::size_t APt50LeftIndex,APt50RightIndex;
  2917. stfnum::t_half(secsec().get(), APBase, APPeak-APBase, left_APRise,
  2918. (double)secsec().get().size(), APMaxT, APt50LeftIndex,
  2919. APt50RightIndex, APt50LeftReal);
  2920. //End determination of the region of maximal slope in the second channel
  2921. //----------------------------
  2922. // Get onset in 2nd channel
  2923. APrtLoHi=stfnum::risetime(secsec().get(), APBase, APPeak-APBase, (double)0,
  2924. APMaxT, 0.2, APtLoIndex, APtHiIndex, APtLoReal);
  2925. APtHiReal = APtLoReal + APrtLoHi;
  2926. APt0Real = APtLoReal-(APtHiReal-APtLoReal)/3.0; // using 20-80% rise time (f/(1-2f) = 0.2/(1-0.4) = 1/3.0)
  2927. }
  2928. // get and set start of latency measurement:
  2929. double latStart=0.0;
  2930. switch (latencyStartMode) {
  2931. // Interestingly, latencyCursor is an int in pascal, although
  2932. // the maxTs aren't. That's why there are double type casts
  2933. // here.
  2934. case stf::peakMode: //Latency cursor is set to the peak
  2935. latStart=APMaxT;
  2936. break;
  2937. case stf::riseMode:
  2938. latStart=APMaxRiseT;
  2939. break;
  2940. case stf::halfMode:
  2941. latStart=APt50LeftReal;
  2942. break;
  2943. case stf::manualMode:
  2944. default:
  2945. latStart=GetLatencyBeg();
  2946. break;
  2947. }
  2948. SetLatencyBeg(latStart);
  2949. APt0Real = tLoReal-(tHiReal-tLoReal)/3.0; // using 20-80% rise time (f/(1-2f) = 0.2/(1-0.4) = 1/3.0)
  2950. // get and set end of latency measurement:
  2951. double latEnd=0.0;
  2952. switch (latencyEndMode) {
  2953. // Interestingly, latencyCursor is an int in pascal, although
  2954. // the maxTs aren't. That's why there are double type casts
  2955. // here.
  2956. case stf::footMode:
  2957. latEnd=tLoReal-(tHiReal-tLoReal)/3.0; // using 20-80% rise time (f/(1-2f) = 0.2/(1-0.4) = 1/3.0)
  2958. break;
  2959. case stf::riseMode:
  2960. latEnd=maxRiseT;
  2961. break;
  2962. case stf::halfMode:
  2963. latEnd=t50LeftReal;
  2964. break;
  2965. case stf::peakMode:
  2966. latEnd=maxT;
  2967. break;
  2968. case stf::manualMode:
  2969. default:
  2970. latEnd=GetLatencyEnd();
  2971. break;
  2972. }
  2973. SetLatencyEnd(latEnd);
  2974. SetLatency(GetLatencyEnd()-GetLatencyBeg());
  2975. #ifdef WITH_PSLOPE
  2976. //-------------------------------------
  2977. // Begin PSlope calculation (PSP Slope)
  2978. //-------------------------------------
  2979. //
  2980. int PSlopeBegVal;
  2981. switch (pslopeBegMode) {
  2982. case stf::psBeg_footMode: // Left PSlope to commencement
  2983. PSlopeBegVal = (int)(tLoReal-(tHiReal-tLoReal)/3.0);
  2984. break;
  2985. case stf::psBeg_thrMode: // Left PSlope to threshold
  2986. PSlopeBegVal = (int)thrT;
  2987. break;
  2988. case stf::psBeg_t50Mode: // Left PSlope to the t50
  2989. PSlopeBegVal = (int)t50LeftReal;
  2990. break;
  2991. case stf::psBeg_manualMode: // Left PSlope cursor manual
  2992. default:
  2993. PSlopeBegVal = PSlopeBeg;
  2994. }
  2995. SetPSlopeBeg(PSlopeBegVal);
  2996. int PSlopeEndVal;
  2997. switch (pslopeEndMode) {
  2998. case stf::psEnd_t50Mode: // Right PSlope to t50rigth
  2999. PSlopeEndVal = (int)t50LeftReal;
  3000. break;
  3001. case stf::psEnd_peakMode: // Right PSlope to peak
  3002. PSlopeEndVal = (int)maxT;
  3003. break;
  3004. case stf::psEnd_DeltaTMode: // Right PSlope to DeltaT time from first peak
  3005. PSlopeEndVal = (int)(PSlopeBeg + DeltaT);
  3006. break;
  3007. case stf::psEnd_manualMode:
  3008. default:
  3009. PSlopeEndVal = PSlopeEnd;
  3010. }
  3011. SetPSlopeEnd(PSlopeEndVal);
  3012. try {
  3013. PSlope = (stfnum::pslope(cursec().get(), PSlopeBeg, PSlopeEnd))*GetSR();
  3014. }
  3015. catch (const std::out_of_range& e) {
  3016. PSlope = 0.0;
  3017. throw e;
  3018. }
  3019. //-----------------------------------
  3020. // End PSlope calculation (PSP Slope)
  3021. //-----------------------------------
  3022. #endif // WITH_PSLOPE
  3023. //--------------------------
  3024. } //End of Measure(,,,,,)
  3025. void wxStfDoc::CopyCursors(const wxStfDoc& c_Recording) {
  3026. measCursor=c_Recording.measCursor;
  3027. correctRangeR(measCursor);
  3028. baseBeg=c_Recording.baseBeg;
  3029. correctRangeR(baseBeg);
  3030. baseEnd=c_Recording.baseEnd;
  3031. correctRangeR(baseEnd);
  3032. peakBeg=c_Recording.peakBeg;
  3033. correctRangeR(peakBeg);
  3034. peakEnd=c_Recording.peakEnd;
  3035. correctRangeR(peakEnd);
  3036. fitBeg=c_Recording.fitBeg;
  3037. correctRangeR(fitBeg);
  3038. fitEnd=c_Recording.fitEnd;
  3039. correctRangeR(fitEnd);
  3040. #ifdef WITH_PSLOPE
  3041. PSlopeBeg = c_Recording.PSlopeBeg; // PSlope left cursor
  3042. correctRangeR(PSlopeBeg);
  3043. PSlopeEnd = c_Recording.PSlopeEnd; // PSlope right cursor
  3044. correctRangeR(PSlopeEnd);
  3045. DeltaT=c_Recording.DeltaT; //distance (number of points) from first cursor
  3046. #endif
  3047. pM=c_Recording.pM; //peakMean, number of points used for averaging
  3048. }
  3049. void wxStfDoc::SetLatencyStartMode(int value) {
  3050. switch (value) {
  3051. case 1:
  3052. latencyStartMode=stf::peakMode;
  3053. break;
  3054. case 2:
  3055. latencyStartMode=stf::riseMode;
  3056. break;
  3057. case 3:
  3058. latencyStartMode=stf::halfMode;
  3059. break;
  3060. case 4:
  3061. latencyStartMode=stf::footMode;
  3062. break;
  3063. case 0:
  3064. default:
  3065. latencyStartMode=stf::manualMode;
  3066. }
  3067. }
  3068. void wxStfDoc::SetLatencyEndMode(int value) {
  3069. switch (value) {
  3070. case 1:
  3071. latencyEndMode=stf::peakMode;
  3072. break;
  3073. case 2:
  3074. latencyEndMode=stf::riseMode;
  3075. break;
  3076. case 3:
  3077. latencyEndMode=stf::halfMode;
  3078. break;
  3079. case 4:
  3080. latencyEndMode=stf::footMode;
  3081. break;
  3082. case 0:
  3083. default:
  3084. latencyEndMode=stf::manualMode;
  3085. }
  3086. }
  3087. void wxStfDoc::SetLatencyWindowMode(int value) {
  3088. if ( value == 1 ) {
  3089. latencyWindowMode = stf::windowMode;
  3090. } else {
  3091. latencyWindowMode = stf::defaultMode;
  3092. }
  3093. }
  3094. void wxStfDoc::correctRangeR(int& value) {
  3095. if (value<0) {
  3096. value=0;
  3097. return;
  3098. }
  3099. if (value>=(int)cursec().size()) {
  3100. value=(int)cursec().size()-1;
  3101. return;
  3102. }
  3103. }
  3104. void wxStfDoc::correctRangeR(std::size_t& value) {
  3105. if (value>=cursec().size()) {
  3106. value=cursec().size()-1;
  3107. return;
  3108. }
  3109. }
  3110. void wxStfDoc::SetMeasCursor(int value) {
  3111. correctRangeR(value);
  3112. measCursor=value;
  3113. }
  3114. double wxStfDoc::GetMeasValue() {
  3115. if (measCursor>=curch().size()) {
  3116. correctRangeR(measCursor);
  3117. }
  3118. return cursec().at(measCursor);
  3119. }
  3120. void wxStfDoc::SetBaseBeg(int value) {
  3121. correctRangeR(value);
  3122. baseBeg=value;
  3123. }
  3124. void wxStfDoc::SetBaseEnd(int value) {
  3125. correctRangeR(value);
  3126. baseEnd=value;
  3127. }
  3128. void wxStfDoc::SetPeakBeg(int value) {
  3129. correctRangeR(value);
  3130. peakBeg=value;
  3131. }
  3132. void wxStfDoc::SetPeakEnd(int value) {
  3133. correctRangeR(value);
  3134. peakEnd=value;
  3135. }
  3136. void wxStfDoc::SetFitBeg(int value) {
  3137. correctRangeR(value);
  3138. fitBeg=value;
  3139. }
  3140. void wxStfDoc::SetFitEnd(int value) {
  3141. correctRangeR(value);
  3142. fitEnd=value;
  3143. }
  3144. void wxStfDoc::SetLatencyBeg(double value) {
  3145. if (value<0.0) {
  3146. value=0.0;
  3147. }
  3148. if (value>=(double)cursec().size()) {
  3149. value=cursec().size()-1.0;
  3150. }
  3151. latencyStartCursor=value;
  3152. }
  3153. void wxStfDoc::SetLatencyEnd(double value) {
  3154. if (value<0.0) {
  3155. value=0.0;
  3156. }
  3157. if (value>=(double)cursec().size()) {
  3158. value=cursec().size()-1.0;
  3159. }
  3160. latencyEndCursor=value;
  3161. }
  3162. void wxStfDoc::SetRTFactor(int value) {
  3163. if (value < 0){
  3164. value = 5;
  3165. }
  3166. else if (value > 50) {
  3167. value = 45;
  3168. }
  3169. RTFactor = value;
  3170. }
  3171. #ifdef WITH_PSLOPE
  3172. void wxStfDoc::SetPSlopeBeg(int value) {
  3173. correctRangeR(value);
  3174. PSlopeBeg = value;
  3175. }
  3176. void wxStfDoc::SetPSlopeEnd(int value) {
  3177. correctRangeR(value);
  3178. PSlopeEnd = value;
  3179. }
  3180. #endif
  3181. stfnum::Table wxStfDoc::CurAsTable() const {
  3182. stfnum::Table table(cursec().size(),size());
  3183. try {
  3184. for (std::size_t nRow=0;nRow<table.nRows();++nRow) {
  3185. std::ostringstream rLabel;
  3186. rLabel << nRow*GetXScale();
  3187. table.SetRowLabel(nRow,rLabel.str());
  3188. for (std::size_t nCol=0;nCol<table.nCols();++nCol) {
  3189. table.at(nRow,nCol)=get().at(nCol).at(GetCurSecIndex()).at(nRow);
  3190. }
  3191. }
  3192. for (std::size_t nCol=0;nCol<table.nCols();++nCol) {
  3193. table.SetColLabel(nCol, get().at(nCol).GetChannelName());
  3194. }
  3195. }
  3196. catch (const std::out_of_range& e) {
  3197. throw e;
  3198. }
  3199. return table;
  3200. }
  3201. stfnum::Table wxStfDoc::CurResultsTable() {
  3202. struct ResultColumnSpec {
  3203. bool enabled;
  3204. std::string label;
  3205. std::function<double()> value;
  3206. std::function<void(stfnum::Table&, int)> fillCursorRows;
  3207. };
  3208. const bool useMedian = GetBaselineMethod() != stfnum::mean_sd;
  3209. const bool withCursors = viewCursors;
  3210. std::vector<ResultColumnSpec> specs;
  3211. specs.reserve(16);
  3212. auto addSpec = [&](bool enabled,
  3213. const std::string& label,
  3214. const std::function<double()>& value,
  3215. const std::function<void(stfnum::Table&, int)>& fillCursorRows) {
  3216. specs.push_back(ResultColumnSpec{enabled, label, value, fillCursorRows});
  3217. };
  3218. addSpec(viewCrosshair, "Crosshair",
  3219. [this]() { return GetMeasValue(); },
  3220. [this, withCursors](stfnum::Table& table, int nCol) {
  3221. if (!withCursors) {
  3222. return;
  3223. }
  3224. table.at(1, nCol) = GetMeasCursor() * GetXScale();
  3225. table.SetEmpty(2, nCol, true);
  3226. });
  3227. addSpec(viewBaseline, std::string("Baseline ") + (useMedian ? "Median" : "Mean"),
  3228. [this]() { return GetBase(); },
  3229. [this, withCursors](stfnum::Table& table, int nCol) {
  3230. if (!withCursors) {
  3231. return;
  3232. }
  3233. table.at(1, nCol) = GetBaseBeg() * GetXScale();
  3234. table.at(2, nCol) = GetBaseEnd() * GetXScale();
  3235. });
  3236. addSpec(viewBaseSD, std::string("Base ") + (useMedian ? "IQR" : "SD"),
  3237. [this]() { return GetBaseSD(); },
  3238. [this, withCursors](stfnum::Table& table, int nCol) {
  3239. if (!withCursors) {
  3240. return;
  3241. }
  3242. table.at(1, nCol) = GetBaseBeg() * GetXScale();
  3243. table.at(2, nCol) = GetBaseEnd() * GetXScale();
  3244. });
  3245. addSpec(viewThreshold, "Threshold",
  3246. [this]() { return GetThreshold(); },
  3247. [this, withCursors](stfnum::Table& table, int nCol) {
  3248. if (!withCursors) {
  3249. return;
  3250. }
  3251. table.at(1, nCol) = GetPeakBeg() * GetXScale();
  3252. table.at(2, nCol) = GetPeakEnd() * GetXScale();
  3253. });
  3254. addSpec(viewPeakzero, "Peak (from 0)",
  3255. [this]() { return GetPeak(); },
  3256. [this, withCursors](stfnum::Table& table, int nCol) {
  3257. if (!withCursors) {
  3258. return;
  3259. }
  3260. table.at(1, nCol) = GetPeakBeg() * GetXScale();
  3261. table.at(2, nCol) = GetPeakEnd() * GetXScale();
  3262. });
  3263. addSpec(viewPeakbase, "Peak (from base)",
  3264. [this]() { return GetPeak() - GetBase(); },
  3265. [this, withCursors](stfnum::Table& table, int nCol) {
  3266. if (!withCursors) {
  3267. return;
  3268. }
  3269. table.at(1, nCol) = GetPeakBeg() * GetXScale();
  3270. table.at(2, nCol) = GetPeakEnd() * GetXScale();
  3271. });
  3272. addSpec(viewPeakthreshold, "Peak (from threshold)",
  3273. [this]() { return (thrT >= 0) ? (GetPeak() - GetThreshold()) : 0.0; },
  3274. [this, withCursors](stfnum::Table& table, int nCol) {
  3275. if (!withCursors) {
  3276. return;
  3277. }
  3278. table.at(1, nCol) = GetPeakBeg() * GetXScale();
  3279. table.at(2, nCol) = GetPeakEnd() * GetXScale();
  3280. });
  3281. addSpec(viewRTLoHi, "RT (Lo-Hi%)",
  3282. [this]() { return GetRTLoHi(); },
  3283. [this, withCursors](stfnum::Table& table, int nCol) {
  3284. if (!withCursors) {
  3285. return;
  3286. }
  3287. table.at(1, nCol) = GetTLoReal() * GetXScale();
  3288. table.at(2, nCol) = GetTHiReal() * GetXScale();
  3289. });
  3290. addSpec(viewInnerRiseTime, "inner rise time",
  3291. [this]() { return GetInnerRiseTime(); },
  3292. [this, withCursors](stfnum::Table& table, int nCol) {
  3293. if (!withCursors) {
  3294. return;
  3295. }
  3296. table.at(1, nCol) = GetInnerLoRT();
  3297. table.at(2, nCol) = GetInnerHiRT();
  3298. });
  3299. addSpec(viewOuterRiseTime, "outer rise time",
  3300. [this]() { return GetOuterRiseTime(); },
  3301. [this, withCursors](stfnum::Table& table, int nCol) {
  3302. if (!withCursors) {
  3303. return;
  3304. }
  3305. table.at(1, nCol) = GetOuterLoRT();
  3306. table.at(2, nCol) = GetOuterHiRT();
  3307. });
  3308. addSpec(viewT50, "t50",
  3309. [this]() { return GetHalfDuration(); },
  3310. [this, withCursors](stfnum::Table& table, int nCol) {
  3311. if (!withCursors) {
  3312. return;
  3313. }
  3314. table.at(1, nCol) = GetT50LeftReal() * GetXScale();
  3315. table.at(2, nCol) = GetT50RightReal() * GetXScale();
  3316. });
  3317. addSpec(viewRD, "Rise/Decay",
  3318. [this]() { return GetSlopeRatio(); },
  3319. [this, withCursors](stfnum::Table& table, int nCol) {
  3320. if (!withCursors) {
  3321. return;
  3322. }
  3323. table.at(1, nCol) = GetMaxRiseT() * GetXScale();
  3324. table.at(2, nCol) = GetMaxDecayT() * GetXScale();
  3325. });
  3326. addSpec(viewSloperise, "Max slope (rise)",
  3327. [this]() { return GetMaxRise(); },
  3328. [this, withCursors](stfnum::Table& table, int nCol) {
  3329. if (!withCursors) {
  3330. return;
  3331. }
  3332. table.at(1, nCol) = GetMaxRiseT() * GetXScale();
  3333. table.SetEmpty(2, nCol, true);
  3334. });
  3335. addSpec(viewSlopedecay, "Max slope (decay)",
  3336. [this]() { return GetMaxDecay(); },
  3337. [this, withCursors](stfnum::Table& table, int nCol) {
  3338. if (!withCursors) {
  3339. return;
  3340. }
  3341. table.at(1, nCol) = GetMaxDecayT() * GetXScale();
  3342. table.SetEmpty(2, nCol, true);
  3343. });
  3344. addSpec(viewLatency, "Latency",
  3345. [this]() { return GetLatency() * GetXScale(); },
  3346. [this, withCursors](stfnum::Table& table, int nCol) {
  3347. if (!withCursors) {
  3348. return;
  3349. }
  3350. table.at(1, nCol) = GetLatencyBeg() * GetXScale();
  3351. table.at(2, nCol) = GetLatencyEnd() * GetXScale();
  3352. });
  3353. #ifdef WITH_PSLOPE
  3354. addSpec(viewPSlope, "PSlope",
  3355. [this]() { return GetPSlope(); },
  3356. [this, withCursors](stfnum::Table& table, int nCol) {
  3357. if (!withCursors) {
  3358. return;
  3359. }
  3360. table.at(1, nCol) = GetPSlopeBeg() * GetXScale();
  3361. table.at(2, nCol) = GetPSlopeEnd() * GetXScale();
  3362. });
  3363. #endif // WITH_PSLOPE
  3364. std::size_t nCols = 0;
  3365. for (std::size_t i = 0; i < specs.size(); ++i) {
  3366. if (specs[i].enabled) {
  3367. ++nCols;
  3368. }
  3369. }
  3370. const std::size_t nRows = withCursors ? 3 : 1;
  3371. stfnum::Table table(nRows, nCols);
  3372. table.SetRowLabel(0, "Value");
  3373. if (withCursors) {
  3374. table.SetRowLabel(1, "Cursor 1");
  3375. table.SetRowLabel(2, "Cursor 2");
  3376. }
  3377. int nCol = 0;
  3378. for (std::size_t i = 0; i < specs.size(); ++i) {
  3379. if (!specs[i].enabled) {
  3380. continue;
  3381. }
  3382. table.SetColLabel(nCol, specs[i].label);
  3383. table.at(0, nCol) = specs[i].value();
  3384. specs[i].fillCursorRows(table, nCol);
  3385. ++nCol;
  3386. }
  3387. return table;
  3388. }
  3389. void wxStfDoc::resize(std::size_t c_n_channels) {
  3390. Recording::resize(c_n_channels);
  3391. yzoom.resize(size());
  3392. sec_attr.resize(size());
  3393. for (std::size_t nchannel = 0; nchannel < size(); ++nchannel) {
  3394. sec_attr[nchannel].resize(at(nchannel).size());
  3395. }
  3396. }
  3397. void wxStfDoc::InsertChannel(Channel& c_Channel, std::size_t pos) {
  3398. Recording::InsertChannel(c_Channel, pos);
  3399. yzoom.resize(size());
  3400. sec_attr.resize(size());
  3401. for (std::size_t nchannel = 0; nchannel < size(); ++nchannel) {
  3402. sec_attr[nchannel].resize(at(nchannel).size());
  3403. }
  3404. }
  3405. void wxStfDoc::SetIsFitted( std::size_t nchannel, std::size_t nsection,
  3406. const Vector_double& bestFitP_, stfnum::storedFunc* fitFunc_,
  3407. double chisqr, std::size_t fitBeg_, std::size_t fitEnd_ )
  3408. {
  3409. if (nchannel >= sec_attr.size() || nsection >= sec_attr[nchannel].size()) {
  3410. throw std::out_of_range("Index out of range in wxStfDoc::SetIsFitted");
  3411. }
  3412. if ( !fitFunc_ ) {
  3413. throw std::runtime_error("Function pointer is zero in wxStfDoc::SetIsFitted");
  3414. }
  3415. if ( fitFunc_->pInfo.size() != bestFitP_.size() ) {
  3416. throw std::runtime_error("Number of best-fit parameters doesn't match number\n \
  3417. of function parameters in wxStfDoc::SetIsFitted");
  3418. }
  3419. sec_attr[nchannel][nsection].fitFunc = fitFunc_;
  3420. if ( sec_attr[nchannel][nsection].bestFitP.size() != bestFitP_.size() )
  3421. sec_attr[nchannel][nsection].bestFitP.resize(bestFitP_.size());
  3422. sec_attr[nchannel][nsection].bestFitP = bestFitP_;
  3423. sec_attr[nchannel][nsection].bestFit =
  3424. sec_attr[nchannel][nsection].fitFunc->output(sec_attr[nchannel][nsection].bestFitP,
  3425. sec_attr[nchannel][nsection].fitFunc->pInfo, chisqr );
  3426. sec_attr[nchannel][nsection].storeFitBeg = fitBeg_;
  3427. sec_attr[nchannel][nsection].storeFitEnd = fitEnd_;
  3428. sec_attr[nchannel][nsection].isFitted = true;
  3429. }
  3430. void wxStfDoc::DeleteFit(std::size_t nchannel, std::size_t nsection) {
  3431. if (nchannel >= sec_attr.size() || nsection >= sec_attr[nchannel].size()) {
  3432. throw std::out_of_range("Index out of range in wxStfDoc::DeleteFit");
  3433. }
  3434. sec_attr[nchannel][nsection].fitFunc = NULL;
  3435. sec_attr[nchannel][nsection].bestFitP.resize( 0 );
  3436. sec_attr[nchannel][nsection].bestFit = stfnum::Table( 0, 0 );
  3437. sec_attr[nchannel][nsection].isFitted = false;
  3438. }
  3439. void wxStfDoc::SetIsIntegrated(std::size_t nchannel, std::size_t nsection, bool value,
  3440. std::size_t begin, std::size_t end, const Vector_double& quad_p_)
  3441. {
  3442. if (nchannel >= sec_attr.size() || nsection >= sec_attr[nchannel].size()) {
  3443. throw std::out_of_range("Index out of range in wxStfDoc::SetIsIntegrated");
  3444. }
  3445. if (value==false) {
  3446. sec_attr[nchannel][nsection].isIntegrated=value;
  3447. return;
  3448. }
  3449. if (end<=begin) {
  3450. throw std::out_of_range("integration limits out of range in Section::SetIsIntegrated");
  3451. }
  3452. int n_intervals=std::div((int)end-(int)begin,2).quot;
  3453. if ((int)quad_p_.size() != n_intervals*3) {
  3454. throw std::out_of_range("Wrong number of parameters for quadratic equations in Section::SetIsIntegrated");
  3455. }
  3456. sec_attr[nchannel][nsection].quad_p = quad_p_;
  3457. sec_attr[nchannel][nsection].isIntegrated=value;
  3458. sec_attr[nchannel][nsection].storeIntBeg=begin;
  3459. sec_attr[nchannel][nsection].storeIntEnd=end;
  3460. }
  3461. void wxStfDoc::ClearEvents(std::size_t nchannel, std::size_t nsection) {
  3462. wxStfView* pView=(wxStfView*)GetFirstView();
  3463. if (pView != nullptr) {
  3464. wxStfGraph* pGraph = pView->GetGraph();
  3465. if (pGraph != nullptr) {
  3466. pGraph->ClearEvents();
  3467. }
  3468. }
  3469. try {
  3470. sec_attr.at(nchannel).at(nsection).eventList.clear();
  3471. }
  3472. catch(const std::out_of_range& e) {
  3473. throw e;
  3474. }
  3475. }
  3476. const stf::SectionAttributes& wxStfDoc::GetSectionAttributes(std::size_t nchannel, std::size_t nsection) const {
  3477. try {
  3478. return sec_attr.at(nchannel).at(nsection);
  3479. }
  3480. catch(const std::out_of_range& e) {
  3481. throw e;
  3482. }
  3483. }
  3484. const stf::SectionAttributes& wxStfDoc::GetCurrentSectionAttributes() const {
  3485. try {
  3486. return sec_attr.at(GetCurChIndex()).at(GetCurSecIndex());
  3487. }
  3488. catch(const std::out_of_range& e) {
  3489. throw e;
  3490. }
  3491. }
  3492. stf::SectionAttributes& wxStfDoc::GetCurrentSectionAttributesW() {
  3493. try {
  3494. return sec_attr.at(GetCurChIndex()).at(GetCurSecIndex());
  3495. }
  3496. catch(const std::out_of_range& e) {
  3497. throw e;
  3498. }
  3499. }
  3500. double wxStfDoc::CalcAreaUnderCurve(std::vector<double> d, std::vector<int> c)
  3501. {
  3502. std::vector<std::size_t> index(d.size());
  3503. std::iota(index.begin(), index.end(), 0);
  3504. std::sort(index.begin(), index.end(), [&](std::size_t a, std::size_t b) { return d[a] < d[b]; });
  3505. std::vector<int> x(c.size());
  3506. for(std::size_t i = 0; i < x.size(); i++)
  3507. {
  3508. x[i] = c[index[i]];
  3509. }
  3510. std::vector<double> FN(x.size()), TN(x.size());
  3511. FN[0] = x[0];
  3512. for(std::size_t i = 1; i < x.size(); i++)
  3513. {
  3514. FN[i] = FN[i - 1] + x[i];
  3515. }
  3516. for(std::size_t i = 0; i < TN.size(); i++)
  3517. {
  3518. TN[i] = i + 1 - FN[i];
  3519. }
  3520. for(std::size_t i = 0; i < TN.size(); i++)
  3521. {
  3522. FN[i] /= FN[FN.size() - 1];
  3523. TN[i] /= TN[TN.size() - 1];
  3524. }
  3525. std::vector<double> diff(FN.size() - 1);
  3526. for(std::size_t i = 0; i < diff.size(); i++)
  3527. {
  3528. diff[i] = FN[i + 1] - FN[i];
  3529. }
  3530. double area = 0.0;
  3531. for(std::size_t i = 0; i < diff.size(); i++)
  3532. {
  3533. area += diff[i] * (TN[i] + TN[i + 1]) / 2;
  3534. }
  3535. return area;
  3536. }
  3537. std::vector<double> wxStfDoc::SortScoringTraceByRawDetection( std::vector<double> &rawDetectionTrace,
  3538. std::vector<int> &scoringTrace)
  3539. {
  3540. if (scoringTrace.size() != rawDetectionTrace.size()) throw std::runtime_error("scoring trace hasn't same size as raw detection trace in wxStfDoc::SortScoringTraceByRawDetection");
  3541. std::vector<std::size_t> idx(scoringTrace.size());
  3542. std::iota(idx.begin(), idx.end(), 0);
  3543. std::sort(idx.begin(), idx.end(), [&](std::size_t a, std::size_t b) { return rawDetectionTrace[a] < rawDetectionTrace[b]; });
  3544. std::vector<double> sortedRawDetectionTrace(rawDetectionTrace.size());
  3545. std::vector<int> sortedScoringTrace(scoringTrace.size());
  3546. for(std::size_t i = 0; i < idx.size(); i++)
  3547. {
  3548. sortedRawDetectionTrace[i] = rawDetectionTrace[idx[i]];
  3549. sortedScoringTrace[i] = scoringTrace[idx[i]];
  3550. }
  3551. scoringTrace = std::move(sortedScoringTrace);
  3552. return sortedRawDetectionTrace;
  3553. }
  3554. std::vector<stf::Event> wxStfDoc::DetectEvents(double threshold_, std::vector<double> &rawDetectionTrace)
  3555. {
  3556. std::size_t eventRange = GetSR() * 1000 * 0.001;
  3557. wxStfView* pView = (wxStfView*)GetFirstView();
  3558. wxStfGraph* pGraph = pView->GetGraph();
  3559. int prevCross = 0;
  3560. std::size_t eventMaxPeakIndex = 0;
  3561. bool hasPeak = false;
  3562. std::vector<stf::Event> detectedEvents;
  3563. for (std::size_t k = 0; k < rawDetectionTrace.size() - 1; k++) {
  3564. // detect threshold crossing (upward)
  3565. if (rawDetectionTrace[k] < threshold_ && rawDetectionTrace[k + 1] >= threshold_) {
  3566. if (prevCross > 0 && (k + 1 - prevCross) > eventRange && hasPeak) {
  3567. // register completed event
  3568. stf::Event evt(eventMaxPeakIndex, eventMaxPeakIndex, 0, new wxCheckBox(pGraph, -1, wxEmptyString));
  3569. detectedEvents.push_back(evt);
  3570. // insert into sec_attr eventList at correct position
  3571. auto &eventList = sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList;
  3572. eventList.push_back(evt);
  3573. // reset for next event
  3574. eventMaxPeakIndex = k + 1;
  3575. hasPeak = false;
  3576. }
  3577. prevCross = k + 1;
  3578. }
  3579. // update peak
  3580. if (!hasPeak || cursec()[k + 1] > cursec()[eventMaxPeakIndex]) {
  3581. eventMaxPeakIndex = k + 1;
  3582. hasPeak = true;
  3583. }
  3584. }
  3585. // final event (only if valid)
  3586. if (prevCross > 0 && hasPeak) {
  3587. stf::Event evt(eventMaxPeakIndex, eventMaxPeakIndex, 0, new wxCheckBox(pGraph, -1, wxEmptyString));
  3588. detectedEvents.push_back(evt);
  3589. auto &eventList = sec_attr.at(GetCurChIndex()).at(GetCurSecIndex()).eventList;
  3590. eventList.push_back(evt);
  3591. }
  3592. return detectedEvents;
  3593. }
  3594. std::vector<int> wxStfDoc::CalcScoringTrace(std::vector<Annotation> expertAnnotations,
  3595. std::size_t startPoint,
  3596. std::size_t endPoint)
  3597. {
  3598. std::vector<int> scoringTrace(endPoint - startPoint + 1, 0);
  3599. for(std::size_t i = 0; i < expertAnnotations.size(); i++){
  3600. scoringTrace[expertAnnotations.at(i).GetAnnotationPosition() - startPoint] = 1;
  3601. }
  3602. return scoringTrace;
  3603. }
  3604. std::vector<double> wxStfDoc::CalcRawDetectionTrace(std::size_t filterOrder, std::size_t startPoint, std::size_t endPoint)
  3605. {
  3606. std::vector<double> rawDetectionTrace(endPoint - startPoint + 1, 0);
  3607. for(std::size_t t = 0; t < rawDetectionTrace.size(); t++){
  3608. if (t - 1 < 0 || (t + startPoint) - filterOrder < 0 || (t + startPoint) - (2 * filterOrder) < 0) continue;
  3609. rawDetectionTrace[t] = rawDetectionTrace[t - 1] + cursec()[t + startPoint] - (2 * cursec()[(t + startPoint) - filterOrder]) + cursec()[(t + startPoint) - (2 * filterOrder)];
  3610. }
  3611. return rawDetectionTrace;
  3612. }
  3613. std::pair<std::size_t, double> wxStfDoc::CalcMaxKappa(std::vector<double> &sortedRawDetectionTrace, std::vector<int> &sortedScoringTrace)
  3614. {
  3615. std::size_t scoringTraceSize = sortedScoringTrace.size();
  3616. std::vector<double> FN(scoringTraceSize + 1), TP(scoringTraceSize + 1), TN(scoringTraceSize + 1), FP(scoringTraceSize + 1);
  3617. FN[0] = 0;
  3618. TN[0] = 0;
  3619. for(std::size_t i = 1; i < FN.size(); i++)
  3620. {
  3621. FN[i] = FN[i - 1] + sortedScoringTrace[i - 1];
  3622. TN[i] = i - FN[i];
  3623. }
  3624. for(std::size_t i = 0; i < TP.size(); i ++)
  3625. {
  3626. TP[i] = FN[FN.size() - 1] - FN[i];
  3627. FP[i] = TN[TN.size() - 1] - TN[i];
  3628. }
  3629. std::vector<double> ACC(TP.size());
  3630. for(std::size_t i = 0; i < ACC.size(); i++)
  3631. {
  3632. ACC[i] = (TP[i] + TN[i]) / (TP[i] + TN[i] + FP[i] + FN[i]);
  3633. }
  3634. //Compute Cohen's Kappa coefficient
  3635. double n = sortedRawDetectionTrace.size();
  3636. // H = TN FN
  3637. // FP TP
  3638. std::vector<double> p_i(2 * TP.size()), pi_(2 * TP.size());
  3639. for(std::size_t i = 0; i < TP.size(); i++)
  3640. {
  3641. p_i[i] = TP[i] + FP[i];
  3642. p_i[i + TP.size()] = FN[i] + TN[i];
  3643. pi_[i] = TP[i] + FN[i];
  3644. pi_[i + TP.size()] = FP[i] + TN[i];
  3645. }
  3646. //pe define and compute
  3647. std::vector<double> pe(TP.size(), 0.0);
  3648. for(std::size_t i = 0; i < pe.size(); i++)
  3649. {
  3650. pe[i] = ((p_i[i] * pi_[i]) + (p_i[i + TP.size()] * pi_[i + TP.size()])) / (n * n);
  3651. }
  3652. std::vector<double> kap(ACC.size());
  3653. double maxKappa = -1.0;
  3654. std::size_t maxKappaIndex = 0;
  3655. for(std::size_t i = 0 ; i < kap.size(); i ++)
  3656. {
  3657. kap[i] = (ACC[i] - pe[i]) / (1 - pe[i]);
  3658. if (kap[i] > maxKappa) {
  3659. maxKappa = kap[i];
  3660. maxKappaIndex = i;
  3661. }
  3662. }
  3663. return std::pair<std::size_t, double>(maxKappaIndex, maxKappa);
  3664. }
  3665. #if 0
  3666. void wxStfDoc::Userdef(std::size_t id) {
  3667. wxBusyCursor wc;
  3668. int fselect=(int)id;
  3669. Recording newR;
  3670. Vector_double init(0);
  3671. // get user input if necessary:
  3672. if (!wxGetApp().GetPluginLib().at(fselect).input.labels.empty()) {
  3673. wxStfUsrDlg myDlg( GetDocumentWindow(),
  3674. wxGetApp().GetPluginLib().at(fselect).input );
  3675. if (myDlg.ShowModal()!=wxID_OK) {
  3676. return;
  3677. }
  3678. init=myDlg.readInput();
  3679. }
  3680. // Apply function to current valarray
  3681. std::map< wxString, double > resultsMap;
  3682. try {
  3683. newR=wxGetApp().GetPluginLib().at(fselect).pluginFunc( *this, init, resultsMap );
  3684. }
  3685. catch (const std::out_of_range& e) {
  3686. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  3687. return;
  3688. }
  3689. catch (const std::runtime_error& e) {
  3690. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  3691. return;
  3692. }
  3693. catch (const std::exception& e) {
  3694. wxGetApp().ExceptMsg(wxString( e.what(), wxConvLocal ));
  3695. return;
  3696. }
  3697. if (newR.size()==0) {
  3698. return;
  3699. }
  3700. wxString newTitle(GetTitle());
  3701. newTitle += wxT(", ");
  3702. newTitle += wxGetApp().GetPluginLib().at(fselect).menuEntry;
  3703. wxStfDoc* pDoc = wxGetApp().NewChild(newR,this,newTitle);
  3704. ((wxStfChildFrame*)pDoc->GetDocumentWindow())->ShowTable(
  3705. stfnum::Table(resultsMap), wxGetApp().GetPluginLib().at(fselect).menuEntry
  3706. );
  3707. }
  3708. #endif

doc.cpp at commit 17f6103, under GPL-2.0 · at the source

Overview

Authors: Alix Guinet1,2,3, Joachim Behr3,4,5, Imre Vida1, Sabine Grosser1
ORCID iDs: Imre Vida
  1. Institute for Integrative Neuroanatomy Charité—Universitätsmedizin Berlin Berlin Germany
  2. Institute for Biology Humboldt ‐ Universität zu Berlin Berlin Germany
  3. Department of Psychiatry, Psychotherapy and Psychosomatic Medicine Brandenburg Medical School Neuruppin Germany
  4. Department of Psychiatry and Psychotherapy Charité—Universitätsmedizin Berlin Berlin Germany
  5. Faculty of Health Science Brandenburg, Joint Faculty of the University of Potsdam Brandenburg University of Technology Cottbus‐Senftenberg and Brandenburg Medical School Theodor Fontane Potsdam Germany
Journal: Hippocampus, volume 36, issue 2, article e70081
Dates: received 8 July 2025; accepted 3 February 2026; published online 16 February 2026; in print March 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1002/hipo.70081 · PMID 41693678 · PMCID PMC12908100 · OpenAlex W7129014717
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: intracellular / patch clamp (modality), rat (organism), cellular / molecular (subfield)
Methods: Spectral & time-frequency, Statistics, Smoothing, state filtering, decompositions, Machine learning, Single-unit activity, calcium imaging
Keywords: discharge, hippocampus, intrinsic physiology, morphology, PCA, pyramidal neurons, subiculum
MeSH: Hippocampus*, Pyramidal Cells*, Action Potentials, Animals, Dendrites, Male, Patch-Clamp Techniques, Principal Component Analysis, Rats, Rats, Sprague-Dawley (* major topic)
Topic: Memory and Neural Mechanisms (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: Deutsche Forschungsgemeinschaft (DFG) (CRC‐TRR 384/1 TP B07, 514483642)
Citations: cited by 1 paper (Europe PMC); 39 references in the paper

Abstract

The subiculum is a main output structure of the hippocampus, transmitting information from the CA1 to the entorhinal cortex in a spatially structured manner. Prior studies revealed a high electrophysiological and molecular heterogeneity of subicular pyramidal neurons (PYNs) with evidence for further spatial subdivisions of the subiculum. In this study we focused on the cellular organization of the proximal to mid‐distal part of the subiculum, designated as subregion 2 (Sub2) and performed a comprehensive electrophysiological and morphological characterization of PYNs by whole‐cell patch‐clamp recordings combined with intracellular labeling in acute rat hippocampal brain slices. Principal component analysis based on discharge pattern‐related parameters and subsequent unsupervised, hierarchical clustering classified the PYNs into three subtypes: regular firing (RF), weak‐bursting (WB), and strong‐bursting (SB) neurons. Electrophysiological analysis revealed further differences between RF neurons and the two subtypes of bursting neurons in their active and passive properties. The three subtypes also showed differences in their morphometric features, including the apical and basal dendritic spread and branching pattern. Additionally, we identified a divergent morphological subset among RF neurons, bearing two apical dendrites. Mapping the three PYN subtypes onto the subiculum revealed specific spatial distributions along the superficial‐deep and proximo‐distal axes. Thus, this work maps the heterogeneity of subicular PYN onto differentially distributed subclasses in the Sub2 region with distinct physiological and morphological features. These findings together with prior observations of divergent anatomical projections from subicular subregions are pivotal for the understanding of how subicular morpho‐electric neuron types relate to each other and contribute to processing and distribution of information to cortical regions from this hippocampal output region.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 2 matches between paragraphs and lines of code.

neurodroid/stimfit

License: GPL-2.0
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: 17f61032870f2cf3054686033d1857e8f79a1070, 17 August 2026
Languages: C/C++ (98), C++ (77), Python (27), Shell (19), C (13), R (1)
Size: 643 files, 235 scripts
Software Heritage: archived
Found in: the text, “Electrophysiology”
Holds: README, license file, environment (pyproject.toml, doc/sphinx/requirements.txt, src/stimfit/py/emf/setup.py), tests, continuous integration, documentation
Not found: CITATION.cff
Tools: NumPy (14 files), Matplotlib (3 files), SciPy (3 files), NEURON (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
237 files

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 235 scripts, each with its path and the digest of its content;
  • 2 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data Availability Statement

The data that support the findings of this study are available from the corresponding author upon reasonable request.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 30 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 4 authors, 7 keywords, 10 MeSH terms, 1 funder, 39 references.

Cite

This paper

Guinet, A., Behr, J., Vida, I., & Grosser, S. (2026). Spatial Organization of Morpho-Electric Subtypes of Pyramidal Neuron in the Subiculum. Hippocampus, 36(2), e70081. https://doi.org/10.1002/hipo.70081

BibTeX

@article{guinet2026spatial,
author = {Guinet, Alix and Behr, Joachim and Vida, Imre and Grosser, Sabine},
title = {{Spatial Organization of Morpho-Electric Subtypes of Pyramidal Neuron in the Subiculum}},
journal = {Hippocampus},
year = {2026},
month = mar,
volume = {36},
number = {2},
pages = {e70081},
publisher = {Wiley},
issn = {1050-9631},
doi = {10.1002/hipo.70081},
url = {https://doi.org/10.1002/hipo.70081},
pmid = {41693678},
pmcid = {PMC12908100}
}

RIS

TY - JOUR
AU - Guinet, Alix
AU - Behr, Joachim
AU - Vida, Imre
AU - Grosser, Sabine
TI - Spatial Organization of Morpho-Electric Subtypes of Pyramidal Neuron in the Subiculum
T2 - Hippocampus
J2 - Hippocampus
PY - 2026
DA - 2026/03/01
VL - 36
IS - 2
SP - e70081
SN - 1050-9631
PB - Wiley
DO - 10.1002/hipo.70081
UR - https://doi.org/10.1002/hipo.70081
LA - en
ER -

CSL-JSON

{
"id": "10.1002/hipo.70081",
"type": "article-journal",
"title": "Spatial Organization of Morpho-Electric Subtypes of Pyramidal Neuron in the Subiculum",
"container-title": "Hippocampus",
"author": [
{
"family": "Guinet",
"given": "Alix"
},
{
"family": "Behr",
"given": "Joachim"
},
{
"family": "Vida",
"given": "Imre"
},
{
"family": "Grosser",
"given": "Sabine"
}
],
"container-title-short": "Hippocampus",
"volume": "36",
"issue": "2",
"page": "e70081",
"DOI": "10.1002/hipo.70081",
"PMID": "41693678",
"PMCID": "PMC12908100",
"ISSN": "1050-9631",
"publisher": "Wiley",
"URL": "https://doi.org/10.1002/hipo.70081",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
1
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1016/j.celrep.2026.117794 [code]
Subicular plateaus signal reward locations during goal-directed behavior.
Journal: Cell reports
In common: intracellular / patch clamp, 11 references
[2] doi:10.1016/j.celrep.2026.117793 [code]
Clustered inputs engage dendritic nonlinearities and calcium signaling to support efficient place-field formation in CA1 pyramidal neurons.
Journal: Cell reports
In common: NEURON, SciPy, Matplotlib, 1 other tool, cellular / molecular, 1 reference
[3] doi: [code]
Going deeper with morphologically detailed neural networks by simulation-based gradient propagation
Journal: Frontiers in computational neuroscience
In common: NEURON, SciPy, Matplotlib, 1 other tool, 1 reference
[4] doi:10.1126/sciadv.aec3961 [code]
Linking functional and structural dendritic spine remodeling during fear learning and extinction in vivo.
Journal: Science advances
In common: NEURON, SciPy, Matplotlib, 1 other tool, 1 reference
[5] doi:10.1063/5.0308450 [code]
Computational modeling of human vagus nerve stimulation with three-dimensional fascicular morphology.
Journal: APL bioengineering
In common: NEURON, SciPy, Matplotlib, 1 other tool, 1 reference
[6] doi:10.1073/pnas.2533168123 [code]
Dendritic morphology and synaptic nonlinearities enhance functional complexity in human cortical neurons.
Journal: Proceedings of the National Academy of Sciences of the United States of America
In common: NEURON, SciPy, Matplotlib, 1 other tool, rat, cellular / molecular
[7] doi:10.1016/j.celrep.2026.117388 [code]
An unsuspected physiological role for mGluRIII glutamate receptors in hippocampal area CA1.
Journal: Cell reports
In common: NEURON, Matplotlib, NumPy, intracellular / patch clamp, cellular / molecular
[8] doi:10.1016/j.ebiom.2026.106418 [code]
Corticostriatal glutamate mechanisms underlying beta synchrony and motor deficits via striatal NMDA receptors in Parkinson's disease.
Journal: EBioMedicine
In common: NEURON, SciPy, Matplotlib, 1 other tool, rat
[9] doi:10.7554/elife.89629 [code]
Active dendrites enable robust spiking computations despite timing jitter.
Journal: eLife
In common: NEURON, Matplotlib, NumPy, 1 reference
[10] doi:10.7554/elife.108352 [code]
Analysis of dendritic input currents during place field dynamics.
Journal: eLife
In common: NEURON, Matplotlib, NumPy, 1 reference

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.