Browse Source

Akvo changes for kernel calculation (#1)

* Work towards loop info

* improvements, table is getting smarter about loop transmission

* better loop logic

* currently broken, but refactoring to enable processing of legacy data

* Binary file read is restored, working on ASCII

* Legacy FID's can be loaded

* Added option for python phasing rather than R

* Logging improvements with saved files

* A few tweaks

* reduced search for nls
add-license-1
Trevor Irons 7 years ago
parent
commit
566074546b
No account linked to committer's email address
6 changed files with 909 additions and 508 deletions
  1. 141
    54
      akvo/gui/akvoGUI.py
  2. 183
    135
      akvo/gui/main.ui
  3. 340
    170
      akvo/tressel/decay.py
  4. 211
    117
      akvo/tressel/mrsurvey.py
  5. 33
    32
      akvo/tressel/rotate.py
  6. 1
    0
      setup.py

+ 141
- 54
akvo/gui/akvoGUI.py View File

53
         return "np.array(%r)" % (self.data)
53
         return "np.array(%r)" % (self.data)
54
 
54
 
55
 from collections import OrderedDict
55
 from collections import OrderedDict
56
-def represent_ordereddict(dumper, data):
57
-    value = []
58
-    for item_key, item_value in data.items():
59
-        node_key = dumper.represent_data(item_key)
60
-        node_value = dumper.represent_data(item_value)
61
-        value.append((node_key, node_value))
62
-    return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)
63
-yaml.add_representer(OrderedDict, represent_ordereddict)
56
+#def represent_ordereddict(dumper, data):
57
+#    print("representing IN DA HOUSE!!!!!!!!!!!!!!!!!!!!!")
58
+#    value = []
59
+#    for item_key, item_value in data.items():
60
+#        node_key = dumper.represent_data(item_key)
61
+#        node_value = dumper.represent_data(item_value)
62
+#        value.append((node_key, node_value))
63
+#    return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)
64
+#yaml.add_representer(OrderedDict, represent_ordereddict)
65
+
66
+def setup_yaml():
67
+    """ https://stackoverflow.com/a/8661021 """
68
+    represent_dict_order = lambda self, data:  self.represent_mapping('tag:yaml.org,2002:map', data.items())
69
+    yaml.add_representer(OrderedDict, represent_dict_order)    
70
+setup_yaml()
64
 
71
 
65
 class AkvoYamlNode(yaml.YAMLObject):
72
 class AkvoYamlNode(yaml.YAMLObject):
66
-    yaml_tag = u'!AkvoData'
73
+    yaml_tag = u'AkvoData'
67
     def __init__(self):
74
     def __init__(self):
68
         self.Akvo_VERSION = version
75
         self.Akvo_VERSION = version
69
-        self.Import = {}
76
+        self.Import = OrderedDict() # {}
70
         self.Processing = OrderedDict() 
77
         self.Processing = OrderedDict() 
71
-        #self.ProcessingFlow = []
72
-        #self.Data = {}
73
-    # For going the other way, data import based on Yaml serialization, 
78
+    #def __init__(self, node):
79
+    #    self.Akvo_VERSION = node["version"]
80
+    #    self.Import = OrderedDict( node["Import"] ) # {}
81
+    #    self.Processing = OrderedDict( node["Processing"] ) 
74
     def __repr__(self):
82
     def __repr__(self):
75
         return "%s(name=%r, Akvo_VESION=%r, Import=%r, Processing=%r)" % (
83
         return "%s(name=%r, Akvo_VESION=%r, Import=%r, Processing=%r)" % (
76
-            self.__class__.__name__, self.Akvo_VERSION, self.Import, OrderedDict(dict(self.Processing)) ) 
84
+            #self.__class__.__name__, self.Akvo_VERSION, self.Import, self.Processing )
85
+            self.__class__.__name__, self.Akvo_VERSION, self.Import, OrderedDict(self.Processing) ) 
86
+            #self.__class__.__name__, self.Akvo_VERSION, self.Import, OrderedDict(self.Processing)) 
77
     
87
     
78
 try:    
88
 try:    
79
     import thread 
89
     import thread 
172
         self.ui.mplwidget_navigator_2.setCanvas(self.ui.mplwidget_2)
182
         self.ui.mplwidget_navigator_2.setCanvas(self.ui.mplwidget_2)
173
 
183
 
174
         ##########################################################################
184
         ##########################################################################
175
-        # modelling Table 
176
-        self.ui.loopTableWidget.setRowCount(40)       
177
-        self.ui.loopTableWidget.setColumnCount(5)      
178
-        self.ui.loopTableWidget.setHorizontalHeaderLabels( ["ch. tag", "Northing [m]","Easting [m]","Height [m]", "Radius"] )
179
-        self.ui.loopTableWidget.cellChanged.connect(self.cellChanged) 
185
+        # Loop Table 
186
+        self.ui.loopTableWidget.setRowCount(80)       
187
+        self.ui.loopTableWidget.setColumnCount(6)      
188
+        self.ui.loopTableWidget.setHorizontalHeaderLabels( ["ch. tag", \
189
+            "Northing [m]","Easting [m]","Height [m]", "Radius","Tx"] )
190
+        
191
+        for ir in range(0, self.ui.loopTableWidget.rowCount() ):
192
+            for ic in range(1, self.ui.loopTableWidget.columnCount() ):
193
+                pCell = QtWidgets.QTableWidgetItem()
194
+                #pCell.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
195
+                pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
196
+                pCell.setBackground( QtGui.QColor("lightgrey").lighter(110) )
197
+                self.ui.loopTableWidget.setItem(ir, ic, pCell)
198
+
199
+        self.ui.loopTableWidget.cellChanged.connect(self.loopCellChanged)
200
+        #self.ui.loopTableWidget.cellPressed.connect(self.loopCellChanged) 
201
+        self.ui.loopTableWidget.itemClicked.connect(self.loopCellClicked) 
202
+        #self.ui.loopTableWidget.cellPressed.connect(self.loopCellClicked) 
180
 
203
 
181
         self.ui.loopTableWidget.setDragDropOverwriteMode(False)
204
         self.ui.loopTableWidget.setDragDropOverwriteMode(False)
182
-        self.ui.loopTableWidget.setDragEnabled(True)
183
-        self.ui.loopTableWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
184
-        
205
+        self.ui.loopTableWidget.setDragEnabled(False)
206
+        #self.ui.loopTableWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
207
+
208
+        self.loops = {}        
209
+
185
         ##########################################################################
210
         ##########################################################################
186
         # layer Table 
211
         # layer Table 
187
         self.ui.layerTableWidget.setRowCount(80)       
212
         self.ui.layerTableWidget.setRowCount(80)       
188
         self.ui.layerTableWidget.setColumnCount(3)      
213
         self.ui.layerTableWidget.setColumnCount(3)      
189
         self.ui.layerTableWidget.setHorizontalHeaderLabels( [r"top [m]", r"bottom [m]", "σ [ Ωm]" ] )
214
         self.ui.layerTableWidget.setHorizontalHeaderLabels( [r"top [m]", r"bottom [m]", "σ [ Ωm]" ] )
190
 
215
 
191
-
216
+        # do we want this
192
         self.ui.layerTableWidget.setDragDropOverwriteMode(False)
217
         self.ui.layerTableWidget.setDragDropOverwriteMode(False)
193
         self.ui.layerTableWidget.setDragEnabled(True)
218
         self.ui.layerTableWidget.setDragEnabled(True)
194
         self.ui.layerTableWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
219
         self.ui.layerTableWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
195
         
220
         
196
         pCell = QtWidgets.QTableWidgetItem()
221
         pCell = QtWidgets.QTableWidgetItem()
197
-        #pCell.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
198
         pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
222
         pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
199
-        #pCell.setBackground( QtGui.QColor("lightblue").lighter(125) )
200
         pCell.setBackground( QtGui.QColor("lightgrey").lighter(110) )
223
         pCell.setBackground( QtGui.QColor("lightgrey").lighter(110) )
201
         self.ui.layerTableWidget.setItem(0, 1, pCell)
224
         self.ui.layerTableWidget.setItem(0, 1, pCell)
202
-        for ir in range(1, 80):
203
-            for ic in range(0, 3):
225
+        for ir in range(1, self.ui.layerTableWidget.rowCount() ):
226
+            for ic in range(0, self.ui.layerTableWidget.columnCount() ):
204
                 pCell = QtWidgets.QTableWidgetItem()
227
                 pCell = QtWidgets.QTableWidgetItem()
205
                 #pCell.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
228
                 #pCell.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
206
                 pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
229
                 pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
208
                 self.ui.layerTableWidget.setItem(ir, ic, pCell)
231
                 self.ui.layerTableWidget.setItem(ir, ic, pCell)
209
         self.ui.layerTableWidget.cellChanged.connect(self.sigmaCellChanged) 
232
         self.ui.layerTableWidget.cellChanged.connect(self.sigmaCellChanged) 
210
 
233
 
234
+
211
     def sigmaCellChanged(self):
235
     def sigmaCellChanged(self):
212
         self.ui.layerTableWidget.cellChanged.disconnect(self.sigmaCellChanged) 
236
         self.ui.layerTableWidget.cellChanged.disconnect(self.sigmaCellChanged) 
213
         # TODO consider building the model whenever this is called. Would be nice to be able to 
237
         # TODO consider building the model whenever this is called. Would be nice to be able to 
259
                     self.ui.layerTableWidget.cellChanged.connect(self.sigmaCellChanged) 
283
                     self.ui.layerTableWidget.cellChanged.connect(self.sigmaCellChanged) 
260
                     return
284
                     return
261
 
285
 
262
-            print("I'm here joey")
263
             # enable next layer
286
             # enable next layer
264
             pCell4 = self.ui.layerTableWidget.item(ii+1, jj)
287
             pCell4 = self.ui.layerTableWidget.item(ii+1, jj)
265
             pCell4.setBackground( QtGui.QColor("lightblue") ) #.lighter(110))
288
             pCell4.setBackground( QtGui.QColor("lightblue") ) #.lighter(110))
277
 
300
 
278
         self.ui.layerTableWidget.cellChanged.connect(self.sigmaCellChanged) 
301
         self.ui.layerTableWidget.cellChanged.connect(self.sigmaCellChanged) 
279
 
302
 
303
+    def loopCellClicked(self, item):
304
+        print("checkstate", item.checkState(),item.row())
280
 
305
 
281
-    def cellChanged(self):
282
-        # TODO consider building the model whenever this is called. Would be nice to be able to 
283
-        # do that. Would require instead dist of T2 I guess. 
306
+        #self.ui.loopTableWidget.itemClicked.disconnect(self.loopCellClicked)
307
+        jj = item.column() 
308
+        ii = item.row()
309
+        tp = type(self.ui.loopTableWidget.item(ii, 0))
310
+        print("tp", tp, ii, jj)
311
+        if str(tp) == "<class 'NoneType'>": 
312
+            return 
313
+        #print("Clicked", ii, jj)
314
+        if jj == 5 and self.ui.loopTableWidget.item(ii, 0).text() in self.loops.keys():
315
+            #print("jj=5")
316
+            self.loops[ self.ui.loopTableWidget.item(ii, 0).text() ]["Tx"] = self.ui.loopTableWidget.item(ii, 5).checkState()
317
+            # update surrogates  
318
+            print("updating surrogates")
319
+            for point in self.loops[ self.ui.loopTableWidget.item(ii, 0).text() ]["points"][1:]:
320
+                pCell = self.ui.loopTableWidget.item(point, 5)
321
+                if self.ui.loopTableWidget.item(ii, 5).checkState():
322
+                    pCell.setCheckState(QtCore.Qt.Checked);
323
+                else:
324
+                    pCell.setCheckState(QtCore.Qt.Unchecked);
325
+ 
326
+            #print( "loops",  self.loops[ self.ui.loopTableWidget.item(ii, 0).text() ]["Tx"]) 
327
+         
328
+        #self.ui.loopTableWidget.itemClicked.connect(self.loopCellClicked) 
329
+
330
+    def loopCellChanged(self):
331
+        self.ui.loopTableWidget.cellChanged.disconnect(self.loopCellChanged) 
332
+        
284
         jj = self.ui.loopTableWidget.currentColumn()
333
         jj = self.ui.loopTableWidget.currentColumn()
285
         ii = self.ui.loopTableWidget.currentRow()
334
         ii = self.ui.loopTableWidget.currentRow()
286
-            
287
-        #if self.ui.loopTableWidget.item(ii, jj) == None:
288
-        #    return
289
 
335
 
290
-        try:
291
-            eval (str( self.ui.loopTableWidget.item(ii, jj).text() ))
292
-        except:
293
-            if jj != 0:
294
-                Error = QtWidgets.QMessageBox()
295
-                Error.setWindowTitle("Error!")
296
-                Error.setText("Non-numeric value encountered")
297
-                Error.setDetailedText("Modelling parameters must be able to be cast into numeric values.")
298
-                Error.exec_()
336
+        if jj == 0 and len( self.ui.loopTableWidget.item(ii, jj).text().strip()) == 0:
337
+            for jjj in range(jj+1,jj+6): 
338
+                pCell = self.ui.loopTableWidget.item(ii, jjj)
339
+                pCell.setBackground( QtGui.QColor("white") )
340
+                pCell.setFlags( QtCore.Qt.NoItemFlags | QtCore.Qt.ItemIsUserCheckable   ) # not selectable 
341
+        elif jj == 0 and len( self.ui.loopTableWidget.item(ii, jj).text().strip() ): # ch. tag modified
342
+            for jjj in range(jj+1,jj+5): 
343
+                pCell = self.ui.loopTableWidget.item(ii, jjj)
344
+                pCell.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled )
345
+                pCell.setBackground( QtGui.QColor("lightblue") )
346
+            if self.ui.loopTableWidget.item(ii, jj).text() not in self.loops.keys():
347
+                # This is a new loop ID 
348
+                self.loops[ self.ui.loopTableWidget.item(ii, jj).text() ] = {}
349
+                self.loops[ self.ui.loopTableWidget.item(ii, jj).text() ]["Tx"] = self.ui.loopTableWidget.item(ii, 5).checkState()
350
+                self.loops[ self.ui.loopTableWidget.item(ii, jj).text() ]["points"] = [ii] 
351
+                # Transmitter cell 
352
+                pCell = self.ui.loopTableWidget.item(ii, jj+5)
353
+                pCell.setCheckState(QtCore.Qt.Unchecked)
354
+                pCell.setFlags( QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled  )
355
+                pCell.setBackground( QtGui.QColor("lightblue") ) 
356
+            else:
357
+                # This is an existing loop ID 
358
+                self.loops[ self.ui.loopTableWidget.item(ii, jj).text() ]["points"].append( ii ) 
359
+                pCell = self.ui.loopTableWidget.item(ii, jj+5)
360
+                pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
361
+                if self.loops[ self.ui.loopTableWidget.item(ii, 0).text() ]["Tx"]:
362
+                    pCell.setCheckState(QtCore.Qt.Checked);
363
+                else:
364
+                    pCell.setCheckState(QtCore.Qt.Unchecked);
365
+                #pCell.setFlags( )
366
+                pCell.setBackground( QtGui.QColor("lightblue") )
367
+
368
+
299
             
