diff --git a/info/dev_notes.txt b/info/dev_notes.txt
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..06d82cd9921be7093eadae8b5d46254d6f6b14a4 100644
--- a/info/dev_notes.txt
+++ b/info/dev_notes.txt
@@ -0,0 +1,35 @@
+# Current pgm:
+
+## Input data (naive_peaks.py):
+  - raw data files: binary data (required)
+  - metadata: csv file that holds the information of the PanelData tab in the according .xlsm file
+  - jason file with settings:
+    - raw data folder
+    - processing settings
+
+# Questions:
+
+  - Should there be the possibility to store the current GUI settings (data and processing parameters, etc.)
+  - Should there be the possibility to load default parameters and/or previously saved parameters (e.g. from json file)?
+  - Is the json file required in the current version, e.g. for documentation (processing parameters, etc.) or further analysis?
+  - Are there any default values for the processing parameters? Should they be stored anywhere (e.g. json file)?
+
+## Processing:
+  - Is the input data (raw data) always organized in the same way, i.e.: Raw data folder that contains data files with the
+    file names containing the exper. name date and time?
+    - Where do we get the following parameters (listed in drop down menu) - from the filenames?
+      - Experiment name
+      - Date
+      - Time
+  - should directly the .xlsm file (PanelData tab) be used instead of an .csv file that holds identical information?
+    - What is the actual output of the lifescale devise?
+      - raw data?
+      - xlsm files  with metadata?
+
+## For plotting and analysis:
+  - Is additional data required from other sources, e.g. the .xlsm files?
+  - What should be plotted/analyzed/calculated?
+
+
+
+
diff --git a/lifescale/gui/MainWindow.py b/lifescale/gui/MainWindow.py
index 73b28c42b8084a82c9882ede3c12191873bdbf76..e1d6bff8ecfb591c724f5d042341995d0d985ac8 100644
--- a/lifescale/gui/MainWindow.py
+++ b/lifescale/gui/MainWindow.py
@@ -12,21 +12,150 @@ from PyQt6 import QtCore, QtGui, QtWidgets
 class Ui_MainWindow(object):
     def setupUi(self, MainWindow):
         MainWindow.setObjectName("MainWindow")
-        MainWindow.resize(800, 600)
+        MainWindow.resize(992, 616)
         self.centralwidget = QtWidgets.QWidget(MainWindow)
         self.centralwidget.setObjectName("centralwidget")
