Commits

iorodeo  committed 8d4e4e8

Worked on adding a nonlinear fit to colorimeter software.

  • Participants
  • Parent commits 9ce39c1

Comments (0)

Files changed (8)

File python/Colorimeter/colorimeter/constants.py

 import os
 
 # Development
-DEVEL_FAKE_MEASURE = False 
-#DEVEL_FAKE_MEASURE = True 
+#DEVEL_FAKE_MEASURE = False 
+DEVEL_FAKE_MEASURE = True 
 
 # Serial ports
 DFLT_PORT_WINDOWS = 'com1' 
 TABLE_COL_COUNT = 2 
 
 # Data fit type
-FIT_TYPE = 'force_zero'
+LINEAR_FIT_TYPE = 'force_zero'
 
 # Plotting
 PLOT_FIGURE_NUM = 1

File python/Colorimeter/colorimeter/gui/measure/measure.py

 from colorimeter import constants 
 from colorimeter import import_export 
 from colorimeter import standard_curve
+from colorimeter import nonlinear_fit
 from colorimeter.main_window import MainWindowWithTable
 
 class MeasureMainWindow(MainWindowWithTable, Ui_MainWindow):
     def initialize(self):
         super(MeasureMainWindow,self).initialize()
         self.coeff = None
+        self.fitType = 'linear'
+        self.fitParam = None
         self.noValueSymbol = constants.NO_VALUE_SYMBOL_LABEL
         self.aboutText = constants.MEASURE_ABOUT_TEXT
 
         value = self.coefficientLineEdit.text()
         value = float(value)
         self.coeff = value
+        self.fitType = 'linear'
+        self.fitParam = None
         self.updateWidgetEnabled()
 
     def coeffFixup(self,value):
         value = str(value)
         if not value:
             self.coeff = None
+            self.fitType = 'linear'
+            self.fitParam = None
             self.updateWidgetEnabled()
 
     def editTestSolutions_Callback(self):
             testSolutionDict.update(self.default_TestSolutionDict)
             pathName = testSolutionDict[itemText]
             data = import_export.importTestSolutionData(pathName)
-            self.coeff = getCoefficientFromData(data)
-            self.coefficientLineEdit.setText('{0:1.1f}'.format(1.0e6*self.coeff))
+            if not self.checkImportData(data):
+                return
+            self.fitType = data['fitType']
+            self.fitParams = data['fitParams']
+            self.coeff = getCoefficientFromData(data,self.fitType,self.fitParams)
+            if self.fitType == 'linear':
+                self.coefficientLineEdit.setText('{0:1.1f}'.format(1.0e6*self.coeff))
+            else:
+                self.coefficientLineEdit.setText(' -- nonlinear --')
             self.setLEDColor(data['led'])
         self.testSolutionIndex = index
         self.updateWidgetEnabled()
             conc = random.random()
         else:
             freq, trans, absorb = self.dev.getMeasurement()
-            conc = absorb[ledNumber]/self.coeff
+            conc = self.getConcentration(absorb[ledNumber])
+
 
         concStr = '{0:1.2f}'.format(conc)
         self.measurePushButton.setFlat(False)
         self.tableWidget.addData('',concStr,selectAndEdit=True)
 