369
             
370
+ 
300
         self.plotLoops()
371
         self.plotLoops()
372
+        self.ui.loopTableWidget.cellChanged.connect(self.loopCellChanged) 
373
+
301
 
374
 
302
     def plotLoops(self):
375
     def plotLoops(self):
303
                
376
                
310
         for ii in range( self.ui.loopTableWidget.rowCount() ):
383
         for ii in range( self.ui.loopTableWidget.rowCount() ):
311
             for jj in range( self.ui.loopTableWidget.columnCount() ):
384
             for jj in range( self.ui.loopTableWidget.columnCount() ):
312
                 tp = type(self.ui.loopTableWidget.item(ii, jj))
385
                 tp = type(self.ui.loopTableWidget.item(ii, jj))
313
-                if str(tp) == "<class 'NoneType'>":  # ugly hack needed by PySide for some strange reason.
314
-                    pass #print ("NONE")
386
+                if str(tp) == "<class 'NoneType'>":  
387
+                    pass 
388
+                elif not len(self.ui.loopTableWidget.item(ii, jj).text()): 
389
+                    pass
315
                 else:
390
                 else:
316
                     if jj == 0: 
391
                     if jj == 0: 
317
                         idx = self.ui.loopTableWidget.item(ii, 0).text()
392
                         idx = self.ui.loopTableWidget.item(ii, 0).text()
331
                 self.ui.mplwidget_3.ax1.plot(  np.array(nor[ii]), np.array(eas[ii])  )
406
                 self.ui.mplwidget_3.ax1.plot(  np.array(nor[ii]), np.array(eas[ii])  )
332
             except:
407
             except:
333
                 pass 
408
                 pass 
409
+        #self.ui.mplwidget_3.figure.axes().set
410
+        plt.gca().set_aspect('equal') #, adjustable='box')
334
         self.ui.mplwidget_3.draw()
411
         self.ui.mplwidget_3.draw()
335
 
412
 
336
     def about(self):
413
     def about(self):
356
         self.RAWDataProc.updateProcTrigger.connect(self.updateProc)
433
         self.RAWDataProc.updateProcTrigger.connect(self.updateProc)
357
 
434
 
358
     def openGMRRAWDataset(self):
435
     def openGMRRAWDataset(self):
359
-
436
+        """ Opens a GMR header file
437
+        """
360
         try:
438
         try:
361
             with open('.gmr.last.path') as f: 
439
             with open('.gmr.last.path') as f: 
362
                 fpath = f.readline()  
440
                 fpath = f.readline()  
495
             # Window centres 
573
             # Window centres 
496
 
574
 
497
         with open(SaveStr, 'w') as outfile:
575
         with open(SaveStr, 'w') as outfile:
498
-            for line in self.logText:
499
-                outfile.write(line+"\n")
576
+            #for line in self.logText:
577
+            #    outfile.write(line+"\n")
578
+            yaml.dump(self.YamlNode, outfile)   
500
             yaml.dump(INFO, outfile, default_flow_style=False)   
579
             yaml.dump(INFO, outfile, default_flow_style=False)   
501
  
580
  
502
     def SavePreprocess(self):
581
     def SavePreprocess(self):
536
         INFO["transFreq"] = self.RAWDataProc.transFreq
615
         INFO["transFreq"] = self.RAWDataProc.transFreq
537
         INFO["headerstr"] = str(self.headerstr)
616
         INFO["headerstr"] = str(self.headerstr)
538
         INFO["log"] = yaml.dump( self.YamlNode )  #self.logText  #MAK 20170127
617
         INFO["log"] = yaml.dump( self.YamlNode )  #self.logText  #MAK 20170127
539
-        
540
-        print ("YAML NODE", yaml.dump( self.YamlNode ) )
541
     
618
     
542
         self.RAWDataProc.DATADICT["INFO"] = INFO 
619
         self.RAWDataProc.DATADICT["INFO"] = INFO 
543
 
620
 
546
 
623
 
547
     # Export XML file suitable for USGS ScienceBase Data Release
624
     # Export XML file suitable for USGS ScienceBase Data Release
548
     def ExportXML(self):
625
     def ExportXML(self):
549
-
626
+        """ This is a filler function for use by USGS collaborators 
627
+        """
550
         return 42
628
         return 42
551
 
629
 
552
     def OpenPreprocess(self):
630
     def OpenPreprocess(self):
601
             #print ( self.RAWDataProc.DATADICT["INFO"]["log"] )
679
             #print ( self.RAWDataProc.DATADICT["INFO"]["log"] )
602
         
680
         
603
         self.logText = self.RAWDataProc.DATADICT["INFO"]["log"] # YAML 
681
         self.logText = self.RAWDataProc.DATADICT["INFO"]["log"] # YAML 
604
-        self.YamlNode = yaml.load( self.logText ) 
682
+
683
+        self.YamlNode = AkvoYamlNode( )  #self.logText )
684
+        self.YamlNode.Akvo_VERSION = (yaml.load( self.logText )).Akvo_VERSION
685
+        self.YamlNode.Import = OrderedDict((yaml.load( self.logText )).Import)
686
+        self.YamlNode.Processing = OrderedDict((yaml.load( self.logText )).Processing)
687
+        #self.YamlNode.Akvo_VERSION =  2 #yaml.load( self.logText )["Akvo_VERSION"] #, Loader=yaml.RoundTripLoader) # offending line! 
688
+        #self.YamlNode = AkvoYamlNode( self.logText ) # offending line! 
689
+        #print("import type", type( self.YamlNode.Processing ))
690
+        self.Log() 
605
             #self.ui.logTextBrowser.append( yaml.dump(self.YamlNode)) #, default_flow_style=False)  )
691
             #self.ui.logTextBrowser.append( yaml.dump(self.YamlNode)) #, default_flow_style=False)  )
606
         #except KeyError:
692
         #except KeyError:
607
         #    pass
693
         #    pass
779
         #for line in nlogText: 
865
         #for line in nlogText: 
780
         #    self.ui.logTextBrowser.append( line )
866
         #    self.ui.logTextBrowser.append( line )
781
         #    self.logText.append( line ) 
867
         #    self.logText.append( line ) 
782
-            
783
-        self.ui.logTextBrowser.clear() 
868
+        self.ui.logTextBrowser.clear()
784
         self.ui.logTextBrowser.append( yaml.dump(self.YamlNode)) #, default_flow_style=False)  )
869
         self.ui.logTextBrowser.append( yaml.dump(self.YamlNode)) #, default_flow_style=False)  )
785
 
870
 
786
     def disable(self):
871
     def disable(self):
885
 
970
 
886
     def calcQ(self):
971
     def calcQ(self):
887
         if "Calc Q" not in self.YamlNode.Processing.keys():
972
         if "Calc Q" not in self.YamlNode.Processing.keys():
973
+            print("In CalcQ", yaml.dump(self.YamlNode.Processing)  )
888
             self.YamlNode.Processing["Calc Q"] = True
974
             self.YamlNode.Processing["Calc Q"] = True
975
+            print( yaml.dump(self.YamlNode.Processing)  )
889
             self.Log()
976
             self.Log()
890
         else:
977
         else:
891
             err_msg = "Q values have already been calculated"
978
             err_msg = "Q values have already been calculated"

+ 183
- 135
akvo/gui/main.ui View File

73
         <rect>
73
         <rect>
74
          <x>0</x>
74
          <x>0</x>
75
          <y>0</y>
75
          <y>0</y>
76
-         <width>982</width>
77
-         <height>921</height>
76
+         <width>967</width>
77
+         <height>922</height>
78
         </rect>
78
         </rect>
79
        </property>
79
        </property>
80
        <layout class="QHBoxLayout" name="horizontalLayout_2">
80
        <layout class="QHBoxLayout" name="horizontalLayout_2">
96
            <enum>Qt::LeftToRight</enum>
96
            <enum>Qt::LeftToRight</enum>
97
           </property>
97
           </property>
98
           <property name="currentIndex">
98
           <property name="currentIndex">
99
-           <number>0</number>
99
+           <number>2</number>
100
           </property>
100
           </property>
101
           <widget class="QWidget" name="tab">
101
           <widget class="QWidget" name="tab">
102
            <property name="minimumSize">
102
            <property name="minimumSize">
801
               <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
801
               <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
802
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
802
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
803
 p, li { white-space: pre-wrap; }
803
 p, li { white-space: pre-wrap; }
804
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:8pt; font-weight:400; font-style:italic;&quot;&gt;
804
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:8pt; font-weight:400; font-style:italic;&quot;&gt;
805
 &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'DejaVu Serif'; font-size:9pt;&quot;&gt;Load supported RAW Dataset header from file menu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
805
 &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'DejaVu Serif'; font-size:9pt;&quot;&gt;Load supported RAW Dataset header from file menu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
806
              </property>
806
              </property>
807
             </widget>
807
             </widget>
854
               <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
854
               <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
855
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
855
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
856
 p, li { white-space: pre-wrap; }
856
 p, li { white-space: pre-wrap; }
857
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:italic;&quot;&gt;
857
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:italic;&quot;&gt;
858
 &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'DejaVu Serif'; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
858
 &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'DejaVu Serif'; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
859
              </property>
859
              </property>
860
             </widget>
860
             </widget>
2489
                 <rect>
2489
                 <rect>
2490
                  <x>20</x>
2490
                  <x>20</x>
2491
                  <y>37</y>
2491
                  <y>37</y>
2492
-                 <width>111</width>
2492
+                 <width>121</width>
2493
                  <height>16</height>
2493
                  <height>16</height>
2494
                 </rect>
2494
                 </rect>
2495
                </property>
2495
                </property>
2513
               <widget class="QLabel" name="label_36">
2513
               <widget class="QLabel" name="label_36">
2514
                <property name="geometry">
2514
                <property name="geometry">
2515
                 <rect>
2515
                 <rect>
2516
-                 <x>560</x>
2517
-                 <y>40</y>
2516
+                 <x>10</x>
2517
+                 <y>190</y>
2518
                  <width>61</width>
2518
                  <width>61</width>
2519
                  <height>16</height>
2519
                  <height>16</height>
2520
                 </rect>
2520
                 </rect>
2526
               <widget class="QLineEdit" name="locEdit">
2526
               <widget class="QLineEdit" name="locEdit">
2527
                <property name="geometry">
2527
                <property name="geometry">
2528
                 <rect>
2528
                 <rect>
2529
-                 <x>560</x>
2530
-                 <y>60</y>
2531
-                 <width>351</width>
2532
-                 <height>81</height>
2529
+                 <x>10</x>
2530
+                 <y>210</y>
2531
+                 <width>441</width>
2532
+                 <height>51</height>
2533
                 </rect>
2533
                 </rect>
2534
                </property>
2534
                </property>
2535
               </widget>
2535
               </widget>
2536
-              <widget class="QDoubleSpinBox" name="decSpinBox">
2536
+              <widget class="Line" name="line">
2537
                <property name="geometry">
2537
                <property name="geometry">
2538
                 <rect>
2538
                 <rect>
2539
-                 <x>420</x>
2540
-                 <y>70</y>
2541
-                 <width>101</width>
2542
-                 <height>31</height>
2539
+                 <x>20</x>
2540
+                 <y>160</y>
2541
+                 <width>371</width>
2542
+                 <height>16</height>
2543
                 </rect>
2543
                 </rect>
2544
                </property>
2544
                </property>
2545
-               <property name="decimals">
2546
-                <number>1</number>
2545
+               <property name="orientation">
2546
+                <enum>Qt::Horizontal</enum>
2547
                </property>
2547
                </property>
2548
-               <property name="minimum">
2549
-                <double>-90.000000000000000</double>
2548
+              </widget>
2549
+              <widget class="QLabel" name="label_52">
2550
+               <property name="geometry">
2551
+                <rect>
2552
+                 <x>0</x>
2553
+                 <y>260</y>
2554
+                 <width>191</width>
2555
+                 <height>31</height>
2556
+                </rect>
2550
                </property>
2557
                </property>
2551
-               <property name="maximum">
2552
-                <double>90.000000000000000</double>
2558
+               <property name="text">
2559
+                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Comments and field notes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
2553
                </property>
2560
                </property>
2554
-               <property name="value">
2555
-                <double>0.000000000000000</double>
2561
+              </widget>
2562
+              <widget class="QTextBrowser" name="txtComments">
2563
+               <property name="geometry">
2564
+                <rect>
2565
+                 <x>10</x>
2566
+                 <y>300</y>
2567
+                 <width>441</width>
2568
+                 <height>221</height>
2569
+                </rect>
2570
+               </property>
2571
+               <property name="readOnly">
2572
+                <bool>false</bool>
2556
                </property>
2573
                </property>
2557
               </widget>
2574
               </widget>
2558
-              <widget class="QDoubleSpinBox" name="incSpinBox">
2575
+              <widget class="QTimeEdit" name="timeEdit">
2559
                <property name="geometry">
2576
                <property name="geometry">
2560
                 <rect>
2577
                 <rect>
2561
-                 <x>420</x>
2562
-                 <y>30</y>
2563
-                 <width>101</width>
2564
-                 <height>31</height>
2578
+                 <x>150</x>
2579
+                 <y>110</y>
2580
+                 <width>118</width>
2581
+                 <height>29</height>
2565
                 </rect>
2582
                 </rect>
2566
                </property>
2583
                </property>
2567
-               <property name="decimals">
2568
-                <number>1</number>
2584
+               <property name="calendarPopup">
2585
+                <bool>true</bool>
2569
                </property>
2586
                </property>
2570
-               <property name="minimum">
2571
-                <double>-90.000000000000000</double>
2587
+              </widget>
2588
+              <widget class="QDateEdit" name="dateEdit">
2589
+               <property name="geometry">
2590
+                <rect>
2591
+                 <x>150</x>
2592
+                 <y>70</y>
2593
+                 <width>112</width>
2594
+                 <height>29</height>
2595
+                </rect>
2572
                </property>