+        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
+        self.verticalLayout.setObjectName("verticalLayout")
+        self.tabWidget_Main = QtWidgets.QTabWidget(self.centralwidget)
+        self.tabWidget_Main.setObjectName("tabWidget_Main")
+        self.tab_peaks = QtWidgets.QWidget()
+        self.tab_peaks.setObjectName("tab_peaks")
+        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab_peaks)
+        self.verticalLayout_2.setObjectName("verticalLayout_2")
+        self.groupBox_data = QtWidgets.QGroupBox(self.tab_peaks)
+        self.groupBox_data.setObjectName("groupBox_data")
+        self.formLayout = QtWidgets.QFormLayout(self.groupBox_data)
+        self.formLayout.setObjectName("formLayout")
+        self.label_data_rawData = QtWidgets.QLabel(self.groupBox_data)
+        self.label_data_rawData.setObjectName("label_data_rawData")
+        self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_data_rawData)
+        self.horizontalLayout_data_rawData = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_data_rawData.setObjectName("horizontalLayout_data_rawData")
+        self.lineEdit_data_rawData = QtWidgets.QLineEdit(self.groupBox_data)
+        self.lineEdit_data_rawData.setObjectName("lineEdit_data_rawData")
+        self.horizontalLayout_data_rawData.addWidget(self.lineEdit_data_rawData)
+        self.pushButton_data_rawData = QtWidgets.QPushButton(self.groupBox_data)
+        self.pushButton_data_rawData.setObjectName("pushButton_data_rawData")
+        self.horizontalLayout_data_rawData.addWidget(self.pushButton_data_rawData)
+        self.formLayout.setLayout(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.horizontalLayout_data_rawData)
+        self.label_data_metadataFile = QtWidgets.QLabel(self.groupBox_data)
+        self.label_data_metadataFile.setObjectName("label_data_metadataFile")
+        self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_data_metadataFile)
+        self.horizontalLayout_data_metadataFile = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_data_metadataFile.setObjectName("horizontalLayout_data_metadataFile")
+        self.lineEdit_data_metadataFile = QtWidgets.QLineEdit(self.groupBox_data)
+        self.lineEdit_data_metadataFile.setObjectName("lineEdit_data_metadataFile")
+        self.horizontalLayout_data_metadataFile.addWidget(self.lineEdit_data_metadataFile)
+        self.pushButton_data_metadataFile = QtWidgets.QPushButton(self.groupBox_data)
+        self.pushButton_data_metadataFile.setObjectName("pushButton_data_metadataFile")
+        self.horizontalLayout_data_metadataFile.addWidget(self.pushButton_data_metadataFile)
+        self.formLayout.setLayout(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.horizontalLayout_data_metadataFile)
+        self.label_data_outputFolder = QtWidgets.QLabel(self.groupBox_data)
+        self.label_data_outputFolder.setObjectName("label_data_outputFolder")
+        self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_data_outputFolder)
+        self.horizontalLayout_data_outputFolder = QtWidgets.QHBoxLayout()
+        self.horizontalLayout_data_outputFolder.setObjectName("horizontalLayout_data_outputFolder")
+        self.lineEdit_data_outputFolder = QtWidgets.QLineEdit(self.groupBox_data)
+        self.lineEdit_data_outputFolder.setObjectName("lineEdit_data_outputFolder")
+        self.horizontalLayout_data_outputFolder.addWidget(self.lineEdit_data_outputFolder)
+        self.pushButton_data_outputFolder = QtWidgets.QPushButton(self.groupBox_data)
+        self.pushButton_data_outputFolder.setObjectName("pushButton_data_outputFolder")
+        self.horizontalLayout_data_outputFolder.addWidget(self.pushButton_data_outputFolder)
+        self.formLayout.setLayout(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.horizontalLayout_data_outputFolder)
+        self.label_data_selectExperiment = QtWidgets.QLabel(self.groupBox_data)
+        self.label_data_selectExperiment.setObjectName("label_data_selectExperiment")
+        self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_data_selectExperiment)
+        self.comboBox_data_selectExperiment = QtWidgets.QComboBox(self.groupBox_data)
+        self.comboBox_data_selectExperiment.setObjectName("comboBox_data_selectExperiment")
+        self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.comboBox_data_selectExperiment)
+        self.verticalLayout_2.addWidget(self.groupBox_data)
+        self.groupBox_processingParameters = QtWidgets.QGroupBox(self.tab_peaks)
+        self.groupBox_processingParameters.setObjectName("groupBox_processingParameters")
+        self.formLayout_2 = QtWidgets.QFormLayout(self.groupBox_processingParameters)
+        self.formLayout_2.setObjectName("formLayout_2")
+        self.label_massTransformation = QtWidgets.QLabel(self.groupBox_processingParameters)
+        self.label_massTransformation.setObjectName("label_massTransformation")
+        self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_massTransformation)
+        self.doubleSpinBox_massTransformation = QtWidgets.QDoubleSpinBox(self.groupBox_processingParameters)
+        self.doubleSpinBox_massTransformation.setDecimals(6)
+        self.doubleSpinBox_massTransformation.setObjectName("doubleSpinBox_massTransformation")
+        self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.doubleSpinBox_massTransformation)
+        self.label_massCutoff = QtWidgets.QLabel(self.groupBox_processingParameters)
+        self.label_massCutoff.setObjectName("label_massCutoff")
+        self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_massCutoff)
+        self.doubleSpinBox_massCutoff = QtWidgets.QDoubleSpinBox(self.groupBox_processingParameters)
+        self.doubleSpinBox_massCutoff.setDecimals(1)
+        self.doubleSpinBox_massCutoff.setObjectName("doubleSpinBox_massCutoff")
+        self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.doubleSpinBox_massCutoff)
+        self.label_peakWidthCutoff = QtWidgets.QLabel(self.groupBox_processingParameters)
+        self.label_peakWidthCutoff.setObjectName("label_peakWidthCutoff")
+        self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_peakWidthCutoff)
+        self.doubleSpinBox_peakWidthCutoff = QtWidgets.QDoubleSpinBox(self.groupBox_processingParameters)
+        self.doubleSpinBox_peakWidthCutoff.setDecimals(1)
+        self.doubleSpinBox_peakWidthCutoff.setObjectName("doubleSpinBox_peakWidthCutoff")
+        self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.doubleSpinBox_peakWidthCutoff)
+        self.label_peakDistanceCutoff = QtWidgets.QLabel(self.groupBox_processingParameters)
+        self.label_peakDistanceCutoff.setObjectName("label_peakDistanceCutoff")
+        self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_peakDistanceCutoff)
+        self.doubleSpinBox_peakDistanceCutoff = QtWidgets.QDoubleSpinBox(self.groupBox_processingParameters)
+        self.doubleSpinBox_peakDistanceCutoff.setDecimals(1)
+        self.doubleSpinBox_peakDistanceCutoff.setObjectName("doubleSpinBox_peakDistanceCutoff")
+        self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.doubleSpinBox_peakDistanceCutoff)
+        self.verticalLayout_2.addWidget(self.groupBox_processingParameters)
+        self.pushButton_run = QtWidgets.QPushButton(self.tab_peaks)
+        self.pushButton_run.setObjectName("pushButton_run")
+        self.verticalLayout_2.addWidget(self.pushButton_run)
+        self.tabWidget_Main.addTab(self.tab_peaks, "")
+        self.tab_analysis = QtWidgets.QWidget()
+        self.tab_analysis.setObjectName("tab_analysis")
+        self.tabWidget_Main.addTab(self.tab_analysis, "")
+        self.verticalLayout.addWidget(self.tabWidget_Main)
         MainWindow.setCentralWidget(self.centralwidget)
         self.menubar = QtWidgets.QMenuBar(MainWindow)