+    def getConcentration(self,absorb):
+        if self.fitType == 'linear':
+            conc = absorb/self.coeff
+        elif self.fitType == 'polynomial':
+            pass
+
     def getSaveFileHeader(self):
         timeStr = time.strftime('%Y-%m-%d %H:%M:%S %Z') 
         headerList = [ 
                 label = str(label)
             conc = str(conc)
             self.tableWidget.addData(label,conc)
-        pass
+
+    def checkImportData(self,data): 
+        msgTitle = 'Import Error'
+        if not data['fitType'] in ('linear', 'polynomial'):
+            msgText = 'unknown fit type: {0}'.format(data['fitType']) 
+            QtGui.QMessageBox.warning(self,msgTitle, msgText)
+            return False
+
+        if data['fitType'] == 'polynomial':
+            if data['fitParams'] not in (2,3,4,5):
+                msgText = 'unsuported polynomial order: {0}'.format(fitParams) 
+                QtGui.QMessageBox.warning(self,msgTitle, msgText)
+                return False
+        return True
 
 def dataListToLabelAndFloat(dataList):
     dataListNew = []
         dataListNew.append((x,y))
     return dataListNew
 
-def getCoefficientFromData(data): 
+def getCoefficientFromData(data,fitType,fitParams): 
     values = data['values']
     abso, conc = zip(*values)
-    coeff = standard_curve.getCoefficient(abso,conc,fitType=constants.FIT_TYPE)
+    if fitType == 'linear':
+        coeff = standard_curve.getCoefficient(abso,conc,fitType=constants.LINEAR_FIT_TYPE)
+    elif fitType == 'polynomial':
+        order = fitParams
+        coeff, dummy0, dummy1 = nonlinear_fit.getPolynomialFit(abso,conc,order=order)
     return coeff
 
 def startMeasureMainWindow(app):

File python/Colorimeter/colorimeter/gui/plot/plot.py

 from colorimeter import constants
 from colorimeter import import_export 
 from colorimeter import standard_curve
+from colorimeter import nonlinear_fit
 from colorimeter.main_window import MainWindowWithTable
 from colorimeter.gui.dialog.test_solution_dialog import TestSolutionDialog
 
         self.actionExport.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_E)
         self.actionImport.triggered.connect(self.importData_Callback)
         self.actionImport.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_I)
+
+        self.fitTypeActionGroup = QtGui.QActionGroup(self)
+        self.fitTypeActionGroup.addAction(self.actionFitTypeLinear)
+        self.fitTypeActionGroup.addAction(self.actionFitTypePolynomial2)
+        self.fitTypeActionGroup.addAction(self.actionFitTypePolynomial3)
+        self.fitTypeActionGroup.addAction(self.actionFitTypePolynomial4)
+        self.fitTypeActionGroup.addAction(self.actionFitTypePolynomial5)
+        self.fitTypeActionGroup.setExclusive(True)
+
+        self.actionFitTypeLinear.triggered.connect(self.fitTypeChanged_Callback)
+        self.actionFitTypePolynomial2.triggered.connect(self.fitTypeChanged_Callback)
+        self.actionFitTypePolynomial3.triggered.connect(self.fitTypeChanged_Callback)
+        self.actionFitTypePolynomial4.triggered.connect(self.fitTypeChanged_Callback)
+        self.actionFitTypePolynomial5.triggered.connect(self.fitTypeChanged_Callback)
+
+        self.concUnitsActionGroup = QtGui.QActionGroup(self)
+        self.concUnitsActionGroup.addAction(self.actionConcentrationUnitsUM)
+        self.concUnitsActionGroup.addAction(self.actionConcentrationUnitsPPM)
+        self.concUnitsActionGroup.setExclusive(True)
+
+        self.actionConcentrationUnitsUM.triggered.connect(self.concUnitsChanged_Callback)
+        self.actionConcentrationUnitsPPM.triggered.connect(self.concUnitsChanged_Callback)
+
         itemDelegate = DoubleItemDelegate(self.tableWidget)
         self.tableWidget.setItemDelegateForColumn(0,itemDelegate)
 
         if data is not None:
             self.setTableData(data['values'])
             self.setLEDColor(data['led'])
+            self.setFitType(data['fitType'],data['fitParams'])
+            self.setConcentrationUnits(data['concentrationUnits'])
         self.updateWidgetEnabled()
+        self.updatePlot(create=False)
         
     def initialize(self):
         super(PlotMainWindow,self).initialize()
         self.noValueSymbol = constants.NO_VALUE_SYMBOL_NUMBER
         self.tableWidget.clean(setup=True)
         self.tableWidget.updateFunc = self.updatePlot
-        concentrationStr = QtCore.QString.fromUtf8("Concentration (\xc2\xb5M)")
-        self.tableWidget.setHorizontalHeaderLabels((concentrationStr,'Absorbance')) 
         self.updateWidgetEnabled()
+        self.setFitType('linear',None)
+        self.setConcentrationUnits('uM')
 
     def exportData_Callback(self):
         dataList = self.tableWidget.getData()