2596
                </property>
2573
-               <property name="maximum">
2574
-                <double>90.000000000000000</double>
2597
+               <property name="calendarPopup">
2598
+                <bool>true</bool>
2575
                </property>
2599
                </property>
2576
-               <property name="value">
2577
-                <double>45.000000000000000</double>
2600
+              </widget>
2601
+              <widget class="QLabel" name="label_50">
2602
+               <property name="geometry">
2603
+                <rect>
2604
+                 <x>24</x>
2605
+                 <y>117</y>
2606
+                 <width>81</width>
2607
+                 <height>16</height>
2608
+                </rect>
2609
+               </property>
2610
+               <property name="text">
2611
+                <string>Survey time</string>
2578
                </property>
2612
                </property>
2579
               </widget>
2613
               </widget>
2580
-              <widget class="QLabel" name="label_37">
2614
+              <widget class="QDoubleSpinBox" name="tempSpinBox">
2581
                <property name="geometry">
2615
                <property name="geometry">
2582
                 <rect>
2616
                 <rect>
2583
-                 <x>300</x>
2617
+                 <x>150</x>
2584
                  <y>30</y>
2618
                  <y>30</y>
2585
                  <width>111</width>
2619
                  <width>111</width>
2586
-                 <height>20</height>
2620
+                 <height>29</height>
2587
                 </rect>
2621
                 </rect>
2588
                </property>
2622
                </property>
2589
-               <property name="text">
2590
-                <string>B Inclination [°]</string>
2623
+               <property name="value">
2624
+                <double>20.000000000000000</double>
2591
                </property>
2625
                </property>
2592
               </widget>
2626
               </widget>
2593
-              <widget class="QLabel" name="label_38">
2627
+              <widget class="QTableWidget" name="loopTableWidget">
2594
                <property name="geometry">
2628
                <property name="geometry">
2595
                 <rect>
2629
                 <rect>
2596
-                 <x>300</x>
2597
-                 <y>75</y>
2598
-                 <width>111</width>
2599
-                 <height>20</height>
2630
+                 <x>10</x>
2631
+                 <y>560</y>
2632
+                 <width>641</width>
2633
+                 <height>291</height>
2634
+                </rect>
2635
+               </property>
2636
+               <property name="toolTip">
2637
+                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This table is used to enter coil geometries the format is as follows: each row specifies a single point on a coil. The first column is the coil index (using the GMR channel is useful), the next three colums specify the point in Northing, Easting, and Elevation. These can either be local coordinates or global ones. The final column specifies the loop radius if it is a circle or figure 8, for non circular or figure 8 loops leave this column blank. For figure-8 loops the coils do not need to be touching (see Irons and Kass, 2017). If a given index has 1 row it will be a circular loop, two rows will be a figure 8, and more than that will be a polygonal representation of the points, linearlly interpolated between them. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
2638
+               </property>
2639
+              </widget>
2640
+              <widget class="QLabel" name="label_54">
2641
+               <property name="geometry">
2642
+                <rect>
2643
+                 <x>10</x>
2644
+                 <y>540</y>
2645
+                 <width>91</width>
2646
+                 <height>16</height>
2600
                 </rect>
2647
                 </rect>
2601
                </property>
2648
                </property>
2602
                <property name="text">
2649
                <property name="text">
2603
-                <string>B Declination [°] </string>
2650
+                <string>Surface loops</string>
2651
+               </property>
2652
+              </widget>
2653
+              <widget class="MyDynamicMplCanvas" name="mplwidget_3" native="true">
2654
+               <property name="geometry">
2655
+                <rect>
2656
+                 <x>460</x>
2657
+                 <y>0</y>
2658
+                 <width>500</width>
2659
+                 <height>500</height>
2660
+                </rect>
2661
+               </property>
2662
+               <property name="sizePolicy">
2663
+                <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2664
+                 <horstretch>0</horstretch>
2665
+                 <verstretch>0</verstretch>
2666
+                </sizepolicy>
2667
+               </property>
2668
+               <property name="minimumSize">
2669
+                <size>
2670
+                 <width>500</width>
2671
+                 <height>500</height>
2672
+                </size>
2604
                </property>
2673
                </property>
2605
               </widget>
2674
               </widget>
2606
               <widget class="QDoubleSpinBox" name="intensitySpinBox">
2675
               <widget class="QDoubleSpinBox" name="intensitySpinBox">
2607
                <property name="geometry">
2676
                <property name="geometry">
2608
                 <rect>
2677
                 <rect>
2609
-                 <x>420</x>
2610
-                 <y>110</y>
2678
+                 <x>790</x>
2679
+                 <y>675</y>
2611
                  <width>101</width>
2680
                  <width>101</width>
2612
                  <height>31</height>
2681
                  <height>31</height>
2613
                 </rect>
2682
                 </rect>
2622
                 <double>50000.000000000000000</double>
2691
                 <double>50000.000000000000000</double>
2623
                </property>
2692
                </property>
2624
               </widget>
2693
               </widget>
2625
-              <widget class="QLabel" name="label_51">
2694
+              <widget class="QLabel" name="label_38">
2626
                <property name="geometry">
2695
                <property name="geometry">
2627
                 <rect>
2696
                 <rect>
2628
-                 <x>300</x>
2629
-                 <y>115</y>
2697
+                 <x>670</x>
2698
+                 <y>640</y>
2630
                  <width>111</width>
2699
                  <width>111</width>
2631
                  <height>20</height>
2700
                  <height>20</height>
2632
                 </rect>
2701
                 </rect>
2633
                </property>
2702
                </property>
2634
                <property name="text">
2703
                <property name="text">
2635
-                <string>B Intensity [nT]</string>
2704
+                <string>B Declination [°] </string>
2636
                </property>
2705
                </property>
2637
               </widget>
2706
               </widget>
2638
-              <widget class="Line" name="line">
2707
+              <widget class="QLabel" name="label_37">
2639
                <property name="geometry">
2708
                <property name="geometry">
2640
                 <rect>
2709
                 <rect>
2641
-                 <x>20</x>
2642
-                 <y>160</y>
2643
-                 <width>901</width>
2644
-                 <height>16</height>
2710
+                 <x>670</x>
2711
+                 <y>600</y>
2712
+                 <width>111</width>
2713
+                 <height>20</height>
2645
                 </rect>
2714
                 </rect>
2646
                </property>
2715
                </property>
2647
-               <property name="orientation">
2648
-                <enum>Qt::Horizontal</enum>
2716
+               <property name="text">
2717
+                <string>B Inclination [°]</string>
2649
                </property>
2718
                </property>
2650
               </widget>
2719
               </widget>
2651
-              <widget class="QLabel" name="label_52">
2720
+              <widget class="QDoubleSpinBox" name="decSpinBox">
2652
                <property name="geometry">
2721
                <property name="geometry">
2653
                 <rect>
2722
                 <rect>
2654
-                 <x>10</x>
2655
-                 <y>180</y>
2656
-                 <width>191</width>
2723
+                 <x>790</x>
2724
+                 <y>635</y>
2725
+                 <width>101</width>
2657
                  <height>31</height>
2726
                  <height>31</height>
2658
                 </rect>
2727
                 </rect>
2659
                </property>
2728
                </property>
2660
-               <property name="text">
2661
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Comments and field notes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
2729
+               <property name="decimals">
2730
+                <number>1</number>
2662
                </property>
2731
                </property>
2663
-              </widget>
2664
-              <widget class="QTextBrowser" name="txtComments">
2665
-               <property name="geometry">
2666
-                <rect>
2667
-                 <x>30</x>
2668
-                 <y>220</y>
2669
-                 <width>881</width>
2670
-                 <height>631</height>
2671
-                </rect>
2732
+               <property name="minimum">
2733
+                <double>-90.000000000000000</double>
2672
                </property>
2734
                </property>
2673
-               <property name="readOnly">
2674
-                <bool>false</bool>
2735
+               <property name="maximum">
2736
+                <double>90.000000000000000</double>
2737
+               </property>
2738
+               <property name="value">
2739
+                <double>0.000000000000000</double>
2675
                </property>
2740
                </property>
2676
               </widget>
2741
               </widget>
2677
-              <widget class="QTimeEdit" name="timeEdit">
2742
+              <widget class="QDoubleSpinBox" name="incSpinBox">
2678
                <property name="geometry">
2743
                <property name="geometry">
2679
                 <rect>
2744
                 <rect>
2680
-                 <x>140</x>
2681
-                 <y>110</y>
2682
-                 <width>118</width>
2683
-                 <height>29</height>
2745
+                 <x>790</x>
2746
+                 <y>595</y>
2747
+                 <width>101</width>
2748
+                 <height>31</height>
2684
                 </rect>
2749
                 </rect>
2685
                </property>
2750
                </property>
2686
-               <property name="calendarPopup">
2687
-                <bool>true</bool>
2751
+               <property name="decimals">
2752
+                <number>1</number>
2753
+               </property>
2754
+               <property name="minimum">
2755
+                <double>-90.000000000000000</double>
2756
+               </property>
2757
+               <property name="maximum">
2758
+                <double>90.000000000000000</double>
2759
+               </property>
2760
+               <property name="value">
2761
+                <double>45.000000000000000</double>
2688
                </property>
2762
                </property>
2689
               </widget>
2763
               </widget>
2690
-              <widget class="QDateEdit" name="dateEdit">
2764
+              <widget class="QLabel" name="label_51">
2691
                <property name="geometry">
2765
                <property name="geometry">
2692
                 <rect>
2766
                 <rect>
2693
-                 <x>140</x>
2694
-                 <y>70</y>
2695
-                 <width>112</width>
2696
-                 <height>29</height>
2767
+                 <x>670</x>
2768
+                 <y>680</y>
2769
+                 <width>111</width>
2770
+                 <height>20</height>
2697
                 </rect>
2771
                 </rect>
2698
                </property>
2772
                </property>
2699
-               <property name="calendarPopup">
2700
-                <bool>true</bool>
2773
+               <property name="text">
2774
+                <string>B Intensity [nT]</string>
2701
                </property>
2775
                </property>
2702
               </widget>
2776
               </widget>
2703
-              <widget class="QLabel" name="label_50">
2777
+              <widget class="QLabel" name="label_57">
2704
                <property name="geometry">
2778
                <property name="geometry">
2705
                 <rect>
2779
                 <rect>
2706
-                 <x>24</x>
2707
-                 <y>117</y>
2708
-                 <width>81</width>
2780
+                 <x>670</x>
2781
+                 <y>560</y>
2782
+                 <width>121</width>
2709
                  <height>16</height>
2783
                  <height>16</height>
2710
                 </rect>
2784
                 </rect>
2711
                </property>
2785
                </property>
2712
                <property name="text">
2786
                <property name="text">
2713
-                <string>Survey time</string>
2787
+                <string>Magnetic field</string>
2714
                </property>
2788
                </property>
2715
               </widget>
2789
               </widget>
2716
-              <widget class="QDoubleSpinBox" name="tempSpinBox">
2790
+              <widget class="Line" name="line_3">
2717
                <property name="geometry">
2791
                <property name="geometry">
2718
                 <rect>
2792
                 <rect>
2719
-                 <x>140</x>
2720
-                 <y>30</y>
2721
-                 <width>111</width>
2722
-                 <height>29</height>
2793
+                 <x>670</x>
2794
+                 <y>540</y>
2795
+                 <width>251</width>
2796
+                 <height>20</height>
2723
                 </rect>
2797
                 </rect>
2724
                </property>
2798
                </property>
2725
-               <property name="value">
2726
-                <double>20.000000000000000</double>
2799
+               <property name="orientation">
2800
+                <enum>Qt::Horizontal</enum>
2727
                </property>
2801
                </property>
2728
               </widget>
2802
               </widget>
2729
              </widget>
2803
              </widget>
2734
            <attribute name="title">
2808
            <attribute name="title">
2735
             <string>Kernel calc</string>
2809
             <string>Kernel calc</string>
2736
            </attribute>
2810
            </attribute>
2737
-           <widget class="QTableWidget" name="loopTableWidget">
2738
-            <property name="geometry">
2739
-             <rect>
2740
-              <x>20</x>
2741
-              <y>70</y>
2742
-              <width>441</width>
2743
-              <height>271</height>
2744
-             </rect>
2745
-            </property>
2746
-            <property name="toolTip">
2747
-             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This table is used to enter coil geometries the format is as follows: each row specifies a single point on a coil. The first column is the coil index (using the GMR channel is useful), the next three colums specify the point in Northing, Easting, and Elevation. These can either be local coordinates or global ones. The final column specifies the loop radius if it is a circle or figure 8, for non circular or figure 8 loops leave this column blank. For figure-8 loops the coils do not need to be touching (see Irons and Kass, 2017). If a given index has 1 row it will be a circular loop, two rows will be a figure 8, and more than that will be a polygonal representation of the points, linearlly interpolated between them. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
2748
-            </property>
2749
-           </widget>
2750
            <widget class="Line" name="line_2">
2811
            <widget class="Line" name="line_2">
2751
             <property name="geometry">
2812
             <property name="geometry">
2752
              <rect>
2813
              <rect>
2760
              <enum>Qt::Horizontal</enum>
2821
              <enum>Qt::Horizontal</enum>
2761
             </property>
2822
             </property>
2762
            </widget>
2823
            </widget>
2763
-           <widget class="QLabel" name="label_54">
2764
-            <property name="geometry">
2765
-             <rect>
2766
-              <x>20</x>
2767
-              <y>40</y>
2768
-              <width>91</width>
2769
-              <height>16</height>
2770
-             </rect>
2771
-            </property>
2772
-            <property name="text">
2773
-             <string>Surface loops</string>
2774
-            </property>
2775
-           </widget>
2776
-           <widget class="MyDynamicMplCanvas" name="mplwidget_3" native="true">
2824
+           <widget class="MyDynamicMplCanvas" name="mplwidget_4" native="true">
2777
             <property name="geometry">
2825
             <property name="geometry">
2778
              <rect>
2826
              <rect>
2779
               <x>480</x>
2827
               <x>480</x>
3098
              <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
3146
              <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
3099
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
3147
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
3100
 p, li { white-space: pre-wrap; }
3148
 p, li { white-space: pre-wrap; }
3101
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
3102
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;All processing steps are recorded here for your records&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
3149
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
3150
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans Serif'; font-size:9pt;&quot;&gt;All processing steps are recorded here for your records&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
3103
             </property>
3151
             </property>
3104
            </widget>