-        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
+        self.menubar.setGeometry(QtCore.QRect(0, 0, 992, 22))
         self.menubar.setObjectName("menubar")
+        self.menuOptions = QtWidgets.QMenu(self.menubar)
+        self.menuOptions.setObjectName("menuOptions")
         MainWindow.setMenuBar(self.menubar)
         self.statusbar = QtWidgets.QStatusBar(MainWindow)
         self.statusbar.setObjectName("statusbar")
         MainWindow.setStatusBar(self.statusbar)
+        self.actionSave_current_GUI_parameters_json = QtGui.QAction(MainWindow)
+        self.actionSave_current_GUI_parameters_json.setObjectName("actionSave_current_GUI_parameters_json")
+        self.actionLoad_GUI_parameters_json = QtGui.QAction(MainWindow)
+        self.actionLoad_GUI_parameters_json.setObjectName("actionLoad_GUI_parameters_json")
+        self.actionLoad_default_parameters = QtGui.QAction(MainWindow)
+        self.actionLoad_default_parameters.setObjectName("actionLoad_default_parameters")
+        self.menuOptions.addAction(self.actionSave_current_GUI_parameters_json)
+        self.menuOptions.addAction(self.actionLoad_GUI_parameters_json)
+        self.menuOptions.addAction(self.actionLoad_default_parameters)
+        self.menubar.addAction(self.menuOptions.menuAction())
 
         self.retranslateUi(MainWindow)
+        self.tabWidget_Main.setCurrentIndex(0)
         QtCore.QMetaObject.connectSlotsByName(MainWindow)
 
     def retranslateUi(self, MainWindow):
         _translate = QtCore.QCoreApplication.translate
         MainWindow.setWindowTitle(_translate("MainWindow", "lifescale tools"))