-        if len(dataList) < 2:
-            msgTitle = 'Export Error'
-            msgText = 'insufficient data for export'
-            QtGui.QMessageBox.warning(self,msgTitle, msgText)
-            return
+        fitType, fitParams = self.getFitTypeAndParams()
+
+        if fitType == 'linear':
+            if len(dataList) < 2:
+                msgTitle = 'Export Error'
+                msgText = 'insufficient data for export w/ linear fit' 
+                msgText += ' - must have at least 2 points'
+                QtGui.QMessageBox.warning(self,msgTitle, msgText)
+                return
+
+        elif fitType == 'polynomial':
+            order = fitParams
+            if len(dataList) <= order:
+                msgTitle = 'Export Error'
+                msgText = 'insufficient data for export w/ order={0} polynomial'.format(order)
+                msgText += ', must have at least {0} points'.format(order+1)
+                QtGui.QMessageBox.warning(self,msgTitle, msgText)
+                return
 
         solutionName, flag = QtGui.QInputDialog.getText(
                 self,
                 import_export.deleteTestSolution(self.userHome,solutionName)
 
         dateStr = time.strftime('%Y-%m-%d %H:%M:%S %Z')
-        import_export.exportTestSolutionData(
-                self.userHome,
-                solutionName,
-                dataList,
-                self.currentColor,
-                dateStr,
-                )
+        dataDict = { 
+                'name': solutionName,
+                'date': dateStr,
+                'led': self.currentColor,
+                'values': [map(float,x) for x in dataList],
+                'fitType': fitType,
+                'concentrationUnits': self.getConcentrationUnits(),
+                }
+        if fitParams is not None:
+            try:
+                dataDict['fitParams'] = list(fitParams),
+            except TypeError:
+                dataDict['fitParams'] = fitParams
+        else:
+            dataDict['fitParams'] = 'None'
+
+        import_export.exportTestSolutionData(self.userHome,dataDict)
+
+    def fitTypeChanged_Callback(self):
+        self.updatePlot()
+
+    def concUnitsChanged_Callback(self):
+        self.setConcentrationStr()
+        self.updatePlot()
 
     def updatePlot(self,create=False):
+
         if not create and not plt.fignum_exists(constants.PLOT_FIGURE_NUM):
             return
         dataList = self.tableWidget.getData(noValueInclude=False)
             return
         xList,yList = zip(*dataList)
 
-        if len(dataList) > 1:
-            slope, xFit, yFit = standard_curve.getLinearFit(
-                    xList,
-                    yList,
-                    fitType=constants.FIT_TYPE,
-                    numPts=constants.PLOT_FIT_NUM_PTS,
-                    )
-            haveSlope = True
+        fitType, fitParams = self.getFitTypeAndParams()
+
+        haveFit = False
+        haveSlope = False
+        if fitType == 'linear':
+            if len(dataList) > 1:
+                slope, xFit, yFit = standard_curve.getLinearFit(
+                        xList,
+                        yList,
+                        fitType=constants.LINEAR_FIT_TYPE,
+                        numPts=constants.PLOT_FIT_NUM_PTS,
+                        )
+                haveFit = True
+                haveSlope = True
+        elif fitType == 'polynomial':
+            order = fitParams
+            if len(dataList) > order:
+                coeff, yFit, xFit = nonlinear_fit.getPolynomialFit(
+                        yList,
+                        xList,
+                        order=order,
+                        numPts=constants.PLOT_FIT_NUM_PTS,
+                        )
+                haveFit = True
         else:
-            haveSlope = False
+            print("unsupported fitType - we shouldn't be here")
 
         plt.clf()
         self.fig = plt.figure(constants.PLOT_FIGURE_NUM)
         self.fig.canvas.manager.set_window_title('Colorimeter Plot')
         ax = self.fig.add_subplot(111)
 
-        if haveSlope:
+        if haveFit:
             hFit = ax.plot(xFit,yFit,'r')
         ax.plot(xList,yList,'ob')
         ax.grid('on')
-        ax.set_xlabel('Concentration')
+        units = self.getConcentrationUnits()
+        ax.set_xlabel('Concentration ({0})'.format(units))
         ax.set_ylabel('Absorbance ('+self.currentColor+' led)')
         if haveSlope:
             self.fig.text(
             self.ledColorWidget.setEnabled(True)
             self.calibratePushButton.setEnabled(True)
 
+    def getFitTypeAndParams(self):
+        if self.actionFitTypeLinear.isChecked():
+            fitType = 'linear'
+            params = None
+        elif self.actionFitTypePolynomial2.isChecked():
+            fitType = 'polynomial' 
+            params = 2
+        elif self.actionFitTypePolynomial3.isChecked():
+            fitType = 'polynomial'
+            params = 3
+        elif self.actionFitTypePolynomial4.isChecked():
+            fitType = 'polynomial'
+            params = 4
+        else:
+            fitType = 'polynomial'
+            params = 5
+        return fitType, params
+
+    def setFitType(self,fitType,fitParams):
+        error = False
+        self.actionFitTypeLinear.setChecked(False)
+        self.actionFitTypePolynomial2.setChecked(False)
+        self.actionFitTypePolynomial3.setChecked(False)
+        self.actionFitTypePolynomial4.setChecked(False)
+        self.actionFitTypePolynomial5.setChecked(False)
+        if fitType.lower() == 'linear':
+            self.actionFitTypeLinear.setChecked(True)
+        elif fitType.lower() == 'polynomial':
+            order = fitParams
+            order2actionDict = {
+                    2: self.actionFitTypePolynomial2,
+                    3: self.actionFitTypePolynomial3,
+                    4: self.actionFitTypePolynomial4,
+                    5: self.actionFitTypePolynomial5,
+                    }
+            try:
+                action = order2actionDict[order]
+                action.setChecked(True)
+            except KeyError:
+                error = True
+                errorMsg = 'unsupported polynomial order'
+        else:
+            error = True
+            errorMsg = 'uknown fit type {0}'.format(fitType)
+
+        if error: 
+            msgTitle = 'Export Error'
+            msgText = 'insufficient data for export w/ linear fit' 
+            msgText += ' - must have at least 2 points'
+            QtGui.QMessageBox.warning(self,msgTitle, msgText)
+
+    def getConcentrationUnits(self):
+        if self.actionConcentrationUnitsUM.isChecked():
+            return 'uM'
+        else:
+            return 'ppm'
+
+    def setConcentrationUnits(self,units):
+        if  units.lower() == 'um':
+            self.actionConcentrationUnitsUM.setChecked(True)
+            self.actionConcentrationUnitsPPM.setChecked(False)
+        else:
+            self.actionConcentrationUnitsUM.setChecked(False)
+            self.actionConcentrationUnitsPPM.setChecked(True)
+        self.setConcentrationStr()
+
+    def setConcentrationStr(self):
+        concUnits = self.getConcentrationUnits().lower()
+        if concUnits == 'um':
+            concentrationStr = QtCore.QString.fromUtf8("Concentration (\xc2\xb5M)")
+        else:
+            concentrationStr = QtCore.QString('Concentration (ppm)')
+        self.tableWidget.setHorizontalHeaderLabels((concentrationStr,'Absorbance')) 
+
 def dataListToFloat(dataList):
     dataListFloat = []
     for x,y in dataList:

File python/Colorimeter/colorimeter/gui/plot/plot.ui

     <property name="title">
      <string>&amp;Options</string>
     </property>
+    <widget class="QMenu" name="menuFitType">
+     <property name="title">
+      <string>Fit Type</string>
+     </property>
+     <addaction name="actionFitTypeLinear"/>
+     <addaction name="actionFitTypePolynomial2"/>
+     <addaction name="actionFitTypePolynomial3"/>
+     <addaction name="actionFitTypePolynomial4"/>
+     <addaction name="actionFitTypePolynomial5"/>
+    </widget>
+    <widget class="QMenu" name="menuConcentrationUnits">
+     <property name="title">
+      <string>Concentration Units</string>
+     </property>
+     <addaction name="actionConcentrationUnitsUM"/>
+     <addaction name="actionConcentrationUnitsPPM"/>
+    </widget>
     <addaction name="actionExport"/>
     <addaction name="actionImport"/>
     <addaction name="actionEditTestSolutions"/>
+    <addaction name="menuFitType"/>
+    <addaction name="menuConcentrationUnits"/>
    </widget>
    <widget class="QMenu" name="menu_Help">
     <property name="title">
     <string>About</string>
    </property>
   </action>
+  <action name="actionFitTypeLinear">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Linear</string>
+   </property>
+  </action>
+  <action name="actionFitTypePolynomial2">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Polynomial (order=2)</string>
+   </property>
+  </action>
+  <action name="actionConcentrationUnitsUM">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>uM</string>
+   </property>
+  </action>
+  <action name="actionConcentrationUnitsPPM">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>ppm</string>
+   </property>
+  </action>
+  <action name="actionFitTypePolynomial3">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Polynomial (order=3)</string>
+   </property>
+  </action>
+  <action name="actionFitTypePolynomial4">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Polynomial (order=4)</string>
+   </property>
+  </action>
+  <action name="actionFitTypePolynomial5">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Polynomial (order=5)</string>
+   </property>
+  </action>
  </widget>
  <customwidgets>
   <customwidget>

File python/Colorimeter/colorimeter/gui/plot/plot_ui.py

 
 # Form implementation generated from reading ui file 'plot.ui'
 #
-# Created: Thu Jul 26 11:24:16 2012
+# Created: Tue Dec 18 17:20:59 2012
 #      by: PyQt4 UI code generator 4.7.2
 #
 # WARNING! All changes made in this file will be lost!
         self.menuFile.setObjectName("menuFile")
         self.menuOptions = QtGui.QMenu(self.menubar)
         self.menuOptions.setObjectName("menuOptions")
+        self.menuFitType = QtGui.QMenu(self.menuOptions)
+        self.menuFitType.setObjectName("menuFitType")
+        self.menuConcentrationUnits = QtGui.QMenu(self.menuOptions)
+        self.menuConcentrationUnits.setObjectName("menuConcentrationUnits")
         self.menu_Help = QtGui.QMenu(self.menubar)
         self.menu_Help.setObjectName("menu_Help")
         MainWindow.setMenuBar(self.menubar)
         self.actionEditTestSolutions.setObjectName("actionEditTestSolutions")
         self.actionAbout = QtGui.QAction(MainWindow)
         self.actionAbout.setObjectName("actionAbout")
+        self.actionFitTypeLinear = QtGui.QAction(MainWindow)
+        self.actionFitTypeLinear.setCheckable(True)
+        self.actionFitTypeLinear.setObjectName("actionFitTypeLinear")
+        self.actionFitTypePolynomial2 = QtGui.QAction(MainWindow)
+        self.actionFitTypePolynomial2.setCheckable(True)
+        self.actionFitTypePolynomial2.setObjectName("actionFitTypePolynomial2")
+        self.actionConcentrationUnitsUM = QtGui.QAction(MainWindow)
+        self.actionConcentrationUnitsUM.setCheckable(True)
+        self.actionConcentrationUnitsUM.setObjectName("actionConcentrationUnitsUM")
+        self.actionConcentrationUnitsPPM = QtGui.QAction(MainWindow)
+        self.actionConcentrationUnitsPPM.setCheckable(True)
+        self.actionConcentrationUnitsPPM.setObjectName("actionConcentrationUnitsPPM")
+        self.actionFitTypePolynomial3 = QtGui.QAction(MainWindow)
+        self.actionFitTypePolynomial3.setCheckable(True)
+        self.actionFitTypePolynomial3.setObjectName("actionFitTypePolynomial3")
+        self.actionFitTypePolynomial4 = QtGui.QAction(MainWindow)
+        self.actionFitTypePolynomial4.setCheckable(True)
+        self.actionFitTypePolynomial4.setObjectName("actionFitTypePolynomial4")
+        self.actionFitTypePolynomial5 = QtGui.QAction(MainWindow)
+        self.actionFitTypePolynomial5.setCheckable(True)
+        self.actionFitTypePolynomial5.setObjectName("actionFitTypePolynomial5")
         self.menuFile.addAction(self.actionSave)
         self.menuFile.addAction(self.actionLoad)
+        self.menuFitType.addAction(self.actionFitTypeLinear)
+        self.menuFitType.addAction(self.actionFitTypePolynomial2)
+        self.menuFitType.addAction(self.actionFitTypePolynomial3)
+        self.menuFitType.addAction(self.actionFitTypePolynomial4)
+        self.menuFitType.addAction(self.actionFitTypePolynomial5)
+        self.menuConcentrationUnits.addAction(self.actionConcentrationUnitsUM)
+        self.menuConcentrationUnits.addAction(self.actionConcentrationUnitsPPM)
         self.menuOptions.addAction(self.actionExport)
         self.menuOptions.addAction(self.actionImport)
         self.menuOptions.addAction(self.actionEditTestSolutions)
+        self.menuOptions.addAction(self.menuFitType.menuAction())
+        self.menuOptions.addAction(self.menuConcentrationUnits.menuAction())
         self.menu_Help.addAction(self.actionAbout)
         self.menubar.addAction(self.menuFile.menuAction())
         self.menubar.addAction(self.menuOptions.menuAction())
         self.plotPushButton.setText(QtGui.QApplication.translate("MainWindow", "Plot", None, QtGui.QApplication.UnicodeUTF8))
         self.menuFile.setTitle(QtGui.QApplication.translate("MainWindow", "&File", None, QtGui.QApplication.UnicodeUTF8))
         self.menuOptions.setTitle(QtGui.QApplication.translate("MainWindow", "&Options", None, QtGui.QApplication.UnicodeUTF8))
+        self.menuFitType.setTitle(QtGui.QApplication.translate("MainWindow", "Fit Type", None, QtGui.QApplication.UnicodeUTF8))
+        self.menuConcentrationUnits.setTitle(QtGui.QApplication.translate("MainWindow", "Concentration Units", None, QtGui.QApplication.UnicodeUTF8))
         self.menu_Help.setTitle(QtGui.QApplication.translate("MainWindow", "&Help", None, QtGui.QApplication.UnicodeUTF8))
         self.actionSave.setText(QtGui.QApplication.translate("MainWindow", "Save...", None, QtGui.QApplication.UnicodeUTF8))
         self.action_About.setText(QtGui.QApplication.translate("MainWindow", "About...", None, QtGui.QApplication.UnicodeUTF8))
         self.actionImport.setText(QtGui.QApplication.translate("MainWindow", "Import Test Solution...", None, QtGui.QApplication.UnicodeUTF8))
         self.actionEditTestSolutions.setText(QtGui.QApplication.translate("MainWindow", "Edit User Test Solutions...", None, QtGui.QApplication.UnicodeUTF8))
         self.actionAbout.setText(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionFitTypeLinear.setText(QtGui.QApplication.translate("MainWindow", "Linear", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionFitTypePolynomial2.setText(QtGui.QApplication.translate("MainWindow", "Polynomial (order=2)", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionConcentrationUnitsUM.setText(QtGui.QApplication.translate("MainWindow", "uM", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionConcentrationUnitsPPM.setText(QtGui.QApplication.translate("MainWindow", "ppm", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionFitTypePolynomial3.setText(QtGui.QApplication.translate("MainWindow", "Polynomial (order=3)", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionFitTypePolynomial4.setText(QtGui.QApplication.translate("MainWindow", "Polynomial (order=4)", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionFitTypePolynomial5.setText(QtGui.QApplication.translate("MainWindow", "Polynomial (order=5)", None, QtGui.QApplication.UnicodeUTF8))
 
 from colorimeter.table_widget import ColorimeterTableWidget

File python/Colorimeter/colorimeter/import_export.py

     """
     with open(fileName,'r') as fid:
         data = yaml.load(fid)
+
+    # For backward compatability
+    if not 'fitType' in data:
+        data['fitType'] = 'linear'
+    if not 'fitParams' in data: 
+        data['fitParams'] = None
+    if not 'concentrationUnits' in data:
+        data['concentrationUnits'] = 'uM'
+    if data['fitParams'] in ('None', 'none'):
+        data['fitParams'] = None
     return data
 
-def exportTestSolutionData(userHome, solutionName, dataList, color, dateStr):
+def exportTestSolutionData(userHome, dataDict): 
     """
     Exports test solution data to the users directory. Data is saved 
     as a yaml file.
     """
+    solutionName = dataDict['name']
     fileName = getUniqueSolutionFileName(userHome, solutionName)
-    dataDict = {
-            'name': solutionName,
-            'date': dateStr,
-            'led' : color,
-            'values': [map(float,x) for x in dataList],
-            }
     with open(fileName,'w') as fid: 
         yaml.dump(dataDict,fid)
 

File python/Colorimeter/colorimeter/main_window.py

             self.setLEDColor(ledColor)
         self.setTableData(dataList)
         self.updateWidgetEnabled()
+        self.updatePlot(create=False)
 
     def loadDataFromFile(self,filename):
         """

File python/Colorimeter/colorimeter/nonlinear_fit.py

+import numpy 
+
+def getPolynomialFit(xList,yList,order=3,numPts=500): 
+    """
+    Compuetes the coefficient (slope of absorbance vs concentration)
+    """
+    coeff = numpy.polyfit(xList,yList,order)
+    xFit = numpy.linspace(min(xList), max(xList), numPts)
+    yFit = numpy.polyval(coeff, xFit)
+    return coeff,xFit,yFit