3152
            </widget>
3105
            <widget class="QLabel" name="label_53">
3153
            <widget class="QLabel" name="label_53">
3130
      <x>0</x>
3178
      <x>0</x>
3131
      <y>0</y>
3179
      <y>0</y>
3132
      <width>1000</width>
3180
      <width>1000</width>
3133
-     <height>19</height>
3181
+     <height>25</height>
3134
     </rect>
3182
     </rect>
3135
    </property>
3183
    </property>
3136
    <widget class="QMenu" name="menuFile">
3184
    <widget class="QMenu" name="menuFile">

+ 340
- 170
akvo/tressel/decay.py View File

1
-import numpy, pylab,array #,rpy2
2
-
1
+import numpy, array #,rpy2
2
+from matplotlib import pyplot as plt
3
+import numpy as np
4
+from scipy.optimize import least_squares
3
 from rpy2.robjects.packages import importr
5
 from rpy2.robjects.packages import importr
4
 
6
 
5
 import rpy2.robjects as robjects
7
 import rpy2.robjects as robjects
52
 
54
 
53
 #################################################
55
 #################################################
54
 # Regress for T2 using rpy2 interface
56
 # Regress for T2 using rpy2 interface
55
-def regressCurve(peaks,times,sigma2=None  ,intercept=True):
57
+def regressCurve(peaks,times,sigma2=1,intercept=True):
56
 
58
 
57
     # TODO, if regression fails, it might be because there is no exponential
59
     # TODO, if regression fails, it might be because there is no exponential
58
     # term, maybe do a second regression then on a linear model. 
60
     # term, maybe do a second regression then on a linear model. 
65
     robjects.globalenv['b1'] = b1
67
     robjects.globalenv['b1'] = b1
66
     robjects.globalenv['b2'] = b2
68
     robjects.globalenv['b2'] = b2
67
     robjects.globalenv['rT2'] = rT2
69
     robjects.globalenv['rT2'] = rT2
68
-    #robjects.globalenv['sigma2'] = sigma2
70
+    robjects.globalenv['sigma2'] = sigma2
69
     value = robjects.FloatVector(peaks)
71
     value = robjects.FloatVector(peaks)
70
     times = robjects.FloatVector(numpy.array(times))
72
     times = robjects.FloatVector(numpy.array(times))
71
     
73
     
72
 #    my_weights = robjects.RVector(value/sigma2)
74
 #    my_weights = robjects.RVector(value/sigma2)
73
 #    robjects.globalenv['my_weigts'] = my_weights
75
 #    robjects.globalenv['my_weigts'] = my_weights
74
 
76
 
75
-    if sigma2 != None:
76
-        # print ("weighting")
77
-        #tw = numpy.array(peaks)/sigma2 
78
-        my_weights = robjects.FloatVector( sigma2 )
79
-    #else:
80
-    #    my_weights = robjects.FloatVector(numpy.ones(len(peaks))) 
77
+#    if sigma2 != 0:
78
+#        print "weighting"
79
+#        tw = numpy.array(peaks)/sigma2 
80
+#        my_weights = robjects.RVector( tw/numpy.max(tw) )
81
+#    else:
82
+#        my_weights = robjects.RVector(numpy.ones(len(peaks))) 
81
 
83
 
82
-        robjects.globalenv['my_weights'] = my_weights
83
-        #print (my_weights)
84
-        #print (len(peaks))
84
+#    robjects.globalenv['my_weights'] = my_weights
85
     
85
     
86
     if (intercept):
86
     if (intercept):
87
         my_list = robjects.r('list(b1=50, b2=1e2, rT2=0.03)')
87
         my_list = robjects.r('list(b1=50, b2=1e2, rT2=0.03)')
107
     env['times'] = times
107
     env['times'] = times
108
     
108
     
109
     # ugly, but I get errors with everything else I've tried
109
     # ugly, but I get errors with everything else I've tried
110
-    #my_weights = robjects.r('rep(1,length(value))')
111
-    #for ii in range(len(my_weights)):
112
-    #    my_weights[ii] *= peaks[ii]/sigma2
113
-
114
-
110
+    my_weights = robjects.r('rep(1,length(value))')
111
+    for ii in range(len(my_weights)):
112
+        my_weights[ii] *= peaks[ii]/sigma2
115
     Error = False
113
     Error = False
116
     #fit = robjects.r.nls(fmla,start=my_list,control=my_cont,weights=my_weights)
114
     #fit = robjects.r.nls(fmla,start=my_list,control=my_cont,weights=my_weights)
117
-    if (sigma2 != None):
118
-        #print("SIGMA 2")
115
+    if (sigma2 != 1):
116
+        print("SIGMA 2")
119
         #fit = robjects.r.tryCatch(robjects.r.suppressWarnings(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port", \
117
         #fit = robjects.r.tryCatch(robjects.r.suppressWarnings(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port", \
120
         #                     weights=my_weights)), 'silent=TRUE')
118
         #                     weights=my_weights)), 'silent=TRUE')
119
+        fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont))#, \
120
+                            # weights=my_weights))
121
+    else:
121
         try:
122
         try:
122
-            fit = robjects.r.tryCatch(    robjects.r.nls(fmla, start=my_list, control=my_cont, weights=my_weights, algorithm="port" , \
123
-                lower=my_lower,upper=my_upper))
123
+            fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port"))#,lower=my_lower,upper=my_upper))
124
         except:
124
         except:
125
             print("regression issue pass")
125
             print("regression issue pass")
126
             Error = True
126
             Error = True
127
+    # If failure fall back on zero regression values   
128
+    if not Error:
129
+        #Error = fit[3][0]
130
+        report =  r.summary(fit)
131
+    b1 = 0
132
+    b2 = 0 
133
+    rT2 = 1
134
+    if (intercept):
135
+        if not Error:
136
+            b1  =  r['$'](report,'par')[0]
137
+            b2  =  r['$'](report,'par')[1]
138
+            rT2 =  r['$'](report,'par')[2]
139
+            #print  report
140
+            #print  r['$'](report,'convergence')
141
+            #print  r['convergence'] #(report,'convergence')
142
+            #print  r['$'](report,'par')[13]
143
+            #print  r['$'](report,'par')[14]
144
+        else:
145
+            print("ERROR DETECTED, regressed values set to default")
146
+            b1 = 1e1
147
+            b2 = 1e-2
148
+            rT2 = 1e-2
149
+            #print r['$'](report,'par')[0]
150
+            #print r['$'](report,'par')[1]
151
+            #print r['$'](report,'par')[2]
152
+        return [b1,b2,rT2] 
153
+    else:
154
+        if not Error:
155
+            rT2 =  r['$'](report,'par')[1]
156
+            b2  =  r['$'](report,'par')[0]
157
+        else:
158
+            print("ERROR DETECTED, regressed values set to default")
159
+        return [b2, rT2] 
160
+
161
+#################################################
162
+# Regress for T2 using rpy2 interface
163
+def regressCurve2(peaks,times,sigma2=[None],intercept=True):
164
+
165
+    if sigma2[0] != None:
166
+        my_weights = robjects.FloatVector( sigma2 )
167
+
168
+    # TODO, if regression fails, it might be because there is no exponential
169
+    # term, maybe do a second regression then on a linear model. 
170
+    b1  = 0                  # Bias
171
+    b2  = 0                  # Linear 
172
+    bb2  = 0                 # Linear 
173
+    rT2 = 0.3                # T2 regressed
174
+    rrT2 = 1.3               # T2 regressed
175
+    r   = robjects.r         
176
+
177
+    # Variable shared between R and Python
178
+    robjects.globalenv['b1'] = b1
179
+    robjects.globalenv['b2'] = b2
180
+    robjects.globalenv['rT2'] = rT2
181
+    
182
+    robjects.globalenv['bb2'] = b2
183
+    robjects.globalenv['rrT2'] = rT2
184
+    
185
+    #robjects.globalenv['sigma2'] = sigma2
186
+    value = robjects.FloatVector(peaks)
187
+    times = robjects.FloatVector(numpy.array(times))
188
+    
189
+    
190
+    if (intercept):
191
+        my_list = robjects.r('list(b1=.50, b2=1e2, rT2=0.03, bb2=1e1, rrT2=1.3)')
192
+        my_lower = robjects.r('list(b1=0, b2=0, rT2=.005, bb2=0, rrT2=.005 )')
193
+        my_upper = robjects.r('list(b1=2000, b2=2000, rT2=.700, bb2=2000, rrT2=1.3 )')
194
+    else:
195
+        my_list  = robjects.r('list(b2=.5, rT2=0.3,  bb2=.5, rrT2=1.3)')
196
+        my_lower = robjects.r('list(b2=0,  rT2=.005, bb2=0,  rrT2=.005)')
197
+        my_upper = robjects.r('list(b2=1,  rT2=2.6,    bb2=1,  rrT2=2.6)')
198
+
199
+    my_cont = robjects.r('nls.control(maxiter=1000, warnOnly=TRUE, printEval=FALSE)')
200
+
201
+    
202
+    if (intercept):
203
+        #fmla = robjects.RFormula('value ~ b1 + exp(-times/rT2)')
204
+        fmla = robjects.Formula('value ~ b1 + b2*exp(-times/rT2) + bb2*exp(-times/rrT2)')
205
+        #fmla = robjects.RFormula('value ~ b1 + b2*times + exp(-times/rT2)')
206
+    else:
207
+        fmla = robjects.Formula('value ~ b2*exp(-times/rT2) + bb2*exp(-times/rrT2)')
208
+
209
+    env = fmla.getenvironment()
210
+    env['value'] = value
211
+    env['times'] = times
212
+    
213
+    # ugly, but I get errors with everything else I've tried
214
+    Error = False
215
+    #fit = robjects.r.nls(fmla,start=my_list,control=my_cont,weights=my_weights)
216
+    if (sigma2[0] != None):
217
+        #print("SIGMA 2")
218
+        #fit = robjects.r.tryCatch(robjects.r.suppressWarnings(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port", \
219
+        #                     weights=my_weights)), 'silent=TRUE')
220
+        fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm='port',weights=my_weights,lower=my_lower,upper=my_upper))#, \
127
                             # weights=my_weights))
221
                             # weights=my_weights))
128
     else:
222
     else:
129
         try:
223
         try:
130
-            fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port",lower=my_lower,upper=my_upper))
224
+            fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port"))#,lower=my_lower,upper=my_upper))
131
         except:
225
         except:
132
             print("regression issue pass")
226
             print("regression issue pass")
133
             Error = True
227
             Error = True
156
             #print r['$'](report,'par')[0]
250
             #print r['$'](report,'par')[0]
157
             #print r['$'](report,'par')[1]
251
             #print r['$'](report,'par')[1]
158
             #print r['$'](report,'par')[2]
252
             #print r['$'](report,'par')[2]
159
-        return [b1,b2,rT2] 
253
+        return [b1,b2,rT2, bb2, rrT2] 
160
     else:
254
     else:
161
         if not Error:
255
         if not Error:
162
             rT2 =  r['$'](report,'par')[1]
256
             rT2 =  r['$'](report,'par')[1]
163
             b2  =  r['$'](report,'par')[0]
257
             b2  =  r['$'](report,'par')[0]
258
+            rrT2 =  r['$'](report,'par')[3]
259
+            bb2  =  r['$'](report,'par')[2]
164
         else:
260
         else:
165
             print("ERROR DETECTED, regressed values set to default")
261
             print("ERROR DETECTED, regressed values set to default")
166
-        return [b2, rT2] 
262
+        return [b2, rT2, bb2, rrT2] 
263
+
264
+def fun(x, t, y):
265
+    """ Cost function for regression, single exponential, no DC term 
266
+        x[0] = A0
267
+        x[1] = zeta 
268
+        x[2] = df
269
+        x[3] = T2
270
+    """
271
+    # concatenated real and imaginary parts  
272
+    pre =  np.concatenate((-x[0]*np.sin(2.*np.pi*x[2]*t + x[1])*np.exp(-t/x[3]), \
273
+                           +x[0]*np.cos(2.*np.pi*x[2]*t + x[1])*np.exp(-t/x[3])))  
274
+    return y-pre
275
+
276
+def fun2(x, t, y):
277
+    """ Cost function for regression, single exponential, no DC term 
278
+        x[0] = A0
279
+        x[1] = zeta 
280
+        x[2] = T2
281
+    """
282
+    # concatenated real and imaginary parts  
283
+    pre =  np.concatenate((x[0]*np.cos(x[1])*np.exp(-t/x[2]), \
284
+                       -1.*x[0]*np.sin(x[1])*np.exp(-t/x[2])))  
285
+    return y-pre
286
+
287
+
288
+def quadratureDetect2(X, Y, tt, x0="None"): 
289
+    """ Pure python quadrature detection using Scipy.  
290
+        X = real part of NMR signal 
291
+        Y = imaginary component of NMR signal 
292
+        tt = time 
293
+    """
294
+    print("Pure Python Quad Det")
295
+    # df 
296
+    if x0=="None":
297
+        x0 = np.array( [1., 0., 0., .2] )
298
+        res_lsq = least_squares(fun, x0, args=(tt, np.concatenate((X, Y))), loss='cauchy', f_scale=1.0,\
299
+            bounds=( [1., -np.pi, -5, .005] , [1000., np.pi, 5, .800] ))
300
+    else:
301
+        res_lsq = least_squares(fun, x0, args=(tt, np.concatenate((X, Y))), loss='cauchy', f_scale=1.0,\
302
+            bounds=( [1., -np.pi, -5, .005] , [1000., np.pi, 5, .800] ))
167
 
303
 
168
-def quadratureDetect(X, Y, tt):
169
-    
170
-    r   = robjects.r         
304
+        #bounds=( [0., 0, -20, .0] , [1., np.pi, 20, .6] ))
171
 
305
 