+        self.groupBox_data.setTitle(_translate("MainWindow", "Data"))
+        self.label_data_rawData.setText(_translate("MainWindow", "Raw data folder"))
+        self.pushButton_data_rawData.setText(_translate("MainWindow", "Browse"))
+        self.label_data_metadataFile.setText(_translate("MainWindow", "Metadata file (optional)"))
+        self.pushButton_data_metadataFile.setText(_translate("MainWindow", "Browse"))
+        self.label_data_outputFolder.setText(_translate("MainWindow", "Output folder"))
+        self.pushButton_data_outputFolder.setText(_translate("MainWindow", "Browse"))
+        self.label_data_selectExperiment.setText(_translate("MainWindow", "Select Experiment"))
+        self.groupBox_processingParameters.setTitle(_translate("MainWindow", "Processing parameters"))
+        self.label_massTransformation.setText(_translate("MainWindow", "Mass transformation [fg/Hz]"))
+        self.label_massCutoff.setText(_translate("MainWindow", "Mass cutoff [fg]"))
+        self.label_peakWidthCutoff.setText(_translate("MainWindow", "Peak width cutoff [???]"))
+        self.label_peakDistanceCutoff.setText(_translate("MainWindow", "Peak distance cutoff [??]"))
+        self.pushButton_run.setText(_translate("MainWindow", "Run"))
+        self.tabWidget_Main.setTabText(self.tabWidget_Main.indexOf(self.tab_peaks), _translate("MainWindow", "Mass Peaks"))
+        self.tabWidget_Main.setTabText(self.tabWidget_Main.indexOf(self.tab_analysis), _translate("MainWindow", "Analysis"))
+        self.menuOptions.setTitle(_translate("MainWindow", "Options"))
+        self.actionSave_current_GUI_parameters_json.setText(_translate("MainWindow", "Save current GUI parameters (json)"))
+        self.actionLoad_GUI_parameters_json.setText(_translate("MainWindow", "Load GUI parameters (json)"))
+        self.actionLoad_default_parameters.setText(_translate("MainWindow", "Load default parameters"))
diff --git a/lifescale/gui/MainWindow.ui b/lifescale/gui/MainWindow.ui
index b8c1b1692ec5e9d4658de10826e20a2573cf08ff..8b01b72c078c0c7b11c1ad10d6185da8c5a62ca0 100644
--- a/lifescale/gui/MainWindow.ui
+++ b/lifescale/gui/MainWindow.ui
@@ -6,25 +6,225 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>800</width>
-    <height>600</height>
+    <width>992</width>
+    <height>616</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>lifescale tools</string>
   </property>