172
-    robjects.r(''' 
173
-         Xc <- function(E0, df, tt, phi, T2) {
174
-	            E0 * -sin(2*pi*df*tt + phi) * exp(-tt/T2)
175
-                }
306
+    x = res_lsq.x 
307
+    return res_lsq.success, x[0], x[2], x[1], x[3]
308
+    
309
+    # no df
310
+    #x = np.array( [1., 0., 0.2] )
311
+    #res_lsq = least_squares(fun2, x, args=(tt, np.concatenate((X, Y))), loss='soft_l1', f_scale=0.1)
312
+    #x = res_lsq.x 
313
+    #return conv, E0,df,phi,T2
314
+    #return res_lsq.success, x[0], 0, x[1], x[2]
176
 
315
 
177
-         Yc <- function(E0, df, tt, phi, T2) {
178
-	            E0 * cos(2*pi*df*tt + phi) * exp(-tt/T2)
179
-                } 
180
-         ''')  
316
+def quadratureDetect(X, Y, tt, CorrectFreq=False, BiExp=False, CorrectDC=False):
317
+ 
318
+    r   = robjects.r        
319
+
320
+    if CorrectDC:
321
+        robjects.r(''' 
322
+             Xc1 <- function(E01, df, tt, phi, T2_1, DC) {
323
+	                DC + E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1)
324
+            }
325
+    
326
+            Yc1 <- function(E01, df, tt, phi, T2_1, DC) {
327
+	                DC - E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1)
328
+            } 
329
+            ''')
330
+    else:   
331
+        robjects.r(''' 
332
+             Xc1 <- function(E01, df, tt, phi, T2_1) {
333
+	                E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1)
334
+            }
335
+    
336
+            Yc1 <- function(E01, df, tt, phi, T2_1) {
337
+	                -E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1)
338
+            } 
339
+            ''')
340
+
341
+    # bi-exponential 
342
+    if CorrectDC:
343
+        robjects.r(''' 
344
+             Xc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2, DC) {
345
+	               DC + E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
346
+	                DC + E02*cos(2*pi*df*tt + phi) * exp(-tt/T2_2)
347
+            }
348
+
349
+            Yc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2, DC) {
350
+	                DC - E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
351
+	                DC - E02*sin(2*pi*df*tt + phi) * exp(-tt/T2_2)
352
+            } 
353
+            ''')
354
+    else:   
355
+        robjects.r(''' 
356
+             Xc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2) {
357
+	               E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
358
+	               E02*cos(2*pi*df*tt + phi) * exp(-tt/T2_2)
359
+            }
360
+
361
+            Yc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2) {
362
+	                -E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
363
+	                -E02*sin(2*pi*df*tt + phi) * exp(-tt/T2_2)
364
+            } 
365
+            ''')
181
 
366
 
182
     # Make 0 vector 
367
     # Make 0 vector 
183
     Zero = robjects.FloatVector(numpy.zeros(len(X)))
368
     Zero = robjects.FloatVector(numpy.zeros(len(X)))
184
     
369
     
185
     # Fitted Parameters
370
     # Fitted Parameters
186
-    E0 = 0.
371
+    E01 = 0.
372
+    E02 = 0.
187
     df = 0.
373
     df = 0.
188
     phi = 0.
374
     phi = 0.
189
-    T2 = 0.
190
-    robjects.globalenv['E0'] = E0
375
+    T2_1 = 0.
376
+    T2_2 = 0.
377
+    DC = 0.
378
+    robjects.globalenv['DC'] = DC
379
+    robjects.globalenv['E01'] = E01
380
+    robjects.globalenv['E02'] = E02
191
     robjects.globalenv['df'] = df
381
     robjects.globalenv['df'] = df
192
     robjects.globalenv['phi'] = phi
382
     robjects.globalenv['phi'] = phi
193
-    robjects.globalenv['T2'] = T2
383
+    robjects.globalenv['T2_1'] = T2_1
384
+    robjects.globalenv['T2_2'] = T2_2
194
     XY = robjects.FloatVector(numpy.concatenate((X,Y)))
385
     XY = robjects.FloatVector(numpy.concatenate((X,Y)))
195
     
386
     
196
     # Arrays
387
     # Arrays
199
     Y = robjects.FloatVector(numpy.array(Y))
390
     Y = robjects.FloatVector(numpy.array(Y))
200
     Zero = robjects.FloatVector(numpy.array(Zero))
391
     Zero = robjects.FloatVector(numpy.array(Zero))
201
 
392
 
202
-    #fmla = robjects.Formula('Zero ~ QI( E0, df, tt, phi, T2, X, Y )')
203
-    #fmla = robjects.Formula('X ~ Xc( E0, df, tt, phi, T2 )')
204
-    #fmla = robjects.Formula('Y ~ Yc( E0, df, tt, phi, T2 )')
205
-    fmla = robjects.Formula('XY ~ c(Xc( E0, df, tt, phi, T2 ), Yc( E0, df, tt, phi, T2 ))')
393
+    
394
+
395
+    if BiExp:
396
+        if CorrectDC:
397
+            fmla = robjects.Formula('XY ~ c(Xc2( E01, E02, df, tt, phi, T2_1, T2_2, DC ), Yc2( E01, E02, df, tt, phi, T2_1, T2_2, DC ))')
398
+            if CorrectFreq:    
399
+                start = robjects.r('list(E01=.100, E02=.01,   df=0,    phi=0.    ,  T2_1=.100, T2_2=.01, DC=0.0)')
400
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  df=-50,  phi=-3.14 ,  T2_1=.001, T2_2=.001, DC=0.0)')
401
+                upper = robjects.r('list(E01=1.00, E02=1.0,   df=50,   phi=3.14  ,  T2_1=.800, T2_2=.8, DC=0.5)')
402
+            else:
403
+                start = robjects.r('list(E01=.100, E02=.01,   phi=0.9   ,  T2_1=.100, T2_2=.01,  DC=0.0)')
404
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  phi=-3.14 ,  T2_1=.001, T2_2=.001, DC=0.0)')
405
+                upper = robjects.r('list(E01=1.00, E02=1.0,   phi=3.14  ,  T2_1=.800, T2_2=.8,   DC=0.5)')
406
+        else:
407
+            fmla = robjects.Formula('XY ~ c(Xc2( E01, E02, df, tt, phi, T2_1, T2_2 ), Yc2( E01, E02, df, tt, phi, T2_1, T2_2))')
408
+            if CorrectFreq:    
409
+                start = robjects.r('list(E01=.100, E02=.01,   df=0,    phi=0.    ,  T2_1=.100, T2_2=.01)')
410
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  df=-50,  phi=-3.14 ,  T2_1=.001, T2_2=.001)')
411
+                upper = robjects.r('list(E01=1.00, E02=1.0,   df=50,   phi=3.14  ,  T2_1=.800, T2_2=.8)')
412
+            else:
413
+                start = robjects.r('list(E01=.100, E02=.01,   phi=0.9   ,  T2_1=.100, T2_2=.01)')
414
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  phi=-3.14 ,  T2_1=.001, T2_2=.001)')
415
+                upper = robjects.r('list(E01=1.00, E02=1.0,   phi=3.14  ,  T2_1=.800, T2_2=.8)')
416
+    else: 
417
+        if CorrectDC:
418
+            fmla = robjects.Formula('XY ~ c(Xc1( E01, df, tt, phi, T2_1, DC), Yc1( E01, df, tt, phi, T2_1,DC))')
419
+            if CorrectFreq:    
420
+                start = robjects.r('list(E01=.100, df=0   , phi=0.   , T2_1=.100, DC=0.0)')
421
+                lower = robjects.r('list(E01=1e-6, df=-50., phi=-3.14, T2_1=.001, DC=0.0)')
422
+                upper = robjects.r('list(E01=1.00, df=50. , phi=3.14 , T2_1=.800, DC=0.5)')
423
+            else:
424
+                start = robjects.r('list(E01=.100, phi= 0.  , T2_1=.100, DC=0.0)')
425
+                lower = robjects.r('list(E01=1e-6, phi=-3.13, T2_1=.001, DC=0.0)')
426
+                upper = robjects.r('list(E01=1.00, phi= 3.13, T2_1=.800, DC=0.5)')
427
+        else:
428
+            fmla = robjects.Formula('XY ~ c(Xc1( E01, df, tt, phi, T2_1), Yc1( E01, df, tt, phi, T2_1))')
429
+            if CorrectFreq:    
430
+                start = robjects.r('list(E01=.100, df=0     , phi=0.   ,  T2_1=.100)')
431
+                lower = robjects.r('list(E01=1e-6, df=-50. , phi=-3.14 ,  T2_1=.001)')
432
+                upper = robjects.r('list(E01=1.00, df=50.  , phi=3.14  ,  T2_1=.800)')
433
+            else:
434
+                start = robjects.r('list(E01=.100, phi= 0.  , T2_1=.100)')
435
+                lower = robjects.r('list(E01=1e-6, phi=-3.13, T2_1=.001)')
436
+                upper = robjects.r('list(E01=1.00, phi= 3.13, T2_1=.800)')
206
 
437
 
207
     env = fmla.getenvironment()
438
     env = fmla.getenvironment()
208
     env['Zero'] = Zero
439
     env['Zero'] = Zero
210
     env['Y'] = Y
441
     env['Y'] = Y
211
     env['XY'] = XY 
442
     env['XY'] = XY 
212
     env['tt'] = tt
443
     env['tt'] = tt
213
-    
214
-    # Bounds and control    
215
-    start = robjects.r('list(E0=100,   df= 0   , phi=   0.00,  T2=.100)')
216
-    lower = robjects.r('list(E0=1,     df=-13.0, phi=  -3.14,  T2=.005)')
217
-    upper = robjects.r('list(E0=1000,  df= 13.0, phi=   3.14,  T2=.800)')
218
 
444
 
219
     cont = robjects.r('nls.control(maxiter=10000, warnOnly=TRUE, printEval=FALSE)')
445
     cont = robjects.r('nls.control(maxiter=10000, warnOnly=TRUE, printEval=FALSE)')
220
     
446
     
221
     fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=start, control=cont, lower=lower, upper=upper, algorithm='port')) #, \
447
     fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=start, control=cont, lower=lower, upper=upper, algorithm='port')) #, \
448
+    #fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=start, control=cont)) #, \
222
     report =  r.summary(fit)
449
     report =  r.summary(fit)
223
-    #print (report)
224
-    
225
-    E0  =  r['$'](report,'par')[0]
226
-    df  =  r['$'](report,'par')[1]
227
-    phi =  r['$'](report,'par')[2]
228
-    T2  =  r['$'](report,'par')[3]
229
-    #print ( E0,df,phi,T2 )
230
-    return E0,df,phi,T2
231
-    
232
-
233
-#################################################
234
-# Regress for T2 using rpy2 interface
235
-def regressSpec(w, wL, X): #,sigma2=1,intercept=True):
236
 
450
 
237
-    # compute s
238
-    s = -1j*w
239
-
240
-    # TODO, if regression fails, it might be because there is no exponential
241
-    # term, maybe do a second regression then on a linear model. 
242
-    a   = 0                  # Linear 
243
-    rT2 = 0.1                # T2 regressed
244
-    r   = robjects.r         
245
-
246
-    # Variable shared between R and Python
247
-    robjects.globalenv['a'] = a
248
-    #robjects.globalenv['w'] = w
249
-    robjects.globalenv['rT2'] = rT2
250
-    robjects.globalenv['wL'] = wL
251
-    robjects.globalenv['nb'] = 0
252
-
253
-    #s = robjects.ComplexVector(numpy.array(s))
254
-    w = robjects.FloatVector(numpy.array(w))
255
-    XX = robjects.FloatVector(X)
256
-    #Xr = robjects.FloatVector(numpy.real(X))
257
-    #Xi = robjects.FloatVector(numpy.imag(X))
258
-    #Xa = robjects.FloatVector(numpy.abs(X))
259
-    #Xri = robjects.FloatVector(numpy.concatenate((Xr,Xi)))
260
-    
261
-    #my_lower = robjects.r('list(a=.001, rT2=.001, nb=.0001)')
262
-    my_lower = robjects.r('list(a=.001, rT2=.001)')
263
-    #my_upper = robjects.r('list(a=1.5, rT2=.300, nb =100.)')
264
-    my_upper = robjects.r('list(a=1.5, rT2=.300)')
265
-     
266
-    #my_list = robjects.r('list(a=.2, rT2=0.03, nb=.1)')
267
-    my_list = robjects.r('list(a=.2, rT2=0.03)')
268
-    my_cont = robjects.r('nls.control(maxiter=5000, warnOnly=TRUE, printEval=FALSE)')
269
-    
270
-    #fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
271
-    ##fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
272
-    #fmla = robjects.Formula('XX ~ a*(wL) / (wL^2 + (s+1/rT2)^2 )') # complex
273
-    #fmla = robjects.Formula('Xa ~ abs(a*(wL) / (wL^2 + (s+1/rT2)^2 )) + nb') # complex
274
-    #fmla = robjects.Formula('XX ~ Re(a*( s + 1./rT2 )  / (wL^2 + (s+1/rT2)^2 ))') # complex
275
-    fmla = robjects.Formula('XX ~ a*(.5/rT2)  / ((1./rT2)^2 + (w-wL)^2 )')
276
-    #fmla = robjects.Formula('Xa ~ (s + 1./T2) / ( wL**2 + (1/T2 + 1j*w)**2 ) ')
277
- 
278
-    env = fmla.getenvironment()
279
-    #env['s'] = s
280
-    env['w'] = w
281
-    #env['Xr'] = Xr
282
-    #env['Xa'] = Xa
283
-    #env['Xi'] = Xi
284
-    #env['Xri'] = Xri
285
-    env['XX'] = XX
286
-     
287
-    #fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list, control=my_cont)) #, lower=my_lower, algorithm='port')) #, \
288
-    fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
289
-    report =  r.summary(fit)
290
-    #print report 
291
-    #print(r.warnings())
292
- 
293
-    a  =  r['$'](report,'par')[0]
294
-    rT2 =  r['$'](report,'par')[1]
295
-    nb =  r['$'](report,'par')[2]
451
+    conv = r['$'](fit,'convergence')[0]
452
+    #if conv:
453
+    #    print (report)
454
+    #    print ("conv", conv)
455
+    print ("Conv",  r['$'](fit,'convergence'))  # T2
456
+    print (report)
457
+    
458
+    if BiExp:
459
+        if CorrectFreq:    
460
+            E0   =  r['$'](report,'par')[0]   # E01
461
+            E0  +=  r['$'](report,'par')[1]   # E02
462
+            df  =  r['$'](report,'par')[2]   # offset
463
+            phi =  r['$'](report,'par')[3]   # phase 
464
+            T2  =  r['$'](report,'par')[4]   # T2
465
+        else:
466
+            E0   =  r['$'](report,'par')[0]   # E01
467
+            E0  +=  r['$'](report,'par')[1]   # E02
468
+            phi =  r['$'](report,'par')[2]   # phase 
469
+            T2  =  r['$'](report,'par')[3]   # T2
470
+    else:
471
+        if CorrectFreq:    
472
+            E0   =  r['$'](report,'par')[0]   # E01
473
+            df  =  r['$'](report,'par')[1]   # offset
474
+            phi =  r['$'](report,'par')[2]   # phase 
475
+            T2  =  r['$'](report,'par')[3]   # T2
476
+        else:
477
+            E0   =  r['$'](report,'par')[0]   # E01
478
+            phi =  r['$'](report,'par')[1]   # phase 
479
+            T2  =  r['$'](report,'par')[2]   # T2
480
+    #phi = 0.907655876627
481
+    #phi = 0
482
+    #print ("df", df)# = 0
483
+    return conv, E0,df,phi,T2
296
     
484
     
297
-    return a, rT2, nb
298
 
485
 
299
 #################################################
486
 #################################################
300
 # Regress for T2 using rpy2 interface
487
 # Regress for T2 using rpy2 interface
301
-def regressModulus(w, wL, X): #,sigma2=1,intercept=True):
488
+def regressSpec(w, wL, X): #,sigma2=1,intercept=True):
302
 
489
 
303
     # compute s
490
     # compute s
304
     s = -1j*w
491
     s = -1j*w
355
     rT2 =  r['$'](report,'par')[1]
542
     rT2 =  r['$'](report,'par')[1]
356
     nb =  r['$'](report,'par')[2]
543
     nb =  r['$'](report,'par')[2]
357
     
544
     
358
-    return a, rT2
545
+    return a, rT2, nb
359
 
546
 
360
 #################################################
547
 #################################################
361
 # Regress for T2 using rpy2 interface
548
 # Regress for T2 using rpy2 interface
362
-def regressSpecComplex(w, wL, X, known=True, win=None): #,sigma2=1,intercept=True):
549
+def regressSpecComplex(w, wL, X): #,sigma2=1,intercept=True):
363
 
550
 
364
     # compute s
551
     # compute s
365
     s = -1j*w
552
     s = -1j*w
394
      
581
      
395
     #print (numpy.shape(X))
582
     #print (numpy.shape(X))
396
     
583
     
397
-    #######################################################################
398
-
399
-    if known:
400
-        # known Frequency
401
-        my_lower = robjects.r('list(a=.001, rT2=.001, phi2=-3.14)')
402
-        my_upper = robjects.r('list(a=3.5, rT2=.300, phi2=3.14)')
403
-        my_list = robjects.r('list(a=.2, rT2=0.03, phi2=0)')
404
-    else:
405
-        # Unknown Frequency
406
-        my_lower = robjects.r('list(a=.001, rT2=.001, phi2=-3.14, wL2=wL-5)')
407
-        my_upper = robjects.r('list(a=3.5, rT2=.300, phi2=3.14, wL2=wL+5)')
408
-        my_list = robjects.r('list(a=.2, rT2=0.03, phi2=0, wL2=wL)')
409
-    
584
+    #my_lower = robjects.r('list(a=.001, rT2=.001, nb=.0001)')
585
+    #my_lower = robjects.r('list(a=.001, rT2=.001)') # Working
586
+    my_lower = robjects.r('list(a=.001, rT2=.001, phi2=-3.14, wL2=wL-5)')
587
+    #my_upper = robjects.r('list(a=1.5, rT2=.300, nb =100.)')
588
+    my_upper = robjects.r('list(a=3.5, rT2=.300, phi2=3.14, wL2=wL+5)')
589
+     
590
+    #my_list = robjects.r('list(a=.2, rT2=0.03, nb=.1)')
591
+    my_list = robjects.r('list(a=.2, rT2=0.03, phi2=0, wL2=wL)')
410
     my_cont = robjects.r('nls.control(maxiter=5000, warnOnly=TRUE, printEval=FALSE)')
592
     my_cont = robjects.r('nls.control(maxiter=5000, warnOnly=TRUE, printEval=FALSE)')
411
     
593
     
412
     #fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
594
     #fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
415
     
597
     
416
     #fmlar = robjects.Formula('Xr ~ (Kwr(a, phi2, s, rT2, wL)) ') # envelope
598
     #fmlar = robjects.Formula('Xr ~ (Kwr(a, phi2, s, rT2, wL)) ') # envelope
417
     #fmlai = robjects.Formula('Xi ~ (Kwi(a, phi2, s, rT2, wL)) ') # envelope
599
     #fmlai = robjects.Formula('Xi ~ (Kwi(a, phi2, s, rT2, wL)) ') # envelope
418
-    
419
-
420
-    
421
-    if known:
422
-        ###########################################3
423
-        # KNOWN freq 
424
-        fmla = robjects.Formula('Xri ~ c(Kwr(a, phi2, s, rT2, wL), Kwi(a, phi2, s, rT2, wL) ) ') # envelope
425
-    else:
426
-        ####################################################################################################3
427
-        # unknown frequency
428
-        fmla = robjects.Formula('Xri ~ c(Kwr(a, phi2, s, rT2, wL2), Kwi(a, phi2, s, rT2, wL2) ) ') # envelope
429
-
600
+    fmla = robjects.Formula('Xri ~ c(Kwr(a, phi2, s, rT2, wL2), Kwi(a, phi2, s, rT2, wL2) ) ') # envelope
430
     #fmla = robjects.Formula('Xri ~ (Kwri(a, phi2, s, rT2, wL)) ') # envelope
601
     #fmla = robjects.Formula('Xri ~ (Kwri(a, phi2, s, rT2, wL)) ') # envelope
431
     
602
     
432
     #fmla = robjects.Formula('Xa ~ (abs(a*(sin(phi2)*s + ((1/rT2)*sin(phi2)) + wL*cos(phi2)) / (wL^2+(s+1/rT2)^2 )))') # envelope
603
     #fmla = robjects.Formula('Xa ~ (abs(a*(sin(phi2)*s + ((1/rT2)*sin(phi2)) + wL*cos(phi2)) / (wL^2+(s+1/rT2)^2 )))') # envelope
451
     env['Xri'] = Xri
622
     env['Xri'] = Xri
452
     env['XX'] = XX
623
     env['XX'] = XX
453
      
624
      
454
-    #fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list, control=my_cont)) #, lower=my_lower, algorithm='port')) #, \
455
-    #fit = robjects.r.tryCatch(robjects.r.nls(fmlar, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
456
-    fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
625
+    fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list, control=my_cont)) #, lower=my_lower, algorithm='port')) #, \
626
+    #fitr = robjects.r.tryCatch(robjects.r.nls(fmlar, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
457
     
627
     
458
     #env = fmlai.getenvironment()
628
     #env = fmlai.getenvironment()
459
     #fiti = robjects.r.tryCatch(robjects.r.nls(fmlai, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
629
     #fiti = robjects.r.tryCatch(robjects.r.nls(fmlai, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
466
     #print( reportr )
636
     #print( reportr )
467
     #print( reporti  )
637
     #print( reporti  )
468
     #exit()
638
     #exit()
469
-    #print ( r.warnings())
639
+    #print  r.warnings()
470
  
640
  
471
     #a   =  (r['$'](reportr,'par')[0] + r['$'](reporti,'par')[0]) / 2.
641
     #a   =  (r['$'](reportr,'par')[0] + r['$'](reporti,'par')[0]) / 2.
472
     #rT2 =  (r['$'](reportr,'par')[1] + r['$'](reporti,'par')[1]) / 2.
642
     #rT2 =  (r['$'](reportr,'par')[1] + r['$'](reporti,'par')[1]) / 2.
475
     rT2 =  r['$'](report,'par')[1] 
645
     rT2 =  r['$'](report,'par')[1] 
476
     nb  =  r['$'](report,'par')[2] #phi2 
646
     nb  =  r['$'](report,'par')[2] #phi2 
477
 
647
 
478
-    #print ("Python wL2", r['$'](report,'par')[3] )   
479
-    #print ("Python zeta", r['$'](report,'par')[2] )   
648
+    print ("Python wL2", r['$'](report,'par')[3] )   
649
+    print ("Python zeta", r['$'](report,'par')[2] )   
480
  
650
  
481
     return a, rT2, nb
651
     return a, rT2, nb
482
 
652
 
517
     envelope   =  numpy.exp(-t/T2)
687
     envelope   =  numpy.exp(-t/T2)
518
     renvelope  =  numpy.exp(-t/rT2)
688
     renvelope  =  numpy.exp(-t/rT2)
519
 
689
 
520
-    outf = file('regress.txt','w')
521
-    for i in range(len(times)):
522
-        outf.write(str(times[i]) + "   " +  str(peaks[i]) + "\n")  
523
-    outf.close()
690
+    #outf = file('regress.txt','w')
691
+    #for i in range(len(times)):
692
+    #    outf.write(str(times[i]) + "   " +  str(peaks[i]) + "\n")  
693
+    #outf.close()
524
 
694
 
525
-    pylab.plot(t,data, 'b')
526
-    pylab.plot(t,cdata, 'g', linewidth=1)
527
-    pylab.plot(t,envelope, color='violet', linewidth=4)
528
-    pylab.plot(t,renvelope, 'r', linewidth=4)
529
-    pylab.plot(times, numpy.array(peaks), 'bo', markersize=8, alpha=.25)
530
-    pylab.legend(['noisy data','clean data','real envelope','regressed env','picks'])
531
-    pylab.savefig("regression.pdf")
695
+    plt.plot(t,data, 'b')
696
+    plt.plot(t,cdata, 'g', linewidth=1)
697
+    plt.plot(t,envelope, color='violet', linewidth=4)
698
+    plt.plot(t,renvelope, 'r', linewidth=4)
699
+    plt.plot(times, numpy.array(peaks), 'bo', markersize=8, alpha=.25)
700
+    plt.legend(['noisy data','clean data','real envelope','regressed env','picks'])
701
+    plt.savefig("regression.pdf")
532
 
702
 
533
 
703
 
534
     # FFT check
704
     # FFT check
535
     fourier = fft(data)
705
     fourier = fft(data)
536
-    pylab.figure()
706
+    plt.figure()
537
     freq = fftfreq(len(data), d=dt)
707
     freq = fftfreq(len(data), d=dt)
538
-    pylab.plot(freq, (fourier.real))
708
+    plt.plot(freq, (fourier.real))
539
     
709
     
540
-    pylab.show()
710
+    plt.show()
541
 
711
 
542
     # TODO do a bunch in batch mode to see if T2 estimate is better with or without 
712
     # TODO do a bunch in batch mode to see if T2 estimate is better with or without 
543
     # weighting and which model is best.
713
     # weighting and which model is best.

+ 211
- 117
akvo/tressel/mrsurvey.py View File

5
 import sys
5
 import sys
6
 import scipy
6
 import scipy
7
 import copy
7
 import copy
8
+import struct
8
 from scipy.io.matlab import mio
9
 from scipy.io.matlab import mio
9
 from numpy import pi
10
 from numpy import pi
10
 from math import floor
11
 from math import floor
15
 import matplotlib.ticker 
16
 import matplotlib.ticker 
16
 from matplotlib.ticker import MaxNLocator
17
 from matplotlib.ticker import MaxNLocator
17
 
18
 
19
+import multiprocessing 
20
+import itertools 
21
+
18
 import akvo.tressel.adapt as adapt
22
 import akvo.tressel.adapt as adapt
19
 #import akvo.tressel.cadapt as adapt # cython for more faster
23
 #import akvo.tressel.cadapt as adapt # cython for more faster
20
 import akvo.tressel.decay as decay
24
 import akvo.tressel.decay as decay
30
 plt.register_cmap(name='magma', cmap=cmaps.magma)
34
 plt.register_cmap(name='magma', cmap=cmaps.magma)
31
 plt.register_cmap(name='magma_r', cmap=cmaps.magma_r)
35
 plt.register_cmap(name='magma_r', cmap=cmaps.magma_r)
32
 
36
 
37
+
38
+def loadGMRBinaryFID( rawfname, istack, info ):
39
+    """ Reads a single binary GMR file and fills into DATADICT
40
+    """
41
+
42
+    #################################################################################
43
+    # figure out key data indices
44
+    # Pulse        
45
+    nps  = (int)((info["prePulseDelay"])*info["samp"])
46
+    npul   = (int)(self.pulseLength[0]*self.samp) #+ 100 
47
+
48
+    # Data 
49
+    nds  = nps+npul+(int)((self.deadTime)*self.samp);        # indice pulse 1 data starts 
50
+    nd1 = (int)(1.*self.samp)                                # samples in first pulse
51
+
52
+    invGain = 1./self.RxGain        
53
+    invCGain = self.CurrentGain        
54
+
55
+    pulse = "Pulse 1"
56
+    chan = self.DATADICT[pulse]["chan"] 
57
+    rchan = self.DATADICT[pulse]["rchan"] 
58
+        
59
+    rawFile = open( rawfname, 'rb')
60
+        
61
+    T = N_samp * self.dt 
62
+    TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
63
+
64
+    for ipm in range(self.nPulseMoments):
65
+        buf1 = rawFile.read(4)
66
+        buf2 = rawFile.read(4)
67
+                
68
+        N_chan = struct.unpack('>i', buf1 )[0]
69
+        N_samp = struct.unpack('>i', buf2 )[0]
70
+
71
+        DATA = np.zeros([N_samp, N_chan+1])
72
+        for ichan in range(N_chan):
73
+            DATADUMP = rawFile.read(4*N_samp)
74
+            for irec in range(N_samp):
75
+                DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
76
+        
77
+    return DATA, TIMES
78
+
33
 class SNMRDataProcessor(QObject):
79
 class SNMRDataProcessor(QObject):
34
     """ Revised class for preprocessing sNMR Data. 
80
     """ Revised class for preprocessing sNMR Data. 
35
         Derived types can read GMR files  
81
         Derived types can read GMR files  
183
         self.transFreq       = HEADER[1]
229
         self.transFreq       = HEADER[1]
184
         self.maxBusV         = HEADER[2]
230
         self.maxBusV         = HEADER[2]
185
         self.pulseLength     = pulseLengthDict.get((int)(HEADER[0]))(1e-3*HEADER[3])
231
         self.pulseLength     = pulseLengthDict.get((int)(HEADER[0]))(1e-3*HEADER[3])