-  <widget class="QWidget" name="centralwidget"/>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QVBoxLayout" name="verticalLayout">
+    <item>
+     <widget class="QTabWidget" name="tabWidget_Main">
+      <property name="currentIndex">
+       <number>0</number>
+      </property>
+      <widget class="QWidget" name="tab_peaks">
+       <attribute name="title">
+        <string>Mass Peaks</string>
+       </attribute>
+       <layout class="QVBoxLayout" name="verticalLayout_2">
+        <item>
+         <widget class="QGroupBox" name="groupBox_data">
+          <property name="title">
+           <string>Data</string>
+          </property>
+          <layout class="QFormLayout" name="formLayout">
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_data_rawData">
+             <property name="text">
+              <string>Raw data folder</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <layout class="QHBoxLayout" name="horizontalLayout_data_rawData">
+             <item>
+              <widget class="QLineEdit" name="lineEdit_data_rawData"/>
+             </item>
+             <item>
+              <widget class="QPushButton" name="pushButton_data_rawData">
+               <property name="text">
+                <string>Browse</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_data_metadataFile">
+             <property name="text">
+              <string>Metadata file (optional)</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <layout class="QHBoxLayout" name="horizontalLayout_data_metadataFile">
+             <item>
+              <widget class="QLineEdit" name="lineEdit_data_metadataFile"/>
+             </item>
+             <item>
+              <widget class="QPushButton" name="pushButton_data_metadataFile">
+               <property name="text">
+                <string>Browse</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="label_data_outputFolder">
+             <property name="text">
+              <string>Output folder</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <layout class="QHBoxLayout" name="horizontalLayout_data_outputFolder">
+             <item>
+              <widget class="QLineEdit" name="lineEdit_data_outputFolder"/>
+             </item>
+             <item>
+              <widget class="QPushButton" name="pushButton_data_outputFolder">
+               <property name="text">
+                <string>Browse</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="label_data_selectExperiment">
+             <property name="text">
+              <string>Select Experiment</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QComboBox" name="comboBox_data_selectExperiment"/>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox_processingParameters">
+          <property name="title">
+           <string>Processing parameters</string>
+          </property>
+          <layout class="QFormLayout" name="formLayout_2">
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_massTransformation">
+             <property name="text">
+              <string>Mass transformation [fg/Hz]</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QDoubleSpinBox" name="doubleSpinBox_massTransformation">
+             <property name="decimals">
+              <number>6</number>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_massCutoff">
+             <property name="text">
+              <string>Mass cutoff [fg]</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QDoubleSpinBox" name="doubleSpinBox_massCutoff">
+             <property name="decimals">
+              <number>1</number>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="label_peakWidthCutoff">
+             <property name="text">
+              <string>Peak width cutoff [???]</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QDoubleSpinBox" name="doubleSpinBox_peakWidthCutoff">
+             <property name="decimals">
+              <number>1</number>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="label_peakDistanceCutoff">
+             <property name="text">
+              <string>Peak distance cutoff [??]</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QDoubleSpinBox" name="doubleSpinBox_peakDistanceCutoff">
+             <property name="decimals">
+              <number>1</number>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="pushButton_run">
+          <property name="text">
+           <string>Run</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tab_analysis">
+       <attribute name="title">
+        <string>Analysis</string>
+       </attribute>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
   <widget class="QMenuBar" name="menubar">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>0</y>
-     <width>800</width>
+     <width>992</width>
      <height>22</height>
     </rect>
    </property>
+   <widget class="QMenu" name="menuOptions">
+    <property name="title">
+     <string>Options</string>
+    </property>
+    <addaction name="actionSave_current_GUI_parameters_json"/>
+    <addaction name="actionLoad_GUI_parameters_json"/>
+    <addaction name="actionLoad_default_parameters"/>
+   </widget>
+   <addaction name="menuOptions"/>
   </widget>
   <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionSave_current_GUI_parameters_json">
+   <property name="text">
+    <string>Save current GUI parameters (json)</string>
+   </property>
+  </action>
+  <action name="actionLoad_GUI_parameters_json">
+   <property name="text">
+    <string>Load GUI parameters (json)</string>
+   </property>
+  </action>
+  <action name="actionLoad_default_parameters">
+   <property name="text">
+    <string>Load default parameters</string>
+   </property>
+  </action>
  </widget>
  <resources/>
  <connections/>
diff --git a/lifescale/gui/gui_main.py b/lifescale/gui/gui_main.py
index a59b42cea5c97cae3d085d150f320fb2624b9f39..4bfbecadb8f7a91011c94714f3e6c0adcc92de1f 100644
--- a/lifescale/gui/gui_main.py
+++ b/lifescale/gui/gui_main.py
@@ -22,7 +22,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         # Init models:
 
 
-
 def main():
     """Main program to start the GUI."""
     # Create the application