186
-        self.interpulseDelay = 1e-3*HEADER[4]   # for T2, Spin Echo
187
-        self.repetitionDelay = HEADER[5]        # delay between first pulse
188
-        self.nPulseMoments   = (int)(HEADER[6]) # Number of pulse moments per stack
189
-        self.TuneCapacitance = HEADER[7]        # tuning capacitance in uF
190
-        self.nTransVersion   = HEADER[8]        # Transmitter version
191
-        self.nDAQVersion     = HEADER[9]        # DAQ software version 
192
-        self.nInterleaves    = HEADER[10]       # num interleaves
232
+        self.interpulseDelay = 1e-3*HEADER[4]      # for T2, Spin Echo
233
+        self.repetitionDelay = HEADER[5]           # delay between first pulse
234
+        self.nPulseMoments   = (int)(HEADER[6])    # Number of pulse moments per stack
235
+        self.TuneCapacitance = HEADER[7]           # tuning capacitance in uF
236
+        self.nTransVersion   = HEADER[8]           # Transmitter version
237
+        self.nDAQVersion     = HEADER[9]           # DAQ software version 
238
+        self.nInterleaves    = HEADER[10]          # num interleaves
239
+
193
         self.gain()
240
         self.gain()
194
         
241
         
195
         # default 
242
         # default 
198
 
245
 
199
         # newer header files contain 64 entries
246
         # newer header files contain 64 entries
200
         if self.nDAQVersion >= 2:
247
         if self.nDAQVersion >= 2:
201
-            #print "new file format"
248
+           #self.deadtime       = HEADER[11]
249
+           #self.unknown        = HEADER[12]
250
+           #self.PreAmpGain     = HEADER[13]
202
             self.samp           = HEADER[14]     # sampling frequency
251
             self.samp           = HEADER[14]     # sampling frequency
203
             self.dt             = 1./self.samp   # sampling rate 
252
             self.dt             = 1./self.samp   # sampling rate 
204
             self.deadTime       = .0055          # instrument dead time before measurement
253
             self.deadTime       = .0055          # instrument dead time before measurement
241
         # Current gain
290
         # Current gain
242
         if floor(self.nDAQVersion) == 1:
291
         if floor(self.nDAQVersion) == 1:
243
             self.CurrentGain = 150.
292
             self.CurrentGain = 150.
244
-        elif floor(self.nDAQVersion) ==2:
293
+        elif floor(self.nDAQVersion) == 2:
245
             self.CurrentGain = 180.
294
             self.CurrentGain = 180.
246
 
295
 
247
     def updateProgress(self):
296
     def updateProgress(self):
524
         NRmax = {}
573
         NRmax = {}
525
         REmax = {}
574
         REmax = {}
526
         IMmax = {}
575
         IMmax = {}
527
-
576
+        E0,phi,df,T2 = 100.,0,0,.2
528
         first = False
577
         first = False
529
         self.sigma = {}
578
         self.sigma = {}
530
         for pulse in self.DATADICT["PULSES"]:
579
         for pulse in self.DATADICT["PULSES"]:
560
                     IP[pulse][chan][ipm,:] = np.angle(ht)[clip::]
609
                     IP[pulse][chan][ipm,:] = np.angle(ht)[clip::]
561
                     #############################################################
610
                     #############################################################
562
                     # Rotated amplitude
611
                     # Rotated amplitude