diff --git a/lifescale/naive_peaks/__init__.py b/lifescale/mass_peak_caller/__init__.py
similarity index 100%
rename from lifescale/naive_peaks/__init__.py
rename to lifescale/mass_peak_caller/__init__.py
diff --git a/lifescale/mass_peak_caller/configure_peakcaller.py b/lifescale/mass_peak_caller/configure_peakcaller.py
new file mode 100644
index 0000000000000000000000000000000000000000..5268d8c30172e67109ba4ec77dda84fab8bed97e
--- /dev/null
+++ b/lifescale/mass_peak_caller/configure_peakcaller.py
@@ -0,0 +1,50 @@
+import os
+import platform
+import json
+
+DEFAULT_CONFIG = {
+    "mass_transformation": 0.00574,
+    "mass_cutoff": 20,
+    "peak_width_cutoff": 5,
+    "peak_distance_cutoff": 5,
+    "raw_data_folder": "~/research/lifescale_raw_data_test/development_raw_data_folder"
+}
+
+LINUX_PATH = "./dev_config.json"
+WINDOWS_PATH = r"C:\Users\LifeScale\Documents\peak_caller_config\peak_caller_config.json"
+
+def load_config():
+    if platform.system() == "Linux":
+        try:
+            with open(LINUX_PATH, "r") as f:
+                config = json.load(f)
+            return config, None
+        except FileNotFoundError as e:
+            config = DEFAULT_CONFIG
+            with open(LINUX_PATH, "w") as f:
+                json.dump(config, f)
+            return config, LINUX_PATH
+    elif platform.system() == "Windows":
+        try:
+            with open(WINDOWS_PATH, "r") as f:
+                config = json.load(f)
+            return config, None
+        except FileNotFoundError as e:
+            config = DEFAULT_CONFIG
+            with open(WINDOWS_PATH, "w") as f:
+                json.dump(config, f)
+            return config, WINDOWS_PATH
+
+def configure_peakcaller(raw_data_folder, mass_transformation, mass_cutoff, peak_width_cutoff, peak_distance_cutoff, config, command):
+    print(locals())
+    new_config = {k:v for k,v in locals().items() if k != "config" and k != "command" and v is not None}
+    print(new_config)
+    old_config = locals()["config"]
+    merged_config = {k:new_config[k] if k in new_config else old_config[k] for k in old_config}
+    if platform.system() == "Linux":
+        with open(LINUX_PATH, "w") as f:
+            json.dump(merged_config, f)
+    elif platform.system() == "Windows":
+        with open(WINDOWS_PATH, "w") as f:
+            json.dump(merged_config, f)
+    return merged_config
diff --git a/lifescale/mass_peak_caller/dev_config.json b/lifescale/mass_peak_caller/dev_config.json
new file mode 100644
index 0000000000000000000000000000000000000000..129888342d7db45b2f7919d3acdb0d7697625476
--- /dev/null
+++ b/lifescale/mass_peak_caller/dev_config.json
@@ -0,0 +1 @@
+{"mass_transformation": 0.00574, "mass_cutoff": 20.0, "peak_width_cutoff": 5.0, "peak_distance_cutoff": 5.0, "raw_data_folder": "/home/heller/pyProjects/gooey_lifescale/LSdata/raw_data"}
\ No newline at end of file
diff --git a/lifescale/mass_peak_caller/naive_peaks.py b/lifescale/mass_peak_caller/naive_peaks.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd51974faf6ab8bd5bd33082fb7d3c7b21cebbd3
--- /dev/null
+++ b/lifescale/mass_peak_caller/naive_peaks.py
@@ -0,0 +1,92 @@
+""" GUI application for processing LifeScale data.
+
+copyright 2019 Joseph Elsherbini
+all rights reserved
+"""
+
+import os
+import struct
+import json
+import re
+import datetime
+from itertools import chain
+from operator import itemgetter
+import numpy as np
+import pandas as pd
+import scipy.signal
+
+NOW = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
+
+def list_experiments(config):
+    raw_data_files = [f for f in os.listdir(os.path.expanduser(config["raw_data_folder"])) if re.search(r"(.+)_(\d{6})_(\d{6})", f) and os.path.splitext(f)[1] == ""]
+    unique_experiments = sorted(sorted(list(set([re.search(r"(.+)_(\d{6})_(\d{6})", f).groups() for f in raw_data_files])),
+                         key=itemgetter(2), reverse=True), key=itemgetter(1), reverse=True)
+    return (["{} {}".format(e[0], get_date_time(e[1], e[2])) for e in unique_experiments], ["_".join(e) for e in unique_experiments])
+
+def get_date_time(date, time):
+    fmt_string = "%m/%d/%Y %H:%M:%S"
+    return datetime.datetime(2000+int(date[0:2]), int(date[2:4]), int(date[4:6]), int(time[0:2]), int(time[2:4]), int(time[4:6])).strftime(fmt_string)
+
+
+def call_peaks(experiment, output_folder, metadata_file, config, command):
+    update_now()
+    all_experiments= list_experiments(config)
+    exp_name = [e[1] for e in zip(all_experiments[0], all_experiments[1]) if e[0] == experiment][0]
+    exp_files = [os.path.join(os.path.expanduser(config["raw_data_folder"]), f) for f in os.listdir(os.path.expanduser(config["raw_data_folder"])) if exp_name in f and os.path.splitext(f)[1] == ""]
+    print(exp_name, exp_files)
+    peaks = write_peaks(exp_name, exp_files, output_folder, metadata_file, config)
+    write_summary(exp_name, peaks, output_folder)
+    # TODO write_plots(exp_name, peaks, output_folder, config)
+    write_config(exp_name, output_folder, config)
+    return config
+
+def update_now():
+    global NOW
+    NOW = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
+
+def parse_metadata(metadata_file):
+    return pd.read_csv(metadata_file)[["Id", "Well"]]
+
+def load_raw_data(exp_name, exp_files):
+    for f_path in exp_files:
+        m = re.search(r"(.+)_(\d{6})_(\d{6})_c(\d+)_v(\d+)", f_path)
+        exp_date, exp_time, exp_cycle, exp_measurement = m.group(2,3,4,5)
+        print(exp_name, exp_date, exp_time, exp_cycle, exp_measurement)
+        n_datapoints = int(os.path.getsize(f_path) / 8)
+        with open(f_path, "rb") as f:
+            content = f.read()
+            a = np.array(struct.unpack("d"*n_datapoints, content))[10:]
+        yield dict(zip(["exp_name", "exp_date", "exp_time", "exp_cycle", "exp_measurement", "data_array"],
+                       [exp_name, exp_date, exp_time, exp_cycle, exp_measurement, a]))
+
+def generate_peaks(measurement, config):
+    filtered_signal = scipy.signal.savgol_filter(measurement["data_array"], window_length=5, polyorder=3)
+    peaks, _ = scipy.signal.find_peaks(-filtered_signal, width=config["peak_width_cutoff"], prominence=config["mass_cutoff"]*config["mass_transformation"], distance=config["peak_distance_cutoff"])
+    masses = scipy.signal.peak_prominences(-filtered_signal, peaks)[0]*(1/config["mass_transformation"])
+    for peak, mass in zip(peaks, masses):
+        yield dict(zip(["exp_name", "exp_date", "exp_time", "exp_cycle", "exp_measurement", "event_index","event_mass"],
+                       [measurement["exp_name"], measurement["exp_date"],measurement["exp_time"],measurement["exp_cycle"],measurement["exp_measurement"], peak, mass]))
+
+def write_peaks(exp_name, exp_files, output_folder, metadata_file, config):
+    peaks = pd.DataFrame(chain.from_iterable([generate_peaks(measurement, config) for measurement in load_raw_data(exp_name, exp_files)]))
+    if metadata_file:
+        metadata = parse_metadata(metadata_file)
+        peaks = peaks.astype({'exp_measurement':'int32'}).merge(metadata.astype({'Id':'int32'}), how='left', left_on='exp_measurement', right_on='Id')
+        peaks["Well"] = ["".join([w[0],w[1:].zfill(2)]) for w in peaks["Well"]]
+    out_path = os.path.join(os.path.expanduser(output_folder), "{}_{}_peaks.csv".format(NOW, exp_name))
+    peaks.to_csv(out_path, index=False)
+    return peaks
+
+def write_summary(exp_name, peaks, output_folder):
+    print(peaks.columns)
+    if "Well" in peaks.columns:
+        summary = peaks.groupby(["Well", "exp_cycle"])["event_mass"].describe()
+    else:
+        summary = peaks.groupby(["exp_measurement", "exp_cycle"])["event_mass"].describe()
+    out_path = os.path.join(os.path.expanduser(output_folder), "{}_{}_summary.csv".format(NOW, exp_name))
+    summary.to_csv(out_path)
+
+def write_config(exp_name, output_folder, config):
+    output_path = os.path.join(os.path.expanduser(output_folder), "{}_{}_config.json".format(NOW, exp_name))
+    with open(output_path, "w") as f:
+        json.dump(config, f)
diff --git a/lifescale/mass_peak_caller/peak_caller_gui.py b/lifescale/mass_peak_caller/peak_caller_gui.py
new file mode 100644
index 0000000000000000000000000000000000000000..d618f3514a25bb44291079c708006335cb7cd049
--- /dev/null
+++ b/lifescale/mass_peak_caller/peak_caller_gui.py
@@ -0,0 +1,60 @@
+import os
+import re
+from datetime import datetime
+from functools import partial
+from operator import itemgetter
+from gooey import Gooey, GooeyParser
+import naive_peaks
+import configure_peakcaller
+
+DISPATCHER = {
+    "call_peaks": naive_peaks.call_peaks,
+    "config": configure_peakcaller.configure_peakcaller
+}
+
+def show_error_modal(error_msg):
+    """ Spawns a modal with error_msg"""
+    # wx imported locally so as not to interfere with Gooey
+    import wx
+    app = wx.App()
+    dlg = wx.MessageDialog(None, error_msg, 'Error', wx.ICON_ERROR)
+    dlg.ShowModal()
+    dlg.Destroy()
+
+def add_call_peak_gui(subs, config):
+    p = subs.add_parser('call_peaks', prog='Call Mass Peaks', help='Get Mass Peaks from Raw Lifescale Data')
+    p.add_argument(
+        'experiment',
+        metavar='Choose an Experiment',
+        help='Choose the name of an experiment',
+        widget='Dropdown',
+        choices=naive_peaks.list_experiments(config)[0])
+    p.add_argument('output_folder', widget="DirChooser")
+    p.add_argument('--metadata_file', '-f', widget="FileChooser", help="If provided, convert vial ids to sample names. Should be the exported csv file called PanelData.csv.")
+
+def add_config_gui(subs, config):
+    p = subs.add_parser('config', prog="Configure Program", help="Options to change where this program looks for data, and the calibration used for frequency to mass conversion.")
+    p.add_argument('--raw_data_folder', widget="DirChooser", help="currently {}".format(config["raw_data_folder"]))
+    p.add_argument('--mass_transformation', type=float, help='currently {} Hz/fg'.format(config["mass_transformation"]))
+    p.add_argument('--mass_cutoff', '-m', type=float, default=20, help='currently {} fg - minimum mass of the peak (minimum 5fg recommended)'.format(config["mass_cutoff"]))
+    p.add_argument('--peak_width_cutoff', '-w', type=float, default=5, help='currently {} - width cutoff for peaks - minimum datapoints looking larger than noise'.format(config["peak_width_cutoff"]))
+    p.add_argument('--peak_distance_cutoff', '-d', type=float, default=5, help='currently {} - distance cutoff for peaks - minimum datapoints between peaks'.format(config["peak_distance_cutoff"]))
+
+@Gooey(program_name='Mass Peak Caller', image_dir='./images', required_cols=1)
+def main():
+    current_config, file_not_found = configure_peakcaller.load_config()
+    if file_not_found:
+        show_error_modal("No configuration file found at {}.\nWrote default configuration to that location.\nContinuing with default config.".format(file_not_found))
+
+    parser = GooeyParser(description='Get Mass Peaks from Raw Lifescale Data')
+    subs = parser.add_subparsers(help='commands', dest='command')
+    add_call_peak_gui(subs, current_config)
+    add_config_gui(subs, current_config)
+
+    args = parser.parse_args()
+    opts = vars(args)
+    func = partial(DISPATCHER[args.command], config=current_config)
+    current_config = func(**opts)
+
+if __name__ == '__main__':
+    main()