563
-                    [E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
612
+                    #if ipm != 0:
613
+                    [success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"], (E0,phi,df,T2))
614
+                    #[ E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
615
+                    #else:
616
+                    #    [success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"])
617
+                    #[success, E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
618
+                    print("success", success, "E0", E0, "phi", phi, "df", df, "T2", T2)
564
                     D = self.RotateAmplitude( ht.real, ht.imag, phi, df, self.DATADICT[pulse]["TIMES"] )
619
                     D = self.RotateAmplitude( ht.real, ht.imag, phi, df, self.DATADICT[pulse]["TIMES"] )
565
                     CA[pulse][chan][ipm,:] = D.imag[clip::]  # amplitude data 
620
                     CA[pulse][chan][ipm,:] = D.imag[clip::]  # amplitude data 
566
                     NR[pulse][chan][ipm,:] = D.real[clip::]  # noise data
621
                     NR[pulse][chan][ipm,:] = D.real[clip::]  # noise data
711
                 for ipm in range(0, self.DATADICT["nPulseMoments"]):
766
                 for ipm in range(0, self.DATADICT["nPulseMoments"]):
712
 
767
 
713
                     # Time since pulse rather than since record starts...
768
                     # Time since pulse rather than since record starts...
714
-                    if clip > 0:
715
-                        time_sp =  1e3 * (self.DATADICT[pulse]["TIMES"][clip-1::] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
716
-                    else:
717
-                        time_sp =  1e3 * (self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
769
+                    #if clip > 0:
770
+                    #    time_sp =  1e3 * (self.DATADICT[pulse]["TIMES"][clip:] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
771
+                    #else:
772
+                    time_sp =  1e3 * (self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
718
 
773
 
719
                     #GT, GD, GTT, sig_stack, isum      = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
774
                     #GT, GD, GTT, sig_stack, isum      = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
720
                     #GT2, GP, GTT, sig_stack_err, isum = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 ) 
775
                     #GT2, GP, GTT, sig_stack_err, isum = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 ) 
721
                     
776
                     
722
-                    GT, GCA, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
723
-                    GT, GNR, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
724
-                    GT, GRE, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["RE"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
725
-                    GT, GIM, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["IM"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
726
-                    GT, GIP, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["IP"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
777
+                    GT, GCA, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
778
+                    GT, GNR, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
779
+                    GT, GRE, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["RE"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
780
+                    GT, GIM, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["IM"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
781
+                    GT, GIP, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["IP"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
727
                     
782
                     
728
                     if ipm == 0:
783
                     if ipm == 0:
729
                     #    self.GATED[chan]["DATA"]  = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
784
                     #    self.GATED[chan]["DATA"]  = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
730
                     #    self.GATED[chan]["ERR"]   = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
785
                     #    self.GATED[chan]["ERR"]   = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
731
                     #    self.GATED[chan]["SIGMA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
786
                     #    self.GATED[chan]["SIGMA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
732
-                        self.GATED[chan]["CA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
733
-                        self.GATED[chan]["NR"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
734
-                        self.GATED[chan]["RE"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
735
-                        self.GATED[chan]["IM"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
736
-                        self.GATED[chan]["IP"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
787
+                        self.GATED[chan]["CA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
788
+                        self.GATED[chan]["NR"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
789
+                        self.GATED[chan]["RE"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
790
+                        self.GATED[chan]["IM"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
791
+                        self.GATED[chan]["IP"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
737
                         self.GATED[chan]["isum"] = isum
792
                         self.GATED[chan]["isum"] = isum
738
 
793
 
739
                     #self.GATED[chan]["DATA"][ipm] = GD.real
794
                     #self.GATED[chan]["DATA"][ipm] = GD.real
740
-                    self.GATEDABSCISSA = GT
741
-                    self.GATEDWINDOW = GTT
795
+                    self.GATEDABSCISSA = GT[clip:]
796
+                    self.GATEDWINDOW = GTT[clip:]
742
                     #self.GATED[chan]["SIGMA"][ipm] =  sig_stack #_err # GP.real
797
                     #self.GATED[chan]["SIGMA"][ipm] =  sig_stack #_err # GP.real
743
                     #self.GATED[chan]["ERR"][ipm] =  GP.real
798
                     #self.GATED[chan]["ERR"][ipm] =  GP.real
744
                     
799
                     
745
-                    self.GATED[chan]["CA"][ipm] = GCA.real
746
-                    self.GATED[chan]["NR"][ipm] = GNR.real
747
-                    self.GATED[chan]["RE"][ipm] = GRE.real
748
-                    self.GATED[chan]["IM"][ipm] = GIM.real
749
-                    self.GATED[chan]["IP"][ipm] = GIP.real
800
+                    self.GATED[chan]["CA"][ipm] = GCA.real[clip:]
801
+                    self.GATED[chan]["NR"][ipm] = GNR.real[clip:]
802
+                    self.GATED[chan]["RE"][ipm] = GRE.real[clip:]
803
+                    self.GATED[chan]["IM"][ipm] = GIM.real[clip:]
804
+                    self.GATED[chan]["IP"][ipm] = GIP.real[clip:]
750
                     
805
                     
751
                     percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) / 
806
                     percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) / 
752
                                        (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
807
                                        (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
753
                     self.progressTrigger.emit(percent)
808
                     self.progressTrigger.emit(percent)
754
 
809
 
755
-                self.GATED[chan]["GTT"] = GTT
756
-                self.GATED[chan]["GT"] = GT
810
+                self.GATED[chan]["GTT"] = GTT[clip:]
811
+                self.GATED[chan]["GT"] = GT[clip:]
757
                 self.GATED[chan]["QQ"] = QQ
812
                 self.GATED[chan]["QQ"] = QQ
758
                 ichan += 1
813
                 ichan += 1
759
         self.doneTrigger.emit() 
814
         self.doneTrigger.emit() 
1754
         self.doneTrigger.emit() 
1809
         self.doneTrigger.emit() 
1755
         self.updateProcTrigger.emit()  
1810
         self.updateProcTrigger.emit()  
1756
 
1811
 
1812
+    def loadGMRBinaryFID( self, rawfname, istack ):
1813
+        """ Reads a single binary GMR file and fills into DATADICT
1814
+        """
1815
+
1816
+        #################################################################################
1817
+        # figure out key data indices
1818
+        # Pulse        
1819
+        nps  = (int)((self.prePulseDelay)*self.samp)
1820
+        npul   = (int)(self.pulseLength[0]*self.samp) #+ 100 
1821
+
1822
+        # Data 
1823
+        nds  = nps+npul+(int)((self.deadTime)*self.samp);        # indice pulse 1 data starts 
1824
+        nd1 = (int)(1.*self.samp)                                # samples in first pulse
1825
+
1826
+        invGain = 1./self.RxGain        
1827
+        invCGain = self.CurrentGain        
1828
+
1829
+        pulse = "Pulse 1"
1830
+        chan = self.DATADICT[pulse]["chan"] 
1831
+        rchan = self.DATADICT[pulse]["rchan"] 
1832
+        
1833
+        rawFile = open( rawfname, 'rb')
1834
+
1835
+        for ipm in range(self.nPulseMoments):
1836
+            buf1 = rawFile.read(4)
1837
+            buf2 = rawFile.read(4)
1838
+                
1839
+            N_chan = struct.unpack('>i', buf1 )[0]
1840
+            N_samp = struct.unpack('>i', buf2 )[0]
1841
+ 
1842
+            T = N_samp * self.dt 
1843
+            TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
1844
+
1845
+            DATA = np.zeros([N_samp, N_chan+1])
1846
+            for ichan in range(N_chan):
1847
+                DATADUMP = rawFile.read(4*N_samp)
1848
+                for irec in range(N_samp):
1849
+                    DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
1850
+                           
1851
+            # Save into Data Cube 
1852
+            for ichan in chan:
1853
+                self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain 
1854
+                self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1855
+                self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
1856
+                self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul] 
1857
+
1858
+            # reference channels?
1859
+            for ichan in rchan:
1860
+                self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain 
1861
+                self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1862
+    
1863
+    def loadGMRASCIIFID( self, rawfname, istack ):
1864
+        """Based on the geoMRI instrument manufactured by VistaClara. Imports 
1865
+        a suite of raw .lvm files with the following format (on one line)
1866
+
1867
+        time(s) DC_Bus/100(V) Current+/75(A)  Curr-/75(A)  Voltage+/200(V) \  
1868
+        Ch1(V) Ch2(V) Ch3(V) Ch4(V)
1869
+
1870
+        Sampling rate is assumed at 50 kHz 
1871
+        """
1872
+        import pandas as pd 
1873
+        #################################################################################
1874
+        # figure out key data indices
1875
+        # Pulse        
1876
+        nps  = (int)((self.prePulseDelay)*self.samp)
1877
+        npul   = (int)(self.pulseLength[0]*self.samp) #+ 100 
1878
+
1879
+        # Data 
1880
+        nds  = nps+npul+(int)((self.deadTime)*self.samp);        # indice pulse 1 data starts 
1881
+        nd1 = (int)(1.*self.samp) - nds                          # samples in first pulse
1882
+        ndr = (int)(1.*self.samp)                                # samples in record 
1883
+
1884
+        invGain = 1./self.RxGain        
1885
+        invCGain = self.CurrentGain        
1886
+
1887
+        pulse = "Pulse 1"
1888
+        chan = self.DATADICT[pulse]["chan"] 
1889
+        rchan = self.DATADICT[pulse]["rchan"] 
1890
+            
1891
+        T = 1.5 #N_samp * self.dt 
1892
+        TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
1893
+        
1894
+        self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1895
+        self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
1896
+
1897
+        # pandas is much faster than numpy for io
1898
+        #DATA = np.loadtxt(rawfname)
1899
+        DATA = pd.read_csv(rawfname, header=None, sep="\t").values
1900
+        for ipm in range(self.nPulseMoments):
1901
+            for ichan in np.append(chan,rchan):
1902
+                self.DATADICT["Pulse 1"][ichan][ipm][istack] =  DATA[:, eval(ichan)+4][nds:(nds+nd1)] * invGain
1903
+                self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,2][nps:nps+npul] * invCGain
1904
+            nds += ndr
1905
+            nps += ndr 
1906
+
1907
+
1757
     def loadFIDData(self, base, procStacks, chanin, rchanin, FIDProc, canvas, deadTime, plot):
1908
     def loadFIDData(self, base, procStacks, chanin, rchanin, FIDProc, canvas, deadTime, plot):
1758
         '''
1909
         '''
1759
-            Loads a GMR FID dataset, reads OLD ASCII files
1910
+            Loads a GMR FID dataset, reads binary and ASCII format files 
1760
         '''
1911
         '''
1761
-        import struct
1912
+
1762
         canvas.reAx3(True,False)
1913
         canvas.reAx3(True,False)
1763
 
1914
 
1764
         chan = []
1915
         chan = []
1774
         self.deadTime       = deadTime       # instrument dead time before measurement
1925
         self.deadTime       = deadTime       # instrument dead time before measurement
1775
         self.samp = 50000.                   # in case this is a reproc, these might have 
1926
         self.samp = 50000.                   # in case this is a reproc, these might have 
1776
         self.dt   = 1./self.samp             # changed
1927
         self.dt   = 1./self.samp             # changed
1777
-        invGain = 1./self.RxGain        
1778
-        invCGain = self.CurrentGain        
1779
 
1928
 
1780
-        #################################################################################
1781
-        # figure out key data indices
1782
-        # Pulse        
1783
-        nps  = (int)((self.prePulseDelay)*self.samp)
1784
-        npul   = (int)(self.pulseLength[0]*self.samp) #+ 100 
1785
-
1786
-        # Data 
1787
-        nds  = nps+npul+(int)((self.deadTime)*self.samp);        # indice pulse 1 data starts 
1788
-        nd1 = (int)(1.*self.samp)                              # samples in first pulse
1789
 
1929
 
1790
         #################################################################################
1930
         #################################################################################
1791
         # Data structures     
1931
         # Data structures     
1813
         ##############################################
1953
         ##############################################
1814
         # Read in binary (.lvm) data
1954
         # Read in binary (.lvm) data
1815
         iistack = 0
1955
         iistack = 0
1816
-        hack = False
1956
+        fnames = []
1817
         for istack in procStacks:
1957
         for istack in procStacks:
1818
-            rawFile = open(base + "_" + str(istack) + ".lvm", 'rb')
1819
-            if hack:
1820
-                subFile = open(base + "sub" + "_" + str(istack) + ".lvm", 'wb')
1821
-            for ipm in range(self.nPulseMoments):
1822
-                
1823
-                # frequency cycing modulation
1824
-                #mod = (-1)**(ipm%2) * (-1)**(istack%2)
1825
-                
1826
-                buf1 = rawFile.read(4)
1827
-                buf2 = rawFile.read(4)
1828
-                
1829
-                if hack:
1830
-                    # hack to do some data analysis
1831
-                    subFile.write(buf1)
1832
-                    subFile.write(buf2)
1833
-                    # end hack
1834
-                
1835
-                #N_chan = struct.unpack('>i', rawFile.read(4))[0]
1836
-                #N_samp = struct.unpack('>i', rawFile.read(4))[0]
1958
+            if self.nDAQVersion < 2.3:
1959
+                #rawfname = base + "_" + str(istack) 
1960
+                self.loadGMRASCIIFID( base + "_" + str(istack), istack )
1961
+            else:
1962
+                self.loadGMRBinaryFID( base + "_" + str(istack) + ".lvm", istack )
1963
+                #fnames.append( base + "_" + str(istack) + ".lvm" )
1837
                 
1964
                 
1838
-                N_chan = struct.unpack('>i', buf1 )[0]
1839
-                N_samp = struct.unpack('>i', buf2 )[0]
1840
-        
1841
- 
1842
-                T = N_samp * self.dt 
1843
-                TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
1844
-
1845
-                DATA = np.zeros([N_samp, N_chan+1])
1846
-                for ichan in range(N_chan):
1847
-                    DATADUMP = rawFile.read(4*N_samp)
1848
-                    #subFile.write(DATADUMP)
1849
-                    for irec in range(N_samp):
1850
-                        DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
1965
+            percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1))  / (len(procStacks)*self.nPulseMoments)))
1966
+            self.progressTrigger.emit(percent) 
1967
+            iistack += 1
1851
 
1968
 
1852
-                if hack:                       
1853
-                    # hack to do some data analysis (array survey)
1854
-                    for ichan in range(5):
1855
-                        for irec in range(N_samp): 
1856
-                            bdata = struct.pack( '>f', DATA[irec,ichan] )               
1857
-                            subFile.write(bdata)
1858
-                    for ichan in range(5,6):
1859
-                        for irec in range(N_samp): 
1860
-                            bdata = struct.pack( '>f', DATA[irec,ichan] + DATA[irec,ichan+2] )               
1861
-                            #bdata = struct.pack( '>f', .23 )               
1862
-                            subFile.write(bdata)
1863
-                    for ichan in range(6,N_chan):
1864
-                        for irec in range(N_samp): 
1865
-                            bdata = struct.pack( '>f', DATA[irec,ichan] )               
1866
-                            subFile.write(bdata)
1969
+        # multiprocessing load data
1970
+        #info = {}
1971
+        #info["prePulseDelay"] = self.prePulseDelay
1972
+        #with multiprocessing.Pool() as pool: 
1973
+        #    results = pool.starmap( loadGMRBinaryFID, zip(itertools.repeat(self), fnames, info ) ) # zip(np.tile(vc, (ns, 1)), np.tile(vgc, (ns,1)), itertools.repeat(sys.argv[1]), itertools.repeat(sys.argv[2]), EPS_CMR))
1867
 
1974
 
1868
-                if plot: 
1975
+        # Plotting
1976
+        if plot: 
1977
+            iistack = 0
1978
+            for istack in procStacks:
1979
+                for ipm in range(self.nPulseMoments):
1869
                     canvas.ax1.clear()
1980
                     canvas.ax1.clear()
1870
                     canvas.ax2.clear()
1981
                     canvas.ax2.clear()
1871
                     canvas.ax3.clear()
1982
                     canvas.ax3.clear()
1872
                            
1983
                            
1873
-                # Save into Data Cube 
1874
-                for ichan in chan:
1875
-                    self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain 
1876
-                    self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1877
-                    self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
1878
-                    self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul] 
1879
-                    if plot:
1880
-                        #canvas.ax2.plot(self.DATADICT["Pulse 1"]["TIMES"],       self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID data ch. "+str(ichan)) #, color='blue')
1984
+                    for ichan in chan:
1881
                         canvas.ax1.plot(self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black')
1985
                         canvas.ax1.plot(self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black')
1882
                         canvas.ax3.plot(self.DATADICT["Pulse 1"]["TIMES"],       self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID data ch. "+str(ichan)) #, color='blue')
1986
                         canvas.ax3.plot(self.DATADICT["Pulse 1"]["TIMES"],       self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID data ch. "+str(ichan)) #, color='blue')
1883
-                        #canvas.draw()
1884
 
1987
 
1885
-                # plot reference channels?
1886
-                for ichan in rchan:
1887
-                    self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain 
1888
-                    self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1889
-                    if plot:
1988
+                    for ichan in rchan:
1890
                         canvas.ax2.plot(self.DATADICT["Pulse 1"]["TIMES"], self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID ref ch. "+str(ichan)) #, color='blue')
1989
                         canvas.ax2.plot(self.DATADICT["Pulse 1"]["TIMES"], self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID ref ch. "+str(ichan)) #, color='blue')
1891
 
1990
 
1892
-                if plot:
1893
                     canvas.ax3.legend(prop={'size':6})
1991
                     canvas.ax3.legend(prop={'size':6})
1894
                     canvas.ax2.legend(prop={'size':6})
1992
                     canvas.ax2.legend(prop={'size':6})
1895
                     
1993
                     
1904
                     canvas.ax2.set_xlabel("time [s]", fontsize=8)
2002
                     canvas.ax2.set_xlabel("time [s]", fontsize=8)
1905
                     canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y') 
2003
                     canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y') 
1906
                     canvas.ax3.ticklabel_format(style='sci', scilimits=(0,0), axis='y') 
2004
                     canvas.ax3.ticklabel_format(style='sci', scilimits=(0,0), axis='y') 
1907
-
1908
                     canvas.draw()
2005
                     canvas.draw()
2006
+
1909
                 percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1))  / (len(procStacks)*self.nPulseMoments)))
2007
                 percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1))  / (len(procStacks)*self.nPulseMoments)))
1910
-                #self.progressTrigger.emit(percent) 
1911
                 self.progressTrigger.emit(percent) 
2008
                 self.progressTrigger.emit(percent) 
1912
-
1913
-            iistack += 1
1914
-
2009
+                iistack += 1
1915
 
2010
 
1916
         self.enableDSP()    
2011
         self.enableDSP()    
1917
         self.doneTrigger.emit()
2012
         self.doneTrigger.emit()
1918
-
1919
     
2013
     
1920
     def load4PhaseT1Data(self, base, procStacks, chan, rchan, FIDProc, canvas, deadTime, plot): 
2014
     def load4PhaseT1Data(self, base, procStacks, chan, rchan, FIDProc, canvas, deadTime, plot): 
1921
 
2015
 

+ 33
- 32
akvo/tressel/rotate.py View File

25
         # decimate
25
         # decimate
26
     # blind decimation
26
     # blind decimation
27
     # 1 instead of T 
27
     # 1 instead of T 
28
-    irsamp = (T) * int(  (1./vL) / dt) # real 
28
+    irsamp = int(T) * int(  (1./vL) / dt) # real 
29
     iisamp =       int(  ((1./vL)/ dt) * ( .5*np.pi / (2.*np.pi) ) ) # imaginary
29
     iisamp =       int(  ((1./vL)/ dt) * ( .5*np.pi / (2.*np.pi) ) ) # imaginary
30
    
30
    
31
 
31
 
78
     #############################################################
78
     #############################################################
79
     ## In-phase 
79
     ## In-phase 
80
     #2*np.cos(wL*t)  
80
     #2*np.cos(wL*t)  
81
-    dw = -2.*np.pi*2
81
+    dw = 0 # -2.*np.pi*2
82
     Q = signal.filtfilt(b, a, xn*2*np.cos((wL+dw)*t))  # X
82
     Q = signal.filtfilt(b, a, xn*2*np.cos((wL+dw)*t))  # X
83
     I = signal.filtfilt(b, a, xn*2*np.sin((wL+dw)*t))  # Y
83
     I = signal.filtfilt(b, a, xn*2*np.sin((wL+dw)*t))  # Y
84
 
84
 
133
 
133
 
134
 def gateIntegrate(T2D, T2T, gpd, sigma, stackEfficiency=2.):
134
 def gateIntegrate(T2D, T2T, gpd, sigma, stackEfficiency=2.):
135
     """ Gate integrate the signal to gpd, gates per decade
135
     """ Gate integrate the signal to gpd, gates per decade
136
+        T2D = the time series to gate integrate, complex 
137
+        T2T = the abscissa values 
138
+        gpd = gates per decade 
139
+        sigma = estimate of standard deviation for theoretical gate noise 
140
+        stackEfficiency = exponential in theoretical gate noise, 2 represents ideal stacking
136
     """
141
     """
137
     
142
     
138
     # use artificial time gates so that early times are fully captured
143
     # use artificial time gates so that early times are fully captured
140
     T2TD = T2T[0] - (T2T[1]-T2T[0])
145
     T2TD = T2T[0] - (T2T[1]-T2T[0])
141
     T2T -= T2TD
146
     T2T -= T2TD
142
     
147
     
143
-    # calculate total number of decades
144
-    nd = np.log10(T2T[-1]/T2T[0]) #np.log10(self.T2T[-1]) -  np.log10(self.T2T[-1])
148
+    #####################################
149
+    # calculate total number of decades #
150
+    # windows edges are approximate until binning but will be adjusted to reflect data timing, this 
151
+    # primarily impacts bins with a few samples  
152
+    nd = np.log10(T2T[-1]/T2T[0])               
145
     tdd = np.logspace( np.log10(T2T[0]), np.log10(T2T[-1]), (int)(gpd*nd)+1, base=10, endpoint=True) 
153
     tdd = np.logspace( np.log10(T2T[0]), np.log10(T2T[-1]), (int)(gpd*nd)+1, base=10, endpoint=True) 
146
-    tdl = tdd[0:-1]     # these are left edges
147
-    tdr = tdd[1::]      # these are left edges
148
-    td = (tdl+tdr) / 2. # window centres
154
+    tdl = tdd[0:-1]                 # approximate window left edges
155
+    tdr = tdd[1::]                  # approximate window right edges
156
+    td = (tdl+tdr) / 2.             # approximate window centres
149
 
157
 
150
-    Vars = np.ones( len(td) ) * sigma**2 #* .15**2
158
+
159
+    Vars = np.zeros( len(td) ) 
151
     htd = np.zeros( len(td), dtype=complex )
160
     htd = np.zeros( len(td), dtype=complex )
152
-    isum = np.zeros( len(td) )
153
-    ii = 0
161
+    isum = np.zeros( len(td), dtype=int )  
154
 
162
 
155
-    SIGSTACK = {}
156
-    SIGSTACK[ii]= []
163
+    ii = 0
157
     for itd in range(len(T2T)):
164
     for itd in range(len(T2T)):
158
         if ( T2T[itd] > tdr[ii] ):
165
         if ( T2T[itd] > tdr[ii] ):
159
-            if (ii < len(td)-1):
160
-                ii += 1
161
-                SIGSTACK[ii] = []
162
-            else:
163
-                #print "overshoot??", ii
164
-                break
166
+            ii += 1
167
+            # correct window edges to centre about data 
168
+            tdr[ii-1] = (T2T[itd-1]+T2T[itd])*.5 
169
+            tdl[ii  ] = (T2T[itd-1]+T2T[itd])*.5
165
         isum[ii] += 1
170
         isum[ii] += 1
166
         htd[ii] += T2D[ itd ]
171
         htd[ii] += T2D[ itd ]
167
-        SIGSTACK[ii].append(T2D[itd])
168
         Vars[ii] += sigma**2
172
         Vars[ii] += sigma**2
173
+        
174
+    td = (tdl+tdr) / 2.             # actual window centres
175
+    sigma2 = np.sqrt( Vars * ((1/(isum))**stackEfficiency) ) 
169
 
176
 
170
-    sigma = np.sqrt( Vars  * (1/isum)**stackEfficiency ) 
171
-    for ii in range(len(td)):
172
-        if len(SIGSTACK[ii]) > 30:
173
-            sigma[ii] = np.var(SIGSTACK[ii]) / ((len(SIGSTACK[ii])-1)**(1/stackEfficiency))
174
-            #sigma[ii] = np.std(SIGSTACK[ii]) * ( 1./(len(SIGSTACK[ii]))**stackEfficiency)
177
+    # Reset abscissa where isum == 1 
178
+    # when there is no windowing going on 
179
+    td[isum==1] = T2T[0:len(td)][isum==1]
175
 
180
 
176
-    # RESET times where isum == 1
177
-    ii = 0
178
-    while (isum[ii] == 1):
179
-        td[ii] = T2T[ii]
180
-        ii += 1
181
+    tdd = np.append(tdl, tdr[-1])
181
 
182
 
182
-    htd /= isum
183
-    T2T += T2TD  
184
-    return td+T2TD, htd, tdd+T2TD, sigma, isum  # centre abscissa, data, window edges, error  
183
+    htd /= isum # average
184
+    T2T += T2TD 
185
+    return td+T2TD, htd, tdd+T2TD, sigma2, isum  # centre abscissa, data, window edges, error 
185
 
186
 
186
 if __name__ == "__main__":
187
 if __name__ == "__main__":
187
 
188
 

+ 1
- 0
setup.py View File

40
           'numpy',
40
           'numpy',
41
           'PyQt5',
41
           'PyQt5',
42
           'pyyaml',
42
           'pyyaml',
43
+          'pandas',
43
           'pyqt-distutils',
44
           'pyqt-distutils',
44
           'cmocean'
45
           'cmocean'
45
       ],
46
       ],

Loading…
Cancel
Save